代码格式化
This commit is contained in:
parent
39afde9f97
commit
43a2b42c7c
|
@ -6,5 +6,6 @@
|
|||
"semi": true,
|
||||
"trailingComma": "es5",
|
||||
"bracketSpacing": false,
|
||||
"arrowParens": "avoid"
|
||||
"arrowParens": "avoid",
|
||||
"jsxBracketSameLine": false
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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>;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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
|
@ -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>
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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<{}> {}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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',
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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<{}> {}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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>;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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
|
@ -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 {}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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
|
@ -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 {}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue