From a4fa4f0da89fbd6dda5ecb77eb2d2ac4fcd9d410 Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Mon, 24 Apr 2023 23:58:54 -0600 Subject: [PATCH] GPT that shiz --- docs/config.json | 63 ++++++-- docs/{react => core}/comparison.md | 0 .../guides/important-defaults.md | 0 docs/{react => core}/installation.md | 0 docs/{react => core}/overview.md | 0 docs/core/reference/fieldApi.md | 139 ++++++++++++++++ docs/core/reference/formApi.md | 150 ++++++++++++++++++ docs/{react => core}/typescript.md | 0 docs/react/reference/Field.md | 54 +++++++ docs/react/reference/formApi.md | 19 +++ docs/react/reference/useField.md | 47 ++++++ docs/react/reference/useForm.md | 39 ++++- examples/react/simple/package.json | 3 +- examples/react/simple/src/index.jsx | 59 ++++++- packages/form-core/src/FieldApi.ts | 36 +++-- packages/react-form/src/useForm.tsx | 18 ++- pnpm-lock.yaml | 9 +- 17 files changed, 599 insertions(+), 37 deletions(-) rename docs/{react => core}/comparison.md (100%) rename docs/{react => core}/guides/important-defaults.md (100%) rename docs/{react => core}/installation.md (100%) rename docs/{react => core}/overview.md (100%) create mode 100644 docs/core/reference/fieldApi.md create mode 100644 docs/core/reference/formApi.md rename docs/{react => core}/typescript.md (100%) create mode 100644 docs/react/reference/Field.md create mode 100644 docs/react/reference/formApi.md create mode 100644 docs/react/reference/useField.md diff --git a/docs/config.json b/docs/config.json index 9be4b04..16bd57e 100644 --- a/docs/config.json +++ b/docs/config.json @@ -6,40 +6,62 @@ }, "menu": [ { - "framework": "react", + "framework": "core", "menuItems": [ { "label": "Getting Started", "children": [ { "label": "Overview", - "to": "react/overview" + "to": "core/overview" }, { "label": "Installation", - "to": "react/installation" + "to": "core/installation" }, { - "label": "Quick Start", - "to": "react/quick-start" + "label": "Comparison", + "to": "core/comparison" + }, + { + "label": "TypeScript", + "to": "core/typescript" } ] }, { - "label": "Guides & Concepts", + "label": "Guides", "children": [ { "label": "Important Defaults", - "to": "react/guides/important-defaults" + "to": "core/guides/important-defaults" } ] }, { - "label": "Examples", + "label": "API Reference", "children": [ { - "label": "Simple", - "to": "react/examples/react/simple" + "label": "FormApi", + "to": "core/reference/formApi" + }, + { + "label": "FieldApi", + "to": "core/reference/fieldApi" + } + ] + } + ] + }, + { + "framework": "react", + "menuItems": [ + { + "label": "Getting Started", + "children": [ + { + "label": "Quick Start", + "to": "react/quick-start" } ] }, @@ -49,6 +71,27 @@ { "label": "useForm", "to": "react/reference/useForm" + }, + { + "label": "useField", + "to": "react/reference/useField" + }, + { + "label": "Field", + "to": "react/reference/Field" + }, + { + "label": "FormApi", + "to": "react/reference/formApi" + } + ] + }, + { + "label": "Examples", + "children": [ + { + "label": "Simple", + "to": "react/examples/simple" } ] } diff --git a/docs/react/comparison.md b/docs/core/comparison.md similarity index 100% rename from docs/react/comparison.md rename to docs/core/comparison.md diff --git a/docs/react/guides/important-defaults.md b/docs/core/guides/important-defaults.md similarity index 100% rename from docs/react/guides/important-defaults.md rename to docs/core/guides/important-defaults.md diff --git a/docs/react/installation.md b/docs/core/installation.md similarity index 100% rename from docs/react/installation.md rename to docs/core/installation.md diff --git a/docs/react/overview.md b/docs/core/overview.md similarity index 100% rename from docs/react/overview.md rename to docs/core/overview.md diff --git a/docs/core/reference/fieldApi.md b/docs/core/reference/fieldApi.md new file mode 100644 index 0000000..29ccca7 --- /dev/null +++ b/docs/core/reference/fieldApi.md @@ -0,0 +1,139 @@ +--- +id: fieldApi +title: Field API +--- + +### Creating a new FieldApi Instance + +Normally, you will not need to create a new `FieldApi` instance directly. Instead, you will use a framework hook/function like `useField` or `createField` to create a new instance for you that utilizes your frameworks reactivity model. However, if you need to create a new instance manually, you can do so by calling the `new FieldApi` constructor. + +```tsx +const fieldApi: FieldApi = new FieldApi(formOptions: Field Options) +``` + +### `FieldOptions` + +An object type representing the options for a field in a form. + +- `name` + - The field name. If `TFormData` is `unknown`, the type will be `string`. Otherwise, it will be `DeepKeys`. +- `defaultValue?: TData` + - An optional default value for the field. +- `form?: FormApi` + - An optional reference to the form API instance. +- `validate?: (value: TData, fieldApi: FieldApi) => ValidationError | Promise` + - An optional validation function for the field. +- `validatePristine?: boolean` + - An optional flag indicating whether to validate the field when it is pristine (untouched). +- `filterValue?: (value: TData) => TData` + - An optional function to filter the field value before setting it in the form state. +- `defaultMeta?: Partial` + - An optional object with default metadata for the field. +- `validateOn?: 'change' | 'blur' | 'change-blur' | 'change-submit' | 'blur-submit' | 'submit'` + - An optional string indicating when to perform field validation. + +### `FieldMeta` + +An object type representing the metadata of a field in a form. + +- `isTouched: boolean` + - A flag indicating whether the field has been touched. +- `touchedError?: ValidationError` + - An optional error related to the touched state of the field. +- `error?: ValidationError` + - An optional error related to the field value. +- `isValidating: boolean` + - A flag indicating whether the field is currently being validated. + +### `FieldApiOptions` + +An object type representing the required options for the `FieldApi` class. + +- Inherits from `FieldOptions` with the `form` property set as required. + +### `FieldApi` + +A class representing the API for managing a form field. + +#### Properties + +- `uid: number` + - A unique identifier for the field instance. +- `form: FormApi` + - A reference to the form API instance. +- `name: DeepKeys` + - The field name. +- `store: Store>` + - The field state store. +- `state: FieldState` + - The current field state. +- `options: RequiredByKey, 'validateOn'>` + - The field options with the `validateOn` property set as required. + +#### Methods + +- `constructor(opts: FieldApiOptions)` + - Initializes a new `FieldApi` instance. +- `mount(): () => void` + - Mounts the field instance to the form. +- `updateStore(): void` + - Updates the field store with the latest form state. +- `update(opts: FieldApiOptions): void` + - Updates the field instance with new options. +- `getValue(): TData` + - Gets the current field value. +- `setValue(updater: Updater, options?: { touch?: boolean; notify?: boolean }): void` + - Sets the field value. +- `getMeta(): FieldMeta` + - Gets the current field metadata. +- `setMeta(updater: Updater): void` + - Sets the field metadata. +- `getInfo(): any` + - Gets the field information object. +- `pushValue(value: TData): void` + - Pushes a new value to the field. +- `insertValue(index: number, value: TData): void` + - Inserts a value at the specified index. +- `removeValue(index: number): void` + - Removes a value at the specified index. +- `swapValues(aIndex: number, bIndex: number): void` + - Swaps the values at the specified indices. +- `getSubField>(name: TName): FieldApi, TFormData>` + - Gets a subfield instance. +- `validate(): Promise` + - Validates the field value. +- `getChangeProps>(props: T = {} as T): ChangeProps & Omit>` + - Gets the change and blur event handlers. +- `getInputProps(props: T = {} as T): InputProps & Omit` + - Gets the input event handlers. + +### `FieldState` + +An object type representing the state of a field. + +- `value: TData` + - The current value of the field. +- `meta: FieldMeta` + - The current metadata of the field. + +### `ChangeProps` + +An object type representing the change and blur event handlers for a field. + +- `value: TData` + - The current value of the field. +- `onChange: (updater: Updater) => void` + - A function to handle the change event. +- `onBlur: (event: any) => void` + - A function to handle the blur event. + +### `InputProps` + +An object type representing the input event handlers for a field. + +- `value: string` + - The current value of the field, coerced to a string. +- `onChange: (event: any) => void` + - A function to handle the change event. +- `onBlur: (event: any) => void` + - A function to handle the blur event. diff --git a/docs/core/reference/formApi.md b/docs/core/reference/formApi.md new file mode 100644 index 0000000..e88e185 --- /dev/null +++ b/docs/core/reference/formApi.md @@ -0,0 +1,150 @@ +--- +id: formApi +title: Form API +--- + +### Creating a new FormApi Instance + +Normally, you will not need to create a new `FormApi` instance directly. Instead, you will use a framework hook/function like `useForm` or `createForm` to create a new instance for you that utilizes your frameworks reactivity model. However, if you need to create a new instance manually, you can do so by calling the `new FormApi` constructor. + +```tsx +const formApi: FormApi = new FormApi(formOptions: FormOptions) +``` + +### `FormOptions` + +An object representing the options for a form. + +- `defaultValues?: TData` + - The default values for the form fields. +- `defaultState?: Partial>` + - The default state for the form. +- `onSubmit?: (values: TData, formApi: FormApi) => Promise` + - A function to be called when the form is submitted and valid. +- `onInvalidSubmit?: (values: TData, formApi: FormApi) => void` + - A function to be called when the form is submitted but invalid. +- `validate?: (values: TData, formApi: FormApi) => Promise` + - A function for custom validation logic for the form. +- `debugForm?: boolean` + - A boolean flag to enable or disable form debugging. +- `validatePristine?: boolean` + - A boolean flag to enable or disable validation for pristine fields. + +### `FormApi` + +A class representing the Form API. It handles the logic and interactions with the form state. + +#### Properties + +- `options: FormOptions` + - The options for the form. +- `store: Store>` + - The internal store for the form state. +- `state: FormState` + - The current state of the form. +- `fieldInfo: Record, FieldInfo>` + - A record of field information for each field in the form. +- `fieldName?: string` + - An optional string representing the name of the field. +- `validationMeta: ValidationMeta` + - The validation metadata for the form. + +#### Methods + +- `constructor(opts?: FormOptions)` + - Constructs a new `FormApi` instance with the given form options. +- `update(options: FormOptions)` + - Updates the form options and form state. +- `reset()` + - Resets the form state to the default values. +- `validateAllFields()` + - Validates all fields in the form. +- `validateForm()` + - Validates the form itself. +- `handleSubmit(e: FormEvent & { __handled?: boolean })` + - Handles the form submission event, performs validation, and calls the appropriate onSubmit or onInvalidSubmit callbacks. +- `getFieldValue>(field: TField)` + - Gets the value of the specified field. +- `getFieldMeta>(field: TField)` + - Gets the metadata of the specified field. +- `getFieldInfo>(field: TField)` + - Gets the field info of the specified field. +- `setFieldMeta>(field: TField, updater: Updater) + - Updates the metadata of the specified field. +- `setFieldValue>(field: TField, updater: Updater>, opts?: { touch?: boolean }) + - Sets the value of the specified field and optionally updates the touched state. +- `pushFieldValue>(field: TField, value: DeepValue, opts?: { touch?: boolean }) + - Pushes a value into an array field. +- `insertFieldValue>(field: TField, index: number, value: DeepValue, opts?: { touch?: boolean }) + - Inserts a value into an array field at the specified index. +- `spliceFieldValue>(field: TField, index: number, opts?: { touch?: boolean }) + - Removes a value from an array field at the specified index. +- `swapFieldValues>(field: TField, index1: number, index2: number) + - Swaps the values at the specified indices within an array field. + +### `FormState` + +An object representing the current state of the form. + +- `values: TData` + - The current values of the form fields. +- `isFormValidating: boolean` + - A boolean indicating if the form is currently validating. +- `formValidationCount: number` + - A counter for tracking the number of validations performed on the form. +- `isFormValid: boolean` + - A boolean indicating if the form is valid. +- `formError?: ValidationError` + - A possible validation error for the form. +- `fieldMeta: Record, FieldMeta>` + - A record of field metadata for each field in the form. +- `isFieldsValidating: boolean` + - A boolean indicating if any of the form fields are currently validating. +- `isFieldsValid: boolean` + - A boolean indicating if all the form fields are valid. +- `isSubmitting: boolean` + - A boolean indicating if the form is currently submitting. +- `isTouched: boolean` + - A boolean indicating if any of the form fields have been touched. +- `isSubmitted: boolean` + - A boolean indicating if the form has been submitted. +- `isValidating: boolean` + - A boolean indicating if the form or any of its fields are currently validating. +- `isValid: boolean` + - A boolean indicating if the form and all its fields are valid. +- `canSubmit: boolean` + - A boolean indicating if the form can be submitted based on its current state. +- `submissionAttempts: number` + - A counter for tracking the number of submission attempts. + +### `FieldInfo` + +An object representing the field information for a specific field within the form. + +- `instances: Record>` + - A record of field instances with unique identifiers as keys. +- `validationCount?: number` + - A counter for tracking the number of validations performed on the field. +- `validationPromise?: Promise` + - A promise representing the current validation state of the field. +- `validationResolve?: (error: ValidationError) => void` + - A function to resolve the validation promise with a possible validation error. +- `validationReject?: (error: unknown) => void` + - A function to reject the validation promise with an error. + +### `ValidationMeta` + +An object representing the validation metadata for a field. + +- `validationCount?: number` + - A counter for tracking the number of validations performed on the field. +- `validationPromise?: Promise` + - A promise representing the current validation state of the field. +- `validationResolve?: (error: ValidationError) => void` + - A function to resolve the validation promise with a possible validation error. +- `validationReject?: (error: unknown) => void` + - A function to reject the validation promise with an error. + +### `ValidationError` + +A type representing a validation error. Possible values are `undefined`, `false`, `null`, or a `string` with an error message. diff --git a/docs/react/typescript.md b/docs/core/typescript.md similarity index 100% rename from docs/react/typescript.md rename to docs/core/typescript.md diff --git a/docs/react/reference/Field.md b/docs/react/reference/Field.md new file mode 100644 index 0000000..8fde22b --- /dev/null +++ b/docs/react/reference/Field.md @@ -0,0 +1,54 @@ +--- +id: field +title: Field +--- + +### `type FieldComponent` + +A type alias representing a field component for a specific form data type. + +```tsx +export type FieldComponent = >({ + children, + ...fieldOptions +}: { + children: (fieldApi: FieldApi, TFormData>) => any + name: TField +} & Omit, TFormData>, 'name'>) => any +``` + +A function component that takes field options and a render function as children and returns a React component. + +### `Field` + +```tsx +export function Field({ + children, + ...fieldOptions +}: { children: (fieldApi: FieldApi) => any } & FieldOptions< + TData, + TFormData +>): any +``` + +A functional React component that renders a form field. + +- `children: (fieldApi: FieldApi) => any` + - A render function that takes a field API instance and returns a React element. +- `fieldOptions: FieldOptions` + - The field options. + +The `Field` component uses the `useField` hook internally to manage the field instance. + +### `createFieldComponent` + +```tsx +export function createFieldComponent( + formApi: FormApi, +): FieldComponent +``` + +A factory function that creates a connected field component for a specific form API instance. + +- `formApi: FormApi` + - The form API instance to connect the field component to. diff --git a/docs/react/reference/formApi.md b/docs/react/reference/formApi.md new file mode 100644 index 0000000..a778758 --- /dev/null +++ b/docs/react/reference/formApi.md @@ -0,0 +1,19 @@ +--- +id: formApi +title: Form API +--- + +### `FormApi` + +When using `@tanstack/react-form`, the [core form API](../../core//reference/formApi.md) is extended with additional methods for React-specific functionality: + +- `Form: FormComponent` + - A pre-bound and type-safe form component, specific to this forms instance. +- `Field: FieldComponent` + - A pre-bound and type-safe field component, specific to this forms instance. +- `useField: UseField` + - A pre-bound and type-safe custom hook to use fields from this form instance. +- `useStore>>(selector?: (state: NoInfer>) => TSelected): TSelected` + - A custom hook to use the form store. +- `Subscribe>>(props: {selector?: (state: NoInfer>) => TSelected; children: ((state: NoInfer) => React.ReactNode) | React.ReactNode}): any` + - A subscription component to provide the selected form state to children. diff --git a/docs/react/reference/useField.md b/docs/react/reference/useField.md new file mode 100644 index 0000000..418b107 --- /dev/null +++ b/docs/react/reference/useField.md @@ -0,0 +1,47 @@ +--- +id: useField +title: useField +--- + +### `UseField` + +A type representing a hook for using a field in a form with the given form data type. + +```tsx +export type UseField = >( + opts?: { name: TField } & FieldOptions< + DeepValue, + TFormData + >, +) => FieldApi, TFormData> +``` + +- A function that takes an optional object with a `name` property and field options, and returns a `FieldApi` instance for the specified field. + +### `useField` + +```tsx +export function useField( + opts: FieldOptions, +): FieldApi +``` + +A hook for managing a field in a form. + +- `opts: FieldOptions` + - An object with field options. + +#### Returns + +- `FieldApi` + - `FieldApi` instance for the specified field. + +### `createUseField` + +```tsx +export function createUseField( + formApi: FormApi, +): UseField +``` + +A function that creates a `UseField` hook bound to the given `formApi`. diff --git a/docs/react/reference/useForm.md b/docs/react/reference/useForm.md index ad4a356..bd4b3ea 100644 --- a/docs/react/reference/useForm.md +++ b/docs/react/reference/useForm.md @@ -3,10 +3,43 @@ id: useForm title: useForm --- +### `useForm` + ```tsx -const {} = useForm({}) +export function useForm( + opts?: FormOptions & { listen?: (state: FormState) => any }, +): FormApi ``` -**Options** +A custom hook that returns an instance of the `FormApi` class. -**Returns** +- `opts` + - Optional form options and a `listen` function to be called with the form state. + +### `FormProps` + +An object type representing the form component props. + +- Inherits from `React.HTMLProps`. +- `children: React.ReactNode` + - The form's child elements. +- `noFormElement?: boolean` + - If true, the form component will not render an HTML form element. + +### `FormComponent` + +A type representing a form component. + +- `(props: FormProps) => any` + - A function that takes `FormProps` as an argument and returns a form component. + +### `createFormComponent` + +```tsx +export function createFormComponent(formApi: FormApi): FormComponent +``` + +A function that creates a form component with the provided form API instance. + +- `formApi` + - An instance of the `FormApi` class. diff --git a/examples/react/simple/package.json b/examples/react/simple/package.json index b37b64f..6f6480c 100644 --- a/examples/react/simple/package.json +++ b/examples/react/simple/package.json @@ -8,10 +8,11 @@ "preview": "vite preview" }, "dependencies": { + "@tanstack/react-form": "workspace:^", "axios": "^0.26.1", "react": "^18.0.0", "react-dom": "^18.0.0", - "@tanstack/react-form": "^4.7.1" + "zod": "^3.21.4" }, "devDependencies": { "@vitejs/plugin-react": "^2.0.0", diff --git a/examples/react/simple/src/index.jsx b/examples/react/simple/src/index.jsx index 4b58ae4..1324021 100644 --- a/examples/react/simple/src/index.jsx +++ b/examples/react/simple/src/index.jsx @@ -1,9 +1,64 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ import React from "react"; import ReactDOM from "react-dom/client"; import { useForm } from "@tanstack/react-form"; -export default function App() {} +export default function App() { + const form = useForm({ + // Memoize your default values to prevent re-renders + defaultValues: React.useMemo( + () => ({ + firstName: "", + lastName: "", + }), + [] + ), + onSubmit: (values) => { + // Do something with form data + }, + }); + + return ( +
+

Simple Form Example

+ {/* A pre-bound form component */} + +
+ {/* A type-safe and pre-bound field component*/} + !value && "A first name is required"} + children={(field) => ( + // Avoid hasty abstractions. Render props are great! + <> + + + + )} + /> +
+
+ ( + <> + + + + )} + /> +
+ [state.canSubmit, state.isSubmitting]} + children={([canSubmit, isSubmitting]) => ( + + )} + /> +
+
+ ); +} const rootElement = document.getElementById("root"); ReactDOM.createRoot(rootElement).render(); diff --git a/packages/form-core/src/FieldApi.ts b/packages/form-core/src/FieldApi.ts index c82d5cd..c39eb63 100644 --- a/packages/form-core/src/FieldApi.ts +++ b/packages/form-core/src/FieldApi.ts @@ -23,6 +23,13 @@ export type FieldOptions = { | 'submit' } +export type FieldMeta = { + isTouched: boolean + touchedError?: ValidationError + error?: ValidationError + isValidating: boolean +} + export type ChangeProps = { onChange: (updater: Updater) => void onBlur: (event: any) => void @@ -33,13 +40,6 @@ export type InputProps = { onBlur: (event: any) => void } -export type FieldMeta = { - isTouched: boolean - touchedError?: ValidationError - error?: ValidationError - isValidating: boolean -} - export type FieldApiOptions = RequiredByKey< FieldOptions, 'form' @@ -211,17 +211,25 @@ export class FieldApi { } try { - const error = await this.options.validate(this.state.value, this) + const rawError = await this.options.validate(this.state.value, this) if (checkLatest()) { + const error = (() => { + if (rawError) { + if (typeof rawError !== 'string') { + return 'Invalid Form Values' + } + + return null + } + + return undefined + })() + this.setMeta((prev) => ({ ...prev, isValidating: false, - error: error - ? typeof error === 'string' - ? error - : 'Invalid Form Values' - : null, + error, })) this.getInfo().validationResolve?.(error) } @@ -245,6 +253,7 @@ export class FieldApi { ): ChangeProps & Omit> => { return { ...props, + value: this.state.value, onChange: (value) => { this.setValue(value) props.onChange(value) @@ -268,6 +277,7 @@ export class FieldApi { ): InputProps & Omit => { return { ...props, + value: String(this.state.value), onChange: (e) => { this.setValue(e.target.value) props.onChange(e.target.value) diff --git a/packages/react-form/src/useForm.tsx b/packages/react-form/src/useForm.tsx index 3e15cd4..b34a042 100644 --- a/packages/react-form/src/useForm.tsx +++ b/packages/react-form/src/useForm.tsx @@ -6,7 +6,6 @@ import React from 'react' import { createFieldComponent, type FieldComponent } from './Field' import { createUseField, type UseField } from './useField' import { formContext } from './formContext' -// declare module '@tanstack/form-core' { // eslint-disable-next-line no-shadow @@ -25,22 +24,27 @@ declare module '@tanstack/form-core' { }) => any } } +// -export function useForm( - opts?: FormOptions & { listen?: (state: FormState) => any }, -): FormApi { - // & { listened: TListen } +export function useForm(opts?: FormOptions): FormApi { const [formApi] = React.useState(() => { + // @ts-ignore const api = new FormApi(opts || {}) api.Form = createFormComponent(api) api.Field = createFieldComponent(api) api.useField = createUseField(api) - api.useStore = (selector) => { + api.useStore = ( + // @ts-ignore + selector, + ) => { // eslint-disable-next-line react-hooks/rules-of-hooks return useStore(api.store, selector) as any } - api.Subscribe = (props) => { + api.Subscribe = ( + // @ts-ignore + props, + ) => { return functionalUpdate( props.children, // eslint-disable-next-line react-hooks/rules-of-hooks diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 06a73bc..c9c7911 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -230,7 +230,7 @@ importers: examples/react/simple: dependencies: '@tanstack/react-form': - specifier: ^4.7.1 + specifier: workspace:^ version: link:../../../packages/react-form axios: specifier: ^0.26.1 @@ -241,6 +241,9 @@ importers: react-dom: specifier: ^18.0.0 version: 18.2.0(react@18.2.0) + zod: + specifier: ^3.21.4 + version: 3.21.4 devDependencies: '@vitejs/plugin-react': specifier: ^2.0.0 @@ -10395,3 +10398,7 @@ packages: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} dev: true + + /zod@3.21.4: + resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==} + dev: false