@vaadin/hilla-react-crud 25.3.0-alpha2 → 25.3.0-alpha4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/autocrud.d.ts +6 -5
- package/autocrud.js.map +1 -1
- package/autoform-field.js.map +1 -1
- package/autoform.d.ts +6 -5
- package/autoform.js.map +1 -1
- package/autogrid.d.ts +2 -1
- package/autogrid.js.map +1 -1
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/model-info.d.ts +4 -3
- package/model-info.js +34 -15
- package/model-info.js.map +1 -1
- package/package.json +7 -7
- package/util.js +1 -1
- package/util.js.map +1 -1
package/autocrud.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { AbstractModel, DetachedModelConstructor, Value } from '@vaadin/hilla-lit-form';
|
|
1
|
+
import type { AbstractModel, DetachedModelConstructor, ProvisionalModel, Value } from '@vaadin/hilla-lit-form';
|
|
2
|
+
import type { Model } from '@vaadin/hilla-models';
|
|
2
3
|
import { type JSX, type ReactNode } from 'react';
|
|
3
4
|
import { type AutoFormProps } from './autoform.js';
|
|
4
5
|
import { type AutoGridProps } from './autogrid.js';
|
|
@@ -6,17 +7,17 @@ import type { CrudService } from './crud.js';
|
|
|
6
7
|
import { type ComponentStyleProps } from './util.js';
|
|
7
8
|
export type AutoCrudFormHeaderRenderer<TItem> = (editedItem: TItem | null, disabled: boolean) => JSX.Element | null | undefined;
|
|
8
9
|
export type AutoCrudToolbarRenderer = (defaultContent: ReactNode) => ReactNode;
|
|
9
|
-
export type AutoCrudFormProps<TModel extends
|
|
10
|
+
export type AutoCrudFormProps<TModel extends ProvisionalModel> = Omit<Partial<AutoFormProps<TModel>>, 'disabled' | 'item' | 'model' | 'onDeleteSuccess' | 'onSubmitSuccess' | 'service'> & Readonly<{
|
|
10
11
|
headerRenderer?: AutoCrudFormHeaderRenderer<Value<TModel>>;
|
|
11
12
|
}>;
|
|
12
13
|
export type AutoCrudGridProps<TItem> = Omit<Partial<AutoGridProps<TItem>>, 'model' | 'onActiveItemChanged' | 'selectedItems' | 'service'>;
|
|
13
|
-
export type AutoCrudProps<TModel extends
|
|
14
|
+
export type AutoCrudProps<TModel extends ProvisionalModel = ProvisionalModel> = ComponentStyleProps & Readonly<{
|
|
14
15
|
service: CrudService<Value<TModel>>;
|
|
15
|
-
model: DetachedModelConstructor<TModel
|
|
16
|
+
model: DetachedModelConstructor<TModel & AbstractModel> | (TModel & Model);
|
|
16
17
|
itemIdProperty?: string;
|
|
17
18
|
noNewButton?: boolean;
|
|
18
19
|
toolbarRenderer?: AutoCrudToolbarRenderer;
|
|
19
20
|
formProps?: AutoCrudFormProps<TModel>;
|
|
20
21
|
gridProps?: AutoCrudGridProps<Value<TModel>>;
|
|
21
22
|
}>;
|
|
22
|
-
export declare function AutoCrud<TModel extends
|
|
23
|
+
export declare function AutoCrud<TModel extends ProvisionalModel>({ service, model, itemIdProperty, noNewButton, toolbarRenderer, formProps, gridProps, style, id, className, }: AutoCrudProps<TModel>): JSX.Element;
|
package/autocrud.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autocrud.js","sourceRoot":"","sources":["src/autocrud.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { AbstractModel, DetachedModelConstructor, Value } from '@vaadin/hilla-lit-form';\nimport { Button } from '@vaadin/react-components/Button.js';\nimport { SplitLayout } from '@vaadin/react-components/SplitLayout.js';\nimport { type JSX, type ReactNode, useId, useRef, useState } from 'react';\nimport { AutoCrudDialog } from './autocrud-dialog.js';\nimport css from './autocrud.obj.js';\nimport { type AutoFormProps, emptyItem, AutoForm } from './autoform.js';\nimport { type AutoGridProps, AutoGrid, type AutoGridRef } from './autogrid.js';\nimport type { CrudService } from './crud.js';\nimport { useMediaQuery } from './media-query.js';\nimport { type ComponentStyleProps, registerStylesheet } from './util.js';\n\nregisterStylesheet(css);\n\nexport type AutoCrudFormHeaderRenderer<TItem> = (\n editedItem: TItem | null,\n disabled: boolean,\n) => JSX.Element | null | undefined;\n\nexport type AutoCrudToolbarRenderer = (defaultContent: ReactNode) => ReactNode;\n\nexport type AutoCrudFormProps<TModel extends AbstractModel> = Omit<\n Partial<AutoFormProps<TModel>>,\n 'disabled' | 'item' | 'model' | 'onDeleteSuccess' | 'onSubmitSuccess' | 'service'\n> &\n Readonly<{\n /**\n * A custom renderer function to create the header for the form. The\n * function receives the edited item as the first parameter, and a boolean\n * indicating whether the form is disabled as the second parameter. The\n * edited item is `null` when creating a new item.\n *\n * By default, the header shows \"New item\" when creating a new item, and\n * \"Edit item\" when editing an existing item.\n */\n headerRenderer?: AutoCrudFormHeaderRenderer<Value<TModel>>;\n }>;\n\nexport type AutoCrudGridProps<TItem> = Omit<\n Partial<AutoGridProps<TItem>>,\n 'model' | 'onActiveItemChanged' | 'selectedItems' | 'service'\n>;\n\nexport type AutoCrudProps<TModel extends AbstractModel = AbstractModel> = ComponentStyleProps &\n Readonly<{\n /**\n * The service to use for fetching the data, as well saving and deleting\n * items. This must be a TypeScript service that has been generated by Hilla\n * from a backend Java service that implements the\n * `com.vaadin.hilla.crud.CrudService` interface.\n */\n service: CrudService<Value<TModel>>;\n /**\n * The entity model to use for the CRUD. This determines which columns to\n * show in the grid, and which fields to show in the form. This must be a\n * Typescript model class that has been generated by Hilla from a backend\n * Java class. The model must match with the type of the items returned by\n * the service. For example, a `PersonModel` can be used with a service that\n * returns `Person` instances.\n *\n * By default, the grid shows columns for all properties of the model which\n * have a type that is supported. Use the `gridProps.visibleColumns` option\n * to customize which columns to show and in which order.\n *\n * By default, the form shows fields for all properties of the model which\n * have a type that is supported. Use the `formProps.visibleFields`\n * option to customize which fields to show and in which order.\n */\n model: DetachedModelConstructor<TModel>;\n /**\n * The property to use to detect an item's ID. The item ID is required for\n * deleting items via the `CrudService.delete` method as well as keeping the\n * selection state after reloading the grid.\n *\n * By default, the component uses the property annotated with\n * `jakarta.persistence.Id`, or a property named `id`, in that order.\n * This option can be used to override the default behavior, or define the ID\n * property in case a class doesn't have a property matching the defaults.\n */\n itemIdProperty?: string;\n /**\n * Determines whether to hide the \"New\" button in the toolbar.\n *\n * NOTE: This setting only hides the button; it does not prevent new items\n * from being sent to the service. Ensure your backend Java service is\n * properly secured to prevent unauthorized creation of new items.\n *\n * @defaultValue `false` meaning the button will be shown.\n */\n noNewButton?: boolean;\n /**\n * A custom renderer function to create the toolbar content. The function\n * receives the default toolbar content as a parameter, which can be used\n * or not, depending on the implementation.\n */\n toolbarRenderer?: AutoCrudToolbarRenderer;\n /**\n * Props to pass to the form. See the `AutoForm` component for details.\n */\n formProps?: AutoCrudFormProps<TModel>;\n /**\n * Props to pass to the grid. See the `AutoGrid` component for details.\n */\n gridProps?: AutoCrudGridProps<Value<TModel>>;\n }>;\n\nfunction defaultFormHeaderRenderer<TItem>(editedItem: TItem | null, disabled: boolean): JSX.Element | null | undefined {\n const style = { color: disabled ? 'var(--lumo-disabled-text-color)' : 'var(--lumo-text-color)' };\n return editedItem ? <h3 style={style}>Edit item</h3> : <h3 style={style}>New item</h3>;\n}\n\n/**\n * Auto CRUD is a component that provides CRUD (create, read, update, delete)\n * functionality based on a Java backend service. It automatically generates a\n * grid that shows data from the service, and a form for creating, updating and\n * deleting items.\n *\n * Example usage:\n * ```tsx\n * import { AutoCrud } from '@hilla/react-crud';\n * import PersonService from 'Frontend/generated/endpoints';\n * import PersonModel from 'Frontend/generated/com/example/application/Person';\n *\n * <AutoCrud service={PersonService} model={PersonModel} />\n * ```\n */\nexport function AutoCrud<TModel extends AbstractModel>({\n service,\n model,\n itemIdProperty,\n noNewButton,\n toolbarRenderer = (defaultContent): ReactNode => defaultContent,\n formProps,\n gridProps,\n style,\n id,\n className,\n}: AutoCrudProps<TModel>): JSX.Element {\n const [item, setItem] = useState<Value<TModel> | typeof emptyItem | undefined>(undefined);\n const fullScreen = useMediaQuery('(max-width: 600px), (max-height: 600px)');\n const autoGridRef = useRef<AutoGridRef>(null);\n const { headerRenderer: customFormHeaderRenderer, ...autoFormProps } = formProps ?? {};\n const formHeaderRenderer: AutoCrudFormHeaderRenderer<Value<TModel>> =\n customFormHeaderRenderer ?? defaultFormHeaderRenderer;\n\n const autoCrudId = useId();\n\n function refreshGrid() {\n autoGridRef.current?.refresh();\n }\n\n function handleCancel() {\n setItem(undefined);\n }\n\n const formHeader = item && item !== emptyItem ? formHeaderRenderer(item, !item) : formHeaderRenderer(null, !item);\n\n const toolbarContent = toolbarRenderer(\n noNewButton ? null : (\n <Button theme=\"primary\" onClick={() => setItem(emptyItem)}>\n + New\n </Button>\n ),\n );\n\n const mainSection = (\n <div className=\"auto-crud-main\">\n <AutoGrid\n {...gridProps}\n service={service}\n model={model as DetachedModelConstructor<AbstractModel<Value<TModel>>>}\n itemIdProperty={itemIdProperty}\n selectedItems={item && item !== emptyItem ? [item] : []}\n onActiveItemChanged={(e) => {\n const activeItem = e.detail.value;\n setItem(activeItem ?? undefined);\n }}\n ref={autoGridRef}\n aria-controls={autoFormProps.id ?? `auto-form-${id ?? autoCrudId}`}\n ></AutoGrid>\n {toolbarContent && <div className=\"auto-crud-toolbar\">{toolbarContent}</div>}\n </div>\n );\n\n const autoForm = (\n <AutoForm\n id={autoFormProps.id ?? `auto-form-${id ?? autoCrudId}`}\n deleteButtonVisible={true}\n {...autoFormProps}\n disabled={!item}\n service={service}\n model={model}\n itemIdProperty={itemIdProperty}\n item={item}\n onSubmitSuccess={({ item: submittedItem }) => {\n if (fullScreen) {\n setItem(undefined);\n } else {\n setItem(submittedItem);\n }\n refreshGrid();\n }}\n onDeleteSuccess={() => {\n setItem(undefined);\n refreshGrid();\n }}\n />\n );\n\n // If the \"New\" button is visible, the form is always shown.\n // Otherwise, the form is only shown when an item is being edited.\n return (\n <div className={`auto-crud ${className ?? ''}`} id={id} style={style}>\n {fullScreen ? (\n <>\n {mainSection}\n <AutoCrudDialog opened={!!item} header={formHeader} onClose={handleCancel}>\n {autoForm}\n </AutoCrudDialog>\n </>\n ) : (\n <SplitLayout theme=\"small\">\n {mainSection}\n {(!noNewButton || (item && item !== emptyItem)) && (\n <div className=\"auto-crud-form\">\n <div className=\"auto-crud-form-header\">{formHeader}</div>\n {autoForm}\n </div>\n )}\n </SplitLayout>\n )}\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"autocrud.js","sourceRoot":"","sources":["src/autocrud.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { AbstractModel, DetachedModelConstructor, ProvisionalModel, Value } from '@vaadin/hilla-lit-form';\nimport type { Model } from '@vaadin/hilla-models';\nimport { Button } from '@vaadin/react-components/Button.js';\nimport { SplitLayout } from '@vaadin/react-components/SplitLayout.js';\nimport { type JSX, type ReactNode, useId, useRef, useState } from 'react';\nimport { AutoCrudDialog } from './autocrud-dialog.js';\nimport css from './autocrud.obj.js';\nimport { type AutoFormProps, emptyItem, AutoForm } from './autoform.js';\nimport { type AutoGridProps, AutoGrid, type AutoGridRef } from './autogrid.js';\nimport type { CrudService } from './crud.js';\nimport { useMediaQuery } from './media-query.js';\nimport { type ComponentStyleProps, registerStylesheet } from './util.js';\n\nregisterStylesheet(css);\n\nexport type AutoCrudFormHeaderRenderer<TItem> = (\n editedItem: TItem | null,\n disabled: boolean,\n) => JSX.Element | null | undefined;\n\nexport type AutoCrudToolbarRenderer = (defaultContent: ReactNode) => ReactNode;\n\nexport type AutoCrudFormProps<TModel extends ProvisionalModel> = Omit<\n Partial<AutoFormProps<TModel>>,\n 'disabled' | 'item' | 'model' | 'onDeleteSuccess' | 'onSubmitSuccess' | 'service'\n> &\n Readonly<{\n /**\n * A custom renderer function to create the header for the form. The\n * function receives the edited item as the first parameter, and a boolean\n * indicating whether the form is disabled as the second parameter. The\n * edited item is `null` when creating a new item.\n *\n * By default, the header shows \"New item\" when creating a new item, and\n * \"Edit item\" when editing an existing item.\n */\n headerRenderer?: AutoCrudFormHeaderRenderer<Value<TModel>>;\n }>;\n\nexport type AutoCrudGridProps<TItem> = Omit<\n Partial<AutoGridProps<TItem>>,\n 'model' | 'onActiveItemChanged' | 'selectedItems' | 'service'\n>;\n\nexport type AutoCrudProps<TModel extends ProvisionalModel = ProvisionalModel> = ComponentStyleProps &\n Readonly<{\n /**\n * The service to use for fetching the data, as well saving and deleting\n * items. This must be a TypeScript service that has been generated by Hilla\n * from a backend Java service that implements the\n * `com.vaadin.hilla.crud.CrudService` interface.\n */\n service: CrudService<Value<TModel>>;\n /**\n * The entity model to use for the CRUD. This determines which columns to\n * show in the grid, and which fields to show in the form. This must be a\n * Typescript model class that has been generated by Hilla from a backend\n * Java class. The model must match with the type of the items returned by\n * the service. For example, a `PersonModel` can be used with a service that\n * returns `Person` instances.\n *\n * By default, the grid shows columns for all properties of the model which\n * have a type that is supported. Use the `gridProps.visibleColumns` option\n * to customize which columns to show and in which order.\n *\n * By default, the form shows fields for all properties of the model which\n * have a type that is supported. Use the `formProps.visibleFields`\n * option to customize which fields to show and in which order.\n */\n model: DetachedModelConstructor<TModel & AbstractModel> | (TModel & Model);\n /**\n * The property to use to detect an item's ID. The item ID is required for\n * deleting items via the `CrudService.delete` method as well as keeping the\n * selection state after reloading the grid.\n *\n * By default, the component uses the property annotated with\n * `jakarta.persistence.Id`, or a property named `id`, in that order.\n * This option can be used to override the default behavior, or define the ID\n * property in case a class doesn't have a property matching the defaults.\n */\n itemIdProperty?: string;\n /**\n * Determines whether to hide the \"New\" button in the toolbar.\n *\n * NOTE: This setting only hides the button; it does not prevent new items\n * from being sent to the service. Ensure your backend Java service is\n * properly secured to prevent unauthorized creation of new items.\n *\n * @defaultValue `false` meaning the button will be shown.\n */\n noNewButton?: boolean;\n /**\n * A custom renderer function to create the toolbar content. The function\n * receives the default toolbar content as a parameter, which can be used\n * or not, depending on the implementation.\n */\n toolbarRenderer?: AutoCrudToolbarRenderer;\n /**\n * Props to pass to the form. See the `AutoForm` component for details.\n */\n formProps?: AutoCrudFormProps<TModel>;\n /**\n * Props to pass to the grid. See the `AutoGrid` component for details.\n */\n gridProps?: AutoCrudGridProps<Value<TModel>>;\n }>;\n\nfunction defaultFormHeaderRenderer<TItem>(editedItem: TItem | null, disabled: boolean): JSX.Element | null | undefined {\n const style = { color: disabled ? 'var(--lumo-disabled-text-color)' : 'var(--lumo-text-color)' };\n return editedItem ? <h3 style={style}>Edit item</h3> : <h3 style={style}>New item</h3>;\n}\n\n/**\n * Auto CRUD is a component that provides CRUD (create, read, update, delete)\n * functionality based on a Java backend service. It automatically generates a\n * grid that shows data from the service, and a form for creating, updating and\n * deleting items.\n *\n * Example usage:\n * ```tsx\n * import { AutoCrud } from '@hilla/react-crud';\n * import PersonService from 'Frontend/generated/endpoints';\n * import PersonModel from 'Frontend/generated/com/example/application/Person';\n *\n * <AutoCrud service={PersonService} model={PersonModel} />\n * ```\n */\nexport function AutoCrud<TModel extends ProvisionalModel>({\n service,\n model,\n itemIdProperty,\n noNewButton,\n toolbarRenderer = (defaultContent): ReactNode => defaultContent,\n formProps,\n gridProps,\n style,\n id,\n className,\n}: AutoCrudProps<TModel>): JSX.Element {\n const [item, setItem] = useState<Value<TModel> | typeof emptyItem | undefined>(undefined);\n const fullScreen = useMediaQuery('(max-width: 600px), (max-height: 600px)');\n const autoGridRef = useRef<AutoGridRef>(null);\n const { headerRenderer: customFormHeaderRenderer, ...autoFormProps } = formProps ?? {};\n const formHeaderRenderer: AutoCrudFormHeaderRenderer<Value<TModel>> =\n customFormHeaderRenderer ?? defaultFormHeaderRenderer;\n\n const autoCrudId = useId();\n\n function refreshGrid() {\n autoGridRef.current?.refresh();\n }\n\n function handleCancel() {\n setItem(undefined);\n }\n\n const formHeader = item && item !== emptyItem ? formHeaderRenderer(item, !item) : formHeaderRenderer(null, !item);\n\n const toolbarContent = toolbarRenderer(\n noNewButton ? null : (\n <Button theme=\"primary\" onClick={() => setItem(emptyItem)}>\n + New\n </Button>\n ),\n );\n\n const mainSection = (\n <div className=\"auto-crud-main\">\n <AutoGrid\n {...gridProps}\n service={service}\n model={model as DetachedModelConstructor<AbstractModel<Value<TModel>>> | Model<Value<TModel>>}\n itemIdProperty={itemIdProperty}\n selectedItems={item && item !== emptyItem ? [item] : []}\n onActiveItemChanged={(e) => {\n const activeItem = e.detail.value;\n setItem(activeItem ?? undefined);\n }}\n ref={autoGridRef}\n aria-controls={autoFormProps.id ?? `auto-form-${id ?? autoCrudId}`}\n ></AutoGrid>\n {toolbarContent && <div className=\"auto-crud-toolbar\">{toolbarContent}</div>}\n </div>\n );\n\n const autoForm = (\n <AutoForm\n id={autoFormProps.id ?? `auto-form-${id ?? autoCrudId}`}\n deleteButtonVisible={true}\n {...autoFormProps}\n disabled={!item}\n service={service}\n model={model}\n itemIdProperty={itemIdProperty}\n item={item}\n onSubmitSuccess={({ item: submittedItem }) => {\n if (fullScreen) {\n setItem(undefined);\n } else {\n setItem(submittedItem);\n }\n refreshGrid();\n }}\n onDeleteSuccess={() => {\n setItem(undefined);\n refreshGrid();\n }}\n />\n );\n\n // If the \"New\" button is visible, the form is always shown.\n // Otherwise, the form is only shown when an item is being edited.\n return (\n <div className={`auto-crud ${className ?? ''}`} id={id} style={style}>\n {fullScreen ? (\n <>\n {mainSection}\n <AutoCrudDialog opened={!!item} header={formHeader} onClose={handleCancel}>\n {autoForm}\n </AutoCrudDialog>\n </>\n ) : (\n <SplitLayout theme=\"small\">\n {mainSection}\n {(!noNewButton || (item && item !== emptyItem)) && (\n <div className=\"auto-crud-form\">\n <div className=\"auto-crud-form-header\">{formHeader}</div>\n {autoForm}\n </div>\n )}\n </SplitLayout>\n )}\n </div>\n );\n}\n"]}
|
package/autoform-field.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autoform-field.js","sourceRoot":"","sources":["src/autoform-field.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,KAAK,EAAsD,MAAM,wBAAwB,CAAC;AAEnG,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,0CAA0C,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;AACtE,OAAO,EAAE,MAAM,EAAE,MAAM,oCAAoC,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,uCAAuC,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AACpE,OAAO,EACL,YAAY,EAEZ,aAAa,EAGb,SAAS,EACT,OAAO,GACR,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AA+H/C,SAAS,gBAAgB,CAAC,IAAwB,EAAE,YAA0B;IAC5E,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAE/C,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,KAAU,EAAE,QAAgB,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;AAC/G,CAAC;AAED,SAAS,kBAAkB,CACzB,oBAAmC,EACnC,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAsB,EAClD,kBAAuB,EAAE;IAEzB,MAAM,YAAY,GAAG,OAAO,IAAI,aAAa,CAAC,oBAAoB,CAAC,CAAC;IACpE,OAAO,YAAY,CAAC,YAAY,EAAE,EAAE,GAAG,UAAU,EAAE,GAAG,eAAe,EAAE,GAAG,YAAY,CAAC,KAAK,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;AAC5G,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAyB;IAClD,OAAO,kBAAkB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAyB;IACrD,OAAO,kBAAkB,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;AACjD,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAyB;IACrD,OAAO,kBAAkB,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAyB;IAClD,MAAM,IAAI,GAAG,iBAAiB,EAAE,CAAC;IACjC,OAAO,kBAAkB,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;AACzD,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAyB;IAClD,OAAO,kBAAkB,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;AAC/C,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAyB;IACtD,MAAM,IAAI,GAAG,qBAAqB,EAAE,CAAC;IACrC,OAAO,kBAAkB,CAAC,cAAc,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAyB;IAClD,MAAM,SAAS,GAAG,KAAK,CAAC,KAAkB,CAAC;IAC3C,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC1D,KAAK,EAAE,kBAAkB,CAAC,KAAK,CAAC;QAChC,KAAK;KACN,CAAC,CAAC,CAAC;IACJ,OAAO,kBAAkB,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;AACtD,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAyB;IACrD,OAAO,kBAAkB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;AAC7C,CAAC;AAED,SAAS,mBAAmB,CAAC,EAAE,KAAK,EAAE,UAAU,EAAsB;IACpE,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IAChC,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAChE,OAAO,KAAC,QAAQ,OAAK,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,SAAG,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAyB;IACrD,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IAC9C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,YAAY,CAAC,iBAAiB,CAAC;IAC9D,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAEhC,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,iBAAiB,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IACjE,MAAM,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;IAC/B,SAAS,CAAC,GAAG,EAAE;QACb,QAAQ,CAAC,aAAa,CAAC,CAAC,GAAG,iBAAiB,EAAE,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACxE,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IAEjB,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrB,MAAM,gBAAgB,GAAG,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC;QACvE,OAAO,OAAO,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,UAAU,GAAqB;QACnC,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,KAAK;QACL,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ;QAC5C,QAAQ,EAAE,OAAO,CAAC,QAAQ;KAC3B,CAAC;IAEF,MAAM,aAAa,GAAuB,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,UAAU,EAAE,CAAC;IAEjG,QAAQ,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;QAChC,KAAK,QAAQ;YACX,OAAO,KAAC,iBAAiB,OAAK,aAAa,GAAsB,CAAC;QACpE,KAAK,SAAS;YACZ,OAAO,KAAC,oBAAoB,OAAK,aAAa,GAAyB,CAAC;QAC1E,KAAK,SAAS;YACZ,OAAO,KAAC,oBAAoB,OAAK,aAAa,GAAyB,CAAC;QAC1E,KAAK,MAAM;YACT,OAAO,KAAC,iBAAiB,OAAK,aAAa,GAAsB,CAAC;QACpE,KAAK,MAAM;YACT,OAAO,KAAC,iBAAiB,OAAK,aAAa,GAAsB,CAAC;QACpE,KAAK,UAAU;YACb,OAAO,KAAC,qBAAqB,OAAK,aAAa,GAA0B,CAAC;QAC5E,KAAK,MAAM;YACT,OAAO,KAAC,iBAAiB,OAAK,aAAa,GAAsB,CAAC;QACpE,KAAK,SAAS;YACZ,OAAO,KAAC,oBAAoB,OAAK,aAAa,GAAyB,CAAC;QAC1E,KAAK,QAAQ;YACX,OAAO,KAAC,mBAAmB,OAAK,aAAa,GAAwB,CAAC;QACxE;YACE,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC","sourcesContent":["import { _enum, type AbstractModel, type EnumModel, type Validator } from '@vaadin/hilla-lit-form';\nimport type { FieldDirectiveResult, UseFormResult } from '@vaadin/hilla-react-form';\nimport { useFormPart } from '@vaadin/hilla-react-form';\nimport { Checkbox } from '@vaadin/react-components/Checkbox.js';\nimport { DatePicker } from '@vaadin/react-components/DatePicker.js';\nimport { DateTimePicker } from '@vaadin/react-components/DateTimePicker.js';\nimport { IntegerField } from '@vaadin/react-components/IntegerField.js';\nimport { NumberField } from '@vaadin/react-components/NumberField.js';\nimport { Select } from '@vaadin/react-components/Select.js';\nimport { TextArea } from '@vaadin/react-components/TextArea.js';\nimport { TextField } from '@vaadin/react-components/TextField.js';\nimport { TimePicker } from '@vaadin/react-components/TimePicker.js';\nimport {\n cloneElement,\n type ComponentType,\n createElement,\n type CSSProperties,\n type JSX,\n useEffect,\n useMemo,\n} from 'react';\nimport { useDatePickerI18n, useDateTimePickerI18n } from './locale.js';\nimport type { PropertyInfo } from './model-info.js';\nimport { convertToTitleCase } from './util.js';\n\nexport type AutoFormFieldProps = Readonly<{\n propertyInfo: PropertyInfo;\n form: UseFormResult<any>;\n options: FieldOptions;\n disabled?: boolean;\n}>;\n\ntype CustomFormFieldProps = FieldDirectiveResult & Readonly<{ label?: string; disabled?: boolean }>;\n\nexport type FieldOptions = Readonly<{\n /**\n * The id to apply to the field.\n */\n id?: string;\n /**\n * The class names to add to the field.\n */\n className?: string;\n /**\n * The style to apply to the field.\n */\n style?: CSSProperties;\n /**\n * The label to show for the field. If not specified, a human-readable label\n * is generated from the property name.\n */\n label?: string;\n /**\n * The placeholder to when the field is empty.\n *\n * Note that some field types, such as checkbox, do not support a placeholder.\n */\n placeholder?: string;\n /**\n * The helper text to display below the field.\n *\n * Note that some field types, such as checkbox, do not support a helper text.\n */\n helperText?: string;\n /**\n * The number of columns to span. This value is passed to the underlying\n * FormLayout, unless a custom layout is used. In that case, the value is\n * ignored.\n */\n colspan?: number;\n /**\n * Whether the field should be disabled.\n */\n disabled?: boolean;\n /**\n * Whether the field should be readonly.\n */\n readonly?: boolean;\n /**\n * The element to render for the field. This allows customizing field props\n * that are not supported by the field options, or to render a different field\n * component. Other field options are automatically applied to the element,\n * and the element is automatically bound to the form. If not specified, a\n * default field element is rendered based on the property type.\n *\n * The element must be a field component, such as TextField, TextArea,\n * NumberField, etc., otherwise form binding will not work. For more\n * sophisticated customizations, use the `renderer` option.\n *\n * If the field options also specify a renderer function, then the element is\n * ignored.\n *\n * Example enabling the clear button for a text field:\n * ```tsx\n * {\n * element: <TextField clearButtonVisible />\n * }\n * ```\n *\n * Example rendering a text area instead of a text field:\n * ```tsx\n * {\n * element: <TextArea />\n * }\n * ```\n */\n element?: JSX.Element;\n /**\n * Allows to specify a custom renderer for the field, for example to render a\n * custom type of field or apply an additional layout around the field. The\n * renderer receives field props that must be applied to the custom field\n * component in order to connect it to the form.\n *\n * In order to customize one of the default fields, or render a different type\n * of field, consider using the `element` option instead.\n *\n * Example:\n * ```tsx\n * {\n * renderer: ({ field }) => (\n * <div>\n * <TextArea {...field} />\n * <p>Number of words: {calculateNumberOfWords()}</p>\n * </div>\n * )\n * }\n * ```\n */\n renderer?(props: { field: CustomFormFieldProps }): JSX.Element;\n /**\n * Validators to apply to the field. The validators are added to the form\n * when the field is rendered.\n * UseMemo is recommended for the validators, so that they are not recreated\n * on every render.\n */\n validators?: Validator[];\n}>;\n\ntype CommonFieldProps = Pick<\n FieldOptions,\n 'className' | 'colspan' | 'disabled' | 'helperText' | 'id' | 'label' | 'placeholder' | 'readonly' | 'style'\n>;\n\ntype FieldRendererProps = Readonly<{\n model: AbstractModel;\n field: FieldDirectiveResult;\n element?: JSX.Element;\n fieldProps: CommonFieldProps;\n}>;\n\nfunction getPropertyModel(form: UseFormResult<any>, propertyInfo: PropertyInfo) {\n const pathParts = propertyInfo.name.split('.');\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n return pathParts.reduce((model: any, property: string) => (model ? model[property] : undefined), form.model);\n}\n\nfunction renderFieldElement(\n defaultComponentType: ComponentType,\n { element, field, fieldProps }: FieldRendererProps,\n additionalProps: any = {},\n) {\n const fieldElement = element ?? createElement(defaultComponentType);\n return cloneElement(fieldElement, { ...fieldProps, ...additionalProps, ...fieldElement.props, ...field });\n}\n\nfunction AutoFormTextField(props: FieldRendererProps) {\n return renderFieldElement(TextField, props);\n}\n\nfunction AutoFormIntegerField(props: FieldRendererProps) {\n return renderFieldElement(IntegerField, props);\n}\n\nfunction AutoFormDecimalField(props: FieldRendererProps) {\n return renderFieldElement(NumberField, props);\n}\n\nfunction AutoFormDateField(props: FieldRendererProps) {\n const i18n = useDatePickerI18n();\n return renderFieldElement(DatePicker, props, { i18n });\n}\n\nfunction AutoFormTimeField(props: FieldRendererProps) {\n return renderFieldElement(TimePicker, props);\n}\n\nfunction AutoFormDateTimeField(props: FieldRendererProps) {\n const i18n = useDateTimePickerI18n();\n return renderFieldElement(DateTimePicker, props, { i18n });\n}\n\nfunction AutoFormEnumField(props: FieldRendererProps) {\n const enumModel = props.model as EnumModel;\n const items = Object.keys(enumModel[_enum]).map((value) => ({\n label: convertToTitleCase(value),\n value,\n }));\n return renderFieldElement(Select, props, { items });\n}\n\nfunction AutoFormBooleanField(props: FieldRendererProps) {\n return renderFieldElement(Checkbox, props);\n}\n\nfunction AutoFormObjectField({ model, fieldProps }: FieldRendererProps) {\n const part = useFormPart(model);\n const jsonString = part.value ? JSON.stringify(part.value) : '';\n return <TextArea {...fieldProps} value={jsonString} readonly />;\n}\n\nexport function AutoFormField(props: AutoFormFieldProps): JSX.Element | null {\n const { form, propertyInfo, options } = props;\n const label = options.label ?? propertyInfo.humanReadableName;\n const model = getPropertyModel(form, propertyInfo);\n const field = form.field(model);\n\n const formPart = useFormPart(model);\n const defaultValidators = useMemo(() => formPart.validators, []);\n const { validators } = options;\n useEffect(() => {\n formPart.setValidators([...defaultValidators, ...(validators ?? [])]);\n }, [validators]);\n\n if (options.renderer) {\n const customFieldProps = { ...field, disabled: props.disabled, label };\n return options.renderer({ field: customFieldProps });\n }\n\n const fieldProps: CommonFieldProps = {\n id: options.id,\n className: options.className,\n style: options.style,\n label,\n placeholder: options.placeholder,\n helperText: options.helperText,\n colspan: options.colspan,\n disabled: options.disabled ?? props.disabled,\n readonly: options.readonly,\n };\n\n const rendererProps: FieldRendererProps = { model, field, element: options.element, fieldProps };\n\n switch (props.propertyInfo.type) {\n case 'string':\n return <AutoFormTextField {...rendererProps}></AutoFormTextField>;\n case 'integer':\n return <AutoFormIntegerField {...rendererProps}></AutoFormIntegerField>;\n case 'decimal':\n return <AutoFormDecimalField {...rendererProps}></AutoFormDecimalField>;\n case 'date':\n return <AutoFormDateField {...rendererProps}></AutoFormDateField>;\n case 'time':\n return <AutoFormTimeField {...rendererProps}></AutoFormTimeField>;\n case 'datetime':\n return <AutoFormDateTimeField {...rendererProps}></AutoFormDateTimeField>;\n case 'enum':\n return <AutoFormEnumField {...rendererProps}></AutoFormEnumField>;\n case 'boolean':\n return <AutoFormBooleanField {...rendererProps}></AutoFormBooleanField>;\n case 'object':\n return <AutoFormObjectField {...rendererProps}></AutoFormObjectField>;\n default:\n return null;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"autoform-field.js","sourceRoot":"","sources":["src/autoform-field.tsx"],"names":[],"mappings":"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","sourcesContent":["import { _enum, type ProvisionalModel, type Validator } from '@vaadin/hilla-lit-form';\nimport type { Enum, EnumModel } from '@vaadin/hilla-models';\nimport type { FieldDirectiveResult, UseFormResult } from '@vaadin/hilla-react-form';\nimport { useFormPart } from '@vaadin/hilla-react-form';\nimport { Checkbox } from '@vaadin/react-components/Checkbox.js';\nimport { DatePicker } from '@vaadin/react-components/DatePicker.js';\nimport { DateTimePicker } from '@vaadin/react-components/DateTimePicker.js';\nimport { IntegerField } from '@vaadin/react-components/IntegerField.js';\nimport { NumberField } from '@vaadin/react-components/NumberField.js';\nimport { Select } from '@vaadin/react-components/Select.js';\nimport { TextArea } from '@vaadin/react-components/TextArea.js';\nimport { TextField } from '@vaadin/react-components/TextField.js';\nimport { TimePicker } from '@vaadin/react-components/TimePicker.js';\nimport {\n cloneElement,\n type ComponentType,\n createElement,\n type CSSProperties,\n type JSX,\n useEffect,\n useMemo,\n} from 'react';\nimport { useDatePickerI18n, useDateTimePickerI18n } from './locale.js';\nimport type { PropertyInfo } from './model-info.js';\nimport { convertToTitleCase } from './util.js';\n\nexport type AutoFormFieldProps = Readonly<{\n propertyInfo: PropertyInfo;\n form: UseFormResult<any>;\n options: FieldOptions;\n disabled?: boolean;\n}>;\n\ntype CustomFormFieldProps = FieldDirectiveResult & Readonly<{ label?: string; disabled?: boolean }>;\n\nexport type FieldOptions = Readonly<{\n /**\n * The id to apply to the field.\n */\n id?: string;\n /**\n * The class names to add to the field.\n */\n className?: string;\n /**\n * The style to apply to the field.\n */\n style?: CSSProperties;\n /**\n * The label to show for the field. If not specified, a human-readable label\n * is generated from the property name.\n */\n label?: string;\n /**\n * The placeholder to when the field is empty.\n *\n * Note that some field types, such as checkbox, do not support a placeholder.\n */\n placeholder?: string;\n /**\n * The helper text to display below the field.\n *\n * Note that some field types, such as checkbox, do not support a helper text.\n */\n helperText?: string;\n /**\n * The number of columns to span. This value is passed to the underlying\n * FormLayout, unless a custom layout is used. In that case, the value is\n * ignored.\n */\n colspan?: number;\n /**\n * Whether the field should be disabled.\n */\n disabled?: boolean;\n /**\n * Whether the field should be readonly.\n */\n readonly?: boolean;\n /**\n * The element to render for the field. This allows customizing field props\n * that are not supported by the field options, or to render a different field\n * component. Other field options are automatically applied to the element,\n * and the element is automatically bound to the form. If not specified, a\n * default field element is rendered based on the property type.\n *\n * The element must be a field component, such as TextField, TextArea,\n * NumberField, etc., otherwise form binding will not work. For more\n * sophisticated customizations, use the `renderer` option.\n *\n * If the field options also specify a renderer function, then the element is\n * ignored.\n *\n * Example enabling the clear button for a text field:\n * ```tsx\n * {\n * element: <TextField clearButtonVisible />\n * }\n * ```\n *\n * Example rendering a text area instead of a text field:\n * ```tsx\n * {\n * element: <TextArea />\n * }\n * ```\n */\n element?: JSX.Element;\n /**\n * Allows to specify a custom renderer for the field, for example to render a\n * custom type of field or apply an additional layout around the field. The\n * renderer receives field props that must be applied to the custom field\n * component in order to connect it to the form.\n *\n * In order to customize one of the default fields, or render a different type\n * of field, consider using the `element` option instead.\n *\n * Example:\n * ```tsx\n * {\n * renderer: ({ field }) => (\n * <div>\n * <TextArea {...field} />\n * <p>Number of words: {calculateNumberOfWords()}</p>\n * </div>\n * )\n * }\n * ```\n */\n renderer?(props: { field: CustomFormFieldProps }): JSX.Element;\n /**\n * Validators to apply to the field. The validators are added to the form\n * when the field is rendered.\n * UseMemo is recommended for the validators, so that they are not recreated\n * on every render.\n */\n validators?: Validator[];\n}>;\n\ntype CommonFieldProps = Pick<\n FieldOptions,\n 'className' | 'colspan' | 'disabled' | 'helperText' | 'id' | 'label' | 'placeholder' | 'readonly' | 'style'\n>;\n\ntype FieldRendererProps = Readonly<{\n model: ProvisionalModel;\n field: FieldDirectiveResult;\n element?: JSX.Element;\n fieldProps: CommonFieldProps;\n}>;\n\nfunction getPropertyModel(form: UseFormResult<any>, propertyInfo: PropertyInfo) {\n const pathParts = propertyInfo.name.split('.');\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n return pathParts.reduce((model: any, property: string) => (model ? model[property] : undefined), form.model);\n}\n\nfunction renderFieldElement(\n defaultComponentType: ComponentType,\n { element, field, fieldProps }: FieldRendererProps,\n additionalProps: any = {},\n) {\n const fieldElement = element ?? createElement(defaultComponentType);\n return cloneElement(fieldElement, { ...fieldProps, ...additionalProps, ...fieldElement.props, ...field });\n}\n\nfunction AutoFormTextField(props: FieldRendererProps) {\n return renderFieldElement(TextField, props);\n}\n\nfunction AutoFormIntegerField(props: FieldRendererProps) {\n return renderFieldElement(IntegerField, props);\n}\n\nfunction AutoFormDecimalField(props: FieldRendererProps) {\n return renderFieldElement(NumberField, props);\n}\n\nfunction AutoFormDateField(props: FieldRendererProps) {\n const i18n = useDatePickerI18n();\n return renderFieldElement(DatePicker, props, { i18n });\n}\n\nfunction AutoFormTimeField(props: FieldRendererProps) {\n return renderFieldElement(TimePicker, props);\n}\n\nfunction AutoFormDateTimeField(props: FieldRendererProps) {\n const i18n = useDateTimePickerI18n();\n return renderFieldElement(DateTimePicker, props, { i18n });\n}\n\nfunction AutoFormEnumField(props: FieldRendererProps) {\n const enumModel = props.model as EnumModel<typeof Enum>;\n const items = Object.keys(enumModel[_enum]).map((value) => ({\n label: convertToTitleCase(value),\n value,\n }));\n return renderFieldElement(Select, props, { items });\n}\n\nfunction AutoFormBooleanField(props: FieldRendererProps) {\n return renderFieldElement(Checkbox, props);\n}\n\nfunction AutoFormObjectField({ model, fieldProps }: FieldRendererProps) {\n const part = useFormPart(model);\n const jsonString = part.value ? JSON.stringify(part.value) : '';\n return <TextArea {...fieldProps} value={jsonString} readonly />;\n}\n\nexport function AutoFormField(props: AutoFormFieldProps): JSX.Element | null {\n const { form, propertyInfo, options } = props;\n const label = options.label ?? propertyInfo.humanReadableName;\n const model = getPropertyModel(form, propertyInfo);\n const field = form.field(model);\n\n const formPart = useFormPart(model);\n const defaultValidators = useMemo(() => formPart.validators, []);\n const { validators } = options;\n useEffect(() => {\n formPart.setValidators([...defaultValidators, ...(validators ?? [])]);\n }, [validators]);\n\n if (options.renderer) {\n const customFieldProps = { ...field, disabled: props.disabled, label };\n return options.renderer({ field: customFieldProps });\n }\n\n const fieldProps: CommonFieldProps = {\n id: options.id,\n className: options.className,\n style: options.style,\n label,\n placeholder: options.placeholder,\n helperText: options.helperText,\n colspan: options.colspan,\n disabled: options.disabled ?? props.disabled,\n readonly: options.readonly,\n };\n\n const rendererProps: FieldRendererProps = { model, field, element: options.element, fieldProps };\n\n switch (props.propertyInfo.type) {\n case 'string':\n return <AutoFormTextField {...rendererProps}></AutoFormTextField>;\n case 'integer':\n return <AutoFormIntegerField {...rendererProps}></AutoFormIntegerField>;\n case 'decimal':\n return <AutoFormDecimalField {...rendererProps}></AutoFormDecimalField>;\n case 'date':\n return <AutoFormDateField {...rendererProps}></AutoFormDateField>;\n case 'time':\n return <AutoFormTimeField {...rendererProps}></AutoFormTimeField>;\n case 'datetime':\n return <AutoFormDateTimeField {...rendererProps}></AutoFormDateTimeField>;\n case 'enum':\n return <AutoFormEnumField {...rendererProps}></AutoFormEnumField>;\n case 'boolean':\n return <AutoFormBooleanField {...rendererProps}></AutoFormBooleanField>;\n case 'object':\n return <AutoFormObjectField {...rendererProps}></AutoFormObjectField>;\n default:\n return null;\n }\n}\n"]}
|
package/autoform.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { EndpointError } from '@vaadin/hilla-frontend';
|
|
2
|
-
import { type AbstractModel, type DetachedModelConstructor, type Value } from '@vaadin/hilla-lit-form';
|
|
2
|
+
import { type AbstractModel, type DetachedModelConstructor, type ProvisionalModel, type Value } from '@vaadin/hilla-lit-form';
|
|
3
|
+
import type { Model } from '@vaadin/hilla-models';
|
|
3
4
|
import { type UseFormResult } from '@vaadin/hilla-react-form';
|
|
4
5
|
import { FormLayout } from '@vaadin/react-components/FormLayout';
|
|
5
6
|
import { type ComponentType, type JSX, type ReactElement } from 'react';
|
|
@@ -21,13 +22,13 @@ export type DeleteErrorEvent = {
|
|
|
21
22
|
export type DeleteEvent<TItem> = {
|
|
22
23
|
item: TItem;
|
|
23
24
|
};
|
|
24
|
-
export type AutoFormLayoutRendererProps<M extends
|
|
25
|
+
export type AutoFormLayoutRendererProps<M extends ProvisionalModel> = Readonly<{
|
|
25
26
|
form: UseFormResult<M>;
|
|
26
27
|
children: ReadonlyArray<ReactElement<AutoFormFieldProps>>;
|
|
27
28
|
}>;
|
|
28
|
-
export type AutoFormProps<M extends
|
|
29
|
+
export type AutoFormProps<M extends ProvisionalModel = ProvisionalModel> = ComponentStyleProps & Readonly<{
|
|
29
30
|
service: FormService<Value<M>>;
|
|
30
|
-
model: DetachedModelConstructor<M
|
|
31
|
+
model: DetachedModelConstructor<M & AbstractModel> | (M & Model);
|
|
31
32
|
itemIdProperty?: string;
|
|
32
33
|
item?: Value<M> | typeof emptyItem | null;
|
|
33
34
|
disabled?: boolean;
|
|
@@ -42,4 +43,4 @@ export type AutoFormProps<M extends AbstractModel = AbstractModel> = ComponentSt
|
|
|
42
43
|
onDeleteError?({ error }: DeleteErrorEvent): void;
|
|
43
44
|
onDeleteSuccess?({ item }: DeleteEvent<Value<M>>): void;
|
|
44
45
|
}>;
|
|
45
|
-
export declare function AutoForm<M extends
|
|
46
|
+
export declare function AutoForm<M extends ProvisionalModel>({ service, model, itemIdProperty, item, onSubmitError, onSubmitSuccess, disabled, layoutRenderer: LayoutRenderer, visibleFields, hiddenFields, formLayoutProps, fieldOptions, style, id, className, deleteButtonVisible, onDeleteSuccess, onDeleteError, }: AutoFormProps<M>): JSX.Element;
|
package/autoform.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autoform.js","sourceRoot":"","sources":["src/autoform.tsx"],"names":[],"mappings":"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","sourcesContent":["import { EndpointError } from '@vaadin/hilla-frontend';\nimport {\n type AbstractModel,\n type DetachedModelConstructor,\n type ProvisionalModelConstructor,\n ValidationError,\n type Value,\n} from '@vaadin/hilla-lit-form';\nimport { useForm, type UseFormResult } from '@vaadin/hilla-react-form';\nimport { Button } from '@vaadin/react-components/Button.js';\nimport { ConfirmDialog } from '@vaadin/react-components/ConfirmDialog';\nimport { FormLayout } from '@vaadin/react-components/FormLayout';\nimport { VerticalLayout } from '@vaadin/react-components/VerticalLayout.js';\nimport {\n type ComponentType,\n type JSX,\n type KeyboardEvent,\n type ReactElement,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { AutoFormField, type AutoFormFieldProps, type FieldOptions } from './autoform-field.js';\nimport css from './autoform.obj.js';\nimport type { FormService } from './crud.js';\nimport { getDefaultProperties, ModelInfo, type PropertyInfo } from './model-info.js';\nimport { type ComponentStyleProps, registerStylesheet } from './util.js';\n\nregisterStylesheet(css);\n\nexport const emptyItem = Symbol();\n\n/**\n * An event that is fired when an error occurs while submitting the form.\n */\nexport type SubmitErrorEvent = {\n /**\n * The error that occurred.\n */\n error: EndpointError;\n /**\n * A function that can be used to set a custom error message. This will be\n * shown in the form at the same position as the default error message.\n * You are not required to call this function if you want to handle the\n * error differently.\n */\n setMessage(message: string): void;\n};\n\n/**\n * An event that is fired when the form has been successfully submitted.\n */\nexport type SubmitEvent<TItem> = {\n /**\n * The item that was submitted, as returned by the service.\n */\n item: TItem;\n};\n\n/**\n * An event that is fired when an error occurs while deleting an item.\n */\nexport type DeleteErrorEvent = {\n /**\n * The error that occurred.\n */\n error: EndpointError;\n /**\n * A function that can be used to set a custom error message. This will be\n * shown in the form at the same position as the default error message.\n * You are not required to call this function if you want to handle the\n * error differently.\n */\n setMessage(message: string): void;\n};\n\n/**\n * An event that is fired when the form has been successfully deleted.\n */\nexport type DeleteEvent<TItem> = {\n /**\n * The item that was deleted, as returned by the service.\n */\n item: TItem;\n};\n\nexport type AutoFormLayoutRendererProps<M extends AbstractModel> = Readonly<{\n form: UseFormResult<M>;\n children: ReadonlyArray<ReactElement<AutoFormFieldProps>>;\n}>;\n\nexport type AutoFormProps<M extends AbstractModel = AbstractModel> = ComponentStyleProps &\n Readonly<{\n /**\n * The service to use for saving and deleting items. This must be a\n * TypeScript service that has been generated by Hilla from a backend Java\n * service that implements the `com.vaadin.hilla.crud.FormService` interface.\n */\n service: FormService<Value<M>>;\n /**\n * The entity model to use, which determines which fields to show in the\n * form. This must be a Typescript model class that has been generated by\n * Hilla from a backend Java class. The model must match with the type of\n * the items handled by the service. For example, a `PersonModel` can be\n * used with a service that handles `Person` instances.\n *\n * By default, the form shows fields for all properties of the model which\n * have a type that is supported. Use the `visibleFields` option to customize\n * which fields to show and in which order.\n */\n model: DetachedModelConstructor<M>;\n /**\n * The property to use to detect an item's ID. The item ID is required for\n * deleting items via the `FormService.delete` method. The delete button\n * will not be shown if no item ID can be found.\n *\n * By default, the component uses the property annotated with\n * `jakarta.persistence.Id`, or a property named `id`, in that order.\n * This option can be used to override the default behavior, or define the ID\n * property in case a class doesn't have a property matching the defaults.\n */\n itemIdProperty?: string;\n /**\n * The item to edit in the form. The form fields are automatically populated\n * with values from the item's properties. In order to create a new item,\n * either pass `null`, or leave this prop as undefined.\n *\n * Use the `onSubmitSuccess` callback to get notified when the item has been\n * saved.\n *\n * When submitting a new item (i.e. when `item` is null or undefined), the\n * form will be automatically cleared, allowing to submit another new item.\n * In order to keep editing the same item after submitting, set the `item`\n * prop to the new item.\n */\n item?: Value<M> | typeof emptyItem | null;\n /**\n * Whether the form should be disabled. This disables all form fields and\n * all buttons.\n */\n disabled?: boolean;\n /**\n * Allows to customize the layout of the form by providing a custom\n * renderer. The renderer receives the form instance and the pre-rendered\n * fields as props. The renderer can either reuse the pre-rendered fields in\n * the custom layout, or render custom fields and connect them to the form\n * manually.\n *\n * Check the component documentation for details and examples.\n *\n * Example using pre-rendered fields:\n * ```tsx\n * <AutoForm layoutRenderer={({ children }) =>\n * <VerticalLayout>\n * {children}\n * <p>All data is collected anonymously.</p>\n * </VerticalLayout>\n * } />\n * ```\n *\n * Example rendering custom fields:\n * ```tsx\n * <AutoForm layoutRenderer={({ form }) =>\n * <VerticalLayout>\n * <TextField {...form.field(form.model.name)} />\n * ...\n * </VerticalLayout>\n * } />\n * ```\n */\n layoutRenderer?: ComponentType<AutoFormLayoutRendererProps<M>>;\n /**\n * Defines the fields to show in the form, and in which order. This takes\n * an array of property names. Properties that are not included in this\n * array will not be shown in the form, and properties that are included,\n * but don't exist in the model, will be ignored.\n */\n visibleFields?: string[];\n /**\n * Defines the fields to hide in the form, keeping the default order. This takes\n * an array of property names. Properties that are not included in this\n * array will not be hidden in the form, and properties that are included,\n * but don't exist in the model, will be ignored.\n */\n hiddenFields?: string[];\n /**\n * Allows to customize the FormLayout used by default. This is especially useful\n * to define the `responsiveSteps`. See the\n * {@link https://hilla.dev/docs/react/components/form-layout | FormLayout documentation}\n * for details.\n */\n formLayoutProps?: ComponentStyleProps & Pick<Parameters<typeof FormLayout>[0], 'responsiveSteps'>;\n /**\n * Allows to customize individual fields of the form. This takes an object\n * where the keys are property names, and the values are options for the\n * respective field for editing that property.\n */\n fieldOptions?: Record<string, FieldOptions>;\n /**\n * Whether to show the delete button in the form. This is disabled by\n * default. If enabled, the delete button will only be shown when editing\n * an existing item, which means that `item` is not null. The delete button\n * will also only be shown if an item has a discernible ID. See the\n * `itemIdProperty` prop for details how the item ID is detected.\n *\n * Use the `onDeleteSuccess` callback to get notified when the item has been\n * deleted.\n *\n * NOTE: This only hides the button, it does not prevent from calling the\n * delete method on the service. To completely disable deleting, you must\n * override the `delete` method in the backend Java service to either throw\n * an exception or annotate it with `@DenyAll` to prevent access.\n */\n deleteButtonVisible?: boolean;\n /**\n * A callback that will be called if an unexpected error occurs while\n * submitting the form.\n *\n * Note that this will not be called for validation errors, which are\n * handled automatically.\n */\n onSubmitError?({ error }: SubmitErrorEvent): void;\n /**\n * A callback that will be called after the form has been successfully\n * submitted and the item has been saved.\n *\n * When submitting a new item (i.e. when `item` is null or undefined), the\n * form will be automatically cleared, allowing to submit another new item.\n * In order to keep editing the same item after submitting, set the `item`\n * prop to the new item.\n */\n onSubmitSuccess?({ item }: SubmitEvent<Value<M>>): void;\n /**\n * A callback that will be called if an unexpected error occurs while\n * deleting an item.\n */\n onDeleteError?({ error }: DeleteErrorEvent): void;\n /**\n * A callback that will be called after the form has been successfully\n * deleted.\n */\n onDeleteSuccess?({ item }: DeleteEvent<Value<M>>): void;\n }>;\n\n/**\n * Auto Form is a component that automatically generates a form for editing,\n * updating and deleting items from a backend service.\n *\n * Example usage:\n * ```tsx\n * import { AutoForm } from '@vaadin/hilla-react-crud';\n * import PersonService from 'Frontend/generated/endpoints';\n * import PersonModel from 'Frontend/generated/com/example/application/Person';\n *\n * <AutoForm\n * service={PersonService}\n * model={PersonModel}\n * onSubmitSuccess={({ item }) => {\n * console.log('Submitted item:', item);\n * }}\n * />\n * ```\n */\nexport function AutoForm<M extends AbstractModel>({\n service,\n model,\n itemIdProperty,\n item = emptyItem,\n onSubmitError,\n onSubmitSuccess,\n disabled,\n layoutRenderer: LayoutRenderer,\n visibleFields,\n hiddenFields,\n formLayoutProps,\n fieldOptions,\n style,\n id,\n className,\n deleteButtonVisible,\n onDeleteSuccess,\n onDeleteError,\n}: AutoFormProps<M>): JSX.Element {\n const form = useForm(model as ProvisionalModelConstructor<M>, {\n onSubmit: async (formItem) => service.save(formItem),\n });\n const formErrorRef = useRef<HTMLDivElement>(null);\n const [formError, setFormError] = useState<JSX.Element | string>('');\n const [showDeleteDialog, setShowDeleteDialog] = useState(false);\n const modelInfo = useMemo(() => new ModelInfo(model, itemIdProperty), [model]);\n\n const isEditMode = item !== undefined && item !== null && item !== emptyItem;\n const showDeleteButton = deleteButtonVisible && isEditMode && modelInfo.idProperty;\n const isSubmitDisabled = !!disabled || (isEditMode && !form.dirty);\n\n useEffect(() => {\n if (item !== emptyItem) {\n form.read(item);\n } else {\n form.clear();\n }\n }, [item]);\n\n useEffect(() => {\n formErrorRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' });\n }, [formError]);\n\n function handleSubmitError(error: unknown) {\n if (error instanceof ValidationError) {\n const nonPropertyErrorMessages = error.errors\n .filter((validationError) => !validationError.property || typeof validationError.property === 'string')\n .map((validationError) => {\n const property =\n validationError.property && typeof validationError.property === 'string'\n ? `${validationError.property}: `\n : '';\n return `${property}${validationError.validatorMessage ?? validationError.message}`;\n });\n if (nonPropertyErrorMessages.length > 0) {\n setFormError(\n <div ref={formErrorRef}>\n Validation errors:\n <ul>\n {nonPropertyErrorMessages.map((message, index) => (\n <li key={index}>{message}</li>\n ))}\n </ul>\n </div>,\n );\n }\n } else if (error instanceof EndpointError) {\n if (onSubmitError) {\n onSubmitError({ error, setMessage: setFormError });\n } else {\n setFormError(error.message);\n }\n } else {\n throw error;\n }\n }\n\n async function handleSubmit(): Promise<void> {\n try {\n setFormError('');\n const newItem = await form.submit();\n if (newItem === undefined) {\n // If update returns an empty object, then no update was performed\n throw new EndpointError('No update performed');\n } else if (onSubmitSuccess) {\n onSubmitSuccess({ item: newItem });\n }\n // Automatically clear the form after submitting a new item.\n // Otherwise, there would be no way for the developer to clear it, as the\n // there is no new value to set for the item prop to trigger the above\n // effect in case the prop is already null, undefined or the empty item.\n if (!item || item === emptyItem) {\n form.clear();\n }\n } catch (error) {\n handleSubmitError(error);\n }\n }\n\n function deleteItem() {\n setShowDeleteDialog(true);\n }\n\n async function confirmDelete() {\n // At this point, item can not be null or emptyItem\n const deletedItem = item as Value<M>;\n try {\n const idProperty = modelInfo.idProperty!;\n // eslint-disable-next-line\n const id = (item as any)[idProperty.name];\n await service.delete(id);\n if (onDeleteSuccess) {\n onDeleteSuccess({ item: deletedItem });\n }\n } catch (error) {\n if (error instanceof EndpointError) {\n if (onDeleteError) {\n onDeleteError({ error, setMessage: setFormError });\n } else {\n setFormError(error.message);\n }\n } else {\n throw error;\n }\n } finally {\n setShowDeleteDialog(false);\n }\n }\n\n function cancelDelete() {\n setShowDeleteDialog(false);\n }\n\n const handleKeyDown = (event: KeyboardEvent): void => {\n if (event.target instanceof HTMLTextAreaElement) {\n return;\n }\n if (event.key === 'Enter' && !isSubmitDisabled) {\n // eslint-disable-next-line no-void\n void handleSubmit();\n }\n };\n\n function createAutoFormField(propertyInfo: PropertyInfo): JSX.Element {\n const fieldOptionsForProperty = fieldOptions?.[propertyInfo.name] ?? {};\n\n return (\n <AutoFormField\n key={propertyInfo.name}\n propertyInfo={propertyInfo}\n form={form}\n disabled={disabled}\n options={fieldOptionsForProperty}\n />\n );\n }\n\n let visibleProperties = visibleFields ? modelInfo.getProperties(visibleFields) : getDefaultProperties(modelInfo);\n\n // When using `hiddenFields`, remove fields to hide using their `name`\n if (hiddenFields) {\n visibleProperties = visibleProperties.filter(({ name }) => !hiddenFields.includes(name));\n }\n\n const fields = visibleProperties.map(createAutoFormField);\n\n const layout = LayoutRenderer ? (\n <LayoutRenderer form={form}>{fields}</LayoutRenderer>\n ) : (\n <FormLayout {...formLayoutProps}>{fields}</FormLayout>\n );\n\n return (\n <div className={`auto-form ${className ?? ''}`} id={id} style={style} data-testid=\"auto-form\">\n <VerticalLayout className=\"auto-form-fields\" onKeyDown={handleKeyDown}>\n {layout}\n {formError ? <div style={{ color: 'var(--lumo-error-color)' }}>{formError}</div> : <></>}\n </VerticalLayout>\n <div className=\"auto-form-toolbar\">\n <Button\n theme=\"primary\"\n disabled={isSubmitDisabled}\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n onClick={handleSubmit}\n >\n Submit\n </Button>\n {form.dirty ? (\n <Button theme=\"tertiary\" onClick={() => form.reset()}>\n Discard\n </Button>\n ) : null}\n {showDeleteButton && (\n <Button className=\"auto-form-delete-button\" theme=\"tertiary error\" onClick={deleteItem}>\n Delete...\n </Button>\n )}\n </div>\n {showDeleteDialog && (\n <ConfirmDialog\n opened\n header=\"Delete item\"\n confirmTheme=\"error\"\n cancelButtonVisible\n // eslint-disable-next-line\n onConfirm={confirmDelete}\n onCancel={cancelDelete}\n >\n Are you sure you want to delete the selected item?\n </ConfirmDialog>\n )}\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"autoform.js","sourceRoot":"","sources":["src/autoform.tsx"],"names":[],"mappings":"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","sourcesContent":["import { EndpointError } from '@vaadin/hilla-frontend';\nimport {\n type AbstractModel,\n type DetachedModelConstructor,\n type ProvisionalModel,\n type ProvisionalModelConstructor,\n ValidationError,\n type Value,\n} from '@vaadin/hilla-lit-form';\nimport type { Model } from '@vaadin/hilla-models';\nimport { useForm, type UseFormResult } from '@vaadin/hilla-react-form';\nimport { Button } from '@vaadin/react-components/Button.js';\nimport { ConfirmDialog } from '@vaadin/react-components/ConfirmDialog';\nimport { FormLayout } from '@vaadin/react-components/FormLayout';\nimport { VerticalLayout } from '@vaadin/react-components/VerticalLayout.js';\nimport {\n type ComponentType,\n type JSX,\n type KeyboardEvent,\n type ReactElement,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { AutoFormField, type AutoFormFieldProps, type FieldOptions } from './autoform-field.js';\nimport css from './autoform.obj.js';\nimport type { FormService } from './crud.js';\nimport { getDefaultProperties, ModelInfo, type PropertyInfo } from './model-info.js';\nimport { type ComponentStyleProps, registerStylesheet } from './util.js';\n\nregisterStylesheet(css);\n\nexport const emptyItem = Symbol();\n\n/**\n * An event that is fired when an error occurs while submitting the form.\n */\nexport type SubmitErrorEvent = {\n /**\n * The error that occurred.\n */\n error: EndpointError;\n /**\n * A function that can be used to set a custom error message. This will be\n * shown in the form at the same position as the default error message.\n * You are not required to call this function if you want to handle the\n * error differently.\n */\n setMessage(message: string): void;\n};\n\n/**\n * An event that is fired when the form has been successfully submitted.\n */\nexport type SubmitEvent<TItem> = {\n /**\n * The item that was submitted, as returned by the service.\n */\n item: TItem;\n};\n\n/**\n * An event that is fired when an error occurs while deleting an item.\n */\nexport type DeleteErrorEvent = {\n /**\n * The error that occurred.\n */\n error: EndpointError;\n /**\n * A function that can be used to set a custom error message. This will be\n * shown in the form at the same position as the default error message.\n * You are not required to call this function if you want to handle the\n * error differently.\n */\n setMessage(message: string): void;\n};\n\n/**\n * An event that is fired when the form has been successfully deleted.\n */\nexport type DeleteEvent<TItem> = {\n /**\n * The item that was deleted, as returned by the service.\n */\n item: TItem;\n};\n\nexport type AutoFormLayoutRendererProps<M extends ProvisionalModel> = Readonly<{\n form: UseFormResult<M>;\n children: ReadonlyArray<ReactElement<AutoFormFieldProps>>;\n}>;\n\nexport type AutoFormProps<M extends ProvisionalModel = ProvisionalModel> = ComponentStyleProps &\n Readonly<{\n /**\n * The service to use for saving and deleting items. This must be a\n * TypeScript service that has been generated by Hilla from a backend Java\n * service that implements the `com.vaadin.hilla.crud.FormService` interface.\n */\n service: FormService<Value<M>>;\n /**\n * The entity model to use, which determines which fields to show in the\n * form. This must be a Typescript model class that has been generated by\n * Hilla from a backend Java class. The model must match with the type of\n * the items handled by the service. For example, a `PersonModel` can be\n * used with a service that handles `Person` instances.\n *\n * By default, the form shows fields for all properties of the model which\n * have a type that is supported. Use the `visibleFields` option to customize\n * which fields to show and in which order.\n */\n model: DetachedModelConstructor<M & AbstractModel> | (M & Model);\n /**\n * The property to use to detect an item's ID. The item ID is required for\n * deleting items via the `FormService.delete` method. The delete button\n * will not be shown if no item ID can be found.\n *\n * By default, the component uses the property annotated with\n * `jakarta.persistence.Id`, or a property named `id`, in that order.\n * This option can be used to override the default behavior, or define the ID\n * property in case a class doesn't have a property matching the defaults.\n */\n itemIdProperty?: string;\n /**\n * The item to edit in the form. The form fields are automatically populated\n * with values from the item's properties. In order to create a new item,\n * either pass `null`, or leave this prop as undefined.\n *\n * Use the `onSubmitSuccess` callback to get notified when the item has been\n * saved.\n *\n * When submitting a new item (i.e. when `item` is null or undefined), the\n * form will be automatically cleared, allowing to submit another new item.\n * In order to keep editing the same item after submitting, set the `item`\n * prop to the new item.\n */\n item?: Value<M> | typeof emptyItem | null;\n /**\n * Whether the form should be disabled. This disables all form fields and\n * all buttons.\n */\n disabled?: boolean;\n /**\n * Allows to customize the layout of the form by providing a custom\n * renderer. The renderer receives the form instance and the pre-rendered\n * fields as props. The renderer can either reuse the pre-rendered fields in\n * the custom layout, or render custom fields and connect them to the form\n * manually.\n *\n * Check the component documentation for details and examples.\n *\n * Example using pre-rendered fields:\n * ```tsx\n * <AutoForm layoutRenderer={({ children }) =>\n * <VerticalLayout>\n * {children}\n * <p>All data is collected anonymously.</p>\n * </VerticalLayout>\n * } />\n * ```\n *\n * Example rendering custom fields:\n * ```tsx\n * <AutoForm layoutRenderer={({ form }) =>\n * <VerticalLayout>\n * <TextField {...form.field(form.model.name)} />\n * ...\n * </VerticalLayout>\n * } />\n * ```\n */\n layoutRenderer?: ComponentType<AutoFormLayoutRendererProps<M>>;\n /**\n * Defines the fields to show in the form, and in which order. This takes\n * an array of property names. Properties that are not included in this\n * array will not be shown in the form, and properties that are included,\n * but don't exist in the model, will be ignored.\n */\n visibleFields?: string[];\n /**\n * Defines the fields to hide in the form, keeping the default order. This takes\n * an array of property names. Properties that are not included in this\n * array will not be hidden in the form, and properties that are included,\n * but don't exist in the model, will be ignored.\n */\n hiddenFields?: string[];\n /**\n * Allows to customize the FormLayout used by default. This is especially useful\n * to define the `responsiveSteps`. See the\n * {@link https://hilla.dev/docs/react/components/form-layout | FormLayout documentation}\n * for details.\n */\n formLayoutProps?: ComponentStyleProps & Pick<Parameters<typeof FormLayout>[0], 'responsiveSteps'>;\n /**\n * Allows to customize individual fields of the form. This takes an object\n * where the keys are property names, and the values are options for the\n * respective field for editing that property.\n */\n fieldOptions?: Record<string, FieldOptions>;\n /**\n * Whether to show the delete button in the form. This is disabled by\n * default. If enabled, the delete button will only be shown when editing\n * an existing item, which means that `item` is not null. The delete button\n * will also only be shown if an item has a discernible ID. See the\n * `itemIdProperty` prop for details how the item ID is detected.\n *\n * Use the `onDeleteSuccess` callback to get notified when the item has been\n * deleted.\n *\n * NOTE: This only hides the button, it does not prevent from calling the\n * delete method on the service. To completely disable deleting, you must\n * override the `delete` method in the backend Java service to either throw\n * an exception or annotate it with `@DenyAll` to prevent access.\n */\n deleteButtonVisible?: boolean;\n /**\n * A callback that will be called if an unexpected error occurs while\n * submitting the form.\n *\n * Note that this will not be called for validation errors, which are\n * handled automatically.\n */\n onSubmitError?({ error }: SubmitErrorEvent): void;\n /**\n * A callback that will be called after the form has been successfully\n * submitted and the item has been saved.\n *\n * When submitting a new item (i.e. when `item` is null or undefined), the\n * form will be automatically cleared, allowing to submit another new item.\n * In order to keep editing the same item after submitting, set the `item`\n * prop to the new item.\n */\n onSubmitSuccess?({ item }: SubmitEvent<Value<M>>): void;\n /**\n * A callback that will be called if an unexpected error occurs while\n * deleting an item.\n */\n onDeleteError?({ error }: DeleteErrorEvent): void;\n /**\n * A callback that will be called after the form has been successfully\n * deleted.\n */\n onDeleteSuccess?({ item }: DeleteEvent<Value<M>>): void;\n }>;\n\n/**\n * Auto Form is a component that automatically generates a form for editing,\n * updating and deleting items from a backend service.\n *\n * Example usage:\n * ```tsx\n * import { AutoForm } from '@vaadin/hilla-react-crud';\n * import PersonService from 'Frontend/generated/endpoints';\n * import PersonModel from 'Frontend/generated/com/example/application/Person';\n *\n * <AutoForm\n * service={PersonService}\n * model={PersonModel}\n * onSubmitSuccess={({ item }) => {\n * console.log('Submitted item:', item);\n * }}\n * />\n * ```\n */\nexport function AutoForm<M extends ProvisionalModel>({\n service,\n model,\n itemIdProperty,\n item = emptyItem,\n onSubmitError,\n onSubmitSuccess,\n disabled,\n layoutRenderer: LayoutRenderer,\n visibleFields,\n hiddenFields,\n formLayoutProps,\n fieldOptions,\n style,\n id,\n className,\n deleteButtonVisible,\n onDeleteSuccess,\n onDeleteError,\n}: AutoFormProps<M>): JSX.Element {\n const form = useForm(model as ProvisionalModelConstructor<M>, {\n onSubmit: async (formItem) => service.save(formItem),\n });\n const formErrorRef = useRef<HTMLDivElement>(null);\n const [formError, setFormError] = useState<JSX.Element | string>('');\n const [showDeleteDialog, setShowDeleteDialog] = useState(false);\n const modelInfo = useMemo(() => new ModelInfo(model, itemIdProperty), [model]);\n\n const isEditMode = item !== undefined && item !== null && item !== emptyItem;\n const showDeleteButton = deleteButtonVisible && isEditMode && modelInfo.idProperty;\n const isSubmitDisabled = !!disabled || (isEditMode && !form.dirty);\n\n useEffect(() => {\n if (item !== emptyItem) {\n form.read(item);\n } else {\n form.clear();\n }\n }, [item]);\n\n useEffect(() => {\n formErrorRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' });\n }, [formError]);\n\n function handleSubmitError(error: unknown) {\n if (error instanceof ValidationError) {\n const nonPropertyErrorMessages = error.errors\n .filter((validationError) => !validationError.property || typeof validationError.property === 'string')\n .map((validationError) => {\n const property =\n validationError.property && typeof validationError.property === 'string'\n ? `${validationError.property}: `\n : '';\n return `${property}${validationError.validatorMessage ?? validationError.message}`;\n });\n if (nonPropertyErrorMessages.length > 0) {\n setFormError(\n <div ref={formErrorRef}>\n Validation errors:\n <ul>\n {nonPropertyErrorMessages.map((message, index) => (\n <li key={index}>{message}</li>\n ))}\n </ul>\n </div>,\n );\n }\n } else if (error instanceof EndpointError) {\n if (onSubmitError) {\n onSubmitError({ error, setMessage: setFormError });\n } else {\n setFormError(error.message);\n }\n } else {\n throw error;\n }\n }\n\n async function handleSubmit(): Promise<void> {\n try {\n setFormError('');\n const newItem = await form.submit();\n if (newItem === undefined) {\n // If update returns an empty object, then no update was performed\n throw new EndpointError('No update performed');\n } else if (onSubmitSuccess) {\n onSubmitSuccess({ item: newItem });\n }\n // Automatically clear the form after submitting a new item.\n // Otherwise, there would be no way for the developer to clear it, as the\n // there is no new value to set for the item prop to trigger the above\n // effect in case the prop is already null, undefined or the empty item.\n if (!item || item === emptyItem) {\n form.clear();\n }\n } catch (error) {\n handleSubmitError(error);\n }\n }\n\n function deleteItem() {\n setShowDeleteDialog(true);\n }\n\n async function confirmDelete() {\n // At this point, item can not be null or emptyItem\n const deletedItem = item as Value<M>;\n try {\n const idProperty = modelInfo.idProperty!;\n // eslint-disable-next-line\n const id = (item as any)[idProperty.name];\n await service.delete(id);\n if (onDeleteSuccess) {\n onDeleteSuccess({ item: deletedItem });\n }\n } catch (error) {\n if (error instanceof EndpointError) {\n if (onDeleteError) {\n onDeleteError({ error, setMessage: setFormError });\n } else {\n setFormError(error.message);\n }\n } else {\n throw error;\n }\n } finally {\n setShowDeleteDialog(false);\n }\n }\n\n function cancelDelete() {\n setShowDeleteDialog(false);\n }\n\n const handleKeyDown = (event: KeyboardEvent): void => {\n if (event.target instanceof HTMLTextAreaElement) {\n return;\n }\n if (event.key === 'Enter' && !isSubmitDisabled) {\n // eslint-disable-next-line no-void\n void handleSubmit();\n }\n };\n\n function createAutoFormField(propertyInfo: PropertyInfo): JSX.Element {\n const fieldOptionsForProperty = fieldOptions?.[propertyInfo.name] ?? {};\n\n return (\n <AutoFormField\n key={propertyInfo.name}\n propertyInfo={propertyInfo}\n form={form}\n disabled={disabled}\n options={fieldOptionsForProperty}\n />\n );\n }\n\n let visibleProperties = visibleFields ? modelInfo.getProperties(visibleFields) : getDefaultProperties(modelInfo);\n\n // When using `hiddenFields`, remove fields to hide using their `name`\n if (hiddenFields) {\n visibleProperties = visibleProperties.filter(({ name }) => !hiddenFields.includes(name));\n }\n\n const fields = visibleProperties.map(createAutoFormField);\n\n const layout = LayoutRenderer ? (\n <LayoutRenderer form={form}>{fields}</LayoutRenderer>\n ) : (\n <FormLayout {...formLayoutProps}>{fields}</FormLayout>\n );\n\n return (\n <div className={`auto-form ${className ?? ''}`} id={id} style={style} data-testid=\"auto-form\">\n <VerticalLayout className=\"auto-form-fields\" onKeyDown={handleKeyDown}>\n {layout}\n {formError ? <div style={{ color: 'var(--lumo-error-color)' }}>{formError}</div> : <></>}\n </VerticalLayout>\n <div className=\"auto-form-toolbar\">\n <Button\n theme=\"primary\"\n disabled={isSubmitDisabled}\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n onClick={handleSubmit}\n >\n Submit\n </Button>\n {form.dirty ? (\n <Button theme=\"tertiary\" onClick={() => form.reset()}>\n Discard\n </Button>\n ) : null}\n {showDeleteButton && (\n <Button className=\"auto-form-delete-button\" theme=\"tertiary error\" onClick={deleteItem}>\n Delete...\n </Button>\n )}\n </div>\n {showDeleteDialog && (\n <ConfirmDialog\n opened\n header=\"Delete item\"\n confirmTheme=\"error\"\n cancelButtonVisible\n // eslint-disable-next-line\n onConfirm={confirmDelete}\n onCancel={cancelDelete}\n >\n Are you sure you want to delete the selected item?\n </ConfirmDialog>\n )}\n </div>\n );\n}\n"]}
|
package/autogrid.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AbstractModel, DetachedModelConstructor } from '@vaadin/hilla-lit-form';
|
|
2
|
+
import type { Model } from '@vaadin/hilla-models';
|
|
2
3
|
import { type GridElement, type GridProps } from '@vaadin/react-components/Grid.js';
|
|
3
4
|
import { type ComponentType, type ForwardedRef, type JSX } from 'react';
|
|
4
5
|
import { type ColumnOptions } from './autogrid-columns.js';
|
|
@@ -12,7 +13,7 @@ export interface AutoGridRef<TItem = any> {
|
|
|
12
13
|
}
|
|
13
14
|
interface AutoGridOwnProps<TItem> {
|
|
14
15
|
service: ListService<TItem>;
|
|
15
|
-
model: DetachedModelConstructor<AbstractModel<TItem
|
|
16
|
+
model: DetachedModelConstructor<AbstractModel<TItem>> | Model<TItem>;
|
|
16
17
|
itemIdProperty?: string;
|
|
17
18
|
experimentalFilter?: FilterUnion;
|
|
18
19
|
visibleColumns?: string[];
|
package/autogrid.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autogrid.js","sourceRoot":"","sources":["src/autogrid.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { AbstractModel, DetachedModelConstructor } from '@vaadin/hilla-lit-form';\nimport { Grid, type GridElement, type GridProps } from '@vaadin/react-components/Grid.js';\nimport { GridColumn } from '@vaadin/react-components/GridColumn.js';\nimport { GridColumnGroup } from '@vaadin/react-components/GridColumnGroup.js';\nimport {\n cloneElement,\n type ComponentType,\n type ForwardedRef,\n forwardRef,\n type JSX,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { ColumnContext, CustomColumnContext, type SortState } from './autogrid-column-context.js';\nimport { type ColumnOptions, getColumnOptions } from './autogrid-columns.js';\nimport { AutoGridFooterItemCountRenderer, AutoGridRowNumberRenderer, FooterContext } from './autogrid-renderers.js';\nimport css from './autogrid.obj.js';\nimport type { ListService } from './crud.js';\nimport { createDataProvider, type DataProvider, isCountService, type ItemCounts } from './data-provider.js';\nimport { type HeaderFilterRendererProps, NoHeaderFilter, HeaderFilterWrapper } from './header-filter';\nimport { HeaderSorter } from './header-sorter';\nimport { getDefaultProperties, ModelInfo, type PropertyInfo } from './model-info.js';\nimport type AndFilter from './types/com/vaadin/hilla/crud/filter/AndFilter.js';\nimport type FilterUnion from './types/com/vaadin/hilla/crud/filter/FilterUnion.js';\nimport { isFilterEmpty, registerStylesheet } from './util';\n\nregisterStylesheet(css);\n\nexport interface AutoGridRef<TItem = any> {\n /**\n * The underlying vaadin-grid DOM element.\n */\n grid: GridElement<TItem> | null;\n\n /**\n * Refreshes the grid by reloading the data from the backend.\n */\n refresh(): void;\n}\n\ninterface AutoGridOwnProps<TItem> {\n /**\n * The service to use for fetching the data. This must be a TypeScript service\n * that has been generated by Hilla from a backend Java service that\n * implements the `com.vaadin.hilla.crud.ListService` interface.\n */\n service: ListService<TItem>;\n /**\n * The entity model to use for the grid, which determines which columns to\n * show and how to render them. This must be a Typescript model class that has\n * been generated by Hilla from a backend Java class. The model must match\n * with the type of the items returned by the service. For example, a\n * `PersonModel` can be used with a service that returns `Person` instances.\n *\n * By default, the grid shows columns for all properties of the model which\n * have a type that is supported. Use the `visibleColumns` option to customize\n * which columns to show and in which order.\n */\n model: DetachedModelConstructor<AbstractModel<TItem>>;\n /**\n * The property to use to detect an item's ID. The item ID is used to keep\n * the selection state when reloading the grid.\n *\n * By default, the component uses the property annotated with\n * `jakarta.persistence.Id`, or a property named `id`, in that order.\n * This option can be used to override the default behavior, or define the ID\n * property in case a class doesn't have a property matching the defaults.\n */\n itemIdProperty?: string;\n /**\n * Allows to provide a filter that is applied when fetching data from the\n * service. This can be used for implementing an external filter UI outside\n * the grid. A custom filter is not compatible with header filters.\n *\n * **NOTE:** This is considered an experimental feature and the API may change\n * in the future.\n */\n experimentalFilter?: FilterUnion;\n /**\n * Allows to customize which columns to show and in which order. This must be\n * an array of property names that are defined in the model. Nested properties\n * can be specified using dot notation, e.g. `address.street`.\n */\n visibleColumns?: string[];\n /**\n * Allows to customize which columns to hide. This must be an array of property\n * names that are defined in the model. Nested properties can be specified using\n * dot notation, e.g. `address.street`.\n */\n hiddenColumns?: string[];\n /**\n * Disables header filters, which are otherwise enabled by default.\n */\n noHeaderFilters?: boolean;\n /**\n * Allows to add custom columns to the grid. This must be an array of\n * `GridColumn` component instances. Custom columns are added after the\n * auto-generated columns.\n */\n customColumns?: JSX.Element[];\n /**\n * Allows to customize the props for individual columns. This is an object\n * where the keys must be property names that are defined in the model, and\n * the values are props that are accepted by the `GridColumn` component.\n * Nested properties can be specified using dot notation, e.g.\n * `address.street`.\n */\n columnOptions?: Record<string, ColumnOptions>;\n /**\n * When enabled, inserts a column with row numbers at the beginning of the\n * grid.\n */\n rowNumbers?: boolean;\n /**\n * When enabled, shows the total count of items in the grid footer.\n * This requires the provided service to implement the CountService interface,\n * otherwise an error will be logged to the console, without any count being\n * rendered.\n */\n totalCount?: boolean;\n /**\n * When enabled, shows the filtered item count in the grid footer.\n * if totalCount is also enabled, it will show both totalCount and filteredCount.\n * This requires the provided service to implement the CountService interface,\n * otherwise an error will be logged to the console, without any count being\n * rendered.\n */\n filteredCount?: boolean;\n /**\n * Allows to customize the grid footer with a custom renderer component for\n * the total count and filtered item count.\n * This requires the provided service to implement the CountService interface,\n * See {@link AutoGrid#totalCount} and {@link AutoGrid#filteredCount}.\n */\n footerCountRenderer?: ComponentType<ItemCounts>;\n /**\n * Allows to set a text or component to be displayed when the underlying grid is empty.\n */\n emptyState?: string | JSX.Element;\n}\n\nexport type AutoGridProps<TItem> = GridProps<TItem> & Readonly<AutoGridOwnProps<TItem>>;\n\ninterface ColumnConfigurationOptions {\n visibleColumns?: string[];\n hiddenColumns?: string[];\n noHeaderFilters?: boolean;\n customColumns?: JSX.Element[];\n columnOptions?: Record<string, ColumnOptions>;\n rowNumbers?: boolean;\n totalCount?: boolean;\n filteredCount?: boolean;\n footerCountRenderer?: ComponentType<ItemCounts>;\n itemCounts?: ItemCounts;\n}\n\nfunction wrapCustomColumn(\n column: JSX.Element,\n setColumnFilter: (filter: FilterUnion, filterKey: string) => void,\n options: ColumnConfigurationOptions,\n) {\n const key = column.key ?? 'no-key';\n const { header, headerRenderer } = column.props;\n const customOptions = options.columnOptions?.[key];\n const { header: customHeader, headerRenderer: customHeaderRenderer, headerFilterRenderer } = customOptions ?? {};\n const columnWithoutHeader = cloneElement(column, {\n header: null,\n headerRenderer: HeaderFilterWrapper,\n });\n return (\n <CustomColumnContext.Provider\n key={key}\n value={{\n setColumnFilter,\n headerFilterRenderer: headerFilterRenderer ?? NoHeaderFilter,\n filterKey: key,\n }}\n >\n <GridColumnGroup\n key={key}\n header={customHeader ?? header}\n headerRenderer={customHeaderRenderer ?? headerRenderer}\n >\n {columnWithoutHeader}\n </GridColumnGroup>\n </CustomColumnContext.Provider>\n );\n}\n\nfunction addCustomColumns(\n columns: JSX.Element[],\n options: ColumnConfigurationOptions,\n setColumnFilter: (filter: FilterUnion, filterKey: string) => void,\n): JSX.Element[] {\n if (!options.customColumns) {\n return columns;\n }\n\n // When using header filters, wrap custom columns into column groups and\n // move header text or renderer to group\n const customColumns = options.noHeaderFilters\n ? options.customColumns\n : options.customColumns.map((column) => wrapCustomColumn(column, setColumnFilter, options));\n\n // When using a custom column order, insert custom columns into auto-generated\n // ones using their `key`\n if (options.visibleColumns) {\n const columnMap = [...columns, ...customColumns].reduce((map, column) => {\n const { key } = column;\n if (key) {\n map.set(key, column);\n }\n return map;\n }, new Map<string, JSX.Element>());\n\n return options.visibleColumns.map((path) => columnMap.get(path)).filter(Boolean) as JSX.Element[];\n }\n\n // Otherwise just append custom columns at the end\n return [...columns, ...customColumns];\n}\n\nfunction useColumns(\n properties: PropertyInfo[],\n setColumnFilter: (filter: FilterUnion, filterKey: string) => void,\n options: ColumnConfigurationOptions,\n) {\n const sortableProperties = properties.filter(\n (propertyInfo) => options.columnOptions?.[propertyInfo.name]?.sortable !== false,\n );\n const [sortState, setSortState] = useState<SortState>(\n sortableProperties.length > 0 ? { [sortableProperties[0].name]: { direction: 'asc' } } : {},\n );\n let columns = properties.map((propertyInfo) => {\n let column;\n const customColumnOptions = options.columnOptions?.[propertyInfo.name];\n\n const { headerFilterRenderer, ...columnProps } = getColumnOptions(propertyInfo, customColumnOptions);\n\n if (!options.noHeaderFilters) {\n column = (\n <GridColumnGroup headerRenderer={HeaderSorter}>\n <GridColumn path={propertyInfo.name} headerRenderer={HeaderFilterWrapper} {...columnProps}></GridColumn>\n </GridColumnGroup>\n );\n } else {\n column = <GridColumn path={propertyInfo.name} headerRenderer={HeaderSorter} {...columnProps}></GridColumn>;\n }\n return (\n <ColumnContext.Provider\n key={propertyInfo.name}\n value={{\n propertyInfo,\n setColumnFilter,\n sortState,\n setSortState,\n customColumnOptions,\n headerFilterRenderer: headerFilterRenderer ?? NoHeaderFilter,\n filterKey: propertyInfo.name,\n }}\n >\n {column}\n </ColumnContext.Provider>\n );\n });\n\n columns = addCustomColumns(columns, options, setColumnFilter);\n\n // When using `hiddenColumns` option, remove columns to hide using their `key`\n if (options.hiddenColumns) {\n columns = columns.filter(({ key }) => !(key && options.hiddenColumns?.includes(key)));\n }\n\n if (options.rowNumbers) {\n columns = [\n <GridColumn key=\"rownumbers\" width=\"4em\" flexGrow={0} renderer={AutoGridRowNumberRenderer}></GridColumn>,\n ...columns,\n ];\n }\n const { totalCount, filteredCount, itemCounts, footerCountRenderer } = options;\n if (totalCount ?? filteredCount) {\n const col = (\n <FooterContext.Provider\n key=\"grid-footer\"\n value={{\n totalCount,\n filteredCount,\n footerCountRenderer,\n itemCounts,\n }}\n >\n <GridColumnGroup footerRenderer={AutoGridFooterItemCountRenderer}>{columns}</GridColumnGroup>\n </FooterContext.Provider>\n );\n columns = [col];\n }\n\n return columns;\n}\n\nfunction AutoGridInner<TItem>(\n {\n service,\n model,\n itemIdProperty,\n experimentalFilter,\n visibleColumns,\n hiddenColumns,\n noHeaderFilters,\n customColumns,\n columnOptions,\n rowNumbers,\n totalCount,\n filteredCount,\n footerCountRenderer,\n emptyState,\n ...gridProps\n }: AutoGridProps<TItem>,\n ref: ForwardedRef<AutoGridRef<TItem>>,\n): JSX.Element {\n const [internalFilter, setInternalFilter] = useState<AndFilter>({ '@type': 'and', children: [] });\n const [itemCounts, setItemCounts] = useState<ItemCounts | undefined>();\n const gridRef = useRef<GridElement<TItem>>(null);\n const dataProviderRef = useRef<DataProvider<TItem>>(undefined);\n\n useImperativeHandle(\n ref,\n () => ({\n get grid() {\n return gridRef.current;\n },\n refresh() {\n dataProviderRef.current?.reset();\n gridRef.current?.clearCache();\n },\n }),\n [],\n );\n\n const setHeaderFilter = (filter: FilterUnion, filterKey: string) => {\n let changed = false;\n filter.key = filterKey;\n const indexOfFilter = filterKey\n ? internalFilter.children.findIndex((f) => (f as FilterUnion).key === filterKey)\n : -1;\n const isEmptyFilter = isFilterEmpty(filter);\n\n if (indexOfFilter >= 0 && isEmptyFilter) {\n internalFilter.children.splice(indexOfFilter, 1);\n changed = true;\n } else if (!isEmptyFilter) {\n if (indexOfFilter >= 0) {\n internalFilter.children[indexOfFilter] = filter;\n changed = true;\n } else {\n internalFilter.children.push(filter);\n changed = true;\n }\n }\n if (changed) {\n setInternalFilter({ ...internalFilter });\n }\n };\n\n const modelInfo = useMemo(() => new ModelInfo(model, itemIdProperty), [model]);\n const properties = visibleColumns ? modelInfo.getProperties(visibleColumns) : getDefaultProperties(modelInfo);\n const children = useColumns(properties, setHeaderFilter, {\n visibleColumns,\n hiddenColumns,\n noHeaderFilters,\n customColumns,\n columnOptions,\n rowNumbers,\n totalCount,\n filteredCount,\n footerCountRenderer,\n itemCounts,\n });\n\n useEffect(() => {\n // Remove all filtering if header filters are removed\n if (noHeaderFilters) {\n setInternalFilter({ '@type': 'and', children: [] });\n }\n }, [noHeaderFilters]);\n\n useEffect(() => {\n // Log an error if totalCount or filteredCount is enabled but the service doesn't implement CountService\n if ((!isCountService(service) && totalCount) ?? filteredCount) {\n console.error(\n '\"totalCount\" and \"filteredCount\" props require the provided service to implement the CountService interface.',\n );\n }\n // Sets the data provider, should be done only once\n const grid = gridRef.current!;\n // Wait for the sorting headers to be rendered so that the sorting state is correct for the first data provider call\n const timeoutId = setTimeout(() => {\n let firstUpdate = true;\n const dataProvider = createDataProvider(service, {\n initialFilter: experimentalFilter ?? internalFilter,\n loadTotalCount: totalCount,\n afterLoad(newItemCounts: ItemCounts) {\n setItemCounts(newItemCounts);\n\n if (firstUpdate) {\n // Workaround for https://github.com/vaadin/react-components/issues/129\n firstUpdate = false;\n setTimeout(() => grid.recalculateColumnWidths(), 0);\n }\n },\n });\n dataProviderRef.current = dataProvider;\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n gridRef.current!.dataProvider = dataProvider.load.bind(dataProvider);\n }, 1);\n\n return () => clearTimeout(timeoutId);\n }, [model, service]);\n\n useEffect(() => {\n // Update the filtering, whenever the filter changes\n const dataProvider = dataProviderRef.current;\n const grid = gridRef.current;\n if (grid && dataProvider) {\n dataProvider.setFilter(experimentalFilter ?? internalFilter);\n grid.clearCache();\n }\n }, [experimentalFilter, internalFilter]);\n\n return (\n <Grid itemIdPath={modelInfo.idProperty?.name} {...gridProps} ref={gridRef}>\n {children}\n\n {emptyState && <span slot=\"empty-state\">{emptyState}</span>}\n </Grid>\n );\n}\n\ntype AutoGrid = <TItem>(\n props: AutoGridProps<TItem> & { ref?: ForwardedRef<AutoGridRef<TItem>> },\n) => ReturnType<typeof AutoGridInner>;\n\n/**\n * Auto Grid is a component for displaying tabular data based on a Java backend\n * service. It automatically generates columns based on the properties of a\n * Java class and provides features such as lazy-loading, sorting and filtering.\n *\n * Example usage:\n * ```tsx\n * import { AutoGrid } from '@vaadin/hilla-react-crud';\n * import PersonService from 'Frontend/generated/endpoints';\n * import PersonModel from 'Frontend/generated/com/example/application/Person';\n *\n * <AutoGrid service={PersonService} model={PersonModel} />\n * ```\n */\nexport const AutoGrid: AutoGrid = forwardRef(AutoGridInner) as AutoGrid;\n\nexport type { ColumnOptions, HeaderFilterRendererProps };\n"]}
|
|
1
|
+
{"version":3,"file":"autogrid.js","sourceRoot":"","sources":["src/autogrid.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { AbstractModel, DetachedModelConstructor } from '@vaadin/hilla-lit-form';\nimport type { Model } from '@vaadin/hilla-models';\nimport { Grid, type GridElement, type GridProps } from '@vaadin/react-components/Grid.js';\nimport { GridColumn } from '@vaadin/react-components/GridColumn.js';\nimport { GridColumnGroup } from '@vaadin/react-components/GridColumnGroup.js';\nimport {\n cloneElement,\n type ComponentType,\n type ForwardedRef,\n forwardRef,\n type JSX,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { ColumnContext, CustomColumnContext, type SortState } from './autogrid-column-context.js';\nimport { type ColumnOptions, getColumnOptions } from './autogrid-columns.js';\nimport { AutoGridFooterItemCountRenderer, AutoGridRowNumberRenderer, FooterContext } from './autogrid-renderers.js';\nimport css from './autogrid.obj.js';\nimport type { ListService } from './crud.js';\nimport { createDataProvider, type DataProvider, isCountService, type ItemCounts } from './data-provider.js';\nimport { type HeaderFilterRendererProps, NoHeaderFilter, HeaderFilterWrapper } from './header-filter';\nimport { HeaderSorter } from './header-sorter';\nimport { getDefaultProperties, ModelInfo, type PropertyInfo } from './model-info.js';\nimport type AndFilter from './types/com/vaadin/hilla/crud/filter/AndFilter.js';\nimport type FilterUnion from './types/com/vaadin/hilla/crud/filter/FilterUnion.js';\nimport { isFilterEmpty, registerStylesheet } from './util';\n\nregisterStylesheet(css);\n\nexport interface AutoGridRef<TItem = any> {\n /**\n * The underlying vaadin-grid DOM element.\n */\n grid: GridElement<TItem> | null;\n\n /**\n * Refreshes the grid by reloading the data from the backend.\n */\n refresh(): void;\n}\n\ninterface AutoGridOwnProps<TItem> {\n /**\n * The service to use for fetching the data. This must be a TypeScript service\n * that has been generated by Hilla from a backend Java service that\n * implements the `com.vaadin.hilla.crud.ListService` interface.\n */\n service: ListService<TItem>;\n /**\n * The entity model to use for the grid, which determines which columns to\n * show and how to render them. This must be a Typescript model class that has\n * been generated by Hilla from a backend Java class. The model must match\n * with the type of the items returned by the service. For example, a\n * `PersonModel` can be used with a service that returns `Person` instances.\n *\n * By default, the grid shows columns for all properties of the model which\n * have a type that is supported. Use the `visibleColumns` option to customize\n * which columns to show and in which order.\n */\n model: DetachedModelConstructor<AbstractModel<TItem>> | Model<TItem>;\n /**\n * The property to use to detect an item's ID. The item ID is used to keep\n * the selection state when reloading the grid.\n *\n * By default, the component uses the property annotated with\n * `jakarta.persistence.Id`, or a property named `id`, in that order.\n * This option can be used to override the default behavior, or define the ID\n * property in case a class doesn't have a property matching the defaults.\n */\n itemIdProperty?: string;\n /**\n * Allows to provide a filter that is applied when fetching data from the\n * service. This can be used for implementing an external filter UI outside\n * the grid. A custom filter is not compatible with header filters.\n *\n * **NOTE:** This is considered an experimental feature and the API may change\n * in the future.\n */\n experimentalFilter?: FilterUnion;\n /**\n * Allows to customize which columns to show and in which order. This must be\n * an array of property names that are defined in the model. Nested properties\n * can be specified using dot notation, e.g. `address.street`.\n */\n visibleColumns?: string[];\n /**\n * Allows to customize which columns to hide. This must be an array of property\n * names that are defined in the model. Nested properties can be specified using\n * dot notation, e.g. `address.street`.\n */\n hiddenColumns?: string[];\n /**\n * Disables header filters, which are otherwise enabled by default.\n */\n noHeaderFilters?: boolean;\n /**\n * Allows to add custom columns to the grid. This must be an array of\n * `GridColumn` component instances. Custom columns are added after the\n * auto-generated columns.\n */\n customColumns?: JSX.Element[];\n /**\n * Allows to customize the props for individual columns. This is an object\n * where the keys must be property names that are defined in the model, and\n * the values are props that are accepted by the `GridColumn` component.\n * Nested properties can be specified using dot notation, e.g.\n * `address.street`.\n */\n columnOptions?: Record<string, ColumnOptions>;\n /**\n * When enabled, inserts a column with row numbers at the beginning of the\n * grid.\n */\n rowNumbers?: boolean;\n /**\n * When enabled, shows the total count of items in the grid footer.\n * This requires the provided service to implement the CountService interface,\n * otherwise an error will be logged to the console, without any count being\n * rendered.\n */\n totalCount?: boolean;\n /**\n * When enabled, shows the filtered item count in the grid footer.\n * if totalCount is also enabled, it will show both totalCount and filteredCount.\n * This requires the provided service to implement the CountService interface,\n * otherwise an error will be logged to the console, without any count being\n * rendered.\n */\n filteredCount?: boolean;\n /**\n * Allows to customize the grid footer with a custom renderer component for\n * the total count and filtered item count.\n * This requires the provided service to implement the CountService interface,\n * See {@link AutoGrid#totalCount} and {@link AutoGrid#filteredCount}.\n */\n footerCountRenderer?: ComponentType<ItemCounts>;\n /**\n * Allows to set a text or component to be displayed when the underlying grid is empty.\n */\n emptyState?: string | JSX.Element;\n}\n\nexport type AutoGridProps<TItem> = GridProps<TItem> & Readonly<AutoGridOwnProps<TItem>>;\n\ninterface ColumnConfigurationOptions {\n visibleColumns?: string[];\n hiddenColumns?: string[];\n noHeaderFilters?: boolean;\n customColumns?: JSX.Element[];\n columnOptions?: Record<string, ColumnOptions>;\n rowNumbers?: boolean;\n totalCount?: boolean;\n filteredCount?: boolean;\n footerCountRenderer?: ComponentType<ItemCounts>;\n itemCounts?: ItemCounts;\n}\n\nfunction wrapCustomColumn(\n column: JSX.Element,\n setColumnFilter: (filter: FilterUnion, filterKey: string) => void,\n options: ColumnConfigurationOptions,\n) {\n const key = column.key ?? 'no-key';\n const { header, headerRenderer } = column.props;\n const customOptions = options.columnOptions?.[key];\n const { header: customHeader, headerRenderer: customHeaderRenderer, headerFilterRenderer } = customOptions ?? {};\n const columnWithoutHeader = cloneElement(column, {\n header: null,\n headerRenderer: HeaderFilterWrapper,\n });\n return (\n <CustomColumnContext.Provider\n key={key}\n value={{\n setColumnFilter,\n headerFilterRenderer: headerFilterRenderer ?? NoHeaderFilter,\n filterKey: key,\n }}\n >\n <GridColumnGroup\n key={key}\n header={customHeader ?? header}\n headerRenderer={customHeaderRenderer ?? headerRenderer}\n >\n {columnWithoutHeader}\n </GridColumnGroup>\n </CustomColumnContext.Provider>\n );\n}\n\nfunction addCustomColumns(\n columns: JSX.Element[],\n options: ColumnConfigurationOptions,\n setColumnFilter: (filter: FilterUnion, filterKey: string) => void,\n): JSX.Element[] {\n if (!options.customColumns) {\n return columns;\n }\n\n // When using header filters, wrap custom columns into column groups and\n // move header text or renderer to group\n const customColumns = options.noHeaderFilters\n ? options.customColumns\n : options.customColumns.map((column) => wrapCustomColumn(column, setColumnFilter, options));\n\n // When using a custom column order, insert custom columns into auto-generated\n // ones using their `key`\n if (options.visibleColumns) {\n const columnMap = [...columns, ...customColumns].reduce((map, column) => {\n const { key } = column;\n if (key) {\n map.set(key, column);\n }\n return map;\n }, new Map<string, JSX.Element>());\n\n return options.visibleColumns.map((path) => columnMap.get(path)).filter(Boolean) as JSX.Element[];\n }\n\n // Otherwise just append custom columns at the end\n return [...columns, ...customColumns];\n}\n\nfunction useColumns(\n properties: PropertyInfo[],\n setColumnFilter: (filter: FilterUnion, filterKey: string) => void,\n options: ColumnConfigurationOptions,\n) {\n const sortableProperties = properties.filter(\n (propertyInfo) => options.columnOptions?.[propertyInfo.name]?.sortable !== false,\n );\n const [sortState, setSortState] = useState<SortState>(\n sortableProperties.length > 0 ? { [sortableProperties[0].name]: { direction: 'asc' } } : {},\n );\n let columns = properties.map((propertyInfo) => {\n let column;\n const customColumnOptions = options.columnOptions?.[propertyInfo.name];\n\n const { headerFilterRenderer, ...columnProps } = getColumnOptions(propertyInfo, customColumnOptions);\n\n if (!options.noHeaderFilters) {\n column = (\n <GridColumnGroup headerRenderer={HeaderSorter}>\n <GridColumn path={propertyInfo.name} headerRenderer={HeaderFilterWrapper} {...columnProps}></GridColumn>\n </GridColumnGroup>\n );\n } else {\n column = <GridColumn path={propertyInfo.name} headerRenderer={HeaderSorter} {...columnProps}></GridColumn>;\n }\n return (\n <ColumnContext.Provider\n key={propertyInfo.name}\n value={{\n propertyInfo,\n setColumnFilter,\n sortState,\n setSortState,\n customColumnOptions,\n headerFilterRenderer: headerFilterRenderer ?? NoHeaderFilter,\n filterKey: propertyInfo.name,\n }}\n >\n {column}\n </ColumnContext.Provider>\n );\n });\n\n columns = addCustomColumns(columns, options, setColumnFilter);\n\n // When using `hiddenColumns` option, remove columns to hide using their `key`\n if (options.hiddenColumns) {\n columns = columns.filter(({ key }) => !(key && options.hiddenColumns?.includes(key)));\n }\n\n if (options.rowNumbers) {\n columns = [\n <GridColumn key=\"rownumbers\" width=\"4em\" flexGrow={0} renderer={AutoGridRowNumberRenderer}></GridColumn>,\n ...columns,\n ];\n }\n const { totalCount, filteredCount, itemCounts, footerCountRenderer } = options;\n if (totalCount ?? filteredCount) {\n const col = (\n <FooterContext.Provider\n key=\"grid-footer\"\n value={{\n totalCount,\n filteredCount,\n footerCountRenderer,\n itemCounts,\n }}\n >\n <GridColumnGroup footerRenderer={AutoGridFooterItemCountRenderer}>{columns}</GridColumnGroup>\n </FooterContext.Provider>\n );\n columns = [col];\n }\n\n return columns;\n}\n\nfunction AutoGridInner<TItem>(\n {\n service,\n model,\n itemIdProperty,\n experimentalFilter,\n visibleColumns,\n hiddenColumns,\n noHeaderFilters,\n customColumns,\n columnOptions,\n rowNumbers,\n totalCount,\n filteredCount,\n footerCountRenderer,\n emptyState,\n ...gridProps\n }: AutoGridProps<TItem>,\n ref: ForwardedRef<AutoGridRef<TItem>>,\n): JSX.Element {\n const [internalFilter, setInternalFilter] = useState<AndFilter>({ '@type': 'and', children: [] });\n const [itemCounts, setItemCounts] = useState<ItemCounts | undefined>();\n const gridRef = useRef<GridElement<TItem>>(null);\n const dataProviderRef = useRef<DataProvider<TItem>>(undefined);\n\n useImperativeHandle(\n ref,\n () => ({\n get grid() {\n return gridRef.current;\n },\n refresh() {\n dataProviderRef.current?.reset();\n gridRef.current?.clearCache();\n },\n }),\n [],\n );\n\n const setHeaderFilter = (filter: FilterUnion, filterKey: string) => {\n let changed = false;\n filter.key = filterKey;\n const indexOfFilter = filterKey\n ? internalFilter.children.findIndex((f) => (f as FilterUnion).key === filterKey)\n : -1;\n const isEmptyFilter = isFilterEmpty(filter);\n\n if (indexOfFilter >= 0 && isEmptyFilter) {\n internalFilter.children.splice(indexOfFilter, 1);\n changed = true;\n } else if (!isEmptyFilter) {\n if (indexOfFilter >= 0) {\n internalFilter.children[indexOfFilter] = filter;\n changed = true;\n } else {\n internalFilter.children.push(filter);\n changed = true;\n }\n }\n if (changed) {\n setInternalFilter({ ...internalFilter });\n }\n };\n\n const modelInfo = useMemo(() => new ModelInfo(model, itemIdProperty), [model]);\n const properties = visibleColumns ? modelInfo.getProperties(visibleColumns) : getDefaultProperties(modelInfo);\n const children = useColumns(properties, setHeaderFilter, {\n visibleColumns,\n hiddenColumns,\n noHeaderFilters,\n customColumns,\n columnOptions,\n rowNumbers,\n totalCount,\n filteredCount,\n footerCountRenderer,\n itemCounts,\n });\n\n useEffect(() => {\n // Remove all filtering if header filters are removed\n if (noHeaderFilters) {\n setInternalFilter({ '@type': 'and', children: [] });\n }\n }, [noHeaderFilters]);\n\n useEffect(() => {\n // Log an error if totalCount or filteredCount is enabled but the service doesn't implement CountService\n if ((!isCountService(service) && totalCount) ?? filteredCount) {\n console.error(\n '\"totalCount\" and \"filteredCount\" props require the provided service to implement the CountService interface.',\n );\n }\n // Sets the data provider, should be done only once\n const grid = gridRef.current!;\n // Wait for the sorting headers to be rendered so that the sorting state is correct for the first data provider call\n const timeoutId = setTimeout(() => {\n let firstUpdate = true;\n const dataProvider = createDataProvider(service, {\n initialFilter: experimentalFilter ?? internalFilter,\n loadTotalCount: totalCount,\n afterLoad(newItemCounts: ItemCounts) {\n setItemCounts(newItemCounts);\n\n if (firstUpdate) {\n // Workaround for https://github.com/vaadin/react-components/issues/129\n firstUpdate = false;\n setTimeout(() => grid.recalculateColumnWidths(), 0);\n }\n },\n });\n dataProviderRef.current = dataProvider;\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n gridRef.current!.dataProvider = dataProvider.load.bind(dataProvider);\n }, 1);\n\n return () => clearTimeout(timeoutId);\n }, [model, service]);\n\n useEffect(() => {\n // Update the filtering, whenever the filter changes\n const dataProvider = dataProviderRef.current;\n const grid = gridRef.current;\n if (grid && dataProvider) {\n dataProvider.setFilter(experimentalFilter ?? internalFilter);\n grid.clearCache();\n }\n }, [experimentalFilter, internalFilter]);\n\n return (\n <Grid itemIdPath={modelInfo.idProperty?.name} {...gridProps} ref={gridRef}>\n {children}\n\n {emptyState && <span slot=\"empty-state\">{emptyState}</span>}\n </Grid>\n );\n}\n\ntype AutoGrid = <TItem>(\n props: AutoGridProps<TItem> & { ref?: ForwardedRef<AutoGridRef<TItem>> },\n) => ReturnType<typeof AutoGridInner>;\n\n/**\n * Auto Grid is a component for displaying tabular data based on a Java backend\n * service. It automatically generates columns based on the properties of a\n * Java class and provides features such as lazy-loading, sorting and filtering.\n *\n * Example usage:\n * ```tsx\n * import { AutoGrid } from '@vaadin/hilla-react-crud';\n * import PersonService from 'Frontend/generated/endpoints';\n * import PersonModel from 'Frontend/generated/com/example/application/Person';\n *\n * <AutoGrid service={PersonService} model={PersonModel} />\n * ```\n */\nexport const AutoGrid: AutoGrid = forwardRef(AutoGridInner) as AutoGrid;\n\nexport type { ColumnOptions, HeaderFilterRendererProps };\n"]}
|
package/index.js
CHANGED
|
@@ -6,7 +6,7 @@ export { useDataProvider, useGridDataProvider, useComboBoxDataProvider } from '.
|
|
|
6
6
|
vaadinObj.registrations ??= [];
|
|
7
7
|
vaadinObj.registrations.push({
|
|
8
8
|
is: feature ? `@vaadin/hilla-react-crud/${feature}` : '@vaadin/hilla-react-crud',
|
|
9
|
-
version: '25.3.0-
|
|
9
|
+
version: '25.3.0-alpha4',
|
|
10
10
|
});
|
|
11
11
|
})();
|
|
12
12
|
//# sourceMappingURL=index.js.map
|
package/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AAEtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAInG,CAAC,CAAC,OAAO,EAAE,SAAS,GAAG,CAAC,UAAU,CAAC,MAAM,KAAK,EAAE,CAAC,EAAE,EAAE;IACnD,SAAS,CAAC,aAAa,KAAK,EAAE,CAAC;IAC/B,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC;QAC3B,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC,4BAA4B,OAAO,EAAE,CAAC,CAAC,CAAC,0BAA0B;QAChF,OAAO,EAAE,eAAe;KACzB,CAAC,CAAC;AACL,CAAC,CAAC,EAAE,CAAC","sourcesContent":["export * from './autogrid-feature.js';\nexport type * from './crud.js';\nexport * from './autoform-feature.js';\nexport * from './autocrud-feature.js';\nexport { useDataProvider, useGridDataProvider, useComboBoxDataProvider } from './data-provider.js';\n\n// @ts-expect-error: esbuild injection\n// eslint-disable-next-line @typescript-eslint/no-unsafe-call\n((feature, vaadinObj = (globalThis.Vaadin ??= {})) => {\n vaadinObj.registrations ??= [];\n vaadinObj.registrations.push({\n is: feature ? `@vaadin/hilla-react-crud/${feature}` : '@vaadin/hilla-react-crud',\n version: '25.3.0-
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AAEtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAInG,CAAC,CAAC,OAAO,EAAE,SAAS,GAAG,CAAC,UAAU,CAAC,MAAM,KAAK,EAAE,CAAC,EAAE,EAAE;IACnD,SAAS,CAAC,aAAa,KAAK,EAAE,CAAC;IAC/B,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC;QAC3B,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC,4BAA4B,OAAO,EAAE,CAAC,CAAC,CAAC,0BAA0B;QAChF,OAAO,EAAE,eAAe;KACzB,CAAC,CAAC;AACL,CAAC,CAAC,EAAE,CAAC","sourcesContent":["export * from './autogrid-feature.js';\nexport type * from './crud.js';\nexport * from './autoform-feature.js';\nexport * from './autocrud-feature.js';\nexport { useDataProvider, useGridDataProvider, useComboBoxDataProvider } from './data-provider.js';\n\n// @ts-expect-error: esbuild injection\n// eslint-disable-next-line @typescript-eslint/no-unsafe-call\n((feature, vaadinObj = (globalThis.Vaadin ??= {})) => {\n vaadinObj.registrations ??= [];\n vaadinObj.registrations.push({\n is: feature ? `@vaadin/hilla-react-crud/${feature}` : '@vaadin/hilla-react-crud',\n version: '25.3.0-alpha4',\n });\n})();\n"]}
|
package/model-info.d.ts
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AbstractModel, type DetachedModelConstructor, type ProvisionalModel } from '@vaadin/hilla-lit-form';
|
|
2
|
+
import { Model, type ModelMetadata } from '@vaadin/hilla-models';
|
|
2
3
|
export type PropertyType = 'boolean' | 'date' | 'datetime' | 'decimal' | 'enum' | 'integer' | 'object' | 'string' | 'time' | undefined;
|
|
3
4
|
export interface PropertyInfo {
|
|
4
5
|
name: string;
|
|
5
6
|
humanReadableName: string;
|
|
6
7
|
type: PropertyType;
|
|
7
8
|
meta: ModelMetadata;
|
|
8
|
-
model:
|
|
9
|
+
model: ProvisionalModel;
|
|
9
10
|
}
|
|
10
11
|
export declare function hasAnnotation(meta: ModelMetadata, annotationName: string): boolean;
|
|
11
12
|
export declare function _generateHeader(path: string): string;
|
|
12
13
|
export declare class ModelInfo {
|
|
13
14
|
private readonly modelInstance;
|
|
14
15
|
readonly idProperty?: PropertyInfo;
|
|
15
|
-
constructor(model: DetachedModelConstructor<AbstractModel
|
|
16
|
+
constructor(model: DetachedModelConstructor<AbstractModel> | Model, idPropertyName?: string);
|
|
16
17
|
private static resolveIdProperty;
|
|
17
18
|
private static resolvePropertyModel;
|
|
18
19
|
getRootProperties(path?: string): PropertyInfo[];
|
package/model-info.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { BooleanModel, NumberModel, StringModel, _meta, createDetachedModel, EnumModel, ObjectModel, } from '@vaadin/hilla-lit-form';
|
|
1
|
+
import { AbstractModel, BooleanModel as BinderBooleanModel, NumberModel as BinderNumberModel, StringModel as BinderStringModel, _meta, createDetachedModel, EnumModel as BinderEnumModel, ObjectModel as BinderObjectModel, } from '@vaadin/hilla-lit-form';
|
|
2
|
+
import { BooleanModel, EnumModel, Model, NumberModel, ObjectModel, StringModel, } from '@vaadin/hilla-models';
|
|
2
3
|
const javaTypeMap = {
|
|
3
4
|
byte: 'integer',
|
|
4
5
|
'java.lang.Byte': 'integer',
|
|
@@ -22,30 +23,30 @@ const javaTypeMap = {
|
|
|
22
23
|
'java.sql.Date': 'datetime',
|
|
23
24
|
};
|
|
24
25
|
function determinePropertyType(model) {
|
|
25
|
-
const
|
|
26
|
+
const javaType = model instanceof AbstractModel ? model[_meta].javaType : model[_meta]?.jvmType;
|
|
26
27
|
const propertyType = javaType ? javaTypeMap[javaType] : undefined;
|
|
27
28
|
if (propertyType) {
|
|
28
29
|
return propertyType;
|
|
29
30
|
}
|
|
30
|
-
if (model instanceof StringModel) {
|
|
31
|
+
if (model instanceof BinderStringModel || model instanceof StringModel) {
|
|
31
32
|
return 'string';
|
|
32
33
|
}
|
|
33
|
-
else if (model instanceof NumberModel) {
|
|
34
|
+
else if (model instanceof BinderNumberModel || model instanceof NumberModel) {
|
|
34
35
|
return 'decimal';
|
|
35
36
|
}
|
|
36
|
-
else if (model instanceof BooleanModel) {
|
|
37
|
+
else if (model instanceof BinderBooleanModel || model instanceof BooleanModel) {
|
|
37
38
|
return 'boolean';
|
|
38
39
|
}
|
|
39
|
-
else if (model instanceof EnumModel) {
|
|
40
|
+
else if (model instanceof BinderEnumModel || model instanceof EnumModel) {
|
|
40
41
|
return 'enum';
|
|
41
42
|
}
|
|
42
|
-
else if (model instanceof ObjectModel) {
|
|
43
|
+
else if (model instanceof BinderObjectModel || model instanceof ObjectModel) {
|
|
43
44
|
return 'object';
|
|
44
45
|
}
|
|
45
46
|
return undefined;
|
|
46
47
|
}
|
|
47
48
|
export function hasAnnotation(meta, annotationName) {
|
|
48
|
-
return meta.annotations?.some((annotation) => annotation.
|
|
49
|
+
return meta.annotations?.some((annotation) => annotation.jvmType === annotationName) ?? false;
|
|
49
50
|
}
|
|
50
51
|
export function _generateHeader(path) {
|
|
51
52
|
return path
|
|
@@ -57,8 +58,17 @@ export function _generateHeader(path) {
|
|
|
57
58
|
}
|
|
58
59
|
const getPropertyNames = (model) => {
|
|
59
60
|
const propertyNames = [];
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
if (model instanceof AbstractModel) {
|
|
62
|
+
const modelClass = model.constructor;
|
|
63
|
+
for (let proto = modelClass; proto !== BinderObjectModel; proto = Object.getPrototypeOf(proto)) {
|
|
64
|
+
const descriptorEntries = Object.entries(Object.getOwnPropertyDescriptors(proto.prototype)).filter(([_name, propertyDescriptor]) => Object.hasOwn(propertyDescriptor, 'get') || !(propertyDescriptor.value instanceof Function));
|
|
65
|
+
propertyNames.unshift(...descriptorEntries.map(([name]) => name));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
for (let proto = model; proto !== ObjectModel; proto = Object.getPrototypeOf(proto)) {
|
|
70
|
+
propertyNames.unshift(...Object.getOwnPropertyNames(proto));
|
|
71
|
+
}
|
|
62
72
|
}
|
|
63
73
|
return propertyNames;
|
|
64
74
|
};
|
|
@@ -66,7 +76,8 @@ export class ModelInfo {
|
|
|
66
76
|
modelInstance;
|
|
67
77
|
idProperty;
|
|
68
78
|
constructor(model, idPropertyName) {
|
|
69
|
-
|
|
79
|
+
const isModel = model === Model || model instanceof Model;
|
|
80
|
+
this.modelInstance = isModel ? model : createDetachedModel(model);
|
|
70
81
|
this.idProperty = ModelInfo.resolveIdProperty(this, idPropertyName);
|
|
71
82
|
}
|
|
72
83
|
static resolveIdProperty(modelInfo, idPropertyName) {
|
|
@@ -82,7 +93,7 @@ export class ModelInfo {
|
|
|
82
93
|
const parts = path.split('.');
|
|
83
94
|
let currentModel = modelInstance;
|
|
84
95
|
for (const part of parts) {
|
|
85
|
-
if (!currentModel || !(currentModel instanceof ObjectModel)) {
|
|
96
|
+
if (!currentModel || !(currentModel instanceof BinderObjectModel || currentModel instanceof ObjectModel)) {
|
|
86
97
|
return undefined;
|
|
87
98
|
}
|
|
88
99
|
currentModel = currentModel[part];
|
|
@@ -94,7 +105,7 @@ export class ModelInfo {
|
|
|
94
105
|
if (!model) {
|
|
95
106
|
return [];
|
|
96
107
|
}
|
|
97
|
-
return getPropertyNames(model
|
|
108
|
+
return getPropertyNames(model)
|
|
98
109
|
.map((name) => {
|
|
99
110
|
const effectivePath = path ? `${path}.${name}` : name;
|
|
100
111
|
return this.getProperty(effectivePath);
|
|
@@ -103,12 +114,20 @@ export class ModelInfo {
|
|
|
103
114
|
}
|
|
104
115
|
getProperty(path) {
|
|
105
116
|
const propertyModel = ModelInfo.resolvePropertyModel(this.modelInstance, path);
|
|
106
|
-
if (!propertyModel
|
|
117
|
+
if (!propertyModel) {
|
|
107
118
|
return undefined;
|
|
108
119
|
}
|
|
109
120
|
const pathParts = path.split('.');
|
|
110
121
|
const name = pathParts[pathParts.length - 1];
|
|
111
|
-
const meta = propertyModel
|
|
122
|
+
const meta = propertyModel instanceof AbstractModel
|
|
123
|
+
? {
|
|
124
|
+
jvmType: propertyModel[_meta].javaType,
|
|
125
|
+
annotations: propertyModel[_meta].annotations?.map((annotation) => ({
|
|
126
|
+
jvmType: annotation.name,
|
|
127
|
+
attributes: annotation.attributes,
|
|
128
|
+
})),
|
|
129
|
+
}
|
|
130
|
+
: (propertyModel[_meta] ?? {});
|
|
112
131
|
const humanReadableName = _generateHeader(name);
|
|
113
132
|
const type = determinePropertyType(propertyModel);
|
|
114
133
|
return {
|
package/model-info.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-info.js","sourceRoot":"","sources":["src/model-info.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n BooleanModel,\n NumberModel,\n StringModel,\n _meta,\n createDetachedModel,\n type AbstractModel,\n type DetachedModelConstructor,\n type ModelMetadata,\n EnumModel,\n ObjectModel,\n} from '@vaadin/hilla-lit-form';\n\nexport type PropertyType =\n | 'boolean'\n | 'date'\n | 'datetime'\n | 'decimal'\n | 'enum'\n | 'integer'\n | 'object'\n | 'string'\n | 'time'\n | undefined;\n\nconst javaTypeMap: Record<string, PropertyType> = {\n byte: 'integer',\n 'java.lang.Byte': 'integer',\n short: 'integer',\n 'java.lang.Short': 'integer',\n int: 'integer',\n 'java.lang.Integer': 'integer',\n long: 'integer',\n 'java.lang.Long': 'integer',\n float: 'decimal',\n 'java.lang.Float': 'decimal',\n double: 'decimal',\n 'java.lang.Double': 'decimal',\n 'java.time.LocalDate': 'date',\n 'java.time.LocalTime': 'time',\n 'java.time.OffsetTime': 'time',\n 'java.time.LocalDateTime': 'datetime',\n 'java.time.OffsetDateTime': 'datetime',\n 'java.time.ZonedDateTime': 'datetime',\n 'java.util.Date': 'datetime',\n 'java.sql.Date': 'datetime',\n};\n\nfunction determinePropertyType(model: AbstractModel): PropertyType {\n // Try detecting by Java type\n const { javaType } = model[_meta];\n const propertyType = javaType ? javaTypeMap[javaType] : undefined;\n if (propertyType) {\n return propertyType;\n }\n\n // Otherwise detect by model instance\n if (model instanceof StringModel) {\n return 'string';\n } else if (model instanceof NumberModel) {\n return 'decimal';\n } else if (model instanceof BooleanModel) {\n return 'boolean';\n } else if (model instanceof EnumModel) {\n return 'enum';\n } else if (model instanceof ObjectModel) {\n return 'object';\n }\n\n return undefined;\n}\n\nexport interface PropertyInfo {\n name: string;\n humanReadableName: string;\n type: PropertyType;\n meta: ModelMetadata;\n model: AbstractModel;\n}\n\nexport function hasAnnotation(meta: ModelMetadata, annotationName: string): boolean {\n return meta.annotations?.some((annotation) => annotation.name === annotationName) ?? false;\n}\n\n// This is from vaadin-grid-column.js, should be used from there maybe. At least we must be 100% sure to match grid and fields\nexport function _generateHeader(path: string): string {\n return path\n .substring(path.lastIndexOf('.') + 1)\n .replace(/([A-Z])/gu, '-$1')\n .toLowerCase()\n .replace(/-/gu, ' ')\n .replace(/^./u, (match) => match.toUpperCase());\n}\n\nconst getPropertyNames = (model: AbstractModel['constructor']): string[] => {\n const propertyNames: string[] = [];\n\n for (let proto = model; proto !== ObjectModel; proto = Object.getPrototypeOf(proto)) {\n // parent properties are added at the beginning\n propertyNames.unshift(...Object.keys(Object.getOwnPropertyDescriptors(proto.prototype)).filter((p) => p !== 'new'));\n }\n\n return propertyNames;\n};\n\nexport class ModelInfo {\n private readonly modelInstance: AbstractModel;\n\n readonly idProperty?: PropertyInfo;\n\n constructor(model: DetachedModelConstructor<AbstractModel>, idPropertyName?: string) {\n this.modelInstance = createDetachedModel(model);\n\n // Try to find id property\n this.idProperty = ModelInfo.resolveIdProperty(this, idPropertyName);\n }\n\n private static resolveIdProperty(modelInfo: ModelInfo, idPropertyName?: string): PropertyInfo | undefined {\n // Return explicit property if defined\n if (idPropertyName) {\n return modelInfo.getProperty(idPropertyName);\n }\n\n // Otherwise check defaults\n const rootProperties = modelInfo.getRootProperties();\n // Check for @Id annotation\n let idProperty = rootProperties.find((propertyInfo) => hasAnnotation(propertyInfo.meta, 'jakarta.persistence.Id'));\n // Check for id name as fallback\n idProperty ??= rootProperties.find((propertyInfo) => propertyInfo.name === 'id');\n\n return idProperty;\n }\n\n private static resolvePropertyModel(modelInstance: AbstractModel, path: string): AbstractModel | undefined {\n const parts = path.split('.');\n let currentModel: AbstractModel | undefined = modelInstance;\n for (const part of parts) {\n if (!currentModel || !(currentModel instanceof ObjectModel)) {\n return undefined;\n }\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n currentModel = (currentModel as any)[part];\n }\n return currentModel;\n }\n\n getRootProperties(path?: string): PropertyInfo[] {\n const model = path ? ModelInfo.resolvePropertyModel(this.modelInstance, path) : this.modelInstance;\n if (!model) {\n return [];\n }\n return getPropertyNames(model.constructor)\n .map((name) => {\n const effectivePath = path ? `${path}.${name}` : name;\n return this.getProperty(effectivePath);\n })\n .filter(Boolean) as PropertyInfo[];\n }\n\n getProperty(path: string): PropertyInfo | undefined {\n const propertyModel = ModelInfo.resolvePropertyModel(this.modelInstance, path);\n if (!propertyModel?.[_meta]) {\n return undefined;\n }\n\n const pathParts = path.split('.');\n const name = pathParts[pathParts.length - 1];\n\n const meta = propertyModel[_meta];\n const humanReadableName = _generateHeader(name);\n const type = determinePropertyType(propertyModel);\n\n return {\n name: path,\n humanReadableName,\n type,\n meta,\n model: propertyModel,\n };\n }\n\n getProperties(paths: string[]): PropertyInfo[] {\n return paths.map((path) => this.getProperty(path)).filter(Boolean) as PropertyInfo[];\n }\n}\n\nexport function getDefaultProperties(modelInfo: ModelInfo): PropertyInfo[] {\n // Start from root properties\n const properties = modelInfo.getRootProperties();\n return (\n properties\n // Auto-expand nested properties of one-to-one relations\n .flatMap((prop) => {\n if (hasAnnotation(prop.meta, 'jakarta.persistence.OneToOne')) {\n return modelInfo.getRootProperties(prop.name);\n }\n return prop;\n })\n // Exclude properties that have an unknown type, or are annotated with id\n // and version\n .filter(\n (prop) =>\n !!prop.type &&\n !(\n hasAnnotation(prop.meta, 'jakarta.persistence.Id') ||\n hasAnnotation(prop.meta, 'jakarta.persistence.Version')\n ),\n )\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"model-info.js","sourceRoot":"","sources":["src/model-info.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AbstractModel,\n BooleanModel as BinderBooleanModel,\n NumberModel as BinderNumberModel,\n StringModel as BinderStringModel,\n _meta,\n createDetachedModel,\n type DetachedModelConstructor,\n EnumModel as BinderEnumModel,\n ObjectModel as BinderObjectModel,\n type ProvisionalModel,\n} from '@vaadin/hilla-lit-form';\nimport {\n type Annotation,\n type AnnotationValue,\n BooleanModel,\n EnumModel,\n Model,\n type ModelMetadata,\n NumberModel,\n ObjectModel,\n StringModel,\n} from '@vaadin/hilla-models';\n\nexport type PropertyType =\n | 'boolean'\n | 'date'\n | 'datetime'\n | 'decimal'\n | 'enum'\n | 'integer'\n | 'object'\n | 'string'\n | 'time'\n | undefined;\n\nconst javaTypeMap: Record<string, PropertyType> = {\n byte: 'integer',\n 'java.lang.Byte': 'integer',\n short: 'integer',\n 'java.lang.Short': 'integer',\n int: 'integer',\n 'java.lang.Integer': 'integer',\n long: 'integer',\n 'java.lang.Long': 'integer',\n float: 'decimal',\n 'java.lang.Float': 'decimal',\n double: 'decimal',\n 'java.lang.Double': 'decimal',\n 'java.time.LocalDate': 'date',\n 'java.time.LocalTime': 'time',\n 'java.time.OffsetTime': 'time',\n 'java.time.LocalDateTime': 'datetime',\n 'java.time.OffsetDateTime': 'datetime',\n 'java.time.ZonedDateTime': 'datetime',\n 'java.util.Date': 'datetime',\n 'java.sql.Date': 'datetime',\n};\n\nfunction determinePropertyType(model: ProvisionalModel): PropertyType {\n // Try detecting by Java type\n const javaType = model instanceof AbstractModel ? model[_meta].javaType : (model as Model)[_meta]?.jvmType;\n const propertyType = javaType ? javaTypeMap[javaType] : undefined;\n if (propertyType) {\n return propertyType;\n }\n\n // Otherwise detect by model instance\n if (model instanceof BinderStringModel || model instanceof StringModel) {\n return 'string';\n } else if (model instanceof BinderNumberModel || model instanceof NumberModel) {\n return 'decimal';\n } else if (model instanceof BinderBooleanModel || model instanceof BooleanModel) {\n return 'boolean';\n } else if (model instanceof BinderEnumModel || model instanceof EnumModel) {\n return 'enum';\n } else if (model instanceof BinderObjectModel || model instanceof ObjectModel) {\n return 'object';\n }\n\n return undefined;\n}\n\nexport interface PropertyInfo {\n name: string;\n humanReadableName: string;\n type: PropertyType;\n meta: ModelMetadata;\n model: ProvisionalModel;\n}\n\nexport function hasAnnotation(meta: ModelMetadata, annotationName: string): boolean {\n return meta.annotations?.some((annotation) => annotation.jvmType === annotationName) ?? false;\n}\n\n// This is from vaadin-grid-column.js, should be used from there maybe. At least we must be 100% sure to match grid and fields\nexport function _generateHeader(path: string): string {\n return path\n .substring(path.lastIndexOf('.') + 1)\n .replace(/([A-Z])/gu, '-$1')\n .toLowerCase()\n .replace(/-/gu, ' ')\n .replace(/^./u, (match) => match.toUpperCase());\n}\n\nconst getPropertyNames = (model: ProvisionalModel): string[] => {\n const propertyNames: string[] = [];\n\n if (model instanceof AbstractModel) {\n const modelClass = model.constructor as unknown as DetachedModelConstructor<AbstractModel>;\n for (let proto = modelClass; proto !== BinderObjectModel; proto = Object.getPrototypeOf(proto)) {\n // skip `constructor`, take only own properties with getters or value\n const descriptorEntries = Object.entries(Object.getOwnPropertyDescriptors(proto.prototype)).filter(\n ([_name, propertyDescriptor]) =>\n Object.hasOwn(propertyDescriptor, 'get') || !(propertyDescriptor.value instanceof Function),\n );\n // parent properties are added at the beginning\n propertyNames.unshift(...descriptorEntries.map(([name]) => name));\n }\n } else {\n for (let proto = model; proto !== ObjectModel; proto = Object.getPrototypeOf(proto)) {\n propertyNames.unshift(...Object.getOwnPropertyNames(proto));\n }\n }\n\n return propertyNames;\n};\n\nexport class ModelInfo {\n private readonly modelInstance: ProvisionalModel;\n\n readonly idProperty?: PropertyInfo;\n\n constructor(model: DetachedModelConstructor<AbstractModel> | Model, idPropertyName?: string) {\n const isModel = model === Model || model instanceof Model;\n this.modelInstance = isModel ? model : createDetachedModel(model as DetachedModelConstructor<AbstractModel>);\n\n // Try to find id property\n this.idProperty = ModelInfo.resolveIdProperty(this, idPropertyName);\n }\n\n private static resolveIdProperty(modelInfo: ModelInfo, idPropertyName?: string): PropertyInfo | undefined {\n // Return explicit property if defined\n if (idPropertyName) {\n return modelInfo.getProperty(idPropertyName);\n }\n\n // Otherwise check defaults\n const rootProperties = modelInfo.getRootProperties();\n // Check for @Id annotation\n let idProperty = rootProperties.find((propertyInfo) => hasAnnotation(propertyInfo.meta, 'jakarta.persistence.Id'));\n // Check for id name as fallback\n idProperty ??= rootProperties.find((propertyInfo) => propertyInfo.name === 'id');\n\n return idProperty;\n }\n\n private static resolvePropertyModel(modelInstance: ProvisionalModel, path: string): ProvisionalModel | undefined {\n const parts = path.split('.');\n let currentModel: ProvisionalModel | undefined = modelInstance;\n for (const part of parts) {\n if (!currentModel || !(currentModel instanceof BinderObjectModel || currentModel instanceof ObjectModel)) {\n return undefined;\n }\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n currentModel = (currentModel as any)[part];\n }\n return currentModel;\n }\n\n getRootProperties(path?: string): PropertyInfo[] {\n const model = path ? ModelInfo.resolvePropertyModel(this.modelInstance, path) : this.modelInstance;\n if (!model) {\n return [];\n }\n return getPropertyNames(model)\n .map((name) => {\n const effectivePath = path ? `${path}.${name}` : name;\n return this.getProperty(effectivePath);\n })\n .filter(Boolean) as PropertyInfo[];\n }\n\n getProperty(path: string): PropertyInfo | undefined {\n const propertyModel = ModelInfo.resolvePropertyModel(this.modelInstance, path);\n if (!propertyModel) {\n return undefined;\n }\n\n const pathParts = path.split('.');\n const name = pathParts[pathParts.length - 1];\n\n const meta =\n propertyModel instanceof AbstractModel\n ? ({\n jvmType: propertyModel[_meta].javaType,\n annotations: propertyModel[_meta].annotations?.map(\n (annotation) =>\n ({\n jvmType: annotation.name,\n attributes: annotation.attributes as Record<string, AnnotationValue>,\n }) satisfies Annotation,\n ),\n } satisfies ModelMetadata)\n : (propertyModel[_meta] ?? {});\n const humanReadableName = _generateHeader(name);\n const type = determinePropertyType(propertyModel);\n\n return {\n name: path,\n humanReadableName,\n type,\n meta,\n model: propertyModel,\n };\n }\n\n getProperties(paths: string[]): PropertyInfo[] {\n return paths.map((path) => this.getProperty(path)).filter(Boolean) as PropertyInfo[];\n }\n}\n\nexport function getDefaultProperties(modelInfo: ModelInfo): PropertyInfo[] {\n // Start from root properties\n const properties = modelInfo.getRootProperties();\n return (\n properties\n // Auto-expand nested properties of one-to-one relations\n .flatMap((prop) => {\n if (hasAnnotation(prop.meta, 'jakarta.persistence.OneToOne')) {\n return modelInfo.getRootProperties(prop.name);\n }\n return prop;\n })\n // Exclude properties that have an unknown type, or are annotated with id\n // and version\n .filter(\n (prop) =>\n !!prop.type &&\n !(\n hasAnnotation(prop.meta, 'jakarta.persistence.Id') ||\n hasAnnotation(prop.meta, 'jakarta.persistence.Version')\n ),\n )\n );\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/hilla-react-crud",
|
|
3
|
-
"version": "25.3.0-
|
|
3
|
+
"version": "25.3.0-alpha4",
|
|
4
4
|
"description": "Hilla CRUD utils for React",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "index.js",
|
|
@@ -54,15 +54,15 @@
|
|
|
54
54
|
"access": "public"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@vaadin/hilla-frontend": "25.3.0-
|
|
58
|
-
"@vaadin/hilla-lit-form": "25.3.0-
|
|
59
|
-
"@vaadin/hilla-react-form": "25.3.0-
|
|
60
|
-
"@vaadin/vaadin-lumo-styles": "25.
|
|
61
|
-
"@vaadin/react-components": "25.
|
|
57
|
+
"@vaadin/hilla-frontend": "25.3.0-alpha4",
|
|
58
|
+
"@vaadin/hilla-lit-form": "25.3.0-alpha4",
|
|
59
|
+
"@vaadin/hilla-react-form": "25.3.0-alpha4",
|
|
60
|
+
"@vaadin/vaadin-lumo-styles": "25.3.0-alpha6",
|
|
61
|
+
"@vaadin/react-components": "25.3.0-alpha6",
|
|
62
62
|
"type-fest": "4.35.0"
|
|
63
63
|
},
|
|
64
64
|
"peerDependencies": {
|
|
65
65
|
"react": "18 || 19",
|
|
66
66
|
"react-dom": "18 || 19"
|
|
67
67
|
}
|
|
68
|
-
}
|
|
68
|
+
}
|
package/util.js
CHANGED
|
@@ -24,7 +24,7 @@ function useFeatureRegistration(feature) {
|
|
|
24
24
|
vaadinObj.registrations ??= [];
|
|
25
25
|
vaadinObj.registrations.push({
|
|
26
26
|
is: feature ? `@vaadin/hilla-react-crud/${feature}` : '@vaadin/hilla-react-crud',
|
|
27
|
-
version: '25.3.0-
|
|
27
|
+
version: '25.3.0-alpha4',
|
|
28
28
|
});
|
|
29
29
|
})(feature);
|
|
30
30
|
}
|
package/util.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.js","sourceRoot":"","sources":["src/util.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,EAAsB,UAAU,EAAE,MAAM,OAAO,CAAC;AAS9D,MAAM,UAAU,kBAAkB,CAAC,WAAmB;IAEpD,MAAM,gBAAgB,GAAG,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAGzD,MAAM,KAAK,GAAG,gBAAgB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC1C,MAAM,cAAc,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IAGvG,OAAO,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,UAAyB;IAC1D,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;SACxC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC;SAC3B,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,QAAQ,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACjD,QAAQ,CAAC,WAAW,GAAG,GAAG,CAAC;IAC3B,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAU,CAAC;AAC7C,SAAS,sBAAsB,CAAC,OAAe;IAC7C,IAAI,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QACpC,OAAO;IACT,CAAC;IAED,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAGhC,CAAC,CAAC,OAAO,EAAE,SAAS,GAAG,CAAC,UAAU,CAAC,MAAM,KAAK,EAAE,CAAC,EAAE,EAAE;QACrD,SAAS,CAAC,aAAa,KAAK,EAAE,CAAC;QAC/B,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC;YAC3B,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC,4BAA4B,OAAO,EAAE,CAAC,CAAC,CAAC,0BAA0B;YAChF,OAAO,EAAE,eAAe;SACzB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAoC,SAAY,EAAE,OAAe;IAClG,OAAO,UAAU,CAAuE,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACrG,sBAAsB,CAAC,OAAO,CAAC,CAAC;QAChC,OAAO,KAAC,SAAS,OAAM,KAAa,EAAE,GAAG,EAAE,GAAG,GAAI,CAAC;IACrD,CAAC,CAAiB,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAmB;IAC/C,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,KAAK,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;QAC1D,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,KAAoB,CAAC,CAAC,CAAC;IAC/E,CAAC;IACD,IAAI,aAAa,IAAI,MAAM,EAAE,CAAC;QAC5B,OAAO,MAAM,CAAC,WAAW,KAAK,EAAE,CAAC;IACnC,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,wBAAwB,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAC3G,CAAC","sourcesContent":["import React, { type CSSProperties, forwardRef } from 'react';\nimport type FilterUnion from './types/com/vaadin/hilla/crud/filter/FilterUnion.js';\n\nexport type ComponentStyleProps = Readonly<{\n id?: string;\n style?: CSSProperties;\n className?: string;\n}>;\n\nexport function convertToTitleCase(inputString: string): string {\n // Convert underscores to spaces\n const stringWithSpaces = inputString.replace(/_/gu, ' ');\n\n // Convert to title case\n const words = stringWithSpaces.split(' ');\n const titleCaseWords = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase());\n\n // Join the title case words with spaces\n return titleCaseWords.join(' ');\n}\n\nexport function registerStylesheet(stylesheet: CSSStyleSheet): void {\n const css = Array.from(stylesheet.cssRules)\n .map((rule) => rule.cssText)\n .join('\\n');\n\n const styleTag = document.createElement('style');\n styleTag.textContent = css;\n document.head.prepend(styleTag);\n}\n\nconst registeredFeatures = new Set<string>();\nfunction useFeatureRegistration(feature: string): void {\n if (registeredFeatures.has(feature)) {\n return;\n }\n\n registeredFeatures.add(feature);\n // @ts-expect-error: esbuild injection\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n ((feature, vaadinObj = (globalThis.Vaadin ??= {})) => {\n vaadinObj.registrations ??= [];\n vaadinObj.registrations.push({\n is: feature ? `@vaadin/hilla-react-crud/${feature}` : '@vaadin/hilla-react-crud',\n version: '25.3.0-
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["src/util.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { type CSSProperties, forwardRef } from 'react';\nimport type FilterUnion from './types/com/vaadin/hilla/crud/filter/FilterUnion.js';\n\nexport type ComponentStyleProps = Readonly<{\n id?: string;\n style?: CSSProperties;\n className?: string;\n}>;\n\nexport function convertToTitleCase(inputString: string): string {\n // Convert underscores to spaces\n const stringWithSpaces = inputString.replace(/_/gu, ' ');\n\n // Convert to title case\n const words = stringWithSpaces.split(' ');\n const titleCaseWords = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase());\n\n // Join the title case words with spaces\n return titleCaseWords.join(' ');\n}\n\nexport function registerStylesheet(stylesheet: CSSStyleSheet): void {\n const css = Array.from(stylesheet.cssRules)\n .map((rule) => rule.cssText)\n .join('\\n');\n\n const styleTag = document.createElement('style');\n styleTag.textContent = css;\n document.head.prepend(styleTag);\n}\n\nconst registeredFeatures = new Set<string>();\nfunction useFeatureRegistration(feature: string): void {\n if (registeredFeatures.has(feature)) {\n return;\n }\n\n registeredFeatures.add(feature);\n // @ts-expect-error: esbuild injection\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n ((feature, vaadinObj = (globalThis.Vaadin ??= {})) => {\n vaadinObj.registrations ??= [];\n vaadinObj.registrations.push({\n is: feature ? `@vaadin/hilla-react-crud/${feature}` : '@vaadin/hilla-react-crud',\n version: '25.3.0-alpha4',\n });\n})(feature);\n}\n\nexport function featureRegistration<C extends (...args: any[]) => any>(Component: C, feature: string): C {\n return forwardRef<unknown, React.JSX.LibraryManagedAttributes<C, NonNullable<unknown>>>((props, ref) => {\n useFeatureRegistration(feature);\n return <Component {...(props as any)} ref={ref} />;\n }) as unknown as C;\n}\n\nexport function isFilterEmpty(filter: FilterUnion): boolean {\n if (filter['@type'] === 'and' || filter['@type'] === 'or') {\n if (filter.children.length === 0) {\n return true;\n }\n return filter.children.every((child) => isFilterEmpty(child as FilterUnion));\n }\n if ('filterValue' in filter) {\n return filter.filterValue === '';\n }\n throw new Error(`Unknown filter type: ${'@type' in filter ? filter['@type'] : JSON.stringify(filter)} `);\n}\n"]}
|