@mantis-core/ui 0.1.3 → 0.2.0
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/COMPONENTS.md +15 -142
- package/PRIMEREACT_MAPPING.md +6 -0
- package/README.md +56 -72
- package/dist/components/Forms/AsyncSelect.d.ts +20 -0
- package/dist/components/Forms/AsyncSelect.d.ts.map +1 -0
- package/dist/components/Forms/AsyncSelect.js +77 -0
- package/dist/components/Forms/AsyncSelect.js.map +1 -0
- package/dist/components/Forms/DirtyFormGuard.d.ts +5 -0
- package/dist/components/Forms/DirtyFormGuard.d.ts.map +1 -0
- package/dist/components/Forms/DirtyFormGuard.js +8 -0
- package/dist/components/Forms/DirtyFormGuard.js.map +1 -0
- package/dist/components/Forms/FileDropzone.d.ts +29 -0
- package/dist/components/Forms/FileDropzone.d.ts.map +1 -0
- package/dist/components/Forms/FileDropzone.js +137 -0
- package/dist/components/Forms/FileDropzone.js.map +1 -0
- package/dist/components/Forms/FormActionsBar.d.ts +26 -0
- package/dist/components/Forms/FormActionsBar.d.ts.map +1 -0
- package/dist/components/Forms/FormActionsBar.js +16 -0
- package/dist/components/Forms/FormActionsBar.js.map +1 -0
- package/dist/components/Forms/useDirtyFormGuard.d.ts +12 -0
- package/dist/components/Forms/useDirtyFormGuard.d.ts.map +1 -0
- package/dist/components/Forms/useDirtyFormGuard.js +36 -0
- package/dist/components/Forms/useDirtyFormGuard.js.map +1 -0
- package/dist/components/ui/ConfirmDialog.d.ts +19 -0
- package/dist/components/ui/ConfirmDialog.d.ts.map +1 -0
- package/dist/components/ui/ConfirmDialog.js +20 -0
- package/dist/components/ui/ConfirmDialog.js.map +1 -0
- package/dist/components/ui/DataState.d.ts +20 -0
- package/dist/components/ui/DataState.d.ts.map +1 -0
- package/dist/components/ui/DataState.js +30 -0
- package/dist/components/ui/DataState.js.map +1 -0
- package/dist/components/ui/StatusBadge.d.ts +12 -0
- package/dist/components/ui/StatusBadge.d.ts.map +1 -0
- package/dist/components/ui/StatusBadge.js +19 -0
- package/dist/components/ui/StatusBadge.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/docs/README.md +72 -0
- package/docs/components/action-column.md +17 -0
- package/docs/components/admin-page-header.md +21 -0
- package/docs/components/admin-page-layout.md +29 -0
- package/docs/components/async-select.md +26 -0
- package/docs/components/button.md +25 -0
- package/docs/components/ckeditor-input.md +24 -0
- package/docs/components/confirm-dialog.md +21 -0
- package/docs/components/data-state.md +19 -0
- package/docs/components/date-input.md +22 -0
- package/docs/components/dirty-form-guard.md +31 -0
- package/docs/components/drop-down.md +28 -0
- package/docs/components/error-message.md +13 -0
- package/docs/components/file-dropzone.md +25 -0
- package/docs/components/filter-pannel.md +23 -0
- package/docs/components/form-actions-bar.md +22 -0
- package/docs/components/form-field-render.md +20 -0
- package/docs/components/form-section.md +15 -0
- package/docs/components/hidden-field.md +19 -0
- package/docs/components/index-column.md +13 -0
- package/docs/components/loading.md +21 -0
- package/docs/components/map-picker-client.md +22 -0
- package/docs/components/map-picker-osm.md +18 -0
- package/docs/components/map-picker.md +21 -0
- package/docs/components/menu-button.md +22 -0
- package/docs/components/multi-select-filter.md +20 -0
- package/docs/components/multi-select.md +23 -0
- package/docs/components/number-input.md +20 -0
- package/docs/components/page-title.md +13 -0
- package/docs/components/paginated-table.md +35 -0
- package/docs/components/pagination.md +27 -0
- package/docs/components/render-field.md +24 -0
- package/docs/components/rich-text-preview.md +18 -0
- package/docs/components/select.md +28 -0
- package/docs/components/status-badge.md +14 -0
- package/docs/components/switch.md +27 -0
- package/docs/components/table-filter-field.md +21 -0
- package/docs/components/table-toolbar.md +19 -0
- package/docs/components/text-area.md +20 -0
- package/docs/components/text-input.md +23 -0
- package/docs/components/time-picker.md +22 -0
- package/docs/components/toggle-inline-button.md +22 -0
- package/docs/components/toggle.md +21 -0
- package/docs/components/upload-files-preview.md +26 -0
- package/docs/contracts/controller-props.md +28 -0
- package/docs/contracts/dependency-map.md +57 -0
- package/docs/contracts/style-imports.md +27 -0
- package/package.json +6 -4
package/COMPONENTS.md
CHANGED
|
@@ -1,147 +1,20 @@
|
|
|
1
|
-
# @mantis-core/ui Component
|
|
1
|
+
# @mantis-core/ui Component Index
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The detailed package documentation is now split into focused files under [`docs/`](./docs/README.md).
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Start Here
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- [Documentation index](./docs/README.md)
|
|
8
|
+
- [Shared contracts](./docs/contracts/controller-props.md)
|
|
9
|
+
- [Underlying library map](./docs/contracts/dependency-map.md)
|
|
10
|
+
- [Style import guide](./docs/contracts/style-imports.md)
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
- Generic action button with variant support.
|
|
11
|
-
- Use for reusable actions, not domain-specific behavior.
|
|
12
|
+
## Key component docs
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
- `CKEditorInput`
|
|
21
|
-
- Rich text editor input built around CKEditor.
|
|
22
|
-
- Includes content metrics type export.
|
|
23
|
-
- `DateInput`
|
|
24
|
-
- Date picker field.
|
|
25
|
-
- `DropDown`
|
|
26
|
-
- PrimeReact dropdown wrapper.
|
|
27
|
-
- `ErrorMessage`
|
|
28
|
-
- Standardized field error rendering.
|
|
29
|
-
- `FormFieldRender`
|
|
30
|
-
- Label + render prop + error wrapper for fields.
|
|
31
|
-
- `HiddenField`
|
|
32
|
-
- Hidden input field support.
|
|
33
|
-
- `MultiSelect`
|
|
34
|
-
- Multi-select control wrapper.
|
|
35
|
-
- `NumberInput`
|
|
36
|
-
- Numeric input control.
|
|
37
|
-
- `RenderField`
|
|
38
|
-
- Field renderer utility.
|
|
39
|
-
- `Select`
|
|
40
|
-
- Select component wrapper.
|
|
41
|
-
- `Switch`
|
|
42
|
-
- Boolean switch input.
|
|
43
|
-
- `TextArea`
|
|
44
|
-
- Multi-line text input.
|
|
45
|
-
- `TextInput`
|
|
46
|
-
- Standard text input.
|
|
47
|
-
- `TimePicker`
|
|
48
|
-
- Time selection input.
|
|
49
|
-
- `Toggle`
|
|
50
|
-
- Toggle control.
|
|
51
|
-
- `ToggleInlineButton`
|
|
52
|
-
- Inline toggle button.
|
|
53
|
-
- `UploadFilesPreview`
|
|
54
|
-
- Upload preview component with item/copy types.
|
|
55
|
-
- `ControllerProps` (type)
|
|
56
|
-
- Shared control typing contract.
|
|
57
|
-
|
|
58
|
-
## Tables
|
|
59
|
-
|
|
60
|
-
- `TableToolbar`
|
|
61
|
-
- Header area for title, meta, and actions.
|
|
62
|
-
- `TableFilterField`
|
|
63
|
-
- Wrapper for consistent table filter controls.
|
|
64
|
-
- `ActionColumn`
|
|
65
|
-
- Action menu column helper.
|
|
66
|
-
- `FilterPannel`
|
|
67
|
-
- Filter container panel.
|
|
68
|
-
- `MultiSelectFilter`
|
|
69
|
-
- Multi-select filter utility for tables.
|
|
70
|
-
- `IndexColumn`
|
|
71
|
-
- Index/ordinal column helper.
|
|
72
|
-
- `MenuButton`
|
|
73
|
-
- Row action menu button.
|
|
74
|
-
- `PaginatedTable`
|
|
75
|
-
- PrimeReact `DataTable` wrapper with Mantis Core defaults.
|
|
76
|
-
- Key props:
|
|
77
|
-
- `showIndexColumn`
|
|
78
|
-
- `showActionColumn`
|
|
79
|
-
- `useMantisCoreStyles`
|
|
80
|
-
- `actionColumnItems`
|
|
81
|
-
|
|
82
|
-
## Page and Admin Layout
|
|
83
|
-
|
|
84
|
-
- `FormSection`
|
|
85
|
-
- Structured form section block.
|
|
86
|
-
- `PageTitle`
|
|
87
|
-
- Page heading helper.
|
|
88
|
-
- `AdminPageHeader`
|
|
89
|
-
- Page header presentation component.
|
|
90
|
-
- `AdminPageConfigurator`
|
|
91
|
-
- Configures admin page layout context.
|
|
92
|
-
- `AdminPageLayoutProvider`
|
|
93
|
-
- Context provider for admin layout.
|
|
94
|
-
- `useAdminPageLayout`
|
|
95
|
-
- Hook to read/update admin layout state.
|
|
96
|
-
- `AdminPageLayoutShell`
|
|
97
|
-
- Shared admin page shell renderer.
|
|
98
|
-
- `AdminBreadcrumbItem`, `AdminPageLayoutConfig` (types)
|
|
99
|
-
- Layout contract types.
|
|
100
|
-
|
|
101
|
-
## Generic UI
|
|
102
|
-
|
|
103
|
-
- `Pagination`
|
|
104
|
-
- Page navigation component.
|
|
105
|
-
- `PaginationInfo`
|
|
106
|
-
- Current page range summary.
|
|
107
|
-
- `PageSizeSelector`
|
|
108
|
-
- Page size control.
|
|
109
|
-
- `RichTextPreview`
|
|
110
|
-
- Sanitized rich-text display helper.
|
|
111
|
-
|
|
112
|
-
## Maps
|
|
113
|
-
|
|
114
|
-
- `MapPicker`
|
|
115
|
-
- Dynamic import wrapper for client map rendering.
|
|
116
|
-
- `MapPickerClient`
|
|
117
|
-
- Leaflet/react-leaflet map picker implementation.
|
|
118
|
-
- `MapPickerOSM`
|
|
119
|
-
- OpenStreetMap picker variant.
|
|
120
|
-
- `LatLng`, `MapPicker*Props` (types)
|
|
121
|
-
- Map contracts.
|
|
122
|
-
|
|
123
|
-
## Runtime Notes
|
|
124
|
-
|
|
125
|
-
1. Many components are client-side (`"use client"`).
|
|
126
|
-
2. Map components depend on Leaflet assets and browser APIs.
|
|
127
|
-
3. Table and paginator visuals depend on `@mantis-core/styles` classes.
|
|
128
|
-
|
|
129
|
-
## Integration Rules
|
|
130
|
-
|
|
131
|
-
1. Keep domain logic outside this package.
|
|
132
|
-
2. Keep text/content generic when possible.
|
|
133
|
-
3. If a component cannot be generic, keep it in app layer instead.
|
|
134
|
-
4. Any new export must be added to:
|
|
135
|
-
- `src/index.ts`
|
|
136
|
-
- this file (`COMPONENTS.md`)
|
|
137
|
-
- package README if category-level behavior changes.
|
|
138
|
-
|
|
139
|
-
## Suggested Validation For New Components
|
|
140
|
-
|
|
141
|
-
```bash
|
|
142
|
-
pnpm --filter @mantis-core/ui build
|
|
143
|
-
pnpm --filter @mantis-core/ui typecheck
|
|
144
|
-
pnpm lint
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
Also validate in at least one consuming app route.
|
|
14
|
+
- [Button](./docs/components/button.md)
|
|
15
|
+
- [TextInput](./docs/components/text-input.md)
|
|
16
|
+
- [Select](./docs/components/select.md)
|
|
17
|
+
- [PaginatedTable](./docs/components/paginated-table.md)
|
|
18
|
+
- [ConfirmDialog](./docs/components/confirm-dialog.md)
|
|
19
|
+
- [StatusBadge](./docs/components/status-badge.md)
|
|
20
|
+
- [MapPicker](./docs/components/map-picker.md)
|
package/README.md
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
# @mantis-core/ui
|
|
2
2
|
|
|
3
|
-
Reusable React component library for Mantis Core
|
|
3
|
+
Reusable React component library for Mantis Core applications.
|
|
4
4
|
|
|
5
5
|
## Purpose
|
|
6
6
|
|
|
7
|
-
`@mantis-core/ui` centralizes
|
|
7
|
+
`@mantis-core/ui` centralizes reusable UI building blocks for forms, tables, admin page layout, generic state handling, and maps. The package is intended to be domain-agnostic and portable across multiple apps.
|
|
8
8
|
|
|
9
9
|
## Scope
|
|
10
10
|
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
- Components
|
|
11
|
+
- Reusable React components and helper types.
|
|
12
|
+
- Wrappers around PrimeReact, `react-select`, CKEditor, and Leaflet where Mantis needs a stable contract.
|
|
13
|
+
- Components that optionally consume the visual contracts defined in `@mantis-core/styles`.
|
|
14
14
|
|
|
15
|
-
## Out
|
|
15
|
+
## Out of Scope
|
|
16
16
|
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
17
|
+
- Business rules and domain-specific workflows.
|
|
18
|
+
- App-level providers and framework bootstrapping.
|
|
19
|
+
- Client branding logic outside shared tokens and styles.
|
|
20
20
|
|
|
21
21
|
## Installation
|
|
22
22
|
|
|
@@ -24,97 +24,81 @@ Reusable React component library for Mantis Core apps.
|
|
|
24
24
|
pnpm add @mantis-core/ui @mantis-core/styles
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
Required peers:
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
- `react`
|
|
30
|
+
- `react-dom`
|
|
31
|
+
- `next`
|
|
32
|
+
- `primereact`
|
|
33
|
+
- `primeicons`
|
|
34
|
+
- `react-hook-form`
|
|
30
35
|
|
|
31
|
-
|
|
32
|
-
Import the required SCSS modules in your app, for example:
|
|
36
|
+
Some exports also require optional peers already pulled by this package surface, such as CKEditor, `react-select`, `react-datepicker`, `leaflet`, and `react-leaflet`.
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
@use "@mantis-core/styles/scss/table.scss";
|
|
36
|
-
@use "@mantis-core/styles/scss/paginator.scss";
|
|
37
|
-
@use "@mantis-core/styles/scss/inputs.scss";
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## Public API Strategy
|
|
38
|
+
## Import Strategy
|
|
41
39
|
|
|
42
|
-
Preferred
|
|
40
|
+
Preferred:
|
|
43
41
|
|
|
44
42
|
```ts
|
|
45
|
-
import { PaginatedTable, TableToolbar,
|
|
43
|
+
import { PaginatedTable, TableToolbar, TextInput } from "@mantis-core/ui";
|
|
46
44
|
```
|
|
47
45
|
|
|
48
|
-
Supported
|
|
46
|
+
Supported subpaths:
|
|
49
47
|
|
|
50
48
|
- `@mantis-core/ui/components/*`
|
|
51
49
|
- `@mantis-core/ui/table-toolbar`
|
|
52
50
|
- `@mantis-core/ui/table-filter-field`
|
|
53
51
|
|
|
54
|
-
Use subpaths only when you need
|
|
55
|
-
|
|
56
|
-
## Component Catalog
|
|
57
|
-
|
|
58
|
-
See full catalog in [COMPONENTS.md](./COMPONENTS.md).
|
|
59
|
-
|
|
60
|
-
Top-level categories:
|
|
61
|
-
|
|
62
|
-
- Buttons: `Button`
|
|
63
|
-
- Loaders: `Loading`
|
|
64
|
-
- Forms: `CKEditorInput`, `TextInput`, `Select`, `Switch`, `UploadFilesPreview`, etc
|
|
65
|
-
- Tables: `PaginatedTable`, `TableToolbar`, `TableFilterField`, `ActionColumn`, etc
|
|
66
|
-
- Page/Admin layout: `AdminPageConfigurator`, `AdminPageLayoutProvider`, `AdminPageLayoutShell`
|
|
67
|
-
- Generic UI: `Pagination`, `PaginationInfo`, `PageSizeSelector`, `RichTextPreview`
|
|
68
|
-
- Maps: `MapPicker`, `MapPickerClient`, `MapPickerOSM`
|
|
69
|
-
|
|
70
|
-
## Core Usage Patterns
|
|
52
|
+
Use subpaths only when you need a narrow import boundary.
|
|
71
53
|
|
|
72
|
-
|
|
54
|
+
## Styling Model
|
|
73
55
|
|
|
74
|
-
|
|
75
|
-
- Keep validation/business rules in the app layer.
|
|
56
|
+
This package does not inject CSS automatically.
|
|
76
57
|
|
|
77
|
-
|
|
58
|
+
- PrimeReact wrappers inherit your PrimeReact theme plus any Mantis SCSS overrides you import.
|
|
59
|
+
- Components with `mantis-core-*` classes require the matching partial from `@mantis-core/styles`.
|
|
60
|
+
- Some components use utility classes or inline styles and do not depend on a Mantis SCSS partial.
|
|
78
61
|
|
|
79
|
-
|
|
62
|
+
Example:
|
|
80
63
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
64
|
+
```scss
|
|
65
|
+
@use "@mantis-core/styles/scss/buttons.scss";
|
|
66
|
+
@use "@mantis-core/styles/scss/inputs.scss";
|
|
67
|
+
@use "@mantis-core/styles/scss/select.scss";
|
|
68
|
+
@use "@mantis-core/styles/scss/table.scss";
|
|
69
|
+
@use "@mantis-core/styles/scss/confirm-dialog.scss";
|
|
70
|
+
```
|
|
85
71
|
|
|
86
|
-
|
|
72
|
+
## Documentation Index
|
|
87
73
|
|
|
88
|
-
|
|
74
|
+
- [docs/README.md](./docs/README.md): package documentation index
|
|
75
|
+
- [COMPONENTS.md](./COMPONENTS.md): lightweight component index
|
|
76
|
+
- [PRIMEREACT_MAPPING.md](./PRIMEREACT_MAPPING.md): lightweight dependency-map pointer
|
|
89
77
|
|
|
90
|
-
|
|
91
|
-
- `AdminPageLayoutShell`
|
|
92
|
-
- `AdminPageConfigurator`
|
|
78
|
+
## Public API Summary
|
|
93
79
|
|
|
94
|
-
|
|
80
|
+
Main export groups:
|
|
95
81
|
|
|
96
|
-
- `
|
|
97
|
-
-
|
|
82
|
+
- Buttons: `Button`
|
|
83
|
+
- Loaders: `Loading`
|
|
84
|
+
- Forms: `TextInput`, `TextArea`, `NumberInput`, `DropDown`, `DateInput`, `Select`, `AsyncSelect`, `Switch`, `Toggle`, `ToggleInlineButton`, `CKEditorInput`, `FileDropzone`, `UploadFilesPreview`
|
|
85
|
+
- Form helpers: `RenderField`, `FormFieldRender`, `FormActionsBar`, `DirtyFormGuard`, `useDirtyFormGuard`, `ErrorMessage`, `HiddenField`
|
|
86
|
+
- Tables: `PaginatedTable`, `TableToolbar`, `TableFilterField`, `ActionColumn`, `MenuButton`, `IndexColumn`, `FilterPannel`, `MultiSelectFilter`
|
|
87
|
+
- Page layout: `AdminPageConfigurator`, `AdminPageLayoutProvider`, `useAdminPageLayout`, `AdminPageLayoutShell`, `AdminPageHeader`, `FormSection`, `PageTitle`
|
|
88
|
+
- Generic UI: `Pagination`, `PaginationInfo`, `PageSizeSelector`, `RichTextPreview`, `StatusBadge`, `DataState`, `ConfirmDialog`
|
|
89
|
+
- Maps: `MapPicker`, `MapPickerClient`, `MapPickerOSM`
|
|
98
90
|
|
|
99
|
-
##
|
|
91
|
+
## Contributor Rules
|
|
100
92
|
|
|
101
|
-
1.
|
|
102
|
-
2.
|
|
103
|
-
3.
|
|
104
|
-
4.
|
|
105
|
-
5. Preserve `mantis-core-*`
|
|
93
|
+
1. Keep new exports domain-agnostic.
|
|
94
|
+
2. Keep the public API anchored in `src/index.ts`.
|
|
95
|
+
3. Document new exports in `COMPONENTS.md`.
|
|
96
|
+
4. Update `PRIMEREACT_MAPPING.md` when the underlying implementation library changes.
|
|
97
|
+
5. Preserve the `mantis-core-*` style contract where shared styles are intended.
|
|
106
98
|
|
|
107
|
-
##
|
|
99
|
+
## Local Validation
|
|
108
100
|
|
|
109
101
|
```bash
|
|
110
102
|
pnpm --filter @mantis-core/ui build
|
|
111
103
|
pnpm --filter @mantis-core/ui typecheck
|
|
112
104
|
```
|
|
113
|
-
|
|
114
|
-
## Release Checklist
|
|
115
|
-
|
|
116
|
-
1. Component behavior verified in consuming app.
|
|
117
|
-
2. Props/types documented.
|
|
118
|
-
3. `src/index.ts` export updated.
|
|
119
|
-
4. `COMPONENTS.md` updated.
|
|
120
|
-
5. Add changeset.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ButtonVariant } from "../Buttons/Button";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import type { Props as ReactSelectProps } from "react-select";
|
|
4
|
+
import { ControllerProps } from "./types";
|
|
5
|
+
type SelectOption = unknown;
|
|
6
|
+
export interface AsyncSelectProps extends Omit<ReactSelectProps<SelectOption>, "name" | "defaultValue" | "options"> {
|
|
7
|
+
controller: ControllerProps;
|
|
8
|
+
block?: boolean;
|
|
9
|
+
variant?: ButtonVariant;
|
|
10
|
+
isCreatable?: boolean;
|
|
11
|
+
loadOptions: (inputValue: string) => Promise<readonly SelectOption[]> | readonly SelectOption[];
|
|
12
|
+
defaultOptions?: readonly SelectOption[] | boolean;
|
|
13
|
+
cacheOptions?: boolean;
|
|
14
|
+
debounceMs?: number;
|
|
15
|
+
onCreateOption?: (inputValue: string) => void | Promise<void>;
|
|
16
|
+
formatCreateLabel?: (inputValue: string) => React.ReactNode;
|
|
17
|
+
}
|
|
18
|
+
declare const AsyncSelect: React.FC<AsyncSelectProps>;
|
|
19
|
+
export default AsyncSelect;
|
|
20
|
+
//# sourceMappingURL=AsyncSelect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AsyncSelect.d.ts","sourceRoot":"","sources":["../../../src/components/Forms/AsyncSelect.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,OAAO,KAAyC,MAAM,OAAO,CAAC;AAI9D,OAAO,KAAK,EAAE,KAAK,IAAI,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE1C,KAAK,YAAY,GAAG,OAAO,CAAC;AAE5B,MAAM,WAAW,gBACf,SAAQ,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAAC;IACjF,UAAU,EAAE,eAAe,CAAC;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,YAAY,EAAE,CAAC,GAAG,SAAS,YAAY,EAAE,CAAC;IAChG,cAAc,CAAC,EAAE,SAAS,YAAY,EAAE,GAAG,OAAO,CAAC;IACnD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,iBAAiB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,KAAK,CAAC,SAAS,CAAC;CAC7D;AAcD,QAAA,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAkH3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import classNames from "classnames";
|
|
4
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
5
|
+
import { Controller } from "react-hook-form";
|
|
6
|
+
import { default as RAsyncSelect } from "react-select/async";
|
|
7
|
+
import { default as RAsyncCreatableSelect } from "react-select/async-creatable";
|
|
8
|
+
const SELECT_VARIANT_TOKENS = {
|
|
9
|
+
primary: { color: "--primary", foreground: "--primary-foreground" },
|
|
10
|
+
secondary: { color: "--secondary", foreground: "--secondary-foreground" },
|
|
11
|
+
accent: { color: "--accent", foreground: "--accent-foreground" },
|
|
12
|
+
success: { color: "--success", foreground: "--success-foreground" },
|
|
13
|
+
warning: { color: "--warning", foreground: "--warning-foreground" },
|
|
14
|
+
error: { color: "--error", foreground: "--error-foreground" },
|
|
15
|
+
info: { color: "--info", foreground: "--info-foreground" },
|
|
16
|
+
gray: { color: "--bluegray-500", foreground: "--primary-foreground" },
|
|
17
|
+
danger: { color: "--error", foreground: "--error-foreground" },
|
|
18
|
+
};
|
|
19
|
+
const AsyncSelect = (props) => {
|
|
20
|
+
const { controller, block, variant = "primary", isCreatable = false, loadOptions, defaultOptions = true, cacheOptions = true, debounceMs = 250, onCreateOption, formatCreateLabel, ...rest } = props;
|
|
21
|
+
const debounceRef = useRef(null);
|
|
22
|
+
const variantTokens = SELECT_VARIANT_TOKENS[variant];
|
|
23
|
+
const SelectComponent = (isCreatable ? RAsyncCreatableSelect : RAsyncSelect);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
return () => {
|
|
26
|
+
if (debounceRef.current) {
|
|
27
|
+
clearTimeout(debounceRef.current);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}, []);
|
|
31
|
+
const debouncedLoadOptions = useCallback((inputValue) => new Promise((resolve, reject) => {
|
|
32
|
+
const executeLoad = async () => {
|
|
33
|
+
try {
|
|
34
|
+
const options = await loadOptions(inputValue);
|
|
35
|
+
resolve(options ?? []);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
reject(error);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
if (debounceRef.current) {
|
|
42
|
+
clearTimeout(debounceRef.current);
|
|
43
|
+
}
|
|
44
|
+
if (debounceMs <= 0) {
|
|
45
|
+
void executeLoad();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
debounceRef.current = setTimeout(() => {
|
|
49
|
+
void executeLoad();
|
|
50
|
+
}, debounceMs);
|
|
51
|
+
}), [debounceMs, loadOptions]);
|
|
52
|
+
return (_jsx(Controller, { defaultValue: controller.defaultValue ?? null, ...controller, render: ({ field, fieldState, formState }) => (_jsx(SelectComponent, { instanceId: field.name, id: field.name, className: classNames("select-input cursor-pointer", `select-variant-${variant}`, rest.className, {
|
|
53
|
+
"w-full": block,
|
|
54
|
+
"select-invalid": fieldState.invalid,
|
|
55
|
+
}), classNamePrefix: 'react-select', value: field.value, onChange: field.onChange, onBlur: field.onBlur, name: field.name, placeholder: 'SELECCIONAR', noOptionsMessage: () => "Sin resultados", loadingMessage: () => "Cargando...", ref: field.ref, cacheOptions: cacheOptions, defaultOptions: defaultOptions, loadOptions: debouncedLoadOptions, menuPortalTarget: typeof document !== "undefined" ? document.body : undefined, menuPosition: 'fixed', ...rest, ...(isCreatable
|
|
56
|
+
? {
|
|
57
|
+
onCreateOption: (inputValue) => {
|
|
58
|
+
void onCreateOption?.(inputValue);
|
|
59
|
+
},
|
|
60
|
+
formatCreateLabel: formatCreateLabel ?? ((inputValue) => `Crear "${inputValue}"`),
|
|
61
|
+
}
|
|
62
|
+
: {}), isDisabled: formState.isSubmitting || rest.isDisabled || rest.isLoading, styles: {
|
|
63
|
+
menuPortal: (base) => ({
|
|
64
|
+
...base,
|
|
65
|
+
zIndex: 1300,
|
|
66
|
+
["--select-accent"]: `var(${variantTokens.color})`,
|
|
67
|
+
["--select-accent-foreground"]: `var(${variantTokens.foreground})`,
|
|
68
|
+
}),
|
|
69
|
+
menu: (base) => ({
|
|
70
|
+
...base,
|
|
71
|
+
zIndex: 1300,
|
|
72
|
+
}),
|
|
73
|
+
...(rest.styles || {}),
|
|
74
|
+
} })) }));
|
|
75
|
+
};
|
|
76
|
+
export default AsyncSelect;
|
|
77
|
+
//# sourceMappingURL=AsyncSelect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AsyncSelect.js","sourceRoot":"","sources":["../../../src/components/Forms/AsyncSelect.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type UseDirtyFormGuardOptions } from "./useDirtyFormGuard";
|
|
2
|
+
export type DirtyFormGuardProps = UseDirtyFormGuardOptions;
|
|
3
|
+
declare const DirtyFormGuard: ({ isDirty, enabled, message }: DirtyFormGuardProps) => null;
|
|
4
|
+
export default DirtyFormGuard;
|
|
5
|
+
//# sourceMappingURL=DirtyFormGuard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DirtyFormGuard.d.ts","sourceRoot":"","sources":["../../../src/components/Forms/DirtyFormGuard.tsx"],"names":[],"mappings":"AAEA,OAA0B,EAAE,KAAK,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAEvF,MAAM,MAAM,mBAAmB,GAAG,wBAAwB,CAAC;AAE3D,QAAA,MAAM,cAAc,GAAI,+BAAsC,mBAAmB,SAGhF,CAAC;AAEF,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import useDirtyFormGuard from "./useDirtyFormGuard";
|
|
3
|
+
const DirtyFormGuard = ({ isDirty, enabled = true, message }) => {
|
|
4
|
+
useDirtyFormGuard({ isDirty, enabled, message });
|
|
5
|
+
return null;
|
|
6
|
+
};
|
|
7
|
+
export default DirtyFormGuard;
|
|
8
|
+
//# sourceMappingURL=DirtyFormGuard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DirtyFormGuard.js","sourceRoot":"","sources":["../../../src/components/Forms/DirtyFormGuard.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,iBAAoD,MAAM,qBAAqB,CAAC;AAIvF,MAAM,cAAc,GAAG,CAAC,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,EAAE,OAAO,EAAuB,EAAE,EAAE;IACnF,iBAAiB,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IACjD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ControllerProps } from "./types";
|
|
2
|
+
type FileDropzoneRejectCode = "too-many-files" | "file-too-large" | "invalid-file-type";
|
|
3
|
+
export interface FileDropzoneRejectedFile {
|
|
4
|
+
file: File;
|
|
5
|
+
code: FileDropzoneRejectCode;
|
|
6
|
+
message: string;
|
|
7
|
+
}
|
|
8
|
+
export interface FileDropzoneProps {
|
|
9
|
+
controller?: ControllerProps;
|
|
10
|
+
value?: File[];
|
|
11
|
+
onChange?: (files: File[]) => void;
|
|
12
|
+
onFilesAccepted?: (files: File[]) => void;
|
|
13
|
+
onFilesRejected?: (rejections: FileDropzoneRejectedFile[]) => void;
|
|
14
|
+
multiple?: boolean;
|
|
15
|
+
maxFiles?: number;
|
|
16
|
+
maxSizeBytes?: number;
|
|
17
|
+
accept?: string | string[];
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
block?: boolean;
|
|
20
|
+
label?: string;
|
|
21
|
+
hint?: string;
|
|
22
|
+
dropTitle?: string;
|
|
23
|
+
dragOverTitle?: string;
|
|
24
|
+
className?: string;
|
|
25
|
+
}
|
|
26
|
+
declare const FileDropzone: ({ controller, value, onChange, ...rest }: FileDropzoneProps) => import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
export default FileDropzone;
|
|
28
|
+
export type { FileDropzoneRejectCode };
|
|
29
|
+
//# sourceMappingURL=FileDropzone.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileDropzone.d.ts","sourceRoot":"","sources":["../../../src/components/Forms/FileDropzone.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,KAAK,sBAAsB,GAAG,gBAAgB,GAAG,gBAAgB,GAAG,mBAAmB,CAAC;AAExF,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,sBAAsB,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;IACnC,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;IAC1C,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,wBAAwB,EAAE,KAAK,IAAI,CAAC;IACnE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA+ND,QAAA,MAAM,YAAY,GAAI,0CAKnB,iBAAiB,4CAsBnB,CAAC;AAEF,eAAe,YAAY,CAAC;AAE5B,YAAY,EAAE,sBAAsB,EAAE,CAAC"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cn } from "@mantis-core/utils";
|
|
4
|
+
import { AlertCircle, File as FileIcon, UploadCloud, X } from "lucide-react";
|
|
5
|
+
import { useMemo, useRef, useState } from "react";
|
|
6
|
+
import { Controller } from "react-hook-form";
|
|
7
|
+
const DEFAULT_DROP_TITLE = "Arrastra y suelta archivos aqui";
|
|
8
|
+
const DEFAULT_DRAG_OVER_TITLE = "Suelta los archivos aqui";
|
|
9
|
+
const DEFAULT_HINT = "o haz clic para seleccionar archivos";
|
|
10
|
+
const toArray = (accept) => {
|
|
11
|
+
if (!accept)
|
|
12
|
+
return [];
|
|
13
|
+
if (Array.isArray(accept))
|
|
14
|
+
return accept.map((item) => item.trim()).filter(Boolean);
|
|
15
|
+
return accept
|
|
16
|
+
.split(",")
|
|
17
|
+
.map((item) => item.trim())
|
|
18
|
+
.filter(Boolean);
|
|
19
|
+
};
|
|
20
|
+
const isAcceptedType = (file, acceptList) => {
|
|
21
|
+
if (acceptList.length === 0)
|
|
22
|
+
return true;
|
|
23
|
+
const fileName = file.name.toLowerCase();
|
|
24
|
+
const fileType = file.type.toLowerCase();
|
|
25
|
+
return acceptList.some((rule) => {
|
|
26
|
+
const normalizedRule = rule.toLowerCase();
|
|
27
|
+
if (normalizedRule.startsWith(".")) {
|
|
28
|
+
return fileName.endsWith(normalizedRule);
|
|
29
|
+
}
|
|
30
|
+
if (normalizedRule.endsWith("/*")) {
|
|
31
|
+
const prefix = normalizedRule.slice(0, normalizedRule.length - 1);
|
|
32
|
+
return fileType.startsWith(prefix);
|
|
33
|
+
}
|
|
34
|
+
return fileType === normalizedRule;
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
const formatBytes = (bytes) => {
|
|
38
|
+
if (bytes < 1024)
|
|
39
|
+
return `${bytes} B`;
|
|
40
|
+
if (bytes < 1024 ** 2)
|
|
41
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
42
|
+
return `${(bytes / 1024 ** 2).toFixed(1)} MB`;
|
|
43
|
+
};
|
|
44
|
+
const FileDropzoneInner = ({ value, onChange, onFilesAccepted, onFilesRejected, multiple = true, maxFiles = 10, maxSizeBytes = 10 * 1024 * 1024, accept, disabled = false, block = true, label, hint = DEFAULT_HINT, dropTitle = DEFAULT_DROP_TITLE, dragOverTitle = DEFAULT_DRAG_OVER_TITLE, className, }) => {
|
|
45
|
+
const [isDraggingOver, setIsDraggingOver] = useState(false);
|
|
46
|
+
const [lastRejections, setLastRejections] = useState([]);
|
|
47
|
+
const inputRef = useRef(null);
|
|
48
|
+
const acceptList = useMemo(() => toArray(accept), [accept]);
|
|
49
|
+
const appendFiles = (incomingFiles) => {
|
|
50
|
+
if (!incomingFiles || incomingFiles.length === 0 || disabled)
|
|
51
|
+
return;
|
|
52
|
+
const current = value ?? [];
|
|
53
|
+
const accepted = [];
|
|
54
|
+
const rejected = [];
|
|
55
|
+
for (const file of Array.from(incomingFiles)) {
|
|
56
|
+
if (!isAcceptedType(file, acceptList)) {
|
|
57
|
+
rejected.push({
|
|
58
|
+
file,
|
|
59
|
+
code: "invalid-file-type",
|
|
60
|
+
message: `El archivo ${file.name} no cumple el tipo permitido.`,
|
|
61
|
+
});
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (file.size > maxSizeBytes) {
|
|
65
|
+
rejected.push({
|
|
66
|
+
file,
|
|
67
|
+
code: "file-too-large",
|
|
68
|
+
message: `El archivo ${file.name} supera el maximo permitido (${formatBytes(maxSizeBytes)}).`,
|
|
69
|
+
});
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (!multiple && current.length + accepted.length >= 1) {
|
|
73
|
+
rejected.push({
|
|
74
|
+
file,
|
|
75
|
+
code: "too-many-files",
|
|
76
|
+
message: "Solo se permite un archivo.",
|
|
77
|
+
});
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (current.length + accepted.length >= maxFiles) {
|
|
81
|
+
rejected.push({
|
|
82
|
+
file,
|
|
83
|
+
code: "too-many-files",
|
|
84
|
+
message: `Solo se permiten ${maxFiles} archivos.`,
|
|
85
|
+
});
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
accepted.push(file);
|
|
89
|
+
}
|
|
90
|
+
if (accepted.length > 0) {
|
|
91
|
+
const mergedFiles = [...current, ...accepted];
|
|
92
|
+
onChange(mergedFiles);
|
|
93
|
+
onFilesAccepted?.(accepted);
|
|
94
|
+
}
|
|
95
|
+
if (rejected.length > 0) {
|
|
96
|
+
setLastRejections(rejected);
|
|
97
|
+
onFilesRejected?.(rejected);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
setLastRejections([]);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
return (_jsxs("div", { className: cn("mantis-core-file-dropzone", className, { "w-full": block }), children: [label ? _jsx("p", { className: 'mantis-core-file-dropzone__label', children: label }) : null, _jsxs("div", { role: 'button', tabIndex: disabled ? -1 : 0, className: cn("mantis-core-file-dropzone__zone", {
|
|
104
|
+
"mantis-core-file-dropzone__zone--drag": isDraggingOver && !disabled,
|
|
105
|
+
"mantis-core-file-dropzone__zone--disabled": disabled,
|
|
106
|
+
}), onClick: () => !disabled && inputRef.current?.click(), onKeyDown: (event) => {
|
|
107
|
+
if ((event.key === "Enter" || event.key === " ") && !disabled) {
|
|
108
|
+
event.preventDefault();
|
|
109
|
+
inputRef.current?.click();
|
|
110
|
+
}
|
|
111
|
+
}, onDragOver: (event) => {
|
|
112
|
+
event.preventDefault();
|
|
113
|
+
if (!disabled)
|
|
114
|
+
setIsDraggingOver(true);
|
|
115
|
+
}, onDragLeave: () => setIsDraggingOver(false), onDrop: (event) => {
|
|
116
|
+
event.preventDefault();
|
|
117
|
+
setIsDraggingOver(false);
|
|
118
|
+
appendFiles(event.dataTransfer.files);
|
|
119
|
+
}, children: [_jsx("input", { ref: inputRef, type: 'file', className: 'hidden', disabled: disabled, multiple: multiple, accept: acceptList.join(","), onChange: (event) => {
|
|
120
|
+
appendFiles(event.target.files);
|
|
121
|
+
event.target.value = "";
|
|
122
|
+
} }), _jsx("div", { className: 'mantis-core-file-dropzone__icon', children: _jsx(UploadCloud, { size: 22 }) }), _jsx("p", { className: 'mantis-core-file-dropzone__title', children: isDraggingOver && !disabled ? dragOverTitle : dropTitle }), _jsx("p", { className: 'mantis-core-file-dropzone__hint', children: hint })] }), value.length > 0 ? (_jsx("ul", { className: 'mantis-core-file-dropzone__list', children: value.map((file, index) => (_jsxs("li", { className: 'mantis-core-file-dropzone__item', children: [_jsxs("span", { className: 'mantis-core-file-dropzone__item-copy', children: [_jsx(FileIcon, { size: 14 }), _jsx("span", { className: 'mantis-core-file-dropzone__item-name', children: file.name }), _jsx("span", { className: 'mantis-core-file-dropzone__item-size', children: formatBytes(file.size) })] }), _jsx("button", { type: 'button', className: 'mantis-core-file-dropzone__remove', onClick: () => {
|
|
123
|
+
const nextFiles = value.filter((_, itemIndex) => itemIndex !== index);
|
|
124
|
+
onChange(nextFiles);
|
|
125
|
+
}, title: 'Quitar archivo', disabled: disabled, children: _jsx(X, { size: 14 }) })] }, `${file.name}-${file.lastModified}-${index}`))) })) : null, lastRejections.length > 0 ? (_jsx("ul", { className: 'mantis-core-file-dropzone__error-list', children: lastRejections.map((rejection, index) => (_jsxs("li", { className: 'mantis-core-file-dropzone__error-item', children: [_jsx(AlertCircle, { size: 12 }), _jsx("span", { children: rejection.message })] }, `${rejection.file.name}-${rejection.code}-${index}`))) })) : null] }));
|
|
126
|
+
};
|
|
127
|
+
const FileDropzone = ({ controller, value, onChange, ...rest }) => {
|
|
128
|
+
if (controller) {
|
|
129
|
+
return (_jsx(Controller, { defaultValue: controller.defaultValue ?? [], ...controller, render: ({ field, formState }) => (_jsx(FileDropzoneInner, { ...rest, value: Array.isArray(field.value) ? field.value : [], onChange: (files) => {
|
|
130
|
+
field.onChange(files);
|
|
131
|
+
onChange?.(files);
|
|
132
|
+
}, disabled: formState.isSubmitting || rest.disabled })) }));
|
|
133
|
+
}
|
|
134
|
+
return _jsx(FileDropzoneInner, { ...rest, value: value ?? [], onChange: onChange ?? (() => undefined) });
|
|
135
|
+
};
|
|
136
|
+
export default FileDropzone;
|
|
137
|
+
//# sourceMappingURL=FileDropzone.js.map
|