@panneau/fields 5.0.0-alpha.6 → 5.0.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/boolean/BooleanField.js +78 -17
  2. package/dist/boolean/BooleanField.js.map +1 -1
  3. package/dist/color/ColorField.js.map +1 -1
  4. package/dist/date/DateField.js.map +1 -1
  5. package/dist/datetime/DateTimeField.js +107 -20
  6. package/dist/datetime/DateTimeField.js.map +1 -1
  7. package/dist/duration/DurationField.js +126 -41
  8. package/dist/duration/DurationField.js.map +1 -1
  9. package/dist/email/EmailField.js.map +1 -1
  10. package/dist/embed/EmbedField.js +185 -69
  11. package/dist/embed/EmbedField.js.map +1 -1
  12. package/dist/file/FileField.d.ts.map +1 -1
  13. package/dist/file/FileField.js +167 -77
  14. package/dist/file/FileField.js.map +1 -1
  15. package/dist/group/GroupField.js.map +1 -1
  16. package/dist/json/JsonField.js +104 -22
  17. package/dist/json/JsonField.js.map +1 -1
  18. package/dist/locales/index.js.map +1 -1
  19. package/dist/localized/LocalizedField.js.map +1 -1
  20. package/dist/location/LocationField.d.ts.map +1 -1
  21. package/dist/location/LocationField.js +351 -138
  22. package/dist/location/LocationField.js.map +1 -1
  23. package/dist/location/LocationMap.d.ts.map +1 -1
  24. package/dist/location/LocationMap.js +155 -65
  25. package/dist/location/LocationMap.js.map +1 -1
  26. package/dist/location/leafletCss.js.map +1 -1
  27. package/dist/location/nominatim.js.map +1 -1
  28. package/dist/missing/MissingField.d.ts +3 -2
  29. package/dist/missing/MissingField.d.ts.map +1 -1
  30. package/dist/missing/MissingField.js +3 -2
  31. package/dist/missing/MissingField.js.map +1 -1
  32. package/dist/number/NumberField.js.map +1 -1
  33. package/dist/range/RangeField.js +82 -11
  34. package/dist/range/RangeField.js.map +1 -1
  35. package/dist/relation/RelationField.js +485 -145
  36. package/dist/relation/RelationField.js.map +1 -1
  37. package/dist/repeater/RepeaterField.js +118 -34
  38. package/dist/repeater/RepeaterField.js.map +1 -1
  39. package/dist/select/SelectField.js.map +1 -1
  40. package/dist/shared/DefinitionField.js.map +1 -1
  41. package/dist/shared/builtIn.js.map +1 -1
  42. package/dist/shared/check.js.map +1 -1
  43. package/dist/shared/context.js.map +1 -1
  44. package/dist/shared/cx.js.map +1 -1
  45. package/dist/shared/dates.js.map +1 -1
  46. package/dist/shared/embed.js.map +1 -1
  47. package/dist/shared/entry.js.map +1 -1
  48. package/dist/shared/relation.js.map +1 -1
  49. package/dist/slug/SlugField.d.ts.map +1 -1
  50. package/dist/slug/SlugField.js +184 -36
  51. package/dist/slug/SlugField.js.map +1 -1
  52. package/dist/tags/TagsField.js.map +1 -1
  53. package/dist/tel/TelField.js.map +1 -1
  54. package/dist/text/TextField.js.map +1 -1
  55. package/dist/textarea/TextAreaField.js.map +1 -1
  56. package/dist/time/TimeField.js +64 -10
  57. package/dist/time/TimeField.js.map +1 -1
  58. package/dist/upload/FilePicker.js +107 -24
  59. package/dist/upload/FilePicker.js.map +1 -1
  60. package/dist/upload/UploadList.js.map +1 -1
  61. package/dist/upload/files.js.map +1 -1
  62. package/dist/upload/useRejectionMessage.js.map +1 -1
  63. package/dist/upload/useUploads.js.map +1 -1
  64. package/dist/uppy/uploader.js.map +1 -1
  65. package/dist/url/UrlField.js +69 -14
  66. package/dist/url/UrlField.js.map +1 -1
  67. package/package.json +4 -4
@@ -1 +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 { MapsConfig } from '../location/types';\nimport type { UploadSource } from '../upload/types';\nimport type { FieldCheckSource } from './check';\nimport type { EmbedSource } from './embed';\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 /**\n * Where fields that take files upload them directly to storage. Without one, they give the\n * files themselves.\n */\n uploadSource: UploadSource | null;\n /** Where fields with `check` check their value, such as a slug's availability. */\n checkSource: FieldCheckSource | null;\n /** Where `embed` fields get what a URL embeds. Without one, they keep the URL alone. */\n embedSource: EmbedSource | null;\n /** The tiles and the address search of `location` fields. */\n maps: MapsConfig | null;\n binding: FieldBinding | null;\n}\n\nconst FieldsContext = createContext<FieldsContextValue>({\n components: {},\n locales: [],\n relationSource: null,\n uploadSource: null,\n checkSource: null,\n embedSource: null,\n maps: 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 /** Make it once, outside of render: fields make an uploader for each source they get. */\n uploadSource?: UploadSource | null;\n /** `createFieldCheckSource(adapter, resource, id)`, for the form of an item. */\n checkSource?: FieldCheckSource | null;\n /** `createEmbedSource(adapter)`. */\n embedSource?: EmbedSource | null;\n /** The tiles and the address search of `location` fields. */\n maps?: MapsConfig | 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, the sources of relations, uploads, checks and embeds, the maps of\n * locations, and a binding. Values not given come from the parent provider.\n */\nexport function FieldsProvider({\n components,\n locales,\n relationSource,\n uploadSource,\n checkSource,\n embedSource,\n maps,\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 uploadSource: uploadSource === undefined ? parent.uploadSource : uploadSource,\n checkSource: checkSource === undefined ? parent.checkSource : checkSource,\n embedSource: embedSource === undefined ? parent.embedSource : embedSource,\n maps: maps === undefined ? parent.maps : maps,\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":";;;;AA0DA,IAAM,gBAAgB,cAAkC;CACpD,YAAY,CAAC;CACb,SAAS,CAAA;CACT,gBAAgB;CAChB,cAAc;CACd,aAAa;CACb,aAAa;CACb,MAAM;CACN,SAAS;AACb,CAAC;;AAGD,SAAgB,mBAAuC;CACnD,OAAO,IAAI,aAAa;AAC5B;;;;;;AAwBA,SAAO,eAAA,IAAA;CAAA,MAAA,IAAAE,EAAA,EAAA;CAAwB,MAAA,EAAA,YAAA,SAAA,gBAAA,cAAA,aAAA,aAAA,MAAA,SAAA,aAAA;CAW3B,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;CACF,MAAA,KAAA,iBAAiB,KAAA,IAAY,OAAM,eAAnC;CACD,MAAA,KAAA,gBAAgB,KAAA,IAAY,OAAM,cAAlC;CACA,MAAA,KAAA,gBAAgB,KAAA,IAAY,OAAM,cAAlC;CACP,MAAA,KAAA,SAAS,KAAA,IAAY,OAAM,OAA3B;CACG,MAAA,KAAA,YAAY,KAAA,IAAY,OAAM,UAA9B;CAAgD,IAAA;CAAA,IAAA,EAAA,OAAA,MAAA,EAAA,OAAA,MAAA,EAAA,OAAA,MAAA,EAAA,OAAA,MAAA,EAAA,OAAA,MAAA,EAAA,OAAA,MAAA,EAAA,OAAA,MAAA,EAAA,QAAA,IAAA;EAT3B,KAAA;GAAA,YAE1B;GAAsF,SACjF;GAAyB,gBAClB;GAAqE,cACvE;GAA+D,aAChE;GAA4D,aAC5D;GAA4D,MACnE;GAAuC,SACpC;EACb;EAAC,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,MAAA;EAAA,EAAA,MAAA;CAAA,OAAA,KAAA,EAAA;CAVD,MAAA,QAAkC;CAUhC,IAAA;CAAA,IAAA,EAAA,QAAA,YAAA,EAAA,QAAA,OAAA;EACK,MAAA,oBAAC,eAAD;GAAsB;GAAQ;EAAhB,CAAA;EAAyC,EAAA,MAAA;EAAA,EAAA,MAAA;EAAA,EAAA,MAAA;CAAA,OAAA,MAAA,EAAA;CAAA,OAAvD;AAAuD;;AAIlE,SAAgB,cAAc,MAAsB;CAChD,MAAM,QAAQ,KAAK,YAAY,GAAG;CAClC,OAAO,UAAU,KAAK,KAAK,KAAK,MAAM,GAAG,KAAK;AAClD"}
1
+ {"version":3,"file":"context.js","names":["ComponentType","ReactNode","createContext","use","Field","MapsConfig","UploadSource","FieldCheckSource","EmbedSource","RelationSource","FieldEntry","BoundFieldProps","value","onChange","onBlur","errors","FieldBindingProps","field","path","parentPath","render","bound","FieldBinding","FieldsContextValue","components","Readonly","Record","locales","relationSource","uploadSource","checkSource","embedSource","maps","binding","FieldsContext","useFieldsContext","FieldsProviderProps","children","FieldsProvider","t0","$","_c","parent","t1","undefined","t2","t3","t4","t5","t6","t7","t8","t9","t10","getParentPath","index","lastIndexOf","slice","_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 { MapsConfig } from '../location/types';\nimport type { UploadSource } from '../upload/types';\nimport type { FieldCheckSource } from './check';\nimport type { EmbedSource } from './embed';\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 /**\n * Where fields that take files upload them directly to storage. Without one, they give the\n * files themselves.\n */\n uploadSource: UploadSource | null;\n /** Where fields with `check` check their value, such as a slug's availability. */\n checkSource: FieldCheckSource | null;\n /** Where `embed` fields get what a URL embeds. Without one, they keep the URL alone. */\n embedSource: EmbedSource | null;\n /** The tiles and the address search of `location` fields. */\n maps: MapsConfig | null;\n binding: FieldBinding | null;\n}\n\nconst FieldsContext = createContext<FieldsContextValue>({\n components: {},\n locales: [],\n relationSource: null,\n uploadSource: null,\n checkSource: null,\n embedSource: null,\n maps: 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 /** Make it once, outside of render: fields make an uploader for each source they get. */\n uploadSource?: UploadSource | null;\n /** `createFieldCheckSource(adapter, resource, id)`, for the form of an item. */\n checkSource?: FieldCheckSource | null;\n /** `createEmbedSource(adapter)`. */\n embedSource?: EmbedSource | null;\n /** The tiles and the address search of `location` fields. */\n maps?: MapsConfig | 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, the sources of relations, uploads, checks and embeds, the maps of\n * locations, and a binding. Values not given come from the parent provider.\n */\nexport function FieldsProvider({\n components,\n locales,\n relationSource,\n uploadSource,\n checkSource,\n embedSource,\n maps,\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 uploadSource: uploadSource === undefined ? parent.uploadSource : uploadSource,\n checkSource: checkSource === undefined ? parent.checkSource : checkSource,\n embedSource: embedSource === undefined ? parent.embedSource : embedSource,\n maps: maps === undefined ? parent.maps : maps,\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":";;;;AA0DA,IAAMkC,gBAAgBhC,cAAkC;CACpDsB,YAAY,CAAC;CACbG,SAAS,CAAA;CACTC,gBAAgB;CAChBC,cAAc;CACdC,aAAa;CACbC,aAAa;CACbC,MAAM;CACNC,SAAS;AACb,CAAC;;AAGD,SAAgBE,mBAAuC;CACnD,OAAOhC,IAAI+B,aAAa;AAC5B;;;;;;AAwBA,SAAOI,eAAAC,IAAA;CAAA,MAAAC,IAAAC,EAAA,EAAA;CAAwB,MAAA,EAAAjB,YAAAG,SAAAC,gBAAAC,cAAAC,aAAAC,aAAAC,MAAAC,SAAAI,aAAAE;CAW3B,MAAAG,SAAevC,IAAI+B,aAAa;CAAE,IAAAS;CAAA,IAAAH,EAAA,OAAAhB,cAAAgB,EAAA,OAAAE,OAAAlB,YAAA;EAG1BmB,KAAAnB,eAAeoB,KAAAA,IAAf;GAAA,GAAgCF,OAAMlB;GAAW,GAAKA;EAA+B,IAAhBkB,OAAMlB;EAAWgB,EAAA,KAAAhB;EAAAgB,EAAA,KAAAE,OAAAlB;EAAAgB,EAAA,KAAAG;CAAA,OAAAA,KAAAH,EAAA;CACjF,MAAAK,KAAAlB,WAAWe,OAAMf;CACV,MAAAmB,KAAAlB,mBAAmBgB,KAAAA,IAAYF,OAAMd,iBAArCA;CACF,MAAAmB,KAAAlB,iBAAiBe,KAAAA,IAAYF,OAAMb,eAAnCA;CACD,MAAAmB,KAAAlB,gBAAgBc,KAAAA,IAAYF,OAAMZ,cAAlCA;CACA,MAAAmB,KAAAlB,gBAAgBa,KAAAA,IAAYF,OAAMX,cAAlCA;CACP,MAAAmB,KAAAlB,SAASY,KAAAA,IAAYF,OAAMV,OAA3BA;CACG,MAAAmB,KAAAlB,YAAYW,KAAAA,IAAYF,OAAMT,UAA9BA;CAAgD,IAAAmB;CAAA,IAAAZ,EAAA,OAAAG,MAAAH,EAAA,OAAAK,MAAAL,EAAA,OAAAM,MAAAN,EAAA,OAAAO,MAAAP,EAAA,OAAAQ,MAAAR,EAAA,OAAAS,MAAAT,EAAA,OAAAU,MAAAV,EAAA,QAAAW,IAAA;EAT3BC,KAAA;GAAA5B,YAE1BmB;GAAsFhB,SACjFkB;GAAyBjB,gBAClBkB;GAAqEjB,cACvEkB;GAA+DjB,aAChEkB;GAA4DjB,aAC5DkB;GAA4DjB,MACnEkB;GAAuCjB,SACpCkB;EACb;EAACX,EAAA,KAAAG;EAAAH,EAAA,KAAAK;EAAAL,EAAA,KAAAM;EAAAN,EAAA,KAAAO;EAAAP,EAAA,KAAAQ;EAAAR,EAAA,KAAAS;EAAAT,EAAA,KAAAU;EAAAV,EAAA,MAAAW;EAAAX,EAAA,MAAAY;CAAA,OAAAA,KAAAZ,EAAA;CAVD,MAAA5B,QAAkCwC;CAUhC,IAAAC;CAAA,IAAAb,EAAA,QAAAH,YAAAG,EAAA,QAAA5B,OAAA;EACKyC,MAAA,oBAAC,eAAD;GAAsBzC;GAAQyB;EAAhB,CAAA;EAAyCG,EAAA,MAAAH;EAAAG,EAAA,MAAA5B;EAAA4B,EAAA,MAAAa;CAAA,OAAAA,MAAAb,EAAA;CAAA,OAAvDa;AAAuD;;AAIlE,SAAgBC,cAAcpC,MAAsB;CAChD,MAAMqC,QAAQrC,KAAKsC,YAAY,GAAG;CAClC,OAAOD,UAAU,KAAK,KAAKrC,KAAKuC,MAAM,GAAGF,KAAK;AAClD"}
@@ -1 +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"}
1
+ {"version":3,"file":"cx.js","names":["cx","classNames","filter","Boolean","join"],"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,SAAgBA,GAAG,GAAGC,YAA2D;CAC7E,OAAOA,WAAWC,OAAOC,OAAO,CAAC,CAACC,KAAK,GAAG;AAC9C"}
@@ -1 +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"}
1
+ {"version":3,"file":"dates.js","names":["CalendarDate","CalendarDateTime","Time","ZonedDateTime","getLocalTimeZone","parseAbsolute","parseDate","parseDateTime","parseTime","toCalendarDate","value","match","undefined","OFFSET","toDateTime","timeZone","trim","iso","replace","test","fromDateTime","toAbsoluteString","toString","toTime","length","fromTime","Pick","withSeconds","pad","part","String","padStart","time","hour","minute","second"],"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,SAAgBS,eAAeC,OAAuD;CAClF,MAAMC,QAAQD,OAAOC,MAAM,sBAAsB;CACjD,IAAIA,QAAQ,OAAOC,KAAAA,GACf,OAAO;CAEX,IAAI;EACA,OAAON,UAAUK,MAAM,EAAE;CAC7B,QAAQ;EACJ,OAAO;CACX;AACJ;AAEA,IAAME,SAAS;;;;;AAMf,SAAgBC,WACZJ,OACAK,WAAmBX,iBAAiB,GACG;CACvC,IAAIM,UAAU,QAAQA,UAAUE,KAAAA,KAAaF,MAAMM,KAAK,MAAM,IAC1D,OAAO;CAEX,MAAMC,MAAMP,MAAMM,KAAK,CAAC,CAACE,QAAQ,KAAK,GAAG;CACzC,IAAI;EACA,OAAOL,OAAOM,KAAKF,GAAG,IAAIZ,cAAcY,KAAKF,QAAQ,IAAIR,cAAcU,GAAG;CAC9E,QAAQ;EACJ,OAAO;CACX;AACJ;;AAGA,SAAgBG,aAAaV,OAAiD;CAC1E,OAAO,cAAcA,QACfA,MAAMW,iBAAiB,CAAC,CAACH,QAAQ,WAAW,GAAG,IAC/CR,MAAMY,SAAS;AACzB;;AAGA,SAAgBC,OAAOb,OAA+C;CAClE,IAAIA,UAAU,QAAQA,UAAUE,KAAAA,KAAa,CAAC,0BAA0BO,KAAKT,KAAK,GAC9E,OAAO;CAEX,IAAI;EACA,OAAOF,UAAUE,MAAMc,SAAS,IAAI,IAAId,UAAUA,KAAK;CAC3D,QAAQ;EACJ,OAAO;CACX;AACJ;;AAGA,SAAgBe,SACZf,OACAiB,cAAc,OACR;CACN,MAAMC,OAAOC,SAAiBC,OAAOD,IAAI,CAAC,CAACE,SAAS,GAAG,GAAG;CAC1D,MAAMC,OAAO,GAAGJ,IAAIlB,MAAMuB,IAAI,EAAC,GAAIL,IAAIlB,MAAMwB,MAAM;CACnD,OAAOP,cAAc,GAAGK,KAAI,GAAIJ,IAAIlB,MAAMyB,MAAM,MAAMH;AAC1D"}
@@ -1 +1 @@
1
- {"version":3,"file":"embed.js","names":[],"sources":["../../src/shared/embed.ts"],"sourcesContent":["import type { EmbedValue } from '@panneau/protocol';\n\n/** Where `embed` fields get what a URL embeds: the backend, from the provider's oEmbed. */\nexport interface EmbedSource {\n resolve(url: string, options: { signal: AbortSignal }): Promise<EmbedValue>;\n}\n\n/** What `createEmbedSource()` needs from an adapter: the `resolveEmbed()` of `@panneau/data`. */\nexport interface EmbedAdapter {\n resolveEmbed?(url: string, options?: { signal?: AbortSignal }): Promise<EmbedValue>;\n}\n\n/**\n * The embed source of an adapter (`GET /embeds?url=`). Null when the adapter can't resolve\n * embeds: `embed` fields then keep the URL alone.\n */\nexport function createEmbedSource(adapter: EmbedAdapter): EmbedSource | null {\n const { resolveEmbed } = adapter;\n if (resolveEmbed === undefined) {\n return null;\n }\n return { resolve: (url, { signal }) => resolveEmbed.call(adapter, url, { signal }) };\n}\n"],"mappings":";;;;;AAgBA,SAAgB,kBAAkB,SAA2C;CACzE,MAAM,EAAE,iBAAiB;CACzB,IAAI,iBAAiB,KAAA,GACjB,OAAO;CAEX,OAAO,EAAE,UAAU,KAAK,EAAE,aAAa,aAAa,KAAK,SAAS,KAAK,EAAE,OAAO,CAAC,EAAE;AACvF"}
1
+ {"version":3,"file":"embed.js","names":["EmbedValue","EmbedSource","resolve","url","options","signal","AbortSignal","Promise","EmbedAdapter","resolveEmbed","createEmbedSource","adapter","undefined","call"],"sources":["../../src/shared/embed.ts"],"sourcesContent":["import type { EmbedValue } from '@panneau/protocol';\n\n/** Where `embed` fields get what a URL embeds: the backend, from the provider's oEmbed. */\nexport interface EmbedSource {\n resolve(url: string, options: { signal: AbortSignal }): Promise<EmbedValue>;\n}\n\n/** What `createEmbedSource()` needs from an adapter: the `resolveEmbed()` of `@panneau/data`. */\nexport interface EmbedAdapter {\n resolveEmbed?(url: string, options?: { signal?: AbortSignal }): Promise<EmbedValue>;\n}\n\n/**\n * The embed source of an adapter (`GET /embeds?url=`). Null when the adapter can't resolve\n * embeds: `embed` fields then keep the URL alone.\n */\nexport function createEmbedSource(adapter: EmbedAdapter): EmbedSource | null {\n const { resolveEmbed } = adapter;\n if (resolveEmbed === undefined) {\n return null;\n }\n return { resolve: (url, { signal }) => resolveEmbed.call(adapter, url, { signal }) };\n}\n"],"mappings":";;;;;AAgBA,SAAgBU,kBAAkBC,SAA2C;CACzE,MAAM,EAAEF,iBAAiBE;CACzB,IAAIF,iBAAiBG,KAAAA,GACjB,OAAO;CAEX,OAAO,EAAEV,UAAUC,KAAK,EAAEE,aAAaI,aAAaI,KAAKF,SAASR,KAAK,EAAEE,OAAO,CAAC,EAAE;AACvF"}
@@ -1 +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\n/** The accepted MIME types of a field that takes files: its `accept`, or its `accept` rule. */\nexport function getAccept(field: Field): string[] | null {\n if (field.accept !== undefined && field.accept.length > 0) {\n return field.accept;\n }\n const rule = findRule(field, 'accept');\n return rule !== undefined && 'value' in rule && Array.isArray(rule.value)\n ? rule.value.filter(isString)\n : null;\n}\n\n/** The largest file of a field, in bytes: its `maxSize` rule. */\nexport function getMaxSize(field: Field): number | null {\n const rule = findRule(field, 'maxSize');\n return rule !== undefined && 'value' in rule && isNumber(rule.value) ? rule.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;;AAQA,SAAgB,UAAU,OAA+B;CACrD,IAAI,MAAM,WAAW,KAAA,KAAa,MAAM,OAAO,SAAS,GACpD,OAAO,MAAM;CAEjB,MAAM,OAAO,SAAS,OAAO,QAAQ;CACrC,OAAO,SAAS,KAAA,KAAa,WAAW,QAAQ,MAAM,QAAQ,KAAK,KAAK,IAClE,KAAK,MAAM,OAAO,QAAQ,IAC1B;AACV;;AAGA,SAAgB,WAAW,OAA6B;CACpD,MAAM,OAAO,SAAS,OAAO,SAAS;CACtC,OAAO,SAAS,KAAA,KAAa,WAAW,QAAQ,SAAS,KAAK,KAAK,IAAI,KAAK,QAAQ;AACxF"}
1
+ {"version":3,"file":"entry.js","names":["Field","Rule","FieldEntryProps","FieldProps","findRule","field","rule","rules","find","candidate","getRuleNumber","found","undefined","value","isRequired","getSetting","name","guard","T","settings","isString","isNumber","Number","isNaN","getFieldProps","props","Omit","placeholder","path","label","description","help","required","readOnly","disabled","errors","onBlur","className","asString","getAccept","accept","length","Array","isArray","filter","getMaxSize"],"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\n/** The accepted MIME types of a field that takes files: its `accept`, or its `accept` rule. */\nexport function getAccept(field: Field): string[] | null {\n if (field.accept !== undefined && field.accept.length > 0) {\n return field.accept;\n }\n const rule = findRule(field, 'accept');\n return rule !== undefined && 'value' in rule && Array.isArray(rule.value)\n ? rule.value.filter(isString)\n : null;\n}\n\n/** The largest file of a field, in bytes: its `maxSize` rule. */\nexport function getMaxSize(field: Field): number | null {\n const rule = findRule(field, 'maxSize');\n return rule !== undefined && 'value' in rule && isNumber(rule.value) ? rule.value : null;\n}\n"],"mappings":";;AAKA,SAAgBI,SAASC,OAAcC,MAAsC;CACzE,OAAOD,MAAME,OAAOC,MAAMC,cAAcA,UAAUH,SAASA,IAAI;AACnE;;AAGA,SAAgBI,cAAcL,OAAcC,MAAyC;CACjF,MAAMK,QAAQP,SAASC,OAAOC,IAAI;CAClC,OAAOK,UAAUC,KAAAA,KAAa,WAAWD,SAAS,OAAOA,MAAME,UAAU,WACnEF,MAAME,QACND,KAAAA;AACV;;AAGA,SAAgBE,WAAWT,OAAuB;CAC9C,OAAOD,SAASC,OAAO,UAAU,MAAMO,KAAAA;AAC3C;;AAGA,SAAgBG,WAAcV,OAAcW,MAAcC,OAAuC;CAC7F,MAAMJ,QAAQR,MAAMc,WAAWH;CAC/B,OAAOC,MAAMJ,KAAK,IAAIA,QAAQD,KAAAA;AAClC;AAEA,IAAaQ,YAAYP,UAAoC,OAAOA,UAAU;AAC9E,IAAaQ,YAAYR,UACrB,OAAOA,UAAU,YAAY,CAACS,OAAOC,MAAMV,KAAK;;;;;;AAOpD,SAAgBW,cACZC,OACwE;CACxE,MAAM,EAAEpB,UAAUoB;CAClB,OAAO;EACHT,MAAMS,MAAMG;EAEZC,OAAOxB,MAAMwB,SAASxB,MAAMW;EAC5Bc,aAAazB,MAAM0B;EACnB,GAAI1B,MAAMsB,gBAAgBf,KAAAA,IAAY,EAAEe,aAAatB,MAAMsB,YAAY,IAAI,CAAC;EAC5EK,UAAUlB,WAAWT,KAAK;EAC1B4B,UAAUR,MAAMQ,aAAa,QAAQ5B,MAAM4B,aAAa;EACxDC,UAAUT,MAAMS,aAAa;EAC7BC,QAAQV,MAAMU,UAAU,CAAA;EACxB,GAAIV,MAAMW,WAAWxB,KAAAA,IAAY,EAAEwB,QAAQX,MAAMW,OAAO,IAAI,CAAC;EAC7D,GAAIX,MAAMY,cAAczB,KAAAA,IAAY,EAAEyB,WAAWZ,MAAMY,UAAU,IAAI,CAAC;CAC1E;AACJ;;AAQA,SAAgBE,UAAUlC,OAA+B;CACrD,IAAIA,MAAMmC,WAAW5B,KAAAA,KAAaP,MAAMmC,OAAOC,SAAS,GACpD,OAAOpC,MAAMmC;CAEjB,MAAMlC,OAAOF,SAASC,OAAO,QAAQ;CACrC,OAAOC,SAASM,KAAAA,KAAa,WAAWN,QAAQoC,MAAMC,QAAQrC,KAAKO,KAAK,IAClEP,KAAKO,MAAM+B,OAAOxB,QAAQ,IAC1B;AACV;;AAGA,SAAgByB,WAAWxC,OAA6B;CACpD,MAAMC,OAAOF,SAASC,OAAO,SAAS;CACtC,OAAOC,SAASM,KAAAA,KAAa,WAAWN,QAAQe,SAASf,KAAKO,KAAK,IAAIP,KAAKO,QAAQ;AACxF"}
@@ -1 +1 @@
1
- {"version":3,"file":"relation.js","names":["id","active","reference","_c"],"sources":["../../src/shared/relation.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\n\nimport 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 * The references of items by id, for values that are only ids (`\"12\"`, `[\"12\", \"13\"]`), as a\n * product's own API gives them. In any order; ids it doesn't find are left out, and keep their\n * id as label. Without it, fields show the ids.\n */\n resolve?(\n resource: string,\n ids: readonly string[],\n options: { signal: AbortSignal },\n ): Promise<RelationReference[]>;\n /**\n * Creates an item from the text typed in the picker, which offers \"Create “…”\" when this\n * method exists. It can open a dialog, and resolves with the reference of the new item, or with\n * `null` when nothing was created.\n */\n create?(resource: string, text: string): Promise<RelationReference | null>;\n /** Whether `create()` can create items of a resource. Defaults to true. */\n canCreate?(resource: string): boolean;\n}\n\n/** What `createRelationSource()` needs from an adapter: the `list()`, `get()` and `create()` of `@panneau/data`. */\nexport interface RelationListAdapter {\n list(\n resource: string,\n query?: ListQuery,\n options?: { signal?: AbortSignal },\n ): Promise<{ data: Item[] }>;\n /** One item, for `resolve()`. Without it, the source doesn't resolve ids. */\n get?(resource: string, id: string, options?: { signal?: AbortSignal }): Promise<Item>;\n /** Creates an item, for `create()`. */\n create?(resource: string, values: Record<string, unknown>): Promise<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'> & Partial<Pick<Definition, 'locales'>>) | 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 * Lets the picker create items, with the adapter's `create()`. `true` sets the typed text as the\n * value of the resource's `itemTitle` field (in every locale of the definition when it is\n * localized), on the resources that allow `create`. A function gives the values of the new item\n * instead, or `null` for a resource where items can't be created.\n */\n create?: boolean | ((text: string, resource: string) => Record<string, unknown> | null);\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, such as the `DataAdapter` of `@panneau/data`: a search is a\n * list query with `q`, ids are resolved with `get()`, and, with the `create` option, items are\n * created with `create()`. Labels come from the resource's `itemTitle`.\n */\nexport function createRelationSource(\n adapter: RelationListAdapter,\n { definition = null, perPage = 20, getLabel, create }: RelationSourceOptions = {},\n): RelationSource {\n const resourceOf = (resource: string) =>\n definition?.resources.find((candidate) => candidate.id === resource);\n const toReference = (item: Item, resource: string): RelationReference => {\n const titleField = resourceOf(resource)?.itemTitle;\n return {\n id: item.id,\n label:\n getLabel?.(item, resource) ??\n (titleField !== undefined ? labelOf(item[titleField]) : null) ??\n item.id,\n };\n };\n const valuesOf = (text: string, resource: string): Record<string, unknown> | null => {\n if (typeof create === 'function') {\n return create(text, resource);\n }\n const definitionResource = resourceOf(resource);\n const titleField = definitionResource?.itemTitle;\n if (definitionResource === undefined || titleField === undefined) {\n return null;\n }\n const field = definitionResource.fields.find((candidate) => candidate.name === titleField);\n const locales = definition?.locales ?? [];\n return {\n [titleField]:\n field?.localized === true && locales.length > 0\n ? Object.fromEntries(locales.map((locale) => [locale, text]))\n : text,\n };\n };\n const source: RelationSource = {\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 return response.data.map((item) => toReference(item, resource));\n },\n };\n const { get, create: createItem } = adapter;\n if (get !== undefined) {\n source.resolve = async (resource, ids, { signal }) => {\n const results = await Promise.allSettled(\n ids.map((id) => get.call(adapter, resource, id, { signal })),\n );\n return results.flatMap((result) =>\n result.status === 'fulfilled' ? [toReference(result.value, resource)] : [],\n );\n };\n }\n if (create !== undefined && create !== false && createItem !== undefined) {\n source.canCreate = (resource) =>\n resourceOf(resource)?.abilities?.create !== false && valuesOf('', resource) !== null;\n source.create = async (resource, text) => {\n const values = valuesOf(text, resource);\n if (values === null) {\n return null;\n }\n return toReference(await createItem.call(adapter, resource, values), resource);\n };\n }\n return source;\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\ninterface ResolvedLabels {\n /** Labels by resource and id: `null` for ids that the source didn't find. */\n labels: Map<string, string | null>;\n /** Requests in flight, so that fields with the same ids share one. */\n pending: Map<string, Promise<void>>;\n}\n\nconst resolved = new WeakMap<RelationSource, ResolvedLabels>();\n\nfunction resolvedOf(source: RelationSource): ResolvedLabels {\n let entry = resolved.get(source);\n if (entry === undefined) {\n entry = { labels: new Map(), pending: new Map() };\n resolved.set(source, entry);\n }\n return entry;\n}\n\nconst keyOf = (resource: string, id: string) => `${resource}\\u0000${id}`;\n\n/** Resolves the ids that no request has resolved or is resolving yet, and waits for all of them. */\nfunction resolveIds(source: RelationSource, resource: string, ids: readonly string[]) {\n const { labels, pending } = resolvedOf(source);\n const fresh = ids.filter((id) => !pending.has(keyOf(resource, id)));\n if (fresh.length > 0 && source.resolve !== undefined) {\n // Shared by every field that asks for these ids: it isn't aborted when one unmounts.\n const request = source\n .resolve(resource, fresh, { signal: new AbortController().signal })\n .then(\n (found) => {\n fresh.forEach((id) =>\n labels.set(\n keyOf(resource, id),\n found.find((candidate) => candidate.id === id)?.label ?? null,\n ),\n );\n },\n () => {\n // Tried again the next time a field shows these ids.\n fresh.forEach((id) => pending.delete(keyOf(resource, id)));\n },\n );\n fresh.forEach((id) => pending.set(keyOf(resource, id), request));\n }\n return Promise.all(ids.map((id) => pending.get(keyOf(resource, id))));\n}\n\n/**\n * Relation references with the labels of plain ids: references whose label is their id (from\n * `\"12\"`) are resolved with the source's `resolve()`, once per source, resource and id. They keep\n * their id as label until then, or when the source doesn't find them.\n */\nexport function useRelationReferences(\n source: RelationSource | null,\n resource: string,\n references: readonly RelationReference[],\n): RelationReference[] {\n const cache = source !== null ? resolvedOf(source).labels : null;\n const [labels, setLabels] = useState<Readonly<Record<string, string>>>({});\n const label = (id: string): string | undefined =>\n labels[keyOf(resource, id)] ?? cache?.get(keyOf(resource, id)) ?? undefined;\n const missing =\n source?.resolve !== undefined && cache !== null\n ? references\n .filter((reference) => reference.label === reference.id)\n .map((reference) => reference.id)\n .filter((id) => !cache.has(keyOf(resource, id)))\n : [];\n const missingKey = missing.join('\\u0000');\n\n useEffect(() => {\n const ids = missingKey === '' ? [] : missingKey.split('\\u0000');\n if (source === null || cache === null || ids.length === 0) {\n return undefined;\n }\n let active = true;\n void resolveIds(source, resource, ids).then(() => {\n if (!active) {\n return;\n }\n const next: Record<string, string> = {};\n ids.forEach((id) => {\n const found = cache.get(keyOf(resource, id));\n if (typeof found === 'string') {\n next[keyOf(resource, id)] = found;\n }\n });\n setLabels((current) => ({ ...current, ...next }));\n });\n return () => {\n active = false;\n };\n }, [source, cache, resource, missingKey]);\n\n return references.map((reference) =>\n reference.label === reference.id\n ? { id: reference.id, label: label(reference.id) ?? reference.id }\n : reference,\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"relation.js","names":["useEffect","useState","Definition","Item","ListQuery","RelationReference","id","label","RelationSource","search","resource","query","options","signal","AbortSignal","Promise","resolve","ids","create","text","canCreate","RelationListAdapter","list","data","get","values","Record","RelationSourceOptions","definition","Pick","Partial","perPage","getLabel","item","labelOf","value","locale","String","localized","first","undefined","Object","createRelationSource","adapter","resourceOf","resources","find","candidate","toReference","titleField","itemTitle","valuesOf","definitionResource","field","fields","name","locales","length","fromEntries","map","source","response","page","q","createItem","results","allSettled","call","flatMap","result","status","abilities","toReferences","Array","isArray","entry","ResolvedLabels","labels","Map","pending","resolved","WeakMap","resolvedOf","set","keyOf","resolveIds","fresh","filter","has","request","AbortController","then","found","forEach","delete","all","useRelationReferences","references","$","_c","cache","t0","Symbol","for","setLabels","missing","_temp","_temp2","id_0","missingKey","join","split","active","next","id_1","current","reference_1","reference","reference_0","_c"],"sources":["../../src/shared/relation.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\n\nimport 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 * The references of items by id, for values that are only ids (`\"12\"`, `[\"12\", \"13\"]`), as a\n * product's own API gives them. In any order; ids it doesn't find are left out, and keep their\n * id as label. Without it, fields show the ids.\n */\n resolve?(\n resource: string,\n ids: readonly string[],\n options: { signal: AbortSignal },\n ): Promise<RelationReference[]>;\n /**\n * Creates an item from the text typed in the picker, which offers \"Create “…”\" when this\n * method exists. It can open a dialog, and resolves with the reference of the new item, or with\n * `null` when nothing was created.\n */\n create?(resource: string, text: string): Promise<RelationReference | null>;\n /** Whether `create()` can create items of a resource. Defaults to true. */\n canCreate?(resource: string): boolean;\n}\n\n/** What `createRelationSource()` needs from an adapter: the `list()`, `get()` and `create()` of `@panneau/data`. */\nexport interface RelationListAdapter {\n list(\n resource: string,\n query?: ListQuery,\n options?: { signal?: AbortSignal },\n ): Promise<{ data: Item[] }>;\n /** One item, for `resolve()`. Without it, the source doesn't resolve ids. */\n get?(resource: string, id: string, options?: { signal?: AbortSignal }): Promise<Item>;\n /** Creates an item, for `create()`. */\n create?(resource: string, values: Record<string, unknown>): Promise<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'> & Partial<Pick<Definition, 'locales'>>) | 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 * Lets the picker create items, with the adapter's `create()`. `true` sets the typed text as the\n * value of the resource's `itemTitle` field (in every locale of the definition when it is\n * localized), on the resources that allow `create`. A function gives the values of the new item\n * instead, or `null` for a resource where items can't be created.\n */\n create?: boolean | ((text: string, resource: string) => Record<string, unknown> | null);\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, such as the `DataAdapter` of `@panneau/data`: a search is a\n * list query with `q`, ids are resolved with `get()`, and, with the `create` option, items are\n * created with `create()`. Labels come from the resource's `itemTitle`.\n */\nexport function createRelationSource(\n adapter: RelationListAdapter,\n { definition = null, perPage = 20, getLabel, create }: RelationSourceOptions = {},\n): RelationSource {\n const resourceOf = (resource: string) =>\n definition?.resources.find((candidate) => candidate.id === resource);\n const toReference = (item: Item, resource: string): RelationReference => {\n const titleField = resourceOf(resource)?.itemTitle;\n return {\n id: item.id,\n label:\n getLabel?.(item, resource) ??\n (titleField !== undefined ? labelOf(item[titleField]) : null) ??\n item.id,\n };\n };\n const valuesOf = (text: string, resource: string): Record<string, unknown> | null => {\n if (typeof create === 'function') {\n return create(text, resource);\n }\n const definitionResource = resourceOf(resource);\n const titleField = definitionResource?.itemTitle;\n if (definitionResource === undefined || titleField === undefined) {\n return null;\n }\n const field = definitionResource.fields.find((candidate) => candidate.name === titleField);\n const locales = definition?.locales ?? [];\n return {\n [titleField]:\n field?.localized === true && locales.length > 0\n ? Object.fromEntries(locales.map((locale) => [locale, text]))\n : text,\n };\n };\n const source: RelationSource = {\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 return response.data.map((item) => toReference(item, resource));\n },\n };\n const { get, create: createItem } = adapter;\n if (get !== undefined) {\n source.resolve = async (resource, ids, { signal }) => {\n const results = await Promise.allSettled(\n ids.map((id) => get.call(adapter, resource, id, { signal })),\n );\n return results.flatMap((result) =>\n result.status === 'fulfilled' ? [toReference(result.value, resource)] : [],\n );\n };\n }\n if (create !== undefined && create !== false && createItem !== undefined) {\n source.canCreate = (resource) =>\n resourceOf(resource)?.abilities?.create !== false && valuesOf('', resource) !== null;\n source.create = async (resource, text) => {\n const values = valuesOf(text, resource);\n if (values === null) {\n return null;\n }\n return toReference(await createItem.call(adapter, resource, values), resource);\n };\n }\n return source;\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\ninterface ResolvedLabels {\n /** Labels by resource and id: `null` for ids that the source didn't find. */\n labels: Map<string, string | null>;\n /** Requests in flight, so that fields with the same ids share one. */\n pending: Map<string, Promise<void>>;\n}\n\nconst resolved = new WeakMap<RelationSource, ResolvedLabels>();\n\nfunction resolvedOf(source: RelationSource): ResolvedLabels {\n let entry = resolved.get(source);\n if (entry === undefined) {\n entry = { labels: new Map(), pending: new Map() };\n resolved.set(source, entry);\n }\n return entry;\n}\n\nconst keyOf = (resource: string, id: string) => `${resource}\\u0000${id}`;\n\n/** Resolves the ids that no request has resolved or is resolving yet, and waits for all of them. */\nfunction resolveIds(source: RelationSource, resource: string, ids: readonly string[]) {\n const { labels, pending } = resolvedOf(source);\n const fresh = ids.filter((id) => !pending.has(keyOf(resource, id)));\n if (fresh.length > 0 && source.resolve !== undefined) {\n // Shared by every field that asks for these ids: it isn't aborted when one unmounts.\n const request = source\n .resolve(resource, fresh, { signal: new AbortController().signal })\n .then(\n (found) => {\n fresh.forEach((id) =>\n labels.set(\n keyOf(resource, id),\n found.find((candidate) => candidate.id === id)?.label ?? null,\n ),\n );\n },\n () => {\n // Tried again the next time a field shows these ids.\n fresh.forEach((id) => pending.delete(keyOf(resource, id)));\n },\n );\n fresh.forEach((id) => pending.set(keyOf(resource, id), request));\n }\n return Promise.all(ids.map((id) => pending.get(keyOf(resource, id))));\n}\n\n/**\n * Relation references with the labels of plain ids: references whose label is their id (from\n * `\"12\"`) are resolved with the source's `resolve()`, once per source, resource and id. They keep\n * their id as label until then, or when the source doesn't find them.\n */\nexport function useRelationReferences(\n source: RelationSource | null,\n resource: string,\n references: readonly RelationReference[],\n): RelationReference[] {\n const cache = source !== null ? resolvedOf(source).labels : null;\n const [labels, setLabels] = useState<Readonly<Record<string, string>>>({});\n const label = (id: string): string | undefined =>\n labels[keyOf(resource, id)] ?? cache?.get(keyOf(resource, id)) ?? undefined;\n const missing =\n source?.resolve !== undefined && cache !== null\n ? references\n .filter((reference) => reference.label === reference.id)\n .map((reference) => reference.id)\n .filter((id) => !cache.has(keyOf(resource, id)))\n : [];\n const missingKey = missing.join('\\u0000');\n\n useEffect(() => {\n const ids = missingKey === '' ? [] : missingKey.split('\\u0000');\n if (source === null || cache === null || ids.length === 0) {\n return undefined;\n }\n let active = true;\n void resolveIds(source, resource, ids).then(() => {\n if (!active) {\n return;\n }\n const next: Record<string, string> = {};\n ids.forEach((id) => {\n const found = cache.get(keyOf(resource, id));\n if (typeof found === 'string') {\n next[keyOf(resource, id)] = found;\n }\n });\n setLabels((current) => ({ ...current, ...next }));\n });\n return () => {\n active = false;\n };\n }, [source, cache, resource, missingKey]);\n\n return references.map((reference) =>\n reference.label === reference.id\n ? { id: reference.id, label: label(reference.id) ?? reference.id }\n : reference,\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"SlugField.d.ts","sourceRoot":"","sources":["../../src/slug/SlugField.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAM5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAa,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGnE;;;GAGG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO5C;AAED,4EAA4E;AAC5E,MAAM,MAAM,SAAS,GAAG,CACpB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,KAC/B,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAEjC,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC3E;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IAC3B,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AA4CD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,EACtB,KAAK,EACL,QAAQ,EACR,MAAM,EACN,MAAM,EACN,WAAW,EACX,OAAO,EACP,UAAgB,EAChB,QAAa,EACb,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,MAAM,EACN,WAAW,EACX,GAAG,KAAK,EACX,EAAE,cAAc,+BA4FhB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAazE;AAeD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,eAAe,+BAqCpD"}
1
+ {"version":3,"file":"SlugField.d.ts","sourceRoot":"","sources":["../../src/slug/SlugField.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAM5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAa,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAMnE;;;GAGG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO5C;AAED,4EAA4E;AAC5E,MAAM,MAAM,SAAS,GAAG,CACpB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,KAC/B,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAEjC,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC3E;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IAC3B,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AA4CD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,EACtB,KAAK,EACL,QAAQ,EACR,MAAM,EACN,MAAM,EACN,WAAW,EACX,OAAO,EACP,UAAgB,EAChB,QAAa,EACb,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,MAAM,EACN,WAAW,EACX,GAAG,KAAK,EACX,EAAE,cAAc,+BA4FhB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAazE;AAeD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,eAAe,+BAqCpD"}
@@ -9,6 +9,7 @@ import { useEffect, useRef, useState } from "react";
9
9
  import { jsx, jsxs } from "react/jsx-runtime";
10
10
  import { WandSparkles } from "lucide-react";
11
11
  //#region src/slug/SlugField.tsx
12
+ var suggestionMessage = messages.useSuggestion;
12
13
  /**
13
14
  * Turns a text into a slug: lowercase ASCII letters and digits separated by dashes, without
14
15
  * accents (`Été à Montréal` gives `ete-a-montreal`).
@@ -82,41 +83,134 @@ function useSlugCheck(value, onCheck, delay, key) {
82
83
  * slug is taken, and gives its URL.
83
84
  */
84
85
  function _temp() {}
85
- function SlugField({ value, onChange, onBlur, source, sourceLabel, onCheck, checkDelay = 300, checkKey = "", disabled, readOnly, prefix, errors, description, ...props }) {
86
+ function SlugField(t0) {
87
+ const $ = c(65);
88
+ let description;
89
+ let disabled;
90
+ let errors;
91
+ let onBlur;
92
+ let onChange;
93
+ let onCheck;
94
+ let prefix;
95
+ let props;
96
+ let readOnly;
97
+ let source;
98
+ let sourceLabel;
99
+ let t1;
100
+ let t2;
101
+ let value;
102
+ if ($[0] !== t0) {
103
+ ({value, onChange, onBlur, source, sourceLabel, onCheck, checkDelay: t1, checkKey: t2, disabled, readOnly, prefix, errors, description, ...props} = t0);
104
+ $[0] = t0;
105
+ $[1] = description;
106
+ $[2] = disabled;
107
+ $[3] = errors;
108
+ $[4] = onBlur;
109
+ $[5] = onChange;
110
+ $[6] = onCheck;
111
+ $[7] = prefix;
112
+ $[8] = props;
113
+ $[9] = readOnly;
114
+ $[10] = source;
115
+ $[11] = sourceLabel;
116
+ $[12] = t1;
117
+ $[13] = t2;
118
+ $[14] = value;
119
+ } else {
120
+ description = $[1];
121
+ disabled = $[2];
122
+ errors = $[3];
123
+ onBlur = $[4];
124
+ onChange = $[5];
125
+ onCheck = $[6];
126
+ prefix = $[7];
127
+ props = $[8];
128
+ readOnly = $[9];
129
+ source = $[10];
130
+ sourceLabel = $[11];
131
+ t1 = $[12];
132
+ t2 = $[13];
133
+ value = $[14];
134
+ }
135
+ const checkDelay = t1 === void 0 ? 300 : t1;
136
+ const checkKey = t2 === void 0 ? "" : t2;
86
137
  const intl = usePanneauIntl();
87
138
  const previousSource = useRef(source);
88
139
  const check = useSlugCheck(value ?? "", onCheck, checkDelay, checkKey);
89
140
  const disabledByBackend = check?.disabled === true;
90
141
  const editable = disabled !== true && readOnly !== true && !disabledByBackend;
91
- useEffect(() => {
92
- const previous = previousSource.current;
93
- previousSource.current = source;
94
- if (!editable || source === previous || source === void 0) return;
95
- const current = value ?? "";
96
- if (current === "" || current === slugify(previous ?? "")) {
97
- const next = slugify(source ?? "");
98
- if (next !== current) onChange?.(next === "" ? null : next);
99
- }
100
- }, [
101
- source,
102
- value,
103
- onChange,
104
- editable
105
- ]);
106
- const generated = slugify(source ?? "");
142
+ let t3;
143
+ let t4;
144
+ if ($[15] !== editable || $[16] !== onChange || $[17] !== source || $[18] !== value) {
145
+ t3 = () => {
146
+ const previous = previousSource.current;
147
+ previousSource.current = source;
148
+ if (!editable || source === previous || source === void 0) return;
149
+ const current = value ?? "";
150
+ if (current === "" || current === slugify(previous ?? "")) {
151
+ const next = slugify(source ?? "");
152
+ if (next !== current) onChange?.(next === "" ? null : next);
153
+ }
154
+ };
155
+ t4 = [
156
+ source,
157
+ value,
158
+ onChange,
159
+ editable
160
+ ];
161
+ $[15] = editable;
162
+ $[16] = onChange;
163
+ $[17] = source;
164
+ $[18] = value;
165
+ $[19] = t3;
166
+ $[20] = t4;
167
+ } else {
168
+ t3 = $[19];
169
+ t4 = $[20];
170
+ }
171
+ useEffect(t3, t4);
172
+ const t5 = source ?? "";
173
+ let t6;
174
+ if ($[21] !== t5) {
175
+ t6 = slugify(t5);
176
+ $[21] = t5;
177
+ $[22] = t6;
178
+ } else t6 = $[22];
179
+ const generated = t6;
107
180
  const taken = check !== null && !check.available;
108
- const suggestion = taken && isString(check.suggestion) ? check.suggestion : null;
109
- const url = check !== null && isString(check.url) && !taken ? check.url : null;
110
- return /* @__PURE__ */ jsx(TextField, {
111
- autoComplete: "off",
112
- ...props,
113
- value,
114
- onChange,
115
- disabled,
116
- readOnly: readOnly === true || disabledByBackend,
117
- ...check !== null && isString(check.prefix) ? { prefix: check.prefix } : prefix !== void 0 ? { prefix } : {},
118
- ...errors !== void 0 ? { errors } : {},
119
- description: taken || url !== null || description !== void 0 ? /* @__PURE__ */ jsxs("span", {
181
+ let t7;
182
+ if ($[23] !== check || $[24] !== taken) {
183
+ t7 = taken && isString(check.suggestion) ? check.suggestion : null;
184
+ $[23] = check;
185
+ $[24] = taken;
186
+ $[25] = t7;
187
+ } else t7 = $[25];
188
+ const suggestion = t7;
189
+ let t8;
190
+ if ($[26] !== check || $[27] !== taken) {
191
+ t8 = check !== null && isString(check.url) && !taken ? check.url : null;
192
+ $[26] = check;
193
+ $[27] = taken;
194
+ $[28] = t8;
195
+ } else t8 = $[28];
196
+ const url = t8;
197
+ const t9 = readOnly === true || disabledByBackend;
198
+ let t10;
199
+ if ($[29] !== check || $[30] !== prefix) {
200
+ t10 = check !== null && isString(check.prefix) ? { prefix: check.prefix } : prefix !== void 0 ? { prefix } : {};
201
+ $[29] = check;
202
+ $[30] = prefix;
203
+ $[31] = t10;
204
+ } else t10 = $[31];
205
+ let t11;
206
+ if ($[32] !== errors) {
207
+ t11 = errors !== void 0 ? { errors } : {};
208
+ $[32] = errors;
209
+ $[33] = t11;
210
+ } else t11 = $[33];
211
+ let t12;
212
+ if ($[34] !== description || $[35] !== editable || $[36] !== intl || $[37] !== onChange || $[38] !== suggestion || $[39] !== taken || $[40] !== url) {
213
+ t12 = taken || url !== null || description !== void 0 ? /* @__PURE__ */ jsxs("span", {
120
214
  className: SlugField_module_default.description,
121
215
  children: [
122
216
  description !== void 0 ? /* @__PURE__ */ jsx("span", { children: description }) : null,
@@ -134,26 +228,80 @@ function SlugField({ value, onChange, onBlur, source, sourceLabel, onCheck, chec
134
228
  size: "sm",
135
229
  appearance: "ghost",
136
230
  onPress: () => onChange?.(suggestion),
137
- children: intl.formatMessage(messages.useSuggestion, { suggestion })
231
+ children: intl.formatMessage(suggestionMessage, { suggestion })
138
232
  }) : null]
139
233
  }) : null
140
234
  ]
141
- }) : void 0,
142
- onBlur: () => {
235
+ }) : void 0;
236
+ $[34] = description;
237
+ $[35] = editable;
238
+ $[36] = intl;
239
+ $[37] = onChange;
240
+ $[38] = suggestion;
241
+ $[39] = taken;
242
+ $[40] = url;
243
+ $[41] = t12;
244
+ } else t12 = $[41];
245
+ let t13;
246
+ if ($[42] !== onBlur || $[43] !== onChange || $[44] !== value) {
247
+ t13 = () => {
143
248
  if (value !== null && value !== void 0) {
144
249
  const next_0 = slugify(value);
145
250
  if (next_0 !== value) onChange?.(next_0 === "" ? null : next_0);
146
251
  }
147
252
  onBlur?.();
148
- },
149
- suffix: source !== void 0 && editable ? /* @__PURE__ */ jsx(IconButton, {
253
+ };
254
+ $[42] = onBlur;
255
+ $[43] = onChange;
256
+ $[44] = value;
257
+ $[45] = t13;
258
+ } else t13 = $[45];
259
+ let t14;
260
+ if ($[46] !== editable || $[47] !== generated || $[48] !== intl || $[49] !== onChange || $[50] !== source || $[51] !== sourceLabel || $[52] !== value) {
261
+ t14 = source !== void 0 && editable ? /* @__PURE__ */ jsx(IconButton, {
150
262
  icon: WandSparkles,
151
263
  size: "sm",
152
264
  label: intl.formatMessage(messages.generateSlug, { field: sourceLabel ?? "" }),
153
265
  isDisabled: generated === "" || generated === value,
154
266
  onPress: () => onChange?.(generated === "" ? null : generated)
155
- }) : void 0
156
- });
267
+ }) : void 0;
268
+ $[46] = editable;
269
+ $[47] = generated;
270
+ $[48] = intl;
271
+ $[49] = onChange;
272
+ $[50] = source;
273
+ $[51] = sourceLabel;
274
+ $[52] = value;
275
+ $[53] = t14;
276
+ } else t14 = $[53];
277
+ let t15;
278
+ if ($[54] !== disabled || $[55] !== onChange || $[56] !== props || $[57] !== t10 || $[58] !== t11 || $[59] !== t12 || $[60] !== t13 || $[61] !== t14 || $[62] !== t9 || $[63] !== value) {
279
+ t15 = /* @__PURE__ */ jsx(TextField, {
280
+ autoComplete: "off",
281
+ ...props,
282
+ value,
283
+ onChange,
284
+ disabled,
285
+ readOnly: t9,
286
+ ...t10,
287
+ ...t11,
288
+ description: t12,
289
+ onBlur: t13,
290
+ suffix: t14
291
+ });
292
+ $[54] = disabled;
293
+ $[55] = onChange;
294
+ $[56] = props;
295
+ $[57] = t10;
296
+ $[58] = t11;
297
+ $[59] = t12;
298
+ $[60] = t13;
299
+ $[61] = t14;
300
+ $[62] = t9;
301
+ $[63] = value;
302
+ $[64] = t15;
303
+ } else t15 = $[64];
304
+ return t15;
157
305
  }
158
306
  /**
159
307
  * The text of a source value: the value itself, or for a localized value, its value in `locale`.
@@ -1 +1 @@
1
- {"version":3,"file":"SlugField.js","names":["check","next","onCheck","_c","styles"],"sources":["../../src/slug/SlugField.tsx"],"sourcesContent":["import { WandSparkles } from 'lucide-react';\nimport { useEffect, useRef, useState } from 'react';\n\nimport type { FieldCheckResponse } from '@panneau/protocol';\nimport { Button, IconButton, usePanneauIntl } from '@panneau/ui';\n\nimport { useFieldsContext } from '../shared/context';\nimport { getFieldProps, getSetting, isString } from '../shared/entry';\nimport { messages } from '../shared/messages';\nimport type { FieldEntryProps } from '../shared/types';\nimport { TextField, type TextFieldProps } from '../text/TextField';\nimport styles from './SlugField.module.css';\n\n/**\n * Turns a text into a slug: lowercase ASCII letters and digits separated by dashes, without\n * accents (`Été à Montréal` gives `ete-a-montreal`).\n */\nexport function slugify(text: string): string {\n return text\n .normalize('NFKD')\n .replace(/[̀-ͯ]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\n/** Checks a slug with the backend: whether it is available, and its URL. */\nexport type SlugCheck = (\n value: string,\n options: { signal: AbortSignal },\n) => Promise<FieldCheckResponse>;\n\nexport interface SlugFieldProps extends Omit<TextFieldProps, 'type' | 'suffix'> {\n /**\n * The text the slug is made from (a title). While the slug is empty or still matches it, the\n * slug follows it.\n */\n source?: string | null;\n /** Name of the source, for the button that generates the slug from it. */\n sourceLabel?: string;\n /**\n * Checks the slug as it is typed, and once when the field shows: the field then shows the\n * URL the backend gives, and says when the slug is taken, with a free one to use instead.\n */\n onCheck?: SlugCheck | null;\n /** How long typing must pause before a check, in milliseconds. Defaults to 300. */\n checkDelay?: number;\n /**\n * What the check depends on besides the slug, such as the form's values: the slug is checked\n * again when it changes. `onCheck` itself can change on every render.\n */\n checkKey?: string;\n}\n\n/**\n * Checks a slug after a pause in typing, and keeps the latest answer for it. A check that\n * fails leaves the field as it is: saving still validates the slug.\n */\nfunction useSlugCheck(\n value: string,\n onCheck: SlugCheck | null | undefined,\n delay: number,\n key: string,\n): FieldCheckResponse | null {\n const [result, setResult] = useState<{ value: string; check: FieldCheckResponse } | null>(null);\n const latest = useRef(onCheck);\n useEffect(() => {\n latest.current = onCheck;\n });\n const enabled = onCheck !== null && onCheck !== undefined;\n useEffect(() => {\n if (!enabled || value === '') {\n return undefined;\n }\n const controller = new AbortController();\n const timer = setTimeout(() => {\n const check = latest.current;\n if (check === null || check === undefined) {\n return;\n }\n check(value, { signal: controller.signal })\n .then((check) => {\n if (!controller.signal.aborted) {\n setResult({ value, check });\n }\n })\n .catch(() => undefined);\n }, delay);\n return () => {\n clearTimeout(timer);\n controller.abort();\n };\n }, [value, enabled, delay, key]);\n return result !== null && result.value === value ? result.check : null;\n}\n\n/**\n * A slug for URLs. Typed text is cleaned up on blur. With a `source`, the slug follows it until\n * it is edited, and a button generates it again. With `onCheck`, the backend says whether the\n * slug is taken, and gives its URL.\n */\nexport function SlugField({\n value,\n onChange,\n onBlur,\n source,\n sourceLabel,\n onCheck,\n checkDelay = 300,\n checkKey = '',\n disabled,\n readOnly,\n prefix,\n errors,\n description,\n ...props\n}: SlugFieldProps) {\n const intl = usePanneauIntl();\n const previousSource = useRef(source);\n const check = useSlugCheck(value ?? '', onCheck, checkDelay, checkKey);\n const disabledByBackend = check?.disabled === true;\n const editable = disabled !== true && readOnly !== true && !disabledByBackend;\n\n useEffect(() => {\n const previous = previousSource.current;\n previousSource.current = source;\n if (!editable || source === previous || source === undefined) {\n return;\n }\n const current = value ?? '';\n if (current === '' || current === slugify(previous ?? '')) {\n const next = slugify(source ?? '');\n if (next !== current) {\n onChange?.(next === '' ? null : next);\n }\n }\n }, [source, value, onChange, editable]);\n\n const generated = slugify(source ?? '');\n const taken = check !== null && !check.available;\n const suggestion = taken && isString(check.suggestion) ? check.suggestion : null;\n const url = check !== null && isString(check.url) && !taken ? check.url : null;\n return (\n <TextField\n autoComplete=\"off\"\n {...props}\n value={value}\n onChange={onChange}\n disabled={disabled}\n readOnly={readOnly === true || disabledByBackend}\n {...(check !== null && isString(check.prefix)\n ? { prefix: check.prefix }\n : prefix !== undefined\n ? { prefix }\n : {})}\n {...(errors !== undefined ? { errors } : {})}\n description={\n taken || url !== null || description !== undefined ? (\n <span className={styles.description}>\n {description !== undefined ? <span>{description}</span> : null}\n {url !== null ? (\n <a href={url} target=\"_blank\" rel=\"noreferrer\" className={styles.url}>\n {url}\n </a>\n ) : null}\n {taken ? (\n <span className={styles.taken} role=\"status\">\n {intl.formatMessage(messages.slugTaken)}\n {suggestion !== null && editable ? (\n <Button\n size=\"sm\"\n appearance=\"ghost\"\n onPress={() => onChange?.(suggestion)}\n >\n {intl.formatMessage(messages.useSuggestion, {\n suggestion,\n })}\n </Button>\n ) : null}\n </span>\n ) : null}\n </span>\n ) : undefined\n }\n onBlur={() => {\n if (value !== null && value !== undefined) {\n const next = slugify(value);\n if (next !== value) {\n onChange?.(next === '' ? null : next);\n }\n }\n onBlur?.();\n }}\n suffix={\n source !== undefined && editable ? (\n <IconButton\n icon={WandSparkles}\n size=\"sm\"\n label={intl.formatMessage(messages.generateSlug, {\n field: sourceLabel ?? '',\n })}\n isDisabled={generated === '' || generated === value}\n onPress={() => onChange?.(generated === '' ? null : generated)}\n />\n ) : undefined\n }\n />\n );\n}\n\n/**\n * The text of a source value: the value itself, or for a localized value, its value in `locale`.\n * Without a locale, the first locale that has a value.\n */\nexport function sourceText(value: unknown, locale?: string): string | null {\n if (typeof value === 'string') {\n return value;\n }\n if (value !== null && typeof value === 'object') {\n const record = value as Record<string, unknown>;\n if (locale !== undefined) {\n const text = record[locale];\n return isString(text) ? text : null;\n }\n return Object.values(record).find(isString) ?? null;\n }\n return null;\n}\n\n/** The form's values that a check depends on: all but the slug itself. */\nfunction checkKeyOf(values: Record<string, unknown> | null, name: string): string {\n if (values === null) {\n return '';\n }\n const { [name]: _slug, ...others } = values;\n try {\n return JSON.stringify(others);\n } catch {\n return '';\n }\n}\n\n/**\n * The `slug` type: `from` names the field it follows, in the slug's own locale, or in the default\n * locale (the first of the fields context) for a slug that isn't localized. Setting `prefix`\n * shows a URL prefix. With `check`, the fields context's check source checks the slug of a field\n * at the top of the resource, with the form's values.\n */\nexport function SlugFieldEntry(props: FieldEntryProps) {\n const { field, parentValue, locale, path } = props;\n const { locales, checkSource } = useFieldsContext();\n const prefix = getSetting(field, 'prefix', isString);\n const values =\n parentValue !== null && typeof parentValue === 'object'\n ? (parentValue as Record<string, unknown>)\n : null;\n const source =\n field.from !== undefined && values !== null\n ? sourceText(values[field.from], locale ?? locales[0])\n : undefined;\n const topLevel = path === field.name || path === `${field.name}.${locale ?? ''}`;\n const onCheck: SlugCheck | null =\n field.check === true && checkSource !== null && topLevel\n ? (value, { signal }) =>\n checkSource.check(\n field.name,\n {\n value,\n ...(locale !== undefined ? { locale } : {}),\n values: values ?? {},\n },\n { signal },\n )\n : null;\n return (\n <SlugField\n {...getFieldProps(props)}\n value={isString(props.value) ? props.value : null}\n onChange={props.onChange}\n onCheck={onCheck}\n checkKey={onCheck !== null ? checkKeyOf(values, field.name) : ''}\n {...(source !== undefined ? { source, sourceLabel: field.from ?? '' } : {})}\n {...(prefix !== undefined ? { prefix } : {})}\n />\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SlugField.js","names":["WandSparkles","useEffect","useRef","useState","FieldCheckResponse","Button","IconButton","usePanneauIntl","useFieldsContext","getFieldProps","getSetting","isString","messages","FieldEntryProps","TextField","TextFieldProps","styles","suggestionMessage","useSuggestion","slugify","text","normalize","replace","toLowerCase","SlugCheck","value","options","signal","AbortSignal","Promise","SlugFieldProps","Omit","source","sourceLabel","onCheck","checkDelay","checkKey","useSlugCheck","delay","key","$","_c","result","setResult","latest","t0","current","enabled","undefined","t1","controller","AbortController","timer","setTimeout","check","then","check_0","aborted","catch","_temp","clearTimeout","abort","t2","SlugField","description","disabled","errors","onBlur","onChange","prefix","props","readOnly","intl","previousSource","disabledByBackend","editable","t3","t4","previous","next","t5","t6","generated","taken","available","t7","suggestion","t8","url","t9","t10","t11","t12","formatMessage","slugTaken","t13","next_0","t14","generateSlug","field","t15","sourceText","locale","record","Record","Object","values","find","checkKeyOf","name","_slug","others","JSON","stringify","SlugFieldEntry","parentValue","path","locales","checkSource","from","topLevel","_c","styles"],"sources":["../../src/slug/SlugField.tsx"],"sourcesContent":["import { WandSparkles } from 'lucide-react';\nimport { useEffect, useRef, useState } from 'react';\n\nimport type { FieldCheckResponse } from '@panneau/protocol';\nimport { Button, IconButton, usePanneauIntl } from '@panneau/ui';\n\nimport { useFieldsContext } from '../shared/context';\nimport { getFieldProps, getSetting, isString } from '../shared/entry';\nimport { messages } from '../shared/messages';\nimport type { FieldEntryProps } from '../shared/types';\nimport { TextField, type TextFieldProps } from '../text/TextField';\nimport styles from './SlugField.module.css';\n\n// Read outside the component: the React Compiler takes any `use…` name for a hook.\nconst suggestionMessage = messages.useSuggestion;\n\n/**\n * Turns a text into a slug: lowercase ASCII letters and digits separated by dashes, without\n * accents (`Été à Montréal` gives `ete-a-montreal`).\n */\nexport function slugify(text: string): string {\n return text\n .normalize('NFKD')\n .replace(/[̀-ͯ]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\n/** Checks a slug with the backend: whether it is available, and its URL. */\nexport type SlugCheck = (\n value: string,\n options: { signal: AbortSignal },\n) => Promise<FieldCheckResponse>;\n\nexport interface SlugFieldProps extends Omit<TextFieldProps, 'type' | 'suffix'> {\n /**\n * The text the slug is made from (a title). While the slug is empty or still matches it, the\n * slug follows it.\n */\n source?: string | null;\n /** Name of the source, for the button that generates the slug from it. */\n sourceLabel?: string;\n /**\n * Checks the slug as it is typed, and once when the field shows: the field then shows the\n * URL the backend gives, and says when the slug is taken, with a free one to use instead.\n */\n onCheck?: SlugCheck | null;\n /** How long typing must pause before a check, in milliseconds. Defaults to 300. */\n checkDelay?: number;\n /**\n * What the check depends on besides the slug, such as the form's values: the slug is checked\n * again when it changes. `onCheck` itself can change on every render.\n */\n checkKey?: string;\n}\n\n/**\n * Checks a slug after a pause in typing, and keeps the latest answer for it. A check that\n * fails leaves the field as it is: saving still validates the slug.\n */\nfunction useSlugCheck(\n value: string,\n onCheck: SlugCheck | null | undefined,\n delay: number,\n key: string,\n): FieldCheckResponse | null {\n const [result, setResult] = useState<{ value: string; check: FieldCheckResponse } | null>(null);\n const latest = useRef(onCheck);\n useEffect(() => {\n latest.current = onCheck;\n });\n const enabled = onCheck !== null && onCheck !== undefined;\n useEffect(() => {\n if (!enabled || value === '') {\n return undefined;\n }\n const controller = new AbortController();\n const timer = setTimeout(() => {\n const check = latest.current;\n if (check === null || check === undefined) {\n return;\n }\n check(value, { signal: controller.signal })\n .then((check) => {\n if (!controller.signal.aborted) {\n setResult({ value, check });\n }\n })\n .catch(() => undefined);\n }, delay);\n return () => {\n clearTimeout(timer);\n controller.abort();\n };\n }, [value, enabled, delay, key]);\n return result !== null && result.value === value ? result.check : null;\n}\n\n/**\n * A slug for URLs. Typed text is cleaned up on blur. With a `source`, the slug follows it until\n * it is edited, and a button generates it again. With `onCheck`, the backend says whether the\n * slug is taken, and gives its URL.\n */\nexport function SlugField({\n value,\n onChange,\n onBlur,\n source,\n sourceLabel,\n onCheck,\n checkDelay = 300,\n checkKey = '',\n disabled,\n readOnly,\n prefix,\n errors,\n description,\n ...props\n}: SlugFieldProps) {\n const intl = usePanneauIntl();\n const previousSource = useRef(source);\n const check = useSlugCheck(value ?? '', onCheck, checkDelay, checkKey);\n const disabledByBackend = check?.disabled === true;\n const editable = disabled !== true && readOnly !== true && !disabledByBackend;\n\n useEffect(() => {\n const previous = previousSource.current;\n previousSource.current = source;\n if (!editable || source === previous || source === undefined) {\n return;\n }\n const current = value ?? '';\n if (current === '' || current === slugify(previous ?? '')) {\n const next = slugify(source ?? '');\n if (next !== current) {\n onChange?.(next === '' ? null : next);\n }\n }\n }, [source, value, onChange, editable]);\n\n const generated = slugify(source ?? '');\n const taken = check !== null && !check.available;\n const suggestion = taken && isString(check.suggestion) ? check.suggestion : null;\n const url = check !== null && isString(check.url) && !taken ? check.url : null;\n return (\n <TextField\n autoComplete=\"off\"\n {...props}\n value={value}\n onChange={onChange}\n disabled={disabled}\n readOnly={readOnly === true || disabledByBackend}\n {...(check !== null && isString(check.prefix)\n ? { prefix: check.prefix }\n : prefix !== undefined\n ? { prefix }\n : {})}\n {...(errors !== undefined ? { errors } : {})}\n description={\n taken || url !== null || description !== undefined ? (\n <span className={styles.description}>\n {description !== undefined ? <span>{description}</span> : null}\n {url !== null ? (\n <a href={url} target=\"_blank\" rel=\"noreferrer\" className={styles.url}>\n {url}\n </a>\n ) : null}\n {taken ? (\n <span className={styles.taken} role=\"status\">\n {intl.formatMessage(messages.slugTaken)}\n {suggestion !== null && editable ? (\n <Button\n size=\"sm\"\n appearance=\"ghost\"\n onPress={() => onChange?.(suggestion)}\n >\n {intl.formatMessage(suggestionMessage, {\n suggestion,\n })}\n </Button>\n ) : null}\n </span>\n ) : null}\n </span>\n ) : undefined\n }\n onBlur={() => {\n if (value !== null && value !== undefined) {\n const next = slugify(value);\n if (next !== value) {\n onChange?.(next === '' ? null : next);\n }\n }\n onBlur?.();\n }}\n suffix={\n source !== undefined && editable ? (\n <IconButton\n icon={WandSparkles}\n size=\"sm\"\n label={intl.formatMessage(messages.generateSlug, {\n field: sourceLabel ?? '',\n })}\n isDisabled={generated === '' || generated === value}\n onPress={() => onChange?.(generated === '' ? null : generated)}\n />\n ) : undefined\n }\n />\n );\n}\n\n/**\n * The text of a source value: the value itself, or for a localized value, its value in `locale`.\n * Without a locale, the first locale that has a value.\n */\nexport function sourceText(value: unknown, locale?: string): string | null {\n if (typeof value === 'string') {\n return value;\n }\n if (value !== null && typeof value === 'object') {\n const record = value as Record<string, unknown>;\n if (locale !== undefined) {\n const text = record[locale];\n return isString(text) ? text : null;\n }\n return Object.values(record).find(isString) ?? null;\n }\n return null;\n}\n\n/** The form's values that a check depends on: all but the slug itself. */\nfunction checkKeyOf(values: Record<string, unknown> | null, name: string): string {\n if (values === null) {\n return '';\n }\n const { [name]: _slug, ...others } = values;\n try {\n return JSON.stringify(others);\n } catch {\n return '';\n }\n}\n\n/**\n * The `slug` type: `from` names the field it follows, in the slug's own locale, or in the default\n * locale (the first of the fields context) for a slug that isn't localized. Setting `prefix`\n * shows a URL prefix. With `check`, the fields context's check source checks the slug of a field\n * at the top of the resource, with the form's values.\n */\nexport function SlugFieldEntry(props: FieldEntryProps) {\n const { field, parentValue, locale, path } = props;\n const { locales, checkSource } = useFieldsContext();\n const prefix = getSetting(field, 'prefix', isString);\n const values =\n parentValue !== null && typeof parentValue === 'object'\n ? (parentValue as Record<string, unknown>)\n : null;\n const source =\n field.from !== undefined && values !== null\n ? sourceText(values[field.from], locale ?? locales[0])\n : undefined;\n const topLevel = path === field.name || path === `${field.name}.${locale ?? ''}`;\n const onCheck: SlugCheck | null =\n field.check === true && checkSource !== null && topLevel\n ? (value, { signal }) =>\n checkSource.check(\n field.name,\n {\n value,\n ...(locale !== undefined ? { locale } : {}),\n values: values ?? {},\n },\n { signal },\n )\n : null;\n return (\n <SlugField\n {...getFieldProps(props)}\n value={isString(props.value) ? props.value : null}\n onChange={props.onChange}\n onCheck={onCheck}\n checkKey={onCheck !== null ? checkKeyOf(values, field.name) : ''}\n {...(source !== undefined ? { source, sourceLabel: field.from ?? '' } : {})}\n {...(prefix !== undefined ? { prefix } : {})}\n />\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"TagsField.js","names":["disabled","readOnly","required","value","_c"],"sources":["../../src/tags/TagsField.tsx"],"sourcesContent":["import type { Option } from '@panneau/protocol';\nimport { type TagSuggestion, TagsInput } from '@panneau/ui';\n\nimport { getFieldProps, getSetting, isString } from '../shared/entry';\nimport type { FieldEntryProps, FieldProps } from '../shared/types';\n\nexport interface TagsFieldProps extends FieldProps<string[]> {\n placeholder?: string;\n /** Tags offered as the user types. */\n suggestions?: readonly TagSuggestion[];\n /** Whether tags outside the suggestions are allowed. Defaults to true. */\n allowCustom?: boolean;\n}\n\n/** A list of words, typed or picked among suggestions. An empty list is `null`. */\nexport function TagsField({ disabled, readOnly, required, ...props }: TagsFieldProps) {\n return (\n <TagsInput {...props} isDisabled={disabled} isReadOnly={readOnly} isRequired={required} />\n );\n}\n\n/** The options of a field as suggestions: their values as strings. */\nfunction toSuggestions(options: readonly Option[]): TagSuggestion[] {\n return options.map((option) => ({ value: String(option.value), label: option.label }));\n}\n\n/**\n * The `tags` type: its `options` as suggestions, and setting `allowCustom` (true by default)\n * for tags outside them.\n */\nexport function TagsFieldEntry(props: FieldEntryProps) {\n const { field } = props;\n const allowCustom = getSetting(\n field,\n 'allowCustom',\n (value): value is boolean => typeof value === 'boolean',\n );\n const value = Array.isArray(props.value) ? props.value.filter(isString) : null;\n return (\n <TagsField\n {...getFieldProps(props)}\n value={value !== null && value.length > 0 ? value : null}\n onChange={props.onChange}\n suggestions={toSuggestions(field.options ?? [])}\n {...(allowCustom !== undefined ? { allowCustom } : {})}\n />\n );\n}\n"],"mappings":";;;;;;AAeA,SAAO,UAAA,IAAA;CAAA,MAAA,IAAAI,EAAA,EAAA;CAAA,IAAA;CAAA,IAAA;CAAA,IAAA;CAAA,IAAA;CAAA,IAAA,EAAA,OAAA,IAAA;EAAmB,CAAA,CAAA,UAAA,UAAA,UAAA,GAAA,SAAA;EAA0D,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA;EAAA,WAAA,EAAA;EAAA,QAAA,EAAA;EAAA,WAAA,EAAA;EAAA,WAAA,EAAA;CAAA;CAAA,IAAA;CAAA,IAAA,EAAA,OAAA,YAAA,EAAA,OAAA,SAAA,EAAA,OAAA,YAAA,EAAA,OAAA,UAAA;EAE5E,KAAA,oBAAC,WAAD;GAAU,GAAK;GAAmBJ,YAAA;GAAsBC,YAAA;GAAsBC,YAAA;EAAQ,CAAA;EAAI,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,OAA1F;AAA0F;;AAKlG,SAAS,cAAc,SAA6C;CAChE,OAAO,QAAQ,KAAK,YAAY;EAAE,OAAO,OAAO,OAAO,KAAK;EAAG,OAAO,OAAO;CAAM,EAAE;AACzF;;;;;AAMA,SAAO,eAAA,OAAA;CAAA,MAAA,IAAAE,EAAA,EAAA;CACH,MAAA,EAAA,UAAkB;CAAM,IAAA;CAAA,IAAA,EAAA,OAAA,OAAA;EACJ,KAAA,WAChB,OACA,eACA,KACJ;EAAC,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAJD,MAAA,cAAoB;CAIlB,IAAA;CAAA,IAAA,EAAA,OAAA,MAAA,OAAA;EACY,KAAA,MAAK,QAAS,MAAK,KAA4C,IAAlC,MAAK,MAAM,OAAQ,QAAe,IAA/D;EAAgE,EAAA,KAAA,MAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAA9E,MAAA,UAAc;CAAiE,IAAA;CAAA,IAAA,EAAA,OAAA,OAAA;EAGnE,KAAA,cAAc,KAAK;EAAC,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CACjB,MAAA,KAAAD,YAAU,QAAQA,QAAK,SAAU,IAAjC,UAAA;CACG,MAAA,KAAA,MAAK;CAAS,IAAA;CAAA,IAAA,EAAA,OAAA,MAAA,SAAA;EACG,KAAA,MAAK,WAAL,CAAA;EAAmB,EAAA,KAAA,MAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,IAAA;CAAA,IAAA,EAAA,OAAA,IAAA;EAAjC,KAAA,cAAc,EAAmB;EAAC,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,IAAA;CAAA,IAAA,EAAA,QAAA,aAAA;EAC1C,KAAA,gBAAgB,KAAA,IAAhB,EAAA,YAA+C,IAA/C,CAA+C;EAAC,EAAA,MAAA;EAAA,EAAA,MAAA;CAAA,OAAA,KAAA,EAAA;CAAA,IAAA;CAAA,IAAA,EAAA,QAAA,MAAA,YAAA,EAAA,QAAA,MAAA,EAAA,QAAA,MAAA,EAAA,QAAA,MAAA,EAAA,QAAA,IAAA;EALzD,KAAA,oBAAC,WAAD;GAAU,GACF;GACG,OAAA;GACG,UAAA;GACG,aAAA;GAAkC,GAC1C;EAAgD,CAAA;EACvD,EAAA,MAAA,MAAA;EAAA,EAAA,MAAA;EAAA,EAAA,MAAA;EAAA,EAAA,MAAA;EAAA,EAAA,MAAA;EAAA,EAAA,MAAA;CAAA,OAAA,KAAA,EAAA;CAAA,OANF;AAME;AAfH,SAAA,MAAA,OAAA;CAAA,OAK8B,OAAO,UAAU;AAAS"}
1
+ {"version":3,"file":"TagsField.js","names":["Option","TagSuggestion","TagsInput","getFieldProps","getSetting","isString","FieldEntryProps","FieldProps","TagsFieldProps","placeholder","suggestions","allowCustom","TagsField","t0","$","_c","disabled","props","readOnly","required","t1","toSuggestions","options","map","option","value","String","label","TagsFieldEntry","field","_temp","Array","isArray","filter","value_0","t2","t3","length","t4","onChange","t5","t6","t7","undefined","t8","_c"],"sources":["../../src/tags/TagsField.tsx"],"sourcesContent":["import type { Option } from '@panneau/protocol';\nimport { type TagSuggestion, TagsInput } from '@panneau/ui';\n\nimport { getFieldProps, getSetting, isString } from '../shared/entry';\nimport type { FieldEntryProps, FieldProps } from '../shared/types';\n\nexport interface TagsFieldProps extends FieldProps<string[]> {\n placeholder?: string;\n /** Tags offered as the user types. */\n suggestions?: readonly TagSuggestion[];\n /** Whether tags outside the suggestions are allowed. Defaults to true. */\n allowCustom?: boolean;\n}\n\n/** A list of words, typed or picked among suggestions. An empty list is `null`. */\nexport function TagsField({ disabled, readOnly, required, ...props }: TagsFieldProps) {\n return (\n <TagsInput {...props} isDisabled={disabled} isReadOnly={readOnly} isRequired={required} />\n );\n}\n\n/** The options of a field as suggestions: their values as strings. */\nfunction toSuggestions(options: readonly Option[]): TagSuggestion[] {\n return options.map((option) => ({ value: String(option.value), label: option.label }));\n}\n\n/**\n * The `tags` type: its `options` as suggestions, and setting `allowCustom` (true by default)\n * for tags outside them.\n */\nexport function TagsFieldEntry(props: FieldEntryProps) {\n const { field } = props;\n const allowCustom = getSetting(\n field,\n 'allowCustom',\n (value): value is boolean => typeof value === 'boolean',\n );\n const value = Array.isArray(props.value) ? props.value.filter(isString) : null;\n return (\n <TagsField\n {...getFieldProps(props)}\n value={value !== null && value.length > 0 ? value : null}\n onChange={props.onChange}\n suggestions={toSuggestions(field.options ?? [])}\n {...(allowCustom !== undefined ? { allowCustom } : {})}\n />\n );\n}\n"],"mappings":"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"}