UNPKG

web-ui-pack

Version:
246 lines (245 loc) 12.5 kB
import WUPBaseElement, { AttributeMap } from "./baseElement"; import IBaseControl from "./controls/baseControl.i"; export declare const enum SubmitActions { /** Disable any action */ none = 0, /** Scroll to first error (if exists) and focus control */ goToError = 1, /** Validate until first error (otherwise validate all) */ validateUntilFirst = 2, /** Validate only that user can change and skip controls that notVisible/disabled/readonly (values from such items still collected) */ validateChangeable = 4, /** Collect to model only changed values */ collectChanged = 8, /** Reset isDirty and assign $value to $initValue for controls (on success only) */ reset = 16, /** Lock the whole form during the pending state (set $isPending = true or provide `promise` to submitEvent.waitFor); * Otherwise user can submit several times in a short time; * If promise resolves during the short-time pending state won't be set, otherwise it takes at least 300ms via helper {@link promiseWait} */ lockOnPending = 32 } declare const tagName = "wup-form"; declare global { namespace WUP.Form { interface SubmitDetails { /** Model collected from controls */ model: Record<string | number, any>; /** Form related to submit event; equal to event.target */ relatedForm: WUPFormElement; /** Event that produced submit event; null if `form.$submit()` is called */ relatedEvent: MouseEvent | KeyboardEvent | null; /** Element that that produced submit event */ submitter: HTMLElement | null; /** Point a promise as callback to allow form show pending state during the promise */ waitFor?: Promise<unknown>; } interface EventMap extends WUP.Base.EventMap { /** Fires before $submit is happened; can be prevented via `e.preventDefault()` */ $willSubmit: CustomEvent<Pick<SubmitDetails, "relatedEvent" | "relatedForm" | "submitter">>; /** Fires by user-submit when validation successful and model is collected * * @tutorial * call `e.preventDefault()` to prevent dispatching `$submitEnd` & closing modal (if form in modal) */ $submit: CustomEvent<SubmitDetails>; /** Fires when submit is end (after http-response); * @tutorial * call `e.preventDefault()` to prevent closing modal (if form in modal) */ $submitEnd: CustomEvent<{ success: boolean; }>; } interface Options { /** Actions that enabled on submit event; You can point several like: `goToError | collectChanged` * @defaultValue goToError | validateUntilFirst | reset | lockOnPending */ submitActions: SubmitActions; /** Enable to store data in localStorage to prevent losing till submitted; * @defaultValue false * @tutorial Troubleshooting * * It doesn't save values that are complex objects. So `wup-select.$options.items = [{text: "N1",value: {id:1,name:'Nik'} }]` is skipped * * Point string-value if default storage-key doesn't fit: based on `url+control.names` @see{@link WUPFormElement.storageKey} * @defaultValue false */ autoStore: boolean | string; /** Focus first possible element when it's appended to layout * @defaultValue false */ autoFocus: boolean; /** Disallow edit/copy value; adds attr [disabled] for styling * @defaultValue false */ disabled: boolean; /** Disallow copy value; adds attr [readonly] for styling * @defaultValue false */ readOnly: boolean; /** Enable/disable browser-autocomplete; if control has no autocomplete option then it's inherited from form * @defaultValue false */ autoComplete: boolean; } interface JSXProps extends WUP.Base.OnlyNames<Options> { "w-submitActions"?: SubmitActions | number; "w-autoStore"?: boolean | string; "w-autoFocus"?: boolean | ""; "w-autoComplete"?: boolean | ""; /** @deprecated use [disabled] instead since related to CSS-styles */ "w-disabled"?: boolean | ""; disabled?: boolean | ""; /** @deprecated use [disabled] instead since related to CSS-styles */ "w-readonly"?: boolean | ""; readonly?: boolean | ""; /** @deprecated SyntheticEvent is not supported. Use ref.addEventListener('$change') instead */ onChange?: never; /** @deprecated SyntheticEvent is not supported. Use ref.addEventListener('$willSubmit') instead */ onWillSubmit?: never; /** @deprecated SyntheticEvent is not supported. Use ref.addEventListener('$submit') instead */ onSubmit?: never; } } interface HTMLElementTagNameMap { [tagName]: WUPFormElement; } } declare module "react" { namespace JSX { interface IntrinsicElements { /** Wrapper of FormHTMLElement that collect values from controls * @see {@link WUPFormElement} */ [tagName]: WUP.Base.ReactHTML<WUPFormElement> & WUP.Form.JSXProps; } } } declare module "preact/jsx-runtime" { namespace JSX { interface HTMLAttributes<RefType> { } interface IntrinsicElements { /** Wrapper of FormHTMLElement that collect values from controls * @see {@link WUPFormElement} */ [tagName]: HTMLAttributes<WUPFormElement> & WUP.Form.JSXProps; } } } /** Wrapper of FormHTMLElement that collect values from controls * @see demo {@link https://yegorich555.github.io/web-ui-pack/controls} * @example * // init form * const form = document.createElement("wup-form"); * form.$options.autoComplete = false; * form.$initModel = { email: "test-me@google.com" }; * form.addEventListener("$submit", (e) => console.warn(e.detail.model) ); * form.$onSubmit = async (e)=>{ await postHere(e.detail.model); } // equal to form.addEventListener * // init control * const el = document.createElement("wup-text"); * el.$options.name = "email"; * el.$options.validations = { required: true, email: true }; * form.appendChild(el); * const btn = form.appendChild(document.createElement("button")); * btn.textContent = "Submit"; * btn.type = "submit"; * document.body.appendChild(form); * // or HTML * <wup-form w-autocomplete w-autofocus> * <wup-text w-name="email" /> * <button type="submit">Submit</submit> * </wup-form>; * @tutorial Troubleshooting/rules: * * options like $initModel, $model overrides control.$initValue, control.$value (every control that matches by $options.name) * * In React ref-parent called after ref-children. So if you want to set control.$initValue over form.$initModel use empty setTimeout on ref-control * @example * <wup-form ref={(el) => { if (el) { el.$initModel = { email: "test-me@google.com" }; el.$onSubmit = async (ev)=>{ await postHere(); } } }} > <wup-text ref={(el) => { if (el) { setTimeout(() => { el.$options.name = "email"; el.$initValue = ""; }); } }} <button type="submit">Submit</button> /> </wup-form> */ export default class WUPFormElement<Model extends Record<string, any> = any, TOptions extends WUP.Form.Options = WUP.Form.Options, Events extends WUP.Form.EventMap = WUP.Form.EventMap> extends WUPBaseElement<TOptions, Events> { #private; static get $styleRoot(): string; static get $style(): string; static get mappedAttributes(): Record<string, AttributeMap>; /** Find form related to control,register and apply initModel if initValue undefined */ static $tryConnect(control: IBaseControl & HTMLElement): WUPFormElement | undefined; /** Map model to control-values */ static $modelToControls<T extends Record<string, any>>(m: T | undefined, controls: IBaseControl[], prop: keyof Pick<IBaseControl, "$value" | "$initValue">): void; /** Collect model from control-values */ static $modelFromControls<T>(prevModel: Partial<T>, controls: IBaseControl[], prop: keyof Pick<IBaseControl, "$value" | "$initValue">, isOnlyChanged?: boolean): Partial<T>; static get observedAttributes(): Array<string>; /** Default options - applied to every element. Change it to configure default behavior */ static $defaults: WUP.Form.Options; /** Fires before $submit is happened; can be prevented via `e.preventDefault()` */ $onWillSubmit?: (ev: WUP.Form.EventMap["$willSubmit"]) => void; /** Dispatched on submit * @tutorial * * need to return promise to lock form and show spinner on http-request * * call `e.preventDefault()` to prevent dispatching `$submitEnd` & closing modal (if form in modal) */ $onSubmit?: (ev: WUP.Form.EventMap["$submit"]) => void | Promise<unknown>; /** Fires when submit is end (after http-response); * @tutorial * call `e.preventDefault()` to prevent closing modal (if form in modal) */ $onSubmitEnd?: (ev: WUP.Form.EventMap["$submitEnd"]) => void; /** Dispatched on submit */ /** All controls related to form */ $controls: IBaseControl<any>[]; /** Returns related to form controls with $options.name != null */ get $controlsAttached(): IBaseControl<any>[]; _model?: Partial<Model>; /** Model related to every control inside (mapped object via control.$options.name); * @see {@link BaseControl.prototype.$value} * @tutorial rules * * `form.$model = { firstName: 'Hell' }` updates only control with $options.name==='firstName' * * `form.$model = { firstName: undefined }` reset only control with $options.name==='firstName' */ get $model(): Partial<Model>; set $model(m: Partial<Model>); _initModel?: Partial<Model>; /** Default/init model related to every control inside; * @see {@link BaseControl.prototype.$initValue} */ get $initModel(): Partial<Model> | undefined; set $initModel(m: Partial<Model> | undefined); /** Pending state (spinner + lock form if SubmitActions.lockOnPending enabled) */ get $isPending(): boolean; set $isPending(v: boolean); /** Returns true if all nested controls (with name) are valid */ get $isValid(): boolean; /** Returns true if some of controls value is changed by user */ get $isChanged(): boolean; /** Call it to manually trigger submit or better to use `gotSubmit` for handling events properly */ $submit(): void; /** Validate all attached controls * @param tillFirstInvalid point `true` if need to find first invalid control; if skipped then will be defined from `$options.submitActions: SubmitActions.validateUntilFirst` * @param includeLocked collect & validate also hidden/readonly/disabled controls * @returns array of invalid controls (empty if all are valid) */ $validate(tillFirstInvalid?: boolean, includeLocked?: boolean): Array<IBaseControl>; /** Called on every spin-render */ renderSpin(target: HTMLElement): { dispose: () => void; }; /** Change pending state */ protected changePending(v: boolean): void; /** Called on submit before validation (to fire validation & $onSubmit if successful) */ gotSubmit(e: KeyboardEvent | MouseEvent | null, submitter: HTMLElement): void; protected gotChanges(propsChanged: Array<keyof WUP.Form.Options> | null): void; protected gotReady(): void; protected connectedCallback(): void; /** Returns storage key based on url+control-names or `$options.autoStore` if `string` */ get storageKey(): string; /** Get & parse value from storage according to option `autoStore`, $model */ storageGet(): Partial<Model> | null; _preventStorageSave?: boolean; /** Save/remove model (only changes) to storage according to option `autoStore`, $model * @returns model saved to localStorage or Null if removed */ storageSave(model?: null | Record<string, any>): Partial<Model> | null; protected disconnectedCallback(): void; } export {};