Created
April 17, 2023 02:00
-
-
Save guiseek/bd6f0d925fa2cba7e45f261b27bc162e to your computer and use it in GitHub Desktop.
Criar formulários com decorators
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'reflect-metadata' | |
type InputType = | |
| 'checkbox' | |
| 'color' | |
| 'datetime-local' | |
| 'email' | |
| 'file' | |
| 'image' | |
| 'month' | |
| 'number' | |
| 'password' | |
| 'radio' | |
| 'range' | |
| 'tel' | |
| 'text' | |
| 'time' | |
| 'url' | |
type InputAutocomplete = | |
| 'name' | |
| 'honorific-prefix' | |
| 'given-name' | |
| 'additional-name' | |
| 'family-name' | |
| 'honorific-suffix' | |
| 'nickname' | |
| 'organization-title' | |
| 'username' | |
| 'new-password' | |
| 'current-password' | |
| 'one-time-code' | |
| 'organization' | |
| 'street-address' | |
| 'address-line1' | |
| 'address-line2' | |
| 'address-line3' | |
| 'address-level4' | |
| 'address-level3' | |
| 'address-level2' | |
| 'address-level1' | |
| 'country' | |
| 'country-name' | |
| 'postal-code' | |
| 'cc-name' | |
| 'cc-given-name' | |
| 'cc-additional-name' | |
| 'cc-family-name' | |
| 'cc-number' | |
| 'cc-exp' | |
| 'cc-exp-month' | |
| 'cc-exp-year' | |
| 'cc-csc' | |
| 'cc-type' | |
| 'transaction-currency' | |
| 'transaction-amount' | |
| 'language' | |
| 'bday' | |
| 'bday-day' | |
| 'bday-month' | |
| 'bday-year' | |
| 'sex' | |
| 'url' | |
| 'photo' | |
| 'tel' | |
| 'tel-country-code' | |
| 'tel-national' | |
| 'tel-area-code' | |
| 'tel-local' | |
| 'tel-local-prefix' | |
| 'tel-local-suffix' | |
| 'tel-extension' | |
| 'email' | |
| 'impp' | |
type InputMode = | |
| 'verbatim' | |
| 'latin' | |
| 'latin-name' | |
| 'latin-prose' | |
| 'full-width-latin' | |
| 'kana' | |
| 'katakana' | |
| 'numeric' | |
| 'tel' | |
| 'email' | |
| 'url' | |
interface Input<T = unknown> extends Partial<HTMLInputElement> { | |
label?: string | |
type: InputType | 'select' | |
autocomplete?: InputAutocomplete | |
required?: boolean | |
readOnly?: boolean | |
maxLength?: number | |
minLength?: number | |
value?: T | any | |
} | |
interface InputText<T = string> extends Input<T> { | |
type: 'text' | 'password' | 'email' | 'url' | |
inputmode?: InputMode | |
value?: T | |
} | |
interface InputNumber<T = number> extends Input<T> { | |
type: 'number' | |
min: string | |
max: string | |
value?: T | |
} | |
interface InputDate<T = Date> extends Input<T> { | |
value?: T | |
} | |
interface InputRadio<T = 'on'> extends Input<T> { | |
type: 'radio' | |
checked: boolean | |
value?: T | |
} | |
interface InputCheckbox<T = 'on'> extends Input<T> { | |
type: 'checkbox' | |
checked?: boolean | |
value?: T | |
} | |
type InputControl<T = unknown> = | |
| Input<T> | |
| InputText<T> | |
| InputNumber<T> | |
| InputDate<T> | |
| InputRadio<T> | |
| InputCheckbox<T> | |
interface FormGroup { | |
[k: string]: InputControl<unknown> | |
} | |
const formsContainer = new Map() | |
export function Input<T>(config: InputControl<T>) { | |
return function (target: any, key: string) { | |
const metadataKey = `__control_${key}` | |
Reflect.defineMetadata(metadataKey, config, target) | |
let group: FormGroup = formsContainer.get(target.constructor.name) | |
if (!group) group = {} | |
group[key] = Reflect.getMetadata(metadataKey, new target.constructor()) | |
formsContainer.set(target.constructor.name, group) | |
} | |
} | |
export function create<K extends keyof HTMLElementTagNameMap>( | |
name: K, | |
attributes: Partial<HTMLElementTagNameMap[K]> = {} | |
) { | |
return Object.assign(document.createElement(name), attributes) | |
} | |
export const createForm = <T extends object>( | |
name: string, | |
...footer: HTMLElement[] | |
) => { | |
const group = formsContainer.get(name) as Record<keyof T, InputControl> | |
const form = create('form') | |
const fieldset = create('fieldset') | |
fieldset.appendChild(create('legend', {innerText: 'Form'})) | |
if (group) { | |
for (const control in group) { | |
const section = create('section') | |
const {label, type, ...props} = group[control] | |
const id = `${name}-${control}`.toLowerCase() | |
let input = | |
type === 'select' | |
? create('select', {id, ...props} as HTMLSelectElement) | |
: create('input', {type, id, ...props} as HTMLInputElement) | |
const labelEl = create('label', {innerText: label}) | |
labelEl.setAttribute('for', id) | |
section.append(labelEl, input) | |
form.append(section, ...footer) | |
} | |
} | |
return form | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {Input, create, createForm} from './control' | |
import './style.css' | |
class User { | |
@Input({ | |
type: 'text', | |
label: 'Nome', | |
required: true, | |
}) | |
name: string | |
@Input({ | |
label: 'Endereço de e-mail', | |
type: 'email', | |
required: true, | |
}) | |
email: string | |
@Input({ | |
label: 'Nome de usuário', | |
type: 'text', | |
required: true, | |
}) | |
username: string | |
@Input({ | |
label: 'Senha', | |
type: 'password', | |
required: true, | |
minLength: 6, | |
}) | |
password: string | |
@Input({ | |
label: 'Apelido', | |
type: 'text', | |
maxLength: 10, | |
}) | |
nickname: string | |
@Input({ | |
label: 'Data de nascimento', | |
type: 'text', | |
autocomplete: 'bday', | |
}) | |
birthday: Date | |
} | |
const button = create('button', {innerText: 'Enviar'}) | |
document.body.appendChild(createForm('User', button)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment