@panneau/fields 4.1.1 → 5.0.0-alpha.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/README.md +72 -0
- package/dist/boolean/BooleanField.d.ts +11 -0
- package/dist/boolean/BooleanField.d.ts.map +1 -0
- package/dist/boolean/BooleanField.js +67 -0
- package/dist/boolean/BooleanField.js.map +1 -0
- package/dist/boolean/index.d.ts +2 -0
- package/dist/boolean/index.d.ts.map +1 -0
- package/dist/boolean/index.js +1 -0
- package/dist/color/ColorField.css +33 -0
- package/dist/color/ColorField.d.ts +16 -0
- package/dist/color/ColorField.d.ts.map +1 -0
- package/dist/color/ColorField.js +187 -0
- package/dist/color/ColorField.js.map +1 -0
- package/dist/color/ColorField.module.js +14 -0
- package/dist/color/ColorField.module.js.map +1 -0
- package/dist/color/index.d.ts +2 -0
- package/dist/color/index.d.ts.map +1 -0
- package/dist/color/index.js +1 -0
- package/dist/date/DateField.d.ts +16 -0
- package/dist/date/DateField.d.ts.map +1 -0
- package/dist/date/DateField.js +132 -0
- package/dist/date/DateField.js.map +1 -0
- package/dist/date/index.d.ts +2 -0
- package/dist/date/index.d.ts.map +1 -0
- package/dist/date/index.js +1 -0
- package/dist/datetime/DateTimeField.d.ts +22 -0
- package/dist/datetime/DateTimeField.d.ts.map +1 -0
- package/dist/datetime/DateTimeField.js +78 -0
- package/dist/datetime/DateTimeField.js.map +1 -0
- package/dist/datetime/index.d.ts +2 -0
- package/dist/datetime/index.d.ts.map +1 -0
- package/dist/datetime/index.js +1 -0
- package/dist/email/EmailField.d.ts +8 -0
- package/dist/email/EmailField.d.ts.map +1 -0
- package/dist/email/EmailField.js +84 -0
- package/dist/email/EmailField.js.map +1 -0
- package/dist/email/index.d.ts +2 -0
- package/dist/email/index.d.ts.map +1 -0
- package/dist/email/index.js +1 -0
- package/dist/group/GroupField.css +14 -0
- package/dist/group/GroupField.d.ts +19 -0
- package/dist/group/GroupField.d.ts.map +1 -0
- package/dist/group/GroupField.js +140 -0
- package/dist/group/GroupField.js.map +1 -0
- package/dist/group/GroupField.module.js +8 -0
- package/dist/group/GroupField.module.js.map +1 -0
- package/dist/group/index.d.ts +2 -0
- package/dist/group/index.d.ts.map +1 -0
- package/dist/group/index.js +1 -0
- package/dist/index.d.ts +8 -12
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -134
- package/dist/locales/index.d.ts +15 -0
- package/dist/locales/index.d.ts.map +1 -0
- package/dist/locales/index.js +21 -0
- package/dist/locales/index.js.map +1 -0
- package/dist/localized/LocalizedField.css +9 -0
- package/dist/localized/LocalizedField.d.ts +36 -0
- package/dist/localized/LocalizedField.d.ts.map +1 -0
- package/dist/localized/LocalizedField.js +207 -0
- package/dist/localized/LocalizedField.js.map +1 -0
- package/dist/localized/LocalizedField.module.js +12 -0
- package/dist/localized/LocalizedField.module.js.map +1 -0
- package/dist/localized/index.d.ts +2 -0
- package/dist/localized/index.d.ts.map +1 -0
- package/dist/localized/index.js +1 -0
- package/dist/missing/MissingField.css +18 -0
- package/dist/missing/MissingField.d.ts +7 -0
- package/dist/missing/MissingField.d.ts.map +1 -0
- package/dist/missing/MissingField.js +68 -0
- package/dist/missing/MissingField.js.map +1 -0
- package/dist/missing/MissingField.module.js +14 -0
- package/dist/missing/MissingField.module.js.map +1 -0
- package/dist/number/NumberField.d.ts +23 -0
- package/dist/number/NumberField.d.ts.map +1 -0
- package/dist/number/NumberField.js +175 -0
- package/dist/number/NumberField.js.map +1 -0
- package/dist/number/index.d.ts +2 -0
- package/dist/number/index.d.ts.map +1 -0
- package/dist/number/index.js +1 -0
- package/dist/relation/RelationField.css +43 -0
- package/dist/relation/RelationField.d.ts +40 -0
- package/dist/relation/RelationField.d.ts.map +1 -0
- package/dist/relation/RelationField.js +293 -0
- package/dist/relation/RelationField.js.map +1 -0
- package/dist/relation/RelationField.module.js +20 -0
- package/dist/relation/RelationField.module.js.map +1 -0
- package/dist/relation/index.d.ts +3 -0
- package/dist/relation/index.d.ts.map +1 -0
- package/dist/relation/index.js +3 -0
- package/dist/repeater/RepeaterField.css +80 -0
- package/dist/repeater/RepeaterField.d.ts +29 -0
- package/dist/repeater/RepeaterField.d.ts.map +1 -0
- package/dist/repeater/RepeaterField.js +220 -0
- package/dist/repeater/RepeaterField.js.map +1 -0
- package/dist/repeater/RepeaterField.module.js +26 -0
- package/dist/repeater/RepeaterField.module.js.map +1 -0
- package/dist/repeater/index.d.ts +2 -0
- package/dist/repeater/index.d.ts.map +1 -0
- package/dist/repeater/index.js +1 -0
- package/dist/select/SelectField.css +5 -0
- package/dist/select/SelectField.d.ts +38 -0
- package/dist/select/SelectField.d.ts.map +1 -0
- package/dist/select/SelectField.js +434 -0
- package/dist/select/SelectField.js.map +1 -0
- package/dist/select/SelectField.module.js +8 -0
- package/dist/select/SelectField.module.js.map +1 -0
- package/dist/select/index.d.ts +2 -0
- package/dist/select/index.d.ts.map +1 -0
- package/dist/select/index.js +1 -0
- package/dist/shared/DefinitionField.d.ts +32 -0
- package/dist/shared/DefinitionField.d.ts.map +1 -0
- package/dist/shared/DefinitionField.js +153 -0
- package/dist/shared/DefinitionField.js.map +1 -0
- package/dist/shared/builtIn.d.ts +17 -0
- package/dist/shared/builtIn.d.ts.map +1 -0
- package/dist/shared/builtIn.js +52 -0
- package/dist/shared/builtIn.js.map +1 -0
- package/dist/shared/context.d.ts +55 -0
- package/dist/shared/context.d.ts.map +1 -0
- package/dist/shared/context.js +72 -0
- package/dist/shared/context.js.map +1 -0
- package/dist/shared/cx.d.ts +3 -0
- package/dist/shared/cx.d.ts.map +1 -0
- package/dist/shared/cx.js +9 -0
- package/dist/shared/cx.js.map +1 -0
- package/dist/shared/dates.d.ts +15 -0
- package/dist/shared/dates.d.ts.map +1 -0
- package/dist/shared/dates.js +49 -0
- package/dist/shared/dates.js.map +1 -0
- package/dist/shared/entry.d.ts +23 -0
- package/dist/shared/entry.d.ts.map +1 -0
- package/dist/shared/entry.js +45 -0
- package/dist/shared/entry.js.map +1 -0
- package/dist/shared/messages.d.ts +2 -0
- package/dist/shared/messages.d.ts.map +1 -0
- package/dist/shared/messages.js +1 -0
- package/dist/shared/relation.d.ts +37 -0
- package/dist/shared/relation.d.ts.map +1 -0
- package/dist/shared/relation.js +49 -0
- package/dist/shared/relation.js.map +1 -0
- package/dist/shared/types.d.ts +50 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/slug/SlugField.d.ts +24 -0
- package/dist/slug/SlugField.d.ts.map +1 -0
- package/dist/slug/SlugField.js +216 -0
- package/dist/slug/SlugField.js.map +1 -0
- package/dist/slug/index.d.ts +2 -0
- package/dist/slug/index.d.ts.map +1 -0
- package/dist/slug/index.js +1 -0
- package/dist/text/TextField.d.ts +24 -0
- package/dist/text/TextField.d.ts.map +1 -0
- package/dist/text/TextField.js +179 -0
- package/dist/text/TextField.js.map +1 -0
- package/dist/text/index.d.ts +2 -0
- package/dist/text/index.d.ts.map +1 -0
- package/dist/text/index.js +1 -0
- package/dist/textarea/TextAreaField.d.ts +14 -0
- package/dist/textarea/TextAreaField.d.ts.map +1 -0
- package/dist/textarea/TextAreaField.js +126 -0
- package/dist/textarea/TextAreaField.js.map +1 -0
- package/dist/textarea/index.d.ts +2 -0
- package/dist/textarea/index.d.ts.map +1 -0
- package/dist/textarea/index.js +1 -0
- package/dist/time/TimeField.d.ts +13 -0
- package/dist/time/TimeField.d.ts.map +1 -0
- package/dist/time/TimeField.js +49 -0
- package/dist/time/TimeField.js.map +1 -0
- package/dist/time/index.d.ts +2 -0
- package/dist/time/index.d.ts.map +1 -0
- package/dist/time/index.js +1 -0
- package/dist/url/UrlField.css +11 -0
- package/dist/url/UrlField.d.ts +16 -0
- package/dist/url/UrlField.d.ts.map +1 -0
- package/dist/url/UrlField.js +94 -0
- package/dist/url/UrlField.js.map +1 -0
- package/dist/url/UrlField.module.js +8 -0
- package/dist/url/UrlField.module.js.map +1 -0
- package/dist/url/index.d.ts +2 -0
- package/dist/url/index.d.ts.map +1 -0
- package/dist/url/index.js +1 -0
- package/package.json +108 -79
- package/assets/css/styles.css +0 -14
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DefinitionField.js","names":["field","path","value","_c"],"sources":["../../src/shared/DefinitionField.tsx"],"sourcesContent":["import { type Field, evaluateCondition } from '@panneau/protocol';\n\n// Groups, repeaters and localized values render nested fields, which render through\n// `DefinitionField`: the modules import each other, which is fine since they only use each other\n// while rendering.\nimport { LocalizedFieldEntry } from '../localized/LocalizedField';\nimport { MissingFieldEntry } from '../missing/MissingField';\nimport { builtInFields } from './builtIn';\nimport { getParentPath, useFieldsContext } from './context';\nimport type { FieldEntryProps } from './types';\n\nexport interface NestedFieldProps {\n field: Field;\n /** Dot path of the field's value. */\n path: string;\n /** The value, without a binding. */\n value?: unknown;\n /** Sets the value, without a binding. */\n onChange?: (value: unknown) => void;\n onBlur?: () => void;\n errors?: readonly string[];\n /** The object the field belongs to, for conditions and generated values. */\n parentValue?: unknown;\n disabled?: boolean;\n readOnly?: boolean;\n className?: string;\n}\n\n/**\n * A field inside another one (a group, a block, a localized value), or at the top of a form.\n * With a binding in context, the binding gives its value and decides whether it shows. Without\n * one, it uses `value` and `onChange`, and hides itself when its `visibleWhen` condition on the\n * parent value is false.\n */\nexport function NestedField({\n field,\n path,\n value,\n onChange,\n onBlur,\n errors,\n parentValue,\n disabled,\n readOnly,\n className,\n}: NestedFieldProps) {\n const { binding: Binding } = useFieldsContext();\n const shared = {\n field,\n path,\n parentValue,\n ...(disabled !== undefined ? { disabled } : {}),\n ...(readOnly !== undefined ? { readOnly } : {}),\n ...(className !== undefined ? { className } : {}),\n };\n if (Binding !== null) {\n return (\n <Binding\n field={field}\n path={path}\n parentPath={getParentPath(path)}\n render={(bound) => <DefinitionField {...shared} {...bound} />}\n />\n );\n }\n if (field.visibleWhen !== undefined && !evaluateCondition(field.visibleWhen, parentValue)) {\n return null;\n }\n return (\n <DefinitionField\n {...shared}\n value={value}\n onChange={onChange ?? (() => undefined)}\n {...(onBlur !== undefined ? { onBlur } : {})}\n {...(errors !== undefined ? { errors } : {})}\n />\n );\n}\n\n/**\n * Renders a field of the definition with the entry of its type: from the fields context, or a\n * built-in one. A localized field renders an input per locale. A type without an entry renders a\n * placeholder that names the missing plugin.\n */\nexport function DefinitionField(props: FieldEntryProps) {\n const { components } = useFieldsContext();\n const { field } = props;\n if (field.localized === true) {\n return <LocalizedFieldEntry {...props} />;\n }\n const Entry = components[field.type] ?? builtInFields[field.type] ?? MissingFieldEntry;\n return <Entry {...props} />;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { FieldEntry } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* The entries of the built-in field types. `richtext` and `media` come with their plugins (Phase
|
|
4
|
+
* 2): until then, `DefinitionField` renders a placeholder for them.
|
|
5
|
+
*/
|
|
6
|
+
export declare const builtInFields: Readonly<Record<string, FieldEntry>>;
|
|
7
|
+
/**
|
|
8
|
+
* The built-in fields as a `@panneau/core` plugin: their entries, and the French messages of
|
|
9
|
+
* every Panneau package (`@panneau/ui`, `@panneau/fields`, `@panneau/forms`…, from
|
|
10
|
+
* `@panneau/intl`). Pass it before the plugins that override a type.
|
|
11
|
+
*/
|
|
12
|
+
export declare const fieldsPlugin: {
|
|
13
|
+
id: string;
|
|
14
|
+
fields: Readonly<Record<string, FieldEntry>>;
|
|
15
|
+
locales: Readonly<Record<string, Record<string, string>>>;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=builtIn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builtIn.d.ts","sourceRoot":"","sources":["../../src/shared/builtIn.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAgB9D,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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { BooleanFieldEntry } from "../boolean/BooleanField.js";
|
|
2
|
+
import { TextFieldEntry } from "../text/TextField.js";
|
|
3
|
+
import { ColorFieldEntry } from "../color/ColorField.js";
|
|
4
|
+
import { DateFieldEntry } from "../date/DateField.js";
|
|
5
|
+
import { DateTimeFieldEntry } from "../datetime/DateTimeField.js";
|
|
6
|
+
import { EmailFieldEntry } from "../email/EmailField.js";
|
|
7
|
+
import { GroupFieldEntry } from "../group/GroupField.js";
|
|
8
|
+
import { NumberFieldEntry } from "../number/NumberField.js";
|
|
9
|
+
import { RelationFieldEntry } from "../relation/RelationField.js";
|
|
10
|
+
import { RepeaterFieldEntry } from "../repeater/RepeaterField.js";
|
|
11
|
+
import { SelectFieldEntry } from "../select/SelectField.js";
|
|
12
|
+
import { SlugFieldEntry } from "../slug/SlugField.js";
|
|
13
|
+
import { TextAreaFieldEntry } from "../textarea/TextAreaField.js";
|
|
14
|
+
import { TimeFieldEntry } from "../time/TimeField.js";
|
|
15
|
+
import { UrlFieldEntry } from "../url/UrlField.js";
|
|
16
|
+
import { fr } from "@panneau/intl";
|
|
17
|
+
//#region src/shared/builtIn.ts
|
|
18
|
+
/**
|
|
19
|
+
* The entries of the built-in field types. `richtext` and `media` come with their plugins (Phase
|
|
20
|
+
* 2): until then, `DefinitionField` renders a placeholder for them.
|
|
21
|
+
*/
|
|
22
|
+
var builtInFields = {
|
|
23
|
+
text: TextFieldEntry,
|
|
24
|
+
textarea: TextAreaFieldEntry,
|
|
25
|
+
number: NumberFieldEntry,
|
|
26
|
+
boolean: BooleanFieldEntry,
|
|
27
|
+
date: DateFieldEntry,
|
|
28
|
+
datetime: DateTimeFieldEntry,
|
|
29
|
+
time: TimeFieldEntry,
|
|
30
|
+
select: SelectFieldEntry,
|
|
31
|
+
email: EmailFieldEntry,
|
|
32
|
+
url: UrlFieldEntry,
|
|
33
|
+
color: ColorFieldEntry,
|
|
34
|
+
slug: SlugFieldEntry,
|
|
35
|
+
relation: RelationFieldEntry,
|
|
36
|
+
group: GroupFieldEntry,
|
|
37
|
+
repeater: RepeaterFieldEntry
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* The built-in fields as a `@panneau/core` plugin: their entries, and the French messages of
|
|
41
|
+
* every Panneau package (`@panneau/ui`, `@panneau/fields`, `@panneau/forms`…, from
|
|
42
|
+
* `@panneau/intl`). Pass it before the plugins that override a type.
|
|
43
|
+
*/
|
|
44
|
+
var fieldsPlugin = {
|
|
45
|
+
id: "panneau-fields",
|
|
46
|
+
fields: builtInFields,
|
|
47
|
+
locales: { fr }
|
|
48
|
+
};
|
|
49
|
+
//#endregion
|
|
50
|
+
export { builtInFields, fieldsPlugin };
|
|
51
|
+
|
|
52
|
+
//# sourceMappingURL=builtIn.js.map
|
|
@@ -0,0 +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 (Phase\n * 2): until then, `DefinitionField` renders a placeholder for them.\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 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":";;;;;;;;;;;;;;;;;;;;;AAuBA,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,UAAU;CACV,OAAO;CACP,UAAU;AACd;;;;;;AAOA,IAAa,eAIT;CACA,IAAI;CACJ,QAAQ;CACR,SAAS,EAAE,GAAG;AAClB"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type ComponentType, type ReactNode } from 'react';
|
|
2
|
+
import type { Field } from '@panneau/protocol';
|
|
3
|
+
import type { RelationSource } from './relation';
|
|
4
|
+
import type { FieldEntry } from './types';
|
|
5
|
+
/** What a binding gives a field: its value, handlers and errors at a path. */
|
|
6
|
+
export interface BoundFieldProps {
|
|
7
|
+
value: unknown;
|
|
8
|
+
onChange: (value: unknown) => void;
|
|
9
|
+
onBlur?: () => void;
|
|
10
|
+
errors?: readonly string[];
|
|
11
|
+
}
|
|
12
|
+
/** Props of a binding: it decides whether a field shows, and where its value comes from. */
|
|
13
|
+
export interface FieldBindingProps {
|
|
14
|
+
field: Field;
|
|
15
|
+
/** Dot path of the field's value. */
|
|
16
|
+
path: string;
|
|
17
|
+
/** Dot path of the object the field belongs to: `''` at the top, `blocks.2` in a block. */
|
|
18
|
+
parentPath: string;
|
|
19
|
+
/** Renders the field with its bound value. */
|
|
20
|
+
render: (bound: BoundFieldProps) => ReactNode;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Binds nested fields (in groups, blocks and localized values) to a store by path, instead of the
|
|
24
|
+
* value and `onChange` of their parent. `@panneau/forms` binds them to its form state, so that
|
|
25
|
+
* every value is set, validated and shown in error by its own path.
|
|
26
|
+
*/
|
|
27
|
+
export type FieldBinding = ComponentType<FieldBindingProps>;
|
|
28
|
+
export interface FieldsContextValue {
|
|
29
|
+
/** Registry entries, by field type, over the built-in ones. */
|
|
30
|
+
components: Readonly<Record<string, FieldEntry>>;
|
|
31
|
+
/** Locales of localized values, in order. */
|
|
32
|
+
locales: readonly string[];
|
|
33
|
+
/** Where relation fields search for items. */
|
|
34
|
+
relationSource: RelationSource | null;
|
|
35
|
+
binding: FieldBinding | null;
|
|
36
|
+
}
|
|
37
|
+
/** The fields context of the closest `FieldsProvider`. */
|
|
38
|
+
export declare function useFieldsContext(): FieldsContextValue;
|
|
39
|
+
export interface FieldsProviderProps {
|
|
40
|
+
/** Registry entries by type, added over those of a parent provider. */
|
|
41
|
+
components?: Readonly<Record<string, FieldEntry>>;
|
|
42
|
+
locales?: readonly string[];
|
|
43
|
+
relationSource?: RelationSource | null;
|
|
44
|
+
binding?: FieldBinding | null;
|
|
45
|
+
children?: ReactNode;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 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
|
|
50
|
+
* provider.
|
|
51
|
+
*/
|
|
52
|
+
export declare function FieldsProvider({ components, locales, relationSource, binding, children, }: FieldsProviderProps): import("react").JSX.Element;
|
|
53
|
+
/** The parent path of a path: `blocks.2` for `blocks.2.title`, `''` for `title`. */
|
|
54
|
+
export declare function getParentPath(path: string): string;
|
|
55
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +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;AASD,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,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,OAAO,EACP,QAAQ,GACX,EAAE,mBAAmB,+BAUrB;AAED,oFAAoF;AACpF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGlD"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { c } from "react/compiler-runtime";
|
|
2
|
+
import { createContext, use } from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
//#region src/shared/context.tsx
|
|
5
|
+
var FieldsContext = createContext({
|
|
6
|
+
components: {},
|
|
7
|
+
locales: [],
|
|
8
|
+
relationSource: null,
|
|
9
|
+
binding: null
|
|
10
|
+
});
|
|
11
|
+
/** The fields context of the closest `FieldsProvider`. */
|
|
12
|
+
function useFieldsContext() {
|
|
13
|
+
return use(FieldsContext);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Provides what fields rendered from a definition need: the components of each type, the locales
|
|
17
|
+
* of localized values, a relation source and a binding. Values not given come from the parent
|
|
18
|
+
* provider.
|
|
19
|
+
*/
|
|
20
|
+
function FieldsProvider(t0) {
|
|
21
|
+
const $ = c(11);
|
|
22
|
+
const { components, locales, relationSource, binding, children } = t0;
|
|
23
|
+
const parent = use(FieldsContext);
|
|
24
|
+
let t1;
|
|
25
|
+
if ($[0] !== components || $[1] !== parent.components) {
|
|
26
|
+
t1 = components !== void 0 ? {
|
|
27
|
+
...parent.components,
|
|
28
|
+
...components
|
|
29
|
+
} : parent.components;
|
|
30
|
+
$[0] = components;
|
|
31
|
+
$[1] = parent.components;
|
|
32
|
+
$[2] = t1;
|
|
33
|
+
} else t1 = $[2];
|
|
34
|
+
const t2 = locales ?? parent.locales;
|
|
35
|
+
const t3 = relationSource === void 0 ? parent.relationSource : relationSource;
|
|
36
|
+
const t4 = binding === void 0 ? parent.binding : binding;
|
|
37
|
+
let t5;
|
|
38
|
+
if ($[3] !== t1 || $[4] !== t2 || $[5] !== t3 || $[6] !== t4) {
|
|
39
|
+
t5 = {
|
|
40
|
+
components: t1,
|
|
41
|
+
locales: t2,
|
|
42
|
+
relationSource: t3,
|
|
43
|
+
binding: t4
|
|
44
|
+
};
|
|
45
|
+
$[3] = t1;
|
|
46
|
+
$[4] = t2;
|
|
47
|
+
$[5] = t3;
|
|
48
|
+
$[6] = t4;
|
|
49
|
+
$[7] = t5;
|
|
50
|
+
} else t5 = $[7];
|
|
51
|
+
const value = t5;
|
|
52
|
+
let t6;
|
|
53
|
+
if ($[8] !== children || $[9] !== value) {
|
|
54
|
+
t6 = /* @__PURE__ */ jsx(FieldsContext, {
|
|
55
|
+
value,
|
|
56
|
+
children
|
|
57
|
+
});
|
|
58
|
+
$[8] = children;
|
|
59
|
+
$[9] = value;
|
|
60
|
+
$[10] = t6;
|
|
61
|
+
} else t6 = $[10];
|
|
62
|
+
return t6;
|
|
63
|
+
}
|
|
64
|
+
/** The parent path of a path: `blocks.2` for `blocks.2.title`, `''` for `title`. */
|
|
65
|
+
function getParentPath(path) {
|
|
66
|
+
const index = path.lastIndexOf(".");
|
|
67
|
+
return index === -1 ? "" : path.slice(0, index);
|
|
68
|
+
}
|
|
69
|
+
//#endregion
|
|
70
|
+
export { FieldsProvider, getParentPath, useFieldsContext };
|
|
71
|
+
|
|
72
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","names":["value","children","_c"],"sources":["../../src/shared/context.tsx"],"sourcesContent":["import { type ComponentType, type ReactNode, createContext, use } from 'react';\n\nimport type { Field } from '@panneau/protocol';\n\nimport type { RelationSource } from './relation';\nimport type { FieldEntry } from './types';\n\n/** What a binding gives a field: its value, handlers and errors at a path. */\nexport interface BoundFieldProps {\n value: unknown;\n onChange: (value: unknown) => void;\n onBlur?: () => void;\n errors?: readonly string[];\n}\n\n/** Props of a binding: it decides whether a field shows, and where its value comes from. */\nexport interface FieldBindingProps {\n field: Field;\n /** Dot path of the field's value. */\n path: string;\n /** Dot path of the object the field belongs to: `''` at the top, `blocks.2` in a block. */\n parentPath: string;\n /** Renders the field with its bound value. */\n render: (bound: BoundFieldProps) => ReactNode;\n}\n\n/**\n * Binds nested fields (in groups, blocks and localized values) to a store by path, instead of the\n * value and `onChange` of their parent. `@panneau/forms` binds them to its form state, so that\n * every value is set, validated and shown in error by its own path.\n */\nexport type FieldBinding = ComponentType<FieldBindingProps>;\n\nexport interface FieldsContextValue {\n /** Registry entries, by field type, over the built-in ones. */\n components: Readonly<Record<string, FieldEntry>>;\n /** Locales of localized values, in order. */\n locales: readonly string[];\n /** Where relation fields search for items. */\n relationSource: RelationSource | null;\n binding: FieldBinding | null;\n}\n\nconst FieldsContext = createContext<FieldsContextValue>({\n components: {},\n locales: [],\n relationSource: null,\n binding: null,\n});\n\n/** The fields context of the closest `FieldsProvider`. */\nexport function useFieldsContext(): FieldsContextValue {\n return use(FieldsContext);\n}\n\nexport interface FieldsProviderProps {\n /** Registry entries by type, added over those of a parent provider. */\n components?: Readonly<Record<string, FieldEntry>>;\n locales?: readonly string[];\n relationSource?: RelationSource | null;\n binding?: FieldBinding | null;\n children?: ReactNode;\n}\n\n/**\n * Provides what fields rendered from a definition need: the components of each type, the locales\n * of localized values, a relation source and a binding. Values not given come from the parent\n * provider.\n */\nexport function FieldsProvider({\n components,\n locales,\n relationSource,\n binding,\n children,\n}: FieldsProviderProps) {\n const parent = use(FieldsContext);\n const value: FieldsContextValue = {\n components:\n components !== undefined ? { ...parent.components, ...components } : parent.components,\n locales: locales ?? parent.locales,\n relationSource: relationSource === undefined ? parent.relationSource : relationSource,\n binding: binding === undefined ? parent.binding : binding,\n };\n return <FieldsContext value={value}>{children}</FieldsContext>;\n}\n\n/** The parent path of a path: `blocks.2` for `blocks.2.title`, `''` for `title`. */\nexport function getParentPath(path: string): string {\n const index = path.lastIndexOf('.');\n return index === -1 ? '' : path.slice(0, index);\n}\n"],"mappings":";;;;AA2CA,IAAM,gBAAgB,cAAkC;CACpD,YAAY,CAAC;CACb,SAAS,CAAA;CACT,gBAAgB;CAChB,SAAS;AACb,CAAC;;AAGD,SAAgB,mBAAuC;CACnD,OAAO,IAAI,aAAa;AAC5B;;;;;;AAgBA,SAAO,eAAA,IAAA;CAAA,MAAA,IAAAE,EAAA,EAAA;CAAwB,MAAA,EAAA,YAAA,SAAA,gBAAA,SAAA,aAAA;CAO3B,MAAA,SAAe,IAAI,aAAa;CAAE,IAAA;CAAA,IAAA,EAAA,OAAA,cAAA,EAAA,OAAA,OAAA,YAAA;EAG1B,KAAA,eAAe,KAAA,IAAf;GAAA,GAAgC,OAAM;GAAW,GAAK;EAA+B,IAAhB,OAAM;EAAW,EAAA,KAAA;EAAA,EAAA,KAAA,OAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CACjF,MAAA,KAAA,WAAW,OAAM;CACV,MAAA,KAAA,mBAAmB,KAAA,IAAY,OAAM,iBAArC;CACP,MAAA,KAAA,YAAY,KAAA,IAAY,OAAM,UAA9B;CAAgD,IAAA;CAAA,IAAA,EAAA,OAAA,MAAA,EAAA,OAAA,MAAA,EAAA,OAAA,MAAA,EAAA,OAAA,IAAA;EAL3B,KAAA;GAAA,YAE1B;GAAsF,SACjF;GAAyB,gBAClB;GAAqE,SAC5E;EACb;EAAC,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAND,MAAA,QAAkC;CAMhC,IAAA;CAAA,IAAA,EAAA,OAAA,YAAA,EAAA,OAAA,OAAA;EACK,KAAA,oBAAC,eAAD;GAAsB;GAAQ;EAAhB,CAAA;EAAyC,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,MAAA;CAAA,OAAA,KAAA,EAAA;CAAA,OAAvD;AAAuD;;AAIlE,SAAgB,cAAc,MAAsB;CAChD,MAAM,QAAQ,KAAK,YAAY,GAAG;CAClC,OAAO,UAAU,KAAK,KAAK,KAAK,MAAM,GAAG,KAAK;AAClD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cx.d.ts","sourceRoot":"","sources":["../../src/shared/cx.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,wBAAgB,EAAE,CAAC,GAAG,UAAU,EAAE,CAAC,MAAM,GAAG,KAAK,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,GAAG,MAAM,CAE/E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cx.js","names":[],"sources":["../../src/shared/cx.ts"],"sourcesContent":["/** Joins class names, skipping empty ones. */\nexport function cx(...classNames: (string | false | null | undefined)[]): string {\n return classNames.filter(Boolean).join(' ');\n}\n"],"mappings":";;AACA,SAAgB,GAAG,GAAG,YAA2D;CAC7E,OAAO,WAAW,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;AAC9C"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type CalendarDate, type CalendarDateTime, type Time, type ZonedDateTime } from '@internationalized/date';
|
|
2
|
+
/** `2026-10-15`, or the date of a date-time. */
|
|
3
|
+
export declare function toCalendarDate(value: string | null | undefined): CalendarDate | null;
|
|
4
|
+
/**
|
|
5
|
+
* A date-time: with an offset or `Z` (`2026-10-15T13:30:00Z`), in the given time zone; without one
|
|
6
|
+
* (`2026-10-15 09:30:00`), as is.
|
|
7
|
+
*/
|
|
8
|
+
export declare function toDateTime(value: string | null | undefined, timeZone?: string): ZonedDateTime | CalendarDateTime | null;
|
|
9
|
+
/** Writes a date-time: in UTC (`…Z`) when it has a time zone, without an offset otherwise. */
|
|
10
|
+
export declare function fromDateTime(value: ZonedDateTime | CalendarDateTime): string;
|
|
11
|
+
/** `09:30` or `09:30:15`. */
|
|
12
|
+
export declare function toTime(value: string | null | undefined): Time | null;
|
|
13
|
+
/** Writes a time as `HH:mm`, or `HH:mm:ss` with seconds. */
|
|
14
|
+
export declare function fromTime(value: Pick<Time, 'hour' | 'minute' | 'second'>, withSeconds?: boolean): string;
|
|
15
|
+
//# sourceMappingURL=dates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dates.d.ts","sourceRoot":"","sources":["../../src/shared/dates.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,IAAI,EACT,KAAK,aAAa,EAMrB,MAAM,yBAAyB,CAAC;AAEjC,gDAAgD;AAChD,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,YAAY,GAAG,IAAI,CAUpF;AAID;;;GAGG;AACH,wBAAgB,UAAU,CACtB,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAChC,QAAQ,GAAE,MAA2B,GACtC,aAAa,GAAG,gBAAgB,GAAG,IAAI,CAUzC;AAED,8FAA8F;AAC9F,wBAAgB,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,gBAAgB,GAAG,MAAM,CAI5E;AAED,6BAA6B;AAC7B,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,GAAG,IAAI,CASpE;AAED,4DAA4D;AAC5D,wBAAgB,QAAQ,CACpB,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC,EAC/C,WAAW,UAAQ,GACpB,MAAM,CAIR"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { getLocalTimeZone, parseAbsolute, parseDate, parseDateTime, parseTime } from "@internationalized/date";
|
|
2
|
+
//#region src/shared/dates.ts
|
|
3
|
+
/** `2026-10-15`, or the date of a date-time. */
|
|
4
|
+
function toCalendarDate(value) {
|
|
5
|
+
const match = value?.match(/^(\d{4}-\d{2}-\d{2})/);
|
|
6
|
+
if (match?.[1] === void 0) return null;
|
|
7
|
+
try {
|
|
8
|
+
return parseDate(match[1]);
|
|
9
|
+
} catch {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
var OFFSET = /(?:Z|[+-]\d{2}:?\d{2})$/i;
|
|
14
|
+
/**
|
|
15
|
+
* A date-time: with an offset or `Z` (`2026-10-15T13:30:00Z`), in the given time zone; without one
|
|
16
|
+
* (`2026-10-15 09:30:00`), as is.
|
|
17
|
+
*/
|
|
18
|
+
function toDateTime(value, timeZone = getLocalTimeZone()) {
|
|
19
|
+
if (value === null || value === void 0 || value.trim() === "") return null;
|
|
20
|
+
const iso = value.trim().replace(" ", "T");
|
|
21
|
+
try {
|
|
22
|
+
return OFFSET.test(iso) ? parseAbsolute(iso, timeZone) : parseDateTime(iso);
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** Writes a date-time: in UTC (`…Z`) when it has a time zone, without an offset otherwise. */
|
|
28
|
+
function fromDateTime(value) {
|
|
29
|
+
return "timeZone" in value ? value.toAbsoluteString().replace(/\.000Z$/, "Z") : value.toString();
|
|
30
|
+
}
|
|
31
|
+
/** `09:30` or `09:30:15`. */
|
|
32
|
+
function toTime(value) {
|
|
33
|
+
if (value === null || value === void 0 || !/^\d{1,2}:\d{2}(:\d{2})?/.test(value)) return null;
|
|
34
|
+
try {
|
|
35
|
+
return parseTime(value.length < 5 ? `0${value}` : value);
|
|
36
|
+
} catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** Writes a time as `HH:mm`, or `HH:mm:ss` with seconds. */
|
|
41
|
+
function fromTime(value, withSeconds = false) {
|
|
42
|
+
const pad = (part) => String(part).padStart(2, "0");
|
|
43
|
+
const time = `${pad(value.hour)}:${pad(value.minute)}`;
|
|
44
|
+
return withSeconds ? `${time}:${pad(value.second)}` : time;
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { fromDateTime, fromTime, toCalendarDate, toDateTime, toTime };
|
|
48
|
+
|
|
49
|
+
//# sourceMappingURL=dates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dates.js","names":[],"sources":["../../src/shared/dates.ts"],"sourcesContent":["import {\n type CalendarDate,\n type CalendarDateTime,\n type Time,\n type ZonedDateTime,\n getLocalTimeZone,\n parseAbsolute,\n parseDate,\n parseDateTime,\n parseTime,\n} from '@internationalized/date';\n\n/** `2026-10-15`, or the date of a date-time. */\nexport function toCalendarDate(value: string | null | undefined): CalendarDate | null {\n const match = value?.match(/^(\\d{4}-\\d{2}-\\d{2})/);\n if (match?.[1] === undefined) {\n return null;\n }\n try {\n return parseDate(match[1]);\n } catch {\n return null;\n }\n}\n\nconst OFFSET = /(?:Z|[+-]\\d{2}:?\\d{2})$/i;\n\n/**\n * A date-time: with an offset or `Z` (`2026-10-15T13:30:00Z`), in the given time zone; without one\n * (`2026-10-15 09:30:00`), as is.\n */\nexport function toDateTime(\n value: string | null | undefined,\n timeZone: string = getLocalTimeZone(),\n): ZonedDateTime | CalendarDateTime | null {\n if (value === null || value === undefined || value.trim() === '') {\n return null;\n }\n const iso = value.trim().replace(' ', 'T');\n try {\n return OFFSET.test(iso) ? parseAbsolute(iso, timeZone) : parseDateTime(iso);\n } catch {\n return null;\n }\n}\n\n/** Writes a date-time: in UTC (`…Z`) when it has a time zone, without an offset otherwise. */\nexport function fromDateTime(value: ZonedDateTime | CalendarDateTime): string {\n return 'timeZone' in value\n ? value.toAbsoluteString().replace(/\\.000Z$/, 'Z')\n : value.toString();\n}\n\n/** `09:30` or `09:30:15`. */\nexport function toTime(value: string | null | undefined): Time | null {\n if (value === null || value === undefined || !/^\\d{1,2}:\\d{2}(:\\d{2})?/.test(value)) {\n return null;\n }\n try {\n return parseTime(value.length < 5 ? `0${value}` : value);\n } catch {\n return null;\n }\n}\n\n/** Writes a time as `HH:mm`, or `HH:mm:ss` with seconds. */\nexport function fromTime(\n value: Pick<Time, 'hour' | 'minute' | 'second'>,\n withSeconds = false,\n): string {\n const pad = (part: number) => String(part).padStart(2, '0');\n const time = `${pad(value.hour)}:${pad(value.minute)}`;\n return withSeconds ? `${time}:${pad(value.second)}` : time;\n}\n"],"mappings":";;;AAaA,SAAgB,eAAe,OAAuD;CAClF,MAAM,QAAQ,OAAO,MAAM,sBAAsB;CACjD,IAAI,QAAQ,OAAO,KAAA,GACf,OAAO;CAEX,IAAI;EACA,OAAO,UAAU,MAAM,EAAE;CAC7B,QAAQ;EACJ,OAAO;CACX;AACJ;AAEA,IAAM,SAAS;;;;;AAMf,SAAgB,WACZ,OACA,WAAmB,iBAAiB,GACG;CACvC,IAAI,UAAU,QAAQ,UAAU,KAAA,KAAa,MAAM,KAAK,MAAM,IAC1D,OAAO;CAEX,MAAM,MAAM,MAAM,KAAK,CAAC,CAAC,QAAQ,KAAK,GAAG;CACzC,IAAI;EACA,OAAO,OAAO,KAAK,GAAG,IAAI,cAAc,KAAK,QAAQ,IAAI,cAAc,GAAG;CAC9E,QAAQ;EACJ,OAAO;CACX;AACJ;;AAGA,SAAgB,aAAa,OAAiD;CAC1E,OAAO,cAAc,QACf,MAAM,iBAAiB,CAAC,CAAC,QAAQ,WAAW,GAAG,IAC/C,MAAM,SAAS;AACzB;;AAGA,SAAgB,OAAO,OAA+C;CAClE,IAAI,UAAU,QAAQ,UAAU,KAAA,KAAa,CAAC,0BAA0B,KAAK,KAAK,GAC9E,OAAO;CAEX,IAAI;EACA,OAAO,UAAU,MAAM,SAAS,IAAI,IAAI,UAAU,KAAK;CAC3D,QAAQ;EACJ,OAAO;CACX;AACJ;;AAGA,SAAgB,SACZ,OACA,cAAc,OACR;CACN,MAAM,OAAO,SAAiB,OAAO,IAAI,CAAC,CAAC,SAAS,GAAG,GAAG;CAC1D,MAAM,OAAO,GAAG,IAAI,MAAM,IAAI,EAAC,GAAI,IAAI,MAAM,MAAM;CACnD,OAAO,cAAc,GAAG,KAAI,GAAI,IAAI,MAAM,MAAM,MAAM;AAC1D"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Field, Rule } from '@panneau/protocol';
|
|
2
|
+
import type { FieldEntryProps, FieldProps } from './types';
|
|
3
|
+
/** The rule of a field with this name, if any. */
|
|
4
|
+
export declare function findRule(field: Field, rule: Rule['rule']): Rule | undefined;
|
|
5
|
+
/** The number of a `min` or `max` rule of a field, if any. */
|
|
6
|
+
export declare function getRuleNumber(field: Field, rule: 'min' | 'max'): number | undefined;
|
|
7
|
+
/** Whether a field has the `required` rule. */
|
|
8
|
+
export declare function isRequired(field: Field): boolean;
|
|
9
|
+
/** A setting of a field (`settings.<name>`), when it has the expected type. */
|
|
10
|
+
export declare function getSetting<T>(field: Field, name: string, guard: (value: unknown) => value is T): T | undefined;
|
|
11
|
+
export declare const isString: (value: unknown) => value is string;
|
|
12
|
+
export declare const isNumber: (value: unknown) => value is number;
|
|
13
|
+
/**
|
|
14
|
+
* The props that every field takes, from a registry entry's props: label (the field's name
|
|
15
|
+
* without one), help, required mark, read-only, errors and handlers. Plugins use it to write their
|
|
16
|
+
* own entries.
|
|
17
|
+
*/
|
|
18
|
+
export declare function getFieldProps(props: FieldEntryProps): Omit<FieldProps<never>, 'value' | 'onChange'> & {
|
|
19
|
+
placeholder?: string;
|
|
20
|
+
};
|
|
21
|
+
/** The value as a string, or `null`. */
|
|
22
|
+
export declare function asString(value: unknown): string | null;
|
|
23
|
+
//# sourceMappingURL=entry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../../src/shared/entry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE3D,kDAAkD;AAClD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS,CAE3E;AAED,8DAA8D;AAC9D,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,SAAS,CAKnF;AAED,+CAA+C;AAC/C,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAEhD;AAED,+EAA+E;AAC/E,wBAAgB,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,KAAK,IAAI,CAAC,iBAG9F;AAED,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAAmC,CAAC;AACvF,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MACE,CAAC;AAEtD;;;;GAIG;AACH,wBAAgB,aAAa,CACzB,KAAK,EAAE,eAAe,GACvB,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC,GAAG;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAe1E;AAED,wCAAwC;AACxC,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAEtD"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//#region src/shared/entry.ts
|
|
2
|
+
/** The rule of a field with this name, if any. */
|
|
3
|
+
function findRule(field, rule) {
|
|
4
|
+
return field.rules?.find((candidate) => candidate.rule === rule);
|
|
5
|
+
}
|
|
6
|
+
/** The number of a `min` or `max` rule of a field, if any. */
|
|
7
|
+
function getRuleNumber(field, rule) {
|
|
8
|
+
const found = findRule(field, rule);
|
|
9
|
+
return found !== void 0 && "value" in found && typeof found.value === "number" ? found.value : void 0;
|
|
10
|
+
}
|
|
11
|
+
/** Whether a field has the `required` rule. */
|
|
12
|
+
function isRequired(field) {
|
|
13
|
+
return findRule(field, "required") !== void 0;
|
|
14
|
+
}
|
|
15
|
+
/** A setting of a field (`settings.<name>`), when it has the expected type. */
|
|
16
|
+
function getSetting(field, name, guard) {
|
|
17
|
+
const value = field.settings?.[name];
|
|
18
|
+
return guard(value) ? value : void 0;
|
|
19
|
+
}
|
|
20
|
+
var isString = (value) => typeof value === "string";
|
|
21
|
+
var isNumber = (value) => typeof value === "number" && !Number.isNaN(value);
|
|
22
|
+
/**
|
|
23
|
+
* The props that every field takes, from a registry entry's props: label (the field's name
|
|
24
|
+
* without one), help, required mark, read-only, errors and handlers. Plugins use it to write their
|
|
25
|
+
* own entries.
|
|
26
|
+
*/
|
|
27
|
+
function getFieldProps(props) {
|
|
28
|
+
const { field } = props;
|
|
29
|
+
return {
|
|
30
|
+
name: props.path,
|
|
31
|
+
label: field.label ?? field.name,
|
|
32
|
+
description: field.help,
|
|
33
|
+
...field.placeholder !== void 0 ? { placeholder: field.placeholder } : {},
|
|
34
|
+
required: isRequired(field),
|
|
35
|
+
readOnly: props.readOnly === true || field.readOnly === true,
|
|
36
|
+
disabled: props.disabled === true,
|
|
37
|
+
errors: props.errors ?? [],
|
|
38
|
+
...props.onBlur !== void 0 ? { onBlur: props.onBlur } : {},
|
|
39
|
+
...props.className !== void 0 ? { className: props.className } : {}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { findRule, getFieldProps, getRuleNumber, getSetting, isNumber, isRequired, isString };
|
|
44
|
+
|
|
45
|
+
//# sourceMappingURL=entry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry.js","names":[],"sources":["../../src/shared/entry.ts"],"sourcesContent":["import type { Field, Rule } from '@panneau/protocol';\n\nimport type { FieldEntryProps, FieldProps } from './types';\n\n/** The rule of a field with this name, if any. */\nexport function findRule(field: Field, rule: Rule['rule']): Rule | undefined {\n return field.rules?.find((candidate) => candidate.rule === rule);\n}\n\n/** The number of a `min` or `max` rule of a field, if any. */\nexport function getRuleNumber(field: Field, rule: 'min' | 'max'): number | undefined {\n const found = findRule(field, rule);\n return found !== undefined && 'value' in found && typeof found.value === 'number'\n ? found.value\n : undefined;\n}\n\n/** Whether a field has the `required` rule. */\nexport function isRequired(field: Field): boolean {\n return findRule(field, 'required') !== undefined;\n}\n\n/** A setting of a field (`settings.<name>`), when it has the expected type. */\nexport function getSetting<T>(field: Field, name: string, guard: (value: unknown) => value is T) {\n const value = field.settings?.[name];\n return guard(value) ? value : undefined;\n}\n\nexport const isString = (value: unknown): value is string => typeof value === 'string';\nexport const isNumber = (value: unknown): value is number =>\n typeof value === 'number' && !Number.isNaN(value);\n\n/**\n * The props that every field takes, from a registry entry's props: label (the field's name\n * without one), help, required mark, read-only, errors and handlers. Plugins use it to write their\n * own entries.\n */\nexport function getFieldProps(\n props: FieldEntryProps,\n): Omit<FieldProps<never>, 'value' | 'onChange'> & { placeholder?: string } {\n const { field } = props;\n return {\n name: props.path,\n // A field always has a label: its name when the definition gives none.\n label: field.label ?? field.name,\n description: field.help,\n ...(field.placeholder !== undefined ? { placeholder: field.placeholder } : {}),\n required: isRequired(field),\n readOnly: props.readOnly === true || field.readOnly === true,\n disabled: props.disabled === true,\n errors: props.errors ?? [],\n ...(props.onBlur !== undefined ? { onBlur: props.onBlur } : {}),\n ...(props.className !== undefined ? { className: props.className } : {}),\n };\n}\n\n/** The value as a string, or `null`. */\nexport function asString(value: unknown): string | null {\n return typeof value === 'string' ? value : null;\n}\n"],"mappings":";;AAKA,SAAgB,SAAS,OAAc,MAAsC;CACzE,OAAO,MAAM,OAAO,MAAM,cAAc,UAAU,SAAS,IAAI;AACnE;;AAGA,SAAgB,cAAc,OAAc,MAAyC;CACjF,MAAM,QAAQ,SAAS,OAAO,IAAI;CAClC,OAAO,UAAU,KAAA,KAAa,WAAW,SAAS,OAAO,MAAM,UAAU,WACnE,MAAM,QACN,KAAA;AACV;;AAGA,SAAgB,WAAW,OAAuB;CAC9C,OAAO,SAAS,OAAO,UAAU,MAAM,KAAA;AAC3C;;AAGA,SAAgB,WAAc,OAAc,MAAc,OAAuC;CAC7F,MAAM,QAAQ,MAAM,WAAW;CAC/B,OAAO,MAAM,KAAK,IAAI,QAAQ,KAAA;AAClC;AAEA,IAAa,YAAY,UAAoC,OAAO,UAAU;AAC9E,IAAa,YAAY,UACrB,OAAO,UAAU,YAAY,CAAC,OAAO,MAAM,KAAK;;;;;;AAOpD,SAAgB,cACZ,OACwE;CACxE,MAAM,EAAE,UAAU;CAClB,OAAO;EACH,MAAM,MAAM;EAEZ,OAAO,MAAM,SAAS,MAAM;EAC5B,aAAa,MAAM;EACnB,GAAI,MAAM,gBAAgB,KAAA,IAAY,EAAE,aAAa,MAAM,YAAY,IAAI,CAAC;EAC5E,UAAU,WAAW,KAAK;EAC1B,UAAU,MAAM,aAAa,QAAQ,MAAM,aAAa;EACxD,UAAU,MAAM,aAAa;EAC7B,QAAQ,MAAM,UAAU,CAAA;EACxB,GAAI,MAAM,WAAW,KAAA,IAAY,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;EAC7D,GAAI,MAAM,cAAc,KAAA,IAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;CAC1E;AACJ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/shared/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { fieldsMessages as messages } from "@panneau/intl";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Definition, Item, ListQuery } from '@panneau/protocol';
|
|
2
|
+
/** A related item: its id, and a label to show it. The value of a relation field. */
|
|
3
|
+
export interface RelationReference {
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
}
|
|
7
|
+
/** Where relation fields search for items. */
|
|
8
|
+
export interface RelationSource {
|
|
9
|
+
/** Items of a resource that match a text, for the picker. */
|
|
10
|
+
search(resource: string, query: string, options: {
|
|
11
|
+
signal: AbortSignal;
|
|
12
|
+
}): Promise<RelationReference[]>;
|
|
13
|
+
}
|
|
14
|
+
/** What `createRelationSource()` needs from an adapter: the `list()` of `@panneau/data`. */
|
|
15
|
+
export interface RelationListAdapter {
|
|
16
|
+
list(resource: string, query?: ListQuery, options?: {
|
|
17
|
+
signal?: AbortSignal;
|
|
18
|
+
}): Promise<{
|
|
19
|
+
data: Item[];
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
export interface RelationSourceOptions {
|
|
23
|
+
/** The definition, for the field that titles the items of each resource (`itemTitle`). */
|
|
24
|
+
definition?: Pick<Definition, 'resources'> | null;
|
|
25
|
+
/** Items per search. Defaults to 20. */
|
|
26
|
+
perPage?: number;
|
|
27
|
+
/** The label of an item, when `itemTitle` isn't enough. */
|
|
28
|
+
getLabel?: (item: Item, resource: string) => string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A relation source over an adapter's `list()`, such as the `DataAdapter` of `@panneau/data`: a
|
|
32
|
+
* search is a list query with `q`. Labels come from the resource's `itemTitle`.
|
|
33
|
+
*/
|
|
34
|
+
export declare function createRelationSource(adapter: RelationListAdapter, { definition, perPage, getLabel }?: RelationSourceOptions): RelationSource;
|
|
35
|
+
/** Reads relation values: references, ids, or lists of them. */
|
|
36
|
+
export declare function toReferences(value: unknown): RelationReference[];
|
|
37
|
+
//# sourceMappingURL=relation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relation.d.ts","sourceRoot":"","sources":["../../src/shared/relation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAErE,qFAAqF;AACrF,MAAM,WAAW,iBAAiB;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,8CAA8C;AAC9C,MAAM,WAAW,cAAc;IAC3B,6DAA6D;IAC7D,MAAM,CACF,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE;QAAE,MAAM,EAAE,WAAW,CAAA;KAAE,GACjC,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;CACnC;AAED,4FAA4F;AAC5F,MAAM,WAAW,mBAAmB;IAChC,IAAI,CACA,QAAQ,EAAE,MAAM,EAChB,KAAK,CAAC,EAAE,SAAS,EACjB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GACnC,OAAO,CAAC;QAAE,IAAI,EAAE,IAAI,EAAE,CAAA;KAAE,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,qBAAqB;IAClC,0FAA0F;IAC1F,UAAU,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,GAAG,IAAI,CAAC;IAClD,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;CACvD;AAeD;;;GAGG;AACH,wBAAgB,oBAAoB,CAChC,OAAO,EAAE,mBAAmB,EAC5B,EAAE,UAAiB,EAAE,OAAY,EAAE,QAAQ,EAAE,GAAE,qBAA0B,GAC1E,cAAc,CAkBhB;AAED,gEAAgE;AAChE,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,iBAAiB,EAAE,CAkBhE"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
//#region src/shared/relation.ts
|
|
2
|
+
function labelOf(value, locale) {
|
|
3
|
+
if (typeof value === "string" || typeof value === "number") return String(value);
|
|
4
|
+
if (value !== null && typeof value === "object") {
|
|
5
|
+
const localized = value;
|
|
6
|
+
const first = (locale !== void 0 ? localized[locale] : void 0) ?? Object.values(localized)[0];
|
|
7
|
+
return typeof first === "string" ? first : null;
|
|
8
|
+
}
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A relation source over an adapter's `list()`, such as the `DataAdapter` of `@panneau/data`: a
|
|
13
|
+
* search is a list query with `q`. Labels come from the resource's `itemTitle`.
|
|
14
|
+
*/
|
|
15
|
+
function createRelationSource(adapter, { definition = null, perPage = 20, getLabel } = {}) {
|
|
16
|
+
return { async search(resource, query, { signal }) {
|
|
17
|
+
const response = await adapter.list(resource, {
|
|
18
|
+
page: 1,
|
|
19
|
+
perPage,
|
|
20
|
+
...query !== "" ? { q: query } : {}
|
|
21
|
+
}, { signal });
|
|
22
|
+
const titleField = definition?.resources.find((r) => r.id === resource)?.itemTitle;
|
|
23
|
+
return response.data.map((item) => ({
|
|
24
|
+
id: item.id,
|
|
25
|
+
label: getLabel?.(item, resource) ?? (titleField !== void 0 ? labelOf(item[titleField]) : null) ?? item.id
|
|
26
|
+
}));
|
|
27
|
+
} };
|
|
28
|
+
}
|
|
29
|
+
/** Reads relation values: references, ids, or lists of them. */
|
|
30
|
+
function toReferences(value) {
|
|
31
|
+
return (Array.isArray(value) ? value : value === null || value === void 0 ? [] : [value]).flatMap((entry) => {
|
|
32
|
+
if (typeof entry === "string" || typeof entry === "number") return [{
|
|
33
|
+
id: String(entry),
|
|
34
|
+
label: String(entry)
|
|
35
|
+
}];
|
|
36
|
+
if (entry !== null && typeof entry === "object" && "id" in entry) {
|
|
37
|
+
const { id, label } = entry;
|
|
38
|
+
if (typeof id === "string" || typeof id === "number") return [{
|
|
39
|
+
id: String(id),
|
|
40
|
+
label: typeof label === "string" ? label : String(id)
|
|
41
|
+
}];
|
|
42
|
+
}
|
|
43
|
+
return [];
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { createRelationSource, toReferences };
|
|
48
|
+
|
|
49
|
+
//# sourceMappingURL=relation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relation.js","names":[],"sources":["../../src/shared/relation.ts"],"sourcesContent":["import type { Definition, Item, ListQuery } from '@panneau/protocol';\n\n/** A related item: its id, and a label to show it. The value of a relation field. */\nexport interface RelationReference {\n id: string;\n label: string;\n}\n\n/** Where relation fields search for items. */\nexport interface RelationSource {\n /** Items of a resource that match a text, for the picker. */\n search(\n resource: string,\n query: string,\n options: { signal: AbortSignal },\n ): Promise<RelationReference[]>;\n}\n\n/** What `createRelationSource()` needs from an adapter: the `list()` of `@panneau/data`. */\nexport interface RelationListAdapter {\n list(\n resource: string,\n query?: ListQuery,\n options?: { signal?: AbortSignal },\n ): Promise<{ data: Item[] }>;\n}\n\nexport interface RelationSourceOptions {\n /** The definition, for the field that titles the items of each resource (`itemTitle`). */\n definition?: Pick<Definition, 'resources'> | null;\n /** Items per search. Defaults to 20. */\n perPage?: number;\n /** The label of an item, when `itemTitle` isn't enough. */\n getLabel?: (item: Item, resource: string) => string;\n}\n\nfunction labelOf(value: unknown, locale?: string): string | null {\n if (typeof value === 'string' || typeof value === 'number') {\n return String(value);\n }\n if (value !== null && typeof value === 'object') {\n const localized = value as Record<string, unknown>;\n const first =\n (locale !== undefined ? localized[locale] : undefined) ?? Object.values(localized)[0];\n return typeof first === 'string' ? first : null;\n }\n return null;\n}\n\n/**\n * A relation source over an adapter's `list()`, such as the `DataAdapter` of `@panneau/data`: a\n * search is a list query with `q`. Labels come from the resource's `itemTitle`.\n */\nexport function createRelationSource(\n adapter: RelationListAdapter,\n { definition = null, perPage = 20, getLabel }: RelationSourceOptions = {},\n): RelationSource {\n return {\n async search(resource, query, { signal }) {\n const response = await adapter.list(\n resource,\n { page: 1, perPage, ...(query !== '' ? { q: query } : {}) },\n { signal },\n );\n const titleField = definition?.resources.find((r) => r.id === resource)?.itemTitle;\n return response.data.map((item) => ({\n id: item.id,\n label:\n getLabel?.(item, resource) ??\n (titleField !== undefined ? labelOf(item[titleField]) : null) ??\n item.id,\n }));\n },\n };\n}\n\n/** Reads relation values: references, ids, or lists of them. */\nexport function toReferences(value: unknown): RelationReference[] {\n const list = Array.isArray(value)\n ? value\n : value === null || value === undefined\n ? []\n : [value];\n return list.flatMap((entry: unknown) => {\n if (typeof entry === 'string' || typeof entry === 'number') {\n return [{ id: String(entry), label: String(entry) }];\n }\n if (entry !== null && typeof entry === 'object' && 'id' in entry) {\n const { id, label } = entry as { id: unknown; label?: unknown };\n if (typeof id === 'string' || typeof id === 'number') {\n return [{ id: String(id), label: typeof label === 'string' ? label : String(id) }];\n }\n }\n return [];\n });\n}\n"],"mappings":";AAoCA,SAAS,QAAQ,OAAgB,QAAgC;CAC7D,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAC9C,OAAO,OAAO,KAAK;CAEvB,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;EAC7C,MAAM,YAAY;EAClB,MAAM,SACD,WAAW,KAAA,IAAY,UAAU,UAAU,KAAA,MAAc,OAAO,OAAO,SAAS,CAAC,CAAC;EACvF,OAAO,OAAO,UAAU,WAAW,QAAQ;CAC/C;CACA,OAAO;AACX;;;;;AAMA,SAAgB,qBACZ,SACA,EAAE,aAAa,MAAM,UAAU,IAAI,aAAoC,CAAC,GAC1D;CACd,OAAO,EACH,MAAM,OAAO,UAAU,OAAO,EAAE,UAAU;EACtC,MAAM,WAAW,MAAM,QAAQ,KAC3B,UACA;GAAE,MAAM;GAAG;GAAS,GAAI,UAAU,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC;EAAG,GAC1D,EAAE,OAAO,CACb;EACA,MAAM,aAAa,YAAY,UAAU,MAAM,MAAM,EAAE,OAAO,QAAQ,CAAC,EAAE;EACzE,OAAO,SAAS,KAAK,KAAK,UAAU;GAChC,IAAI,KAAK;GACT,OACI,WAAW,MAAM,QAAQ,MACxB,eAAe,KAAA,IAAY,QAAQ,KAAK,WAAW,IAAI,SACxD,KAAK;EACb,EAAE;CACN,EACJ;AACJ;;AAGA,SAAgB,aAAa,OAAqC;CAM9D,QALa,MAAM,QAAQ,KAAK,IAC1B,QACA,UAAU,QAAQ,UAAU,KAAA,IAC1B,CAAA,IACA,CAAC,KAAK,EAAA,CACF,SAAS,UAAmB;EACpC,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAC9C,OAAO,CAAC;GAAE,IAAI,OAAO,KAAK;GAAG,OAAO,OAAO,KAAK;EAAE,CAAC;EAEvD,IAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,QAAQ,OAAO;GAC9D,MAAM,EAAE,IAAI,UAAU;GACtB,IAAI,OAAO,OAAO,YAAY,OAAO,OAAO,UACxC,OAAO,CAAC;IAAE,IAAI,OAAO,EAAE;IAAG,OAAO,OAAO,UAAU,WAAW,QAAQ,OAAO,EAAE;GAAE,CAAC;EAEzF;EACA,OAAO,CAAA;CACX,CAAC;AACL"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { ComponentType, ReactNode } from 'react';
|
|
2
|
+
import type { Field } from '@panneau/protocol';
|
|
3
|
+
/**
|
|
4
|
+
* Props of every field component. Fields are controlled: they show `value` and report changes
|
|
5
|
+
* through `onChange`, and keep no value of their own. An empty value is `null`.
|
|
6
|
+
*/
|
|
7
|
+
export interface FieldProps<V> {
|
|
8
|
+
value?: V | null;
|
|
9
|
+
onChange?: (value: V | null) => void;
|
|
10
|
+
/** Called when the field loses focus: forms show its client errors from then on. */
|
|
11
|
+
onBlur?: () => void;
|
|
12
|
+
/** Error messages. Any message makes the field invalid. */
|
|
13
|
+
errors?: readonly string[];
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
readOnly?: boolean;
|
|
16
|
+
/** Marks the field as required. Validation itself is the form's job. */
|
|
17
|
+
required?: boolean;
|
|
18
|
+
label?: ReactNode;
|
|
19
|
+
/** Help text under the field. */
|
|
20
|
+
description?: ReactNode;
|
|
21
|
+
/** Name of the field, also used as the name of its input. */
|
|
22
|
+
name?: string;
|
|
23
|
+
id?: string;
|
|
24
|
+
autoFocus?: boolean;
|
|
25
|
+
/** Accessible label, for a field without a visible label. */
|
|
26
|
+
'aria-label'?: string;
|
|
27
|
+
className?: string;
|
|
28
|
+
labelClassName?: string;
|
|
29
|
+
descriptionClassName?: string;
|
|
30
|
+
errorsClassName?: string;
|
|
31
|
+
}
|
|
32
|
+
/** Props of a registry entry: a field component driven by a field of the definition. */
|
|
33
|
+
export interface FieldEntryProps {
|
|
34
|
+
/** The field, as the definition describes it. */
|
|
35
|
+
field: Field;
|
|
36
|
+
/** Dot path of the value (`blocks.2.title`), unique in the form. */
|
|
37
|
+
path: string;
|
|
38
|
+
value: unknown;
|
|
39
|
+
onChange: (value: unknown) => void;
|
|
40
|
+
onBlur?: () => void;
|
|
41
|
+
errors?: readonly string[];
|
|
42
|
+
disabled?: boolean;
|
|
43
|
+
readOnly?: boolean;
|
|
44
|
+
/** The object the field belongs to: the form's values, or a group or a block. */
|
|
45
|
+
parentValue?: unknown;
|
|
46
|
+
className?: string;
|
|
47
|
+
}
|
|
48
|
+
/** A registry entry: renders the field of a type from its definition. */
|
|
49
|
+
export type FieldEntry = ComponentType<FieldEntryProps>;
|
|
50
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/shared/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE/C;;;GAGG;AACH,MAAM,WAAW,UAAU,CAAC,CAAC;IACzB,KAAK,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;IACrC,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,2DAA2D;IAC3D,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,iCAAiC;IACjC,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,wFAAwF;AACxF,MAAM,WAAW,eAAe;IAC5B,iDAAiD;IACjD,KAAK,EAAE,KAAK,CAAC;IACb,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,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;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iFAAiF;IACjF,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,yEAAyE;AACzE,MAAM,MAAM,UAAU,GAAG,aAAa,CAAC,eAAe,CAAC,CAAC"}
|