代码格式化

This commit is contained in:
liaoxuezhi 2019-05-08 13:52:04 +08:00
parent 39afde9f97
commit 43a2b42c7c
53 changed files with 4271 additions and 4701 deletions

View File

@ -6,5 +6,6 @@
"semi": true,
"trailingComma": "es5",
"bracketSpacing": false,
"arrowParens": "avoid"
"arrowParens": "avoid",
"jsxBracketSameLine": false
}

View File

@ -9,7 +9,8 @@
"start": "fis3 server start --www ./public --port 8888 --no-daemon --no-browse",
"stop": "fis3 server stop",
"dev": "fis3 release -cwd ./public",
"publish2npm": "sh publish.sh && npm publish"
"publish2npm": "sh publish.sh && npm publish",
"prettier": "prettier --write src/**/*.tsx"
},
"repository": {
"type": "git",
@ -120,6 +121,7 @@
"lint-staged": "^8.1.6",
"marked": "^0.3.7",
"mobx-wiretap": "^0.12.0",
"prettier": "1.17.0",
"react-frame-component": "^2.0.0",
"react-router": "3.2.0",
"react-test-renderer": "^16.8.6",

View File

@ -1,23 +1,46 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import {
filter
} from '../utils/tpl';
import {Renderer, RendererProps} from '../factory';
import {filter} from '../utils/tpl';
import Button from '../components/Button';
import pick = require('lodash/pick');
const ActionProps = [
'dialog', 'drawer', 'url', 'link', 'confirmText', 'tooltip', 'disabledTip', 'className', 'asyncApi', 'redirect',
'size', 'level', 'primary', 'feedback', 'api', 'blank', 'tooltipPlacement', 'to', 'content', 'required',
'type', 'actionType', 'label', 'icon', 'reload', 'target', 'close', 'messages', 'mergeData', 'index',
'copy', 'content'
'dialog',
'drawer',
'url',
'link',
'confirmText',
'tooltip',
'disabledTip',
'className',
'asyncApi',
'redirect',
'size',
'level',
'primary',
'feedback',
'api',
'blank',
'tooltipPlacement',
'to',
'content',
'required',
'type',
'actionType',
'label',
'icon',
'reload',
'target',
'close',
'messages',
'mergeData',
'index',
'copy',
'content',
];
import { filterContents } from './Remark';
import { ClassNamesFn, themeable } from '../theme';
import { Omit } from '../types';
import { autobind } from '../utils/helper';
import {filterContents} from './Remark';
import {ClassNamesFn, themeable} from '../theme';
import {Omit} from '../types';
import {autobind} from '../utils/helper';
export interface ActionProps {
className?: string;
@ -28,13 +51,13 @@ export interface ActionProps {
iconClassName?: string;
size?: 'xs' | 'sm' | 'md' | 'lg';
level?: 'info' | 'success' | 'warning' | 'danger' | 'link';
onAction?: (e: React.MouseEvent<any> | void | null, action:object) => void;
onAction?: (e: React.MouseEvent<any> | void | null, action: object) => void;
isCurrentUrl?: (link: string) => boolean;
onClick?: (e:React.MouseEvent<any>) => void;
onClick?: (e: React.MouseEvent<any>) => void;
primary?: boolean;
activeClassName: string;
componentClass: React.ReactType;
tooltipPlacement: "bottom" | "top" | "right" | "left" | undefined;
tooltipPlacement: 'bottom' | 'top' | 'right' | 'left' | undefined;
disabled?: boolean;
block?: boolean;
data?: any;
@ -52,22 +75,18 @@ export interface ActionProps {
const allowedType = ['button', 'submit', 'reset'];
export class Action extends React.Component<ActionProps> {
static defaultProps:Pick<ActionProps, "type" | "componentClass" | "tooltipPlacement" | "activeClassName"> = {
static defaultProps: Pick<ActionProps, 'type' | 'componentClass' | 'tooltipPlacement' | 'activeClassName'> = {
type: 'button',
componentClass: 'button',
tooltipPlacement: 'bottom',
activeClassName: 'is-active'
activeClassName: 'is-active',
};
dom:any;
dom: any;
@autobind
handleAction(e:React.MouseEvent<any>) {
const {
onAction,
onClick,
disabled
} = this.props;
handleAction(e: React.MouseEvent<any>) {
const {onAction, onClick, disabled} = this.props;
onClick && onClick(e);
@ -105,7 +124,7 @@ export class Action extends React.Component<ActionProps> {
active,
activeLevel,
tooltipContainer,
classnames: cx
classnames: cx,
} = this.props;
let isActive = !!active;
@ -118,20 +137,20 @@ export class Action extends React.Component<ActionProps> {
<a
className={cx(className, {
[activeClassName || 'is-active']: isActive,
'is-disabled': disabled
'is-disabled': disabled,
})}
onClick={this.handleAction}
>
{label}
{icon ? <i className={cx('Button-icon', icon)} /> : null}
{icon ? <i className={cx('Button-icon', icon)} /> : null}
</a>
) : (
<Button
className={cx(className, {
[activeClassName || 'is-active']: isActive
[activeClassName || 'is-active']: isActive,
})}
size={size}
level={activeLevel && isActive ? activeLevel : (level || (primary ? 'primary': undefined))}
level={activeLevel && isActive ? activeLevel : level || (primary ? 'primary' : undefined)}
onClick={this.handleAction}
type={type && ~allowedType.indexOf(type) ? type : 'button'}
disabled={disabled}
@ -141,9 +160,9 @@ export class Action extends React.Component<ActionProps> {
placement={tooltipPlacement}
tooltipContainer={tooltipContainer}
block={block}
iconOnly={!!(icon && !label && level !== "link")}
iconOnly={!!(icon && !label && level !== 'link')}
>
{label ? (<span>{filter(label, data)}</span>) : null}
{label ? <span>{filter(label, data)}</span> : null}
{icon ? <i className={cx('Button-icon', icon, iconClassName)} /> : null}
</Button>
);
@ -154,53 +173,43 @@ export default themeable(Action);
@Renderer({
test: /(^|\/)action$/,
name: 'action'
name: 'action',
})
export class ActionRenderer extends React.Component<RendererProps & Omit<ActionProps, 'onAction' | 'isCurrentUrl' | 'tooltipContainer'> & {
onAction: (e: React.MouseEvent<any> | void | null, action:object, data:any) => void;
btnDisabled?: boolean;
}> {
export class ActionRenderer extends React.Component<
RendererProps &
Omit<ActionProps, 'onAction' | 'isCurrentUrl' | 'tooltipContainer'> & {
onAction: (e: React.MouseEvent<any> | void | null, action: object, data: any) => void;
btnDisabled?: boolean;
}
> {
@autobind
handleAction(e: React.MouseEvent<any> | void | null, action:any) {
const {
env,
onAction,
data
} = this.props;
handleAction(e: React.MouseEvent<any> | void | null, action: any) {
const {env, onAction, data} = this.props;
if (action.confirmText && env.confirm) {
env
.confirm(filter(action.confirmText, data))
.then((confirmed:boolean) => confirmed && onAction(e, action, data));
env.confirm(filter(action.confirmText, data)).then(
(confirmed: boolean) => confirmed && onAction(e, action, data)
);
} else {
onAction(e, action, data);
}
}
@autobind
isCurrentAction(link:string) {
const {
env,
data
} = this.props;
isCurrentAction(link: string) {
const {env, data} = this.props;
return env.isCurrentUrl(filter(link, data));
}
render() {
const {
env,
disabled,
btnDisabled,
...rest
} = this.props;
const {env, disabled, btnDisabled, ...rest} = this.props;
return (
<Action
<Action
{...rest}
disabled={disabled || btnDisabled}
onAction={this.handleAction}
isCurrentUrl={this.isCurrentAction}
onAction={this.handleAction}
isCurrentUrl={this.isCurrentAction}
tooltipContainer={env.getModalContainer ? env.getModalContainer() : undefined}
/>
);
@ -209,18 +218,18 @@ export class ActionRenderer extends React.Component<RendererProps & Omit<ActionP
@Renderer({
test: /(^|\/)button$/,
name: 'button'
name: 'button',
})
export class ButtonRenderer extends ActionRenderer {}
@Renderer({
test: /(^|\/)submit$/,
name: 'submit'
name: 'submit',
})
export class SubmitRenderer extends ActionRenderer {}
@Renderer({
test: /(^|\/)reset$/,
name: 'reset'
name: 'reset',
})
export class ResetRenderer extends ActionRenderer {}

View File

@ -1,23 +1,14 @@
import { Renderer, RendererProps } from "../factory";
import React = require("react");
import Alert, { AlertProps } from "../components/Alert2";
import {Renderer, RendererProps} from '../factory';
import React = require('react');
import Alert, {AlertProps} from '../components/Alert2';
@Renderer({
test: /(^|\/)alert$/,
name: 'alert'
name: 'alert',
})
export class TplRenderer extends React.Component<AlertProps & RendererProps> {
render() {
const {
render,
body,
...rest
} = this.props;
return (
<Alert {...rest}>
{render('body', body)}
</Alert>
);
const {render, body, ...rest} = this.props;
return <Alert {...rest}>{render('body', body)}</Alert>;
}
};
}

View File

@ -1,32 +1,29 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import { autobind } from '../utils/helper';
import { volumeIcon, muteIcon, playIcon, pauseIcon} from '../components/icons';
import {Renderer, RendererProps} from '../factory';
import {autobind} from '../utils/helper';
import {volumeIcon, muteIcon, playIcon, pauseIcon} from '../components/icons';
export interface AudioProps extends RendererProps {
className?: string;
inline?: boolean,
src?: string,
autoPlay?: boolean,
loop?: boolean,
rates?: number[]
inline?: boolean;
src?: string;
autoPlay?: boolean;
loop?: boolean;
rates?: number[];
}
export interface AudioState {
isReady?: boolean,
muted?: boolean,
playing?: boolean,
played: number,
seeking?: boolean,
volume: number,
prevVolume: number,
loaded?: number,
playbackRate: number,
showHandlePlaybackRate: boolean,
showHandleVolume: boolean
isReady?: boolean;
muted?: boolean;
playing?: boolean;
played: number;
seeking?: boolean;
volume: number;
prevVolume: number;
loaded?: number;
playbackRate: number;
showHandlePlaybackRate: boolean;
showHandleVolume: boolean;
}
export class Audio extends React.Component<AudioProps, AudioState> {
@ -34,16 +31,19 @@ export class Audio extends React.Component<AudioProps, AudioState> {
progressTimeout: any;
durationTimeout: any;
static defaultProps:Pick<AudioProps, 'inline' | 'autoPlay' | 'playbackRate' | 'loop' | 'rates' | 'progressInterval'> = {
static defaultProps: Pick<
AudioProps,
'inline' | 'autoPlay' | 'playbackRate' | 'loop' | 'rates' | 'progressInterval'
> = {
inline: true,
autoPlay: false,
playbackRate: 1,
loop: false,
rates: [1.0, 2.0, 4.0],
progressInterval: 1000
progressInterval: 1000,
};
state:AudioState = {
state: AudioState = {
isReady: false,
muted: false,
playing: false,
@ -54,19 +54,22 @@ export class Audio extends React.Component<AudioProps, AudioState> {
loaded: 0,
playbackRate: 1.0,
showHandlePlaybackRate: false,
showHandleVolume: false
}
showHandleVolume: false,
};
componentWillUnmount () {
componentWillUnmount() {
clearTimeout(this.progressTimeout);
}
componentDidMount() {
const autoPlay = this.props.autoPlay;
const playing = autoPlay ? true : false;
this.setState({
playing: playing
}, this.progress);
this.setState(
{
playing: playing,
},
this.progress
);
}
@autobind
@ -77,33 +80,33 @@ export class Audio extends React.Component<AudioProps, AudioState> {
const duration = this.audio.duration;
const played = currentTime / duration;
let playing = this.state.playing;
playing = (played != 1 && playing) ? true : false;
playing = played != 1 && playing ? true : false;
this.setState({
played,
playing
playing,
});
this.progressTimeout = setTimeout(this.progress, (this.props.progressInterval / this.state.playbackRate))
this.progressTimeout = setTimeout(this.progress, this.props.progressInterval / this.state.playbackRate);
}
}
@autobind
audioRef(audio:any) {
audioRef(audio: any) {
this.audio = audio;
}
@autobind
load() {
this.setState({
isReady: true
isReady: true,
});
}
@autobind
handlePlaybackRate(rate:number) {
handlePlaybackRate(rate: number) {
this.audio.playbackRate = rate;
this.setState({
playbackRate: rate,
showHandlePlaybackRate: false
showHandlePlaybackRate: false,
});
}
@ -117,7 +120,7 @@ export class Audio extends React.Component<AudioProps, AudioState> {
this.audio.muted = !muted;
this.setState({
muted: !muted,
volume: curVolume
volume: curVolume,
});
}
@ -129,7 +132,7 @@ export class Audio extends React.Component<AudioProps, AudioState> {
let playing = this.state.playing;
playing ? this.audio.pause() : this.audio.play();
this.setState({
playing: !playing
playing: !playing,
});
}
@ -144,7 +147,7 @@ export class Audio extends React.Component<AudioProps, AudioState> {
}
@autobind
getDuration () {
getDuration() {
if (!this.audio || !this.props.src) {
return '0:00';
}
@ -152,11 +155,11 @@ export class Audio extends React.Component<AudioProps, AudioState> {
this.onDurationCheck();
return '0:00';
}
const { duration, seekable } = this.audio;
const {duration, seekable} = this.audio;
// on iOS, live streams return Infinity for the duration
// so instead we use the end of the seekable timerange
if (duration === Infinity && seekable.length > 0) {
return seekable.end(seekable.length - 1)
return seekable.end(seekable.length - 1);
}
return this.formatTime(duration);
}
@ -172,21 +175,21 @@ export class Audio extends React.Component<AudioProps, AudioState> {
}
@autobind
onSeekChange(e:any) {
onSeekChange(e: any) {
if (!this.props.src) {
return;
}
const played = e.target.value;
this.setState({ played: played });
this.setState({played: played});
}
@autobind
onSeekMouseDown() {
this.setState({ seeking: true });
this.setState({seeking: true});
}
@autobind
onSeekMouseUp(e:any) {
onSeekMouseUp(e: any) {
if (!this.state.seeking) {
return;
}
@ -196,15 +199,15 @@ export class Audio extends React.Component<AudioProps, AudioState> {
const loop = this.props.loop;
let playing = this.state.playing;
playing = (played < 1 || loop) ? playing : false;
playing = played < 1 || loop ? playing : false;
this.setState({
playing: playing,
seeking: false
seeking: false,
});
}
@autobind
setVolume(e:any) {
setVolume(e: any) {
if (!this.props.src) {
return;
}
@ -212,12 +215,12 @@ export class Audio extends React.Component<AudioProps, AudioState> {
this.audio.volume = volume;
this.setState({
volume: volume,
prevVolume: volume
prevVolume: volume,
});
}
@autobind
formatTime(seconds:number) {
formatTime(seconds: number) {
const date = new Date(seconds * 1000);
const hh = date.getUTCHours();
const mm = date.getUTCMinutes();
@ -229,8 +232,8 @@ export class Audio extends React.Component<AudioProps, AudioState> {
}
@autobind
pad(string:number) {
return ('0' + string).slice(-2)
pad(string: number) {
return ('0' + string).slice(-2);
}
@autobind
@ -239,39 +242,23 @@ export class Audio extends React.Component<AudioProps, AudioState> {
return;
}
this.setState({
showHandlePlaybackRate: !this.state.showHandlePlaybackRate
showHandlePlaybackRate: !this.state.showHandlePlaybackRate,
});
}
@autobind
toggleHandleVolume(type:boolean) {
toggleHandleVolume(type: boolean) {
if (!this.props.src) {
return;
}
this.setState({
showHandleVolume: type
showHandleVolume: type,
});
}
render() {
const {
className,
inline,
src,
autoPlay,
loop,
rates,
classnames: cx
} = this.props;
const {
playing,
played,
volume,
muted,
playbackRate,
showHandlePlaybackRate,
showHandleVolume
} = this.state;
const {className, inline, src, autoPlay, loop, rates, classnames: cx} = this.props;
const {playing, played, volume, muted, playbackRate, showHandlePlaybackRate, showHandleVolume} = this.state;
return (
<div className={cx(inline ? 'Audio--inline' : '')}>
@ -282,55 +269,75 @@ export class Audio extends React.Component<AudioProps, AudioState> {
autoPlay={autoPlay}
controls
muted={muted}
loop={loop}>
<source src={src}/>
loop={loop}
>
<source src={src} />
</audio>
<div className={cx('Audio', className)}>
{rates && rates.length ?
(<div className={cx('Audio-rates')}>
<div className={cx('Audio-rate')}
onClick={this.toggleHandlePlaybackRate}>
{rates && rates.length ? (
<div className={cx('Audio-rates')}>
<div className={cx('Audio-rate')} onClick={this.toggleHandlePlaybackRate}>
x{playbackRate.toFixed(1)}
</div>
{showHandlePlaybackRate ?
(<div className={cx('Audio-rateControl')}>
{rates.map((rate, index) =>
<span className={cx('Audio-rateControlItem')}
key={index}
onClick={() => this.handlePlaybackRate(rate)}>
{showHandlePlaybackRate ? (
<div className={cx('Audio-rateControl')}>
{rates.map((rate, index) => (
<span
className={cx('Audio-rateControlItem')}
key={index}
onClick={() => this.handlePlaybackRate(rate)}
>
x{rate.toFixed(1)}
</span>
)} </div>)
: null}
</div>)
: (<div className={cx('Audio-rates-holder')}></div>) }
</span>
))}{' '}
</div>
) : null}
</div>
) : (
<div className={cx('Audio-rates-holder')} />
)}
<div className={cx('Audio-play')} onClick={this.handlePlaying}>
{playing ? pauseIcon : playIcon}
</div>
<div className={cx('Audio-times')}>{this.getCurrentTime()} / {this.getDuration()}</div>
<div className={cx('Audio-times')}>
{this.getCurrentTime()} / {this.getDuration()}
</div>
<div className={cx('Audio-process')}>
<input
type="range"
min={0} max={1} step="any"
min={0}
max={1}
step="any"
value={played || 0}
onMouseDown={this.onSeekMouseDown}
onChange={this.onSeekChange}
onMouseUp={this.onSeekMouseUp}/>
onMouseUp={this.onSeekMouseUp}
/>
</div>
<div className={cx('Audio-volume')}
onMouseEnter={() => this.toggleHandleVolume(true)}
onMouseLeave={() => this.toggleHandleVolume(false)}>
{showHandleVolume ?
(<div className={cx('Audio-volumeControl')}>
<div
className={cx('Audio-volume')}
onMouseEnter={() => this.toggleHandleVolume(true)}
onMouseLeave={() => this.toggleHandleVolume(false)}
>
{showHandleVolume ? (
<div className={cx('Audio-volumeControl')}>
<input
type='range' min={0} max={1} step='any'
type="range"
min={0}
max={1}
step="any"
value={volume}
onChange={this.setVolume} />
<div className={cx('Audio-volumeControlIcon')}
onClick={this.handleMute}>
onChange={this.setVolume}
/>
<div className={cx('Audio-volumeControlIcon')} onClick={this.handleMute}>
{volume > 0 ? volumeIcon : muteIcon}
</div></div>)
: volume > 0 ? volumeIcon : muteIcon}
</div>
</div>
) : volume > 0 ? (
volumeIcon
) : (
muteIcon
)}
</div>
</div>
</div>
@ -340,6 +347,6 @@ export class Audio extends React.Component<AudioProps, AudioState> {
@Renderer({
test: /(^|\/)audio/,
name: 'audio'
name: 'audio',
})
export class AudioRenderer extends Audio {};
export class AudioRenderer extends Audio {}

View File

@ -1,16 +1,11 @@
import * as React from 'react';
import ButtonGroup from './Form/ButtonGroup';
import {
Renderer
} from '../factory';
import {Renderer} from '../factory';
export default ButtonGroup;
@Renderer({
test: /(^|\/)(?:button|action)\-group$/,
name: 'button-group'
name: 'button-group',
})
export class ButtonGroupRenderer extends ButtonGroup {
}
export class ButtonGroupRenderer extends ButtonGroup {}

View File

@ -1,36 +1,26 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import {
Action
} from '../types';
import {Renderer, RendererProps} from '../factory';
import {Action} from '../types';
export interface ButtonToolbarProps extends RendererProps {
buttons: Array<Action>;
}
export default class ButtonToolbar extends React.Component<ButtonToolbarProps, object> {
static propsList: Array<string> = [
"buttons",
];
static propsList: Array<string> = ['buttons'];
render() {
const {
buttons,
className,
classnames: cx,
render
} = this.props;
const {buttons, className, classnames: cx, render} = this.props;
return (
<div
className={cx("ButtonToolbar", className)}
>
{Array.isArray(buttons) ? buttons.map((button, key) => render(`${key}`, button, {
key
})) : null}
<div className={cx('ButtonToolbar', className)}>
{Array.isArray(buttons)
? buttons.map((button, key) =>
render(`${key}`, button, {
key,
})
)
: null}
</div>
);
}
@ -38,6 +28,6 @@ export default class ButtonToolbar extends React.Component<ButtonToolbarProps, o
@Renderer({
test: /(^|\/)button-toolbar$/,
name: 'button-toolbar'
name: 'button-toolbar',
})
export class ButtonToolbarRenderer extends ButtonToolbar {};
export class ButtonToolbarRenderer extends ButtonToolbar {}

File diff suppressed because it is too large Load Diff

View File

@ -1,31 +1,20 @@
import * as React from 'react';
import {
findDOMNode
} from 'react-dom';
import {
Renderer,
RendererProps
} from '../factory';
import {
SchemaNode,
Schema,
Action
} from '../types';
import {
filter, evalExpression
} from '../utils/tpl';
import {findDOMNode} from 'react-dom';
import {Renderer, RendererProps} from '../factory';
import {SchemaNode, Schema, Action} from '../types';
import {filter, evalExpression} from '../utils/tpl';
import * as cx from 'classnames';
import Checkbox from '../components/Checkbox';
import { IItem} from '../store/list';
import { padArr, isVisible, isDisabled, noop } from '../utils/helper';
import { resolveVariable } from '../utils/tpl-builtin';
import {IItem} from '../store/list';
import {padArr, isVisible, isDisabled, noop} from '../utils/helper';
import {resolveVariable} from '../utils/tpl-builtin';
import QuickEdit from './QuickEdit';
import PopOver from './PopOver';
import { TableCell } from './Table';
import {TableCell} from './Table';
import Copyable from './Copyable';
export interface CardProps extends RendererProps {
onCheck: (item:IItem) => void;
onCheck: (item: IItem) => void;
multiple?: boolean;
highlightClassName?: string;
hideCheckToggler?: boolean;
@ -33,8 +22,7 @@ export interface CardProps extends RendererProps {
checkOnItemClick?: boolean;
}
export class Card extends React.Component<CardProps> {
static defaultProps:Partial<CardProps> = {
static defaultProps: Partial<CardProps> = {
className: '',
avatarClassName: '',
bodyClassName: '',
@ -42,10 +30,10 @@ export class Card extends React.Component<CardProps> {
titleClassName: '',
highlightClassName: '',
subTitleClassName: '',
descClassName: ''
descClassName: '',
};
static propsList:Array<string> = [
static propsList: Array<string> = [
'multiple',
'avatarClassName',
'bodyClassName',
@ -54,10 +42,10 @@ export class Card extends React.Component<CardProps> {
'highlightClassName',
'subTitleClassName',
'descClassName',
'hideCheckToggler'
'hideCheckToggler',
];
constructor(props:CardProps) {
constructor(props: CardProps) {
super(props);
this.getPopOverContainer = this.getPopOverContainer.bind(this);
@ -68,14 +56,14 @@ export class Card extends React.Component<CardProps> {
this.handleCheck = this.handleCheck.bind(this);
}
handleClick(e:React.MouseEvent<HTMLDivElement>) {
const target:HTMLElement = e.target as HTMLElement;
handleClick(e: React.MouseEvent<HTMLDivElement>) {
const target: HTMLElement = e.target as HTMLElement;
const ns = this.props.classPrefix;
if (
!e.currentTarget.contains(target)
|| ~['INPUT', 'TEXTAREA'].indexOf(target.tagName)
|| target.closest(`button, a, .${ns}Form-item`)
!e.currentTarget.contains(target) ||
~['INPUT', 'TEXTAREA'].indexOf(target.tagName) ||
target.closest(`button, a, .${ns}Form-item`)
) {
return;
}
@ -89,17 +77,16 @@ export class Card extends React.Component<CardProps> {
this.props.onCheck && this.props.onCheck(item);
}
handleAction(e:React.UIEvent<any>, action: Action, ctx: object) {
handleAction(e: React.UIEvent<any>, action: Action, ctx: object) {
const {onAction, item} = this.props;
onAction && onAction(e, action, ctx || item.data);
}
handleQuickChange(values:object, saveImmediately?: boolean, saveSlient?: boolean) {
handleQuickChange(values: object, saveImmediately?: boolean, saveSlient?: boolean) {
const {onQuickChange, item} = this.props;
onQuickChange && onQuickChange(item, values, saveImmediately, saveSlient);
}
getPopOverContainer() {
return findDOMNode(this);
}
@ -115,24 +102,24 @@ export class Card extends React.Component<CardProps> {
multiple,
hideCheckToggler,
classnames: cx,
classPrefix: ns
classPrefix: ns,
} = this.props;
if (dragging) {
return (
<div className={cx("Card-dragBtn")}>
<div className={cx('Card-dragBtn')}>
<i className="fa fa-exchange" />
</div>
);
} else if (selectable && !hideCheckToggler) {
return (
<div className={cx("Card-checkBtn")}>
<Checkbox
classPrefix={ns}
type={multiple ? 'checkbox' : 'radio'}
disabled={!checkable}
checked={selected}
onChange={checkOnItemClick ? noop : this.handleCheck}
<div className={cx('Card-checkBtn')}>
<Checkbox
classPrefix={ns}
type={multiple ? 'checkbox' : 'radio'}
disabled={!checkable}
checked={selected}
onChange={checkOnItemClick ? noop : this.handleCheck}
/>
</div>
);
@ -142,36 +129,36 @@ export class Card extends React.Component<CardProps> {
}
renderActions() {
const {
actions,
render,
dragging,
actionsCount,
data,
classnames: cx
} = this.props;
const {actions, render, dragging, actionsCount, data, classnames: cx} = this.props;
if (Array.isArray(actions)) {
const group = padArr(actions.filter(item => isVisible(item, data)), actionsCount)
const group = padArr(actions.filter(item => isVisible(item, data)), actionsCount);
return group.map((actions, groupIndex) => (
<div key={groupIndex} className={cx("Card-actions")}>
<div key={groupIndex} className={cx('Card-actions')}>
{actions.map((action, index) => {
const size = action.size || 'sm';
return render(`action/${index}`, {
level: 'link',
type: 'button',
...action,
size
}, {
isMenuItem: true,
key: index,
index,
disabled: dragging || isDisabled(action, data),
className: cx('Card-action', action.className || `${size ? `Card-action--${size}` : ''}`),
componentClass: 'a',
onAction: this.handleAction
})
return render(
`action/${index}`,
{
level: 'link',
type: 'button',
...action,
size,
},
{
isMenuItem: true,
key: index,
index,
disabled: dragging || isDisabled(action, data),
className: cx(
'Card-action',
action.className || `${size ? `Card-action--${size}` : ''}`
),
componentClass: 'a',
onAction: this.handleAction,
}
);
})}
</div>
));
@ -180,36 +167,31 @@ export class Card extends React.Component<CardProps> {
return null;
}
renderChild(node:SchemaNode, region: string = 'body', key:any = 0): JSX.Element {
const {
render
} = this.props;
renderChild(node: SchemaNode, region: string = 'body', key: any = 0): JSX.Element {
const {render} = this.props;
if (typeof node === 'string' || typeof node === 'number') {
return render(region, node, {key}) as JSX.Element;
}
const childNode:Schema = node as Schema;
const childNode: Schema = node as Schema;
if (childNode.type === 'hbox' || childNode.type === 'grid') {
return render(region, node, {
key,
itemRender: this.itemRender
itemRender: this.itemRender,
}) as JSX.Element;
}
return this.renderFeild(region,childNode, key, this.props);
return this.renderFeild(region, childNode, key, this.props);
}
itemRender(field:any, index:number, props:any) {
itemRender(field: any, index: number, props: any) {
return this.renderFeild(`column/${index}`, field, index, props);
}
renderFeild(region:string, field:any, key:any, props:any) {
const {
render,
classnames: cx
} = props;
renderFeild(region: string, field: any, key: any, props: any) {
const {render, classnames: cx} = props;
const data = this.props.data;
const $$id = field.$$id ? `${field.$$id}-field` : '';
@ -220,33 +202,37 @@ export class Card extends React.Component<CardProps> {
<label className={cx('Card-fieldLabel', field.labelClassName)}>{field.label}</label>
) : null}
{render(region, {
...field,
field: field,
$$id,
type: 'card-item-field',
}, {
className: cx("Card-fieldValue", field.className),
value: field.name ? resolveVariable(field.name, data) : undefined,
popOverContainer: this.getPopOverContainer,
onAction: this.handleAction,
onQuickChange: this.handleQuickChange
}) as JSX.Element}
{
render(
region,
{
...field,
field: field,
$$id,
type: 'card-item-field',
},
{
className: cx('Card-fieldValue', field.className),
value: field.name ? resolveVariable(field.name, data) : undefined,
popOverContainer: this.getPopOverContainer,
onAction: this.handleAction,
onQuickChange: this.handleQuickChange,
}
) as JSX.Element
}
</div>
);
}
renderBody() {
const {
body
} = this.props;
const {body} = this.props;
if (!body) {
return null;
}
if (Array.isArray(body)) {
return body.map((child, index) => this.renderChild(child, `body/${index}`, index))
return body.map((child, index) => this.renderChild(child, `body/${index}`, index));
}
return this.renderChild(body, 'body');
@ -266,7 +252,7 @@ export class Card extends React.Component<CardProps> {
checkOnItemClick,
checkable,
classnames: cx,
classPrefix: ns
classPrefix: ns,
} = this.props;
let heading = null;
@ -281,7 +267,7 @@ export class Card extends React.Component<CardProps> {
subTitle: subTitleTpl,
subTitlePlaceholder,
desc: descTpl,
descPlaceholder
descPlaceholder,
} = header;
const highlight = !!evalExpression(highlightTpl, data as object);
@ -297,7 +283,7 @@ export class Card extends React.Component<CardProps> {
<img className={cx('Card-img', header.imageClassName || imageClassName)} src={avatar} />
</span>
) : null}
<div className={cx("Card-meta")}>
<div className={cx('Card-meta')}>
{highlight ? (
<i className={cx('Card-highlight', header.highlightClassName || highlightClassName)} />
) : null}
@ -309,15 +295,19 @@ export class Card extends React.Component<CardProps> {
) : null}
{subTitle || subTitlePlaceholder ? (
<div className={cx('Card-subTitle', header.subTitleClassName || subTitleClassName)}>{render('sub-title', subTitle || subTitlePlaceholder, {
className: cx(!subTitle ? 'Card-placeholder' : undefined)
})}</div>
<div className={cx('Card-subTitle', header.subTitleClassName || subTitleClassName)}>
{render('sub-title', subTitle || subTitlePlaceholder, {
className: cx(!subTitle ? 'Card-placeholder' : undefined),
})}
</div>
) : null}
{desc || descPlaceholder ? (
<div className={cx('Card-desc', header.descClassName || descClassName)}>{render('desc', desc || descPlaceholder, {
className: !desc ? 'text-muted' : undefined
})}</div>
<div className={cx('Card-desc', header.descClassName || descClassName)}>
{render('desc', desc || descPlaceholder, {
className: !desc ? 'text-muted' : undefined,
})}
</div>
) : null}
</div>
</div>
@ -326,14 +316,14 @@ export class Card extends React.Component<CardProps> {
const body = this.renderBody();
return (
<div onClick={checkOnItemClick && checkable ? this.handleClick : undefined} className={cx('Card', className)}>
<div
onClick={checkOnItemClick && checkable ? this.handleClick : undefined}
className={cx('Card', className)}
>
{this.renderToolbar()}
{heading}
{body ? (
<div className={cx('Card-body', bodyClassName)}>{body}</div>
) : null}
{body ? <div className={cx('Card-body', bodyClassName)}>{body}</div> : null}
{this.renderActions()}
</div>
);
@ -342,15 +332,13 @@ export class Card extends React.Component<CardProps> {
@Renderer({
test: /(^|\/)card$/,
name: 'card'
name: 'card',
})
export class CardRenderer extends Card {
};
export class CardRenderer extends Card {}
@Renderer({
test: /(^|\/)card-item-field$/,
name: 'card-item'
name: 'card-item',
})
@QuickEdit()
@PopOver()
@ -358,15 +346,10 @@ export class CardRenderer extends Card {
export class CardItemFieldRenderer extends TableCell {
static defaultProps = {
...TableCell.defaultProps,
wrapperComponent: 'div'
wrapperComponent: 'div',
};
static propsList = [
'quickEdit',
'popOver',
'copyable',
...TableCell.propsList
];
static propsList = ['quickEdit', 'popOver', 'copyable', ...TableCell.propsList];
render() {
let {
@ -390,23 +373,21 @@ export class CardItemFieldRenderer extends TableCell {
const schema = {
...field,
className: innerClassName,
type: field && field.type || 'plain',
type: (field && field.type) || 'plain',
};
let body = children ? children : render('field', schema, {
...rest,
value,
data
});
let body = children
? children
: render('field', schema, {
...rest,
value,
data,
});
if (width) {
style = style || {};
style.width = style.width || width;
body = (
<div style={{width: !/%/.test(String(width)) ? width : ''}}>
{body}
</div>
);
body = <div style={{width: !/%/.test(String(width)) ? width : ''}}>{body}</div>;
}
if (!Component) {
@ -414,14 +395,9 @@ export class CardItemFieldRenderer extends TableCell {
}
return (
<Component
style={style}
className={className}
tabIndex={tabIndex}
onKeyUp={onKeyUp}
>
<Component style={style} className={className} tabIndex={tabIndex} onKeyUp={onKeyUp}>
{body}
</Component>
)
);
}
};
}

View File

@ -1,30 +1,22 @@
import * as React from 'react';
import {
findDOMNode
} from 'react-dom';
import {
Renderer,
RendererProps
} from '../factory';
import {
SchemaNode,
Action
} from '../types';
import {findDOMNode} from 'react-dom';
import {Renderer, RendererProps} from '../factory';
import {SchemaNode, Action} from '../types';
import * as cx from 'classnames';
import Button from '../components/Button';
import { ListStore, IListStore, IItem} from '../store/list';
import { observer } from 'mobx-react';
import { anyChanged, getScrollParent, difference, ucFirst } from '../utils/helper';
import { resolveVariable } from '../utils/tpl-builtin';
import {ListStore, IListStore, IItem} from '../store/list';
import {observer} from 'mobx-react';
import {anyChanged, getScrollParent, difference, ucFirst} from '../utils/helper';
import {resolveVariable} from '../utils/tpl-builtin';
import Sortable = require('sortablejs');
import { filter } from '../utils/tpl';
import {filter} from '../utils/tpl';
import debounce = require('lodash/debounce');
import { resizeSensor } from '../utils/resize-sensor';
import {resizeSensor} from '../utils/resize-sensor';
export interface Column {
type: string;
[propName:string]: any;
};
[propName: string]: any;
}
export interface GridProps extends RendererProps {
title?: string; // 标题
@ -37,16 +29,21 @@ export interface GridProps extends RendererProps {
footerClassName?: string;
itemClassName?: string;
card?: any;
source?:string;
source?: string;
selectable?: boolean;
selected?: Array<any>;
multiple?: boolean;
valueField?: string;
draggable?:boolean;
onSelect: (selectedItems:Array<object>, unSelectedItems:Array<object>) => void;
onSave?: (items:Array<object> | object, diff: Array<object> | object, rowIndexes: Array<number> | number, unModifiedItems?:Array<object>) => void;
onSaveOrder?: (moved: Array<object>, items:Array<object>) => void;
onQuery: (values:object) => void;
draggable?: boolean;
onSelect: (selectedItems: Array<object>, unSelectedItems: Array<object>) => void;
onSave?: (
items: Array<object> | object,
diff: Array<object> | object,
rowIndexes: Array<number> | number,
unModifiedItems?: Array<object>
) => void;
onSaveOrder?: (moved: Array<object>, items: Array<object>) => void;
onQuery: (values: object) => void;
hideCheckToggler?: boolean;
itemCheckableOn?: string;
itemDraggableOn?: string;
@ -71,10 +68,10 @@ export default class Cards extends React.Component<GridProps, object> {
'itemCheckableOn',
'itemDraggableOn',
'masonryLayout',
"items",
"valueField"
'items',
'valueField',
];
static defaultProps:Partial<GridProps>= {
static defaultProps: Partial<GridProps> = {
className: '',
placeholder: '没有数据',
source: '$items',
@ -86,7 +83,7 @@ export default class Cards extends React.Component<GridProps, object> {
hideCheckToggler: false,
masonryLayout: false,
affixHeader: true,
itemsClassName: ''
itemsClassName: '',
};
dragTip?: HTMLElement;
@ -95,7 +92,7 @@ export default class Cards extends React.Component<GridProps, object> {
body?: any;
// fixAlignmentLazy: Function;
unSensor: Function;
constructor(props:GridProps) {
constructor(props: GridProps) {
super(props);
this.handleAction = this.handleAction.bind(this);
@ -115,10 +112,10 @@ export default class Cards extends React.Component<GridProps, object> {
// })
}
static syncItems(store:IListStore, props:GridProps, prevProps?:GridProps) {
static syncItems(store: IListStore, props: GridProps, prevProps?: GridProps) {
const source = props.source;
const value = props.value || props.items;
let items:Array<object> = [];
let items: Array<object> = [];
let updateItems = true;
if (Array.isArray(value)) {
@ -148,7 +145,7 @@ export default class Cards extends React.Component<GridProps, object> {
multiple,
hideCheckToggler,
itemCheckableOn,
itemDraggableOn
itemDraggableOn,
} = this.props;
store.update({
@ -159,7 +156,7 @@ export default class Cards extends React.Component<GridProps, object> {
multiple,
hideCheckToggler,
itemCheckableOn,
itemDraggableOn
itemDraggableOn,
});
Cards.syncItems(store, this.props);
@ -167,7 +164,7 @@ export default class Cards extends React.Component<GridProps, object> {
}
componentDidMount() {
let parent:HTMLElement | Window | null = getScrollParent(findDOMNode(this) as HTMLElement);
let parent: HTMLElement | Window | null = getScrollParent(findDOMNode(this) as HTMLElement);
if (!parent || parent === document.body) {
parent = window;
}
@ -178,20 +175,26 @@ export default class Cards extends React.Component<GridProps, object> {
window.addEventListener('resize', this.affixDetect);
}
componentWillReceiveProps(nextProps:GridProps) {
componentWillReceiveProps(nextProps: GridProps) {
const props = this.props;
const store = nextProps.store;
if (anyChanged([
'selectable',
'draggable',
'orderBy',
'orderDir',
'multiple',
'hideCheckToggler',
'itemCheckableOn',
'itemDraggableOn'
], props, nextProps)) {
if (
anyChanged(
[
'selectable',
'draggable',
'orderBy',
'orderDir',
'multiple',
'hideCheckToggler',
'itemCheckableOn',
'itemDraggableOn',
],
props,
nextProps
)
) {
store.update({
selectable: nextProps.selectable,
draggable: nextProps.draggable,
@ -200,15 +203,14 @@ export default class Cards extends React.Component<GridProps, object> {
multiple: nextProps.multiple,
hideCheckToggler: nextProps.hideCheckToggler,
itemCheckableOn: nextProps.itemCheckableOn,
itemDraggableOn: nextProps.itemDraggableOn
})
itemDraggableOn: nextProps.itemDraggableOn,
});
}
if (anyChanged([
'source',
'value',
'items'
], props, nextProps) || !nextProps.value && !nextProps.items && nextProps.data !== props.data) {
if (
anyChanged(['source', 'value', 'items'], props, nextProps) ||
(!nextProps.value && !nextProps.items && nextProps.data !== props.data)
) {
Cards.syncItems(store, nextProps, props);
this.syncSelected();
} else if (props.selected !== nextProps.selected) {
@ -239,11 +241,11 @@ export default class Cards extends React.Component<GridProps, object> {
// cards.forEach((item: HTMLElement) => item.style.cssText += `min-height: ${maxHeight}px;`);
// }
bodyRef(ref:HTMLDivElement) {
bodyRef(ref: HTMLDivElement) {
this.body = ref;
}
itemsRef(ref:HTMLDivElement) {
itemsRef(ref: HTMLDivElement) {
if (ref) {
// this.unSensor = resizeSensor(ref.parentNode as HTMLElement, this.fixAlignmentLazy);
} else {
@ -261,7 +263,7 @@ export default class Cards extends React.Component<GridProps, object> {
const dom = findDOMNode(this) as HTMLElement;
const clip = (this.body as HTMLElement).getBoundingClientRect();
const offsetY = this.props.env.affixOffsetTop || 0;
const affixed = clip.top < offsetY && (clip.top + clip.height - 40) > offsetY;
const affixed = clip.top < offsetY && clip.top + clip.height - 40 > offsetY;
const afixedDom = dom.querySelector(`.${ns}Cards-fixedTop`) as HTMLElement;
this.body.offsetWidth && (afixedDom.style.cssText = `top: ${offsetY}px;width: ${this.body.offsetWidth}px;`);
@ -269,10 +271,8 @@ export default class Cards extends React.Component<GridProps, object> {
// store.markHeaderAffix(clip.top < offsetY && (clip.top + clip.height - 40) > offsetY);
}
handleAction(e:React.UIEvent<any>, action: Action, ctx: object) {
const {
onAction
} = this.props;
handleAction(e: React.UIEvent<any>, action: Action, ctx: object) {
const {onAction} = this.props;
// 需要支持特殊事件吗?
onAction(e, action, ctx);
@ -284,24 +284,19 @@ export default class Cards extends React.Component<GridProps, object> {
}
handleCheckAll() {
const {
store,
} = this.props;
const {store} = this.props;
store.toggleAll();
this.syncSelected();
}
syncSelected() {
const {
store,
onSelect
} = this.props;
const {store, onSelect} = this.props;
onSelect && onSelect(store.selectedItems.map(item => item.data), store.unSelectedItems.map(item => item.data));
}
handleQuickChange(item: IItem, values:object, saveImmediately?: boolean | any, saveSilent?: boolean) {
handleQuickChange(item: IItem, values: object, saveImmediately?: boolean | any, saveSilent?: boolean) {
item.change(values, saveSilent);
if (!saveImmediately || saveSilent) {
@ -309,16 +304,18 @@ export default class Cards extends React.Component<GridProps, object> {
}
if (saveImmediately && saveImmediately.api) {
this.props.onAction(null, {
actionType: 'ajax',
api: saveImmediately.api
}, values);
this.props.onAction(
null,
{
actionType: 'ajax',
api: saveImmediately.api,
},
values
);
return;
}
const {
onSave
} = this.props;
const {onSave} = this.props;
if (!onSave) {
return;
@ -328,10 +325,7 @@ export default class Cards extends React.Component<GridProps, object> {
}
handleSave() {
const {
store,
onSave
} = this.props;
const {store, onSave} = this.props;
if (!onSave || !store.modifiedItems.length) {
return;
@ -345,10 +339,7 @@ export default class Cards extends React.Component<GridProps, object> {
}
handleSaveOrder() {
const {
store,
onSaveOrder
} = this.props;
const {store, onSaveOrder} = this.props;
if (!onSaveOrder || !store.movedItems.length) {
return;
@ -358,41 +349,34 @@ export default class Cards extends React.Component<GridProps, object> {
}
reset() {
const {
store
} = this.props;
const {store} = this.props;
store.reset();
}
bulkUpdate(value:object, items:Array<object>) {
const {
store
} = this.props;
bulkUpdate(value: object, items: Array<object>) {
const {store} = this.props;
const items2 = store.items.filter(item => ~items.indexOf(item.pristine));
items2.forEach(item => item.change(value));
}
getSelected() {
const {
store
} = this.props;
const {store} = this.props;
return store.selectedItems.map(item => item.data);
}
dragTipRef(ref:any) {
dragTipRef(ref: any) {
if (!this.dragTip && ref) {
this.initDragging();
} else if (this.dragTip && !ref) {
this.destroyDragging()
this.destroyDragging();
}
this.dragTip = ref;
}
initDragging() {
const store = this.props.store;
const dom = findDOMNode(this) as HTMLElement;
@ -401,21 +385,21 @@ export default class Cards extends React.Component<GridProps, object> {
group: 'table',
handle: `.${ns}Card-dragBtn`,
ghostClass: `is-dragging`,
onEnd: (e:any) => {
onEnd: (e: any) => {
// 没有移动
if (e.newIndex === e.oldIndex) {
return;
}
const parent = e.to as HTMLElement;
if (e.oldIndex < parent.childNodes.length -1) {
if (e.oldIndex < parent.childNodes.length - 1) {
parent.insertBefore(e.item, parent.childNodes[e.oldIndex]);
} else {
parent.appendChild(e.item);
}
store.exchange(e.oldIndex, e.newIndex);
}
},
});
}
@ -423,19 +407,8 @@ export default class Cards extends React.Component<GridProps, object> {
this.sortable && this.sortable.destroy();
}
renderActions(region:string) {
let {
actions,
render,
store,
multiple,
selectable,
classnames: cx,
classPrefix: ns,
env
} = this.props;
renderActions(region: string) {
let {actions, render, store, multiple, selectable, classnames: cx, classPrefix: ns, env} = this.props;
actions = Array.isArray(actions) ? actions.concat() : [];
@ -443,15 +416,17 @@ export default class Cards extends React.Component<GridProps, object> {
actions.unshift({
type: 'button',
children: (
<Button
<Button
key="checkall"
classPrefix={ns}
tooltip="切换全选"
onClick={this.handleCheckAll}
size="sm"
tooltip="切换全选"
onClick={this.handleCheckAll}
size="sm"
level={store.allChecked ? 'info' : 'default'}
></Button>
)
>
</Button>
),
});
}
@ -467,7 +442,7 @@ export default class Cards extends React.Component<GridProps, object> {
tooltipContainer={env && env.getModalContainer ? env.getModalContainer() : undefined}
size="sm"
active={store.dragging}
onClick={(e:React.MouseEvent<any>) => {
onClick={(e: React.MouseEvent<any>) => {
e.preventDefault();
store.toggleDragging();
store.dragging && store.clear();
@ -475,61 +450,81 @@ export default class Cards extends React.Component<GridProps, object> {
>
<i className="fa fa-exchange" />
</Button>
)
),
});
}
return Array.isArray(actions) && actions.length ? (
<div className={cx("Cards-actions")}>
{actions.map((action, key) => render(`action/${key}`, {
type: 'button',
...action
}, {
onAction: this.handleAction,
key,
btnDisabled: store.dragging
}))}
<div className={cx('Cards-actions')}>
{actions.map((action, key) =>
render(
`action/${key}`,
{
type: 'button',
...action,
},
{
onAction: this.handleAction,
key,
btnDisabled: store.dragging,
}
)
)}
</div>
) : null;
}
renderHeading() {
let {
title,
store,
hideQuickSaveBtn,
classnames: cx,
data
} = this.props;
let {title, store, hideQuickSaveBtn, classnames: cx, data} = this.props;
if (title || store.modified && !hideQuickSaveBtn || store.moved) {
if (title || (store.modified && !hideQuickSaveBtn) || store.moved) {
return (
<div className={cx("Cards-heading")}>
<div className={cx('Cards-heading')}>
{store.modified && !hideQuickSaveBtn ? (
<span>
{`当前有 ${store.modified} 条记录修改了内容, 但并没有提交。请选择:`}
<button type="button" className={cx("Button Button--xs Button--success m-l-sm")} onClick={this.handleSave}>
<button
type="button"
className={cx('Button Button--xs Button--success m-l-sm')}
onClick={this.handleSave}
>
<i className="fa fa-check m-r-xs" />
</button>
<button type="button" className={cx("Button Button--xs Button--danger m-l-sm")} onClick={this.reset}>
<button
type="button"
className={cx('Button Button--xs Button--danger m-l-sm')}
onClick={this.reset}
>
<i className="fa fa-times m-r-xs" />
</button>
</span>
) : store.moved ? (
) : store.moved ? (
<span>
{`当前有 ${store.moved} 条记录修改了顺序, 但并没有提交。请选择:`}
<button type="button" className={cx("Button Button--xs Button--success m-l-sm")} onClick={this.handleSaveOrder}>
<button
type="button"
className={cx('Button Button--xs Button--success m-l-sm')}
onClick={this.handleSaveOrder}
>
<i className="fa fa-check m-r-xs" />
</button>
<button type="button" className={cx("Button Button--xs Button--danger m-l-sm")} onClick={this.reset}>
<button
type="button"
className={cx('Button Button--xs Button--danger m-l-sm')}
onClick={this.reset}
>
<i className="fa fa-times m-r-xs" />
</button>
</span>
) : title ? filter(title, data) : ''}
) : title ? (
filter(title, data)
) : (
''
)}
</div>
);
}
@ -546,33 +541,40 @@ export default class Cards extends React.Component<GridProps, object> {
showHeader,
render,
store,
classnames: cx
} = this.props;
classnames: cx,
} = this.props;
if (showHeader === false) {
return null;
}
const actions = this.renderActions('header');
const child = headerToolbarRender ? headerToolbarRender({
...this.props,
selectedItems: store.selectedItems.map(item => item.data),
items: store.items.map(item => item.data),
unSelectedItems: store.unSelectedItems.map(item => item.data),
}) : null;
const toolbarNode = actions || child || store.dragging ? (
<div className={cx('Cards-toolbar')} key="header-toolbar">
{actions}
{child}
{store.dragging ? <div className={cx("Cards-dragTip")} ref={this.dragTipRef}></div> : null}
</div>
) : null;
const child = headerToolbarRender
? headerToolbarRender({
...this.props,
selectedItems: store.selectedItems.map(item => item.data),
items: store.items.map(item => item.data),
unSelectedItems: store.unSelectedItems.map(item => item.data),
})
: null;
const toolbarNode =
actions || child || store.dragging ? (
<div className={cx('Cards-toolbar')} key="header-toolbar">
{actions}
{child}
{store.dragging ? (
<div className={cx('Cards-dragTip')} ref={this.dragTipRef}>
</div>
) : null}
</div>
) : null;
const headerNode = header ? (
<div className={cx('Cards-header', headerClassName)} key="header">
{render('header', header)}
</div>
) : null;
return headerNode && toolbarNode ? [headerNode, toolbarNode] : (headerNode || toolbarNode || null);
return headerNode && toolbarNode ? [headerNode, toolbarNode] : headerNode || toolbarNode || null;
}
renderFooter() {
@ -584,33 +586,36 @@ export default class Cards extends React.Component<GridProps, object> {
render,
showFooter,
store,
classnames: cx
} = this.props;
classnames: cx,
} = this.props;
if (showFooter === false) {
return null;
}
const actions = this.renderActions('footer');
const child = footerToolbarRender ? footerToolbarRender({
...this.props,
selectedItems: store.selectedItems.map(item => item.data),
items: store.items.map(item => item.data),
unSelectedItems: store.unSelectedItems.map(item => item.data),
}) : null;
const child = footerToolbarRender
? footerToolbarRender({
...this.props,
selectedItems: store.selectedItems.map(item => item.data),
items: store.items.map(item => item.data),
unSelectedItems: store.unSelectedItems.map(item => item.data),
})
: null;
const toolbarNode = actions || child ? (
<div className={cx('Cards-toolbar')} key="footer-toolbar">
{actions}
{child}
</div>
) : null;
const toolbarNode =
actions || child ? (
<div className={cx('Cards-toolbar')} key="footer-toolbar">
{actions}
{child}
</div>
) : null;
const footerNode = footer ? (
<div className={cx('Cards-footer', footerClassName)} key="footer">
{render('footer', footer)}
</div>
) : null;
return footerNode && toolbarNode ? [toolbarNode, footerNode] : (footerNode || toolbarNode || null);
return footerNode && toolbarNode ? [toolbarNode, footerNode] : footerNode || toolbarNode || null;
}
render() {
@ -629,36 +634,41 @@ export default class Cards extends React.Component<GridProps, object> {
checkOnItemClick,
masonryLayout,
itemsClassName,
classnames: cx
classnames: cx,
} = this.props;
let itemFinalClassName:string = columnsCount ? `Grid-col--sm${Math.round(12/columnsCount)}` : itemClassName || '';
let itemFinalClassName: string = columnsCount
? `Grid-col--sm${Math.round(12 / columnsCount)}`
: itemClassName || '';
const header = this.renderHeader();
const heading = this.renderHeading();
const footer = this.renderFooter();
let masonryClassName = '';
let masonryClassName = '';
if (masonryLayout) {
masonryClassName = 'Cards--masonry ' + itemFinalClassName.split(/\s/).map(item => {
if (/^Grid-col--(xs|sm|md|lg)(\d+)/.test(item)) {
return `Cards--masonry${ucFirst(RegExp.$1)}${RegExp.$2}`;
}
masonryClassName =
'Cards--masonry ' +
itemFinalClassName
.split(/\s/)
.map(item => {
if (/^Grid-col--(xs|sm|md|lg)(\d+)/.test(item)) {
return `Cards--masonry${ucFirst(RegExp.$1)}${RegExp.$2}`;
}
return item;
}).join(' ');
return item;
})
.join(' ');
}
return (
<div
ref={this.bodyRef}
className={cx('Cards', className, {
'Cards--unsaved': !!store.modified || !!store.moved
'Cards--unsaved': !!store.modified || !!store.moved,
})}
>
{affixHeader ? (
<div
className={cx("Cards-fixedTop")}
>
<div className={cx('Cards-fixedTop')}>
{heading}
{header}
</div>
@ -669,13 +679,15 @@ export default class Cards extends React.Component<GridProps, object> {
<div ref={this.itemsRef} className={cx('Cards-body Grid', itemsClassName, masonryClassName)}>
{store.items.map((item, index) => {
return (
(
<div key={item.index} className={cx(itemFinalClassName)}>
{render(`${index}`, {
<div key={item.index} className={cx(itemFinalClassName)}>
{render(
`${index}`,
{
type: 'card',
...card
}, {
className: cx(card && card.className || '', {
...card,
},
{
className: cx((card && card.className) || '', {
'is-checked': item.checked,
'is-modified': item.modified,
'is-moved': item.moved,
@ -693,17 +705,15 @@ export default class Cards extends React.Component<GridProps, object> {
checkOnItemClick,
onAction,
onCheck: this.handleCheck,
onQuickChange: store.dragging ? null : this.handleQuickChange
})}
</div>
)
onQuickChange: store.dragging ? null : this.handleQuickChange,
}
)}
</div>
);
})}
</div>
) : (
<div className={cx("Cards-placeholder")}>
{placeholder}
</div>
<div className={cx('Cards-placeholder')}>{placeholder}</div>
)}
{footer}
@ -716,7 +726,7 @@ export default class Cards extends React.Component<GridProps, object> {
test: /(^|\/)(?:crud\/body\/grid|cards)$/,
name: 'cards',
storeType: ListStore.name,
weight: -100 // 默认的 grid 不是这样,这个只识别 crud 下面的 grid
weight: -100, // 默认的 grid 不是这样,这个只识别 crud 下面的 grid
})
export class CardsRenderer extends Cards {
dragging: boolean;
@ -730,5 +740,4 @@ export class CardsRenderer extends Cards {
avatarClassName?: string;
body?: SchemaNode;
actions?: Array<Action>;
};
}

View File

@ -1,28 +1,19 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import {
Renderer,
RendererProps
} from '../factory';
import {Renderer, RendererProps} from '../factory';
import {ServiceStore, IServiceStore} from '../store/service';
import {
Api,
ApiObject,
Action
} from '../types';
import {
filter, evalExpression} from '../utils/tpl';
import {Api, ApiObject, Action} from '../types';
import {filter, evalExpression} from '../utils/tpl';
import * as cx from 'classnames';
import LazyComponent from '../components/LazyComponent';
import {resizeSensor} from '../utils/resize-sensor';
import { resolveVariableAndFilter } from '../utils/tpl-builtin';
import { isApiOutdated } from '../utils/api';
import { ScopedContext, IScopedContext } from '../Scoped';
import {resolveVariableAndFilter} from '../utils/tpl-builtin';
import {isApiOutdated} from '../utils/api';
import {ScopedContext, IScopedContext} from '../Scoped';
export interface ChartProps extends RendererProps {
chartRef?: (echart:any) => void;
onDataFilter?: (config:any) => any;
chartRef?: (echart: any) => void;
onDataFilter?: (config: any) => any;
api?: Api;
source?: string;
config?: object;
@ -32,24 +23,22 @@ export interface ChartProps extends RendererProps {
replaceChartOption: boolean;
}
export class Chart extends React.Component<ChartProps> {
static defaultProps:Partial<ChartProps> = {
static defaultProps: Partial<ChartProps> = {
offsetY: 50,
replaceChartOption: false
replaceChartOption: false,
};
static propsList:Array<string> = [
];
static propsList: Array<string> = [];
ref:any;
echarts:any;
unSensor:Function;
pending?:object;
ref: any;
echarts: any;
unSensor: Function;
pending?: object;
timer: number;
mounted: boolean;
reloadCancel: Function;
constructor(props:ChartProps) {
constructor(props: ChartProps) {
super(props);
this.refFn = this.refFn.bind(this);
@ -58,13 +47,7 @@ export class Chart extends React.Component<ChartProps> {
}
componentWillMount() {
const {
config,
api,
data,
initFetch,
source
} = this.props;
const {config, api, data, initFetch, source} = this.props;
this.mounted = true;
@ -74,18 +57,20 @@ export class Chart extends React.Component<ChartProps> {
} else if (api && initFetch !== false) {
this.reload();
}
config && this.renderChart(config);
}
componentDidUpdate(prevProps:ChartProps) {
componentDidUpdate(prevProps: ChartProps) {
const props = this.props;
const api:string = props.api && (props.api as ApiObject).url || (props.api as string);
const api: string = (props.api && (props.api as ApiObject).url) || (props.api as string);
if (isApiOutdated(prevProps.api, props.api, prevProps.data, props.data)) {
this.reload();
} else if (props.source && /^\$(?:([a-z0-9_.]+)|{.+})$/.test(props.source)) {
const prevRet = prevProps.source ? resolveVariableAndFilter(prevProps.source, prevProps.data, '| raw') : null;
const prevRet = prevProps.source
? resolveVariableAndFilter(prevProps.source, prevProps.data, '| raw')
: null;
const ret = resolveVariableAndFilter(props.source, props.data, '| raw');
if (prevRet !== ret) {
@ -101,28 +86,25 @@ export class Chart extends React.Component<ChartProps> {
clearTimeout(this.timer);
}
handleClick(ctx:object) {
const {
onAction,
clickAction,
} = this.props;
handleClick(ctx: object) {
const {onAction, clickAction} = this.props;
clickAction && onAction && onAction(null, clickAction, ctx);
}
refFn(ref:any) {
refFn(ref: any) {
const chartRef = this.props.chartRef;
if (ref) {
(require as any)(['echarts', 'echarts/map/js/china', 'echarts/map/js/world'], (echarts:any) => {
(require as any)(['echarts', 'echarts/map/js/china', 'echarts/map/js/world'], (echarts: any) => {
(window as any).echarts = echarts;
this.echarts = echarts.init(ref);
this.echarts.on('click', this.handleClick)
this.echarts.on('click', this.handleClick);
this.unSensor = resizeSensor(ref, () => {
const width = ref.offsetWidth;
const height = ref.offsetHeight;
this.echarts.resize({
width,
height
height,
});
});
@ -137,13 +119,8 @@ export class Chart extends React.Component<ChartProps> {
this.ref = ref;
}
reload(query?:any) {
const {
api,
env,
store,
interval
} = this.props;
reload(query?: any) {
const {api, env, store, interval} = this.props;
if (query) {
return this.receive(query);
@ -162,12 +139,10 @@ export class Chart extends React.Component<ChartProps> {
this.echarts && this.echarts.hideLoading();
}
this.echarts && this.echarts.showLoading();
env
.fetcher(api, store.data, {
cancelExecutor: (executor:Function) => this.reloadCancel = executor
})
env.fetcher(api, store.data, {
cancelExecutor: (executor: Function) => (this.reloadCancel = executor),
})
.then(result => {
delete this.reloadCancel;
this.renderChart(result.data || {});
@ -185,7 +160,7 @@ export class Chart extends React.Component<ChartProps> {
});
}
receive(data:object) {
receive(data: object) {
const store = this.props.store;
store.updateData(data);
@ -201,7 +176,7 @@ export class Chart extends React.Component<ChartProps> {
config = config || this.pending;
if (typeof config === 'string') {
config = (new Function("return " + config))();
config = new Function('return ' + config)();
}
onDataFilter && (config = onDataFilter(config) || config);
@ -215,41 +190,24 @@ export class Chart extends React.Component<ChartProps> {
}
render() {
const {
className,
width,
height,
classPrefix: ns
} = this.props;
const {className, width, height, classPrefix: ns} = this.props;
let style = this.props.style || {};
width && (style.width = width);
height && (style.height = height);
return (
<LazyComponent
unMountOnHidden
placeholder={(
<div
className={cx(`${ns}Chart`, className)}
style={style}
>
placeholder={
<div className={cx(`${ns}Chart`, className)} style={style}>
<div className={`${ns}Chart-placeholder`}>
<i key="loading" className="fa fa-spinner fa-spin fa-2x fa-fw" />
</div>
</div>
)}
component={() => (
<div
className={cx(`${ns}Chart`, className)}
style={style}
ref={this.refFn}
/>
)}
}
component={() => <div className={cx(`${ns}Chart`, className)} style={style} ref={this.refFn} />}
/>
);
}
}
@ -273,4 +231,4 @@ export class ChartRenderer extends Chart {
const scoped = this.context as IScopedContext;
scoped.unRegisterComponent(this);
}
};
}

View File

@ -1,12 +1,7 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import {Renderer, RendererProps} from '../factory';
import * as cx from 'classnames';
import {
Collapse as BasicCollapse
} from 'react-bootstrap';
import {Collapse as BasicCollapse} from 'react-bootstrap';
export interface CollapseProps extends RendererProps {
title?: string; // 标题
@ -17,7 +12,7 @@ export interface CollapseProps extends RendererProps {
bodyClassName?: string;
headingClassName?: string;
// 内容口子
children?: JSX.Element | ((props?:any) => JSX.Element);
children?: JSX.Element | ((props?: any) => JSX.Element);
}
export interface CollapseState {
@ -26,24 +21,24 @@ export interface CollapseState {
export default class Collapse extends React.Component<CollapseProps, CollapseState> {
static propsList: Array<string> = [
"wrapperComponent",
"headingComponent",
"bodyClassName",
"collapsed",
"headingClassName"
'wrapperComponent',
'headingComponent',
'bodyClassName',
'collapsed',
'headingClassName',
];
static defaultProps:Partial<CollapseProps>= {
static defaultProps: Partial<CollapseProps> = {
wrapperComponent: 'div',
headingComponent: 'h4',
className: '',
headingClassName: '',
bodyClassName: '',
collapsable: true
collapsable: true,
};
state = {
collapsed: false
collapsed: false,
};
constructor(props: CollapseProps) {
@ -53,19 +48,19 @@ export default class Collapse extends React.Component<CollapseProps, CollapseSta
this.state.collapsed = !!props.collapsed;
}
componentWillReceiveProps(nextProps:CollapseProps) {
componentWillReceiveProps(nextProps: CollapseProps) {
const props = this.props;
if (props.collapsed !== nextProps.collapsed) {
this.setState({
collapsed: !!nextProps.collapsed
collapsed: !!nextProps.collapsed,
});
}
}
toggleCollapsed() {
this.setState({
collapsed: !this.state.collapsed
collapsed: !this.state.collapsed,
});
}
@ -83,36 +78,39 @@ export default class Collapse extends React.Component<CollapseProps, CollapseSta
body,
bodyClassName,
render,
collapsable
collapsable,
} = this.props;
// todo 换掉 bootstrap 的 collapse
return (
<WrapperComponent className={cx(`Collapse`, {
'is-collapsed': this.state.collapsed,
[`Collapse--${size}`]: size,
'Collapse--collapsable': collapsable
}, className)}>
<WrapperComponent
className={cx(
`Collapse`,
{
'is-collapsed': this.state.collapsed,
[`Collapse--${size}`]: size,
'Collapse--collapsable': collapsable,
},
className
)}
>
{title ? (
<HeadingComponent className={cx(`Collapse-header`, headingClassName)}>
{render('heading', title)}
{collapsable && (
<span
onClick={this.toggleCollapsed}
className={cx('Collapse-arrow')}
></span>
)}
{collapsable && <span onClick={this.toggleCollapsed} className={cx('Collapse-arrow')} />}
</HeadingComponent>
) : null}
<BasicCollapse in={collapsable ? !this.state.collapsed : true}>
<div className={cx(`Collapse-body`, bodyClassName)}>
{children ? (
typeof children === 'function' ? children(this.props) : children
) : body ? (
render('body', body)
) : null}
{children
? typeof children === 'function'
? children(this.props)
: children
: body
? render('body', body)
: null}
</div>
</BasicCollapse>
</WrapperComponent>
@ -122,6 +120,6 @@ export default class Collapse extends React.Component<CollapseProps, CollapseSta
@Renderer({
test: /(^|\/)collapse$/,
name: 'collapse'
name: 'collapse',
})
export class CollapseRenderer extends Collapse {};
export class CollapseRenderer extends Collapse {}

View File

@ -1,63 +1,44 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import {Renderer, RendererProps} from '../factory';
import {SchemaNode} from '../types';
export interface ContainerProps extends RendererProps {
body?: SchemaNode;
children?: (props:any) => React.ReactNode;
children?: (props: any) => React.ReactNode;
className?: string;
}
export default class Container<T> extends React.Component<ContainerProps & T, object> {
static propsList: Array<string> = [
"body",
"className",
];
static defaultProps= {
static propsList: Array<string> = ['body', 'className'];
static defaultProps = {
className: '',
};
renderBody():JSX.Element | null {
const {
children,
body,
render,
classnames: cx
} = this.props;
renderBody(): JSX.Element | null {
const {children, body, render, classnames: cx} = this.props;
return (
<div className={cx("Container-body")}>
{children ? (
typeof children === 'function' ? children(this.props) as JSX.Element : children as JSX.Element
) : body ? (
render('body', body) as JSX.Element
) : null}
<div className={cx('Container-body')}>
{children
? typeof children === 'function'
? (children(this.props) as JSX.Element)
: (children as JSX.Element)
: body
? (render('body', body) as JSX.Element)
: null}
</div>
);
}
render() {
const {
className,
size,
classnames: cx
} = this.props;
const {className, size, classnames: cx} = this.props;
return (
<div
className={cx('Container', className)}
>
{this.renderBody()}
</div>
);
return <div className={cx('Container', className)}>{this.renderBody()}</div>;
}
}
@Renderer({
test: /(^|\/)container$/,
name: 'container'
name: 'container',
})
export class ContainerRenderer extends Container<{}> {};
export class ContainerRenderer extends Container<{}> {}

View File

@ -1,57 +1,56 @@
/**
* @file scoped.jsx.
* @author fex
*/
* @file scoped.jsx.
* @author fex
*/
import * as React from 'react';
import { RendererProps } from '../factory';
import {RendererProps} from '../factory';
import * as cx from 'classnames';
import hoistNonReactStatic = require('hoist-non-react-statics');
import Button from '../components/Button';
import { filter } from '../utils/tpl';
import {filter} from '../utils/tpl';
export interface CopyableConfig {
}
export interface CopyableConfig {}
export interface CopyableConfig {
icon?: string;
content?: string;
[propName:string]: any;
};
[propName: string]: any;
}
export interface CopyableProps extends RendererProps {
name?: string;
label?: string;
copyable: boolean | CopyableConfig;
};
}
export const HocCopyable = () => (Component: React.ComponentType<any>): any => {
class QuickEditComponent extends React.PureComponent<CopyableProps, any> {
static ComposedComponent = Component;
handleClick(content: string) {
const { env } = this.props;
const {env} = this.props;
env.copy && env.copy(content);
}
render() {
const {
copyable,
name,
className,
data,
noHoc,
classnames: cx
} = this.props;
const {copyable, name, className, data, noHoc, classnames: cx} = this.props;
if (copyable && !noHoc) {
const content = filter((copyable as CopyableConfig).content || '${' + name + ' | raw }', data);
if (content) {
return (<Component {...this.props} className={cx(`Field--copyable`, className)}>
<Component {...this.props} wrapperComponent={''} noHoc />
<i key="edit-btn" data-tooltip="点击复制" className={cx("Field-copyBtn fa fa-clipboard")} onClick={this.handleClick.bind(this, content)} />
</Component>);
return (
<Component {...this.props} className={cx(`Field--copyable`, className)}>
<Component {...this.props} wrapperComponent={''} noHoc />
<i
key="edit-btn"
data-tooltip="点击复制"
className={cx('Field-copyBtn fa fa-clipboard')}
onClick={this.handleClick.bind(this, content)}
/>
</Component>
);
}
}
return (<Component {...this.props} />);
return <Component {...this.props} />;
}
}
hoistNonReactStatic(QuickEditComponent, Component);

View File

@ -1,16 +1,8 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import {Renderer, RendererProps} from '../factory';
import {ServiceStore, IServiceStore} from '../store/service';
import {
Api,
SchemaNode
} from '../types';
import {
filter
} from '../utils/tpl';
import {Api, SchemaNode} from '../types';
import {filter} from '../utils/tpl';
import * as cx from 'classnames';
import * as moment from 'moment';
@ -22,66 +14,55 @@ export interface DateProps extends RendererProps {
}
export class DateField extends React.Component<DateProps, object> {
static defaultProps:Partial<DateProps> = {
static defaultProps: Partial<DateProps> = {
placeholder: '-',
format: 'YYYY-MM-DD',
valueFormat: 'X'
valueFormat: 'X',
};
render() {
const {
className,
value,
valueFormat,
format,
placeholder,
classnames: cx
} = this.props;
const {className, value, valueFormat, format, placeholder, classnames: cx} = this.props;
let viewValue:React.ReactNode = <span className="text-muted">{placeholder}</span>;
let viewValue: React.ReactNode = <span className="text-muted">{placeholder}</span>;
if (value) {
let date = moment(value, valueFormat);
viewValue = date.isValid() ? date.format(format) : <span className="text-danger"></span>;
}
return (
<span className={cx('DateField', className)}>
{viewValue}
</span>
);
return <span className={cx('DateField', className)}>{viewValue}</span>;
}
}
@Renderer({
test: /(^|\/)date$/,
name: 'date-field'
name: 'date-field',
})
export class DateFieldRenderer extends DateField {
static defaultProps:Partial<DateProps> = {
static defaultProps: Partial<DateProps> = {
...DateField.defaultProps,
format: 'YYYY-MM-DD'
format: 'YYYY-MM-DD',
};
};
}
@Renderer({
test: /(^|\/)datetime$/,
name: 'datetime-field'
name: 'datetime-field',
})
export class DateTimeFieldRenderer extends DateField {
static defaultProps:Partial<DateProps> = {
static defaultProps: Partial<DateProps> = {
...DateField.defaultProps,
format: 'YYYY-MM-DD HH:mm:ss'
format: 'YYYY-MM-DD HH:mm:ss',
};
};
}
@Renderer({
test: /(^|\/)time$/,
name: 'time-field'
name: 'time-field',
})
export class TimeFieldRenderer extends DateField {
static defaultProps:Partial<DateProps> = {
static defaultProps: Partial<DateProps> = {
...DateField.defaultProps,
format: 'HH:mm'
format: 'HH:mm',
};
};
}

View File

@ -1,34 +1,25 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import Scoped, { ScopedContext, IScopedContext } from '../Scoped';
import {
Renderer,
RendererProps
} from '../factory';
import Scoped, {ScopedContext, IScopedContext} from '../Scoped';
import {Renderer, RendererProps} from '../factory';
import {ServiceStore, IServiceStore} from '../store/service';
import { observer } from "mobx-react";
import {
SchemaNode,
Schema,
Action
} from '../types';
import {
filter
} from '../utils/tpl';
import {observer} from 'mobx-react';
import {SchemaNode, Schema, Action} from '../types';
import {filter} from '../utils/tpl';
import Modal from '../components/Modal';
import findLast = require('lodash/findLast');
import {guid} from '../utils/helper'
import {guid} from '../utils/helper';
import {reaction} from 'mobx';
import { closeIcon } from '../components/icons';
import { ModalStore, IModalStore } from '../store/modal';
import {closeIcon} from '../components/icons';
import {ModalStore, IModalStore} from '../store/modal';
export interface DialogProps extends RendererProps {
title?: string; // 标题
size?: 'md' | 'lg' | 'sm' | 'xl';
closeOnEsc?: boolean;
onClose: () => void;
onConfirm: (values:Array<object>, action: Action, ctx: object, targets: Array<any>) => void;
children?: React.ReactNode | ((props?:any) => React.ReactNode);
onConfirm: (values: Array<object>, action: Action, ctx: object, targets: Array<any>) => void;
children?: React.ReactNode | ((props?: any) => React.ReactNode);
store: IModalStore;
className?: string;
header?: SchemaNode;
@ -41,7 +32,6 @@ export interface DialogProps extends RendererProps {
lazyRender?: boolean;
wrapperComponent: React.ReactType;
showCloseButton?: boolean;
}
export interface DialogState {
@ -50,20 +40,20 @@ export interface DialogState {
export default class Dialog extends React.Component<DialogProps, DialogState> {
static propsList: Array<string> = [
"title",
"size",
"closeOnEsc",
"children",
"bodyClassName",
"headerClassName",
"confirm",
"onClose",
"onConfirm",
"show",
"showCloseButton",
"actions"
'title',
'size',
'closeOnEsc',
'children',
'bodyClassName',
'headerClassName',
'confirm',
'onClose',
'onConfirm',
'show',
'showCloseButton',
'actions',
];
static defaultProps:Partial<DialogProps>= {
static defaultProps: Partial<DialogProps> = {
title: '弹框',
bodyClassName: '',
confirm: true,
@ -71,17 +61,17 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
lazyRender: false,
showCloseButton: true,
wrapperComponent: Modal,
closeOnEsc: false
closeOnEsc: false,
};
reaction:any;
reaction: any;
$$id: string = guid();
constructor(props:DialogProps) {
constructor(props: DialogProps) {
super(props);
this.state = {
entered: !!this.props.show
}
entered: !!this.props.show,
};
this.handleSelfClose = this.handleSelfClose.bind(this);
this.handleAction = this.handleAction.bind(this);
this.handleDialogConfirm = this.handleDialogConfirm.bind(this);
@ -117,21 +107,18 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
this.reaction && this.reaction();
}
buildActions():Array<Action> {
const {
actions,
confirm
} = this.props;
buildActions(): Array<Action> {
const {actions, confirm} = this.props;
if (typeof actions !== 'undefined') {
return actions;
}
let ret:Array<Action> = [];
let ret: Array<Action> = [];
ret.push({
type: 'button',
actionType: 'cancel',
label: '取消'
label: '取消',
});
if (confirm) {
@ -139,7 +126,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
type: 'button',
actionType: 'confirm',
label: '确认',
primary: true
primary: true,
});
}
@ -147,10 +134,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
}
handleSelfClose() {
const {
onClose,
store
} = this.props;
const {onClose, store} = this.props;
// clear error
store.updateMessage();
@ -158,10 +142,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
}
handleAction(e: React.UIEvent<any>, action: Action, data: object) {
const {
store,
onAction
} = this.props;
const {store, onAction} = this.props;
if (action.type === 'reset') {
store.reset();
@ -172,10 +153,8 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
}
}
handleDialogConfirm(values: object[], action:Action, ...args:Array<any>) {
const {
store
} = this.props;
handleDialogConfirm(values: object[], action: Action, ...args: Array<any>) {
const {store} = this.props;
if (action.mergeData && values.length === 1 && values[0]) {
store.updateData(values[0]);
@ -190,10 +169,8 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
store.closeDialog();
}
handleDialogClose(...args:Array<any>) {
const {
store
} = this.props;
handleDialogClose(...args: Array<any>) {
const {store} = this.props;
const action = store.action as Action;
const dialog = action.dialog as any;
@ -204,11 +181,9 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
store.closeDialog();
}
handleDrawerConfirm(values: object[], action:Action, ...args:Array<any>) {
const {
store
} = this.props;
handleDrawerConfirm(values: object[], action: Action, ...args: Array<any>) {
const {store} = this.props;
if (action.mergeData && values.length === 1 && values[0]) {
store.updateData(values[0]);
@ -223,10 +198,8 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
store.closeDrawer();
}
handleDrawerClose(...args:Array<any>) {
const {
store
} = this.props;
handleDrawerClose(...args: Array<any>) {
const {store} = this.props;
const action = store.action as Action;
const drawer = action.drawer as any;
@ -239,79 +212,72 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
}
handleEntered() {
this.state.entered || this.setState({
entered: true
})
this.state.entered ||
this.setState({
entered: true,
});
}
handleExited() {
const {store} = this.props;
store.reset();
this.state.entered && this.setState({
entered: false
})
this.state.entered &&
this.setState({
entered: false,
});
}
handleFormInit(data:any) {
const {
store
} = this.props;
handleFormInit(data: any) {
const {store} = this.props;
store.setFormData(data);
}
handleFormChange(data:any) {
const {
store
} = this.props;
handleFormChange(data: any) {
const {store} = this.props;
store.setFormData(data);
}
handleFormSaved(data:any, response:any) {
const {
store
} = this.props;
handleFormSaved(data: any, response: any) {
const {store} = this.props;
store.setFormData({
...data,
...response
...response,
});
}
handleChildFinished(value:any, action:Action) {
handleChildFinished(value: any, action: Action) {
// 下面会覆盖
}
renderBody(body:SchemaNode, key?:any):React.ReactNode {
let {
render,
store,
} = this.props;
renderBody(body: SchemaNode, key?: any): React.ReactNode {
let {render, store} = this.props;
if (Array.isArray(body)) {
return body.map((body, key) => this.renderBody(body, key));
}
let subProps:any = {
let subProps: any = {
key,
disabled: store.loading,
onAction: this.handleAction,
onFinished: this.handleChildFinished
onFinished: this.handleChildFinished,
};
if (!(body as Schema).type) {
return render(`body${key ? `/${key}` : ''}`, body, subProps);
}
let schema:Schema = body as Schema;
let schema: Schema = body as Schema;
if (schema.type === 'form') {
schema = {
mode: 'horizontal',
wrapWithPanel: false,
submitText: null,
...schema
...schema,
};
// 同步数据到 Dialog 层,方便 actions 根据表单数据联动。
@ -330,31 +296,35 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
return null;
}
const {
store,
render,
classnames: cx
} = this.props;
const {store, render, classnames: cx} = this.props;
return (
<div className={cx('Modal-footer')}>
{store.loading || store.error ? (
<div className={cx("Dialog-info")} key="info">
{store.loading ? render('info', {
type: 'spinner'
}, {
key: 'info',
size: 'sm'
}) : null}
{store.error ? (<span className={cx("Dialog-error")}>{store.msg}</span>) : null}
<div className={cx('Dialog-info')} key="info">
{store.loading
? render(
'info',
{
type: 'spinner',
},
{
key: 'info',
size: 'sm',
}
)
: null}
{store.error ? <span className={cx('Dialog-error')}>{store.msg}</span> : null}
</div>
) : null}
{actions.map((action, key) => render(`action/${key}`, action, {
data: store.formData,
onAction: this.handleAction,
key,
disabled: action.disabled || store.loading
}))}
{actions.map((action, key) =>
render(`action/${key}`, action, {
data: store.formData,
onAction: this.handleAction,
key,
disabled: action.disabled || store.loading,
})
)}
</div>
);
}
@ -377,7 +347,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
showCloseButton,
env,
classnames: cx,
classPrefix
classPrefix,
} = this.props;
// console.log('Render Dialog');
@ -401,60 +371,82 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
disabled={store.loading}
>
{title && typeof title === 'string' ? (
<div className={cx("Modal-header", headerClassName)}>
{showCloseButton !== false && !store.loading ? (<a data-tooltip="关闭弹窗" onClick={this.handleSelfClose} className={cx("Modal-close")}>{closeIcon}</a>) : null}
<div className={cx("Modal-title")}>{filter(title, store.formData)}</div>
<div className={cx('Modal-header', headerClassName)}>
{showCloseButton !== false && !store.loading ? (
<a data-tooltip="关闭弹窗" onClick={this.handleSelfClose} className={cx('Modal-close')}>
{closeIcon}
</a>
) : null}
<div className={cx('Modal-title')}>{filter(title, store.formData)}</div>
</div>
) : title ? (
<div className={cx("Modal-header", headerClassName)}>
{showCloseButton !== false && !store.loading ? (<a data-tooltip="关闭弹窗" onClick={this.handleSelfClose} className={cx("Modal-close")}>{closeIcon}</a>) : null}
<div className={cx('Modal-header', headerClassName)}>
{showCloseButton !== false && !store.loading ? (
<a data-tooltip="关闭弹窗" onClick={this.handleSelfClose} className={cx('Modal-close')}>
{closeIcon}
</a>
) : null}
{render('title', title, {
data: store.formData
data: store.formData,
})}
</div>
) : showCloseButton !== false && !store.loading ? (
<a data-tooltip="关闭弹窗" onClick={this.handleSelfClose} className={cx("Modal-close")}>{closeIcon}</a>
<a data-tooltip="关闭弹窗" onClick={this.handleSelfClose} className={cx('Modal-close')}>
{closeIcon}
</a>
) : null}
{header ? render('header', header, {
data: store.formData
}) : null}
{header
? render('header', header, {
data: store.formData,
})
: null}
{!this.state.entered && lazyRender ? (
<div className={cx("Modal-body", bodyClassName)}>
</div>
) : body ? (
<div className={cx("Modal-body", bodyClassName)}>
{this.renderBody(body, 'body')}
</div>
<div className={cx('Modal-body', bodyClassName)} />
) : body ? (
<div className={cx('Modal-body', bodyClassName)}>{this.renderBody(body, 'body')}</div>
) : null}
{this.renderFooter()}
{body ? render('drawer', { // 支持嵌套
...(store.action as Action) && (store.action as Action).drawer as object,
type: 'drawer'
}, {
key: 'drawer',
data: store.drawerData,
onConfirm: this.handleDrawerConfirm,
onClose: this.handleDrawerClose,
show: store.drawerOpen,
onAction: this.handleAction
}) : null}
{body
? render(
'drawer',
{
// 支持嵌套
...((store.action as Action) && ((store.action as Action).drawer as object)),
type: 'drawer',
},
{
key: 'drawer',
data: store.drawerData,
onConfirm: this.handleDrawerConfirm,
onClose: this.handleDrawerClose,
show: store.drawerOpen,
onAction: this.handleAction,
}
)
: null}
{body ? render('dialog', { // 支持嵌套
...(store.action as Action) && (store.action as Action).dialog as object,
type: 'dialog'
}, {
key: 'dialog',
data: store.dialogData,
onConfirm: this.handleDialogConfirm,
onClose: this.handleDialogClose,
show: store.dialogOpen,
onAction: this.handleAction
}) : null}
{body
? render(
'dialog',
{
// 支持嵌套
...((store.action as Action) && ((store.action as Action).dialog as object)),
type: 'dialog',
},
{
key: 'dialog',
data: store.dialogData,
onConfirm: this.handleDialogConfirm,
onClose: this.handleDialogClose,
show: store.dialogOpen,
onAction: this.handleAction,
}
)
: null}
</Wrapper>
);
}
@ -465,7 +457,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
storeType: ModalStore.name,
storeExtendsData: false,
name: 'dialog',
isolateScope: true
isolateScope: true,
})
export class DialogRenderer extends Dialog {
static contextType = ScopedContext;
@ -490,14 +482,16 @@ export class DialogRenderer extends Dialog {
}
const components = scoped.getComponents();
const targets:Array<any> = [];
const {
onConfirm,
store
} = this.props;
const targets: Array<any> = [];
const {onConfirm, store} = this.props;
if (action.target) {
targets.push(...action.target.split(',').map(name => scoped.getComponentByName(name)).filter(item => item && item.doAction));
targets.push(
...action.target
.split(',')
.map(name => scoped.getComponentByName(name))
.filter(item => item && item.doAction)
);
}
if (!targets.length) {
@ -511,21 +505,27 @@ export class DialogRenderer extends Dialog {
if (targets.length) {
store.markBusying(true);
store.updateMessage();
Promise
.all(targets.map(target => target.doAction({
...action,
from: this.$$id
}, ctx, true)))
Promise.all(
targets.map(target =>
target.doAction(
{
...action,
from: this.$$id,
},
ctx,
true
)
)
)
.then(values => {
if (
(action.type === 'submit'
|| action.actionType === 'submit'
|| action.actionType === 'confirm')
&& action.close !== false
(action.type === 'submit' ||
action.actionType === 'submit' ||
action.actionType === 'confirm') &&
action.close !== false
) {
onConfirm
&& onConfirm(values, rawAction || action, ctx, targets)
onConfirm && onConfirm(values, rawAction || action, ctx, targets);
} else if (action.close) {
this.handleSelfClose();
}
@ -539,17 +539,17 @@ export class DialogRenderer extends Dialog {
return true;
}
return false;
}
handleAction(e: React.UIEvent<any>, action: Action, data: object, throwErrors: boolean = false, delegate?:boolean) {
const {
onAction,
store,
onConfirm,
} = this.props;
handleAction(
e: React.UIEvent<any>,
action: Action,
data: object,
throwErrors: boolean = false,
delegate?: boolean
) {
const {onAction, store, onConfirm} = this.props;
if (action.from === this.$$id) {
return onAction ? onAction(e, action, data, throwErrors, true) : false;
@ -563,16 +563,24 @@ export class DialogRenderer extends Dialog {
} else if (action.actionType === 'close' || action.actionType === 'cancel') {
this.handleSelfClose();
} else if (action.actionType === 'confirm') {
this.tryChildrenToHandle({
...action,
actionType: 'submit'
}, data, action) || this.handleSelfClose();
this.tryChildrenToHandle(
{
...action,
actionType: 'submit',
},
data,
action
) || this.handleSelfClose();
} else if (action.actionType === 'next' || action.actionType === 'prev') {
if (action.type === 'submit') {
this.tryChildrenToHandle({
...action,
actionType: 'submit'
}, data, action) || this.handleSelfClose();
this.tryChildrenToHandle(
{
...action,
actionType: 'submit',
},
data,
action
) || this.handleSelfClose();
} else {
onConfirm([data], action, data, []);
}
@ -588,20 +596,22 @@ export class DialogRenderer extends Dialog {
}
}
handleChildFinished(value:any, action:Action) {
if (action && action.from === this.$$id || action.close === false) {
handleChildFinished(value: any, action: Action) {
if ((action && action.from === this.$$id) || action.close === false) {
return;
}
const scoped = this.context as IScopedContext;
const components = scoped.getComponents().filter((item:any) => !~['drawer', 'dialog'].indexOf(item.props.type));
const components = scoped
.getComponents()
.filter((item: any) => !~['drawer', 'dialog'].indexOf(item.props.type));
const onConfirm = this.props.onConfirm;
const onClose = this.props.onClose;
if (
components.length === 1
&& (components[0].props.type === 'form' || components[0].props.type === 'wizard')
&& (action.close === true || components[0].props.closeDialogOnSubmit !== false)
components.length === 1 &&
(components[0].props.type === 'form' || components[0].props.type === 'wizard') &&
(action.close === true || components[0].props.closeDialogOnSubmit !== false)
) {
onConfirm && onConfirm([value], action, {}, components);
} else if (action.close === true) {
@ -621,11 +631,11 @@ export class DialogRenderer extends Dialog {
scoped.reload(action.reload, store.data);
} else {
// 没有设置,则自动让页面中 crud 刷新。
scoped.getComponents()
scoped
.getComponents()
.filter((item: any) => item.props.type === 'crud')
.forEach((item: any) => item.reload && item.reload());
}
}
handleDrawerConfirm(values: object[], action: Action, ...rest: Array<any>) {
@ -642,10 +652,11 @@ export class DialogRenderer extends Dialog {
scoped.reload(action.reload, store.data);
} else {
// 没有设置,则自动让页面中 crud 刷新。
scoped.getComponents()
scoped
.getComponents()
.filter((item: any) => item.props.type === 'crud')
.forEach((item: any) => item.reload && item.reload());
}
}, 300);
}
};
}

View File

@ -1,33 +1,25 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import {Renderer, RendererProps} from '../factory';
import {ServiceStore, IServiceStore} from '../store/service';
import {
filter
} from '../utils/tpl';
import {filter} from '../utils/tpl';
import * as cx from 'classnames';
export interface DividerProps extends RendererProps {
}
export interface DividerProps extends RendererProps {}
export default class Divider extends React.Component<DividerProps, object> {
static defaultProps:Partial<DividerProps>= {
className: ''
static defaultProps: Partial<DividerProps> = {
className: '',
};
render() {
const cx = this.props.classnames;
const className = this.props.className;
return (
<div className={cx('Divider', className)}></div>
);
return <div className={cx('Divider', className)} />;
}
}
@Renderer({
test: /(^|\/)(?:divider|hr)$/,
name: 'divider'
name: 'divider',
})
export class DividerRenderer extends Divider {}

View File

@ -1,24 +1,17 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import Scoped, { ScopedContext, IScopedContext } from '../Scoped';
import {
Renderer,
RendererProps
} from '../factory';
import { ServiceStore, IServiceStore } from '../store/service';
import { observer } from "mobx-react";
import {
SchemaNode,
Schema,
Action
} from '../types';
import Scoped, {ScopedContext, IScopedContext} from '../Scoped';
import {Renderer, RendererProps} from '../factory';
import {ServiceStore, IServiceStore} from '../store/service';
import {observer} from 'mobx-react';
import {SchemaNode, Schema, Action} from '../types';
import * as cx from 'classnames';
import {default as DrawerContainer} from '../components/Drawer';
import findLast = require('lodash/findLast');
import { guid } from '../utils/helper'
import { reaction } from 'mobx';
import { findDOMNode } from 'react-dom';
import { IModalStore, ModalStore } from '../store/modal';
import {guid} from '../utils/helper';
import {reaction} from 'mobx';
import {findDOMNode} from 'react-dom';
import {IModalStore, ModalStore} from '../store/modal';
export interface DrawerProps extends RendererProps {
title?: string; // 标题
@ -43,18 +36,18 @@ export interface DrawerProps extends RendererProps {
export default class Drawer extends React.Component<DrawerProps, object> {
static propsList: Array<string> = [
"title",
"size",
"closeOnEsc",
"children",
"bodyClassName",
"confirm",
"position",
"onClose",
"onConfirm",
"show",
"resizable",
"overlay"
'title',
'size',
'closeOnEsc',
'children',
'bodyClassName',
'confirm',
'position',
'onClose',
'onConfirm',
'show',
'resizable',
'overlay',
];
static defaultProps: Partial<DrawerProps> = {
title: '',
@ -63,15 +56,15 @@ export default class Drawer extends React.Component<DrawerProps, object> {
position: 'right',
resizable: false,
overlay: true,
closeOnEsc: false
closeOnEsc: false,
};
reaction: any;
$$id: string = guid();
drawer: any;
state = {
resizeCoord: 0
}
resizeCoord: 0,
};
constructor(props: DrawerProps) {
super(props);
@ -111,10 +104,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
}
buildActions(): Array<Action> {
const {
actions,
confirm
} = this.props;
const {actions, confirm} = this.props;
if (typeof actions !== 'undefined') {
return actions;
@ -124,7 +114,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
ret.push({
type: 'button',
actionType: 'close',
label: '取消'
label: '取消',
});
if (confirm) {
@ -132,7 +122,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
type: 'button',
actionType: 'confirm',
label: '确认',
primary: true
primary: true,
});
}
@ -140,10 +130,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
}
handleSelfClose() {
const {
onClose,
store
} = this.props;
const {onClose, store} = this.props;
// clear error
store.updateMessage();
@ -151,10 +138,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
}
handleAction(e: React.UIEvent<any>, action: Action, data: object) {
const {
onClose,
onAction
} = this.props;
const {onClose, onAction} = this.props;
if (action.actionType === 'close') {
onClose();
@ -163,10 +147,8 @@ export default class Drawer extends React.Component<DrawerProps, object> {
}
}
handleDrawerConfirm(values: object[], action:Action, ...args: Array<any>) {
const {
store
} = this.props;
handleDrawerConfirm(values: object[], action: Action, ...args: Array<any>) {
const {store} = this.props;
if (action.mergeData && values.length === 1 && values[0]) {
store.updateData(values[0]);
@ -183,9 +165,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
}
handleDrawerClose(...args: Array<any>) {
const {
store
} = this.props;
const {store} = this.props;
const action = store.action as Action;
const drawer = action.drawer as any;
@ -197,10 +177,8 @@ export default class Drawer extends React.Component<DrawerProps, object> {
store.closeDrawer();
}
handleDialogConfirm(values: object[], action:Action, ...args: Array<any>) {
const {
store
} = this.props;
handleDialogConfirm(values: object[], action: Action, ...args: Array<any>) {
const {store} = this.props;
if (action.mergeData && values.length === 1 && values[0]) {
store.updateData(values[0]);
@ -217,9 +195,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
}
handleDialogClose(...args: Array<any>) {
const {
store
} = this.props;
const {store} = this.props;
const action = store.action as Action;
const dialog = action.dialog as any;
@ -235,30 +211,24 @@ export default class Drawer extends React.Component<DrawerProps, object> {
// 下面会覆盖
}
handleFormInit(data:any) {
const {
store
} = this.props;
handleFormInit(data: any) {
const {store} = this.props;
store.setFormData(data);
}
handleFormChange(data:any) {
const {
store
} = this.props;
handleFormChange(data: any) {
const {store} = this.props;
store.setFormData(data);
}
handleFormSaved(data:any, response:any) {
const {
store
} = this.props;
handleFormSaved(data: any, response: any) {
const {store} = this.props;
store.setFormData({
...data,
...response
...response,
});
}
@ -268,17 +238,14 @@ export default class Drawer extends React.Component<DrawerProps, object> {
}
renderBody(body: SchemaNode, key?: any): React.ReactNode {
let {
render,
store,
} = this.props;
let {render, store} = this.props;
let schema: Schema = body as Schema;
let subProps: any = {
key,
disabled: store.loading,
onAction: this.handleAction,
onFinished: this.handleChildFinished
onFinished: this.handleChildFinished,
};
if (schema.type === 'form') {
@ -286,7 +253,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
mode: 'horizontal',
wrapWithPanel: false,
submitText: null,
...schema
...schema,
};
// 同步数据到 Dialog 层,方便 actions 根据表单数据联动。
@ -305,83 +272,93 @@ export default class Drawer extends React.Component<DrawerProps, object> {
return null;
}
const {
store,
render,
classnames: cx
} = this.props;
const {store, render, classnames: cx} = this.props;
return (
<div className={cx("Drawer-footer")}>
<div className={cx('Drawer-footer')}>
{store.loading || store.error ? (
<div className={cx("Drawer-info")}>
{store.loading ? render('info', {
type: 'spinner'
}, {
key: 'info'
}) : null}
{store.error ? (<span className={cx("Drawer-msg")}>{store.msg}</span>) : null}
<div className={cx('Drawer-info')}>
{store.loading
? render(
'info',
{
type: 'spinner',
},
{
key: 'info',
}
)
: null}
{store.error ? <span className={cx('Drawer-msg')}>{store.msg}</span> : null}
</div>
) : null}
{actions.map((action, key) => render(`action/${key}`, action, {
onAction: this.handleAction,
data: store.formData,
key,
disabled: action.disabled || store.loading
}))}
{actions.map((action, key) =>
render(`action/${key}`, action, {
onAction: this.handleAction,
data: store.formData,
key,
disabled: action.disabled || store.loading,
})
)}
</div>
);
}
renderResizeCtrl() {
const {
classnames: cx
} = this.props;
const {classnames: cx} = this.props;
return (
<div
className={cx('Drawer-resizeCtrl')}
onMouseDown={this.resizeMouseDown}
>
<div className={cx("Drawer-resizeIcon")}>···</div>
<div className={cx('Drawer-resizeCtrl')} onMouseDown={this.resizeMouseDown}>
<div className={cx('Drawer-resizeIcon')}>···</div>
</div>
);
}
resizeMouseDown(e: React.MouseEvent<any>) {
const {
position,
classPrefix: ns
} = this.props;
const {position, classPrefix: ns} = this.props;
this.drawer = (findDOMNode(this) as HTMLElement).querySelector(`.${ns}Drawer-content`) as HTMLElement;
const resizeCtrl = (findDOMNode(this) as HTMLElement).querySelector(`.${ns}Drawer-content .${ns}Drawer-resizeCtrl`) as HTMLElement;
const resizeCtrl = (findDOMNode(this) as HTMLElement).querySelector(
`.${ns}Drawer-content .${ns}Drawer-resizeCtrl`
) as HTMLElement;
const drawerWidth = getComputedStyle(this.drawer).width as string;
const drawerHeight = getComputedStyle(this.drawer).height as string;
this.setState({
resizeCoord:
(position === 'left' && e.clientX - resizeCtrl.offsetWidth - parseInt(drawerWidth.substring(0, drawerWidth.length - 2))) ||
(position === 'right' && document.body.offsetWidth - e.clientX - resizeCtrl.offsetWidth - parseInt(drawerWidth.substring(0, drawerWidth.length - 2))) ||
(position === 'top' && e.clientY - resizeCtrl.offsetHeight - parseInt(drawerHeight.substring(0, drawerHeight.length - 2))) ||
(position === 'bottom' && document.body.offsetHeight - e.clientY - resizeCtrl.offsetHeight - parseInt(drawerHeight.substring(0, drawerHeight.length - 2))) || 0
})
(position === 'left' &&
e.clientX - resizeCtrl.offsetWidth - parseInt(drawerWidth.substring(0, drawerWidth.length - 2))) ||
(position === 'right' &&
document.body.offsetWidth -
e.clientX -
resizeCtrl.offsetWidth -
parseInt(drawerWidth.substring(0, drawerWidth.length - 2))) ||
(position === 'top' &&
e.clientY -
resizeCtrl.offsetHeight -
parseInt(drawerHeight.substring(0, drawerHeight.length - 2))) ||
(position === 'bottom' &&
document.body.offsetHeight -
e.clientY -
resizeCtrl.offsetHeight -
parseInt(drawerHeight.substring(0, drawerHeight.length - 2))) ||
0,
});
document.body.addEventListener('mousemove', this.bindResize);
document.body.addEventListener('mouseup', this.removeResize);
}
bindResize(e: any) {
const {
position
} = this.props;
const {position} = this.props;
const maxWH = 'calc(100% - 50px)';
const drawerStyle = this.drawer.style;
let wh =
(position === 'left' && e.clientX) ||
(position === 'right' && document.body.offsetWidth - e.clientX) ||
(position === 'top' && e.clientY) ||
(position === 'bottom' && document.body.offsetHeight - e.clientY) || 0;
(position === 'bottom' && document.body.offsetHeight - e.clientY) ||
0;
wh = wh - this.state.resizeCoord + 'px';
if (position === 'left' || position === 'right') {
@ -418,7 +395,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
overlay,
closeOnOutside,
classPrefix: ns,
classnames: cx
classnames: cx,
} = this.props;
const Container = wrapperComponent || DrawerContainer;
@ -438,46 +415,60 @@ export default class Drawer extends React.Component<DrawerProps, object> {
closeOnOutside={closeOnOutside}
container={env && env.getModalContainer ? env.getModalContainer() : undefined}
>
<div className={cx("Drawer-header")}>
<div className={cx('Drawer-header')}>
{title ? (
<div className={cx("Drawer-title")}>{render('title', title, {
data: store.formData
})}</div>
<div className={cx('Drawer-title')}>
{render('title', title, {
data: store.formData,
})}
</div>
) : null}
{header ? render('header', header, {
data: store.formData
}) : null}
{header
? render('header', header, {
data: store.formData,
})
: null}
</div>
<div className={cx("Drawer-body", bodyClassName)}>
{body ? this.renderBody(body, 'body') : null}
</div>
<div className={cx('Drawer-body', bodyClassName)}>{body ? this.renderBody(body, 'body') : null}</div>
{this.renderFooter()}
{body ? render('dialog', {
...(store.action as Action) && (store.action as Action).dialog as object,
type: 'dialog'
}, {
key: 'dialog',
data: store.dialogData,
onConfirm: this.handleDialogConfirm,
onClose: this.handleDialogClose,
onAction: this.handleAction,
show: store.dialogOpen,
}) : null}
{body
? render(
'dialog',
{
...((store.action as Action) && ((store.action as Action).dialog as object)),
type: 'dialog',
},
{
key: 'dialog',
data: store.dialogData,
onConfirm: this.handleDialogConfirm,
onClose: this.handleDialogClose,
onAction: this.handleAction,
show: store.dialogOpen,
}
)
: null}
{body ? render('drawer', {
...(store.action as Action) && (store.action as Action).drawer as object,
type: 'drawer'
}, {
key: 'drawer',
data: store.drawerData,
onConfirm: this.handleDrawerConfirm,
onClose: this.handleDrawerClose,
onAction: this.handleAction,
show: store.drawerOpen,
}) : null}
{body
? render(
'drawer',
{
...((store.action as Action) && ((store.action as Action).drawer as object)),
type: 'drawer',
},
{
key: 'drawer',
data: store.drawerData,
onConfirm: this.handleDrawerConfirm,
onClose: this.handleDrawerClose,
onAction: this.handleAction,
show: store.drawerOpen,
}
)
: null}
{resizable ? this.renderResizeCtrl() : null}
</Container>
@ -490,7 +481,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
storeType: ModalStore.name,
storeExtendsData: false,
name: 'drawer',
isolateScope: true
isolateScope: true,
})
export class DrawerRenderer extends Drawer {
static contextType = ScopedContext;
@ -516,13 +507,15 @@ export class DrawerRenderer extends Drawer {
const components = scoped.getComponents();
const targets: Array<any> = [];
const {
onConfirm,
store
} = this.props;
const {onConfirm, store} = this.props;
if (action.target) {
targets.push(...action.target.split(',').map(name => scoped.getComponentByName(name)).filter(item => item && item.doAction));
targets.push(
...action.target
.split(',')
.map(name => scoped.getComponentByName(name))
.filter(item => item && item.doAction)
);
}
if (!targets.length) {
@ -537,20 +530,26 @@ export class DrawerRenderer extends Drawer {
store.markBusying(true);
store.updateMessage();
Promise
.all(targets.map(target => target.doAction({
...action,
from: this.$$id
}, ctx, true)))
Promise.all(
targets.map(target =>
target.doAction(
{
...action,
from: this.$$id,
},
ctx,
true
)
)
)
.then(values => {
if (
(action.type === 'submit'
|| action.actionType === 'submit'
|| action.actionType === 'confirm')
&& action.close !== false
(action.type === 'submit' ||
action.actionType === 'submit' ||
action.actionType === 'confirm') &&
action.close !== false
) {
onConfirm
&& onConfirm(values, rawAction || action, ctx, targets)
onConfirm && onConfirm(values, rawAction || action, ctx, targets);
} else if (action.close) {
this.handleSelfClose();
}
@ -564,16 +563,17 @@ export class DrawerRenderer extends Drawer {
return true;
}
return false;
}
handleAction(e: React.UIEvent<any>, action: Action, data: object, throwErrors: boolean = false, delegate?: boolean) {
const {
onClose,
onAction,
store
} = this.props;
handleAction(
e: React.UIEvent<any>,
action: Action,
data: object,
throwErrors: boolean = false,
delegate?: boolean
) {
const {onClose, onAction, store} = this.props;
if (action.from === this.$$id) {
return onAction ? onAction(e, action, data, throwErrors, true) : false;
@ -599,12 +599,14 @@ export class DrawerRenderer extends Drawer {
}
handleChildFinished(value: any, action: Action) {
if (action && action.from === this.$$id || action.close === false) {
if ((action && action.from === this.$$id) || action.close === false) {
return;
}
const scoped = this.context as IScopedContext;
const components = scoped.getComponents().filter((item: any) => !~['drawer', 'dialog'].indexOf(item.props.type));
const components = scoped
.getComponents()
.filter((item: any) => !~['drawer', 'dialog'].indexOf(item.props.type));
const onConfirm = this.props.onConfirm;
if (components.length === 1 && (components[0].props.type === 'form' || components[0].props.type === 'wizard')) {
@ -624,11 +626,11 @@ export class DrawerRenderer extends Drawer {
scoped.reload(action.reload, store.data);
} else {
// 没有设置,则自动让页面中 crud 刷新。
scoped.getComponents()
scoped
.getComponents()
.filter((item: any) => item.props.type === 'crud')
.forEach((item: any) => item.reload && item.reload());
}
}
handleDrawerConfirm(values: object[], action: Action, ...rest: Array<any>) {
@ -645,10 +647,11 @@ export class DrawerRenderer extends Drawer {
scoped.reload(action.reload, store.data);
} else {
// 没有设置,则自动让页面中 crud 刷新。
scoped.getComponents()
scoped
.getComponents()
.filter((item: any) => item.props.type === 'crud')
.forEach((item: any) => item.reload && item.reload());
}
}, 300);
}
};
}

View File

@ -1,15 +1,11 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import {Renderer, RendererProps} from '../factory';
import {RootCloseWrapper} from 'react-overlays';
import Overlay from '../components/Overlay';
import PopOver from '../components/PopOver';
import * as cx from 'classnames';
import { isVisible } from '../utils/helper';
import { filter } from '../utils/tpl';
import {isVisible} from '../utils/helper';
import {filter} from '../utils/tpl';
export interface DropDownButtonProps extends RendererProps {
block?: boolean;
@ -18,24 +14,23 @@ export interface DropDownButtonProps extends RendererProps {
buttons?: Array<any>;
caretIcon?: string;
iconOnly?: boolean;
};
}
export interface DropDownButtonState {
isOpened: boolean;
};
}
export default class DropDownButton extends React.Component<DropDownButtonProps, DropDownButtonState> {
state:DropDownButtonState = {
isOpened: false
state: DropDownButtonState = {
isOpened: false,
};
static defaultProps = {
caretIcon: 'fa fa-angle-down'
caretIcon: 'fa fa-angle-down',
};
target:any;
constructor(props:DropDownButtonProps) {
target: any;
constructor(props: DropDownButtonProps) {
super(props);
this.open = this.open.bind(this);
@ -44,69 +39,57 @@ export default class DropDownButton extends React.Component<DropDownButtonProps,
this.domRef = this.domRef.bind(this);
}
domRef(ref:any) {
domRef(ref: any) {
this.target = ref;
}
toogle(e:React.MouseEvent<any>) {
toogle(e: React.MouseEvent<any>) {
e.preventDefault();
this.setState({
isOpened: !this.state.isOpened
isOpened: !this.state.isOpened,
});
}
open() {
this.setState({
isOpened: true
isOpened: true,
});
}
close() {
this.setState({
isOpened: false
isOpened: false,
});
}
renderOuter() {
const {
render,
buttons,
data,
popOverContainer,
classnames: cx,
classPrefix: ns,
children,
align
} = this.props;
const {render, buttons, data, popOverContainer, classnames: cx, classPrefix: ns, children, align} = this.props;
let body = (
<RootCloseWrapper
disabled={!this.state.isOpened}
onRootClose={this.close}
>
<ul
className={cx("DropDown-menu")}
>
{children ? children : Array.isArray(buttons) ? buttons.map((button, index) => {
if (!isVisible(button, data)) {
return null;
} else if (button === 'divider' || button.type === 'divider') {
return (
<li key={index} className={cx("DropDown-divider")} />
);
}
<RootCloseWrapper disabled={!this.state.isOpened} onRootClose={this.close}>
<ul className={cx('DropDown-menu')}>
{children
? children
: Array.isArray(buttons)
? buttons.map((button, index) => {
if (!isVisible(button, data)) {
return null;
} else if (button === 'divider' || button.type === 'divider') {
return <li key={index} className={cx('DropDown-divider')} />;
}
return (
<li key={index}>
{render(`button/${index}`, {
type: 'button',
...button,
isMenuItem: true
})}
</li>
);
}) : null}
return (
<li key={index}>
{render(`button/${index}`, {
type: 'button',
...button,
isMenuItem: true,
})}
</li>
);
})
: null}
</ul>
</RootCloseWrapper>
);
@ -119,11 +102,11 @@ export default class DropDownButton extends React.Component<DropDownButtonProps,
target={() => this.target}
show
>
<PopOver
<PopOver
overlay
onHide={this.close}
classPrefix={ns}
className={cx("DropDown-popover")}
className={cx('DropDown-popover')}
style={{minWidth: this.target.offsetWidth}}
>
{body}
@ -149,7 +132,7 @@ export default class DropDownButton extends React.Component<DropDownButtonProps,
caretIcon,
align,
iconOnly,
data
data,
} = this.props;
return (
@ -157,18 +140,24 @@ export default class DropDownButton extends React.Component<DropDownButtonProps,
className={cx('DropDown ', {
'DropDown--block': block,
'DropDown--alignRight': align === 'right',
'is-opened': this.state.isOpened
'is-opened': this.state.isOpened,
})}
ref={this.domRef}
>
<button
onClick={this.toogle}
disabled={disabled || btnDisabled}
className={cx('Button', className, (typeof level === 'undefined' ? 'Button--default' : level ? `Button--${level}` : ''), {
'Button--block': block,
'Button--primary': primary,
'Button--iconOnly': iconOnly
}, size ? `Button--${size}` : '')}
className={cx(
'Button',
className,
typeof level === 'undefined' ? 'Button--default' : level ? `Button--${level}` : '',
{
'Button--block': block,
'Button--primary': primary,
'Button--iconOnly': iconOnly,
},
size ? `Button--${size}` : ''
)}
>
{typeof label === 'string' ? filter(label, data) : label}
<i className={cx('DropDown-caret', caretIcon)} />
@ -182,8 +171,6 @@ export default class DropDownButton extends React.Component<DropDownButtonProps,
@Renderer({
test: /(^|\/)dropdown-button$/,
name: 'dropdown-button'
name: 'dropdown-button',
})
export class DropDownButtonRenderer extends DropDownButton {
}
export class DropDownButtonRenderer extends DropDownButton {}

View File

@ -1,13 +1,8 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import {
Schema,
} from '../types';
import { resolveVariable } from '../utils/tpl-builtin';
import { createObject, isObject } from '../utils/helper';
import {Renderer, RendererProps} from '../factory';
import {Schema} from '../types';
import {resolveVariable} from '../utils/tpl-builtin';
import {createObject, isObject} from '../utils/helper';
export interface EachProps extends RendererProps {
name: string;
@ -15,34 +10,35 @@ export interface EachProps extends RendererProps {
}
export default class Each extends React.Component<EachProps> {
static defaultProps: Partial<EachProps> = {
className: '',
};
render() {
const {
data,
name,
className,
render,
value,
items
} = this.props;
const {data, name, className, render, value, items} = this.props;
const arr = typeof value !== 'undefined'
? (isObject(value) ? Object.keys(value).map(key =>({
key: key,
value: value[key]
}))
: Array.isArray(value) ? value : []) : resolveVariable(name, data);
const arr =
typeof value !== 'undefined'
? isObject(value)
? Object.keys(value).map(key => ({
key: key,
value: value[key],
}))
: Array.isArray(value)
? value
: []
: resolveVariable(name, data);
return (
<div className={className}>
{Array.isArray(arr) && items ? arr.map((item:any, index:number) => render(`item/${index}`, items, {
data: createObject(data, isObject(item) ? item : {[name]: item, item: item}),
key: index
})) : null}
{Array.isArray(arr) && items
? arr.map((item: any, index: number) =>
render(`item/${index}`, items, {
data: createObject(data, isObject(item) ? item : {[name]: item, item: item}),
key: index,
})
)
: null}
</div>
);
}
@ -50,6 +46,6 @@ export default class Each extends React.Component<EachProps> {
@Renderer({
test: /(^|\/)(?:repeat|each)$/,
name: "each"
name: 'each',
})
export class EachRenderer extends Each { }
export class EachRenderer extends Each {}

View File

@ -1,15 +1,10 @@
import * as React from "react";
import { Renderer, RendererProps } from "../factory";
import { Schema } from "../types";
import * as cx from "classnames";
import pick = require("lodash/pick");
import * as React from 'react';
import {Renderer, RendererProps} from '../factory';
import {Schema} from '../types';
import * as cx from 'classnames';
import pick = require('lodash/pick');
export const ColProps = [
"lg",
"md",
"sm",
"xs"
];
export const ColProps = ['lg', 'md', 'sm', 'xs'];
export type Column = Schema & {
xs?: number;
@ -44,19 +39,19 @@ export interface ColumnArray extends Array<ColumnNode> {}
export interface GridProps extends RendererProps {
columns: Array<Column>;
itemRender?: (item: any, key:number, length:number, props:any) => JSX.Element;
itemRender?: (item: any, key: number, length: number, props: any) => JSX.Element;
}
function fromBsClass(cn:string) {
if (typeof cn === "string" && cn) {
function fromBsClass(cn: string) {
if (typeof cn === 'string' && cn) {
return cn.replace(/\bcol-(xs|sm|md|lg)-(\d+)\b/g, (_, bp, size) => `Grid-col--${bp}${size}`);
}
return cn;
}
function copProps2Class(props:any):string {
const cns:Array<string> = [];
function copProps2Class(props: any): string {
const cns: Array<string> = [];
const modifiers = ColProps;
modifiers.forEach(modifier => props && props[modifier] && cns.push(`Grid-col--${modifier}${props[modifier]}`));
@ -65,16 +60,13 @@ function copProps2Class(props:any):string {
}
export default class Grid<T> extends React.Component<GridProps & T, object> {
static propsList: Array<string> = ["columns"];
static propsList: Array<string> = ['columns'];
static defaultProps = {};
renderChild(region:string, node:Schema, key: number, length: number) {
const {
render,
itemRender
} = this.props;
renderChild(region: string, node: Schema, key: number, length: number) {
const {render, itemRender} = this.props;
return itemRender ? itemRender(node, key, length, this.props) : render(region, node);
return itemRender ? itemRender(node, key, length, this.props) : render(region, node);
}
renderColumn(column: ColumnNode, key: number, length: number) {
@ -83,34 +75,26 @@ export default class Grid<T> extends React.Component<GridProps & T, object> {
} = pick(column, ColProps);
colProps = {
...colProps
...colProps,
};
const cx = this.props.classnames;
return (
<div
key={key}
className={cx(copProps2Class(colProps), fromBsClass((column as Column).columnClassName))}
>
{Array.isArray(column)
? this.renderColumns(column)
: this.renderChild(`column/${key}`, column, key, length)}
<div key={key} className={cx(copProps2Class(colProps), fromBsClass((column as Column).columnClassName))}>
{Array.isArray(column)
? this.renderColumns(column)
: this.renderChild(`column/${key}`, column, key, length)}
</div>
);
}
renderColumns(columns: ColumnArray): React.ReactElement<any> | null {
const {
className,
classnames: cx
} = this.props;
const {className, classnames: cx} = this.props;
return (
<div className={cx('Grid', className)}>
{columns.map((column, key) =>
this.renderColumn(column, key, columns.length)
)}
{columns.map((column, key) => this.renderColumn(column, key, columns.length))}
</div>
);
}
@ -122,6 +106,6 @@ export default class Grid<T> extends React.Component<GridProps & T, object> {
@Renderer({
test: /(^|\/)grid$/,
name: 'grid'
name: 'grid',
})
export class GridRenderer extends Grid<{}> {}

View File

@ -1,9 +1,8 @@
import * as React from "react";
import { Renderer, RendererProps } from "../factory";
import { Api, SchemaNode, Schema, Action } from "../types";
import * as cx from "classnames";
import { isVisible } from "../utils/helper";
import * as React from 'react';
import {Renderer, RendererProps} from '../factory';
import {Api, SchemaNode, Schema, Action} from '../types';
import * as cx from 'classnames';
import {isVisible} from '../utils/helper';
export type Column = Schema & {
columnClassName?: string;
@ -12,28 +11,22 @@ export type Column = Schema & {
export interface HBoxProps extends RendererProps {
columns: Array<Column>;
className: string;
itemRender?: (item: any, key:number, length:number, props:any) => JSX.Element;
itemRender?: (item: any, key: number, length: number, props: any) => JSX.Element;
}
export default class HBox extends React.Component<HBoxProps, object> {
static propsList: Array<string> = ["columns"];
static propsList: Array<string> = ['columns'];
static defaultProps: Partial<HBoxProps> = {};
renderChild(region:string, node:Schema) {
const {
render
} = this.props;
renderChild(region: string, node: Schema) {
const {render} = this.props;
return render(region, node);
}
renderColumn(column: Column, key: number, length: number) {
const {
itemRender,
data,
classPrefix: ns
} = this.props;
const {itemRender, data, classPrefix: ns} = this.props;
if (!isVisible(column, data)) {
return null;
@ -42,32 +35,22 @@ export default class HBox extends React.Component<HBoxProps, object> {
let style = {
width: column.width,
height: column.height,
...column.style
...column.style,
};
return (
<div
key={key}
className={cx(`${ns}Hbox-col`, (column as Column).columnClassName)}
style={style}
>
<div key={key} className={cx(`${ns}Hbox-col`, (column as Column).columnClassName)} style={style}>
{itemRender ? itemRender(column, key, length, this.props) : this.renderChild(`column/${key}`, column)}
</div>
);
}
render() {
const {
className,
columns,
classPrefix: ns
} = this.props;
const {className, columns, classPrefix: ns} = this.props;
return (
<div className={cx(`${ns}Hbox`, className)}>
{columns.map((column, key) =>
this.renderColumn(column, key, columns.length)
)}
{columns.map((column, key) => this.renderColumn(column, key, columns.length))}
</div>
);
}
@ -75,6 +58,6 @@ export default class HBox extends React.Component<HBoxProps, object> {
@Renderer({
test: /(^|\/)hbox$/,
name: 'hbox'
name: 'hbox',
})
export class HBoxRenderer extends HBox {}

View File

@ -1,11 +1,6 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import {
filter
} from '../utils/tpl';
import {Renderer, RendererProps} from '../factory';
import {filter} from '../utils/tpl';
export interface IFrameProps extends RendererProps {
className?: string;
@ -13,30 +8,19 @@ export interface IFrameProps extends RendererProps {
}
export default class IFrame extends React.Component<IFrameProps, object> {
static propsList: Array<string> = [
"src",
"className"
];
static defaultProps:Partial<IFrameProps>= {
static propsList: Array<string> = ['src', 'className'];
static defaultProps: Partial<IFrameProps> = {
className: '',
width: '100%',
height: '100%',
frameBorder: 0
frameBorder: 0,
};
render() {
let {
className,
src,
width,
height,
frameBorder,
data,
style
} = this.props;
let {className, src, width, height, frameBorder, data, style} = this.props;
style = {
...style
...style,
};
width !== void 0 && (style.width = width);
@ -55,6 +39,6 @@ export default class IFrame extends React.Component<IFrameProps, object> {
@Renderer({
test: /(^|\/)iframe$/,
name: 'iframe'
name: 'iframe',
})
export class IFrameRenderer extends IFrame {};
export class IFrameRenderer extends IFrame {}

View File

@ -1,33 +1,32 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import {Renderer, RendererProps} from '../factory';
export interface IconProps extends RendererProps {
icon: string;
}
export class Icon extends React.Component<IconProps, object> {
static defaultProps:Partial<IconProps> = {
static defaultProps: Partial<IconProps> = {
icon: '',
vendor: 'fa'
vendor: 'fa',
};
render() {
const {
icon,
vendor,
classnames: cx,
className
} = this.props;
const {icon, vendor, classnames: cx, className} = this.props;
return (<i className={cx(vendor === 'iconfont' ? `iconfont icon-${icon}` : `${vendor} ${vendor}-${icon}`, className)} />);
return (
<i
className={cx(
vendor === 'iconfont' ? `iconfont icon-${icon}` : `${vendor} ${vendor}-${icon}`,
className
)}
/>
);
}
}
@Renderer({
test: /(^|\/)icon$/,
name: 'icon'
name: 'icon',
})
export class TplRenderer extends Icon {};
export class TplRenderer extends Icon {}

View File

@ -1,16 +1,8 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import {Renderer, RendererProps} from '../factory';
import {ServiceStore, IServiceStore} from '../store/service';
import {
Api,
SchemaNode
} from '../types';
import {
filter
} from '../utils/tpl';
import {Api, SchemaNode} from '../types';
import {filter} from '../utils/tpl';
import * as cx from 'classnames';
import * as moment from 'moment';
@ -22,10 +14,10 @@ export interface ImageProps extends RendererProps {
}
export class ImageField extends React.Component<ImageProps, object> {
static defaultProps:Partial<ImageProps> = {
static defaultProps: Partial<ImageProps> = {
className: 'thumb-lg',
imageClassName: 'r',
defaultImage: 'https://fex.bdstatic.com/n/static/amis/renderers/crud/field/placeholder_cfad9b1.png'
defaultImage: 'https://fex.bdstatic.com/n/static/amis/renderers/crud/field/placeholder_cfad9b1.png',
};
render() {
@ -38,7 +30,7 @@ export class ImageField extends React.Component<ImageProps, object> {
data,
imageClassName,
classnames: cx,
src
src,
} = this.props;
const finnalSrc = src ? filter(src) : '';
@ -48,8 +40,8 @@ export class ImageField extends React.Component<ImageProps, object> {
<div className={cx('ImageField', className)}>
<img className={imageClassName} src={finnalSrc || value || defaultImage} />
{title || description ? (
<div key="caption" className={cx("ImageField-caption")}>
{title ? (<div className="text-md">{filter(title, data)}</div>) : null}
<div key="caption" className={cx('ImageField-caption')}>
{title ? <div className="text-md">{filter(title, data)}</div> : null}
{render('description', description as string)}
</div>
) : null}
@ -60,21 +52,21 @@ export class ImageField extends React.Component<ImageProps, object> {
@Renderer({
test: /(^|\/)image$/,
name: 'image'
name: 'image',
})
export class ImageFieldRenderer extends ImageField {};
export class ImageFieldRenderer extends ImageField {}
@Renderer({
test: /(^|\/)images$/
test: /(^|\/)images$/,
})
export class ImagesFieldRenderer extends ImageField {
static defaultProps:Partial<ImageProps> = {
static defaultProps: Partial<ImageProps> = {
...ImageField.defaultProps,
multiple: true,
delimiter: ','
delimiter: ',',
};
render() {
return <p>Todo</p>
return <p>Todo</p>;
}
};
}

View File

@ -1,16 +1,8 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import {Renderer, RendererProps} from '../factory';
import {ServiceStore, IServiceStore} from '../store/service';
import {
Api,
SchemaNode
} from '../types';
import {
filter
} from '../utils/tpl';
import {Api, SchemaNode} from '../types';
import {filter} from '../utils/tpl';
import * as cx from 'classnames';
import JSONTree from 'react-json-tree';
@ -53,36 +45,34 @@ const twilight = {
WebkitUserSelect: 'none',
backgroundColor: 'rgba(255, 255, 255, 0.4)',
whiteSpace: 'nowrap',
display: 'inline-block'
}
}
display: 'inline-block',
},
};
export class JSONField extends React.Component<JSONProps, object> {
static defaultProps:Partial<JSONProps> = {
static defaultProps: Partial<JSONProps> = {
placeholder: '-',
levelExpand: 1
levelExpand: 1,
};
valueRenderer(raw:any) {
valueRenderer(raw: any) {
if (typeof raw === 'string' && /^\"?https?:\/\//.test(raw)) {
return (<a href={raw.replace(/^\"(.*)\"$/, '$1')} target="_blank">{raw}</a>);
return (
<a href={raw.replace(/^\"(.*)\"$/, '$1')} target="_blank">
{raw}
</a>
);
}
return raw;
}
shouldExpandNode = (keyName:any, data:any, level:any) => {
const {
levelExpand
} = this.props;
shouldExpandNode = (keyName: any, data: any, level: any) => {
const {levelExpand} = this.props;
return level < levelExpand;
};
render() {
const {
className,
value,
classnames: cx
} = this.props;
const {className, value, classnames: cx} = this.props;
let data = value;
@ -91,7 +81,7 @@ export class JSONField extends React.Component<JSONProps, object> {
data = JSON.parse(value);
} catch (e) {
data = {
error: e.message
error: e.message,
};
}
}
@ -111,6 +101,6 @@ export class JSONField extends React.Component<JSONProps, object> {
@Renderer({
test: /(^|\/)json$/,
name: 'json'
name: 'json',
})
export class JSONFieldRenderer extends JSONField {}
export class JSONFieldRenderer extends JSONField {}

View File

@ -1,11 +1,6 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import {
filter
} from '../utils/tpl';
import {Renderer, RendererProps} from '../factory';
import {filter} from '../utils/tpl';
export interface LinkProps extends RendererProps {
className?: string;
@ -17,26 +12,18 @@ export interface LinkProps extends RendererProps {
export class LinkField extends React.Component<LinkProps, object> {
static defaultProps = {
className: '',
blank: false
blank: false,
};
render() {
const {
className,
body,
href,
classnames: cx,
blank,
data,
render
} = this.props;
const {className, body, href, classnames: cx, blank, data, render} = this.props;
let value = this.props.value;
const finnalHref = href ? filter(href, data) : '';
return (
<a href={finnalHref || value} target={blank ? '_blank' : '_self'} className={cx('Link', className)}>
{body ? render('body', body) : (finnalHref || value || '链接')}
{body ? render('body', body) : finnalHref || value || '链接'}
</a>
);
}
@ -44,6 +31,6 @@ export class LinkField extends React.Component<LinkProps, object> {
@Renderer({
test: /(^|\/)link$/,
name: 'link'
name: 'link',
})
export class LinkFieldRenderer extends LinkField {};
export class LinkFieldRenderer extends LinkField {}

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +1,8 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import {Renderer, RendererProps} from '../factory';
import {ServiceStore, IServiceStore} from '../store/service';
import {
Api,
SchemaNode,
PlainObject
} from '../types';
import {
filter
} from '../utils/tpl';
import {Api, SchemaNode, PlainObject} from '../types';
import {filter} from '../utils/tpl';
import * as cx from 'classnames';
export interface MappingProps extends RendererProps {
@ -21,39 +12,28 @@ export interface MappingProps extends RendererProps {
}
export class MappingField extends React.Component<MappingProps, object> {
static defaultProps:Partial<MappingProps> = {
static defaultProps: Partial<MappingProps> = {
placeholder: '-',
map: {
'*': '通配值'
}
'*': '通配值',
},
};
render() {
const {
className,
value,
placeholder,
map,
render,
classnames: cx
} = this.props;
const {className, value, placeholder, map, render, classnames: cx} = this.props;
let viewValue:React.ReactNode = <span className="text-muted">{placeholder}</span>;
let viewValue: React.ReactNode = <span className="text-muted">{placeholder}</span>;
if (typeof value !== "undefined" && map && (map[value] || map['*'])) {
if (typeof value !== 'undefined' && map && (map[value] || map['*'])) {
viewValue = render('tpl', map[value] || map['*']);
}
return (
<span className={cx('MappingField', className)}>
{viewValue}
</span>
);
return <span className={cx('MappingField', className)}>{viewValue}</span>;
}
}
@Renderer({
test: /(^|\/)(?:map|mapping)$/,
name: 'mapping'
name: 'mapping',
})
export class MappingFieldRenderer extends MappingField {};
export class MappingFieldRenderer extends MappingField {}

View File

@ -1,17 +1,14 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import {
Renderer,
RendererProps
} from '../factory';
import { ServiceStore, IServiceStore } from '../store/service';
import {Renderer, RendererProps} from '../factory';
import {ServiceStore, IServiceStore} from '../store/service';
import * as cx from 'classnames';
import getExprProperties from '../utils/filter-schema';
import { filter, evalExpression } from '../utils/tpl';
import { createObject, mapTree, someTree } from '../utils/helper';
import { resolveVariable } from '../utils/tpl-builtin';
import { isApiOutdated } from '../utils/api';
import { ScopedContext, IScopedContext } from '../Scoped';
import {filter, evalExpression} from '../utils/tpl';
import {createObject, mapTree, someTree} from '../utils/helper';
import {resolveVariable} from '../utils/tpl-builtin';
import {isApiOutdated} from '../utils/api';
import {ScopedContext, IScopedContext} from '../Scoped';
export interface Link {
className?: string;
@ -21,14 +18,14 @@ export interface Link {
active?: boolean;
unfolded?: boolean;
children?: Links;
[propName:string]: any;
};
export interface Links extends Array<Link> {};
[propName: string]: any;
}
export interface Links extends Array<Link> {}
export interface NavigationState {
links: Links;
error?: string;
};
}
export interface NavigationProps extends RendererProps {
className?: string;
@ -39,37 +36,41 @@ export interface NavigationProps extends RendererProps {
export default class Navigation extends React.Component<NavigationProps, NavigationState> {
static defaultProps: Partial<NavigationProps> = {
togglerClassName: 'fa fa-angle-down'
togglerClassName: 'fa fa-angle-down',
};
mounted:boolean = true;
constructor(props:NavigationProps) {
mounted: boolean = true;
constructor(props: NavigationProps) {
super(props);
this.renderItem = this.renderItem.bind(this);
this.state = {
links: this.syncLinks(props, props.source && /^\$(?:([a-z0-9_.]+)|{.+})$/.test(props.source) && resolveVariable(props.source, props.data) || props.links)
links: this.syncLinks(
props,
(props.source &&
/^\$(?:([a-z0-9_.]+)|{.+})$/.test(props.source) &&
resolveVariable(props.source, props.data)) ||
props.links
),
};
}
componentDidMount() {
const {
source
} = this.props;
const {source} = this.props;
if (source && !/^\$(?:([a-z0-9_.]+)|{.+})$/.test(source)) {
this.reload();
}
}
componentWillReceiveProps(nextProps:NavigationProps) {
componentWillReceiveProps(nextProps: NavigationProps) {
const props = this.props;
if (nextProps.source && /^\$(?:([a-z0-9_.]+)|{.+})$/.test(nextProps.source)) {
if (nextProps.source !== props.source) {
this.setState({
links: this.syncLinks(nextProps)
links: this.syncLinks(nextProps),
});
} else {
const links = resolveVariable(nextProps.source, nextProps.data);
@ -77,22 +78,22 @@ export default class Navigation extends React.Component<NavigationProps, Navigat
if (links !== prevLinks) {
this.setState({
links: this.syncLinks(nextProps, links)
links: this.syncLinks(nextProps, links),
});
}
}
} else if (props.links !== nextProps.links) {
this.setState({
links: this.syncLinks(nextProps)
links: this.syncLinks(nextProps),
});
} else if (nextProps.location && props.location !== nextProps.location) {
this.setState({
links: this.syncLinks(nextProps, this.state.links, true)
links: this.syncLinks(nextProps, this.state.links, true),
});
}
}
componentDidUpdate(prevProps:NavigationProps) {
componentDidUpdate(prevProps: NavigationProps) {
const props = this.props;
if (props.source && !/^\$(?:([a-z0-9_.]+)|{.+})$/.test(props.source)) {
@ -104,20 +105,15 @@ export default class Navigation extends React.Component<NavigationProps, Navigat
this.mounted = false;
}
reload(target?:string, query?:any, values?:object) {
reload(target?: string, query?: any, values?: object) {
if (query) {
return this.receive(query);
}
const {
data,
env,
source
} = this.props;
const {data, env, source} = this.props;
const finalData = values ? createObject(data, values) : data;
if (!source || source.sendOn && !evalExpression(source.sendOn, data)) {
if (!source || (source.sendOn && !evalExpression(source.sendOn, data))) {
return;
}
@ -126,118 +122,126 @@ export default class Navigation extends React.Component<NavigationProps, Navigat
if (!this.mounted) {
return;
}
if (!payload.ok) {
this.setState({
error: payload.msg || '获取链接错误'
error: payload.msg || '获取链接错误',
});
} else {
const links = Array.isArray(payload.data) ? payload.data : (payload.data.links || payload.data.options || payload.data.items || payload.data.rows);
const links = Array.isArray(payload.data)
? payload.data
: payload.data.links || payload.data.options || payload.data.items || payload.data.rows;
if (!Array.isArray(links)) {
throw new Error('payload.data.options is not array.')
throw new Error('payload.data.options is not array.');
}
this.setState({
links: this.syncLinks(this.props, links, true)
}, () => {
if (payload.data && payload.data.value && !someTree(this.state.links, (item:any) => item.active)) {
env.jumpTo(filter(payload.data.value as string, data))
this.setState(
{
links: this.syncLinks(this.props, links, true),
},
() => {
if (
payload.data &&
payload.data.value &&
!someTree(this.state.links, (item: any) => item.active)
) {
env.jumpTo(filter(payload.data.value as string, data));
}
}
});
);
}
})
.catch(e => this.mounted && this.setState({
error: e.message
}));
.catch(
e =>
this.mounted &&
this.setState({
error: e.message,
})
);
}
receive(values: object) {
const {
store,
initApi
} = this.props;
const {store, initApi} = this.props;
this.reload(undefined, undefined, values);
}
syncLinks(props:NavigationProps, links = props.links, clearActive?: boolean):Links {
const {
data,
env
} = props;
syncLinks(props: NavigationProps, links = props.links, clearActive?: boolean): Links {
const {data, env} = props;
if (!Array.isArray(links) || !links.length) {
return [];
}
return mapTree(links, (link:Link) => {
return {
...link,
...getExprProperties(link, data as object),
active: !clearActive && link.active || !!(link.hasOwnProperty('to') && env && env.isCurrentUrl(filter(link.to as string, data))),
unfolded: link.unfolded || link.children && link.children.some(link => !!link.active)
}
}, 1, true);
return mapTree(
links,
(link: Link) => {
return {
...link,
...getExprProperties(link, data as object),
active:
(!clearActive && link.active) ||
!!(link.hasOwnProperty('to') && env && env.isCurrentUrl(filter(link.to as string, data))),
unfolded: link.unfolded || (link.children && link.children.some(link => !!link.active)),
};
},
1,
true
);
}
handleClick(link:{
label?: string;
to?: string;
icon?: string;
children?: Links;
}) {
handleClick(link: {label?: string; to?: string; icon?: string; children?: Links}) {
if (!link.to) {
link.children && link.children.length && this.toggleLink(link);
return;
}
const {
env,
data
} = this.props;
const {env, data} = this.props;
env && env.jumpTo(filter(link.to as string, data), link as any);
}
toggleLink(target:Link) {
toggleLink(target: Link) {
this.setState({
links: mapTree(this.state.links, (link:Link) => target === link ? {
...link,
unfolded: !link.unfolded
} : link)
links: mapTree(this.state.links, (link: Link) =>
target === link
? {
...link,
unfolded: !link.unfolded,
}
: link
),
});
}
renderItem(link:Link, index:number) {
renderItem(link: Link, index: number) {
if (link.hidden === true || link.visible === false) {
return null;
}
const isActive:boolean = !!link.active;
const {
disabled,
togglerClassName,
classnames: cx
} = this.props;
const isActive: boolean = !!link.active;
const {disabled, togglerClassName, classnames: cx} = this.props;
return (
<li
<li
key={index}
className={cx('Nav-item', link.className, {
'is-disabled': disabled || link.disabled,
'is-active': isActive,
'is-unfolded': link.unfolded
'is-unfolded': link.unfolded,
})}
>
<a onClick={this.handleClick.bind(this, link)}>
{link.icon ? (<i className={cx('Nav-itemIcon', link.icon)} />) : null}
{link.icon ? <i className={cx('Nav-itemIcon', link.icon)} /> : null}
{link.label}
</a>
{link.children && link.children.length ? (<i onClick={() => this.toggleLink(link)} className={cx('Nav-itemToggler', togglerClassName)} />) : null}
{link.children && link.children.length ? (
<i onClick={() => this.toggleLink(link)} className={cx('Nav-itemToggler', togglerClassName)} />
) : null}
{link.children && link.children.length ? (
<ul className={cx("Nav-subItems")}>
<ul className={cx('Nav-subItems')}>
{link.children.map((link, index) => this.renderItem(link, index))}
</ul>
) : null}
@ -245,19 +249,13 @@ export default class Navigation extends React.Component<NavigationProps, Navigat
);
}
render():JSX.Element {
const {
className,
stacked,
classnames: cx
} = this.props;
render(): JSX.Element {
const {className, stacked, classnames: cx} = this.props;
const links = this.state.links;
return (
<ul
className={cx('Nav', className, stacked ? 'Nav--stacked' : 'Nav--tabs')}
>
<ul className={cx('Nav', className, stacked ? 'Nav--stacked' : 'Nav--tabs')}>
{links.map(this.renderItem)}
</ul>
);
@ -266,7 +264,7 @@ export default class Navigation extends React.Component<NavigationProps, Navigat
@Renderer({
test: /(^|\/)(?:nav|navigation)$/,
name: 'nav'
name: 'nav',
})
export class NavigationRenderer extends Navigation {
static contextType = ScopedContext;
@ -281,4 +279,4 @@ export class NavigationRenderer extends Navigation {
scoped.unRegisterComponent(this);
super.componentWillUnmount();
}
}
}

View File

@ -1,17 +1,8 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import {Renderer, RendererProps} from '../factory';
import {ServiceStore, IServiceStore} from '../store/service';
import {
Api,
SchemaNode,
Action
} from '../types';
import {
filter
} from '../utils/tpl';
import {Api, SchemaNode, Action} from '../types';
import {filter} from '../utils/tpl';
import * as cx from 'classnames';
import * as moment from 'moment';
@ -21,43 +12,39 @@ export interface OperationProps extends RendererProps {
}
export class OperationField extends React.Component<OperationProps, object> {
static propsList: Array<string> = [
"buttons",
"label",
];
static propsList: Array<string> = ['buttons', 'label'];
static defaultProps:Partial<OperationProps> = {
};
static defaultProps: Partial<OperationProps> = {};
render() {
const {
className,
buttons,
render,
classnames: cx
} = this.props;
const {className, buttons, render, classnames: cx} = this.props;
return (
<div className={cx('OperationField', className)}>
{Array.isArray(buttons) ? buttons.map((button, index) => render(`${index}`, {
type: 'button',
size: button.size || 'sm',
level: button.level || (button.icon && !button.label ? 'link' : ''),
...button
}, {
key: index
})) : null}
{Array.isArray(buttons)
? buttons.map((button, index) =>
render(
`${index}`,
{
type: 'button',
size: button.size || 'sm',
level: button.level || (button.icon && !button.label ? 'link' : ''),
...button,
},
{
key: index,
}
)
)
: null}
</div>
);
}
}
@Renderer({
// test: /(^|\/)table\/(.*\/)operation$/,
test: (path: string) => /(^|\/)table\/(.*\/)operation$/.test(path),
name: 'operation'
name: 'operation',
})
export class OperationFieldRenderer extends OperationField {};
export class OperationFieldRenderer extends OperationField {}

View File

@ -1,28 +1,16 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import {
Renderer,
RendererProps
} from '../factory';
import { observer } from "mobx-react";
import { ServiceStore, IServiceStore } from '../store/service';
import {
Api,
SchemaNode,
Action,
Location,
ApiObject,
FunctionPropertyNames
} from '../types';
import {
filter, evalExpression
} from '../utils/tpl';
import {Renderer, RendererProps} from '../factory';
import {observer} from 'mobx-react';
import {ServiceStore, IServiceStore} from '../store/service';
import {Api, SchemaNode, Action, Location, ApiObject, FunctionPropertyNames} from '../types';
import {filter, evalExpression} from '../utils/tpl';
import * as cx from 'classnames';
import * as qs from 'qs';
import { isVisible, autobind, bulkBindFunctions } from '../utils/helper';
import { ScopedContext, IScopedContext } from '../Scoped';
import {isVisible, autobind, bulkBindFunctions} from '../utils/helper';
import {ScopedContext, IScopedContext} from '../Scoped';
import Alert from '../components/Alert2';
import { isApiOutdated } from '../utils/api';
import {isApiOutdated} from '../utils/api';
export interface PageProps extends RendererProps {
title?: string; // 标题
@ -57,7 +45,7 @@ export interface PageProps extends RendererProps {
export default class Page extends React.Component<PageProps> {
timer: NodeJS.Timer;
mounted: boolean;
static defaultProps = {
asideClassName: '',
bodyClassName: '',
@ -65,8 +53,7 @@ export default class Page extends React.Component<PageProps> {
initFetch: true,
// primaryField: 'id',
toolbarClassName: '',
messages: {
},
messages: {},
};
static propsList: Array<string> = [
@ -83,27 +70,23 @@ export default class Page extends React.Component<PageProps> {
'body',
'aside',
'messages',
'style'
'style',
];
componentWillMount() {
const {
store,
location
} = this.props;
const {store, location} = this.props;
// autobind 会让继承里面的 super 指向有问题,所以先这样!
bulkBindFunctions<Page/*为毛 this 的类型自动识别不出来*/>(this, [
"handleAction",
"handleDialogConfirm",
"handleDialogClose",
"handleDrawerConfirm",
"handleDrawerClose",
"handleClick",
"reload",
"silentReload",
"initInterval"
bulkBindFunctions<Page /*为毛 this 的类型自动识别不出来*/>(this, [
'handleAction',
'handleDialogConfirm',
'handleDialogClose',
'handleDrawerConfirm',
'handleDrawerClose',
'handleClick',
'reload',
'silentReload',
'initInterval',
]);
if (location && location.search) {
@ -124,20 +107,21 @@ export default class Page extends React.Component<PageProps> {
}
componentDidMount() {
const {
initApi,
initFetch,
store,
messages
} = this.props;
const {initApi, initFetch, store, messages} = this.props;
this.mounted = true;
if (initApi && initFetch && (!(initApi as ApiObject).sendOn || evalExpression((initApi as ApiObject).sendOn as string, store.data))) {
store.fetchInitData(initApi, store.data, {
successMessage: messages && messages.fetchSuccess,
errorMessage: messages && messages.fetchFailed
}).then(this.initInterval);
if (
initApi &&
initFetch &&
(!(initApi as ApiObject).sendOn || evalExpression((initApi as ApiObject).sendOn as string, store.data))
) {
store
.fetchInitData(initApi, store.data, {
successMessage: messages && messages.fetchSuccess,
errorMessage: messages && messages.fetchFailed,
})
.then(this.initInterval);
}
}
@ -145,11 +129,11 @@ export default class Page extends React.Component<PageProps> {
const props = this.props;
const store = props.store;
if (
nextProps.location
&& (!props.location || props.location.search !== nextProps.location.search)
) {
const query = nextProps.location.query || nextProps.location.search && qs.parse(nextProps.location.search.substring(1)) || {};
if (nextProps.location && (!props.location || props.location.search !== nextProps.location.search)) {
const query =
nextProps.location.query ||
(nextProps.location.search && qs.parse(nextProps.location.search.substring(1))) ||
{};
store.updateData({
...query,
query: query,
@ -164,17 +148,18 @@ export default class Page extends React.Component<PageProps> {
if (
// 前一次不构成条件,这次更新构成了条件,则需要重新拉取
props.initFetchOn && props.initFetch && !prevProps.initFetch
(props.initFetchOn && props.initFetch && !prevProps.initFetch) ||
// 构成了条件,同时 url 里面有变量,且上次和这次还不一样,则需要重新拉取。
|| props.initFetch !== false && isApiOutdated(prevProps.initApi, initApi, prevProps.data, props.data)
(props.initFetch !== false && isApiOutdated(prevProps.initApi, initApi, prevProps.data, props.data))
) {
const messages = props.messages;
(!(initApi as ApiObject).sendOn || evalExpression((initApi as ApiObject).sendOn as string, store.data))
&& store.fetchData(initApi as Api, store.data, {
successMessage: messages && messages.fetchSuccess,
errorMessage: messages && messages.fetchFailed
}).then(this.initInterval);
(!(initApi as ApiObject).sendOn || evalExpression((initApi as ApiObject).sendOn as string, store.data)) &&
store
.fetchData(initApi as Api, store.data, {
successMessage: messages && messages.fetchSuccess,
errorMessage: messages && messages.fetchFailed,
})
.then(this.initInterval);
}
}
@ -183,16 +168,12 @@ export default class Page extends React.Component<PageProps> {
clearTimeout(this.timer);
}
reloadTarget(target:string, data?:any) {
reloadTarget(target: string, data?: any) {
// 会被覆写
}
handleAction(e:React.UIEvent<any> | void, action:Action, ctx: object, delegate?: boolean) {
const {
env,
store,
messages
} = this.props;
handleAction(e: React.UIEvent<any> | void, action: Action, ctx: object, delegate?: boolean) {
const {env, store, messages} = this.props;
// delegate 表示不是当前层的事件,而是孩子节点的。
delegate || store.setCurrentAction(action);
@ -208,28 +189,27 @@ export default class Page extends React.Component<PageProps> {
} else if (action.actionType === 'drawer') {
store.openDrawer(ctx);
} else if (action.actionType === 'ajax') {
store.saveRemote(action.api as string, ctx, {
successMessage: action.messages && action.messages.success || messages && messages.saveSuccess,
errorMessage: action.messages && action.messages.failed || messages && messages.saveSuccess
})
.then(async () => {
if (action.feedback && isVisible(action.feedback, store.data)) {
await this.openFeedback(action.feedback, store.data);
}
store
.saveRemote(action.api as string, ctx, {
successMessage: (action.messages && action.messages.success) || (messages && messages.saveSuccess),
errorMessage: (action.messages && action.messages.failed) || (messages && messages.saveSuccess),
})
.then(async () => {
if (action.feedback && isVisible(action.feedback, store.data)) {
await this.openFeedback(action.feedback, store.data);
}
action.redirect && env.jumpTo(filter(action.redirect, store.data), action);
action.reload && this.reloadTarget(action.reload, store.data);
})
.catch(() => { });;
action.redirect && env.jumpTo(filter(action.redirect, store.data), action);
action.reload && this.reloadTarget(action.reload, store.data);
})
.catch(() => {});
} else if (action.actionType === 'copy' && (action.content || action.copy)) {
env.copy && env.copy(filter(action.content || action.copy, ctx));
}
}
handleDialogConfirm(values: object[], action: Action, ...args: Array<any>) {
const {
store
} = this.props;
const {store} = this.props;
if (action.mergeData && values.length === 1 && values[0]) {
store.updateData(values[0]);
@ -243,18 +223,13 @@ export default class Page extends React.Component<PageProps> {
store.closeDialog();
}
handleDialogClose() {
const {
store
} = this.props;
const {store} = this.props;
store.closeDialog();
}
handleDrawerConfirm(values: object[], action: Action, ...args:Array<any>) {
const {
store
} = this.props;
handleDrawerConfirm(values: object[], action: Action, ...args: Array<any>) {
const {store} = this.props;
if (action.mergeData && values.length === 1 && values[0]) {
store.updateData(values[0]);
@ -269,80 +244,68 @@ export default class Page extends React.Component<PageProps> {
}
handleDrawerClose() {
const {
store
} = this.props;
const {store} = this.props;
store.closeDrawer();
}
handleClick(e: any) {
const target: HTMLElement = e.target as HTMLElement;
const { env } = this.props;
const {env} = this.props;
if (env && target.tagName === 'A' && target.hasAttribute('data-link')) {
env.jumpTo(target.getAttribute('data-link') as string);
e.preventDefault();
}
};
}
openFeedback(dialog:any, ctx:any) {
return new Promise((resolve) => {
const {
store
} = this.props;
openFeedback(dialog: any, ctx: any) {
return new Promise(resolve => {
const {store} = this.props;
store.setCurrentAction({
type: 'button',
actionType: 'dialog',
dialog: dialog
dialog: dialog,
});
store.openDialog(ctx, undefined, (confirmed) => {
resolve(confirmed)
store.openDialog(ctx, undefined, confirmed => {
resolve(confirmed);
});
})
});
}
reload(subpath?: any, query?:any, ctx?: any, silent?: boolean) {
reload(subpath?: any, query?: any, ctx?: any, silent?: boolean) {
if (query) {
return this.receive(query);
}
const {
store,
initApi
} = this.props;
const {store, initApi} = this.props;
clearTimeout(this.timer);
initApi && store.fetchData(initApi, store.data, {
silent
}).then(this.initInterval);
initApi &&
store
.fetchData(initApi, store.data, {
silent,
})
.then(this.initInterval);
}
receive(values: object) {
const {
store
} = this.props;
const {store} = this.props;
store.updateData(values);
this.reload();
}
silentReload(target?:string, query?:any) {
silentReload(target?: string, query?: any) {
this.reload(query, undefined, undefined, true);
}
initInterval(value: any) {
const {
interval,
silentPolling,
stopAutoRefreshWhen,
data
} = this.props;
const {interval, silentPolling, stopAutoRefreshWhen, data} = this.props;
interval
&& this.mounted
&& (!stopAutoRefreshWhen || !evalExpression(stopAutoRefreshWhen, data))
&& (this.timer = setTimeout(silentPolling ? this.silentReload : this.reload, Math.max(interval, 3000)));
interval &&
this.mounted &&
(!stopAutoRefreshWhen || !evalExpression(stopAutoRefreshWhen, data)) &&
(this.timer = setTimeout(silentPolling ? this.silentReload : this.reload, Math.max(interval, 3000)));
return value;
}
@ -357,11 +320,11 @@ export default class Page extends React.Component<PageProps> {
render,
store,
env,
classnames: cx
classnames: cx,
} = this.props;
const subProps = {
onAction: this.handleAction
onAction: this.handleAction,
};
let header, right;
@ -371,24 +334,24 @@ export default class Page extends React.Component<PageProps> {
{title ? (
<h2 className={cx('Page-title')}>
{render('title', title, subProps)}
{remark ? render('remark', {
type: 'remark',
tooltip: remark,
container: env && env.getModalContainer ? env.getModalContainer() : undefined
}) : null}
{remark
? render('remark', {
type: 'remark',
tooltip: remark,
container: env && env.getModalContainer ? env.getModalContainer() : undefined,
})
: null}
</h2>
) : null}
{subTitle && (<small className={cx('Page-subTitle')}>{render('subTitle', subTitle, subProps)}</small>)}
{subTitle && (
<small className={cx('Page-subTitle')}>{render('subTitle', subTitle, subProps)}</small>
)}
</div>
);
}
if (toolbar) {
right = (
<div className={cx(`Page-toolbar`, toolbarClassName)}>
{render('toolbar', toolbar, subProps)}
</div>
);
right = <div className={cx(`Page-toolbar`, toolbarClassName)}>{render('toolbar', toolbar, subProps)}</div>;
}
if (header && right) {
@ -417,42 +380,42 @@ export default class Page extends React.Component<PageProps> {
} = this.props;
const subProps = {
onAction: this.handleAction
onAction: this.handleAction,
};
const hasAside = aside && (!Array.isArray(aside) || aside.length);
return (
<div
className={cx(`Page`, hasAside ? `Page--withSidebar` : '', className)}
onClick={this.handleClick}
>
{hasAside ? <div className={cx(`Page-aside`, asideClassName)}>
{render('aside', aside as any, {
...subProps,
...(typeof aside === 'string' ? {
inline: false,
className: `Page-asideTplWrapper`
} : null)
})}
</div> : null}
<div className={cx(`Page`, hasAside ? `Page--withSidebar` : '', className)} onClick={this.handleClick}>
{hasAside ? (
<div className={cx(`Page-aside`, asideClassName)}>
{render('aside', aside as any, {
...subProps,
...(typeof aside === 'string'
? {
inline: false,
className: `Page-asideTplWrapper`,
}
: null),
})}
</div>
) : null}
<div className={cx('Page-content')}>
{header ? render('header', header, subProps) : null}
<div className={cx('Page-main')}>
{this.renderHeader()}
<div className={cx(`Page-body`, bodyClassName)}>
{store.loading ? render('spinner', {
type: 'spinner',
overlay: true,
size: 'lg'
}) : null}
{store.loading
? render('spinner', {
type: 'spinner',
overlay: true,
size: 'lg',
})
: null}
{store.error ? (
<Alert
level="danger"
showCloseButton
onClose={store.clearMessage}>
<Alert level="danger" showCloseButton onClose={store.clearMessage}>
{store.msg}
</Alert>
) : null}
@ -462,39 +425,47 @@ export default class Page extends React.Component<PageProps> {
</div>
</div>
{render('dialog', {
...(store.action as Action) && (store.action as Action).dialog as object,
type: 'dialog'
}, {
key: 'dialog',
data: store.dialogData,
onConfirm: this.handleDialogConfirm,
onClose: this.handleDialogClose,
show: store.dialogOpen,
onAction: this.handleAction
})}
{render(
'dialog',
{
...((store.action as Action) && ((store.action as Action).dialog as object)),
type: 'dialog',
},
{
key: 'dialog',
data: store.dialogData,
onConfirm: this.handleDialogConfirm,
onClose: this.handleDialogClose,
show: store.dialogOpen,
onAction: this.handleAction,
}
)}
{render('drawer', {
...(store.action as Action) && (store.action as Action).drawer as object,
type: 'drawer'
}, {
key: 'drawer',
data: store.drawerData,
onConfirm: this.handleDrawerConfirm,
onClose: this.handleDrawerClose,
show: store.drawerOpen,
onAction: this.handleAction
})}
{render(
'drawer',
{
...((store.action as Action) && ((store.action as Action).drawer as object)),
type: 'drawer',
},
{
key: 'drawer',
data: store.drawerData,
onConfirm: this.handleDrawerConfirm,
onClose: this.handleDrawerClose,
show: store.drawerOpen,
onAction: this.handleAction,
}
)}
</div>
);
}
};
}
@Renderer({
test: /(?:^|\/)page$/,
name: 'page',
storeType: ServiceStore.name,
isolateScope: true
isolateScope: true,
})
export class PageRenderer extends Page {
static contextType = ScopedContext;
@ -512,12 +483,12 @@ export class PageRenderer extends Page {
super.componentWillUnmount();
}
reloadTarget(target:string, data?:any) {
reloadTarget(target: string, data?: any) {
const scoped = this.context as IScopedContext;
scoped.reload(target, data);
}
handleAction(e:React.UIEvent<any>, action:Action, ctx:object, throwErrors: boolean = false, delegate?: boolean) {
handleAction(e: React.UIEvent<any>, action: Action, ctx: object, throwErrors: boolean = false, delegate?: boolean) {
const scoped = this.context as IScopedContext;
if (action.actionType === 'reload') {
@ -525,10 +496,15 @@ export class PageRenderer extends Page {
} else if (action.target) {
action.target.split(',').forEach(name => {
let target = scoped.getComponentByName(name);
target && target.doAction && target.doAction({
...action,
target: undefined
}, ctx);
target &&
target.doAction &&
target.doAction(
{
...action,
target: undefined,
},
ctx
);
});
} else {
super.handleAction(e, action, ctx, delegate);
@ -552,7 +528,6 @@ export class PageRenderer extends Page {
.filter((item: any) => item.props.type === 'crud')
.forEach((item: any) => item.reload && item.reload());
}
}
handleDrawerConfirm(values: object[], action: Action, ...rest: Array<any>) {
@ -576,4 +551,4 @@ export class PageRenderer extends Page {
}
}, 300);
}
};
}

View File

@ -1,8 +1,5 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import {Renderer, RendererProps} from '../factory';
export interface PaginationProps extends RendererProps {
activePage?: number;
@ -14,7 +11,7 @@ export interface PaginationProps extends RendererProps {
pageNum?: number;
changePageNum: (value: number) => void;
showPageInput: boolean;
};
}
export interface DefaultProps {
activePage: number;
@ -34,8 +31,8 @@ export default class Pagination extends React.PureComponent<PaginationProps, any
maxButtons: 5,
mode: 'normal',
hasNext: false,
showPageInput: true
}
showPageInput: true,
};
constructor(props: PaginationProps) {
super(props);
@ -43,43 +40,39 @@ export default class Pagination extends React.PureComponent<PaginationProps, any
}
renderSimple() {
const {
activePage,
hasNext,
onPageChange,
classnames: cx
} = this.props as PropsWithDefault;
const {activePage, hasNext, onPageChange, classnames: cx} = this.props as PropsWithDefault;
return (
<ul className={cx("Pagination", "Pagination--sm")}>
<ul className={cx('Pagination', 'Pagination--sm')}>
<li
className={cx({
disabled: activePage < 2
disabled: activePage < 2,
})}
onClick={activePage < 2 ? e => e.preventDefault() : () => onPageChange(activePage - 1)}
>
<a><i className="fa fa-chevron-left" /></a>
<a>
<i className="fa fa-chevron-left" />
</a>
</li>
<li
className={cx({
disabled: !hasNext
disabled: !hasNext,
})}
onClick={!hasNext ? e => e.preventDefault() : () => onPageChange(activePage + 1)}
>
<a><i className="fa fa-chevron-right" /></a>
<a>
<i className="fa fa-chevron-right" />
</a>
</li>
</ul>
);
}
handlePageChange(e: React.ChangeEvent<any>) {
const {
changePageNum,
items
} = this.props;
const {changePageNum, items} = this.props;
let value = e.currentTarget.value;
if ((typeof value === 'number' || /^\d+$/.test(value)) && value > 0 || value === '') {
if (((typeof value === 'number' || /^\d+$/.test(value)) && value > 0) || value === '') {
if (value !== '') {
value = parseInt(value, 10);
value = (value > (items as number) ? items : value) as number;
@ -89,15 +82,8 @@ export default class Pagination extends React.PureComponent<PaginationProps, any
}
renderNormal() {
let {
activePage,
items,
maxButtons,
onPageChange,
pageNum,
classnames: cx,
showPageInput
} = this.props as PropsWithDefault;
let {activePage, items, maxButtons, onPageChange, pageNum, classnames: cx, showPageInput} = this
.props as PropsWithDefault;
let pageButtons: any = [];
let startPage: number;
@ -112,13 +98,7 @@ export default class Pagination extends React.PureComponent<PaginationProps, any
}
if (maxButtons && maxButtons < items) {
startPage = Math.max(
Math.min(
activePage - Math.floor(maxButtons / 2),
items - maxButtons + 1
),
1
);
startPage = Math.max(Math.min(activePage - Math.floor(maxButtons / 2), items - maxButtons + 1), 1);
endPage = startPage + maxButtons - 1;
} else {
startPage = 1;
@ -127,9 +107,13 @@ export default class Pagination extends React.PureComponent<PaginationProps, any
for (let page = startPage; page <= endPage; ++page) {
pageButtons.push(
<li onClick={() => onPageChange(page)} key={page} className={cx({
active: page === activePage
})}>
<li
onClick={() => onPageChange(page)}
key={page}
className={cx({
active: page === activePage,
})}
>
<a role="button">{page}</a>
</li>
);
@ -138,16 +122,20 @@ export default class Pagination extends React.PureComponent<PaginationProps, any
if (startPage > 1) {
if (startPage > 2) {
pageButtons.unshift(
<li onClick={() => onPageChange(startPage - 1)} key="prev-ellipsis" >
<li onClick={() => onPageChange(startPage - 1)} key="prev-ellipsis">
<a role="button">...</a>
</li>
);
}
pageButtons.unshift(
<li onClick={() => onPageChange(1)} key={1} className={cx({
active: 1 === activePage
})}>
<li
onClick={() => onPageChange(1)}
key={1}
className={cx({
active: 1 === activePage,
})}
>
<a role="button">{1}</a>
</li>
);
@ -156,16 +144,26 @@ export default class Pagination extends React.PureComponent<PaginationProps, any
if (endPage < items) {
if (items - endPage > 1) {
pageButtons.push(
<li className={cx("Pagination-ellipsis")} onClick={() => onPageChange(endPage + 1)} key="next-ellipsis" >
<a role="button"><span>...</span></a>
<li
className={cx('Pagination-ellipsis')}
onClick={() => onPageChange(endPage + 1)}
key="next-ellipsis"
>
<a role="button">
<span>...</span>
</a>
</li>
);
}
pageButtons.push(
<li onClick={() => onPageChange(items)} key={items} className={cx({
active: items === activePage
})}>
<li
onClick={() => onPageChange(items)}
key={items}
className={cx({
active: items === activePage,
})}
>
<a role="button">{items}</a>
</li>
);
@ -173,59 +171,65 @@ export default class Pagination extends React.PureComponent<PaginationProps, any
pageButtons.unshift(
<li
className={cx("Pagination-prev", {
disabled: activePage === 1
className={cx('Pagination-prev', {
disabled: activePage === 1,
})}
onClick={activePage === 1 ? (e: any) => e.preventDefault() : () => onPageChange(activePage - 1)}
key="prev">
<span></span>
key="prev"
>
<span />
</li>
);
pageButtons.push(
<li
className={cx("Pagination-next", {
disabled: activePage === items
className={cx('Pagination-next', {
disabled: activePage === items,
})}
onClick={activePage === items ? (e: any) => e.preventDefault() : () => onPageChange(activePage + 1)}
key="next">
<span></span>
key="next"
>
<span />
</li>
);
return (
<div>
<ul
className={cx("Pagination", "Pagination--sm")}
onSelect={(value: number) => onPageChange(value)}
>
<ul className={cx('Pagination', 'Pagination--sm')} onSelect={(value: number) => onPageChange(value)}>
{pageButtons}
</ul>
{items > 9 && showPageInput ? (
<div className="inline m-l-xs w-xs" key="toPage">
<span className={cx("Pagination-inputGroup")}>
<input type="text" className={cx("Pagination-input")}
<span className={cx('Pagination-inputGroup')}>
<input
type="text"
className={cx('Pagination-input')}
onChange={this.handlePageChange}
onFocus={(e: any) => e.currentTarget.select()}
onKeyUp={(e: any) => e.keyCode == 13 && onPageChange(parseInt(e.currentTarget.value, 10))}
onKeyUp={(e: any) =>
e.keyCode == 13 && onPageChange(parseInt(e.currentTarget.value, 10))
}
value={pageNum}
/>
<span>
<button onClick={() => onPageChange(pageNum as number)} type="submit" className={cx('Button', 'Button--default')}>Go</button>
<button
onClick={() => onPageChange(pageNum as number)}
type="submit"
className={cx('Button', 'Button--default')}
>
Go
</button>
</span>
</span>
</div>
) : null}
</div>
);
}
render() {
const {
mode
} = this.props;
const {mode} = this.props;
return mode === 'simple' ? this.renderSimple() : this.renderNormal();
}
@ -233,6 +237,6 @@ export default class Pagination extends React.PureComponent<PaginationProps, any
@Renderer({
test: /(^|\/)pagination$/,
name: 'pagination'
name: 'pagination',
})
export class PaginationRenderer extends Pagination { }
export class PaginationRenderer extends Pagination {}

View File

@ -1,14 +1,8 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import {
SchemaNode,
Action
} from '../types';
import { getScrollParent } from '../utils/helper';
import { findDOMNode } from 'react-dom';
import {Renderer, RendererProps} from '../factory';
import {SchemaNode, Action} from '../types';
import {getScrollParent} from '../utils/helper';
import {findDOMNode} from 'react-dom';
export interface PanelProps extends RendererProps {
title?: string; // 标题
@ -26,12 +20,7 @@ export interface PanelProps extends RendererProps {
}
export default class Panel extends React.Component<PanelProps> {
static propsList: Array<string> = [
"headerClassName",
"footerClassName",
"actionsClassName",
"bodyClassName"
];
static propsList: Array<string> = ['headerClassName', 'footerClassName', 'actionsClassName', 'bodyClassName'];
static defaultProps = {
// className: 'Panel--default',
// headerClassName: 'Panel-heading',
@ -46,7 +35,7 @@ export default class Panel extends React.Component<PanelProps> {
componentDidMount() {
const dom = findDOMNode(this) as HTMLElement;
let parent:HTMLElement | Window | null = dom ? getScrollParent(dom) : null;
let parent: HTMLElement | Window | null = dom ? getScrollParent(dom) : null;
if (!parent || parent === document.body) {
parent = window;
}
@ -73,11 +62,11 @@ export default class Panel extends React.Component<PanelProps> {
const clip = footerDom.getBoundingClientRect();
const clientHeight = window.innerHeight;
const affixed = clip.top > clientHeight;
footerDom.offsetWidth && (affixDom.style.cssText = `width: ${footerDom.offsetWidth}px;`);
affixed ? affixDom.classList.add('in') : affixDom.classList.remove('in');
}
renderBody(): JSX.Element | null {
const {
type,
@ -100,27 +89,28 @@ export default class Panel extends React.Component<PanelProps> {
const subProps = {
data,
...rest
...rest,
};
return children ? (
<div className={bodyClassName || `${ns}Panel-body`}>{typeof children === 'function' ? children(this.props) : children}</div>
<div className={bodyClassName || `${ns}Panel-body`}>
{typeof children === 'function' ? children(this.props) : children}
</div>
) : body ? (
<div className={bodyClassName || `${ns}Panel-body`}>{render('body', body, subProps)}</div>
) : null;
}
renderActions() {
const {
actions,
render,
} = this.props;
const {actions, render} = this.props;
if (Array.isArray(actions) && actions.length) {
return actions.map((action, key) => render('action', action, {
type: action.type || 'button',
key: key
}));
return actions.map((action, key) =>
render('action', action, {
type: action.type || 'button',
key: key,
})
);
}
return null;
@ -149,38 +139,35 @@ export default class Panel extends React.Component<PanelProps> {
const subProps = {
data,
...rest
...rest,
};
const footerDoms = [];
const actions = this.renderActions();
actions && footerDoms.push(
<div key="actions" className={cx(`Panel-btnToolbar`, actionsClassName || `Panel-footer`)}>
{actions}
</div>
);
actions &&
footerDoms.push(
<div key="actions" className={cx(`Panel-btnToolbar`, actionsClassName || `Panel-footer`)}>
{actions}
</div>
);
footer && footerDoms.push(
<div key="footer" className={cx(footerClassName || `Panel-footer`)}>
{render('footer', footer, subProps)}
</div>
);
footer &&
footerDoms.push(
<div key="footer" className={cx(footerClassName || `Panel-footer`)}>
{render('footer', footer, subProps)}
</div>
);
let footerDom = footerDoms.length ? (
<div ref={this.footerDom}>
{footerDoms}
</div>
) : null;
let footerDom = footerDoms.length ? <div ref={this.footerDom}>{footerDoms}</div> : null;
return (
<div
className={cx(`Panel`, className || `Panel--default`)}
>
<div className={cx(`Panel`, className || `Panel--default`)}>
{header ? (
<div className={cx(headerClassName || `Panel-heading`)}>{render('header', header, subProps)}</div>
) : title ? (
<div className={cx(headerClassName || `Panel-heading`)}><h3 className={cx(`Panel-title`)}>{render('title', title, subProps)}</h3></div>
<div className={cx(headerClassName || `Panel-heading`)}>
<h3 className={cx(`Panel-title`)}>{render('title', title, subProps)}</h3>
</div>
) : null}
{this.renderBody()}
@ -188,7 +175,7 @@ export default class Panel extends React.Component<PanelProps> {
{footerDom}
{affixFooter && footerDoms.length ? (
<div ref={this.affixDom} className={cx("Panel-fixedBottom")}>
<div ref={this.affixDom} className={cx('Panel-fixedBottom')}>
{footerDoms}
</div>
) : null}
@ -199,6 +186,6 @@ export default class Panel extends React.Component<PanelProps> {
@Renderer({
test: /(^|\/)panel$/,
name: 'panel'
name: 'panel',
})
export class PanelRenderer extends Panel {}

View File

@ -1,11 +1,6 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import {
filter
} from '../utils/tpl';
import {Renderer, RendererProps} from '../factory';
import {filter} from '../utils/tpl';
import * as cx from 'classnames';
export interface PlainProps extends RendererProps {
@ -19,30 +14,26 @@ export interface PlainProps extends RendererProps {
}
export class Plain extends React.Component<PlainProps, object> {
static defaultProps:Partial<PlainProps> = {
static defaultProps: Partial<PlainProps> = {
wrapperComponent: '',
inline: true,
placeholder: '-'
placeholder: '-',
};
render() {
const {
className,
wrapperComponent,
value,
text,
data,
tpl,
inline,
placeholder,
classnames: cx
} = this.props;
const {className, wrapperComponent, value, text, data, tpl, inline, placeholder, classnames: cx} = this.props;
const Component = wrapperComponent || (inline ? 'span' : 'div');
return (
<Component className={cx('PlainField', className)}>
{tpl || text ? filter((tpl || text as string), data) : (typeof value === 'undefined' || value === '' || value === null ? <span className="text-muted">{placeholder}</span> : String(value))}
{tpl || text ? (
filter(tpl || (text as string), data)
) : typeof value === 'undefined' || value === '' || value === null ? (
<span className="text-muted">{placeholder}</span>
) : (
String(value)
)}
</Component>
);
}
@ -50,6 +41,6 @@ export class Plain extends React.Component<PlainProps, object> {
@Renderer({
test: /(^|\/)(?:plain|text)$/,
name: 'plain'
name: 'plain',
})
export class PlainRenderer extends Plain {};
export class PlainRenderer extends Plain {}

View File

@ -1,53 +1,57 @@
/**
* @file scoped.jsx.
* @author fex
*/
* @file scoped.jsx.
* @author fex
*/
import * as React from 'react';
import {findDOMNode} from 'react-dom';
import { RendererProps } from '../factory';
import {RendererProps} from '../factory';
import * as cx from 'classnames';
import hoistNonReactStatic = require('hoist-non-react-statics');
import { RootCloseWrapper} from 'react-overlays';
import {RootCloseWrapper} from 'react-overlays';
import PopOver from '../components/PopOver';
import Overlay from '../components/Overlay';
export interface PopOverConfig {
}
export interface PopOverConfig {}
const allowedPositions = [
'center',
'top'
];
const allowedPositions = ['center', 'top'];
export interface PopOverConfig {
saveImmediately?: boolean;
mode?: 'dialog' | 'drawer' | 'popOver';
title?: string;
size?: 'sm' | 'md' | 'lg' | 'xl';
position: 'center' | 'left-top' | 'right-top' | 'left-bottom' | 'right-bottom'
| 'fixed-center' | 'fixed-left-top' | 'fixed-right-top' | 'fixed-left-bottom' | 'fixed-right-bottom';
[propName:string]: any;
};
position:
| 'center'
| 'left-top'
| 'right-top'
| 'left-bottom'
| 'right-bottom'
| 'fixed-center'
| 'fixed-left-top'
| 'fixed-right-top'
| 'fixed-left-bottom'
| 'fixed-right-bottom';
[propName: string]: any;
}
export interface PopOverProps extends RendererProps {
name?: string;
label?: string;
popOver: boolean | PopOverConfig;
onPopOverOpen: (popover:any) => void;
onPopOverClose: (popover:any) => void;
};
onPopOverOpen: (popover: any) => void;
onPopOverClose: (popover: any) => void;
}
export interface PopOverState {
isOpened: boolean;
};
export const HocPopOver = (config:Partial<PopOverConfig> = {}) => (Component: React.ComponentType<any>):any => {
}
export const HocPopOver = (config: Partial<PopOverConfig> = {}) => (Component: React.ComponentType<any>): any => {
class PopOverComponent extends React.Component<PopOverProps, PopOverState> {
target:HTMLElement;
target: HTMLElement;
static ComposedComponent = Component;
constructor(props:PopOverProps) {
constructor(props: PopOverProps) {
super(props);
this.openPopOver = this.openPopOver.bind(this);
@ -55,19 +59,22 @@ export const HocPopOver = (config:Partial<PopOverConfig> = {}) => (Component: Re
this.targetRef = this.targetRef.bind(this);
// this.handleClickOutside = this.handleClickOutside.bind(this);
this.state = {
isOpened: false
isOpened: false,
};
}
targetRef(ref:any) {
targetRef(ref: any) {
this.target = ref;
}
openPopOver() {
const onPopOverOpen = this.props.onPopOverOpen;
this.setState({
isOpened: true
}, () => onPopOverOpen && onPopOverOpen(this.props.popOver));
this.setState(
{
isOpened: true,
},
() => onPopOverOpen && onPopOverOpen(this.props.popOver)
);
}
closePopOver() {
@ -76,24 +83,23 @@ export const HocPopOver = (config:Partial<PopOverConfig> = {}) => (Component: Re
}
const onPopOverClose = this.props.onPopOverClose;
this.setState({
isOpened: false
}, () => onPopOverClose && onPopOverClose(this.props.popOver));
this.setState(
{
isOpened: false,
},
() => onPopOverClose && onPopOverClose(this.props.popOver)
);
}
buildSchema() {
const {
popOver,
name,
label
} = this.props;
const {popOver, name, label} = this.props;
let schema;
if (popOver === true) {
schema = {
type: 'panel',
body: '${name}'
body: '${name}',
};
} else if (popOver && (popOver.mode === 'dialog' || popOver.mode === 'drawer')) {
schema = {
@ -102,15 +108,15 @@ export const HocPopOver = (config:Partial<PopOverConfig> = {}) => (Component: Re
{
label: '关闭',
type: 'button',
actionType: 'cancel'
}
actionType: 'cancel',
},
],
...popOver
...popOver,
};
} else if (popOver) {
schema = {
type: 'panel',
...popOver
...popOver,
};
}
@ -118,41 +124,33 @@ export const HocPopOver = (config:Partial<PopOverConfig> = {}) => (Component: Re
}
renderPopOver() {
let {
popOver,
render,
popOverContainer,
classnames: cx,
classPrefix: ns
} = this.props;
let {popOver, render, popOverContainer, classnames: cx, classPrefix: ns} = this.props;
if (popOver && ((popOver as PopOverConfig).mode === 'dialog'
|| (popOver as PopOverConfig).mode === 'drawer')) {
if (
popOver &&
((popOver as PopOverConfig).mode === 'dialog' || (popOver as PopOverConfig).mode === 'drawer')
) {
return render('popover-detail', this.buildSchema(), {
show: true,
onClose: this.closePopOver,
onConfirm: this.closePopOver
onConfirm: this.closePopOver,
});
}
const content = render('popover-detail', this.buildSchema(), {
className: cx((popOver as PopOverConfig).className)
className: cx((popOver as PopOverConfig).className),
}) as JSX.Element;
if (!popOverContainer) {
popOverContainer = () => findDOMNode(this);
}
const position = popOver && (popOver as PopOverConfig).position || '';
const position = (popOver && (popOver as PopOverConfig).position) || '';
const isFixed = /^fixed\-/.test(position);
return isFixed ? (
<RootCloseWrapper
disabled={!this.state.isOpened}
onRootClose={this.closePopOver}
><div className={cx(`PopOverAble--fixed PopOverAble--${position}`)}>
{content}
</div>
<RootCloseWrapper disabled={!this.state.isOpened} onRootClose={this.closePopOver}>
<div className={cx(`PopOverAble--fixed PopOverAble--${position}`)}>{content}</div>
</RootCloseWrapper>
) : (
<Overlay
@ -163,10 +161,7 @@ export const HocPopOver = (config:Partial<PopOverConfig> = {}) => (Component: Re
rootClose
show
>
<PopOver
classPrefix={ns}
className={cx("PopOverAble-popover")}
>
<PopOver classPrefix={ns} className={cx('PopOverAble-popover')}>
{content}
</PopOver>
</Overlay>
@ -174,31 +169,25 @@ export const HocPopOver = (config:Partial<PopOverConfig> = {}) => (Component: Re
}
render() {
const {
onQuickChange,
popOver,
popOverEnabled,
className,
noHoc,
classnames: cx,
render
} = this.props;
const {onQuickChange, popOver, popOverEnabled, className, noHoc, classnames: cx, render} = this.props;
if (!popOver || popOverEnabled === false || noHoc) {
return (
<Component {...this.props} />
);
return <Component {...this.props} />;
}
return (
<Component
<Component
{...this.props}
className={cx(`Field--popOverAble`, className, {
'in': this.state.isOpened
in: this.state.isOpened,
})}
>
<Component {...this.props} wrapperComponent={''} noHoc ref={this.targetRef} />
<i key="popover-btn" className={cx("Field-popOverBtn fa fa-search-plus")} onClick={this.openPopOver} />
<i
key="popover-btn"
className={cx('Field-popOverBtn fa fa-search-plus')}
onClick={this.openPopOver}
/>
{this.state.isOpened ? this.renderPopOver() : null}
</Component>
);

View File

@ -1,17 +1,8 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import {Renderer, RendererProps} from '../factory';
import {ServiceStore, IServiceStore} from '../store/service';
import {
Api,
SchemaNode,
PlainObject
} from '../types';
import {
filter
} from '../utils/tpl';
import {Api, SchemaNode, PlainObject} from '../types';
import {filter} from '../utils/tpl';
import * as cx from 'classnames';
export interface ProgressProps extends RendererProps {
@ -23,23 +14,17 @@ export interface ProgressProps extends RendererProps {
}
export class ProgressField extends React.Component<ProgressProps, object> {
static defaultProps:Partial<ProgressProps> = {
static defaultProps: Partial<ProgressProps> = {
placeholder: '-',
progressClassName: 'progress-xs progress-striped active m-b-none',
progressBarClassName: '',
map: [
'bg-danger',
'bg-warning',
'bg-info',
'bg-success',
'bg-success'
],
showLabel: true
map: ['bg-danger', 'bg-warning', 'bg-info', 'bg-success', 'bg-success'],
showLabel: true,
};
autoClassName(value:number) {
autoClassName(value: number) {
const map = this.props.map;
let index = Math.floor(value * map.length / 100);
let index = Math.floor((value * map.length) / 100);
index = Math.max(0, Math.min(map.length - 1, index));
return map[index];
}
@ -52,11 +37,11 @@ export class ProgressField extends React.Component<ProgressProps, object> {
progressBarClassName,
map,
showLabel,
classnames: cx
classnames: cx,
} = this.props;
let value = this.props.value;
let viewValue:React.ReactNode = <span className="text-muted">{placeholder}</span>;
let viewValue: React.ReactNode = <span className="text-muted">{placeholder}</span>;
if (/^\d*\.?\d+$/.test(value)) {
value = parseFloat(value);
@ -64,30 +49,25 @@ export class ProgressField extends React.Component<ProgressProps, object> {
if (typeof value === 'number') {
viewValue = [
<div key="progress" className={cx("progress", progressClassName)}>
<div key="progress" className={cx('progress', progressClassName)}>
<div
className={cx("progress-bar", progressBarClassName || this.autoClassName(value))}
className={cx('progress-bar', progressBarClassName || this.autoClassName(value))}
title={`${value}%`}
style={{
width: `${value}%`
width: `${value}%`,
}}
>
</div>
/>
</div>,
showLabel ? <div key="value">{value}%</div> : null
showLabel ? <div key="value">{value}%</div> : null,
];
}
return (
<span className={cx('ProgressField', className)}>
{viewValue}
</span>
);
return <span className={cx('ProgressField', className)}>{viewValue}</span>;
}
}
@Renderer({
test: /(^|\/)progress$/,
name: 'progress'
name: 'progress',
})
export class ProgressFieldRenderer extends ProgressField {};
export class ProgressFieldRenderer extends ProgressField {}

View File

@ -1,33 +1,25 @@
import * as React from 'react';
import * as cx from 'classnames';
import {
Renderer,
RendererProps,
} from '../factory';
import {
FormItem,
FormControlProps
} from './Form/Item';
import {
filter
} from '../utils/tpl';
import {Renderer, RendererProps} from '../factory';
import {FormItem, FormControlProps} from './Form/Item';
import {filter} from '../utils/tpl';
import QrCode = require('qrcode.react');
export interface QRCodeProps extends FormControlProps {
codeSize?: number;
backgroundColor?: string;
foregroundColor?: string;
level?: string
level?: string;
placeholder: string;
}
export default class QRCode extends React.Component<QRCodeProps, any>{
export default class QRCode extends React.Component<QRCodeProps, any> {
static defaultProps: Partial<QRCodeProps> = {
codeSize: 128,
backgroundColor: '#fff',
foregroundColor: '#000',
level: 'L',
placeholder: '-'
placeholder: '-',
};
render() {
@ -40,7 +32,7 @@ export default class QRCode extends React.Component<QRCodeProps, any>{
level,
value,
data,
classPrefix: ns
classPrefix: ns,
} = this.props;
return (
@ -52,20 +44,23 @@ export default class QRCode extends React.Component<QRCodeProps, any>{
bgColor={backgroundColor}
fgColor={foregroundColor}
level={level || 'L'}
/>) : <span className={`${ns}QrCode--placeholder`}>{placeholder}</span>}
/>
) : (
<span className={`${ns}QrCode--placeholder`}>{placeholder}</span>
)}
</div>
)
);
}
}
@Renderer({
test: /(^|\/)qr\-?code$/,
name: 'qrcode'
name: 'qrcode',
})
export class QRCodeRenderer extends QRCode { }
export class QRCodeRenderer extends QRCode {}
@FormItem({
type: 'qr-code',
sizeMutable: false
sizeMutable: false,
})
export class QRCodeControlRenderer extends QRCode {}
export class QRCodeControlRenderer extends QRCode {}

View File

@ -1,25 +1,24 @@
/**
* @file scoped.jsx.
* @author fex
*/
* @file scoped.jsx.
* @author fex
*/
import * as React from 'react';
import {findDOMNode} from 'react-dom';
import find = require('lodash/find');
import * as PropTypes from 'prop-types';
import isPlainObject = require('lodash/isPlainObject');
import { RendererProps } from '../factory';
import {RendererProps} from '../factory';
import * as cx from 'classnames';
import hoistNonReactStatic = require('hoist-non-react-statics');
import onClickOutside from "react-onclickoutside";
import { Action } from '../types';
import onClickOutside from 'react-onclickoutside';
import {Action} from '../types';
import * as keycode from 'keycode';
import matches = require('dom-helpers/query/matches');
import Overlay from '../components/Overlay';
import PopOver from '../components/PopOver';
export interface QuickEditConfig {
}
export interface QuickEditConfig {}
export interface QuickEditConfig {
saveImmediately?: boolean;
@ -30,31 +29,29 @@ export interface QuickEditConfig {
fieldSet?: any;
focusable?: boolean;
popOverClassName?: string;
[propName:string]: any;
};
[propName: string]: any;
}
export interface QuickEditProps extends RendererProps {
name?: string;
label?: string;
quickEdit: boolean | QuickEditConfig;
quickEditEnabled?: boolean;
};
}
export interface QuickEditState {
isOpened: boolean;
};
}
let inited: boolean = false;
let currentOpened: any;
let inited:boolean = false;
let currentOpened:any;
export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component: React.ComponentType<any>):any => {
export const HocQuickEdit = (config: Partial<QuickEditConfig> = {}) => (Component: React.ComponentType<any>): any => {
class QuickEditComponent extends React.PureComponent<QuickEditProps, QuickEditState> {
target:HTMLElement;
target: HTMLElement;
overlay: HTMLElement;
static ComposedComponent = Component;
constructor(props:QuickEditProps) {
constructor(props: QuickEditProps) {
super(props);
this.openQuickEdit = this.openQuickEdit.bind(this);
@ -65,9 +62,9 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
this.overlayRef = this.overlayRef.bind(this);
this.handleWindowKeyPress = this.handleWindowKeyPress.bind(this);
this.handleWindowKeyDown = this.handleWindowKeyDown.bind(this);
this.state = {
isOpened: false
isOpened: false,
};
}
@ -83,9 +80,9 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
document.body.addEventListener('keydown', this.handleWindowKeyDown);
}
handleWindowKeyPress(e:Event) {
handleWindowKeyPress(e: Event) {
const ns = this.props.classPrefix;
let el:HTMLElement = (e.target as HTMLElement).closest(`.${ns}Field--quickEditable`) as HTMLElement;
let el: HTMLElement = (e.target as HTMLElement).closest(`.${ns}Field--quickEditable`) as HTMLElement;
if (!el) {
return;
}
@ -93,104 +90,111 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
if (!table) {
return;
}
if (keycode(e) === 'space' && !~['INPUT', 'TEXTAREA'].indexOf(el.tagName)) {
e.preventDefault();
e.stopPropagation();
}
}
handleWindowKeyDown(e:Event) {
handleWindowKeyDown(e: Event) {
const code = keycode(e);
if (code === 'esc' && currentOpened) {
currentOpened.closeQuickEdit();
} else if (
~['INPUT', 'TEXTAREA'].indexOf((e.target as HTMLElement).tagName)
|| (e.target as HTMLElement).contentEditable === 'true'
|| !~['up', 'down', 'left', 'right'].indexOf(code)
~['INPUT', 'TEXTAREA'].indexOf((e.target as HTMLElement).tagName) ||
(e.target as HTMLElement).contentEditable === 'true' ||
!~['up', 'down', 'left', 'right'].indexOf(code)
) {
return;
}
e.preventDefault();
const ns = this.props.classPrefix;
let el:HTMLElement = (e.target as HTMLElement).closest(`.${ns}Field--quickEditable`) as HTMLElement || document.querySelector(`.${ns}Field--quickEditable`);
let el: HTMLElement =
((e.target as HTMLElement).closest(`.${ns}Field--quickEditable`) as HTMLElement) ||
document.querySelector(`.${ns}Field--quickEditable`);
if (!el) {
return;
}
let table = el.closest('table');
if (!table) {
return;
}
let current = table.querySelector(`.${ns}Field--quickEditable:focus`) as HTMLTableDataCellElement;
if (!current) {
let dom = table.querySelector(`.${ns}Field--quickEditable[tabindex]`) as HTMLElement;
dom && dom.focus();
let dom = table.querySelector(`.${ns}Field--quickEditable[tabindex]`) as HTMLElement;
dom && dom.focus();
} else {
let prevTr, nextTr, prevTd, nextTd;
switch (code) {
case 'up':
prevTr = (current.parentNode as HTMLElement).previousSibling as HTMLTableCellElement;
if (prevTr) {
let index = current.cellIndex;
(prevTr.children[index] as HTMLElement).focus();
}
break;
case 'down':
nextTr = (current.parentNode as HTMLElement).nextSibling as HTMLTableCellElement;
if (nextTr) {
let index = current.cellIndex;
(nextTr.children[index] as HTMLElement).focus();
}
break;
case 'left':
prevTd = current.previousElementSibling as HTMLTableCellElement;
while (prevTd) {
if (matches(prevTd, `.${ns}Field--quickEditable[tabindex]`)) {
break;
}
prevTd = prevTd.previousElementSibling;
}
if (prevTd) {
(prevTd as HTMLElement).focus();
} else if ((current.parentNode as HTMLElement).previousSibling) {
let tds = ((current.parentNode as HTMLElement).previousSibling as HTMLElement).querySelectorAll(`.${ns}Field--quickEditable[tabindex]`);
if (tds.length) {
(tds[tds.length - 1] as HTMLElement).focus();
}
}
break;
case 'right':
nextTd = current.nextSibling;
while (nextTd) {
if (matches(nextTd, `.${ns}Field--quickEditable[tabindex]`)) {
break;
}
nextTd = nextTd.nextSibling;
}
if (nextTd) {
(nextTd as HTMLElement).focus();
} else if ((current.parentNode as HTMLElement).nextSibling) {
nextTd = ((current.parentNode as HTMLElement).nextSibling as HTMLElement).querySelector(`.${ns}Field--quickEditable[tabindex]`);
if (nextTd) {
nextTd.focus();
}
}
break;
}
let prevTr, nextTr, prevTd, nextTd;
switch (code) {
case 'up':
prevTr = (current.parentNode as HTMLElement).previousSibling as HTMLTableCellElement;
if (prevTr) {
let index = current.cellIndex;
(prevTr.children[index] as HTMLElement).focus();
}
break;
case 'down':
nextTr = (current.parentNode as HTMLElement).nextSibling as HTMLTableCellElement;
if (nextTr) {
let index = current.cellIndex;
(nextTr.children[index] as HTMLElement).focus();
}
break;
case 'left':
prevTd = current.previousElementSibling as HTMLTableCellElement;
while (prevTd) {
if (matches(prevTd, `.${ns}Field--quickEditable[tabindex]`)) {
break;
}
prevTd = prevTd.previousElementSibling;
}
if (prevTd) {
(prevTd as HTMLElement).focus();
} else if ((current.parentNode as HTMLElement).previousSibling) {
let tds = ((current.parentNode as HTMLElement)
.previousSibling as HTMLElement).querySelectorAll(
`.${ns}Field--quickEditable[tabindex]`
);
if (tds.length) {
(tds[tds.length - 1] as HTMLElement).focus();
}
}
break;
case 'right':
nextTd = current.nextSibling;
while (nextTd) {
if (matches(nextTd, `.${ns}Field--quickEditable[tabindex]`)) {
break;
}
nextTd = nextTd.nextSibling;
}
if (nextTd) {
(nextTd as HTMLElement).focus();
} else if ((current.parentNode as HTMLElement).nextSibling) {
nextTd = ((current.parentNode as HTMLElement).nextSibling as HTMLElement).querySelector(
`.${ns}Field--quickEditable[tabindex]`
);
if (nextTd) {
nextTd.focus();
}
}
break;
}
}
}
@ -198,14 +202,12 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
// this.closeQuickEdit();
// }
overlayRef(ref:any) {
overlayRef(ref: any) {
this.overlay = ref;
}
handleAction(e:any, action:Action, ctx:object) {
const {
onAction
} = this.props;
handleAction(e: any, action: Action, ctx: object) {
const {onAction} = this.props;
if (action.actionType === 'cancel' || action.actionType === 'close') {
this.closeQuickEdit();
@ -215,11 +217,8 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
onAction && onAction(e, action, ctx);
}
handleSubmit(values:object) {
const {
onQuickChange,
quickEdit
} = this.props;
handleSubmit(values: object) {
const {onQuickChange, quickEdit} = this.props;
this.closeQuickEdit();
onQuickChange(values, (quickEdit as QuickEditConfig).saveImmediately);
@ -228,7 +227,7 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
openQuickEdit() {
currentOpened = this;
this.setState({
isOpened: true
isOpened: true,
});
}
@ -238,21 +237,21 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
}
currentOpened = null;
const ns = this.props.classPrefix;
this.setState({
isOpened: false
}, () => {
let el = findDOMNode(this) as HTMLElement;
let table = el.closest('table') as HTMLElement;
(table && table.querySelectorAll(`td.${ns}Field--quickEditable:focus`).length || el) && el.focus();
});
this.setState(
{
isOpened: false,
},
() => {
let el = findDOMNode(this) as HTMLElement;
let table = el.closest('table') as HTMLElement;
((table && table.querySelectorAll(`td.${ns}Field--quickEditable:focus`).length) || el) &&
el.focus();
}
);
}
buildSchema() {
const {
quickEdit,
name,
label
} = this.props;
const {quickEdit, name, label} = this.props;
let schema;
@ -266,18 +265,23 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
type: 'text',
name,
placeholder: label,
label: false
}
]
label: false,
},
],
};
} else if (quickEdit) {
if ((quickEdit.controls && !~['combo', 'group', 'panel', 'fieldSet'].indexOf((quickEdit as any).type)) || quickEdit.tabs || quickEdit.fieldSet) {
if (
(quickEdit.controls &&
!~['combo', 'group', 'panel', 'fieldSet'].indexOf((quickEdit as any).type)) ||
quickEdit.tabs ||
quickEdit.fieldSet
) {
schema = {
title: '',
autoFocus: (quickEdit as QuickEditConfig).mode !== 'inline',
mode: (quickEdit as QuickEditConfig).mode === 'inline' ? 'inline' : 'normal',
...quickEdit,
type: 'form'
type: 'form',
};
} else {
schema = {
@ -292,9 +296,9 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
name: quickEdit.name || name,
placeholder: label,
label: false,
...quickEdit
}
]
...quickEdit,
},
],
};
}
}
@ -303,26 +307,29 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
schema = {
...schema,
wrapWithPanel: (quickEdit as QuickEditConfig).mode !== 'inline',
actions: (quickEdit as QuickEditConfig).mode === 'inline' ? [] : [
{
type: 'button',
label: '取消',
actionType: 'cancel'
},
actions:
(quickEdit as QuickEditConfig).mode === 'inline'
? []
: [
{
type: 'button',
label: '取消',
actionType: 'cancel',
},
{
label: '确认',
type: 'submit',
primary: true
}
]
}
{
label: '确认',
type: 'submit',
primary: true,
},
],
};
}
return schema || 'error';
}
handleKeyUp(e:Event) {
handleKeyUp(e: Event) {
const code = keycode(e);
if (code === 'space' && !~['INPUT', 'TEXTAREA'].indexOf((e.target as HTMLElement).tagName)) {
e.preventDefault();
@ -332,13 +339,7 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
}
renderPopOver() {
let {
quickEdit,
render,
popOverContainer,
classPrefix: ns,
classnames: cx
} = this.props;
let {quickEdit, render, popOverContainer, classPrefix: ns, classnames: cx} = this.props;
const content = (
<div className={cx((quickEdit as QuickEditConfig).className)}>
@ -346,7 +347,7 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
onSubmit: this.handleSubmit,
onAction: this.handleAction,
onChange: null,
popOverContainer: popOverContainer ? () => this.overlay : null
popOverContainer: popOverContainer ? () => this.overlay : null,
})}
</div>
);
@ -375,20 +376,10 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
}
render() {
const {
onQuickChange,
quickEdit,
quickEditEnabled,
className,
classnames: cx,
render,
noHoc
} = this.props;
const {onQuickChange, quickEdit, quickEditEnabled, className, classnames: cx, render, noHoc} = this.props;
if (!quickEdit || !onQuickChange || quickEditEnabled === false || noHoc) {
return (
<Component {...this.props} />
);
return <Component {...this.props} />;
}
if ((quickEdit as QuickEditConfig).mode === 'inline') {
@ -397,22 +388,27 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
{render('inline-form', this.buildSchema(), {
wrapperComponent: 'div',
className: cx('Form--quickEdit'),
onChange: (values:object) => onQuickChange(values, (quickEdit as QuickEditConfig).saveImmediately)
onChange: (values: object) =>
onQuickChange(values, (quickEdit as QuickEditConfig).saveImmediately),
})}
</Component>
)
);
} else {
return (
<Component
<Component
{...this.props}
className={cx(`Field--quickEditable`, className, {
'in': this.state.isOpened
in: this.state.isOpened,
})}
tabIndex={(quickEdit as QuickEditConfig).focusable === false ? undefined : '0'}
onKeyUp={this.handleKeyUp}
>
<Component {...this.props} wrapperComponent={''} noHoc />
<i key="edit-btn" className={cx("Field-quickEditBtn fa fa-edit")} onClick={this.openQuickEdit} />
<i
key="edit-btn"
className={cx('Field-quickEditBtn fa fa-edit')}
onClick={this.openQuickEdit}
/>
{this.state.isOpened ? this.renderPopOver() : null}
</Component>
);

View File

@ -1,18 +1,26 @@
import * as React from "react";
import { Renderer, RendererProps } from "../factory";
import { Api, SchemaNode, Schema, Action } from "../types";
import * as cx from "classnames";
import * as React from 'react';
import {Renderer, RendererProps} from '../factory';
import {Api, SchemaNode, Schema, Action} from '../types';
import * as cx from 'classnames';
import TooltipWrapper from '../components/TooltipWrapper';
import { filter } from "../utils/tpl";
import {filter} from '../utils/tpl';
export function filterContents(tooltip:string | undefined | {title?: string; content?: string; body?: string}, data:any) {
export function filterContents(
tooltip: string | undefined | {title?: string; content?: string; body?: string},
data: any
) {
if (typeof tooltip === 'string') {
return filter(tooltip, data);
} else if (tooltip) {
return tooltip.title ? {
title: filter(tooltip.title, data),
content: tooltip.content || tooltip.body ? filter(tooltip.content || tooltip.body || '', data) : undefined,
} : (tooltip.content || tooltip.body ? filter(tooltip.content || tooltip.body || '', data) : undefined);
return tooltip.title
? {
title: filter(tooltip.title, data),
content:
tooltip.content || tooltip.body ? filter(tooltip.content || tooltip.body || '', data) : undefined,
}
: tooltip.content || tooltip.body
? filter(tooltip.content || tooltip.body || '', data)
: undefined;
}
return tooltip;
}
@ -45,7 +53,7 @@ export default class Remark extends React.Component<RemarkProps> {
classPrefix: ns,
classnames: cx,
content,
data
data,
} = this.props;
return (
@ -53,14 +61,14 @@ export default class Remark extends React.Component<RemarkProps> {
classPrefix={ns}
classnames={cx}
tooltip={filterContents(tooltip || content, data)}
placement={tooltip && tooltip.placement || placement}
rootClose={tooltip && tooltip.rootClose || rootClose}
trigger={tooltip && tooltip.trigger || trigger}
placement={(tooltip && tooltip.placement) || placement}
rootClose={(tooltip && tooltip.rootClose) || rootClose}
trigger={(tooltip && tooltip.trigger) || trigger}
container={container}
delay={tooltip && tooltip.delay}
>
<div className={cx(`Remark`, tooltip && tooltip.className || className || `Remark--warning`)}>
<i className={cx('Remark-icon', tooltip && tooltip.icon || icon)} />
<div className={cx(`Remark`, (tooltip && tooltip.className) || className || `Remark--warning`)}>
<i className={cx('Remark-icon', (tooltip && tooltip.icon) || icon)} />
</div>
</TooltipWrapper>
);
@ -69,6 +77,6 @@ export default class Remark extends React.Component<RemarkProps> {
@Renderer({
test: /(^|\/)remark$/,
name: 'remark'
name: 'remark',
})
export class RemarkRenderer extends Remark {}

View File

@ -1,23 +1,13 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import {
Renderer,
RendererProps
} from '../factory';
import {Renderer, RendererProps} from '../factory';
import {ServiceStore, IServiceStore} from '../store/service';
import {
Api,
SchemaNode,
ApiObject,
RendererData
} from '../types';
import {
filter, evalExpression
} from '../utils/tpl';
import {Api, SchemaNode, ApiObject, RendererData} from '../types';
import {filter, evalExpression} from '../utils/tpl';
import * as cx from 'classnames';
import Scoped, { ScopedContext, IScopedContext } from '../Scoped';
import { observer } from 'mobx-react';
import { isApiOutdated } from '../utils/api';
import Scoped, {ScopedContext, IScopedContext} from '../Scoped';
import {observer} from 'mobx-react';
import {isApiOutdated} from '../utils/api';
export interface ServiceProps extends RendererProps {
api?: Api;
@ -34,15 +24,11 @@ export default class Service extends React.Component<ServiceProps> {
timer: NodeJS.Timeout;
mounted: boolean;
static defaultProps:Partial<ServiceProps> = {
};
static defaultProps: Partial<ServiceProps> = {};
static propsList:Array<string> = [
];
static propsList: Array<string> = [];
constructor(props:ServiceProps) {
constructor(props: ServiceProps) {
super(props);
this.handleQuery = this.handleQuery.bind(this);
@ -52,40 +38,36 @@ export default class Service extends React.Component<ServiceProps> {
}
componentDidMount() {
const {
schemaApi,
initFetchSchema,
api,
initFetch,
store
} = this.props;
const {schemaApi, initFetchSchema, api, initFetch, store} = this.props;
this.mounted = true;
if (schemaApi && initFetchSchema !== false && (!(schemaApi as ApiObject).sendOn || evalExpression((schemaApi as ApiObject).sendOn as string, store.data))) {
store
.fetchSchema(schemaApi, store.data)
.then(this.initInterval);
if (
schemaApi &&
initFetchSchema !== false &&
(!(schemaApi as ApiObject).sendOn || evalExpression((schemaApi as ApiObject).sendOn as string, store.data))
) {
store.fetchSchema(schemaApi, store.data).then(this.initInterval);
}
if (api && initFetch !== false && (!(api as ApiObject).sendOn || evalExpression((api as ApiObject).sendOn as string, store.data))) {
store
.fetchInitData(api, store.data)
.then(this.initInterval);
if (
api &&
initFetch !== false &&
(!(api as ApiObject).sendOn || evalExpression((api as ApiObject).sendOn as string, store.data))
) {
store.fetchInitData(api, store.data).then(this.initInterval);
}
}
componentDidUpdate(prevProps:ServiceProps) {
componentDidUpdate(prevProps: ServiceProps) {
const props = this.props;
const store = props.store;
isApiOutdated(prevProps.api, props.api, prevProps.data, props.data) && store
.fetchData(props.api as Api, store.data)
.then(this.initInterval);
isApiOutdated(prevProps.api, props.api, prevProps.data, props.data) &&
store.fetchData(props.api as Api, store.data).then(this.initInterval);
isApiOutdated(prevProps.schemaApi, props.schemaApi, prevProps.data, props.data) && store
.fetchSchema(props.schemaApi as Api, store.data)
.then(this.initInterval);
isApiOutdated(prevProps.schemaApi, props.schemaApi, prevProps.data, props.data) &&
store.fetchSchema(props.schemaApi as Api, store.data).then(this.initInterval);
}
componentWillUnmount() {
@ -94,46 +76,40 @@ export default class Service extends React.Component<ServiceProps> {
}
initInterval(value: any) {
const {
interval,
silentPolling,
stopAutoRefreshWhen,
data
} = this.props;
const {interval, silentPolling, stopAutoRefreshWhen, data} = this.props;
interval
&& this.mounted
&& (!stopAutoRefreshWhen || !evalExpression(stopAutoRefreshWhen, data))
&& (this.timer = setTimeout(silentPolling ? this.silentReload : this.reload, Math.max(interval, 3000)));
interval &&
this.mounted &&
(!stopAutoRefreshWhen || !evalExpression(stopAutoRefreshWhen, data)) &&
(this.timer = setTimeout(silentPolling ? this.silentReload : this.reload, Math.max(interval, 3000)));
return value;
}
reload(subpath?: string, query?: any, ctx?: RendererData, silent?: boolean) {
if (query) {
return this.receive(query);
}
const {
schemaApi,
fetchSchema,
api,
fetch,
store
} = this.props;
const {schemaApi, fetchSchema, api, fetch, store} = this.props;
clearTimeout(this.timer);
if (schemaApi && fetchSchema !== false && (!(schemaApi as ApiObject).sendOn || evalExpression((schemaApi as ApiObject).sendOn as string, store.data))) {
store
.fetchSchema(schemaApi, store.data)
.then(this.initInterval);
if (
schemaApi &&
fetchSchema !== false &&
(!(schemaApi as ApiObject).sendOn || evalExpression((schemaApi as ApiObject).sendOn as string, store.data))
) {
store.fetchSchema(schemaApi, store.data).then(this.initInterval);
}
if (api && fetch !== false && (!(api as ApiObject).sendOn || evalExpression((api as ApiObject).sendOn as string, store.data))) {
if (
api &&
fetch !== false &&
(!(api as ApiObject).sendOn || evalExpression((api as ApiObject).sendOn as string, store.data))
) {
store
.fetchData(api, store.data, {
silent
silent,
})
.then(this.initInterval);
}
@ -143,66 +119,61 @@ export default class Service extends React.Component<ServiceProps> {
this.reload(target, query, undefined, true);
}
receive(values:object) {
const {
store
} = this.props;
receive(values: object) {
const {store} = this.props;
store.updateData(values);
this.reload();
}
handleQuery(query:any) {
handleQuery(query: any) {
this.receive(query);
}
renderBody() {
const {
render,
store,
body: schema,
classnames: cx
} = this.props;
const {render, store, body: schema, classnames: cx} = this.props;
return (
<div className={cx("Service-body")}>
{render('body', store.schema || schema, {
key: store.schemaKey || 'body',
onQuery: this.handleQuery
}) as JSX.Element}
<div className={cx('Service-body')}>
{
render('body', store.schema || schema, {
key: store.schemaKey || 'body',
onQuery: this.handleQuery,
}) as JSX.Element
}
</div>
);
}
render() {
const {
className,
store,
render,
classPrefix: ns,
classnames: cx
} = this.props;
const {className, store, render, classPrefix: ns, classnames: cx} = this.props;
return (
<div
className={cx(`${ns}Service`, className)}
>
<div className={cx(`${ns}Service`, className)}>
{store.error ? (
<div className={cx(`Alert Alert--danger`)}>
<button className={cx("Alert-close")} onClick={() => store.updateMessage('')} type="button"><span>×</span></button>
<button className={cx('Alert-close')} onClick={() => store.updateMessage('')} type="button">
<span>×</span>
</button>
{store.msg}
</div>
) : null}
{this.renderBody()}
{store.loading ? render('info', {
type: 'spinner',
overlay: true
}, {
key: 'info',
size: 'lg',
}) : null}
{store.loading
? render(
'info',
{
type: 'spinner',
overlay: true,
},
{
key: 'info',
size: 'lg',
}
)
: null}
</div>
);
}
@ -211,7 +182,7 @@ export default class Service extends React.Component<ServiceProps> {
@Renderer({
test: /(^|\/)service$/,
storeType: ServiceStore.name,
name: 'service'
name: 'service',
})
export class ServiceRenderer extends Service {
static contextType = ScopedContext;
@ -227,4 +198,4 @@ export class ServiceRenderer extends Service {
const scoped = this.context as IScopedContext;
scoped.unRegisterComponent(this);
}
};
}

View File

@ -1,9 +1,8 @@
import Spinner from "../components/Spinner";
import { Renderer } from "../factory";
import Spinner from '../components/Spinner';
import {Renderer} from '../factory';
@Renderer({
test: /(^|\/)spinner$/,
name: 'spinner'
name: 'spinner',
})
export class SpinnerRenderer extends Spinner {}
export class SpinnerRenderer extends Spinner {}

View File

@ -1,17 +1,8 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import {Renderer, RendererProps} from '../factory';
import {ServiceStore, IServiceStore} from '../store/service';
import {
Api,
SchemaNode,
PlainObject
} from '../types';
import {
filter
} from '../utils/tpl';
import {Api, SchemaNode, PlainObject} from '../types';
import {filter} from '../utils/tpl';
import * as cx from 'classnames';
export interface StatusProps extends RendererProps {
@ -21,11 +12,11 @@ export interface StatusProps extends RendererProps {
}
export class StatusField extends React.Component<StatusProps, object> {
static defaultProps:Partial<StatusProps> = {
static defaultProps: Partial<StatusProps> = {
placeholder: '-',
map: {
0: 'fa fa-times text-danger',
1: 'fa fa-check text-success'
1: 'fa fa-check text-success',
},
labelMap: {
// 0: '失败',
@ -34,19 +25,12 @@ export class StatusField extends React.Component<StatusProps, object> {
};
render() {
const {
className,
placeholder,
map,
labelMap,
classnames: cx,
data
} = this.props;
const {className, placeholder, map, labelMap, classnames: cx, data} = this.props;
let value = this.props.value;
let viewValue:React.ReactNode = <span className="text-muted">{placeholder}</span>;
let wrapClassName:string = '';
let viewValue: React.ReactNode = <span className="text-muted">{placeholder}</span>;
let wrapClassName: string = '';
if (value !== undefined && value !== "" && map) {
if (value !== undefined && value !== '' && map) {
if (typeof value === 'boolean') {
value = value ? 1 : 0;
} else if (/^\d+$/.test(value)) {
@ -54,23 +38,24 @@ export class StatusField extends React.Component<StatusProps, object> {
}
wrapClassName = `StatusField--${value}`;
viewValue = (<i className={cx("StatusField-icon", map[value])} key="icon" />);
viewValue = <i className={cx('StatusField-icon', map[value])} key="icon" />;
if (labelMap && labelMap[value]) {
viewValue = [viewValue, (<span className={cx("StatusField-label")} key="label">{filter(labelMap[value], data)}</span>)];
viewValue = [
viewValue,
<span className={cx('StatusField-label')} key="label">
{filter(labelMap[value], data)}
</span>,
];
}
}
return (
<span className={cx('StatusField', wrapClassName, className)}>
{viewValue}
</span>
);
return <span className={cx('StatusField', wrapClassName, className)}>{viewValue}</span>;
}
}
@Renderer({
test: /(^|\/)status$/,
name: 'status'
name: 'status',
})
export class StatusFieldRenderer extends StatusField {};
export class StatusFieldRenderer extends StatusField {}

View File

@ -1,17 +1,8 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import {Renderer, RendererProps} from '../factory';
import {ServiceStore, IServiceStore} from '../store/service';
import {
Api,
SchemaNode,
PlainObject
} from '../types';
import {
filter
} from '../utils/tpl';
import {Api, SchemaNode, PlainObject} from '../types';
import {filter} from '../utils/tpl';
import * as cx from 'classnames';
import Switch from '../components/Switch';
@ -24,34 +15,32 @@ export interface SwitchProps extends RendererProps {
}
export class SwitchField extends React.Component<SwitchProps, object> {
static defaultProps:Partial<SwitchProps> = {
static defaultProps: Partial<SwitchProps> = {
placeholder: '-',
trueValue: true,
falseValue: false,
readOnly: true,
saveImmediately: false
saveImmediately: false,
};
constructor(props:SwitchProps) {
constructor(props: SwitchProps) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(checked:boolean) {
const {
onQuickChange,
name,
trueValue,
falseValue,
saveImmediately,
readOnly,
disabled
} = this.props;
handleChange(checked: boolean) {
const {onQuickChange, name, trueValue, falseValue, saveImmediately, readOnly, disabled} = this.props;
onQuickChange && !readOnly && !disabled && onQuickChange({
[name as string]: checked ? trueValue : falseValue
}, saveImmediately);
onQuickChange &&
!readOnly &&
!disabled &&
onQuickChange(
{
[name as string]: checked ? trueValue : falseValue,
},
saveImmediately
);
}
render() {
@ -64,19 +53,19 @@ export class SwitchField extends React.Component<SwitchProps, object> {
falseValue,
onQuickChange,
option,
disabled
disabled,
} = this.props;
let viewValue:React.ReactNode = <span className="text-muted">{placeholder}</span>;
let viewValue: React.ReactNode = <span className="text-muted">{placeholder}</span>;
let showOption = false;
if (value == trueValue || value == falseValue) {
showOption = !!option;
viewValue = (
<Switch
<Switch
inline
classPrefix={ns}
checked={value == trueValue}
checked={value == trueValue}
onChange={this.handleChange}
disabled={disabled || !onQuickChange}
/>
@ -94,6 +83,6 @@ export class SwitchField extends React.Component<SwitchProps, object> {
@Renderer({
test: /(^|\/)switch$/,
name: 'switch'
name: 'switch',
})
export class SwitchFieldRenderer extends SwitchField {};
export class SwitchFieldRenderer extends SwitchField {}

File diff suppressed because it is too large Load Diff

View File

@ -1,31 +1,12 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import { ServiceStore, IServiceStore } from '../store/service';
import {
Api,
SchemaNode,
Schema,
Action
} from '../types';
import {
filter,
evalExpression
} from '../utils/tpl';
import {
Tabs as BsTabs,
TabContainer,
TabContent,
TabPane,
NavItem,
Nav,
Tab
} from 'react-bootstrap';
import {Renderer, RendererProps} from '../factory';
import {ServiceStore, IServiceStore} from '../store/service';
import {Api, SchemaNode, Schema, Action} from '../types';
import {filter, evalExpression} from '../utils/tpl';
import {Tabs as BsTabs, TabContainer, TabContent, TabPane, NavItem, Nav, Tab} from 'react-bootstrap';
import cx = require('classnames');
import find = require('lodash/find');
import { isVisible } from '../utils/helper';
import {isVisible} from '../utils/helper';
import findIndex = require('lodash/findIndex');
export type TabProps = Schema & {
@ -50,12 +31,11 @@ export interface TabsState {
}
export default class Tabs extends React.Component<TabsProps, TabsState> {
static defaultProps: Partial<TabsProps> = {
className: '',
mode: '',
mountOnEnter: true,
unmountOnExit: false
unmountOnExit: false,
};
id = Date.now() + '';
@ -71,13 +51,13 @@ export default class Tabs extends React.Component<TabsProps, TabsState> {
} else if (location && Array.isArray(tabs)) {
const hash = location.hash.substring(1);
const tab: TabProps = find(tabs, tab => tab.hash === hash) as TabProps;
activeKey = tab && tab.hash ? tab.hash : (tabs[0] && tabs[0].hash || 0);
activeKey = tab && tab.hash ? tab.hash : (tabs[0] && tabs[0].hash) || 0;
}
this.state = {
prevKey: undefined,
activeKey: activeKey
}
activeKey: activeKey,
};
this.handleSelect = this.handleSelect.bind(this);
this.currentIndex = this.currentIndex.bind(this);
@ -97,7 +77,7 @@ export default class Tabs extends React.Component<TabsProps, TabsState> {
if (tab && tab.hash && tab.hash !== this.state.activeKey) {
this.setState({
activeKey: tab.hash,
prevKey: this.state.activeKey
prevKey: this.state.activeKey,
});
}
} else if (props.tabs !== nextProps.tabs) {
@ -111,53 +91,54 @@ export default class Tabs extends React.Component<TabsProps, TabsState> {
}
if (tab) {
activeKey = tab.hash
} else if (!nextProps.tabs || !nextProps.tabs.some((item, index) => item.hash ? item.hash === activeKey : index === activeKey)) {
activeKey = nextProps.tabs && nextProps.tabs[0] && nextProps.tabs[0].hash || 0;
activeKey = tab.hash;
} else if (
!nextProps.tabs ||
!nextProps.tabs.some((item, index) => (item.hash ? item.hash === activeKey : index === activeKey))
) {
activeKey = (nextProps.tabs && nextProps.tabs[0] && nextProps.tabs[0].hash) || 0;
}
this.setState({
prevKey: undefined,
activeKey: activeKey
activeKey: activeKey,
});
}
}
handleSelect(key: any) {
const {
env
} = this.props;
const {env} = this.props;
// 是 hash需要更新到地址栏
if (typeof key === 'string' && env) {
env.updateLocation(`#${key}`)
env.updateLocation(`#${key}`);
} else if (typeof this.state.prevKey === 'string' && env) {
env.updateLocation(`#`);
}
this.setState({
activeKey: key,
prevKey: this.state.activeKey
prevKey: this.state.activeKey,
});
}
switchTo(index: number) {
const {
tabs
} = this.props;
const {tabs} = this.props;
Array.isArray(tabs) && tabs[index] && this.setState({
activeKey: tabs[index].hash || index
})
Array.isArray(tabs) &&
tabs[index] &&
this.setState({
activeKey: tabs[index].hash || index,
});
}
currentIndex(): number {
const {
tabs
} = this.props;
const {tabs} = this.props;
return Array.isArray(tabs)
? findIndex(tabs, (tab: TabProps, index) => tab.hash ? tab.hash === this.state.activeKey : index === this.state.activeKey)
? findIndex(tabs, (tab: TabProps, index) =>
tab.hash ? tab.hash === this.state.activeKey : index === this.state.activeKey
)
: -1;
}
@ -173,7 +154,7 @@ export default class Tabs extends React.Component<TabsProps, TabsState> {
render,
data,
mode: dMode,
tabsMode
tabsMode,
} = this.props;
if (!Array.isArray(tabs)) {
@ -186,9 +167,13 @@ export default class Tabs extends React.Component<TabsProps, TabsState> {
return (
<TabContainer
id={this.id}
className={cx(`Tabs`, {
[`Tabs--${mode}`]: mode
}, className)}
className={cx(
`Tabs`,
{
[`Tabs--${mode}`]: mode,
},
className
)}
activeKey={this.state.activeKey}
onSelect={this.handleSelect}
>
@ -199,9 +184,15 @@ export default class Tabs extends React.Component<TabsProps, TabsState> {
className={cx('Tabs-link')}
key={index}
eventKey={tab.hash || index}
disabled={tab.disabled || tab.disabledOn && evalExpression(tab.disabledOn, data)}
disabled={tab.disabled || (tab.disabledOn && evalExpression(tab.disabledOn, data))}
>
{tab.icon ? (<div><i className={tab.icon} /> {tab.title}</div>) : tab.title}
{tab.icon ? (
<div>
<i className={tab.icon} /> {tab.title}
</div>
) : (
tab.title
)}
</NavItem>
))}
</Nav>
@ -218,7 +209,9 @@ export default class Tabs extends React.Component<TabsProps, TabsState> {
mountOnEnter={mountOnEnter}
unmountOnExit={typeof tab.reload === 'boolean' ? tab.reload : tab.unmountOnExit}
>
{tabRender ? tabRender(tab, this.props) : render(`tab/${index}`, tab.tab || tab.body || '')}
{tabRender
? tabRender(tab, this.props)
: render(`tab/${index}`, tab.tab || tab.body || '')}
</TabPane>
))}
</TabContent>
@ -230,6 +223,6 @@ export default class Tabs extends React.Component<TabsProps, TabsState> {
@Renderer({
test: /(^|\/)tabs$/,
name: 'tabs'
name: 'tabs',
})
export class TabsRenderer extends Tabs { }
export class TabsRenderer extends Tabs {}

View File

@ -1,12 +1,9 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import { ServiceStore, IServiceStore } from '../store/service';
import {Renderer, RendererProps} from '../factory';
import {ServiceStore, IServiceStore} from '../store/service';
import * as cx from 'classnames';
import getExprProperties from '../utils/filter-schema';
import { Api, Payload } from '../types';
import {Api, Payload} from '../types';
import update = require('react-addons-update');
export interface TaskProps extends RendererProps {
@ -15,7 +12,7 @@ export interface TaskProps extends RendererProps {
checkApi: Api;
submitApi: Api;
reSubmitApi: Api;
tableClassName?: string;
taskNameLabel?: string;
operationLabel?: string;
@ -40,12 +37,12 @@ export interface TaskItem {
label?: string;
key?: string;
remark?: string;
status?:any;
status?: any;
}
export interface TaskState {
error?: string;
items: Array<TaskItem>
items: Array<TaskItem>;
}
export default class Task extends React.Component<TaskProps, TaskState> {
@ -68,141 +65,150 @@ export default class Task extends React.Component<TaskProps, TaskState> {
errorStatusCode: 3,
finishStatusCode: 4,
canRetryStatusCode: 5,
interval: 3000
interval: 3000,
};
timer:any;
constructor(props:TaskProps) {
timer: any;
constructor(props: TaskProps) {
super(props);
this.state = {
items: props.items ? props.items.concat() : []
items: props.items ? props.items.concat() : [],
};
this.handleLoaded = this.handleLoaded.bind(this);
this.tick = this.tick.bind(this);
}
componentWillReceiveProps(nextProps:TaskProps) {
componentWillReceiveProps(nextProps: TaskProps) {
const props = this.props;
if (props.items !== nextProps.items) {
this.setState({
items: nextProps.items ? nextProps.items.concat() : []
items: nextProps.items ? nextProps.items.concat() : [],
});
}
}
componentDidMount() {
this.tick(!!this.props.checkApi);
}
componentWillUnmount() {
clearTimeout(this.timer);
}
tick(force = false) {
const {
loadingStatusCode,
data,
interval,
checkApi,
env
} = this.props;
const {loadingStatusCode, data, interval, checkApi, env} = this.props;
const items = this.state.items;
// 如果每个 task 都完成了, 则不需要取查看状态.
if (!force && !items.some(item => item.status === loadingStatusCode)) {
return;
}
if (interval && !checkApi) {
return alert('checkApi 没有设置, 不能及时获取任务状态');
}
env && env.fetcher(checkApi, data)
.then(this.handleLoaded)
.catch(e => this.setState({ error: e }))
env &&
env
.fetcher(checkApi, data)
.then(this.handleLoaded)
.catch(e => this.setState({error: e}));
}
handleLoaded(ret:Payload) {
handleLoaded(ret: Payload) {
if (!Array.isArray(ret.data)) {
return alert('返回格式不正确, 期望 response.data 为数组, 包含每个 task 的状态信息');
}
this.setState({
items: ret.data
items: ret.data,
});
const interval = this.props.interval;
clearTimeout(this.timer);
this.timer = setTimeout(this.tick, interval);
}
submitTask(item:TaskItem, index:number, retry = false) {
const {
submitApi,
reSubmitApi,
loadingStatusCode,
errorStatusCode,
data,
env
} = this.props;
submitTask(item: TaskItem, index: number, retry = false) {
const {submitApi, reSubmitApi, loadingStatusCode, errorStatusCode, data, env} = this.props;
if (!retry && !submitApi) {
return alert('submitApi 没有配置');
} else if (retry && !reSubmitApi) {
return alert('reSubmitApi 没有配置');
}
this.setState(update(this.state, {
items: {
$splice: [
[index, 1, {
...item,
status: loadingStatusCode
}]
]
}
} as any));
env && env.fetcher(retry ? reSubmitApi : submitApi, {
...data,
...item
})
.then((ret:Payload) => {
if (ret && ret.data) {
if (Array.isArray(ret.data)) {
this.handleLoaded(ret);
} else {
const items = this.state.items.map(item => item.key === ret.data.key ? {
...item,
...ret.data
} : item);
this.handleLoaded({
...ret,
data: items
});
}
return;
}
clearTimeout(this.timer);
this.timer = setTimeout(this.tick, 4);
})
.catch(e => this.setState(update(this.state, {
items: {
$splice: [
[index, 1, {
...item,
status: errorStatusCode,
remark: e.message || e
}]
]
}
} as any)))
this.setState(
update(this.state, {
items: {
$splice: [
[
index,
1,
{
...item,
status: loadingStatusCode,
},
],
],
},
} as any)
);
env &&
env
.fetcher(retry ? reSubmitApi : submitApi, {
...data,
...item,
})
.then((ret: Payload) => {
if (ret && ret.data) {
if (Array.isArray(ret.data)) {
this.handleLoaded(ret);
} else {
const items = this.state.items.map(item =>
item.key === ret.data.key
? {
...item,
...ret.data,
}
: item
);
this.handleLoaded({
...ret,
data: items,
});
}
return;
}
clearTimeout(this.timer);
this.timer = setTimeout(this.tick, 4);
})
.catch(e =>
this.setState(
update(this.state, {
items: {
$splice: [
[
index,
1,
{
...item,
status: errorStatusCode,
remark: e.message || e,
},
],
],
},
} as any)
)
);
}
render() {
const {
className,
@ -220,11 +226,11 @@ export default class Task extends React.Component<TaskProps, TaskState> {
readyStatusCode,
loadingStatusCode,
canRetryStatusCode,
render
render,
} = this.props;
const items = this.state.items;
const error = this.state.error;
return (
<div className={className}>
<table className={tableClassName}>
@ -237,36 +243,48 @@ export default class Task extends React.Component<TaskProps, TaskState> {
</tr>
</thead>
<tbody>
{error ? (
<tr>
<td colSpan={4}><div className="text-danger">{error}</div></td>
</tr>
) : items.map((item, key) => (
<tr key={key}>
<td>{item.label}</td>
<td>
{item.status == loadingStatusCode ? (
<i className="fa fa-spinner fa-spin fa-2x fa-fw" />
) : item.status == canRetryStatusCode ? (
<a
onClick={() => this.submitTask(item, key, true)}
className={cx('btn', retryBtnClassName || btnClassName)}
>
{retryBtnText || btnText}
</a>
) : (
<a
onClick={() => this.submitTask(item, key)}
className={cx('btn', btnClassName, { disabled: item.status !== readyStatusCode })}
>
{btnText}
</a>
)}
</td>
<td><span className={cx('label', statusLabelMap && statusLabelMap[item.status || 0])}>{statusTextMap && statusTextMap[item.status || 0]}</span></td>
<td>{item.remark ? render(`${key}/remark`, item.remark): null}</td>
</tr>
))}
{error ? (
<tr>
<td colSpan={4}>
<div className="text-danger">{error}</div>
</td>
</tr>
) : (
items.map((item, key) => (
<tr key={key}>
<td>{item.label}</td>
<td>
{item.status == loadingStatusCode ? (
<i className="fa fa-spinner fa-spin fa-2x fa-fw" />
) : item.status == canRetryStatusCode ? (
<a
onClick={() => this.submitTask(item, key, true)}
className={cx('btn', retryBtnClassName || btnClassName)}
>
{retryBtnText || btnText}
</a>
) : (
<a
onClick={() => this.submitTask(item, key)}
className={cx('btn', btnClassName, {
disabled: item.status !== readyStatusCode,
})}
>
{btnText}
</a>
)}
</td>
<td>
<span
className={cx('label', statusLabelMap && statusLabelMap[item.status || 0])}
>
{statusTextMap && statusTextMap[item.status || 0]}
</span>
</td>
<td>{item.remark ? render(`${key}/remark`, item.remark) : null}</td>
</tr>
))
)}
</tbody>
</table>
</div>
@ -276,6 +294,6 @@ export default class Task extends React.Component<TaskProps, TaskState> {
@Renderer({
test: /(^|\/)tasks$/,
name: 'tasks'
name: 'tasks',
})
export class TaskRenderer extends Task { }
export class TaskRenderer extends Task {}

View File

@ -1,14 +1,9 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import {
filter
} from '../utils/tpl';
import {Renderer, RendererProps} from '../factory';
import {filter} from '../utils/tpl';
import * as cx from 'classnames';
import { anyChanged } from '../utils/helper';
import { escapeHtml } from '../utils/tpl-builtin';
import {anyChanged} from '../utils/helper';
import {escapeHtml} from '../utils/tpl-builtin';
export interface TplProps extends RendererProps {
className?: string;
@ -22,48 +17,32 @@ export interface TplProps extends RendererProps {
}
export class Tpl extends React.Component<TplProps, object> {
static defaultProps:Partial<TplProps> = {
static defaultProps: Partial<TplProps> = {
inline: true,
placeholder: '',
value: ''
value: '',
};
dom:any;
dom: any;
constructor(props:TplProps) {
constructor(props: TplProps) {
super(props);
this.htmlRef = this.htmlRef.bind(this);
}
componentDidUpdate(prevProps:TplProps) {
if (anyChanged([
'data',
'tpl',
'html',
'text',
'raw',
'value'
], this.props, prevProps)) {
componentDidUpdate(prevProps: TplProps) {
if (anyChanged(['data', 'tpl', 'html', 'text', 'raw', 'value'], this.props, prevProps)) {
this._render();
}
}
htmlRef(dom:any) {
htmlRef(dom: any) {
this.dom = dom;
this._render();
}
getContent() {
const {
tpl,
html,
text,
raw,
value,
data,
placeholder
} = this.props;
const {tpl, html, text, raw, value, data, placeholder} = this.props;
if (raw) {
return raw;
@ -74,7 +53,11 @@ export class Tpl extends React.Component<TplProps, object> {
} else if (text) {
return escapeHtml(filter(text, data));
} else {
return (value == null || value === '' ? `<span class="text-muted">${placeholder}</span>` : (typeof value === 'string' ? value: JSON.stringify(value)));
return value == null || value === ''
? `<span class="text-muted">${placeholder}</span>`
: typeof value === 'string'
? value
: JSON.stringify(value);
}
}
@ -87,23 +70,16 @@ export class Tpl extends React.Component<TplProps, object> {
}
render() {
const {
className,
wrapperComponent,
inline,
classnames: cx
} = this.props;
const {className, wrapperComponent, inline, classnames: cx} = this.props;
const Component = wrapperComponent || (inline ? 'span' : 'div');
return (
<Component children={this.getContent()} ref={this.htmlRef} className={cx('TplField', className)} />
);
return <Component children={this.getContent()} ref={this.htmlRef} className={cx('TplField', className)} />;
}
}
@Renderer({
test: /(^|\/)(?:tpl|html)$/,
name: 'tpl'
name: 'tpl',
})
export class TplRenderer extends Tpl {};
export class TplRenderer extends Tpl {}

View File

@ -1,8 +1,7 @@
import * as React from "react";
import { Renderer, RendererProps } from "../factory";
import { Api, SchemaNode, Schema, Action } from "../types";
import * as cx from "classnames";
import * as React from 'react';
import {Renderer, RendererProps} from '../factory';
import {Api, SchemaNode, Schema, Action} from '../types';
import * as cx from 'classnames';
export type Row = Schema & {
rowClassName?: string;
@ -15,45 +14,37 @@ export interface HBoxProps extends RendererProps {
}
export default class VBox extends React.Component<HBoxProps, object> {
static propsList: Array<string> = ["rows"];
static propsList: Array<string> = ['rows'];
static defaultProps: Partial<HBoxProps> = {};
renderChild(region:string, node:Schema) {
const {
render
} = this.props;
renderChild(region: string, node: Schema) {
const {render} = this.props;
return render(region, node);
}
renderCell(row: Row, key: any) {
const {
classPrefix: ns
} = this.props;
const {classPrefix: ns} = this.props;
return (
<div
className={cx(`${ns}Vbox-cell`, (row as Row).cellClassName)}
>
<div className={cx(`${ns}Vbox-cell`, (row as Row).cellClassName)}>
{this.renderChild(`row/${key}`, row)}
</div>
);
}
render() {
const {
className,
rows,
classPrefix: ns
} = this.props;
const {className, rows, classPrefix: ns} = this.props;
return (
<div className={cx(`${ns}Vbox`, className)}>
{Array.isArray(rows) ? rows.map((row, key) =>
<div className={cx('row-row', (row as Row).rowClassName)} key={key}>
{this.renderCell(row, key)}
</div>
) : null}
{Array.isArray(rows)
? rows.map((row, key) => (
<div className={cx('row-row', (row as Row).rowClassName)} key={key}>
{this.renderCell(row, key)}
</div>
))
: null}
</div>
);
}
@ -61,6 +52,6 @@ export default class VBox extends React.Component<HBoxProps, object> {
@Renderer({
test: /(^|\/)vbox$/,
name: 'vbox'
name: 'vbox',
})
export class VBoxRenderer extends VBox {}

View File

@ -5,19 +5,20 @@
/* eslint fecs-indent: [0, "space", 2, 2] */
import * as React from 'react';
import { Player, Shortcut, BigPlayButton } from 'video-react';
import { padArr } from '../utils/helper';
import {Player, Shortcut, BigPlayButton} from 'video-react';
import {padArr} from '../utils/helper';
import * as cx from 'classnames';
import {
Renderer,
RendererProps
} from '../factory';
import { resolveVariable } from '../utils/tpl-builtin';
import { filter } from '../utils/tpl';
import {Renderer, RendererProps} from '../factory';
import {resolveVariable} from '../utils/tpl-builtin';
import {filter} from '../utils/tpl';
// import css
import "video-react/dist/video-react.css";
import 'video-react/dist/video-react.css';
const str2seconds: (str: string) => number = str => str.split(':').reverse().reduce((seconds, value, index) => seconds + (parseInt(value, 10) || 0) * Math.pow(60, index), 0);
const str2seconds: (str: string) => number = str =>
str
.split(':')
.reverse()
.reduce((seconds, value, index) => seconds + (parseInt(value, 10) || 0) * Math.pow(60, index), 0);
export interface FlvSourceProps {
src?: string;
@ -36,18 +37,21 @@ let currentPlaying: any = null;
export class FlvSource extends React.Component<FlvSourceProps, any> {
flvPlayer: any;
componentDidMount() {
let { src, video, config, manager, isLive, autoPlay, actions } = this.props;
let {src, video, config, manager, isLive, autoPlay, actions} = this.props;
(require as any)(['flv.js'], (flvjs: any) => {
// load hls video source base on hls.js
if (flvjs.isSupported()) {
video = video || manager.video && manager.video.video;
video = video || (manager.video && manager.video.video);
let flvPlayer = flvjs.createPlayer({
type: 'flv',
url: src,
isLive: isLive
}, config);
let flvPlayer = flvjs.createPlayer(
{
type: 'flv',
url: src,
isLive: isLive,
},
config
);
flvPlayer.attachMediaElement(video);
this.flvPlayer = flvPlayer;
let loaded = false;
@ -99,12 +103,7 @@ export class FlvSource extends React.Component<FlvSourceProps, any> {
}
render() {
return (
<source
src={this.props.src}
type={this.props.type || 'video/x-flv'}
/>
);
return <source src={this.props.src} type={this.props.type || 'video/x-flv'} />;
}
}
@ -118,20 +117,20 @@ export interface HlsSourceProps {
autoPlay?: boolean;
actions?: any;
order?: number;
};
}
export class HlsSource extends React.Component<HlsSourceProps, any> {
hls: any;
componentDidMount() {
let { src, video, config, manager, isLive, autoPlay, actions } = this.props;
let {src, video, config, manager, isLive, autoPlay, actions} = this.props;
(require as any)(['hls.js'], (Hls: any) => {
// load hls video source base on hls.js
if (Hls.isSupported()) {
video = video || manager.video && manager.video.video;
video = video || (manager.video && manager.video.video);
let hls = this.hls = new Hls({
autoStartLoad: false
});
let hls = (this.hls = new Hls({
autoStartLoad: false,
}));
hls.attachMedia(video);
hls.loadSource(src);
@ -167,12 +166,7 @@ export class HlsSource extends React.Component<HlsSourceProps, any> {
}
render() {
return (
<source
src={this.props.src}
type={this.props.type || 'application/x-mpegURL'}
/>
);
return <source src={this.props.src} type={this.props.type || 'application/x-mpegURL'} />;
}
}
@ -187,14 +181,14 @@ export interface VideoProps extends RendererProps {
export interface VideoState {
posterInfo?: any;
videoState?: any;
};
}
export default class Video extends React.Component<VideoProps, VideoState> {
static defaultProps = {
columnsCount: 8,
isLive: false,
jumpFrame: true,
aspectRatio: "auto"
aspectRatio: 'auto',
};
frameDom: any;
@ -207,7 +201,7 @@ export default class Video extends React.Component<VideoProps, VideoState> {
this.state = {
posterInfo: null,
videoState: {}
videoState: {},
};
this.frameRef = this.frameRef.bind(this);
@ -223,8 +217,8 @@ export default class Video extends React.Component<VideoProps, VideoState> {
this.setState({
posterInfo: {
width: image.width,
height: image.height
}
height: image.height,
},
});
image = image.onload = null;
};
@ -248,7 +242,7 @@ export default class Video extends React.Component<VideoProps, VideoState> {
player.subscribeToStateChange((state: any) => {
this.setState({
videoState: state
videoState: state,
});
if (!state.paused) {
@ -259,7 +253,6 @@ export default class Video extends React.Component<VideoProps, VideoState> {
currentPlaying = player;
}
if (!this.frameDom || !this.times) {
return;
}
@ -268,7 +261,7 @@ export default class Video extends React.Component<VideoProps, VideoState> {
const times = this.times;
const len = times.length;
while (index < len) {
if (times[index - 1] && state.currentTime <= (times[index + 1] - (times[index + 1] - times[index]) / 2)) {
if (times[index - 1] && state.currentTime <= times[index + 1] - (times[index + 1] - times[index]) / 2) {
break;
} else if (state.currentTime <= times[index]) {
break;
@ -284,7 +277,7 @@ export default class Video extends React.Component<VideoProps, VideoState> {
}
moveCursorToIndex(index: number) {
const { classPrefix: ns } = this.props;
const {classPrefix: ns} = this.props;
if (!this.frameDom || !this.cursorDom) {
return;
}
@ -295,7 +288,12 @@ export default class Video extends React.Component<VideoProps, VideoState> {
const item = items[index];
const frameRect = this.frameDom.getBoundingClientRect();
const rect = item.getBoundingClientRect();
this.cursorDom.setAttribute("style", `width: ${rect.width - 4}px; height: ${rect.height - 4}px; left: ${rect.left + 2 - frameRect.left}px; top: ${rect.top + 2 - frameRect.top}px;`);
this.cursorDom.setAttribute(
'style',
`width: ${rect.width - 4}px; height: ${rect.height - 4}px; left: ${rect.left +
2 -
frameRect.left}px; top: ${rect.top + 2 - frameRect.top}px;`
);
}
}
@ -316,14 +314,7 @@ export default class Video extends React.Component<VideoProps, VideoState> {
}
renderFrames() {
let {
frames,
framesClassName,
columnsCount,
data,
jumpFrame,
classPrefix: ns
} = this.props;
let {frames, framesClassName, columnsCount, data, jumpFrame, classPrefix: ns} = this.props;
if (typeof frames === 'string' && frames[0] === '$') {
frames = resolveVariable(frames, data);
@ -334,7 +325,7 @@ export default class Video extends React.Component<VideoProps, VideoState> {
}
const items: Array<object> = [];
const times: Array<number> = this.times = [];
const times: Array<number> = (this.times = []);
Object.keys(frames).forEach(time => {
if (!frames[time]) {
return;
@ -344,7 +335,7 @@ export default class Video extends React.Component<VideoProps, VideoState> {
items.push({
time: time,
src: frames[time]
src: frames[time],
});
});
@ -366,15 +357,21 @@ export default class Video extends React.Component<VideoProps, VideoState> {
<div className="pull-in-xxs" key={i}>
<div className={`${ns}Hbox ${ns}Video-frameItem`}>
{items.map((item, key) => (
<div className={`${ns}Hbox-col Wrapper--xxs ${ns}Video-frame`} key={key} onClick={() => this.jumpToIndex(i * (columnsCount as number) + key)}>
<div
className={`${ns}Hbox-col Wrapper--xxs ${ns}Video-frame`}
key={key}
onClick={() => this.jumpToIndex(i * (columnsCount as number) + key)}
>
<img className="w-full" alt="poster" src={item.src} />
<div className={`${ns}Text--center`}>{item.time}</div>
</div>
))}
{/* 补充空白 */restCount ? blankArray.map((_, index) => (
<div className={`${ns}Hbox-col Wrapper--xxs`} key={`blank_${index}`} />
)) : null}
{/* 补充空白 */ restCount
? blankArray.map((_, index) => (
<div className={`${ns}Hbox-col Wrapper--xxs`} key={`blank_${index}`} />
))
: null}
</div>
</div>
);
@ -398,18 +395,18 @@ export default class Video extends React.Component<VideoProps, VideoState> {
videoType,
playerClassName,
classPrefix: ns,
aspectRatio
aspectRatio,
} = this.props;
let source = this.props.src || name && data && (data as any)[name] || amisConfig && amisConfig.value;
let source = this.props.src || (name && data && (data as any)[name]) || (amisConfig && amisConfig.value);
const videoState = this.state.videoState;
let highlight = videoState.duration && minVideoDuration && videoState.duration < minVideoDuration;
let src = filter(source, data);
let sourceNode;
if (src && /\.flv(?:$|\?)/.test(src) && isLive || videoType === 'video/x-flv') {
if ((src && /\.flv(?:$|\?)/.test(src) && isLive) || videoType === 'video/x-flv') {
sourceNode = <FlvSource autoPlay={autoPlay} order={999.0} isLive={isLive} src={src} />;
} else if (src && /\.m3u8(?:$|\?)/.test(src) || videoType === 'application/x-mpegURL') {
} else if ((src && /\.m3u8(?:$|\?)/.test(src)) || videoType === 'application/x-mpegURL') {
sourceNode = <HlsSource autoPlay={autoPlay} order={999.0} src={src} />;
} else {
sourceNode = <source src={src} />;
@ -430,25 +427,21 @@ export default class Video extends React.Component<VideoProps, VideoState> {
<Shortcut disabled />
</Player>
{highlight ? (<p className={`m-t-xs ${ns}Text--danger`}> {minVideoDuration} </p>) : null}
{highlight ? <p className={`m-t-xs ${ns}Text--danger`}> {minVideoDuration} </p> : null}
</div>
);
}
renderPosterAndPlayer() {
let {
poster,
data,
locals,
minPosterDimension,
classPrefix: ns
} = this.props;
let {poster, data, locals, minPosterDimension, classPrefix: ns} = this.props;
const posterInfo = this.state.posterInfo || {};
let dimensionClassName = '';
if (posterInfo && minPosterDimension
&& (minPosterDimension.width || minPosterDimension.height)
&& (minPosterDimension.width > posterInfo.width || minPosterDimension.height > posterInfo.height)
if (
posterInfo &&
minPosterDimension &&
(minPosterDimension.width || minPosterDimension.height) &&
(minPosterDimension.width > posterInfo.width || minPosterDimension.height > posterInfo.height)
) {
dimensionClassName = `${ns}Text--danger`;
}
@ -458,24 +451,39 @@ export default class Video extends React.Component<VideoProps, VideoState> {
<div className={`${ns}Hbox`}>
<div className={`${ns}Hbox-col`}>
<div className="Wrapper--xs">
<img onLoad={this.onImageLoaded as any} className="w-full" alt="poster" src={filter(poster, data)} />
<p className="m-t-xs"> <span className={dimensionClassName}>{posterInfo.width || '-'} x {posterInfo.height || '-'}</span>
{dimensionClassName ? (<span> <span className={`${ns}Text--danger`}>{minPosterDimension.width || '-'} x {minPosterDimension.height || '-'}</span></span>) : null}
<img
onLoad={this.onImageLoaded as any}
className="w-full"
alt="poster"
src={filter(poster, data)}
/>
<p className="m-t-xs">
{' '}
<span className={dimensionClassName}>
{posterInfo.width || '-'} x {posterInfo.height || '-'}
</span>
{dimensionClassName ? (
<span>
{' '}
{' '}
<span className={`${ns}Text--danger`}>
{minPosterDimension.width || '-'} x {minPosterDimension.height || '-'}
</span>
</span>
) : null}
</p>
</div>
</div>
<div className={`${ns}Hbox-col`}><div className="Wrapper--xs">{this.renderPlayer()}</div></div>
<div className={`${ns}Hbox-col`}>
<div className="Wrapper--xs">{this.renderPlayer()}</div>
</div>
</div>
</div>
);
}
render() {
let {
splitPoster,
className,
classPrefix: ns
} = this.props;
let {splitPoster, className, classPrefix: ns} = this.props;
return (
<div className={cx(`${ns}Video`, className)} onClick={this.onClick as any}>
@ -486,9 +494,8 @@ export default class Video extends React.Component<VideoProps, VideoState> {
}
}
@Renderer({
test: /(^|\/)video$/,
name: 'video'
name: 'video',
})
export class VideoRenderer extends Video { };
export class VideoRenderer extends Video {}

View File

@ -1,27 +1,16 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import Scoped, { ScopedContext, IScopedContext } from '../Scoped';
import Scoped, {ScopedContext, IScopedContext} from '../Scoped';
import {
Renderer,
RendererProps
} from '../factory';
import {Renderer, RendererProps} from '../factory';
import {ServiceStore, IServiceStore} from '../store/service';
import {
Api,
SchemaNode,
Schema,
Action
} from '../types';
import {
filter,
evalExpression
} from '../utils/tpl';
import {Api, SchemaNode, Schema, Action} from '../types';
import {filter, evalExpression} from '../utils/tpl';
import cx = require('classnames');
import { observer } from 'mobx-react';
import { createObject, until } from '../utils/helper';
import { buildApi, isValidApi, isApiOutdated } from '../utils/api';
import { IFormStore } from '../store/form';
import {observer} from 'mobx-react';
import {createObject, until} from '../utils/helper';
import {buildApi, isValidApi, isApiOutdated} from '../utils/api';
import {IFormStore} from '../store/form';
export type TabProps = Schema & {
title?: string; // 标题
@ -40,7 +29,7 @@ export interface WizardProps extends RendererProps {
actionNextSaveLabel?: string;
actionFinishLabel?: string;
mode?: 'horizontal' | 'vertical';
onFinished: (values:object, action:any) => any;
onFinished: (values: object, action: any) => any;
}
export interface WizardState {
@ -48,8 +37,7 @@ export interface WizardState {
}
export default class Wizard extends React.Component<WizardProps, WizardState> {
static defaultProps:Partial<WizardProps>= {
static defaultProps: Partial<WizardProps> = {
mode: 'horizontal', // vertical
readOnly: false,
messages: {},
@ -57,29 +45,29 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
actionPrevLabel: '上一步',
actionNextLabel: '下一步',
actionNextSaveLabel: '保存并下一步',
actionFinishLabel: '完成'
actionFinishLabel: '完成',
};
static propsList: Array<string> = [
"steps",
"mode",
"messages",
"actionClassName",
"actionPrevLabel",
"actionNextLabel",
"actionNextSaveLabel",
"actionFinishLabel",
"onFinished"
'steps',
'mode',
'messages',
'actionClassName',
'actionPrevLabel',
'actionNextLabel',
'actionNextSaveLabel',
'actionFinishLabel',
'onFinished',
];
dom:any;
form:any;
asyncCancel:() => void;
constructor(props:WizardProps) {
dom: any;
form: any;
asyncCancel: () => void;
constructor(props: WizardProps) {
super(props);
this.state = {
currentStep: -1 // init 完后会设置成 1
currentStep: -1, // init 完后会设置成 1
};
this.handleAction = this.handleAction.bind(this);
@ -100,11 +88,8 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
initFinishedField,
store,
data,
messages: {
fetchSuccess,
fetchFailed
},
onInit
messages: {fetchSuccess, fetchFailed},
onInit,
} = this.props;
if (initApi && initFetch !== false && (!initApi.sendOn || evalExpression(initApi.sendOn, data))) {
@ -117,44 +102,45 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
return;
}
return until(() => store.checkRemote(initAsyncApi, store.data)
, (ret:any) => ret && ret[initFinishedField || 'finished']
, (cancel) => this.asyncCancel = cancel);
}
return until(
() => store.checkRemote(initAsyncApi, store.data),
(ret: any) => ret && ret[initFinishedField || 'finished'],
cancel => (this.asyncCancel = cancel)
);
},
})
.then(value => {
onInit && onInit(store.data);
if (value && value.data && typeof value.data.step === 'number') {
this.setState({
currentStep: value.data.step
currentStep: value.data.step,
});
} else {
this.setState({
currentStep: 1
currentStep: 1,
});
}
return value;
});
} else {
this.setState({
currentStep: 1
}, () => onInit && onInit(store.data));
this.setState(
{
currentStep: 1,
},
() => onInit && onInit(store.data)
);
}
}
componentDidUpdate(prevProps:WizardProps) {
componentDidUpdate(prevProps: WizardProps) {
const props = this.props;
const {
store,
fetchSuccess,
fetchFailed
} = props;
const {store, fetchSuccess, fetchFailed} = props;
if (isApiOutdated(prevProps.initApi, props.initApi, prevProps.data, props.data)) {
store.fetchData(props.initApi, store.data, {
successMessage: fetchSuccess,
errorMessage: fetchFailed
errorMessage: fetchFailed,
});
}
}
@ -163,16 +149,16 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
this.asyncCancel && this.asyncCancel();
}
gotoStep(index:number) {
gotoStep(index: number) {
const steps = this.props.steps || [];
index = Math.max(Math.min(steps.length, index), 1);
this.setState({
currentStep: index
currentStep: index,
});
}
formRef(ref:any) {
formRef(ref: any) {
if (ref) {
while (ref && ref.getWrappedInstance) {
ref = ref.getWrappedInstance();
@ -183,15 +169,15 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
}
}
submitToTarget(target:string, values:object) {
submitToTarget(target: string, values: object) {
throw new Error('Please implements this!');
}
reloadTarget(target:string, data:any) {
reloadTarget(target: string, data: any) {
throw new Error('Please implements this!');
}
domRef(ref:any) {
domRef(ref: any) {
this.dom = ref;
}
@ -199,50 +185,37 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
return this.dom;
}
handleAction(e: React.UIEvent<any> | void, action: Action, data: object, throwErrors?:boolean) {
const {
onAction,
store
} = this.props;
handleAction(e: React.UIEvent<any> | void, action: Action, data: object, throwErrors?: boolean) {
const {onAction, store} = this.props;
if (action.actionType === 'next' || action.type === 'submit') {
this.form.doAction({
...action,
actionType: 'submit'
}, data);
this.form.doAction(
{
...action,
actionType: 'submit',
},
data
);
} else if (action.actionType === 'prev') {
this.gotoStep(this.state.currentStep - 1);
} else if (action.type === 'reset') {
this.form.reset();
} else if (action.actionType === 'dialog') {
store.openDialog(data);
} else if (onAction) {
} else if (onAction) {
onAction(e, action, data);
}
}
handleChange(values:object) {
const {
store
} = this.props;
handleChange(values: object) {
const {store} = this.props;
store.updateData(values);
}
// 接管里面 form 的提交,不能直接让 form 提交,因为 wizard 自己需要知道进度。
handleSubmit(values:object, action:Action) {
const {
store,
steps,
api,
asyncApi,
finishedField,
target,
redirect,
reload,
env,
onFinished
} = this.props;
handleSubmit(values: object, action: Action) {
const {store, steps, api, asyncApi, finishedField, target, redirect, reload, env, onFinished} = this.props;
const step = steps[this.state.currentStep - 1];
store.updateData(values);
@ -250,9 +223,10 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
if (this.state.currentStep < steps.length) {
let finnalAsyncApi = action.asyncApi || asyncApi;
finnalAsyncApi && store.updateData({
[finishedField || 'finished']: false
});
finnalAsyncApi &&
store.updateData({
[finishedField || 'finished']: false,
});
if (step.api || action.api) {
store
@ -261,30 +235,34 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
if (!finnalAsyncApi || store.data[finishedField || 'finished']) {
return;
}
return until(() => store.checkRemote(finnalAsyncApi as Api, store.data)
, (ret:any) => ret && ret[finishedField || 'finished']
, (cancel) => this.asyncCancel = cancel);
}
return until(
() => store.checkRemote(finnalAsyncApi as Api, store.data),
(ret: any) => ret && ret[finishedField || 'finished'],
cancel => (this.asyncCancel = cancel)
);
},
})
.then(() => this.gotoStep(this.state.currentStep + 1))
.catch(e => {
// do nothing
})
} else {
});
} else {
this.gotoStep(this.state.currentStep + 1);
}
} else { // 最后一步
}
} else {
// 最后一步
if (target) {
this.submitToTarget(target, store.data);
} else if (action.api || step.api || api) {
let finnalAsyncApi = action.asyncApi || step.asyncApi || asyncApi;
finnalAsyncApi && store.updateData({
[finishedField || 'finished']: false
});
finnalAsyncApi &&
store.updateData({
[finishedField || 'finished']: false,
});
const formStore = this.form ? this.form.props.store as IFormStore : store;
const formStore = this.form ? (this.form.props.store as IFormStore) : store;
store.markSaving(true);
formStore
@ -293,11 +271,13 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
if (!finnalAsyncApi || store.data[finishedField || 'finished']) {
return;
}
return until(() => store.checkRemote(finnalAsyncApi as Api, store.data)
, (ret:any) => ret && ret[finishedField || 'finished']
, (cancel) => this.asyncCancel = cancel);
}
return until(
() => store.checkRemote(finnalAsyncApi as Api, store.data),
(ret: any) => ret && ret[finishedField || 'finished'],
cancel => (this.asyncCancel = cancel)
);
},
})
.then(value => {
store.markSaving(false);
@ -305,13 +285,13 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
// 如果是 false 后面的操作就不执行
return value;
}
if (redirect) {
env.updateLocation(filter(redirect, store.data));
} else if (reload) {
this.reloadTarget(reload, store.data);
}
return value;
})
.catch(e => {
@ -324,10 +304,8 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
return false;
}
handleDialogConfirm(values: object[], action:Action, ctx:any, targets:Array<any>) {
const {
store
} = this.props;
handleDialogConfirm(values: object[], action: Action, ctx: any, targets: Array<any>) {
const {store} = this.props;
if (action.mergeData && values.length === 1 && values[0] && targets[0].props.type === 'form') {
store.updateData(values[0]);
@ -337,48 +315,44 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
}
handleDialogClose() {
const {
store
} = this.props;
const {store} = this.props;
store.closeDialog();
}
renderSteps() {
const {
steps,
store,
mode,
classPrefix: ns
} = this.props;
const {steps, store, mode, classPrefix: ns} = this.props;
const currentStep = this.state.currentStep;
return (
<div className={`${ns}Wizard-steps clearfix ${ns}Wizard--${mode}`} id="form-wizard">
{Array.isArray(steps) && steps.length ? (
<ul>
{steps.map((step, key) => {
const canJump = isJumpable(step, key, currentStep, store.data);
return (
<li
key={key}
className={cx({
'is-complete': canJump,
'is-active': currentStep === key + 1
})}
onClick={() => canJump ? this.gotoStep(key + 1) : null}
>
<span
className={cx("Badge", {
// 'Badge--success': canJump && currentStep != key + 1,
'Badge--info': currentStep === key + 1 || canJump && currentStep != key + 1
<ul>
{steps.map((step, key) => {
const canJump = isJumpable(step, key, currentStep, store.data);
return (
<li
key={key}
className={cx({
'is-complete': canJump,
'is-active': currentStep === key + 1,
})}
>{key + 1}</span>
{step.title || step.label || `${key + 1}`}
</li>
);
})}
</ul>
onClick={() => (canJump ? this.gotoStep(key + 1) : null)}
>
<span
className={cx('Badge', {
// 'Badge--success': canJump && currentStep != key + 1,
'Badge--info':
currentStep === key + 1 || (canJump && currentStep != key + 1),
})}
>
{key + 1}
</span>
{step.title || step.label || `${key + 1}`}
</li>
);
})}
</ul>
) : null}
</div>
);
@ -403,7 +377,7 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
if (!Array.isArray(steps)) {
return null;
}
const currentStepIndex = this.state.currentStep;
const nextStep = steps[currentStepIndex];
const prevStep = steps[currentStepIndex - 2];
@ -415,58 +389,62 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
}
const prevCanJump = prevStep ? isJumpable(prevStep, currentStepIndex - 2, currentStepIndex, store.data) : false;
if (step.actions && Array.isArray(step.actions)) {
return step.actions.length ? (
<div className={cx('Panel-footer')}>
{step.actions.map((action:Action, index:number) => render(`action/${index}`, action, {
key: index,
onAction: this.handleAction,
disabled: action.disabled
|| waiting
|| disabled
|| action.actionType === 'prev' && !prevCanJump
|| action.actionType === 'next' && readOnly && (!!step.api || !nextStep)
}))}
{step.actions.map((action: Action, index: number) =>
render(`action/${index}`, action, {
key: index,
onAction: this.handleAction,
disabled:
action.disabled ||
waiting ||
disabled ||
(action.actionType === 'prev' && !prevCanJump) ||
(action.actionType === 'next' && readOnly && (!!step.api || !nextStep)),
})
)}
</div>
) : null;
}
return (
<div className={cx('Panel-footer')}>
{render(`prev-btn`, {
type: 'button',
label: actionPrevLabel,
actionType: 'prev',
className: actionClassName
}, {
disabled: waiting || !prevCanJump || disabled,
onAction: this.handleAction
})}
{render(`next-btn`, {
type: 'button',
label: !nextStep ? actionFinishLabel : !step.api ? actionNextLabel : actionNextSaveLabel,
actionType: 'next',
primary: !nextStep || !!step.api,
className: actionClassName
}, {
disabled: waiting || disabled || readOnly && (!!step.api || !nextStep),
onAction: this.handleAction
})}
{render(
`prev-btn`,
{
type: 'button',
label: actionPrevLabel,
actionType: 'prev',
className: actionClassName,
},
{
disabled: waiting || !prevCanJump || disabled,
onAction: this.handleAction,
}
)}
{render(
`next-btn`,
{
type: 'button',
label: !nextStep ? actionFinishLabel : !step.api ? actionNextLabel : actionNextSaveLabel,
actionType: 'next',
primary: !nextStep || !!step.api,
className: actionClassName,
},
{
disabled: waiting || disabled || (readOnly && (!!step.api || !nextStep)),
onAction: this.handleAction,
}
)}
</div>
);
}
render() {
const {
className,
steps,
render,
store,
mode,
classPrefix: ns
} = this.props;
const {className, steps, render, store, mode, classPrefix: ns} = this.props;
const currentStep = this.state.currentStep;
const step = Array.isArray(steps) ? steps[currentStep - 1] : null;
@ -475,57 +453,74 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
<div ref={this.domRef} className={cx(`${ns}Panel ${ns}Panel--default ${ns}Wizard`, className)}>
{this.renderSteps()}
<div className={`${ns}Wizard-stepContent clearfix`}>
{step ? render('body', {
...step,
type: 'form',
wrapWithPanel: false,
{step ? (
render(
'body',
{
...step,
type: 'form',
wrapWithPanel: false,
// 接口相关需要外部来接管
api: null
}, {
key: this.state.currentStep,
ref: this.formRef,
onSubmit: this.handleSubmit,
onAction: this.handleAction,
disabled: store.loading,
popOverContainer: this.getPopOverContainer,
onChange: this.handleChange
}) : currentStep === -1 ? '初始中。。' : (
// 接口相关需要外部来接管
api: null,
},
{
key: this.state.currentStep,
ref: this.formRef,
onSubmit: this.handleSubmit,
onAction: this.handleAction,
disabled: store.loading,
popOverContainer: this.getPopOverContainer,
onChange: this.handleChange,
}
)
) : currentStep === -1 ? (
'初始中。。'
) : (
<p className="text-danger"></p>
)}
</div>
{render('dialog', {
...(store.action as Action) && (store.action as Action).dialog as object,
type: 'dialog'
}, {
key: 'dialog',
data: store.dialogData,
onConfirm: this.handleDialogConfirm,
onClose: this.handleDialogClose,
show: store.dialogOpen
})}
{render(
'dialog',
{
...((store.action as Action) && ((store.action as Action).dialog as object)),
type: 'dialog',
},
{
key: 'dialog',
data: store.dialogData,
onConfirm: this.handleDialogConfirm,
onClose: this.handleDialogClose,
show: store.dialogOpen,
}
)}
{this.renderActions()}
{store.loading ? render('spinner', {
type: 'spinner',
overlay: true,
size: 'lg'
}) : null}
{store.loading
? render('spinner', {
type: 'spinner',
overlay: true,
size: 'lg',
})
: null}
</div>
);
}
}
function isJumpable(step:any, index:number, currentStep:number, data:any) {
function isJumpable(step: any, index: number, currentStep: number, data: any) {
let canJump = false;
if (step && step.hasOwnProperty('jumpable')) {
canJump = step.jumpable;
} else if (step && step.jumpableOn) {
canJump = evalExpression(step.jumpableOn, createObject(data, {
currentStep
}));
canJump = evalExpression(
step.jumpableOn,
createObject(data, {
currentStep,
})
);
} else {
canJump = index + 1 < currentStep;
}
@ -537,7 +532,7 @@ function isJumpable(step:any, index:number, currentStep:number, data:any) {
test: /(^|\/)wizard$/,
storeType: ServiceStore.name,
name: 'wizard',
isolateScope: true
isolateScope: true,
})
export class WizardRenderer extends Wizard {
static contextType = ScopedContext;
@ -556,17 +551,17 @@ export class WizardRenderer extends Wizard {
return this.handleAction(undefined, action, data, throwErrors);
}
submitToTarget(target:string, values:object) {
submitToTarget(target: string, values: object) {
const scoped = this.context as IScopedContext;
scoped.send(target, values);
}
reloadTarget(target:string, data:any) {
reloadTarget(target: string, data: any) {
const scoped = this.context as IScopedContext;
scoped.reload(target, data);
}
handleDialogConfirm(values: object[], action:Action, ctx:any, targets:Array<any>) {
handleDialogConfirm(values: object[], action: Action, ctx: any, targets: Array<any>) {
super.handleDialogConfirm(values, action, ctx, targets);
const store = this.props.store;

View File

@ -1,49 +1,35 @@
import * as React from 'react';
import {
Renderer,
RendererProps
} from '../factory';
import {Renderer, RendererProps} from '../factory';
import {SchemaNode} from '../types';
import * as cx from 'classnames';
export interface WrapperProps extends RendererProps {
body?: SchemaNode;
className?: string;
children?: JSX.Element | ((props?:any) => JSX.Element);
children?: JSX.Element | ((props?: any) => JSX.Element);
size?: 'xs' | 'sm' | 'md' | 'lg' | 'none';
}
export default class Wrapper extends React.Component<WrapperProps, object> {
static propsList: Array<string> = [
"body",
"className",
"children",
"size"
];
static defaultProps:Partial<WrapperProps>= {
static propsList: Array<string> = ['body', 'className', 'children', 'size'];
static defaultProps: Partial<WrapperProps> = {
className: 'bg-white',
};
renderBody():JSX.Element | null {
const {
children,
body,
render
} = this.props;
renderBody(): JSX.Element | null {
const {children, body, render} = this.props;
return children ? (
typeof children === 'function' ? children(this.props) as JSX.Element : children as JSX.Element
) : body ? (
render('body', body) as JSX.Element
) : null;
return children
? typeof children === 'function'
? (children(this.props) as JSX.Element)
: (children as JSX.Element)
: body
? (render('body', body) as JSX.Element)
: null;
}
render() {
const {
className,
size,
classnames: cx
} = this.props;
const {className, size, classnames: cx} = this.props;
return (
<div
@ -57,6 +43,6 @@ export default class Wrapper extends React.Component<WrapperProps, object> {
@Renderer({
test: /(^|\/)wrapper$/,
name: 'wrapper'
name: 'wrapper',
})
export class WrapperRenderer extends Wrapper {};
export class WrapperRenderer extends Wrapper {}

View File

@ -11,35 +11,35 @@ const bsMapping: {
} = {
level: 'bsStyle',
classPrefix: 'bsClass',
size: 'bsSize'
size: 'bsSize',
};
/**
* bootstrap bs
*
* @param {Object} rawProps
* @return {Object}
*/
export const props2BsProps = (rawProps: { [propName: string]: any; }) => {
let props: { [propName: string]: any; } = {};
* bootstrap bs
*
* @param {Object} rawProps
* @return {Object}
*/
export const props2BsProps = (rawProps: {[propName: string]: any}) => {
let props: {[propName: string]: any} = {};
Object.keys(rawProps).forEach(key => props[bsMapping[key] || key] = rawProps[key]);
Object.keys(rawProps).forEach(key => (props[bsMapping[key] || key] = rawProps[key]));
return props;
};
/**
* props2BsProps hoc
*
* @param {*} ComposedComponent
* @return {Component}
*/
export const props2BsPropsHoc: (ComposedComponent: React.ComponentType<any>) => React.ComponentType<any> = (ComposedComponent) => {
* props2BsProps hoc
*
* @param {*} ComposedComponent
* @return {Component}
*/
export const props2BsPropsHoc: (
ComposedComponent: React.ComponentType<any>
) => React.ComponentType<any> = ComposedComponent => {
class BsComponent extends React.Component<any> {
render() {
return (
<ComposedComponent {...props2BsProps(this.props)} />
);
return <ComposedComponent {...props2BsProps(this.props)} />;
}
}
@ -63,15 +63,13 @@ function getContainerDimensions(containerNode: any) {
width = window.innerWidth;
height = window.innerHeight;
scroll =
getScrollTop(ownerDocument(containerNode).documentElement) ||
getScrollTop(containerNode);
scroll = getScrollTop(ownerDocument(containerNode).documentElement) || getScrollTop(containerNode);
} else {
({ width, height } = getOffset(containerNode));
({width, height} = getOffset(containerNode));
scroll = getScrollTop(containerNode);
}
return { width, height, scroll };
return {width, height, scroll};
}
function getTopDelta(top: any, overlayHeight: any, container: any, padding: any) {
@ -107,16 +105,14 @@ function getLeftDelta(left: any, overlayWidth: any, container: any, padding: any
return 0;
}
export function calculatePosition(
placement: any, overlayNode: any, target: any, container: any, padding: any
) {
export function calculatePosition(placement: any, overlayNode: any, target: any, container: any, padding: any) {
const childOffset = container.tagName === 'BODY' ? getOffset(target) : getPosition(target, container);
const {
height: overlayHeight,
width: overlayWidth
} = getOffset(overlayNode);
const {height: overlayHeight, width: overlayWidth} = getOffset(overlayNode);
let positionLeft = 0, positionTop = 0, arrowOffsetLeft: any = '', arrowOffsetTop: any = '';
let positionLeft = 0,
positionTop = 0,
arrowOffsetLeft: any = '',
arrowOffsetTop: any = '';
if (~placement.indexOf('-')) {
const tests = placement.split(/\s+/);
@ -127,14 +123,18 @@ export function calculatePosition(
myX = myX || atX;
myY = myY || atY;
positionLeft = atX === 'left'
? childOffset.left : atX === 'right'
? (childOffset.left + childOffset.width)
: (childOffset.left + childOffset.width / 2);
positionTop = atY === 'top'
? childOffset.top : atY === 'bottom'
? (childOffset.top + childOffset.height)
: (childOffset.top + childOffset.height / 2);
positionLeft =
atX === 'left'
? childOffset.left
: atX === 'right'
? childOffset.left + childOffset.width
: childOffset.left + childOffset.width / 2;
positionTop =
atY === 'top'
? childOffset.top
: atY === 'bottom'
? childOffset.top + childOffset.height
: childOffset.top + childOffset.height / 2;
positionLeft -= myX === 'left' ? 0 : myX === 'right' ? overlayWidth : overlayWidth / 2;
positionTop -= myY === 'top' ? 0 : myY === 'bottom' ? overlayHeight : overlayHeight / 2;
@ -146,12 +146,14 @@ export function calculatePosition(
x: clip.x + positionLeft - childOffset.left,
y: clip.y + positionTop - childOffset.top,
width: overlayWidth,
height: overlayHeight
}
height: overlayHeight,
};
if (
transformed.x > 0 && (transformed.x + transformed.width) < window.innerWidth
&& transformed.y > 0 && (transformed.y + transformed.height) < window.innerHeight
transformed.x > 0 &&
transformed.x + transformed.width < window.innerWidth &&
transformed.y > 0 &&
transformed.y + transformed.height < window.innerHeight
) {
break;
}
@ -170,12 +172,10 @@ export function calculatePosition(
}
positionTop = childOffset.top + (childOffset.height - overlayHeight) / 2;
const topDelta = getTopDelta(
positionTop, overlayHeight, container, padding
);
const topDelta = getTopDelta(positionTop, overlayHeight, container, padding);
positionTop += topDelta;
arrowOffsetTop = 50 * (1 - 2 * topDelta / overlayHeight) + '%';
arrowOffsetTop = 50 * (1 - (2 * topDelta) / overlayHeight) + '%';
} else if (placement === 'top' || placement === 'bottom') {
// atY = placement;
// atX = myX = 'center';
@ -187,22 +187,18 @@ export function calculatePosition(
}
positionLeft = childOffset.left + (childOffset.width - overlayWidth) / 2;
const leftDelta = getLeftDelta(
positionLeft, overlayWidth, container, padding
);
const leftDelta = getLeftDelta(positionLeft, overlayWidth, container, padding);
positionLeft += leftDelta;
arrowOffsetLeft = 50 * (1 - 2 * leftDelta / overlayHeight) + '%';
} else if (placement = 'center') {
arrowOffsetLeft = 50 * (1 - (2 * leftDelta) / overlayHeight) + '%';
} else if ((placement = 'center')) {
// atX = atY = myX = myY = 'center';
positionLeft = childOffset.left + (childOffset.width - overlayWidth) / 2;
positionTop = childOffset.top + (childOffset.height - overlayHeight) / 2;
arrowOffsetLeft = arrowOffsetTop = void 0;
} else {
throw new Error(
`calcOverlayPosition(): No such placement of "${placement}" found.`
);
throw new Error(`calcOverlayPosition(): No such placement of "${placement}" found.`);
}
return { positionLeft, positionTop, arrowOffsetLeft, arrowOffsetTop };
}
return {positionLeft, positionTop, arrowOffsetLeft, arrowOffsetTop};
}