@funstack/ui 0.1.0-alpha.2 → 0.1.0-alpha.3

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 (223) hide show
  1. package/dist/accordion/accordion-context.mjs.map +1 -1
  2. package/dist/accordion/accordion-item.mjs +18 -4
  3. package/dist/accordion/accordion-item.mjs.map +1 -1
  4. package/dist/accordion/accordion-panel.mjs +2 -3
  5. package/dist/accordion/accordion-panel.mjs.map +1 -1
  6. package/dist/accordion/accordion-root.d.mts.map +1 -1
  7. package/dist/accordion/accordion-root.mjs +32 -14
  8. package/dist/accordion/accordion-root.mjs.map +1 -1
  9. package/dist/accordion/accordion-trigger.mjs +4 -5
  10. package/dist/accordion/accordion-trigger.mjs.map +1 -1
  11. package/dist/checkbox/checkbox.d.mts +5 -3
  12. package/dist/checkbox/checkbox.d.mts.map +1 -1
  13. package/dist/checkbox/checkbox.mjs +25 -9
  14. package/dist/checkbox/checkbox.mjs.map +1 -1
  15. package/dist/checkbox-group/checkbox-group-context.mjs.map +1 -1
  16. package/dist/checkbox-group/checkbox-group-item.d.mts +2 -1
  17. package/dist/checkbox-group/checkbox-group-item.d.mts.map +1 -1
  18. package/dist/checkbox-group/checkbox-group-item.mjs +3 -1
  19. package/dist/checkbox-group/checkbox-group-item.mjs.map +1 -1
  20. package/dist/checkbox-group/checkbox-group-root.d.mts +5 -2
  21. package/dist/checkbox-group/checkbox-group-root.d.mts.map +1 -1
  22. package/dist/checkbox-group/checkbox-group-root.mjs +24 -5
  23. package/dist/checkbox-group/checkbox-group-root.mjs.map +1 -1
  24. package/dist/combobox/combobox-context.mjs.map +1 -1
  25. package/dist/combobox/combobox-input.d.mts +3 -1
  26. package/dist/combobox/combobox-input.d.mts.map +1 -1
  27. package/dist/combobox/combobox-input.mjs +16 -2
  28. package/dist/combobox/combobox-input.mjs.map +1 -1
  29. package/dist/combobox/combobox-list.mjs +1 -1
  30. package/dist/combobox/combobox-list.mjs.map +1 -1
  31. package/dist/combobox/combobox-root.d.mts.map +1 -1
  32. package/dist/combobox/combobox-root.mjs +19 -4
  33. package/dist/combobox/combobox-root.mjs.map +1 -1
  34. package/dist/dialog/dialog-close.d.mts +17 -10
  35. package/dist/dialog/dialog-close.d.mts.map +1 -1
  36. package/dist/dialog/dialog-close.mjs +31 -20
  37. package/dist/dialog/dialog-close.mjs.map +1 -1
  38. package/dist/dialog/dialog-context.mjs.map +1 -1
  39. package/dist/dialog/dialog-form.d.mts +12 -5
  40. package/dist/dialog/dialog-form.d.mts.map +1 -1
  41. package/dist/dialog/dialog-form.mjs +16 -28
  42. package/dist/dialog/dialog-form.mjs.map +1 -1
  43. package/dist/dialog/dialog-popup.d.mts +10 -4
  44. package/dist/dialog/dialog-popup.d.mts.map +1 -1
  45. package/dist/dialog/dialog-popup.mjs +106 -41
  46. package/dist/dialog/dialog-popup.mjs.map +1 -1
  47. package/dist/dialog/dialog-root.d.mts +14 -8
  48. package/dist/dialog/dialog-root.d.mts.map +1 -1
  49. package/dist/dialog/dialog-root.mjs +66 -11
  50. package/dist/dialog/dialog-root.mjs.map +1 -1
  51. package/dist/dialog/dialog-trigger.d.mts +3 -2
  52. package/dist/dialog/dialog-trigger.d.mts.map +1 -1
  53. package/dist/dialog/dialog-trigger.mjs.map +1 -1
  54. package/dist/dialog/index.d.mts +4 -2
  55. package/dist/dialog/index.d.mts.map +1 -1
  56. package/dist/dialog/index.mjs +4 -2
  57. package/dist/dialog/index.mjs.map +1 -1
  58. package/dist/field/field-control.d.mts +33 -0
  59. package/dist/field/field-control.d.mts.map +1 -0
  60. package/dist/field/field-control.mjs +68 -0
  61. package/dist/field/field-control.mjs.map +1 -0
  62. package/dist/field/field-description.d.mts +24 -0
  63. package/dist/field/field-description.d.mts.map +1 -0
  64. package/dist/field/field-description.mjs +34 -0
  65. package/dist/field/field-description.mjs.map +1 -0
  66. package/dist/field/field-error.d.mts +30 -0
  67. package/dist/field/field-error.d.mts.map +1 -0
  68. package/dist/field/field-error.mjs +42 -0
  69. package/dist/field/field-error.mjs.map +1 -0
  70. package/dist/field/field-label.d.mts +25 -0
  71. package/dist/field/field-label.d.mts.map +1 -0
  72. package/dist/field/field-label.mjs +46 -0
  73. package/dist/field/field-label.mjs.map +1 -0
  74. package/dist/field/field-root.d.mts +53 -0
  75. package/dist/field/field-root.d.mts.map +1 -0
  76. package/dist/field/field-root.mjs +317 -0
  77. package/dist/field/field-root.mjs.map +1 -0
  78. package/dist/field/index.d.mts +18 -0
  79. package/dist/field/index.d.mts.map +1 -0
  80. package/dist/field/index.mjs +17 -0
  81. package/dist/field/index.mjs.map +1 -0
  82. package/dist/form/form-root.d.mts +58 -0
  83. package/dist/form/form-root.d.mts.map +1 -0
  84. package/dist/form/form-root.mjs +146 -0
  85. package/dist/form/form-root.mjs.map +1 -0
  86. package/dist/form/form-submit.d.mts +31 -0
  87. package/dist/form/form-submit.d.mts.map +1 -0
  88. package/dist/form/form-submit.mjs +52 -0
  89. package/dist/form/form-submit.mjs.map +1 -0
  90. package/dist/form/index.d.mts +11 -0
  91. package/dist/form/index.d.mts.map +1 -0
  92. package/dist/form/index.mjs +11 -0
  93. package/dist/form/index.mjs.map +1 -0
  94. package/dist/index.d.mts +27 -1
  95. package/dist/index.mjs +6 -1
  96. package/dist/internal/content-focus-probe.mjs +42 -0
  97. package/dist/internal/content-focus-probe.mjs.map +1 -0
  98. package/dist/internal/field-context.d.mts +19 -0
  99. package/dist/internal/field-context.d.mts.map +1 -0
  100. package/dist/internal/field-context.mjs +14 -0
  101. package/dist/internal/field-context.mjs.map +1 -0
  102. package/dist/internal/focus-first-invalid.mjs +67 -0
  103. package/dist/internal/focus-first-invalid.mjs.map +1 -0
  104. package/dist/internal/form-status-watcher.mjs +31 -0
  105. package/dist/internal/form-status-watcher.mjs.map +1 -0
  106. package/dist/internal/modal-registry.mjs +39 -0
  107. package/dist/internal/modal-registry.mjs.map +1 -0
  108. package/dist/internal/types.d.mts +3 -1
  109. package/dist/internal/types.d.mts.map +1 -1
  110. package/dist/internal/use-controllable-action.mjs +33 -15
  111. package/dist/internal/use-controllable-action.mjs.map +1 -1
  112. package/dist/internal/use-field-control.mjs +107 -0
  113. package/dist/internal/use-field-control.mjs.map +1 -0
  114. package/dist/internal/use-form-owner-event.mjs +44 -0
  115. package/dist/internal/use-form-owner-event.mjs.map +1 -0
  116. package/dist/internal/use-form-reset.mjs +10 -33
  117. package/dist/internal/use-form-reset.mjs.map +1 -1
  118. package/dist/internal/use-latest.mjs +32 -0
  119. package/dist/internal/use-latest.mjs.map +1 -0
  120. package/dist/internal/use-modal-registration.mjs +47 -0
  121. package/dist/internal/use-modal-registration.mjs.map +1 -0
  122. package/dist/internal/use-native-group-reconciliation.mjs +6 -5
  123. package/dist/internal/use-native-group-reconciliation.mjs.map +1 -1
  124. package/dist/internal/use-native-select-value.mjs +7 -5
  125. package/dist/internal/use-native-select-value.mjs.map +1 -1
  126. package/dist/internal/use-popover-popup.mjs +8 -22
  127. package/dist/internal/use-popover-popup.mjs.map +1 -1
  128. package/dist/internal/use-popover-root.mjs +58 -23
  129. package/dist/internal/use-popover-root.mjs.map +1 -1
  130. package/dist/internal/validity.d.mts +24 -0
  131. package/dist/internal/validity.d.mts.map +1 -0
  132. package/dist/internal/validity.mjs +108 -0
  133. package/dist/internal/validity.mjs.map +1 -0
  134. package/dist/menu/menu-group.d.mts.map +1 -1
  135. package/dist/menu/menu-group.mjs +3 -2
  136. package/dist/menu/menu-group.mjs.map +1 -1
  137. package/dist/menu/menu-radio-group.mjs +9 -4
  138. package/dist/menu/menu-radio-group.mjs.map +1 -1
  139. package/dist/menu/menu-root.mjs.map +1 -1
  140. package/dist/radio-group/radio-group-root.d.mts +4 -2
  141. package/dist/radio-group/radio-group-root.d.mts.map +1 -1
  142. package/dist/radio-group/radio-group-root.mjs +34 -8
  143. package/dist/radio-group/radio-group-root.mjs.map +1 -1
  144. package/dist/select/select-group.d.mts.map +1 -1
  145. package/dist/select/select-group.mjs +6 -4
  146. package/dist/select/select-group.mjs.map +1 -1
  147. package/dist/select/select-root.d.mts +4 -2
  148. package/dist/select/select-root.d.mts.map +1 -1
  149. package/dist/select/select-root.mjs +26 -6
  150. package/dist/select/select-root.mjs.map +1 -1
  151. package/dist/slider/index.d.mts +32 -0
  152. package/dist/slider/index.d.mts.map +1 -0
  153. package/dist/slider/index.mjs +27 -0
  154. package/dist/slider/index.mjs.map +1 -0
  155. package/dist/slider/slider-arithmetic.mjs +120 -0
  156. package/dist/slider/slider-arithmetic.mjs.map +1 -0
  157. package/dist/slider/slider-context.mjs +13 -0
  158. package/dist/slider/slider-context.mjs.map +1 -0
  159. package/dist/slider/slider-indicator.d.mts +22 -0
  160. package/dist/slider/slider-indicator.d.mts.map +1 -0
  161. package/dist/slider/slider-indicator.mjs +36 -0
  162. package/dist/slider/slider-indicator.mjs.map +1 -0
  163. package/dist/slider/slider-label.d.mts +23 -0
  164. package/dist/slider/slider-label.d.mts.map +1 -0
  165. package/dist/slider/slider-label.mjs +44 -0
  166. package/dist/slider/slider-label.mjs.map +1 -0
  167. package/dist/slider/slider-root.d.mts +111 -0
  168. package/dist/slider/slider-root.d.mts.map +1 -0
  169. package/dist/slider/slider-root.mjs +582 -0
  170. package/dist/slider/slider-root.mjs.map +1 -0
  171. package/dist/slider/slider-thumb.d.mts +58 -0
  172. package/dist/slider/slider-thumb.d.mts.map +1 -0
  173. package/dist/slider/slider-thumb.mjs +145 -0
  174. package/dist/slider/slider-thumb.mjs.map +1 -0
  175. package/dist/slider/slider-track.d.mts +29 -0
  176. package/dist/slider/slider-track.d.mts.map +1 -0
  177. package/dist/slider/slider-track.mjs +54 -0
  178. package/dist/slider/slider-track.mjs.map +1 -0
  179. package/dist/slider/slider-value.d.mts +34 -0
  180. package/dist/slider/slider-value.d.mts.map +1 -0
  181. package/dist/slider/slider-value.mjs +38 -0
  182. package/dist/slider/slider-value.mjs.map +1 -0
  183. package/dist/switch/switch.d.mts +5 -3
  184. package/dist/switch/switch.d.mts.map +1 -1
  185. package/dist/switch/switch.mjs +24 -8
  186. package/dist/switch/switch.mjs.map +1 -1
  187. package/dist/tabs/tabs-root.mjs +13 -3
  188. package/dist/tabs/tabs-root.mjs.map +1 -1
  189. package/dist/toast/index.d.mts +25 -0
  190. package/dist/toast/index.d.mts.map +1 -0
  191. package/dist/toast/index.mjs +26 -0
  192. package/dist/toast/index.mjs.map +1 -0
  193. package/dist/toast/toast-action.d.mts +39 -0
  194. package/dist/toast/toast-action.d.mts.map +1 -0
  195. package/dist/toast/toast-action.mjs +64 -0
  196. package/dist/toast/toast-action.mjs.map +1 -0
  197. package/dist/toast/toast-close.d.mts +25 -0
  198. package/dist/toast/toast-close.d.mts.map +1 -0
  199. package/dist/toast/toast-close.mjs +46 -0
  200. package/dist/toast/toast-close.mjs.map +1 -0
  201. package/dist/toast/toast-context.mjs +19 -0
  202. package/dist/toast/toast-context.mjs.map +1 -0
  203. package/dist/toast/toast-description.d.mts +24 -0
  204. package/dist/toast/toast-description.d.mts.map +1 -0
  205. package/dist/toast/toast-description.mjs +35 -0
  206. package/dist/toast/toast-description.mjs.map +1 -0
  207. package/dist/toast/toast-root.d.mts +43 -0
  208. package/dist/toast/toast-root.d.mts.map +1 -0
  209. package/dist/toast/toast-root.mjs +204 -0
  210. package/dist/toast/toast-root.mjs.map +1 -0
  211. package/dist/toast/toast-title.d.mts +24 -0
  212. package/dist/toast/toast-title.d.mts.map +1 -0
  213. package/dist/toast/toast-title.mjs +33 -0
  214. package/dist/toast/toast-title.mjs.map +1 -0
  215. package/dist/toast/toast-viewport.d.mts +52 -0
  216. package/dist/toast/toast-viewport.d.mts.map +1 -0
  217. package/dist/toast/toast-viewport.mjs +621 -0
  218. package/dist/toast/toast-viewport.mjs.map +1 -0
  219. package/dist/toast/toast.d.mts +119 -0
  220. package/dist/toast/toast.d.mts.map +1 -0
  221. package/dist/toast/toast.mjs +153 -0
  222. package/dist/toast/toast.mjs.map +1 -0
  223. package/package.json +5 -1
@@ -1 +1 @@
1
- {"version":3,"file":"dialog-root.mjs","names":[],"sources":["../../src/dialog/dialog-root.tsx"],"sourcesContent":["\"use client\";\nimport { useCallback, useId, useRef, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createActionDetails } from \"../internal/action-details.js\";\nimport { transitionTypes } from \"../internal/transition-types.js\";\nimport type { ActionDetails, ChangeAction, ChangeReason, StateValue } from \"../internal/types.js\";\nimport { useControllableAction } from \"../internal/use-controllable-action.js\";\nimport { useDevWarning } from \"../internal/use-dev-warning.js\";\nimport { resolveStateValue } from \"../internal/use-render.js\";\nimport { DialogContext } from \"./dialog-context.js\";\nimport type { DialogCloseIntent, DialogContextValue, DialogMode } from \"./dialog-context.js\";\n\n/** Details passed as the second argument of `openChangeAction`. */\nexport interface DialogOpenChangeDetails extends ActionDetails {\n /**\n * `\"activate\"`: the user acted on a part: Trigger (open), Close or Form submit (close).\n * `\"dismiss\"`: the user closed the dialog without a part: Escape, Android back, an assistive-\n * technology dismiss gesture, or a click outside (`lightDismiss` / `closedby=\"any\"`).\n * `\"platform\"`: the dialog was found open on mount (opened before hydration); `event` is\n * `undefined`.\n */\n readonly reason: ChangeReason;\n /**\n * On close: `dialog.returnValue`: the `value` of the Close button or the form submitter that\n * closed it, `\"\"` for a dismiss. On open: `undefined`.\n */\n readonly returnValue: string | undefined;\n}\n\nexport interface DialogRootState {\n /** Optimistic open state (`false` in JS-free mode). Content rendered from it must not suspend. */\n readonly open: boolean;\n /** An `openChangeAction` dispatched through this Dialog is in flight (library mode only). */\n readonly pending: boolean;\n readonly modal: boolean;\n}\n\nexport interface DialogRootProps {\n /**\n * Controlled open state. `undefined` means uncontrolled (and, with `defaultOpen` and\n * `openChangeAction` also absent, JS-free mode).\n */\n open?: boolean;\n /**\n * Initial open state in uncontrolled library mode. The dialog opens after hydration, never in\n * the server HTML.\n */\n defaultOpen?: boolean;\n /**\n * Awaited for `true` (the dialog opens when it resolves and the content has rendered); a\n * notification for `false` (the dialog is already closed; the action runs inside the Root's\n * transition). May be a Server Function. Rejections reach the nearest Error Boundary above\n * Root.\n *\n * A controlled parent that does not adopt `false` gets the dialog re-opened once the action\n * settles: closing is the browser's, this is not a veto.\n *\n * A `setState` placed after your own `await` is not a Transition update; wrap it in\n * `startTransition` (React limitation).\n */\n openChangeAction?: ChangeAction<boolean, DialogOpenChangeDetails>;\n /** `showModal()` (top layer, backdrop, inert page, Escape) or `show()`. Default `true`. */\n modal?: boolean;\n children?: StateValue<ReactNode, DialogRootState>;\n}\n\n/** How long a recorded close intent survives when the dialog did not close (design 2.3). */\nfunction clearLater(\n intentRef: { current: DialogCloseIntent | undefined },\n intent: DialogCloseIntent,\n dialogRef: { current: HTMLDialogElement | null },\n): void {\n setTimeout(() => {\n // The dialog closed: its `close` event (queued as a task) consumes the intent. Still open:\n // the close was cancelled (a prevented `cancel` or `command`), so the intent is stale.\n if (intentRef.current === intent && dialogRef.current?.open === true) {\n intentRef.current = undefined;\n }\n }, 0);\n}\n\n/**\n * Owns the ids, the mode, the open state and the context. Renders no DOM, so it has no `ref`,\n * `className` or `style`; `pending` is exposed through `children(state)` and the Trigger.\n *\n * Without `open`, `defaultOpen` and `openChangeAction` the Dialog runs in **JS-free mode**: the\n * Trigger is a native `command=\"show-modal\"` invoker and the browser owns everything. With any of\n * them it runs in **library mode**: opening is a transition (the Trigger shows `data-pending`,\n * content renders from committed state, `showModal()` runs when it has committed); closing is\n * the browser's and `openChangeAction(false)` is a notification.\n */\nexport function DialogRoot({\n open,\n defaultOpen,\n openChangeAction,\n modal = true,\n children,\n}: DialogRootProps) {\n // Mode is decided once, from the first render's props (design 14).\n const isLibraryNow =\n open !== undefined || defaultOpen !== undefined || openChangeAction !== undefined;\n const [mode] = useState<DialogMode>(isLibraryNow ? \"library\" : \"static\");\n const isLibrary = mode === \"library\";\n useDevWarning(\n isLibrary !== isLibraryNow,\n \"Dialog.Root: the mode changed after mount (`open`, `defaultOpen` and `openChangeAction` \" +\n \"were all absent on one render and present on another). Switching between JS-free and \" +\n \"library mode is not supported.\",\n );\n\n // Hooks cannot be conditional: static mode calls the hook too and never dispatches.\n const { committed, optimistic, pending, dispatch, latestInFlight } = useControllableAction<\n boolean,\n DialogOpenChangeDetails\n >({\n controlled: isLibrary ? open : undefined,\n defaultValue: isLibrary ? defaultOpen : undefined,\n initialValue: false,\n action: isLibrary ? openChangeAction : undefined,\n transitionTypes: (next) => transitionTypes(\"dialog\", next ? \"open\" : \"close\"),\n warn: { component: \"Dialog.Root\", prop: \"open\" },\n });\n\n const id = useId();\n const triggerRef = useRef<HTMLElement | null>(null);\n const dialogRef = useRef<HTMLDialogElement | null>(null);\n const closeIntent = useRef<DialogCloseIntent | undefined>(undefined);\n\n const recordCloseIntent = useCallback((intent: DialogCloseIntent | undefined) => {\n closeIntent.current = intent;\n if (intent !== undefined) clearLater(closeIntent, intent, dialogRef);\n }, []);\n const takeCloseIntent = useCallback(() => {\n const intent = closeIntent.current;\n closeIntent.current = undefined;\n return intent;\n }, []);\n // Advanced on every open (the open dispatch and the Popup's native `beforetoggle` to open), so a Close action that\n // settles after the user dismissed and re-opened the dialog does not close the new open (the\n // PO's open-generation rule, design/components/popover.md 3.4; review M2).\n const generation = useRef(0);\n const openGeneration = useCallback(() => generation.current, []);\n const nextOpenGeneration = useCallback(() => {\n generation.current++;\n }, []);\n\n // What the user already intends: the latest in-flight dispatch, else the optimistic value\n // (conventions 3.8 rule 1). Judging no-ops against it also deduplicates the StrictMode re-run\n // of the mount reconciliation and recognises the library's own `close()` echoing back.\n const intended = (): boolean => latestInFlight()?.next ?? optimistic;\n\n const requestOpen = (event: Event | undefined, reason: ChangeReason): void => {\n if (!isLibrary || intended()) return;\n nextOpenGeneration();\n dispatch(true, createActionDetails(event, { reason, returnValue: undefined }));\n };\n\n const notifyClosed = (event: Event, reason: ChangeReason, returnValue: string): void => {\n if (!isLibrary || !intended()) return;\n dispatch(false, createActionDetails(event, { reason, returnValue }));\n };\n\n const openState = isLibrary && optimistic;\n const pendingState = isLibrary && pending;\n\n // Not memoised: `dispatch` is a new function on every render of the Root (Accordion does the\n // same), so parts re-render with the Root, which they do anyway when its state changes.\n const context: DialogContextValue = {\n mode,\n ids: { dialog: id, title: `${id}-title`, description: `${id}-description` },\n modal,\n committed: isLibrary && committed,\n optimistic: openState,\n pending: pendingState,\n requestOpen,\n notifyClosed,\n latestInFlight,\n triggerRef,\n dialogRef,\n recordCloseIntent,\n takeCloseIntent,\n openGeneration,\n nextOpenGeneration,\n };\n\n const state: DialogRootState = {\n open: openState,\n pending: pendingState,\n modal,\n };\n\n return <DialogContext value={context}>{resolveStateValue(children, state)}</DialogContext>;\n}\n\nDialogRoot.displayName = \"DialogRoot\";\n"],"mappings":";;;;;;;;;;;AAmEA,SAAS,WACP,WACA,QACA,WACM;CACN,iBAAiB;EAGf,IAAI,UAAU,YAAY,UAAU,UAAU,SAAS,SAAS,MAC9D,UAAU,UAAU,KAAA;CAExB,GAAG,CAAC;AACN;;;;;;;;;;;AAYA,SAAgB,WAAW,EACzB,MACA,aACA,kBACA,QAAQ,MACR,YACkB;CAElB,MAAM,eACJ,SAAS,KAAA,KAAa,gBAAgB,KAAA,KAAa,qBAAqB,KAAA;CAC1E,MAAM,CAAC,QAAQ,SAAqB,eAAe,YAAY,QAAQ;CACvE,MAAM,YAAY,SAAS;CAC3B,cACE,cAAc,cACd,6MAGF;CAGA,MAAM,EAAE,WAAW,YAAY,SAAS,UAAU,mBAAmB,sBAGnE;EACA,YAAY,YAAY,OAAO,KAAA;EAC/B,cAAc,YAAY,cAAc,KAAA;EACxC,cAAc;EACd,QAAQ,YAAY,mBAAmB,KAAA;EACvC,kBAAkB,SAAS,gBAAgB,UAAU,OAAO,SAAS,OAAO;EAC5E,MAAM;GAAE,WAAW;GAAe,MAAM;EAAO;CACjD,CAAC;CAED,MAAM,KAAK,MAAM;CACjB,MAAM,aAAa,OAA2B,IAAI;CAClD,MAAM,YAAY,OAAiC,IAAI;CACvD,MAAM,cAAc,OAAsC,KAAA,CAAS;CAEnE,MAAM,oBAAoB,aAAa,WAA0C;EAC/E,YAAY,UAAU;EACtB,IAAI,WAAW,KAAA,GAAW,WAAW,aAAa,QAAQ,SAAS;CACrE,GAAG,CAAC,CAAC;CACL,MAAM,kBAAkB,kBAAkB;EACxC,MAAM,SAAS,YAAY;EAC3B,YAAY,UAAU,KAAA;EACtB,OAAO;CACT,GAAG,CAAC,CAAC;CAIL,MAAM,aAAa,OAAO,CAAC;CAC3B,MAAM,iBAAiB,kBAAkB,WAAW,SAAS,CAAC,CAAC;CAC/D,MAAM,qBAAqB,kBAAkB;EAC3C,WAAW;CACb,GAAG,CAAC,CAAC;CAKL,MAAM,iBAA0B,eAAe,CAAC,EAAE,QAAQ;CAE1D,MAAM,eAAe,OAA0B,WAA+B;EAC5E,IAAI,CAAC,aAAa,SAAS,GAAG;EAC9B,mBAAmB;EACnB,SAAS,MAAM,oBAAoB,OAAO;GAAE;GAAQ,aAAa,KAAA;EAAU,CAAC,CAAC;CAC/E;CAEA,MAAM,gBAAgB,OAAc,QAAsB,gBAA8B;EACtF,IAAI,CAAC,aAAa,CAAC,SAAS,GAAG;EAC/B,SAAS,OAAO,oBAAoB,OAAO;GAAE;GAAQ;EAAY,CAAC,CAAC;CACrE;CAEA,MAAM,YAAY,aAAa;CAC/B,MAAM,eAAe,aAAa;CAIlC,MAAM,UAA8B;EAClC;EACA,KAAK;GAAE,QAAQ;GAAI,OAAO,GAAG,GAAG;GAAS,aAAa,GAAG,GAAG;EAAc;EAC1E;EACA,WAAW,aAAa;EACxB,YAAY;EACZ,SAAS;EACT;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;CAQA,OAAO,oBAAC,eAAD;EAAe,OAAO;EAAU,UAAA,kBAAkB,UAAU;GALjE,MAAM;GACN,SAAS;GACT;EAGqE,CAAC;CAAiB,CAAA;AAC3F;AAEA,WAAW,cAAc"}
1
+ {"version":3,"file":"dialog-root.mjs","names":[],"sources":["../../src/dialog/dialog-root.tsx"],"sourcesContent":["\"use client\";\nimport { useCallback, useId, useInsertionEffect, useMemo, useRef, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createActionDetails } from \"../internal/action-details.js\";\nimport { transitionTypes } from \"../internal/transition-types.js\";\nimport type { ActionDetails, ChangeAction, ChangeReason, StateValue } from \"../internal/types.js\";\nimport { useControllableAction } from \"../internal/use-controllable-action.js\";\nimport { useDevWarning } from \"../internal/use-dev-warning.js\";\nimport { useLatest } from \"../internal/use-latest.js\";\nimport { resolveStateValue } from \"../internal/use-render.js\";\nimport { DialogContext } from \"./dialog-context.js\";\nimport type { DialogCloseIntent, DialogContextValue, DialogMode } from \"./dialog-context.js\";\n\n/** Details passed as the second argument of `openChangeAction`. */\nexport interface DialogOpenChangeDetails extends ActionDetails {\n /**\n * `\"activate\"`: the user acted on a part: Trigger (open), Close or Form submit (close).\n * `\"dismiss\"`: the user closed the dialog without a part: Escape, Android back, an assistive-\n * technology dismiss gesture, or a click outside (`lightDismiss` / `closedby=\"any\"`).\n * `\"platform\"`: the dialog was found open on mount (opened before hydration); `event` is\n * `undefined`.\n */\n readonly reason: ChangeReason;\n /**\n * On close: `dialog.returnValue`: the `value` of the Close button or the form submitter that\n * closed it, `\"\"` for a dismiss. On open: `undefined`.\n */\n readonly returnValue: string | undefined;\n}\n\nexport interface DialogRootState {\n /** Optimistic open state (`false` in JS-free mode). Content rendered from it must not suspend. */\n readonly open: boolean;\n /** An `openChangeAction` dispatched through this Dialog is in flight (library mode only). */\n readonly pending: boolean;\n readonly modal: boolean;\n}\n\nexport interface DialogRootProps {\n /**\n * Controlled open state. `undefined` means uncontrolled (and, with `defaultOpen` and\n * `openChangeAction` also absent, JS-free mode).\n */\n open?: boolean;\n /**\n * Initial open state in uncontrolled library mode. The dialog opens after hydration, never in\n * the server HTML.\n */\n defaultOpen?: boolean;\n /**\n * Awaited for `true` (the dialog opens when it resolves and the content has rendered) and for\n * a `false` the user asked a part for (`Dialog.Close`, a `Dialog.Form` submit or settled\n * action, a foreign close command: the dialog closes with `details.returnValue` when it\n * resolves, with a view transition if the Popup's `transition` is set; a controlled parent\n * that keeps `open` vetoes it). A notification for a dismissal (Escape, Android back, a click\n * outside: the dialog is already closed, `returnValue` is `\"\"`; the action runs inside the\n * Root's transition, and a controlled parent that keeps `open` gets it re-opened, which is\n * not a veto). A dismissal while a part's close is in flight is not notified on its own: if that\n * close is vetoed, the dismissal is notified (`\"dismiss\"`) when the veto settles, and the dialog\n * stays closed. May be a Server Function. Rejections reach the nearest Error Boundary above\n * Root.\n *\n * A `setState` placed after your own `await` is not a Transition update; wrap it in\n * `startTransition` (React limitation).\n */\n openChangeAction?: ChangeAction<boolean, DialogOpenChangeDetails>;\n /** `showModal()` (top layer, backdrop, inert page, Escape) or `show()`. Default `true`. */\n modal?: boolean;\n children?: StateValue<ReactNode, DialogRootState>;\n}\n\n/** How long a recorded close intent survives when the dialog did not close (design 2.3). */\nfunction clearLater(\n intentRef: { current: DialogCloseIntent | undefined },\n intent: DialogCloseIntent,\n dialogRef: { current: HTMLDialogElement | null },\n): void {\n setTimeout(() => {\n // The dialog closed: its `close` event (queued as a task) consumes the intent. Still open:\n // the close was cancelled (a prevented `cancel` or `command`), so the intent is stale.\n if (intentRef.current === intent && dialogRef.current?.open === true) {\n intentRef.current = undefined;\n }\n }, 0);\n}\n\n/**\n * Owns the ids, the mode, the open state and the context. Renders no DOM, so it has no `ref`,\n * `className` or `style`; `pending` is exposed through `children(state)` and the Trigger.\n *\n * Without `open`, `defaultOpen` and `openChangeAction` the Dialog runs in **JS-free mode**: the\n * Trigger is a native `command=\"show-modal\"` invoker and the browser owns everything. With any of\n * them it runs in **library mode**: opening is a transition (the Trigger shows `data-pending`,\n * content renders from committed state, `showModal()` runs when it has committed), and so is a\n * close a part asks for (Close, Form, a foreign close command: `openChangeAction(false)` is\n * awaited, `close(returnValue)` runs when it has committed). Dismissals (Escape, a click\n * outside) are the browser's and `openChangeAction(false)` is a notification.\n */\nexport function DialogRoot({\n open,\n defaultOpen,\n openChangeAction,\n modal = true,\n children,\n}: DialogRootProps) {\n // Mode is decided once, from the first render's props (design 14).\n const isLibraryNow =\n open !== undefined || defaultOpen !== undefined || openChangeAction !== undefined;\n const [mode] = useState<DialogMode>(isLibraryNow ? \"library\" : \"static\");\n const isLibrary = mode === \"library\";\n useDevWarning(\n isLibrary !== isLibraryNow,\n \"Dialog.Root: the mode changed after mount (`open`, `defaultOpen` and `openChangeAction` \" +\n \"were all absent on one render and present on another). Switching between JS-free and \" +\n \"library mode is not supported.\",\n );\n\n // Hooks cannot be conditional: static mode calls the hook too and never dispatches.\n const { committed, optimistic, pending, dispatch, latestInFlight } = useControllableAction<\n boolean,\n DialogOpenChangeDetails\n >({\n controlled: isLibrary ? open : undefined,\n defaultValue: isLibrary ? defaultOpen : undefined,\n initialValue: false,\n action: isLibrary ? openChangeAction : undefined,\n transitionTypes: (next) => transitionTypes(\"dialog\", next ? \"open\" : \"close\"),\n warn: { component: \"Dialog.Root\", prop: \"open\" },\n });\n\n const id = useId();\n const triggerRef = useRef<HTMLElement | null>(null);\n const dialogRef = useRef<HTMLDialogElement | null>(null);\n const closeIntent = useRef<DialogCloseIntent | undefined>(undefined);\n\n const recordCloseIntent = useCallback((intent: DialogCloseIntent | undefined) => {\n closeIntent.current = intent;\n if (intent !== undefined) clearLater(closeIntent, intent, dialogRef);\n }, []);\n const takeCloseIntent = useCallback(() => {\n const intent = closeIntent.current;\n closeIntent.current = undefined;\n return intent;\n }, []);\n // Advanced on every open (the open dispatch and the Popup's native `beforetoggle` to open), so a Close action that\n // settles after the user dismissed and re-opened the dialog does not close the new open (the\n // PO's open-generation rule, design/components/popover.md 3.4; review M2).\n const generation = useRef(0);\n const openGeneration = useCallback(() => generation.current, []);\n const nextOpenGeneration = useCallback(() => {\n generation.current++;\n }, []);\n\n // The optimistic value of the last commit, for callers that run after an `await` (a Close\n // action's continuation) and would otherwise read the value of the render they were created in\n // (Popover 14.1). Through `useLatest`, as `usePopoverRoot`, so `intended` and everything built\n // on it keep their identity (review of #48, finding 5).\n const committedOptimistic = useLatest(optimistic);\n\n // What the user already intends: the latest in-flight dispatch, else the optimistic value\n // (conventions 3.8 rule 1). Judging no-ops against it also deduplicates the StrictMode re-run\n // of the mount reconciliation and recognises the library's own `close()` echoing back.\n const intended = useCallback(\n (): boolean => latestInFlight()?.next ?? committedOptimistic.current,\n [latestInFlight, committedOptimistic],\n );\n\n // The `returnValue` of a pending part-initiated close (design 2.3), handed to the apply\n // effect's `close()`. Stale once `true` is intended again (a vetoed close snapped back, or a\n // re-open), so the commit that shows it clears it.\n const closeReturnValue = useRef<string | undefined>(undefined);\n useInsertionEffect(() => {\n if (latestInFlight()?.next ?? optimistic) closeReturnValue.current = undefined;\n });\n const takeCloseReturnValue = useCallback((): string => {\n const value = closeReturnValue.current;\n closeReturnValue.current = undefined;\n return value ?? \"\";\n }, []);\n\n const requestOpen = useCallback(\n (event: Event | undefined, reason: ChangeReason): void => {\n if (!isLibrary || intended()) return;\n nextOpenGeneration();\n dispatch(true, createActionDetails(event, { reason, returnValue: undefined }));\n },\n [isLibrary, intended, nextOpenGeneration, dispatch],\n );\n\n // A library-owned close (design 2.3): `openChangeAction(false)` is awaited and the Popup's\n // apply effect calls `close(returnValue)` in the commit that ends it.\n const requestClose = useCallback(\n (event: Event | undefined, reason: ChangeReason, returnValue: string): void => {\n if (!isLibrary || !intended()) return;\n closeReturnValue.current = returnValue;\n dispatch(false, createActionDetails(event, { reason, returnValue }));\n },\n [isLibrary, intended, dispatch],\n );\n\n const notifyClosed = useCallback(\n (event: Event, reason: ChangeReason, returnValue: string): void => {\n if (!isLibrary || !intended()) return;\n dispatch(false, createActionDetails(event, { reason, returnValue }));\n },\n [isLibrary, intended, dispatch],\n );\n\n const openState = isLibrary && optimistic;\n const pendingState = isLibrary && pending;\n const committedState = isLibrary && committed;\n\n // Memoised (#48): `dispatch` is stable, so the value changes only with the open state, pending\n // and `modal`, and the parts do not re-render for an unrelated render of the Root.\n const context = useMemo<DialogContextValue>(\n () => ({\n mode,\n ids: { dialog: id, title: `${id}-title`, description: `${id}-description` },\n modal,\n committed: committedState,\n optimistic: openState,\n pending: pendingState,\n intended,\n requestClose,\n takeCloseReturnValue,\n requestOpen,\n notifyClosed,\n latestInFlight,\n triggerRef,\n dialogRef,\n recordCloseIntent,\n takeCloseIntent,\n openGeneration,\n nextOpenGeneration,\n }),\n [\n mode,\n id,\n modal,\n committedState,\n openState,\n pendingState,\n intended,\n requestClose,\n takeCloseReturnValue,\n requestOpen,\n notifyClosed,\n latestInFlight,\n recordCloseIntent,\n takeCloseIntent,\n openGeneration,\n nextOpenGeneration,\n ],\n );\n\n const state: DialogRootState = {\n open: openState,\n pending: pendingState,\n modal,\n };\n\n return <DialogContext value={context}>{resolveStateValue(children, state)}</DialogContext>;\n}\n\nDialogRoot.displayName = \"DialogRoot\";\n"],"mappings":";;;;;;;;;;;;AAwEA,SAAS,WACP,WACA,QACA,WACM;CACN,iBAAiB;EAGf,IAAI,UAAU,YAAY,UAAU,UAAU,SAAS,SAAS,MAC9D,UAAU,UAAU,KAAA;CAExB,GAAG,CAAC;AACN;;;;;;;;;;;;;AAcA,SAAgB,WAAW,EACzB,MACA,aACA,kBACA,QAAQ,MACR,YACkB;CAElB,MAAM,eACJ,SAAS,KAAA,KAAa,gBAAgB,KAAA,KAAa,qBAAqB,KAAA;CAC1E,MAAM,CAAC,QAAQ,SAAqB,eAAe,YAAY,QAAQ;CACvE,MAAM,YAAY,SAAS;CAC3B,cACE,cAAc,cACd,6MAGF;CAGA,MAAM,EAAE,WAAW,YAAY,SAAS,UAAU,mBAAmB,sBAGnE;EACA,YAAY,YAAY,OAAO,KAAA;EAC/B,cAAc,YAAY,cAAc,KAAA;EACxC,cAAc;EACd,QAAQ,YAAY,mBAAmB,KAAA;EACvC,kBAAkB,SAAS,gBAAgB,UAAU,OAAO,SAAS,OAAO;EAC5E,MAAM;GAAE,WAAW;GAAe,MAAM;EAAO;CACjD,CAAC;CAED,MAAM,KAAK,MAAM;CACjB,MAAM,aAAa,OAA2B,IAAI;CAClD,MAAM,YAAY,OAAiC,IAAI;CACvD,MAAM,cAAc,OAAsC,KAAA,CAAS;CAEnE,MAAM,oBAAoB,aAAa,WAA0C;EAC/E,YAAY,UAAU;EACtB,IAAI,WAAW,KAAA,GAAW,WAAW,aAAa,QAAQ,SAAS;CACrE,GAAG,CAAC,CAAC;CACL,MAAM,kBAAkB,kBAAkB;EACxC,MAAM,SAAS,YAAY;EAC3B,YAAY,UAAU,KAAA;EACtB,OAAO;CACT,GAAG,CAAC,CAAC;CAIL,MAAM,aAAa,OAAO,CAAC;CAC3B,MAAM,iBAAiB,kBAAkB,WAAW,SAAS,CAAC,CAAC;CAC/D,MAAM,qBAAqB,kBAAkB;EAC3C,WAAW;CACb,GAAG,CAAC,CAAC;CAML,MAAM,sBAAsB,UAAU,UAAU;CAKhD,MAAM,WAAW,kBACA,eAAe,CAAC,EAAE,QAAQ,oBAAoB,SAC7D,CAAC,gBAAgB,mBAAmB,CACtC;CAKA,MAAM,mBAAmB,OAA2B,KAAA,CAAS;CAC7D,yBAAyB;EACvB,IAAI,eAAe,CAAC,EAAE,QAAQ,YAAY,iBAAiB,UAAU,KAAA;CACvE,CAAC;CACD,MAAM,uBAAuB,kBAA0B;EACrD,MAAM,QAAQ,iBAAiB;EAC/B,iBAAiB,UAAU,KAAA;EAC3B,OAAO,SAAS;CAClB,GAAG,CAAC,CAAC;CAEL,MAAM,cAAc,aACjB,OAA0B,WAA+B;EACxD,IAAI,CAAC,aAAa,SAAS,GAAG;EAC9B,mBAAmB;EACnB,SAAS,MAAM,oBAAoB,OAAO;GAAE;GAAQ,aAAa,KAAA;EAAU,CAAC,CAAC;CAC/E,GACA;EAAC;EAAW;EAAU;EAAoB;CAAQ,CACpD;CAIA,MAAM,eAAe,aAClB,OAA0B,QAAsB,gBAA8B;EAC7E,IAAI,CAAC,aAAa,CAAC,SAAS,GAAG;EAC/B,iBAAiB,UAAU;EAC3B,SAAS,OAAO,oBAAoB,OAAO;GAAE;GAAQ;EAAY,CAAC,CAAC;CACrE,GACA;EAAC;EAAW;EAAU;CAAQ,CAChC;CAEA,MAAM,eAAe,aAClB,OAAc,QAAsB,gBAA8B;EACjE,IAAI,CAAC,aAAa,CAAC,SAAS,GAAG;EAC/B,SAAS,OAAO,oBAAoB,OAAO;GAAE;GAAQ;EAAY,CAAC,CAAC;CACrE,GACA;EAAC;EAAW;EAAU;CAAQ,CAChC;CAEA,MAAM,YAAY,aAAa;CAC/B,MAAM,eAAe,aAAa;CAClC,MAAM,iBAAiB,aAAa;CAIpC,MAAM,UAAU,eACP;EACL;EACA,KAAK;GAAE,QAAQ;GAAI,OAAO,GAAG,GAAG;GAAS,aAAa,GAAG,GAAG;EAAc;EAC1E;EACA,WAAW;EACX,YAAY;EACZ,SAAS;EACT;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,IACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CACF;CAQA,OAAO,oBAAC,eAAD;EAAe,OAAO;EAAU,UAAA,kBAAkB,UAAU;GALjE,MAAM;GACN,SAAS;GACT;EAGqE,CAAC;CAAiB,CAAA;AAC3F;AAEA,WAAW,cAAc"}
@@ -5,8 +5,9 @@ export interface DialogTriggerState {
5
5
  /** Optimistic open state (library mode); `false` in JS-free mode. */
6
6
  readonly open: boolean;
7
7
  /**
8
- * The Root's transition is pending: an open in flight, a close notification in flight, or a
9
- * re-open requested while a close notification is still settling.
8
+ * The Root's transition is pending: an open in flight, a close a part asked for in flight
9
+ * (`openChangeAction(false)` awaited while the dialog is still open), a close notification in
10
+ * flight, or a re-open requested while a close notification is still settling.
10
11
  */
11
12
  readonly pending: boolean;
12
13
  readonly disabled: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"dialog-trigger.d.mts","names":[],"sources":["../../src/dialog/dialog-trigger.tsx"],"mappings":";;;iBASiB;;WAEN;;;;;WAKA;WACA;;iBAGM,2BAA2B,KAC1C;;EAIA;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;;EAE9C,WAAW,WAAW,WAAW;;EAEjC,SAAS,WAAW,iCAAiC;;;;;;;;;;;;wBAevC,gBACd,UACA,WACA,OACA,UACA,WACG,QACF,qBAAkB;yBAPL"}
1
+ {"version":3,"file":"dialog-trigger.d.mts","names":[],"sources":["../../src/dialog/dialog-trigger.tsx"],"mappings":";;;iBASiB;;WAEN;;;;;;WAMA;WACA;;iBAGM,2BAA2B,KAC1C;;EAIA;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;;EAE9C,WAAW,WAAW,WAAW;;EAEjC,SAAS,WAAW,iCAAiC;;;;;;;;;;;;wBAevC,gBACd,UACA,WACA,OACA,UACA,WACG,QACF,qBAAkB;yBAPL"}
@@ -1 +1 @@
1
- {"version":3,"file":"dialog-trigger.mjs","names":[],"sources":["../../src/dialog/dialog-trigger.tsx"],"sourcesContent":["\"use client\";\nimport { useCallback, useEffect } from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, MouseEvent, ReactNode } from \"react\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { rendersButton, useRender } from \"../internal/use-render.js\";\nimport type { DataAttributes } from \"../internal/use-render.js\";\nimport { useDialogContext } from \"./dialog-context.js\";\n\nexport interface DialogTriggerState {\n /** Optimistic open state (library mode); `false` in JS-free mode. */\n readonly open: boolean;\n /**\n * The Root's transition is pending: an open in flight, a close notification in flight, or a\n * re-open requested while a close notification is still settling.\n */\n readonly pending: boolean;\n readonly disabled: boolean;\n}\n\nexport interface DialogTriggerProps extends Omit<\n ComponentPropsWithRef<\"button\">,\n \"onClick\" | \"className\" | \"style\" | \"children\"\n> {\n /** Native `disabled`: a disabled button is not a valid invoker and never dispatches. */\n disabled?: boolean;\n className?: StateValue<string | undefined, DialogTriggerState>;\n style?: StateValue<CSSProperties | undefined, DialogTriggerState>;\n /** Rendered from optimistic state; must not suspend. */\n children?: StateValue<ReactNode, DialogTriggerState>;\n /** Should render a `<button>`; another element works only in library mode (click handler). */\n render?: RenderProp<ComponentPropsWithRef<\"button\">, DialogTriggerState>;\n}\n\nconst isDev = process.env.NODE_ENV !== \"production\";\n\n/**\n * The opener: `<button type=\"button\" commandfor command=\"show-modal\">`. In JS-free mode the\n * browser runs the command. In library mode the click is cancelled (so the command does not run)\n * and dispatched through the Root's transition; the button shows optimistic `data-open` and\n * `data-pending` until the dialog has opened. Non-modal dialogs have no command and always use a\n * click handler (`show()`).\n *\n * Owned keys (conventions 1.2): `type` (on a `<button>` only), `disabled`, `commandfor`,\n * `command`, `onClick`, `data-open`, `data-pending`, `data-disabled`.\n */\nexport function DialogTrigger({\n disabled = false,\n className,\n style,\n children,\n render,\n ...rest\n}: DialogTriggerProps) {\n const ctx = useDialogContext(\"Dialog.Trigger\");\n const isLibrary = ctx.mode === \"library\";\n const { modal, ids, triggerRef, dialogRef } = ctx;\n\n const state: DialogTriggerState = {\n open: ctx.optimistic,\n // Every Root dispatch is an open or a close notification and both belong to the Trigger\n // (design 4.2). Not `optimistic !== committed`: a re-open while a close notification is\n // still settling has both `true` (the close never committed) and is pending all the same.\n pending: ctx.pending,\n disabled,\n };\n\n const libraryRef = useCallback(\n (element: HTMLElement) => {\n if (isDev && !isLibrary && modal && element.tagName !== \"BUTTON\") {\n console.error(\n `Dialog.Trigger: rendered a <${element.tagName.toLowerCase()}> in JS-free mode. Only a ` +\n '<button> runs `command=\"show-modal\"`; this trigger will not open the dialog. Render ' +\n \"a <button>, or put the Dialog in library mode (`defaultOpen={false}`).\",\n );\n }\n triggerRef.current = element;\n return () => {\n if (triggerRef.current === element) triggerRef.current = null;\n };\n },\n [isLibrary, modal, triggerRef],\n );\n\n // A Trigger without a Popup opens nothing (design 14, dev warnings).\n useEffect(() => {\n if (!isDev) return;\n if (document.getElementById(ids.dialog) === null) {\n console.error(\n \"Dialog.Trigger: no Dialog.Popup is rendered for this Dialog.Root; the trigger opens nothing.\",\n );\n }\n }, [ids.dialog]);\n\n // Library mode (both modal and non-modal) and non-modal JS-free mode need a click handler;\n // modal JS-free mode is the browser's command.\n const installClick = isLibrary || !modal;\n const onClick = (event: MouseEvent<HTMLElement>) => {\n if (isLibrary) {\n // Cancelling the click cancels the command (it runs as the button's activation\n // behaviour, only if the click was not cancelled).\n event.preventDefault();\n if (disabled) return;\n ctx.requestOpen(event.nativeEvent, \"activate\");\n return;\n }\n if (disabled) return;\n const dialog = dialogRef.current;\n if (dialog !== null && !dialog.open && dialog.isConnected) dialog.show();\n };\n\n const libraryProps: ComponentPropsWithRef<\"button\"> & DataAttributes = {\n // `type` only applies to a <button>; on any other element the key is not owned.\n ...(rendersButton(render) && { type: \"button\" as const }),\n // Owned: a render element must not re-enable a disabled Trigger (in JS-free mode the\n // browser would run its command).\n disabled,\n commandfor: modal ? ids.dialog : undefined,\n command: modal ? \"show-modal\" : undefined,\n // Only owned when installed, so a render element keeps its own `onClick` in modal JS-free\n // mode (Accordion's rule).\n ...(installClick && { onClick }),\n \"data-open\": dataAttr(state.open),\n \"data-pending\": dataAttr(state.pending),\n \"data-disabled\": dataAttr(isLibrary && disabled),\n };\n\n return useRender<ComponentPropsWithRef<\"button\">, DialogTriggerState>({\n defaultElement: \"button\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps,\n ref: libraryRef,\n });\n}\n\nDialogTrigger.displayName = \"DialogTrigger\";\n"],"mappings":";;;;;;AAkCA,MAAM,QAAQ,QAAQ,IAAI,aAAa;;;;;;;;;;;AAYvC,SAAgB,cAAc,EAC5B,WAAW,OACX,WACA,OACA,UACA,QACA,GAAG,QACkB;CACrB,MAAM,MAAM,iBAAiB,gBAAgB;CAC7C,MAAM,YAAY,IAAI,SAAS;CAC/B,MAAM,EAAE,OAAO,KAAK,YAAY,cAAc;CAE9C,MAAM,QAA4B;EAChC,MAAM,IAAI;EAIV,SAAS,IAAI;EACb;CACF;CAEA,MAAM,aAAa,aAChB,YAAyB;EACxB,IAAI,SAAS,CAAC,aAAa,SAAS,QAAQ,YAAY,UACtD,QAAQ,MACN,+BAA+B,QAAQ,QAAQ,YAAY,EAAE,yLAG/D;EAEF,WAAW,UAAU;EACrB,aAAa;GACX,IAAI,WAAW,YAAY,SAAS,WAAW,UAAU;EAC3D;CACF,GACA;EAAC;EAAW;EAAO;CAAU,CAC/B;CAGA,gBAAgB;EACd,IAAI,CAAC,OAAO;EACZ,IAAI,SAAS,eAAe,IAAI,MAAM,MAAM,MAC1C,QAAQ,MACN,8FACF;CAEJ,GAAG,CAAC,IAAI,MAAM,CAAC;CAIf,MAAM,eAAe,aAAa,CAAC;CACnC,MAAM,WAAW,UAAmC;EAClD,IAAI,WAAW;GAGb,MAAM,eAAe;GACrB,IAAI,UAAU;GACd,IAAI,YAAY,MAAM,aAAa,UAAU;GAC7C;EACF;EACA,IAAI,UAAU;EACd,MAAM,SAAS,UAAU;EACzB,IAAI,WAAW,QAAQ,CAAC,OAAO,QAAQ,OAAO,aAAa,OAAO,KAAK;CACzE;CAEA,MAAM,eAAiE;EAErE,GAAI,cAAc,MAAM,KAAK,EAAE,MAAM,SAAkB;EAGvD;EACA,YAAY,QAAQ,IAAI,SAAS,KAAA;EACjC,SAAS,QAAQ,eAAe,KAAA;EAGhC,GAAI,gBAAgB,EAAE,QAAQ;EAC9B,aAAa,SAAS,MAAM,IAAI;EAChC,gBAAgB,SAAS,MAAM,OAAO;EACtC,iBAAiB,SAAS,aAAa,QAAQ;CACjD;CAEA,OAAO,UAA+D;EACpE,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA,OAAO;EACP;EACA,KAAK;CACP,CAAC;AACH;AAEA,cAAc,cAAc"}
1
+ {"version":3,"file":"dialog-trigger.mjs","names":[],"sources":["../../src/dialog/dialog-trigger.tsx"],"sourcesContent":["\"use client\";\nimport { useCallback, useEffect } from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, MouseEvent, ReactNode } from \"react\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { rendersButton, useRender } from \"../internal/use-render.js\";\nimport type { DataAttributes } from \"../internal/use-render.js\";\nimport { useDialogContext } from \"./dialog-context.js\";\n\nexport interface DialogTriggerState {\n /** Optimistic open state (library mode); `false` in JS-free mode. */\n readonly open: boolean;\n /**\n * The Root's transition is pending: an open in flight, a close a part asked for in flight\n * (`openChangeAction(false)` awaited while the dialog is still open), a close notification in\n * flight, or a re-open requested while a close notification is still settling.\n */\n readonly pending: boolean;\n readonly disabled: boolean;\n}\n\nexport interface DialogTriggerProps extends Omit<\n ComponentPropsWithRef<\"button\">,\n \"onClick\" | \"className\" | \"style\" | \"children\"\n> {\n /** Native `disabled`: a disabled button is not a valid invoker and never dispatches. */\n disabled?: boolean;\n className?: StateValue<string | undefined, DialogTriggerState>;\n style?: StateValue<CSSProperties | undefined, DialogTriggerState>;\n /** Rendered from optimistic state; must not suspend. */\n children?: StateValue<ReactNode, DialogTriggerState>;\n /** Should render a `<button>`; another element works only in library mode (click handler). */\n render?: RenderProp<ComponentPropsWithRef<\"button\">, DialogTriggerState>;\n}\n\nconst isDev = process.env.NODE_ENV !== \"production\";\n\n/**\n * The opener: `<button type=\"button\" commandfor command=\"show-modal\">`. In JS-free mode the\n * browser runs the command. In library mode the click is cancelled (so the command does not run)\n * and dispatched through the Root's transition; the button shows optimistic `data-open` and\n * `data-pending` until the dialog has opened. Non-modal dialogs have no command and always use a\n * click handler (`show()`).\n *\n * Owned keys (conventions 1.2): `type` (on a `<button>` only), `disabled`, `commandfor`,\n * `command`, `onClick`, `data-open`, `data-pending`, `data-disabled`.\n */\nexport function DialogTrigger({\n disabled = false,\n className,\n style,\n children,\n render,\n ...rest\n}: DialogTriggerProps) {\n const ctx = useDialogContext(\"Dialog.Trigger\");\n const isLibrary = ctx.mode === \"library\";\n const { modal, ids, triggerRef, dialogRef } = ctx;\n\n const state: DialogTriggerState = {\n open: ctx.optimistic,\n // Every Root dispatch is an open, a part-initiated close or a dismissal notification, and\n // all of them belong to the Trigger (design 4.2). Not `optimistic !== committed`: a re-open\n // while a close notification is still settling has both `true` (the close never committed)\n // and is pending all the same.\n pending: ctx.pending,\n disabled,\n };\n\n const libraryRef = useCallback(\n (element: HTMLElement) => {\n if (isDev && !isLibrary && modal && element.tagName !== \"BUTTON\") {\n console.error(\n `Dialog.Trigger: rendered a <${element.tagName.toLowerCase()}> in JS-free mode. Only a ` +\n '<button> runs `command=\"show-modal\"`; this trigger will not open the dialog. Render ' +\n \"a <button>, or put the Dialog in library mode (`defaultOpen={false}`).\",\n );\n }\n triggerRef.current = element;\n return () => {\n if (triggerRef.current === element) triggerRef.current = null;\n };\n },\n [isLibrary, modal, triggerRef],\n );\n\n // A Trigger without a Popup opens nothing (design 14, dev warnings).\n useEffect(() => {\n if (!isDev) return;\n if (document.getElementById(ids.dialog) === null) {\n console.error(\n \"Dialog.Trigger: no Dialog.Popup is rendered for this Dialog.Root; the trigger opens nothing.\",\n );\n }\n }, [ids.dialog]);\n\n // Library mode (both modal and non-modal) and non-modal JS-free mode need a click handler;\n // modal JS-free mode is the browser's command.\n const installClick = isLibrary || !modal;\n const onClick = (event: MouseEvent<HTMLElement>) => {\n if (isLibrary) {\n // Cancelling the click cancels the command (it runs as the button's activation\n // behaviour, only if the click was not cancelled).\n event.preventDefault();\n if (disabled) return;\n ctx.requestOpen(event.nativeEvent, \"activate\");\n return;\n }\n if (disabled) return;\n const dialog = dialogRef.current;\n if (dialog !== null && !dialog.open && dialog.isConnected) dialog.show();\n };\n\n const libraryProps: ComponentPropsWithRef<\"button\"> & DataAttributes = {\n // `type` only applies to a <button>; on any other element the key is not owned.\n ...(rendersButton(render) && { type: \"button\" as const }),\n // Owned: a render element must not re-enable a disabled Trigger (in JS-free mode the\n // browser would run its command).\n disabled,\n commandfor: modal ? ids.dialog : undefined,\n command: modal ? \"show-modal\" : undefined,\n // Only owned when installed, so a render element keeps its own `onClick` in modal JS-free\n // mode (Accordion's rule).\n ...(installClick && { onClick }),\n \"data-open\": dataAttr(state.open),\n \"data-pending\": dataAttr(state.pending),\n \"data-disabled\": dataAttr(isLibrary && disabled),\n };\n\n return useRender<ComponentPropsWithRef<\"button\">, DialogTriggerState>({\n defaultElement: \"button\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps,\n ref: libraryRef,\n });\n}\n\nDialogTrigger.displayName = \"DialogTrigger\";\n"],"mappings":";;;;;;AAmCA,MAAM,QAAQ,QAAQ,IAAI,aAAa;;;;;;;;;;;AAYvC,SAAgB,cAAc,EAC5B,WAAW,OACX,WACA,OACA,UACA,QACA,GAAG,QACkB;CACrB,MAAM,MAAM,iBAAiB,gBAAgB;CAC7C,MAAM,YAAY,IAAI,SAAS;CAC/B,MAAM,EAAE,OAAO,KAAK,YAAY,cAAc;CAE9C,MAAM,QAA4B;EAChC,MAAM,IAAI;EAKV,SAAS,IAAI;EACb;CACF;CAEA,MAAM,aAAa,aAChB,YAAyB;EACxB,IAAI,SAAS,CAAC,aAAa,SAAS,QAAQ,YAAY,UACtD,QAAQ,MACN,+BAA+B,QAAQ,QAAQ,YAAY,EAAE,yLAG/D;EAEF,WAAW,UAAU;EACrB,aAAa;GACX,IAAI,WAAW,YAAY,SAAS,WAAW,UAAU;EAC3D;CACF,GACA;EAAC;EAAW;EAAO;CAAU,CAC/B;CAGA,gBAAgB;EACd,IAAI,CAAC,OAAO;EACZ,IAAI,SAAS,eAAe,IAAI,MAAM,MAAM,MAC1C,QAAQ,MACN,8FACF;CAEJ,GAAG,CAAC,IAAI,MAAM,CAAC;CAIf,MAAM,eAAe,aAAa,CAAC;CACnC,MAAM,WAAW,UAAmC;EAClD,IAAI,WAAW;GAGb,MAAM,eAAe;GACrB,IAAI,UAAU;GACd,IAAI,YAAY,MAAM,aAAa,UAAU;GAC7C;EACF;EACA,IAAI,UAAU;EACd,MAAM,SAAS,UAAU;EACzB,IAAI,WAAW,QAAQ,CAAC,OAAO,QAAQ,OAAO,aAAa,OAAO,KAAK;CACzE;CAEA,MAAM,eAAiE;EAErE,GAAI,cAAc,MAAM,KAAK,EAAE,MAAM,SAAkB;EAGvD;EACA,YAAY,QAAQ,IAAI,SAAS,KAAA;EACjC,SAAS,QAAQ,eAAe,KAAA;EAGhC,GAAI,gBAAgB,EAAE,QAAQ;EAC9B,aAAa,SAAS,MAAM,IAAI;EAChC,gBAAgB,SAAS,MAAM,OAAO;EACtC,iBAAiB,SAAS,aAAa,QAAQ;CACjD;CAEA,OAAO,UAA+D;EACpE,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA,OAAO;EACP;EACA,KAAK;CACP,CAAC;AACH;AAEA,cAAc,cAAc"}
@@ -11,8 +11,10 @@ import { DialogTrigger, DialogTriggerProps, DialogTriggerState } from "./dialog-
11
11
  * Dialog built on `<dialog>` and Invoker Commands: `Root` (no DOM) > `Trigger`
12
12
  * (`button[command=show-modal]`) + `Popup` (`dialog`) > `Title`, `Description`, `Form`, `Close`.
13
13
  * JS-free when the Root has none of `open` / `defaultOpen` / `openChangeAction`; otherwise
14
- * opening is a transition (pending Trigger, content from committed state) and closing is the
15
- * browser's, notified through `openChangeAction(false)`.
14
+ * opening is a transition (pending Trigger, content from committed state), and so is a close a
15
+ * part asks for (Close, Form, a foreign close command: `openChangeAction(false)` awaited, then
16
+ * `close(returnValue)`); dismissals (Escape, a click outside) are the browser's, notified
17
+ * through `openChangeAction(false)`.
16
18
  */
17
19
  export declare const Dialog: {
18
20
  readonly Root: typeof DialogRoot;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/dialog/index.ts"],"mappings":";;;;;;;;;;;;;;;;qBAkBa;WACX,aAAI;WACJ,gBAAO;WACP,cAAK;WACL,cAAK;WACL,aAAI;WACJ,cAAK;WACL,oBAAW"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/dialog/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;qBAoBa;WACX,aAAI;WACJ,gBAAO;WACP,cAAK;WACL,cAAK;WACL,aAAI;WACJ,cAAK;WACL,oBAAW"}
