Commit 378063c2 by Vladislav Lagunov

[WIP] Рефакторинг ~/fields

parent 114d9872
import * as React from 'react';
import { memoize } from 'lodash';
import { FieldProps } from '~/fields/context';
import filterMap from '~/functions/filter-map';
import { Modifier } from '~/fields';
// Props
export interface Props<A = any> {
id?: string;
source: A[];
}
// Component
export default class ArrayIDS<A = any> extends React.Component<Props<A>> {
projectContext = (input: FieldProps) => ({
...input,
value: this.getValue(input.value),
onValueChange: this.handleValueChange(input.onValueChange),
});
getValue = memoize(valueIn => {
const { source } = this.props;
const id = this.props.id || 'id';
return filterMap(valueIn, x => source.find(s => s[id] === x) || null);
});
handleValueChange = memoize((onValueChange) => (valueOut: A[]) => {
const id = this.props.id || 'id';
onValueChange && onValueChange(valueOut ? valueOut.map(x => x[id]) : []);
});
render() {
return <Modifier proj={this.projectContext}>{this.props.children}</Modifier>
}
}
...@@ -18,7 +18,7 @@ import classNames = require('classnames'); ...@@ -18,7 +18,7 @@ import classNames = require('classnames');
// Props // Props
export type Props<A> = StandardProps<React.HTMLProps<HTMLDivElement>, string> & FieldProps & { export type Props<A> = StandardProps<React.HTMLProps<HTMLDivElement>, string, 'value'> & FieldProps<A> & {
ctx?: Ctx; ctx?: Ctx;
source: Source<A>; source: Source<A>;
debounce?: number; debounce?: number;
...@@ -54,7 +54,7 @@ export default class AutoComplete<A=string> extends React.Component<Props<A>, St ...@@ -54,7 +54,7 @@ export default class AutoComplete<A=string> extends React.Component<Props<A>, St
openOnClick: true, openOnClick: true,
fullWidth: true, fullWidth: true,
printItem: x => x ? String(x) : '', printItem: x => x ? String(x) : '',
}; } as any;
state: State<A> = { ...ST.init(), value: null }; state: State<A> = { ...ST.init(), value: null };
anchorEl: HTMLElement|null; // Ссылка на <input/> anchorEl: HTMLElement|null; // Ссылка на <input/>
rectEl: HTMLElement|null; // Ссылка на элемент для выравнивания, может быть тем же что и `inputEl` rectEl: HTMLElement|null; // Ссылка на элемент для выравнивания, может быть тем же что и `inputEl`
...@@ -137,7 +137,7 @@ export default class AutoComplete<A=string> extends React.Component<Props<A>, St ...@@ -137,7 +137,7 @@ export default class AutoComplete<A=string> extends React.Component<Props<A>, St
const { printItem } = this.props; const { printItem } = this.props;
if (this.state.value !== null) return this.state.value; if (this.state.value !== null) return this.state.value;
const { value } = this.props; const { value } = this.props;
return (printItem || String)(value) || ''; return (printItem || String)(value!) || '';
}; };
componentDidMount() { componentDidMount() {
......
...@@ -8,12 +8,14 @@ import { StandardProps } from '@material-ui/core'; ...@@ -8,12 +8,14 @@ import { StandardProps } from '@material-ui/core';
// Props // Props
export type Props = StandardProps<React.HTMLProps<HTMLDivElement>, ClassKey, 'value'> & FieldProps<boolean> & WithStyles<ClassKey> & { export type Props = StandardProps<React.HTMLProps<HTMLDivElement>, ClassKey, 'value'> & FieldProps<boolean> & {
alignLeft?: boolean; alignLeft?: boolean;
} }
// Component // Component
// @ts-ignore
@withStyles(styles)
class BooleanField extends React.Component<Props> { class BooleanField extends React.Component<Props> {
handleChange: SwitchProps['onChange'] = (e, v) => { handleChange: SwitchProps['onChange'] = (e, v) => {
const { onValueChange } = this.props; const { onValueChange } = this.props;
...@@ -39,7 +41,7 @@ class BooleanField extends React.Component<Props> { ...@@ -39,7 +41,7 @@ class BooleanField extends React.Component<Props> {
} }
} }
export default withStyles(styles)(BooleanField); export default BooleanField;
// CSS классы // CSS классы
......
import * as React from 'react'; import * as React from 'react';
import { isEqual } from 'lodash';
import { FieldProps, Modifier } from '~/fields';
// Props // Props
export type Props = { export type Props<A> = {
delay?: number, delay?: number,
children: React.ReactElement<{ value: A, onValueChange?(x: A): void, onBlur?(e: React.FocusEvent): void; }>;
}
// State
export type State<A> = {
value?: A;
} }
// Component // Component
export class Debounce extends React.Component<Props> { export class Debounce<A> extends React.Component<Props<A>, State<A>> {
value: any; static defaultProps = {
prevContext: FieldProps; delay: 500,
};
state: State<A> = {};
timer: number|null = null; timer: number|null = null;
callback: Function|null = null; callback: Function|null = null;
projectContext = (context: FieldProps) => { componentWillReceiveProps(nextProps: Props<A>) {
if (!this.prevContext || !isEqual(context.value, this.prevContext.value)) { if ('value' in this.state) this.setState(prev => ({ value: undefined }));
this.value = context.value; }
}
this.prevContext = context;
return {
...context,
value: this.value,
onValueChange: this.handleChange,
onBlur: this.handleBlur,
};
};
handleChange = (value) => { handleChange = (value) => {
if (this.timer) clearTimeout(this.timer); if (this.timer) clearTimeout(this.timer);
this.value = value; this.setState({ value })
this.forceUpdate(); this.forceUpdate();
this.callback = () => { this.callback = () => {
if (!this.prevContext) return;
this.timer = null; this.timer = null;
this.callback = null; this.callback = null;
const { onValueChange } = this.prevContext; const { onValueChange } = this.props.children.props;
onValueChange && onValueChange(value); onValueChange && onValueChange(value);
}; };
this.timer = setTimeout(this.callback, this.props.delay || 500); this.timer = setTimeout(this.callback, this.props.delay);
}; };
handleBlur = (e: React.ChangeEvent<HTMLInputElement>) => { handleBlur = (e: React.FocusEvent) => {
this.callback && this.callback(); this.callback && this.callback();
const { onBlur } = this.props.children.props;
onBlur && onBlur(e);
}; };
render() { render() {
return <Modifier proj={this.projectContext}>{this.props.children}</Modifier>; return React.cloneElement(this.props.children, {
value: this.state.value !== undefined ? this.state.value : this.props.children.props.value,
onValueChange: this.handleChange,
onBlur: this.handleBlur,
});
} }
} }
export default Debounce;; export default Debounce;
import * as React from 'react';
// Props
export type Props<A> = {
delay?: number,
children: React.ReactElement<{ value: A, onValueChange?(x: A): void, onBlur?(e: React.FocusEvent): void; }>;
}
// State
export type State<A> = {
value?: A;
}
// Component
export class Debounce<A> extends React.Component<Props<A>, State<A>> {
static defaultProps = {
delay: 500,
};
state: State<A> = {};
timer: number|null = null;
callback: Function|null = null;
componentWillReceiveProps(nextProps: Props<A>) {
if ('value' in this.state) this.setState(prev => ({ value: undefined }));
}
handleChange = (value) => {
if (this.timer) clearTimeout(this.timer);
this.setState({ value })
this.forceUpdate();
this.callback = () => {
this.timer = null;
this.callback = null;
const { onValueChange } = this.props.children.props;
onValueChange && onValueChange(value);
};
this.timer = setTimeout(this.callback, this.props.delay);
};
handleBlur = (e: React.FocusEvent) => {
this.callback && this.callback();
const { onBlur } = this.props.children.props;
onBlur && onBlur(e);
};
render() {
return React.cloneElement(this.props.children, {
value: this.state.value !== undefined ? this.state.value : this.props.children.props.value,
onValueChange: this.handleChange,
onBlur: this.handleBlur,
});
}
}
export default Debounce;
import * as React from 'react';
import { FieldProps } from './';
// import * as F from './';
// import { StandardProps } from '@material-ui/core';
import memoize from '~/functions/memoize';
import { ObjectKey, ObjectPath } from '~/functions/get-at';
// Props
export type CardProps<T> = {
model: {
initial: T;
modified: T|null;
};
dispatch(action: { tag: 'Change', value, at }): void;
}
//
export type Any<T=any> = React.ReactType<FieldProps<T>>;
export type AnyC<T=any> = React.ReactElement<FieldProps<T>>;
export type GetProps<T extends Any> = T extends React.ReactType<infer Props> ? Props : never;
// Props
export type ZoomComponentProps<C extends Any> = {
Component: C;
} & GetProps<C>;
export type CreateZoomOptions<T> = {
validate?(value: T): any;
disabled?(value: T): any;
};
export default function createForm<O extends CreateZoomOptions<any>>(options: O) {
type T = O extends CreateZoomOptions<infer T> ? T : never;
function bindZoom(self: React.Component<CardProps<T>>) {
function Zoom<K1 extends keyof T, C extends Any<T[K1]>>(...keys: [K1]): (Component: C, props?: GetProps<C>) => JSX.Element;
function Zoom<K1 extends keyof T, K2 extends keyof T[K1], C extends Any<T[K1][K2]>>(...keys: [K1, K2]): (Component: C, props?: GetProps<C>) => JSX.Element;
function Zoom<K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2], C extends Any<T[K1][K2][K3]>>(...keys: [K1, K2, K3]): (Component: C, props?: GetProps<C>) => JSX.Element;
function Zoom(...keys) {
return (Component, props={}) => {
const { dispatch } = self.props;
const value = self.props.model.modified || self.props.model.initial;
const error = options.validate ? options.validate(value) : {};
const disabled = options.disabled ? options.disabled(value) : {};
const onValueChange = (value, at: ObjectPath=[]) => dispatch({ tag: 'Change', value, at });
const props_ = {
...props,
value: zoomAny(keys, value),
error: zoomAny(keys, error),
disabled: zoomAny(keys, disabled),
onValueChange: zoomOnChange(keys, onValueChange),
};
return React.createElement(Component, props_);
};
}
return Zoom;
}
// @ts-ignore
return { ...options, bindZoom } as { bindZoom: typeof bindZoom } & O;
}
const zoomOnChange = memoize((path: ObjectKey[], onChange: ((x: any, at: ObjectPath) => void)|undefined) => {
return (next, at=[]) => {
onChange && onChange(next, [...path, ...at]);
};
});
const zoomAny = memoize((path: ObjectKey[], input: any) => {
let iter = input as any;
for (const key of path) {
if (typeof(iter) !== 'object' || iter === null) return iter;
iter = iter[key];
}
return iter;
});
...@@ -13,9 +13,10 @@ import DayPicker from 'react-dates/lib/components/DayPicker'; ...@@ -13,9 +13,10 @@ import DayPicker from 'react-dates/lib/components/DayPicker';
// Props // Props
export type Props = StandardProps<React.HTMLProps<HTMLDivElement>, ClassKey, 'value'> & FieldProps<moment.Moment|null> & WithStyles<ClassKey> & { export type Props = StandardProps<React.HTMLProps<HTMLDivElement>, ClassKey, 'value'> & FieldProps<moment.Moment> & WithStyles<ClassKey> & {
InputProps?: Partial<TextFieldProps>; InputProps?: Partial<TextFieldProps>;
format?: string; format?: string;
nunNull?: boolean;
} }
...@@ -28,7 +29,13 @@ export interface State { ...@@ -28,7 +29,13 @@ export interface State {
// Component // Component
// @ts-ignore
@withStyles(styles)
class MomentField extends React.Component<Props, State> { class MomentField extends React.Component<Props, State> {
static defaultProps = {
nunNull: false,
} as any;
rootEl: HTMLElement|null; rootEl: HTMLElement|null;
pickerEl: HTMLElement|null; pickerEl: HTMLElement|null;
state: State = { open: false, textFieldValue: null, value: null }; state: State = { open: false, textFieldValue: null, value: null };
...@@ -124,7 +131,7 @@ class MomentField extends React.Component<Props, State> { ...@@ -124,7 +131,7 @@ class MomentField extends React.Component<Props, State> {
} }
render() { render() {
const { classes, ctx, error, InputProps, onBlur, onChange, onFocus, value, format: formapProps, disabled, ...rest } = this.props; const { classes, ctx, error, InputProps, onBlur, onChange, onFocus, value, format: formapProps, disabled, nunNull, ...rest } = this.props;
const { rootEl } = this; const { rootEl } = this;
const { open } = this.state; const { open } = this.state;
const rootClass = classes.root; const rootClass = classes.root;
...@@ -132,7 +139,7 @@ class MomentField extends React.Component<Props, State> { ...@@ -132,7 +139,7 @@ class MomentField extends React.Component<Props, State> {
const endAdornment = <React.Fragment> const endAdornment = <React.Fragment>
<Icon data-ignore-focus-in className={classes.iconDate} onClick={this.handleVisibilityToggle}>date_range</Icon> <Icon data-ignore-focus-in className={classes.iconDate} onClick={this.handleVisibilityToggle}>date_range</Icon>
<Icon data-ignore-focus-in className={classes.iconClose} onClick={this.handleClear}>close</Icon> {!nunNull && <Icon data-ignore-focus-in className={classes.iconClose} onClick={this.handleClear}>close</Icon>}
</React.Fragment>; </React.Fragment>;
return <div {...rest} className={rootClass} ref={this.handleRootRef}> return <div {...rest} className={rootClass} ref={this.handleRootRef}>
...@@ -163,7 +170,7 @@ class MomentField extends React.Component<Props, State> { ...@@ -163,7 +170,7 @@ class MomentField extends React.Component<Props, State> {
} }
} }
export default withStyles(styles)(MomentField); export default MomentField;
// CSS классы // CSS классы
......
...@@ -12,7 +12,7 @@ import { FieldProps } from '~/fields'; ...@@ -12,7 +12,7 @@ import { FieldProps } from '~/fields';
// Props // Props
export type Props<A> = StandardProps<React.HTMLProps<HTMLDivElement>, string, 'value'> & FieldProps & { export type Props<A> = StandardProps<React.HTMLProps<HTMLDivElement>, string, 'value'> & FieldProps<A> & {
source: Source<A>; source: Source<A>;
fullWidth?: boolean; fullWidth?: boolean;
textFieldProps?: Partial<TextFieldProps>; textFieldProps?: Partial<TextFieldProps>;
...@@ -29,10 +29,11 @@ export type Props<A> = StandardProps<React.HTMLProps<HTMLDivElement>, string, 'v ...@@ -29,10 +29,11 @@ export type Props<A> = StandardProps<React.HTMLProps<HTMLDivElement>, string, 'v
export default class MultiSelect<A> extends React.Component<Props<A>> { export default class MultiSelect<A> extends React.Component<Props<A>> {
static defaultProps = { static defaultProps = {
printItem: String, printItem: String,
}; } as any;
handleDelete = memoize((idx: number) => (e?: React.MouseEvent<HTMLDivElement>) => { handleDelete = memoize((idx: number) => (e?: React.MouseEvent<HTMLDivElement>) => {
const { value, onValueChange, disabled } = this.props; if (disabled) return; const { value, onValueChange, disabled } = this.props; if (disabled) return;
// @ts-ignore
const nextValue = value!.slice(); const nextValue = value!.slice();
nextValue.splice(idx, 1); nextValue.splice(idx, 1);
if (e) { if (e) {
...@@ -48,6 +49,7 @@ export default class MultiSelect<A> extends React.Component<Props<A>> { ...@@ -48,6 +49,7 @@ export default class MultiSelect<A> extends React.Component<Props<A>> {
handleValueChange = (item: A) => { handleValueChange = (item: A) => {
const { value, onValueChange } = this.props;; const { value, onValueChange } = this.props;;
// @ts-ignore
const nextValue = value!.slice(); const nextValue = value!.slice();
if (!item) { if (!item) {
...@@ -70,8 +72,9 @@ export default class MultiSelect<A> extends React.Component<Props<A>> { ...@@ -70,8 +72,9 @@ export default class MultiSelect<A> extends React.Component<Props<A>> {
const { classes, printItem, disabled } = this.props; const { classes, printItem, disabled } = this.props;
const id = this.props.id || 'id'; const id = this.props.id || 'id';
const chipClassName = classNames({ [classes!.disabled!]: disabled }); const chipClassName = classNames({ [classes!.disabled!]: disabled });
// @ts-ignore
return !value.length ? null : <div><span className={classes!.chips}> return !value.length ? null : <div><span className={classes!.chips}>
{value!.map((ch, i) => ( {(value as any).map((ch, i) => (
<Chip key={ch[id]} className={chipClassName} label={printItem!(ch)} onClick={this.handleDelete(i)} onDelete={this.handleDelete(i)}/> <Chip key={ch[id]} className={chipClassName} label={printItem!(ch)} onClick={this.handleDelete(i)} onDelete={this.handleDelete(i)}/>
))} ))}
</span></div>; </span></div>;
...@@ -79,6 +82,7 @@ export default class MultiSelect<A> extends React.Component<Props<A>> { ...@@ -79,6 +82,7 @@ export default class MultiSelect<A> extends React.Component<Props<A>> {
renderSuggestion = (item: A) => { renderSuggestion = (item: A) => {
const { renderItem, printItem, value } = this.props; const { renderItem, printItem, value } = this.props;
// @ts-ignore
const selected = !!value!.find(x => this.isEqual(x, item)); const selected = !!value!.find(x => this.isEqual(x, item));
const id = this.props.id || 'id'; const id = this.props.id || 'id';
...@@ -91,7 +95,7 @@ export default class MultiSelect<A> extends React.Component<Props<A>> { ...@@ -91,7 +95,7 @@ export default class MultiSelect<A> extends React.Component<Props<A>> {
}; };
render() { render() {
const { classes, fullWidth, className, source, renderItem, printItem, id, textFieldProps, value, error, disabled, onValueChange, placeholder, ...rest } = this.props; const { ctx, classes, fullWidth, className, source, renderItem, printItem, id, textFieldProps, value, error, disabled, onValueChange, placeholder, ...rest } = this.props;
const rootClass = classNames(className, classes!.root, { const rootClass = classNames(className, classes!.root, {
[classes!.disabled!]: disabled, [classes!.disabled!]: disabled,
[classes!.fullWidth!]: fullWidth, [classes!.fullWidth!]: fullWidth,
...@@ -99,7 +103,8 @@ export default class MultiSelect<A> extends React.Component<Props<A>> { ...@@ -99,7 +103,8 @@ export default class MultiSelect<A> extends React.Component<Props<A>> {
return <div {...rest} className={rootClass}> return <div {...rest} className={rootClass}>
{this.renderChips(value)} {this.renderChips(value)}
<AutoComplete<A> <AutoComplete<A|null>
ctx={ctx}
value={null} value={null}
printItem={printItem} printItem={printItem}
source={source} source={source}
......
...@@ -10,7 +10,7 @@ const eyeSlash = require('./eye-slash.svg'); ...@@ -10,7 +10,7 @@ const eyeSlash = require('./eye-slash.svg');
// Props // Props
export type Props = StandardProps<React.HTMLProps<HTMLDivElement>, ClassKey, 'ref'|'onKeyDown'|'disabled'> & FieldProps<string> & WithStyles<ClassKey> & { export type Props = StandardProps<React.HTMLProps<HTMLDivElement>, ClassKey, 'ref'|'onKeyDown'|'disabled'> & FieldProps<string> & {
__: Gettext; __: Gettext;
ctx?: LocaleCtx; ctx?: LocaleCtx;
type?: React.HTMLProps<HTMLInputElement>['type']; type?: React.HTMLProps<HTMLInputElement>['type'];
...@@ -32,6 +32,8 @@ export type State = { ...@@ -32,6 +32,8 @@ export type State = {
// Component // Component
// @ts-ignore
@withStyles(styles)
export class TextField extends React.Component<Props, State> { export class TextField extends React.Component<Props, State> {
state: State = { type: null }; state: State = { type: null };
...@@ -75,7 +77,7 @@ export class TextField extends React.Component<Props, State> { ...@@ -75,7 +77,7 @@ export class TextField extends React.Component<Props, State> {
Input: InputProp, Input: InputProp,
placeholder, placeholder,
...rest ...rest
} = this.props; } = this.props as Props & WithStyles<ClassKey>;
const rootClass = classNames(className, classes.root, { const rootClass = classNames(className, classes.root, {
[classes.disabled]: disabled, [classes.disabled]: disabled,
...@@ -113,7 +115,7 @@ export class TextField extends React.Component<Props, State> { ...@@ -113,7 +115,7 @@ export class TextField extends React.Component<Props, State> {
} }
} }
export default withStyles(styles)(withGettext(require('./i18n.yml'))(TextField)); export default withGettext(require('./i18n.yml'))(TextField);
// CSS классы // CSS классы
......
import * as React from 'react';
// import { FieldProps } from './';
// import * as F from './';
// import { StandardProps } from '@material-ui/core';
// import memoize from '~/functions/memoize';
type FieldProps<T=any> = { value: T }
// Props
export type CardProps<T> = {
model: {
initial: T;
modified: T|null;
};
dispatch(action: { tag: 'Change', value, at }): void;
}
//
export type Any<T=any> = React.ReactType<FieldProps<T>>;
export type AnyC<T=any> = React.ReactElement<FieldProps<T>>;
export type GetProps<T extends Any> = T extends React.ReactType<infer Props> ? Props : never;
// Props
export type ZoomComponentProps<C extends Any> = {
Component: C;
} & GetProps<C>;
/* export default function createZoom<T>(self: React.Component<CardProps<T>>) {
* const { Provider, Consumer } = React.createContext<string[]>([]);
*
* class ZoomComponent<C extends Any> extends React.Component<ZoomComponentProps<C>> {
* projectContext = (path: string[]) => (input: FieldProps) => {
* return {
* ...input,
* value: zoomAny(path, input.value),
* error: zoomAny(path, input.error),
* disabled: zoomAny(path, input.disabled),
* onValueChange: zoomOnChange(path, input.onValueChange),
* };
* }
*
* consumerRender = (path: string[]) => {
* // @ts-ignore
* const { Component, ...rest} = this.props;
* // @ts-ignore
* return <Component {...this.projectProps(rest)}/>;
* };
*
* render() {
* return <Consumer>{this.consumerRender}</Consumer>;
* }
* }
*
* function Zoom<K1 extends keyof T, C extends Any<T[K1]>>(...keys: [K1]): React.ComponentClass<ZoomComponentProps<C>>;
* function Zoom<K1 extends keyof T, K2 extends keyof T[K1], C extends Any<T[K1][K2]>>(...keys: [K1, K2]): React.ComponentClass<ZoomComponentProps<C>>;
* function Zoom<K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2], C extends Any<T[K1][K2][K3]>>(...keys: [K1, K2]): React.ComponentClass<ZoomComponentProps<C>>;
* function Zoom(...keys): any {
* return props => <Provider value={keys}><ZoomComponent {...props}/></Provider>;
* }
*
* return Zoom;
* }
*
* const zoomOnChange = memoize((path: ObjectKey[], onChange: ((x: any, at: ObjectPath) => void)|undefined) => {
* return (next, at=[]) => {
* onChange && onChange(next, [...path, ...at]);
* };
* });
*
* const zoomAny = memoize((path: ObjectKey[], input: any) => {
* let iter = input as any;
* for (const key of path) {
* if (typeof(iter) !== 'object' || iter === null) return iter;
* iter = iter[key];
* }
* return iter;
* });
*
* */
// Props
type Props = {
model: {
initial: Form;
modified: Form|null;
},
dispatch(action: any): void;
}
// Form
type Form = {
login: string;
password: string;
one: {
two: {
'name': string;
three: {
counter: number
},
},
},
}
// @ts-ignore
class TextField extends React.Component<FieldProps<string>> {
render() { return <div/>; }
}
// @ts-ignore
declare const NumberField: React.ReactType<FieldProps<number>>;
// @ts-ignore
declare const FormField: React.ReactType<FieldProps<Form['one']>>;
type T = Form;
/* function Zoom2(props: { in: keyof T }, context) {
* return <div></div>;
* }; */
declare function Zoom2<K1 extends keyof T, C extends Any<T[K1]>>(props: { in: K1, Component: C }, context): JSX.Element;
declare function Zoom2<K1 extends keyof T, K2 extends keyof T[K1], C extends Any<T[K1][K2]>>(props: { in: [K1, K2], Component: C }, context): JSX.Element;
declare function Zoom2<K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2], C extends Any<T[K1][K2][K3]>>(props: { in: [K1, K2, K3], Component: C }, context): JSX.Element;
export class Widget extends React.Component<Props> {
render() {
// const Zoom = createZoom<Form>(this);
// const ZoomName = Zoom('login');
return <div>
<Zoom2
in="one"
Component={FormField}
/>
</div>;
}
}
...@@ -2,7 +2,6 @@ import * as React from 'react'; ...@@ -2,7 +2,6 @@ import * as React from 'react';
import { AuthCtx as Ctx } from '~/context'; import { AuthCtx as Ctx } from '~/context';
import { I18nString } from '~/gettext'; import { I18nString } from '~/gettext';
import { ObjectPath } from '~/functions/get-at'; import { ObjectPath } from '~/functions/get-at';
const hoistNonReactStatics = require('hoist-non-react-statics');
export const { Provider, Consumer } = React.createContext<FieldProps>(void 0 as any); export const { Provider, Consumer } = React.createContext<FieldProps>(void 0 as any);
...@@ -11,53 +10,15 @@ export const { Provider, Consumer } = React.createContext<FieldProps>(void 0 as ...@@ -11,53 +10,15 @@ export const { Provider, Consumer } = React.createContext<FieldProps>(void 0 as
// Контекст поля // Контекст поля
export interface FieldProps<Value=any> { export interface FieldProps<Value=any> {
ctx?: Ctx; ctx?: Ctx;
value?: Value; value: Value;
disabled?: Disabled; disabled?: Disabled;
error?: Error|Validation<any>; error?: Error|Validation<any>;
FieldWrapper?: React.ComponentType;
onValueChange?(value: Value): void; onValueChange?(value: Value): void;
onValueChange?(value: any, at?: ObjectPath): void; onValueChange?(value: any, at?: ObjectPath): void;
onFocus?(e?: React.SyntheticEvent): void; onFocus?(e?: React.SyntheticEvent): void;
onBlur?(e?: React.SyntheticEvent): void; onBlur?(e?: React.SyntheticEvent): void;
onClick?(e?: React.SyntheticEvent): void; onClick?(e?: React.SyntheticEvent): void;
onKeyDown?(e: React.KeyboardEvent): void; onKeyDown?(e: React.KeyboardEvent): void;
beginAdornments?: React.ReactNode;
endAdornments?: React.ReactNode;
}
// Обертка для полей используемых внутри
export function withFieldContext<P extends FieldProps<any>, C extends React.ComponentType<P>>(Component: C): C {
class WithFieldContext extends React.Component<P> {
render() {
return <Consumer>{this.renderer}</Consumer>;
};
renderer = (context: FieldProps) => {
// @ts-ignore
return React.createElement(Component, { ...this.props, ...context });
};
}
hoistNonReactStatics(WithFieldContext, Component);
return WithFieldContext as any;
}
export interface ModifierProps {
proj(input: FieldProps): FieldProps;
children?: React.ReactNode;
}
// Модификация контекста
export class Modifier extends React.Component<ModifierProps> {
render() {
return <Consumer>{this.renderer}</Consumer>;
}
renderer = context => <Provider value={this.props.proj(context)}>{this.props.children}</Provider>
} }
......
...@@ -24,7 +24,7 @@ export const MomentField = withFieldContext(MomentField_) as typeof MomentField_ ...@@ -24,7 +24,7 @@ export const MomentField = withFieldContext(MomentField_) as typeof MomentField_
export const SelectField = withFieldContext(SelectField_) as typeof SelectField_; export const SelectField = withFieldContext(SelectField_) as typeof SelectField_;
export const TextField = withFieldContext(TextField_) as typeof TextField_; export const TextField = withFieldContext(TextField_) as typeof TextField_;
export const AutoComplete = withFieldContext(AutoComplete_) as typeof AutoComplete_; export const AutoComplete = withFieldContext(AutoComplete_) as typeof AutoComplete_;
export const MultiSelect = withFieldContext(MultiSelect_) as typeof MultiSelect_; export const MultiSelect = withFieldContext(MultiSelect_ as any) as typeof MultiSelect_;
export const undecorated = { export const undecorated = {
......
...@@ -123,9 +123,9 @@ function assignData(dst: Translations, ...srcs: GettextData[]): Translations { ...@@ -123,9 +123,9 @@ function assignData(dst: Translations, ...srcs: GettextData[]): Translations {
const locale = data[''].locale; if (!locale || typeof(locale) !== 'string') return; const locale = data[''].locale; if (!locale || typeof(locale) !== 'string') return;
const explicitKeys = data['%explicit']; const explicitKeys = data['%explicit'];
for (const k of Object.keys(data)) { for (const k of Object.keys(data)) {
if (k === '' || k === '%pairs') continue; if (k === '' || k === '%explicit') continue;
dst[locale] = dst[locale] || {}; dst[locale] = dst[locale] || {};
Object.assign(dst[locale], data[k]); dst[locale][k] = data[k];
} }
if (explicitKeys) for (const explicit of explicitKeys) { if (explicitKeys) for (const explicit of explicitKeys) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment