diff --git a/package-lock.json b/package-lock.json index d3ee26d..c5bb90f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "react-context-form", - "version": "2.11.0", + "version": "3.0.0-0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -48,17 +48,18 @@ "dev": true }, "@types/prop-types": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.5.3.tgz", - "integrity": "sha512-sfjHrNF4zWRv3fJUGyZW46wVxhYJ/GeWIPdKxbnLIhY3bWR0Ncl2kIhZI7rpjY9KtUQAkDP8jWEmaGQGFFvruA==", + "version": "15.5.8", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.5.8.tgz", + "integrity": "sha512-3AQoUxQcQtLHsK25wtTWIoIpgYjH3vSDroZOUr7PpCHw/jLY1RB9z9E8dBT/OSmwStVgkRNvdh+ZHNiomRieaw==", "dev": true }, "@types/react": { - "version": "16.3.17", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.3.17.tgz", - "integrity": "sha512-f2ZTOSF7l9sRdXSbzLI84Z2wsVnj3qUjfJhtDLSi7lTWFMo1WSou7eQ2vkQga8100zhzzDjSyGbj+Viz7i927g==", + "version": "16.8.1", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.1.tgz", + "integrity": "sha512-tD1ETKJcuhANOejRc/p7OgQ16DKnbGi0M3LccelKlPnUCDp2a5koVxZFoRN9HN+A+m84HB5VGN7I+r3nNhS3PA==", "dev": true, "requires": { + "@types/prop-types": "*", "csstype": "^2.2.0" } }, @@ -2074,9 +2075,9 @@ } }, "csstype": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.5.3.tgz", - "integrity": "sha512-G5HnoK8nOiAq3DXIEoY2n/8Vb7Lgrms+jGJl8E4EJpQEeVONEnPFJSl8IK505wPBoxxtrtHhrRm4WX2GgdqarA==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.2.tgz", + "integrity": "sha512-Rl7PvTae0pflc1YtxtKbiSqq20Ts6vpIYOD5WBafl4y123DyHUeLrRdQP66sQW8/6gmX8jrYJLXwNeMqYVJcow==", "dev": true }, "currently-unhandled": { @@ -9449,15 +9450,27 @@ } }, "react": { - "version": "16.4.1", - "resolved": "https://registry.npmjs.org/react/-/react-16.4.1.tgz", - "integrity": "sha512-3GEs0giKp6E0Oh/Y9ZC60CmYgUPnp7voH9fbjWsvXtYFb4EWtgQub0ADSq0sJR0BbHc4FThLLtzlcFaFXIorwg==", + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.7.0.tgz", + "integrity": "sha512-StCz3QY8lxTb5cl2HJxjwLFOXPIFQp+p+hxQfc8WE0QiLfCtIlKj8/+5tjjKm8uSTlAW+fCPaavGFS06V9Ar3A==", "dev": true, "requires": { - "fbjs": "^0.8.16", "loose-envify": "^1.1.0", "object-assign": "^4.1.1", - "prop-types": "^15.6.0" + "prop-types": "^15.6.2", + "scheduler": "^0.12.0" + }, + "dependencies": { + "prop-types": { + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", + "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", + "dev": true, + "requires": { + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" + } + } } }, "react-dom": { @@ -9836,6 +9849,16 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true }, + "scheduler": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.12.0.tgz", + "integrity": "sha512-t7MBR28Akcp4Jm+QoR63XgAi9YgCUmgvDHqf5otgAj4QvdoBE4ImCX0ffehefePPG+aitiYHp0g/mW6s4Tp+dw==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", diff --git a/package.json b/package.json index 70ea4b1..09c7204 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-context-form", - "version": "2.11.0", + "version": "3.0.0-3", "description": "One more way to handle forms on React", "main": "build/index.js", "typings": "build/index.d.ts", @@ -37,8 +37,7 @@ "@types/enzyme": "^2.8.9", "@types/mocha": "^2.2.48", "@types/node": "^10.3.3", - "@types/prop-types": "^15.5.3", - "@types/react": "^16.3.17", + "@types/react": "^16.8.1", "@types/sinon": "^2.3.3", "awesome-typescript-loader": "^3.5.0", "axios": "^0.18.0", @@ -62,8 +61,7 @@ "mocha": "^5.2.0", "nyc": "^11.9.0", "pre-commit": "^1.2.2", - "prop-types": "^15.6.1", - "react": "^16.4.1", + "react": "^16.7.0", "react-dom": "^16.4.1", "react-test-renderer": "^16.4.1", "sinon": "^6.0.0", @@ -78,9 +76,9 @@ "webpack-node-externals": "^1.7.2" }, "peerDependencies": { + "axios": "^0.18.0", "class-validator": "^0.7.2", - "prop-types": "^15.6.0", - "react": "^16.2.0" + "react": "^16.7.0" }, "nyc": { "extension": [ @@ -99,7 +97,6 @@ ], "report-dir": "./tests/output" }, - "dependencies": {}, "pre-commit": [ "lint", "test" diff --git a/src/AutoFocus/AutoFocus.tsx b/src/AutoFocus/AutoFocus.tsx index 2aba4a5..b20a336 100644 --- a/src/AutoFocus/AutoFocus.tsx +++ b/src/AutoFocus/AutoFocus.tsx @@ -1,16 +1,17 @@ import * as React from "react"; -import * as PropTypes from "prop-types"; -import { AutoValidate } from "../AutoValidate/AutoValidate"; -import { AutoFocusProps, AutoFocusPropTypes } from "./AutoFocusProps"; -import { AutoFocusContext, AutoFocusContextTypes } from "./AutoFocusContext"; +import { AutoValidate, AutoValidateProps } from "../AutoValidate"; +import { FormContext, FormContextValue } from "../Form"; -export class AutoFocus extends React.Component { - public static readonly propTypes = AutoFocusPropTypes; - public static readonly contextTypes = AutoFocusContextTypes; +export interface AutoFocusProps extends AutoValidateProps { + to: string, +} + +export class AutoFocus extends React.PureComponent { + public static readonly contextType = FormContext; public props: AutoFocusProps; - public context: AutoFocusContext; + public context: FormContextValue; public render(): JSX.Element { const { to, ...childProps } = this.props; diff --git a/src/AutoFocus/AutoFocusContext.ts b/src/AutoFocus/AutoFocusContext.ts deleted file mode 100644 index 8dd6fcb..0000000 --- a/src/AutoFocus/AutoFocusContext.ts +++ /dev/null @@ -1,9 +0,0 @@ -import * as PropTypes from "prop-types"; - -export interface AutoFocusContext { - getDOMElement: (attribute: string) => HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | undefined, -} - -export const AutoFocusContextTypes: {[P in keyof AutoFocusContext]: PropTypes.Validator} = { - getDOMElement: PropTypes.func.isRequired, -}; diff --git a/src/AutoFocus/AutoFocusProps.ts b/src/AutoFocus/AutoFocusProps.ts deleted file mode 100644 index bf348d4..0000000 --- a/src/AutoFocus/AutoFocusProps.ts +++ /dev/null @@ -1,12 +0,0 @@ -import * as PropTypes from "prop-types"; - -import { AutoValidateProps, AutoValidatePropTypes } from "../AutoValidate/AutoValidateProps"; - -export interface AutoFocusProps extends AutoValidateProps { - to: string, -} - -export const AutoFocusPropTypes: {[P in keyof AutoFocusProps]: PropTypes.Validator} = { - to: PropTypes.string.isRequired, - ...AutoValidatePropTypes -}; diff --git a/src/AutoFocus/index.ts b/src/AutoFocus/index.ts index 4dcba60..b2163f9 100644 --- a/src/AutoFocus/index.ts +++ b/src/AutoFocus/index.ts @@ -1,3 +1 @@ export * from "./AutoFocus"; -export * from "./AutoFocusContext"; -export * from "./AutoFocusProps"; diff --git a/src/AutoUpdate/AutoUpdate.ts b/src/AutoUpdate/AutoUpdate.ts deleted file mode 100644 index 6ce00db..0000000 --- a/src/AutoUpdate/AutoUpdate.ts +++ /dev/null @@ -1,55 +0,0 @@ -import * as React from "react"; -import * as PropTypes from "prop-types"; - -import { InputContextTypes, InputContext } from "../Input"; -import { AutoUpdateProps, AutoUpdatePropTypes, AutoUpdateDefaultProps } from "./AutoUpdateProps"; -import { AutoUpdateContext, AutoUpdateContextTypes } from "./AutoUpdateContext"; - -export class AutoUpdate extends React.Component { - public static readonly propTypes = AutoUpdatePropTypes; - public static readonly defaultProps = AutoUpdateDefaultProps; - public static readonly contextTypes = { - ...InputContextTypes, - getDOMElement: PropTypes.func.isRequired - }; - - public static readonly childContextTypes = AutoUpdateContextTypes; - - public context: InputContext & { - getDOMElement: (attribute: string) => HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | undefined, - }; - - public getChildContext(): AutoUpdateContext { - return { - onBlur: this.handleBlur, - onChange: this.handleChange, - } - } - - public render(): JSX.Element { - return this.props.children; - } - - protected handleUpdate = (): void => { - this.context.onAttributeChange(this.props.attribute, this.props.value(this.context.value)); - - const element = this.context.getDOMElement(this.props.attribute); - if (element instanceof HTMLElement) { - // blur event does not triggered if element not focused - element.focus(); - element.blur(); - } - } - - protected handleBlur = (): void => { - this.props.onBlur && this.handleUpdate(); - - this.context.onBlur(); - } - - protected handleChange = (value: any): void => { - this.props.onChange && this.context.onAttributeChange(this.props.attribute, this.props.value(value)); - - this.context.onChange(value); - } -} diff --git a/src/AutoUpdate/AutoUpdate.tsx b/src/AutoUpdate/AutoUpdate.tsx new file mode 100644 index 0000000..75c161b --- /dev/null +++ b/src/AutoUpdate/AutoUpdate.tsx @@ -0,0 +1,67 @@ +import * as React from "react"; + +import { FormContext, FormContextValue } from "../Form"; +import { FormGroupContext, FormGroupContextValue } from "../FormGroup"; + +export interface AutoUpdateProps { + value: (value: any) => any, + attribute: string, + children: JSX.Element, + onBlur?: boolean, + onChange?: boolean, +} + +export const AutoUpdateDefaultProps = { + onBlur: true, + onChange: false, +}; + +class AutoUpdateLayout extends React.PureComponent< + AutoUpdateProps & { getDOMElement: FormContextValue["getDOMElement"]} +> { + public static readonly defaultProps = AutoUpdateDefaultProps; + public static readonly contextType = FormGroupContext; + + public context: FormGroupContextValue; + + public render(): JSX.Element { + return + } + + protected get childContextValue(): FormGroupContextValue { + return { + ...this.context, + onBlur: this.handleBlur, + onChange: this.handleChange, + } + } + + protected handleUpdate = (): void => { + this.context.onAttributeChange(this.props.attribute, this.props.value(this.context.value)); + + const element = this.props.getDOMElement(this.props.attribute); + if (element instanceof HTMLElement) { + // blur event does not triggered if element not focused + element.focus(); + element.blur(); + } + } + + protected handleBlur = (): void => { + this.props.onBlur && this.handleUpdate(); + + this.context.onBlur(); + } + + protected handleChange = (value: any): void => { + this.props.onChange && this.context.onAttributeChange(this.props.attribute, this.props.value(value)); + + this.context.onChange(value); + } +} + +export const AutoUpdate = ((props: AutoUpdateProps) => ( + + {(context: FormContextValue) => } + +)); diff --git a/src/AutoUpdate/AutoUpdateContext.ts b/src/AutoUpdate/AutoUpdateContext.ts deleted file mode 100644 index a942004..0000000 --- a/src/AutoUpdate/AutoUpdateContext.ts +++ /dev/null @@ -1,11 +0,0 @@ -import * as PropTypes from "prop-types"; - -export interface AutoUpdateContext { - onBlur: () => void, - onChange: (value: any) => void, -} - -export const AutoUpdateContextTypes: {[P in keyof AutoUpdateContext]: PropTypes.Validator} = { - onBlur: PropTypes.func.isRequired, - onChange: PropTypes.func.isRequired, -}; diff --git a/src/AutoUpdate/AutoUpdateProps.ts b/src/AutoUpdate/AutoUpdateProps.ts deleted file mode 100644 index e7ee0d8..0000000 --- a/src/AutoUpdate/AutoUpdateProps.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as PropTypes from "prop-types"; - -export interface AutoUpdateProps { - value: (value: any) => any, - attribute: string, - children: JSX.Element, - onBlur?: boolean, - onChange?: boolean, -} - -export const AutoUpdatePropTypes: {[P in keyof AutoUpdateProps]: PropTypes.Validator} = { - value: PropTypes.func.isRequired, - attribute: PropTypes.string.isRequired, - children: PropTypes.element.isRequired, - onBlur: PropTypes.bool, - onChange: PropTypes.bool, -}; - -export const AutoUpdateDefaultProps = { - onBlur: true, - onChange: false, -}; diff --git a/src/AutoUpdate/index.ts b/src/AutoUpdate/index.ts index 145297c..5e5ad1d 100644 --- a/src/AutoUpdate/index.ts +++ b/src/AutoUpdate/index.ts @@ -1,3 +1 @@ export * from "./AutoUpdate"; -export * from "./AutoUpdateProps"; -export * from "./AutoUpdateContext"; diff --git a/src/AutoValidate/AutoValidate.tsx b/src/AutoValidate/AutoValidate.tsx index da37c49..75ebb35 100644 --- a/src/AutoValidate/AutoValidate.tsx +++ b/src/AutoValidate/AutoValidate.tsx @@ -1,39 +1,51 @@ import * as React from "react"; -import * as PropTypes from "prop-types"; - -import { AutoValidateDefaultProps, AutoValidateProps, AutoValidatePropTypes } from "./AutoValidateProps"; -import { AutoValidateContext, AutoValidateContextTypes } from "./AutoValidateContext"; -import { InputContext } from "../Input/InputContext"; -import { InputContextTypes } from "../Input"; +import { FormContext, FormContextValue } from "../Form"; import { ModelError } from "../Model"; +import { FormGroupContext, FormGroupContextValue } from "../FormGroup"; + +export interface AutoValidateProps { + groupName?: string, + children: JSX.Element, + + onBlur?: boolean, + onChange?: boolean, + onLength?: number, + always?: boolean + + onValidated?: (isValid: boolean) => void, -export class AutoValidate extends React.Component { - public static readonly propTypes = AutoValidatePropTypes; + on?: (nextValue: string, previousValue: string) => boolean +} + +export const AutoValidateDefaultProps: {[P in keyof AutoValidateProps]?: AutoValidateProps[P]} = { + onBlur: true, + onChange: false, + always: false, + onValidated: () => undefined, +}; + +class AutoValidateLayout extends React.PureComponent< + AutoValidateProps & { validate: (group: string) => Promise} +> { public static readonly defaultProps = AutoValidateDefaultProps; - public static readonly childContextTypes = AutoValidateContextTypes; + public static readonly contextType = FormGroupContext; - public static readonly contextTypes = { - ...InputContextTypes, - validate: PropTypes.func.isRequired, - }; + public context: FormGroupContextValue; - public context: InputContext & { - readonly validate: (group: string) => Promise; - }; + public render(): JSX.Element { + return ; + } - public getChildContext(): AutoValidateContext { + public get childContextValue(): FormGroupContextValue { const isOnChange = this.props.onChange || this.props.onLength || this.props.on || this.props.always; const isOnBlur = this.props.onBlur || this.props.always; return { + ...this.context, onChange: isOnChange ? this.handleChange : this.context.onChange, onBlur: isOnBlur ? this.handleBlur : this.context.onBlur, }; } - public render(): JSX.Element { - return this.props.children; - } - protected handleChange = (nextValue: any): void => { const previousValue = this.context.value; const onChange = this.context.onChange(nextValue); @@ -54,7 +66,13 @@ export class AutoValidate extends React.Component { }; protected async validate(): Promise { - const errors = await this.context.validate(this.props.groupName || this.context.name); + const errors = await this.props.validate(this.props.groupName || this.context.name); this.props.onValidated && this.props.onValidated(errors.length === 0); } } + +export const AutoValidate = (props: AutoValidateProps) => ( + + {(context: FormContextValue) => } + +); diff --git a/src/AutoValidate/AutoValidateContext.ts b/src/AutoValidate/AutoValidateContext.ts deleted file mode 100644 index 5b38474..0000000 --- a/src/AutoValidate/AutoValidateContext.ts +++ /dev/null @@ -1,11 +0,0 @@ -import * as PropTypes from "prop-types"; - -export interface AutoValidateContext { - onChange: (value: any) => void, - onBlur: () => void, -} - -export const AutoValidateContextTypes: {[P in keyof AutoValidateContext]: PropTypes.Validator} = { - onChange: PropTypes.func.isRequired, - onBlur: PropTypes.func.isRequired, -}; diff --git a/src/AutoValidate/AutoValidateProps.ts b/src/AutoValidate/AutoValidateProps.ts deleted file mode 100644 index 8a4ad76..0000000 --- a/src/AutoValidate/AutoValidateProps.ts +++ /dev/null @@ -1,35 +0,0 @@ -import * as PropTypes from "prop-types"; - -export interface AutoValidateProps { - groupName?: string, - children: JSX.Element, - - onBlur?: boolean, - onChange?: boolean, - onLength?: number, - always?: boolean - - onValidated?: (isValid: boolean) => void, - - on?: (nextValue: string, previousValue: string) => boolean -} - -export const AutoValidatePropTypes: {[P in keyof AutoValidateProps]: PropTypes.Validator} = { - children: PropTypes.element.isRequired, - groupName: PropTypes.string, - - onBlur: PropTypes.bool, - onChange: PropTypes.bool, - onLength: PropTypes.number, - always: PropTypes.bool, - onValidated: PropTypes.func, - - on: PropTypes.func -}; - -export const AutoValidateDefaultProps: {[P in keyof AutoValidateProps]?: AutoValidateProps[P]} = { - onBlur: true, - onChange: false, - always: false, - onValidated: () => undefined, -}; diff --git a/src/AutoValidate/index.ts b/src/AutoValidate/index.ts index 6e2cea4..8bdb350 100644 --- a/src/AutoValidate/index.ts +++ b/src/AutoValidate/index.ts @@ -1,2 +1 @@ export * from "./AutoValidate"; -export * from "./AutoValidateProps"; diff --git a/src/Button/BaseButton.tsx b/src/Button/BaseButton.tsx index b7d2254..3159a1a 100644 --- a/src/Button/BaseButton.tsx +++ b/src/Button/BaseButton.tsx @@ -1,12 +1,10 @@ import * as React from "react"; -import * as PropTypes from "prop-types"; +import { FormGroupContext, FormGroupContextValue } from "../FormGroup"; -import { InputContext, InputContextTypes } from "../Input"; +export abstract class BaseButton extends React.Component & T> { + public static contextType = FormGroupContext; -export abstract class BaseButton extends React.Component> { - public static contextTypes = InputContextTypes; - - public context: InputContext; + public context: FormGroupContextValue; public get childProps(): React.HTMLProps { return { diff --git a/src/Button/Button.tsx b/src/Button/Button.tsx index 21e6c6b..a98ed2a 100644 --- a/src/Button/Button.tsx +++ b/src/Button/Button.tsx @@ -1,11 +1,18 @@ import * as React from "react"; -import * as PropTypes from "prop-types"; import { BaseButton } from "./BaseButton"; -import { ButtonDefaultProps, ButtonProps, ButtonPropTypes } from "./ButtonProps"; + +export interface ButtonProps extends React.HTMLProps { + action: any, + activeClassName?: string +} + +export const ButtonDefaultProps: {[P in keyof ButtonProps]?: ButtonProps[P]} = { + className: "btn", + activeClassName: "is-active" +}; export class Button extends BaseButton { - public static readonly propTypes = ButtonPropTypes; public static readonly defaultProps = ButtonDefaultProps; public props: ButtonProps; diff --git a/src/Button/ButtonProps.ts b/src/Button/ButtonProps.ts deleted file mode 100644 index f3c6785..0000000 --- a/src/Button/ButtonProps.ts +++ /dev/null @@ -1,17 +0,0 @@ -import * as React from "react"; -import * as PropTypes from "prop-types"; - -export interface ButtonProps extends React.HTMLProps { - action: any, - activeClassName?: string -} - -export const ButtonPropTypes: {[P in keyof ButtonProps]: PropTypes.Validator} = { - action: PropTypes.any.isRequired, - activeClassName: PropTypes.string -}; - -export const ButtonDefaultProps: {[P in keyof ButtonProps]?: ButtonProps[P]} = { - className: "btn", - activeClassName: "is-active" -}; diff --git a/src/Button/Checkbox.tsx b/src/Button/Checkbox.tsx index 408cc84..2b32b31 100644 --- a/src/Button/Checkbox.tsx +++ b/src/Button/Checkbox.tsx @@ -1,14 +1,18 @@ import * as React from "react"; -import * as PropTypes from "prop-types"; import { BaseButton } from "./BaseButton"; -import { CheckboxDefaultProps, CheckboxProps, CheckboxPropTypes } from "./CheckboxProps"; -export class Checkbox extends BaseButton { - public static readonly propTypes = CheckboxPropTypes; - public static readonly defaultProps = CheckboxDefaultProps; +export interface CheckboxProps extends React.HTMLProps { + activeClassName?: string +} - public props: CheckboxProps; +export const CheckboxDefaultProps: {[P in keyof CheckboxProps]?: CheckboxProps[P]} = { + className: "checkbox", + activeClassName: "is-active" +}; + +export class Checkbox extends BaseButton { + public static readonly defaultProps = CheckboxDefaultProps; public render(): JSX.Element { const { activeClassName, ...HTMLProps } = this.props; diff --git a/src/Button/CheckboxProps.ts b/src/Button/CheckboxProps.ts deleted file mode 100644 index 5dfccd9..0000000 --- a/src/Button/CheckboxProps.ts +++ /dev/null @@ -1,15 +0,0 @@ -import * as React from "react"; -import * as PropTypes from "prop-types"; - -export interface CheckboxProps extends React.HTMLProps { - activeClassName?: string -} - -export const CheckboxPropTypes: {[P in keyof CheckboxProps]: PropTypes.Validator} = { - activeClassName: PropTypes.string -}; - -export const CheckboxDefaultProps: {[P in keyof CheckboxProps]?: CheckboxProps[P]} = { - className: "checkbox", - activeClassName: "is-active" -}; diff --git a/src/Button/index.ts b/src/Button/index.ts index 48b06f8..c7e7ca2 100644 --- a/src/Button/index.ts +++ b/src/Button/index.ts @@ -1,7 +1,3 @@ export * from "./BaseButton"; - export * from "./Button"; -export * from "./ButtonProps"; - export * from "./Checkbox"; -export * from "./CheckboxProps"; diff --git a/src/Decorators/ApplyFilter.ts b/src/Decorators/ApplyFilter.ts new file mode 100644 index 0000000..b5c0531 --- /dev/null +++ b/src/Decorators/ApplyFilter.ts @@ -0,0 +1,15 @@ +export function ApplyFilter(filter: (property: any) => T): PropertyDecorator { + return (target: any, propertyKey: string | symbol): void => { + let value = target[propertyKey]; + + Object.defineProperty(target, propertyKey, { + get(): T { + return value; + }, + set(newValue: T): void { + value = filter(newValue); + }, + configurable: true, + }); + }; +} diff --git a/src/Decorators/MatchToField.ts b/src/Decorators/MatchToField.ts new file mode 100644 index 0000000..5d85bba --- /dev/null +++ b/src/Decorators/MatchToField.ts @@ -0,0 +1,16 @@ +import { registerDecorator, ValidationArguments, ValidationOptions } from "class-validator"; + +export function MatchToField(field: string, validationOptions?: ValidationOptions) { + return (object: object, propertyName: string) => { + registerDecorator({ + propertyName, + target: object.constructor, + options: validationOptions, + validator: { + validate(value: string, args: ValidationArguments): boolean { + return value.toString() === args.object[field].toString(); + } + } + }) + } +} diff --git a/src/Decorators/Matches.ts b/src/Decorators/Matches.ts new file mode 100644 index 0000000..d1c4a3b --- /dev/null +++ b/src/Decorators/Matches.ts @@ -0,0 +1,16 @@ +import { registerDecorator, ValidationOptions } from "class-validator"; + +export function Matches(pattern: RegExp, validationOptions?: ValidationOptions) { + return (object: object, propertyName: string) => { + registerDecorator({ + target: object.constructor, + propertyName, + options: validationOptions, + validator: { + validate(value: string): boolean { + return !!value && pattern.test(value.toString()); + } + } + }) + } +} diff --git a/src/Decorators/index.ts b/src/Decorators/index.ts new file mode 100644 index 0000000..8286482 --- /dev/null +++ b/src/Decorators/index.ts @@ -0,0 +1,3 @@ +export * from "./Matches"; +export * from "./ApplyFilter"; +export * from "./MatchToField"; diff --git a/src/EventInterceptor/EventInterceptor.tsx b/src/EventInterceptor/EventInterceptor.tsx index 61e1f3f..b6360a7 100644 --- a/src/EventInterceptor/EventInterceptor.tsx +++ b/src/EventInterceptor/EventInterceptor.tsx @@ -1,8 +1,8 @@ import * as React from "react"; -import * as PropTypes from "prop-types"; -import { FormContextTypes, FormContext } from "../Form"; +import { FormContext, FormContextValue } from "../Form"; import { ModelValue } from "../Model"; +import { EventInterceptorContext, EventInterceptorContextValue } from "./EventInterceptorContext"; export enum Event { onChange = "onChange", @@ -17,48 +17,37 @@ export interface EventInterceptorProps { events: Event[]; } -export const EventInterceptorPropTypes: {[P in keyof EventInterceptorProps]: PropTypes.Validator} = { - events: PropTypes.arrayOf(PropTypes.oneOf(Object.values(Event))).isRequired, - onChange: PropTypes.func, - onFocus: PropTypes.func, - onBlur: PropTypes.func -}; - -export interface EventInterceptorContext { - onChange: (attribute: string, value: any) => void; - onFocus?: (attribute: string, value: any) => void; - onBlur?: (attribute: string, value: any) => void; -} - -export const EventInterceptorContextTypes: {[P in keyof EventInterceptorContext]: PropTypes.Validator} = { - onChange: PropTypes.func.isRequired, - onFocus: PropTypes.func, - onBlur: PropTypes.func -} - export class EventInterceptor extends React.Component { - public static readonly childContextTypes = EventInterceptorContextTypes; - public static readonly propTypes = EventInterceptorPropTypes; - public static readonly contextTypes = FormContextTypes; + public static readonly contextType = FormContext; + + public readonly context: FormContextValue; - public readonly context: FormContext; + public render(): React.ReactNode { + return ( + + + + ); + } - public getChildContext(): EventInterceptorContext { + protected get formContextValue(): FormContextValue { return { + ...this.context, onChange: this.props.events.includes(Event.onChange) ? this.handleChange : this.context.onChange, - onFocus: this.props.events.includes(Event.onFocus) ? this.props.onFocus : undefined, - onBlur: this.props.events.includes(Event.onBlur) ? this.props.onBlur : undefined }; } - public render(): React.ReactNode { - return this.props.children; + protected get childContextValue(): EventInterceptorContextValue { + return { + onFocus: this.props.events.includes(Event.onFocus) ? this.props.onFocus : undefined, + onBlur: this.props.events.includes(Event.onBlur) ? this.props.onBlur : undefined + }; } protected handleChange = (attribute: string, value: any): void => { this.props.onChange && this.props.onChange(attribute, value, this.getValue(attribute)); this.context.onChange(attribute, value); - } + }; protected getValue = (name: string): string => { const founded = this.context.values.find((value: ModelValue) => value.attribute === name); diff --git a/src/EventInterceptor/EventInterceptorContext.ts b/src/EventInterceptor/EventInterceptorContext.ts new file mode 100644 index 0000000..fc570ab --- /dev/null +++ b/src/EventInterceptor/EventInterceptorContext.ts @@ -0,0 +1,8 @@ +import * as React from "react"; + +export interface EventInterceptorContextValue { + onFocus?: (attribute: string, value: any) => void; + onBlur?: (attribute: string, value: any) => void; +} + +export const EventInterceptorContext = React.createContext({}); diff --git a/src/EventInterceptor/index.ts b/src/EventInterceptor/index.ts index 26571b0..bb2fcab 100644 --- a/src/EventInterceptor/index.ts +++ b/src/EventInterceptor/index.ts @@ -1 +1,2 @@ export * from "./EventInterceptor"; +export * from "./EventInterceptorContext"; diff --git a/src/Form/Form.tsx b/src/Form/Form.tsx index 3c75573..43a09d6 100644 --- a/src/Form/Form.tsx +++ b/src/Form/Form.tsx @@ -1,12 +1,22 @@ import * as React from "react"; -import * as PropTypes from "prop-types"; - -import { Model, ModelError } from "../Model"; -import { FormContext, FormContextTypes } from "./FormContext"; -import { FormProps, FormPropTypes, StorageRequiredInterface } from "./FormProps"; - +import { Model, ModelError, ModelInterface } from "../Model"; +import { FormContext, FormContextValue } from "./FormContext"; import { addError } from "../helpers"; +export type StorageRequiredInterface = Pick; + +export interface FormProps { + instantiate: () => M; /* This method will be used for creating model instance in Form state */ + method?: string; /* Name of method of model, which will be called on form submit */ + onSubmit?: (model: M, childContext: FormContextValue) => Promise; // will be called if no method provided + storageKey?: string; /* If provided Model will be saved to localStorage on unmount and loaded on mount */ + resetAfterSubmit?: boolean; + afterSubmit?: () => void; + storage?: StorageRequiredInterface; + onValidate?: (groups: Array<{ name: string, isValid: boolean }>) => void; + formRef?: (node: HTMLFormElement) => void; +} + export interface FormState { model: M; mounted: { [key: string]: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement; }; @@ -16,8 +26,6 @@ export interface FormState { declare const localStorage: StorageRequiredInterface | undefined; export class Form extends React.Component & FormProps, FormState> { - public static readonly childContextTypes = FormContextTypes; - public static readonly propTypes = FormPropTypes; public state: FormState; public storage = this.props.storage || ((typeof window).toLowerCase() !== "undefined" ? localStorage : undefined); @@ -32,24 +40,6 @@ export class Form }; } - public getChildContext(): FormContext { - return { - onChange: this.handleChange, - values: this.state.model.values, - - onMount: this.handleMount, - onUnmount: this.handleUnmount, - onReset: this.handleReset, - - validate: this.validate, - getDOMElement: this.getDOMElement, - - isLoading: this.state.isLoading, - addError: this.handleErrorAdded, - getError: this.state.model.getError, - }; - } - public async componentWillMount() { this.loadFromStorage(); await this.state.model.get(); @@ -74,7 +64,7 @@ export class Form if ("function" === (typeof action).toLowerCase()) { await action() } else if (this.props.onSubmit) { - await this.props.onSubmit(this.state.model, this.getChildContext()); + await this.props.onSubmit(this.state.model, this.childContextValue); } } catch (error) { submitError = error; @@ -90,7 +80,7 @@ export class Form this.forceUpdate(); if (submitError) { - return addError(this.getChildContext(), submitError); + return addError(this.childContextValue, submitError); } this.props.resetAfterSubmit && this.state.model.reset(); @@ -114,9 +104,11 @@ export class Form } return ( -
- {this.props.children} -
+ +
+ {this.props.children} +
+
); } @@ -163,6 +155,24 @@ export class Form this.storage.setItem(this.props.storageKey, JSON.stringify(localStorageValue)); } + protected get childContextValue(): FormContextValue { + return { + onChange: this.handleChange, + values: this.state.model.values, + + onMount: this.handleMount, + onUnmount: this.handleUnmount, + onReset: this.handleReset, + + validate: this.validate, + getDOMElement: this.getDOMElement, + + isLoading: this.state.isLoading, + addError: this.handleErrorAdded, + getError: this.state.model.getError, + }; + } + protected handleChange = (attribute: string, value: any): void => { if (this.state.model[attribute] === value) { return; diff --git a/src/Form/FormContext.ts b/src/Form/FormContext.ts index 562da96..7182901 100644 --- a/src/Form/FormContext.ts +++ b/src/Form/FormContext.ts @@ -1,8 +1,7 @@ -import * as PropTypes from "prop-types"; - +import * as React from "react"; import { ModelValue, ModelInterface, ModelError } from "../Model"; -export interface FormContext { +export interface FormContextValue { values: ModelValue[]; addError: (newError: ModelError) => void, getError: (attribute: string) => ModelError | undefined, @@ -19,18 +18,17 @@ export interface FormContext { isLoading: boolean; } -export const FormContextTypes: {[P in keyof FormContext]: PropTypes.Validator} = { - values: PropTypes.arrayOf(PropTypes.object).isRequired, - addError: PropTypes.func.isRequired, - getError: PropTypes.func.isRequired, - - onChange: PropTypes.func.isRequired, - onMount: PropTypes.func.isRequired, - onUnmount: PropTypes.func.isRequired, - onReset: PropTypes.func.isRequired, - - validate: PropTypes.func.isRequired, - getDOMElement: PropTypes.func.isRequired, - - isLoading: PropTypes.bool.isRequired, +export const FormContextDefaultValue: FormContextValue = { + values: [], + addError: () => undefined, + getError: () => undefined, + onChange: () => undefined, + onMount: () => undefined, + onUnmount: () => undefined, + onReset: () => undefined, + validate: () => Promise.resolve([]), + getDOMElement: () => undefined, + isLoading: false, }; + +export const FormContext = React.createContext(FormContextDefaultValue); diff --git a/src/Form/FormProps.ts b/src/Form/FormProps.ts deleted file mode 100644 index 39c1747..0000000 --- a/src/Form/FormProps.ts +++ /dev/null @@ -1,37 +0,0 @@ -import * as React from "react"; -import * as PropTypes from "prop-types"; - -import { FormContext } from "./FormContext"; -import { ModelInterface, ModelValue } from "../Model"; - -export type StorageRequiredInterface = Pick; - -export const StorageRequiredInterfaceTypes: - {[P in keyof StorageRequiredInterface]: PropTypes.Validator} = { - getItem: PropTypes.func.isRequired, - setItem: PropTypes.func.isRequired - }; - -export interface FormProps { - instantiate: () => M; /* This method will be used for creating model instance in Form state */ - method?: string; /* Name of method of model, which will be called on form submit */ - onSubmit?: (model: M, childContext: FormContext) => Promise; // will be called if no method provided - storageKey?: string; /* If provided Model will be saved to localStorage on unmount and loaded on mount */ - resetAfterSubmit?: boolean; - afterSubmit?: () => void; - storage?: StorageRequiredInterface; - onValidate?: (groups: Array<{ name: string, isValid: boolean }>) => void; - formRef?: (node: HTMLFormElement) => void; -} - -export const FormPropTypes: {[P in keyof FormProps]: PropTypes.Validator} = { - instantiate: PropTypes.func.isRequired, - method: PropTypes.string, - onSubmit: PropTypes.func, - storageKey: PropTypes.string, - resetAfterSubmit: PropTypes.bool, - afterSubmit: PropTypes.func, - storage: PropTypes.shape(StorageRequiredInterfaceTypes), - onValidate: PropTypes.func, - formRef: PropTypes.func, -}; diff --git a/src/Form/index.ts b/src/Form/index.ts index d9564d5..87b3848 100644 --- a/src/Form/index.ts +++ b/src/Form/index.ts @@ -1,3 +1,2 @@ export * from "./Form"; -export * from "./FormProps"; export * from "./FormContext"; diff --git a/src/FormGroup/FormGroup.tsx b/src/FormGroup/FormGroup.tsx index 88dc77a..39f4c72 100644 --- a/src/FormGroup/FormGroup.tsx +++ b/src/FormGroup/FormGroup.tsx @@ -1,29 +1,42 @@ import * as React from "react"; -import * as PropTypes from "prop-types"; import { ModelValue } from "../Model"; -import { FormContext, FormContextTypes } from "../Form/FormContext"; -import { FormGroupContext, FormGroupContextTypes } from "./FormGroupContext"; -import { EventInterceptorContext, EventInterceptorContextTypes } from "../EventInterceptor"; -import { FormGroupDefaultProps, FormGroupProps, FormGroupPropTypes } from "./FormGroupProps"; +import { FormContext, FormContextValue } from "../Form"; +import { FormGroupContext, FormGroupContextValue } from "./FormGroupContext"; +import { EventInterceptorContext, EventInterceptorContextValue } from "../EventInterceptor"; + +export interface FormGroupProps extends React.HTMLProps { + name: string; /* field name (will be passed to input) */ + idPrefix?: string; /* id prefix for input and label */ + + errorClassName?: string; /* className, which will be appended when field have error */ + focusClassName?: string; /* className, which will be appended when focus on input */ + valueClassName?: string; + ref?: any; // https://github.com/Microsoft/TypeScript/issues/16019 +} + +export const FormGroupDefaultProps: {[P in keyof FormGroupProps]?: FormGroupProps[P]} = { + className: "form-group", + idPrefix: "rcf", + + errorClassName: "has-error", + focusClassName: "has-focus", + valueClassName: "has-value" +}; export interface FormGroupState { isFocused: boolean; } -export class FormGroup extends React.Component { - public static readonly propTypes = FormGroupPropTypes; +class FormGroupLayout extends React.Component< + FormGroupProps & { interceptor: EventInterceptorContextValue }, + FormGroupState +> { public static readonly defaultProps = FormGroupDefaultProps; - public static readonly childContextTypes = FormGroupContextTypes; - public static readonly contextTypes = { - ...FormContextTypes, - ...EventInterceptorContextTypes - }; + public static readonly contextType = FormContext; - public context: FormContext & EventInterceptorContext; - public state: FormGroupState = { - isFocused: false, - }; + public context: FormContextValue; + public state: FormGroupState = { isFocused: false }; public id: string; constructor(props) { @@ -32,24 +45,6 @@ export class FormGroup extends React.Component { this.id = (Date.now() + Math.random()).toString().replace(/\./g, ""); } - public getChildContext(): FormGroupContext { - const value = this.value; - return { - id: `${this.props.idPrefix}_${this.id}`, - name: this.props.name, - - value: value ? value.value : undefined, - - onChange: this.handleChange, - onAttributeChange: this.context.onChange, - onBlur: this.handleBlur, - onFocus: this.handleFocus, - onMount: this.handleMount, - - error: value ? value.error : undefined, - }; - } - public componentWillUnmount() { this.context.onUnmount(this.props.name); } @@ -58,12 +53,18 @@ export class FormGroup extends React.Component { public handleBlur = (): void => { this.setState({ isFocused: false }); - this.context.onBlur && this.context.onBlur(this.props.name, this.value ? this.value.value : undefined); + this.props.interceptor.onBlur && this.props.interceptor.onBlur( + this.props.name, + this.value ? this.value.value : undefined + ); }; public handleFocus = (): void => { this.setState({ isFocused: true }); - this.context.onFocus && this.context.onFocus(this.props.name, this.value ? this.value.value : undefined); + this.props.interceptor.onFocus && this.props.interceptor.onFocus( + this.props.name, + this.value ? this.value.value : undefined + ); }; public handleMount = (ref: HTMLElement): void => this.context.onMount(this.props.name, ref); @@ -82,16 +83,38 @@ export class FormGroup extends React.Component { errorClassName, focusClassName, valueClassName, + interceptor, ...childProps } = this.props; return ( -
- {this.props.children} -
+ +
+ {this.props.children} +
+
); } + protected get childContextValue(): FormGroupContextValue { + const value = this.value; + + return { + id: `${this.props.idPrefix}_${this.id}`, + name: this.props.name, + + value: value ? value.value : undefined, + + onChange: this.handleChange, + onAttributeChange: this.context.onChange, + onBlur: this.handleBlur, + onFocus: this.handleFocus, + onMount: this.handleMount, + + error: value ? value.error : undefined, + }; + } + protected get className(): string { return [ this.props.className, @@ -110,3 +133,9 @@ export class FormGroup extends React.Component { .trim(); } } + +export const FormGroup = (props: FormGroupProps) => ( + + {(context: EventInterceptorContextValue) => } + +); diff --git a/src/FormGroup/FormGroupContext.ts b/src/FormGroup/FormGroupContext.ts index d9353d4..2f552e5 100644 --- a/src/FormGroup/FormGroupContext.ts +++ b/src/FormGroup/FormGroupContext.ts @@ -1,13 +1,28 @@ -import * as PropTypes from "prop-types" +import * as React from "react"; -import { HintContext, HintContextTypes } from "../Hint"; -import { LabelContext, LabelContextTypes } from "../Label"; -import { InputContext, InputContextTypes } from "../Input/InputContext"; +export interface FormGroupContextValue { + error?: string; + id: string; -export interface FormGroupContext extends LabelContext, HintContext, InputContext { } + name: string; + value: TValue; -export const FormGroupContextTypes: {[P in keyof FormGroupContext]: PropTypes.Validator} = { - ...LabelContextTypes, - ...HintContextTypes, - ...InputContextTypes, + onChange: (value: TValue) => void; + onAttributeChange: (attribute: string, value: any) => void; + onFocus: () => void; + onBlur: () => void; + onMount: (ref: HTMLElement) => void; +} + +export const FormGroupContextDefaultValue: FormGroupContextValue = { + id: "", + name: "", + value: "", + onChange: () => undefined, + onAttributeChange: () => undefined, + onFocus: () => undefined, + onBlur: () => undefined, + onMount: () => undefined, }; + +export const FormGroupContext = React.createContext(FormGroupContextDefaultValue); diff --git a/src/FormGroup/FormGroupProps.ts b/src/FormGroup/FormGroupProps.ts deleted file mode 100644 index 8096545..0000000 --- a/src/FormGroup/FormGroupProps.ts +++ /dev/null @@ -1,30 +0,0 @@ -import * as React from "react"; -import * as PropTypes from "prop-types"; - -export interface FormGroupProps extends React.HTMLProps { - name: string; /* field name (will be passed to input) */ - idPrefix?: string; /* id prefix for input and label */ - - errorClassName?: string; /* className, which will be appended when field have error */ - focusClassName?: string; /* className, which will be appended when focus on input */ - valueClassName?: string; - ref?: any; // https://github.com/Microsoft/TypeScript/issues/16019 -} - -export const FormGroupPropTypes: {[P in keyof FormGroupProps]: PropTypes.Validator} = { - name: PropTypes.string.isRequired, - idPrefix: PropTypes.string, - - errorClassName: PropTypes.string, - focusClassName: PropTypes.string, - valueClassName: PropTypes.string -}; - -export const FormGroupDefaultProps: {[P in keyof FormGroupProps]?: FormGroupProps[P]} = { - className: "form-group", - idPrefix: "rcf", - - errorClassName: "has-error", - focusClassName: "has-focus", - valueClassName: "has-value" -}; diff --git a/src/FormGroup/index.ts b/src/FormGroup/index.ts index 96a07d7..b8b6637 100644 --- a/src/FormGroup/index.ts +++ b/src/FormGroup/index.ts @@ -1,3 +1,2 @@ export * from "./FormGroup"; export * from "./FormGroupContext"; -export * from "./FormGroupProps"; diff --git a/src/Hint/Hint.tsx b/src/Hint/Hint.tsx index c0c015f..562caf1 100644 --- a/src/Hint/Hint.tsx +++ b/src/Hint/Hint.tsx @@ -1,14 +1,13 @@ import * as React from "react"; -import * as PropTypes from "prop-types"; import { HintDefaultProps } from "./HintProps" -import { HintContext, HintContextTypes } from "./HintContext"; +import { FormGroupContext, FormGroupContextValue } from "../FormGroup"; export class Hint extends React.Component> { public static readonly defaultProps = HintDefaultProps; - public static readonly contextTypes = HintContextTypes; + public static readonly contextType = FormGroupContext; - public context: HintContext; + public context: FormGroupContextValue; public get error(): string | undefined { return this.context.error; diff --git a/src/Hint/HintContext.ts b/src/Hint/HintContext.ts deleted file mode 100644 index eecc7ab..0000000 --- a/src/Hint/HintContext.ts +++ /dev/null @@ -1,9 +0,0 @@ -import * as PropTypes from "prop-types"; - -export interface HintContext { - error?: string; -} - -export const HintContextTypes: {[P in keyof HintContext]: PropTypes.Validator} = { - error: PropTypes.string, -}; diff --git a/src/Hint/index.ts b/src/Hint/index.ts index 4934c61..eeb4f97 100644 --- a/src/Hint/index.ts +++ b/src/Hint/index.ts @@ -1,3 +1,2 @@ export * from "./Hint"; -export * from "./HintContext"; export * from "./HintProps"; diff --git a/src/Input/BaseInput.tsx b/src/Input/BaseInput.tsx index a9f6bbe..0be1003 100644 --- a/src/Input/BaseInput.tsx +++ b/src/Input/BaseInput.tsx @@ -1,33 +1,26 @@ import * as React from "react"; -import * as PropTypes from "prop-types"; +import { cursorPositionController } from "../helpers"; +import { TransformTypes } from "./TransformTypes"; +import { FormGroupContext, FormGroupContextValue } from "../FormGroup"; -import { cursorPositionController } from "../helpers/cursorPositionController"; +export interface BaseInputProps extends React.HTMLProps { + transform?: TransformTypes; +} -import { PasswordGroupContextTypes, PasswordGroupContext } from "../PasswordGroup"; -import { BaseInputDefaultProps, BaseInputProps, BaseInputPropTypes } from "./BaseInputProps"; -import { InputContext, InputContextTypes } from "./InputContext"; -import { TransformTypes } from "./TransformTypes"; +export const BaseInputDefaultProps: {[P in keyof BaseInputProps]?: BaseInputProps[P]} = { + transform: TransformTypes.none, + className: "form-control" +}; -export class BaseInput extends React.Component { - public static readonly contextTypes = { - ...InputContextTypes, - ...PasswordGroupContextTypes - }; +export class BaseInput extends React.PureComponent { + public static readonly contextType = FormGroupContext; public static readonly defaultProps = BaseInputDefaultProps; - public static readonly propTypes = BaseInputPropTypes; - public context: InputContext & PasswordGroupContext; + public context: FormGroupContextValue; protected get childProps(): React.HTMLProps & T { const { transform, ...TProps } = this.props as any; // https://github.com/Microsoft/TypeScript/issues/16780 - let passwordInputProps = {}; - if (this.context.isHidden) { - passwordInputProps = { - type: "password" - }; - } - return { ...TProps, @@ -40,7 +33,6 @@ export class BaseInput extends React.Component { onChange: this.handleChange, onBlur: this.handleBlur, onFocus: this.handleFocus, - ...passwordInputProps }; } diff --git a/src/Input/BaseInputProps.ts b/src/Input/BaseInputProps.ts deleted file mode 100644 index d86d2fa..0000000 --- a/src/Input/BaseInputProps.ts +++ /dev/null @@ -1,16 +0,0 @@ -import * as React from "react"; -import * as PropTypes from "prop-types"; -import {TransformTypes} from "./TransformTypes"; - -export interface BaseInputProps extends React.HTMLProps { - transform?: TransformTypes; -} - -export const BaseInputPropTypes: {[P in keyof BaseInputProps]: PropTypes.Validator} = { - transform: PropTypes.oneOf(Object.keys(TransformTypes)) -}; - -export const BaseInputDefaultProps: {[P in keyof BaseInputProps]?: BaseInputProps[P]} = { - transform: TransformTypes.none, - className: "form-control" -}; diff --git a/src/Input/Input.tsx b/src/Input/Input.tsx index 4d2c33a..ccfb2bd 100644 --- a/src/Input/Input.tsx +++ b/src/Input/Input.tsx @@ -1,6 +1,6 @@ import * as React from "react"; -import {BaseInput} from "./BaseInput"; +import { BaseInput } from "./BaseInput"; export class Input extends BaseInput> { diff --git a/src/Input/InputContext.ts b/src/Input/InputContext.ts deleted file mode 100644 index 3cfab6a..0000000 --- a/src/Input/InputContext.ts +++ /dev/null @@ -1,27 +0,0 @@ -import * as PropTypes from "prop-types"; - -export interface InputContext { - id: string; - - name: string; - value: TValue; - - onChange: (value: TValue) => void; - onAttributeChange: (attribute: string, value: any) => void; - onFocus: () => void; - onBlur: () => void; - onMount: (ref: HTMLElement) => void; -} - -export const InputContextTypes: {[P in keyof InputContext]: PropTypes.Validator} = { - id: PropTypes.string.isRequired, - - name: PropTypes.string.isRequired, - value: PropTypes.any, - - onChange: PropTypes.func.isRequired, - onAttributeChange: PropTypes.func.isRequired, - onFocus: PropTypes.func.isRequired, - onBlur: PropTypes.func.isRequired, - onMount: PropTypes.func.isRequired, -}; diff --git a/src/Input/PasswordInput.tsx b/src/Input/PasswordInput.tsx new file mode 100644 index 0000000..e26cece --- /dev/null +++ b/src/Input/PasswordInput.tsx @@ -0,0 +1,17 @@ +import * as React from "react"; +import { BaseInput } from "./BaseInput"; +import { PasswordGroupContext, PasswordGroupContextValue } from "../PasswordGroup"; + +export class PasswordInput extends BaseInput> { + public render() { + return ( + + {(context: PasswordGroupContextValue) => } + + ); + } + + protected input = (context: PasswordGroupContextValue) => ( + + ) +} diff --git a/src/Input/index.ts b/src/Input/index.ts index 2ce5658..544bf2d 100644 --- a/src/Input/index.ts +++ b/src/Input/index.ts @@ -1,9 +1,6 @@ export * from "./Input"; -export * from "./InputContext"; -export * from "./BaseInputProps"; - export * from "./TransformTypes"; - export * from "./BaseInput"; export * from "./TextArea"; export * from "./NumericInput"; +export * from "./PasswordInput"; diff --git a/src/InputRange/InputRange.tsx b/src/InputRange/InputRange.tsx index b5abe0a..be9b63e 100644 --- a/src/InputRange/InputRange.tsx +++ b/src/InputRange/InputRange.tsx @@ -1,30 +1,28 @@ import * as React from "react"; -import * as PropTypes from "prop-types"; -import { FormGroupContextTypes, FormGroupContext } from "../FormGroup"; -import { InputContextTypes, InputContext } from "../Input"; -import { InputRangeProps } from "./InputRangeProps"; +import { FormGroupContext, FormGroupContextValue } from "../FormGroup"; -export class InputRange extends React.Component { - public static contextTypes = FormGroupContextTypes; - public static childContextTypes = InputContextTypes; +export interface InputRangeProps { + max: number, + min?: number, + children: JSX.Element +} - public context: FormGroupContext; +export class InputRange extends React.PureComponent { + public static contextType = FormGroupContext; - public getChildContext(): InputContext { - const { error, ...context } = this.context; + public context: FormGroupContextValue; - return { - ...context, - ...{ - onChange: this.handleChange, - onBlur: this.handleBlur - } - } + public render(): JSX.Element { + return ; } - public render(): JSX.Element { - return this.props.children; + public get childContextValue(): FormGroupContextValue { + return { + ...this.context, + onChange: this.handleChange, + onBlur: this.handleBlur + } } protected handleChange = (value: number): void => { @@ -44,6 +42,8 @@ export class InputRange extends React.Component { }; protected handleBlur = (): void => { + this.context.onBlur(); + let value = Number(this.context.value); if (value > this.props.max) { diff --git a/src/InputRange/InputRangeProps.ts b/src/InputRange/InputRangeProps.ts deleted file mode 100644 index 998bcf5..0000000 --- a/src/InputRange/InputRangeProps.ts +++ /dev/null @@ -1,13 +0,0 @@ -import * as PropTypes from "prop-types"; - -export interface InputRangeProps { - max: number, - min?: number, - children: JSX.Element -} - -export const InputRangePropTypes: {[P in keyof InputRangeProps]: PropTypes.Validator} = { - max: PropTypes.number.isRequired, - min: PropTypes.number, - children: PropTypes.element.isRequired -}; diff --git a/src/InputRange/index.ts b/src/InputRange/index.ts index 9023533..f9e4981 100644 --- a/src/InputRange/index.ts +++ b/src/InputRange/index.ts @@ -1,2 +1 @@ export * from "./InputRange"; -export * from "./InputRangeProps"; diff --git a/src/Label/Label.tsx b/src/Label/Label.tsx index dc735a0..0b895ea 100644 --- a/src/Label/Label.tsx +++ b/src/Label/Label.tsx @@ -1,20 +1,9 @@ import * as React from "react"; -import * as PropTypes from "prop-types"; -import { LabelContext, LabelContextTypes } from "./LabelContext"; +import { FormGroupContext, FormGroupContextValue } from "../FormGroup"; -export class Label extends React.Component> { - public static contextTypes = LabelContextTypes; - public context: LabelContext; - - public render(): JSX.Element { - const childProps = { - ...this.props, - htmlFor: this.context.id, - }; - - return ( -