@@ -10,8 +10,10 @@ import { DialogTrigger } from "./dialog-trigger.mjs";
10
10
  * Dialog built on `<dialog>` and Invoker Commands: `Root` (no DOM) > `Trigger`
11
11
  * (`button[command=show-modal]`) + `Popup` (`dialog`) > `Title`, `Description`, `Form`, `Close`.
12
12
  * JS-free when the Root has none of `open` / `defaultOpen` / `openChangeAction`; otherwise
13
- * opening is a transition (pending Trigger, content from committed state) and closing is the
14
- * browser's, notified through `openChangeAction(false)`.
13
+ * opening is a transition (pending Trigger, content from committed state), and so is a close a
14
+ * part asks for (Close, Form, a foreign close command: `openChangeAction(false)` awaited, then
15
+ * `close(returnValue)`); dismissals (Escape, a click outside) are the browser's, notified
16
+ * through `openChangeAction(false)`.
15
17
  */
16
18
  const Dialog = {
17
19
  Root: DialogRoot,
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/dialog/index.ts"],"sourcesContent":["// Ships the platform attribute typings (`command`, `commandfor`) with this subpath\n// (conventions 7.4). Type-only: the built JS has no import of it.\nimport \"../internal/platform-attributes.js\";\nimport { DialogClose } from \"./dialog-close.js\";\nimport { DialogDescription } from \"./dialog-description.js\";\nimport { DialogForm } from \"./dialog-form.js\";\nimport { DialogPopup } from \"./dialog-popup.js\";\nimport { DialogRoot } from \"./dialog-root.js\";\nimport { DialogTitle } from \"./dialog-title.js\";\nimport { DialogTrigger } from \"./dialog-trigger.js\";\n\n/**\n * Dialog built on `<dialog>` and Invoker Commands: `Root` (no DOM) > `Trigger`\n * (`button[command=show-modal]`) + `Popup` (`dialog`) > `Title`, `Description`, `Form`, `Close`.\n * JS-free when the Root has none of `open` / `defaultOpen` / `openChangeAction`; otherwise\n * opening is a transition (pending Trigger, content from committed state) and closing is the\n * browser's, notified through `openChangeAction(false)`.\n */\nexport const Dialog = {\n Root: DialogRoot,\n Trigger: DialogTrigger,\n Popup: DialogPopup,\n Close: DialogClose,\n Form: DialogForm,\n Title: DialogTitle,\n Description: DialogDescription,\n} as const;\n\nexport type { DialogRootProps, DialogRootState, DialogOpenChangeDetails } from \"./dialog-root.js\";\nexport type { DialogTriggerProps, DialogTriggerState } from \"./dialog-trigger.js\";\nexport type { DialogPopupProps, DialogPopupState } from \"./dialog-popup.js\";\nexport type { DialogCloseProps, DialogCloseState, DialogCloseDetails } from \"./dialog-close.js\";\nexport type { DialogFormProps, DialogFormState } from \"./dialog-form.js\";\nexport type { DialogTitleProps, DialogTitleState } from \"./dialog-title.js\";\nexport type { DialogDescriptionProps, DialogDescriptionState } from \"./dialog-description.js\";\n"],"mappings":";;;;;;;;;;;;;;;AAkBA,MAAa,SAAS;CACpB,MAAM;CACN,SAAS;CACT,OAAO;CACP,OAAO;CACP,MAAM;CACN,OAAO;CACP,aAAa;AACf"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/dialog/index.ts"],"sourcesContent":["// Ships the platform attribute typings (`command`, `commandfor`) with this subpath\n// (conventions 7.4). Type-only: the built JS has no import of it.\nimport \"../internal/platform-attributes.js\";\nimport { DialogClose } from \"./dialog-close.js\";\nimport { DialogDescription } from \"./dialog-description.js\";\nimport { DialogForm } from \"./dialog-form.js\";\nimport { DialogPopup } from \"./dialog-popup.js\";\nimport { DialogRoot } from \"./dialog-root.js\";\nimport { DialogTitle } from \"./dialog-title.js\";\nimport { DialogTrigger } from \"./dialog-trigger.js\";\n\n/**\n * Dialog built on `<dialog>` and Invoker Commands: `Root` (no DOM) > `Trigger`\n * (`button[command=show-modal]`) + `Popup` (`dialog`) > `Title`, `Description`, `Form`, `Close`.\n * JS-free when the Root has none of `open` / `defaultOpen` / `openChangeAction`; otherwise\n * opening is a transition (pending Trigger, content from committed state), and so is a close a\n * part asks for (Close, Form, a foreign close command: `openChangeAction(false)` awaited, then\n * `close(returnValue)`); dismissals (Escape, a click outside) are the browser's, notified\n * through `openChangeAction(false)`.\n */\nexport const Dialog = {\n Root: DialogRoot,\n Trigger: DialogTrigger,\n Popup: DialogPopup,\n Close: DialogClose,\n Form: DialogForm,\n Title: DialogTitle,\n Description: DialogDescription,\n} as const;\n\nexport type { DialogRootProps, DialogRootState, DialogOpenChangeDetails } from \"./dialog-root.js\";\nexport type { DialogTriggerProps, DialogTriggerState } from \"./dialog-trigger.js\";\nexport type { DialogPopupProps, DialogPopupState } from \"./dialog-popup.js\";\nexport type { DialogCloseProps, DialogCloseState, DialogCloseDetails } from \"./dialog-close.js\";\nexport type { DialogFormProps, DialogFormState } from \"./dialog-form.js\";\nexport type { DialogTitleProps, DialogTitleState } from \"./dialog-title.js\";\nexport type { DialogDescriptionProps, DialogDescriptionState } from \"./dialog-description.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;AAoBA,MAAa,SAAS;CACpB,MAAM;CACN,SAAS;CACT,OAAO;CACP,OAAO;CACP,MAAM;CACN,OAAO;CACP,aAAa;AACf"}
@@ -0,0 +1,33 @@
1
+ import { RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { FieldState } from "../internal/field-context.mjs";
3
+ import { CSSProperties, ComponentPropsWithRef } from "react";
4
+ //#region src/field/field-control.d.ts
5
+ export interface FieldControlProps extends Omit<ComponentPropsWithRef<"input">, "id" | "aria-invalid" | "className" | "style" | "children"> {
6
+ /** Native `name`; defaults to the Field's `name`. */
7
+ name?: string;
8
+ /** Native `disabled`; the Field's `disabled` also disables the control. Owned. */
9
+ disabled?: boolean;
10
+ /** Native `required`; the Field's `required` also requires the control. Owned. */
11
+ required?: boolean;
12
+ className?: StateValue<string | undefined, FieldState>;
13
+ style?: StateValue<CSSProperties | undefined, FieldState>;
14
+ /** Must render an `<input>`, `<textarea>` or `<select>`: `render={<textarea rows={3} />}`. */
15
+ render?: RenderProp<ComponentPropsWithRef<"input">, FieldState>;
16
+ }
17
+ /**
18
+ * The field's native control: an `<input>` (or a `<textarea>` / `<select>` through `render`)
19
+ * wired to the Field: `id` for the Label, `aria-describedby` (the consumer's tokens, the
20
+ * description, the error while shown), an explicit `aria-invalid`, and `name` / `disabled` /
21
+ * `required` from the Field unless given. Everything else passes through, including `onChange`
22
+ * / `value` for a controlled input; the Field's listeners are native and run independently.
23
+ *
24
+ * Owned keys (conventions 1.2): `id`, `name`, `disabled`, `required`, `aria-invalid`,
25
+ * `aria-describedby` (token-merged), `data-invalid`, `data-disabled`, `data-required`,
26
+ * `data-pending`.
27
+ */
28
+ export declare function FieldControl({ name, disabled, required, className, style, render, ...rest }: FieldControlProps): import("react").ReactNode;
29
+ export declare namespace FieldControl {
30
+ var displayName: string;
31
+ }
32
+ //#endregion
33
+ //# sourceMappingURL=field-control.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-control.d.mts","names":[],"sources":["../../src/field/field-control.tsx"],"mappings":";;;;iBAYiB,0BAA0B,KACzC;;EAIA;;EAEA;;EAEA;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;;EAE9C,SAAS,WAAW,gCAAgC;;;;;;;;;;;;;wBAgBtC,eACd,MACA,UACA,UACA,WACA,OACA,WACG,QACF,oCAAiB;yBARJ"}
@@ -0,0 +1,68 @@
1
+ "use client";
2
+ import { useRender } from "../internal/use-render.mjs";
3
+ import { useFieldContext } from "../internal/field-context.mjs";
4
+ import { useFieldControl, useMergedRef } from "../internal/use-field-control.mjs";
5
+ import { fieldDataAttributes } from "./field-root.mjs";
6
+ import { useCallback, useRef } from "react";
7
+ //#region src/field/field-control.tsx
8
+ const isDev = process.env.NODE_ENV !== "production";
9
+ const CONTROL_TAGS = /* @__PURE__ */ new Set([
10
+ "INPUT",
11
+ "TEXTAREA",
12
+ "SELECT"
13
+ ]);
14
+ /**
15
+ * The field's native control: an `<input>` (or a `<textarea>` / `<select>` through `render`)
16
+ * wired to the Field: `id` for the Label, `aria-describedby` (the consumer's tokens, the
17
+ * description, the error while shown), an explicit `aria-invalid`, and `name` / `disabled` /
18
+ * `required` from the Field unless given. Everything else passes through, including `onChange`
19
+ * / `value` for a controlled input; the Field's listeners are native and run independently.
20
+ *
21
+ * Owned keys (conventions 1.2): `id`, `name`, `disabled`, `required`, `aria-invalid`,
22
+ * `aria-describedby` (token-merged), `data-invalid`, `data-disabled`, `data-required`,
23
+ * `data-pending`.
24
+ */
25
+ function FieldControl({ name, disabled, required, className, style, render, ...rest }) {
26
+ const ctx = useFieldContext("Field.Control");
27
+ const { id: consumerId, ...props } = rest;
28
+ const field = useFieldControl({
29
+ labelable: true,
30
+ name,
31
+ disabled,
32
+ required,
33
+ id: consumerId,
34
+ component: "Field.Control"
35
+ });
36
+ const warned = useRef(false);
37
+ const checkElement = useCallback((el) => {
38
+ if (isDev && !warned.current && !CONTROL_TAGS.has(el.tagName)) {
39
+ warned.current = true;
40
+ console.error(`Field.Control: expected to render an <input>, <textarea> or <select> but got <${el.tagName.toLowerCase()}>. Validation, labelling and form participation need a native form control.`);
41
+ }
42
+ return () => {};
43
+ }, []);
44
+ const ref = useMergedRef(checkElement, field.ref);
45
+ return useRender({
46
+ defaultElement: "input",
47
+ render,
48
+ state: ctx.state,
49
+ className,
50
+ style,
51
+ children: void 0,
52
+ props,
53
+ libraryProps: {
54
+ id: field.id,
55
+ name: field.name,
56
+ disabled: field.disabled,
57
+ required: field.required,
58
+ ...field.attributes,
59
+ ...fieldDataAttributes(ctx.state)
60
+ },
61
+ ref
62
+ });
63
+ }
64
+ FieldControl.displayName = "FieldControl";
65
+ //#endregion
66
+ export { FieldControl };
67
+
68
+ //# sourceMappingURL=field-control.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-control.mjs","names":[],"sources":["../../src/field/field-control.tsx"],"sourcesContent":["\"use client\";\nimport { useCallback, useRef } from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties } from \"react\";\nimport { useFieldContext } from \"../internal/field-context.js\";\nimport type { FieldState } from \"../internal/field-context.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useFieldControl, useMergedRef } from \"../internal/use-field-control.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport { fieldDataAttributes } from \"./field-root.js\";\n\nconst isDev = process.env.NODE_ENV !== \"production\";\n\nexport interface FieldControlProps extends Omit<\n ComponentPropsWithRef<\"input\">,\n \"id\" | \"aria-invalid\" | \"className\" | \"style\" | \"children\"\n> {\n /** Native `name`; defaults to the Field's `name`. */\n name?: string;\n /** Native `disabled`; the Field's `disabled` also disables the control. Owned. */\n disabled?: boolean;\n /** Native `required`; the Field's `required` also requires the control. Owned. */\n required?: boolean;\n className?: StateValue<string | undefined, FieldState>;\n style?: StateValue<CSSProperties | undefined, FieldState>;\n /** Must render an `<input>`, `<textarea>` or `<select>`: `render={<textarea rows={3} />}`. */\n render?: RenderProp<ComponentPropsWithRef<\"input\">, FieldState>;\n}\n\nconst CONTROL_TAGS = new Set([\"INPUT\", \"TEXTAREA\", \"SELECT\"]);\n\n/**\n * The field's native control: an `<input>` (or a `<textarea>` / `<select>` through `render`)\n * wired to the Field: `id` for the Label, `aria-describedby` (the consumer's tokens, the\n * description, the error while shown), an explicit `aria-invalid`, and `name` / `disabled` /\n * `required` from the Field unless given. Everything else passes through, including `onChange`\n * / `value` for a controlled input; the Field's listeners are native and run independently.\n *\n * Owned keys (conventions 1.2): `id`, `name`, `disabled`, `required`, `aria-invalid`,\n * `aria-describedby` (token-merged), `data-invalid`, `data-disabled`, `data-required`,\n * `data-pending`.\n */\nexport function FieldControl({\n name,\n disabled,\n required,\n className,\n style,\n render,\n ...rest\n}: FieldControlProps) {\n const ctx = useFieldContext(\"Field.Control\");\n // `id` is not in the props type (the Field owns it); a JS caller's is ignored with a warning.\n const { id: consumerId, ...props } = rest as typeof rest & { id?: string };\n const field = useFieldControl({\n labelable: true,\n name,\n disabled,\n required,\n id: consumerId,\n component: \"Field.Control\",\n });\n const warned = useRef(false);\n const checkElement = useCallback((el: HTMLElement) => {\n if (isDev && !warned.current && !CONTROL_TAGS.has(el.tagName)) {\n warned.current = true;\n console.error(\n \"Field.Control: expected to render an <input>, <textarea> or <select> but got \" +\n `<${el.tagName.toLowerCase()}>. Validation, labelling and form participation need a ` +\n \"native form control.\",\n );\n }\n // A cleanup, so the merged ref never calls this with `null`.\n return () => {};\n }, []);\n const ref = useMergedRef<HTMLElement>(checkElement, field.ref);\n\n return useRender<ComponentPropsWithRef<\"input\">, FieldState>({\n defaultElement: \"input\",\n render,\n state: ctx.state,\n className,\n style,\n children: undefined,\n props,\n libraryProps: {\n id: field.id,\n name: field.name,\n disabled: field.disabled,\n required: field.required,\n ...field.attributes,\n ...fieldDataAttributes(ctx.state),\n },\n ref,\n });\n}\n\nFieldControl.displayName = \"FieldControl\";\n"],"mappings":";;;;;;;AAUA,MAAM,QAAQ,QAAQ,IAAI,aAAa;AAkBvC,MAAM,+BAAe,IAAI,IAAI;CAAC;CAAS;CAAY;AAAQ,CAAC;;;;;;;;;;;;AAa5D,SAAgB,aAAa,EAC3B,MACA,UACA,UACA,WACA,OACA,QACA,GAAG,QACiB;CACpB,MAAM,MAAM,gBAAgB,eAAe;CAE3C,MAAM,EAAE,IAAI,YAAY,GAAG,UAAU;CACrC,MAAM,QAAQ,gBAAgB;EAC5B,WAAW;EACX;EACA;EACA;EACA,IAAI;EACJ,WAAW;CACb,CAAC;CACD,MAAM,SAAS,OAAO,KAAK;CAC3B,MAAM,eAAe,aAAa,OAAoB;EACpD,IAAI,SAAS,CAAC,OAAO,WAAW,CAAC,aAAa,IAAI,GAAG,OAAO,GAAG;GAC7D,OAAO,UAAU;GACjB,QAAQ,MACN,iFACM,GAAG,QAAQ,YAAY,EAAE,4EAEjC;EACF;EAEA,aAAa,CAAC;CAChB,GAAG,CAAC,CAAC;CACL,MAAM,MAAM,aAA0B,cAAc,MAAM,GAAG;CAE7D,OAAO,UAAsD;EAC3D,gBAAgB;EAChB;EACA,OAAO,IAAI;EACX;EACA;EACA,UAAU,KAAA;EACV;EACA,cAAc;GACZ,IAAI,MAAM;GACV,MAAM,MAAM;GACZ,UAAU,MAAM;GAChB,UAAU,MAAM;GAChB,GAAG,MAAM;GACT,GAAG,oBAAoB,IAAI,KAAK;EAClC;EACA;CACF,CAAC;AACH;AAEA,aAAa,cAAc"}
@@ -0,0 +1,24 @@
1
+ import { RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { FieldState } from "../internal/field-context.mjs";
3
+ import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
4
+ //#region src/field/field-description.d.ts
5
+ export interface FieldDescriptionProps extends Omit<ComponentPropsWithRef<"p">, "id" | "className" | "style" | "children"> {
6
+ className?: StateValue<string | undefined, FieldState>;
7
+ style?: StateValue<CSSProperties | undefined, FieldState>;
8
+ children?: StateValue<ReactNode, FieldState>;
9
+ render?: RenderProp<ComponentPropsWithRef<"p">, FieldState>;
10
+ }
11
+ /**
12
+ * Help text for the field: a `<p>` whose id the control always lists in `aria-describedby`
13
+ * (a dangling reference when no Description is rendered is skipped by assistive technology, so
14
+ * nothing registers and the server HTML is exact).
15
+ *
16
+ * Owned keys (conventions 1.2): `id`, `data-invalid`, `data-disabled`, `data-required`,
17
+ * `data-pending`.
18
+ */
19
+ export declare function FieldDescription({ className, style, children, render, ...rest }: FieldDescriptionProps): ReactNode;
20
+ export declare namespace FieldDescription {
21
+ var displayName: string;
22
+ }
23
+ //#endregion
24
+ //# sourceMappingURL=field-description.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-description.d.mts","names":[],"sources":["../../src/field/field-description.tsx"],"mappings":";;;;iBAQiB,8BAA8B,KAC7C;EAGA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,4BAA4B;;;;;;;;;;wBAWlC,mBACd,WACA,OACA,UACA,WACG,QACF,wBAAqB;yBANR"}
@@ -0,0 +1,34 @@
1
+ "use client";
2
+ import { useRender } from "../internal/use-render.mjs";
3
+ import { useFieldContext } from "../internal/field-context.mjs";
4
+ import { fieldDataAttributes } from "./field-root.mjs";
5
+ //#region src/field/field-description.tsx
6
+ /**
7
+ * Help text for the field: a `<p>` whose id the control always lists in `aria-describedby`
8
+ * (a dangling reference when no Description is rendered is skipped by assistive technology, so
9
+ * nothing registers and the server HTML is exact).
10
+ *
11
+ * Owned keys (conventions 1.2): `id`, `data-invalid`, `data-disabled`, `data-required`,
12
+ * `data-pending`.
13
+ */
14
+ function FieldDescription({ className, style, children, render, ...rest }) {
15
+ const ctx = useFieldContext("Field.Description");
16
+ return useRender({
17
+ defaultElement: "p",
18
+ render,
19
+ state: ctx.state,
20
+ className,
21
+ style,
22
+ children,
23
+ props: rest,
24
+ libraryProps: {
25
+ id: ctx.ids.description,
26
+ ...fieldDataAttributes(ctx.state)
27
+ }
28
+ });
29
+ }
30
+ FieldDescription.displayName = "FieldDescription";
31
+ //#endregion
32
+ export { FieldDescription };
33
+
34
+ //# sourceMappingURL=field-description.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-description.mjs","names":[],"sources":["../../src/field/field-description.tsx"],"sourcesContent":["\"use client\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport { useFieldContext } from \"../internal/field-context.js\";\nimport type { FieldState } from \"../internal/field-context.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport { fieldDataAttributes } from \"./field-root.js\";\n\nexport interface FieldDescriptionProps extends Omit<\n ComponentPropsWithRef<\"p\">,\n \"id\" | \"className\" | \"style\" | \"children\"\n> {\n className?: StateValue<string | undefined, FieldState>;\n style?: StateValue<CSSProperties | undefined, FieldState>;\n children?: StateValue<ReactNode, FieldState>;\n render?: RenderProp<ComponentPropsWithRef<\"p\">, FieldState>;\n}\n\n/**\n * Help text for the field: a `<p>` whose id the control always lists in `aria-describedby`\n * (a dangling reference when no Description is rendered is skipped by assistive technology, so\n * nothing registers and the server HTML is exact).\n *\n * Owned keys (conventions 1.2): `id`, `data-invalid`, `data-disabled`, `data-required`,\n * `data-pending`.\n */\nexport function FieldDescription({\n className,\n style,\n children,\n render,\n ...rest\n}: FieldDescriptionProps) {\n const ctx = useFieldContext(\"Field.Description\");\n return useRender<ComponentPropsWithRef<\"p\">, FieldState>({\n defaultElement: \"p\",\n render,\n state: ctx.state,\n className,\n style,\n children,\n props: rest,\n libraryProps: { id: ctx.ids.description, ...fieldDataAttributes(ctx.state) },\n });\n}\n\nFieldDescription.displayName = \"FieldDescription\";\n"],"mappings":";;;;;;;;;;;;;AA0BA,SAAgB,iBAAiB,EAC/B,WACA,OACA,UACA,QACA,GAAG,QACqB;CACxB,MAAM,MAAM,gBAAgB,mBAAmB;CAC/C,OAAO,UAAkD;EACvD,gBAAgB;EAChB;EACA,OAAO,IAAI;EACX;EACA;EACA;EACA,OAAO;EACP,cAAc;GAAE,IAAI,IAAI,IAAI;GAAa,GAAG,oBAAoB,IAAI,KAAK;EAAE;CAC7E,CAAC;AACH;AAEA,iBAAiB,cAAc"}
@@ -0,0 +1,30 @@
1
+ import { RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { FieldState } from "../internal/field-context.mjs";
3
+ import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
4
+ //#region src/field/field-error.d.ts
5
+ export interface FieldErrorProps extends Omit<ComponentPropsWithRef<"div">, "id" | "className" | "style" | "children"> {
6
+ className?: StateValue<string | undefined, FieldState>;
7
+ style?: StateValue<CSSProperties | undefined, FieldState>;
8
+ /**
9
+ * Rendered only while `state.invalid`. Default: `state.messages` joined by a space. A function
10
+ * gets the state: `({ validity, messages }) => validity.valueMissing ? "Enter your email" : messages.join(" ")`.
11
+ * Must not suspend (urgent render, design 2.4).
12
+ */
13
+ children?: StateValue<ReactNode, FieldState>;
14
+ render?: RenderProp<ComponentPropsWithRef<"div">, FieldState>;
15
+ }
16
+ /**
17
+ * The field's error message: a `div` rendered only while an error is shown, whose id the control
18
+ * then lists in `aria-describedby`. No role and no live region: the library focuses the first
19
+ * invalid control after a submission attempt, and focus reads the description (design 4.2).
20
+ * Its presence lets the Field cancel the platform's validation bubble.
21
+ *
22
+ * Owned keys (conventions 1.2): `id`, `data-invalid`, `data-disabled`, `data-required`,
23
+ * `data-pending`.
24
+ */
25
+ export declare function FieldError({ className, style, children, render, ...rest }: FieldErrorProps): ReactNode;
26
+ export declare namespace FieldError {
27
+ var displayName: string;
28
+ }
29
+ //#endregion
30
+ //# sourceMappingURL=field-error.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-error.d.mts","names":[],"sources":["../../src/field/field-error.tsx"],"mappings":";;;;iBASiB,wBAAwB,KACvC;EAGA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;;;;;;EAM9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,8BAA8B;;;;;;;;;;;wBAgBpC,aACd,WACA,OACA,UACA,WACG,QACF,kBAAe;yBANF"}
@@ -0,0 +1,42 @@
1
+ "use client";
2
+ import { useRender } from "../internal/use-render.mjs";
3
+ import { useFieldContext } from "../internal/field-context.mjs";
4
+ import { fieldDataAttributes } from "./field-root.mjs";
5
+ import { useLayoutEffect } from "react";
6
+ //#region src/field/field-error.tsx
7
+ function defaultChildren(state) {
8
+ return state.messages.join(" ");
9
+ }
10
+ /**
11
+ * The field's error message: a `div` rendered only while an error is shown, whose id the control
12
+ * then lists in `aria-describedby`. No role and no live region: the library focuses the first
13
+ * invalid control after a submission attempt, and focus reads the description (design 4.2).
14
+ * Its presence lets the Field cancel the platform's validation bubble.
15
+ *
16
+ * Owned keys (conventions 1.2): `id`, `data-invalid`, `data-disabled`, `data-required`,
17
+ * `data-pending`.
18
+ */
19
+ function FieldError({ className, style, children = defaultChildren, render, ...rest }) {
20
+ const ctx = useFieldContext("Field.Error");
21
+ const { registerError } = ctx;
22
+ useLayoutEffect(registerError, [registerError]);
23
+ const element = useRender({
24
+ defaultElement: "div",
25
+ render,
26
+ state: ctx.state,
27
+ className,
28
+ style,
29
+ children,
30
+ props: rest,
31
+ libraryProps: {
32
+ id: ctx.ids.error,
33
+ ...fieldDataAttributes(ctx.state)
34
+ }
35
+ });
36
+ return ctx.state.invalid ? element : null;
37
+ }
38
+ FieldError.displayName = "FieldError";
39
+ //#endregion
40
+ export { FieldError };
41
+
42
+ //# sourceMappingURL=field-error.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-error.mjs","names":[],"sources":["../../src/field/field-error.tsx"],"sourcesContent":["\"use client\";\nimport { useLayoutEffect } from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport { useFieldContext } from \"../internal/field-context.js\";\nimport type { FieldState } from \"../internal/field-context.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport { fieldDataAttributes } from \"./field-root.js\";\n\nexport interface FieldErrorProps extends Omit<\n ComponentPropsWithRef<\"div\">,\n \"id\" | \"className\" | \"style\" | \"children\"\n> {\n className?: StateValue<string | undefined, FieldState>;\n style?: StateValue<CSSProperties | undefined, FieldState>;\n /**\n * Rendered only while `state.invalid`. Default: `state.messages` joined by a space. A function\n * gets the state: `({ validity, messages }) => validity.valueMissing ? \"Enter your email\" : messages.join(\" \")`.\n * Must not suspend (urgent render, design 2.4).\n */\n children?: StateValue<ReactNode, FieldState>;\n render?: RenderProp<ComponentPropsWithRef<\"div\">, FieldState>;\n}\n\nfunction defaultChildren(state: FieldState): ReactNode {\n return state.messages.join(\" \");\n}\n\n/**\n * The field's error message: a `div` rendered only while an error is shown, whose id the control\n * then lists in `aria-describedby`. No role and no live region: the library focuses the first\n * invalid control after a submission attempt, and focus reads the description (design 4.2).\n * Its presence lets the Field cancel the platform's validation bubble.\n *\n * Owned keys (conventions 1.2): `id`, `data-invalid`, `data-disabled`, `data-required`,\n * `data-pending`.\n */\nexport function FieldError({\n className,\n style,\n children = defaultChildren,\n render,\n ...rest\n}: FieldErrorProps) {\n const ctx = useFieldContext(\"Field.Error\");\n const { registerError } = ctx;\n useLayoutEffect(registerError, [registerError]);\n const element = useRender<ComponentPropsWithRef<\"div\">, FieldState>({\n defaultElement: \"div\",\n render,\n state: ctx.state,\n className,\n style,\n children,\n props: rest,\n libraryProps: { id: ctx.ids.error, ...fieldDataAttributes(ctx.state) },\n });\n return ctx.state.invalid ? element : null;\n}\n\nFieldError.displayName = \"FieldError\";\n"],"mappings":";;;;;;AAwBA,SAAS,gBAAgB,OAA8B;CACrD,OAAO,MAAM,SAAS,KAAK,GAAG;AAChC;;;;;;;;;;AAWA,SAAgB,WAAW,EACzB,WACA,OACA,WAAW,iBACX,QACA,GAAG,QACe;CAClB,MAAM,MAAM,gBAAgB,aAAa;CACzC,MAAM,EAAE,kBAAkB;CAC1B,gBAAgB,eAAe,CAAC,aAAa,CAAC;CAC9C,MAAM,UAAU,UAAoD;EAClE,gBAAgB;EAChB;EACA,OAAO,IAAI;EACX;EACA;EACA;EACA,OAAO;EACP,cAAc;GAAE,IAAI,IAAI,IAAI;GAAO,GAAG,oBAAoB,IAAI,KAAK;EAAE;CACvE,CAAC;CACD,OAAO,IAAI,MAAM,UAAU,UAAU;AACvC;AAEA,WAAW,cAAc"}
@@ -0,0 +1,25 @@
1
+ import { RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { FieldState } from "../internal/field-context.mjs";
3
+ import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
4
+ //#region src/field/field-label.d.ts
5
+ export interface FieldLabelProps extends Omit<ComponentPropsWithRef<"label">, "htmlFor" | "id" | "className" | "style" | "children"> {
6
+ className?: StateValue<string | undefined, FieldState>;
7
+ style?: StateValue<CSSProperties | undefined, FieldState>;
8
+ children?: StateValue<ReactNode, FieldState>;
9
+ /** Must render a `<label>`. */
10
+ render?: RenderProp<ComponentPropsWithRef<"label">, FieldState>;
11
+ }
12
+ /**
13
+ * The field's visible name: `<label for>` pointing at the control. For a group control (a
14
+ * RadioGroup or CheckboxGroup `<fieldset>`) it renders no `for` once the group registered, and
15
+ * the group names itself with `aria-labelledby` → this id (design 4.2).
16
+ *
17
+ * Owned keys (conventions 1.2): `id`, `htmlFor`, `data-invalid`, `data-disabled`,
18
+ * `data-required`, `data-pending`.
19
+ */
20
+ export declare function FieldLabel({ className, style, children, render, ...rest }: FieldLabelProps): ReactNode;
21
+ export declare namespace FieldLabel {
22
+ var displayName: string;
23
+ }
24
+ //#endregion
25
+ //# sourceMappingURL=field-label.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-label.d.mts","names":[],"sources":["../../src/field/field-label.tsx"],"mappings":";;;;iBAWiB,wBAAwB,KACvC;EAGA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;;EAEjC,SAAS,WAAW,gCAAgC;;;;;;;;;;wBAWtC,aAAa,WAAW,OAAO,UAAU,WAAW,QAAQ,kBAAe;yBAA3E"}
@@ -0,0 +1,46 @@
1
+ "use client";
2
+ import { useRender } from "../internal/use-render.mjs";
3
+ import { useFieldContext } from "../internal/field-context.mjs";
4
+ import { fieldDataAttributes } from "./field-root.mjs";
5
+ import { useCallback, useRef } from "react";
6
+ //#region src/field/field-label.tsx
7
+ const isDev = process.env.NODE_ENV !== "production";
8
+ /**
9
+ * The field's visible name: `<label for>` pointing at the control. For a group control (a
10
+ * RadioGroup or CheckboxGroup `<fieldset>`) it renders no `for` once the group registered, and
11
+ * the group names itself with `aria-labelledby` → this id (design 4.2).
12
+ *
13
+ * Owned keys (conventions 1.2): `id`, `htmlFor`, `data-invalid`, `data-disabled`,
14
+ * `data-required`, `data-pending`.
15
+ */
16
+ function FieldLabel({ className, style, children, render, ...rest }) {
17
+ const ctx = useFieldContext("Field.Label");
18
+ const warned = useRef(false);
19
+ const ref = useCallback((el) => {
20
+ if (isDev && !warned.current && el.tagName !== "LABEL") {
21
+ warned.current = true;
22
+ console.error(`Field.Label: expected to render a <label> element but got <${el.tagName.toLowerCase()}>. Label activation and the control's accessible name need a real <label>.`);
23
+ }
24
+ return () => {};
25
+ }, []);
26
+ return useRender({
27
+ defaultElement: "label",
28
+ render,
29
+ state: ctx.state,
30
+ className,
31
+ style,
32
+ children,
33
+ props: rest,
34
+ libraryProps: {
35
+ id: ctx.ids.label,
36
+ htmlFor: ctx.labelable ? ctx.ids.control : void 0,
37
+ ...fieldDataAttributes(ctx.state)
38
+ },
39
+ ref
40
+ });
41
+ }
42
+ FieldLabel.displayName = "FieldLabel";
43
+ //#endregion
44
+ export { FieldLabel };
45
+
46
+ //# sourceMappingURL=field-label.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-label.mjs","names":[],"sources":["../../src/field/field-label.tsx"],"sourcesContent":["\"use client\";\nimport { useCallback, useRef } from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport { useFieldContext } from \"../internal/field-context.js\";\nimport type { FieldState } from \"../internal/field-context.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport { fieldDataAttributes } from \"./field-root.js\";\n\nconst isDev = process.env.NODE_ENV !== \"production\";\n\nexport interface FieldLabelProps extends Omit<\n ComponentPropsWithRef<\"label\">,\n \"htmlFor\" | \"id\" | \"className\" | \"style\" | \"children\"\n> {\n className?: StateValue<string | undefined, FieldState>;\n style?: StateValue<CSSProperties | undefined, FieldState>;\n children?: StateValue<ReactNode, FieldState>;\n /** Must render a `<label>`. */\n render?: RenderProp<ComponentPropsWithRef<\"label\">, FieldState>;\n}\n\n/**\n * The field's visible name: `<label for>` pointing at the control. For a group control (a\n * RadioGroup or CheckboxGroup `<fieldset>`) it renders no `for` once the group registered, and\n * the group names itself with `aria-labelledby` → this id (design 4.2).\n *\n * Owned keys (conventions 1.2): `id`, `htmlFor`, `data-invalid`, `data-disabled`,\n * `data-required`, `data-pending`.\n */\nexport function FieldLabel({ className, style, children, render, ...rest }: FieldLabelProps) {\n const ctx = useFieldContext(\"Field.Label\");\n const warned = useRef(false);\n const ref = useCallback((el: HTMLElement) => {\n if (isDev && !warned.current && el.tagName !== \"LABEL\") {\n warned.current = true;\n console.error(\n `Field.Label: expected to render a <label> element but got <${el.tagName.toLowerCase()}>. ` +\n \"Label activation and the control's accessible name need a real <label>.\",\n );\n }\n // A cleanup, so the merged ref never calls this with `null`.\n return () => {};\n }, []);\n return useRender<ComponentPropsWithRef<\"label\">, FieldState>({\n defaultElement: \"label\",\n render,\n state: ctx.state,\n className,\n style,\n children,\n props: rest,\n libraryProps: {\n id: ctx.ids.label,\n htmlFor: ctx.labelable ? ctx.ids.control : undefined,\n ...fieldDataAttributes(ctx.state),\n },\n ref,\n });\n}\n\nFieldLabel.displayName = \"FieldLabel\";\n"],"mappings":";;;;;;AASA,MAAM,QAAQ,QAAQ,IAAI,aAAa;;;;;;;;;AAqBvC,SAAgB,WAAW,EAAE,WAAW,OAAO,UAAU,QAAQ,GAAG,QAAyB;CAC3F,MAAM,MAAM,gBAAgB,aAAa;CACzC,MAAM,SAAS,OAAO,KAAK;CAC3B,MAAM,MAAM,aAAa,OAAoB;EAC3C,IAAI,SAAS,CAAC,OAAO,WAAW,GAAG,YAAY,SAAS;GACtD,OAAO,UAAU;GACjB,QAAQ,MACN,8DAA8D,GAAG,QAAQ,YAAY,EAAE,2EAEzF;EACF;EAEA,aAAa,CAAC;CAChB,GAAG,CAAC,CAAC;CACL,OAAO,UAAsD;EAC3D,gBAAgB;EAChB;EACA,OAAO,IAAI;EACX;EACA;EACA;EACA,OAAO;EACP,cAAc;GACZ,IAAI,IAAI,IAAI;GACZ,SAAS,IAAI,YAAY,IAAI,IAAI,UAAU,KAAA;GAC3C,GAAG,oBAAoB,IAAI,KAAK;EAClC;EACA;CACF,CAAC;AACH;AAEA,WAAW,cAAc"}
@@ -0,0 +1,53 @@
1
+ import { RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { FieldErrorValue, FieldValidity } from "../internal/validity.mjs";
3
+ import { FieldState } from "../internal/field-context.mjs";
4
+ import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
5
+ //#region src/field/field-root.d.ts
6
+ /**
7
+ * A custom constraint: receives the field's submitted value (`formData.get(name)` as a string,
8
+ * `""` when absent) and the whole form's `FormData`. Return a message (or several) for an error,
9
+ * `null` / `undefined` / `""` for none.
10
+ */
11
+ export type FieldValidate = (value: string, formData: FormData) => FieldErrorValue;
12
+ export interface FieldRootProps extends Omit<ComponentPropsWithRef<"div">, "className" | "style" | "children"> {
13
+ /** Form field name: the control's default `name`, the key in `Form.Root errors`, and the entry `validate` reads. */
14
+ name?: string;
15
+ /** Disables the control (native `disabled`) and marks every part `data-disabled`. */
16
+ disabled?: boolean;
17
+ /** Makes the control `required` (native) and marks every part `data-required`. */
18
+ required?: boolean;
19
+ /**
20
+ * A custom constraint, written into the control with `setCustomValidity`. Sync. Receives the
21
+ * field's submitted value (`formData.get(name)` as a string, `""` when absent) and the whole
22
+ * form's `FormData`, so a cross-field rule can read the other field. Runs at mount, on every
23
+ * `input` / `change` in the form (and a FUNSTACK control's value change), on blur, after a
24
+ * reset, on a submission attempt, and in every commit in which this prop changed identity, so
25
+ * a rule that reads outside state (a list that loads, a sibling value in React state) updates
26
+ * on render. An inline function therefore re-runs on every render of the Field; wrap it in
27
+ * `useCallback` to re-run only when its inputs change. Return a string
28
+ * or strings for an error, `null` / `undefined` / `""` for none. Shown when the platform's
29
+ * `:user-invalid` heuristic says so, like a native constraint. Not serialisable: a Field with
30
+ * `validate` is rendered from a client component.
31
+ */
32
+ validate?: FieldValidate;
33
+ className?: StateValue<string | undefined, FieldState>;
34
+ style?: StateValue<CSSProperties | undefined, FieldState>;
35
+ children?: StateValue<ReactNode, FieldState>;
36
+ render?: RenderProp<ComponentPropsWithRef<"div">, FieldState>;
37
+ }
38
+ /**
39
+ * The field: a `div` that owns the ids, the urgent validity mirror, the custom-validity slot
40
+ * (`validate` and the server error), the control registration and the native listeners
41
+ * (design/components/field-form.md 2, 14). `data-pending` is the enclosing form's submission
42
+ * (`useFormStatus`), the one part that folds the form channel into its own state, because a
43
+ * Field has no action of its own.
44
+ *
45
+ * Owned keys (conventions 1.2): `data-invalid`, `data-disabled`, `data-required`, `data-pending`.
46
+ */
47
+ export declare function FieldRoot({ name, disabled, required, validate, className, style, children, render, ...rest }: FieldRootProps): import("react").JSX.Element;
48
+ export declare namespace FieldRoot {
49
+ var displayName: string;
50
+ }
51
+ //#endregion
52
+ export type { FieldState, FieldValidity };
53
+ //# sourceMappingURL=field-root.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-root.d.mts","names":[],"sources":["../../src/field/field-root.tsx"],"mappings":";;;;;;;;;;YAiDY,iBAAiB,eAAe,UAAU,aAAa;iBAElD,uBAAuB,KACtC;;EAIA;;EAEA;;EAEA;;;;;;;;;;;;;;EAcA,WAAW;EACX,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,8BAA8B;;;;;;;;;;;wBA0CpC,YACd,MACA,UACA,UACA,UACA,WACA,OACA,UACA,WACG,QACF,iCAAc,IAAA;yBAVD"}