@donotdev/crud 0.0.6 → 0.0.7
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/README.md +18 -2
- package/dist/CrudService.d.ts +91 -8
- package/dist/CrudService.d.ts.map +1 -1
- package/dist/CrudService.js +1 -1
- package/dist/CrudStore.d.ts +45 -2
- package/dist/CrudStore.d.ts.map +1 -1
- package/dist/CrudStore.js +1 -1
- package/dist/FieldRegistry.d.ts +56 -20
- package/dist/FieldRegistry.d.ts.map +1 -1
- package/dist/FieldRegistry.js +1 -1
- package/dist/adapters/FirestoreAdapter.d.ts +4 -4
- package/dist/adapters/FirestoreAdapter.d.ts.map +1 -1
- package/dist/adapters/FirestoreAdapter.js +1 -1
- package/dist/adapters/FunctionsAdapter.d.ts +3 -2
- package/dist/adapters/FunctionsAdapter.d.ts.map +1 -1
- package/dist/adapters/FunctionsAdapter.js +1 -1
- package/dist/builtinFieldTypes.d.ts +4 -0
- package/dist/builtinFieldTypes.d.ts.map +1 -1
- package/dist/builtinFieldTypes.js +1 -1
- package/dist/components/CrudButton.d.ts +1 -1
- package/dist/components/CrudButton.d.ts.map +1 -1
- package/dist/components/CrudButton.js +1 -1
- package/dist/components/DisplayFieldRenderer.d.ts +15 -11
- package/dist/components/DisplayFieldRenderer.d.ts.map +1 -1
- package/dist/components/DisplayFieldRenderer.js +1 -1
- package/dist/components/EntityCardList.d.ts +27 -0
- package/dist/components/EntityCardList.d.ts.map +1 -0
- package/dist/components/EntityCardList.js +1 -0
- package/dist/components/EntityDisplayRenderer.d.ts +43 -0
- package/dist/components/EntityDisplayRenderer.d.ts.map +1 -0
- package/dist/components/EntityDisplayRenderer.js +1 -0
- package/dist/components/EntityFormRenderer.d.ts +21 -26
- package/dist/components/EntityFormRenderer.d.ts.map +1 -1
- package/dist/components/EntityFormRenderer.js +1 -5
- package/dist/components/EntityList.d.ts +24 -0
- package/dist/components/EntityList.d.ts.map +1 -0
- package/dist/components/EntityList.js +1 -0
- package/dist/components/FormFieldRenderer.d.ts +3 -10
- package/dist/components/FormFieldRenderer.d.ts.map +1 -1
- package/dist/components/FormFieldRenderer.js +1 -1
- package/dist/components/FormLayout.d.ts +1 -9
- package/dist/components/FormLayout.d.ts.map +1 -1
- package/dist/components/controlled/complex/ControlledAddressField.d.ts +8 -0
- package/dist/components/controlled/complex/ControlledAddressField.d.ts.map +1 -0
- package/dist/components/controlled/complex/ControlledAddressField.js +1 -0
- package/dist/components/controlled/complex/ControlledDateField.d.ts +8 -0
- package/dist/components/controlled/complex/ControlledDateField.d.ts.map +1 -0
- package/dist/components/controlled/complex/ControlledDateField.js +1 -0
- package/dist/components/controlled/complex/ControlledGeoPointField.d.ts +8 -0
- package/dist/components/controlled/complex/ControlledGeoPointField.d.ts.map +1 -0
- package/dist/components/controlled/complex/ControlledGeoPointField.js +1 -0
- package/dist/components/controlled/complex/ControlledMapField.d.ts +8 -0
- package/dist/components/controlled/complex/ControlledMapField.d.ts.map +1 -0
- package/dist/components/controlled/complex/ControlledMapField.js +1 -0
- package/dist/components/controlled/complex/ControlledMultiInputField.d.ts +8 -0
- package/dist/components/controlled/complex/ControlledMultiInputField.d.ts.map +1 -0
- package/dist/components/controlled/complex/ControlledMultiInputField.js +1 -0
- package/dist/components/controlled/complex/ControlledRichTextField.d.ts +8 -0
- package/dist/components/controlled/complex/ControlledRichTextField.d.ts.map +1 -0
- package/dist/components/controlled/complex/ControlledRichTextField.js +1 -0
- package/dist/components/controlled/complex/ControlledTimestampField.d.ts +8 -0
- package/dist/components/controlled/complex/ControlledTimestampField.d.ts.map +1 -0
- package/dist/components/controlled/complex/ControlledTimestampField.js +1 -0
- package/dist/components/controlled/complex/index.d.ts +8 -0
- package/dist/components/controlled/complex/index.d.ts.map +1 -0
- package/dist/components/controlled/complex/index.js +1 -0
- package/dist/components/controlled/file/ControlledDocumentField.d.ts +8 -0
- package/dist/components/controlled/file/ControlledDocumentField.d.ts.map +1 -0
- package/dist/components/controlled/file/ControlledDocumentField.js +1 -0
- package/dist/components/controlled/file/ControlledFileField.d.ts +8 -0
- package/dist/components/controlled/file/ControlledFileField.d.ts.map +1 -0
- package/dist/components/controlled/file/ControlledFileField.js +1 -0
- package/dist/components/controlled/file/ControlledImageField.d.ts +8 -0
- package/dist/components/controlled/file/ControlledImageField.d.ts.map +1 -0
- package/dist/components/controlled/file/ControlledImageField.js +1 -0
- package/dist/components/controlled/file/ControlledMultiDocumentField.d.ts +8 -0
- package/dist/components/controlled/file/ControlledMultiDocumentField.d.ts.map +1 -0
- package/dist/components/controlled/file/ControlledMultiDocumentField.js +1 -0
- package/dist/components/controlled/file/ControlledMultiFileField.d.ts +8 -0
- package/dist/components/controlled/file/ControlledMultiFileField.d.ts.map +1 -0
- package/dist/components/controlled/file/ControlledMultiFileField.js +1 -0
- package/dist/components/controlled/file/ControlledMultiImageField.d.ts +8 -0
- package/dist/components/controlled/file/ControlledMultiImageField.d.ts.map +1 -0
- package/dist/components/controlled/file/ControlledMultiImageField.js +1 -0
- package/dist/components/controlled/file/index.d.ts +7 -0
- package/dist/components/controlled/file/index.d.ts.map +1 -0
- package/dist/components/controlled/file/index.js +1 -0
- package/dist/components/controlled/index.d.ts +12 -0
- package/dist/components/controlled/index.d.ts.map +1 -0
- package/dist/components/controlled/index.js +1 -0
- package/dist/components/controlled/input/ControlledCheckboxField.d.ts +8 -0
- package/dist/components/controlled/input/ControlledCheckboxField.d.ts.map +1 -0
- package/dist/components/controlled/input/ControlledCheckboxField.js +1 -0
- package/dist/components/controlled/input/ControlledNumberField.d.ts +8 -0
- package/dist/components/controlled/input/ControlledNumberField.d.ts.map +1 -0
- package/dist/components/controlled/input/ControlledNumberField.js +1 -0
- package/dist/components/controlled/input/ControlledPasswordField.d.ts +8 -0
- package/dist/components/controlled/input/ControlledPasswordField.d.ts.map +1 -0
- package/dist/components/controlled/input/ControlledPasswordField.js +1 -0
- package/dist/components/controlled/input/ControlledPhoneField.d.ts +8 -0
- package/dist/components/controlled/input/ControlledPhoneField.d.ts.map +1 -0
- package/dist/components/controlled/input/ControlledPhoneField.js +1 -0
- package/dist/components/controlled/input/ControlledRangeField.d.ts +8 -0
- package/dist/components/controlled/input/ControlledRangeField.d.ts.map +1 -0
- package/dist/components/controlled/input/ControlledRangeField.js +1 -0
- package/dist/components/controlled/input/ControlledSwitchField.d.ts +8 -0
- package/dist/components/controlled/input/ControlledSwitchField.d.ts.map +1 -0
- package/dist/components/controlled/input/ControlledSwitchField.js +1 -0
- package/dist/components/controlled/input/ControlledTextField.d.ts +8 -0
- package/dist/components/controlled/input/ControlledTextField.d.ts.map +1 -0
- package/dist/components/controlled/input/ControlledTextField.js +1 -0
- package/dist/components/controlled/input/ControlledTextareaField.d.ts +8 -0
- package/dist/components/controlled/input/ControlledTextareaField.d.ts.map +1 -0
- package/dist/components/controlled/input/ControlledTextareaField.js +1 -0
- package/dist/components/controlled/input/index.d.ts +9 -0
- package/dist/components/controlled/input/index.d.ts.map +1 -0
- package/dist/components/controlled/input/index.js +1 -0
- package/dist/components/controlled/select/ControlledComboboxField.d.ts +9 -0
- package/dist/components/controlled/select/ControlledComboboxField.d.ts.map +1 -0
- package/dist/components/controlled/select/ControlledComboboxField.js +1 -0
- package/dist/components/controlled/select/ControlledDropdownField.d.ts +9 -0
- package/dist/components/controlled/select/ControlledDropdownField.d.ts.map +1 -0
- package/dist/components/controlled/select/ControlledDropdownField.js +1 -0
- package/dist/components/controlled/select/ControlledMultiDropdownField.d.ts +9 -0
- package/dist/components/controlled/select/ControlledMultiDropdownField.d.ts.map +1 -0
- package/dist/components/controlled/select/ControlledMultiDropdownField.js +1 -0
- package/dist/components/controlled/select/ControlledRadioField.d.ts +9 -0
- package/dist/components/controlled/select/ControlledRadioField.d.ts.map +1 -0
- package/dist/components/controlled/select/ControlledRadioField.js +1 -0
- package/dist/components/controlled/select/index.d.ts +5 -0
- package/dist/components/controlled/select/index.d.ts.map +1 -0
- package/dist/components/controlled/select/index.js +1 -0
- package/dist/components/controlled/types.d.ts +23 -0
- package/dist/components/controlled/types.d.ts.map +1 -0
- package/dist/components/controlled/types.js +1 -0
- package/dist/components/form/fields/AddressFieldComponent.d.ts.map +1 -1
- package/dist/components/form/fields/AddressFieldComponent.js +1 -1
- package/dist/components/form/fields/AvatarFieldComponent.d.ts +2 -2
- package/dist/components/form/fields/BadgeFieldComponent.d.ts +2 -2
- package/dist/components/form/fields/ButtonFieldComponent.d.ts +1 -9
- package/dist/components/form/fields/ButtonFieldComponent.d.ts.map +1 -1
- package/dist/components/form/fields/ButtonFieldComponent.js +1 -1
- package/dist/components/form/fields/ComboboxComponent.d.ts.map +1 -1
- package/dist/components/form/fields/CurrencyFieldComponent.d.ts +1 -9
- package/dist/components/form/fields/CurrencyFieldComponent.d.ts.map +1 -1
- package/dist/components/form/fields/CurrencyFieldComponent.js +1 -1
- package/dist/components/form/fields/DateFieldComponent.d.ts.map +1 -1
- package/dist/components/form/fields/DateFieldComponent.js +1 -1
- package/dist/components/form/fields/DocumentFieldComponent.d.ts +47 -0
- package/dist/components/form/fields/DocumentFieldComponent.d.ts.map +1 -0
- package/dist/components/form/fields/DocumentFieldComponent.js +1 -0
- package/dist/components/form/fields/DropdownComponent.d.ts.map +1 -1
- package/dist/components/form/fields/DropdownComponent.js +1 -1
- package/dist/components/form/fields/FileFieldComponent.d.ts +31 -15
- package/dist/components/form/fields/FileFieldComponent.d.ts.map +1 -1
- package/dist/components/form/fields/FileFieldComponent.js +1 -1
- package/dist/components/form/fields/GeoPointFieldComponent.d.ts.map +1 -1
- package/dist/components/form/fields/GeoPointFieldComponent.js +1 -1
- package/dist/components/form/fields/HiddenFieldComponent.d.ts +1 -1
- package/dist/components/form/fields/HiddenFieldComponent.d.ts.map +1 -1
- package/dist/components/form/fields/HiddenFieldComponent.js +1 -1
- package/dist/components/form/fields/ImageFieldComponent.d.ts +8 -14
- package/dist/components/form/fields/ImageFieldComponent.d.ts.map +1 -1
- package/dist/components/form/fields/ImageFieldComponent.js +1 -1
- package/dist/components/form/fields/MapFieldComponent.d.ts.map +1 -1
- package/dist/components/form/fields/MapFieldComponent.js +1 -1
- package/dist/components/form/fields/MultiDropdownComponent.js +1 -1
- package/dist/components/form/fields/MultiInputTextFieldComponent.d.ts +1 -9
- package/dist/components/form/fields/MultiInputTextFieldComponent.d.ts.map +1 -1
- package/dist/components/form/fields/MultiInputTextFieldComponent.js +1 -1
- package/dist/components/form/fields/NumberFieldComponent.d.ts +2 -0
- package/dist/components/form/fields/NumberFieldComponent.d.ts.map +1 -1
- package/dist/components/form/fields/NumberFieldComponent.js +1 -1
- package/dist/components/form/fields/PasswordFieldComponent.d.ts +1 -9
- package/dist/components/form/fields/PasswordFieldComponent.d.ts.map +1 -1
- package/dist/components/form/fields/PhoneNumberComponent.d.ts +1 -9
- package/dist/components/form/fields/PhoneNumberComponent.d.ts.map +1 -1
- package/dist/components/form/fields/PhoneNumberComponent.js +1 -1
- package/dist/components/form/fields/RadioFieldComponent.d.ts +1 -9
- package/dist/components/form/fields/RadioFieldComponent.d.ts.map +1 -1
- package/dist/components/form/fields/ReferenceFieldComponent.d.ts +33 -12
- package/dist/components/form/fields/ReferenceFieldComponent.d.ts.map +1 -1
- package/dist/components/form/fields/ReferenceFieldComponent.js +1 -1
- package/dist/components/form/fields/RichTextComponent.d.ts +32 -0
- package/dist/components/form/fields/RichTextComponent.d.ts.map +1 -0
- package/dist/components/form/fields/RichTextComponent.js +1 -0
- package/dist/components/form/fields/SwitchFieldComponent.d.ts.map +1 -1
- package/dist/components/form/fields/TextAreaComponent.d.ts +1 -9
- package/dist/components/form/fields/TextAreaComponent.d.ts.map +1 -1
- package/dist/components/form/fields/TextAreaComponent.js +1 -1
- package/dist/components/form/fields/TextFieldComponent.d.ts +1 -9
- package/dist/components/form/fields/TextFieldComponent.d.ts.map +1 -1
- package/dist/components/form/fields/index.d.ts +4 -2
- package/dist/components/form/fields/index.d.ts.map +1 -1
- package/dist/components/form/fields/index.js +1 -1
- package/dist/components/form/fields/internal/TiptapEditor.d.ts +13 -0
- package/dist/components/form/fields/internal/TiptapEditor.d.ts.map +1 -0
- package/dist/components/form/fields/internal/TiptapEditor.js +52 -0
- package/dist/components/form/index.d.ts +10 -0
- package/dist/components/form/index.d.ts.map +1 -0
- package/dist/components/form/index.js +1 -0
- package/dist/components/form/internal/ImageViewerDialog.d.ts +1 -1
- package/dist/components/form/internal/ImageViewerDialog.d.ts.map +1 -1
- package/dist/components/index.d.ts +8 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -1
- package/dist/contexts/UploadContext.d.ts +16 -0
- package/dist/contexts/UploadContext.d.ts.map +1 -0
- package/dist/contexts/UploadContext.js +1 -0
- package/dist/contexts/index.d.ts +3 -0
- package/dist/contexts/index.d.ts.map +1 -0
- package/dist/contexts/index.js +1 -0
- package/dist/forms/hooks/index.d.ts +2 -0
- package/dist/forms/hooks/index.d.ts.map +1 -1
- package/dist/forms/hooks/index.js +1 -1
- package/dist/forms/hooks/useController.d.ts +29 -0
- package/dist/forms/hooks/useController.d.ts.map +1 -0
- package/dist/forms/hooks/useController.js +1 -0
- package/dist/forms/hooks/useEntityField.d.ts.map +1 -1
- package/dist/forms/hooks/useEntityField.js +1 -1
- package/dist/forms/hooks/useEntityForm.d.ts +8 -76
- package/dist/forms/hooks/useEntityForm.d.ts.map +1 -1
- package/dist/forms/hooks/useEntityForm.js +1 -1
- package/dist/forms/index.d.ts +6 -4
- package/dist/forms/index.d.ts.map +1 -1
- package/dist/forms/index.js +1 -1
- package/dist/forms/types.d.ts +31 -5
- package/dist/forms/types.d.ts.map +1 -1
- package/dist/forms/utils/getFieldsForOperation.d.ts +5 -5
- package/dist/forms/utils/getFieldsForOperation.d.ts.map +1 -1
- package/dist/forms/utils/getFieldsForOperation.js +1 -1
- package/dist/forms/utils/index.d.ts +9 -5
- package/dist/forms/utils/index.d.ts.map +1 -1
- package/dist/forms/utils/index.js +1 -1
- package/dist/forms/utils/isFieldEditable.d.ts +0 -8
- package/dist/forms/utils/isFieldEditable.d.ts.map +1 -1
- package/dist/forms/utils/optionHelpers.d.ts +54 -0
- package/dist/forms/utils/optionHelpers.d.ts.map +1 -0
- package/dist/forms/utils/optionHelpers.js +1 -0
- package/dist/forms/utils/translateFieldLabel.d.ts +70 -0
- package/dist/forms/utils/translateFieldLabel.d.ts.map +1 -0
- package/dist/forms/utils/translateFieldLabel.js +1 -0
- package/dist/forms/utils/validateEntity.d.ts +5 -2
- package/dist/forms/utils/validateEntity.d.ts.map +1 -1
- package/dist/forms/utils/validateEntity.js +1 -1
- package/dist/hooks/index.d.ts +3 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useFileUpload.d.ts +67 -0
- package/dist/hooks/useFileUpload.d.ts.map +1 -0
- package/dist/hooks/useFileUpload.js +1 -0
- package/dist/index.d.ts +15 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/stores/FormStore.d.ts +78 -0
- package/dist/stores/FormStore.d.ts.map +1 -0
- package/dist/stores/FormStore.js +1 -0
- package/dist/stores/UploadStore.d.ts +105 -0
- package/dist/stores/UploadStore.d.ts.map +1 -0
- package/dist/stores/UploadStore.js +1 -0
- package/dist/stores/index.d.ts +11 -0
- package/dist/stores/index.d.ts.map +1 -0
- package/dist/stores/index.js +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/useCrud.d.ts +32 -74
- package/dist/useCrud.d.ts.map +1 -1
- package/dist/useCrud.js +1 -1
- package/dist/useCrudCardList.d.ts +62 -0
- package/dist/useCrudCardList.d.ts.map +1 -0
- package/dist/useCrudCardList.js +1 -0
- package/dist/useCrudList.d.ts +61 -0
- package/dist/useCrudList.d.ts.map +1 -0
- package/dist/useCrudList.js +1 -0
- package/dist/utils/fileStorage.d.ts +58 -0
- package/dist/utils/fileStorage.d.ts.map +1 -0
- package/dist/utils/fileStorage.js +1 -0
- package/dist/utils/imageProcessing.d.ts +1 -1
- package/dist/utils/imageProcessing.d.ts.map +1 -1
- package/dist/utils/imageProcessing.js +1 -1
- package/dist/utils/imageStorage.d.ts +1 -10
- package/dist/utils/imageStorage.d.ts.map +1 -1
- package/dist/utils/imageStorage.js +1 -1
- package/dist/utils/mergeWithOptimistic.d.ts +27 -0
- package/dist/utils/mergeWithOptimistic.d.ts.map +1 -0
- package/dist/utils/mergeWithOptimistic.js +1 -0
- package/dist/utils/uploadValidation.d.ts +37 -0
- package/dist/utils/uploadValidation.d.ts.map +1 -0
- package/dist/utils/uploadValidation.js +1 -0
- package/package.json +22 -5
- package/dist/components/ControlledFields.d.ts +0 -49
- package/dist/components/ControlledFields.d.ts.map +0 -1
- package/dist/components/ControlledFields.js +0 -1
- package/dist/context/FormUploadContext.d.ts +0 -36
- package/dist/context/FormUploadContext.d.ts.map +0 -1
- package/dist/context/FormUploadContext.js +0 -1
- package/dist/context/index.d.ts +0 -2
- package/dist/context/index.d.ts.map +0 -1
- package/dist/context/index.js +0 -1
- package/dist/forms/utils/createEntitySchema.d.ts +0 -53
- package/dist/forms/utils/createEntitySchema.d.ts.map +0 -1
- package/dist/forms/utils/createEntitySchema.js +0 -1
- package/dist/forms/utils/normalizeToFieldConfig.d.ts +0 -47
- package/dist/forms/utils/normalizeToFieldConfig.d.ts.map +0 -1
- package/dist/forms/utils/normalizeToFieldConfig.js +0 -1
|
@@ -3,87 +3,19 @@ import type { UseEntityFormOptions, EntityFormReturn } from '../types';
|
|
|
3
3
|
/**
|
|
4
4
|
* Creates a type-safe form instance from an entity definition.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
9
|
-
*
|
|
6
|
+
* Simple, predictable behavior:
|
|
7
|
+
* - Edit mode: defaultValues (DB object) is used as-is
|
|
8
|
+
* - Create mode: starts empty, optional auto-save to localStorage
|
|
9
|
+
*
|
|
10
|
+
* When formId is provided:
|
|
11
|
+
* - Tracks form status (uploading/validating/submitting)
|
|
12
|
+
* - Orchestrates file uploads before validation
|
|
13
|
+
* - Provides cleanup function for unmount
|
|
10
14
|
*
|
|
11
15
|
* @template E - Entity type from defineEntity()
|
|
12
16
|
* @param entity - Entity definition from defineEntity()
|
|
13
17
|
* @param options - Form configuration options
|
|
14
18
|
* @returns Form instance with React Hook Form API plus entity-aware extensions
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* ```tsx
|
|
18
|
-
* import { useEntityForm } from '@donotdev/crud/forms';
|
|
19
|
-
* import { productEntity } from './entities/product';
|
|
20
|
-
*
|
|
21
|
-
* function ProductForm({ existingProduct, onSuccess }) {
|
|
22
|
-
* const form = useEntityForm(productEntity, {
|
|
23
|
-
* operation: existingProduct ? 'edit' : 'create',
|
|
24
|
-
* defaultValues: existingProduct,
|
|
25
|
-
* viewerRole: 'admin'
|
|
26
|
-
* });
|
|
27
|
-
*
|
|
28
|
-
* const onSubmit = async (data) => {
|
|
29
|
-
* await saveProduct(data);
|
|
30
|
-
* onSuccess();
|
|
31
|
-
* };
|
|
32
|
-
*
|
|
33
|
-
* return (
|
|
34
|
-
* <form onSubmit={form.handleSubmit(onSubmit)}>
|
|
35
|
-
* {form.fields.map(({ name, config, editable }) => (
|
|
36
|
-
* editable ? (
|
|
37
|
-
* <input
|
|
38
|
-
* key={name}
|
|
39
|
-
* {...form.register(name)}
|
|
40
|
-
* placeholder={config.label}
|
|
41
|
-
* />
|
|
42
|
-
* ) : (
|
|
43
|
-
* <span key={name}>{form.getValues(name)}</span>
|
|
44
|
-
* )
|
|
45
|
-
* ))}
|
|
46
|
-
* <button type="submit" disabled={form.formState.isSubmitting}>
|
|
47
|
-
* {form.operation === 'create' ? 'Create' : 'Update'}
|
|
48
|
-
* </button>
|
|
49
|
-
* </form>
|
|
50
|
-
* );
|
|
51
|
-
* }
|
|
52
|
-
* ```
|
|
53
|
-
*
|
|
54
|
-
* @example
|
|
55
|
-
* ```tsx
|
|
56
|
-
* // With custom UI components
|
|
57
|
-
* import { useEntityForm } from '@donotdev/crud/forms';
|
|
58
|
-
* import { Input, Select, DatePicker } from './ui';
|
|
59
|
-
*
|
|
60
|
-
* function CustomForm() {
|
|
61
|
-
* const form = useEntityForm(orderEntity, { operation: 'create' });
|
|
62
|
-
*
|
|
63
|
-
* // Custom 2-column layout
|
|
64
|
-
* return (
|
|
65
|
-
* <form onSubmit={form.handleSubmit(onSubmit)}>
|
|
66
|
-
* <div className="grid grid-cols-2 gap-4">
|
|
67
|
-
* <Input label="Customer" {...form.register('customer')} />
|
|
68
|
-
* <DatePicker label="Date" {...form.register('orderDate')} />
|
|
69
|
-
* </div>
|
|
70
|
-
* <Select
|
|
71
|
-
* label="Status"
|
|
72
|
-
* options={form.fields.find(f => f.name === 'status')?.config.validation?.options}
|
|
73
|
-
* {...form.register('status')}
|
|
74
|
-
* />
|
|
75
|
-
* </form>
|
|
76
|
-
* );
|
|
77
|
-
* }
|
|
78
|
-
* ```
|
|
79
|
-
*
|
|
80
|
-
* @see {@link Entity} for entity definition structure
|
|
81
|
-
* @see {@link EntityFormReturn} for return type
|
|
82
|
-
* @see {@link getFieldsForOperation} for field filtering logic
|
|
83
|
-
*
|
|
84
|
-
* @version 0.0.1
|
|
85
|
-
* @since 0.0.1
|
|
86
|
-
* @author AMBROISE PARK Consulting
|
|
87
19
|
*/
|
|
88
20
|
export declare function useEntityForm<E extends Entity>(entity: E, options?: UseEntityFormOptions<E>): EntityFormReturn<E>;
|
|
89
21
|
//# sourceMappingURL=useEntityForm.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useEntityForm.d.ts","sourceRoot":"","sources":["../../../src/forms/hooks/useEntityForm.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useEntityForm.d.ts","sourceRoot":"","sources":["../../../src/forms/hooks/useEntityForm.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAQ7C,OAAO,KAAK,EACV,oBAAoB,EACpB,gBAAgB,EAEjB,MAAM,UAAU,CAAC;AAElB;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAC5C,MAAM,EAAE,CAAC,EACT,OAAO,GAAE,oBAAoB,CAAC,CAAC,CAAM,GACpC,gBAAgB,CAAC,CAAC,CAAC,CAiarB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{useMemo as
|
|
1
|
+
"use client";import{valibotResolver as B}from"@hookform/resolvers/valibot";import{useMemo as l,useEffect as O,useRef as g,useCallback as w}from"react";import{useForm as ee,useWatch as te}from"react-hook-form";import{useLocalStorage as re,BACKEND_GENERATED_FIELD_NAMES as se}from"@donotdev/core";import{createSchemas as oe}from"@donotdev/schemas";import{useFormStore as V,useFormStatus as ne,useUploadProgress as ae}from"../../stores";import{useUploadStore as L}from"../../stores/UploadStore";import{checkForBlobUrls as x}from"../../utils/uploadValidation";import{getFieldsForOperation as ue}from"../utils";function ve(i,N={}){const{formId:e,operation:_,defaultValues:a,viewerRole:F="admin",mode:C="onBlur",t:J,autoSave:m=!1}=N,n=_??(a?"edit":"create"),K=ne(e??""),W=ae(e??""),b=l(()=>oe(i),[i.name]),P=l(()=>B(b.create),[b.create]),U=l(()=>B(b.draft),[b.draft]),q=l(()=>async(t,o,s)=>((t?.status??a?.status)==="draft"?U:P)(t,o,s),[P,U,a?.status]),p=l(()=>{if(!a)return a;const t={...a};return Object.entries(i.fields).forEach(([o,s])=>{if(s.type==="switch"){const r=t[o];if(r==null){const S=s.options?.fieldSpecific;t[o]=S?.uncheckedValue??!1}}}),t},[a,i.fields]),u=ee({defaultValues:p,mode:C,resolver:q,shouldUnregister:!1,shouldFocusError:!0}),v=g(null);O(()=>{if(n==="edit"&&p){const t=p.id??null;t&&t!==v.current?(u.reset(p),v.current=t):!t&&v.current===null&&(u.reset(p),v.current="initialized")}},[n,p,u]);const G=l(()=>`${i.name.toLowerCase()}-form-draft`,[i.name]),{value:D,setValue:j,removeValue:k}=re(G,{defaultValue:null,syncAcrossTabs:!0}),R=g(!1);O(()=>{!m||n!=="create"||a||R.current||(D&&u.reset(D),R.current=!0)},[m,n,a,D,u]);const c=te({control:u.control}),E=g(!1),h=g(null),y=g(void 0),I=g(!0);O(()=>{if(I.current){I.current=!1,y.current=c?JSON.stringify(c):void 0;return}if(!m||n!=="create"||E.current||!R.current)return;const t=c?JSON.stringify(c):void 0;if(y.current!==t)return y.current=t,h.current&&clearTimeout(h.current),h.current=setTimeout(()=>{if(!c)return;Object.values(c).some(s=>s!=null&&s!=="")&&j(c)},3e3),()=>{h.current&&clearTimeout(h.current)}},[c,m,n,j]);const M=w(async()=>{if(!e)return!0;const t=L.getState(),o=V.getState();if(!t.hasPendingUploads(e))return!0;try{o.setUploading(e,0),await t.uploadAll(e),await new Promise(d=>setTimeout(d,50));let s=u.getValues(),r=x(s),S=0;const f=5;for(;r.length>0&&S<f;)S++,await new Promise(d=>setTimeout(d,100)),s=u.getValues(),r=x(s);if(r.length>0){const d=`Upload incomplete: files still pending at ${r.join(", ")}`;return o.setError(e,d),!1}return!0}catch(s){const r=s instanceof Error?s.message:"File upload failed";return V.getState().setError(e,r),!1}},[e,u]),A=w(t=>n!=="create"?t:Object.fromEntries(Object.entries(t).filter(([o])=>!se.includes(o))),[n]),$=w(t=>{if(typeof window>"u")return;const o=Object.keys(t).find(r=>r!=="root"&&t[r]);if(!o)return;const s=document.querySelector(`input[name="${o}"][aria-invalid="true"], textarea[name="${o}"][aria-invalid="true"], select[name="${o}"][aria-invalid="true"]`);s&&(s.scrollIntoView({behavior:"smooth",block:"center"}),s.focus())},[]),z=u.handleSubmit,H=l(()=>((t,o)=>async s=>{s?.preventDefault?.();const r=e?V.getState():null;e&&r&&r.startSubmit(e),E.current=!0;try{if(!await M()){E.current=!1;return}e&&r&&r.setValidating(e),await z(async(f,d)=>{e&&r&&r.setSubmitting(e);const Y=A(f);try{await t(Y,d),m&&n==="create"&&k(),e&&r&&r.setSuccess(e)}catch(T){const Z=T instanceof Error?T.message:"Submission failed";throw e&&r&&r.setError(e,Z),T}},f=>{e&&r&&r.setError(e,"Validation failed"),$(f),o?.(f)})(s)}finally{E.current=!1}}),[z,e,M,A,$,m,n,k]),Q=w(()=>{e&&(V.getState().cleanup(e),L.getState().cleanup(e))},[e]),X=l(()=>ue(i,{operation:n,viewerRole:F,availableFields:n==="edit"&&a?Object.keys(a):void 0}),[i,n,F,a]);return{...u,handleSubmit:H,fields:X,operation:n,entity:i,t:J??(t=>t),viewerRole:F,formId:e,formStatus:e?K:"idle",uploadProgress:e?W:0,cleanup:Q}}export{ve as useEntityForm};
|
package/dist/forms/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Import from '@donotdev/crud/forms' to access form building blocks:
|
|
6
6
|
* - Hooks: useEntityForm, useEntityField
|
|
7
|
-
* - Utilities: getFieldsForOperation,
|
|
7
|
+
* - Utilities: getFieldsForOperation, validateEntity
|
|
8
8
|
* - Types: InferEntityData, EntityFormReturn, etc.
|
|
9
9
|
*
|
|
10
10
|
* @example
|
|
@@ -31,7 +31,9 @@
|
|
|
31
31
|
*/
|
|
32
32
|
export { useEntityForm } from './hooks/useEntityForm';
|
|
33
33
|
export { useEntityField } from './hooks/useEntityField';
|
|
34
|
-
export {
|
|
35
|
-
export type {
|
|
36
|
-
export
|
|
34
|
+
export { useController } from './hooks/useController';
|
|
35
|
+
export type { UseControllerProps, UseControllerReturn, ControllerRenderProps, ControllerFieldState, } from './hooks/useController';
|
|
36
|
+
export { isFieldEditable, getFieldsForOperation, validateEntity, translateFieldLabel, translateLabel, yearOptions, rangeOptions, } from './utils';
|
|
37
|
+
export type { ViewerRole, RenderableField, GetFieldsForOperationOptions, EntityFieldsInput, SchemaOperation, ValidationIssue, ValidationResult, SelectOption, } from './utils';
|
|
38
|
+
export type { InferEntityData, InferEntityInput, InferEntityOutput, UseEntityFormOptions, EntityFormReturn, EntityFieldReturn, Entity, EntityField, FieldType, Visibility, Editable, ValidationRules, dndevSchema, } from './types';
|
|
37
39
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/forms/index.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/forms/index.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,YAAY,EACV,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,mBAAmB,EACnB,cAAc,EAEd,WAAW,EACX,YAAY,GACb,MAAM,SAAS,CAAC;AAGjB,YAAY,EAEV,UAAU,EACV,eAAe,EACf,4BAA4B,EAC5B,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,YAAY,GACb,MAAM,SAAS,CAAC;AAEjB,YAAY,EAEV,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,EAEjB,MAAM,EACN,WAAW,EACX,SAAS,EACT,UAAU,EACV,QAAQ,EACR,eAAe,EACf,WAAW,GACZ,MAAM,SAAS,CAAC"}
|
package/dist/forms/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{useEntityForm as
|
|
1
|
+
import{useEntityForm as r}from"./hooks/useEntityForm";import{useEntityField as i}from"./hooks/useEntityField";import{useController as l}from"./hooks/useController";import{isFieldEditable as s,getFieldsForOperation as p,validateEntity as d,translateFieldLabel as F,translateLabel as m,yearOptions as f,rangeOptions as x}from"./utils";export{p as getFieldsForOperation,s as isFieldEditable,x as rangeOptions,F as translateFieldLabel,m as translateLabel,l as useController,i as useEntityField,r as useEntityForm,d as validateEntity,f as yearOptions};
|
package/dist/forms/types.d.ts
CHANGED
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
* @since 0.0.1
|
|
7
7
|
* @author AMBROISE PARK Consulting
|
|
8
8
|
*/
|
|
9
|
-
import type {
|
|
10
|
-
import type {
|
|
9
|
+
import type { Entity, EntityField, FieldType, FieldTypeToValue, Visibility, Editable, ValidationRules, dndevSchema } from '@donotdev/core';
|
|
10
|
+
import type { FormStatus } from '../stores/FormStore';
|
|
11
11
|
import type { RenderableField, ViewerRole } from './utils';
|
|
12
|
-
|
|
12
|
+
import type { UseFormReturn, ControllerRenderProps, FieldPath } from 'react-hook-form';
|
|
13
|
+
export type { Entity, EntityField, FieldType, Visibility, Editable, ValidationRules, dndevSchema, RenderableField, ViewerRole, };
|
|
13
14
|
/**
|
|
14
15
|
* Infers the data type from an entity definition.
|
|
15
16
|
*
|
|
@@ -41,7 +42,7 @@ export type { Entity, EntityField, FieldConfig, FieldType, Visibility, Editable,
|
|
|
41
42
|
export type InferEntityData<E extends Entity> = {
|
|
42
43
|
[K in keyof E['fields']]: E['fields'][K] extends {
|
|
43
44
|
type: infer T;
|
|
44
|
-
} ? T extends FieldType ? FieldTypeToValue[T] : unknown : unknown;
|
|
45
|
+
} ? T extends FieldType ? T extends keyof FieldTypeToValue ? FieldTypeToValue[T] : unknown : unknown : unknown;
|
|
45
46
|
};
|
|
46
47
|
/**
|
|
47
48
|
* Infers form input type (values before validation).
|
|
@@ -75,6 +76,12 @@ export type InferEntityOutput<E extends Entity> = InferEntityData<E>;
|
|
|
75
76
|
* @author AMBROISE PARK Consulting
|
|
76
77
|
*/
|
|
77
78
|
export interface UseEntityFormOptions<E extends Entity> {
|
|
79
|
+
/**
|
|
80
|
+
* Form ID for tracking form/upload state.
|
|
81
|
+
* Required for deferred file uploads and form status tracking.
|
|
82
|
+
* Generate with useId() in the parent component.
|
|
83
|
+
*/
|
|
84
|
+
formId?: string;
|
|
78
85
|
/**
|
|
79
86
|
* Form operation type.
|
|
80
87
|
* - 'create': Excludes technical fields, all other fields editable
|
|
@@ -145,6 +152,25 @@ export interface EntityFormReturn<E extends Entity> extends UseFormReturn<InferE
|
|
|
145
152
|
* Current viewer role.
|
|
146
153
|
*/
|
|
147
154
|
viewerRole: ViewerRole;
|
|
155
|
+
/**
|
|
156
|
+
* Form ID for state tracking.
|
|
157
|
+
* Undefined if not provided in options.
|
|
158
|
+
*/
|
|
159
|
+
formId: string | undefined;
|
|
160
|
+
/**
|
|
161
|
+
* Current form status from FormStore.
|
|
162
|
+
* Tracks: idle → uploading → validating → submitting → success/error
|
|
163
|
+
*/
|
|
164
|
+
formStatus: FormStatus;
|
|
165
|
+
/**
|
|
166
|
+
* Upload progress 0-100 when status is 'uploading'.
|
|
167
|
+
*/
|
|
168
|
+
uploadProgress: number;
|
|
169
|
+
/**
|
|
170
|
+
* Cleanup function to call on unmount.
|
|
171
|
+
* Cleans up FormStore and UploadStore state for this form.
|
|
172
|
+
*/
|
|
173
|
+
cleanup: () => void;
|
|
148
174
|
}
|
|
149
175
|
/**
|
|
150
176
|
* Return type for useEntityField hook.
|
|
@@ -172,7 +198,7 @@ export interface EntityFieldReturn<E extends Entity, K extends keyof InferEntity
|
|
|
172
198
|
/**
|
|
173
199
|
* Normalized field configuration.
|
|
174
200
|
*/
|
|
175
|
-
config:
|
|
201
|
+
config: EntityField;
|
|
176
202
|
/**
|
|
177
203
|
* Whether field is editable (based on role, operation, config).
|
|
178
204
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/forms/types.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/forms/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,16 +6,16 @@
|
|
|
6
6
|
* @since 0.0.1
|
|
7
7
|
* @author AMBROISE PARK Consulting
|
|
8
8
|
*/
|
|
9
|
-
import type { Entity, EntityField,
|
|
9
|
+
import type { Entity, EntityField, FieldType } from '@donotdev/core';
|
|
10
10
|
/** Entity fields input - supports Entity, Entity['fields'], or plain Record */
|
|
11
|
-
export type EntityFieldsInput = Entity | Entity['fields'] | Record<string, EntityField<FieldType
|
|
12
|
-
import {
|
|
11
|
+
export type EntityFieldsInput = Entity | Entity['fields'] | Record<string, EntityField<FieldType>>;
|
|
12
|
+
import type { ViewerRole } from './isFieldEditable';
|
|
13
13
|
/** Field with computed editability for rendering */
|
|
14
14
|
export interface RenderableField<T extends FieldType = FieldType> {
|
|
15
15
|
/** Field name/key */
|
|
16
16
|
name: string;
|
|
17
|
-
/**
|
|
18
|
-
config:
|
|
17
|
+
/** Field configuration */
|
|
18
|
+
config: EntityField<T>;
|
|
19
19
|
/** Whether field is editable (input vs read-only) */
|
|
20
20
|
editable: boolean;
|
|
21
21
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getFieldsForOperation.d.ts","sourceRoot":"","sources":["../../../src/forms/utils/getFieldsForOperation.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"getFieldsForOperation.d.ts","sourceRoot":"","sources":["../../../src/forms/utils/getFieldsForOperation.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAErE,+EAA+E;AAC/E,MAAM,MAAM,iBAAiB,GACzB,MAAM,GACN,MAAM,CAAC,QAAQ,CAAC,GAChB,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC;AAI3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,oDAAoD;AACpD,MAAM,WAAW,eAAe,CAAC,CAAC,SAAS,SAAS,GAAG,SAAS;IAC9D,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,0BAA0B;IAC1B,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IACvB,qDAAqD;IACrD,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,kCAAkC;AAClC,MAAM,WAAW,4BAA4B;IAC3C,0BAA0B;IAC1B,SAAS,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC7B,yCAAyC;IACzC,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,4BAA4B,GACpC,eAAe,EAAE,CAoDnB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{isFieldEditable as b}from"./isFieldEditable";function p(e,c){const{operation:l,viewerRole:f="admin",availableFields:o}=c,a=e&&typeof e=="object"&&"fields"in e&&"name"in e?e.fields:e,r=Object.entries(a),t=[];for(const[s,u]of r){const i=u,n=i.visibility||"user";if(l==="create"){if(n==="technical"||n==="hidden")continue}else if(n==="hidden"||o&&!o.includes(s))continue;let d=b(i.editable,f,l);l==="edit"&&n==="technical"&&i.editable===void 0&&(d=!1),t.push({name:s,config:i,editable:d})}return t}export{p as getFieldsForOperation};
|
|
@@ -6,9 +6,13 @@
|
|
|
6
6
|
* @since 0.0.1
|
|
7
7
|
* @author AMBROISE PARK Consulting
|
|
8
8
|
*/
|
|
9
|
-
export { isFieldEditable
|
|
10
|
-
export {
|
|
11
|
-
export { getFieldsForOperation
|
|
12
|
-
export {
|
|
13
|
-
export { validateEntity
|
|
9
|
+
export { isFieldEditable } from './isFieldEditable';
|
|
10
|
+
export type { ViewerRole } from './isFieldEditable';
|
|
11
|
+
export { getFieldsForOperation } from './getFieldsForOperation';
|
|
12
|
+
export type { RenderableField, GetFieldsForOperationOptions, EntityFieldsInput, } from './getFieldsForOperation';
|
|
13
|
+
export { validateEntity } from './validateEntity';
|
|
14
|
+
export type { ValidationIssue, ValidationResult, SchemaOperation, } from './validateEntity';
|
|
15
|
+
export { translateFieldLabel, translateLabel } from './translateFieldLabel';
|
|
16
|
+
export { yearOptions, rangeOptions } from './optionHelpers';
|
|
17
|
+
export type { SelectOption } from './optionHelpers';
|
|
14
18
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/forms/utils/index.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,OAAO,EAAE,eAAe,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/forms/utils/index.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,YAAY,EACV,eAAe,EACf,4BAA4B,EAC5B,iBAAiB,GAClB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC5D,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isFieldEditable as r}from"./isFieldEditable";import{
|
|
1
|
+
import{isFieldEditable as r}from"./isFieldEditable";import{getFieldsForOperation as a}from"./getFieldsForOperation";import{validateEntity as l}from"./validateEntity";import{translateFieldLabel as n,translateLabel as s}from"./translateFieldLabel";import{yearOptions as f,rangeOptions as m}from"./optionHelpers";export{a as getFieldsForOperation,r as isFieldEditable,m as rangeOptions,n as translateFieldLabel,s as translateLabel,l as validateEntity,f as yearOptions};
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Field editability utility
|
|
3
|
-
* @description Determines if a field is editable based on editable config, viewer role, and operation.
|
|
4
|
-
*
|
|
5
|
-
* @version 0.0.1
|
|
6
|
-
* @since 0.0.1
|
|
7
|
-
* @author AMBROISE PARK Consulting
|
|
8
|
-
*/
|
|
9
1
|
import type { Editable } from '@donotdev/core';
|
|
10
2
|
/** Viewer role type - used for editability checks */
|
|
11
3
|
export type ViewerRole = 'public' | 'guest' | 'user' | 'admin' | 'super';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"isFieldEditable.d.ts","sourceRoot":"","sources":["../../../src/forms/utils/isFieldEditable.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"isFieldEditable.d.ts","sourceRoot":"","sources":["../../../src/forms/utils/isFieldEditable.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,qDAAqD;AACrD,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAEzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAC9B,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,QAAQ,GAAG,MAAM,GAC3B,OAAO,CAcT"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Option generation helpers for select fields
|
|
3
|
+
* @description Simple utilities to generate options arrays for select/dropdown fields.
|
|
4
|
+
*
|
|
5
|
+
* @version 0.0.1
|
|
6
|
+
* @since 0.0.1
|
|
7
|
+
* @author AMBROISE PARK Consulting
|
|
8
|
+
*/
|
|
9
|
+
/** Standard option format for select fields */
|
|
10
|
+
export interface SelectOption {
|
|
11
|
+
value: string;
|
|
12
|
+
label: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Generates year options for a select field.
|
|
16
|
+
*
|
|
17
|
+
* @param startYear - First year in the range
|
|
18
|
+
* @param endYear - Last year in the range (defaults to current year)
|
|
19
|
+
* @param descending - Whether to sort descending (newest first, default: true)
|
|
20
|
+
* @returns Array of options with value and label
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* // In entity definition
|
|
25
|
+
* year: {
|
|
26
|
+
* type: 'select',
|
|
27
|
+
* validation: {
|
|
28
|
+
* required: true,
|
|
29
|
+
* options: yearOptions(1995) // 2026, 2025, 2024, ... 1995
|
|
30
|
+
* }
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare function yearOptions(startYear: number, endYear?: number, descending?: boolean): SelectOption[];
|
|
35
|
+
/**
|
|
36
|
+
* Generates numeric range options for a select field.
|
|
37
|
+
*
|
|
38
|
+
* @param start - First number in the range
|
|
39
|
+
* @param end - Last number in the range
|
|
40
|
+
* @param step - Increment between numbers (default: 1)
|
|
41
|
+
* @param descending - Whether to sort descending (default: false)
|
|
42
|
+
* @returns Array of options with value and label
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* // Quantities 1-10
|
|
47
|
+
* options: rangeOptions(1, 10)
|
|
48
|
+
*
|
|
49
|
+
* // Percentages 0-100 by 10
|
|
50
|
+
* options: rangeOptions(0, 100, 10)
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare function rangeOptions(start: number, end: number, step?: number, descending?: boolean): SelectOption[];
|
|
54
|
+
//# sourceMappingURL=optionHelpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"optionHelpers.d.ts","sourceRoot":"","sources":["../../../src/forms/utils/optionHelpers.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,+CAA+C;AAC/C,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,MAAiC,EAC1C,UAAU,GAAE,OAAc,GACzB,YAAY,EAAE,CAchB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,MAAU,EAChB,UAAU,GAAE,OAAe,GAC1B,YAAY,EAAE,CAchB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function o(n,t=new Date().getFullYear(),i=!0){const r=[];if(i)for(let e=t;e>=n;e--)r.push({value:String(e),label:String(e)});else for(let e=n;e<=t;e++)r.push({value:String(e),label:String(e)});return r}function u(n,t,i=1,r=!1){const e=[];if(r)for(let l=t;l>=n;l-=i)e.push({value:String(l),label:String(l)});else for(let l=n;l<=t;l+=i)e.push({value:String(l),label:String(l)});return e}export{u as rangeOptions,o as yearOptions};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Field label translation utility
|
|
3
|
+
* @description Centralized helper for translating field labels with namespace support.
|
|
4
|
+
* Handles namespace:key syntax, config.label fallback, and fields.${fieldName} pattern.
|
|
5
|
+
*
|
|
6
|
+
* @version 0.0.1
|
|
7
|
+
* @since 0.0.1
|
|
8
|
+
* @author AMBROISE PARK Consulting
|
|
9
|
+
*/
|
|
10
|
+
import type { EntityField, FieldType } from '@donotdev/core';
|
|
11
|
+
/**
|
|
12
|
+
* Translates a label string with namespace support.
|
|
13
|
+
* Used for translating option labels, field labels, or any label string.
|
|
14
|
+
*
|
|
15
|
+
* Supports namespace:key syntax (e.g., "common:actions.save")
|
|
16
|
+
*
|
|
17
|
+
* @param label - Label string to translate (may include namespace:key syntax)
|
|
18
|
+
* @param t - Translation function from useTranslation hook
|
|
19
|
+
* @returns Translated label string
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* translateLabel('common:status', t); // Translates "status" from "common" namespace
|
|
24
|
+
* translateLabel('make', t); // Translates "make" in current namespace
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @version 0.0.1
|
|
28
|
+
* @since 0.0.1
|
|
29
|
+
* @author AMBROISE PARK Consulting
|
|
30
|
+
*/
|
|
31
|
+
export declare function translateLabel(label: string, t: (key: string, options?: Record<string, any>) => string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Translates a field label using the provided translation function.
|
|
34
|
+
*
|
|
35
|
+
* Supports multiple label formats:
|
|
36
|
+
* - Namespace syntax: `"namespace:key"` → translates `key` in `namespace`
|
|
37
|
+
* - Direct label: `"label"` → translates `label` in current namespace
|
|
38
|
+
* - Field name fallback: uses field name if no label in config
|
|
39
|
+
*
|
|
40
|
+
* @param fieldName - Field name (used as fallback)
|
|
41
|
+
* @param config - Field configuration (may contain label)
|
|
42
|
+
* @param t - Translation function from useTranslation hook
|
|
43
|
+
* @returns Translated label string
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* import { translateFieldLabel } from '@donotdev/crud/forms';
|
|
48
|
+
* import { useTranslation } from '@donotdev/core';
|
|
49
|
+
*
|
|
50
|
+
* const { t } = useTranslation('entity-car');
|
|
51
|
+
* const label = translateFieldLabel('make', carEntity.fields.make, t);
|
|
52
|
+
* // Returns: translated "make" from entity-car namespace
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* // With namespace syntax in config.label
|
|
58
|
+
* const label = translateFieldLabel('status', {
|
|
59
|
+
* label: 'common:status',
|
|
60
|
+
* type: 'select'
|
|
61
|
+
* }, t);
|
|
62
|
+
* // Returns: translated "status" from "common" namespace
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* @version 0.0.1
|
|
66
|
+
* @since 0.0.1
|
|
67
|
+
* @author AMBROISE PARK Consulting
|
|
68
|
+
*/
|
|
69
|
+
export declare function translateFieldLabel<T extends FieldType = FieldType>(fieldName: string, config: EntityField<T> | undefined, t: (key: string, options?: Record<string, any>) => string): string;
|
|
70
|
+
//# sourceMappingURL=translateFieldLabel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translateFieldLabel.d.ts","sourceRoot":"","sources":["../../../src/forms/utils/translateFieldLabel.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE7D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,MAAM,GACxD,MAAM,CAWR;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,SAAS,GAAG,SAAS,EACjE,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,EAClC,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,MAAM,GACxD,MAAM,CAGR"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function o(t,n){if(typeof t=="string"&&t.includes(":")){const e=t.split(":"),r=e[1]??t,s=e[0];return n(r,{ns:s})}return n(t)}function c(t,n,e){const r=n?.label||t;return o(r,e)}export{c as translateFieldLabel,o as translateLabel};
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { Entity } from '@donotdev/core';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Validation operation type
|
|
4
|
+
*/
|
|
5
|
+
export type SchemaOperation = 'create' | 'draft' | 'update' | 'full';
|
|
3
6
|
/**
|
|
4
7
|
* Validation issue structure
|
|
5
8
|
*/
|
|
@@ -30,7 +33,7 @@ export interface ValidationResult<T> {
|
|
|
30
33
|
* @template E - Entity type from defineEntity()
|
|
31
34
|
* @param entity - Entity definition from defineEntity()
|
|
32
35
|
* @param data - Data to validate
|
|
33
|
-
* @param operation - Validation context ('create'
|
|
36
|
+
* @param operation - Validation context ('create', 'draft', 'update', or 'full')
|
|
34
37
|
* @returns Validation result with typed issues
|
|
35
38
|
*
|
|
36
39
|
* @example
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validateEntity.d.ts","sourceRoot":"","sources":["../../../src/forms/utils/validateEntity.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"validateEntity.d.ts","sourceRoot":"","sources":["../../../src/forms/utils/validateEntity.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAG7C;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,mCAAmC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,kCAAkC;IAClC,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,oCAAoC;IACpC,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,EAC7C,MAAM,EAAE,CAAC,EACT,IAAI,EAAE,OAAO,EACb,SAAS,GAAE,eAA0B,GACpC,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAgC3C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as o from"valibot";import{
|
|
1
|
+
import*as o from"valibot";import{createSchemas as p}from"@donotdev/schemas";function h(a,c,r="create"){const s=p(a),u={create:s.create,draft:s.draft,update:s.update,full:s.get}[r],e=o.safeParse(u,c);return e.success?{success:!0,data:e.output}:{success:!1,issues:e.issues.map(t=>({path:t.path?.map(m=>m.key).join(".")||"",message:t.message}))}}export{h as validateEntity};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{useFileUpload as l}from"./useFileUpload";export{l as useFileUpload};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { Picture } from '@donotdev/types';
|
|
2
|
+
/** Metadata for a file in the upload queue */
|
|
3
|
+
export interface FileMetadata {
|
|
4
|
+
/** Unique ID for React keys */
|
|
5
|
+
id: string;
|
|
6
|
+
/** Original file object (for new uploads) */
|
|
7
|
+
file: File;
|
|
8
|
+
/** Local preview URL (blob: for new, https: for existing) */
|
|
9
|
+
previewURL: string;
|
|
10
|
+
/** Upload progress 0-100 (null = not started) */
|
|
11
|
+
progress: number | null;
|
|
12
|
+
/** Uploaded result (null = pending) */
|
|
13
|
+
uploaded: Picture | null;
|
|
14
|
+
/** Error message if upload failed */
|
|
15
|
+
error: string | null;
|
|
16
|
+
}
|
|
17
|
+
export interface UseFileUploadOptions {
|
|
18
|
+
/** Field name for UploadStore registration */
|
|
19
|
+
name: string;
|
|
20
|
+
/** Current value from form */
|
|
21
|
+
value: Picture | Picture[] | null | undefined;
|
|
22
|
+
/** Change handler to update form */
|
|
23
|
+
onChange: (value: Picture | Picture[] | null) => void;
|
|
24
|
+
/** Allow multiple files */
|
|
25
|
+
multiple?: boolean;
|
|
26
|
+
/** Max number of files (multiple mode) */
|
|
27
|
+
maxFiles?: number;
|
|
28
|
+
/** Max file size in bytes (default: 10MB) */
|
|
29
|
+
maxSize?: number;
|
|
30
|
+
/** Accepted MIME types (e.g., ['image/*'], ['application/pdf']) */
|
|
31
|
+
accept?: string[];
|
|
32
|
+
/** Storage path prefix */
|
|
33
|
+
storagePath?: string;
|
|
34
|
+
/** Upload function - injected to avoid coupling to specific storage */
|
|
35
|
+
uploadFn?: (file: File, onProgress: (progress: number) => void) => Promise<Picture>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* useFileUpload - Unified hook for file uploads
|
|
39
|
+
*
|
|
40
|
+
* Features:
|
|
41
|
+
* - Local preview before upload
|
|
42
|
+
* - Deferred upload on form submit (when inside EntityFormRenderer)
|
|
43
|
+
* - Immediate upload when standalone (no UploadProvider context)
|
|
44
|
+
* - UploadStore integration
|
|
45
|
+
* - File validation
|
|
46
|
+
* - Progress tracking
|
|
47
|
+
* - Works with Image, File, Document field types
|
|
48
|
+
*/
|
|
49
|
+
export declare function useFileUpload({ name, value, onChange, multiple, maxFiles, maxSize, accept, storagePath, uploadFn, }: UseFileUploadOptions): {
|
|
50
|
+
/** Current files (pending + uploaded) */
|
|
51
|
+
files: FileMetadata[];
|
|
52
|
+
/** Add new files (from drop/paste/input) */
|
|
53
|
+
addFiles: (newFiles: File[]) => void;
|
|
54
|
+
/** Remove a file by ID */
|
|
55
|
+
removeFile: (id: string) => void;
|
|
56
|
+
/** Whether any files are pending upload */
|
|
57
|
+
hasPending: boolean;
|
|
58
|
+
/** Whether upload is in progress */
|
|
59
|
+
isUploading: boolean;
|
|
60
|
+
/** Overall progress 0-100 */
|
|
61
|
+
progress: number;
|
|
62
|
+
/** Form ID from context (undefined if standalone) */
|
|
63
|
+
formId: string | undefined;
|
|
64
|
+
/** Whether uploads are deferred (true inside EntityFormRenderer, false standalone) */
|
|
65
|
+
isDeferred: boolean;
|
|
66
|
+
};
|
|
67
|
+
//# sourceMappingURL=useFileUpload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFileUpload.d.ts","sourceRoot":"","sources":["../../src/hooks/useFileUpload.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAK/C,8CAA8C;AAC9C,MAAM,WAAW,YAAY;IAC3B,+BAA+B;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,6CAA6C;IAC7C,IAAI,EAAE,IAAI,CAAC;IACX,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,uCAAuC;IACvC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAC;IACzB,qCAAqC;IACrC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,KAAK,EAAE,OAAO,GAAG,OAAO,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC;IAC9C,oCAAoC;IACpC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,EAAE,GAAG,IAAI,KAAK,IAAI,CAAC;IACtD,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,0BAA0B;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACrF;AA6DD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,QAAgB,EAChB,QAAa,EACb,OAA0B,EAC1B,MAAW,EACX,WAAuB,EACvB,QAAQ,GACT,EAAE,oBAAoB;IA4YnB,yCAAyC;;IAEzC,4CAA4C;yBArGjC,IAAI,EAAE;IAuGjB,0BAA0B;qBA9BrB,MAAM;IAgCX,2CAA2C;;IAE3C,oCAAoC;;IAEpC,6BAA6B;;IAE7B,qDAAqD;;IAErD,sFAAsF;;EAGzF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{useState as G,useCallback as R,useEffect as v,useRef as A,startTransition as H}from"react";import{handleError as j}from"@donotdev/core";import{useUploadContext as K}from"../contexts/UploadContext";import{useUploadStore as k}from"../stores/UploadStore";let B=0;function M(l){if(l){const d=l.split("/").pop()?.split("?")[0];if(d)return`file-${d}`}B+=1;const u=typeof performance<"u"?performance.now():Date.now(),g=Math.random().toString(36).slice(2,11);return`file-${B}-${u}-${g}`}function Q(l){return URL.createObjectURL(l)}function V(l){l.startsWith("blob:")&&URL.revokeObjectURL(l)}function X(l,u,g){return u.length>0&&!u.some(m=>{if(m.endsWith("/*")){const L=m.slice(0,-2);return l.type.startsWith(L)}return l.type===m})?{valid:!1,error:`File type ${l.type} not accepted`}:l.size>g?{valid:!1,error:`File exceeds ${(g/1048576).toFixed(0)}MB limit`}:{valid:!0}}function er({name:l,value:u,onChange:g,multiple:d=!1,maxFiles:m=10,maxSize:L=10*1024*1024,accept:x=[],storagePath:Y="uploads",uploadFn:U}){const h=K(),w=!!h,[f,c]=G([]),E=A(!1),P=A(null),F=A(u),$=A([]);v(()=>{if(E.current){E.current=!1;return}const e=u?JSON.stringify((Array.isArray(u)?u:[u]).map(s=>s.fullUrl).filter(Boolean).sort()):null;e!==P.current&&(P.current=e,F.current=u,H(()=>{if(u){const n=(Array.isArray(u)?u:[u]).filter(t=>t.fullUrl).map((t,r)=>({id:`${M(t.fullUrl)}-existing-${r}`,file:new File([],"existing"),previewURL:t.thumbUrl||t.fullUrl,progress:100,uploaded:t,error:null}));c(n)}else c([])}))},[u]),v(()=>{$.current=f},[f]),v(()=>()=>{$.current.forEach(e=>V(e.previewURL))},[]);const p=R((e,s=!1)=>{const t=(s?e.filter(i=>!i.error):e.filter(i=>i.uploaded&&!i.error)).map(i=>i.uploaded?i.uploaded:s&&w?{fullUrl:i.previewURL,thumbUrl:i.previewURL}:null).filter(i=>i!==null);let r;d?r=t.length>0?t:null:r=t[0]||null;const o=F.current;(()=>{if(!r&&!o)return!1;if(!r||!o)return!0;if(d&&Array.isArray(r)&&Array.isArray(o)){if(r.length!==o.length)return!0;const i=r.map(b=>b.fullUrl).sort().join(","),y=o.map(b=>b.fullUrl).sort().join(",");return i!==y}return!d&&!Array.isArray(r)&&!Array.isArray(o)?r.fullUrl!==o.fullUrl:!0})()&&(E.current=!0,F.current=r,g(r))},[d,g,w]),S=R(async()=>{if(!U)return;let e=[];if(c(n=>(e=n.filter(t=>!t.uploaded&&!t.error),e.length===0?n:n.map(t=>e.some(r=>r.id===t.id)?{...t,progress:0}:t))),e.length===0)return;const s=await Promise.allSettled(e.map(async n=>{try{const t=await U(n.file,r=>{c(o=>o.map(a=>a.id===n.id?{...a,progress:r}:a))});return{id:n.id,picture:t,error:null}}catch(t){const r=t instanceof Error?t.message:"Upload failed";return{id:n.id,picture:null,error:r}}}));c(n=>{const t=n.map(r=>{const o=s.find(a=>a.status==="fulfilled"&&a.value.id===r.id);if(o&&o.status==="fulfilled"){const a=o.value;return{...r,uploaded:a.picture,progress:a.picture?100:null,error:a.error}}return r});return p(t,!1),t})},[U,p]),C=A(S);C.current=S;const I=k(e=>e.registerUpload),N=k(e=>e.unregisterUpload);v(()=>{if(!(!h||!l))return I(h,l,async()=>{await C.current()},()=>$.current.some(e=>!e.uploaded&&!e.error)),()=>{N(h,l)}},[h,l,I,N]);const T=R(async e=>{if(U){c(s=>s.map(n=>n.id===e.id?{...n,progress:0}:n));try{const s=await U(e.file,n=>{c(t=>t.map(r=>r.id===e.id?{...r,progress:n}:r))});c(n=>{const t=n.map(r=>r.id===e.id?{...r,uploaded:s,progress:100,error:null}:r);return p(t,!1),t})}catch(s){const n=s instanceof Error?s.message:"Upload failed";c(t=>t.map(r=>r.id===e.id?{...r,error:n,progress:null}:r))}}},[U,p]),D=R(e=>{c(s=>{const t=(d?m:1)-s.length;if(t<=0)return j(new Error("Maximum files reached"),{userMessage:d?`Maximum ${m} files allowed`:"Only one file allowed",severity:"warning",showNotification:!0}),s;const r=e.slice(0,t),o=[];for(const i of r){const y=X(i,x,L);if(!y.valid){j(new Error(y.error),{userMessage:y.error,severity:"warning",showNotification:!0});continue}o.push({id:M(),file:i,previewURL:Q(i),progress:null,uploaded:null,error:null})}if(o.length===0)return s;const a=d?[...s,...o]:o;return w?setTimeout(()=>{p(a,!0)},0):o.forEach(i=>T(i)),a})},[d,m,x,L,w,T,p]),O=R(e=>{c(s=>{const n=s.find(r=>r.id===e);n&&V(n.previewURL);const t=s.filter(r=>r.id!==e);return p(t,!1),t})},[p]),W=f.some(e=>!e.uploaded&&!e.error),J=f.some(e=>e.progress!==null&&e.progress<100),q=f.length>0?f.reduce((e,s)=>e+(s.progress??0),0)/f.length:0;return{files:f,addFiles:D,removeFile:O,hasPending:W,isUploading:J,progress:q,formId:h,isDeferred:w}}export{er as useFileUpload};
|