代码格式化
This commit is contained in:
parent
39afde9f97
commit
43a2b42c7c
|
@ -6,5 +6,6 @@
|
||||||
"semi": true,
|
"semi": true,
|
||||||
"trailingComma": "es5",
|
"trailingComma": "es5",
|
||||||
"bracketSpacing": false,
|
"bracketSpacing": false,
|
||||||
"arrowParens": "avoid"
|
"arrowParens": "avoid",
|
||||||
|
"jsxBracketSameLine": false
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,8 @@
|
||||||
"start": "fis3 server start --www ./public --port 8888 --no-daemon --no-browse",
|
"start": "fis3 server start --www ./public --port 8888 --no-daemon --no-browse",
|
||||||
"stop": "fis3 server stop",
|
"stop": "fis3 server stop",
|
||||||
"dev": "fis3 release -cwd ./public",
|
"dev": "fis3 release -cwd ./public",
|
||||||
"publish2npm": "sh publish.sh && npm publish"
|
"publish2npm": "sh publish.sh && npm publish",
|
||||||
|
"prettier": "prettier --write src/**/*.tsx"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -120,6 +121,7 @@
|
||||||
"lint-staged": "^8.1.6",
|
"lint-staged": "^8.1.6",
|
||||||
"marked": "^0.3.7",
|
"marked": "^0.3.7",
|
||||||
"mobx-wiretap": "^0.12.0",
|
"mobx-wiretap": "^0.12.0",
|
||||||
|
"prettier": "1.17.0",
|
||||||
"react-frame-component": "^2.0.0",
|
"react-frame-component": "^2.0.0",
|
||||||
"react-router": "3.2.0",
|
"react-router": "3.2.0",
|
||||||
"react-test-renderer": "^16.8.6",
|
"react-test-renderer": "^16.8.6",
|
||||||
|
|
|
@ -1,23 +1,46 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
import {filter} from '../utils/tpl';
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import {
|
|
||||||
filter
|
|
||||||
} from '../utils/tpl';
|
|
||||||
import Button from '../components/Button';
|
import Button from '../components/Button';
|
||||||
import pick = require('lodash/pick');
|
import pick = require('lodash/pick');
|
||||||
const ActionProps = [
|
const ActionProps = [
|
||||||
'dialog', 'drawer', 'url', 'link', 'confirmText', 'tooltip', 'disabledTip', 'className', 'asyncApi', 'redirect',
|
'dialog',
|
||||||
'size', 'level', 'primary', 'feedback', 'api', 'blank', 'tooltipPlacement', 'to', 'content', 'required',
|
'drawer',
|
||||||
'type', 'actionType', 'label', 'icon', 'reload', 'target', 'close', 'messages', 'mergeData', 'index',
|
'url',
|
||||||
'copy', 'content'
|
'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 {filterContents} from './Remark';
|
||||||
import { ClassNamesFn, themeable } from '../theme';
|
import {ClassNamesFn, themeable} from '../theme';
|
||||||
import { Omit } from '../types';
|
import {Omit} from '../types';
|
||||||
import { autobind } from '../utils/helper';
|
import {autobind} from '../utils/helper';
|
||||||
|
|
||||||
export interface ActionProps {
|
export interface ActionProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
@ -28,13 +51,13 @@ export interface ActionProps {
|
||||||
iconClassName?: string;
|
iconClassName?: string;
|
||||||
size?: 'xs' | 'sm' | 'md' | 'lg';
|
size?: 'xs' | 'sm' | 'md' | 'lg';
|
||||||
level?: 'info' | 'success' | 'warning' | 'danger' | 'link';
|
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;
|
isCurrentUrl?: (link: string) => boolean;
|
||||||
onClick?: (e:React.MouseEvent<any>) => void;
|
onClick?: (e: React.MouseEvent<any>) => void;
|
||||||
primary?: boolean;
|
primary?: boolean;
|
||||||
activeClassName: string;
|
activeClassName: string;
|
||||||
componentClass: React.ReactType;
|
componentClass: React.ReactType;
|
||||||
tooltipPlacement: "bottom" | "top" | "right" | "left" | undefined;
|
tooltipPlacement: 'bottom' | 'top' | 'right' | 'left' | undefined;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
block?: boolean;
|
block?: boolean;
|
||||||
data?: any;
|
data?: any;
|
||||||
|
@ -52,22 +75,18 @@ export interface ActionProps {
|
||||||
const allowedType = ['button', 'submit', 'reset'];
|
const allowedType = ['button', 'submit', 'reset'];
|
||||||
|
|
||||||
export class Action extends React.Component<ActionProps> {
|
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',
|
type: 'button',
|
||||||
componentClass: 'button',
|
componentClass: 'button',
|
||||||
tooltipPlacement: 'bottom',
|
tooltipPlacement: 'bottom',
|
||||||
activeClassName: 'is-active'
|
activeClassName: 'is-active',
|
||||||
};
|
};
|
||||||
|
|
||||||
dom:any;
|
dom: any;
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
handleAction(e:React.MouseEvent<any>) {
|
handleAction(e: React.MouseEvent<any>) {
|
||||||
const {
|
const {onAction, onClick, disabled} = this.props;
|
||||||
onAction,
|
|
||||||
onClick,
|
|
||||||
disabled
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
onClick && onClick(e);
|
onClick && onClick(e);
|
||||||
|
|
||||||
|
@ -105,7 +124,7 @@ export class Action extends React.Component<ActionProps> {
|
||||||
active,
|
active,
|
||||||
activeLevel,
|
activeLevel,
|
||||||
tooltipContainer,
|
tooltipContainer,
|
||||||
classnames: cx
|
classnames: cx,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
let isActive = !!active;
|
let isActive = !!active;
|
||||||
|
@ -118,20 +137,20 @@ export class Action extends React.Component<ActionProps> {
|
||||||
<a
|
<a
|
||||||
className={cx(className, {
|
className={cx(className, {
|
||||||
[activeClassName || 'is-active']: isActive,
|
[activeClassName || 'is-active']: isActive,
|
||||||
'is-disabled': disabled
|
'is-disabled': disabled,
|
||||||
})}
|
})}
|
||||||
onClick={this.handleAction}
|
onClick={this.handleAction}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
{icon ? <i className={cx('Button-icon', icon)} /> : null}
|
{icon ? <i className={cx('Button-icon', icon)} /> : null}
|
||||||
</a>
|
</a>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
className={cx(className, {
|
className={cx(className, {
|
||||||
[activeClassName || 'is-active']: isActive
|
[activeClassName || 'is-active']: isActive,
|
||||||
})}
|
})}
|
||||||
size={size}
|
size={size}
|
||||||
level={activeLevel && isActive ? activeLevel : (level || (primary ? 'primary': undefined))}
|
level={activeLevel && isActive ? activeLevel : level || (primary ? 'primary' : undefined)}
|
||||||
onClick={this.handleAction}
|
onClick={this.handleAction}
|
||||||
type={type && ~allowedType.indexOf(type) ? type : 'button'}
|
type={type && ~allowedType.indexOf(type) ? type : 'button'}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
@ -141,9 +160,9 @@ export class Action extends React.Component<ActionProps> {
|
||||||
placement={tooltipPlacement}
|
placement={tooltipPlacement}
|
||||||
tooltipContainer={tooltipContainer}
|
tooltipContainer={tooltipContainer}
|
||||||
block={block}
|
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}
|
{icon ? <i className={cx('Button-icon', icon, iconClassName)} /> : null}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
@ -154,53 +173,43 @@ export default themeable(Action);
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)action$/,
|
test: /(^|\/)action$/,
|
||||||
name: 'action'
|
name: 'action',
|
||||||
})
|
})
|
||||||
export class ActionRenderer extends React.Component<RendererProps & Omit<ActionProps, 'onAction' | 'isCurrentUrl' | 'tooltipContainer'> & {
|
export class ActionRenderer extends React.Component<
|
||||||
onAction: (e: React.MouseEvent<any> | void | null, action:object, data:any) => void;
|
RendererProps &
|
||||||
btnDisabled?: boolean;
|
Omit<ActionProps, 'onAction' | 'isCurrentUrl' | 'tooltipContainer'> & {
|
||||||
}> {
|
onAction: (e: React.MouseEvent<any> | void | null, action: object, data: any) => void;
|
||||||
|
btnDisabled?: boolean;
|
||||||
|
}
|
||||||
|
> {
|
||||||
@autobind
|
@autobind
|
||||||
handleAction(e: React.MouseEvent<any> | void | null, action:any) {
|
handleAction(e: React.MouseEvent<any> | void | null, action: any) {
|
||||||
const {
|
const {env, onAction, data} = this.props;
|
||||||
env,
|
|
||||||
onAction,
|
|
||||||
data
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (action.confirmText && env.confirm) {
|
if (action.confirmText && env.confirm) {
|
||||||
env
|
env.confirm(filter(action.confirmText, data)).then(
|
||||||
.confirm(filter(action.confirmText, data))
|
(confirmed: boolean) => confirmed && onAction(e, action, data)
|
||||||
.then((confirmed:boolean) => confirmed && onAction(e, action, data));
|
);
|
||||||
} else {
|
} else {
|
||||||
onAction(e, action, data);
|
onAction(e, action, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
isCurrentAction(link:string) {
|
isCurrentAction(link: string) {
|
||||||
const {
|
const {env, data} = this.props;
|
||||||
env,
|
|
||||||
data
|
|
||||||
} = this.props;
|
|
||||||
return env.isCurrentUrl(filter(link, data));
|
return env.isCurrentUrl(filter(link, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {env, disabled, btnDisabled, ...rest} = this.props;
|
||||||
env,
|
|
||||||
disabled,
|
|
||||||
btnDisabled,
|
|
||||||
...rest
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Action
|
<Action
|
||||||
{...rest}
|
{...rest}
|
||||||
disabled={disabled || btnDisabled}
|
disabled={disabled || btnDisabled}
|
||||||
onAction={this.handleAction}
|
onAction={this.handleAction}
|
||||||
isCurrentUrl={this.isCurrentAction}
|
isCurrentUrl={this.isCurrentAction}
|
||||||
tooltipContainer={env.getModalContainer ? env.getModalContainer() : undefined}
|
tooltipContainer={env.getModalContainer ? env.getModalContainer() : undefined}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -209,18 +218,18 @@ export class ActionRenderer extends React.Component<RendererProps & Omit<ActionP
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)button$/,
|
test: /(^|\/)button$/,
|
||||||
name: 'button'
|
name: 'button',
|
||||||
})
|
})
|
||||||
export class ButtonRenderer extends ActionRenderer {}
|
export class ButtonRenderer extends ActionRenderer {}
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)submit$/,
|
test: /(^|\/)submit$/,
|
||||||
name: 'submit'
|
name: 'submit',
|
||||||
})
|
})
|
||||||
export class SubmitRenderer extends ActionRenderer {}
|
export class SubmitRenderer extends ActionRenderer {}
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)reset$/,
|
test: /(^|\/)reset$/,
|
||||||
name: 'reset'
|
name: 'reset',
|
||||||
})
|
})
|
||||||
export class ResetRenderer extends ActionRenderer {}
|
export class ResetRenderer extends ActionRenderer {}
|
||||||
|
|
|
@ -1,23 +1,14 @@
|
||||||
import { Renderer, RendererProps } from "../factory";
|
import {Renderer, RendererProps} from '../factory';
|
||||||
import React = require("react");
|
import React = require('react');
|
||||||
import Alert, { AlertProps } from "../components/Alert2";
|
import Alert, {AlertProps} from '../components/Alert2';
|
||||||
|
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)alert$/,
|
test: /(^|\/)alert$/,
|
||||||
name: 'alert'
|
name: 'alert',
|
||||||
})
|
})
|
||||||
export class TplRenderer extends React.Component<AlertProps & RendererProps> {
|
export class TplRenderer extends React.Component<AlertProps & RendererProps> {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {render, body, ...rest} = this.props;
|
||||||
render,
|
return <Alert {...rest}>{render('body', body)}</Alert>;
|
||||||
body,
|
|
||||||
...rest
|
|
||||||
} = this.props;
|
|
||||||
return (
|
|
||||||
<Alert {...rest}>
|
|
||||||
{render('body', body)}
|
|
||||||
</Alert>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,32 +1,29 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
import {autobind} from '../utils/helper';
|
||||||
RendererProps
|
import {volumeIcon, muteIcon, playIcon, pauseIcon} from '../components/icons';
|
||||||
} from '../factory';
|
|
||||||
import { autobind } from '../utils/helper';
|
|
||||||
import { volumeIcon, muteIcon, playIcon, pauseIcon} from '../components/icons';
|
|
||||||
|
|
||||||
export interface AudioProps extends RendererProps {
|
export interface AudioProps extends RendererProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
inline?: boolean,
|
inline?: boolean;
|
||||||
src?: string,
|
src?: string;
|
||||||
autoPlay?: boolean,
|
autoPlay?: boolean;
|
||||||
loop?: boolean,
|
loop?: boolean;
|
||||||
rates?: number[]
|
rates?: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AudioState {
|
export interface AudioState {
|
||||||
isReady?: boolean,
|
isReady?: boolean;
|
||||||
muted?: boolean,
|
muted?: boolean;
|
||||||
playing?: boolean,
|
playing?: boolean;
|
||||||
played: number,
|
played: number;
|
||||||
seeking?: boolean,
|
seeking?: boolean;
|
||||||
volume: number,
|
volume: number;
|
||||||
prevVolume: number,
|
prevVolume: number;
|
||||||
loaded?: number,
|
loaded?: number;
|
||||||
playbackRate: number,
|
playbackRate: number;
|
||||||
showHandlePlaybackRate: boolean,
|
showHandlePlaybackRate: boolean;
|
||||||
showHandleVolume: boolean
|
showHandleVolume: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Audio extends React.Component<AudioProps, AudioState> {
|
export class Audio extends React.Component<AudioProps, AudioState> {
|
||||||
|
@ -34,16 +31,19 @@ export class Audio extends React.Component<AudioProps, AudioState> {
|
||||||
progressTimeout: any;
|
progressTimeout: any;
|
||||||
durationTimeout: 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,
|
inline: true,
|
||||||
autoPlay: false,
|
autoPlay: false,
|
||||||
playbackRate: 1,
|
playbackRate: 1,
|
||||||
loop: false,
|
loop: false,
|
||||||
rates: [1.0, 2.0, 4.0],
|
rates: [1.0, 2.0, 4.0],
|
||||||
progressInterval: 1000
|
progressInterval: 1000,
|
||||||
};
|
};
|
||||||
|
|
||||||
state:AudioState = {
|
state: AudioState = {
|
||||||
isReady: false,
|
isReady: false,
|
||||||
muted: false,
|
muted: false,
|
||||||
playing: false,
|
playing: false,
|
||||||
|
@ -54,19 +54,22 @@ export class Audio extends React.Component<AudioProps, AudioState> {
|
||||||
loaded: 0,
|
loaded: 0,
|
||||||
playbackRate: 1.0,
|
playbackRate: 1.0,
|
||||||
showHandlePlaybackRate: false,
|
showHandlePlaybackRate: false,
|
||||||
showHandleVolume: false
|
showHandleVolume: false,
|
||||||
}
|
};
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount() {
|
||||||
clearTimeout(this.progressTimeout);
|
clearTimeout(this.progressTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const autoPlay = this.props.autoPlay;
|
const autoPlay = this.props.autoPlay;
|
||||||
const playing = autoPlay ? true : false;
|
const playing = autoPlay ? true : false;
|
||||||
this.setState({
|
this.setState(
|
||||||
playing: playing
|
{
|
||||||
}, this.progress);
|
playing: playing,
|
||||||
|
},
|
||||||
|
this.progress
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
|
@ -77,33 +80,33 @@ export class Audio extends React.Component<AudioProps, AudioState> {
|
||||||
const duration = this.audio.duration;
|
const duration = this.audio.duration;
|
||||||
const played = currentTime / duration;
|
const played = currentTime / duration;
|
||||||
let playing = this.state.playing;
|
let playing = this.state.playing;
|
||||||
playing = (played != 1 && playing) ? true : false;
|
playing = played != 1 && playing ? true : false;
|
||||||
this.setState({
|
this.setState({
|
||||||
played,
|
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
|
@autobind
|
||||||
audioRef(audio:any) {
|
audioRef(audio: any) {
|
||||||
this.audio = audio;
|
this.audio = audio;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
load() {
|
load() {
|
||||||
this.setState({
|
this.setState({
|
||||||
isReady: true
|
isReady: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
handlePlaybackRate(rate:number) {
|
handlePlaybackRate(rate: number) {
|
||||||
this.audio.playbackRate = rate;
|
this.audio.playbackRate = rate;
|
||||||
this.setState({
|
this.setState({
|
||||||
playbackRate: rate,
|
playbackRate: rate,
|
||||||
showHandlePlaybackRate: false
|
showHandlePlaybackRate: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +120,7 @@ export class Audio extends React.Component<AudioProps, AudioState> {
|
||||||
this.audio.muted = !muted;
|
this.audio.muted = !muted;
|
||||||
this.setState({
|
this.setState({
|
||||||
muted: !muted,
|
muted: !muted,
|
||||||
volume: curVolume
|
volume: curVolume,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,7 +132,7 @@ export class Audio extends React.Component<AudioProps, AudioState> {
|
||||||
let playing = this.state.playing;
|
let playing = this.state.playing;
|
||||||
playing ? this.audio.pause() : this.audio.play();
|
playing ? this.audio.pause() : this.audio.play();
|
||||||
this.setState({
|
this.setState({
|
||||||
playing: !playing
|
playing: !playing,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +147,7 @@ export class Audio extends React.Component<AudioProps, AudioState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
getDuration () {
|
getDuration() {
|
||||||
if (!this.audio || !this.props.src) {
|
if (!this.audio || !this.props.src) {
|
||||||
return '0:00';
|
return '0:00';
|
||||||
}
|
}
|
||||||
|
@ -152,11 +155,11 @@ export class Audio extends React.Component<AudioProps, AudioState> {
|
||||||
this.onDurationCheck();
|
this.onDurationCheck();
|
||||||
return '0:00';
|
return '0:00';
|
||||||
}
|
}
|
||||||
const { duration, seekable } = this.audio;
|
const {duration, seekable} = this.audio;
|
||||||
// on iOS, live streams return Infinity for the duration
|
// on iOS, live streams return Infinity for the duration
|
||||||
// so instead we use the end of the seekable timerange
|
// so instead we use the end of the seekable timerange
|
||||||
if (duration === Infinity && seekable.length > 0) {
|
if (duration === Infinity && seekable.length > 0) {
|
||||||
return seekable.end(seekable.length - 1)
|
return seekable.end(seekable.length - 1);
|
||||||
}
|
}
|
||||||
return this.formatTime(duration);
|
return this.formatTime(duration);
|
||||||
}
|
}
|
||||||
|
@ -172,21 +175,21 @@ export class Audio extends React.Component<AudioProps, AudioState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
onSeekChange(e:any) {
|
onSeekChange(e: any) {
|
||||||
if (!this.props.src) {
|
if (!this.props.src) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const played = e.target.value;
|
const played = e.target.value;
|
||||||
this.setState({ played: played });
|
this.setState({played: played});
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
onSeekMouseDown() {
|
onSeekMouseDown() {
|
||||||
this.setState({ seeking: true });
|
this.setState({seeking: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
onSeekMouseUp(e:any) {
|
onSeekMouseUp(e: any) {
|
||||||
if (!this.state.seeking) {
|
if (!this.state.seeking) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -196,15 +199,15 @@ export class Audio extends React.Component<AudioProps, AudioState> {
|
||||||
|
|
||||||
const loop = this.props.loop;
|
const loop = this.props.loop;
|
||||||
let playing = this.state.playing;
|
let playing = this.state.playing;
|
||||||
playing = (played < 1 || loop) ? playing : false;
|
playing = played < 1 || loop ? playing : false;
|
||||||
this.setState({
|
this.setState({
|
||||||
playing: playing,
|
playing: playing,
|
||||||
seeking: false
|
seeking: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
setVolume(e:any) {
|
setVolume(e: any) {
|
||||||
if (!this.props.src) {
|
if (!this.props.src) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -212,12 +215,12 @@ export class Audio extends React.Component<AudioProps, AudioState> {
|
||||||
this.audio.volume = volume;
|
this.audio.volume = volume;
|
||||||
this.setState({
|
this.setState({
|
||||||
volume: volume,
|
volume: volume,
|
||||||
prevVolume: volume
|
prevVolume: volume,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
formatTime(seconds:number) {
|
formatTime(seconds: number) {
|
||||||
const date = new Date(seconds * 1000);
|
const date = new Date(seconds * 1000);
|
||||||
const hh = date.getUTCHours();
|
const hh = date.getUTCHours();
|
||||||
const mm = date.getUTCMinutes();
|
const mm = date.getUTCMinutes();
|
||||||
|
@ -229,8 +232,8 @@ export class Audio extends React.Component<AudioProps, AudioState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
pad(string:number) {
|
pad(string: number) {
|
||||||
return ('0' + string).slice(-2)
|
return ('0' + string).slice(-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
|
@ -239,39 +242,23 @@ export class Audio extends React.Component<AudioProps, AudioState> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
showHandlePlaybackRate: !this.state.showHandlePlaybackRate
|
showHandlePlaybackRate: !this.state.showHandlePlaybackRate,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
toggleHandleVolume(type:boolean) {
|
toggleHandleVolume(type: boolean) {
|
||||||
if (!this.props.src) {
|
if (!this.props.src) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
showHandleVolume: type
|
showHandleVolume: type,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {className, inline, src, autoPlay, loop, rates, classnames: cx} = this.props;
|
||||||
className,
|
const {playing, played, volume, muted, playbackRate, showHandlePlaybackRate, showHandleVolume} = this.state;
|
||||||
inline,
|
|
||||||
src,
|
|
||||||
autoPlay,
|
|
||||||
loop,
|
|
||||||
rates,
|
|
||||||
classnames: cx
|
|
||||||
} = this.props;
|
|
||||||
const {
|
|
||||||
playing,
|
|
||||||
played,
|
|
||||||
volume,
|
|
||||||
muted,
|
|
||||||
playbackRate,
|
|
||||||
showHandlePlaybackRate,
|
|
||||||
showHandleVolume
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cx(inline ? 'Audio--inline' : '')}>
|
<div className={cx(inline ? 'Audio--inline' : '')}>
|
||||||
|
@ -282,55 +269,75 @@ export class Audio extends React.Component<AudioProps, AudioState> {
|
||||||
autoPlay={autoPlay}
|
autoPlay={autoPlay}
|
||||||
controls
|
controls
|
||||||
muted={muted}
|
muted={muted}
|
||||||
loop={loop}>
|
loop={loop}
|
||||||
<source src={src}/>
|
>
|
||||||
|
<source src={src} />
|
||||||
</audio>
|
</audio>
|
||||||
<div className={cx('Audio', className)}>
|
<div className={cx('Audio', className)}>
|
||||||
{rates && rates.length ?
|
{rates && rates.length ? (
|
||||||
(<div className={cx('Audio-rates')}>
|
<div className={cx('Audio-rates')}>
|
||||||
<div className={cx('Audio-rate')}
|
<div className={cx('Audio-rate')} onClick={this.toggleHandlePlaybackRate}>
|
||||||
onClick={this.toggleHandlePlaybackRate}>
|
|
||||||
x{playbackRate.toFixed(1)}
|
x{playbackRate.toFixed(1)}
|
||||||
</div>
|
</div>
|
||||||
{showHandlePlaybackRate ?
|
{showHandlePlaybackRate ? (
|
||||||
(<div className={cx('Audio-rateControl')}>
|
<div className={cx('Audio-rateControl')}>
|
||||||
{rates.map((rate, index) =>
|
{rates.map((rate, index) => (
|
||||||
<span className={cx('Audio-rateControlItem')}
|
<span
|
||||||
key={index}
|
className={cx('Audio-rateControlItem')}
|
||||||
onClick={() => this.handlePlaybackRate(rate)}>
|
key={index}
|
||||||
|
onClick={() => this.handlePlaybackRate(rate)}
|
||||||
|
>
|
||||||
x{rate.toFixed(1)}
|
x{rate.toFixed(1)}
|
||||||
</span>
|
</span>
|
||||||
)} </div>)
|
))}{' '}
|
||||||
: null}
|
</div>
|
||||||
</div>)
|
) : null}
|
||||||
: (<div className={cx('Audio-rates-holder')}></div>) }
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className={cx('Audio-rates-holder')} />
|
||||||
|
)}
|
||||||
<div className={cx('Audio-play')} onClick={this.handlePlaying}>
|
<div className={cx('Audio-play')} onClick={this.handlePlaying}>
|
||||||
{playing ? pauseIcon : playIcon}
|
{playing ? pauseIcon : playIcon}
|
||||||
</div>
|
</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')}>
|
<div className={cx('Audio-process')}>
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
min={0} max={1} step="any"
|
min={0}
|
||||||
|
max={1}
|
||||||
|
step="any"
|
||||||
value={played || 0}
|
value={played || 0}
|
||||||
onMouseDown={this.onSeekMouseDown}
|
onMouseDown={this.onSeekMouseDown}
|
||||||
onChange={this.onSeekChange}
|
onChange={this.onSeekChange}
|
||||||
onMouseUp={this.onSeekMouseUp}/>
|
onMouseUp={this.onSeekMouseUp}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={cx('Audio-volume')}
|
<div
|
||||||
onMouseEnter={() => this.toggleHandleVolume(true)}
|
className={cx('Audio-volume')}
|
||||||
onMouseLeave={() => this.toggleHandleVolume(false)}>
|
onMouseEnter={() => this.toggleHandleVolume(true)}
|
||||||
{showHandleVolume ?
|
onMouseLeave={() => this.toggleHandleVolume(false)}
|
||||||
(<div className={cx('Audio-volumeControl')}>
|
>
|
||||||
|
{showHandleVolume ? (
|
||||||
|
<div className={cx('Audio-volumeControl')}>
|
||||||
<input
|
<input
|
||||||
type='range' min={0} max={1} step='any'
|
type="range"
|
||||||
|
min={0}
|
||||||
|
max={1}
|
||||||
|
step="any"
|
||||||
value={volume}
|
value={volume}
|
||||||
onChange={this.setVolume} />
|
onChange={this.setVolume}
|
||||||
<div className={cx('Audio-volumeControlIcon')}
|
/>
|
||||||
onClick={this.handleMute}>
|
<div className={cx('Audio-volumeControlIcon')} onClick={this.handleMute}>
|
||||||
{volume > 0 ? volumeIcon : muteIcon}
|
{volume > 0 ? volumeIcon : muteIcon}
|
||||||
</div></div>)
|
</div>
|
||||||
: volume > 0 ? volumeIcon : muteIcon}
|
</div>
|
||||||
|
) : volume > 0 ? (
|
||||||
|
volumeIcon
|
||||||
|
) : (
|
||||||
|
muteIcon
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -340,6 +347,6 @@ export class Audio extends React.Component<AudioProps, AudioState> {
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)audio/,
|
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 * as React from 'react';
|
||||||
import ButtonGroup from './Form/ButtonGroup';
|
import ButtonGroup from './Form/ButtonGroup';
|
||||||
import {
|
import {Renderer} from '../factory';
|
||||||
Renderer
|
|
||||||
} from '../factory';
|
|
||||||
|
|
||||||
|
|
||||||
export default ButtonGroup;
|
export default ButtonGroup;
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)(?:button|action)\-group$/,
|
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 * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
import {Action} from '../types';
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import {
|
|
||||||
Action
|
|
||||||
} from '../types';
|
|
||||||
|
|
||||||
export interface ButtonToolbarProps extends RendererProps {
|
export interface ButtonToolbarProps extends RendererProps {
|
||||||
buttons: Array<Action>;
|
buttons: Array<Action>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ButtonToolbar extends React.Component<ButtonToolbarProps, object> {
|
export default class ButtonToolbar extends React.Component<ButtonToolbarProps, object> {
|
||||||
static propsList: Array<string> = [
|
static propsList: Array<string> = ['buttons'];
|
||||||
"buttons",
|
|
||||||
];
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {buttons, className, classnames: cx, render} = this.props;
|
||||||
buttons,
|
|
||||||
className,
|
|
||||||
classnames: cx,
|
|
||||||
render
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={cx('ButtonToolbar', className)}>
|
||||||
className={cx("ButtonToolbar", className)}
|
{Array.isArray(buttons)
|
||||||
>
|
? buttons.map((button, key) =>
|
||||||
{Array.isArray(buttons) ? buttons.map((button, key) => render(`${key}`, button, {
|
render(`${key}`, button, {
|
||||||
key
|
key,
|
||||||
})) : null}
|
})
|
||||||
|
)
|
||||||
|
: null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -38,6 +28,6 @@ export default class ButtonToolbar extends React.Component<ButtonToolbarProps, o
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)button-toolbar$/,
|
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 * as React from 'react';
|
||||||
import {
|
import {findDOMNode} from 'react-dom';
|
||||||
findDOMNode
|
import {Renderer, RendererProps} from '../factory';
|
||||||
} from 'react-dom';
|
import {SchemaNode, Schema, Action} from '../types';
|
||||||
import {
|
import {filter, evalExpression} from '../utils/tpl';
|
||||||
Renderer,
|
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import {
|
|
||||||
SchemaNode,
|
|
||||||
Schema,
|
|
||||||
Action
|
|
||||||
} from '../types';
|
|
||||||
import {
|
|
||||||
filter, evalExpression
|
|
||||||
} from '../utils/tpl';
|
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
import Checkbox from '../components/Checkbox';
|
import Checkbox from '../components/Checkbox';
|
||||||
import { IItem} from '../store/list';
|
import {IItem} from '../store/list';
|
||||||
import { padArr, isVisible, isDisabled, noop } from '../utils/helper';
|
import {padArr, isVisible, isDisabled, noop} from '../utils/helper';
|
||||||
import { resolveVariable } from '../utils/tpl-builtin';
|
import {resolveVariable} from '../utils/tpl-builtin';
|
||||||
import QuickEdit from './QuickEdit';
|
import QuickEdit from './QuickEdit';
|
||||||
import PopOver from './PopOver';
|
import PopOver from './PopOver';
|
||||||
import { TableCell } from './Table';
|
import {TableCell} from './Table';
|
||||||
import Copyable from './Copyable';
|
import Copyable from './Copyable';
|
||||||
|
|
||||||
export interface CardProps extends RendererProps {
|
export interface CardProps extends RendererProps {
|
||||||
onCheck: (item:IItem) => void;
|
onCheck: (item: IItem) => void;
|
||||||
multiple?: boolean;
|
multiple?: boolean;
|
||||||
highlightClassName?: string;
|
highlightClassName?: string;
|
||||||
hideCheckToggler?: boolean;
|
hideCheckToggler?: boolean;
|
||||||
|
@ -33,8 +22,7 @@ export interface CardProps extends RendererProps {
|
||||||
checkOnItemClick?: boolean;
|
checkOnItemClick?: boolean;
|
||||||
}
|
}
|
||||||
export class Card extends React.Component<CardProps> {
|
export class Card extends React.Component<CardProps> {
|
||||||
|
static defaultProps: Partial<CardProps> = {
|
||||||
static defaultProps:Partial<CardProps> = {
|
|
||||||
className: '',
|
className: '',
|
||||||
avatarClassName: '',
|
avatarClassName: '',
|
||||||
bodyClassName: '',
|
bodyClassName: '',
|
||||||
|
@ -42,10 +30,10 @@ export class Card extends React.Component<CardProps> {
|
||||||
titleClassName: '',
|
titleClassName: '',
|
||||||
highlightClassName: '',
|
highlightClassName: '',
|
||||||
subTitleClassName: '',
|
subTitleClassName: '',
|
||||||
descClassName: ''
|
descClassName: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
static propsList:Array<string> = [
|
static propsList: Array<string> = [
|
||||||
'multiple',
|
'multiple',
|
||||||
'avatarClassName',
|
'avatarClassName',
|
||||||
'bodyClassName',
|
'bodyClassName',
|
||||||
|
@ -54,10 +42,10 @@ export class Card extends React.Component<CardProps> {
|
||||||
'highlightClassName',
|
'highlightClassName',
|
||||||
'subTitleClassName',
|
'subTitleClassName',
|
||||||
'descClassName',
|
'descClassName',
|
||||||
'hideCheckToggler'
|
'hideCheckToggler',
|
||||||
];
|
];
|
||||||
|
|
||||||
constructor(props:CardProps) {
|
constructor(props: CardProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.getPopOverContainer = this.getPopOverContainer.bind(this);
|
this.getPopOverContainer = this.getPopOverContainer.bind(this);
|
||||||
|
@ -68,14 +56,14 @@ export class Card extends React.Component<CardProps> {
|
||||||
this.handleCheck = this.handleCheck.bind(this);
|
this.handleCheck = this.handleCheck.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClick(e:React.MouseEvent<HTMLDivElement>) {
|
handleClick(e: React.MouseEvent<HTMLDivElement>) {
|
||||||
const target:HTMLElement = e.target as HTMLElement;
|
const target: HTMLElement = e.target as HTMLElement;
|
||||||
const ns = this.props.classPrefix;
|
const ns = this.props.classPrefix;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!e.currentTarget.contains(target)
|
!e.currentTarget.contains(target) ||
|
||||||
|| ~['INPUT', 'TEXTAREA'].indexOf(target.tagName)
|
~['INPUT', 'TEXTAREA'].indexOf(target.tagName) ||
|
||||||
|| target.closest(`button, a, .${ns}Form-item`)
|
target.closest(`button, a, .${ns}Form-item`)
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -89,17 +77,16 @@ export class Card extends React.Component<CardProps> {
|
||||||
this.props.onCheck && this.props.onCheck(item);
|
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;
|
const {onAction, item} = this.props;
|
||||||
onAction && onAction(e, action, ctx || item.data);
|
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;
|
const {onQuickChange, item} = this.props;
|
||||||
onQuickChange && onQuickChange(item, values, saveImmediately, saveSlient);
|
onQuickChange && onQuickChange(item, values, saveImmediately, saveSlient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
getPopOverContainer() {
|
getPopOverContainer() {
|
||||||
return findDOMNode(this);
|
return findDOMNode(this);
|
||||||
}
|
}
|
||||||
|
@ -115,24 +102,24 @@ export class Card extends React.Component<CardProps> {
|
||||||
multiple,
|
multiple,
|
||||||
hideCheckToggler,
|
hideCheckToggler,
|
||||||
classnames: cx,
|
classnames: cx,
|
||||||
classPrefix: ns
|
classPrefix: ns,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (dragging) {
|
if (dragging) {
|
||||||
return (
|
return (
|
||||||
<div className={cx("Card-dragBtn")}>
|
<div className={cx('Card-dragBtn')}>
|
||||||
<i className="fa fa-exchange" />
|
<i className="fa fa-exchange" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else if (selectable && !hideCheckToggler) {
|
} else if (selectable && !hideCheckToggler) {
|
||||||
return (
|
return (
|
||||||
<div className={cx("Card-checkBtn")}>
|
<div className={cx('Card-checkBtn')}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
classPrefix={ns}
|
classPrefix={ns}
|
||||||
type={multiple ? 'checkbox' : 'radio'}
|
type={multiple ? 'checkbox' : 'radio'}
|
||||||
disabled={!checkable}
|
disabled={!checkable}
|
||||||
checked={selected}
|
checked={selected}
|
||||||
onChange={checkOnItemClick ? noop : this.handleCheck}
|
onChange={checkOnItemClick ? noop : this.handleCheck}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -142,36 +129,36 @@ export class Card extends React.Component<CardProps> {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderActions() {
|
renderActions() {
|
||||||
const {
|
const {actions, render, dragging, actionsCount, data, classnames: cx} = this.props;
|
||||||
actions,
|
|
||||||
render,
|
|
||||||
dragging,
|
|
||||||
actionsCount,
|
|
||||||
data,
|
|
||||||
classnames: cx
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (Array.isArray(actions)) {
|
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) => (
|
return group.map((actions, groupIndex) => (
|
||||||
<div key={groupIndex} className={cx("Card-actions")}>
|
<div key={groupIndex} className={cx('Card-actions')}>
|
||||||
{actions.map((action, index) => {
|
{actions.map((action, index) => {
|
||||||
const size = action.size || 'sm';
|
const size = action.size || 'sm';
|
||||||
|
|
||||||
return render(`action/${index}`, {
|
return render(
|
||||||
level: 'link',
|
`action/${index}`,
|
||||||
type: 'button',
|
{
|
||||||
...action,
|
level: 'link',
|
||||||
size
|
type: 'button',
|
||||||
}, {
|
...action,
|
||||||
isMenuItem: true,
|
size,
|
||||||
key: index,
|
},
|
||||||
index,
|
{
|
||||||
disabled: dragging || isDisabled(action, data),
|
isMenuItem: true,
|
||||||
className: cx('Card-action', action.className || `${size ? `Card-action--${size}` : ''}`),
|
key: index,
|
||||||
componentClass: 'a',
|
index,
|
||||||
onAction: this.handleAction
|
disabled: dragging || isDisabled(action, data),
|
||||||
})
|
className: cx(
|
||||||
|
'Card-action',
|
||||||
|
action.className || `${size ? `Card-action--${size}` : ''}`
|
||||||
|
),
|
||||||
|
componentClass: 'a',
|
||||||
|
onAction: this.handleAction,
|
||||||
|
}
|
||||||
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
));
|
));
|
||||||
|
@ -180,36 +167,31 @@ export class Card extends React.Component<CardProps> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderChild(node:SchemaNode, region: string = 'body', key:any = 0): JSX.Element {
|
renderChild(node: SchemaNode, region: string = 'body', key: any = 0): JSX.Element {
|
||||||
const {
|
const {render} = this.props;
|
||||||
render
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (typeof node === 'string' || typeof node === 'number') {
|
if (typeof node === 'string' || typeof node === 'number') {
|
||||||
return render(region, node, {key}) as JSX.Element;
|
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') {
|
if (childNode.type === 'hbox' || childNode.type === 'grid') {
|
||||||
return render(region, node, {
|
return render(region, node, {
|
||||||
key,
|
key,
|
||||||
itemRender: this.itemRender
|
itemRender: this.itemRender,
|
||||||
}) as JSX.Element;
|
}) 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);
|
return this.renderFeild(`column/${index}`, field, index, props);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderFeild(region:string, field:any, key:any, props:any) {
|
renderFeild(region: string, field: any, key: any, props: any) {
|
||||||
const {
|
const {render, classnames: cx} = props;
|
||||||
render,
|
|
||||||
classnames: cx
|
|
||||||
} = props;
|
|
||||||
const data = this.props.data;
|
const data = this.props.data;
|
||||||
|
|
||||||
const $$id = field.$$id ? `${field.$$id}-field` : '';
|
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>
|
<label className={cx('Card-fieldLabel', field.labelClassName)}>{field.label}</label>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{render(region, {
|
{
|
||||||
...field,
|
render(
|
||||||
field: field,
|
region,
|
||||||
$$id,
|
{
|
||||||
type: 'card-item-field',
|
...field,
|
||||||
}, {
|
field: field,
|
||||||
className: cx("Card-fieldValue", field.className),
|
$$id,
|
||||||
value: field.name ? resolveVariable(field.name, data) : undefined,
|
type: 'card-item-field',
|
||||||
popOverContainer: this.getPopOverContainer,
|
},
|
||||||
onAction: this.handleAction,
|
{
|
||||||
onQuickChange: this.handleQuickChange
|
className: cx('Card-fieldValue', field.className),
|
||||||
}) as JSX.Element}
|
value: field.name ? resolveVariable(field.name, data) : undefined,
|
||||||
|
popOverContainer: this.getPopOverContainer,
|
||||||
|
onAction: this.handleAction,
|
||||||
|
onQuickChange: this.handleQuickChange,
|
||||||
|
}
|
||||||
|
) as JSX.Element
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderBody() {
|
renderBody() {
|
||||||
const {
|
const {body} = this.props;
|
||||||
body
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (!body) {
|
if (!body) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(body)) {
|
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');
|
return this.renderChild(body, 'body');
|
||||||
|
@ -266,7 +252,7 @@ export class Card extends React.Component<CardProps> {
|
||||||
checkOnItemClick,
|
checkOnItemClick,
|
||||||
checkable,
|
checkable,
|
||||||
classnames: cx,
|
classnames: cx,
|
||||||
classPrefix: ns
|
classPrefix: ns,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
let heading = null;
|
let heading = null;
|
||||||
|
@ -281,7 +267,7 @@ export class Card extends React.Component<CardProps> {
|
||||||
subTitle: subTitleTpl,
|
subTitle: subTitleTpl,
|
||||||
subTitlePlaceholder,
|
subTitlePlaceholder,
|
||||||
desc: descTpl,
|
desc: descTpl,
|
||||||
descPlaceholder
|
descPlaceholder,
|
||||||
} = header;
|
} = header;
|
||||||
|
|
||||||
const highlight = !!evalExpression(highlightTpl, data as object);
|
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} />
|
<img className={cx('Card-img', header.imageClassName || imageClassName)} src={avatar} />
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
<div className={cx("Card-meta")}>
|
<div className={cx('Card-meta')}>
|
||||||
{highlight ? (
|
{highlight ? (
|
||||||
<i className={cx('Card-highlight', header.highlightClassName || highlightClassName)} />
|
<i className={cx('Card-highlight', header.highlightClassName || highlightClassName)} />
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -309,15 +295,19 @@ export class Card extends React.Component<CardProps> {
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{subTitle || subTitlePlaceholder ? (
|
{subTitle || subTitlePlaceholder ? (
|
||||||
<div className={cx('Card-subTitle', header.subTitleClassName || subTitleClassName)}>{render('sub-title', subTitle || subTitlePlaceholder, {
|
<div className={cx('Card-subTitle', header.subTitleClassName || subTitleClassName)}>
|
||||||
className: cx(!subTitle ? 'Card-placeholder' : undefined)
|
{render('sub-title', subTitle || subTitlePlaceholder, {
|
||||||
})}</div>
|
className: cx(!subTitle ? 'Card-placeholder' : undefined),
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{desc || descPlaceholder ? (
|
{desc || descPlaceholder ? (
|
||||||
<div className={cx('Card-desc', header.descClassName || descClassName)}>{render('desc', desc || descPlaceholder, {
|
<div className={cx('Card-desc', header.descClassName || descClassName)}>
|
||||||
className: !desc ? 'text-muted' : undefined
|
{render('desc', desc || descPlaceholder, {
|
||||||
})}</div>
|
className: !desc ? 'text-muted' : undefined,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -326,14 +316,14 @@ export class Card extends React.Component<CardProps> {
|
||||||
|
|
||||||
const body = this.renderBody();
|
const body = this.renderBody();
|
||||||
|
|
||||||
|
|
||||||
return (
|
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()}
|
{this.renderToolbar()}
|
||||||
{heading}
|
{heading}
|
||||||
{body ? (
|
{body ? <div className={cx('Card-body', bodyClassName)}>{body}</div> : null}
|
||||||
<div className={cx('Card-body', bodyClassName)}>{body}</div>
|
|
||||||
) : null}
|
|
||||||
{this.renderActions()}
|
{this.renderActions()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -342,15 +332,13 @@ export class Card extends React.Component<CardProps> {
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)card$/,
|
test: /(^|\/)card$/,
|
||||||
name: 'card'
|
name: 'card',
|
||||||
})
|
})
|
||||||
export class CardRenderer extends Card {
|
export class CardRenderer extends Card {}
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)card-item-field$/,
|
test: /(^|\/)card-item-field$/,
|
||||||
name: 'card-item'
|
name: 'card-item',
|
||||||
})
|
})
|
||||||
@QuickEdit()
|
@QuickEdit()
|
||||||
@PopOver()
|
@PopOver()
|
||||||
|
@ -358,15 +346,10 @@ export class CardRenderer extends Card {
|
||||||
export class CardItemFieldRenderer extends TableCell {
|
export class CardItemFieldRenderer extends TableCell {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
...TableCell.defaultProps,
|
...TableCell.defaultProps,
|
||||||
wrapperComponent: 'div'
|
wrapperComponent: 'div',
|
||||||
};
|
};
|
||||||
|
|
||||||
static propsList = [
|
static propsList = ['quickEdit', 'popOver', 'copyable', ...TableCell.propsList];
|
||||||
'quickEdit',
|
|
||||||
'popOver',
|
|
||||||
'copyable',
|
|
||||||
...TableCell.propsList
|
|
||||||
];
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let {
|
let {
|
||||||
|
@ -390,23 +373,21 @@ export class CardItemFieldRenderer extends TableCell {
|
||||||
const schema = {
|
const schema = {
|
||||||
...field,
|
...field,
|
||||||
className: innerClassName,
|
className: innerClassName,
|
||||||
type: field && field.type || 'plain',
|
type: (field && field.type) || 'plain',
|
||||||
};
|
};
|
||||||
|
|
||||||
let body = children ? children : render('field', schema, {
|
let body = children
|
||||||
...rest,
|
? children
|
||||||
value,
|
: render('field', schema, {
|
||||||
data
|
...rest,
|
||||||
});
|
value,
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
|
||||||
if (width) {
|
if (width) {
|
||||||
style = style || {};
|
style = style || {};
|
||||||
style.width = style.width || width;
|
style.width = style.width || width;
|
||||||
body = (
|
body = <div style={{width: !/%/.test(String(width)) ? width : ''}}>{body}</div>;
|
||||||
<div style={{width: !/%/.test(String(width)) ? width : ''}}>
|
|
||||||
{body}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Component) {
|
if (!Component) {
|
||||||
|
@ -414,14 +395,9 @@ export class CardItemFieldRenderer extends TableCell {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Component
|
<Component style={style} className={className} tabIndex={tabIndex} onKeyUp={onKeyUp}>
|
||||||
style={style}
|
|
||||||
className={className}
|
|
||||||
tabIndex={tabIndex}
|
|
||||||
onKeyUp={onKeyUp}
|
|
||||||
>
|
|
||||||
{body}
|
{body}
|
||||||
</Component>
|
</Component>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,30 +1,22 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import {findDOMNode} from 'react-dom';
|
||||||
findDOMNode
|
import {Renderer, RendererProps} from '../factory';
|
||||||
} from 'react-dom';
|
import {SchemaNode, Action} from '../types';
|
||||||
import {
|
|
||||||
Renderer,
|
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import {
|
|
||||||
SchemaNode,
|
|
||||||
Action
|
|
||||||
} from '../types';
|
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
import Button from '../components/Button';
|
import Button from '../components/Button';
|
||||||
import { ListStore, IListStore, IItem} from '../store/list';
|
import {ListStore, IListStore, IItem} from '../store/list';
|
||||||
import { observer } from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
import { anyChanged, getScrollParent, difference, ucFirst } from '../utils/helper';
|
import {anyChanged, getScrollParent, difference, ucFirst} from '../utils/helper';
|
||||||
import { resolveVariable } from '../utils/tpl-builtin';
|
import {resolveVariable} from '../utils/tpl-builtin';
|
||||||
import Sortable = require('sortablejs');
|
import Sortable = require('sortablejs');
|
||||||
import { filter } from '../utils/tpl';
|
import {filter} from '../utils/tpl';
|
||||||
import debounce = require('lodash/debounce');
|
import debounce = require('lodash/debounce');
|
||||||
import { resizeSensor } from '../utils/resize-sensor';
|
import {resizeSensor} from '../utils/resize-sensor';
|
||||||
|
|
||||||
export interface Column {
|
export interface Column {
|
||||||
type: string;
|
type: string;
|
||||||
[propName:string]: any;
|
[propName: string]: any;
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface GridProps extends RendererProps {
|
export interface GridProps extends RendererProps {
|
||||||
title?: string; // 标题
|
title?: string; // 标题
|
||||||
|
@ -37,16 +29,21 @@ export interface GridProps extends RendererProps {
|
||||||
footerClassName?: string;
|
footerClassName?: string;
|
||||||
itemClassName?: string;
|
itemClassName?: string;
|
||||||
card?: any;
|
card?: any;
|
||||||
source?:string;
|
source?: string;
|
||||||
selectable?: boolean;
|
selectable?: boolean;
|
||||||
selected?: Array<any>;
|
selected?: Array<any>;
|
||||||
multiple?: boolean;
|
multiple?: boolean;
|
||||||
valueField?: string;
|
valueField?: string;
|
||||||
draggable?:boolean;
|
draggable?: boolean;
|
||||||
onSelect: (selectedItems:Array<object>, unSelectedItems:Array<object>) => void;
|
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;
|
onSave?: (
|
||||||
onSaveOrder?: (moved: Array<object>, items:Array<object>) => void;
|
items: Array<object> | object,
|
||||||
onQuery: (values:object) => void;
|
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;
|
hideCheckToggler?: boolean;
|
||||||
itemCheckableOn?: string;
|
itemCheckableOn?: string;
|
||||||
itemDraggableOn?: string;
|
itemDraggableOn?: string;
|
||||||
|
@ -71,10 +68,10 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||||
'itemCheckableOn',
|
'itemCheckableOn',
|
||||||
'itemDraggableOn',
|
'itemDraggableOn',
|
||||||
'masonryLayout',
|
'masonryLayout',
|
||||||
"items",
|
'items',
|
||||||
"valueField"
|
'valueField',
|
||||||
];
|
];
|
||||||
static defaultProps:Partial<GridProps>= {
|
static defaultProps: Partial<GridProps> = {
|
||||||
className: '',
|
className: '',
|
||||||
placeholder: '没有数据',
|
placeholder: '没有数据',
|
||||||
source: '$items',
|
source: '$items',
|
||||||
|
@ -86,7 +83,7 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||||
hideCheckToggler: false,
|
hideCheckToggler: false,
|
||||||
masonryLayout: false,
|
masonryLayout: false,
|
||||||
affixHeader: true,
|
affixHeader: true,
|
||||||
itemsClassName: ''
|
itemsClassName: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
dragTip?: HTMLElement;
|
dragTip?: HTMLElement;
|
||||||
|
@ -95,7 +92,7 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||||
body?: any;
|
body?: any;
|
||||||
// fixAlignmentLazy: Function;
|
// fixAlignmentLazy: Function;
|
||||||
unSensor: Function;
|
unSensor: Function;
|
||||||
constructor(props:GridProps) {
|
constructor(props: GridProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.handleAction = this.handleAction.bind(this);
|
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 source = props.source;
|
||||||
const value = props.value || props.items;
|
const value = props.value || props.items;
|
||||||
let items:Array<object> = [];
|
let items: Array<object> = [];
|
||||||
let updateItems = true;
|
let updateItems = true;
|
||||||
|
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
|
@ -148,7 +145,7 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||||
multiple,
|
multiple,
|
||||||
hideCheckToggler,
|
hideCheckToggler,
|
||||||
itemCheckableOn,
|
itemCheckableOn,
|
||||||
itemDraggableOn
|
itemDraggableOn,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
store.update({
|
store.update({
|
||||||
|
@ -159,7 +156,7 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||||
multiple,
|
multiple,
|
||||||
hideCheckToggler,
|
hideCheckToggler,
|
||||||
itemCheckableOn,
|
itemCheckableOn,
|
||||||
itemDraggableOn
|
itemDraggableOn,
|
||||||
});
|
});
|
||||||
|
|
||||||
Cards.syncItems(store, this.props);
|
Cards.syncItems(store, this.props);
|
||||||
|
@ -167,7 +164,7 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
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) {
|
if (!parent || parent === document.body) {
|
||||||
parent = window;
|
parent = window;
|
||||||
}
|
}
|
||||||
|
@ -178,20 +175,26 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||||
window.addEventListener('resize', this.affixDetect);
|
window.addEventListener('resize', this.affixDetect);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps:GridProps) {
|
componentWillReceiveProps(nextProps: GridProps) {
|
||||||
const props = this.props;
|
const props = this.props;
|
||||||
const store = nextProps.store;
|
const store = nextProps.store;
|
||||||
|
|
||||||
if (anyChanged([
|
if (
|
||||||
'selectable',
|
anyChanged(
|
||||||
'draggable',
|
[
|
||||||
'orderBy',
|
'selectable',
|
||||||
'orderDir',
|
'draggable',
|
||||||
'multiple',
|
'orderBy',
|
||||||
'hideCheckToggler',
|
'orderDir',
|
||||||
'itemCheckableOn',
|
'multiple',
|
||||||
'itemDraggableOn'
|
'hideCheckToggler',
|
||||||
], props, nextProps)) {
|
'itemCheckableOn',
|
||||||
|
'itemDraggableOn',
|
||||||
|
],
|
||||||
|
props,
|
||||||
|
nextProps
|
||||||
|
)
|
||||||
|
) {
|
||||||
store.update({
|
store.update({
|
||||||
selectable: nextProps.selectable,
|
selectable: nextProps.selectable,
|
||||||
draggable: nextProps.draggable,
|
draggable: nextProps.draggable,
|
||||||
|
@ -200,15 +203,14 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||||
multiple: nextProps.multiple,
|
multiple: nextProps.multiple,
|
||||||
hideCheckToggler: nextProps.hideCheckToggler,
|
hideCheckToggler: nextProps.hideCheckToggler,
|
||||||
itemCheckableOn: nextProps.itemCheckableOn,
|
itemCheckableOn: nextProps.itemCheckableOn,
|
||||||
itemDraggableOn: nextProps.itemDraggableOn
|
itemDraggableOn: nextProps.itemDraggableOn,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (anyChanged([
|
if (
|
||||||
'source',
|
anyChanged(['source', 'value', 'items'], props, nextProps) ||
|
||||||
'value',
|
(!nextProps.value && !nextProps.items && nextProps.data !== props.data)
|
||||||
'items'
|
) {
|
||||||
], props, nextProps) || !nextProps.value && !nextProps.items && nextProps.data !== props.data) {
|
|
||||||
Cards.syncItems(store, nextProps, props);
|
Cards.syncItems(store, nextProps, props);
|
||||||
this.syncSelected();
|
this.syncSelected();
|
||||||
} else if (props.selected !== nextProps.selected) {
|
} 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;`);
|
// cards.forEach((item: HTMLElement) => item.style.cssText += `min-height: ${maxHeight}px;`);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
bodyRef(ref:HTMLDivElement) {
|
bodyRef(ref: HTMLDivElement) {
|
||||||
this.body = ref;
|
this.body = ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
itemsRef(ref:HTMLDivElement) {
|
itemsRef(ref: HTMLDivElement) {
|
||||||
if (ref) {
|
if (ref) {
|
||||||
// this.unSensor = resizeSensor(ref.parentNode as HTMLElement, this.fixAlignmentLazy);
|
// this.unSensor = resizeSensor(ref.parentNode as HTMLElement, this.fixAlignmentLazy);
|
||||||
} else {
|
} else {
|
||||||
|
@ -261,7 +263,7 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||||
const dom = findDOMNode(this) as HTMLElement;
|
const dom = findDOMNode(this) as HTMLElement;
|
||||||
const clip = (this.body as HTMLElement).getBoundingClientRect();
|
const clip = (this.body as HTMLElement).getBoundingClientRect();
|
||||||
const offsetY = this.props.env.affixOffsetTop || 0;
|
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;
|
const afixedDom = dom.querySelector(`.${ns}Cards-fixedTop`) as HTMLElement;
|
||||||
|
|
||||||
this.body.offsetWidth && (afixedDom.style.cssText = `top: ${offsetY}px;width: ${this.body.offsetWidth}px;`);
|
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);
|
// store.markHeaderAffix(clip.top < offsetY && (clip.top + clip.height - 40) > offsetY);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAction(e:React.UIEvent<any>, action: Action, ctx: object) {
|
handleAction(e: React.UIEvent<any>, action: Action, ctx: object) {
|
||||||
const {
|
const {onAction} = this.props;
|
||||||
onAction
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
// 需要支持特殊事件吗?
|
// 需要支持特殊事件吗?
|
||||||
onAction(e, action, ctx);
|
onAction(e, action, ctx);
|
||||||
|
@ -284,24 +284,19 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCheckAll() {
|
handleCheckAll() {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
store.toggleAll();
|
store.toggleAll();
|
||||||
this.syncSelected();
|
this.syncSelected();
|
||||||
}
|
}
|
||||||
|
|
||||||
syncSelected() {
|
syncSelected() {
|
||||||
const {
|
const {store, onSelect} = this.props;
|
||||||
store,
|
|
||||||
onSelect
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
onSelect && onSelect(store.selectedItems.map(item => item.data), store.unSelectedItems.map(item => item.data));
|
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);
|
item.change(values, saveSilent);
|
||||||
|
|
||||||
if (!saveImmediately || saveSilent) {
|
if (!saveImmediately || saveSilent) {
|
||||||
|
@ -309,16 +304,18 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (saveImmediately && saveImmediately.api) {
|
if (saveImmediately && saveImmediately.api) {
|
||||||
this.props.onAction(null, {
|
this.props.onAction(
|
||||||
actionType: 'ajax',
|
null,
|
||||||
api: saveImmediately.api
|
{
|
||||||
}, values);
|
actionType: 'ajax',
|
||||||
|
api: saveImmediately.api,
|
||||||
|
},
|
||||||
|
values
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {onSave} = this.props;
|
||||||
onSave
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (!onSave) {
|
if (!onSave) {
|
||||||
return;
|
return;
|
||||||
|
@ -328,10 +325,7 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSave() {
|
handleSave() {
|
||||||
const {
|
const {store, onSave} = this.props;
|
||||||
store,
|
|
||||||
onSave
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (!onSave || !store.modifiedItems.length) {
|
if (!onSave || !store.modifiedItems.length) {
|
||||||
return;
|
return;
|
||||||
|
@ -345,10 +339,7 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSaveOrder() {
|
handleSaveOrder() {
|
||||||
const {
|
const {store, onSaveOrder} = this.props;
|
||||||
store,
|
|
||||||
onSaveOrder
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (!onSaveOrder || !store.movedItems.length) {
|
if (!onSaveOrder || !store.movedItems.length) {
|
||||||
return;
|
return;
|
||||||
|
@ -358,41 +349,34 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||||
}
|
}
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
store.reset();
|
store.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
bulkUpdate(value:object, items:Array<object>) {
|
bulkUpdate(value: object, items: Array<object>) {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const items2 = store.items.filter(item => ~items.indexOf(item.pristine));
|
const items2 = store.items.filter(item => ~items.indexOf(item.pristine));
|
||||||
items2.forEach(item => item.change(value));
|
items2.forEach(item => item.change(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
getSelected() {
|
getSelected() {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return store.selectedItems.map(item => item.data);
|
return store.selectedItems.map(item => item.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
dragTipRef(ref:any) {
|
dragTipRef(ref: any) {
|
||||||
if (!this.dragTip && ref) {
|
if (!this.dragTip && ref) {
|
||||||
this.initDragging();
|
this.initDragging();
|
||||||
} else if (this.dragTip && !ref) {
|
} else if (this.dragTip && !ref) {
|
||||||
this.destroyDragging()
|
this.destroyDragging();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dragTip = ref;
|
this.dragTip = ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
initDragging() {
|
initDragging() {
|
||||||
const store = this.props.store;
|
const store = this.props.store;
|
||||||
const dom = findDOMNode(this) as HTMLElement;
|
const dom = findDOMNode(this) as HTMLElement;
|
||||||
|
@ -401,21 +385,21 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||||
group: 'table',
|
group: 'table',
|
||||||
handle: `.${ns}Card-dragBtn`,
|
handle: `.${ns}Card-dragBtn`,
|
||||||
ghostClass: `is-dragging`,
|
ghostClass: `is-dragging`,
|
||||||
onEnd: (e:any) => {
|
onEnd: (e: any) => {
|
||||||
// 没有移动
|
// 没有移动
|
||||||
if (e.newIndex === e.oldIndex) {
|
if (e.newIndex === e.oldIndex) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parent = e.to as HTMLElement;
|
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]);
|
parent.insertBefore(e.item, parent.childNodes[e.oldIndex]);
|
||||||
} else {
|
} else {
|
||||||
parent.appendChild(e.item);
|
parent.appendChild(e.item);
|
||||||
}
|
}
|
||||||
|
|
||||||
store.exchange(e.oldIndex, e.newIndex);
|
store.exchange(e.oldIndex, e.newIndex);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,19 +407,8 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||||
this.sortable && this.sortable.destroy();
|
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() : [];
|
actions = Array.isArray(actions) ? actions.concat() : [];
|
||||||
|
|
||||||
|
@ -443,15 +416,17 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||||
actions.unshift({
|
actions.unshift({
|
||||||
type: 'button',
|
type: 'button',
|
||||||
children: (
|
children: (
|
||||||
<Button
|
<Button
|
||||||
key="checkall"
|
key="checkall"
|
||||||
classPrefix={ns}
|
classPrefix={ns}
|
||||||
tooltip="切换全选"
|
tooltip="切换全选"
|
||||||
onClick={this.handleCheckAll}
|
onClick={this.handleCheckAll}
|
||||||
size="sm"
|
size="sm"
|
||||||
level={store.allChecked ? 'info' : 'default'}
|
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}
|
tooltipContainer={env && env.getModalContainer ? env.getModalContainer() : undefined}
|
||||||
size="sm"
|
size="sm"
|
||||||
active={store.dragging}
|
active={store.dragging}
|
||||||
onClick={(e:React.MouseEvent<any>) => {
|
onClick={(e: React.MouseEvent<any>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
store.toggleDragging();
|
store.toggleDragging();
|
||||||
store.dragging && store.clear();
|
store.dragging && store.clear();
|
||||||
|
@ -475,61 +450,81 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||||
>
|
>
|
||||||
<i className="fa fa-exchange" />
|
<i className="fa fa-exchange" />
|
||||||
</Button>
|
</Button>
|
||||||
)
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return Array.isArray(actions) && actions.length ? (
|
return Array.isArray(actions) && actions.length ? (
|
||||||
<div className={cx("Cards-actions")}>
|
<div className={cx('Cards-actions')}>
|
||||||
{actions.map((action, key) => render(`action/${key}`, {
|
{actions.map((action, key) =>
|
||||||
type: 'button',
|
render(
|
||||||
...action
|
`action/${key}`,
|
||||||
}, {
|
{
|
||||||
onAction: this.handleAction,
|
type: 'button',
|
||||||
key,
|
...action,
|
||||||
btnDisabled: store.dragging
|
},
|
||||||
}))}
|
{
|
||||||
|
onAction: this.handleAction,
|
||||||
|
key,
|
||||||
|
btnDisabled: store.dragging,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : null;
|
) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderHeading() {
|
renderHeading() {
|
||||||
let {
|
let {title, store, hideQuickSaveBtn, classnames: cx, data} = this.props;
|
||||||
title,
|
|
||||||
store,
|
|
||||||
hideQuickSaveBtn,
|
|
||||||
classnames: cx,
|
|
||||||
data
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (title || store.modified && !hideQuickSaveBtn || store.moved) {
|
if (title || (store.modified && !hideQuickSaveBtn) || store.moved) {
|
||||||
return (
|
return (
|
||||||
<div className={cx("Cards-heading")}>
|
<div className={cx('Cards-heading')}>
|
||||||
{store.modified && !hideQuickSaveBtn ? (
|
{store.modified && !hideQuickSaveBtn ? (
|
||||||
<span>
|
<span>
|
||||||
{`当前有 ${store.modified} 条记录修改了内容, 但并没有提交。请选择:`}
|
{`当前有 ${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" />
|
<i className="fa fa-check m-r-xs" />
|
||||||
提交
|
提交
|
||||||
</button>
|
</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" />
|
<i className="fa fa-times m-r-xs" />
|
||||||
放弃
|
放弃
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
) : store.moved ? (
|
) : store.moved ? (
|
||||||
<span>
|
<span>
|
||||||
{`当前有 ${store.moved} 条记录修改了顺序, 但并没有提交。请选择:`}
|
{`当前有 ${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" />
|
<i className="fa fa-check m-r-xs" />
|
||||||
提交
|
提交
|
||||||
</button>
|
</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" />
|
<i className="fa fa-times m-r-xs" />
|
||||||
放弃
|
放弃
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
) : title ? filter(title, data) : ''}
|
) : title ? (
|
||||||
|
filter(title, data)
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -546,33 +541,40 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||||
showHeader,
|
showHeader,
|
||||||
render,
|
render,
|
||||||
store,
|
store,
|
||||||
classnames: cx
|
classnames: cx,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (showHeader === false) {
|
if (showHeader === false) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const actions = this.renderActions('header');
|
const actions = this.renderActions('header');
|
||||||
const child = headerToolbarRender ? headerToolbarRender({
|
const child = headerToolbarRender
|
||||||
...this.props,
|
? headerToolbarRender({
|
||||||
selectedItems: store.selectedItems.map(item => item.data),
|
...this.props,
|
||||||
items: store.items.map(item => item.data),
|
selectedItems: store.selectedItems.map(item => item.data),
|
||||||
unSelectedItems: store.unSelectedItems.map(item => item.data),
|
items: store.items.map(item => item.data),
|
||||||
}) : null;
|
unSelectedItems: store.unSelectedItems.map(item => item.data),
|
||||||
const toolbarNode = actions || child || store.dragging ? (
|
})
|
||||||
<div className={cx('Cards-toolbar')} key="header-toolbar">
|
: null;
|
||||||
{actions}
|
const toolbarNode =
|
||||||
{child}
|
actions || child || store.dragging ? (
|
||||||
{store.dragging ? <div className={cx("Cards-dragTip")} ref={this.dragTipRef}>请拖动右边的按钮进行排序</div> : null}
|
<div className={cx('Cards-toolbar')} key="header-toolbar">
|
||||||
</div>
|
{actions}
|
||||||
) : null;
|
{child}
|
||||||
|
{store.dragging ? (
|
||||||
|
<div className={cx('Cards-dragTip')} ref={this.dragTipRef}>
|
||||||
|
请拖动右边的按钮进行排序
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
const headerNode = header ? (
|
const headerNode = header ? (
|
||||||
<div className={cx('Cards-header', headerClassName)} key="header">
|
<div className={cx('Cards-header', headerClassName)} key="header">
|
||||||
{render('header', header)}
|
{render('header', header)}
|
||||||
</div>
|
</div>
|
||||||
) : null;
|
) : null;
|
||||||
return headerNode && toolbarNode ? [headerNode, toolbarNode] : (headerNode || toolbarNode || null);
|
return headerNode && toolbarNode ? [headerNode, toolbarNode] : headerNode || toolbarNode || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderFooter() {
|
renderFooter() {
|
||||||
|
@ -584,33 +586,36 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||||
render,
|
render,
|
||||||
showFooter,
|
showFooter,
|
||||||
store,
|
store,
|
||||||
classnames: cx
|
classnames: cx,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (showFooter === false) {
|
if (showFooter === false) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const actions = this.renderActions('footer');
|
const actions = this.renderActions('footer');
|
||||||
const child = footerToolbarRender ? footerToolbarRender({
|
const child = footerToolbarRender
|
||||||
...this.props,
|
? footerToolbarRender({
|
||||||
selectedItems: store.selectedItems.map(item => item.data),
|
...this.props,
|
||||||
items: store.items.map(item => item.data),
|
selectedItems: store.selectedItems.map(item => item.data),
|
||||||
unSelectedItems: store.unSelectedItems.map(item => item.data),
|
items: store.items.map(item => item.data),
|
||||||
}) : null;
|
unSelectedItems: store.unSelectedItems.map(item => item.data),
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
|
||||||
const toolbarNode = actions || child ? (
|
const toolbarNode =
|
||||||
<div className={cx('Cards-toolbar')} key="footer-toolbar">
|
actions || child ? (
|
||||||
{actions}
|
<div className={cx('Cards-toolbar')} key="footer-toolbar">
|
||||||
{child}
|
{actions}
|
||||||
</div>
|
{child}
|
||||||
) : null;
|
</div>
|
||||||
|
) : null;
|
||||||
const footerNode = footer ? (
|
const footerNode = footer ? (
|
||||||
<div className={cx('Cards-footer', footerClassName)} key="footer">
|
<div className={cx('Cards-footer', footerClassName)} key="footer">
|
||||||
{render('footer', footer)}
|
{render('footer', footer)}
|
||||||
</div>
|
</div>
|
||||||
) : null;
|
) : null;
|
||||||
return footerNode && toolbarNode ? [toolbarNode, footerNode] : (footerNode || toolbarNode || null);
|
return footerNode && toolbarNode ? [toolbarNode, footerNode] : footerNode || toolbarNode || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -629,36 +634,41 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||||
checkOnItemClick,
|
checkOnItemClick,
|
||||||
masonryLayout,
|
masonryLayout,
|
||||||
itemsClassName,
|
itemsClassName,
|
||||||
classnames: cx
|
classnames: cx,
|
||||||
} = this.props;
|
} = 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 header = this.renderHeader();
|
||||||
const heading = this.renderHeading();
|
const heading = this.renderHeading();
|
||||||
const footer = this.renderFooter();
|
const footer = this.renderFooter();
|
||||||
let masonryClassName = '';
|
let masonryClassName = '';
|
||||||
|
|
||||||
if (masonryLayout) {
|
if (masonryLayout) {
|
||||||
masonryClassName = 'Cards--masonry ' + itemFinalClassName.split(/\s/).map(item => {
|
masonryClassName =
|
||||||
if (/^Grid-col--(xs|sm|md|lg)(\d+)/.test(item)) {
|
'Cards--masonry ' +
|
||||||
return `Cards--masonry${ucFirst(RegExp.$1)}${RegExp.$2}`;
|
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;
|
return item;
|
||||||
}).join(' ');
|
})
|
||||||
|
.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={this.bodyRef}
|
ref={this.bodyRef}
|
||||||
className={cx('Cards', className, {
|
className={cx('Cards', className, {
|
||||||
'Cards--unsaved': !!store.modified || !!store.moved
|
'Cards--unsaved': !!store.modified || !!store.moved,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{affixHeader ? (
|
{affixHeader ? (
|
||||||
<div
|
<div className={cx('Cards-fixedTop')}>
|
||||||
className={cx("Cards-fixedTop")}
|
|
||||||
>
|
|
||||||
{heading}
|
{heading}
|
||||||
{header}
|
{header}
|
||||||
</div>
|
</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)}>
|
<div ref={this.itemsRef} className={cx('Cards-body Grid', itemsClassName, masonryClassName)}>
|
||||||
{store.items.map((item, index) => {
|
{store.items.map((item, index) => {
|
||||||
return (
|
return (
|
||||||
(
|
<div key={item.index} className={cx(itemFinalClassName)}>
|
||||||
<div key={item.index} className={cx(itemFinalClassName)}>
|
{render(
|
||||||
{render(`${index}`, {
|
`${index}`,
|
||||||
|
{
|
||||||
type: 'card',
|
type: 'card',
|
||||||
...card
|
...card,
|
||||||
}, {
|
},
|
||||||
className: cx(card && card.className || '', {
|
{
|
||||||
|
className: cx((card && card.className) || '', {
|
||||||
'is-checked': item.checked,
|
'is-checked': item.checked,
|
||||||
'is-modified': item.modified,
|
'is-modified': item.modified,
|
||||||
'is-moved': item.moved,
|
'is-moved': item.moved,
|
||||||
|
@ -693,17 +705,15 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||||
checkOnItemClick,
|
checkOnItemClick,
|
||||||
onAction,
|
onAction,
|
||||||
onCheck: this.handleCheck,
|
onCheck: this.handleCheck,
|
||||||
onQuickChange: store.dragging ? null : this.handleQuickChange
|
onQuickChange: store.dragging ? null : this.handleQuickChange,
|
||||||
})}
|
}
|
||||||
</div>
|
)}
|
||||||
)
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className={cx("Cards-placeholder")}>
|
<div className={cx('Cards-placeholder')}>{placeholder}</div>
|
||||||
{placeholder}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{footer}
|
{footer}
|
||||||
|
@ -716,7 +726,7 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||||
test: /(^|\/)(?:crud\/body\/grid|cards)$/,
|
test: /(^|\/)(?:crud\/body\/grid|cards)$/,
|
||||||
name: 'cards',
|
name: 'cards',
|
||||||
storeType: ListStore.name,
|
storeType: ListStore.name,
|
||||||
weight: -100 // 默认的 grid 不是这样,这个只识别 crud 下面的 grid
|
weight: -100, // 默认的 grid 不是这样,这个只识别 crud 下面的 grid
|
||||||
})
|
})
|
||||||
export class CardsRenderer extends Cards {
|
export class CardsRenderer extends Cards {
|
||||||
dragging: boolean;
|
dragging: boolean;
|
||||||
|
@ -730,5 +740,4 @@ export class CardsRenderer extends Cards {
|
||||||
avatarClassName?: string;
|
avatarClassName?: string;
|
||||||
body?: SchemaNode;
|
body?: SchemaNode;
|
||||||
actions?: Array<Action>;
|
actions?: Array<Action>;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,28 +1,19 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as PropTypes from 'prop-types';
|
import * as PropTypes from 'prop-types';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import {ServiceStore, IServiceStore} from '../store/service';
|
import {ServiceStore, IServiceStore} from '../store/service';
|
||||||
import {
|
import {Api, ApiObject, Action} from '../types';
|
||||||
Api,
|
import {filter, evalExpression} from '../utils/tpl';
|
||||||
ApiObject,
|
|
||||||
Action
|
|
||||||
} from '../types';
|
|
||||||
import {
|
|
||||||
filter, evalExpression} from '../utils/tpl';
|
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
import LazyComponent from '../components/LazyComponent';
|
import LazyComponent from '../components/LazyComponent';
|
||||||
import {resizeSensor} from '../utils/resize-sensor';
|
import {resizeSensor} from '../utils/resize-sensor';
|
||||||
import { resolveVariableAndFilter } from '../utils/tpl-builtin';
|
import {resolveVariableAndFilter} from '../utils/tpl-builtin';
|
||||||
import { isApiOutdated } from '../utils/api';
|
import {isApiOutdated} from '../utils/api';
|
||||||
import { ScopedContext, IScopedContext } from '../Scoped';
|
import {ScopedContext, IScopedContext} from '../Scoped';
|
||||||
|
|
||||||
|
|
||||||
export interface ChartProps extends RendererProps {
|
export interface ChartProps extends RendererProps {
|
||||||
chartRef?: (echart:any) => void;
|
chartRef?: (echart: any) => void;
|
||||||
onDataFilter?: (config:any) => any;
|
onDataFilter?: (config: any) => any;
|
||||||
api?: Api;
|
api?: Api;
|
||||||
source?: string;
|
source?: string;
|
||||||
config?: object;
|
config?: object;
|
||||||
|
@ -32,24 +23,22 @@ export interface ChartProps extends RendererProps {
|
||||||
replaceChartOption: boolean;
|
replaceChartOption: boolean;
|
||||||
}
|
}
|
||||||
export class Chart extends React.Component<ChartProps> {
|
export class Chart extends React.Component<ChartProps> {
|
||||||
|
static defaultProps: Partial<ChartProps> = {
|
||||||
static defaultProps:Partial<ChartProps> = {
|
|
||||||
offsetY: 50,
|
offsetY: 50,
|
||||||
replaceChartOption: false
|
replaceChartOption: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
static propsList:Array<string> = [
|
static propsList: Array<string> = [];
|
||||||
];
|
|
||||||
|
|
||||||
ref:any;
|
ref: any;
|
||||||
echarts:any;
|
echarts: any;
|
||||||
unSensor:Function;
|
unSensor: Function;
|
||||||
pending?:object;
|
pending?: object;
|
||||||
timer: number;
|
timer: number;
|
||||||
mounted: boolean;
|
mounted: boolean;
|
||||||
reloadCancel: Function;
|
reloadCancel: Function;
|
||||||
|
|
||||||
constructor(props:ChartProps) {
|
constructor(props: ChartProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.refFn = this.refFn.bind(this);
|
this.refFn = this.refFn.bind(this);
|
||||||
|
@ -58,13 +47,7 @@ export class Chart extends React.Component<ChartProps> {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
const {
|
const {config, api, data, initFetch, source} = this.props;
|
||||||
config,
|
|
||||||
api,
|
|
||||||
data,
|
|
||||||
initFetch,
|
|
||||||
source
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
this.mounted = true;
|
this.mounted = true;
|
||||||
|
|
||||||
|
@ -74,18 +57,20 @@ export class Chart extends React.Component<ChartProps> {
|
||||||
} else if (api && initFetch !== false) {
|
} else if (api && initFetch !== false) {
|
||||||
this.reload();
|
this.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
config && this.renderChart(config);
|
config && this.renderChart(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps:ChartProps) {
|
componentDidUpdate(prevProps: ChartProps) {
|
||||||
const props = this.props;
|
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)) {
|
if (isApiOutdated(prevProps.api, props.api, prevProps.data, props.data)) {
|
||||||
this.reload();
|
this.reload();
|
||||||
} else if (props.source && /^\$(?:([a-z0-9_.]+)|{.+})$/.test(props.source)) {
|
} 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');
|
const ret = resolveVariableAndFilter(props.source, props.data, '| raw');
|
||||||
|
|
||||||
if (prevRet !== ret) {
|
if (prevRet !== ret) {
|
||||||
|
@ -101,28 +86,25 @@ export class Chart extends React.Component<ChartProps> {
|
||||||
clearTimeout(this.timer);
|
clearTimeout(this.timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClick(ctx:object) {
|
handleClick(ctx: object) {
|
||||||
const {
|
const {onAction, clickAction} = this.props;
|
||||||
onAction,
|
|
||||||
clickAction,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
clickAction && onAction && onAction(null, clickAction, ctx);
|
clickAction && onAction && onAction(null, clickAction, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
refFn(ref:any) {
|
refFn(ref: any) {
|
||||||
const chartRef = this.props.chartRef;
|
const chartRef = this.props.chartRef;
|
||||||
if (ref) {
|
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;
|
(window as any).echarts = echarts;
|
||||||
this.echarts = echarts.init(ref);
|
this.echarts = echarts.init(ref);
|
||||||
this.echarts.on('click', this.handleClick)
|
this.echarts.on('click', this.handleClick);
|
||||||
this.unSensor = resizeSensor(ref, () => {
|
this.unSensor = resizeSensor(ref, () => {
|
||||||
const width = ref.offsetWidth;
|
const width = ref.offsetWidth;
|
||||||
const height = ref.offsetHeight;
|
const height = ref.offsetHeight;
|
||||||
this.echarts.resize({
|
this.echarts.resize({
|
||||||
width,
|
width,
|
||||||
height
|
height,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -137,13 +119,8 @@ export class Chart extends React.Component<ChartProps> {
|
||||||
this.ref = ref;
|
this.ref = ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
reload(query?:any) {
|
reload(query?: any) {
|
||||||
const {
|
const {api, env, store, interval} = this.props;
|
||||||
api,
|
|
||||||
env,
|
|
||||||
store,
|
|
||||||
interval
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (query) {
|
if (query) {
|
||||||
return this.receive(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.hideLoading();
|
||||||
}
|
}
|
||||||
this.echarts && this.echarts.showLoading();
|
this.echarts && this.echarts.showLoading();
|
||||||
|
|
||||||
|
|
||||||
env
|
env.fetcher(api, store.data, {
|
||||||
.fetcher(api, store.data, {
|
cancelExecutor: (executor: Function) => (this.reloadCancel = executor),
|
||||||
cancelExecutor: (executor:Function) => this.reloadCancel = executor
|
})
|
||||||
})
|
|
||||||
.then(result => {
|
.then(result => {
|
||||||
delete this.reloadCancel;
|
delete this.reloadCancel;
|
||||||
this.renderChart(result.data || {});
|
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;
|
const store = this.props.store;
|
||||||
|
|
||||||
store.updateData(data);
|
store.updateData(data);
|
||||||
|
@ -201,7 +176,7 @@ export class Chart extends React.Component<ChartProps> {
|
||||||
|
|
||||||
config = config || this.pending;
|
config = config || this.pending;
|
||||||
if (typeof config === 'string') {
|
if (typeof config === 'string') {
|
||||||
config = (new Function("return " + config))();
|
config = new Function('return ' + config)();
|
||||||
}
|
}
|
||||||
onDataFilter && (config = onDataFilter(config) || config);
|
onDataFilter && (config = onDataFilter(config) || config);
|
||||||
|
|
||||||
|
@ -215,41 +190,24 @@ export class Chart extends React.Component<ChartProps> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {className, width, height, classPrefix: ns} = this.props;
|
||||||
className,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
classPrefix: ns
|
|
||||||
} = this.props;
|
|
||||||
let style = this.props.style || {};
|
let style = this.props.style || {};
|
||||||
|
|
||||||
width && (style.width = width);
|
width && (style.width = width);
|
||||||
height && (style.height = height);
|
height && (style.height = height);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<LazyComponent
|
<LazyComponent
|
||||||
unMountOnHidden
|
unMountOnHidden
|
||||||
placeholder={(
|
placeholder={
|
||||||
<div
|
<div className={cx(`${ns}Chart`, className)} style={style}>
|
||||||
className={cx(`${ns}Chart`, className)}
|
|
||||||
style={style}
|
|
||||||
>
|
|
||||||
<div className={`${ns}Chart-placeholder`}>
|
<div className={`${ns}Chart-placeholder`}>
|
||||||
<i key="loading" className="fa fa-spinner fa-spin fa-2x fa-fw" />
|
<i key="loading" className="fa fa-spinner fa-spin fa-2x fa-fw" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
}
|
||||||
component={() => (
|
component={() => <div className={cx(`${ns}Chart`, className)} style={style} ref={this.refFn} />}
|
||||||
<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;
|
const scoped = this.context as IScopedContext;
|
||||||
scoped.unRegisterComponent(this);
|
scoped.unRegisterComponent(this);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,12 +1,7 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
import {
|
import {Collapse as BasicCollapse} from 'react-bootstrap';
|
||||||
Collapse as BasicCollapse
|
|
||||||
} from 'react-bootstrap';
|
|
||||||
|
|
||||||
export interface CollapseProps extends RendererProps {
|
export interface CollapseProps extends RendererProps {
|
||||||
title?: string; // 标题
|
title?: string; // 标题
|
||||||
|
@ -17,7 +12,7 @@ export interface CollapseProps extends RendererProps {
|
||||||
bodyClassName?: string;
|
bodyClassName?: string;
|
||||||
headingClassName?: string;
|
headingClassName?: string;
|
||||||
// 内容口子
|
// 内容口子
|
||||||
children?: JSX.Element | ((props?:any) => JSX.Element);
|
children?: JSX.Element | ((props?: any) => JSX.Element);
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CollapseState {
|
export interface CollapseState {
|
||||||
|
@ -26,24 +21,24 @@ export interface CollapseState {
|
||||||
|
|
||||||
export default class Collapse extends React.Component<CollapseProps, CollapseState> {
|
export default class Collapse extends React.Component<CollapseProps, CollapseState> {
|
||||||
static propsList: Array<string> = [
|
static propsList: Array<string> = [
|
||||||
"wrapperComponent",
|
'wrapperComponent',
|
||||||
"headingComponent",
|
'headingComponent',
|
||||||
"bodyClassName",
|
'bodyClassName',
|
||||||
"collapsed",
|
'collapsed',
|
||||||
"headingClassName"
|
'headingClassName',
|
||||||
];
|
];
|
||||||
|
|
||||||
static defaultProps:Partial<CollapseProps>= {
|
static defaultProps: Partial<CollapseProps> = {
|
||||||
wrapperComponent: 'div',
|
wrapperComponent: 'div',
|
||||||
headingComponent: 'h4',
|
headingComponent: 'h4',
|
||||||
className: '',
|
className: '',
|
||||||
headingClassName: '',
|
headingClassName: '',
|
||||||
bodyClassName: '',
|
bodyClassName: '',
|
||||||
collapsable: true
|
collapsable: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
collapsed: false
|
collapsed: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: CollapseProps) {
|
constructor(props: CollapseProps) {
|
||||||
|
@ -53,19 +48,19 @@ export default class Collapse extends React.Component<CollapseProps, CollapseSta
|
||||||
this.state.collapsed = !!props.collapsed;
|
this.state.collapsed = !!props.collapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps:CollapseProps) {
|
componentWillReceiveProps(nextProps: CollapseProps) {
|
||||||
const props = this.props;
|
const props = this.props;
|
||||||
|
|
||||||
if (props.collapsed !== nextProps.collapsed) {
|
if (props.collapsed !== nextProps.collapsed) {
|
||||||
this.setState({
|
this.setState({
|
||||||
collapsed: !!nextProps.collapsed
|
collapsed: !!nextProps.collapsed,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleCollapsed() {
|
toggleCollapsed() {
|
||||||
this.setState({
|
this.setState({
|
||||||
collapsed: !this.state.collapsed
|
collapsed: !this.state.collapsed,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,36 +78,39 @@ export default class Collapse extends React.Component<CollapseProps, CollapseSta
|
||||||
body,
|
body,
|
||||||
bodyClassName,
|
bodyClassName,
|
||||||
render,
|
render,
|
||||||
collapsable
|
collapsable,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
// todo 换掉 bootstrap 的 collapse
|
// todo 换掉 bootstrap 的 collapse
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WrapperComponent className={cx(`Collapse`, {
|
<WrapperComponent
|
||||||
'is-collapsed': this.state.collapsed,
|
className={cx(
|
||||||
[`Collapse--${size}`]: size,
|
`Collapse`,
|
||||||
'Collapse--collapsable': collapsable
|
{
|
||||||
}, className)}>
|
'is-collapsed': this.state.collapsed,
|
||||||
|
[`Collapse--${size}`]: size,
|
||||||
|
'Collapse--collapsable': collapsable,
|
||||||
|
},
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
{title ? (
|
{title ? (
|
||||||
<HeadingComponent className={cx(`Collapse-header`, headingClassName)}>
|
<HeadingComponent className={cx(`Collapse-header`, headingClassName)}>
|
||||||
{render('heading', title)}
|
{render('heading', title)}
|
||||||
{collapsable && (
|
{collapsable && <span onClick={this.toggleCollapsed} className={cx('Collapse-arrow')} />}
|
||||||
<span
|
|
||||||
onClick={this.toggleCollapsed}
|
|
||||||
className={cx('Collapse-arrow')}
|
|
||||||
></span>
|
|
||||||
)}
|
|
||||||
</HeadingComponent>
|
</HeadingComponent>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<BasicCollapse in={collapsable ? !this.state.collapsed : true}>
|
<BasicCollapse in={collapsable ? !this.state.collapsed : true}>
|
||||||
<div className={cx(`Collapse-body`, bodyClassName)}>
|
<div className={cx(`Collapse-body`, bodyClassName)}>
|
||||||
{children ? (
|
{children
|
||||||
typeof children === 'function' ? children(this.props) : children
|
? typeof children === 'function'
|
||||||
) : body ? (
|
? children(this.props)
|
||||||
render('body', body)
|
: children
|
||||||
) : null}
|
: body
|
||||||
|
? render('body', body)
|
||||||
|
: null}
|
||||||
</div>
|
</div>
|
||||||
</BasicCollapse>
|
</BasicCollapse>
|
||||||
</WrapperComponent>
|
</WrapperComponent>
|
||||||
|
@ -122,6 +120,6 @@ export default class Collapse extends React.Component<CollapseProps, CollapseSta
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)collapse$/,
|
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 * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import {SchemaNode} from '../types';
|
import {SchemaNode} from '../types';
|
||||||
|
|
||||||
export interface ContainerProps extends RendererProps {
|
export interface ContainerProps extends RendererProps {
|
||||||
body?: SchemaNode;
|
body?: SchemaNode;
|
||||||
children?: (props:any) => React.ReactNode;
|
children?: (props: any) => React.ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Container<T> extends React.Component<ContainerProps & T, object> {
|
export default class Container<T> extends React.Component<ContainerProps & T, object> {
|
||||||
static propsList: Array<string> = [
|
static propsList: Array<string> = ['body', 'className'];
|
||||||
"body",
|
static defaultProps = {
|
||||||
"className",
|
|
||||||
];
|
|
||||||
static defaultProps= {
|
|
||||||
className: '',
|
className: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
renderBody():JSX.Element | null {
|
renderBody(): JSX.Element | null {
|
||||||
const {
|
const {children, body, render, classnames: cx} = this.props;
|
||||||
children,
|
|
||||||
body,
|
|
||||||
render,
|
|
||||||
classnames: cx
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cx("Container-body")}>
|
<div className={cx('Container-body')}>
|
||||||
{children ? (
|
{children
|
||||||
typeof children === 'function' ? children(this.props) as JSX.Element : children as JSX.Element
|
? typeof children === 'function'
|
||||||
) : body ? (
|
? (children(this.props) as JSX.Element)
|
||||||
render('body', body) as JSX.Element
|
: (children as JSX.Element)
|
||||||
) : null}
|
: body
|
||||||
|
? (render('body', body) as JSX.Element)
|
||||||
|
: null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {className, size, classnames: cx} = this.props;
|
||||||
className,
|
|
||||||
size,
|
|
||||||
classnames: cx
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
return <div className={cx('Container', className)}>{this.renderBody()}</div>;
|
||||||
<div
|
|
||||||
className={cx('Container', className)}
|
|
||||||
>
|
|
||||||
{this.renderBody()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)container$/,
|
test: /(^|\/)container$/,
|
||||||
name: 'container'
|
name: 'container',
|
||||||
})
|
})
|
||||||
export class ContainerRenderer extends Container<{}> {};
|
export class ContainerRenderer extends Container<{}> {}
|
||||||
|
|
|
@ -1,57 +1,56 @@
|
||||||
/**
|
/**
|
||||||
* @file scoped.jsx.
|
* @file scoped.jsx.
|
||||||
* @author fex
|
* @author fex
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { RendererProps } from '../factory';
|
import {RendererProps} from '../factory';
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
import hoistNonReactStatic = require('hoist-non-react-statics');
|
import hoistNonReactStatic = require('hoist-non-react-statics');
|
||||||
import Button from '../components/Button';
|
import Button from '../components/Button';
|
||||||
import { filter } from '../utils/tpl';
|
import {filter} from '../utils/tpl';
|
||||||
|
|
||||||
export interface CopyableConfig {
|
export interface CopyableConfig {}
|
||||||
}
|
|
||||||
|
|
||||||
export interface CopyableConfig {
|
export interface CopyableConfig {
|
||||||
icon?: string;
|
icon?: string;
|
||||||
content?: string;
|
content?: string;
|
||||||
[propName:string]: any;
|
[propName: string]: any;
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface CopyableProps extends RendererProps {
|
export interface CopyableProps extends RendererProps {
|
||||||
name?: string;
|
name?: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
copyable: boolean | CopyableConfig;
|
copyable: boolean | CopyableConfig;
|
||||||
};
|
}
|
||||||
|
|
||||||
export const HocCopyable = () => (Component: React.ComponentType<any>): any => {
|
export const HocCopyable = () => (Component: React.ComponentType<any>): any => {
|
||||||
class QuickEditComponent extends React.PureComponent<CopyableProps, any> {
|
class QuickEditComponent extends React.PureComponent<CopyableProps, any> {
|
||||||
static ComposedComponent = Component;
|
static ComposedComponent = Component;
|
||||||
handleClick(content: string) {
|
handleClick(content: string) {
|
||||||
const { env } = this.props;
|
const {env} = this.props;
|
||||||
env.copy && env.copy(content);
|
env.copy && env.copy(content);
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {copyable, name, className, data, noHoc, classnames: cx} = this.props;
|
||||||
copyable,
|
|
||||||
name,
|
|
||||||
className,
|
|
||||||
data,
|
|
||||||
noHoc,
|
|
||||||
classnames: cx
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (copyable && !noHoc) {
|
if (copyable && !noHoc) {
|
||||||
const content = filter((copyable as CopyableConfig).content || '${' + name + ' | raw }', data);
|
const content = filter((copyable as CopyableConfig).content || '${' + name + ' | raw }', data);
|
||||||
if (content) {
|
if (content) {
|
||||||
return (<Component {...this.props} className={cx(`Field--copyable`, className)}>
|
return (
|
||||||
<Component {...this.props} wrapperComponent={''} noHoc />
|
<Component {...this.props} className={cx(`Field--copyable`, className)}>
|
||||||
<i key="edit-btn" data-tooltip="点击复制" className={cx("Field-copyBtn fa fa-clipboard")} onClick={this.handleClick.bind(this, content)} />
|
<Component {...this.props} wrapperComponent={''} noHoc />
|
||||||
</Component>);
|
<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);
|
hoistNonReactStatic(QuickEditComponent, Component);
|
||||||
|
|
|
@ -1,16 +1,8 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import {ServiceStore, IServiceStore} from '../store/service';
|
import {ServiceStore, IServiceStore} from '../store/service';
|
||||||
import {
|
import {Api, SchemaNode} from '../types';
|
||||||
Api,
|
import {filter} from '../utils/tpl';
|
||||||
SchemaNode
|
|
||||||
} from '../types';
|
|
||||||
import {
|
|
||||||
filter
|
|
||||||
} from '../utils/tpl';
|
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
|
|
||||||
|
@ -22,66 +14,55 @@ export interface DateProps extends RendererProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DateField extends React.Component<DateProps, object> {
|
export class DateField extends React.Component<DateProps, object> {
|
||||||
static defaultProps:Partial<DateProps> = {
|
static defaultProps: Partial<DateProps> = {
|
||||||
placeholder: '-',
|
placeholder: '-',
|
||||||
format: 'YYYY-MM-DD',
|
format: 'YYYY-MM-DD',
|
||||||
valueFormat: 'X'
|
valueFormat: 'X',
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {className, value, valueFormat, format, placeholder, classnames: cx} = this.props;
|
||||||
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) {
|
if (value) {
|
||||||
let date = moment(value, valueFormat);
|
let date = moment(value, valueFormat);
|
||||||
viewValue = date.isValid() ? date.format(format) : <span className="text-danger">日期无效</span>;
|
viewValue = date.isValid() ? date.format(format) : <span className="text-danger">日期无效</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <span className={cx('DateField', className)}>{viewValue}</span>;
|
||||||
<span className={cx('DateField', className)}>
|
|
||||||
{viewValue}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)date$/,
|
test: /(^|\/)date$/,
|
||||||
name: 'date-field'
|
name: 'date-field',
|
||||||
})
|
})
|
||||||
export class DateFieldRenderer extends DateField {
|
export class DateFieldRenderer extends DateField {
|
||||||
static defaultProps:Partial<DateProps> = {
|
static defaultProps: Partial<DateProps> = {
|
||||||
...DateField.defaultProps,
|
...DateField.defaultProps,
|
||||||
format: 'YYYY-MM-DD'
|
format: 'YYYY-MM-DD',
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)datetime$/,
|
test: /(^|\/)datetime$/,
|
||||||
name: 'datetime-field'
|
name: 'datetime-field',
|
||||||
})
|
})
|
||||||
export class DateTimeFieldRenderer extends DateField {
|
export class DateTimeFieldRenderer extends DateField {
|
||||||
static defaultProps:Partial<DateProps> = {
|
static defaultProps: Partial<DateProps> = {
|
||||||
...DateField.defaultProps,
|
...DateField.defaultProps,
|
||||||
format: 'YYYY-MM-DD HH:mm:ss'
|
format: 'YYYY-MM-DD HH:mm:ss',
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)time$/,
|
test: /(^|\/)time$/,
|
||||||
name: 'time-field'
|
name: 'time-field',
|
||||||
})
|
})
|
||||||
export class TimeFieldRenderer extends DateField {
|
export class TimeFieldRenderer extends DateField {
|
||||||
static defaultProps:Partial<DateProps> = {
|
static defaultProps: Partial<DateProps> = {
|
||||||
...DateField.defaultProps,
|
...DateField.defaultProps,
|
||||||
format: 'HH:mm'
|
format: 'HH:mm',
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,34 +1,25 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as PropTypes from 'prop-types';
|
import * as PropTypes from 'prop-types';
|
||||||
import Scoped, { ScopedContext, IScopedContext } from '../Scoped';
|
import Scoped, {ScopedContext, IScopedContext} from '../Scoped';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import {ServiceStore, IServiceStore} from '../store/service';
|
import {ServiceStore, IServiceStore} from '../store/service';
|
||||||
import { observer } from "mobx-react";
|
import {observer} from 'mobx-react';
|
||||||
import {
|
import {SchemaNode, Schema, Action} from '../types';
|
||||||
SchemaNode,
|
import {filter} from '../utils/tpl';
|
||||||
Schema,
|
|
||||||
Action
|
|
||||||
} from '../types';
|
|
||||||
import {
|
|
||||||
filter
|
|
||||||
} from '../utils/tpl';
|
|
||||||
import Modal from '../components/Modal';
|
import Modal from '../components/Modal';
|
||||||
import findLast = require('lodash/findLast');
|
import findLast = require('lodash/findLast');
|
||||||
import {guid} from '../utils/helper'
|
import {guid} from '../utils/helper';
|
||||||
import {reaction} from 'mobx';
|
import {reaction} from 'mobx';
|
||||||
import { closeIcon } from '../components/icons';
|
import {closeIcon} from '../components/icons';
|
||||||
import { ModalStore, IModalStore } from '../store/modal';
|
import {ModalStore, IModalStore} from '../store/modal';
|
||||||
|
|
||||||
export interface DialogProps extends RendererProps {
|
export interface DialogProps extends RendererProps {
|
||||||
title?: string; // 标题
|
title?: string; // 标题
|
||||||
size?: 'md' | 'lg' | 'sm' | 'xl';
|
size?: 'md' | 'lg' | 'sm' | 'xl';
|
||||||
closeOnEsc?: boolean;
|
closeOnEsc?: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onConfirm: (values:Array<object>, action: Action, ctx: object, targets: Array<any>) => void;
|
onConfirm: (values: Array<object>, action: Action, ctx: object, targets: Array<any>) => void;
|
||||||
children?: React.ReactNode | ((props?:any) => React.ReactNode);
|
children?: React.ReactNode | ((props?: any) => React.ReactNode);
|
||||||
store: IModalStore;
|
store: IModalStore;
|
||||||
className?: string;
|
className?: string;
|
||||||
header?: SchemaNode;
|
header?: SchemaNode;
|
||||||
|
@ -41,7 +32,6 @@ export interface DialogProps extends RendererProps {
|
||||||
lazyRender?: boolean;
|
lazyRender?: boolean;
|
||||||
wrapperComponent: React.ReactType;
|
wrapperComponent: React.ReactType;
|
||||||
showCloseButton?: boolean;
|
showCloseButton?: boolean;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DialogState {
|
export interface DialogState {
|
||||||
|
@ -50,20 +40,20 @@ export interface DialogState {
|
||||||
|
|
||||||
export default class Dialog extends React.Component<DialogProps, DialogState> {
|
export default class Dialog extends React.Component<DialogProps, DialogState> {
|
||||||
static propsList: Array<string> = [
|
static propsList: Array<string> = [
|
||||||
"title",
|
'title',
|
||||||
"size",
|
'size',
|
||||||
"closeOnEsc",
|
'closeOnEsc',
|
||||||
"children",
|
'children',
|
||||||
"bodyClassName",
|
'bodyClassName',
|
||||||
"headerClassName",
|
'headerClassName',
|
||||||
"confirm",
|
'confirm',
|
||||||
"onClose",
|
'onClose',
|
||||||
"onConfirm",
|
'onConfirm',
|
||||||
"show",
|
'show',
|
||||||
"showCloseButton",
|
'showCloseButton',
|
||||||
"actions"
|
'actions',
|
||||||
];
|
];
|
||||||
static defaultProps:Partial<DialogProps>= {
|
static defaultProps: Partial<DialogProps> = {
|
||||||
title: '弹框',
|
title: '弹框',
|
||||||
bodyClassName: '',
|
bodyClassName: '',
|
||||||
confirm: true,
|
confirm: true,
|
||||||
|
@ -71,17 +61,17 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
|
||||||
lazyRender: false,
|
lazyRender: false,
|
||||||
showCloseButton: true,
|
showCloseButton: true,
|
||||||
wrapperComponent: Modal,
|
wrapperComponent: Modal,
|
||||||
closeOnEsc: false
|
closeOnEsc: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
reaction:any;
|
reaction: any;
|
||||||
$$id: string = guid();
|
$$id: string = guid();
|
||||||
constructor(props:DialogProps) {
|
constructor(props: DialogProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
entered: !!this.props.show
|
entered: !!this.props.show,
|
||||||
}
|
};
|
||||||
this.handleSelfClose = this.handleSelfClose.bind(this);
|
this.handleSelfClose = this.handleSelfClose.bind(this);
|
||||||
this.handleAction = this.handleAction.bind(this);
|
this.handleAction = this.handleAction.bind(this);
|
||||||
this.handleDialogConfirm = this.handleDialogConfirm.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();
|
this.reaction && this.reaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
buildActions():Array<Action> {
|
buildActions(): Array<Action> {
|
||||||
const {
|
const {actions, confirm} = this.props;
|
||||||
actions,
|
|
||||||
confirm
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (typeof actions !== 'undefined') {
|
if (typeof actions !== 'undefined') {
|
||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret:Array<Action> = [];
|
let ret: Array<Action> = [];
|
||||||
ret.push({
|
ret.push({
|
||||||
type: 'button',
|
type: 'button',
|
||||||
actionType: 'cancel',
|
actionType: 'cancel',
|
||||||
label: '取消'
|
label: '取消',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (confirm) {
|
if (confirm) {
|
||||||
|
@ -139,7 +126,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
|
||||||
type: 'button',
|
type: 'button',
|
||||||
actionType: 'confirm',
|
actionType: 'confirm',
|
||||||
label: '确认',
|
label: '确认',
|
||||||
primary: true
|
primary: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,10 +134,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSelfClose() {
|
handleSelfClose() {
|
||||||
const {
|
const {onClose, store} = this.props;
|
||||||
onClose,
|
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
// clear error
|
// clear error
|
||||||
store.updateMessage();
|
store.updateMessage();
|
||||||
|
@ -158,10 +142,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAction(e: React.UIEvent<any>, action: Action, data: object) {
|
handleAction(e: React.UIEvent<any>, action: Action, data: object) {
|
||||||
const {
|
const {store, onAction} = this.props;
|
||||||
store,
|
|
||||||
onAction
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (action.type === 'reset') {
|
if (action.type === 'reset') {
|
||||||
store.reset();
|
store.reset();
|
||||||
|
@ -172,10 +153,8 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDialogConfirm(values: object[], action:Action, ...args:Array<any>) {
|
handleDialogConfirm(values: object[], action: Action, ...args: Array<any>) {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (action.mergeData && values.length === 1 && values[0]) {
|
if (action.mergeData && values.length === 1 && values[0]) {
|
||||||
store.updateData(values[0]);
|
store.updateData(values[0]);
|
||||||
|
@ -190,10 +169,8 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
|
||||||
store.closeDialog();
|
store.closeDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDialogClose(...args:Array<any>) {
|
handleDialogClose(...args: Array<any>) {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const action = store.action as Action;
|
const action = store.action as Action;
|
||||||
const dialog = action.dialog as any;
|
const dialog = action.dialog as any;
|
||||||
|
@ -204,11 +181,9 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
|
||||||
|
|
||||||
store.closeDialog();
|
store.closeDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDrawerConfirm(values: object[], action:Action, ...args:Array<any>) {
|
handleDrawerConfirm(values: object[], action: Action, ...args: Array<any>) {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (action.mergeData && values.length === 1 && values[0]) {
|
if (action.mergeData && values.length === 1 && values[0]) {
|
||||||
store.updateData(values[0]);
|
store.updateData(values[0]);
|
||||||
|
@ -223,10 +198,8 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
|
||||||
store.closeDrawer();
|
store.closeDrawer();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDrawerClose(...args:Array<any>) {
|
handleDrawerClose(...args: Array<any>) {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const action = store.action as Action;
|
const action = store.action as Action;
|
||||||
const drawer = action.drawer as any;
|
const drawer = action.drawer as any;
|
||||||
|
@ -239,79 +212,72 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEntered() {
|
handleEntered() {
|
||||||
this.state.entered || this.setState({
|
this.state.entered ||
|
||||||
entered: true
|
this.setState({
|
||||||
})
|
entered: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleExited() {
|
handleExited() {
|
||||||
const {store} = this.props;
|
const {store} = this.props;
|
||||||
store.reset();
|
store.reset();
|
||||||
this.state.entered && this.setState({
|
this.state.entered &&
|
||||||
entered: false
|
this.setState({
|
||||||
})
|
entered: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleFormInit(data:any) {
|
handleFormInit(data: any) {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
store.setFormData(data);
|
store.setFormData(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleFormChange(data:any) {
|
handleFormChange(data: any) {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
store.setFormData(data);
|
store.setFormData(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleFormSaved(data:any, response:any) {
|
handleFormSaved(data: any, response: any) {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
store.setFormData({
|
store.setFormData({
|
||||||
...data,
|
...data,
|
||||||
...response
|
...response,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChildFinished(value:any, action:Action) {
|
handleChildFinished(value: any, action: Action) {
|
||||||
// 下面会覆盖
|
// 下面会覆盖
|
||||||
}
|
}
|
||||||
|
|
||||||
renderBody(body:SchemaNode, key?:any):React.ReactNode {
|
renderBody(body: SchemaNode, key?: any): React.ReactNode {
|
||||||
let {
|
let {render, store} = this.props;
|
||||||
render,
|
|
||||||
store,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (Array.isArray(body)) {
|
if (Array.isArray(body)) {
|
||||||
return body.map((body, key) => this.renderBody(body, key));
|
return body.map((body, key) => this.renderBody(body, key));
|
||||||
}
|
}
|
||||||
|
|
||||||
let subProps:any = {
|
let subProps: any = {
|
||||||
key,
|
key,
|
||||||
disabled: store.loading,
|
disabled: store.loading,
|
||||||
onAction: this.handleAction,
|
onAction: this.handleAction,
|
||||||
onFinished: this.handleChildFinished
|
onFinished: this.handleChildFinished,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!(body as Schema).type) {
|
if (!(body as Schema).type) {
|
||||||
return render(`body${key ? `/${key}` : ''}`, body, subProps);
|
return render(`body${key ? `/${key}` : ''}`, body, subProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
let schema:Schema = body as Schema;
|
let schema: Schema = body as Schema;
|
||||||
|
|
||||||
if (schema.type === 'form') {
|
if (schema.type === 'form') {
|
||||||
schema = {
|
schema = {
|
||||||
mode: 'horizontal',
|
mode: 'horizontal',
|
||||||
wrapWithPanel: false,
|
wrapWithPanel: false,
|
||||||
submitText: null,
|
submitText: null,
|
||||||
...schema
|
...schema,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 同步数据到 Dialog 层,方便 actions 根据表单数据联动。
|
// 同步数据到 Dialog 层,方便 actions 根据表单数据联动。
|
||||||
|
@ -330,31 +296,35 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {store, render, classnames: cx} = this.props;
|
||||||
store,
|
|
||||||
render,
|
|
||||||
classnames: cx
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cx('Modal-footer')}>
|
<div className={cx('Modal-footer')}>
|
||||||
{store.loading || store.error ? (
|
{store.loading || store.error ? (
|
||||||
<div className={cx("Dialog-info")} key="info">
|
<div className={cx('Dialog-info')} key="info">
|
||||||
{store.loading ? render('info', {
|
{store.loading
|
||||||
type: 'spinner'
|
? render(
|
||||||
}, {
|
'info',
|
||||||
key: 'info',
|
{
|
||||||
size: 'sm'
|
type: 'spinner',
|
||||||
}) : null}
|
},
|
||||||
{store.error ? (<span className={cx("Dialog-error")}>{store.msg}</span>) : null}
|
{
|
||||||
|
key: 'info',
|
||||||
|
size: 'sm',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
: null}
|
||||||
|
{store.error ? <span className={cx('Dialog-error')}>{store.msg}</span> : null}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{actions.map((action, key) => render(`action/${key}`, action, {
|
{actions.map((action, key) =>
|
||||||
data: store.formData,
|
render(`action/${key}`, action, {
|
||||||
onAction: this.handleAction,
|
data: store.formData,
|
||||||
key,
|
onAction: this.handleAction,
|
||||||
disabled: action.disabled || store.loading
|
key,
|
||||||
}))}
|
disabled: action.disabled || store.loading,
|
||||||
|
})
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -377,7 +347,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
|
||||||
showCloseButton,
|
showCloseButton,
|
||||||
env,
|
env,
|
||||||
classnames: cx,
|
classnames: cx,
|
||||||
classPrefix
|
classPrefix,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
// console.log('Render Dialog');
|
// console.log('Render Dialog');
|
||||||
|
@ -401,60 +371,82 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
|
||||||
disabled={store.loading}
|
disabled={store.loading}
|
||||||
>
|
>
|
||||||
{title && typeof title === 'string' ? (
|
{title && typeof title === 'string' ? (
|
||||||
<div className={cx("Modal-header", headerClassName)}>
|
<div className={cx('Modal-header', headerClassName)}>
|
||||||
{showCloseButton !== false && !store.loading ? (<a data-tooltip="关闭弹窗" onClick={this.handleSelfClose} className={cx("Modal-close")}>{closeIcon}</a>) : null}
|
{showCloseButton !== false && !store.loading ? (
|
||||||
<div className={cx("Modal-title")}>{filter(title, store.formData)}</div>
|
<a data-tooltip="关闭弹窗" onClick={this.handleSelfClose} className={cx('Modal-close')}>
|
||||||
|
{closeIcon}
|
||||||
|
</a>
|
||||||
|
) : null}
|
||||||
|
<div className={cx('Modal-title')}>{filter(title, store.formData)}</div>
|
||||||
</div>
|
</div>
|
||||||
) : title ? (
|
) : title ? (
|
||||||
<div className={cx("Modal-header", headerClassName)}>
|
<div className={cx('Modal-header', headerClassName)}>
|
||||||
{showCloseButton !== false && !store.loading ? (<a data-tooltip="关闭弹窗" onClick={this.handleSelfClose} className={cx("Modal-close")}>{closeIcon}</a>) : null}
|
{showCloseButton !== false && !store.loading ? (
|
||||||
|
<a data-tooltip="关闭弹窗" onClick={this.handleSelfClose} className={cx('Modal-close')}>
|
||||||
|
{closeIcon}
|
||||||
|
</a>
|
||||||
|
) : null}
|
||||||
{render('title', title, {
|
{render('title', title, {
|
||||||
data: store.formData
|
data: store.formData,
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
) : showCloseButton !== false && !store.loading ? (
|
) : 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}
|
) : null}
|
||||||
|
|
||||||
{header ? render('header', header, {
|
{header
|
||||||
data: store.formData
|
? render('header', header, {
|
||||||
}) : null}
|
data: store.formData,
|
||||||
|
})
|
||||||
|
: null}
|
||||||
|
|
||||||
{!this.state.entered && lazyRender ? (
|
{!this.state.entered && lazyRender ? (
|
||||||
<div className={cx("Modal-body", bodyClassName)}>
|
<div className={cx('Modal-body', bodyClassName)} />
|
||||||
|
) : body ? (
|
||||||
</div>
|
<div className={cx('Modal-body', bodyClassName)}>{this.renderBody(body, 'body')}</div>
|
||||||
) : body ? (
|
|
||||||
<div className={cx("Modal-body", bodyClassName)}>
|
|
||||||
{this.renderBody(body, 'body')}
|
|
||||||
</div>
|
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{this.renderFooter()}
|
{this.renderFooter()}
|
||||||
|
|
||||||
{body ? render('drawer', { // 支持嵌套
|
{body
|
||||||
...(store.action as Action) && (store.action as Action).drawer as object,
|
? render(
|
||||||
type: 'drawer'
|
'drawer',
|
||||||
}, {
|
{
|
||||||
key: 'drawer',
|
// 支持嵌套
|
||||||
data: store.drawerData,
|
...((store.action as Action) && ((store.action as Action).drawer as object)),
|
||||||
onConfirm: this.handleDrawerConfirm,
|
type: 'drawer',
|
||||||
onClose: this.handleDrawerClose,
|
},
|
||||||
show: store.drawerOpen,
|
{
|
||||||
onAction: this.handleAction
|
key: 'drawer',
|
||||||
}) : null}
|
data: store.drawerData,
|
||||||
|
onConfirm: this.handleDrawerConfirm,
|
||||||
|
onClose: this.handleDrawerClose,
|
||||||
|
show: store.drawerOpen,
|
||||||
|
onAction: this.handleAction,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
: null}
|
||||||
|
|
||||||
{body ? render('dialog', { // 支持嵌套
|
{body
|
||||||
...(store.action as Action) && (store.action as Action).dialog as object,
|
? render(
|
||||||
type: 'dialog'
|
'dialog',
|
||||||
}, {
|
{
|
||||||
key: 'dialog',
|
// 支持嵌套
|
||||||
data: store.dialogData,
|
...((store.action as Action) && ((store.action as Action).dialog as object)),
|
||||||
onConfirm: this.handleDialogConfirm,
|
type: 'dialog',
|
||||||
onClose: this.handleDialogClose,
|
},
|
||||||
show: store.dialogOpen,
|
{
|
||||||
onAction: this.handleAction
|
key: 'dialog',
|
||||||
}) : null}
|
data: store.dialogData,
|
||||||
|
onConfirm: this.handleDialogConfirm,
|
||||||
|
onClose: this.handleDialogClose,
|
||||||
|
show: store.dialogOpen,
|
||||||
|
onAction: this.handleAction,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
: null}
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -465,7 +457,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
|
||||||
storeType: ModalStore.name,
|
storeType: ModalStore.name,
|
||||||
storeExtendsData: false,
|
storeExtendsData: false,
|
||||||
name: 'dialog',
|
name: 'dialog',
|
||||||
isolateScope: true
|
isolateScope: true,
|
||||||
})
|
})
|
||||||
export class DialogRenderer extends Dialog {
|
export class DialogRenderer extends Dialog {
|
||||||
static contextType = ScopedContext;
|
static contextType = ScopedContext;
|
||||||
|
@ -490,14 +482,16 @@ export class DialogRenderer extends Dialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
const components = scoped.getComponents();
|
const components = scoped.getComponents();
|
||||||
const targets:Array<any> = [];
|
const targets: Array<any> = [];
|
||||||
const {
|
const {onConfirm, store} = this.props;
|
||||||
onConfirm,
|
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (action.target) {
|
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) {
|
if (!targets.length) {
|
||||||
|
@ -511,21 +505,27 @@ export class DialogRenderer extends Dialog {
|
||||||
if (targets.length) {
|
if (targets.length) {
|
||||||
store.markBusying(true);
|
store.markBusying(true);
|
||||||
store.updateMessage();
|
store.updateMessage();
|
||||||
|
|
||||||
Promise
|
Promise.all(
|
||||||
.all(targets.map(target => target.doAction({
|
targets.map(target =>
|
||||||
...action,
|
target.doAction(
|
||||||
from: this.$$id
|
{
|
||||||
}, ctx, true)))
|
...action,
|
||||||
|
from: this.$$id,
|
||||||
|
},
|
||||||
|
ctx,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
.then(values => {
|
.then(values => {
|
||||||
if (
|
if (
|
||||||
(action.type === 'submit'
|
(action.type === 'submit' ||
|
||||||
|| action.actionType === 'submit'
|
action.actionType === 'submit' ||
|
||||||
|| action.actionType === 'confirm')
|
action.actionType === 'confirm') &&
|
||||||
&& action.close !== false
|
action.close !== false
|
||||||
) {
|
) {
|
||||||
onConfirm
|
onConfirm && onConfirm(values, rawAction || action, ctx, targets);
|
||||||
&& onConfirm(values, rawAction || action, ctx, targets)
|
|
||||||
} else if (action.close) {
|
} else if (action.close) {
|
||||||
this.handleSelfClose();
|
this.handleSelfClose();
|
||||||
}
|
}
|
||||||
|
@ -539,17 +539,17 @@ export class DialogRenderer extends Dialog {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAction(e: React.UIEvent<any>, action: Action, data: object, throwErrors: boolean = false, delegate?:boolean) {
|
handleAction(
|
||||||
const {
|
e: React.UIEvent<any>,
|
||||||
onAction,
|
action: Action,
|
||||||
store,
|
data: object,
|
||||||
onConfirm,
|
throwErrors: boolean = false,
|
||||||
|
delegate?: boolean
|
||||||
} = this.props;
|
) {
|
||||||
|
const {onAction, store, onConfirm} = this.props;
|
||||||
|
|
||||||
if (action.from === this.$$id) {
|
if (action.from === this.$$id) {
|
||||||
return onAction ? onAction(e, action, data, throwErrors, true) : false;
|
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') {
|
} else if (action.actionType === 'close' || action.actionType === 'cancel') {
|
||||||
this.handleSelfClose();
|
this.handleSelfClose();
|
||||||
} else if (action.actionType === 'confirm') {
|
} else if (action.actionType === 'confirm') {
|
||||||
this.tryChildrenToHandle({
|
this.tryChildrenToHandle(
|
||||||
...action,
|
{
|
||||||
actionType: 'submit'
|
...action,
|
||||||
}, data, action) || this.handleSelfClose();
|
actionType: 'submit',
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
action
|
||||||
|
) || this.handleSelfClose();
|
||||||
} else if (action.actionType === 'next' || action.actionType === 'prev') {
|
} else if (action.actionType === 'next' || action.actionType === 'prev') {
|
||||||
if (action.type === 'submit') {
|
if (action.type === 'submit') {
|
||||||
this.tryChildrenToHandle({
|
this.tryChildrenToHandle(
|
||||||
...action,
|
{
|
||||||
actionType: 'submit'
|
...action,
|
||||||
}, data, action) || this.handleSelfClose();
|
actionType: 'submit',
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
action
|
||||||
|
) || this.handleSelfClose();
|
||||||
} else {
|
} else {
|
||||||
onConfirm([data], action, data, []);
|
onConfirm([data], action, data, []);
|
||||||
}
|
}
|
||||||
|
@ -588,20 +596,22 @@ export class DialogRenderer extends Dialog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChildFinished(value:any, action:Action) {
|
handleChildFinished(value: any, action: Action) {
|
||||||
if (action && action.from === this.$$id || action.close === false) {
|
if ((action && action.from === this.$$id) || action.close === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const scoped = this.context as IScopedContext;
|
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 onConfirm = this.props.onConfirm;
|
||||||
const onClose = this.props.onClose;
|
const onClose = this.props.onClose;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
components.length === 1
|
components.length === 1 &&
|
||||||
&& (components[0].props.type === 'form' || components[0].props.type === 'wizard')
|
(components[0].props.type === 'form' || components[0].props.type === 'wizard') &&
|
||||||
&& (action.close === true || components[0].props.closeDialogOnSubmit !== false)
|
(action.close === true || components[0].props.closeDialogOnSubmit !== false)
|
||||||
) {
|
) {
|
||||||
onConfirm && onConfirm([value], action, {}, components);
|
onConfirm && onConfirm([value], action, {}, components);
|
||||||
} else if (action.close === true) {
|
} else if (action.close === true) {
|
||||||
|
@ -621,11 +631,11 @@ export class DialogRenderer extends Dialog {
|
||||||
scoped.reload(action.reload, store.data);
|
scoped.reload(action.reload, store.data);
|
||||||
} else {
|
} else {
|
||||||
// 没有设置,则自动让页面中 crud 刷新。
|
// 没有设置,则自动让页面中 crud 刷新。
|
||||||
scoped.getComponents()
|
scoped
|
||||||
|
.getComponents()
|
||||||
.filter((item: any) => item.props.type === 'crud')
|
.filter((item: any) => item.props.type === 'crud')
|
||||||
.forEach((item: any) => item.reload && item.reload());
|
.forEach((item: any) => item.reload && item.reload());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDrawerConfirm(values: object[], action: Action, ...rest: Array<any>) {
|
handleDrawerConfirm(values: object[], action: Action, ...rest: Array<any>) {
|
||||||
|
@ -642,10 +652,11 @@ export class DialogRenderer extends Dialog {
|
||||||
scoped.reload(action.reload, store.data);
|
scoped.reload(action.reload, store.data);
|
||||||
} else {
|
} else {
|
||||||
// 没有设置,则自动让页面中 crud 刷新。
|
// 没有设置,则自动让页面中 crud 刷新。
|
||||||
scoped.getComponents()
|
scoped
|
||||||
|
.getComponents()
|
||||||
.filter((item: any) => item.props.type === 'crud')
|
.filter((item: any) => item.props.type === 'crud')
|
||||||
.forEach((item: any) => item.reload && item.reload());
|
.forEach((item: any) => item.reload && item.reload());
|
||||||
}
|
}
|
||||||
}, 300);
|
}, 300);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,33 +1,25 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import {ServiceStore, IServiceStore} from '../store/service';
|
import {ServiceStore, IServiceStore} from '../store/service';
|
||||||
import {
|
import {filter} from '../utils/tpl';
|
||||||
filter
|
|
||||||
} from '../utils/tpl';
|
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
|
|
||||||
export interface DividerProps extends RendererProps {
|
export interface DividerProps extends RendererProps {}
|
||||||
}
|
|
||||||
|
|
||||||
export default class Divider extends React.Component<DividerProps, object> {
|
export default class Divider extends React.Component<DividerProps, object> {
|
||||||
static defaultProps:Partial<DividerProps>= {
|
static defaultProps: Partial<DividerProps> = {
|
||||||
className: ''
|
className: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const cx = this.props.classnames;
|
const cx = this.props.classnames;
|
||||||
const className = this.props.className;
|
const className = this.props.className;
|
||||||
return (
|
return <div className={cx('Divider', className)} />;
|
||||||
<div className={cx('Divider', className)}></div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)(?:divider|hr)$/,
|
test: /(^|\/)(?:divider|hr)$/,
|
||||||
name: 'divider'
|
name: 'divider',
|
||||||
})
|
})
|
||||||
export class DividerRenderer extends Divider {}
|
export class DividerRenderer extends Divider {}
|
||||||
|
|
|
@ -1,24 +1,17 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as PropTypes from 'prop-types';
|
import * as PropTypes from 'prop-types';
|
||||||
import Scoped, { ScopedContext, IScopedContext } from '../Scoped';
|
import Scoped, {ScopedContext, IScopedContext} from '../Scoped';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
import {ServiceStore, IServiceStore} from '../store/service';
|
||||||
RendererProps
|
import {observer} from 'mobx-react';
|
||||||
} from '../factory';
|
import {SchemaNode, Schema, Action} from '../types';
|
||||||
import { ServiceStore, IServiceStore } from '../store/service';
|
|
||||||
import { observer } from "mobx-react";
|
|
||||||
import {
|
|
||||||
SchemaNode,
|
|
||||||
Schema,
|
|
||||||
Action
|
|
||||||
} from '../types';
|
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
import {default as DrawerContainer} from '../components/Drawer';
|
import {default as DrawerContainer} from '../components/Drawer';
|
||||||
import findLast = require('lodash/findLast');
|
import findLast = require('lodash/findLast');
|
||||||
import { guid } from '../utils/helper'
|
import {guid} from '../utils/helper';
|
||||||
import { reaction } from 'mobx';
|
import {reaction} from 'mobx';
|
||||||
import { findDOMNode } from 'react-dom';
|
import {findDOMNode} from 'react-dom';
|
||||||
import { IModalStore, ModalStore } from '../store/modal';
|
import {IModalStore, ModalStore} from '../store/modal';
|
||||||
|
|
||||||
export interface DrawerProps extends RendererProps {
|
export interface DrawerProps extends RendererProps {
|
||||||
title?: string; // 标题
|
title?: string; // 标题
|
||||||
|
@ -43,18 +36,18 @@ export interface DrawerProps extends RendererProps {
|
||||||
|
|
||||||
export default class Drawer extends React.Component<DrawerProps, object> {
|
export default class Drawer extends React.Component<DrawerProps, object> {
|
||||||
static propsList: Array<string> = [
|
static propsList: Array<string> = [
|
||||||
"title",
|
'title',
|
||||||
"size",
|
'size',
|
||||||
"closeOnEsc",
|
'closeOnEsc',
|
||||||
"children",
|
'children',
|
||||||
"bodyClassName",
|
'bodyClassName',
|
||||||
"confirm",
|
'confirm',
|
||||||
"position",
|
'position',
|
||||||
"onClose",
|
'onClose',
|
||||||
"onConfirm",
|
'onConfirm',
|
||||||
"show",
|
'show',
|
||||||
"resizable",
|
'resizable',
|
||||||
"overlay"
|
'overlay',
|
||||||
];
|
];
|
||||||
static defaultProps: Partial<DrawerProps> = {
|
static defaultProps: Partial<DrawerProps> = {
|
||||||
title: '',
|
title: '',
|
||||||
|
@ -63,15 +56,15 @@ export default class Drawer extends React.Component<DrawerProps, object> {
|
||||||
position: 'right',
|
position: 'right',
|
||||||
resizable: false,
|
resizable: false,
|
||||||
overlay: true,
|
overlay: true,
|
||||||
closeOnEsc: false
|
closeOnEsc: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
reaction: any;
|
reaction: any;
|
||||||
$$id: string = guid();
|
$$id: string = guid();
|
||||||
drawer: any;
|
drawer: any;
|
||||||
state = {
|
state = {
|
||||||
resizeCoord: 0
|
resizeCoord: 0,
|
||||||
}
|
};
|
||||||
constructor(props: DrawerProps) {
|
constructor(props: DrawerProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
|
@ -111,10 +104,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
|
||||||
}
|
}
|
||||||
|
|
||||||
buildActions(): Array<Action> {
|
buildActions(): Array<Action> {
|
||||||
const {
|
const {actions, confirm} = this.props;
|
||||||
actions,
|
|
||||||
confirm
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (typeof actions !== 'undefined') {
|
if (typeof actions !== 'undefined') {
|
||||||
return actions;
|
return actions;
|
||||||
|
@ -124,7 +114,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
|
||||||
ret.push({
|
ret.push({
|
||||||
type: 'button',
|
type: 'button',
|
||||||
actionType: 'close',
|
actionType: 'close',
|
||||||
label: '取消'
|
label: '取消',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (confirm) {
|
if (confirm) {
|
||||||
|
@ -132,7 +122,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
|
||||||
type: 'button',
|
type: 'button',
|
||||||
actionType: 'confirm',
|
actionType: 'confirm',
|
||||||
label: '确认',
|
label: '确认',
|
||||||
primary: true
|
primary: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,10 +130,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSelfClose() {
|
handleSelfClose() {
|
||||||
const {
|
const {onClose, store} = this.props;
|
||||||
onClose,
|
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
// clear error
|
// clear error
|
||||||
store.updateMessage();
|
store.updateMessage();
|
||||||
|
@ -151,10 +138,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAction(e: React.UIEvent<any>, action: Action, data: object) {
|
handleAction(e: React.UIEvent<any>, action: Action, data: object) {
|
||||||
const {
|
const {onClose, onAction} = this.props;
|
||||||
onClose,
|
|
||||||
onAction
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (action.actionType === 'close') {
|
if (action.actionType === 'close') {
|
||||||
onClose();
|
onClose();
|
||||||
|
@ -163,10 +147,8 @@ export default class Drawer extends React.Component<DrawerProps, object> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDrawerConfirm(values: object[], action:Action, ...args: Array<any>) {
|
handleDrawerConfirm(values: object[], action: Action, ...args: Array<any>) {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (action.mergeData && values.length === 1 && values[0]) {
|
if (action.mergeData && values.length === 1 && values[0]) {
|
||||||
store.updateData(values[0]);
|
store.updateData(values[0]);
|
||||||
|
@ -183,9 +165,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDrawerClose(...args: Array<any>) {
|
handleDrawerClose(...args: Array<any>) {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const action = store.action as Action;
|
const action = store.action as Action;
|
||||||
const drawer = action.drawer as any;
|
const drawer = action.drawer as any;
|
||||||
|
@ -197,10 +177,8 @@ export default class Drawer extends React.Component<DrawerProps, object> {
|
||||||
store.closeDrawer();
|
store.closeDrawer();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDialogConfirm(values: object[], action:Action, ...args: Array<any>) {
|
handleDialogConfirm(values: object[], action: Action, ...args: Array<any>) {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (action.mergeData && values.length === 1 && values[0]) {
|
if (action.mergeData && values.length === 1 && values[0]) {
|
||||||
store.updateData(values[0]);
|
store.updateData(values[0]);
|
||||||
|
@ -217,9 +195,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDialogClose(...args: Array<any>) {
|
handleDialogClose(...args: Array<any>) {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const action = store.action as Action;
|
const action = store.action as Action;
|
||||||
const dialog = action.dialog as any;
|
const dialog = action.dialog as any;
|
||||||
|
@ -235,30 +211,24 @@ export default class Drawer extends React.Component<DrawerProps, object> {
|
||||||
// 下面会覆盖
|
// 下面会覆盖
|
||||||
}
|
}
|
||||||
|
|
||||||
handleFormInit(data:any) {
|
handleFormInit(data: any) {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
store.setFormData(data);
|
store.setFormData(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleFormChange(data:any) {
|
handleFormChange(data: any) {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
store.setFormData(data);
|
store.setFormData(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleFormSaved(data:any, response:any) {
|
handleFormSaved(data: any, response: any) {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
store.setFormData({
|
store.setFormData({
|
||||||
...data,
|
...data,
|
||||||
...response
|
...response,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,17 +238,14 @@ export default class Drawer extends React.Component<DrawerProps, object> {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderBody(body: SchemaNode, key?: any): React.ReactNode {
|
renderBody(body: SchemaNode, key?: any): React.ReactNode {
|
||||||
let {
|
let {render, store} = this.props;
|
||||||
render,
|
|
||||||
store,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
let schema: Schema = body as Schema;
|
let schema: Schema = body as Schema;
|
||||||
let subProps: any = {
|
let subProps: any = {
|
||||||
key,
|
key,
|
||||||
disabled: store.loading,
|
disabled: store.loading,
|
||||||
onAction: this.handleAction,
|
onAction: this.handleAction,
|
||||||
onFinished: this.handleChildFinished
|
onFinished: this.handleChildFinished,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (schema.type === 'form') {
|
if (schema.type === 'form') {
|
||||||
|
@ -286,7 +253,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
|
||||||
mode: 'horizontal',
|
mode: 'horizontal',
|
||||||
wrapWithPanel: false,
|
wrapWithPanel: false,
|
||||||
submitText: null,
|
submitText: null,
|
||||||
...schema
|
...schema,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 同步数据到 Dialog 层,方便 actions 根据表单数据联动。
|
// 同步数据到 Dialog 层,方便 actions 根据表单数据联动。
|
||||||
|
@ -305,83 +272,93 @@ export default class Drawer extends React.Component<DrawerProps, object> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {store, render, classnames: cx} = this.props;
|
||||||
store,
|
|
||||||
render,
|
|
||||||
classnames: cx
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cx("Drawer-footer")}>
|
<div className={cx('Drawer-footer')}>
|
||||||
{store.loading || store.error ? (
|
{store.loading || store.error ? (
|
||||||
<div className={cx("Drawer-info")}>
|
<div className={cx('Drawer-info')}>
|
||||||
{store.loading ? render('info', {
|
{store.loading
|
||||||
type: 'spinner'
|
? render(
|
||||||
}, {
|
'info',
|
||||||
key: 'info'
|
{
|
||||||
}) : null}
|
type: 'spinner',
|
||||||
{store.error ? (<span className={cx("Drawer-msg")}>{store.msg}</span>) : null}
|
},
|
||||||
|
{
|
||||||
|
key: 'info',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
: null}
|
||||||
|
{store.error ? <span className={cx('Drawer-msg')}>{store.msg}</span> : null}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{actions.map((action, key) => render(`action/${key}`, action, {
|
{actions.map((action, key) =>
|
||||||
onAction: this.handleAction,
|
render(`action/${key}`, action, {
|
||||||
data: store.formData,
|
onAction: this.handleAction,
|
||||||
key,
|
data: store.formData,
|
||||||
disabled: action.disabled || store.loading
|
key,
|
||||||
}))}
|
disabled: action.disabled || store.loading,
|
||||||
|
})
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderResizeCtrl() {
|
renderResizeCtrl() {
|
||||||
const {
|
const {classnames: cx} = this.props;
|
||||||
classnames: cx
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={cx('Drawer-resizeCtrl')} onMouseDown={this.resizeMouseDown}>
|
||||||
className={cx('Drawer-resizeCtrl')}
|
<div className={cx('Drawer-resizeIcon')}>···</div>
|
||||||
onMouseDown={this.resizeMouseDown}
|
|
||||||
>
|
|
||||||
<div className={cx("Drawer-resizeIcon")}>···</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
resizeMouseDown(e: React.MouseEvent<any>) {
|
resizeMouseDown(e: React.MouseEvent<any>) {
|
||||||
const {
|
const {position, classPrefix: ns} = this.props;
|
||||||
position,
|
|
||||||
classPrefix: ns
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
this.drawer = (findDOMNode(this) as HTMLElement).querySelector(`.${ns}Drawer-content`) as HTMLElement;
|
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 drawerWidth = getComputedStyle(this.drawer).width as string;
|
||||||
const drawerHeight = getComputedStyle(this.drawer).height as string;
|
const drawerHeight = getComputedStyle(this.drawer).height as string;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
resizeCoord:
|
resizeCoord:
|
||||||
(position === 'left' && e.clientX - resizeCtrl.offsetWidth - parseInt(drawerWidth.substring(0, drawerWidth.length - 2))) ||
|
(position === 'left' &&
|
||||||
(position === 'right' && document.body.offsetWidth - e.clientX - resizeCtrl.offsetWidth - parseInt(drawerWidth.substring(0, drawerWidth.length - 2))) ||
|
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 === 'right' &&
|
||||||
(position === 'bottom' && document.body.offsetHeight - e.clientY - resizeCtrl.offsetHeight - parseInt(drawerHeight.substring(0, drawerHeight.length - 2))) || 0
|
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('mousemove', this.bindResize);
|
||||||
document.body.addEventListener('mouseup', this.removeResize);
|
document.body.addEventListener('mouseup', this.removeResize);
|
||||||
}
|
}
|
||||||
|
|
||||||
bindResize(e: any) {
|
bindResize(e: any) {
|
||||||
const {
|
const {position} = this.props;
|
||||||
position
|
|
||||||
} = this.props;
|
|
||||||
const maxWH = 'calc(100% - 50px)';
|
const maxWH = 'calc(100% - 50px)';
|
||||||
const drawerStyle = this.drawer.style;
|
const drawerStyle = this.drawer.style;
|
||||||
let wh =
|
let wh =
|
||||||
(position === 'left' && e.clientX) ||
|
(position === 'left' && e.clientX) ||
|
||||||
(position === 'right' && document.body.offsetWidth - e.clientX) ||
|
(position === 'right' && document.body.offsetWidth - e.clientX) ||
|
||||||
(position === 'top' && e.clientY) ||
|
(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';
|
wh = wh - this.state.resizeCoord + 'px';
|
||||||
|
|
||||||
if (position === 'left' || position === 'right') {
|
if (position === 'left' || position === 'right') {
|
||||||
|
@ -418,7 +395,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
|
||||||
overlay,
|
overlay,
|
||||||
closeOnOutside,
|
closeOnOutside,
|
||||||
classPrefix: ns,
|
classPrefix: ns,
|
||||||
classnames: cx
|
classnames: cx,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const Container = wrapperComponent || DrawerContainer;
|
const Container = wrapperComponent || DrawerContainer;
|
||||||
|
@ -438,46 +415,60 @@ export default class Drawer extends React.Component<DrawerProps, object> {
|
||||||
closeOnOutside={closeOnOutside}
|
closeOnOutside={closeOnOutside}
|
||||||
container={env && env.getModalContainer ? env.getModalContainer() : undefined}
|
container={env && env.getModalContainer ? env.getModalContainer() : undefined}
|
||||||
>
|
>
|
||||||
<div className={cx("Drawer-header")}>
|
<div className={cx('Drawer-header')}>
|
||||||
{title ? (
|
{title ? (
|
||||||
<div className={cx("Drawer-title")}>{render('title', title, {
|
<div className={cx('Drawer-title')}>
|
||||||
data: store.formData
|
{render('title', title, {
|
||||||
})}</div>
|
data: store.formData,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{header ? render('header', header, {
|
{header
|
||||||
data: store.formData
|
? render('header', header, {
|
||||||
}) : null}
|
data: store.formData,
|
||||||
|
})
|
||||||
|
: null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={cx("Drawer-body", bodyClassName)}>
|
<div className={cx('Drawer-body', bodyClassName)}>{body ? this.renderBody(body, 'body') : null}</div>
|
||||||
{body ? this.renderBody(body, 'body') : null}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{this.renderFooter()}
|
{this.renderFooter()}
|
||||||
|
|
||||||
{body ? render('dialog', {
|
{body
|
||||||
...(store.action as Action) && (store.action as Action).dialog as object,
|
? render(
|
||||||
type: 'dialog'
|
'dialog',
|
||||||
}, {
|
{
|
||||||
key: 'dialog',
|
...((store.action as Action) && ((store.action as Action).dialog as object)),
|
||||||
data: store.dialogData,
|
type: 'dialog',
|
||||||
onConfirm: this.handleDialogConfirm,
|
},
|
||||||
onClose: this.handleDialogClose,
|
{
|
||||||
onAction: this.handleAction,
|
key: 'dialog',
|
||||||
show: store.dialogOpen,
|
data: store.dialogData,
|
||||||
}) : null}
|
onConfirm: this.handleDialogConfirm,
|
||||||
|
onClose: this.handleDialogClose,
|
||||||
|
onAction: this.handleAction,
|
||||||
|
show: store.dialogOpen,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
: null}
|
||||||
|
|
||||||
{body ? render('drawer', {
|
{body
|
||||||
...(store.action as Action) && (store.action as Action).drawer as object,
|
? render(
|
||||||
type: 'drawer'
|
'drawer',
|
||||||
}, {
|
{
|
||||||
key: 'drawer',
|
...((store.action as Action) && ((store.action as Action).drawer as object)),
|
||||||
data: store.drawerData,
|
type: 'drawer',
|
||||||
onConfirm: this.handleDrawerConfirm,
|
},
|
||||||
onClose: this.handleDrawerClose,
|
{
|
||||||
onAction: this.handleAction,
|
key: 'drawer',
|
||||||
show: store.drawerOpen,
|
data: store.drawerData,
|
||||||
}) : null}
|
onConfirm: this.handleDrawerConfirm,
|
||||||
|
onClose: this.handleDrawerClose,
|
||||||
|
onAction: this.handleAction,
|
||||||
|
show: store.drawerOpen,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
: null}
|
||||||
|
|
||||||
{resizable ? this.renderResizeCtrl() : null}
|
{resizable ? this.renderResizeCtrl() : null}
|
||||||
</Container>
|
</Container>
|
||||||
|
@ -490,7 +481,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
|
||||||
storeType: ModalStore.name,
|
storeType: ModalStore.name,
|
||||||
storeExtendsData: false,
|
storeExtendsData: false,
|
||||||
name: 'drawer',
|
name: 'drawer',
|
||||||
isolateScope: true
|
isolateScope: true,
|
||||||
})
|
})
|
||||||
export class DrawerRenderer extends Drawer {
|
export class DrawerRenderer extends Drawer {
|
||||||
static contextType = ScopedContext;
|
static contextType = ScopedContext;
|
||||||
|
@ -516,13 +507,15 @@ export class DrawerRenderer extends Drawer {
|
||||||
|
|
||||||
const components = scoped.getComponents();
|
const components = scoped.getComponents();
|
||||||
const targets: Array<any> = [];
|
const targets: Array<any> = [];
|
||||||
const {
|
const {onConfirm, store} = this.props;
|
||||||
onConfirm,
|
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (action.target) {
|
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) {
|
if (!targets.length) {
|
||||||
|
@ -537,20 +530,26 @@ export class DrawerRenderer extends Drawer {
|
||||||
store.markBusying(true);
|
store.markBusying(true);
|
||||||
store.updateMessage();
|
store.updateMessage();
|
||||||
|
|
||||||
Promise
|
Promise.all(
|
||||||
.all(targets.map(target => target.doAction({
|
targets.map(target =>
|
||||||
...action,
|
target.doAction(
|
||||||
from: this.$$id
|
{
|
||||||
}, ctx, true)))
|
...action,
|
||||||
|
from: this.$$id,
|
||||||
|
},
|
||||||
|
ctx,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
.then(values => {
|
.then(values => {
|
||||||
if (
|
if (
|
||||||
(action.type === 'submit'
|
(action.type === 'submit' ||
|
||||||
|| action.actionType === 'submit'
|
action.actionType === 'submit' ||
|
||||||
|| action.actionType === 'confirm')
|
action.actionType === 'confirm') &&
|
||||||
&& action.close !== false
|
action.close !== false
|
||||||
) {
|
) {
|
||||||
onConfirm
|
onConfirm && onConfirm(values, rawAction || action, ctx, targets);
|
||||||
&& onConfirm(values, rawAction || action, ctx, targets)
|
|
||||||
} else if (action.close) {
|
} else if (action.close) {
|
||||||
this.handleSelfClose();
|
this.handleSelfClose();
|
||||||
}
|
}
|
||||||
|
@ -564,16 +563,17 @@ export class DrawerRenderer extends Drawer {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAction(e: React.UIEvent<any>, action: Action, data: object, throwErrors: boolean = false, delegate?: boolean) {
|
handleAction(
|
||||||
const {
|
e: React.UIEvent<any>,
|
||||||
onClose,
|
action: Action,
|
||||||
onAction,
|
data: object,
|
||||||
store
|
throwErrors: boolean = false,
|
||||||
} = this.props;
|
delegate?: boolean
|
||||||
|
) {
|
||||||
|
const {onClose, onAction, store} = this.props;
|
||||||
|
|
||||||
if (action.from === this.$$id) {
|
if (action.from === this.$$id) {
|
||||||
return onAction ? onAction(e, action, data, throwErrors, true) : false;
|
return onAction ? onAction(e, action, data, throwErrors, true) : false;
|
||||||
|
@ -599,12 +599,14 @@ export class DrawerRenderer extends Drawer {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChildFinished(value: any, action: Action) {
|
handleChildFinished(value: any, action: Action) {
|
||||||
if (action && action.from === this.$$id || action.close === false) {
|
if ((action && action.from === this.$$id) || action.close === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const scoped = this.context as IScopedContext;
|
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 onConfirm = this.props.onConfirm;
|
||||||
|
|
||||||
if (components.length === 1 && (components[0].props.type === 'form' || components[0].props.type === 'wizard')) {
|
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);
|
scoped.reload(action.reload, store.data);
|
||||||
} else {
|
} else {
|
||||||
// 没有设置,则自动让页面中 crud 刷新。
|
// 没有设置,则自动让页面中 crud 刷新。
|
||||||
scoped.getComponents()
|
scoped
|
||||||
|
.getComponents()
|
||||||
.filter((item: any) => item.props.type === 'crud')
|
.filter((item: any) => item.props.type === 'crud')
|
||||||
.forEach((item: any) => item.reload && item.reload());
|
.forEach((item: any) => item.reload && item.reload());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDrawerConfirm(values: object[], action: Action, ...rest: Array<any>) {
|
handleDrawerConfirm(values: object[], action: Action, ...rest: Array<any>) {
|
||||||
|
@ -645,10 +647,11 @@ export class DrawerRenderer extends Drawer {
|
||||||
scoped.reload(action.reload, store.data);
|
scoped.reload(action.reload, store.data);
|
||||||
} else {
|
} else {
|
||||||
// 没有设置,则自动让页面中 crud 刷新。
|
// 没有设置,则自动让页面中 crud 刷新。
|
||||||
scoped.getComponents()
|
scoped
|
||||||
|
.getComponents()
|
||||||
.filter((item: any) => item.props.type === 'crud')
|
.filter((item: any) => item.props.type === 'crud')
|
||||||
.forEach((item: any) => item.reload && item.reload());
|
.forEach((item: any) => item.reload && item.reload());
|
||||||
}
|
}
|
||||||
}, 300);
|
}, 300);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import {RootCloseWrapper} from 'react-overlays';
|
import {RootCloseWrapper} from 'react-overlays';
|
||||||
import Overlay from '../components/Overlay';
|
import Overlay from '../components/Overlay';
|
||||||
import PopOver from '../components/PopOver';
|
import PopOver from '../components/PopOver';
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
import { isVisible } from '../utils/helper';
|
import {isVisible} from '../utils/helper';
|
||||||
import { filter } from '../utils/tpl';
|
import {filter} from '../utils/tpl';
|
||||||
|
|
||||||
|
|
||||||
export interface DropDownButtonProps extends RendererProps {
|
export interface DropDownButtonProps extends RendererProps {
|
||||||
block?: boolean;
|
block?: boolean;
|
||||||
|
@ -18,24 +14,23 @@ export interface DropDownButtonProps extends RendererProps {
|
||||||
buttons?: Array<any>;
|
buttons?: Array<any>;
|
||||||
caretIcon?: string;
|
caretIcon?: string;
|
||||||
iconOnly?: boolean;
|
iconOnly?: boolean;
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface DropDownButtonState {
|
export interface DropDownButtonState {
|
||||||
isOpened: boolean;
|
isOpened: boolean;
|
||||||
};
|
}
|
||||||
|
|
||||||
export default class DropDownButton extends React.Component<DropDownButtonProps, DropDownButtonState> {
|
export default class DropDownButton extends React.Component<DropDownButtonProps, DropDownButtonState> {
|
||||||
|
state: DropDownButtonState = {
|
||||||
state:DropDownButtonState = {
|
isOpened: false,
|
||||||
isOpened: false
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
caretIcon: 'fa fa-angle-down'
|
caretIcon: 'fa fa-angle-down',
|
||||||
};
|
};
|
||||||
|
|
||||||
target:any;
|
target: any;
|
||||||
constructor(props:DropDownButtonProps) {
|
constructor(props: DropDownButtonProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.open = this.open.bind(this);
|
this.open = this.open.bind(this);
|
||||||
|
@ -44,69 +39,57 @@ export default class DropDownButton extends React.Component<DropDownButtonProps,
|
||||||
this.domRef = this.domRef.bind(this);
|
this.domRef = this.domRef.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
domRef(ref:any) {
|
domRef(ref: any) {
|
||||||
this.target = ref;
|
this.target = ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
toogle(e:React.MouseEvent<any>) {
|
toogle(e: React.MouseEvent<any>) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
isOpened: !this.state.isOpened
|
isOpened: !this.state.isOpened,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
open() {
|
open() {
|
||||||
this.setState({
|
this.setState({
|
||||||
isOpened: true
|
isOpened: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
this.setState({
|
this.setState({
|
||||||
isOpened: false
|
isOpened: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderOuter() {
|
renderOuter() {
|
||||||
const {
|
const {render, buttons, data, popOverContainer, classnames: cx, classPrefix: ns, children, align} = this.props;
|
||||||
render,
|
|
||||||
buttons,
|
|
||||||
data,
|
|
||||||
popOverContainer,
|
|
||||||
classnames: cx,
|
|
||||||
classPrefix: ns,
|
|
||||||
children,
|
|
||||||
align
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
let body = (
|
let body = (
|
||||||
<RootCloseWrapper
|
<RootCloseWrapper disabled={!this.state.isOpened} onRootClose={this.close}>
|
||||||
disabled={!this.state.isOpened}
|
<ul className={cx('DropDown-menu')}>
|
||||||
onRootClose={this.close}
|
{children
|
||||||
>
|
? children
|
||||||
<ul
|
: Array.isArray(buttons)
|
||||||
className={cx("DropDown-menu")}
|
? buttons.map((button, index) => {
|
||||||
>
|
if (!isVisible(button, data)) {
|
||||||
{children ? children : Array.isArray(buttons) ? buttons.map((button, index) => {
|
return null;
|
||||||
if (!isVisible(button, data)) {
|
} else if (button === 'divider' || button.type === 'divider') {
|
||||||
return null;
|
return <li key={index} className={cx('DropDown-divider')} />;
|
||||||
} else if (button === 'divider' || button.type === 'divider') {
|
}
|
||||||
return (
|
|
||||||
<li key={index} className={cx("DropDown-divider")} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li key={index}>
|
<li key={index}>
|
||||||
{render(`button/${index}`, {
|
{render(`button/${index}`, {
|
||||||
type: 'button',
|
type: 'button',
|
||||||
...button,
|
...button,
|
||||||
isMenuItem: true
|
isMenuItem: true,
|
||||||
})}
|
})}
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
}) : null}
|
})
|
||||||
|
: null}
|
||||||
</ul>
|
</ul>
|
||||||
</RootCloseWrapper>
|
</RootCloseWrapper>
|
||||||
);
|
);
|
||||||
|
@ -119,11 +102,11 @@ export default class DropDownButton extends React.Component<DropDownButtonProps,
|
||||||
target={() => this.target}
|
target={() => this.target}
|
||||||
show
|
show
|
||||||
>
|
>
|
||||||
<PopOver
|
<PopOver
|
||||||
overlay
|
overlay
|
||||||
onHide={this.close}
|
onHide={this.close}
|
||||||
classPrefix={ns}
|
classPrefix={ns}
|
||||||
className={cx("DropDown-popover")}
|
className={cx('DropDown-popover')}
|
||||||
style={{minWidth: this.target.offsetWidth}}
|
style={{minWidth: this.target.offsetWidth}}
|
||||||
>
|
>
|
||||||
{body}
|
{body}
|
||||||
|
@ -149,7 +132,7 @@ export default class DropDownButton extends React.Component<DropDownButtonProps,
|
||||||
caretIcon,
|
caretIcon,
|
||||||
align,
|
align,
|
||||||
iconOnly,
|
iconOnly,
|
||||||
data
|
data,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -157,18 +140,24 @@ export default class DropDownButton extends React.Component<DropDownButtonProps,
|
||||||
className={cx('DropDown ', {
|
className={cx('DropDown ', {
|
||||||
'DropDown--block': block,
|
'DropDown--block': block,
|
||||||
'DropDown--alignRight': align === 'right',
|
'DropDown--alignRight': align === 'right',
|
||||||
'is-opened': this.state.isOpened
|
'is-opened': this.state.isOpened,
|
||||||
})}
|
})}
|
||||||
ref={this.domRef}
|
ref={this.domRef}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
onClick={this.toogle}
|
onClick={this.toogle}
|
||||||
disabled={disabled || btnDisabled}
|
disabled={disabled || btnDisabled}
|
||||||
className={cx('Button', className, (typeof level === 'undefined' ? 'Button--default' : level ? `Button--${level}` : ''), {
|
className={cx(
|
||||||
'Button--block': block,
|
'Button',
|
||||||
'Button--primary': primary,
|
className,
|
||||||
'Button--iconOnly': iconOnly
|
typeof level === 'undefined' ? 'Button--default' : level ? `Button--${level}` : '',
|
||||||
}, size ? `Button--${size}` : '')}
|
{
|
||||||
|
'Button--block': block,
|
||||||
|
'Button--primary': primary,
|
||||||
|
'Button--iconOnly': iconOnly,
|
||||||
|
},
|
||||||
|
size ? `Button--${size}` : ''
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{typeof label === 'string' ? filter(label, data) : label}
|
{typeof label === 'string' ? filter(label, data) : label}
|
||||||
<i className={cx('DropDown-caret', caretIcon)} />
|
<i className={cx('DropDown-caret', caretIcon)} />
|
||||||
|
@ -182,8 +171,6 @@ export default class DropDownButton extends React.Component<DropDownButtonProps,
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)dropdown-button$/,
|
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 * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
import {Schema} from '../types';
|
||||||
RendererProps
|
import {resolveVariable} from '../utils/tpl-builtin';
|
||||||
} from '../factory';
|
import {createObject, isObject} from '../utils/helper';
|
||||||
import {
|
|
||||||
Schema,
|
|
||||||
} from '../types';
|
|
||||||
import { resolveVariable } from '../utils/tpl-builtin';
|
|
||||||
import { createObject, isObject } from '../utils/helper';
|
|
||||||
|
|
||||||
export interface EachProps extends RendererProps {
|
export interface EachProps extends RendererProps {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -15,34 +10,35 @@ export interface EachProps extends RendererProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Each extends React.Component<EachProps> {
|
export default class Each extends React.Component<EachProps> {
|
||||||
|
|
||||||
static defaultProps: Partial<EachProps> = {
|
static defaultProps: Partial<EachProps> = {
|
||||||
className: '',
|
className: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {data, name, className, render, value, items} = this.props;
|
||||||
data,
|
|
||||||
name,
|
|
||||||
className,
|
|
||||||
render,
|
|
||||||
value,
|
|
||||||
items
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const arr = typeof value !== 'undefined'
|
const arr =
|
||||||
? (isObject(value) ? Object.keys(value).map(key =>({
|
typeof value !== 'undefined'
|
||||||
key: key,
|
? isObject(value)
|
||||||
value: value[key]
|
? Object.keys(value).map(key => ({
|
||||||
}))
|
key: key,
|
||||||
: Array.isArray(value) ? value : []) : resolveVariable(name, data);
|
value: value[key],
|
||||||
|
}))
|
||||||
|
: Array.isArray(value)
|
||||||
|
? value
|
||||||
|
: []
|
||||||
|
: resolveVariable(name, data);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
{Array.isArray(arr) && items ? arr.map((item:any, index:number) => render(`item/${index}`, items, {
|
{Array.isArray(arr) && items
|
||||||
data: createObject(data, isObject(item) ? item : {[name]: item, item: item}),
|
? arr.map((item: any, index: number) =>
|
||||||
key: index
|
render(`item/${index}`, items, {
|
||||||
})) : null}
|
data: createObject(data, isObject(item) ? item : {[name]: item, item: item}),
|
||||||
|
key: index,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
: null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -50,6 +46,6 @@ export default class Each extends React.Component<EachProps> {
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)(?:repeat|each)$/,
|
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 * as React from 'react';
|
||||||
import { Renderer, RendererProps } from "../factory";
|
import {Renderer, RendererProps} from '../factory';
|
||||||
import { Schema } from "../types";
|
import {Schema} from '../types';
|
||||||
import * as cx from "classnames";
|
import * as cx from 'classnames';
|
||||||
import pick = require("lodash/pick");
|
import pick = require('lodash/pick');
|
||||||
|
|
||||||
export const ColProps = [
|
export const ColProps = ['lg', 'md', 'sm', 'xs'];
|
||||||
"lg",
|
|
||||||
"md",
|
|
||||||
"sm",
|
|
||||||
"xs"
|
|
||||||
];
|
|
||||||
|
|
||||||
export type Column = Schema & {
|
export type Column = Schema & {
|
||||||
xs?: number;
|
xs?: number;
|
||||||
|
@ -44,19 +39,19 @@ export interface ColumnArray extends Array<ColumnNode> {}
|
||||||
|
|
||||||
export interface GridProps extends RendererProps {
|
export interface GridProps extends RendererProps {
|
||||||
columns: Array<Column>;
|
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) {
|
function fromBsClass(cn: string) {
|
||||||
if (typeof cn === "string" && cn) {
|
if (typeof cn === 'string' && cn) {
|
||||||
return cn.replace(/\bcol-(xs|sm|md|lg)-(\d+)\b/g, (_, bp, size) => `Grid-col--${bp}${size}`);
|
return cn.replace(/\bcol-(xs|sm|md|lg)-(\d+)\b/g, (_, bp, size) => `Grid-col--${bp}${size}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return cn;
|
return cn;
|
||||||
}
|
}
|
||||||
|
|
||||||
function copProps2Class(props:any):string {
|
function copProps2Class(props: any): string {
|
||||||
const cns:Array<string> = [];
|
const cns: Array<string> = [];
|
||||||
const modifiers = ColProps;
|
const modifiers = ColProps;
|
||||||
|
|
||||||
modifiers.forEach(modifier => props && props[modifier] && cns.push(`Grid-col--${modifier}${props[modifier]}`));
|
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> {
|
export default class Grid<T> extends React.Component<GridProps & T, object> {
|
||||||
static propsList: Array<string> = ["columns"];
|
static propsList: Array<string> = ['columns'];
|
||||||
static defaultProps = {};
|
static defaultProps = {};
|
||||||
|
|
||||||
renderChild(region:string, node:Schema, key: number, length: number) {
|
renderChild(region: string, node: Schema, key: number, length: number) {
|
||||||
const {
|
const {render, itemRender} = this.props;
|
||||||
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) {
|
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);
|
} = pick(column, ColProps);
|
||||||
|
|
||||||
colProps = {
|
colProps = {
|
||||||
...colProps
|
...colProps,
|
||||||
};
|
};
|
||||||
|
|
||||||
const cx = this.props.classnames;
|
const cx = this.props.classnames;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div key={key} className={cx(copProps2Class(colProps), fromBsClass((column as Column).columnClassName))}>
|
||||||
key={key}
|
{Array.isArray(column)
|
||||||
className={cx(copProps2Class(colProps), fromBsClass((column as Column).columnClassName))}
|
? this.renderColumns(column)
|
||||||
>
|
: this.renderChild(`column/${key}`, column, key, length)}
|
||||||
{Array.isArray(column)
|
|
||||||
? this.renderColumns(column)
|
|
||||||
: this.renderChild(`column/${key}`, column, key, length)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderColumns(columns: ColumnArray): React.ReactElement<any> | null {
|
renderColumns(columns: ColumnArray): React.ReactElement<any> | null {
|
||||||
const {
|
const {className, classnames: cx} = this.props;
|
||||||
className,
|
|
||||||
classnames: cx
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cx('Grid', className)}>
|
<div className={cx('Grid', className)}>
|
||||||
{columns.map((column, key) =>
|
{columns.map((column, key) => this.renderColumn(column, key, columns.length))}
|
||||||
this.renderColumn(column, key, columns.length)
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -122,6 +106,6 @@ export default class Grid<T> extends React.Component<GridProps & T, object> {
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)grid$/,
|
test: /(^|\/)grid$/,
|
||||||
name: 'grid'
|
name: 'grid',
|
||||||
})
|
})
|
||||||
export class GridRenderer extends Grid<{}> {}
|
export class GridRenderer extends Grid<{}> {}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import * as React from "react";
|
import * as React from 'react';
|
||||||
import { Renderer, RendererProps } from "../factory";
|
import {Renderer, RendererProps} from '../factory';
|
||||||
import { Api, SchemaNode, Schema, Action } from "../types";
|
import {Api, SchemaNode, Schema, Action} from '../types';
|
||||||
import * as cx from "classnames";
|
import * as cx from 'classnames';
|
||||||
import { isVisible } from "../utils/helper";
|
import {isVisible} from '../utils/helper';
|
||||||
|
|
||||||
|
|
||||||
export type Column = Schema & {
|
export type Column = Schema & {
|
||||||
columnClassName?: string;
|
columnClassName?: string;
|
||||||
|
@ -12,28 +11,22 @@ export type Column = Schema & {
|
||||||
export interface HBoxProps extends RendererProps {
|
export interface HBoxProps extends RendererProps {
|
||||||
columns: Array<Column>;
|
columns: Array<Column>;
|
||||||
className: string;
|
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> {
|
export default class HBox extends React.Component<HBoxProps, object> {
|
||||||
static propsList: Array<string> = ["columns"];
|
static propsList: Array<string> = ['columns'];
|
||||||
|
|
||||||
static defaultProps: Partial<HBoxProps> = {};
|
static defaultProps: Partial<HBoxProps> = {};
|
||||||
|
|
||||||
renderChild(region:string, node:Schema) {
|
renderChild(region: string, node: Schema) {
|
||||||
const {
|
const {render} = this.props;
|
||||||
render
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return render(region, node);
|
return render(region, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderColumn(column: Column, key: number, length: number) {
|
renderColumn(column: Column, key: number, length: number) {
|
||||||
const {
|
const {itemRender, data, classPrefix: ns} = this.props;
|
||||||
itemRender,
|
|
||||||
data,
|
|
||||||
classPrefix: ns
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (!isVisible(column, data)) {
|
if (!isVisible(column, data)) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -42,32 +35,22 @@ export default class HBox extends React.Component<HBoxProps, object> {
|
||||||
let style = {
|
let style = {
|
||||||
width: column.width,
|
width: column.width,
|
||||||
height: column.height,
|
height: column.height,
|
||||||
...column.style
|
...column.style,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div key={key} className={cx(`${ns}Hbox-col`, (column as Column).columnClassName)} style={style}>
|
||||||
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)}
|
{itemRender ? itemRender(column, key, length, this.props) : this.renderChild(`column/${key}`, column)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {className, columns, classPrefix: ns} = this.props;
|
||||||
className,
|
|
||||||
columns,
|
|
||||||
classPrefix: ns
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cx(`${ns}Hbox`, className)}>
|
<div className={cx(`${ns}Hbox`, className)}>
|
||||||
{columns.map((column, key) =>
|
{columns.map((column, key) => this.renderColumn(column, key, columns.length))}
|
||||||
this.renderColumn(column, key, columns.length)
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -75,6 +58,6 @@ export default class HBox extends React.Component<HBoxProps, object> {
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)hbox$/,
|
test: /(^|\/)hbox$/,
|
||||||
name: 'hbox'
|
name: 'hbox',
|
||||||
})
|
})
|
||||||
export class HBoxRenderer extends HBox {}
|
export class HBoxRenderer extends HBox {}
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
import {filter} from '../utils/tpl';
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import {
|
|
||||||
filter
|
|
||||||
} from '../utils/tpl';
|
|
||||||
|
|
||||||
export interface IFrameProps extends RendererProps {
|
export interface IFrameProps extends RendererProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
@ -13,30 +8,19 @@ export interface IFrameProps extends RendererProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class IFrame extends React.Component<IFrameProps, object> {
|
export default class IFrame extends React.Component<IFrameProps, object> {
|
||||||
static propsList: Array<string> = [
|
static propsList: Array<string> = ['src', 'className'];
|
||||||
"src",
|
static defaultProps: Partial<IFrameProps> = {
|
||||||
"className"
|
|
||||||
];
|
|
||||||
static defaultProps:Partial<IFrameProps>= {
|
|
||||||
className: '',
|
className: '',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
frameBorder: 0
|
frameBorder: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let {
|
let {className, src, width, height, frameBorder, data, style} = this.props;
|
||||||
className,
|
|
||||||
src,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
frameBorder,
|
|
||||||
data,
|
|
||||||
style
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
style = {
|
style = {
|
||||||
...style
|
...style,
|
||||||
};
|
};
|
||||||
|
|
||||||
width !== void 0 && (style.width = width);
|
width !== void 0 && (style.width = width);
|
||||||
|
@ -55,6 +39,6 @@ export default class IFrame extends React.Component<IFrameProps, object> {
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)iframe$/,
|
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 * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
|
|
||||||
export interface IconProps extends RendererProps {
|
export interface IconProps extends RendererProps {
|
||||||
icon: string;
|
icon: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Icon extends React.Component<IconProps, object> {
|
export class Icon extends React.Component<IconProps, object> {
|
||||||
static defaultProps:Partial<IconProps> = {
|
static defaultProps: Partial<IconProps> = {
|
||||||
icon: '',
|
icon: '',
|
||||||
vendor: 'fa'
|
vendor: 'fa',
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {icon, vendor, classnames: cx, className} = this.props;
|
||||||
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({
|
@Renderer({
|
||||||
test: /(^|\/)icon$/,
|
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 * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import {ServiceStore, IServiceStore} from '../store/service';
|
import {ServiceStore, IServiceStore} from '../store/service';
|
||||||
import {
|
import {Api, SchemaNode} from '../types';
|
||||||
Api,
|
import {filter} from '../utils/tpl';
|
||||||
SchemaNode
|
|
||||||
} from '../types';
|
|
||||||
import {
|
|
||||||
filter
|
|
||||||
} from '../utils/tpl';
|
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
|
|
||||||
|
@ -22,10 +14,10 @@ export interface ImageProps extends RendererProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ImageField extends React.Component<ImageProps, object> {
|
export class ImageField extends React.Component<ImageProps, object> {
|
||||||
static defaultProps:Partial<ImageProps> = {
|
static defaultProps: Partial<ImageProps> = {
|
||||||
className: 'thumb-lg',
|
className: 'thumb-lg',
|
||||||
imageClassName: 'r',
|
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() {
|
render() {
|
||||||
|
@ -38,7 +30,7 @@ export class ImageField extends React.Component<ImageProps, object> {
|
||||||
data,
|
data,
|
||||||
imageClassName,
|
imageClassName,
|
||||||
classnames: cx,
|
classnames: cx,
|
||||||
src
|
src,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const finnalSrc = src ? filter(src) : '';
|
const finnalSrc = src ? filter(src) : '';
|
||||||
|
@ -48,8 +40,8 @@ export class ImageField extends React.Component<ImageProps, object> {
|
||||||
<div className={cx('ImageField', className)}>
|
<div className={cx('ImageField', className)}>
|
||||||
<img className={imageClassName} src={finnalSrc || value || defaultImage} />
|
<img className={imageClassName} src={finnalSrc || value || defaultImage} />
|
||||||
{title || description ? (
|
{title || description ? (
|
||||||
<div key="caption" className={cx("ImageField-caption")}>
|
<div key="caption" className={cx('ImageField-caption')}>
|
||||||
{title ? (<div className="text-md">{filter(title, data)}</div>) : null}
|
{title ? <div className="text-md">{filter(title, data)}</div> : null}
|
||||||
{render('description', description as string)}
|
{render('description', description as string)}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -60,21 +52,21 @@ export class ImageField extends React.Component<ImageProps, object> {
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)image$/,
|
test: /(^|\/)image$/,
|
||||||
name: 'image'
|
name: 'image',
|
||||||
})
|
})
|
||||||
export class ImageFieldRenderer extends ImageField {};
|
export class ImageFieldRenderer extends ImageField {}
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)images$/
|
test: /(^|\/)images$/,
|
||||||
})
|
})
|
||||||
export class ImagesFieldRenderer extends ImageField {
|
export class ImagesFieldRenderer extends ImageField {
|
||||||
static defaultProps:Partial<ImageProps> = {
|
static defaultProps: Partial<ImageProps> = {
|
||||||
...ImageField.defaultProps,
|
...ImageField.defaultProps,
|
||||||
multiple: true,
|
multiple: true,
|
||||||
delimiter: ','
|
delimiter: ',',
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <p>Todo</p>
|
return <p>Todo</p>;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,16 +1,8 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import {ServiceStore, IServiceStore} from '../store/service';
|
import {ServiceStore, IServiceStore} from '../store/service';
|
||||||
import {
|
import {Api, SchemaNode} from '../types';
|
||||||
Api,
|
import {filter} from '../utils/tpl';
|
||||||
SchemaNode
|
|
||||||
} from '../types';
|
|
||||||
import {
|
|
||||||
filter
|
|
||||||
} from '../utils/tpl';
|
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
|
|
||||||
import JSONTree from 'react-json-tree';
|
import JSONTree from 'react-json-tree';
|
||||||
|
@ -53,36 +45,34 @@ const twilight = {
|
||||||
WebkitUserSelect: 'none',
|
WebkitUserSelect: 'none',
|
||||||
backgroundColor: 'rgba(255, 255, 255, 0.4)',
|
backgroundColor: 'rgba(255, 255, 255, 0.4)',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
display: 'inline-block'
|
display: 'inline-block',
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
export class JSONField extends React.Component<JSONProps, object> {
|
export class JSONField extends React.Component<JSONProps, object> {
|
||||||
static defaultProps:Partial<JSONProps> = {
|
static defaultProps: Partial<JSONProps> = {
|
||||||
placeholder: '-',
|
placeholder: '-',
|
||||||
levelExpand: 1
|
levelExpand: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
valueRenderer(raw:any) {
|
valueRenderer(raw: any) {
|
||||||
if (typeof raw === 'string' && /^\"?https?:\/\//.test(raw)) {
|
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;
|
return raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldExpandNode = (keyName:any, data:any, level:any) => {
|
shouldExpandNode = (keyName: any, data: any, level: any) => {
|
||||||
const {
|
const {levelExpand} = this.props;
|
||||||
levelExpand
|
|
||||||
} = this.props;
|
|
||||||
return level < levelExpand;
|
return level < levelExpand;
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {className, value, classnames: cx} = this.props;
|
||||||
className,
|
|
||||||
value,
|
|
||||||
classnames: cx
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
let data = value;
|
let data = value;
|
||||||
|
|
||||||
|
@ -91,7 +81,7 @@ export class JSONField extends React.Component<JSONProps, object> {
|
||||||
data = JSON.parse(value);
|
data = JSON.parse(value);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
data = {
|
data = {
|
||||||
error: e.message
|
error: e.message,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,6 +101,6 @@ export class JSONField extends React.Component<JSONProps, object> {
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)json$/,
|
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 * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
import {filter} from '../utils/tpl';
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import {
|
|
||||||
filter
|
|
||||||
} from '../utils/tpl';
|
|
||||||
|
|
||||||
export interface LinkProps extends RendererProps {
|
export interface LinkProps extends RendererProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
@ -17,26 +12,18 @@ export interface LinkProps extends RendererProps {
|
||||||
export class LinkField extends React.Component<LinkProps, object> {
|
export class LinkField extends React.Component<LinkProps, object> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
className: '',
|
className: '',
|
||||||
blank: false
|
blank: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {className, body, href, classnames: cx, blank, data, render} = this.props;
|
||||||
className,
|
|
||||||
body,
|
|
||||||
href,
|
|
||||||
classnames: cx,
|
|
||||||
blank,
|
|
||||||
data,
|
|
||||||
render
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
let value = this.props.value;
|
let value = this.props.value;
|
||||||
const finnalHref = href ? filter(href, data) : '';
|
const finnalHref = href ? filter(href, data) : '';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a href={finnalHref || value} target={blank ? '_blank' : '_self'} className={cx('Link', className)}>
|
<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>
|
</a>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -44,6 +31,6 @@ export class LinkField extends React.Component<LinkProps, object> {
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)link$/,
|
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 * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import {ServiceStore, IServiceStore} from '../store/service';
|
import {ServiceStore, IServiceStore} from '../store/service';
|
||||||
import {
|
import {Api, SchemaNode, PlainObject} from '../types';
|
||||||
Api,
|
import {filter} from '../utils/tpl';
|
||||||
SchemaNode,
|
|
||||||
PlainObject
|
|
||||||
} from '../types';
|
|
||||||
import {
|
|
||||||
filter
|
|
||||||
} from '../utils/tpl';
|
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
|
|
||||||
export interface MappingProps extends RendererProps {
|
export interface MappingProps extends RendererProps {
|
||||||
|
@ -21,39 +12,28 @@ export interface MappingProps extends RendererProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MappingField extends React.Component<MappingProps, object> {
|
export class MappingField extends React.Component<MappingProps, object> {
|
||||||
static defaultProps:Partial<MappingProps> = {
|
static defaultProps: Partial<MappingProps> = {
|
||||||
placeholder: '-',
|
placeholder: '-',
|
||||||
map: {
|
map: {
|
||||||
'*': '通配值'
|
'*': '通配值',
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {className, value, placeholder, map, render, classnames: cx} = this.props;
|
||||||
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['*']);
|
viewValue = render('tpl', map[value] || map['*']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <span className={cx('MappingField', className)}>{viewValue}</span>;
|
||||||
<span className={cx('MappingField', className)}>
|
|
||||||
{viewValue}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)(?:map|mapping)$/,
|
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 React from 'react';
|
||||||
import * as PropTypes from 'prop-types';
|
import * as PropTypes from 'prop-types';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
import {ServiceStore, IServiceStore} from '../store/service';
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import { ServiceStore, IServiceStore } from '../store/service';
|
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
import getExprProperties from '../utils/filter-schema';
|
import getExprProperties from '../utils/filter-schema';
|
||||||
import { filter, evalExpression } from '../utils/tpl';
|
import {filter, evalExpression} from '../utils/tpl';
|
||||||
import { createObject, mapTree, someTree } from '../utils/helper';
|
import {createObject, mapTree, someTree} from '../utils/helper';
|
||||||
import { resolveVariable } from '../utils/tpl-builtin';
|
import {resolveVariable} from '../utils/tpl-builtin';
|
||||||
import { isApiOutdated } from '../utils/api';
|
import {isApiOutdated} from '../utils/api';
|
||||||
import { ScopedContext, IScopedContext } from '../Scoped';
|
import {ScopedContext, IScopedContext} from '../Scoped';
|
||||||
|
|
||||||
export interface Link {
|
export interface Link {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
@ -21,14 +18,14 @@ export interface Link {
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
unfolded?: boolean;
|
unfolded?: boolean;
|
||||||
children?: Links;
|
children?: Links;
|
||||||
[propName:string]: any;
|
[propName: string]: any;
|
||||||
};
|
}
|
||||||
export interface Links extends Array<Link> {};
|
export interface Links extends Array<Link> {}
|
||||||
|
|
||||||
export interface NavigationState {
|
export interface NavigationState {
|
||||||
links: Links;
|
links: Links;
|
||||||
error?: string;
|
error?: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface NavigationProps extends RendererProps {
|
export interface NavigationProps extends RendererProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
@ -39,37 +36,41 @@ export interface NavigationProps extends RendererProps {
|
||||||
|
|
||||||
export default class Navigation extends React.Component<NavigationProps, NavigationState> {
|
export default class Navigation extends React.Component<NavigationProps, NavigationState> {
|
||||||
static defaultProps: Partial<NavigationProps> = {
|
static defaultProps: Partial<NavigationProps> = {
|
||||||
togglerClassName: 'fa fa-angle-down'
|
togglerClassName: 'fa fa-angle-down',
|
||||||
};
|
};
|
||||||
|
|
||||||
mounted:boolean = true;
|
mounted: boolean = true;
|
||||||
constructor(props:NavigationProps) {
|
constructor(props: NavigationProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.renderItem = this.renderItem.bind(this);
|
this.renderItem = this.renderItem.bind(this);
|
||||||
|
|
||||||
this.state = {
|
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() {
|
componentDidMount() {
|
||||||
const {
|
const {source} = this.props;
|
||||||
source
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (source && !/^\$(?:([a-z0-9_.]+)|{.+})$/.test(source)) {
|
if (source && !/^\$(?:([a-z0-9_.]+)|{.+})$/.test(source)) {
|
||||||
this.reload();
|
this.reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps:NavigationProps) {
|
componentWillReceiveProps(nextProps: NavigationProps) {
|
||||||
const props = this.props;
|
const props = this.props;
|
||||||
|
|
||||||
if (nextProps.source && /^\$(?:([a-z0-9_.]+)|{.+})$/.test(nextProps.source)) {
|
if (nextProps.source && /^\$(?:([a-z0-9_.]+)|{.+})$/.test(nextProps.source)) {
|
||||||
if (nextProps.source !== props.source) {
|
if (nextProps.source !== props.source) {
|
||||||
this.setState({
|
this.setState({
|
||||||
links: this.syncLinks(nextProps)
|
links: this.syncLinks(nextProps),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const links = resolveVariable(nextProps.source, nextProps.data);
|
const links = resolveVariable(nextProps.source, nextProps.data);
|
||||||
|
@ -77,22 +78,22 @@ export default class Navigation extends React.Component<NavigationProps, Navigat
|
||||||
|
|
||||||
if (links !== prevLinks) {
|
if (links !== prevLinks) {
|
||||||
this.setState({
|
this.setState({
|
||||||
links: this.syncLinks(nextProps, links)
|
links: this.syncLinks(nextProps, links),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (props.links !== nextProps.links) {
|
} else if (props.links !== nextProps.links) {
|
||||||
this.setState({
|
this.setState({
|
||||||
links: this.syncLinks(nextProps)
|
links: this.syncLinks(nextProps),
|
||||||
});
|
});
|
||||||
} else if (nextProps.location && props.location !== nextProps.location) {
|
} else if (nextProps.location && props.location !== nextProps.location) {
|
||||||
this.setState({
|
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;
|
const props = this.props;
|
||||||
|
|
||||||
if (props.source && !/^\$(?:([a-z0-9_.]+)|{.+})$/.test(props.source)) {
|
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;
|
this.mounted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
reload(target?:string, query?:any, values?:object) {
|
reload(target?: string, query?: any, values?: object) {
|
||||||
|
|
||||||
if (query) {
|
if (query) {
|
||||||
return this.receive(query);
|
return this.receive(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {data, env, source} = this.props;
|
||||||
data,
|
|
||||||
env,
|
|
||||||
source
|
|
||||||
} = this.props;
|
|
||||||
const finalData = values ? createObject(data, values) : data;
|
const finalData = values ? createObject(data, values) : data;
|
||||||
|
|
||||||
if (!source || source.sendOn && !evalExpression(source.sendOn, data)) {
|
if (!source || (source.sendOn && !evalExpression(source.sendOn, data))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,118 +122,126 @@ export default class Navigation extends React.Component<NavigationProps, Navigat
|
||||||
if (!this.mounted) {
|
if (!this.mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!payload.ok) {
|
if (!payload.ok) {
|
||||||
this.setState({
|
this.setState({
|
||||||
error: payload.msg || '获取链接错误'
|
error: payload.msg || '获取链接错误',
|
||||||
});
|
});
|
||||||
} else {
|
} 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)) {
|
if (!Array.isArray(links)) {
|
||||||
throw new Error('payload.data.options is not array.')
|
throw new Error('payload.data.options is not array.');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState(
|
||||||
links: this.syncLinks(this.props, links, true)
|
{
|
||||||
}, () => {
|
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))
|
() => {
|
||||||
|
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({
|
.catch(
|
||||||
error: e.message
|
e =>
|
||||||
}));
|
this.mounted &&
|
||||||
|
this.setState({
|
||||||
|
error: e.message,
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
receive(values: object) {
|
receive(values: object) {
|
||||||
const {
|
const {store, initApi} = this.props;
|
||||||
store,
|
|
||||||
initApi
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
this.reload(undefined, undefined, values);
|
this.reload(undefined, undefined, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
syncLinks(props:NavigationProps, links = props.links, clearActive?: boolean):Links {
|
syncLinks(props: NavigationProps, links = props.links, clearActive?: boolean): Links {
|
||||||
const {
|
const {data, env} = props;
|
||||||
data,
|
|
||||||
env
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
if (!Array.isArray(links) || !links.length) {
|
if (!Array.isArray(links) || !links.length) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return mapTree(links, (link:Link) => {
|
return mapTree(
|
||||||
return {
|
links,
|
||||||
...link,
|
(link: Link) => {
|
||||||
...getExprProperties(link, data as object),
|
return {
|
||||||
active: !clearActive && link.active || !!(link.hasOwnProperty('to') && env && env.isCurrentUrl(filter(link.to as string, data))),
|
...link,
|
||||||
unfolded: link.unfolded || link.children && link.children.some(link => !!link.active)
|
...getExprProperties(link, data as object),
|
||||||
}
|
active:
|
||||||
}, 1, true);
|
(!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:{
|
handleClick(link: {label?: string; to?: string; icon?: string; children?: Links}) {
|
||||||
label?: string;
|
|
||||||
to?: string;
|
|
||||||
icon?: string;
|
|
||||||
children?: Links;
|
|
||||||
}) {
|
|
||||||
if (!link.to) {
|
if (!link.to) {
|
||||||
link.children && link.children.length && this.toggleLink(link);
|
link.children && link.children.length && this.toggleLink(link);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {env, data} = this.props;
|
||||||
env,
|
|
||||||
data
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
env && env.jumpTo(filter(link.to as string, data), link as any);
|
env && env.jumpTo(filter(link.to as string, data), link as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleLink(target:Link) {
|
toggleLink(target: Link) {
|
||||||
this.setState({
|
this.setState({
|
||||||
links: mapTree(this.state.links, (link:Link) => target === link ? {
|
links: mapTree(this.state.links, (link: Link) =>
|
||||||
...link,
|
target === link
|
||||||
unfolded: !link.unfolded
|
? {
|
||||||
} : link)
|
...link,
|
||||||
|
unfolded: !link.unfolded,
|
||||||
|
}
|
||||||
|
: link
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderItem(link:Link, index:number) {
|
renderItem(link: Link, index: number) {
|
||||||
if (link.hidden === true || link.visible === false) {
|
if (link.hidden === true || link.visible === false) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const isActive:boolean = !!link.active;
|
const isActive: boolean = !!link.active;
|
||||||
const {
|
const {disabled, togglerClassName, classnames: cx} = this.props;
|
||||||
disabled,
|
|
||||||
togglerClassName,
|
|
||||||
classnames: cx
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
key={index}
|
key={index}
|
||||||
className={cx('Nav-item', link.className, {
|
className={cx('Nav-item', link.className, {
|
||||||
'is-disabled': disabled || link.disabled,
|
'is-disabled': disabled || link.disabled,
|
||||||
'is-active': isActive,
|
'is-active': isActive,
|
||||||
'is-unfolded': link.unfolded
|
'is-unfolded': link.unfolded,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<a onClick={this.handleClick.bind(this, link)}>
|
<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}
|
{link.label}
|
||||||
</a>
|
</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 ? (
|
{link.children && link.children.length ? (
|
||||||
<ul className={cx("Nav-subItems")}>
|
<ul className={cx('Nav-subItems')}>
|
||||||
{link.children.map((link, index) => this.renderItem(link, index))}
|
{link.children.map((link, index) => this.renderItem(link, index))}
|
||||||
</ul>
|
</ul>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -245,19 +249,13 @@ export default class Navigation extends React.Component<NavigationProps, Navigat
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render():JSX.Element {
|
render(): JSX.Element {
|
||||||
const {
|
const {className, stacked, classnames: cx} = this.props;
|
||||||
className,
|
|
||||||
stacked,
|
|
||||||
classnames: cx
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const links = this.state.links;
|
const links = this.state.links;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ul
|
<ul className={cx('Nav', className, stacked ? 'Nav--stacked' : 'Nav--tabs')}>
|
||||||
className={cx('Nav', className, stacked ? 'Nav--stacked' : 'Nav--tabs')}
|
|
||||||
>
|
|
||||||
{links.map(this.renderItem)}
|
{links.map(this.renderItem)}
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
|
@ -266,7 +264,7 @@ export default class Navigation extends React.Component<NavigationProps, Navigat
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)(?:nav|navigation)$/,
|
test: /(^|\/)(?:nav|navigation)$/,
|
||||||
name: 'nav'
|
name: 'nav',
|
||||||
})
|
})
|
||||||
export class NavigationRenderer extends Navigation {
|
export class NavigationRenderer extends Navigation {
|
||||||
static contextType = ScopedContext;
|
static contextType = ScopedContext;
|
||||||
|
@ -281,4 +279,4 @@ export class NavigationRenderer extends Navigation {
|
||||||
scoped.unRegisterComponent(this);
|
scoped.unRegisterComponent(this);
|
||||||
super.componentWillUnmount();
|
super.componentWillUnmount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,8 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import {ServiceStore, IServiceStore} from '../store/service';
|
import {ServiceStore, IServiceStore} from '../store/service';
|
||||||
import {
|
import {Api, SchemaNode, Action} from '../types';
|
||||||
Api,
|
import {filter} from '../utils/tpl';
|
||||||
SchemaNode,
|
|
||||||
Action
|
|
||||||
} from '../types';
|
|
||||||
import {
|
|
||||||
filter
|
|
||||||
} from '../utils/tpl';
|
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
|
|
||||||
|
@ -21,43 +12,39 @@ export interface OperationProps extends RendererProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class OperationField extends React.Component<OperationProps, object> {
|
export class OperationField extends React.Component<OperationProps, object> {
|
||||||
static propsList: Array<string> = [
|
static propsList: Array<string> = ['buttons', 'label'];
|
||||||
"buttons",
|
|
||||||
"label",
|
|
||||||
];
|
|
||||||
|
|
||||||
static defaultProps:Partial<OperationProps> = {
|
static defaultProps: Partial<OperationProps> = {};
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {className, buttons, render, classnames: cx} = this.props;
|
||||||
className,
|
|
||||||
buttons,
|
|
||||||
render,
|
|
||||||
classnames: cx
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cx('OperationField', className)}>
|
<div className={cx('OperationField', className)}>
|
||||||
{Array.isArray(buttons) ? buttons.map((button, index) => render(`${index}`, {
|
{Array.isArray(buttons)
|
||||||
type: 'button',
|
? buttons.map((button, index) =>
|
||||||
size: button.size || 'sm',
|
render(
|
||||||
level: button.level || (button.icon && !button.label ? 'link' : ''),
|
`${index}`,
|
||||||
...button
|
{
|
||||||
}, {
|
type: 'button',
|
||||||
key: index
|
size: button.size || 'sm',
|
||||||
})) : null}
|
level: button.level || (button.icon && !button.label ? 'link' : ''),
|
||||||
|
...button,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: index,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
: null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
// test: /(^|\/)table\/(.*\/)operation$/,
|
// test: /(^|\/)table\/(.*\/)operation$/,
|
||||||
test: (path: string) => /(^|\/)table\/(.*\/)operation$/.test(path),
|
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 React from 'react';
|
||||||
import * as PropTypes from 'prop-types';
|
import * as PropTypes from 'prop-types';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
import {observer} from 'mobx-react';
|
||||||
RendererProps
|
import {ServiceStore, IServiceStore} from '../store/service';
|
||||||
} from '../factory';
|
import {Api, SchemaNode, Action, Location, ApiObject, FunctionPropertyNames} from '../types';
|
||||||
import { observer } from "mobx-react";
|
import {filter, evalExpression} from '../utils/tpl';
|
||||||
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 cx from 'classnames';
|
||||||
import * as qs from 'qs';
|
import * as qs from 'qs';
|
||||||
import { isVisible, autobind, bulkBindFunctions } from '../utils/helper';
|
import {isVisible, autobind, bulkBindFunctions} from '../utils/helper';
|
||||||
import { ScopedContext, IScopedContext } from '../Scoped';
|
import {ScopedContext, IScopedContext} from '../Scoped';
|
||||||
import Alert from '../components/Alert2';
|
import Alert from '../components/Alert2';
|
||||||
import { isApiOutdated } from '../utils/api';
|
import {isApiOutdated} from '../utils/api';
|
||||||
|
|
||||||
export interface PageProps extends RendererProps {
|
export interface PageProps extends RendererProps {
|
||||||
title?: string; // 标题
|
title?: string; // 标题
|
||||||
|
@ -57,7 +45,7 @@ export interface PageProps extends RendererProps {
|
||||||
export default class Page extends React.Component<PageProps> {
|
export default class Page extends React.Component<PageProps> {
|
||||||
timer: NodeJS.Timer;
|
timer: NodeJS.Timer;
|
||||||
mounted: boolean;
|
mounted: boolean;
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
asideClassName: '',
|
asideClassName: '',
|
||||||
bodyClassName: '',
|
bodyClassName: '',
|
||||||
|
@ -65,8 +53,7 @@ export default class Page extends React.Component<PageProps> {
|
||||||
initFetch: true,
|
initFetch: true,
|
||||||
// primaryField: 'id',
|
// primaryField: 'id',
|
||||||
toolbarClassName: '',
|
toolbarClassName: '',
|
||||||
messages: {
|
messages: {},
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static propsList: Array<string> = [
|
static propsList: Array<string> = [
|
||||||
|
@ -83,27 +70,23 @@ export default class Page extends React.Component<PageProps> {
|
||||||
'body',
|
'body',
|
||||||
'aside',
|
'aside',
|
||||||
'messages',
|
'messages',
|
||||||
'style'
|
'style',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
const {
|
const {store, location} = this.props;
|
||||||
store,
|
|
||||||
location
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
// autobind 会让继承里面的 super 指向有问题,所以先这样!
|
// autobind 会让继承里面的 super 指向有问题,所以先这样!
|
||||||
bulkBindFunctions<Page/*为毛 this 的类型自动识别不出来?*/>(this, [
|
bulkBindFunctions<Page /*为毛 this 的类型自动识别不出来?*/>(this, [
|
||||||
"handleAction",
|
'handleAction',
|
||||||
"handleDialogConfirm",
|
'handleDialogConfirm',
|
||||||
"handleDialogClose",
|
'handleDialogClose',
|
||||||
"handleDrawerConfirm",
|
'handleDrawerConfirm',
|
||||||
"handleDrawerClose",
|
'handleDrawerClose',
|
||||||
"handleClick",
|
'handleClick',
|
||||||
"reload",
|
'reload',
|
||||||
"silentReload",
|
'silentReload',
|
||||||
"initInterval"
|
'initInterval',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (location && location.search) {
|
if (location && location.search) {
|
||||||
|
@ -124,20 +107,21 @@ export default class Page extends React.Component<PageProps> {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const {
|
const {initApi, initFetch, store, messages} = this.props;
|
||||||
initApi,
|
|
||||||
initFetch,
|
|
||||||
store,
|
|
||||||
messages
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
this.mounted = true;
|
this.mounted = true;
|
||||||
|
|
||||||
if (initApi && initFetch && (!(initApi as ApiObject).sendOn || evalExpression((initApi as ApiObject).sendOn as string, store.data))) {
|
if (
|
||||||
store.fetchInitData(initApi, store.data, {
|
initApi &&
|
||||||
successMessage: messages && messages.fetchSuccess,
|
initFetch &&
|
||||||
errorMessage: messages && messages.fetchFailed
|
(!(initApi as ApiObject).sendOn || evalExpression((initApi as ApiObject).sendOn as string, store.data))
|
||||||
}).then(this.initInterval);
|
) {
|
||||||
|
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 props = this.props;
|
||||||
const store = props.store;
|
const store = props.store;
|
||||||
|
|
||||||
if (
|
if (nextProps.location && (!props.location || props.location.search !== nextProps.location.search)) {
|
||||||
nextProps.location
|
const query =
|
||||||
&& (!props.location || props.location.search !== nextProps.location.search)
|
nextProps.location.query ||
|
||||||
) {
|
(nextProps.location.search && qs.parse(nextProps.location.search.substring(1))) ||
|
||||||
const query = nextProps.location.query || nextProps.location.search && qs.parse(nextProps.location.search.substring(1)) || {};
|
{};
|
||||||
store.updateData({
|
store.updateData({
|
||||||
...query,
|
...query,
|
||||||
query: query,
|
query: query,
|
||||||
|
@ -164,17 +148,18 @@ export default class Page extends React.Component<PageProps> {
|
||||||
|
|
||||||
if (
|
if (
|
||||||
// 前一次不构成条件,这次更新构成了条件,则需要重新拉取
|
// 前一次不构成条件,这次更新构成了条件,则需要重新拉取
|
||||||
props.initFetchOn && props.initFetch && !prevProps.initFetch
|
(props.initFetchOn && props.initFetch && !prevProps.initFetch) ||
|
||||||
|
|
||||||
// 构成了条件,同时 url 里面有变量,且上次和这次还不一样,则需要重新拉取。
|
// 构成了条件,同时 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;
|
const messages = props.messages;
|
||||||
(!(initApi as ApiObject).sendOn || evalExpression((initApi as ApiObject).sendOn as string, store.data))
|
(!(initApi as ApiObject).sendOn || evalExpression((initApi as ApiObject).sendOn as string, store.data)) &&
|
||||||
&& store.fetchData(initApi as Api, store.data, {
|
store
|
||||||
successMessage: messages && messages.fetchSuccess,
|
.fetchData(initApi as Api, store.data, {
|
||||||
errorMessage: messages && messages.fetchFailed
|
successMessage: messages && messages.fetchSuccess,
|
||||||
}).then(this.initInterval);
|
errorMessage: messages && messages.fetchFailed,
|
||||||
|
})
|
||||||
|
.then(this.initInterval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,16 +168,12 @@ export default class Page extends React.Component<PageProps> {
|
||||||
clearTimeout(this.timer);
|
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) {
|
handleAction(e: React.UIEvent<any> | void, action: Action, ctx: object, delegate?: boolean) {
|
||||||
const {
|
const {env, store, messages} = this.props;
|
||||||
env,
|
|
||||||
store,
|
|
||||||
messages
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
// delegate 表示不是当前层的事件,而是孩子节点的。
|
// delegate 表示不是当前层的事件,而是孩子节点的。
|
||||||
delegate || store.setCurrentAction(action);
|
delegate || store.setCurrentAction(action);
|
||||||
|
@ -208,28 +189,27 @@ export default class Page extends React.Component<PageProps> {
|
||||||
} else if (action.actionType === 'drawer') {
|
} else if (action.actionType === 'drawer') {
|
||||||
store.openDrawer(ctx);
|
store.openDrawer(ctx);
|
||||||
} else if (action.actionType === 'ajax') {
|
} else if (action.actionType === 'ajax') {
|
||||||
store.saveRemote(action.api as string, ctx, {
|
store
|
||||||
successMessage: action.messages && action.messages.success || messages && messages.saveSuccess,
|
.saveRemote(action.api as string, ctx, {
|
||||||
errorMessage: action.messages && action.messages.failed || messages && messages.saveSuccess
|
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)) {
|
.then(async () => {
|
||||||
await this.openFeedback(action.feedback, store.data);
|
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.redirect && env.jumpTo(filter(action.redirect, store.data), action);
|
||||||
action.reload && this.reloadTarget(action.reload, store.data);
|
action.reload && this.reloadTarget(action.reload, store.data);
|
||||||
})
|
})
|
||||||
.catch(() => { });;
|
.catch(() => {});
|
||||||
} else if (action.actionType === 'copy' && (action.content || action.copy)) {
|
} else if (action.actionType === 'copy' && (action.content || action.copy)) {
|
||||||
env.copy && env.copy(filter(action.content || action.copy, ctx));
|
env.copy && env.copy(filter(action.content || action.copy, ctx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDialogConfirm(values: object[], action: Action, ...args: Array<any>) {
|
handleDialogConfirm(values: object[], action: Action, ...args: Array<any>) {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (action.mergeData && values.length === 1 && values[0]) {
|
if (action.mergeData && values.length === 1 && values[0]) {
|
||||||
store.updateData(values[0]);
|
store.updateData(values[0]);
|
||||||
|
@ -243,18 +223,13 @@ export default class Page extends React.Component<PageProps> {
|
||||||
store.closeDialog();
|
store.closeDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
handleDialogClose() {
|
handleDialogClose() {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
store.closeDialog();
|
store.closeDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDrawerConfirm(values: object[], action: Action, ...args:Array<any>) {
|
handleDrawerConfirm(values: object[], action: Action, ...args: Array<any>) {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (action.mergeData && values.length === 1 && values[0]) {
|
if (action.mergeData && values.length === 1 && values[0]) {
|
||||||
store.updateData(values[0]);
|
store.updateData(values[0]);
|
||||||
|
@ -269,80 +244,68 @@ export default class Page extends React.Component<PageProps> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDrawerClose() {
|
handleDrawerClose() {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
store.closeDrawer();
|
store.closeDrawer();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClick(e: any) {
|
handleClick(e: any) {
|
||||||
const target: HTMLElement = e.target as HTMLElement;
|
const target: HTMLElement = e.target as HTMLElement;
|
||||||
const { env } = this.props;
|
const {env} = this.props;
|
||||||
|
|
||||||
if (env && target.tagName === 'A' && target.hasAttribute('data-link')) {
|
if (env && target.tagName === 'A' && target.hasAttribute('data-link')) {
|
||||||
env.jumpTo(target.getAttribute('data-link') as string);
|
env.jumpTo(target.getAttribute('data-link') as string);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
openFeedback(dialog:any, ctx:any) {
|
openFeedback(dialog: any, ctx: any) {
|
||||||
return new Promise((resolve) => {
|
return new Promise(resolve => {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
store.setCurrentAction({
|
store.setCurrentAction({
|
||||||
type: 'button',
|
type: 'button',
|
||||||
actionType: 'dialog',
|
actionType: 'dialog',
|
||||||
dialog: dialog
|
dialog: dialog,
|
||||||
});
|
});
|
||||||
store.openDialog(ctx, undefined, (confirmed) => {
|
store.openDialog(ctx, undefined, confirmed => {
|
||||||
resolve(confirmed)
|
resolve(confirmed);
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
reload(subpath?: any, query?:any, ctx?: any, silent?: boolean) {
|
reload(subpath?: any, query?: any, ctx?: any, silent?: boolean) {
|
||||||
|
|
||||||
if (query) {
|
if (query) {
|
||||||
return this.receive(query);
|
return this.receive(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {store, initApi} = this.props;
|
||||||
store,
|
|
||||||
initApi
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
clearTimeout(this.timer);
|
clearTimeout(this.timer);
|
||||||
initApi && store.fetchData(initApi, store.data, {
|
initApi &&
|
||||||
silent
|
store
|
||||||
}).then(this.initInterval);
|
.fetchData(initApi, store.data, {
|
||||||
|
silent,
|
||||||
|
})
|
||||||
|
.then(this.initInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
receive(values: object) {
|
receive(values: object) {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
store.updateData(values);
|
store.updateData(values);
|
||||||
this.reload();
|
this.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
silentReload(target?:string, query?:any) {
|
silentReload(target?: string, query?: any) {
|
||||||
this.reload(query, undefined, undefined, true);
|
this.reload(query, undefined, undefined, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
initInterval(value: any) {
|
initInterval(value: any) {
|
||||||
const {
|
const {interval, silentPolling, stopAutoRefreshWhen, data} = this.props;
|
||||||
interval,
|
|
||||||
silentPolling,
|
|
||||||
stopAutoRefreshWhen,
|
|
||||||
data
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
interval
|
interval &&
|
||||||
&& this.mounted
|
this.mounted &&
|
||||||
&& (!stopAutoRefreshWhen || !evalExpression(stopAutoRefreshWhen, data))
|
(!stopAutoRefreshWhen || !evalExpression(stopAutoRefreshWhen, data)) &&
|
||||||
&& (this.timer = setTimeout(silentPolling ? this.silentReload : this.reload, Math.max(interval, 3000)));
|
(this.timer = setTimeout(silentPolling ? this.silentReload : this.reload, Math.max(interval, 3000)));
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,11 +320,11 @@ export default class Page extends React.Component<PageProps> {
|
||||||
render,
|
render,
|
||||||
store,
|
store,
|
||||||
env,
|
env,
|
||||||
classnames: cx
|
classnames: cx,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const subProps = {
|
const subProps = {
|
||||||
onAction: this.handleAction
|
onAction: this.handleAction,
|
||||||
};
|
};
|
||||||
let header, right;
|
let header, right;
|
||||||
|
|
||||||
|
@ -371,24 +334,24 @@ export default class Page extends React.Component<PageProps> {
|
||||||
{title ? (
|
{title ? (
|
||||||
<h2 className={cx('Page-title')}>
|
<h2 className={cx('Page-title')}>
|
||||||
{render('title', title, subProps)}
|
{render('title', title, subProps)}
|
||||||
{remark ? render('remark', {
|
{remark
|
||||||
type: 'remark',
|
? render('remark', {
|
||||||
tooltip: remark,
|
type: 'remark',
|
||||||
container: env && env.getModalContainer ? env.getModalContainer() : undefined
|
tooltip: remark,
|
||||||
}) : null}
|
container: env && env.getModalContainer ? env.getModalContainer() : undefined,
|
||||||
|
})
|
||||||
|
: null}
|
||||||
</h2>
|
</h2>
|
||||||
) : null}
|
) : null}
|
||||||
{subTitle && (<small className={cx('Page-subTitle')}>{render('subTitle', subTitle, subProps)}</small>)}
|
{subTitle && (
|
||||||
|
<small className={cx('Page-subTitle')}>{render('subTitle', subTitle, subProps)}</small>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolbar) {
|
if (toolbar) {
|
||||||
right = (
|
right = <div className={cx(`Page-toolbar`, toolbarClassName)}>{render('toolbar', toolbar, subProps)}</div>;
|
||||||
<div className={cx(`Page-toolbar`, toolbarClassName)}>
|
|
||||||
{render('toolbar', toolbar, subProps)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header && right) {
|
if (header && right) {
|
||||||
|
@ -417,42 +380,42 @@ export default class Page extends React.Component<PageProps> {
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const subProps = {
|
const subProps = {
|
||||||
onAction: this.handleAction
|
onAction: this.handleAction,
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasAside = aside && (!Array.isArray(aside) || aside.length);
|
const hasAside = aside && (!Array.isArray(aside) || aside.length);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={cx(`Page`, hasAside ? `Page--withSidebar` : '', className)} onClick={this.handleClick}>
|
||||||
className={cx(`Page`, hasAside ? `Page--withSidebar` : '', className)}
|
{hasAside ? (
|
||||||
onClick={this.handleClick}
|
<div className={cx(`Page-aside`, asideClassName)}>
|
||||||
>
|
{render('aside', aside as any, {
|
||||||
{hasAside ? <div className={cx(`Page-aside`, asideClassName)}>
|
...subProps,
|
||||||
{render('aside', aside as any, {
|
...(typeof aside === 'string'
|
||||||
...subProps,
|
? {
|
||||||
...(typeof aside === 'string' ? {
|
inline: false,
|
||||||
inline: false,
|
className: `Page-asideTplWrapper`,
|
||||||
className: `Page-asideTplWrapper`
|
}
|
||||||
} : null)
|
: null),
|
||||||
})}
|
})}
|
||||||
</div> : null}
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
<div className={cx('Page-content')}>
|
<div className={cx('Page-content')}>
|
||||||
{header ? render('header', header, subProps) : null}
|
{header ? render('header', header, subProps) : null}
|
||||||
<div className={cx('Page-main')}>
|
<div className={cx('Page-main')}>
|
||||||
{this.renderHeader()}
|
{this.renderHeader()}
|
||||||
<div className={cx(`Page-body`, bodyClassName)}>
|
<div className={cx(`Page-body`, bodyClassName)}>
|
||||||
{store.loading ? render('spinner', {
|
{store.loading
|
||||||
type: 'spinner',
|
? render('spinner', {
|
||||||
overlay: true,
|
type: 'spinner',
|
||||||
size: 'lg'
|
overlay: true,
|
||||||
}) : null}
|
size: 'lg',
|
||||||
|
})
|
||||||
|
: null}
|
||||||
|
|
||||||
{store.error ? (
|
{store.error ? (
|
||||||
<Alert
|
<Alert level="danger" showCloseButton onClose={store.clearMessage}>
|
||||||
level="danger"
|
|
||||||
showCloseButton
|
|
||||||
onClose={store.clearMessage}>
|
|
||||||
{store.msg}
|
{store.msg}
|
||||||
</Alert>
|
</Alert>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -462,39 +425,47 @@ export default class Page extends React.Component<PageProps> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{render('dialog', {
|
{render(
|
||||||
...(store.action as Action) && (store.action as Action).dialog as object,
|
'dialog',
|
||||||
type: 'dialog'
|
{
|
||||||
}, {
|
...((store.action as Action) && ((store.action as Action).dialog as object)),
|
||||||
key: 'dialog',
|
type: 'dialog',
|
||||||
data: store.dialogData,
|
},
|
||||||
onConfirm: this.handleDialogConfirm,
|
{
|
||||||
onClose: this.handleDialogClose,
|
key: 'dialog',
|
||||||
show: store.dialogOpen,
|
data: store.dialogData,
|
||||||
onAction: this.handleAction
|
onConfirm: this.handleDialogConfirm,
|
||||||
})}
|
onClose: this.handleDialogClose,
|
||||||
|
show: store.dialogOpen,
|
||||||
|
onAction: this.handleAction,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
|
||||||
{render('drawer', {
|
{render(
|
||||||
...(store.action as Action) && (store.action as Action).drawer as object,
|
'drawer',
|
||||||
type: 'drawer'
|
{
|
||||||
}, {
|
...((store.action as Action) && ((store.action as Action).drawer as object)),
|
||||||
key: 'drawer',
|
type: 'drawer',
|
||||||
data: store.drawerData,
|
},
|
||||||
onConfirm: this.handleDrawerConfirm,
|
{
|
||||||
onClose: this.handleDrawerClose,
|
key: 'drawer',
|
||||||
show: store.drawerOpen,
|
data: store.drawerData,
|
||||||
onAction: this.handleAction
|
onConfirm: this.handleDrawerConfirm,
|
||||||
})}
|
onClose: this.handleDrawerClose,
|
||||||
|
show: store.drawerOpen,
|
||||||
|
onAction: this.handleAction,
|
||||||
|
}
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(?:^|\/)page$/,
|
test: /(?:^|\/)page$/,
|
||||||
name: 'page',
|
name: 'page',
|
||||||
storeType: ServiceStore.name,
|
storeType: ServiceStore.name,
|
||||||
isolateScope: true
|
isolateScope: true,
|
||||||
})
|
})
|
||||||
export class PageRenderer extends Page {
|
export class PageRenderer extends Page {
|
||||||
static contextType = ScopedContext;
|
static contextType = ScopedContext;
|
||||||
|
@ -512,12 +483,12 @@ export class PageRenderer extends Page {
|
||||||
super.componentWillUnmount();
|
super.componentWillUnmount();
|
||||||
}
|
}
|
||||||
|
|
||||||
reloadTarget(target:string, data?:any) {
|
reloadTarget(target: string, data?: any) {
|
||||||
const scoped = this.context as IScopedContext;
|
const scoped = this.context as IScopedContext;
|
||||||
scoped.reload(target, data);
|
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;
|
const scoped = this.context as IScopedContext;
|
||||||
|
|
||||||
if (action.actionType === 'reload') {
|
if (action.actionType === 'reload') {
|
||||||
|
@ -525,10 +496,15 @@ export class PageRenderer extends Page {
|
||||||
} else if (action.target) {
|
} else if (action.target) {
|
||||||
action.target.split(',').forEach(name => {
|
action.target.split(',').forEach(name => {
|
||||||
let target = scoped.getComponentByName(name);
|
let target = scoped.getComponentByName(name);
|
||||||
target && target.doAction && target.doAction({
|
target &&
|
||||||
...action,
|
target.doAction &&
|
||||||
target: undefined
|
target.doAction(
|
||||||
}, ctx);
|
{
|
||||||
|
...action,
|
||||||
|
target: undefined,
|
||||||
|
},
|
||||||
|
ctx
|
||||||
|
);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
super.handleAction(e, action, ctx, delegate);
|
super.handleAction(e, action, ctx, delegate);
|
||||||
|
@ -552,7 +528,6 @@ export class PageRenderer extends Page {
|
||||||
.filter((item: any) => item.props.type === 'crud')
|
.filter((item: any) => item.props.type === 'crud')
|
||||||
.forEach((item: any) => item.reload && item.reload());
|
.forEach((item: any) => item.reload && item.reload());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDrawerConfirm(values: object[], action: Action, ...rest: Array<any>) {
|
handleDrawerConfirm(values: object[], action: Action, ...rest: Array<any>) {
|
||||||
|
@ -576,4 +551,4 @@ export class PageRenderer extends Page {
|
||||||
}
|
}
|
||||||
}, 300);
|
}, 300);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
|
|
||||||
export interface PaginationProps extends RendererProps {
|
export interface PaginationProps extends RendererProps {
|
||||||
activePage?: number;
|
activePage?: number;
|
||||||
|
@ -14,7 +11,7 @@ export interface PaginationProps extends RendererProps {
|
||||||
pageNum?: number;
|
pageNum?: number;
|
||||||
changePageNum: (value: number) => void;
|
changePageNum: (value: number) => void;
|
||||||
showPageInput: boolean;
|
showPageInput: boolean;
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface DefaultProps {
|
export interface DefaultProps {
|
||||||
activePage: number;
|
activePage: number;
|
||||||
|
@ -34,8 +31,8 @@ export default class Pagination extends React.PureComponent<PaginationProps, any
|
||||||
maxButtons: 5,
|
maxButtons: 5,
|
||||||
mode: 'normal',
|
mode: 'normal',
|
||||||
hasNext: false,
|
hasNext: false,
|
||||||
showPageInput: true
|
showPageInput: true,
|
||||||
}
|
};
|
||||||
|
|
||||||
constructor(props: PaginationProps) {
|
constructor(props: PaginationProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -43,43 +40,39 @@ export default class Pagination extends React.PureComponent<PaginationProps, any
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSimple() {
|
renderSimple() {
|
||||||
const {
|
const {activePage, hasNext, onPageChange, classnames: cx} = this.props as PropsWithDefault;
|
||||||
activePage,
|
|
||||||
hasNext,
|
|
||||||
onPageChange,
|
|
||||||
classnames: cx
|
|
||||||
} = this.props as PropsWithDefault;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ul className={cx("Pagination", "Pagination--sm")}>
|
<ul className={cx('Pagination', 'Pagination--sm')}>
|
||||||
<li
|
<li
|
||||||
className={cx({
|
className={cx({
|
||||||
disabled: activePage < 2
|
disabled: activePage < 2,
|
||||||
})}
|
})}
|
||||||
onClick={activePage < 2 ? e => e.preventDefault() : () => onPageChange(activePage - 1)}
|
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>
|
||||||
<li
|
<li
|
||||||
className={cx({
|
className={cx({
|
||||||
disabled: !hasNext
|
disabled: !hasNext,
|
||||||
})}
|
})}
|
||||||
onClick={!hasNext ? e => e.preventDefault() : () => onPageChange(activePage + 1)}
|
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>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePageChange(e: React.ChangeEvent<any>) {
|
handlePageChange(e: React.ChangeEvent<any>) {
|
||||||
const {
|
const {changePageNum, items} = this.props;
|
||||||
changePageNum,
|
|
||||||
items
|
|
||||||
} = this.props;
|
|
||||||
let value = e.currentTarget.value;
|
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 !== '') {
|
if (value !== '') {
|
||||||
value = parseInt(value, 10);
|
value = parseInt(value, 10);
|
||||||
value = (value > (items as number) ? items : value) as number;
|
value = (value > (items as number) ? items : value) as number;
|
||||||
|
@ -89,15 +82,8 @@ export default class Pagination extends React.PureComponent<PaginationProps, any
|
||||||
}
|
}
|
||||||
|
|
||||||
renderNormal() {
|
renderNormal() {
|
||||||
let {
|
let {activePage, items, maxButtons, onPageChange, pageNum, classnames: cx, showPageInput} = this
|
||||||
activePage,
|
.props as PropsWithDefault;
|
||||||
items,
|
|
||||||
maxButtons,
|
|
||||||
onPageChange,
|
|
||||||
pageNum,
|
|
||||||
classnames: cx,
|
|
||||||
showPageInput
|
|
||||||
} = this.props as PropsWithDefault;
|
|
||||||
|
|
||||||
let pageButtons: any = [];
|
let pageButtons: any = [];
|
||||||
let startPage: number;
|
let startPage: number;
|
||||||
|
@ -112,13 +98,7 @@ export default class Pagination extends React.PureComponent<PaginationProps, any
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maxButtons && maxButtons < items) {
|
if (maxButtons && maxButtons < items) {
|
||||||
startPage = Math.max(
|
startPage = Math.max(Math.min(activePage - Math.floor(maxButtons / 2), items - maxButtons + 1), 1);
|
||||||
Math.min(
|
|
||||||
activePage - Math.floor(maxButtons / 2),
|
|
||||||
items - maxButtons + 1
|
|
||||||
),
|
|
||||||
1
|
|
||||||
);
|
|
||||||
endPage = startPage + maxButtons - 1;
|
endPage = startPage + maxButtons - 1;
|
||||||
} else {
|
} else {
|
||||||
startPage = 1;
|
startPage = 1;
|
||||||
|
@ -127,9 +107,13 @@ export default class Pagination extends React.PureComponent<PaginationProps, any
|
||||||
|
|
||||||
for (let page = startPage; page <= endPage; ++page) {
|
for (let page = startPage; page <= endPage; ++page) {
|
||||||
pageButtons.push(
|
pageButtons.push(
|
||||||
<li onClick={() => onPageChange(page)} key={page} className={cx({
|
<li
|
||||||
active: page === activePage
|
onClick={() => onPageChange(page)}
|
||||||
})}>
|
key={page}
|
||||||
|
className={cx({
|
||||||
|
active: page === activePage,
|
||||||
|
})}
|
||||||
|
>
|
||||||
<a role="button">{page}</a>
|
<a role="button">{page}</a>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
@ -138,16 +122,20 @@ export default class Pagination extends React.PureComponent<PaginationProps, any
|
||||||
if (startPage > 1) {
|
if (startPage > 1) {
|
||||||
if (startPage > 2) {
|
if (startPage > 2) {
|
||||||
pageButtons.unshift(
|
pageButtons.unshift(
|
||||||
<li onClick={() => onPageChange(startPage - 1)} key="prev-ellipsis" >
|
<li onClick={() => onPageChange(startPage - 1)} key="prev-ellipsis">
|
||||||
<a role="button">...</a>
|
<a role="button">...</a>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pageButtons.unshift(
|
pageButtons.unshift(
|
||||||
<li onClick={() => onPageChange(1)} key={1} className={cx({
|
<li
|
||||||
active: 1 === activePage
|
onClick={() => onPageChange(1)}
|
||||||
})}>
|
key={1}
|
||||||
|
className={cx({
|
||||||
|
active: 1 === activePage,
|
||||||
|
})}
|
||||||
|
>
|
||||||
<a role="button">{1}</a>
|
<a role="button">{1}</a>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
@ -156,16 +144,26 @@ export default class Pagination extends React.PureComponent<PaginationProps, any
|
||||||
if (endPage < items) {
|
if (endPage < items) {
|
||||||
if (items - endPage > 1) {
|
if (items - endPage > 1) {
|
||||||
pageButtons.push(
|
pageButtons.push(
|
||||||
<li className={cx("Pagination-ellipsis")} onClick={() => onPageChange(endPage + 1)} key="next-ellipsis" >
|
<li
|
||||||
<a role="button"><span>...</span></a>
|
className={cx('Pagination-ellipsis')}
|
||||||
|
onClick={() => onPageChange(endPage + 1)}
|
||||||
|
key="next-ellipsis"
|
||||||
|
>
|
||||||
|
<a role="button">
|
||||||
|
<span>...</span>
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pageButtons.push(
|
pageButtons.push(
|
||||||
<li onClick={() => onPageChange(items)} key={items} className={cx({
|
<li
|
||||||
active: items === activePage
|
onClick={() => onPageChange(items)}
|
||||||
})}>
|
key={items}
|
||||||
|
className={cx({
|
||||||
|
active: items === activePage,
|
||||||
|
})}
|
||||||
|
>
|
||||||
<a role="button">{items}</a>
|
<a role="button">{items}</a>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
@ -173,59 +171,65 @@ export default class Pagination extends React.PureComponent<PaginationProps, any
|
||||||
|
|
||||||
pageButtons.unshift(
|
pageButtons.unshift(
|
||||||
<li
|
<li
|
||||||
className={cx("Pagination-prev", {
|
className={cx('Pagination-prev', {
|
||||||
disabled: activePage === 1
|
disabled: activePage === 1,
|
||||||
})}
|
})}
|
||||||
onClick={activePage === 1 ? (e: any) => e.preventDefault() : () => onPageChange(activePage - 1)}
|
onClick={activePage === 1 ? (e: any) => e.preventDefault() : () => onPageChange(activePage - 1)}
|
||||||
key="prev">
|
key="prev"
|
||||||
<span></span>
|
>
|
||||||
|
<span />
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
|
||||||
pageButtons.push(
|
pageButtons.push(
|
||||||
<li
|
<li
|
||||||
className={cx("Pagination-next", {
|
className={cx('Pagination-next', {
|
||||||
disabled: activePage === items
|
disabled: activePage === items,
|
||||||
})}
|
})}
|
||||||
onClick={activePage === items ? (e: any) => e.preventDefault() : () => onPageChange(activePage + 1)}
|
onClick={activePage === items ? (e: any) => e.preventDefault() : () => onPageChange(activePage + 1)}
|
||||||
key="next">
|
key="next"
|
||||||
<span></span>
|
>
|
||||||
|
<span />
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ul
|
<ul className={cx('Pagination', 'Pagination--sm')} onSelect={(value: number) => onPageChange(value)}>
|
||||||
className={cx("Pagination", "Pagination--sm")}
|
|
||||||
onSelect={(value: number) => onPageChange(value)}
|
|
||||||
>
|
|
||||||
{pageButtons}
|
{pageButtons}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{items > 9 && showPageInput ? (
|
{items > 9 && showPageInput ? (
|
||||||
<div className="inline m-l-xs w-xs" key="toPage">
|
<div className="inline m-l-xs w-xs" key="toPage">
|
||||||
<span className={cx("Pagination-inputGroup")}>
|
<span className={cx('Pagination-inputGroup')}>
|
||||||
<input type="text" className={cx("Pagination-input")}
|
<input
|
||||||
|
type="text"
|
||||||
|
className={cx('Pagination-input')}
|
||||||
onChange={this.handlePageChange}
|
onChange={this.handlePageChange}
|
||||||
onFocus={(e: any) => e.currentTarget.select()}
|
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}
|
value={pageNum}
|
||||||
/>
|
/>
|
||||||
<span>
|
<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>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {mode} = this.props;
|
||||||
mode
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return mode === 'simple' ? this.renderSimple() : this.renderNormal();
|
return mode === 'simple' ? this.renderSimple() : this.renderNormal();
|
||||||
}
|
}
|
||||||
|
@ -233,6 +237,6 @@ export default class Pagination extends React.PureComponent<PaginationProps, any
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)pagination$/,
|
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 * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
import {SchemaNode, Action} from '../types';
|
||||||
RendererProps
|
import {getScrollParent} from '../utils/helper';
|
||||||
} from '../factory';
|
import {findDOMNode} from 'react-dom';
|
||||||
import {
|
|
||||||
SchemaNode,
|
|
||||||
Action
|
|
||||||
} from '../types';
|
|
||||||
import { getScrollParent } from '../utils/helper';
|
|
||||||
import { findDOMNode } from 'react-dom';
|
|
||||||
|
|
||||||
export interface PanelProps extends RendererProps {
|
export interface PanelProps extends RendererProps {
|
||||||
title?: string; // 标题
|
title?: string; // 标题
|
||||||
|
@ -26,12 +20,7 @@ export interface PanelProps extends RendererProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Panel extends React.Component<PanelProps> {
|
export default class Panel extends React.Component<PanelProps> {
|
||||||
static propsList: Array<string> = [
|
static propsList: Array<string> = ['headerClassName', 'footerClassName', 'actionsClassName', 'bodyClassName'];
|
||||||
"headerClassName",
|
|
||||||
"footerClassName",
|
|
||||||
"actionsClassName",
|
|
||||||
"bodyClassName"
|
|
||||||
];
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
// className: 'Panel--default',
|
// className: 'Panel--default',
|
||||||
// headerClassName: 'Panel-heading',
|
// headerClassName: 'Panel-heading',
|
||||||
|
@ -46,7 +35,7 @@ export default class Panel extends React.Component<PanelProps> {
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const dom = findDOMNode(this) as HTMLElement;
|
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) {
|
if (!parent || parent === document.body) {
|
||||||
parent = window;
|
parent = window;
|
||||||
}
|
}
|
||||||
|
@ -73,11 +62,11 @@ export default class Panel extends React.Component<PanelProps> {
|
||||||
const clip = footerDom.getBoundingClientRect();
|
const clip = footerDom.getBoundingClientRect();
|
||||||
const clientHeight = window.innerHeight;
|
const clientHeight = window.innerHeight;
|
||||||
const affixed = clip.top > clientHeight;
|
const affixed = clip.top > clientHeight;
|
||||||
|
|
||||||
footerDom.offsetWidth && (affixDom.style.cssText = `width: ${footerDom.offsetWidth}px;`);
|
footerDom.offsetWidth && (affixDom.style.cssText = `width: ${footerDom.offsetWidth}px;`);
|
||||||
affixed ? affixDom.classList.add('in') : affixDom.classList.remove('in');
|
affixed ? affixDom.classList.add('in') : affixDom.classList.remove('in');
|
||||||
}
|
}
|
||||||
|
|
||||||
renderBody(): JSX.Element | null {
|
renderBody(): JSX.Element | null {
|
||||||
const {
|
const {
|
||||||
type,
|
type,
|
||||||
|
@ -100,27 +89,28 @@ export default class Panel extends React.Component<PanelProps> {
|
||||||
|
|
||||||
const subProps = {
|
const subProps = {
|
||||||
data,
|
data,
|
||||||
...rest
|
...rest,
|
||||||
};
|
};
|
||||||
|
|
||||||
return children ? (
|
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 ? (
|
) : body ? (
|
||||||
<div className={bodyClassName || `${ns}Panel-body`}>{render('body', body, subProps)}</div>
|
<div className={bodyClassName || `${ns}Panel-body`}>{render('body', body, subProps)}</div>
|
||||||
) : null;
|
) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderActions() {
|
renderActions() {
|
||||||
const {
|
const {actions, render} = this.props;
|
||||||
actions,
|
|
||||||
render,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (Array.isArray(actions) && actions.length) {
|
if (Array.isArray(actions) && actions.length) {
|
||||||
return actions.map((action, key) => render('action', action, {
|
return actions.map((action, key) =>
|
||||||
type: action.type || 'button',
|
render('action', action, {
|
||||||
key: key
|
type: action.type || 'button',
|
||||||
}));
|
key: key,
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -149,38 +139,35 @@ export default class Panel extends React.Component<PanelProps> {
|
||||||
|
|
||||||
const subProps = {
|
const subProps = {
|
||||||
data,
|
data,
|
||||||
...rest
|
...rest,
|
||||||
};
|
};
|
||||||
|
|
||||||
const footerDoms = [];
|
const footerDoms = [];
|
||||||
const actions = this.renderActions();
|
const actions = this.renderActions();
|
||||||
actions && footerDoms.push(
|
actions &&
|
||||||
<div key="actions" className={cx(`Panel-btnToolbar`, actionsClassName || `Panel-footer`)}>
|
footerDoms.push(
|
||||||
{actions}
|
<div key="actions" className={cx(`Panel-btnToolbar`, actionsClassName || `Panel-footer`)}>
|
||||||
</div>
|
{actions}
|
||||||
);
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
footer && footerDoms.push(
|
footer &&
|
||||||
<div key="footer" className={cx(footerClassName || `Panel-footer`)}>
|
footerDoms.push(
|
||||||
{render('footer', footer, subProps)}
|
<div key="footer" className={cx(footerClassName || `Panel-footer`)}>
|
||||||
</div>
|
{render('footer', footer, subProps)}
|
||||||
);
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
let footerDom = footerDoms.length ? (
|
let footerDom = footerDoms.length ? <div ref={this.footerDom}>{footerDoms}</div> : null;
|
||||||
<div ref={this.footerDom}>
|
|
||||||
{footerDoms}
|
|
||||||
</div>
|
|
||||||
) : null;
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={cx(`Panel`, className || `Panel--default`)}>
|
||||||
className={cx(`Panel`, className || `Panel--default`)}
|
|
||||||
>
|
|
||||||
{header ? (
|
{header ? (
|
||||||
<div className={cx(headerClassName || `Panel-heading`)}>{render('header', header, subProps)}</div>
|
<div className={cx(headerClassName || `Panel-heading`)}>{render('header', header, subProps)}</div>
|
||||||
) : title ? (
|
) : 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}
|
) : null}
|
||||||
|
|
||||||
{this.renderBody()}
|
{this.renderBody()}
|
||||||
|
@ -188,7 +175,7 @@ export default class Panel extends React.Component<PanelProps> {
|
||||||
{footerDom}
|
{footerDom}
|
||||||
|
|
||||||
{affixFooter && footerDoms.length ? (
|
{affixFooter && footerDoms.length ? (
|
||||||
<div ref={this.affixDom} className={cx("Panel-fixedBottom")}>
|
<div ref={this.affixDom} className={cx('Panel-fixedBottom')}>
|
||||||
{footerDoms}
|
{footerDoms}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -199,6 +186,6 @@ export default class Panel extends React.Component<PanelProps> {
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)panel$/,
|
test: /(^|\/)panel$/,
|
||||||
name: 'panel'
|
name: 'panel',
|
||||||
})
|
})
|
||||||
export class PanelRenderer extends Panel {}
|
export class PanelRenderer extends Panel {}
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
import {filter} from '../utils/tpl';
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import {
|
|
||||||
filter
|
|
||||||
} from '../utils/tpl';
|
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
|
|
||||||
export interface PlainProps extends RendererProps {
|
export interface PlainProps extends RendererProps {
|
||||||
|
@ -19,30 +14,26 @@ export interface PlainProps extends RendererProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Plain extends React.Component<PlainProps, object> {
|
export class Plain extends React.Component<PlainProps, object> {
|
||||||
static defaultProps:Partial<PlainProps> = {
|
static defaultProps: Partial<PlainProps> = {
|
||||||
wrapperComponent: '',
|
wrapperComponent: '',
|
||||||
inline: true,
|
inline: true,
|
||||||
placeholder: '-'
|
placeholder: '-',
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {className, wrapperComponent, value, text, data, tpl, inline, placeholder, classnames: cx} = this.props;
|
||||||
className,
|
|
||||||
wrapperComponent,
|
|
||||||
value,
|
|
||||||
text,
|
|
||||||
data,
|
|
||||||
tpl,
|
|
||||||
inline,
|
|
||||||
placeholder,
|
|
||||||
classnames: cx
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const Component = wrapperComponent || (inline ? 'span' : 'div');
|
const Component = wrapperComponent || (inline ? 'span' : 'div');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Component className={cx('PlainField', className)}>
|
<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>
|
</Component>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -50,6 +41,6 @@ export class Plain extends React.Component<PlainProps, object> {
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)(?:plain|text)$/,
|
test: /(^|\/)(?:plain|text)$/,
|
||||||
name: 'plain'
|
name: 'plain',
|
||||||
})
|
})
|
||||||
export class PlainRenderer extends Plain {};
|
export class PlainRenderer extends Plain {}
|
||||||
|
|
|
@ -1,53 +1,57 @@
|
||||||
/**
|
/**
|
||||||
* @file scoped.jsx.
|
* @file scoped.jsx.
|
||||||
* @author fex
|
* @author fex
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {findDOMNode} from 'react-dom';
|
import {findDOMNode} from 'react-dom';
|
||||||
import { RendererProps } from '../factory';
|
import {RendererProps} from '../factory';
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
import hoistNonReactStatic = require('hoist-non-react-statics');
|
import hoistNonReactStatic = require('hoist-non-react-statics');
|
||||||
import { RootCloseWrapper} from 'react-overlays';
|
import {RootCloseWrapper} from 'react-overlays';
|
||||||
import PopOver from '../components/PopOver';
|
import PopOver from '../components/PopOver';
|
||||||
import Overlay from '../components/Overlay';
|
import Overlay from '../components/Overlay';
|
||||||
|
|
||||||
export interface PopOverConfig {
|
export interface PopOverConfig {}
|
||||||
}
|
|
||||||
|
|
||||||
const allowedPositions = [
|
const allowedPositions = ['center', 'top'];
|
||||||
'center',
|
|
||||||
'top'
|
|
||||||
];
|
|
||||||
|
|
||||||
export interface PopOverConfig {
|
export interface PopOverConfig {
|
||||||
saveImmediately?: boolean;
|
saveImmediately?: boolean;
|
||||||
mode?: 'dialog' | 'drawer' | 'popOver';
|
mode?: 'dialog' | 'drawer' | 'popOver';
|
||||||
title?: string;
|
title?: string;
|
||||||
size?: 'sm' | 'md' | 'lg' | 'xl';
|
size?: 'sm' | 'md' | 'lg' | 'xl';
|
||||||
position: 'center' | 'left-top' | 'right-top' | 'left-bottom' | 'right-bottom'
|
position:
|
||||||
| 'fixed-center' | 'fixed-left-top' | 'fixed-right-top' | 'fixed-left-bottom' | 'fixed-right-bottom';
|
| 'center'
|
||||||
[propName:string]: any;
|
| '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 {
|
export interface PopOverProps extends RendererProps {
|
||||||
name?: string;
|
name?: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
popOver: boolean | PopOverConfig;
|
popOver: boolean | PopOverConfig;
|
||||||
onPopOverOpen: (popover:any) => void;
|
onPopOverOpen: (popover: any) => void;
|
||||||
onPopOverClose: (popover:any) => void;
|
onPopOverClose: (popover: any) => void;
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface PopOverState {
|
export interface PopOverState {
|
||||||
isOpened: boolean;
|
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> {
|
class PopOverComponent extends React.Component<PopOverProps, PopOverState> {
|
||||||
target:HTMLElement;
|
target: HTMLElement;
|
||||||
static ComposedComponent = Component;
|
static ComposedComponent = Component;
|
||||||
constructor(props:PopOverProps) {
|
constructor(props: PopOverProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.openPopOver = this.openPopOver.bind(this);
|
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.targetRef = this.targetRef.bind(this);
|
||||||
// this.handleClickOutside = this.handleClickOutside.bind(this);
|
// this.handleClickOutside = this.handleClickOutside.bind(this);
|
||||||
this.state = {
|
this.state = {
|
||||||
isOpened: false
|
isOpened: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
targetRef(ref:any) {
|
targetRef(ref: any) {
|
||||||
this.target = ref;
|
this.target = ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
openPopOver() {
|
openPopOver() {
|
||||||
const onPopOverOpen = this.props.onPopOverOpen;
|
const onPopOverOpen = this.props.onPopOverOpen;
|
||||||
this.setState({
|
this.setState(
|
||||||
isOpened: true
|
{
|
||||||
}, () => onPopOverOpen && onPopOverOpen(this.props.popOver));
|
isOpened: true,
|
||||||
|
},
|
||||||
|
() => onPopOverOpen && onPopOverOpen(this.props.popOver)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
closePopOver() {
|
closePopOver() {
|
||||||
|
@ -76,24 +83,23 @@ export const HocPopOver = (config:Partial<PopOverConfig> = {}) => (Component: Re
|
||||||
}
|
}
|
||||||
|
|
||||||
const onPopOverClose = this.props.onPopOverClose;
|
const onPopOverClose = this.props.onPopOverClose;
|
||||||
this.setState({
|
this.setState(
|
||||||
isOpened: false
|
{
|
||||||
}, () => onPopOverClose && onPopOverClose(this.props.popOver));
|
isOpened: false,
|
||||||
|
},
|
||||||
|
() => onPopOverClose && onPopOverClose(this.props.popOver)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
buildSchema() {
|
buildSchema() {
|
||||||
const {
|
const {popOver, name, label} = this.props;
|
||||||
popOver,
|
|
||||||
name,
|
|
||||||
label
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
let schema;
|
let schema;
|
||||||
|
|
||||||
if (popOver === true) {
|
if (popOver === true) {
|
||||||
schema = {
|
schema = {
|
||||||
type: 'panel',
|
type: 'panel',
|
||||||
body: '${name}'
|
body: '${name}',
|
||||||
};
|
};
|
||||||
} else if (popOver && (popOver.mode === 'dialog' || popOver.mode === 'drawer')) {
|
} else if (popOver && (popOver.mode === 'dialog' || popOver.mode === 'drawer')) {
|
||||||
schema = {
|
schema = {
|
||||||
|
@ -102,15 +108,15 @@ export const HocPopOver = (config:Partial<PopOverConfig> = {}) => (Component: Re
|
||||||
{
|
{
|
||||||
label: '关闭',
|
label: '关闭',
|
||||||
type: 'button',
|
type: 'button',
|
||||||
actionType: 'cancel'
|
actionType: 'cancel',
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
...popOver
|
...popOver,
|
||||||
};
|
};
|
||||||
} else if (popOver) {
|
} else if (popOver) {
|
||||||
schema = {
|
schema = {
|
||||||
type: 'panel',
|
type: 'panel',
|
||||||
...popOver
|
...popOver,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,41 +124,33 @@ export const HocPopOver = (config:Partial<PopOverConfig> = {}) => (Component: Re
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPopOver() {
|
renderPopOver() {
|
||||||
let {
|
let {popOver, render, popOverContainer, classnames: cx, classPrefix: ns} = this.props;
|
||||||
popOver,
|
|
||||||
render,
|
|
||||||
popOverContainer,
|
|
||||||
classnames: cx,
|
|
||||||
classPrefix: ns
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (popOver && ((popOver as PopOverConfig).mode === 'dialog'
|
if (
|
||||||
|| (popOver as PopOverConfig).mode === 'drawer')) {
|
popOver &&
|
||||||
|
((popOver as PopOverConfig).mode === 'dialog' || (popOver as PopOverConfig).mode === 'drawer')
|
||||||
|
) {
|
||||||
return render('popover-detail', this.buildSchema(), {
|
return render('popover-detail', this.buildSchema(), {
|
||||||
show: true,
|
show: true,
|
||||||
onClose: this.closePopOver,
|
onClose: this.closePopOver,
|
||||||
onConfirm: this.closePopOver
|
onConfirm: this.closePopOver,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = render('popover-detail', this.buildSchema(), {
|
const content = render('popover-detail', this.buildSchema(), {
|
||||||
className: cx((popOver as PopOverConfig).className)
|
className: cx((popOver as PopOverConfig).className),
|
||||||
}) as JSX.Element;
|
}) as JSX.Element;
|
||||||
|
|
||||||
if (!popOverContainer) {
|
if (!popOverContainer) {
|
||||||
popOverContainer = () => findDOMNode(this);
|
popOverContainer = () => findDOMNode(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
const position = popOver && (popOver as PopOverConfig).position || '';
|
const position = (popOver && (popOver as PopOverConfig).position) || '';
|
||||||
const isFixed = /^fixed\-/.test(position);
|
const isFixed = /^fixed\-/.test(position);
|
||||||
|
|
||||||
return isFixed ? (
|
return isFixed ? (
|
||||||
<RootCloseWrapper
|
<RootCloseWrapper disabled={!this.state.isOpened} onRootClose={this.closePopOver}>
|
||||||
disabled={!this.state.isOpened}
|
<div className={cx(`PopOverAble--fixed PopOverAble--${position}`)}>{content}</div>
|
||||||
onRootClose={this.closePopOver}
|
|
||||||
><div className={cx(`PopOverAble--fixed PopOverAble--${position}`)}>
|
|
||||||
{content}
|
|
||||||
</div>
|
|
||||||
</RootCloseWrapper>
|
</RootCloseWrapper>
|
||||||
) : (
|
) : (
|
||||||
<Overlay
|
<Overlay
|
||||||
|
@ -163,10 +161,7 @@ export const HocPopOver = (config:Partial<PopOverConfig> = {}) => (Component: Re
|
||||||
rootClose
|
rootClose
|
||||||
show
|
show
|
||||||
>
|
>
|
||||||
<PopOver
|
<PopOver classPrefix={ns} className={cx('PopOverAble-popover')}>
|
||||||
classPrefix={ns}
|
|
||||||
className={cx("PopOverAble-popover")}
|
|
||||||
>
|
|
||||||
{content}
|
{content}
|
||||||
</PopOver>
|
</PopOver>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
@ -174,31 +169,25 @@ export const HocPopOver = (config:Partial<PopOverConfig> = {}) => (Component: Re
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {onQuickChange, popOver, popOverEnabled, className, noHoc, classnames: cx, render} = this.props;
|
||||||
onQuickChange,
|
|
||||||
popOver,
|
|
||||||
popOverEnabled,
|
|
||||||
className,
|
|
||||||
noHoc,
|
|
||||||
classnames: cx,
|
|
||||||
render
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (!popOver || popOverEnabled === false || noHoc) {
|
if (!popOver || popOverEnabled === false || noHoc) {
|
||||||
return (
|
return <Component {...this.props} />;
|
||||||
<Component {...this.props} />
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Component
|
<Component
|
||||||
{...this.props}
|
{...this.props}
|
||||||
className={cx(`Field--popOverAble`, className, {
|
className={cx(`Field--popOverAble`, className, {
|
||||||
'in': this.state.isOpened
|
in: this.state.isOpened,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Component {...this.props} wrapperComponent={''} noHoc ref={this.targetRef} />
|
<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}
|
{this.state.isOpened ? this.renderPopOver() : null}
|
||||||
</Component>
|
</Component>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,17 +1,8 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import {ServiceStore, IServiceStore} from '../store/service';
|
import {ServiceStore, IServiceStore} from '../store/service';
|
||||||
import {
|
import {Api, SchemaNode, PlainObject} from '../types';
|
||||||
Api,
|
import {filter} from '../utils/tpl';
|
||||||
SchemaNode,
|
|
||||||
PlainObject
|
|
||||||
} from '../types';
|
|
||||||
import {
|
|
||||||
filter
|
|
||||||
} from '../utils/tpl';
|
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
|
|
||||||
export interface ProgressProps extends RendererProps {
|
export interface ProgressProps extends RendererProps {
|
||||||
|
@ -23,23 +14,17 @@ export interface ProgressProps extends RendererProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ProgressField extends React.Component<ProgressProps, object> {
|
export class ProgressField extends React.Component<ProgressProps, object> {
|
||||||
static defaultProps:Partial<ProgressProps> = {
|
static defaultProps: Partial<ProgressProps> = {
|
||||||
placeholder: '-',
|
placeholder: '-',
|
||||||
progressClassName: 'progress-xs progress-striped active m-b-none',
|
progressClassName: 'progress-xs progress-striped active m-b-none',
|
||||||
progressBarClassName: '',
|
progressBarClassName: '',
|
||||||
map: [
|
map: ['bg-danger', 'bg-warning', 'bg-info', 'bg-success', 'bg-success'],
|
||||||
'bg-danger',
|
showLabel: true,
|
||||||
'bg-warning',
|
|
||||||
'bg-info',
|
|
||||||
'bg-success',
|
|
||||||
'bg-success'
|
|
||||||
],
|
|
||||||
showLabel: true
|
|
||||||
};
|
};
|
||||||
|
|
||||||
autoClassName(value:number) {
|
autoClassName(value: number) {
|
||||||
const map = this.props.map;
|
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));
|
index = Math.max(0, Math.min(map.length - 1, index));
|
||||||
return map[index];
|
return map[index];
|
||||||
}
|
}
|
||||||
|
@ -52,11 +37,11 @@ export class ProgressField extends React.Component<ProgressProps, object> {
|
||||||
progressBarClassName,
|
progressBarClassName,
|
||||||
map,
|
map,
|
||||||
showLabel,
|
showLabel,
|
||||||
classnames: cx
|
classnames: cx,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
let value = this.props.value;
|
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)) {
|
if (/^\d*\.?\d+$/.test(value)) {
|
||||||
value = parseFloat(value);
|
value = parseFloat(value);
|
||||||
|
@ -64,30 +49,25 @@ export class ProgressField extends React.Component<ProgressProps, object> {
|
||||||
|
|
||||||
if (typeof value === 'number') {
|
if (typeof value === 'number') {
|
||||||
viewValue = [
|
viewValue = [
|
||||||
<div key="progress" className={cx("progress", progressClassName)}>
|
<div key="progress" className={cx('progress', progressClassName)}>
|
||||||
<div
|
<div
|
||||||
className={cx("progress-bar", progressBarClassName || this.autoClassName(value))}
|
className={cx('progress-bar', progressBarClassName || this.autoClassName(value))}
|
||||||
title={`${value}%`}
|
title={`${value}%`}
|
||||||
style={{
|
style={{
|
||||||
width: `${value}%`
|
width: `${value}%`,
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
</div>
|
|
||||||
</div>,
|
</div>,
|
||||||
showLabel ? <div key="value">{value}%</div> : null
|
showLabel ? <div key="value">{value}%</div> : null,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <span className={cx('ProgressField', className)}>{viewValue}</span>;
|
||||||
<span className={cx('ProgressField', className)}>
|
|
||||||
{viewValue}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)progress$/,
|
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 React from 'react';
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
import {FormItem, FormControlProps} from './Form/Item';
|
||||||
RendererProps,
|
import {filter} from '../utils/tpl';
|
||||||
} from '../factory';
|
|
||||||
import {
|
|
||||||
FormItem,
|
|
||||||
FormControlProps
|
|
||||||
} from './Form/Item';
|
|
||||||
import {
|
|
||||||
filter
|
|
||||||
} from '../utils/tpl';
|
|
||||||
import QrCode = require('qrcode.react');
|
import QrCode = require('qrcode.react');
|
||||||
|
|
||||||
export interface QRCodeProps extends FormControlProps {
|
export interface QRCodeProps extends FormControlProps {
|
||||||
codeSize?: number;
|
codeSize?: number;
|
||||||
backgroundColor?: string;
|
backgroundColor?: string;
|
||||||
foregroundColor?: string;
|
foregroundColor?: string;
|
||||||
level?: string
|
level?: string;
|
||||||
placeholder: 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> = {
|
static defaultProps: Partial<QRCodeProps> = {
|
||||||
codeSize: 128,
|
codeSize: 128,
|
||||||
backgroundColor: '#fff',
|
backgroundColor: '#fff',
|
||||||
foregroundColor: '#000',
|
foregroundColor: '#000',
|
||||||
level: 'L',
|
level: 'L',
|
||||||
placeholder: '-'
|
placeholder: '-',
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -40,7 +32,7 @@ export default class QRCode extends React.Component<QRCodeProps, any>{
|
||||||
level,
|
level,
|
||||||
value,
|
value,
|
||||||
data,
|
data,
|
||||||
classPrefix: ns
|
classPrefix: ns,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -52,20 +44,23 @@ export default class QRCode extends React.Component<QRCodeProps, any>{
|
||||||
bgColor={backgroundColor}
|
bgColor={backgroundColor}
|
||||||
fgColor={foregroundColor}
|
fgColor={foregroundColor}
|
||||||
level={level || 'L'}
|
level={level || 'L'}
|
||||||
/>) : <span className={`${ns}QrCode--placeholder`}>{placeholder}</span>}
|
/>
|
||||||
|
) : (
|
||||||
|
<span className={`${ns}QrCode--placeholder`}>{placeholder}</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)qr\-?code$/,
|
test: /(^|\/)qr\-?code$/,
|
||||||
name: 'qrcode'
|
name: 'qrcode',
|
||||||
})
|
})
|
||||||
export class QRCodeRenderer extends QRCode { }
|
export class QRCodeRenderer extends QRCode {}
|
||||||
|
|
||||||
@FormItem({
|
@FormItem({
|
||||||
type: 'qr-code',
|
type: 'qr-code',
|
||||||
sizeMutable: false
|
sizeMutable: false,
|
||||||
})
|
})
|
||||||
export class QRCodeControlRenderer extends QRCode {}
|
export class QRCodeControlRenderer extends QRCode {}
|
||||||
|
|
|
@ -1,25 +1,24 @@
|
||||||
/**
|
/**
|
||||||
* @file scoped.jsx.
|
* @file scoped.jsx.
|
||||||
* @author fex
|
* @author fex
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {findDOMNode} from 'react-dom';
|
import {findDOMNode} from 'react-dom';
|
||||||
import find = require('lodash/find');
|
import find = require('lodash/find');
|
||||||
import * as PropTypes from 'prop-types';
|
import * as PropTypes from 'prop-types';
|
||||||
import isPlainObject = require('lodash/isPlainObject');
|
import isPlainObject = require('lodash/isPlainObject');
|
||||||
import { RendererProps } from '../factory';
|
import {RendererProps} from '../factory';
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
import hoistNonReactStatic = require('hoist-non-react-statics');
|
import hoistNonReactStatic = require('hoist-non-react-statics');
|
||||||
import onClickOutside from "react-onclickoutside";
|
import onClickOutside from 'react-onclickoutside';
|
||||||
import { Action } from '../types';
|
import {Action} from '../types';
|
||||||
import * as keycode from 'keycode';
|
import * as keycode from 'keycode';
|
||||||
import matches = require('dom-helpers/query/matches');
|
import matches = require('dom-helpers/query/matches');
|
||||||
import Overlay from '../components/Overlay';
|
import Overlay from '../components/Overlay';
|
||||||
import PopOver from '../components/PopOver';
|
import PopOver from '../components/PopOver';
|
||||||
|
|
||||||
export interface QuickEditConfig {
|
export interface QuickEditConfig {}
|
||||||
}
|
|
||||||
|
|
||||||
export interface QuickEditConfig {
|
export interface QuickEditConfig {
|
||||||
saveImmediately?: boolean;
|
saveImmediately?: boolean;
|
||||||
|
@ -30,31 +29,29 @@ export interface QuickEditConfig {
|
||||||
fieldSet?: any;
|
fieldSet?: any;
|
||||||
focusable?: boolean;
|
focusable?: boolean;
|
||||||
popOverClassName?: string;
|
popOverClassName?: string;
|
||||||
[propName:string]: any;
|
[propName: string]: any;
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface QuickEditProps extends RendererProps {
|
export interface QuickEditProps extends RendererProps {
|
||||||
name?: string;
|
name?: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
quickEdit: boolean | QuickEditConfig;
|
quickEdit: boolean | QuickEditConfig;
|
||||||
quickEditEnabled?: boolean;
|
quickEditEnabled?: boolean;
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface QuickEditState {
|
export interface QuickEditState {
|
||||||
isOpened: boolean;
|
isOpened: boolean;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
let inited: boolean = false;
|
||||||
|
let currentOpened: any;
|
||||||
|
|
||||||
let inited:boolean = false;
|
export const HocQuickEdit = (config: Partial<QuickEditConfig> = {}) => (Component: React.ComponentType<any>): any => {
|
||||||
let currentOpened:any;
|
|
||||||
|
|
||||||
export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component: React.ComponentType<any>):any => {
|
|
||||||
|
|
||||||
class QuickEditComponent extends React.PureComponent<QuickEditProps, QuickEditState> {
|
class QuickEditComponent extends React.PureComponent<QuickEditProps, QuickEditState> {
|
||||||
target:HTMLElement;
|
target: HTMLElement;
|
||||||
overlay: HTMLElement;
|
overlay: HTMLElement;
|
||||||
static ComposedComponent = Component;
|
static ComposedComponent = Component;
|
||||||
constructor(props:QuickEditProps) {
|
constructor(props: QuickEditProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.openQuickEdit = this.openQuickEdit.bind(this);
|
this.openQuickEdit = this.openQuickEdit.bind(this);
|
||||||
|
@ -65,9 +62,9 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
|
||||||
this.overlayRef = this.overlayRef.bind(this);
|
this.overlayRef = this.overlayRef.bind(this);
|
||||||
this.handleWindowKeyPress = this.handleWindowKeyPress.bind(this);
|
this.handleWindowKeyPress = this.handleWindowKeyPress.bind(this);
|
||||||
this.handleWindowKeyDown = this.handleWindowKeyDown.bind(this);
|
this.handleWindowKeyDown = this.handleWindowKeyDown.bind(this);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
isOpened: false
|
isOpened: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,9 +80,9 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
|
||||||
document.body.addEventListener('keydown', this.handleWindowKeyDown);
|
document.body.addEventListener('keydown', this.handleWindowKeyDown);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleWindowKeyPress(e:Event) {
|
handleWindowKeyPress(e: Event) {
|
||||||
const ns = this.props.classPrefix;
|
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) {
|
if (!el) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -93,104 +90,111 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
|
||||||
if (!table) {
|
if (!table) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keycode(e) === 'space' && !~['INPUT', 'TEXTAREA'].indexOf(el.tagName)) {
|
if (keycode(e) === 'space' && !~['INPUT', 'TEXTAREA'].indexOf(el.tagName)) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleWindowKeyDown(e:Event) {
|
handleWindowKeyDown(e: Event) {
|
||||||
const code = keycode(e);
|
const code = keycode(e);
|
||||||
|
|
||||||
if (code === 'esc' && currentOpened) {
|
if (code === 'esc' && currentOpened) {
|
||||||
currentOpened.closeQuickEdit();
|
currentOpened.closeQuickEdit();
|
||||||
} else if (
|
} else if (
|
||||||
~['INPUT', 'TEXTAREA'].indexOf((e.target as HTMLElement).tagName)
|
~['INPUT', 'TEXTAREA'].indexOf((e.target as HTMLElement).tagName) ||
|
||||||
|| (e.target as HTMLElement).contentEditable === 'true'
|
(e.target as HTMLElement).contentEditable === 'true' ||
|
||||||
|| !~['up', 'down', 'left', 'right'].indexOf(code)
|
!~['up', 'down', 'left', 'right'].indexOf(code)
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const ns = this.props.classPrefix;
|
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) {
|
if (!el) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let table = el.closest('table');
|
let table = el.closest('table');
|
||||||
if (!table) {
|
if (!table) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let current = table.querySelector(`.${ns}Field--quickEditable:focus`) as HTMLTableDataCellElement;
|
let current = table.querySelector(`.${ns}Field--quickEditable:focus`) as HTMLTableDataCellElement;
|
||||||
|
|
||||||
if (!current) {
|
if (!current) {
|
||||||
let dom = table.querySelector(`.${ns}Field--quickEditable[tabindex]`) as HTMLElement;
|
let dom = table.querySelector(`.${ns}Field--quickEditable[tabindex]`) as HTMLElement;
|
||||||
dom && dom.focus();
|
dom && dom.focus();
|
||||||
} else {
|
} else {
|
||||||
let prevTr, nextTr, prevTd, nextTd;
|
let prevTr, nextTr, prevTd, nextTd;
|
||||||
|
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 'up':
|
case 'up':
|
||||||
prevTr = (current.parentNode as HTMLElement).previousSibling as HTMLTableCellElement;
|
prevTr = (current.parentNode as HTMLElement).previousSibling as HTMLTableCellElement;
|
||||||
|
|
||||||
if (prevTr) {
|
if (prevTr) {
|
||||||
let index = current.cellIndex;
|
let index = current.cellIndex;
|
||||||
(prevTr.children[index] as HTMLElement).focus();
|
(prevTr.children[index] as HTMLElement).focus();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'down':
|
case 'down':
|
||||||
nextTr = (current.parentNode as HTMLElement).nextSibling as HTMLTableCellElement;
|
nextTr = (current.parentNode as HTMLElement).nextSibling as HTMLTableCellElement;
|
||||||
|
|
||||||
if (nextTr) {
|
if (nextTr) {
|
||||||
let index = current.cellIndex;
|
let index = current.cellIndex;
|
||||||
(nextTr.children[index] as HTMLElement).focus();
|
(nextTr.children[index] as HTMLElement).focus();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'left':
|
case 'left':
|
||||||
prevTd = current.previousElementSibling as HTMLTableCellElement;
|
prevTd = current.previousElementSibling as HTMLTableCellElement;
|
||||||
|
|
||||||
while (prevTd) {
|
while (prevTd) {
|
||||||
if (matches(prevTd, `.${ns}Field--quickEditable[tabindex]`)) {
|
if (matches(prevTd, `.${ns}Field--quickEditable[tabindex]`)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
prevTd = prevTd.previousElementSibling;
|
prevTd = prevTd.previousElementSibling;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prevTd) {
|
if (prevTd) {
|
||||||
(prevTd as HTMLElement).focus();
|
(prevTd as HTMLElement).focus();
|
||||||
} else if ((current.parentNode as HTMLElement).previousSibling) {
|
} else if ((current.parentNode as HTMLElement).previousSibling) {
|
||||||
let tds = ((current.parentNode as HTMLElement).previousSibling as HTMLElement).querySelectorAll(`.${ns}Field--quickEditable[tabindex]`);
|
let tds = ((current.parentNode as HTMLElement)
|
||||||
|
.previousSibling as HTMLElement).querySelectorAll(
|
||||||
if (tds.length) {
|
`.${ns}Field--quickEditable[tabindex]`
|
||||||
(tds[tds.length - 1] as HTMLElement).focus();
|
);
|
||||||
}
|
|
||||||
}
|
if (tds.length) {
|
||||||
break;
|
(tds[tds.length - 1] as HTMLElement).focus();
|
||||||
case 'right':
|
}
|
||||||
nextTd = current.nextSibling;
|
}
|
||||||
while (nextTd) {
|
break;
|
||||||
if (matches(nextTd, `.${ns}Field--quickEditable[tabindex]`)) {
|
case 'right':
|
||||||
break;
|
nextTd = current.nextSibling;
|
||||||
}
|
while (nextTd) {
|
||||||
|
if (matches(nextTd, `.${ns}Field--quickEditable[tabindex]`)) {
|
||||||
nextTd = nextTd.nextSibling;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextTd) {
|
nextTd = nextTd.nextSibling;
|
||||||
(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 as HTMLElement).focus();
|
||||||
if (nextTd) {
|
} else if ((current.parentNode as HTMLElement).nextSibling) {
|
||||||
nextTd.focus();
|
nextTd = ((current.parentNode as HTMLElement).nextSibling as HTMLElement).querySelector(
|
||||||
}
|
`.${ns}Field--quickEditable[tabindex]`
|
||||||
}
|
);
|
||||||
break;
|
|
||||||
}
|
if (nextTd) {
|
||||||
|
nextTd.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,14 +202,12 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
|
||||||
// this.closeQuickEdit();
|
// this.closeQuickEdit();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
overlayRef(ref:any) {
|
overlayRef(ref: any) {
|
||||||
this.overlay = ref;
|
this.overlay = ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAction(e:any, action:Action, ctx:object) {
|
handleAction(e: any, action: Action, ctx: object) {
|
||||||
const {
|
const {onAction} = this.props;
|
||||||
onAction
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (action.actionType === 'cancel' || action.actionType === 'close') {
|
if (action.actionType === 'cancel' || action.actionType === 'close') {
|
||||||
this.closeQuickEdit();
|
this.closeQuickEdit();
|
||||||
|
@ -215,11 +217,8 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
|
||||||
onAction && onAction(e, action, ctx);
|
onAction && onAction(e, action, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit(values:object) {
|
handleSubmit(values: object) {
|
||||||
const {
|
const {onQuickChange, quickEdit} = this.props;
|
||||||
onQuickChange,
|
|
||||||
quickEdit
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
this.closeQuickEdit();
|
this.closeQuickEdit();
|
||||||
onQuickChange(values, (quickEdit as QuickEditConfig).saveImmediately);
|
onQuickChange(values, (quickEdit as QuickEditConfig).saveImmediately);
|
||||||
|
@ -228,7 +227,7 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
|
||||||
openQuickEdit() {
|
openQuickEdit() {
|
||||||
currentOpened = this;
|
currentOpened = this;
|
||||||
this.setState({
|
this.setState({
|
||||||
isOpened: true
|
isOpened: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,21 +237,21 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
|
||||||
}
|
}
|
||||||
currentOpened = null;
|
currentOpened = null;
|
||||||
const ns = this.props.classPrefix;
|
const ns = this.props.classPrefix;
|
||||||
this.setState({
|
this.setState(
|
||||||
isOpened: false
|
{
|
||||||
}, () => {
|
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();
|
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() {
|
buildSchema() {
|
||||||
const {
|
const {quickEdit, name, label} = this.props;
|
||||||
quickEdit,
|
|
||||||
name,
|
|
||||||
label
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
let schema;
|
let schema;
|
||||||
|
|
||||||
|
@ -266,18 +265,23 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
|
||||||
type: 'text',
|
type: 'text',
|
||||||
name,
|
name,
|
||||||
placeholder: label,
|
placeholder: label,
|
||||||
label: false
|
label: false,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
} else if (quickEdit) {
|
} 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 = {
|
schema = {
|
||||||
title: '',
|
title: '',
|
||||||
autoFocus: (quickEdit as QuickEditConfig).mode !== 'inline',
|
autoFocus: (quickEdit as QuickEditConfig).mode !== 'inline',
|
||||||
mode: (quickEdit as QuickEditConfig).mode === 'inline' ? 'inline' : 'normal',
|
mode: (quickEdit as QuickEditConfig).mode === 'inline' ? 'inline' : 'normal',
|
||||||
...quickEdit,
|
...quickEdit,
|
||||||
type: 'form'
|
type: 'form',
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
schema = {
|
schema = {
|
||||||
|
@ -292,9 +296,9 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
|
||||||
name: quickEdit.name || name,
|
name: quickEdit.name || name,
|
||||||
placeholder: label,
|
placeholder: label,
|
||||||
label: false,
|
label: false,
|
||||||
...quickEdit
|
...quickEdit,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -303,26 +307,29 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
|
||||||
schema = {
|
schema = {
|
||||||
...schema,
|
...schema,
|
||||||
wrapWithPanel: (quickEdit as QuickEditConfig).mode !== 'inline',
|
wrapWithPanel: (quickEdit as QuickEditConfig).mode !== 'inline',
|
||||||
actions: (quickEdit as QuickEditConfig).mode === 'inline' ? [] : [
|
actions:
|
||||||
{
|
(quickEdit as QuickEditConfig).mode === 'inline'
|
||||||
type: 'button',
|
? []
|
||||||
label: '取消',
|
: [
|
||||||
actionType: 'cancel'
|
{
|
||||||
},
|
type: 'button',
|
||||||
|
label: '取消',
|
||||||
|
actionType: 'cancel',
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
label: '确认',
|
label: '确认',
|
||||||
type: 'submit',
|
type: 'submit',
|
||||||
primary: true
|
primary: true,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return schema || 'error';
|
return schema || 'error';
|
||||||
}
|
}
|
||||||
|
|
||||||
handleKeyUp(e:Event) {
|
handleKeyUp(e: Event) {
|
||||||
const code = keycode(e);
|
const code = keycode(e);
|
||||||
if (code === 'space' && !~['INPUT', 'TEXTAREA'].indexOf((e.target as HTMLElement).tagName)) {
|
if (code === 'space' && !~['INPUT', 'TEXTAREA'].indexOf((e.target as HTMLElement).tagName)) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -332,13 +339,7 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPopOver() {
|
renderPopOver() {
|
||||||
let {
|
let {quickEdit, render, popOverContainer, classPrefix: ns, classnames: cx} = this.props;
|
||||||
quickEdit,
|
|
||||||
render,
|
|
||||||
popOverContainer,
|
|
||||||
classPrefix: ns,
|
|
||||||
classnames: cx
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const content = (
|
const content = (
|
||||||
<div className={cx((quickEdit as QuickEditConfig).className)}>
|
<div className={cx((quickEdit as QuickEditConfig).className)}>
|
||||||
|
@ -346,7 +347,7 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
|
||||||
onSubmit: this.handleSubmit,
|
onSubmit: this.handleSubmit,
|
||||||
onAction: this.handleAction,
|
onAction: this.handleAction,
|
||||||
onChange: null,
|
onChange: null,
|
||||||
popOverContainer: popOverContainer ? () => this.overlay : null
|
popOverContainer: popOverContainer ? () => this.overlay : null,
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -375,20 +376,10 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {onQuickChange, quickEdit, quickEditEnabled, className, classnames: cx, render, noHoc} = this.props;
|
||||||
onQuickChange,
|
|
||||||
quickEdit,
|
|
||||||
quickEditEnabled,
|
|
||||||
className,
|
|
||||||
classnames: cx,
|
|
||||||
render,
|
|
||||||
noHoc
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (!quickEdit || !onQuickChange || quickEditEnabled === false || noHoc) {
|
if (!quickEdit || !onQuickChange || quickEditEnabled === false || noHoc) {
|
||||||
return (
|
return <Component {...this.props} />;
|
||||||
<Component {...this.props} />
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((quickEdit as QuickEditConfig).mode === 'inline') {
|
if ((quickEdit as QuickEditConfig).mode === 'inline') {
|
||||||
|
@ -397,22 +388,27 @@ export const HocQuickEdit = (config:Partial<QuickEditConfig> = {}) => (Component
|
||||||
{render('inline-form', this.buildSchema(), {
|
{render('inline-form', this.buildSchema(), {
|
||||||
wrapperComponent: 'div',
|
wrapperComponent: 'div',
|
||||||
className: cx('Form--quickEdit'),
|
className: cx('Form--quickEdit'),
|
||||||
onChange: (values:object) => onQuickChange(values, (quickEdit as QuickEditConfig).saveImmediately)
|
onChange: (values: object) =>
|
||||||
|
onQuickChange(values, (quickEdit as QuickEditConfig).saveImmediately),
|
||||||
})}
|
})}
|
||||||
</Component>
|
</Component>
|
||||||
)
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<Component
|
<Component
|
||||||
{...this.props}
|
{...this.props}
|
||||||
className={cx(`Field--quickEditable`, className, {
|
className={cx(`Field--quickEditable`, className, {
|
||||||
'in': this.state.isOpened
|
in: this.state.isOpened,
|
||||||
})}
|
})}
|
||||||
tabIndex={(quickEdit as QuickEditConfig).focusable === false ? undefined : '0'}
|
tabIndex={(quickEdit as QuickEditConfig).focusable === false ? undefined : '0'}
|
||||||
onKeyUp={this.handleKeyUp}
|
onKeyUp={this.handleKeyUp}
|
||||||
>
|
>
|
||||||
<Component {...this.props} wrapperComponent={''} noHoc />
|
<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}
|
{this.state.isOpened ? this.renderPopOver() : null}
|
||||||
</Component>
|
</Component>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,18 +1,26 @@
|
||||||
import * as React from "react";
|
import * as React from 'react';
|
||||||
import { Renderer, RendererProps } from "../factory";
|
import {Renderer, RendererProps} from '../factory';
|
||||||
import { Api, SchemaNode, Schema, Action } from "../types";
|
import {Api, SchemaNode, Schema, Action} from '../types';
|
||||||
import * as cx from "classnames";
|
import * as cx from 'classnames';
|
||||||
import TooltipWrapper from '../components/TooltipWrapper';
|
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') {
|
if (typeof tooltip === 'string') {
|
||||||
return filter(tooltip, data);
|
return filter(tooltip, data);
|
||||||
} else if (tooltip) {
|
} else if (tooltip) {
|
||||||
return tooltip.title ? {
|
return tooltip.title
|
||||||
title: filter(tooltip.title, data),
|
? {
|
||||||
content: tooltip.content || tooltip.body ? filter(tooltip.content || tooltip.body || '', data) : undefined,
|
title: filter(tooltip.title, data),
|
||||||
} : (tooltip.content || tooltip.body ? filter(tooltip.content || tooltip.body || '', data) : undefined);
|
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;
|
return tooltip;
|
||||||
}
|
}
|
||||||
|
@ -45,7 +53,7 @@ export default class Remark extends React.Component<RemarkProps> {
|
||||||
classPrefix: ns,
|
classPrefix: ns,
|
||||||
classnames: cx,
|
classnames: cx,
|
||||||
content,
|
content,
|
||||||
data
|
data,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -53,14 +61,14 @@ export default class Remark extends React.Component<RemarkProps> {
|
||||||
classPrefix={ns}
|
classPrefix={ns}
|
||||||
classnames={cx}
|
classnames={cx}
|
||||||
tooltip={filterContents(tooltip || content, data)}
|
tooltip={filterContents(tooltip || content, data)}
|
||||||
placement={tooltip && tooltip.placement || placement}
|
placement={(tooltip && tooltip.placement) || placement}
|
||||||
rootClose={tooltip && tooltip.rootClose || rootClose}
|
rootClose={(tooltip && tooltip.rootClose) || rootClose}
|
||||||
trigger={tooltip && tooltip.trigger || trigger}
|
trigger={(tooltip && tooltip.trigger) || trigger}
|
||||||
container={container}
|
container={container}
|
||||||
delay={tooltip && tooltip.delay}
|
delay={tooltip && tooltip.delay}
|
||||||
>
|
>
|
||||||
<div className={cx(`Remark`, tooltip && tooltip.className || className || `Remark--warning`)}>
|
<div className={cx(`Remark`, (tooltip && tooltip.className) || className || `Remark--warning`)}>
|
||||||
<i className={cx('Remark-icon', tooltip && tooltip.icon || icon)} />
|
<i className={cx('Remark-icon', (tooltip && tooltip.icon) || icon)} />
|
||||||
</div>
|
</div>
|
||||||
</TooltipWrapper>
|
</TooltipWrapper>
|
||||||
);
|
);
|
||||||
|
@ -69,6 +77,6 @@ export default class Remark extends React.Component<RemarkProps> {
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)remark$/,
|
test: /(^|\/)remark$/,
|
||||||
name: 'remark'
|
name: 'remark',
|
||||||
})
|
})
|
||||||
export class RemarkRenderer extends Remark {}
|
export class RemarkRenderer extends Remark {}
|
||||||
|
|
|
@ -1,23 +1,13 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as PropTypes from 'prop-types';
|
import * as PropTypes from 'prop-types';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import {ServiceStore, IServiceStore} from '../store/service';
|
import {ServiceStore, IServiceStore} from '../store/service';
|
||||||
import {
|
import {Api, SchemaNode, ApiObject, RendererData} from '../types';
|
||||||
Api,
|
import {filter, evalExpression} from '../utils/tpl';
|
||||||
SchemaNode,
|
|
||||||
ApiObject,
|
|
||||||
RendererData
|
|
||||||
} from '../types';
|
|
||||||
import {
|
|
||||||
filter, evalExpression
|
|
||||||
} from '../utils/tpl';
|
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
import Scoped, { ScopedContext, IScopedContext } from '../Scoped';
|
import Scoped, {ScopedContext, IScopedContext} from '../Scoped';
|
||||||
import { observer } from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
import { isApiOutdated } from '../utils/api';
|
import {isApiOutdated} from '../utils/api';
|
||||||
|
|
||||||
export interface ServiceProps extends RendererProps {
|
export interface ServiceProps extends RendererProps {
|
||||||
api?: Api;
|
api?: Api;
|
||||||
|
@ -34,15 +24,11 @@ export default class Service extends React.Component<ServiceProps> {
|
||||||
timer: NodeJS.Timeout;
|
timer: NodeJS.Timeout;
|
||||||
mounted: boolean;
|
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);
|
super(props);
|
||||||
|
|
||||||
this.handleQuery = this.handleQuery.bind(this);
|
this.handleQuery = this.handleQuery.bind(this);
|
||||||
|
@ -52,40 +38,36 @@ export default class Service extends React.Component<ServiceProps> {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const {
|
const {schemaApi, initFetchSchema, api, initFetch, store} = this.props;
|
||||||
schemaApi,
|
|
||||||
initFetchSchema,
|
|
||||||
api,
|
|
||||||
initFetch,
|
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
this.mounted = true;
|
this.mounted = true;
|
||||||
|
|
||||||
if (schemaApi && initFetchSchema !== false && (!(schemaApi as ApiObject).sendOn || evalExpression((schemaApi as ApiObject).sendOn as string, store.data))) {
|
if (
|
||||||
store
|
schemaApi &&
|
||||||
.fetchSchema(schemaApi, store.data)
|
initFetchSchema !== false &&
|
||||||
.then(this.initInterval);
|
(!(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))) {
|
if (
|
||||||
store
|
api &&
|
||||||
.fetchInitData(api, store.data)
|
initFetch !== false &&
|
||||||
.then(this.initInterval);
|
(!(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 props = this.props;
|
||||||
const store = props.store;
|
const store = props.store;
|
||||||
|
|
||||||
isApiOutdated(prevProps.api, props.api, prevProps.data, props.data) && store
|
isApiOutdated(prevProps.api, props.api, prevProps.data, props.data) &&
|
||||||
.fetchData(props.api as Api, store.data)
|
store.fetchData(props.api as Api, store.data).then(this.initInterval);
|
||||||
.then(this.initInterval);
|
|
||||||
|
|
||||||
isApiOutdated(prevProps.schemaApi, props.schemaApi, prevProps.data, props.data) && store
|
isApiOutdated(prevProps.schemaApi, props.schemaApi, prevProps.data, props.data) &&
|
||||||
.fetchSchema(props.schemaApi as Api, store.data)
|
store.fetchSchema(props.schemaApi as Api, store.data).then(this.initInterval);
|
||||||
.then(this.initInterval);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
@ -94,46 +76,40 @@ export default class Service extends React.Component<ServiceProps> {
|
||||||
}
|
}
|
||||||
|
|
||||||
initInterval(value: any) {
|
initInterval(value: any) {
|
||||||
const {
|
const {interval, silentPolling, stopAutoRefreshWhen, data} = this.props;
|
||||||
interval,
|
|
||||||
silentPolling,
|
|
||||||
stopAutoRefreshWhen,
|
|
||||||
data
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
interval
|
interval &&
|
||||||
&& this.mounted
|
this.mounted &&
|
||||||
&& (!stopAutoRefreshWhen || !evalExpression(stopAutoRefreshWhen, data))
|
(!stopAutoRefreshWhen || !evalExpression(stopAutoRefreshWhen, data)) &&
|
||||||
&& (this.timer = setTimeout(silentPolling ? this.silentReload : this.reload, Math.max(interval, 3000)));
|
(this.timer = setTimeout(silentPolling ? this.silentReload : this.reload, Math.max(interval, 3000)));
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
reload(subpath?: string, query?: any, ctx?: RendererData, silent?: boolean) {
|
reload(subpath?: string, query?: any, ctx?: RendererData, silent?: boolean) {
|
||||||
|
|
||||||
if (query) {
|
if (query) {
|
||||||
return this.receive(query);
|
return this.receive(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {schemaApi, fetchSchema, api, fetch, store} = this.props;
|
||||||
schemaApi,
|
|
||||||
fetchSchema,
|
|
||||||
api,
|
|
||||||
fetch,
|
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
clearTimeout(this.timer);
|
clearTimeout(this.timer);
|
||||||
|
|
||||||
if (schemaApi && fetchSchema !== false && (!(schemaApi as ApiObject).sendOn || evalExpression((schemaApi as ApiObject).sendOn as string, store.data))) {
|
if (
|
||||||
store
|
schemaApi &&
|
||||||
.fetchSchema(schemaApi, store.data)
|
fetchSchema !== false &&
|
||||||
.then(this.initInterval);
|
(!(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
|
store
|
||||||
.fetchData(api, store.data, {
|
.fetchData(api, store.data, {
|
||||||
silent
|
silent,
|
||||||
})
|
})
|
||||||
.then(this.initInterval);
|
.then(this.initInterval);
|
||||||
}
|
}
|
||||||
|
@ -143,66 +119,61 @@ export default class Service extends React.Component<ServiceProps> {
|
||||||
this.reload(target, query, undefined, true);
|
this.reload(target, query, undefined, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
receive(values:object) {
|
receive(values: object) {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
store.updateData(values);
|
store.updateData(values);
|
||||||
this.reload();
|
this.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleQuery(query:any) {
|
handleQuery(query: any) {
|
||||||
this.receive(query);
|
this.receive(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderBody() {
|
renderBody() {
|
||||||
const {
|
const {render, store, body: schema, classnames: cx} = this.props;
|
||||||
render,
|
|
||||||
store,
|
|
||||||
body: schema,
|
|
||||||
classnames: cx
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cx("Service-body")}>
|
<div className={cx('Service-body')}>
|
||||||
{render('body', store.schema || schema, {
|
{
|
||||||
key: store.schemaKey || 'body',
|
render('body', store.schema || schema, {
|
||||||
onQuery: this.handleQuery
|
key: store.schemaKey || 'body',
|
||||||
}) as JSX.Element}
|
onQuery: this.handleQuery,
|
||||||
|
}) as JSX.Element
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {className, store, render, classPrefix: ns, classnames: cx} = this.props;
|
||||||
className,
|
|
||||||
store,
|
|
||||||
render,
|
|
||||||
classPrefix: ns,
|
|
||||||
classnames: cx
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={cx(`${ns}Service`, className)}>
|
||||||
className={cx(`${ns}Service`, className)}
|
|
||||||
>
|
|
||||||
{store.error ? (
|
{store.error ? (
|
||||||
<div className={cx(`Alert Alert--danger`)}>
|
<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}
|
{store.msg}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{this.renderBody()}
|
{this.renderBody()}
|
||||||
|
|
||||||
{store.loading ? render('info', {
|
{store.loading
|
||||||
type: 'spinner',
|
? render(
|
||||||
overlay: true
|
'info',
|
||||||
}, {
|
{
|
||||||
key: 'info',
|
type: 'spinner',
|
||||||
size: 'lg',
|
overlay: true,
|
||||||
}) : null}
|
},
|
||||||
|
{
|
||||||
|
key: 'info',
|
||||||
|
size: 'lg',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
: null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -211,7 +182,7 @@ export default class Service extends React.Component<ServiceProps> {
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)service$/,
|
test: /(^|\/)service$/,
|
||||||
storeType: ServiceStore.name,
|
storeType: ServiceStore.name,
|
||||||
name: 'service'
|
name: 'service',
|
||||||
})
|
})
|
||||||
export class ServiceRenderer extends Service {
|
export class ServiceRenderer extends Service {
|
||||||
static contextType = ScopedContext;
|
static contextType = ScopedContext;
|
||||||
|
@ -227,4 +198,4 @@ export class ServiceRenderer extends Service {
|
||||||
const scoped = this.context as IScopedContext;
|
const scoped = this.context as IScopedContext;
|
||||||
scoped.unRegisterComponent(this);
|
scoped.unRegisterComponent(this);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import Spinner from "../components/Spinner";
|
import Spinner from '../components/Spinner';
|
||||||
import { Renderer } from "../factory";
|
import {Renderer} from '../factory';
|
||||||
|
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)spinner$/,
|
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 * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import {ServiceStore, IServiceStore} from '../store/service';
|
import {ServiceStore, IServiceStore} from '../store/service';
|
||||||
import {
|
import {Api, SchemaNode, PlainObject} from '../types';
|
||||||
Api,
|
import {filter} from '../utils/tpl';
|
||||||
SchemaNode,
|
|
||||||
PlainObject
|
|
||||||
} from '../types';
|
|
||||||
import {
|
|
||||||
filter
|
|
||||||
} from '../utils/tpl';
|
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
|
|
||||||
export interface StatusProps extends RendererProps {
|
export interface StatusProps extends RendererProps {
|
||||||
|
@ -21,11 +12,11 @@ export interface StatusProps extends RendererProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class StatusField extends React.Component<StatusProps, object> {
|
export class StatusField extends React.Component<StatusProps, object> {
|
||||||
static defaultProps:Partial<StatusProps> = {
|
static defaultProps: Partial<StatusProps> = {
|
||||||
placeholder: '-',
|
placeholder: '-',
|
||||||
map: {
|
map: {
|
||||||
0: 'fa fa-times text-danger',
|
0: 'fa fa-times text-danger',
|
||||||
1: 'fa fa-check text-success'
|
1: 'fa fa-check text-success',
|
||||||
},
|
},
|
||||||
labelMap: {
|
labelMap: {
|
||||||
// 0: '失败',
|
// 0: '失败',
|
||||||
|
@ -34,19 +25,12 @@ export class StatusField extends React.Component<StatusProps, object> {
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {className, placeholder, map, labelMap, classnames: cx, data} = this.props;
|
||||||
className,
|
|
||||||
placeholder,
|
|
||||||
map,
|
|
||||||
labelMap,
|
|
||||||
classnames: cx,
|
|
||||||
data
|
|
||||||
} = this.props;
|
|
||||||
let value = this.props.value;
|
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>;
|
||||||
let wrapClassName:string = '';
|
let wrapClassName: string = '';
|
||||||
|
|
||||||
if (value !== undefined && value !== "" && map) {
|
if (value !== undefined && value !== '' && map) {
|
||||||
if (typeof value === 'boolean') {
|
if (typeof value === 'boolean') {
|
||||||
value = value ? 1 : 0;
|
value = value ? 1 : 0;
|
||||||
} else if (/^\d+$/.test(value)) {
|
} else if (/^\d+$/.test(value)) {
|
||||||
|
@ -54,23 +38,24 @@ export class StatusField extends React.Component<StatusProps, object> {
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapClassName = `StatusField--${value}`;
|
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]) {
|
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 (
|
return <span className={cx('StatusField', wrapClassName, className)}>{viewValue}</span>;
|
||||||
<span className={cx('StatusField', wrapClassName, className)}>
|
|
||||||
{viewValue}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)status$/,
|
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 * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import {ServiceStore, IServiceStore} from '../store/service';
|
import {ServiceStore, IServiceStore} from '../store/service';
|
||||||
import {
|
import {Api, SchemaNode, PlainObject} from '../types';
|
||||||
Api,
|
import {filter} from '../utils/tpl';
|
||||||
SchemaNode,
|
|
||||||
PlainObject
|
|
||||||
} from '../types';
|
|
||||||
import {
|
|
||||||
filter
|
|
||||||
} from '../utils/tpl';
|
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
import Switch from '../components/Switch';
|
import Switch from '../components/Switch';
|
||||||
|
|
||||||
|
@ -24,34 +15,32 @@ export interface SwitchProps extends RendererProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SwitchField extends React.Component<SwitchProps, object> {
|
export class SwitchField extends React.Component<SwitchProps, object> {
|
||||||
static defaultProps:Partial<SwitchProps> = {
|
static defaultProps: Partial<SwitchProps> = {
|
||||||
placeholder: '-',
|
placeholder: '-',
|
||||||
trueValue: true,
|
trueValue: true,
|
||||||
falseValue: false,
|
falseValue: false,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
saveImmediately: false
|
saveImmediately: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props:SwitchProps) {
|
constructor(props: SwitchProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.handleChange = this.handleChange.bind(this);
|
this.handleChange = this.handleChange.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange(checked:boolean) {
|
handleChange(checked: boolean) {
|
||||||
const {
|
const {onQuickChange, name, trueValue, falseValue, saveImmediately, readOnly, disabled} = this.props;
|
||||||
onQuickChange,
|
|
||||||
name,
|
|
||||||
trueValue,
|
|
||||||
falseValue,
|
|
||||||
saveImmediately,
|
|
||||||
readOnly,
|
|
||||||
disabled
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
onQuickChange && !readOnly && !disabled && onQuickChange({
|
onQuickChange &&
|
||||||
[name as string]: checked ? trueValue : falseValue
|
!readOnly &&
|
||||||
}, saveImmediately);
|
!disabled &&
|
||||||
|
onQuickChange(
|
||||||
|
{
|
||||||
|
[name as string]: checked ? trueValue : falseValue,
|
||||||
|
},
|
||||||
|
saveImmediately
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -64,19 +53,19 @@ export class SwitchField extends React.Component<SwitchProps, object> {
|
||||||
falseValue,
|
falseValue,
|
||||||
onQuickChange,
|
onQuickChange,
|
||||||
option,
|
option,
|
||||||
disabled
|
disabled,
|
||||||
} = this.props;
|
} = 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;
|
let showOption = false;
|
||||||
|
|
||||||
if (value == trueValue || value == falseValue) {
|
if (value == trueValue || value == falseValue) {
|
||||||
showOption = !!option;
|
showOption = !!option;
|
||||||
viewValue = (
|
viewValue = (
|
||||||
<Switch
|
<Switch
|
||||||
inline
|
inline
|
||||||
classPrefix={ns}
|
classPrefix={ns}
|
||||||
checked={value == trueValue}
|
checked={value == trueValue}
|
||||||
onChange={this.handleChange}
|
onChange={this.handleChange}
|
||||||
disabled={disabled || !onQuickChange}
|
disabled={disabled || !onQuickChange}
|
||||||
/>
|
/>
|
||||||
|
@ -94,6 +83,6 @@ export class SwitchField extends React.Component<SwitchProps, object> {
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)switch$/,
|
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 * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
import {ServiceStore, IServiceStore} from '../store/service';
|
||||||
RendererProps
|
import {Api, SchemaNode, Schema, Action} from '../types';
|
||||||
} from '../factory';
|
import {filter, evalExpression} from '../utils/tpl';
|
||||||
import { ServiceStore, IServiceStore } from '../store/service';
|
import {Tabs as BsTabs, TabContainer, TabContent, TabPane, NavItem, Nav, Tab} from 'react-bootstrap';
|
||||||
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 cx = require('classnames');
|
||||||
import find = require('lodash/find');
|
import find = require('lodash/find');
|
||||||
import { isVisible } from '../utils/helper';
|
import {isVisible} from '../utils/helper';
|
||||||
import findIndex = require('lodash/findIndex');
|
import findIndex = require('lodash/findIndex');
|
||||||
|
|
||||||
export type TabProps = Schema & {
|
export type TabProps = Schema & {
|
||||||
|
@ -50,12 +31,11 @@ export interface TabsState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Tabs extends React.Component<TabsProps, TabsState> {
|
export default class Tabs extends React.Component<TabsProps, TabsState> {
|
||||||
|
|
||||||
static defaultProps: Partial<TabsProps> = {
|
static defaultProps: Partial<TabsProps> = {
|
||||||
className: '',
|
className: '',
|
||||||
mode: '',
|
mode: '',
|
||||||
mountOnEnter: true,
|
mountOnEnter: true,
|
||||||
unmountOnExit: false
|
unmountOnExit: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
id = Date.now() + '';
|
id = Date.now() + '';
|
||||||
|
@ -71,13 +51,13 @@ export default class Tabs extends React.Component<TabsProps, TabsState> {
|
||||||
} else if (location && Array.isArray(tabs)) {
|
} else if (location && Array.isArray(tabs)) {
|
||||||
const hash = location.hash.substring(1);
|
const hash = location.hash.substring(1);
|
||||||
const tab: TabProps = find(tabs, tab => tab.hash === hash) as TabProps;
|
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 = {
|
this.state = {
|
||||||
prevKey: undefined,
|
prevKey: undefined,
|
||||||
activeKey: activeKey
|
activeKey: activeKey,
|
||||||
}
|
};
|
||||||
|
|
||||||
this.handleSelect = this.handleSelect.bind(this);
|
this.handleSelect = this.handleSelect.bind(this);
|
||||||
this.currentIndex = this.currentIndex.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) {
|
if (tab && tab.hash && tab.hash !== this.state.activeKey) {
|
||||||
this.setState({
|
this.setState({
|
||||||
activeKey: tab.hash,
|
activeKey: tab.hash,
|
||||||
prevKey: this.state.activeKey
|
prevKey: this.state.activeKey,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (props.tabs !== nextProps.tabs) {
|
} else if (props.tabs !== nextProps.tabs) {
|
||||||
|
@ -111,53 +91,54 @@ export default class Tabs extends React.Component<TabsProps, TabsState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tab) {
|
if (tab) {
|
||||||
activeKey = tab.hash
|
activeKey = tab.hash;
|
||||||
} else if (!nextProps.tabs || !nextProps.tabs.some((item, index) => item.hash ? item.hash === activeKey : index === activeKey)) {
|
} else if (
|
||||||
activeKey = nextProps.tabs && nextProps.tabs[0] && nextProps.tabs[0].hash || 0;
|
!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({
|
this.setState({
|
||||||
prevKey: undefined,
|
prevKey: undefined,
|
||||||
activeKey: activeKey
|
activeKey: activeKey,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSelect(key: any) {
|
handleSelect(key: any) {
|
||||||
const {
|
const {env} = this.props;
|
||||||
env
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
// 是 hash,需要更新到地址栏
|
// 是 hash,需要更新到地址栏
|
||||||
if (typeof key === 'string' && env) {
|
if (typeof key === 'string' && env) {
|
||||||
env.updateLocation(`#${key}`)
|
env.updateLocation(`#${key}`);
|
||||||
} else if (typeof this.state.prevKey === 'string' && env) {
|
} else if (typeof this.state.prevKey === 'string' && env) {
|
||||||
env.updateLocation(`#`);
|
env.updateLocation(`#`);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
activeKey: key,
|
activeKey: key,
|
||||||
prevKey: this.state.activeKey
|
prevKey: this.state.activeKey,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
switchTo(index: number) {
|
switchTo(index: number) {
|
||||||
const {
|
const {tabs} = this.props;
|
||||||
tabs
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
Array.isArray(tabs) && tabs[index] && this.setState({
|
Array.isArray(tabs) &&
|
||||||
activeKey: tabs[index].hash || index
|
tabs[index] &&
|
||||||
})
|
this.setState({
|
||||||
|
activeKey: tabs[index].hash || index,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
currentIndex(): number {
|
currentIndex(): number {
|
||||||
const {
|
const {tabs} = this.props;
|
||||||
tabs
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return Array.isArray(tabs)
|
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;
|
: -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +154,7 @@ export default class Tabs extends React.Component<TabsProps, TabsState> {
|
||||||
render,
|
render,
|
||||||
data,
|
data,
|
||||||
mode: dMode,
|
mode: dMode,
|
||||||
tabsMode
|
tabsMode,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (!Array.isArray(tabs)) {
|
if (!Array.isArray(tabs)) {
|
||||||
|
@ -186,9 +167,13 @@ export default class Tabs extends React.Component<TabsProps, TabsState> {
|
||||||
return (
|
return (
|
||||||
<TabContainer
|
<TabContainer
|
||||||
id={this.id}
|
id={this.id}
|
||||||
className={cx(`Tabs`, {
|
className={cx(
|
||||||
[`Tabs--${mode}`]: mode
|
`Tabs`,
|
||||||
}, className)}
|
{
|
||||||
|
[`Tabs--${mode}`]: mode,
|
||||||
|
},
|
||||||
|
className
|
||||||
|
)}
|
||||||
activeKey={this.state.activeKey}
|
activeKey={this.state.activeKey}
|
||||||
onSelect={this.handleSelect}
|
onSelect={this.handleSelect}
|
||||||
>
|
>
|
||||||
|
@ -199,9 +184,15 @@ export default class Tabs extends React.Component<TabsProps, TabsState> {
|
||||||
className={cx('Tabs-link')}
|
className={cx('Tabs-link')}
|
||||||
key={index}
|
key={index}
|
||||||
eventKey={tab.hash || 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>
|
</NavItem>
|
||||||
))}
|
))}
|
||||||
</Nav>
|
</Nav>
|
||||||
|
@ -218,7 +209,9 @@ export default class Tabs extends React.Component<TabsProps, TabsState> {
|
||||||
mountOnEnter={mountOnEnter}
|
mountOnEnter={mountOnEnter}
|
||||||
unmountOnExit={typeof tab.reload === 'boolean' ? tab.reload : tab.unmountOnExit}
|
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>
|
</TabPane>
|
||||||
))}
|
))}
|
||||||
</TabContent>
|
</TabContent>
|
||||||
|
@ -230,6 +223,6 @@ export default class Tabs extends React.Component<TabsProps, TabsState> {
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)tabs$/,
|
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 * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
import {ServiceStore, IServiceStore} from '../store/service';
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import { ServiceStore, IServiceStore } from '../store/service';
|
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
import getExprProperties from '../utils/filter-schema';
|
import getExprProperties from '../utils/filter-schema';
|
||||||
import { Api, Payload } from '../types';
|
import {Api, Payload} from '../types';
|
||||||
import update = require('react-addons-update');
|
import update = require('react-addons-update');
|
||||||
|
|
||||||
export interface TaskProps extends RendererProps {
|
export interface TaskProps extends RendererProps {
|
||||||
|
@ -15,7 +12,7 @@ export interface TaskProps extends RendererProps {
|
||||||
checkApi: Api;
|
checkApi: Api;
|
||||||
submitApi: Api;
|
submitApi: Api;
|
||||||
reSubmitApi: Api;
|
reSubmitApi: Api;
|
||||||
|
|
||||||
tableClassName?: string;
|
tableClassName?: string;
|
||||||
taskNameLabel?: string;
|
taskNameLabel?: string;
|
||||||
operationLabel?: string;
|
operationLabel?: string;
|
||||||
|
@ -40,12 +37,12 @@ export interface TaskItem {
|
||||||
label?: string;
|
label?: string;
|
||||||
key?: string;
|
key?: string;
|
||||||
remark?: string;
|
remark?: string;
|
||||||
status?:any;
|
status?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TaskState {
|
export interface TaskState {
|
||||||
error?: string;
|
error?: string;
|
||||||
items: Array<TaskItem>
|
items: Array<TaskItem>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Task extends React.Component<TaskProps, TaskState> {
|
export default class Task extends React.Component<TaskProps, TaskState> {
|
||||||
|
@ -68,141 +65,150 @@ export default class Task extends React.Component<TaskProps, TaskState> {
|
||||||
errorStatusCode: 3,
|
errorStatusCode: 3,
|
||||||
finishStatusCode: 4,
|
finishStatusCode: 4,
|
||||||
canRetryStatusCode: 5,
|
canRetryStatusCode: 5,
|
||||||
interval: 3000
|
interval: 3000,
|
||||||
};
|
};
|
||||||
|
|
||||||
timer:any;
|
timer: any;
|
||||||
|
|
||||||
constructor(props:TaskProps) {
|
constructor(props: TaskProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
items: props.items ? props.items.concat() : []
|
items: props.items ? props.items.concat() : [],
|
||||||
};
|
};
|
||||||
|
|
||||||
this.handleLoaded = this.handleLoaded.bind(this);
|
this.handleLoaded = this.handleLoaded.bind(this);
|
||||||
this.tick = this.tick.bind(this);
|
this.tick = this.tick.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps:TaskProps) {
|
componentWillReceiveProps(nextProps: TaskProps) {
|
||||||
const props = this.props;
|
const props = this.props;
|
||||||
|
|
||||||
if (props.items !== nextProps.items) {
|
if (props.items !== nextProps.items) {
|
||||||
this.setState({
|
this.setState({
|
||||||
items: nextProps.items ? nextProps.items.concat() : []
|
items: nextProps.items ? nextProps.items.concat() : [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.tick(!!this.props.checkApi);
|
this.tick(!!this.props.checkApi);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
clearTimeout(this.timer);
|
clearTimeout(this.timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
tick(force = false) {
|
tick(force = false) {
|
||||||
const {
|
const {loadingStatusCode, data, interval, checkApi, env} = this.props;
|
||||||
loadingStatusCode,
|
|
||||||
data,
|
|
||||||
interval,
|
|
||||||
checkApi,
|
|
||||||
env
|
|
||||||
} = this.props;
|
|
||||||
const items = this.state.items;
|
const items = this.state.items;
|
||||||
|
|
||||||
// 如果每个 task 都完成了, 则不需要取查看状态.
|
// 如果每个 task 都完成了, 则不需要取查看状态.
|
||||||
if (!force && !items.some(item => item.status === loadingStatusCode)) {
|
if (!force && !items.some(item => item.status === loadingStatusCode)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interval && !checkApi) {
|
if (interval && !checkApi) {
|
||||||
return alert('checkApi 没有设置, 不能及时获取任务状态');
|
return alert('checkApi 没有设置, 不能及时获取任务状态');
|
||||||
}
|
}
|
||||||
|
|
||||||
env && env.fetcher(checkApi, data)
|
env &&
|
||||||
.then(this.handleLoaded)
|
env
|
||||||
.catch(e => this.setState({ error: e }))
|
.fetcher(checkApi, data)
|
||||||
|
.then(this.handleLoaded)
|
||||||
|
.catch(e => this.setState({error: e}));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLoaded(ret:Payload) {
|
handleLoaded(ret: Payload) {
|
||||||
if (!Array.isArray(ret.data)) {
|
if (!Array.isArray(ret.data)) {
|
||||||
return alert('返回格式不正确, 期望 response.data 为数组, 包含每个 task 的状态信息');
|
return alert('返回格式不正确, 期望 response.data 为数组, 包含每个 task 的状态信息');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
items: ret.data
|
items: ret.data,
|
||||||
});
|
});
|
||||||
|
|
||||||
const interval = this.props.interval;
|
const interval = this.props.interval;
|
||||||
clearTimeout(this.timer);
|
clearTimeout(this.timer);
|
||||||
this.timer = setTimeout(this.tick, interval);
|
this.timer = setTimeout(this.tick, interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
submitTask(item:TaskItem, index:number, retry = false) {
|
submitTask(item: TaskItem, index: number, retry = false) {
|
||||||
const {
|
const {submitApi, reSubmitApi, loadingStatusCode, errorStatusCode, data, env} = this.props;
|
||||||
submitApi,
|
|
||||||
reSubmitApi,
|
|
||||||
loadingStatusCode,
|
|
||||||
errorStatusCode,
|
|
||||||
data,
|
|
||||||
env
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (!retry && !submitApi) {
|
if (!retry && !submitApi) {
|
||||||
return alert('submitApi 没有配置');
|
return alert('submitApi 没有配置');
|
||||||
} else if (retry && !reSubmitApi) {
|
} else if (retry && !reSubmitApi) {
|
||||||
return alert('reSubmitApi 没有配置');
|
return alert('reSubmitApi 没有配置');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState(update(this.state, {
|
this.setState(
|
||||||
items: {
|
update(this.state, {
|
||||||
$splice: [
|
items: {
|
||||||
[index, 1, {
|
$splice: [
|
||||||
...item,
|
[
|
||||||
status: loadingStatusCode
|
index,
|
||||||
}]
|
1,
|
||||||
]
|
{
|
||||||
}
|
...item,
|
||||||
} as any));
|
status: loadingStatusCode,
|
||||||
|
},
|
||||||
env && env.fetcher(retry ? reSubmitApi : submitApi, {
|
],
|
||||||
...data,
|
],
|
||||||
...item
|
},
|
||||||
})
|
} as any)
|
||||||
.then((ret:Payload) => {
|
);
|
||||||
if (ret && ret.data) {
|
|
||||||
if (Array.isArray(ret.data)) {
|
env &&
|
||||||
this.handleLoaded(ret);
|
env
|
||||||
} else {
|
.fetcher(retry ? reSubmitApi : submitApi, {
|
||||||
const items = this.state.items.map(item => item.key === ret.data.key ? {
|
...data,
|
||||||
...item,
|
...item,
|
||||||
...ret.data
|
})
|
||||||
} : item);
|
.then((ret: Payload) => {
|
||||||
this.handleLoaded({
|
if (ret && ret.data) {
|
||||||
...ret,
|
if (Array.isArray(ret.data)) {
|
||||||
data: items
|
this.handleLoaded(ret);
|
||||||
});
|
} else {
|
||||||
}
|
const items = this.state.items.map(item =>
|
||||||
return;
|
item.key === ret.data.key
|
||||||
}
|
? {
|
||||||
|
...item,
|
||||||
clearTimeout(this.timer);
|
...ret.data,
|
||||||
this.timer = setTimeout(this.tick, 4);
|
}
|
||||||
})
|
: item
|
||||||
.catch(e => this.setState(update(this.state, {
|
);
|
||||||
items: {
|
this.handleLoaded({
|
||||||
$splice: [
|
...ret,
|
||||||
[index, 1, {
|
data: items,
|
||||||
...item,
|
});
|
||||||
status: errorStatusCode,
|
}
|
||||||
remark: e.message || e
|
return;
|
||||||
}]
|
}
|
||||||
]
|
|
||||||
}
|
clearTimeout(this.timer);
|
||||||
} as any)))
|
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() {
|
render() {
|
||||||
const {
|
const {
|
||||||
className,
|
className,
|
||||||
|
@ -220,11 +226,11 @@ export default class Task extends React.Component<TaskProps, TaskState> {
|
||||||
readyStatusCode,
|
readyStatusCode,
|
||||||
loadingStatusCode,
|
loadingStatusCode,
|
||||||
canRetryStatusCode,
|
canRetryStatusCode,
|
||||||
render
|
render,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const items = this.state.items;
|
const items = this.state.items;
|
||||||
const error = this.state.error;
|
const error = this.state.error;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<table className={tableClassName}>
|
<table className={tableClassName}>
|
||||||
|
@ -237,36 +243,48 @@ export default class Task extends React.Component<TaskProps, TaskState> {
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{error ? (
|
{error ? (
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan={4}><div className="text-danger">{error}</div></td>
|
<td colSpan={4}>
|
||||||
</tr>
|
<div className="text-danger">{error}</div>
|
||||||
) : items.map((item, key) => (
|
</td>
|
||||||
<tr key={key}>
|
</tr>
|
||||||
<td>{item.label}</td>
|
) : (
|
||||||
<td>
|
items.map((item, key) => (
|
||||||
{item.status == loadingStatusCode ? (
|
<tr key={key}>
|
||||||
<i className="fa fa-spinner fa-spin fa-2x fa-fw" />
|
<td>{item.label}</td>
|
||||||
) : item.status == canRetryStatusCode ? (
|
<td>
|
||||||
<a
|
{item.status == loadingStatusCode ? (
|
||||||
onClick={() => this.submitTask(item, key, true)}
|
<i className="fa fa-spinner fa-spin fa-2x fa-fw" />
|
||||||
className={cx('btn', retryBtnClassName || btnClassName)}
|
) : item.status == canRetryStatusCode ? (
|
||||||
>
|
<a
|
||||||
{retryBtnText || btnText}
|
onClick={() => this.submitTask(item, key, true)}
|
||||||
</a>
|
className={cx('btn', retryBtnClassName || btnClassName)}
|
||||||
) : (
|
>
|
||||||
<a
|
{retryBtnText || btnText}
|
||||||
onClick={() => this.submitTask(item, key)}
|
</a>
|
||||||
className={cx('btn', btnClassName, { disabled: item.status !== readyStatusCode })}
|
) : (
|
||||||
>
|
<a
|
||||||
{btnText}
|
onClick={() => this.submitTask(item, key)}
|
||||||
</a>
|
className={cx('btn', btnClassName, {
|
||||||
)}
|
disabled: item.status !== readyStatusCode,
|
||||||
</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>
|
{btnText}
|
||||||
</tr>
|
</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>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
@ -276,6 +294,6 @@ export default class Task extends React.Component<TaskProps, TaskState> {
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)tasks$/,
|
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 * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
import {filter} from '../utils/tpl';
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import {
|
|
||||||
filter
|
|
||||||
} from '../utils/tpl';
|
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
import { anyChanged } from '../utils/helper';
|
import {anyChanged} from '../utils/helper';
|
||||||
import { escapeHtml } from '../utils/tpl-builtin';
|
import {escapeHtml} from '../utils/tpl-builtin';
|
||||||
|
|
||||||
export interface TplProps extends RendererProps {
|
export interface TplProps extends RendererProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
@ -22,48 +17,32 @@ export interface TplProps extends RendererProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Tpl extends React.Component<TplProps, object> {
|
export class Tpl extends React.Component<TplProps, object> {
|
||||||
static defaultProps:Partial<TplProps> = {
|
static defaultProps: Partial<TplProps> = {
|
||||||
inline: true,
|
inline: true,
|
||||||
placeholder: '',
|
placeholder: '',
|
||||||
value: ''
|
value: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
dom:any;
|
dom: any;
|
||||||
|
|
||||||
constructor(props:TplProps) {
|
constructor(props: TplProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.htmlRef = this.htmlRef.bind(this);
|
this.htmlRef = this.htmlRef.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps: TplProps) {
|
||||||
componentDidUpdate(prevProps:TplProps) {
|
if (anyChanged(['data', 'tpl', 'html', 'text', 'raw', 'value'], this.props, prevProps)) {
|
||||||
if (anyChanged([
|
|
||||||
'data',
|
|
||||||
'tpl',
|
|
||||||
'html',
|
|
||||||
'text',
|
|
||||||
'raw',
|
|
||||||
'value'
|
|
||||||
], this.props, prevProps)) {
|
|
||||||
this._render();
|
this._render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
htmlRef(dom:any) {
|
htmlRef(dom: any) {
|
||||||
this.dom = dom;
|
this.dom = dom;
|
||||||
this._render();
|
this._render();
|
||||||
}
|
}
|
||||||
|
|
||||||
getContent() {
|
getContent() {
|
||||||
const {
|
const {tpl, html, text, raw, value, data, placeholder} = this.props;
|
||||||
tpl,
|
|
||||||
html,
|
|
||||||
text,
|
|
||||||
raw,
|
|
||||||
value,
|
|
||||||
data,
|
|
||||||
placeholder
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (raw) {
|
if (raw) {
|
||||||
return raw;
|
return raw;
|
||||||
|
@ -74,7 +53,11 @@ export class Tpl extends React.Component<TplProps, object> {
|
||||||
} else if (text) {
|
} else if (text) {
|
||||||
return escapeHtml(filter(text, data));
|
return escapeHtml(filter(text, data));
|
||||||
} else {
|
} 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() {
|
render() {
|
||||||
const {
|
const {className, wrapperComponent, inline, classnames: cx} = this.props;
|
||||||
className,
|
|
||||||
wrapperComponent,
|
|
||||||
inline,
|
|
||||||
classnames: cx
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const Component = wrapperComponent || (inline ? 'span' : 'div');
|
const Component = wrapperComponent || (inline ? 'span' : 'div');
|
||||||
|
|
||||||
return (
|
return <Component children={this.getContent()} ref={this.htmlRef} className={cx('TplField', className)} />;
|
||||||
<Component children={this.getContent()} ref={this.htmlRef} className={cx('TplField', className)} />
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)(?:tpl|html)$/,
|
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 * as React from 'react';
|
||||||
import { Renderer, RendererProps } from "../factory";
|
import {Renderer, RendererProps} from '../factory';
|
||||||
import { Api, SchemaNode, Schema, Action } from "../types";
|
import {Api, SchemaNode, Schema, Action} from '../types';
|
||||||
import * as cx from "classnames";
|
import * as cx from 'classnames';
|
||||||
|
|
||||||
|
|
||||||
export type Row = Schema & {
|
export type Row = Schema & {
|
||||||
rowClassName?: string;
|
rowClassName?: string;
|
||||||
|
@ -15,45 +14,37 @@ export interface HBoxProps extends RendererProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class VBox extends React.Component<HBoxProps, object> {
|
export default class VBox extends React.Component<HBoxProps, object> {
|
||||||
static propsList: Array<string> = ["rows"];
|
static propsList: Array<string> = ['rows'];
|
||||||
|
|
||||||
static defaultProps: Partial<HBoxProps> = {};
|
static defaultProps: Partial<HBoxProps> = {};
|
||||||
|
|
||||||
renderChild(region:string, node:Schema) {
|
renderChild(region: string, node: Schema) {
|
||||||
const {
|
const {render} = this.props;
|
||||||
render
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return render(region, node);
|
return render(region, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderCell(row: Row, key: any) {
|
renderCell(row: Row, key: any) {
|
||||||
const {
|
const {classPrefix: ns} = this.props;
|
||||||
classPrefix: ns
|
|
||||||
} = this.props;
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={cx(`${ns}Vbox-cell`, (row as Row).cellClassName)}>
|
||||||
className={cx(`${ns}Vbox-cell`, (row as Row).cellClassName)}
|
|
||||||
>
|
|
||||||
{this.renderChild(`row/${key}`, row)}
|
{this.renderChild(`row/${key}`, row)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {className, rows, classPrefix: ns} = this.props;
|
||||||
className,
|
|
||||||
rows,
|
|
||||||
classPrefix: ns
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cx(`${ns}Vbox`, className)}>
|
<div className={cx(`${ns}Vbox`, className)}>
|
||||||
{Array.isArray(rows) ? rows.map((row, key) =>
|
{Array.isArray(rows)
|
||||||
<div className={cx('row-row', (row as Row).rowClassName)} key={key}>
|
? rows.map((row, key) => (
|
||||||
{this.renderCell(row, key)}
|
<div className={cx('row-row', (row as Row).rowClassName)} key={key}>
|
||||||
</div>
|
{this.renderCell(row, key)}
|
||||||
) : null}
|
</div>
|
||||||
|
))
|
||||||
|
: null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -61,6 +52,6 @@ export default class VBox extends React.Component<HBoxProps, object> {
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)vbox$/,
|
test: /(^|\/)vbox$/,
|
||||||
name: 'vbox'
|
name: 'vbox',
|
||||||
})
|
})
|
||||||
export class VBoxRenderer extends VBox {}
|
export class VBoxRenderer extends VBox {}
|
||||||
|
|
|
@ -5,19 +5,20 @@
|
||||||
/* eslint fecs-indent: [0, "space", 2, 2] */
|
/* eslint fecs-indent: [0, "space", 2, 2] */
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Player, Shortcut, BigPlayButton } from 'video-react';
|
import {Player, Shortcut, BigPlayButton} from 'video-react';
|
||||||
import { padArr } from '../utils/helper';
|
import {padArr} from '../utils/helper';
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
import {resolveVariable} from '../utils/tpl-builtin';
|
||||||
RendererProps
|
import {filter} from '../utils/tpl';
|
||||||
} from '../factory';
|
|
||||||
import { resolveVariable } from '../utils/tpl-builtin';
|
|
||||||
import { filter } from '../utils/tpl';
|
|
||||||
// import css
|
// 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 {
|
export interface FlvSourceProps {
|
||||||
src?: string;
|
src?: string;
|
||||||
|
@ -36,18 +37,21 @@ let currentPlaying: any = null;
|
||||||
export class FlvSource extends React.Component<FlvSourceProps, any> {
|
export class FlvSource extends React.Component<FlvSourceProps, any> {
|
||||||
flvPlayer: any;
|
flvPlayer: any;
|
||||||
componentDidMount() {
|
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) => {
|
(require as any)(['flv.js'], (flvjs: any) => {
|
||||||
// load hls video source base on hls.js
|
// load hls video source base on hls.js
|
||||||
if (flvjs.isSupported()) {
|
if (flvjs.isSupported()) {
|
||||||
video = video || manager.video && manager.video.video;
|
video = video || (manager.video && manager.video.video);
|
||||||
|
|
||||||
let flvPlayer = flvjs.createPlayer({
|
let flvPlayer = flvjs.createPlayer(
|
||||||
type: 'flv',
|
{
|
||||||
url: src,
|
type: 'flv',
|
||||||
isLive: isLive
|
url: src,
|
||||||
}, config);
|
isLive: isLive,
|
||||||
|
},
|
||||||
|
config
|
||||||
|
);
|
||||||
flvPlayer.attachMediaElement(video);
|
flvPlayer.attachMediaElement(video);
|
||||||
this.flvPlayer = flvPlayer;
|
this.flvPlayer = flvPlayer;
|
||||||
let loaded = false;
|
let loaded = false;
|
||||||
|
@ -99,12 +103,7 @@ export class FlvSource extends React.Component<FlvSourceProps, any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return <source src={this.props.src} type={this.props.type || 'video/x-flv'} />;
|
||||||
<source
|
|
||||||
src={this.props.src}
|
|
||||||
type={this.props.type || 'video/x-flv'}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,20 +117,20 @@ export interface HlsSourceProps {
|
||||||
autoPlay?: boolean;
|
autoPlay?: boolean;
|
||||||
actions?: any;
|
actions?: any;
|
||||||
order?: number;
|
order?: number;
|
||||||
};
|
}
|
||||||
export class HlsSource extends React.Component<HlsSourceProps, any> {
|
export class HlsSource extends React.Component<HlsSourceProps, any> {
|
||||||
hls: any;
|
hls: any;
|
||||||
componentDidMount() {
|
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) => {
|
(require as any)(['hls.js'], (Hls: any) => {
|
||||||
// load hls video source base on hls.js
|
// load hls video source base on hls.js
|
||||||
if (Hls.isSupported()) {
|
if (Hls.isSupported()) {
|
||||||
video = video || manager.video && manager.video.video;
|
video = video || (manager.video && manager.video.video);
|
||||||
|
|
||||||
let hls = this.hls = new Hls({
|
let hls = (this.hls = new Hls({
|
||||||
autoStartLoad: false
|
autoStartLoad: false,
|
||||||
});
|
}));
|
||||||
hls.attachMedia(video);
|
hls.attachMedia(video);
|
||||||
hls.loadSource(src);
|
hls.loadSource(src);
|
||||||
|
|
||||||
|
@ -167,12 +166,7 @@ export class HlsSource extends React.Component<HlsSourceProps, any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return <source src={this.props.src} type={this.props.type || 'application/x-mpegURL'} />;
|
||||||
<source
|
|
||||||
src={this.props.src}
|
|
||||||
type={this.props.type || 'application/x-mpegURL'}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,14 +181,14 @@ export interface VideoProps extends RendererProps {
|
||||||
export interface VideoState {
|
export interface VideoState {
|
||||||
posterInfo?: any;
|
posterInfo?: any;
|
||||||
videoState?: any;
|
videoState?: any;
|
||||||
};
|
}
|
||||||
|
|
||||||
export default class Video extends React.Component<VideoProps, VideoState> {
|
export default class Video extends React.Component<VideoProps, VideoState> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
columnsCount: 8,
|
columnsCount: 8,
|
||||||
isLive: false,
|
isLive: false,
|
||||||
jumpFrame: true,
|
jumpFrame: true,
|
||||||
aspectRatio: "auto"
|
aspectRatio: 'auto',
|
||||||
};
|
};
|
||||||
|
|
||||||
frameDom: any;
|
frameDom: any;
|
||||||
|
@ -207,7 +201,7 @@ export default class Video extends React.Component<VideoProps, VideoState> {
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
posterInfo: null,
|
posterInfo: null,
|
||||||
videoState: {}
|
videoState: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.frameRef = this.frameRef.bind(this);
|
this.frameRef = this.frameRef.bind(this);
|
||||||
|
@ -223,8 +217,8 @@ export default class Video extends React.Component<VideoProps, VideoState> {
|
||||||
this.setState({
|
this.setState({
|
||||||
posterInfo: {
|
posterInfo: {
|
||||||
width: image.width,
|
width: image.width,
|
||||||
height: image.height
|
height: image.height,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
image = image.onload = null;
|
image = image.onload = null;
|
||||||
};
|
};
|
||||||
|
@ -248,7 +242,7 @@ export default class Video extends React.Component<VideoProps, VideoState> {
|
||||||
|
|
||||||
player.subscribeToStateChange((state: any) => {
|
player.subscribeToStateChange((state: any) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
videoState: state
|
videoState: state,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!state.paused) {
|
if (!state.paused) {
|
||||||
|
@ -259,7 +253,6 @@ export default class Video extends React.Component<VideoProps, VideoState> {
|
||||||
currentPlaying = player;
|
currentPlaying = player;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!this.frameDom || !this.times) {
|
if (!this.frameDom || !this.times) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -268,7 +261,7 @@ export default class Video extends React.Component<VideoProps, VideoState> {
|
||||||
const times = this.times;
|
const times = this.times;
|
||||||
const len = times.length;
|
const len = times.length;
|
||||||
while (index < len) {
|
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;
|
break;
|
||||||
} else if (state.currentTime <= times[index]) {
|
} else if (state.currentTime <= times[index]) {
|
||||||
break;
|
break;
|
||||||
|
@ -284,7 +277,7 @@ export default class Video extends React.Component<VideoProps, VideoState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
moveCursorToIndex(index: number) {
|
moveCursorToIndex(index: number) {
|
||||||
const { classPrefix: ns } = this.props;
|
const {classPrefix: ns} = this.props;
|
||||||
if (!this.frameDom || !this.cursorDom) {
|
if (!this.frameDom || !this.cursorDom) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -295,7 +288,12 @@ export default class Video extends React.Component<VideoProps, VideoState> {
|
||||||
const item = items[index];
|
const item = items[index];
|
||||||
const frameRect = this.frameDom.getBoundingClientRect();
|
const frameRect = this.frameDom.getBoundingClientRect();
|
||||||
const rect = item.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() {
|
renderFrames() {
|
||||||
let {
|
let {frames, framesClassName, columnsCount, data, jumpFrame, classPrefix: ns} = this.props;
|
||||||
frames,
|
|
||||||
framesClassName,
|
|
||||||
columnsCount,
|
|
||||||
data,
|
|
||||||
jumpFrame,
|
|
||||||
classPrefix: ns
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (typeof frames === 'string' && frames[0] === '$') {
|
if (typeof frames === 'string' && frames[0] === '$') {
|
||||||
frames = resolveVariable(frames, data);
|
frames = resolveVariable(frames, data);
|
||||||
|
@ -334,7 +325,7 @@ export default class Video extends React.Component<VideoProps, VideoState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const items: Array<object> = [];
|
const items: Array<object> = [];
|
||||||
const times: Array<number> = this.times = [];
|
const times: Array<number> = (this.times = []);
|
||||||
Object.keys(frames).forEach(time => {
|
Object.keys(frames).forEach(time => {
|
||||||
if (!frames[time]) {
|
if (!frames[time]) {
|
||||||
return;
|
return;
|
||||||
|
@ -344,7 +335,7 @@ export default class Video extends React.Component<VideoProps, VideoState> {
|
||||||
|
|
||||||
items.push({
|
items.push({
|
||||||
time: time,
|
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="pull-in-xxs" key={i}>
|
||||||
<div className={`${ns}Hbox ${ns}Video-frameItem`}>
|
<div className={`${ns}Hbox ${ns}Video-frameItem`}>
|
||||||
{items.map((item, key) => (
|
{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} />
|
<img className="w-full" alt="poster" src={item.src} />
|
||||||
<div className={`${ns}Text--center`}>{item.time}</div>
|
<div className={`${ns}Text--center`}>{item.time}</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{/* 补充空白 */restCount ? blankArray.map((_, index) => (
|
{/* 补充空白 */ restCount
|
||||||
<div className={`${ns}Hbox-col Wrapper--xxs`} key={`blank_${index}`} />
|
? blankArray.map((_, index) => (
|
||||||
)) : null}
|
<div className={`${ns}Hbox-col Wrapper--xxs`} key={`blank_${index}`} />
|
||||||
|
))
|
||||||
|
: null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -398,18 +395,18 @@ export default class Video extends React.Component<VideoProps, VideoState> {
|
||||||
videoType,
|
videoType,
|
||||||
playerClassName,
|
playerClassName,
|
||||||
classPrefix: ns,
|
classPrefix: ns,
|
||||||
aspectRatio
|
aspectRatio,
|
||||||
} = this.props;
|
} = 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;
|
const videoState = this.state.videoState;
|
||||||
let highlight = videoState.duration && minVideoDuration && videoState.duration < minVideoDuration;
|
let highlight = videoState.duration && minVideoDuration && videoState.duration < minVideoDuration;
|
||||||
let src = filter(source, data);
|
let src = filter(source, data);
|
||||||
let sourceNode;
|
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} />;
|
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} />;
|
sourceNode = <HlsSource autoPlay={autoPlay} order={999.0} src={src} />;
|
||||||
} else {
|
} else {
|
||||||
sourceNode = <source src={src} />;
|
sourceNode = <source src={src} />;
|
||||||
|
@ -430,25 +427,21 @@ export default class Video extends React.Component<VideoProps, VideoState> {
|
||||||
<Shortcut disabled />
|
<Shortcut disabled />
|
||||||
</Player>
|
</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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPosterAndPlayer() {
|
renderPosterAndPlayer() {
|
||||||
let {
|
let {poster, data, locals, minPosterDimension, classPrefix: ns} = this.props;
|
||||||
poster,
|
|
||||||
data,
|
|
||||||
locals,
|
|
||||||
minPosterDimension,
|
|
||||||
classPrefix: ns
|
|
||||||
} = this.props;
|
|
||||||
const posterInfo = this.state.posterInfo || {};
|
const posterInfo = this.state.posterInfo || {};
|
||||||
let dimensionClassName = '';
|
let dimensionClassName = '';
|
||||||
|
|
||||||
if (posterInfo && minPosterDimension
|
if (
|
||||||
&& (minPosterDimension.width || minPosterDimension.height)
|
posterInfo &&
|
||||||
&& (minPosterDimension.width > posterInfo.width || minPosterDimension.height > posterInfo.height)
|
minPosterDimension &&
|
||||||
|
(minPosterDimension.width || minPosterDimension.height) &&
|
||||||
|
(minPosterDimension.width > posterInfo.width || minPosterDimension.height > posterInfo.height)
|
||||||
) {
|
) {
|
||||||
dimensionClassName = `${ns}Text--danger`;
|
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`}>
|
||||||
<div className={`${ns}Hbox-col`}>
|
<div className={`${ns}Hbox-col`}>
|
||||||
<div className="Wrapper--xs">
|
<div className="Wrapper--xs">
|
||||||
<img onLoad={this.onImageLoaded as any} className="w-full" alt="poster" src={filter(poster, data)} />
|
<img
|
||||||
<p className="m-t-xs">封面 <span className={dimensionClassName}>{posterInfo.width || '-'} x {posterInfo.height || '-'}</span>
|
onLoad={this.onImageLoaded as any}
|
||||||
{dimensionClassName ? (<span> 封面尺寸小于 <span className={`${ns}Text--danger`}>{minPosterDimension.width || '-'} x {minPosterDimension.height || '-'}</span></span>) : null}
|
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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let {
|
let {splitPoster, className, classPrefix: ns} = this.props;
|
||||||
splitPoster,
|
|
||||||
className,
|
|
||||||
classPrefix: ns
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cx(`${ns}Video`, className)} onClick={this.onClick as any}>
|
<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({
|
@Renderer({
|
||||||
test: /(^|\/)video$/,
|
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 React from 'react';
|
||||||
import * as PropTypes from 'prop-types';
|
import * as PropTypes from 'prop-types';
|
||||||
import Scoped, { ScopedContext, IScopedContext } from '../Scoped';
|
import Scoped, {ScopedContext, IScopedContext} from '../Scoped';
|
||||||
|
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import {ServiceStore, IServiceStore} from '../store/service';
|
import {ServiceStore, IServiceStore} from '../store/service';
|
||||||
import {
|
import {Api, SchemaNode, Schema, Action} from '../types';
|
||||||
Api,
|
import {filter, evalExpression} from '../utils/tpl';
|
||||||
SchemaNode,
|
|
||||||
Schema,
|
|
||||||
Action
|
|
||||||
} from '../types';
|
|
||||||
import {
|
|
||||||
filter,
|
|
||||||
evalExpression
|
|
||||||
} from '../utils/tpl';
|
|
||||||
import cx = require('classnames');
|
import cx = require('classnames');
|
||||||
import { observer } from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
import { createObject, until } from '../utils/helper';
|
import {createObject, until} from '../utils/helper';
|
||||||
import { buildApi, isValidApi, isApiOutdated } from '../utils/api';
|
import {buildApi, isValidApi, isApiOutdated} from '../utils/api';
|
||||||
import { IFormStore } from '../store/form';
|
import {IFormStore} from '../store/form';
|
||||||
|
|
||||||
export type TabProps = Schema & {
|
export type TabProps = Schema & {
|
||||||
title?: string; // 标题
|
title?: string; // 标题
|
||||||
|
@ -40,7 +29,7 @@ export interface WizardProps extends RendererProps {
|
||||||
actionNextSaveLabel?: string;
|
actionNextSaveLabel?: string;
|
||||||
actionFinishLabel?: string;
|
actionFinishLabel?: string;
|
||||||
mode?: 'horizontal' | 'vertical';
|
mode?: 'horizontal' | 'vertical';
|
||||||
onFinished: (values:object, action:any) => any;
|
onFinished: (values: object, action: any) => any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WizardState {
|
export interface WizardState {
|
||||||
|
@ -48,8 +37,7 @@ export interface WizardState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Wizard extends React.Component<WizardProps, WizardState> {
|
export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||||
|
static defaultProps: Partial<WizardProps> = {
|
||||||
static defaultProps:Partial<WizardProps>= {
|
|
||||||
mode: 'horizontal', // vertical
|
mode: 'horizontal', // vertical
|
||||||
readOnly: false,
|
readOnly: false,
|
||||||
messages: {},
|
messages: {},
|
||||||
|
@ -57,29 +45,29 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||||
actionPrevLabel: '上一步',
|
actionPrevLabel: '上一步',
|
||||||
actionNextLabel: '下一步',
|
actionNextLabel: '下一步',
|
||||||
actionNextSaveLabel: '保存并下一步',
|
actionNextSaveLabel: '保存并下一步',
|
||||||
actionFinishLabel: '完成'
|
actionFinishLabel: '完成',
|
||||||
};
|
};
|
||||||
|
|
||||||
static propsList: Array<string> = [
|
static propsList: Array<string> = [
|
||||||
"steps",
|
'steps',
|
||||||
"mode",
|
'mode',
|
||||||
"messages",
|
'messages',
|
||||||
"actionClassName",
|
'actionClassName',
|
||||||
"actionPrevLabel",
|
'actionPrevLabel',
|
||||||
"actionNextLabel",
|
'actionNextLabel',
|
||||||
"actionNextSaveLabel",
|
'actionNextSaveLabel',
|
||||||
"actionFinishLabel",
|
'actionFinishLabel',
|
||||||
"onFinished"
|
'onFinished',
|
||||||
];
|
];
|
||||||
|
|
||||||
dom:any;
|
dom: any;
|
||||||
form:any;
|
form: any;
|
||||||
asyncCancel:() => void;
|
asyncCancel: () => void;
|
||||||
constructor(props:WizardProps) {
|
constructor(props: WizardProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
currentStep: -1 // init 完后会设置成 1
|
currentStep: -1, // init 完后会设置成 1
|
||||||
};
|
};
|
||||||
|
|
||||||
this.handleAction = this.handleAction.bind(this);
|
this.handleAction = this.handleAction.bind(this);
|
||||||
|
@ -100,11 +88,8 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||||
initFinishedField,
|
initFinishedField,
|
||||||
store,
|
store,
|
||||||
data,
|
data,
|
||||||
messages: {
|
messages: {fetchSuccess, fetchFailed},
|
||||||
fetchSuccess,
|
onInit,
|
||||||
fetchFailed
|
|
||||||
},
|
|
||||||
onInit
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (initApi && initFetch !== false && (!initApi.sendOn || evalExpression(initApi.sendOn, data))) {
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
return until(() => store.checkRemote(initAsyncApi, store.data)
|
return until(
|
||||||
, (ret:any) => ret && ret[initFinishedField || 'finished']
|
() => store.checkRemote(initAsyncApi, store.data),
|
||||||
, (cancel) => this.asyncCancel = cancel);
|
(ret: any) => ret && ret[initFinishedField || 'finished'],
|
||||||
}
|
cancel => (this.asyncCancel = cancel)
|
||||||
|
);
|
||||||
|
},
|
||||||
})
|
})
|
||||||
.then(value => {
|
.then(value => {
|
||||||
onInit && onInit(store.data);
|
onInit && onInit(store.data);
|
||||||
|
|
||||||
if (value && value.data && typeof value.data.step === 'number') {
|
if (value && value.data && typeof value.data.step === 'number') {
|
||||||
this.setState({
|
this.setState({
|
||||||
currentStep: value.data.step
|
currentStep: value.data.step,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
currentStep: 1
|
currentStep: 1,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState(
|
||||||
currentStep: 1
|
{
|
||||||
}, () => onInit && onInit(store.data));
|
currentStep: 1,
|
||||||
|
},
|
||||||
|
() => onInit && onInit(store.data)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps:WizardProps) {
|
componentDidUpdate(prevProps: WizardProps) {
|
||||||
const props = this.props;
|
const props = this.props;
|
||||||
const {
|
const {store, fetchSuccess, fetchFailed} = props;
|
||||||
store,
|
|
||||||
fetchSuccess,
|
|
||||||
fetchFailed
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
if (isApiOutdated(prevProps.initApi, props.initApi, prevProps.data, props.data)) {
|
if (isApiOutdated(prevProps.initApi, props.initApi, prevProps.data, props.data)) {
|
||||||
store.fetchData(props.initApi, store.data, {
|
store.fetchData(props.initApi, store.data, {
|
||||||
successMessage: fetchSuccess,
|
successMessage: fetchSuccess,
|
||||||
errorMessage: fetchFailed
|
errorMessage: fetchFailed,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,16 +149,16 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||||
this.asyncCancel && this.asyncCancel();
|
this.asyncCancel && this.asyncCancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
gotoStep(index:number) {
|
gotoStep(index: number) {
|
||||||
const steps = this.props.steps || [];
|
const steps = this.props.steps || [];
|
||||||
index = Math.max(Math.min(steps.length, index), 1);
|
index = Math.max(Math.min(steps.length, index), 1);
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
currentStep: index
|
currentStep: index,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
formRef(ref:any) {
|
formRef(ref: any) {
|
||||||
if (ref) {
|
if (ref) {
|
||||||
while (ref && ref.getWrappedInstance) {
|
while (ref && ref.getWrappedInstance) {
|
||||||
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!');
|
throw new Error('Please implements this!');
|
||||||
}
|
}
|
||||||
|
|
||||||
reloadTarget(target:string, data:any) {
|
reloadTarget(target: string, data: any) {
|
||||||
throw new Error('Please implements this!');
|
throw new Error('Please implements this!');
|
||||||
}
|
}
|
||||||
|
|
||||||
domRef(ref:any) {
|
domRef(ref: any) {
|
||||||
this.dom = ref;
|
this.dom = ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,50 +185,37 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||||
return this.dom;
|
return this.dom;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAction(e: React.UIEvent<any> | void, action: Action, data: object, throwErrors?:boolean) {
|
handleAction(e: React.UIEvent<any> | void, action: Action, data: object, throwErrors?: boolean) {
|
||||||
const {
|
const {onAction, store} = this.props;
|
||||||
onAction,
|
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (action.actionType === 'next' || action.type === 'submit') {
|
if (action.actionType === 'next' || action.type === 'submit') {
|
||||||
this.form.doAction({
|
this.form.doAction(
|
||||||
...action,
|
{
|
||||||
actionType: 'submit'
|
...action,
|
||||||
}, data);
|
actionType: 'submit',
|
||||||
|
},
|
||||||
|
data
|
||||||
|
);
|
||||||
} else if (action.actionType === 'prev') {
|
} else if (action.actionType === 'prev') {
|
||||||
this.gotoStep(this.state.currentStep - 1);
|
this.gotoStep(this.state.currentStep - 1);
|
||||||
} else if (action.type === 'reset') {
|
} else if (action.type === 'reset') {
|
||||||
this.form.reset();
|
this.form.reset();
|
||||||
} else if (action.actionType === 'dialog') {
|
} else if (action.actionType === 'dialog') {
|
||||||
store.openDialog(data);
|
store.openDialog(data);
|
||||||
} else if (onAction) {
|
} else if (onAction) {
|
||||||
onAction(e, action, data);
|
onAction(e, action, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange(values:object) {
|
handleChange(values: object) {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
store.updateData(values);
|
store.updateData(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 接管里面 form 的提交,不能直接让 form 提交,因为 wizard 自己需要知道进度。
|
// 接管里面 form 的提交,不能直接让 form 提交,因为 wizard 自己需要知道进度。
|
||||||
handleSubmit(values:object, action:Action) {
|
handleSubmit(values: object, action: Action) {
|
||||||
const {
|
const {store, steps, api, asyncApi, finishedField, target, redirect, reload, env, onFinished} = this.props;
|
||||||
store,
|
|
||||||
steps,
|
|
||||||
api,
|
|
||||||
asyncApi,
|
|
||||||
finishedField,
|
|
||||||
target,
|
|
||||||
redirect,
|
|
||||||
reload,
|
|
||||||
env,
|
|
||||||
onFinished
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const step = steps[this.state.currentStep - 1];
|
const step = steps[this.state.currentStep - 1];
|
||||||
store.updateData(values);
|
store.updateData(values);
|
||||||
|
@ -250,9 +223,10 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||||
if (this.state.currentStep < steps.length) {
|
if (this.state.currentStep < steps.length) {
|
||||||
let finnalAsyncApi = action.asyncApi || asyncApi;
|
let finnalAsyncApi = action.asyncApi || asyncApi;
|
||||||
|
|
||||||
finnalAsyncApi && store.updateData({
|
finnalAsyncApi &&
|
||||||
[finishedField || 'finished']: false
|
store.updateData({
|
||||||
});
|
[finishedField || 'finished']: false,
|
||||||
|
});
|
||||||
|
|
||||||
if (step.api || action.api) {
|
if (step.api || action.api) {
|
||||||
store
|
store
|
||||||
|
@ -261,30 +235,34 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||||
if (!finnalAsyncApi || store.data[finishedField || 'finished']) {
|
if (!finnalAsyncApi || store.data[finishedField || 'finished']) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return until(() => store.checkRemote(finnalAsyncApi as Api, store.data)
|
return until(
|
||||||
, (ret:any) => ret && ret[finishedField || 'finished']
|
() => store.checkRemote(finnalAsyncApi as Api, store.data),
|
||||||
, (cancel) => this.asyncCancel = cancel);
|
(ret: any) => ret && ret[finishedField || 'finished'],
|
||||||
}
|
cancel => (this.asyncCancel = cancel)
|
||||||
|
);
|
||||||
|
},
|
||||||
})
|
})
|
||||||
.then(() => this.gotoStep(this.state.currentStep + 1))
|
.then(() => this.gotoStep(this.state.currentStep + 1))
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
// do nothing
|
// do nothing
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
this.gotoStep(this.state.currentStep + 1);
|
this.gotoStep(this.state.currentStep + 1);
|
||||||
}
|
}
|
||||||
} else { // 最后一步
|
} else {
|
||||||
|
// 最后一步
|
||||||
if (target) {
|
if (target) {
|
||||||
this.submitToTarget(target, store.data);
|
this.submitToTarget(target, store.data);
|
||||||
} else if (action.api || step.api || api) {
|
} else if (action.api || step.api || api) {
|
||||||
let finnalAsyncApi = action.asyncApi || step.asyncApi || asyncApi;
|
let finnalAsyncApi = action.asyncApi || step.asyncApi || asyncApi;
|
||||||
|
|
||||||
finnalAsyncApi && store.updateData({
|
finnalAsyncApi &&
|
||||||
[finishedField || 'finished']: false
|
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);
|
store.markSaving(true);
|
||||||
|
|
||||||
formStore
|
formStore
|
||||||
|
@ -293,11 +271,13 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||||
if (!finnalAsyncApi || store.data[finishedField || 'finished']) {
|
if (!finnalAsyncApi || store.data[finishedField || 'finished']) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return until(() => store.checkRemote(finnalAsyncApi as Api, store.data)
|
return until(
|
||||||
, (ret:any) => ret && ret[finishedField || 'finished']
|
() => store.checkRemote(finnalAsyncApi as Api, store.data),
|
||||||
, (cancel) => this.asyncCancel = cancel);
|
(ret: any) => ret && ret[finishedField || 'finished'],
|
||||||
}
|
cancel => (this.asyncCancel = cancel)
|
||||||
|
);
|
||||||
|
},
|
||||||
})
|
})
|
||||||
.then(value => {
|
.then(value => {
|
||||||
store.markSaving(false);
|
store.markSaving(false);
|
||||||
|
@ -305,13 +285,13 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||||
// 如果是 false 后面的操作就不执行
|
// 如果是 false 后面的操作就不执行
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (redirect) {
|
if (redirect) {
|
||||||
env.updateLocation(filter(redirect, store.data));
|
env.updateLocation(filter(redirect, store.data));
|
||||||
} else if (reload) {
|
} else if (reload) {
|
||||||
this.reloadTarget(reload, store.data);
|
this.reloadTarget(reload, store.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
|
@ -324,10 +304,8 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDialogConfirm(values: object[], action:Action, ctx:any, targets:Array<any>) {
|
handleDialogConfirm(values: object[], action: Action, ctx: any, targets: Array<any>) {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (action.mergeData && values.length === 1 && values[0] && targets[0].props.type === 'form') {
|
if (action.mergeData && values.length === 1 && values[0] && targets[0].props.type === 'form') {
|
||||||
store.updateData(values[0]);
|
store.updateData(values[0]);
|
||||||
|
@ -337,48 +315,44 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDialogClose() {
|
handleDialogClose() {
|
||||||
const {
|
const {store} = this.props;
|
||||||
store
|
|
||||||
} = this.props;
|
|
||||||
store.closeDialog();
|
store.closeDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSteps() {
|
renderSteps() {
|
||||||
const {
|
const {steps, store, mode, classPrefix: ns} = this.props;
|
||||||
steps,
|
|
||||||
store,
|
|
||||||
mode,
|
|
||||||
classPrefix: ns
|
|
||||||
} = this.props;
|
|
||||||
const currentStep = this.state.currentStep;
|
const currentStep = this.state.currentStep;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${ns}Wizard-steps clearfix ${ns}Wizard--${mode}`} id="form-wizard">
|
<div className={`${ns}Wizard-steps clearfix ${ns}Wizard--${mode}`} id="form-wizard">
|
||||||
{Array.isArray(steps) && steps.length ? (
|
{Array.isArray(steps) && steps.length ? (
|
||||||
<ul>
|
<ul>
|
||||||
{steps.map((step, key) => {
|
{steps.map((step, key) => {
|
||||||
const canJump = isJumpable(step, key, currentStep, store.data);
|
const canJump = isJumpable(step, key, currentStep, store.data);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
key={key}
|
key={key}
|
||||||
className={cx({
|
className={cx({
|
||||||
'is-complete': canJump,
|
'is-complete': canJump,
|
||||||
'is-active': currentStep === key + 1
|
'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
|
|
||||||
})}
|
})}
|
||||||
>{key + 1}</span>
|
onClick={() => (canJump ? this.gotoStep(key + 1) : null)}
|
||||||
{step.title || step.label || `第 ${key + 1} 步`}
|
>
|
||||||
</li>
|
<span
|
||||||
);
|
className={cx('Badge', {
|
||||||
})}
|
// 'Badge--success': canJump && currentStep != key + 1,
|
||||||
</ul>
|
'Badge--info':
|
||||||
|
currentStep === key + 1 || (canJump && currentStep != key + 1),
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{key + 1}
|
||||||
|
</span>
|
||||||
|
{step.title || step.label || `第 ${key + 1} 步`}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -403,7 +377,7 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||||
if (!Array.isArray(steps)) {
|
if (!Array.isArray(steps)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentStepIndex = this.state.currentStep;
|
const currentStepIndex = this.state.currentStep;
|
||||||
const nextStep = steps[currentStepIndex];
|
const nextStep = steps[currentStepIndex];
|
||||||
const prevStep = steps[currentStepIndex - 2];
|
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;
|
const prevCanJump = prevStep ? isJumpable(prevStep, currentStepIndex - 2, currentStepIndex, store.data) : false;
|
||||||
|
|
||||||
if (step.actions && Array.isArray(step.actions)) {
|
if (step.actions && Array.isArray(step.actions)) {
|
||||||
return step.actions.length ? (
|
return step.actions.length ? (
|
||||||
<div className={cx('Panel-footer')}>
|
<div className={cx('Panel-footer')}>
|
||||||
{step.actions.map((action:Action, index:number) => render(`action/${index}`, action, {
|
{step.actions.map((action: Action, index: number) =>
|
||||||
key: index,
|
render(`action/${index}`, action, {
|
||||||
onAction: this.handleAction,
|
key: index,
|
||||||
disabled: action.disabled
|
onAction: this.handleAction,
|
||||||
|| waiting
|
disabled:
|
||||||
|| disabled
|
action.disabled ||
|
||||||
|| action.actionType === 'prev' && !prevCanJump
|
waiting ||
|
||||||
|| action.actionType === 'next' && readOnly && (!!step.api || !nextStep)
|
disabled ||
|
||||||
}))}
|
(action.actionType === 'prev' && !prevCanJump) ||
|
||||||
|
(action.actionType === 'next' && readOnly && (!!step.api || !nextStep)),
|
||||||
|
})
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : null;
|
) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cx('Panel-footer')}>
|
<div className={cx('Panel-footer')}>
|
||||||
{render(`prev-btn`, {
|
{render(
|
||||||
type: 'button',
|
`prev-btn`,
|
||||||
label: actionPrevLabel,
|
{
|
||||||
actionType: 'prev',
|
type: 'button',
|
||||||
className: actionClassName
|
label: actionPrevLabel,
|
||||||
}, {
|
actionType: 'prev',
|
||||||
disabled: waiting || !prevCanJump || disabled,
|
className: actionClassName,
|
||||||
onAction: this.handleAction
|
},
|
||||||
})}
|
{
|
||||||
|
disabled: waiting || !prevCanJump || disabled,
|
||||||
{render(`next-btn`, {
|
onAction: this.handleAction,
|
||||||
type: 'button',
|
}
|
||||||
label: !nextStep ? actionFinishLabel : !step.api ? actionNextLabel : actionNextSaveLabel,
|
)}
|
||||||
actionType: 'next',
|
|
||||||
primary: !nextStep || !!step.api,
|
{render(
|
||||||
className: actionClassName
|
`next-btn`,
|
||||||
}, {
|
{
|
||||||
disabled: waiting || disabled || readOnly && (!!step.api || !nextStep),
|
type: 'button',
|
||||||
onAction: this.handleAction
|
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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {className, steps, render, store, mode, classPrefix: ns} = this.props;
|
||||||
className,
|
|
||||||
steps,
|
|
||||||
render,
|
|
||||||
store,
|
|
||||||
mode,
|
|
||||||
classPrefix: ns
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const currentStep = this.state.currentStep;
|
const currentStep = this.state.currentStep;
|
||||||
const step = Array.isArray(steps) ? steps[currentStep - 1] : null;
|
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)}>
|
<div ref={this.domRef} className={cx(`${ns}Panel ${ns}Panel--default ${ns}Wizard`, className)}>
|
||||||
{this.renderSteps()}
|
{this.renderSteps()}
|
||||||
<div className={`${ns}Wizard-stepContent clearfix`}>
|
<div className={`${ns}Wizard-stepContent clearfix`}>
|
||||||
{step ? render('body', {
|
{step ? (
|
||||||
...step,
|
render(
|
||||||
type: 'form',
|
'body',
|
||||||
wrapWithPanel: false,
|
{
|
||||||
|
...step,
|
||||||
|
type: 'form',
|
||||||
|
wrapWithPanel: false,
|
||||||
|
|
||||||
// 接口相关需要外部来接管
|
// 接口相关需要外部来接管
|
||||||
api: null
|
api: null,
|
||||||
}, {
|
},
|
||||||
key: this.state.currentStep,
|
{
|
||||||
ref: this.formRef,
|
key: this.state.currentStep,
|
||||||
onSubmit: this.handleSubmit,
|
ref: this.formRef,
|
||||||
onAction: this.handleAction,
|
onSubmit: this.handleSubmit,
|
||||||
disabled: store.loading,
|
onAction: this.handleAction,
|
||||||
popOverContainer: this.getPopOverContainer,
|
disabled: store.loading,
|
||||||
onChange: this.handleChange
|
popOverContainer: this.getPopOverContainer,
|
||||||
}) : currentStep === -1 ? '初始中。。' : (
|
onChange: this.handleChange,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
) : currentStep === -1 ? (
|
||||||
|
'初始中。。'
|
||||||
|
) : (
|
||||||
<p className="text-danger">配置错误</p>
|
<p className="text-danger">配置错误</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{render('dialog', {
|
{render(
|
||||||
...(store.action as Action) && (store.action as Action).dialog as object,
|
'dialog',
|
||||||
type: 'dialog'
|
{
|
||||||
}, {
|
...((store.action as Action) && ((store.action as Action).dialog as object)),
|
||||||
key: 'dialog',
|
type: 'dialog',
|
||||||
data: store.dialogData,
|
},
|
||||||
onConfirm: this.handleDialogConfirm,
|
{
|
||||||
onClose: this.handleDialogClose,
|
key: 'dialog',
|
||||||
show: store.dialogOpen
|
data: store.dialogData,
|
||||||
})}
|
onConfirm: this.handleDialogConfirm,
|
||||||
|
onClose: this.handleDialogClose,
|
||||||
|
show: store.dialogOpen,
|
||||||
|
}
|
||||||
|
)}
|
||||||
{this.renderActions()}
|
{this.renderActions()}
|
||||||
|
|
||||||
{store.loading ? render('spinner', {
|
{store.loading
|
||||||
type: 'spinner',
|
? render('spinner', {
|
||||||
overlay: true,
|
type: 'spinner',
|
||||||
size: 'lg'
|
overlay: true,
|
||||||
}) : null}
|
size: 'lg',
|
||||||
|
})
|
||||||
|
: null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isJumpable(step:any, index:number, currentStep:number, data:any) {
|
function isJumpable(step: any, index: number, currentStep: number, data: any) {
|
||||||
let canJump = false;
|
let canJump = false;
|
||||||
|
|
||||||
if (step && step.hasOwnProperty('jumpable')) {
|
if (step && step.hasOwnProperty('jumpable')) {
|
||||||
canJump = step.jumpable;
|
canJump = step.jumpable;
|
||||||
} else if (step && step.jumpableOn) {
|
} else if (step && step.jumpableOn) {
|
||||||
canJump = evalExpression(step.jumpableOn, createObject(data, {
|
canJump = evalExpression(
|
||||||
currentStep
|
step.jumpableOn,
|
||||||
}));
|
createObject(data, {
|
||||||
|
currentStep,
|
||||||
|
})
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
canJump = index + 1 < currentStep;
|
canJump = index + 1 < currentStep;
|
||||||
}
|
}
|
||||||
|
@ -537,7 +532,7 @@ function isJumpable(step:any, index:number, currentStep:number, data:any) {
|
||||||
test: /(^|\/)wizard$/,
|
test: /(^|\/)wizard$/,
|
||||||
storeType: ServiceStore.name,
|
storeType: ServiceStore.name,
|
||||||
name: 'wizard',
|
name: 'wizard',
|
||||||
isolateScope: true
|
isolateScope: true,
|
||||||
})
|
})
|
||||||
export class WizardRenderer extends Wizard {
|
export class WizardRenderer extends Wizard {
|
||||||
static contextType = ScopedContext;
|
static contextType = ScopedContext;
|
||||||
|
@ -556,17 +551,17 @@ export class WizardRenderer extends Wizard {
|
||||||
return this.handleAction(undefined, action, data, throwErrors);
|
return this.handleAction(undefined, action, data, throwErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
submitToTarget(target:string, values:object) {
|
submitToTarget(target: string, values: object) {
|
||||||
const scoped = this.context as IScopedContext;
|
const scoped = this.context as IScopedContext;
|
||||||
scoped.send(target, values);
|
scoped.send(target, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
reloadTarget(target:string, data:any) {
|
reloadTarget(target: string, data: any) {
|
||||||
const scoped = this.context as IScopedContext;
|
const scoped = this.context as IScopedContext;
|
||||||
scoped.reload(target, data);
|
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);
|
super.handleDialogConfirm(values, action, ctx, targets);
|
||||||
|
|
||||||
const store = this.props.store;
|
const store = this.props.store;
|
||||||
|
|
|
@ -1,49 +1,35 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import {Renderer, RendererProps} from '../factory';
|
||||||
Renderer,
|
|
||||||
RendererProps
|
|
||||||
} from '../factory';
|
|
||||||
import {SchemaNode} from '../types';
|
import {SchemaNode} from '../types';
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
|
|
||||||
export interface WrapperProps extends RendererProps {
|
export interface WrapperProps extends RendererProps {
|
||||||
body?: SchemaNode;
|
body?: SchemaNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
children?: JSX.Element | ((props?:any) => JSX.Element);
|
children?: JSX.Element | ((props?: any) => JSX.Element);
|
||||||
size?: 'xs' | 'sm' | 'md' | 'lg' | 'none';
|
size?: 'xs' | 'sm' | 'md' | 'lg' | 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Wrapper extends React.Component<WrapperProps, object> {
|
export default class Wrapper extends React.Component<WrapperProps, object> {
|
||||||
static propsList: Array<string> = [
|
static propsList: Array<string> = ['body', 'className', 'children', 'size'];
|
||||||
"body",
|
static defaultProps: Partial<WrapperProps> = {
|
||||||
"className",
|
|
||||||
"children",
|
|
||||||
"size"
|
|
||||||
];
|
|
||||||
static defaultProps:Partial<WrapperProps>= {
|
|
||||||
className: 'bg-white',
|
className: 'bg-white',
|
||||||
};
|
};
|
||||||
|
|
||||||
renderBody():JSX.Element | null {
|
renderBody(): JSX.Element | null {
|
||||||
const {
|
const {children, body, render} = this.props;
|
||||||
children,
|
|
||||||
body,
|
|
||||||
render
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return children ? (
|
return children
|
||||||
typeof children === 'function' ? children(this.props) as JSX.Element : children as JSX.Element
|
? typeof children === 'function'
|
||||||
) : body ? (
|
? (children(this.props) as JSX.Element)
|
||||||
render('body', body) as JSX.Element
|
: (children as JSX.Element)
|
||||||
) : null;
|
: body
|
||||||
|
? (render('body', body) as JSX.Element)
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {className, size, classnames: cx} = this.props;
|
||||||
className,
|
|
||||||
size,
|
|
||||||
classnames: cx
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -57,6 +43,6 @@ export default class Wrapper extends React.Component<WrapperProps, object> {
|
||||||
|
|
||||||
@Renderer({
|
@Renderer({
|
||||||
test: /(^|\/)wrapper$/,
|
test: /(^|\/)wrapper$/,
|
||||||
name: 'wrapper'
|
name: 'wrapper',
|
||||||
})
|
})
|
||||||
export class WrapperRenderer extends Wrapper {};
|
export class WrapperRenderer extends Wrapper {}
|
||||||
|
|
|
@ -11,35 +11,35 @@ const bsMapping: {
|
||||||
} = {
|
} = {
|
||||||
level: 'bsStyle',
|
level: 'bsStyle',
|
||||||
classPrefix: 'bsClass',
|
classPrefix: 'bsClass',
|
||||||
size: 'bsSize'
|
size: 'bsSize',
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主要目的是希望在是用 bootstrap 组件的时候不需要带 bs 前缀。
|
* 主要目的是希望在是用 bootstrap 组件的时候不需要带 bs 前缀。
|
||||||
*
|
*
|
||||||
* @param {Object} rawProps 原始属性对象。
|
* @param {Object} rawProps 原始属性对象。
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
export const props2BsProps = (rawProps: { [propName: string]: any; }) => {
|
export const props2BsProps = (rawProps: {[propName: string]: any}) => {
|
||||||
let props: { [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;
|
return props;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* props2BsProps 的 hoc 版本
|
* props2BsProps 的 hoc 版本
|
||||||
*
|
*
|
||||||
* @param {*} ComposedComponent 组合组件
|
* @param {*} ComposedComponent 组合组件
|
||||||
* @return {Component}
|
* @return {Component}
|
||||||
*/
|
*/
|
||||||
export const props2BsPropsHoc: (ComposedComponent: React.ComponentType<any>) => React.ComponentType<any> = (ComposedComponent) => {
|
export const props2BsPropsHoc: (
|
||||||
|
ComposedComponent: React.ComponentType<any>
|
||||||
|
) => React.ComponentType<any> = ComposedComponent => {
|
||||||
class BsComponent extends React.Component<any> {
|
class BsComponent extends React.Component<any> {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return <ComposedComponent {...props2BsProps(this.props)} />;
|
||||||
<ComposedComponent {...props2BsProps(this.props)} />
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,15 +63,13 @@ function getContainerDimensions(containerNode: any) {
|
||||||
width = window.innerWidth;
|
width = window.innerWidth;
|
||||||
height = window.innerHeight;
|
height = window.innerHeight;
|
||||||
|
|
||||||
scroll =
|
scroll = getScrollTop(ownerDocument(containerNode).documentElement) || getScrollTop(containerNode);
|
||||||
getScrollTop(ownerDocument(containerNode).documentElement) ||
|
|
||||||
getScrollTop(containerNode);
|
|
||||||
} else {
|
} else {
|
||||||
({ width, height } = getOffset(containerNode));
|
({width, height} = getOffset(containerNode));
|
||||||
scroll = getScrollTop(containerNode);
|
scroll = getScrollTop(containerNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { width, height, scroll };
|
return {width, height, scroll};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTopDelta(top: any, overlayHeight: any, container: any, padding: any) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function calculatePosition(
|
export function calculatePosition(placement: any, overlayNode: any, target: any, container: any, padding: any) {
|
||||||
placement: any, overlayNode: any, target: any, container: any, padding: any
|
|
||||||
) {
|
|
||||||
const childOffset = container.tagName === 'BODY' ? getOffset(target) : getPosition(target, container);
|
const childOffset = container.tagName === 'BODY' ? getOffset(target) : getPosition(target, container);
|
||||||
const {
|
const {height: overlayHeight, width: overlayWidth} = getOffset(overlayNode);
|
||||||
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('-')) {
|
if (~placement.indexOf('-')) {
|
||||||
const tests = placement.split(/\s+/);
|
const tests = placement.split(/\s+/);
|
||||||
|
@ -127,14 +123,18 @@ export function calculatePosition(
|
||||||
myX = myX || atX;
|
myX = myX || atX;
|
||||||
myY = myY || atY;
|
myY = myY || atY;
|
||||||
|
|
||||||
positionLeft = atX === 'left'
|
positionLeft =
|
||||||
? childOffset.left : atX === 'right'
|
atX === 'left'
|
||||||
? (childOffset.left + childOffset.width)
|
? childOffset.left
|
||||||
: (childOffset.left + childOffset.width / 2);
|
: atX === 'right'
|
||||||
positionTop = atY === 'top'
|
? childOffset.left + childOffset.width
|
||||||
? childOffset.top : atY === 'bottom'
|
: childOffset.left + childOffset.width / 2;
|
||||||
? (childOffset.top + childOffset.height)
|
positionTop =
|
||||||
: (childOffset.top + childOffset.height / 2);
|
atY === 'top'
|
||||||
|
? childOffset.top
|
||||||
|
: atY === 'bottom'
|
||||||
|
? childOffset.top + childOffset.height
|
||||||
|
: childOffset.top + childOffset.height / 2;
|
||||||
|
|
||||||
positionLeft -= myX === 'left' ? 0 : myX === 'right' ? overlayWidth : overlayWidth / 2;
|
positionLeft -= myX === 'left' ? 0 : myX === 'right' ? overlayWidth : overlayWidth / 2;
|
||||||
positionTop -= myY === 'top' ? 0 : myY === 'bottom' ? overlayHeight : overlayHeight / 2;
|
positionTop -= myY === 'top' ? 0 : myY === 'bottom' ? overlayHeight : overlayHeight / 2;
|
||||||
|
@ -146,12 +146,14 @@ export function calculatePosition(
|
||||||
x: clip.x + positionLeft - childOffset.left,
|
x: clip.x + positionLeft - childOffset.left,
|
||||||
y: clip.y + positionTop - childOffset.top,
|
y: clip.y + positionTop - childOffset.top,
|
||||||
width: overlayWidth,
|
width: overlayWidth,
|
||||||
height: overlayHeight
|
height: overlayHeight,
|
||||||
}
|
};
|
||||||
|
|
||||||
if (
|
if (
|
||||||
transformed.x > 0 && (transformed.x + transformed.width) < window.innerWidth
|
transformed.x > 0 &&
|
||||||
&& transformed.y > 0 && (transformed.y + transformed.height) < window.innerHeight
|
transformed.x + transformed.width < window.innerWidth &&
|
||||||
|
transformed.y > 0 &&
|
||||||
|
transformed.y + transformed.height < window.innerHeight
|
||||||
) {
|
) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -170,12 +172,10 @@ export function calculatePosition(
|
||||||
}
|
}
|
||||||
|
|
||||||
positionTop = childOffset.top + (childOffset.height - overlayHeight) / 2;
|
positionTop = childOffset.top + (childOffset.height - overlayHeight) / 2;
|
||||||
const topDelta = getTopDelta(
|
const topDelta = getTopDelta(positionTop, overlayHeight, container, padding);
|
||||||
positionTop, overlayHeight, container, padding
|
|
||||||
);
|
|
||||||
|
|
||||||
positionTop += topDelta;
|
positionTop += topDelta;
|
||||||
arrowOffsetTop = 50 * (1 - 2 * topDelta / overlayHeight) + '%';
|
arrowOffsetTop = 50 * (1 - (2 * topDelta) / overlayHeight) + '%';
|
||||||
} else if (placement === 'top' || placement === 'bottom') {
|
} else if (placement === 'top' || placement === 'bottom') {
|
||||||
// atY = placement;
|
// atY = placement;
|
||||||
// atX = myX = 'center';
|
// atX = myX = 'center';
|
||||||
|
@ -187,22 +187,18 @@ export function calculatePosition(
|
||||||
}
|
}
|
||||||
|
|
||||||
positionLeft = childOffset.left + (childOffset.width - overlayWidth) / 2;
|
positionLeft = childOffset.left + (childOffset.width - overlayWidth) / 2;
|
||||||
const leftDelta = getLeftDelta(
|
const leftDelta = getLeftDelta(positionLeft, overlayWidth, container, padding);
|
||||||
positionLeft, overlayWidth, container, padding
|
|
||||||
);
|
|
||||||
|
|
||||||
positionLeft += leftDelta;
|
positionLeft += leftDelta;
|
||||||
arrowOffsetLeft = 50 * (1 - 2 * leftDelta / overlayHeight) + '%';
|
arrowOffsetLeft = 50 * (1 - (2 * leftDelta) / overlayHeight) + '%';
|
||||||
} else if (placement = 'center') {
|
} else if ((placement = 'center')) {
|
||||||
// atX = atY = myX = myY = 'center';
|
// atX = atY = myX = myY = 'center';
|
||||||
positionLeft = childOffset.left + (childOffset.width - overlayWidth) / 2;
|
positionLeft = childOffset.left + (childOffset.width - overlayWidth) / 2;
|
||||||
positionTop = childOffset.top + (childOffset.height - overlayHeight) / 2;
|
positionTop = childOffset.top + (childOffset.height - overlayHeight) / 2;
|
||||||
arrowOffsetLeft = arrowOffsetTop = void 0;
|
arrowOffsetLeft = arrowOffsetTop = void 0;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw new Error(`calcOverlayPosition(): No such placement of "${placement}" found.`);
|
||||||
`calcOverlayPosition(): No such placement of "${placement}" found.`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return { positionLeft, positionTop, arrowOffsetLeft, arrowOffsetTop };
|
return {positionLeft, positionTop, arrowOffsetLeft, arrowOffsetTop};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue