@panneau/fields 5.0.0-alpha.2 → 5.0.0-alpha.4
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 +36 -1
- package/dist/file/FileField.css +54 -0
- package/dist/file/FileField.d.ts +50 -0
- package/dist/file/FileField.d.ts.map +1 -0
- package/dist/file/FileField.js +198 -0
- package/dist/file/FileField.js.map +1 -0
- package/dist/file/FileField.module.js +22 -0
- package/dist/file/FileField.module.js.map +1 -0
- package/dist/file/index.d.ts +3 -0
- package/dist/file/index.d.ts.map +1 -0
- package/dist/file/index.js +1 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/shared/builtIn.d.ts +3 -2
- package/dist/shared/builtIn.d.ts.map +1 -1
- package/dist/shared/builtIn.js +5 -2
- package/dist/shared/builtIn.js.map +1 -1
- package/dist/shared/context.d.ts +10 -2
- package/dist/shared/context.d.ts.map +1 -1
- package/dist/shared/context.js +22 -18
- package/dist/shared/context.js.map +1 -1
- package/dist/shared/entry.d.ts +4 -0
- package/dist/shared/entry.d.ts.map +1 -1
- package/dist/shared/entry.js +12 -1
- package/dist/shared/entry.js.map +1 -1
- package/dist/upload/FilePicker.css +24 -0
- package/dist/upload/FilePicker.d.ts +14 -0
- package/dist/upload/FilePicker.d.ts.map +1 -0
- package/dist/upload/FilePicker.js +47 -0
- package/dist/upload/FilePicker.js.map +1 -0
- package/dist/upload/FilePicker.module.js +12 -0
- package/dist/upload/FilePicker.module.js.map +1 -0
- package/dist/upload/UploadList.css +93 -0
- package/dist/upload/UploadList.d.ts +18 -0
- package/dist/upload/UploadList.d.ts.map +1 -0
- package/dist/upload/UploadList.js +144 -0
- package/dist/upload/UploadList.js.map +1 -0
- package/dist/upload/UploadList.module.js +32 -0
- package/dist/upload/UploadList.module.js.map +1 -0
- package/dist/upload/files.d.ts +5 -0
- package/dist/upload/files.d.ts.map +1 -0
- package/dist/upload/files.js +26 -0
- package/dist/upload/files.js.map +1 -0
- package/dist/upload/types.d.ts +58 -0
- package/dist/upload/types.d.ts.map +1 -0
- package/dist/upload/useRejectionMessage.d.ts +4 -0
- package/dist/upload/useRejectionMessage.d.ts.map +1 -0
- package/dist/upload/useRejectionMessage.js +25 -0
- package/dist/upload/useRejectionMessage.js.map +1 -0
- package/dist/upload/useUploads.d.ts +30 -0
- package/dist/upload/useUploads.d.ts.map +1 -0
- package/dist/upload/useUploads.js +115 -0
- package/dist/upload/useUploads.js.map +1 -0
- package/dist/uppy/index.d.ts +3 -0
- package/dist/uppy/index.d.ts.map +1 -0
- package/dist/uppy/index.js +1 -0
- package/dist/uppy/uploader.d.ts +44 -0
- package/dist/uppy/uploader.d.ts.map +1 -0
- package/dist/uppy/uploader.js +139 -0
- package/dist/uppy/uploader.js.map +1 -0
- package/package.json +30 -5
package/README.md
CHANGED
|
@@ -4,6 +4,7 @@ Controlled field components for the protocol's built-in field types, built on [`
|
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npm install @panneau/fields@^5.0.0-alpha.0 @panneau/ui@^5.0.0-alpha.0 react-aria-components react-intl
|
|
7
|
+
npm install @uppy/core@^6.3.0 @uppy/aws-s3@^6.3.0 # only for @panneau/fields/uppy
|
|
7
8
|
```
|
|
8
9
|
|
|
9
10
|
```tsx
|
|
@@ -37,6 +38,8 @@ Every field takes `value`, `onChange`, `onBlur`, `errors`, `disabled`, `readOnly
|
|
|
37
38
|
| `@panneau/fields/group` | `GroupField` | an object |
|
|
38
39
|
| `@panneau/fields/repeater` | `RepeaterField` (items or typed blocks) | a list of objects; blocks have a `type` |
|
|
39
40
|
| `@panneau/fields/relation` | `RelationField`, `createRelationSource()` | `{ id, label }`, or a list with `multiple` |
|
|
41
|
+
| `@panneau/fields/file` | `FileField` | a file, or a list with `multiple` (below) |
|
|
42
|
+
| `@panneau/fields/uppy` | `createUppyUploadSource()` | the upload source on Uppy 6 (below) |
|
|
40
43
|
| `@panneau/fields/locales` | `en`, `fr` | deprecated: use `@panneau/intl` |
|
|
41
44
|
|
|
42
45
|
## From a definition
|
|
@@ -65,7 +68,39 @@ import {
|
|
|
65
68
|
- **Relations** search a `RelationSource`. `createRelationSource()` makes one from any adapter with a `list()` method, such as the `DataAdapter` of `@panneau/data`. Labels come from the resource's `itemTitle`. Without a source, a relation field shows its references, read-only.
|
|
66
69
|
- **Plain ids.** Values can be ids (`"12"`, `["12", "13"]`), as a product's own API gives them. The field shows their labels once the source's optional `resolve(resource, ids)` gives them (`createRelationSource()` implements it with the adapter's `get()`), and their ids until then. Fields that show the same ids share one request. `useRelationReferences()` does the same for other components.
|
|
67
70
|
- **Creating from the picker.** When the source has `create(resource, text)`, the picker offers "Create “…”" for a text that matches no item, and adds the new item. `createRelationSource(adapter, { create: true })` creates it with the adapter's `create()`, with the text as its `itemTitle` (in every locale when it is localized), on the resources that allow `create`; a function gives other values. A field's `onCreate` prop replaces the source's, and can open a dialog with a full form: it resolves with the new reference, or `null`. `onCreate={null}` turns creating off.
|
|
68
|
-
- **
|
|
71
|
+
- **Files** upload through the `uploadSource` of the provider (below). Without one, the value holds the files themselves.
|
|
72
|
+
- **Plugin slot:** a type without an entry, such as `richtext` and `media` without their plugins (`@panneau/editor`, `@panneau/media`), renders a placeholder that names it. A plugin that provides the type replaces it.
|
|
73
|
+
|
|
74
|
+
## Files and uploads
|
|
75
|
+
|
|
76
|
+
`FileField` (the `file` type) holds files that belong to an item, outside the media library: an attachment, a PDF, a CSV ([decision 0025](../../docs/decisions/0025-presigned-uploads.md)). Its value is a file, or a list of them with `multiple`:
|
|
77
|
+
|
|
78
|
+
- a **stored file**, `{ name, url, mime, size }`, as items give it: the field shows it with a link;
|
|
79
|
+
- an **uploaded file**, `{ upload, name, mime, size }`, once the browser sent the file to storage: requests send its `upload`;
|
|
80
|
+
- the **`File` itself**, without an upload source: the adapter sends it in `multipart/form-data`.
|
|
81
|
+
|
|
82
|
+
A new file replaces the file of a single value, and is added to a multiple one. `accept` and `maxSize` (from the field's `accept`, its `accept` rule and its `maxSize` rule) refuse files before they are sent. A read-only field only shows its files.
|
|
83
|
+
|
|
84
|
+
**Upload sources.** An `UploadSource` makes an uploader for each field, which reports each file's progress and gives its upload once it is done. `createUppyUploadSource()` of `@panneau/fields/uppy` makes one on Uppy 6: files go to S3-compatible storage with URLs that the backend signs, in parts when they are large (they can pause and resume), and each upload is completed with the backend. `@uppy/core` and `@uppy/aws-s3` 6 are optional peer dependencies, only for that entry point: nothing else in the package imports Uppy. Make the source once, and pass it as `uploadSource` to `FieldsProvider`, or to `FormView` of `@panneau/forms`, when the definition announces direct uploads:
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
import { createUppyUploadSource } from '@panneau/fields/uppy';
|
|
88
|
+
|
|
89
|
+
const uploadSource =
|
|
90
|
+
definition.uploads?.mode === 'presigned' && adapter.presignedUploads !== undefined
|
|
91
|
+
? createUppyUploadSource(
|
|
92
|
+
adapter.presignedUploads({ endpoint: definition.uploads.endpoint }),
|
|
93
|
+
)
|
|
94
|
+
: null;
|
|
95
|
+
|
|
96
|
+
<FieldsProvider components={builtInFields} uploadSource={uploadSource}>
|
|
97
|
+
…
|
|
98
|
+
</FieldsProvider>;
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`adapter.presignedUploads()` comes from `@panneau/data`; `createUppyUploadSource()` only needs an object with its `signRequest()` and `complete()` (`PresignedUploadsLike`), so fields don't depend on `@panneau/data`. A field's `uploadSource` prop overrides the provider's, and `null` turns uploads off.
|
|
102
|
+
|
|
103
|
+
**Building blocks.** `@panneau/fields` also exports what the file field is made of, for other fields that take files (the media field of `@panneau/media`): `useUploads()` (checks files, uploads them through a source, and gives their state), `UploadList` (a progress bar per file, with pause, resume, cancel and retry), `FilePicker` (a button and a drop zone), and `formatSize()`, `matchesMime()`, `getAccept()`, `getMaxSize()`.
|
|
69
104
|
|
|
70
105
|
## Messages
|
|
71
106
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
.panneau-file-field-field {
|
|
2
|
+
display: grid;
|
|
3
|
+
gap: var(--panneau-space-2);
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.panneau-file-field-files {
|
|
7
|
+
display: grid;
|
|
8
|
+
gap: var(--panneau-space-1);
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
list-style: none;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.panneau-file-field-file {
|
|
15
|
+
display: flex;
|
|
16
|
+
align-items: center;
|
|
17
|
+
gap: var(--panneau-space-2);
|
|
18
|
+
min-height: var(--panneau-control-height-sm);
|
|
19
|
+
padding-inline: var(--panneau-space-2) var(--panneau-space-1);
|
|
20
|
+
background-color: var(--panneau-color-surface-sunken);
|
|
21
|
+
border: var(--panneau-border-width) solid var(--panneau-color-border);
|
|
22
|
+
border-radius: var(--panneau-radius-md);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.panneau-file-field-icon {
|
|
26
|
+
flex: none;
|
|
27
|
+
width: 1rem;
|
|
28
|
+
height: 1rem;
|
|
29
|
+
color: var(--panneau-color-text-muted);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.panneau-file-field-name {
|
|
33
|
+
flex: 1 1 auto;
|
|
34
|
+
min-width: 0;
|
|
35
|
+
overflow: hidden;
|
|
36
|
+
font-size: var(--panneau-font-size-sm);
|
|
37
|
+
text-overflow: ellipsis;
|
|
38
|
+
white-space: nowrap;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
a.panneau-file-field-name {
|
|
42
|
+
color: var(--panneau-color-link);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.panneau-file-field-size {
|
|
46
|
+
flex: none;
|
|
47
|
+
font-size: var(--panneau-font-size-sm);
|
|
48
|
+
color: var(--panneau-color-text-muted);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.panneau-file-field-none {
|
|
52
|
+
font-size: var(--panneau-font-size-md);
|
|
53
|
+
color: var(--panneau-color-text-muted);
|
|
54
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { StoredFile } from '@panneau/protocol';
|
|
2
|
+
import type { FieldEntryProps, FieldProps } from '../shared/types';
|
|
3
|
+
import type { UploadSource, UploadedFile } from '../upload/types';
|
|
4
|
+
/**
|
|
5
|
+
* A file of a `file` value: a stored file, as items give it; an upload, once the browser sent the
|
|
6
|
+
* file to storage; or the file itself, sent in a multipart request without direct uploads.
|
|
7
|
+
*/
|
|
8
|
+
export type FileValue = StoredFile | UploadedFile | File;
|
|
9
|
+
export interface FileFieldBaseProps extends Omit<FieldProps<never>, 'value' | 'onChange'> {
|
|
10
|
+
/** Accepted MIME types (`application/pdf`, `image/*`). Others are refused before they are sent. */
|
|
11
|
+
accept?: readonly string[] | null;
|
|
12
|
+
/** Largest file, in bytes. Larger ones are refused before they are sent. */
|
|
13
|
+
maxSize?: number | null;
|
|
14
|
+
/**
|
|
15
|
+
* Where files are uploaded directly to storage. Defaults to the one of the fields context.
|
|
16
|
+
* Without one, the value holds the files themselves, which the adapter sends in
|
|
17
|
+
* `multipart/form-data`.
|
|
18
|
+
*/
|
|
19
|
+
uploadSource?: UploadSource | null;
|
|
20
|
+
}
|
|
21
|
+
export interface SingleFileFieldProps extends FileFieldBaseProps {
|
|
22
|
+
multiple?: false;
|
|
23
|
+
value?: FileValue | null;
|
|
24
|
+
onChange?: (value: FileValue | null) => void;
|
|
25
|
+
}
|
|
26
|
+
export interface MultipleFileFieldProps extends FileFieldBaseProps {
|
|
27
|
+
multiple: true;
|
|
28
|
+
value?: readonly FileValue[] | null;
|
|
29
|
+
/** Called with the files. An empty list is `null`. */
|
|
30
|
+
onChange?: (value: FileValue[] | null) => void;
|
|
31
|
+
}
|
|
32
|
+
export type FileFieldProps = SingleFileFieldProps | MultipleFileFieldProps;
|
|
33
|
+
/** Whether a value is a file of a `file` value. */
|
|
34
|
+
export declare function isFileValue(value: unknown): value is FileValue;
|
|
35
|
+
/** The files of a value, single or multiple. */
|
|
36
|
+
export declare function toFiles(value: unknown): FileValue[];
|
|
37
|
+
/**
|
|
38
|
+
* Files that belong to an item, outside the media library: an attachment, a PDF, a CSV (the
|
|
39
|
+
* `file` type). With an upload source, files go directly to storage, and the value references
|
|
40
|
+
* each upload (`{ upload, name, mime, size }`). Without one, the value is the file itself. Items
|
|
41
|
+
* give stored files, which the field shows with a link. A new file replaces the file of a single
|
|
42
|
+
* value, and is added to a multiple one.
|
|
43
|
+
*/
|
|
44
|
+
export declare function FileField(props: FileFieldProps): import("react").JSX.Element;
|
|
45
|
+
/**
|
|
46
|
+
* The registry entry of the `file` type: the field's `multiple`, `accept` (or its `accept` rule)
|
|
47
|
+
* and `maxSize` rule, with the upload source of the fields context.
|
|
48
|
+
*/
|
|
49
|
+
export declare function FileFieldEntry(props: FieldEntryProps): import("react").JSX.Element;
|
|
50
|
+
//# sourceMappingURL=FileField.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileField.d.ts","sourceRoot":"","sources":["../../src/file/FileField.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAMpD,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAInE,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAKlE;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,YAAY,GAAG,IAAI,CAAC;AAEzD,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;IACrF,mGAAmG;IACnG,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;IAClC,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB;;;;OAIG;IACH,YAAY,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,oBAAqB,SAAQ,kBAAkB;IAC5D,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,KAAK,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,KAAK,IAAI,CAAC;CAChD;AAED,MAAM,WAAW,sBAAuB,SAAQ,kBAAkB;IAC9D,QAAQ,EAAE,IAAI,CAAC;IACf,KAAK,CAAC,EAAE,SAAS,SAAS,EAAE,GAAG,IAAI,CAAC;IACpC,sDAAsD;IACtD,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,IAAI,KAAK,IAAI,CAAC;CAClD;AAED,MAAM,MAAM,cAAc,GAAG,oBAAoB,GAAG,sBAAsB,CAAC;AAY3E,mDAAmD;AACnD,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAS9D;AAED,gDAAgD;AAChD,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,EAAE,CAKnD;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,+BAgI9C;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,eAAe,+BAYpD"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { useFieldsContext } from "../shared/context.js";
|
|
2
|
+
import { getAccept, getFieldProps, getMaxSize } from "../shared/entry.js";
|
|
3
|
+
import { messages } from "../shared/messages.js";
|
|
4
|
+
import { FilePicker } from "../upload/FilePicker.js";
|
|
5
|
+
import { formatSize } from "../upload/files.js";
|
|
6
|
+
import { UploadList } from "../upload/UploadList.js";
|
|
7
|
+
import { useRejectionMessage } from "../upload/useRejectionMessage.js";
|
|
8
|
+
import { useUploads } from "../upload/useUploads.js";
|
|
9
|
+
import FileField_module_default from "./FileField.module.js";
|
|
10
|
+
import { c } from "react/compiler-runtime";
|
|
11
|
+
import { Fieldset, IconButton, usePanneauIntl } from "@panneau/ui";
|
|
12
|
+
import { useEffect, useRef, useState } from "react";
|
|
13
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
14
|
+
import { FileText, X } from "lucide-react";
|
|
15
|
+
//#region src/file/FileField.tsx
|
|
16
|
+
function fileSize(file) {
|
|
17
|
+
return typeof file.size === "number" ? file.size : null;
|
|
18
|
+
}
|
|
19
|
+
function fileUrl(file) {
|
|
20
|
+
return !(file instanceof Blob) && "url" in file && typeof file.url === "string" ? file.url : null;
|
|
21
|
+
}
|
|
22
|
+
/** Whether a value is a file of a `file` value. */
|
|
23
|
+
function isFileValue(value) {
|
|
24
|
+
if (typeof File !== "undefined" && value instanceof File) return true;
|
|
25
|
+
return typeof value === "object" && value !== null && typeof value.name === "string";
|
|
26
|
+
}
|
|
27
|
+
/** The files of a value, single or multiple. */
|
|
28
|
+
function toFiles(value) {
|
|
29
|
+
if (Array.isArray(value)) return value.filter(isFileValue);
|
|
30
|
+
return isFileValue(value) ? [value] : [];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Files that belong to an item, outside the media library: an attachment, a PDF, a CSV (the
|
|
34
|
+
* `file` type). With an upload source, files go directly to storage, and the value references
|
|
35
|
+
* each upload (`{ upload, name, mime, size }`). Without one, the value is the file itself. Items
|
|
36
|
+
* give stored files, which the field shows with a link. A new file replaces the file of a single
|
|
37
|
+
* value, and is added to a multiple one.
|
|
38
|
+
*/
|
|
39
|
+
function FileField(props) {
|
|
40
|
+
const intl = usePanneauIntl();
|
|
41
|
+
const context = useFieldsContext();
|
|
42
|
+
const { accept = null, maxSize = null, uploadSource = context.uploadSource, label, description, errors = [], disabled, readOnly, required, className, labelClassName, descriptionClassName, errorsClassName } = props;
|
|
43
|
+
const multiple = props.multiple === true;
|
|
44
|
+
const files = toFiles(props.value);
|
|
45
|
+
const editable = disabled !== true && readOnly !== true;
|
|
46
|
+
const [rejections, setRejections] = useState([]);
|
|
47
|
+
const rejectionMessage = useRejectionMessage(maxSize);
|
|
48
|
+
const latest = useRef(files);
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
latest.current = files;
|
|
51
|
+
});
|
|
52
|
+
const set = (next) => {
|
|
53
|
+
latest.current = next;
|
|
54
|
+
if (props.multiple === true) props.onChange?.(next.length > 0 ? next : null);
|
|
55
|
+
else props.onChange?.(next[0] ?? null);
|
|
56
|
+
};
|
|
57
|
+
const upload = useUploads({
|
|
58
|
+
source: uploadSource,
|
|
59
|
+
accept,
|
|
60
|
+
maxSize,
|
|
61
|
+
onUploaded: (file) => set(multiple ? [...latest.current, file] : [file]),
|
|
62
|
+
onRejected: (file_0, reason) => setRejections((current) => [...current, rejectionMessage(file_0, reason)])
|
|
63
|
+
});
|
|
64
|
+
return /* @__PURE__ */ jsx(Fieldset, {
|
|
65
|
+
legend: label,
|
|
66
|
+
description,
|
|
67
|
+
errors: [...errors, ...rejections],
|
|
68
|
+
isRequired: required,
|
|
69
|
+
isDisabled: disabled,
|
|
70
|
+
className,
|
|
71
|
+
legendClassName: labelClassName,
|
|
72
|
+
descriptionClassName,
|
|
73
|
+
errorsClassName,
|
|
74
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
75
|
+
className: FileField_module_default.field,
|
|
76
|
+
children: [
|
|
77
|
+
files.length > 0 ? /* @__PURE__ */ jsx("ul", {
|
|
78
|
+
className: FileField_module_default.files,
|
|
79
|
+
children: files.map((file_1, index) => {
|
|
80
|
+
const { name } = file_1;
|
|
81
|
+
const url = fileUrl(file_1);
|
|
82
|
+
const size = fileSize(file_1);
|
|
83
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
84
|
+
className: FileField_module_default.file,
|
|
85
|
+
children: [
|
|
86
|
+
/* @__PURE__ */ jsx(FileText, {
|
|
87
|
+
"aria-hidden": true,
|
|
88
|
+
className: FileField_module_default.icon
|
|
89
|
+
}),
|
|
90
|
+
url !== null ? /* @__PURE__ */ jsx("a", {
|
|
91
|
+
href: url,
|
|
92
|
+
className: FileField_module_default.name,
|
|
93
|
+
target: "_blank",
|
|
94
|
+
rel: "noreferrer",
|
|
95
|
+
children: name
|
|
96
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
97
|
+
className: FileField_module_default.name,
|
|
98
|
+
children: name
|
|
99
|
+
}),
|
|
100
|
+
size !== null ? /* @__PURE__ */ jsx("span", {
|
|
101
|
+
className: FileField_module_default.size,
|
|
102
|
+
children: formatSize(size, intl.locale)
|
|
103
|
+
}) : null,
|
|
104
|
+
editable ? /* @__PURE__ */ jsx(IconButton, {
|
|
105
|
+
icon: X,
|
|
106
|
+
size: "sm",
|
|
107
|
+
label: intl.formatMessage(messages.removeFile, { name }),
|
|
108
|
+
onPress: () => set(latest.current.filter((_, at) => at !== index))
|
|
109
|
+
}) : null
|
|
110
|
+
]
|
|
111
|
+
}, `${String(index)}-${name}`);
|
|
112
|
+
})
|
|
113
|
+
}) : upload.items.length === 0 ? /* @__PURE__ */ jsx("span", {
|
|
114
|
+
className: FileField_module_default.none,
|
|
115
|
+
children: intl.formatMessage(messages.noFile)
|
|
116
|
+
}) : null,
|
|
117
|
+
/* @__PURE__ */ jsx(UploadList, {
|
|
118
|
+
items: upload.items,
|
|
119
|
+
onPause: upload.pause,
|
|
120
|
+
onResume: upload.resume,
|
|
121
|
+
onCancel: upload.cancel,
|
|
122
|
+
onRetry: upload.retry
|
|
123
|
+
}),
|
|
124
|
+
editable ? /* @__PURE__ */ jsx(FilePicker, {
|
|
125
|
+
accept,
|
|
126
|
+
multiple,
|
|
127
|
+
...!multiple && files.length > 0 ? { label: intl.formatMessage(messages.replaceFile) } : {},
|
|
128
|
+
onSelect: (selected) => {
|
|
129
|
+
setRejections([]);
|
|
130
|
+
upload.add(selected);
|
|
131
|
+
}
|
|
132
|
+
}) : null
|
|
133
|
+
]
|
|
134
|
+
})
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The registry entry of the `file` type: the field's `multiple`, `accept` (or its `accept` rule)
|
|
139
|
+
* and `maxSize` rule, with the upload source of the fields context.
|
|
140
|
+
*/
|
|
141
|
+
function FileFieldEntry(props) {
|
|
142
|
+
const $ = c(15);
|
|
143
|
+
const { field, value, onChange } = props;
|
|
144
|
+
let t0;
|
|
145
|
+
if ($[0] !== props) {
|
|
146
|
+
t0 = getFieldProps(props);
|
|
147
|
+
$[0] = props;
|
|
148
|
+
$[1] = t0;
|
|
149
|
+
} else t0 = $[1];
|
|
150
|
+
let t1;
|
|
151
|
+
if ($[2] !== field) {
|
|
152
|
+
t1 = getAccept(field);
|
|
153
|
+
$[2] = field;
|
|
154
|
+
$[3] = t1;
|
|
155
|
+
} else t1 = $[3];
|
|
156
|
+
let t2;
|
|
157
|
+
if ($[4] !== field) {
|
|
158
|
+
t2 = getMaxSize(field);
|
|
159
|
+
$[4] = field;
|
|
160
|
+
$[5] = t2;
|
|
161
|
+
} else t2 = $[5];
|
|
162
|
+
let t3;
|
|
163
|
+
if ($[6] !== t0 || $[7] !== t1 || $[8] !== t2) {
|
|
164
|
+
t3 = {
|
|
165
|
+
...t0,
|
|
166
|
+
accept: t1,
|
|
167
|
+
maxSize: t2
|
|
168
|
+
};
|
|
169
|
+
$[6] = t0;
|
|
170
|
+
$[7] = t1;
|
|
171
|
+
$[8] = t2;
|
|
172
|
+
$[9] = t3;
|
|
173
|
+
} else t3 = $[9];
|
|
174
|
+
const common = t3;
|
|
175
|
+
let t4;
|
|
176
|
+
if ($[10] !== common || $[11] !== field.multiple || $[12] !== onChange || $[13] !== value) {
|
|
177
|
+
t4 = field.multiple === true ? /* @__PURE__ */ jsx(FileField, {
|
|
178
|
+
...common,
|
|
179
|
+
multiple: true,
|
|
180
|
+
value: toFiles(value),
|
|
181
|
+
onChange
|
|
182
|
+
}) : /* @__PURE__ */ jsx(FileField, {
|
|
183
|
+
...common,
|
|
184
|
+
value: toFiles(value)[0] ?? null,
|
|
185
|
+
onChange
|
|
186
|
+
});
|
|
187
|
+
$[10] = common;
|
|
188
|
+
$[11] = field.multiple;
|
|
189
|
+
$[12] = onChange;
|
|
190
|
+
$[13] = value;
|
|
191
|
+
$[14] = t4;
|
|
192
|
+
} else t4 = $[14];
|
|
193
|
+
return t4;
|
|
194
|
+
}
|
|
195
|
+
//#endregion
|
|
196
|
+
export { FileField, FileFieldEntry, isFileValue, toFiles };
|
|
197
|
+
|
|
198
|
+
//# sourceMappingURL=FileField.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileField.js","names":["file","onChange","styles","_c"],"sources":["../../src/file/FileField.tsx"],"sourcesContent":["import { FileText, X } from 'lucide-react';\nimport { useEffect, useRef, useState } from 'react';\n\nimport type { StoredFile } from '@panneau/protocol';\nimport { Fieldset, IconButton, usePanneauIntl } from '@panneau/ui';\n\nimport { useFieldsContext } from '../shared/context';\nimport { getAccept, getFieldProps, getMaxSize } from '../shared/entry';\nimport { messages } from '../shared/messages';\nimport type { FieldEntryProps, FieldProps } from '../shared/types';\nimport { FilePicker } from '../upload/FilePicker';\nimport { UploadList } from '../upload/UploadList';\nimport { formatSize } from '../upload/files';\nimport type { UploadSource, UploadedFile } from '../upload/types';\nimport { useRejectionMessage } from '../upload/useRejectionMessage';\nimport { useUploads } from '../upload/useUploads';\nimport styles from './FileField.module.css';\n\n/**\n * A file of a `file` value: a stored file, as items give it; an upload, once the browser sent the\n * file to storage; or the file itself, sent in a multipart request without direct uploads.\n */\nexport type FileValue = StoredFile | UploadedFile | File;\n\nexport interface FileFieldBaseProps extends Omit<FieldProps<never>, 'value' | 'onChange'> {\n /** Accepted MIME types (`application/pdf`, `image/*`). Others are refused before they are sent. */\n accept?: readonly string[] | null;\n /** Largest file, in bytes. Larger ones are refused before they are sent. */\n maxSize?: number | null;\n /**\n * Where files are uploaded directly to storage. Defaults to the one of the fields context.\n * Without one, the value holds the files themselves, which the adapter sends in\n * `multipart/form-data`.\n */\n uploadSource?: UploadSource | null;\n}\n\nexport interface SingleFileFieldProps extends FileFieldBaseProps {\n multiple?: false;\n value?: FileValue | null;\n onChange?: (value: FileValue | null) => void;\n}\n\nexport interface MultipleFileFieldProps extends FileFieldBaseProps {\n multiple: true;\n value?: readonly FileValue[] | null;\n /** Called with the files. An empty list is `null`. */\n onChange?: (value: FileValue[] | null) => void;\n}\n\nexport type FileFieldProps = SingleFileFieldProps | MultipleFileFieldProps;\n\nfunction fileSize(file: FileValue): number | null {\n return typeof file.size === 'number' ? file.size : null;\n}\n\nfunction fileUrl(file: FileValue): string | null {\n return !(file instanceof Blob) && 'url' in file && typeof file.url === 'string'\n ? file.url\n : null;\n}\n\n/** Whether a value is a file of a `file` value. */\nexport function isFileValue(value: unknown): value is FileValue {\n if (typeof File !== 'undefined' && value instanceof File) {\n return true;\n }\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as { name?: unknown }).name === 'string'\n );\n}\n\n/** The files of a value, single or multiple. */\nexport function toFiles(value: unknown): FileValue[] {\n if (Array.isArray(value)) {\n return value.filter(isFileValue);\n }\n return isFileValue(value) ? [value] : [];\n}\n\n/**\n * Files that belong to an item, outside the media library: an attachment, a PDF, a CSV (the\n * `file` type). With an upload source, files go directly to storage, and the value references\n * each upload (`{ upload, name, mime, size }`). Without one, the value is the file itself. Items\n * give stored files, which the field shows with a link. A new file replaces the file of a single\n * value, and is added to a multiple one.\n */\nexport function FileField(props: FileFieldProps) {\n const intl = usePanneauIntl();\n const context = useFieldsContext();\n const {\n accept = null,\n maxSize = null,\n uploadSource = context.uploadSource,\n label,\n description,\n errors = [],\n disabled,\n readOnly,\n required,\n className,\n labelClassName,\n descriptionClassName,\n errorsClassName,\n } = props;\n const multiple = props.multiple === true;\n const files = toFiles(props.value);\n const editable = disabled !== true && readOnly !== true;\n const [rejections, setRejections] = useState<string[]>([]);\n const rejectionMessage = useRejectionMessage(maxSize);\n\n // The files as last set, so that uploads that end together all add theirs.\n const latest = useRef(files);\n useEffect(() => {\n latest.current = files;\n });\n const set = (next: FileValue[]) => {\n latest.current = next;\n if (props.multiple === true) {\n props.onChange?.(next.length > 0 ? next : null);\n } else {\n props.onChange?.(next[0] ?? null);\n }\n };\n\n const upload = useUploads({\n source: uploadSource,\n accept,\n maxSize,\n onUploaded: (file) => set(multiple ? [...latest.current, file] : [file]),\n onRejected: (file, reason) =>\n setRejections((current) => [...current, rejectionMessage(file, reason)]),\n });\n\n return (\n <Fieldset\n legend={label}\n description={description}\n errors={[...errors, ...rejections]}\n isRequired={required}\n isDisabled={disabled}\n className={className}\n legendClassName={labelClassName}\n descriptionClassName={descriptionClassName}\n errorsClassName={errorsClassName}\n >\n <div className={styles.field}>\n {files.length > 0 ? (\n <ul className={styles.files}>\n {files.map((file, index) => {\n const { name } = file;\n const url = fileUrl(file);\n const size = fileSize(file);\n return (\n <li key={`${String(index)}-${name}`} className={styles.file}>\n <FileText aria-hidden className={styles.icon} />\n {url !== null ? (\n <a\n href={url}\n className={styles.name}\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n {name}\n </a>\n ) : (\n <span className={styles.name}>{name}</span>\n )}\n {size !== null ? (\n <span className={styles.size}>\n {formatSize(size, intl.locale)}\n </span>\n ) : null}\n {editable ? (\n <IconButton\n icon={X}\n size=\"sm\"\n label={intl.formatMessage(messages.removeFile, {\n name,\n })}\n onPress={() =>\n set(latest.current.filter((_, at) => at !== index))\n }\n />\n ) : null}\n </li>\n );\n })}\n </ul>\n ) : upload.items.length === 0 ? (\n <span className={styles.none}>{intl.formatMessage(messages.noFile)}</span>\n ) : null}\n <UploadList\n items={upload.items}\n onPause={upload.pause}\n onResume={upload.resume}\n onCancel={upload.cancel}\n onRetry={upload.retry}\n />\n {editable ? (\n <FilePicker\n accept={accept}\n multiple={multiple}\n {...(!multiple && files.length > 0\n ? { label: intl.formatMessage(messages.replaceFile) }\n : {})}\n onSelect={(selected) => {\n setRejections([]);\n upload.add(selected);\n }}\n />\n ) : null}\n </div>\n </Fieldset>\n );\n}\n\n/**\n * The registry entry of the `file` type: the field's `multiple`, `accept` (or its `accept` rule)\n * and `maxSize` rule, with the upload source of the fields context.\n */\nexport function FileFieldEntry(props: FieldEntryProps) {\n const { field, value, onChange } = props;\n const common = {\n ...getFieldProps(props),\n accept: getAccept(field),\n maxSize: getMaxSize(field),\n };\n return field.multiple === true ? (\n <FileField {...common} multiple value={toFiles(value)} onChange={onChange} />\n ) : (\n <FileField {...common} value={toFiles(value)[0] ?? null} onChange={onChange} />\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import "./FileField.css";
|
|
2
|
+
//#region src/file/FileField.module.css
|
|
3
|
+
var field = "panneau-file-field-field";
|
|
4
|
+
var files = "panneau-file-field-files";
|
|
5
|
+
var file = "panneau-file-field-file";
|
|
6
|
+
var icon = "panneau-file-field-icon";
|
|
7
|
+
var name = "panneau-file-field-name";
|
|
8
|
+
var size = "panneau-file-field-size";
|
|
9
|
+
var none = "panneau-file-field-none";
|
|
10
|
+
var FileField_module_default = {
|
|
11
|
+
field,
|
|
12
|
+
files,
|
|
13
|
+
file,
|
|
14
|
+
icon,
|
|
15
|
+
name,
|
|
16
|
+
size,
|
|
17
|
+
none
|
|
18
|
+
};
|
|
19
|
+
//#endregion
|
|
20
|
+
export { FileField_module_default as default, field, file, files, icon, name, none, size };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=FileField.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileField.module.js","names":[],"sources":["../../src/file/FileField.module.css"],"sourcesContent":[".field {\n display: grid;\n gap: var(--panneau-space-2);\n}\n\n.files {\n display: grid;\n gap: var(--panneau-space-1);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.file {\n display: flex;\n align-items: center;\n gap: var(--panneau-space-2);\n min-height: var(--panneau-control-height-sm);\n padding-inline: var(--panneau-space-2) var(--panneau-space-1);\n background-color: var(--panneau-color-surface-sunken);\n border: var(--panneau-border-width) solid var(--panneau-color-border);\n border-radius: var(--panneau-radius-md);\n}\n\n.icon {\n flex: none;\n width: 1rem;\n height: 1rem;\n color: var(--panneau-color-text-muted);\n}\n\n.name {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n font-size: var(--panneau-font-size-sm);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\na.name {\n color: var(--panneau-color-link);\n}\n\n.size {\n flex: none;\n font-size: var(--panneau-font-size-sm);\n color: var(--panneau-color-text-muted);\n}\n\n.none {\n font-size: var(--panneau-font-size-md);\n color: var(--panneau-color-text-muted);\n}\n"],"mappings":""}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { type FileFieldBaseProps, FileField, FileFieldEntry, type FileFieldProps, type FileValue, type MultipleFileFieldProps, type SingleFileFieldProps, isFileValue, toFiles, } from './FileField';
|
|
2
|
+
export type { UploadItem, UploadItemStatus, UploadSource, UploadedFile, Uploader, } from '../upload/types';
|
|
3
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/file/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,kBAAkB,EACvB,SAAS,EACT,cAAc,EACd,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,WAAW,EACX,OAAO,GACV,MAAM,aAAa,CAAC;AACrB,YAAY,EACR,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,QAAQ,GACX,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { FileField, FileFieldEntry, isFileValue, toFiles } from "./FileField.js";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
export { DefinitionField, NestedField, type NestedFieldProps } from './shared/DefinitionField';
|
|
2
2
|
export { builtInFields, fieldsPlugin } from './shared/builtIn';
|
|
3
3
|
export { type BoundFieldProps, type FieldBinding, type FieldBindingProps, FieldsProvider, type FieldsContextValue, type FieldsProviderProps, getParentPath, useFieldsContext, } from './shared/context';
|
|
4
|
-
export { findRule, getFieldProps, getRuleNumber, isRequired } from './shared/entry';
|
|
4
|
+
export { findRule, getAccept, getFieldProps, getMaxSize, getRuleNumber, isRequired, } from './shared/entry';
|
|
5
5
|
export { type RelationListAdapter, type RelationReference, type RelationSource, type RelationSourceOptions, createRelationSource, toReferences, useRelationReferences, } from './shared/relation';
|
|
6
6
|
export type { FieldEntry, FieldEntryProps, FieldProps } from './shared/types';
|
|
7
7
|
export { MissingFieldEntry } from './missing/MissingField';
|
|
8
|
+
export { type FileFieldBaseProps, FileField, FileFieldEntry, type FileFieldProps, type FileValue, type MultipleFileFieldProps, type SingleFileFieldProps, isFileValue, toFiles, } from './file/FileField';
|
|
9
|
+
export { FilePicker, type FilePickerProps } from './upload/FilePicker';
|
|
10
|
+
export { UploadList, type UploadListProps } from './upload/UploadList';
|
|
11
|
+
export { formatSize, matchesMime } from './upload/files';
|
|
12
|
+
export { type UploadItem, type UploadItemStatus, type UploadRejection, type UploadSource, type UploadedFile, type Uploader, } from './upload/types';
|
|
13
|
+
export { useRejectionMessage } from './upload/useRejectionMessage';
|
|
14
|
+
export { type UseUploadsOptions, type UseUploadsResult, useUploads } from './upload/useUploads';
|
|
8
15
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC/F,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EACH,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,cAAc,EACd,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,aAAa,EACb,gBAAgB,GACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC/F,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EACH,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,cAAc,EACd,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,aAAa,EACb,gBAAgB,GACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACH,QAAQ,EACR,SAAS,EACT,aAAa,EACb,UAAU,EACV,aAAa,EACb,UAAU,GACb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACH,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,oBAAoB,EACpB,YAAY,EACZ,qBAAqB,GACxB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EACH,KAAK,kBAAkB,EACvB,SAAS,EACT,cAAc,EACd,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,WAAW,EACX,OAAO,GACV,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EACH,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,QAAQ,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { FieldsProvider, getParentPath, useFieldsContext } from "./shared/context.js";
|
|
2
|
-
import { findRule, getFieldProps, getRuleNumber, isRequired } from "./shared/entry.js";
|
|
2
|
+
import { findRule, getAccept, getFieldProps, getMaxSize, getRuleNumber, isRequired } from "./shared/entry.js";
|
|
3
3
|
import { MissingFieldEntry } from "./missing/MissingField.js";
|
|
4
|
+
import { FilePicker } from "./upload/FilePicker.js";
|
|
5
|
+
import { formatSize, matchesMime } from "./upload/files.js";
|
|
6
|
+
import { UploadList } from "./upload/UploadList.js";
|
|
7
|
+
import { useRejectionMessage } from "./upload/useRejectionMessage.js";
|
|
8
|
+
import { useUploads } from "./upload/useUploads.js";
|
|
9
|
+
import { FileField, FileFieldEntry, isFileValue, toFiles } from "./file/FileField.js";
|
|
4
10
|
import { createRelationSource, toReferences, useRelationReferences } from "./shared/relation.js";
|
|
5
11
|
import { builtInFields, fieldsPlugin } from "./shared/builtIn.js";
|
|
6
12
|
import { DefinitionField, NestedField } from "./shared/DefinitionField.js";
|
|
7
|
-
export { DefinitionField, FieldsProvider, MissingFieldEntry, NestedField, builtInFields, createRelationSource, fieldsPlugin, findRule, getFieldProps, getParentPath, getRuleNumber, isRequired, toReferences, useFieldsContext, useRelationReferences };
|
|
13
|
+
export { DefinitionField, FieldsProvider, FileField, FileFieldEntry, FilePicker, MissingFieldEntry, NestedField, UploadList, builtInFields, createRelationSource, fieldsPlugin, findRule, formatSize, getAccept, getFieldProps, getMaxSize, getParentPath, getRuleNumber, isFileValue, isRequired, matchesMime, toFiles, toReferences, useFieldsContext, useRejectionMessage, useRelationReferences, useUploads };
|
package/dist/shared/builtIn.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { FieldEntry } from './types';
|
|
2
2
|
/**
|
|
3
|
-
* The entries of the built-in field types. `richtext` and `media` come with their plugins
|
|
4
|
-
*
|
|
3
|
+
* The entries of the built-in field types. `richtext` and `media` come with their plugins
|
|
4
|
+
* (`@panneau/editor` and `@panneau/media`): without them, `DefinitionField` renders a placeholder.
|
|
5
|
+
* `file` uploads through the upload source of the fields context, or gives the files themselves.
|
|
5
6
|
*/
|
|
6
7
|
export declare const builtInFields: Readonly<Record<string, FieldEntry>>;
|
|
7
8
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builtIn.d.ts","sourceRoot":"","sources":["../../src/shared/builtIn.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"builtIn.d.ts","sourceRoot":"","sources":["../../src/shared/builtIn.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAiB9D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,YAAY,EAAE;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IAC7C,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;CAK7D,CAAC"}
|
package/dist/shared/builtIn.js
CHANGED
|
@@ -4,6 +4,7 @@ import { ColorFieldEntry } from "../color/ColorField.js";
|
|
|
4
4
|
import { DateFieldEntry } from "../date/DateField.js";
|
|
5
5
|
import { DateTimeFieldEntry } from "../datetime/DateTimeField.js";
|
|
6
6
|
import { EmailFieldEntry } from "../email/EmailField.js";
|
|
7
|
+
import { FileFieldEntry } from "../file/FileField.js";
|
|
7
8
|
import { GroupFieldEntry } from "../group/GroupField.js";
|
|
8
9
|
import { NumberFieldEntry } from "../number/NumberField.js";
|
|
9
10
|
import { RelationFieldEntry } from "../relation/RelationField.js";
|
|
@@ -16,8 +17,9 @@ import { UrlFieldEntry } from "../url/UrlField.js";
|
|
|
16
17
|
import { fr } from "@panneau/intl";
|
|
17
18
|
//#region src/shared/builtIn.ts
|
|
18
19
|
/**
|
|
19
|
-
* The entries of the built-in field types. `richtext` and `media` come with their plugins
|
|
20
|
-
*
|
|
20
|
+
* The entries of the built-in field types. `richtext` and `media` come with their plugins
|
|
21
|
+
* (`@panneau/editor` and `@panneau/media`): without them, `DefinitionField` renders a placeholder.
|
|
22
|
+
* `file` uploads through the upload source of the fields context, or gives the files themselves.
|
|
21
23
|
*/
|
|
22
24
|
var builtInFields = {
|
|
23
25
|
text: TextFieldEntry,
|
|
@@ -32,6 +34,7 @@ var builtInFields = {
|
|
|
32
34
|
url: UrlFieldEntry,
|
|
33
35
|
color: ColorFieldEntry,
|
|
34
36
|
slug: SlugFieldEntry,
|
|
37
|
+
file: FileFieldEntry,
|
|
35
38
|
relation: RelationFieldEntry,
|
|
36
39
|
group: GroupFieldEntry,
|
|
37
40
|
repeater: RepeaterFieldEntry
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builtIn.js","names":[],"sources":["../../src/shared/builtIn.ts"],"sourcesContent":["import { fr } from '@panneau/intl';\n\nimport { BooleanFieldEntry } from '../boolean/BooleanField';\nimport { ColorFieldEntry } from '../color/ColorField';\nimport { DateFieldEntry } from '../date/DateField';\nimport { DateTimeFieldEntry } from '../datetime/DateTimeField';\nimport { EmailFieldEntry } from '../email/EmailField';\nimport { GroupFieldEntry } from '../group/GroupField';\nimport { NumberFieldEntry } from '../number/NumberField';\nimport { RelationFieldEntry } from '../relation/RelationField';\nimport { RepeaterFieldEntry } from '../repeater/RepeaterField';\nimport { SelectFieldEntry } from '../select/SelectField';\nimport { SlugFieldEntry } from '../slug/SlugField';\nimport { TextFieldEntry } from '../text/TextField';\nimport { TextAreaFieldEntry } from '../textarea/TextAreaField';\nimport { TimeFieldEntry } from '../time/TimeField';\nimport { UrlFieldEntry } from '../url/UrlField';\nimport type { FieldEntry } from './types';\n\n/**\n * The entries of the built-in field types. `richtext` and `media` come with their plugins
|
|
1
|
+
{"version":3,"file":"builtIn.js","names":[],"sources":["../../src/shared/builtIn.ts"],"sourcesContent":["import { fr } from '@panneau/intl';\n\nimport { BooleanFieldEntry } from '../boolean/BooleanField';\nimport { ColorFieldEntry } from '../color/ColorField';\nimport { DateFieldEntry } from '../date/DateField';\nimport { DateTimeFieldEntry } from '../datetime/DateTimeField';\nimport { EmailFieldEntry } from '../email/EmailField';\nimport { FileFieldEntry } from '../file/FileField';\nimport { GroupFieldEntry } from '../group/GroupField';\nimport { NumberFieldEntry } from '../number/NumberField';\nimport { RelationFieldEntry } from '../relation/RelationField';\nimport { RepeaterFieldEntry } from '../repeater/RepeaterField';\nimport { SelectFieldEntry } from '../select/SelectField';\nimport { SlugFieldEntry } from '../slug/SlugField';\nimport { TextFieldEntry } from '../text/TextField';\nimport { TextAreaFieldEntry } from '../textarea/TextAreaField';\nimport { TimeFieldEntry } from '../time/TimeField';\nimport { UrlFieldEntry } from '../url/UrlField';\nimport type { FieldEntry } from './types';\n\n/**\n * The entries of the built-in field types. `richtext` and `media` come with their plugins\n * (`@panneau/editor` and `@panneau/media`): without them, `DefinitionField` renders a placeholder.\n * `file` uploads through the upload source of the fields context, or gives the files themselves.\n */\nexport const builtInFields: Readonly<Record<string, FieldEntry>> = {\n text: TextFieldEntry,\n textarea: TextAreaFieldEntry,\n number: NumberFieldEntry,\n boolean: BooleanFieldEntry,\n date: DateFieldEntry,\n datetime: DateTimeFieldEntry,\n time: TimeFieldEntry,\n select: SelectFieldEntry,\n email: EmailFieldEntry,\n url: UrlFieldEntry,\n color: ColorFieldEntry,\n slug: SlugFieldEntry,\n file: FileFieldEntry,\n relation: RelationFieldEntry,\n group: GroupFieldEntry,\n repeater: RepeaterFieldEntry,\n};\n\n/**\n * The built-in fields as a `@panneau/core` plugin: their entries, and the French messages of\n * every Panneau package (`@panneau/ui`, `@panneau/fields`, `@panneau/forms`…, from\n * `@panneau/intl`). Pass it before the plugins that override a type.\n */\nexport const fieldsPlugin: {\n id: string;\n fields: Readonly<Record<string, FieldEntry>>;\n locales: Readonly<Record<string, Record<string, string>>>;\n} = {\n id: 'panneau-fields',\n fields: builtInFields,\n locales: { fr },\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAyBA,IAAa,gBAAsD;CAC/D,MAAM;CACN,UAAU;CACV,QAAQ;CACR,SAAS;CACT,MAAM;CACN,UAAU;CACV,MAAM;CACN,QAAQ;CACR,OAAO;CACP,KAAK;CACL,OAAO;CACP,MAAM;CACN,MAAM;CACN,UAAU;CACV,OAAO;CACP,UAAU;AACd;;;;;;AAOA,IAAa,eAIT;CACA,IAAI;CACJ,QAAQ;CACR,SAAS,EAAE,GAAG;AAClB"}
|
package/dist/shared/context.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ComponentType, type ReactNode } from 'react';
|
|
2
2
|
import type { Field } from '@panneau/protocol';
|
|
3
|
+
import type { UploadSource } from '../upload/types';
|
|
3
4
|
import type { RelationSource } from './relation';
|
|
4
5
|
import type { FieldEntry } from './types';
|
|
5
6
|
/** What a binding gives a field: its value, handlers and errors at a path. */
|
|
@@ -32,6 +33,11 @@ export interface FieldsContextValue {
|
|
|
32
33
|
locales: readonly string[];
|
|
33
34
|
/** Where relation fields search for items. */
|
|
34
35
|
relationSource: RelationSource | null;
|
|
36
|
+
/**
|
|
37
|
+
* Where fields that take files upload them directly to storage. Without one, they give the
|
|
38
|
+
* files themselves.
|
|
39
|
+
*/
|
|
40
|
+
uploadSource: UploadSource | null;
|
|
35
41
|
binding: FieldBinding | null;
|
|
36
42
|
}
|
|
37
43
|
/** The fields context of the closest `FieldsProvider`. */
|
|
@@ -41,15 +47,17 @@ export interface FieldsProviderProps {
|
|
|
41
47
|
components?: Readonly<Record<string, FieldEntry>>;
|
|
42
48
|
locales?: readonly string[];
|
|
43
49
|
relationSource?: RelationSource | null;
|
|
50
|
+
/** Make it once, outside of render: fields make an uploader for each source they get. */
|
|
51
|
+
uploadSource?: UploadSource | null;
|
|
44
52
|
binding?: FieldBinding | null;
|
|
45
53
|
children?: ReactNode;
|
|
46
54
|
}
|
|
47
55
|
/**
|
|
48
56
|
* Provides what fields rendered from a definition need: the components of each type, the locales
|
|
49
|
-
* of localized values, a relation source and a binding. Values not given come from the parent
|
|
57
|
+
* of localized values, a relation source, an upload source and a binding. Values not given come from the parent
|
|
50
58
|
* provider.
|
|
51
59
|
*/
|
|
52
|
-
export declare function FieldsProvider({ components, locales, relationSource, binding, children, }: FieldsProviderProps): import("react").JSX.Element;
|
|
60
|
+
export declare function FieldsProvider({ components, locales, relationSource, uploadSource, binding, children, }: FieldsProviderProps): import("react").JSX.Element;
|
|
53
61
|
/** The parent path of a path: `blocks.2` for `blocks.2.title`, `''` for `title`. */
|
|
54
62
|
export declare function getParentPath(path: string): string;
|
|
55
63
|
//# sourceMappingURL=context.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/shared/context.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,SAAS,EAAsB,MAAM,OAAO,CAAC;AAE/E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,8EAA8E;AAC9E,MAAM,WAAW,eAAe;IAC5B,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9B;AAED,4FAA4F;AAC5F,MAAM,WAAW,iBAAiB;IAC9B,KAAK,EAAE,KAAK,CAAC;IACb,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,2FAA2F;IAC3F,UAAU,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,MAAM,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,SAAS,CAAC;CACjD;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,aAAa,CAAC,iBAAiB,CAAC,CAAC;AAE5D,MAAM,WAAW,kBAAkB;IAC/B,+DAA+D;IAC/D,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IACjD,6CAA6C;IAC7C,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,8CAA8C;IAC9C,cAAc,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;CAChC;
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/shared/context.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,SAAS,EAAsB,MAAM,OAAO,CAAC;AAE/E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,8EAA8E;AAC9E,MAAM,WAAW,eAAe;IAC5B,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9B;AAED,4FAA4F;AAC5F,MAAM,WAAW,iBAAiB;IAC9B,KAAK,EAAE,KAAK,CAAC;IACb,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,2FAA2F;IAC3F,UAAU,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,MAAM,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,SAAS,CAAC;CACjD;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,aAAa,CAAC,iBAAiB,CAAC,CAAC;AAE5D,MAAM,WAAW,kBAAkB;IAC/B,+DAA+D;IAC/D,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IACjD,6CAA6C;IAC7C,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,8CAA8C;IAC9C,cAAc,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC;;;OAGG;IACH,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;IAClC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;CAChC;AAUD,0DAA0D;AAC1D,wBAAgB,gBAAgB,IAAI,kBAAkB,CAErD;AAED,MAAM,WAAW,mBAAmB;IAChC,uEAAuE;IACvE,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IAClD,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,cAAc,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IACvC,yFAAyF;IACzF,YAAY,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACnC,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,EAAE,SAAS,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAC3B,UAAU,EACV,OAAO,EACP,cAAc,EACd,YAAY,EACZ,OAAO,EACP,QAAQ,GACX,EAAE,mBAAmB,+BAWrB;AAED,oFAAoF;AACpF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGlD"}
|