From 16701d6115fcb661deceaae229ad430422a8a95d Mon Sep 17 00:00:00 2001 From: ido Date: Sat, 29 Jun 2024 19:30:42 +0300 Subject: [PATCH] feat(BInput): support json value --- .../src/pages/state-json-upload.astro | 17 +++++++++++ .../components-control/form-utils/parse.ts | 21 +++++++++++++- .../src/components-control/input-parse.ts | 29 +++++++++++++------ .../forms/src/components/form/BInput.astro | 5 ++-- .../forms/src/components/form/BTextarea.astro | 3 +- 5 files changed, 62 insertions(+), 13 deletions(-) create mode 100644 examples/simple-form/src/pages/state-json-upload.astro diff --git a/examples/simple-form/src/pages/state-json-upload.astro b/examples/simple-form/src/pages/state-json-upload.astro new file mode 100644 index 0000000..ecc334f --- /dev/null +++ b/examples/simple-form/src/pages/state-json-upload.astro @@ -0,0 +1,17 @@ +--- +import { BButton, BInput, Bind, BindForm } from '@astro-utils/forms/forms.js'; +import Layout from '../layouts/Layout.astro'; + +const bind = Bind({json: {a: 2}}); + +function onSubmit() { + console.log(bind.json); +} +--- + + + + + Submit + + diff --git a/packages/forms/src/components-control/form-utils/parse.ts b/packages/forms/src/components-control/form-utils/parse.ts index d31a176..f3a2058 100644 --- a/packages/forms/src/components-control/form-utils/parse.ts +++ b/packages/forms/src/components-control/form-utils/parse.ts @@ -1,5 +1,5 @@ import type { AstroGlobal } from 'astro'; -import { z } from 'zod'; +import { ZodIssueCode, z } from 'zod'; import { getFormMultiValue } from '../../form-tools/post.js'; import AboutFormName from './about-form-name.js'; @@ -71,6 +71,25 @@ export function parseDate(about: AboutFormName, type: DateTypes, min?: string | about.catchParse(date); } +export function parseJSON(about: AboutFormName) { + const EMPTY_OBJECT = {}; + + about.catchParse(z.string() + .transform((str, ctx): z.infer> => { + try { + return JSON.parse(str, (key: string, value: any) => { + if (EMPTY_OBJECT[key] !== undefined) { + return; + } + return value; + }); + } catch (e) { + ctx.addIssue({ code: ZodIssueCode.custom, message: 'Invalid JSON' }); + return z.NEVER; + } + })); +} + export function parseEmail(about: AboutFormName) { about.catchParse(z.string().email()); } diff --git a/packages/forms/src/components-control/input-parse.ts b/packages/forms/src/components-control/input-parse.ts index fb72909..b6e2bb4 100644 --- a/packages/forms/src/components-control/input-parse.ts +++ b/packages/forms/src/components-control/input-parse.ts @@ -3,9 +3,10 @@ import { getFormValue } from '../form-tools/post.js'; import AboutFormName from './form-utils/about-form-name.js'; import type HTMLInputRadioPlugin from './form-utils/bind-form-plugins/input-radio.js'; import { BindForm } from './form-utils/bind-form.js'; -import { parseCheckbox, parseColor, parseDate, parseEmail, parseFiles, parseNumber, parseURL } from './form-utils/parse.js'; +import { parseCheckbox, parseColor, parseDate, parseEmail, parseFiles, parseJSON, parseNumber, parseURL } from './form-utils/parse.js'; import { validateFunc, validateRequire, validateStringPatters } from './form-utils/validate.js'; import {getProperty} from 'dot-prop'; +import { ZodType } from 'zod'; const OK_NOT_STRING_VALUE = ['checkbox', 'file']; const OK_INPUT_VALUE_NULL = ['checkbox']; @@ -35,7 +36,7 @@ type InputTypes = | 'url' | 'week'; -type ExtendedInputTypes = InputTypes | 'int'; +type ExtendedInputTypes = InputTypes | 'int' | 'json'; export async function getInputValue(astro: AstroGlobal, bindId: string, bind: BindForm) { const { value, name, readonly } = astro.props; @@ -66,8 +67,12 @@ export async function validateFormInput(astro: AstroGlobal, bind: BindForm, // specific validation by type / function validateByInputType(astro, aboutInput, bind); - if (!aboutInput.hadError && typeof validate == 'function') { - await validateFunc(aboutInput, validate); + if (!aboutInput.hadError) { + if(typeof validate == 'function'){ + await validateFunc(aboutInput, validate); + } else if(validate instanceof ZodType){ + aboutInput.catchParse(validate); + } } aboutInput.setValue(); @@ -112,6 +117,10 @@ function validateByInputType(astro: AstroGlobal, aboutInput: AboutFormName, bind parseURL(aboutInput); break; + case 'json': + parseJSON(aboutInput); + break; + case 'file': parseFiles(aboutInput, astro, multiple, readonly); break; @@ -129,7 +138,7 @@ function toDateTimeLocal(date: Date) { } -function stringifyDate(date?: Date | string, type?: ExtendedInputTypes) { +function stringifyCustomValue(date?: Date | string, type?: ExtendedInputTypes) { if (typeof date === 'string' || !date) { return date; } @@ -145,15 +154,17 @@ function stringifyDate(date?: Date | string, type?: ExtendedInputTypes) { return toDateTimeLocal(date).slice(0, 7); case 'week': return formatToDateWeek(date); + case 'json': + return JSON.stringify(date); } return date; } export function inputReturnValueAttr(astro: AstroGlobal, bind: BindForm) { - const value = stringifyDate(getProperty(bind, astro.props.name, astro.props.value), astro.props.type); - const min = stringifyDate(astro.props.min, astro.props.type); - const max = stringifyDate(astro.props.max, astro.props.type); + const value = stringifyCustomValue(getProperty(bind, astro.props.name, astro.props.value), astro.props.type); + const min = stringifyCustomValue(astro.props.min, astro.props.type); + const max = stringifyCustomValue(astro.props.max, astro.props.type); switch (astro.props.type as ExtendedInputTypes) { case 'checkbox': @@ -174,7 +185,7 @@ function formatToDateWeek(date: Date): string { } -export function caseTypes(type: ExtendedInputTypes): { type: InputTypes; } & { [key: string]: string; } { +export function caseTypes(type: ExtendedInputTypes): { type: ExtendedInputTypes; } & { [key: string]: string; } { if (type == 'int') { return { type: 'number', diff --git a/packages/forms/src/components/form/BInput.astro b/packages/forms/src/components/form/BInput.astro index b281917..a7944c1 100644 --- a/packages/forms/src/components/form/BInput.astro +++ b/packages/forms/src/components/form/BInput.astro @@ -5,8 +5,9 @@ import { validateFrom } from '../../form-tools/csrf.js'; import { ModifyDeep } from '../../utils.js'; import { addOnSubmitClickEvent } from '../../form-tools/events.js'; import { MissingNamePropError } from '../../errors/MissingNamePropError.js'; +import { ZodType } from 'zod'; -type inputTypes = astroHTML.JSX.InputHTMLAttributes['type'] | 'int'; +type inputTypes = astroHTML.JSX.InputHTMLAttributes['type'] | 'int' | 'json'; interface ModifyInputProps { type?: inputTypes; @@ -22,7 +23,7 @@ interface ModifyInputProps { export interface Props> extends Partial> { name: string; errorMessage?: string; - validate?: Function; + validate?: Function | ZodType; as?: T; props?: React.ComponentProps; } diff --git a/packages/forms/src/components/form/BTextarea.astro b/packages/forms/src/components/form/BTextarea.astro index 6b378e9..4db2359 100644 --- a/packages/forms/src/components/form/BTextarea.astro +++ b/packages/forms/src/components/form/BTextarea.astro @@ -6,6 +6,7 @@ import { ModifyDeep } from '../../utils.js'; import { getProperty } from 'dot-prop'; import { addOnSubmitClickEvent } from '../../form-tools/events.js'; import { MissingNamePropError } from '../../errors/MissingNamePropError.js'; +import { ZodType } from 'zod'; interface ModifyInputProps { minlength?: number; @@ -15,7 +16,7 @@ interface ModifyInputProps { export interface Props> extends Partial> { name: string; errorMessage?: string; - validate?: Function; + validate?: Function | ZodType; as?: T; props?: React.ComponentProps; onSubmitClick?: string;