@bgub/fig-headless 0.0.0-stage → 0.1.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE +21 -0
  3. package/README.md +595 -2
  4. package/dist/accordion.d.ts +46 -0
  5. package/dist/accordion.js +2 -0
  6. package/dist/accordion.js.map +1 -0
  7. package/dist/changes--uvVfvy8.d.ts +11 -0
  8. package/dist/changes-DglRuvQa.js +2 -0
  9. package/dist/changes-DglRuvQa.js.map +1 -0
  10. package/dist/checkbox.d.ts +24 -0
  11. package/dist/checkbox.js +2 -0
  12. package/dist/checkbox.js.map +1 -0
  13. package/dist/combobox.d.ts +53 -0
  14. package/dist/combobox.js +2 -0
  15. package/dist/combobox.js.map +1 -0
  16. package/dist/command.d.ts +42 -0
  17. package/dist/command.js +2 -0
  18. package/dist/command.js.map +1 -0
  19. package/dist/composite-CG4JghHi.d.ts +4 -0
  20. package/dist/composite-CiKLX-Ti.js +2 -0
  21. package/dist/composite-CiKLX-Ti.js.map +1 -0
  22. package/dist/controllable-value-CDbRzlId.js +2 -0
  23. package/dist/controllable-value-CDbRzlId.js.map +1 -0
  24. package/dist/diagnostics-CoURcwqb.js +2 -0
  25. package/dist/diagnostics-CoURcwqb.js.map +1 -0
  26. package/dist/dialog.d.ts +44 -0
  27. package/dist/dialog.js +2 -0
  28. package/dist/dialog.js.map +1 -0
  29. package/dist/field.d.ts +35 -0
  30. package/dist/field.js +2 -0
  31. package/dist/field.js.map +1 -0
  32. package/dist/form-reset-B7G3-0OT.js +2 -0
  33. package/dist/form-reset-B7G3-0OT.js.map +1 -0
  34. package/dist/hover-HF7JXkR2.js +2 -0
  35. package/dist/hover-HF7JXkR2.js.map +1 -0
  36. package/dist/ids-Cvu36mFL.js +2 -0
  37. package/dist/ids-Cvu36mFL.js.map +1 -0
  38. package/dist/listbox-DcD6dHSg.js +2 -0
  39. package/dist/listbox-DcD6dHSg.js.map +1 -0
  40. package/dist/listbox.d.ts +32 -0
  41. package/dist/listbox.js +2 -0
  42. package/dist/listbox.js.map +1 -0
  43. package/dist/menu/context-menu.d.ts +15 -0
  44. package/dist/menu/context-menu.js +2 -0
  45. package/dist/menu/context-menu.js.map +1 -0
  46. package/dist/menu/submenu.d.ts +20 -0
  47. package/dist/menu/submenu.js +2 -0
  48. package/dist/menu/submenu.js.map +1 -0
  49. package/dist/menu-B4pKuF3z.js +2 -0
  50. package/dist/menu-B4pKuF3z.js.map +1 -0
  51. package/dist/menu.d.ts +52 -0
  52. package/dist/menu.js +1 -0
  53. package/dist/open-state-BmP__rp4.d.ts +6 -0
  54. package/dist/open-state-G79p9WiE.js +2 -0
  55. package/dist/open-state-G79p9WiE.js.map +1 -0
  56. package/dist/part-reference-B7zINPUK.js +2 -0
  57. package/dist/part-reference-B7zINPUK.js.map +1 -0
  58. package/dist/parts-I0jdefyp.js +2 -0
  59. package/dist/parts-I0jdefyp.js.map +1 -0
  60. package/dist/popover-CSPJNmHy.d.ts +37 -0
  61. package/dist/popover-UfJHN1h2.js +2 -0
  62. package/dist/popover-UfJHN1h2.js.map +1 -0
  63. package/dist/popover.d.ts +2 -0
  64. package/dist/popover.js +1 -0
  65. package/dist/popup/position.d.ts +26 -0
  66. package/dist/popup/position.js +2 -0
  67. package/dist/popup/position.js.map +1 -0
  68. package/dist/popup-state-CFSmDEwK.js +2 -0
  69. package/dist/popup-state-CFSmDEwK.js.map +1 -0
  70. package/dist/radio-group.d.ts +52 -0
  71. package/dist/radio-group.js +2 -0
  72. package/dist/radio-group.js.map +1 -0
  73. package/dist/relations-uMHtgW03.js +2 -0
  74. package/dist/relations-uMHtgW03.js.map +1 -0
  75. package/dist/select.d.ts +45 -0
  76. package/dist/select.js +2 -0
  77. package/dist/select.js.map +1 -0
  78. package/dist/slider.d.ts +35 -0
  79. package/dist/slider.js +2 -0
  80. package/dist/slider.js.map +1 -0
  81. package/dist/switch.d.ts +24 -0
  82. package/dist/switch.js +2 -0
  83. package/dist/switch.js.map +1 -0
  84. package/dist/tabs/indicator.d.ts +20 -0
  85. package/dist/tabs/indicator.js +2 -0
  86. package/dist/tabs/indicator.js.map +1 -0
  87. package/dist/tabs.d.ts +49 -0
  88. package/dist/tabs.js +2 -0
  89. package/dist/tabs.js.map +1 -0
  90. package/dist/toast.d.ts +34 -0
  91. package/dist/toast.js +2 -0
  92. package/dist/toast.js.map +1 -0
  93. package/dist/toggle-B0hOxshF.js +2 -0
  94. package/dist/toggle-B0hOxshF.js.map +1 -0
  95. package/dist/toggle-q6B-U4Nq.d.ts +29 -0
  96. package/dist/toolbar.d.ts +25 -0
  97. package/dist/toolbar.js +2 -0
  98. package/dist/toolbar.js.map +1 -0
  99. package/dist/tooltip.d.ts +31 -0
  100. package/dist/tooltip.js +2 -0
  101. package/dist/tooltip.js.map +1 -0
  102. package/dist-development/accordion.js +2 -0
  103. package/dist-development/accordion.js.map +1 -0
  104. package/dist-development/changes-DglRuvQa.js +2 -0
  105. package/dist-development/changes-DglRuvQa.js.map +1 -0
  106. package/dist-development/checkbox.js +2 -0
  107. package/dist-development/checkbox.js.map +1 -0
  108. package/dist-development/combobox.js +2 -0
  109. package/dist-development/combobox.js.map +1 -0
  110. package/dist-development/command.js +2 -0
  111. package/dist-development/command.js.map +1 -0
  112. package/dist-development/composite-DoFLgUjR.js +2 -0
  113. package/dist-development/composite-DoFLgUjR.js.map +1 -0
  114. package/dist-development/controllable-value-CDbRzlId.js +2 -0
  115. package/dist-development/controllable-value-CDbRzlId.js.map +1 -0
  116. package/dist-development/diagnostics-BkQV0MaT.js +2 -0
  117. package/dist-development/diagnostics-BkQV0MaT.js.map +1 -0
  118. package/dist-development/dialog.js +2 -0
  119. package/dist-development/dialog.js.map +1 -0
  120. package/dist-development/field.js +2 -0
  121. package/dist-development/field.js.map +1 -0
  122. package/dist-development/form-reset-B7G3-0OT.js +2 -0
  123. package/dist-development/form-reset-B7G3-0OT.js.map +1 -0
  124. package/dist-development/hover-HF7JXkR2.js +2 -0
  125. package/dist-development/hover-HF7JXkR2.js.map +1 -0
  126. package/dist-development/ids-Cvu36mFL.js +2 -0
  127. package/dist-development/ids-Cvu36mFL.js.map +1 -0
  128. package/dist-development/listbox-B-oe_0c0.js +2 -0
  129. package/dist-development/listbox-B-oe_0c0.js.map +1 -0
  130. package/dist-development/listbox.js +2 -0
  131. package/dist-development/listbox.js.map +1 -0
  132. package/dist-development/menu/context-menu.js +2 -0
  133. package/dist-development/menu/context-menu.js.map +1 -0
  134. package/dist-development/menu/submenu.js +2 -0
  135. package/dist-development/menu/submenu.js.map +1 -0
  136. package/dist-development/menu-DBxBFdYm.js +2 -0
  137. package/dist-development/menu-DBxBFdYm.js.map +1 -0
  138. package/dist-development/menu.js +1 -0
  139. package/dist-development/open-state-G79p9WiE.js +2 -0
  140. package/dist-development/open-state-G79p9WiE.js.map +1 -0
  141. package/dist-development/part-reference-B7zINPUK.js +2 -0
  142. package/dist-development/part-reference-B7zINPUK.js.map +1 -0
  143. package/dist-development/parts-I0jdefyp.js +2 -0
  144. package/dist-development/parts-I0jdefyp.js.map +1 -0
  145. package/dist-development/popover-DXxGM3tO.js +2 -0
  146. package/dist-development/popover-DXxGM3tO.js.map +1 -0
  147. package/dist-development/popover.js +1 -0
  148. package/dist-development/popup/position.js +2 -0
  149. package/dist-development/popup/position.js.map +1 -0
  150. package/dist-development/popup-state-BUYfC2nG.js +2 -0
  151. package/dist-development/popup-state-BUYfC2nG.js.map +1 -0
  152. package/dist-development/radio-group.js +2 -0
  153. package/dist-development/radio-group.js.map +1 -0
  154. package/dist-development/relations-CZpjLINW.js +2 -0
  155. package/dist-development/relations-CZpjLINW.js.map +1 -0
  156. package/dist-development/select.js +2 -0
  157. package/dist-development/select.js.map +1 -0
  158. package/dist-development/slider.js +2 -0
  159. package/dist-development/slider.js.map +1 -0
  160. package/dist-development/switch.js +2 -0
  161. package/dist-development/switch.js.map +1 -0
  162. package/dist-development/tabs/indicator.js +2 -0
  163. package/dist-development/tabs/indicator.js.map +1 -0
  164. package/dist-development/tabs.js +2 -0
  165. package/dist-development/tabs.js.map +1 -0
  166. package/dist-development/toast.js +2 -0
  167. package/dist-development/toast.js.map +1 -0
  168. package/dist-development/toggle-CIbzprg5.js +2 -0
  169. package/dist-development/toggle-CIbzprg5.js.map +1 -0
  170. package/dist-development/toolbar.js +2 -0
  171. package/dist-development/toolbar.js.map +1 -0
  172. package/dist-development/tooltip.js +2 -0
  173. package/dist-development/tooltip.js.map +1 -0
  174. package/package.json +177 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"radio-group.js","names":[],"sources":["../src/radio-group/parts.ts","../src/radio-group/radio-group.tsx"],"sourcesContent":["import { createMixin, type MixinContext } from \"@bgub/fig\";\nimport { on } from \"@bgub/fig-dom\";\nimport type { Composite, Orientation } from \"../internal/composite.ts\";\nimport { expectHost } from \"../internal/diagnostics.ts\";\nimport { bindPart } from \"../internal/parts.ts\";\n\n/** Widget-level state every part reads. Built once per root render. */\nexport interface RadioGroupPartState {\n readonly bindFormReset: (node: HTMLElement, signal: AbortSignal) => void;\n readonly disabled: boolean;\n readonly name: string;\n readonly orientation: Orientation;\n readonly readOnly: boolean;\n readonly registry: Composite;\n readonly required: boolean;\n readonly select: (value: unknown, event: Event, trigger: Element) => void;\n}\n\ninterface RadioOwnState {\n readonly checked: boolean;\n readonly disabled: boolean;\n readonly value: unknown;\n}\n\nexport const radioGroupMixin = /* @__PURE__ */ createMixin(\n (context: MixinContext, state: RadioGroupPartState) => ({\n \"aria-orientation\": state.orientation,\n \"aria-readonly\": state.readOnly ? \"true\" : undefined,\n bind: bindPart(context, state.registry, (node, signal) =>\n state.registry.bindContainer(node, signal),\n ),\n \"data-disabled\": state.disabled ? \"\" : undefined,\n \"data-orientation\": state.orientation,\n \"data-readonly\": state.readOnly ? \"\" : undefined,\n // Radios sharing a name give the browser the roving tab stop, arrow\n // movement on both axes, wrapping, skipping disabled, and Space. The\n // group only has to hear what the platform decided.\n mix: [\n on(\"click\", (event) => {\n if (\n state.readOnly &&\n state.registry.itemAt(event.target) !== undefined\n ) {\n event.preventDefault();\n }\n }),\n on(\"change\", (event) => {\n const radio = state.registry.itemAt(event.target);\n if (radio === undefined) return;\n state.select(radio.value, event, radio.node);\n }),\n ],\n role: \"radiogroup\",\n }),\n);\n\n/** Applies to a native `<input type=\"radio\">`, which owns the interaction. */\nexport const radioMixin = /* @__PURE__ */ createMixin(\n (context: MixinContext, state: RadioGroupPartState, own: RadioOwnState) => {\n expectHost(context, \"radio\", \"input\");\n const disabled = own.disabled || context.props.disabled === true;\n return {\n bind: bindPart(context, state.registry, (node, signal) => {\n state.registry.bindItem(node, signal, own.value, disabled);\n state.bindFormReset(node, signal);\n }),\n checked: own.checked,\n \"data-checked\": own.checked ? \"\" : undefined,\n \"data-disabled\": disabled ? \"\" : undefined,\n \"data-readonly\": state.readOnly ? \"\" : undefined,\n disabled: disabled ? true : undefined,\n name: context.props.name ?? state.name,\n required: state.required ? true : context.props.required,\n type: \"radio\",\n // The submitted value is the string form of the identity. Supply a\n // `value` prop when the two should differ.\n value: context.props.value ?? String(own.value),\n };\n },\n);\n","import { useControllableValue } from \"../internal/controllable-value.ts\";\nimport {\n type FigNode,\n type MixinDescriptor,\n useBeforePaint,\n useId,\n useMemo,\n useStableEvent,\n} from \"@bgub/fig\";\nimport {\n type ChangeDetails,\n createChangeDetails,\n} from \"../internal/changes.ts\";\nimport {\n createComposite,\n type Orientation,\n sameValue,\n} from \"../internal/composite.ts\";\nimport { useRegistrationReconcile } from \"../internal/reconcile.ts\";\nimport { assertAccessibleName } from \"../internal/diagnostics.ts\";\nimport { createFormReset } from \"../internal/form-reset.ts\";\nimport { radioGroupMixin, radioMixin } from \"./parts.ts\";\n\nexport type RadioGroupOrientation = Orientation;\n\nexport type RadioGroupValueChangeDetails = ChangeDetails;\n\nexport type RadioGroupValueChangeHandler<Value = unknown> = (\n value: Value | null,\n details: RadioGroupValueChangeDetails,\n signal: AbortSignal,\n) => void;\n\nexport interface RadioOptions {\n disabled?: boolean;\n}\n\nexport interface RadioGroupOptions<Value = unknown> {\n defaultValue?: Value | null;\n /** Disables every radio in the group. */\n disabled?: boolean;\n /** The submitted field name. Generated when absent. */\n name?: string;\n onValueChange?: RadioGroupValueChangeHandler<Value>;\n /** Labels the group's axis. Defaults to `vertical`, the ARIA default. */\n orientation?: RadioGroupOrientation;\n /** Prevents user changes while keeping radios focusable and submitted. */\n readOnly?: boolean;\n /** Requires a choice before the form submits. */\n required?: boolean;\n value?: Value | null;\n}\n\nexport interface RadioGroupParts<Value = unknown> {\n /** The field name every radio shares. */\n readonly name: string;\n readonly value: Value | null;\n /** Applies to a native `<input type=\"radio\">`. */\n radio(value: Value, options?: RadioOptions): MixinDescriptor;\n root(): MixinDescriptor;\n}\n\nexport interface RadioGroupProps<\n Value = unknown,\n> extends RadioGroupOptions<Value> {\n children: (group: RadioGroupParts<Value>) => FigNode;\n}\n\n/**\n * Coordinates one radio group over native radio inputs.\n *\n * Radios sharing a name are already a group to the browser, which owns the\n * single tab stop, arrow movement on either axis, wrapping, skipping disabled\n * radios, Space, form submission, and validity. The widget owns the selected\n * value and reports changes, and adds no keyboard handling of its own.\n */\nexport function useRadioGroup<Value = unknown>(\n options: RadioGroupOptions<Value> = {},\n): RadioGroupParts<Value> {\n const {\n disabled = false,\n orientation = \"vertical\",\n readOnly = false,\n required = false,\n } = options;\n const requestReconcile = useRegistrationReconcile();\n const selection = useControllableValue<Value | null>({\n value: options.value,\n defaultValue: options.defaultValue ?? null,\n onChange: options.onValueChange,\n equal: sameValue,\n reconcile: requestReconcile,\n });\n const value = selection.value;\n // Registration only: nothing renders from it, so there is nothing to\n // reconcile when a descendant mounts a radio.\n const registry = useMemo(\n () =>\n createComposite({\n container: '[role=\"radiogroup\"]',\n item: 'input[type=\"radio\"]',\n name: \"radio group\",\n }),\n [],\n );\n useBeforePaint(() => {\n const container = registry.containerNode();\n if (container !== null) assertAccessibleName(container, \"radio group\");\n });\n\n const formReset = useMemo(() => createFormReset(selection.reset), []);\n const select = useStableEvent(\n (next: unknown, event: Event, trigger: Element) => {\n if (\n event.defaultPrevented ||\n disabled ||\n trigger.matches(\":disabled\") ||\n readOnly\n ) {\n requestReconcile();\n return;\n }\n selection.request(\n () => next as Value,\n createChangeDetails(event, trigger),\n );\n },\n );\n\n const id = useId();\n const name = options.name ?? `${id}-radio-group`;\n const state = {\n bindFormReset: formReset.bind,\n disabled,\n name,\n orientation,\n readOnly,\n registry,\n required,\n select,\n };\n\n return {\n name,\n radio: (radioValue, radioOptions = {}) =>\n radioMixin(state, {\n checked: value !== null && sameValue(radioValue, value),\n disabled: disabled || radioOptions.disabled === true,\n value: radioValue,\n }),\n root: () => radioGroupMixin(state),\n value,\n };\n}\n\n/**\n * {@link useRadioGroup} as a component, for a group that is not already a\n * component of its own. Selection re-renders this root rather than the caller.\n */\nexport function RadioGroup<Value = unknown>(\n props: RadioGroupProps<Value>,\n): FigNode {\n return props.children(useRadioGroup(props));\n}\n"],"mappings":"yaAwBA,MAAa,EAAkC,GAC5C,EAAuB,KAAgC,CACtD,mBAAoB,EAAM,YAC1B,gBAAiB,EAAM,SAAW,OAAS,IAAA,GAC3C,KAAM,EAAS,EAAS,EAAM,UAAW,EAAM,IAC7C,EAAM,SAAS,cAAc,EAAM,CAAM,CAC3C,EACA,gBAAiB,EAAM,SAAW,GAAK,IAAA,GACvC,mBAAoB,EAAM,YAC1B,gBAAiB,EAAM,SAAW,GAAK,IAAA,GAIvC,IAAK,CACH,EAAG,QAAU,GAAU,CAEnB,EAAM,UACN,EAAM,SAAS,OAAO,EAAM,MAAM,IAAM,IAAA,IAExC,EAAM,eAAe,CAEzB,CAAC,EACD,EAAG,SAAW,GAAU,CACtB,IAAM,EAAQ,EAAM,SAAS,OAAO,EAAM,MAAM,EAC5C,IAAU,IAAA,IACd,EAAM,OAAO,EAAM,MAAO,EAAO,EAAM,IAAI,CAC7C,CAAC,CACH,EACA,KAAM,YACR,EACF,EAGa,EAA6B,GACvC,EAAuB,EAA4B,IAAuB,CACzE,EAAW,EAAS,QAAS,OAAO,EACpC,IAAM,EAAW,EAAI,UAAY,EAAQ,MAAM,WAAa,GAC5D,MAAO,CACL,KAAM,EAAS,EAAS,EAAM,UAAW,EAAM,IAAW,CACxD,EAAM,SAAS,SAAS,EAAM,EAAQ,EAAI,MAAO,CAAQ,EACzD,EAAM,cAAc,EAAM,CAAM,CAClC,CAAC,EACD,QAAS,EAAI,QACb,eAAgB,EAAI,QAAU,GAAK,IAAA,GACnC,gBAAiB,EAAW,GAAK,IAAA,GACjC,gBAAiB,EAAM,SAAW,GAAK,IAAA,GACvC,SAAU,EAAW,GAAO,IAAA,GAC5B,KAAM,EAAQ,MAAM,MAAQ,EAAM,KAClC,SAAU,EAAM,SAAW,GAAO,EAAQ,MAAM,SAChD,KAAM,QAGN,MAAO,EAAQ,MAAM,OAAS,OAAO,EAAI,KAAK,CAChD,CACF,CACF,ECHA,SAAgB,EACd,EAAoC,CAAC,EACb,CACxB,GAAM,CACJ,WAAW,GACX,cAAc,WACd,WAAW,GACX,WAAW,IACT,EACE,EAAmB,EAAyB,EAC5C,EAAY,EAAmC,CACnD,MAAO,EAAQ,MACf,aAAc,EAAQ,cAAgB,KACtC,SAAU,EAAQ,cAClB,MAAO,EACP,UAAW,CACb,CAAC,EACK,EAAQ,EAAU,MAGlB,EAAW,MAEb,EAAgB,CACd,UAAW,sBACX,KAAM,sBACN,KAAM,aACR,CAAC,EACH,CAAC,CACH,EACA,MAAqB,CACnB,IAAM,EAAY,EAAS,cAAc,EACrC,IAAc,MAAM,EAAqB,EAAW,aAAa,CACvE,CAAC,EAED,IAAM,EAAY,MAAc,EAAgB,EAAU,KAAK,EAAG,CAAC,CAAC,EAC9D,EAAS,GACZ,EAAe,EAAc,IAAqB,CACjD,GACE,EAAM,kBACN,GACA,EAAQ,QAAQ,WAAW,GAC3B,EACA,CACA,EAAiB,EACjB,MACF,CACA,EAAU,YACF,EACN,EAAoB,EAAO,CAAO,CACpC,CACF,CACF,EAEM,EAAK,EAAM,EACX,EAAO,EAAQ,MAAQ,GAAG,EAAG,cAC7B,EAAQ,CACZ,cAAe,EAAU,KACzB,WACA,OACA,cACA,WACA,WACA,WACA,QACF,EAEA,MAAO,CACL,OACA,OAAQ,EAAY,EAAe,CAAC,IAClC,EAAW,EAAO,CAChB,QAAS,IAAU,MAAQ,EAAU,EAAY,CAAK,EACtD,SAAU,GAAY,EAAa,WAAa,GAChD,MAAO,CACT,CAAC,EACH,SAAY,EAAgB,CAAK,EACjC,OACF,CACF,CAMA,SAAgB,EACd,EACS,CACT,OAAO,EAAM,SAAS,EAAc,CAAK,CAAC,CAC5C"}
@@ -0,0 +1,2 @@
1
+ import{o as e,r as t}from"./parts-I0jdefyp.js";import{r as n,s as r}from"./diagnostics-BkQV0MaT.js";import{i}from"./composite-DoFLgUjR.js";function a(a,o){let s=e(o);function c(e,t,n){s.bind(e,t,n),r(s.items(),`panel`)}function l(){let e=a.items(),o=s.items();r(o,`panel`);for(let n of e){let e=o.find(e=>i(e.value,n.value));t(n.node,`aria-controls`,e?.node.id)}for(let r of o){let a=e.find(e=>i(e.value,r.value));n(a?.node),t(r.node,`aria-labelledby`,a?.node.id)}}return{bindPanel:c,sync:l}}export{a as t};
2
+ //# sourceMappingURL=relations-CZpjLINW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relations-CZpjLINW.js","names":[],"sources":["../src/internal/relations.ts"],"sourcesContent":["import { type Composite, sameValue } from \"./composite.ts\";\nimport { assertPanelOwner, assertUniqueValues } from \"./diagnostics.ts\";\nimport { setIdReference } from \"./parts.ts\";\nimport { createPartCollection } from \"./registration.ts\";\n\n/**\n * Pairs composite items with the panels they control.\n *\n * Generated ids cover the ordinary case at render time, but a caller may put\n * its own id on either host, so the committed DOM is the only place the real\n * pairing can be resolved.\n */\nexport function createRelations(\n composite: Composite,\n registrationChanged: () => void,\n) {\n const panels = createPartCollection<unknown>(registrationChanged);\n\n function bindPanel(\n node: HTMLElement,\n signal: AbortSignal,\n value: unknown,\n ): void {\n panels.bind(node, signal, value);\n assertUniqueValues(panels.items(), \"panel\");\n }\n\n function sync(): void {\n const items = composite.items();\n const mountedPanels = panels.items();\n assertUniqueValues(mountedPanels, \"panel\");\n for (const item of items) {\n const panel = mountedPanels.find((entry) =>\n sameValue(entry.value, item.value),\n );\n setIdReference(item.node, \"aria-controls\", panel?.node.id);\n }\n for (const panel of mountedPanels) {\n const owner = items.find((item) => sameValue(item.value, panel.value));\n assertPanelOwner(owner?.node);\n setIdReference(panel.node, \"aria-labelledby\", owner?.node.id);\n }\n }\n\n return { bindPanel, sync };\n}\n"],"mappings":"2IAYA,SAAgB,EACd,EACA,EACA,CACA,IAAM,EAAS,EAA8B,CAAmB,EAEhE,SAAS,EACP,EACA,EACA,EACM,CACN,EAAO,KAAK,EAAM,EAAQ,CAAK,EAC/B,EAAmB,EAAO,MAAM,EAAG,OAAO,CAC5C,CAEA,SAAS,GAAa,CACpB,IAAM,EAAQ,EAAU,MAAM,EACxB,EAAgB,EAAO,MAAM,EACnC,EAAmB,EAAe,OAAO,EACzC,IAAK,IAAM,KAAQ,EAAO,CACxB,IAAM,EAAQ,EAAc,KAAM,GAChC,EAAU,EAAM,MAAO,EAAK,KAAK,CACnC,EACA,EAAe,EAAK,KAAM,gBAAiB,GAAO,KAAK,EAAE,CAC3D,CACA,IAAK,IAAM,KAAS,EAAe,CACjC,IAAM,EAAQ,EAAM,KAAM,GAAS,EAAU,EAAK,MAAO,EAAM,KAAK,CAAC,EACrE,EAAiB,GAAO,IAAI,EAC5B,EAAe,EAAM,KAAM,kBAAmB,GAAO,KAAK,EAAE,CAC9D,CACF,CAEA,MAAO,CAAE,YAAW,MAAK,CAC3B"}
@@ -0,0 +1,2 @@
1
+ import{t as e}from"./changes-DglRuvQa.js";import{a as t,i as n,n as r,r as i,s as a}from"./parts-I0jdefyp.js";import{c as o}from"./diagnostics-BkQV0MaT.js";import{i as s}from"./composite-DoFLgUjR.js";import{n as c}from"./controllable-value-CDbRzlId.js";import{t as l}from"./ids-Cvu36mFL.js";import{t as u}from"./form-reset-B7G3-0OT.js";import{t as d}from"./part-reference-B7zINPUK.js";import{t as f}from"./listbox-B-oe_0c0.js";import{n as p}from"./popover-DXxGM3tO.js";import{createMixin as m,useBeforePaint as h,useId as g,useMemo as _,useStableEvent as v,useState as y}from"@bgub/fig";import{on as b}from"@bgub/fig-dom";const x=m((e,t)=>{let i=t.disabled||e.props.disabled===!0;return{...n(e,{disabled:i,id:t.triggerId}),"aria-activedescendant":t.open&&t.highlighted!==null?t.registry.option(t.highlighted)?.node.id??t.idFor(t.highlighted,`option`):void 0,"aria-haspopup":`listbox`,"aria-readonly":t.readOnly?`true`:void 0,"data-readonly":t.readOnly?``:void 0,disabled:i?!0:void 0,bind:r(e,t.registry,t.bindTrigger),mix:b(`keydown`,e=>{if(e.defaultPrevented||i||e.isComposing||e.altKey||e.ctrlKey||e.metaKey||e.shiftKey)return;let n=t.registry.move(t.highlighted,e.key);if(n!==void 0){e.preventDefault(),t.setHighlighted(n.value,!0),t.open||t.popover.setOpen(!0);return}if(e.key===`Enter`||e.key===` `){if(e.preventDefault(),!t.open){t.popover.setOpen(!0);return}let n=t.registry.option(t.highlighted);n!==void 0&&!t.readOnly&&t.select(n,e);return}let r=t.registry.typeahead(t.highlighted,e.key);r!==void 0&&(t.setHighlighted(r.value,!0),!t.open&&!t.readOnly&&t.select(r,e))}),role:`combobox`}}),S=m((e,t,n)=>[n,x(t)]),C=m((e,t)=>{let n=d(e,`aria-labelledby`,t.triggerId);return{...n.props,bind:r(e,t.registry,(e,r)=>{t.labels.set(e,n),t.registry.bindContainer(e,r)}),mix:[b(`pointerdown`,e=>{t.registry.optionAt(e.target)!==void 0&&e.preventDefault()}),b(`click`,e=>{if(e.defaultPrevented)return;let n=t.registry.optionAt(e.target);n!==void 0&&(n.disabled?e.preventDefault():e.button===0&&t.select(n,e))}),b(`pointermove`,e=>{if(e.pointerType===`touch`)return;let n=t.registry.optionAt(e.target);n!==void 0&&!n.disabled&&t.setHighlighted(n.value,!1)})],role:`listbox`}}),w=m((e,t,n)=>[n,C(t)]),T=m((e,t,n)=>{let i=n.disabled||e.props.disabled===!0;return{"aria-disabled":i?`true`:void 0,"aria-selected":n.selected?`true`:`false`,bind:r(e,t.registry,(e,r)=>t.registry.bindOption(e,r,{disabled:i,textValue:n.textValue,value:n.value})),"data-disabled":i?``:void 0,"data-highlighted":s(t.highlighted,n.value)?``:void 0,"data-selected":n.selected?``:void 0,id:e.props.id??t.idFor(n.value,`option`),role:`option`,type:e.type===`button`?e.props.type??`button`:void 0,tabindex:-1}}),E=m((e,t)=>(o(e,`select hidden input`,`input`),{disabled:t.disabled?!0:void 0,name:e.props.name??t.name,type:`hidden`,value:e.props.value??t.formValue,bind:r(e,t.registry,t.bindHiddenInput)}));function D(n={}){let{disabled:r=!1,readOnly:o=!1}=n,d=n.value!==void 0,m=n.defaultValue!==void 0,b=t(),x=c({value:n.value,defaultValue:n.defaultValue??null,onChange:n.onValueChange,equal:s,reconcile:b}),C=x.value,[D,O]=y(()=>({value:C})),k=_(()=>f(`select`,b),[]),A=p({defaultOpen:n.defaultOpen,id:n.id,onOpenChange:n.onOpenChange,open:n.open}),j=_(()=>new WeakMap,[]),M=_(()=>a(b),[]),N=_(()=>u(x.reset),[]),P=v((t,n)=>{r||o||t.disabled||M.node()?.matches(`:disabled`)||x.request(()=>t.value,e(n,t.node))&&A.setOpen(!1)}),F=_(()=>({open:!1}),[]),I=v((e,t)=>{t!==void 0&&(F.scrollTo=t?{value:e}:void 0),s(D.value,e)?t&&b():O({value:e})});h(t=>{let n=A.open&&!F.open;F.open=A.open;let r=k.options();if(!d&&r.length>0){let t=C===null?void 0:k.option(C);if(C!==null&&t===void 0||C===null&&!m){let t=r.find(e=>!e.disabled),n=t===void 0?null:t.value;if(!s(n,C)){let t=e(null);x.request(()=>n,t)&&I(n,void 0)}}}if(A.open){let e=k.option(D.value)?.disabled===!1?D.value:k.option(C)?.disabled===!1?C:r.find(e=>!e.disabled)?.value??null;I(e,void 0),n&&F.scrollTo===void 0&&(F.scrollTo={value:e});let i=F.scrollTo;i!==void 0&&s(i.value,e)?queueMicrotask(()=>{t.aborted||F.scrollTo!==i||(F.scrollTo=void 0,k.option(e)?.node.scrollIntoView?.({block:`nearest`,inline:`nearest`}))}):F.scrollTo=void 0}else F.scrollTo=void 0;let a=M.node(),o=k.containerNode();if(a!==null&&o!==null){j.get(o)?.sync(o,a.id);let e=A.open&&D.value!==null?k.option(D.value)?.node.id??R(D.value,`option`):void 0;i(a,`aria-activedescendant`,e)}});let L=`${g()}-trigger`,R=l(),z=n.getFormValue??String,B={labels:j,bindHiddenInput:N.bind,bindTrigger:M.bind,disabled:r,formValue:C===null?``:z(C),highlighted:D.value,idFor:R,name:n.name,open:A.open,popover:A,readOnly:o,registry:k,select:P,setHighlighted:I,triggerId:L};return{hiddenInput:()=>E(B),open:A.open,option:(e,t={})=>T(B,{disabled:t.disabled===!0,selected:C!==null&&s(C,e),textValue:t.textValue,value:e}),popup:()=>w(B,A.popover()),setOpen:e=>A.setOpen(e),trigger:()=>S(B,A.trigger()),value:C}}function O(e){return e.children(D(e))}export{O as Select,D as useSelect};
2
+ //# sourceMappingURL=select.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select.js","names":[],"sources":["../src/select/select.tsx"],"sourcesContent":["import {\n createPartReference,\n type PartReference,\n} from \"../internal/part-reference.ts\";\nimport {\n createMixin,\n type FigNode,\n type MixinContext,\n type MixinDescriptor,\n useBeforePaint,\n useId,\n useMemo,\n useStableEvent,\n useState,\n} from \"@bgub/fig\";\nimport { on } from \"@bgub/fig-dom\";\nimport {\n type ChangeDetails,\n createChangeDetails,\n} from \"../internal/changes.ts\";\nimport { useControllableValue } from \"../internal/controllable-value.ts\";\nimport { sameValue } from \"../internal/composite.ts\";\nimport { expectHost } from \"../internal/diagnostics.ts\";\nimport { createFormReset } from \"../internal/form-reset.ts\";\nimport { usePartIds } from \"../internal/ids.ts\";\nimport { createListbox, type ListboxOption } from \"../internal/listbox.ts\";\nimport { bindPart, setIdReference, triggerProps } from \"../internal/parts.ts\";\nimport { createPartSlot } from \"../internal/registration.ts\";\nimport { useRegistrationReconcile } from \"../internal/reconcile.ts\";\nimport type {\n PopoverOpenChangeDetails,\n PopoverOpenChangeHandler,\n PopoverParts,\n} from \"../popover/popover.tsx\";\nimport { usePopover } from \"../popover/popover.tsx\";\n\nexport type SelectOpenChangeDetails = PopoverOpenChangeDetails;\nexport type SelectOpenChangeHandler = PopoverOpenChangeHandler;\nexport type SelectValueChangeDetails = ChangeDetails;\n\nexport type SelectValueChangeHandler<Value = unknown> = (\n value: Value | null,\n details: SelectValueChangeDetails,\n signal: AbortSignal,\n) => void;\n\nexport interface SelectOptionOptions {\n disabled?: boolean;\n /** Text used for typeahead when it differs from the host's text content. */\n textValue?: string;\n}\n\nexport interface SelectOptions<Value = unknown> {\n defaultOpen?: boolean;\n defaultValue?: Value | null;\n disabled?: boolean;\n /** Converts the selected identity to its submitted value. Defaults to String. */\n getFormValue?: (value: Value) => string;\n id?: string;\n name?: string;\n onOpenChange?: SelectOpenChangeHandler;\n onValueChange?: SelectValueChangeHandler<Value>;\n open?: boolean;\n readOnly?: boolean;\n value?: Value | null;\n}\n\nexport interface SelectParts<Value = unknown> {\n readonly open: boolean;\n readonly value: Value | null;\n hiddenInput(): MixinDescriptor;\n option(value: Value, options?: SelectOptionOptions): MixinDescriptor;\n popup(): MixinDescriptor;\n setOpen(open: boolean): void;\n trigger(): MixinDescriptor;\n}\n\nexport interface SelectProps<Value = unknown> extends SelectOptions<Value> {\n children: (select: SelectParts<Value>) => FigNode;\n}\n\ntype SelectRegistry = ReturnType<typeof createListbox>;\n\ninterface SelectState {\n readonly labels: WeakMap<HTMLElement, PartReference>;\n readonly bindHiddenInput: (node: HTMLElement, signal: AbortSignal) => void;\n readonly bindTrigger: (node: HTMLElement, signal: AbortSignal) => void;\n readonly disabled: boolean;\n readonly formValue: string;\n readonly highlighted: unknown;\n readonly idFor: (value: unknown, part: string) => string;\n readonly name: string | undefined;\n readonly open: boolean;\n readonly popover: PopoverParts;\n readonly readOnly: boolean;\n readonly registry: SelectRegistry;\n readonly select: (option: ListboxOption, event: Event) => void;\n readonly setHighlighted: (value: unknown, scroll: boolean) => void;\n readonly triggerId: string;\n}\n\ninterface SelectOptionState {\n readonly disabled: boolean;\n readonly selected: boolean;\n readonly textValue: string | undefined;\n readonly value: unknown;\n}\n\nconst selectTriggerBehavior = /* @__PURE__ */ createMixin(\n (context: MixinContext, state: SelectState) => {\n const disabled = state.disabled || context.props.disabled === true;\n return {\n ...triggerProps(context, { disabled, id: state.triggerId }),\n \"aria-activedescendant\":\n state.open && state.highlighted !== null\n ? (state.registry.option(state.highlighted)?.node.id ??\n state.idFor(state.highlighted, \"option\"))\n : undefined,\n \"aria-haspopup\": \"listbox\",\n \"aria-readonly\": state.readOnly ? \"true\" : undefined,\n \"data-readonly\": state.readOnly ? \"\" : undefined,\n disabled: disabled ? true : undefined,\n bind: bindPart(context, state.registry, state.bindTrigger),\n mix: on(\"keydown\", (event) => {\n if (event.defaultPrevented) return;\n if (\n disabled ||\n event.isComposing ||\n event.altKey ||\n event.ctrlKey ||\n event.metaKey ||\n event.shiftKey\n ) {\n return;\n }\n const moved = state.registry.move(state.highlighted, event.key);\n if (moved !== undefined) {\n event.preventDefault();\n state.setHighlighted(moved.value, true);\n if (!state.open) state.popover.setOpen(true);\n return;\n }\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n if (!state.open) {\n state.popover.setOpen(true);\n return;\n }\n const highlighted = state.registry.option(state.highlighted);\n if (highlighted !== undefined && !state.readOnly) {\n state.select(highlighted, event);\n }\n return;\n }\n const match = state.registry.typeahead(state.highlighted, event.key);\n if (match === undefined) return;\n state.setHighlighted(match.value, true);\n if (!state.open && !state.readOnly) state.select(match, event);\n }),\n role: \"combobox\",\n };\n },\n);\n\nconst selectTriggerMixin = /* @__PURE__ */ createMixin(\n (\n _context: MixinContext,\n state: SelectState,\n popoverTrigger: MixinDescriptor,\n ) => [popoverTrigger, selectTriggerBehavior(state)],\n);\n\nconst selectPopupBehavior = /* @__PURE__ */ createMixin(\n (context: MixinContext, state: SelectState) => {\n const label = createPartReference(\n context,\n \"aria-labelledby\",\n state.triggerId,\n );\n return {\n ...label.props,\n bind: bindPart(context, state.registry, (node, signal) => {\n state.labels.set(node, label);\n state.registry.bindContainer(node, signal);\n }),\n mix: [\n on(\"pointerdown\", (event) => {\n if (state.registry.optionAt(event.target) !== undefined) {\n event.preventDefault();\n }\n }),\n on(\"click\", (event) => {\n if (event.defaultPrevented) return;\n const option = state.registry.optionAt(event.target);\n if (option === undefined) return;\n if (option.disabled) event.preventDefault();\n else if (event.button === 0) state.select(option, event);\n }),\n on(\"pointermove\", (event) => {\n if (event.pointerType === \"touch\") return;\n const option = state.registry.optionAt(event.target);\n if (option !== undefined && !option.disabled) {\n state.setHighlighted(option.value, false);\n }\n }),\n ],\n role: \"listbox\",\n };\n },\n);\n\nconst selectPopupMixin = /* @__PURE__ */ createMixin(\n (\n _context: MixinContext,\n state: SelectState,\n popoverPopup: MixinDescriptor,\n ) => [popoverPopup, selectPopupBehavior(state)],\n);\n\nconst selectOptionMixin = /* @__PURE__ */ createMixin(\n (context: MixinContext, state: SelectState, own: SelectOptionState) => {\n const disabled = own.disabled || context.props.disabled === true;\n return {\n \"aria-disabled\": disabled ? \"true\" : undefined,\n \"aria-selected\": own.selected ? \"true\" : \"false\",\n bind: bindPart(context, state.registry, (node, signal) =>\n state.registry.bindOption(node, signal, {\n disabled,\n textValue: own.textValue,\n value: own.value,\n }),\n ),\n \"data-disabled\": disabled ? \"\" : undefined,\n \"data-highlighted\": sameValue(state.highlighted, own.value)\n ? \"\"\n : undefined,\n \"data-selected\": own.selected ? \"\" : undefined,\n id: context.props.id ?? state.idFor(own.value, \"option\"),\n role: \"option\",\n type:\n context.type === \"button\"\n ? (context.props.type ?? \"button\")\n : undefined,\n tabindex: -1,\n };\n },\n);\n\nconst selectHiddenInputMixin = /* @__PURE__ */ createMixin(\n (context: MixinContext, state: SelectState) => {\n expectHost(context, \"select hidden input\", \"input\");\n return {\n disabled: state.disabled ? true : undefined,\n name: context.props.name ?? state.name,\n type: \"hidden\",\n value: context.props.value ?? state.formValue,\n bind: bindPart(context, state.registry, state.bindHiddenInput),\n };\n },\n);\n\n/** Coordinates a custom select whose trigger, popup, and options are authored. */\nexport function useSelect<Value = unknown>(\n options: SelectOptions<Value> = {},\n): SelectParts<Value> {\n const { disabled = false, readOnly = false } = options;\n const controlled = options.value !== undefined;\n const explicitDefault = options.defaultValue !== undefined;\n const registrationChanged = useRegistrationReconcile();\n const selection = useControllableValue<Value | null>({\n value: options.value,\n defaultValue: options.defaultValue ?? null,\n onChange: options.onValueChange,\n equal: sameValue,\n reconcile: registrationChanged,\n });\n const value = selection.value;\n const [highlighted, setHighlightedState] = useState<{\n readonly value: unknown;\n }>(() => ({ value }));\n const registry = useMemo(\n () => createListbox(\"select\", registrationChanged),\n [],\n );\n const popover = usePopover({\n defaultOpen: options.defaultOpen,\n id: options.id,\n onOpenChange: options.onOpenChange,\n open: options.open,\n });\n const labels = useMemo(() => new WeakMap<HTMLElement, PartReference>(), []);\n const trigger = useMemo(() => createPartSlot(registrationChanged), []);\n const formReset = useMemo(() => createFormReset(selection.reset), []);\n const select = useStableEvent((option: ListboxOption, event: Event) => {\n if (\n disabled ||\n readOnly ||\n option.disabled ||\n trigger.node()?.matches(\":disabled\")\n )\n return;\n if (\n !selection.request(\n () => option.value as Value,\n createChangeDetails(event, option.node),\n )\n )\n return;\n popover.setOpen(false);\n });\n const navigation = useMemo<{ open: boolean; scrollTo?: { value: unknown } }>(\n () => ({ open: false }),\n [],\n );\n const setHighlighted = useStableEvent(\n (next: unknown, scroll: boolean | undefined) => {\n if (scroll !== undefined)\n navigation.scrollTo = scroll ? { value: next } : undefined;\n if (!sameValue(highlighted.value, next)) {\n setHighlightedState({ value: next });\n } else if (scroll) registrationChanged();\n },\n );\n\n useBeforePaint((signal) => {\n const opening = popover.open && !navigation.open;\n navigation.open = popover.open;\n const mounted = registry.options();\n if (!controlled && mounted.length > 0) {\n const selected = value === null ? undefined : registry.option(value);\n const shouldRepair =\n (value !== null && selected === undefined) ||\n (value === null && !explicitDefault);\n if (shouldRepair) {\n const fallback = mounted.find((entry) => !entry.disabled);\n const next = fallback === undefined ? null : fallback.value;\n if (!sameValue(next, value)) {\n const details = createChangeDetails(null);\n if (selection.request(() => next as Value | null, details)) {\n setHighlighted(next, undefined);\n }\n }\n }\n }\n if (popover.open) {\n const next =\n registry.option(highlighted.value)?.disabled === false\n ? highlighted.value\n : registry.option(value)?.disabled === false\n ? value\n : (mounted.find((entry) => !entry.disabled)?.value ?? null);\n setHighlighted(next, undefined);\n // Wait until every layout hook (including optional positioning) has\n // applied its size constraints. Keep intent across interrupted commits.\n if (opening && navigation.scrollTo === undefined)\n navigation.scrollTo = { value: next };\n const scrollTo = navigation.scrollTo;\n if (scrollTo !== undefined && sameValue(scrollTo.value, next)) {\n queueMicrotask(() => {\n if (signal.aborted || navigation.scrollTo !== scrollTo) return;\n navigation.scrollTo = undefined;\n registry\n .option(next)\n ?.node.scrollIntoView?.({ block: \"nearest\", inline: \"nearest\" });\n });\n } else navigation.scrollTo = undefined;\n } else navigation.scrollTo = undefined;\n const triggerNode = trigger.node();\n const popupNode = registry.containerNode();\n if (triggerNode !== null && popupNode !== null) {\n labels.get(popupNode)?.sync(popupNode, triggerNode.id);\n const optionId =\n popover.open && highlighted.value !== null\n ? (registry.option(highlighted.value)?.node.id ??\n idFor(highlighted.value, \"option\"))\n : undefined;\n setIdReference(triggerNode, \"aria-activedescendant\", optionId);\n }\n });\n\n const id = useId();\n const triggerId = `${id}-trigger`;\n const idFor = usePartIds();\n const getFormValue = options.getFormValue ?? String;\n const state: SelectState = {\n labels,\n bindHiddenInput: formReset.bind,\n bindTrigger: trigger.bind,\n disabled,\n formValue: value === null ? \"\" : getFormValue(value),\n highlighted: highlighted.value,\n idFor,\n name: options.name,\n open: popover.open,\n popover,\n readOnly,\n registry,\n select,\n setHighlighted,\n triggerId,\n };\n\n return {\n hiddenInput: () => selectHiddenInputMixin(state),\n open: popover.open,\n option: (optionValue, optionOptions = {}) =>\n selectOptionMixin(state, {\n disabled: optionOptions.disabled === true,\n selected: value !== null && sameValue(value, optionValue),\n textValue: optionOptions.textValue,\n value: optionValue,\n }),\n popup: () => selectPopupMixin(state, popover.popover()),\n setOpen: (open) => popover.setOpen(open),\n trigger: () => selectTriggerMixin(state, popover.trigger()),\n value,\n };\n}\n\n/** {@link useSelect} as a render-callback component. */\nexport function Select<Value = unknown>(props: SelectProps<Value>): FigNode {\n return props.children(useSelect(props));\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{t as e}from"./changes-DglRuvQa.js";import{a as t,n,o as r}from"./parts-I0jdefyp.js";import{c as i,i as a,n as o}from"./diagnostics-BkQV0MaT.js";import{n as s}from"./controllable-value-CDbRzlId.js";import{t as c}from"./form-reset-B7G3-0OT.js";import{createMixin as l,useBeforePaint as u,useMemo as d,useStableEvent as f}from"@bgub/fig";import{on as p}from"@bgub/fig-dom";const m=new Set([`ArrowLeft`,`ArrowRight`,`ArrowUp`,`ArrowDown`,`Home`,`End`,`PageUp`,`PageDown`]),h=l((e,t)=>{i(e,`slider control`,`input`);let r=t.readOnly||e.props.readonly===!0;return{bind:n(e,t.owner,t.bind),type:`range`,min:t.min,max:t.max,step:t.step,value:t.value,name:e.props.name??t.name,disabled:t.disabled?!0:e.props.disabled,"data-disabled":t.disabled||e.props.disabled===!0?``:void 0,"data-readonly":r?``:void 0,"aria-readonly":r?`true`:void 0,readonly:r?!0:void 0,mix:[p(`keydown`,e=>{r&&m.has(e.key)&&e.preventDefault()}),p(`pointerdown`,e=>{!r||e.defaultPrevented||e.button!==0||(e.preventDefault(),e.currentTarget instanceof HTMLInputElement&&!e.currentTarget.matches(`:disabled`)&&e.currentTarget.focus({preventScroll:!0}))}),p(`input`,e=>{e.currentTarget instanceof HTMLInputElement&&t.input(e.currentTarget,e,!1)}),p(`change`,e=>{e.currentTarget instanceof HTMLInputElement&&t.input(e.currentTarget,e,!0)})]}});function g(n={}){let i=n.min!==void 0&&Number.isFinite(n.min)?n.min:0,l=n.max!==void 0&&Number.isFinite(n.max)?n.max:100,p=n.step===`any`?`any`:n.step!==void 0&&Number.isFinite(n.step)&&n.step>0?n.step:1,m=n.value!==void 0,g=t(),_=s({value:n.value,defaultValue:n.defaultValue,onChange:(e,t,r)=>{n.onValueChange?.(v(e,i,l,p),t,r)},equal:(e,t)=>v(e,i,l,p)===v(t,i,l,p),reconcile:g}),y=v(_.value,i,l,p),b=f(()=>v(_.current(),i,l,p)),x=d(()=>r(g),[]),S=d(()=>({pending:void 0}),[]),C=f((e,t,r)=>{n.onValueCommit?.(e,t,r)}),w=f((t,n,r)=>_.request(()=>t,e(n,r))),T=f((e,t,r)=>{if(t.defaultPrevented||e.matches(`:disabled`)||e.readOnly||n.readOnly){e.value=String(b()),r&&(S.pending=void 0);return}let a=v(e.valueAsNumber,i,l,p),o=a!==b(),s=r&&S.pending===a||w(a,t,e);s&&o&&(S.pending=a),s||(e.value=String(b())),(!s||m)&&g(),r&&(s&&S.pending===a?(S.pending=void 0,C(a,{event:t,trigger:e})):S.pending=void 0)}),E=f(()=>{S.pending=void 0,_.reset(),g()}),D=d(()=>c(E),[]);u(()=>{let e=x.items();a(e,`slider control`);for(let{node:t}of e)o(t)});let O={owner:x,value:y,min:i,max:l,step:p,disabled:n.disabled===!0,readOnly:n.readOnly===!0,name:n.name,bind:(e,t)=>{x.bind(e,t,void 0),D.bind(e,t)},input:T};return{value:y,control:()=>h(O),setValue:f(e=>{let t=v(e,i,l,p),n=t!==b();w(t,null)&&n&&(S.pending=void 0),m&&g()})}}function _(e){return e.children(g(e))}function v(e,t,n,r){if(n<=t)return t;let i=t/2+n/2,a=Math.max(t,Math.min(n,e!==void 0&&Number.isFinite(e)?e:i));if(r===`any`)return a;let o=a-t,s=Number.isFinite(o)?o/r:a/r-t/r;if(!Number.isSafeInteger(Math.floor(s)))return a;let c=Math.round(s+Math.min(1e-7,2**-52*Math.abs(s))),l=Math.min(r*1e-7,2**-52*Math.max(Math.abs(t),Math.abs(n),Math.abs(r))*2);b(t,r,c)-n>l&&--c;let u=b(t,r,c),d=Math.max(y(t),y(r)),f=d<=100?Number(u.toFixed(d)):u;return Math.max(t,Math.min(n,f))}function y(e){let[t=``,n=`0`]=String(e).split(`e`);return Math.max(0,(t.split(`.`)[1]?.length??0)-Number(n))}function b(e,t,n){let r=n*t;return Number.isFinite(r)?e+r:(e/t+n)*t}export{_ as Slider,g as useSlider};
2
+ //# sourceMappingURL=slider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slider.js","names":[],"sources":["../src/slider/slider.tsx"],"sourcesContent":["import {\n createMixin,\n type FigNode,\n type MixinDescriptor,\n useBeforePaint,\n useMemo,\n useStableEvent,\n} from \"@bgub/fig\";\nimport { on } from \"@bgub/fig-dom\";\nimport {\n type ChangeDetails,\n createChangeDetails,\n} from \"../internal/changes.ts\";\nimport {\n assertControlLabel,\n assertSinglePart,\n expectHost,\n} from \"../internal/diagnostics.ts\";\nimport { useControllableValue } from \"../internal/controllable-value.ts\";\nimport { createFormReset } from \"../internal/form-reset.ts\";\nimport { bindPart } from \"../internal/parts.ts\";\nimport { createPartCollection } from \"../internal/registration.ts\";\nimport { useRegistrationReconcile } from \"../internal/reconcile.ts\";\n\nexport type SliderValueChangeDetails = ChangeDetails;\nexport type SliderValueCommitDetails = Pick<ChangeDetails, \"event\" | \"trigger\">;\nexport type SliderValueChangeHandler = (\n value: number,\n details: SliderValueChangeDetails,\n signal: AbortSignal,\n) => void;\nexport type SliderValueCommitHandler = (\n value: number,\n details: SliderValueCommitDetails,\n signal: AbortSignal,\n) => void;\n\nexport interface SliderOptions {\n value?: number;\n /** Defaults to the native range midpoint, snapped to step. */\n defaultValue?: number;\n min?: number;\n max?: number;\n step?: number | \"any\";\n disabled?: boolean;\n readOnly?: boolean;\n name?: string;\n onValueChange?: SliderValueChangeHandler;\n /** Reports a native change after accepted input; does not undo earlier input. */\n onValueCommit?: SliderValueCommitHandler;\n}\n\nexport interface SliderParts {\n readonly value: number;\n /** Applies to one native input range. Label it with a label or ARIA. */\n control(): MixinDescriptor;\n setValue(value: number): void;\n}\n\nexport interface SliderProps extends SliderOptions {\n children: (slider: SliderParts) => FigNode;\n}\n\ninterface SliderState {\n readonly owner: object;\n readonly value: number;\n readonly min: number;\n readonly max: number;\n readonly step: number | \"any\";\n readonly disabled: boolean;\n readonly readOnly: boolean;\n readonly name: string | undefined;\n readonly bind: (node: HTMLElement, signal: AbortSignal) => void;\n readonly input: (\n node: HTMLInputElement,\n event: Event,\n commit: boolean,\n ) => void;\n}\n\nconst mutatingKeys = new Set([\n \"ArrowLeft\",\n \"ArrowRight\",\n \"ArrowUp\",\n \"ArrowDown\",\n \"Home\",\n \"End\",\n \"PageUp\",\n \"PageDown\",\n]);\n\nconst sliderControl = /* @__PURE__ */ createMixin(\n (context, state: SliderState) => {\n expectHost(context, \"slider control\", \"input\");\n const readOnly = state.readOnly || context.props.readonly === true;\n return {\n bind: bindPart(context, state.owner, state.bind),\n type: \"range\",\n min: state.min,\n max: state.max,\n step: state.step,\n value: state.value,\n name: context.props.name ?? state.name,\n disabled: state.disabled ? true : context.props.disabled,\n \"data-disabled\":\n state.disabled || context.props.disabled === true ? \"\" : undefined,\n \"data-readonly\": readOnly ? \"\" : undefined,\n \"aria-readonly\": readOnly ? \"true\" : undefined,\n readonly: readOnly ? true : undefined,\n mix: [\n on(\"keydown\", (event) => {\n if (readOnly && mutatingKeys.has(event.key)) event.preventDefault();\n }),\n on(\"pointerdown\", (event) => {\n if (!readOnly || event.defaultPrevented || event.button !== 0) return;\n event.preventDefault();\n if (\n event.currentTarget instanceof HTMLInputElement &&\n !event.currentTarget.matches(\":disabled\")\n ) {\n event.currentTarget.focus({ preventScroll: true });\n }\n }),\n on(\"input\", (event) => {\n if (event.currentTarget instanceof HTMLInputElement)\n state.input(event.currentTarget, event, false);\n }),\n on(\"change\", (event) => {\n if (event.currentTarget instanceof HTMLInputElement)\n state.input(event.currentTarget, event, true);\n }),\n ],\n };\n },\n);\n\n/** Single-thumb range using native pointer, keyboard, focus and form behavior. */\nexport function useSlider(options: SliderOptions = {}): SliderParts {\n const min =\n options.min !== undefined && Number.isFinite(options.min) ? options.min : 0;\n const max =\n options.max !== undefined && Number.isFinite(options.max)\n ? options.max\n : 100;\n const step =\n options.step === \"any\"\n ? \"any\"\n : options.step !== undefined &&\n Number.isFinite(options.step) &&\n options.step > 0\n ? options.step\n : 1;\n const controlled = options.value !== undefined;\n const requestReconcile = useRegistrationReconcile();\n const stateValue = useControllableValue<number | undefined>({\n value: options.value,\n defaultValue: options.defaultValue,\n onChange: (next, details, signal) => {\n options.onValueChange?.(sanitize(next, min, max, step), details, signal);\n },\n equal: (left, right) =>\n sanitize(left, min, max, step) === sanitize(right, min, max, step),\n reconcile: requestReconcile,\n });\n const value = sanitize(stateValue.value, min, max, step);\n const currentValue = useStableEvent(() =>\n sanitize(stateValue.current(), min, max, step),\n );\n const inputs = useMemo(\n () => createPartCollection<undefined>(requestReconcile),\n [],\n );\n const tracker = useMemo(\n () => ({ pending: undefined as number | undefined }),\n [],\n );\n const emitCommit = useStableEvent(\n (next: number, details: SliderValueCommitDetails, signal: AbortSignal) => {\n options.onValueCommit?.(next, details, signal);\n },\n );\n const requestValue = useStableEvent(\n (next: number, event: Event | null, node?: HTMLInputElement): boolean => {\n return stateValue.request(() => next, createChangeDetails(event, node));\n },\n );\n const input = useStableEvent(\n (node: HTMLInputElement, event: Event, commit: boolean) => {\n if (\n event.defaultPrevented ||\n node.matches(\":disabled\") ||\n node.readOnly ||\n options.readOnly\n ) {\n node.value = String(currentValue());\n if (commit) tracker.pending = undefined;\n return;\n }\n const next = sanitize(node.valueAsNumber, min, max, step);\n const changed = next !== currentValue();\n // Native input and change may arrive before the owner rerenders.\n // The input request has already been accepted; committing it is not\n // a second value change.\n const accepted =\n (commit && tracker.pending === next) || requestValue(next, event, node);\n if (accepted && changed) tracker.pending = next;\n if (!accepted) node.value = String(currentValue());\n // Reconciliation restores refused controlled input after the owner has\n // had a chance to commit its update, without corrupting a native change\n // event following input in the same batch.\n if (!accepted || controlled) requestReconcile();\n if (commit) {\n if (accepted && tracker.pending === next) {\n tracker.pending = undefined;\n emitCommit(next, { event, trigger: node });\n } else tracker.pending = undefined;\n }\n },\n );\n const reset = useStableEvent(() => {\n tracker.pending = undefined;\n stateValue.reset();\n requestReconcile();\n });\n const formReset = useMemo(() => createFormReset(reset), []);\n useBeforePaint(() => {\n const controls = inputs.items();\n assertSinglePart(controls, \"slider control\");\n for (const { node } of controls) assertControlLabel(node);\n });\n\n const state: SliderState = {\n owner: inputs,\n value,\n min,\n max,\n step,\n disabled: options.disabled === true,\n readOnly: options.readOnly === true,\n name: options.name,\n bind: (node, signal) => {\n inputs.bind(node, signal, undefined);\n formReset.bind(node, signal);\n },\n input,\n };\n const setValue = useStableEvent((next: number) => {\n const normalized = sanitize(next, min, max, step);\n const changed = normalized !== currentValue();\n if (requestValue(normalized, null) && changed) tracker.pending = undefined;\n if (controlled) requestReconcile();\n });\n return { value, control: () => sliderControl(state), setValue };\n}\n\nexport function Slider(props: SliderProps): FigNode {\n return props.children(useSlider(props));\n}\n\n/** Native range normalization with an explicit min (also the step base). */\nfunction sanitize(\n value: number | undefined,\n min: number,\n max: number,\n step: number | \"any\",\n): number {\n if (max <= min) return min;\n const midpoint = min / 2 + max / 2;\n const finiteValue =\n value !== undefined && Number.isFinite(value) ? value : midpoint;\n const clamped = Math.max(min, Math.min(max, finiteValue));\n if (step === \"any\") return clamped;\n const distance = clamped - min;\n const steps = Number.isFinite(distance)\n ? distance / step\n : clamped / step - min / step;\n // Beyond safe integer precision the step is smaller than representable changes.\n if (!Number.isSafeInteger(Math.floor(steps))) return clamped;\n let count = Math.round(\n steps + Math.min(1e-7, Number.EPSILON * Math.abs(steps)),\n );\n const tolerance = Math.min(\n step * 1e-7,\n Number.EPSILON * Math.max(Math.abs(min), Math.abs(max), Math.abs(step)) * 2,\n );\n if (stepValue(min, step, count) - max > tolerance) count -= 1;\n const snapped = stepValue(min, step, count);\n // Remove decimal arithmetic noise without losing significant fractional digits.\n const places = Math.max(decimalPlaces(min), decimalPlaces(step));\n const rounded = places <= 100 ? Number(snapped.toFixed(places)) : snapped;\n return Math.max(min, Math.min(max, rounded));\n}\n\nfunction decimalPlaces(value: number): number {\n const [mantissa = \"\", exponent = \"0\"] = String(value).split(\"e\");\n return Math.max(0, (mantissa.split(\".\")[1]?.length ?? 0) - Number(exponent));\n}\n\n/** Avoid overflowing the intermediate product when min and max straddle zero. */\nfunction stepValue(min: number, step: number, count: number): number {\n const offset = count * step;\n return Number.isFinite(offset) ? min + offset : (min / step + count) * step;\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{t as e}from"./toggle-CIbzprg5.js";function t(t={}){return e(t,`switch`)}function n(e){return e.children(t(e))}export{n as Switch,t as useSwitch};
2
+ //# sourceMappingURL=switch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"switch.js","names":[],"sources":["../src/switch/switch.tsx"],"sourcesContent":["import type { FigNode } from \"@bgub/fig\";\nimport type {\n CheckedChangeDetails,\n CheckedChangeHandler,\n ToggleControlOptions,\n ToggleControlParts,\n} from \"../internal/toggle.ts\";\nimport { useToggleControl } from \"../internal/toggle.ts\";\n\nexport type SwitchCheckedChangeDetails = CheckedChangeDetails;\n\nexport type SwitchCheckedChangeHandler = CheckedChangeHandler;\n\n/** A switch is on or off, so it has no indeterminate state. */\nexport type SwitchOptions = Omit<ToggleControlOptions, \"indeterminate\">;\n\nexport type SwitchParts = ToggleControlParts;\n\nexport interface SwitchProps extends SwitchOptions {\n children: (control: SwitchParts) => FigNode;\n}\n\n/**\n * Coordinates one switch: a native checkbox carrying `role=\"switch\"`, so the\n * platform still owns toggling, focus, Space, and form submission while\n * assistive technology announces it as on or off rather than checked.\n */\nexport function useSwitch(options: SwitchOptions = {}): SwitchParts {\n return useToggleControl(options, \"switch\");\n}\n\n/**\n * {@link useSwitch} as a component, for a switch that is not already a\n * component of its own.\n */\nexport function Switch(props: SwitchProps): FigNode {\n return props.children(useSwitch(props));\n}\n"],"mappings":"yCA2BA,SAAgB,EAAU,EAAyB,CAAC,EAAgB,CAClE,OAAO,EAAiB,EAAS,QAAQ,CAC3C,CAMA,SAAgB,EAAO,EAA6B,CAClD,OAAO,EAAM,SAAS,EAAU,CAAK,CAAC,CACxC"}
@@ -0,0 +1,2 @@
1
+ import{a as e,n as t,s as n}from"../parts-I0jdefyp.js";import{createMixin as r,useBeforePaint as i,useMemo as a}from"@bgub/fig";const o=r((e,n)=>({bind:t(e,n,n.bindList)})),s=r((e,n)=>({bind:t(e,n,n.bindIndicator),hidden:!0,role:`presentation`}));function c(){let t=e(),n=a(()=>u(t),[]);return i(()=>{n.sync()}),{indicator:()=>s(n),list:()=>o(n)}}function l(e){return e.children(c())}function u(e){let t=n(e),r=n(e),i=null,a=[];function o(e,n){t.bind(e,n)&&n.addEventListener(`abort`,()=>{t.node()===null&&(i?.disconnect(),i=null)},{once:!0})}function s(){let e=r.node();if(t.node()===null){i?.disconnect(),i=null;return}if(c(),typeof ResizeObserver>`u`)return;i===null&&(i=new ResizeObserver(c),a=[]);let n=d(e),o=e===null?n:[e,...n];if(o.length!==a.length||o.some((e,t)=>e!==a[t])){i.disconnect();for(let e of o)i.observe(e);a=o}}function c(){let e=t.node();if(e===null)return;let n=r.node();p(e,n,d(n).find(e=>e.getAttribute(`aria-selected`)===`true`)??null)}return{bindIndicator:o,bindList:r.bind,sync:s}}function d(e){return e===null?[]:[...e.querySelectorAll(`[role="tab"]`)].filter(t=>t.closest(`[role="tablist"]`)===e)}const f=[`bottom`,`height`,`left`,`right`,`top`,`width`];function p(e,t,n){let r=e.style;if(t===null||n===null){e.hidden=!0;for(let e of f)r.removeProperty(`--active-tab-${e}`);return}let i=n.getBoundingClientRect(),a=t.getBoundingClientRect(),o=getComputedStyle(n),s=getComputedStyle(t),c=m(o,`width`,i.width),l=m(o,`height`,i.height),u=t.offsetWidth||m(s,`width`,a.width),d=t.offsetHeight||m(s,`height`,a.height),p=u>0?a.width/u:1,h=d>0?a.height/d:1,g=p?(i.left-a.left)/p+t.scrollLeft-t.clientLeft:n.offsetLeft,_=h?(i.top-a.top)/h+t.scrollTop-t.clientTop:n.offsetTop,v={bottom:t.scrollHeight-_-l,height:l,left:g,right:t.scrollWidth-g-c,top:_,width:c};for(let e of f)r.setProperty(`--active-tab-${e}`,`${v[e]}px`);e.hidden=!(c>0&&l>0)}function m(e,t,n){let r=parseFloat(e[t]);return Number.isFinite(r)?e.boxSizing===`border-box`?r:(t===`width`?[`Left`,`Right`]:[`Top`,`Bottom`]).reduce((t,n)=>t+(parseFloat(e[`padding${n}`])||0)+(parseFloat(e[`border${n}Width`])||0),r):n}export{l as TabsIndicator,c as useTabsIndicator};
2
+ //# sourceMappingURL=indicator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indicator.js","names":[],"sources":["../../src/tabs/indicator.ts"],"sourcesContent":["import {\n createMixin,\n type FigNode,\n type MixinContext,\n type MixinDescriptor,\n useBeforePaint,\n useMemo,\n} from \"@bgub/fig\";\nimport { bindPart } from \"../internal/parts.ts\";\nimport { createPartSlot } from \"../internal/registration.ts\";\nimport { useRegistrationReconcile } from \"../internal/reconcile.ts\";\n\nexport interface TabsIndicatorParts {\n indicator(): MixinDescriptor;\n /** Composes with the matching `tabs.list()` descriptor. */\n list(): MixinDescriptor;\n}\n\nexport interface TabsIndicatorProps {\n children: (indicator: TabsIndicatorParts) => FigNode;\n}\n\ntype TabsIndicatorRegistry = ReturnType<typeof createTabsIndicatorRegistry>;\n\nconst indicatorListMixin = /* @__PURE__ */ createMixin(\n (context: MixinContext, registry: TabsIndicatorRegistry) => ({\n bind: bindPart(context, registry, registry.bindList),\n }),\n);\n\nconst indicatorMixin = /* @__PURE__ */ createMixin(\n (context: MixinContext, registry: TabsIndicatorRegistry) => ({\n bind: bindPart(context, registry, registry.bindIndicator),\n hidden: true,\n role: \"presentation\",\n }),\n);\n\n/**\n * Optional visual behavior for a tabs widget. The core tabs entry owns only\n * selection and accessibility; this hook measures the active tab and publishes\n * its box as `--active-tab-*` CSS properties.\n */\nexport function useTabsIndicator(): TabsIndicatorParts {\n const requestReconcile = useRegistrationReconcile();\n const registry = useMemo(\n () => createTabsIndicatorRegistry(requestReconcile),\n [],\n );\n\n useBeforePaint(() => {\n registry.sync();\n });\n\n return {\n indicator: () => indicatorMixin(registry),\n list: () => indicatorListMixin(registry),\n };\n}\n\n/** {@link useTabsIndicator} as a render-callback component. */\nexport function TabsIndicator(props: TabsIndicatorProps): FigNode {\n return props.children(useTabsIndicator());\n}\n\nfunction createTabsIndicatorRegistry(registrationChanged: () => void) {\n const indicator = createPartSlot(registrationChanged);\n const list = createPartSlot(registrationChanged);\n let observer: ResizeObserver | null = null;\n let observed: HTMLElement[] = [];\n\n function bindIndicator(node: HTMLElement, signal: AbortSignal): void {\n if (!indicator.bind(node, signal)) return;\n signal.addEventListener(\n \"abort\",\n () => {\n if (indicator.node() !== null) return;\n observer?.disconnect();\n observer = null;\n },\n { once: true },\n );\n }\n\n function sync(): void {\n const listNode = list.node();\n const indicatorNode = indicator.node();\n if (indicatorNode === null) {\n observer?.disconnect();\n observer = null;\n return;\n }\n updatePosition();\n\n if (typeof ResizeObserver === \"undefined\") return;\n // Resize delivery must only measure; re-observing would schedule another\n // initial notification even when none of the observed sizes changed.\n if (observer === null) {\n observer = new ResizeObserver(updatePosition);\n observed = [];\n }\n const tabs = ownedTabs(listNode);\n const targets = listNode === null ? tabs : [listNode, ...tabs];\n if (\n targets.length !== observed.length ||\n targets.some((target, index) => target !== observed[index])\n ) {\n observer.disconnect();\n for (const target of targets) observer.observe(target);\n observed = targets;\n }\n }\n\n function updatePosition(): void {\n const indicatorNode = indicator.node();\n if (indicatorNode === null) return;\n const listNode = list.node();\n const active = ownedTabs(listNode).find(\n (tab) => tab.getAttribute(\"aria-selected\") === \"true\",\n );\n positionTabsIndicator(indicatorNode, listNode, active ?? null);\n }\n\n return {\n bindIndicator,\n bindList: list.bind,\n sync,\n };\n}\n\nfunction ownedTabs(list: HTMLElement | null): HTMLElement[] {\n if (list === null) return [];\n return [...list.querySelectorAll<HTMLElement>('[role=\"tab\"]')].filter(\n (tab) => tab.closest('[role=\"tablist\"]') === list,\n );\n}\n\nconst edges = [\"bottom\", \"height\", \"left\", \"right\", \"top\", \"width\"] as const;\n\n/** Publishes the active tab's box as `--active-tab-*` custom properties. */\nfunction positionTabsIndicator(\n indicator: HTMLElement,\n list: HTMLElement | null,\n tab: HTMLElement | null,\n): void {\n const style = indicator.style;\n if (list === null || tab === null) {\n indicator.hidden = true;\n for (const edge of edges) style.removeProperty(`--active-tab-${edge}`);\n return;\n }\n\n const tabRect = tab.getBoundingClientRect();\n const listRect = list.getBoundingClientRect();\n const tabStyle = getComputedStyle(tab);\n const listStyle = getComputedStyle(list);\n const width = borderBoxSize(tabStyle, \"width\", tabRect.width);\n const height = borderBoxSize(tabStyle, \"height\", tabRect.height);\n // Layout dimensions include scrollbar gutters; computed content-box sizes\n // do not. Dividing by the latter mistakes a scrollbar for extra scaling.\n const listWidth =\n list.offsetWidth || borderBoxSize(listStyle, \"width\", listRect.width);\n const listHeight =\n list.offsetHeight || borderBoxSize(listStyle, \"height\", listRect.height);\n const scaleX = listWidth > 0 ? listRect.width / listWidth : 1;\n const scaleY = listHeight > 0 ? listRect.height / listHeight : 1;\n const left = scaleX\n ? (tabRect.left - listRect.left) / scaleX +\n list.scrollLeft -\n list.clientLeft\n : tab.offsetLeft;\n const top = scaleY\n ? (tabRect.top - listRect.top) / scaleY + list.scrollTop - list.clientTop\n : tab.offsetTop;\n const box = {\n bottom: list.scrollHeight - top - height,\n height,\n left,\n right: list.scrollWidth - left - width,\n top,\n width,\n };\n\n for (const edge of edges) {\n style.setProperty(`--active-tab-${edge}`, `${box[edge]}px`);\n }\n indicator.hidden = !(width > 0 && height > 0);\n}\n\nfunction borderBoxSize(\n style: CSSStyleDeclaration,\n dimension: \"width\" | \"height\",\n fallback: number,\n): number {\n const size = parseFloat(style[dimension]);\n if (!Number.isFinite(size)) return fallback;\n if (style.boxSizing === \"border-box\") return size;\n const edges =\n dimension === \"width\"\n ? ([\"Left\", \"Right\"] as const)\n : ([\"Top\", \"Bottom\"] as const);\n return edges.reduce(\n (total, edge) =>\n total +\n (parseFloat(style[`padding${edge}`]) || 0) +\n (parseFloat(style[`border${edge}Width`]) || 0),\n size,\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{t as e}from"./changes-DglRuvQa.js";import{a as t,i as n,n as r,t as i}from"./parts-I0jdefyp.js";import{t as a}from"./diagnostics-BkQV0MaT.js";import{i as o,n as s,t as c}from"./composite-DoFLgUjR.js";import{n as l}from"./controllable-value-CDbRzlId.js";import{t as u}from"./ids-Cvu36mFL.js";import{t as d}from"./relations-CZpjLINW.js";import{createMixin as f,useBeforePaint as p,useMemo as m,useStableEvent as h,useState as g}from"@bgub/fig";import{on as _}from"@bgub/fig-dom";const v=f((e,t,n)=>({...x(t),"aria-orientation":t.orientation,bind:r(e,t.registry,(e,n)=>t.registry.bindContainer(e,n)),mix:[_(`click`,i(t.registry,(e,n)=>t.select(e.value,n,e.node))),_(`keydown`,e=>{let r=t.registry.itemAt(e.target);if(r!==void 0){if(!n.activateOnFocus&&!r.disabled&&(e.key===`Enter`||e.key===` `)){e.preventDefault(),t.select(r.value,e,r.node);return}t.registry.moveFocus(e,{loop:n.loopFocus,orientation:t.orientation})}}),_(`focusin`,e=>{let r=t.registry.itemAt(e.target);r!==void 0&&(t.setHighlighted(r.value),n.activateOnFocus&&!r.disabled&&!t.registry.pointerActive()&&t.select(r.value,e,r.node))}),_(`focusout`,e=>{let n=e.currentTarget,r=e.relatedTarget;n instanceof Element&&(!(r instanceof Node)||!n.contains(r))&&t.resetHighlight()}),_(`pointerdown`,(e,n)=>{t.registry.notePointerDown(e.currentTarget,n)})],role:`tablist`})),y=f((e,t,i)=>{let a=i.disabled||e.props.disabled===!0;return{...x(t),...n(e,{disabled:a,id:i.tabId}),"aria-controls":i.panelId,"aria-selected":i.selected?`true`:`false`,"data-active":i.selected?``:void 0,bind:r(e,t.registry,(e,n)=>t.registry.bindItem(e,n,i.value,a)),role:`tab`,tabindex:i.highlighted?0:-1}}),b=f((e,t,n)=>({...x(t),"aria-labelledby":n.tabId,"data-hidden":n.active?void 0:``,bind:r(e,t.registry,(e,r)=>t.registry.bindPanel(e,r,n.value)),hidden:!n.active||void 0,id:e.props.id??n.panelId,inert:!n.active||void 0,role:`tabpanel`,tabindex:n.active?e.props.tabindex??0:-1}));function x(e){return{"data-orientation":e.orientation}}function S(e){let t=c({container:`[role="tablist"]`,item:`[role="tab"]`,name:`tabs`,registrationChanged:e}),n=d(t,e);function r(){n.sync();let e=t.containerNode();e!==null&&a(e,`tab list`)}return{...t,bindPanel:n.bindPanel,sync:r}}const C=Symbol(`fig-headless.tabs.none`);function w(n){let{controlled:r}=n,i=t(),a=l({value:r?n.value:void 0,defaultValue:n.value,onChange:n.onValueChange,equal:o,reconcile:i}),{value:c}=a,[u,d]=g(()=>n.value===null?C:n.value),f=m(()=>({exempt:r||n.implicitDefault?C:n.value,value:c}),[]),_=!r&&n.implicitDefault,v=m(()=>S(i),[]),y=h(e=>{d(()=>e)}),b=h((t,n,i)=>{a.request(()=>t,e(n,i))&&!r&&y(t)}),x=h(()=>{let e=v.items().filter(s),t=(c===null?void 0:e.find(e=>o(e.value,c)))??e[0];t!==void 0&&y(t.value)});return p(()=>{v.sync();let t=v.items(),n=t.filter(s),i=f.value,l=!o(i,c),d=r?null:T(t,c,_,f);if(d!==null){if(o(d.value,i))return;let t=n.find(e=>o(e.value,d.value))??n[0];f.value=d.value,t!==void 0&&y(t.value),a.restore(d.value,e(null));return}f.value=c;let p=E(n,c,u,l&&!v.containsFocus());o(p,u)||y(p)}),{highlightedValue:u,registry:v,resetHighlight:x,select:b,setHighlighted:y,value:c}}function T(e,t,n,r){if(e.length===0)return null;let i=t===null?void 0:e.find(e=>o(e.value,t)),a=i!==void 0&&(i.disabled||!s(i)),c=i===void 0&&t!==null,l=t===null&&n;if(o(t,r.exempt)){if(a)return null;r.exempt=C}if(!a&&!c&&!l)return null;let u=e.find(e=>!e.disabled&&s(e)),d=u===void 0?null:u.value;return o(t,d)?null:{value:d}}function E(e,t,n,r){let i=e.find(e=>o(e.value,n)),a=t===null?void 0:e.find(e=>o(e.value,t));if(i===void 0){let t=a??e[0];return t===void 0?n:t.value}return r&&a!==void 0&&!a.disabled?a.value:n}function D(e={}){let t=e,{orientation:n=`horizontal`}=t,r=t.value,i=r!==void 0,a=w({controlled:i,implicitDefault:!i&&t.defaultValue===void 0,onValueChange:t.onValueChange,value:r===void 0?t.defaultValue??null:r}),{value:s}=a,c=u(),l={orientation:n,registry:a.registry,resetHighlight:a.resetHighlight,select:a.select,setHighlighted:a.setHighlighted};function d(e){return s!==null&&o(e,s)}function f(e){return b(l,{active:d(e),panelId:c(e,`panel`),tabId:c(e,`tab`),value:e})}return{value:s,list:(e={})=>v(l,{activateOnFocus:e.activateOnFocus===!0,loopFocus:e.loopFocus!==!1}),panel:f,tab:(e,t={})=>y(l,{disabled:t.disabled===!0,highlighted:o(a.highlightedValue,e),panelId:c(e,`panel`),selected:d(e),tabId:c(e,`tab`),value:e})}}function O(e){return e.children(D(e))}export{O as Tabs,D as useTabs};
2
+ //# sourceMappingURL=tabs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs.js","names":[],"sources":["../src/tabs/parts.ts","../src/tabs/registry.ts","../src/tabs/selection.ts","../src/tabs/tabs.tsx"],"sourcesContent":["import { createMixin, type MixinContext } from \"@bgub/fig\";\nimport { on } from \"@bgub/fig-dom\";\nimport { activateOnClick, bindPart, triggerProps } from \"../internal/parts.ts\";\nimport type { TabsOrientation, TabsRegistry } from \"./registry.ts\";\n\n/** Widget-level state every part reads. Built once per root render. */\nexport interface TabsPartState {\n readonly orientation: TabsOrientation;\n readonly registry: TabsRegistry;\n readonly resetHighlight: () => void;\n readonly select: (value: unknown, event: Event, trigger: Element) => void;\n readonly setHighlighted: (value: unknown) => void;\n}\n\ninterface TabsListOwnState {\n readonly activateOnFocus: boolean;\n readonly loopFocus: boolean;\n}\n\ninterface TabsTabOwnState {\n readonly disabled: boolean;\n readonly highlighted: boolean;\n readonly panelId: string;\n readonly selected: boolean;\n readonly tabId: string;\n readonly value: unknown;\n}\n\ninterface TabsPanelOwnState {\n readonly active: boolean;\n readonly panelId: string;\n readonly tabId: string;\n readonly value: unknown;\n}\n\n// The list owns every tab interaction: tabs are plain hosts carrying\n// attributes, so listeners stay O(1) in the number of tabs and each tab's own\n// handlers still run first, in the target phase.\nexport const listMixin = /* @__PURE__ */ createMixin(\n (context: MixinContext, state: TabsPartState, own: TabsListOwnState) => ({\n ...partProps(state),\n \"aria-orientation\": state.orientation,\n bind: bindPart(context, state.registry, (node, signal) =>\n state.registry.bindContainer(node, signal),\n ),\n mix: [\n on(\n \"click\",\n activateOnClick(state.registry, (tab, event) =>\n state.select(tab.value, event, tab.node),\n ),\n ),\n on(\"keydown\", (event) => {\n const tab = state.registry.itemAt(event.target);\n if (tab === undefined) return;\n // Automatic activation already selected on focus, and native buttons\n // turn Enter and Space into clicks.\n if (\n !own.activateOnFocus &&\n !tab.disabled &&\n (event.key === \"Enter\" || event.key === \" \")\n ) {\n event.preventDefault();\n state.select(tab.value, event, tab.node);\n return;\n }\n state.registry.moveFocus(event, {\n loop: own.loopFocus,\n orientation: state.orientation,\n });\n }),\n on(\"focusin\", (event) => {\n const tab = state.registry.itemAt(event.target);\n if (tab === undefined) return;\n state.setHighlighted(tab.value);\n if (\n own.activateOnFocus &&\n !tab.disabled &&\n !state.registry.pointerActive()\n ) {\n state.select(tab.value, event, tab.node);\n }\n }),\n on(\"focusout\", (event) => {\n const list = event.currentTarget;\n const next = event.relatedTarget;\n if (\n list instanceof Element &&\n (!(next instanceof Node) || !list.contains(next))\n ) {\n state.resetHighlight();\n }\n }),\n // Click owns primary-pointer activation, so focus must not race it.\n on(\"pointerdown\", (event, signal) => {\n state.registry.notePointerDown(event.currentTarget, signal);\n }),\n ],\n role: \"tablist\",\n }),\n);\n\nexport const tabMixin = /* @__PURE__ */ createMixin(\n (context: MixinContext, state: TabsPartState, own: TabsTabOwnState) => {\n const disabled = own.disabled || context.props.disabled === true;\n return {\n ...partProps(state),\n ...triggerProps(context, { disabled, id: own.tabId }),\n \"aria-controls\": own.panelId,\n \"aria-selected\": own.selected ? \"true\" : \"false\",\n \"data-active\": own.selected ? \"\" : undefined,\n bind: bindPart(context, state.registry, (node, signal) =>\n state.registry.bindItem(node, signal, own.value, disabled),\n ),\n role: \"tab\",\n tabindex: own.highlighted ? 0 : -1,\n };\n },\n);\n\nexport const panelMixin = /* @__PURE__ */ createMixin(\n (context: MixinContext, state: TabsPartState, own: TabsPanelOwnState) => ({\n ...partProps(state),\n \"aria-labelledby\": own.tabId,\n \"data-hidden\": own.active ? undefined : \"\",\n bind: bindPart(context, state.registry, (node, signal) =>\n state.registry.bindPanel(node, signal, own.value),\n ),\n hidden: own.active ? undefined : true,\n id: context.props.id ?? own.panelId,\n inert: own.active ? undefined : true,\n role: \"tabpanel\",\n tabindex: own.active ? (context.props.tabindex ?? 0) : -1,\n }),\n);\n\nfunction partProps(state: TabsPartState) {\n return {\n \"data-orientation\": state.orientation,\n };\n}\n","import {\n type CompositeItem,\n createComposite,\n type Orientation,\n sameValue,\n} from \"../internal/composite.ts\";\nimport { createRelations } from \"../internal/relations.ts\";\nimport { assertAccessibleName } from \"../internal/diagnostics.ts\";\n\nexport type TabsOrientation = Orientation;\n\nexport type TabsRegistry = ReturnType<typeof createTabsRegistry>;\n\n/**\n * The shared composite plus what only tabs need: panel relationships.\n */\nexport function createTabsRegistry(registrationChanged: () => void) {\n const composite = createComposite({\n container: '[role=\"tablist\"]',\n item: '[role=\"tab\"]',\n name: \"tabs\",\n registrationChanged,\n });\n\n const relations = createRelations(composite, registrationChanged);\n\n /** Synchronizes panel relationships against the committed DOM. */\n function sync(): void {\n relations.sync();\n const container = composite.containerNode();\n if (container !== null) assertAccessibleName(container, \"tab list\");\n }\n\n return {\n ...composite,\n bindPanel: relations.bindPanel,\n sync,\n };\n}\n\nexport type { CompositeItem as TabsTabRegistration };\nexport { sameValue };\n","import { useBeforePaint, useMemo, useStableEvent, useState } from \"@bgub/fig\";\nimport {\n type ChangeDetails,\n createChangeDetails,\n} from \"../internal/changes.ts\";\nimport {\n type CompositeItem,\n isNativeEnabled,\n sameValue,\n} from \"../internal/composite.ts\";\nimport { useControllableValue } from \"../internal/controllable-value.ts\";\nimport { useRegistrationReconcile } from \"../internal/reconcile.ts\";\nimport { createTabsRegistry } from \"./registry.ts\";\n\nexport type TabsValueChangeDetails = ChangeDetails;\n\nexport type TabsValueChangeHandler<Value = unknown> = (\n value: Value | null,\n details: TabsValueChangeDetails,\n signal: AbortSignal,\n) => void;\n\nexport interface TabsSelectionOptions<Value> {\n readonly controlled: boolean;\n /**\n * Neither `value` nor `defaultValue`, so the root selects the first enabled\n * tab itself. An explicit `defaultValue` of `null` means the opposite: the\n * application asked for no selection.\n */\n readonly implicitDefault: boolean;\n readonly onValueChange: TabsValueChangeHandler<Value> | undefined;\n /** The controlled value, or the initial value of an uncontrolled root. */\n readonly value: Value | null;\n}\n\ninterface SelectionTracker {\n /** An explicitly requested default stays selected even while disabled. */\n exempt: unknown;\n value: unknown;\n}\n\nconst none = Symbol(\"fig-headless.tabs.none\");\n\n/**\n * Owns tab selection: user activation, the roving tab stop, and the automatic\n * repairs an uncontrolled root makes when its tabs change.\n */\nexport function useTabsSelection<Value>(options: TabsSelectionOptions<Value>) {\n const { controlled } = options;\n const registrationChanged = useRegistrationReconcile();\n const selection = useControllableValue<Value | null>({\n value: controlled ? options.value : undefined,\n defaultValue: options.value,\n onChange: options.onValueChange,\n equal: sameValue,\n reconcile: registrationChanged,\n });\n const { value } = selection;\n const [highlighted, setHighlightedState] = useState<unknown>(() =>\n options.value === null ? none : options.value,\n );\n const tracker = useMemo<SelectionTracker>(\n () => ({\n exempt: controlled || options.implicitDefault ? none : options.value,\n value,\n }),\n [],\n );\n const autoSelect = !controlled && options.implicitDefault;\n const registry = useMemo(() => createTabsRegistry(registrationChanged), []);\n\n const setHighlighted = useStableEvent((highlighted: unknown) => {\n setHighlightedState(() => highlighted);\n });\n\n const select = useStableEvent(\n (next: unknown, event: Event, trigger: Element) => {\n if (\n selection.request(\n () => next as Value | null,\n createChangeDetails(event, trigger),\n ) &&\n !controlled\n ) {\n setHighlighted(next);\n }\n },\n );\n\n const resetHighlight = useStableEvent(() => {\n const tabs = registry.items().filter(isNativeEnabled);\n const selected =\n value === null\n ? undefined\n : tabs.find((tab) => sameValue(tab.value, value));\n const next = selected ?? tabs[0];\n if (next !== undefined) setHighlighted(next.value);\n });\n\n useBeforePaint(() => {\n registry.sync();\n const tabs = registry.items();\n const focusable = tabs.filter(isNativeEnabled);\n const previous = tracker.value;\n const changed = !sameValue(previous, value);\n\n const repair = controlled\n ? null\n : planRepair(tabs, value, autoSelect, tracker);\n if (repair !== null) {\n // A repair committed in an earlier pass may not have rendered yet.\n if (sameValue(repair.value, previous)) return;\n const target =\n focusable.find((tab) => sameValue(tab.value, repair.value)) ??\n focusable[0];\n tracker.value = repair.value;\n if (target !== undefined) setHighlighted(target.value);\n selection.restore(\n repair.value as Value | null,\n createChangeDetails(null),\n );\n return;\n }\n tracker.value = value;\n\n const next = nextHighlight(\n focusable,\n value,\n highlighted,\n changed && !registry.containsFocus(),\n );\n if (!sameValue(next, highlighted)) setHighlighted(next);\n });\n\n return {\n highlightedValue: highlighted,\n registry,\n resetHighlight,\n select,\n setHighlighted,\n value,\n };\n}\n\n/**\n * Decides how an uncontrolled root reacts to the tabs it actually has. An\n * automatic change is reported through `onValueChange` with a `null` event,\n * which is what separates it from a user activation.\n */\nfunction planRepair(\n tabs: readonly CompositeItem[],\n value: unknown,\n autoSelect: boolean,\n tracker: SelectionTracker,\n): { readonly value: unknown } | null {\n // Every tab is unregistered: the subtree unmounted or an Activity hid it.\n // Either way the selection is worth keeping for when tabs return.\n if (tabs.length === 0) return null;\n\n const selected =\n value === null\n ? undefined\n : tabs.find((tab) => sameValue(tab.value, value));\n const disabled =\n selected !== undefined && (selected.disabled || !isNativeEnabled(selected));\n const missing = selected === undefined && value !== null;\n // A root that was never told what to select picks the first enabled tab.\n const unselected = value === null && autoSelect;\n\n if (sameValue(value, tracker.exempt)) {\n if (disabled) return null;\n tracker.exempt = none;\n }\n if (!disabled && !missing && !unselected) return null;\n\n const enabled = tabs.find((tab) => !tab.disabled && isNativeEnabled(tab));\n // `undefined` is a usable tab value, so never collapse it with `??`.\n const fallback = enabled === undefined ? null : enabled.value;\n return sameValue(value, fallback) ? null : { value: fallback };\n}\n\n/**\n * Keeps the roving tab stop on a mounted tab. Selection may claim it, but\n * never while the user is navigating inside the list.\n */\nfunction nextHighlight(\n tabs: readonly CompositeItem[],\n value: unknown,\n highlighted: unknown,\n selectionMayClaim: boolean,\n): unknown {\n const current = tabs.find((tab) => sameValue(tab.value, highlighted));\n const selected =\n value === null\n ? undefined\n : tabs.find((tab) => sameValue(tab.value, value));\n if (current === undefined) {\n const fallback = selected ?? tabs[0];\n return fallback === undefined ? highlighted : fallback.value;\n }\n if (selectionMayClaim && selected !== undefined && !selected.disabled) {\n return selected.value;\n }\n return highlighted;\n}\n","import type { FigNode, MixinDescriptor } from \"@bgub/fig\";\nimport { listMixin, panelMixin, tabMixin } from \"./parts.ts\";\nimport { sameValue } from \"../internal/composite.ts\";\nimport { usePartIds } from \"../internal/ids.ts\";\nimport type { TabsOrientation } from \"./registry.ts\";\nimport { type TabsValueChangeHandler, useTabsSelection } from \"./selection.ts\";\n\nexport type { TabsOrientation };\nexport type {\n TabsValueChangeDetails,\n TabsValueChangeHandler,\n} from \"./selection.ts\";\n\nexport interface TabsListOptions {\n /** Selects a tab as soon as it takes focus. */\n activateOnFocus?: boolean;\n /** Wraps arrow-key movement at both ends. Defaults to `true`. */\n loopFocus?: boolean;\n}\n\nexport interface TabsTabOptions {\n disabled?: boolean;\n}\n\nexport interface TabsParts<Value = unknown> {\n readonly value: Value | null;\n list(options?: TabsListOptions): MixinDescriptor;\n /** Marks a panel the caller keeps mounted; inactive panels hide. */\n panel(value: Value): MixinDescriptor;\n tab(value: Value, options?: TabsTabOptions): MixinDescriptor;\n}\n\nexport interface TabsOptions<Value = unknown> {\n defaultValue?: Value | null;\n onValueChange?: TabsValueChangeHandler<Value>;\n orientation?: TabsOrientation;\n value?: Value | null;\n}\n\nexport interface TabsProps<Value = unknown> extends TabsOptions<Value> {\n children: (tabs: TabsParts<Value>) => FigNode;\n}\n\n/**\n * Coordinates one tabs widget: selection, focus, and the generated\n * relationships between parts. Call it in the component that renders the\n * widget; every element stays the caller's.\n */\nexport function useTabs<Value = unknown>(\n options: TabsOptions<Value> = {},\n): TabsParts<Value> {\n const props = options;\n const { orientation = \"horizontal\" } = props;\n const controlledValue = props.value;\n const controlled = controlledValue !== undefined;\n const implicitDefault = !controlled && props.defaultValue === undefined;\n const selection = useTabsSelection<Value>({\n controlled,\n implicitDefault,\n onValueChange: props.onValueChange,\n value:\n controlledValue === undefined\n ? (props.defaultValue ?? null)\n : controlledValue,\n });\n const { value } = selection;\n const idFor = usePartIds();\n const state = {\n orientation,\n registry: selection.registry,\n resetHighlight: selection.resetHighlight,\n select: selection.select,\n setHighlighted: selection.setHighlighted,\n };\n function isSelected(partValue: unknown): boolean {\n return value !== null && sameValue(partValue, value);\n }\n\n function panel(panelValue: Value): MixinDescriptor {\n return panelMixin(state, {\n active: isSelected(panelValue),\n panelId: idFor(panelValue, \"panel\"),\n tabId: idFor(panelValue, \"tab\"),\n value: panelValue,\n });\n }\n\n return {\n value,\n list: (options = {}) =>\n listMixin(state, {\n activateOnFocus: options.activateOnFocus === true,\n loopFocus: options.loopFocus !== false,\n }),\n panel,\n tab: (tabValue, options = {}) =>\n tabMixin(state, {\n disabled: options.disabled === true,\n highlighted: sameValue(selection.highlightedValue, tabValue),\n panelId: idFor(tabValue, \"panel\"),\n selected: isSelected(tabValue),\n tabId: idFor(tabValue, \"tab\"),\n value: tabValue,\n }),\n };\n}\n\n/**\n * {@link useTabs} as a component, for a widget that is not already one of its\n * own. Selection re-renders this root rather than the caller.\n */\nexport function Tabs<Value = unknown>(props: TabsProps<Value>): FigNode {\n return props.children(useTabs(props));\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{t as e}from"./changes-DglRuvQa.js";import{a as t,i as n,n as r,o as i}from"./parts-I0jdefyp.js";import{c as a,i as o,s,t as c}from"./diagnostics-BkQV0MaT.js";import{i as l,r as u}from"./composite-DoFLgUjR.js";import{createMixin as d,useBeforePaint as f,useMemo as p,useStableEvent as m}from"@bgub/fig";import{on as h}from"@bgub/fig-dom";function g(e,t){let n=new Map,r=new Set,a,d=i(e);function f(e,t){let n=e.ownerDocument;if(!d.bind(e,t,n))return;let r=()=>h(`document`,d.items().some(e=>e.value.hidden));n.addEventListener(`visibilitychange`,r,{signal:t}),r(),u(t,()=>{r();let t=d.items();t.some(t=>t.node===e)||(m(e,!1),h(`focus`,t.some(e=>e.node.contains(e.node.ownerDocument.activeElement))))})}function p(t,i,a){let o=n.get(t);if(o?.signal===i&&o.duration===a.duration&&l(o.value,a.value))return;v(o);let s=o?.signal===i,c=s?o:{...a,node:t,signal:i,remaining:0,started:0,timer:void 0};c.duration=a.duration,c.value=a.value,c.remaining=a.duration??0,c.timer=void 0,n.set(t,c),r.size===0&&g(c),e(),s||u(i,()=>{n.get(t)===c&&(n.delete(t),v(c),e())})}function m(e,t){t?a=e:a===e&&(a=void 0),h(`pointer`,a!==void 0)}function h(e,t){let i=r.size>0;t?r.add(e):r.delete(e);let a=r.size>0;if(i!==a)for(let e of n.values())a?_(e):g(e)}function g(e){if(e.duration===null||e.timer!==void 0||e.remaining<0)return;e.started=Date.now();let r=setTimeout(()=>{let i=n.get(e.node);i?.timer===r&&(i.timer=void 0,i.remaining=-1,t(i))},e.remaining);e.timer=r}function _(e){e.timer!==void 0&&(clearTimeout(e.timer),e.timer=void 0,e.remaining=Math.max(0,e.remaining-(Date.now()-e.started)))}function v(e){e?.timer!==void 0&&clearTimeout(e.timer)}function y(){let e=d.items();o(e,`toast region`);for(let t of e)c(t.node,`toast region`);s([...n.values()],`toast`)}return{bindRegion:f,bindToast:p,setPaused:h,setPointerPaused:m,validate:y}}const _=d((e,t)=>({"aria-atomic":`false`,"aria-label":e.props[`aria-label`]??t.label,"aria-live":t.priority,"aria-relevant":`additions text`,bind:r(e,t.registry,t.registry.bindRegion),"data-priority":t.priority,mix:[h(`focusin`,()=>t.registry.setPaused(`focus`,!0)),h(`focusout`,e=>{let n=e.currentTarget,r=e.relatedTarget;n instanceof Element&&r instanceof Node&&n.contains(r)||t.registry.setPaused(`focus`,!1)}),h(`pointerenter`,e=>{e.currentTarget instanceof HTMLElement&&t.registry.setPointerPaused(e.currentTarget,!0)}),h(`pointerleave`,e=>{e.currentTarget instanceof HTMLElement&&t.registry.setPointerPaused(e.currentTarget,!1)})],role:`region`})),v=d((e,t,n)=>({"aria-atomic":`true`,bind:r(e,t.registry,(e,r)=>t.registry.bindToast(e,r,{duration:n.duration,value:n.value}))})),y=d((e,t,r)=>(a(e,`toast dismiss control`,`button`),{...n(e,{disabled:!1}),mix:h(`click`,e=>{e.defaultPrevented||e.currentTarget instanceof Element&&t.dismiss(r,`dismiss`,e,e.currentTarget)})}));function b(n){let r=t(),i=m((e,t,r)=>{n.onDismiss(e,t,r)}),a=m((t,n,r,a)=>{let o={...e(r,a),reason:n};i(t,o)}),o=p(()=>g(r,e=>{a(e.value,`timeout`,null,e.node)}),[]);f(()=>{o.validate()});let s={dismiss:a,label:n.label??`Notifications`,priority:n.priority??`polite`,registry:o};return{dismiss:e=>y(s,e),region:()=>_(s),toast:(e,t={})=>v(s,{duration:t.duration===null?null:Math.max(0,t.duration??5e3),value:e})}}function x(e){return e.children(b(e))}export{x as ToastRegion,b as useToastRegion};
2
+ //# sourceMappingURL=toast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toast.js","names":[],"sources":["../src/toast/registry.ts","../src/toast/toast.tsx"],"sourcesContent":["import { onAbort, sameValue } from \"../internal/composite.ts\";\nimport {\n assertAccessibleName,\n assertSinglePart,\n assertUniqueValues,\n} from \"../internal/diagnostics.ts\";\nimport { createPartCollection } from \"../internal/registration.ts\";\n\nexport interface ToastRegistration {\n readonly duration: number | null;\n readonly node: HTMLElement;\n readonly value: unknown;\n}\n\ninterface TimerRegistration {\n duration: number | null;\n value: unknown;\n readonly node: HTMLElement;\n readonly signal: AbortSignal;\n remaining: number;\n started: number;\n timer: ReturnType<typeof setTimeout> | undefined;\n}\n\ntype PauseReason = \"document\" | \"focus\" | \"pointer\";\n\n/** Mounted toast lifetimes and pause state, independent of rendered markup. */\nexport function createToastRegistry(\n registrationChanged: () => void,\n onTimeout: (registration: ToastRegistration) => void,\n) {\n const toasts = new Map<HTMLElement, TimerRegistration>();\n const paused = new Set<PauseReason>();\n let hoveredRegion: HTMLElement | undefined;\n const regions = createPartCollection<Document>(registrationChanged);\n\n function bindRegion(node: HTMLElement, signal: AbortSignal): void {\n const ownerDocument = node.ownerDocument;\n if (!regions.bind(node, signal, ownerDocument)) return;\n const syncVisibility = () =>\n setPaused(\n \"document\",\n regions.items().some((entry) => entry.value.hidden),\n );\n ownerDocument.addEventListener(\"visibilitychange\", syncVisibility, {\n signal,\n });\n syncVisibility();\n onAbort(signal, () => {\n syncVisibility();\n const mounted = regions.items();\n if (!mounted.some((entry) => entry.node === node)) {\n setPointerPaused(node, false);\n setPaused(\n \"focus\",\n mounted.some((entry) =>\n entry.node.contains(entry.node.ownerDocument.activeElement),\n ),\n );\n }\n });\n }\n\n function bindToast(\n node: HTMLElement,\n signal: AbortSignal,\n config: { readonly duration: number | null; readonly value: unknown },\n ): void {\n const previous = toasts.get(node);\n const preserve =\n previous?.signal === signal &&\n previous.duration === config.duration &&\n sameValue(previous.value, config.value);\n if (preserve) return;\n clear(previous);\n const sameLifetime = previous?.signal === signal;\n const registration: TimerRegistration = sameLifetime\n ? previous\n : {\n ...config,\n node,\n signal,\n remaining: 0,\n started: 0,\n timer: undefined,\n };\n registration.duration = config.duration;\n registration.value = config.value;\n registration.remaining = config.duration ?? 0;\n registration.timer = undefined;\n toasts.set(node, registration);\n if (paused.size === 0) start(registration);\n registrationChanged();\n if (!sameLifetime)\n onAbort(signal, () => {\n if (toasts.get(node) !== registration) return;\n toasts.delete(node);\n clear(registration);\n registrationChanged();\n });\n }\n\n function setPointerPaused(node: HTMLElement, pause: boolean): void {\n if (pause) hoveredRegion = node;\n else if (hoveredRegion === node) hoveredRegion = undefined;\n setPaused(\"pointer\", hoveredRegion !== undefined);\n }\n\n function setPaused(reason: PauseReason, pause: boolean): void {\n const wasPaused = paused.size > 0;\n if (pause) paused.add(reason);\n else paused.delete(reason);\n const isPaused = paused.size > 0;\n if (wasPaused === isPaused) return;\n for (const toast of toasts.values()) {\n if (isPaused) stop(toast);\n else start(toast);\n }\n }\n\n function start(toast: TimerRegistration): void {\n if (\n toast.duration === null ||\n toast.timer !== undefined ||\n toast.remaining < 0\n ) {\n return;\n }\n toast.started = Date.now();\n const timer = setTimeout(() => {\n const current = toasts.get(toast.node);\n if (current?.timer !== timer) return;\n current.timer = undefined;\n current.remaining = -1;\n onTimeout(current);\n }, toast.remaining);\n toast.timer = timer;\n }\n\n function stop(toast: TimerRegistration): void {\n if (toast.timer === undefined) return;\n clearTimeout(toast.timer);\n toast.timer = undefined;\n toast.remaining = Math.max(\n 0,\n toast.remaining - (Date.now() - toast.started),\n );\n }\n\n function clear(toast: TimerRegistration | undefined): void {\n if (toast?.timer !== undefined) clearTimeout(toast.timer);\n }\n\n function validate(): void {\n const mountedRegions = regions.items();\n assertSinglePart(mountedRegions, \"toast region\");\n for (const mountedRegion of mountedRegions) {\n assertAccessibleName(mountedRegion.node, \"toast region\");\n }\n assertUniqueValues([...toasts.values()], \"toast\");\n }\n\n return {\n bindRegion,\n bindToast,\n setPaused,\n setPointerPaused,\n validate,\n };\n}\n","import {\n createMixin,\n type FigNode,\n type MixinContext,\n type MixinDescriptor,\n useBeforePaint,\n useMemo,\n useStableEvent,\n} from \"@bgub/fig\";\nimport { on } from \"@bgub/fig-dom\";\nimport {\n type ChangeDetails,\n createChangeDetails,\n} from \"../internal/changes.ts\";\nimport { expectHost } from \"../internal/diagnostics.ts\";\nimport { bindPart, triggerProps } from \"../internal/parts.ts\";\nimport { useRegistrationReconcile } from \"../internal/reconcile.ts\";\nimport { createToastRegistry, type ToastRegistration } from \"./registry.ts\";\n\nexport type ToastPriority = \"assertive\" | \"polite\";\nexport type ToastDismissReason = \"dismiss\" | \"timeout\";\n\nexport interface ToastDismissDetails extends ChangeDetails {\n readonly reason: ToastDismissReason;\n}\n\nexport type ToastDismissHandler<Value = unknown> = (\n value: Value,\n details: ToastDismissDetails,\n signal: AbortSignal,\n) => void;\n\nexport interface ToastOptions {\n /** Automatic dismissal delay. `null` keeps the toast. Defaults to 5000ms. */\n duration?: number | null;\n}\n\nexport interface ToastRegionOptions<Value = unknown> {\n /** Accessible name for the collection. Defaults to `Notifications`. */\n label?: string;\n onDismiss: ToastDismissHandler<Value>;\n /** Announcement urgency for this region. Defaults to `polite`. */\n priority?: ToastPriority;\n}\n\nexport interface ToastRegionParts<Value = unknown> {\n dismiss(value: Value): MixinDescriptor;\n region(): MixinDescriptor;\n toast(value: Value, options?: ToastOptions): MixinDescriptor;\n}\n\nexport interface ToastRegionProps<\n Value = unknown,\n> extends ToastRegionOptions<Value> {\n children: (region: ToastRegionParts<Value>) => FigNode;\n}\n\ntype ToastRegistry = ReturnType<typeof createToastRegistry>;\n\ninterface ToastRegionState {\n readonly dismiss: (\n value: unknown,\n reason: ToastDismissReason,\n event: Event | null,\n trigger: Element,\n ) => void;\n readonly label: string;\n readonly priority: ToastPriority;\n readonly registry: ToastRegistry;\n}\n\ninterface ToastState {\n readonly duration: number | null;\n readonly value: unknown;\n}\n\nconst toastRegionMixin = /* @__PURE__ */ createMixin(\n (context: MixinContext, state: ToastRegionState) => ({\n \"aria-atomic\": \"false\",\n \"aria-label\": context.props[\"aria-label\"] ?? state.label,\n \"aria-live\": state.priority,\n \"aria-relevant\": \"additions text\",\n bind: bindPart(context, state.registry, state.registry.bindRegion),\n \"data-priority\": state.priority,\n mix: [\n on(\"focusin\", () => state.registry.setPaused(\"focus\", true)),\n on(\"focusout\", (event) => {\n const region = event.currentTarget;\n const next = event.relatedTarget;\n if (\n region instanceof Element &&\n next instanceof Node &&\n region.contains(next)\n ) {\n return;\n }\n state.registry.setPaused(\"focus\", false);\n }),\n on(\"pointerenter\", (event) => {\n if (event.currentTarget instanceof HTMLElement)\n state.registry.setPointerPaused(event.currentTarget, true);\n }),\n on(\"pointerleave\", (event) => {\n if (event.currentTarget instanceof HTMLElement)\n state.registry.setPointerPaused(event.currentTarget, false);\n }),\n ],\n role: \"region\",\n }),\n);\n\nconst toastMixin = /* @__PURE__ */ createMixin(\n (context: MixinContext, state: ToastRegionState, own: ToastState) => ({\n \"aria-atomic\": \"true\",\n bind: bindPart(context, state.registry, (node, signal) =>\n state.registry.bindToast(node, signal, {\n duration: own.duration,\n value: own.value,\n }),\n ),\n }),\n);\n\nconst toastDismissMixin = /* @__PURE__ */ createMixin(\n (context: MixinContext, state: ToastRegionState, value: unknown) => {\n expectHost(context, \"toast dismiss control\", \"button\");\n return {\n ...triggerProps(context, { disabled: false }),\n mix: on(\"click\", (event) => {\n if (event.defaultPrevented) return;\n if (event.currentTarget instanceof Element) {\n state.dismiss(value, \"dismiss\", event, event.currentTarget);\n }\n }),\n };\n },\n);\n\n/** Coordinates announcements and pauseable lifetimes for caller-owned toasts. */\nexport function useToastRegion<Value = unknown>(\n options: ToastRegionOptions<Value>,\n): ToastRegionParts<Value> {\n const requestReconcile = useRegistrationReconcile();\n const emitDismiss = useStableEvent(\n (value: unknown, details: ToastDismissDetails, signal: AbortSignal) => {\n options.onDismiss(value as Value, details, signal);\n },\n );\n const dismiss = useStableEvent(\n (\n value: unknown,\n reason: ToastDismissReason,\n event: Event | null,\n trigger: Element,\n ) => {\n const details: ToastDismissDetails = {\n ...createChangeDetails(event, trigger),\n reason,\n };\n emitDismiss(value, details);\n },\n );\n const registry = useMemo(\n () =>\n createToastRegistry(requestReconcile, (toast: ToastRegistration) => {\n dismiss(toast.value, \"timeout\", null, toast.node);\n }),\n [],\n );\n\n useBeforePaint(() => {\n registry.validate();\n });\n\n const state: ToastRegionState = {\n dismiss,\n label: options.label ?? \"Notifications\",\n priority: options.priority ?? \"polite\",\n registry,\n };\n\n return {\n dismiss: (value) => toastDismissMixin(state, value),\n region: () => toastRegionMixin(state),\n toast: (value, toastOptions = {}) =>\n toastMixin(state, {\n duration:\n toastOptions.duration === null\n ? null\n : Math.max(0, toastOptions.duration ?? 5000),\n value,\n }),\n };\n}\n\n/** {@link useToastRegion} as a render-callback component. */\nexport function ToastRegion<Value = unknown>(\n props: ToastRegionProps<Value>,\n): FigNode {\n return props.children(useToastRegion(props));\n}\n"],"mappings":"wVA2BA,SAAgB,EACd,EACA,EACA,CACA,IAAM,EAAS,IAAI,IACb,EAAS,IAAI,IACf,EACE,EAAU,EAA+B,CAAmB,EAElE,SAAS,EAAW,EAAmB,EAA2B,CAChE,IAAM,EAAgB,EAAK,cAC3B,GAAI,CAAC,EAAQ,KAAK,EAAM,EAAQ,CAAa,EAAG,OAChD,IAAM,MACJ,EACE,WACA,EAAQ,MAAM,CAAC,CAAC,KAAM,GAAU,EAAM,MAAM,MAAM,CACpD,EACF,EAAc,iBAAiB,mBAAoB,EAAgB,CACjE,QACF,CAAC,EACD,EAAe,EACf,EAAQ,MAAc,CACpB,EAAe,EACf,IAAM,EAAU,EAAQ,MAAM,EACzB,EAAQ,KAAM,GAAU,EAAM,OAAS,CAAI,IAC9C,EAAiB,EAAM,EAAK,EAC5B,EACE,QACA,EAAQ,KAAM,GACZ,EAAM,KAAK,SAAS,EAAM,KAAK,cAAc,aAAa,CAC5D,CACF,EAEJ,CAAC,CACH,CAEA,SAAS,EACP,EACA,EACA,EACM,CACN,IAAM,EAAW,EAAO,IAAI,CAAI,EAKhC,GAHE,GAAU,SAAW,GACrB,EAAS,WAAa,EAAO,UAC7B,EAAU,EAAS,MAAO,EAAO,KAAK,EAC1B,OACd,EAAM,CAAQ,EACd,IAAM,EAAe,GAAU,SAAW,EACpC,EAAkC,EACpC,EACA,CACE,GAAG,EACH,OACA,SACA,UAAW,EACX,QAAS,EACT,MAAO,IAAA,EACT,EACJ,EAAa,SAAW,EAAO,SAC/B,EAAa,MAAQ,EAAO,MAC5B,EAAa,UAAY,EAAO,UAAY,EAC5C,EAAa,MAAQ,IAAA,GACrB,EAAO,IAAI,EAAM,CAAY,EACzB,EAAO,OAAS,GAAG,EAAM,CAAY,EACzC,EAAoB,EACf,GACH,EAAQ,MAAc,CAChB,EAAO,IAAI,CAAI,IAAM,IACzB,EAAO,OAAO,CAAI,EAClB,EAAM,CAAY,EAClB,EAAoB,EACtB,CAAC,CACL,CAEA,SAAS,EAAiB,EAAmB,EAAsB,CAC7D,EAAO,EAAgB,EAClB,IAAkB,IAAM,EAAgB,IAAA,IACjD,EAAU,UAAW,IAAkB,IAAA,EAAS,CAClD,CAEA,SAAS,EAAU,EAAqB,EAAsB,CAC5D,IAAM,EAAY,EAAO,KAAO,EAC5B,EAAO,EAAO,IAAI,CAAM,EACvB,EAAO,OAAO,CAAM,EACzB,IAAM,EAAW,EAAO,KAAO,EAC3B,OAAc,EAClB,IAAK,IAAM,KAAS,EAAO,OAAO,EAC5B,EAAU,EAAK,CAAK,EACnB,EAAM,CAAK,CAEpB,CAEA,SAAS,EAAM,EAAgC,CAC7C,GACE,EAAM,WAAa,MACnB,EAAM,QAAU,IAAA,IAChB,EAAM,UAAY,EAElB,OAEF,EAAM,QAAU,KAAK,IAAI,EACzB,IAAM,EAAQ,eAAiB,CAC7B,IAAM,EAAU,EAAO,IAAI,EAAM,IAAI,EACjC,GAAS,QAAU,IACvB,EAAQ,MAAQ,IAAA,GAChB,EAAQ,UAAY,GACpB,EAAU,CAAO,EACnB,EAAG,EAAM,SAAS,EAClB,EAAM,MAAQ,CAChB,CAEA,SAAS,EAAK,EAAgC,CACxC,EAAM,QAAU,IAAA,KACpB,aAAa,EAAM,KAAK,EACxB,EAAM,MAAQ,IAAA,GACd,EAAM,UAAY,KAAK,IACrB,EACA,EAAM,WAAa,KAAK,IAAI,EAAI,EAAM,QACxC,EACF,CAEA,SAAS,EAAM,EAA4C,CACrD,GAAO,QAAU,IAAA,IAAW,aAAa,EAAM,KAAK,CAC1D,CAEA,SAAS,GAAiB,CACxB,IAAM,EAAiB,EAAQ,MAAM,EACrC,EAAiB,EAAgB,cAAc,EAC/C,IAAK,IAAM,KAAiB,EAC1B,EAAqB,EAAc,KAAM,cAAc,EAEzD,EAAmB,CAAC,GAAG,EAAO,OAAO,CAAC,EAAG,OAAO,CAClD,CAEA,MAAO,CACL,aACA,YACA,YACA,mBACA,UACF,CACF,CC7FA,MAAM,EAAmC,GACtC,EAAuB,KAA6B,CACnD,cAAe,QACf,aAAc,EAAQ,MAAM,eAAiB,EAAM,MACnD,YAAa,EAAM,SACnB,gBAAiB,iBACjB,KAAM,EAAS,EAAS,EAAM,SAAU,EAAM,SAAS,UAAU,EACjE,gBAAiB,EAAM,SACvB,IAAK,CACH,EAAG,cAAiB,EAAM,SAAS,UAAU,QAAS,EAAI,CAAC,EAC3D,EAAG,WAAa,GAAU,CACxB,IAAM,EAAS,EAAM,cACf,EAAO,EAAM,cAEjB,aAAkB,SAClB,aAAgB,MAChB,EAAO,SAAS,CAAI,GAItB,EAAM,SAAS,UAAU,QAAS,EAAK,CACzC,CAAC,EACD,EAAG,eAAiB,GAAU,CACxB,EAAM,yBAAyB,aACjC,EAAM,SAAS,iBAAiB,EAAM,cAAe,EAAI,CAC7D,CAAC,EACD,EAAG,eAAiB,GAAU,CACxB,EAAM,yBAAyB,aACjC,EAAM,SAAS,iBAAiB,EAAM,cAAe,EAAK,CAC9D,CAAC,CACH,EACA,KAAM,QACR,EACF,EAEM,EAA6B,GAChC,EAAuB,EAAyB,KAAqB,CACpE,cAAe,OACf,KAAM,EAAS,EAAS,EAAM,UAAW,EAAM,IAC7C,EAAM,SAAS,UAAU,EAAM,EAAQ,CACrC,SAAU,EAAI,SACd,MAAO,EAAI,KACb,CAAC,CACH,CACF,EACF,EAEM,EAAoC,GACvC,EAAuB,EAAyB,KAC/C,EAAW,EAAS,wBAAyB,QAAQ,EAC9C,CACL,GAAG,EAAa,EAAS,CAAE,SAAU,EAAM,CAAC,EAC5C,IAAK,EAAG,QAAU,GAAU,CACtB,EAAM,kBACN,EAAM,yBAAyB,SACjC,EAAM,QAAQ,EAAO,UAAW,EAAO,EAAM,aAAa,CAE9D,CAAC,CACH,EAEJ,EAGA,SAAgB,EACd,EACyB,CACzB,IAAM,EAAmB,EAAyB,EAC5C,EAAc,GACjB,EAAgB,EAA8B,IAAwB,CACrE,EAAQ,UAAU,EAAgB,EAAS,CAAM,CACnD,CACF,EACM,EAAU,GAEZ,EACA,EACA,EACA,IACG,CACH,IAAM,EAA+B,CACnC,GAAG,EAAoB,EAAO,CAAO,EACrC,QACF,EACA,EAAY,EAAO,CAAO,CAC5B,CACF,EACM,EAAW,MAEb,EAAoB,EAAmB,GAA6B,CAClE,EAAQ,EAAM,MAAO,UAAW,KAAM,EAAM,IAAI,CAClD,CAAC,EACH,CAAC,CACH,EAEA,MAAqB,CACnB,EAAS,SAAS,CACpB,CAAC,EAED,IAAM,EAA0B,CAC9B,UACA,MAAO,EAAQ,OAAS,gBACxB,SAAU,EAAQ,UAAY,SAC9B,UACF,EAEA,MAAO,CACL,QAAU,GAAU,EAAkB,EAAO,CAAK,EAClD,WAAc,EAAiB,CAAK,EACpC,OAAQ,EAAO,EAAe,CAAC,IAC7B,EAAW,EAAO,CAChB,SACE,EAAa,WAAa,KACtB,KACA,KAAK,IAAI,EAAG,EAAa,UAAY,GAAI,EAC/C,OACF,CAAC,CACL,CACF,CAGA,SAAgB,EACd,EACS,CACT,OAAO,EAAM,SAAS,EAAe,CAAK,CAAC,CAC7C"}
@@ -0,0 +1,2 @@
1
+ import{t as e}from"./changes-DglRuvQa.js";import{a as t,n,o as r}from"./parts-I0jdefyp.js";import{c as i,i as a}from"./diagnostics-BkQV0MaT.js";import{n as o}from"./controllable-value-CDbRzlId.js";import{t as s}from"./form-reset-B7G3-0OT.js";import{createMixin as c,useBeforePaint as l,useMemo as u,useStableEvent as d}from"@bgub/fig";import{on as f}from"@bgub/fig-dom";const p=c((e,t)=>(i(e,t.role===`switch`?`switch control`:`checkbox control`,`input`),{bind:n(e,t.owner,t.noteInput),checked:t.checked,"data-checked":t.checked?``:void 0,"data-disabled":t.disabled||e.props.disabled===!0?``:void 0,"data-indeterminate":t.indeterminate?``:void 0,"data-readonly":t.readOnly?``:void 0,disabled:t.disabled?!0:e.props.disabled,mix:[f(`click`,e=>{t.readOnly&&e.preventDefault()}),f(`change`,e=>{let n=e.currentTarget;n instanceof HTMLInputElement&&t.toggle(n.checked,e,n)})],name:e.props.name??t.name,"aria-readonly":t.readOnly?`true`:void 0,required:t.required?!0:e.props.required,role:t.role,type:`checkbox`,value:e.props.value??t.value}));function m(n,i){let{disabled:c=!1,indeterminate:f=!1,readOnly:m=!1,required:h=!1}=n,g=t(),_=o({value:n.checked,defaultValue:n.defaultChecked===!0,onChange:n.onCheckedChange,reconcile:g}),v=_.value,y=u(()=>r(g),[]),b=u(()=>s(_.reset),[]),x=d((t,n,r)=>{if(n.defaultPrevented||c||r.matches(`:disabled`)||m){g();return}_.request(()=>t,e(n,r))}),S=d(t=>{_.request(()=>t,e(null))});l(()=>{let e=y.items();a(e,`checkbox or switch control`);let t=e.at(-1)?.node;t instanceof HTMLInputElement&&(t.indeterminate=f)});let C={owner:y,checked:v,disabled:c,indeterminate:f,name:n.name,noteInput:(e,t)=>{y.bind(e,t,void 0),b.bind(e,t)},readOnly:m,required:h,role:i,toggle:x,value:n.value};return{checked:v,control:()=>p(C),setChecked:S}}export{m as t};
2
+ //# sourceMappingURL=toggle-CIbzprg5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toggle-CIbzprg5.js","names":[],"sources":["../src/internal/toggle.ts"],"sourcesContent":["import {\n createMixin,\n type MixinContext,\n type MixinDescriptor,\n useBeforePaint,\n useMemo,\n useStableEvent,\n} from \"@bgub/fig\";\nimport { on } from \"@bgub/fig-dom\";\nimport { type ChangeDetails, createChangeDetails } from \"./changes.ts\";\nimport { useControllableValue } from \"./controllable-value.ts\";\nimport { assertSinglePart, expectHost } from \"./diagnostics.ts\";\nimport { createFormReset } from \"./form-reset.ts\";\nimport { bindPart } from \"./parts.ts\";\nimport { createPartCollection } from \"./registration.ts\";\nimport { useRegistrationReconcile } from \"./reconcile.ts\";\n\nexport type CheckedChangeDetails = ChangeDetails;\n\nexport type CheckedChangeHandler = (\n checked: boolean,\n details: CheckedChangeDetails,\n signal: AbortSignal,\n) => void;\n\nexport interface ToggleControlOptions {\n checked?: boolean;\n defaultChecked?: boolean;\n disabled?: boolean;\n /**\n * Neither checked nor unchecked. The platform has no attribute for it, so\n * this is the one part of the control the widget must write itself.\n */\n indeterminate?: boolean;\n name?: string;\n onCheckedChange?: CheckedChangeHandler;\n /** Prevents user changes while keeping the control focusable and submitted. */\n readOnly?: boolean;\n required?: boolean;\n value?: string;\n}\n\nexport interface ToggleControlParts {\n readonly checked: boolean;\n /** Applies to a native `<input type=\"checkbox\">`. */\n control(): MixinDescriptor;\n setChecked(checked: boolean): void;\n}\n\ninterface ToggleState {\n readonly owner: object;\n readonly checked: boolean;\n readonly disabled: boolean;\n readonly indeterminate: boolean;\n readonly name: string | undefined;\n readonly noteInput: (node: HTMLElement, signal: AbortSignal) => void;\n readonly readOnly: boolean;\n readonly required: boolean;\n readonly role: \"switch\" | undefined;\n readonly toggle: (checked: boolean, event: Event, node: Element) => void;\n readonly value: string | undefined;\n}\n\nconst toggleMixin = /* @__PURE__ */ createMixin(\n (context: MixinContext, state: ToggleState) => {\n expectHost(\n context,\n state.role === \"switch\" ? \"switch control\" : \"checkbox control\",\n \"input\",\n );\n return {\n bind: bindPart(context, state.owner, state.noteInput),\n checked: state.checked,\n \"data-checked\": state.checked ? \"\" : undefined,\n \"data-disabled\":\n state.disabled || context.props.disabled === true ? \"\" : undefined,\n \"data-indeterminate\": state.indeterminate ? \"\" : undefined,\n \"data-readonly\": state.readOnly ? \"\" : undefined,\n disabled: state.disabled ? true : context.props.disabled,\n // The browser has already toggled by the time this runs, so the control\n // reports what happened rather than deciding it.\n mix: [\n on(\"click\", (event) => {\n if (state.readOnly) event.preventDefault();\n }),\n on(\"change\", (event) => {\n const node = event.currentTarget;\n if (node instanceof HTMLInputElement) {\n state.toggle(node.checked, event, node);\n }\n }),\n ],\n name: context.props.name ?? state.name,\n \"aria-readonly\": state.readOnly ? \"true\" : undefined,\n required: state.required ? true : context.props.required,\n role: state.role,\n type: \"checkbox\",\n value: context.props.value ?? state.value,\n };\n },\n);\n\n/**\n * A native checkbox, shared by the checkbox and the switch.\n *\n * The platform owns toggling, focus, Space, form submission, and validity.\n * The widget owns the checked value it reports, and writes `indeterminate`,\n * which exists only as a property.\n */\nexport function useToggleControl(\n options: ToggleControlOptions,\n role: \"switch\" | undefined,\n): ToggleControlParts {\n const {\n disabled = false,\n indeterminate = false,\n readOnly = false,\n required = false,\n } = options;\n const requestReconcile = useRegistrationReconcile();\n const stateValue = useControllableValue({\n value: options.checked,\n defaultValue: options.defaultChecked === true,\n onChange: options.onCheckedChange,\n reconcile: requestReconcile,\n });\n const checked = stateValue.value;\n const input = useMemo(\n () => createPartCollection<undefined>(requestReconcile),\n [],\n );\n const formReset = useMemo(() => createFormReset(stateValue.reset), []);\n\n const toggle = useStableEvent(\n (next: boolean, event: Event, node: Element) => {\n if (\n event.defaultPrevented ||\n disabled ||\n node.matches(\":disabled\") ||\n readOnly\n ) {\n requestReconcile();\n return;\n }\n stateValue.request(() => next, createChangeDetails(event, node));\n },\n );\n\n const setChecked = useStableEvent((next: boolean) => {\n stateValue.request(() => next, createChangeDetails(null));\n });\n\n useBeforePaint(() => {\n const inputs = input.items();\n assertSinglePart(inputs, \"checkbox or switch control\");\n const node = inputs.at(-1)?.node;\n if (node instanceof HTMLInputElement) node.indeterminate = indeterminate;\n });\n\n const state: ToggleState = {\n owner: input,\n checked,\n disabled,\n indeterminate,\n name: options.name,\n noteInput: (node, signal) => {\n input.bind(node, signal, undefined);\n formReset.bind(node, signal);\n },\n readOnly,\n required,\n role,\n toggle,\n value: options.value,\n };\n\n return {\n checked,\n control: () => toggleMixin(state),\n setChecked,\n };\n}\n"],"mappings":"kXA+DA,MAAM,EAA8B,GACjC,EAAuB,KACtB,EACE,EACA,EAAM,OAAS,SAAW,iBAAmB,mBAC7C,OACF,EACO,CACL,KAAM,EAAS,EAAS,EAAM,MAAO,EAAM,SAAS,EACpD,QAAS,EAAM,QACf,eAAgB,EAAM,QAAU,GAAK,IAAA,GACrC,gBACE,EAAM,UAAY,EAAQ,MAAM,WAAa,GAAO,GAAK,IAAA,GAC3D,qBAAsB,EAAM,cAAgB,GAAK,IAAA,GACjD,gBAAiB,EAAM,SAAW,GAAK,IAAA,GACvC,SAAU,EAAM,SAAW,GAAO,EAAQ,MAAM,SAGhD,IAAK,CACH,EAAG,QAAU,GAAU,CACjB,EAAM,UAAU,EAAM,eAAe,CAC3C,CAAC,EACD,EAAG,SAAW,GAAU,CACtB,IAAM,EAAO,EAAM,cACf,aAAgB,kBAClB,EAAM,OAAO,EAAK,QAAS,EAAO,CAAI,CAE1C,CAAC,CACH,EACA,KAAM,EAAQ,MAAM,MAAQ,EAAM,KAClC,gBAAiB,EAAM,SAAW,OAAS,IAAA,GAC3C,SAAU,EAAM,SAAW,GAAO,EAAQ,MAAM,SAChD,KAAM,EAAM,KACZ,KAAM,WACN,MAAO,EAAQ,MAAM,OAAS,EAAM,KACtC,EAEJ,EASA,SAAgB,EACd,EACA,EACoB,CACpB,GAAM,CACJ,WAAW,GACX,gBAAgB,GAChB,WAAW,GACX,WAAW,IACT,EACE,EAAmB,EAAyB,EAC5C,EAAa,EAAqB,CACtC,MAAO,EAAQ,QACf,aAAc,EAAQ,iBAAmB,GACzC,SAAU,EAAQ,gBAClB,UAAW,CACb,CAAC,EACK,EAAU,EAAW,MACrB,EAAQ,MACN,EAAgC,CAAgB,EACtD,CAAC,CACH,EACM,EAAY,MAAc,EAAgB,EAAW,KAAK,EAAG,CAAC,CAAC,EAE/D,EAAS,GACZ,EAAe,EAAc,IAAkB,CAC9C,GACE,EAAM,kBACN,GACA,EAAK,QAAQ,WAAW,GACxB,EACA,CACA,EAAiB,EACjB,MACF,CACA,EAAW,YAAc,EAAM,EAAoB,EAAO,CAAI,CAAC,CACjE,CACF,EAEM,EAAa,EAAgB,GAAkB,CACnD,EAAW,YAAc,EAAM,EAAoB,IAAI,CAAC,CAC1D,CAAC,EAED,MAAqB,CACnB,IAAM,EAAS,EAAM,MAAM,EAC3B,EAAiB,EAAQ,4BAA4B,EACrD,IAAM,EAAO,EAAO,GAAG,EAAE,CAAC,EAAE,KACxB,aAAgB,mBAAkB,EAAK,cAAgB,EAC7D,CAAC,EAED,IAAM,EAAqB,CACzB,MAAO,EACP,UACA,WACA,gBACA,KAAM,EAAQ,KACd,WAAY,EAAM,IAAW,CAC3B,EAAM,KAAK,EAAM,EAAQ,IAAA,EAAS,EAClC,EAAU,KAAK,EAAM,CAAM,CAC7B,EACA,WACA,WACA,OACA,SACA,MAAO,EAAQ,KACjB,EAEA,MAAO,CACL,UACA,YAAe,EAAY,CAAK,EAChC,YACF,CACF"}
@@ -0,0 +1,2 @@
1
+ import{a as e,n as t}from"./parts-I0jdefyp.js";import{i as n,t as r}from"./composite-DoFLgUjR.js";import{createMixin as i,useBeforePaint as a,useMemo as o,useStableEvent as s,useState as c}from"@bgub/fig";import{on as l}from"@bgub/fig-dom";const u=i((e,n)=>({"aria-orientation":n.orientation,bind:t(e,n.registry,n.registry.bindContainer),"data-orientation":n.orientation,mix:[l(`click`,e=>{n.registry.itemAt(e.target)?.disabled===!0&&e.preventDefault()}),l(`focusin`,e=>{let t=n.registry.itemAt(e.target);t!==void 0&&!t.disabled&&n.setHighlighted(t.value)}),l(`keydown`,e=>{if(n.registry.itemAt(e.target)===void 0)return;let t=n.registry.moveFocus(e,{loop:n.loopFocus,orientation:n.orientation,skipDisabled:!0});t!==void 0&&n.setHighlighted(t.value)})],role:`toolbar`})),d=i((e,r,i)=>{let a=i.disabled||e.props.disabled===!0;return{"aria-disabled":a?`true`:void 0,bind:t(e,r.registry,(e,t)=>r.registry.bindItem(e,t,i.value,a)),"data-disabled":a?``:void 0,"data-fig-toolbar-item":``,disabled:e.type===`button`&&a?!0:e.props.disabled,tabindex:r.highlighted===null?a?-1:e.props.tabindex:n(r.highlighted,i.value)?0:-1,type:e.type===`button`?e.props.type??`button`:void 0}});function f(t={}){let{loopFocus:i=!0,orientation:l=`horizontal`}=t,f=e(),p=o(()=>r({container:`[role="toolbar"]`,item:`[data-fig-toolbar-item]`,name:`toolbar`,registrationChanged:f}),[]),[m,h]=c(()=>({value:null})),g=s(e=>{n(m.value,e)||h({value:e})});a(()=>{let e=p.item(m.value);e!==void 0&&!e.disabled||g(p.items().find(e=>!e.disabled)?.value??null)});let _={highlighted:m.value,loopFocus:i,orientation:l,registry:p,setHighlighted:g};return{item:(e,t={})=>d(_,{disabled:t.disabled===!0,value:e}),root:()=>u(_)}}function p(e){return e.children(f(e))}export{p as Toolbar,f as useToolbar};
2
+ //# sourceMappingURL=toolbar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolbar.js","names":[],"sources":["../src/toolbar/toolbar.tsx"],"sourcesContent":["import {\n createMixin,\n type FigNode,\n type MixinContext,\n type MixinDescriptor,\n useBeforePaint,\n useMemo,\n useStableEvent,\n useState,\n} from \"@bgub/fig\";\nimport { on } from \"@bgub/fig-dom\";\nimport {\n createComposite,\n type Orientation,\n sameValue,\n} from \"../internal/composite.ts\";\nimport { bindPart } from \"../internal/parts.ts\";\nimport { useRegistrationReconcile } from \"../internal/reconcile.ts\";\n\nexport type ToolbarOrientation = Orientation;\n\nexport interface ToolbarItemOptions {\n disabled?: boolean;\n}\n\nexport interface ToolbarOptions {\n /** Wraps arrow movement at both ends. Defaults to `true`. */\n loopFocus?: boolean;\n orientation?: ToolbarOrientation;\n}\n\nexport interface ToolbarParts<Value = unknown> {\n item(value: Value, options?: ToolbarItemOptions): MixinDescriptor;\n root(): MixinDescriptor;\n}\n\nexport interface ToolbarProps<Value = unknown> extends ToolbarOptions {\n children: (toolbar: ToolbarParts<Value>) => FigNode;\n}\n\ntype ToolbarRegistry = ReturnType<typeof createComposite>;\n\ninterface ToolbarState {\n readonly highlighted: unknown;\n readonly loopFocus: boolean;\n readonly orientation: Orientation;\n readonly registry: ToolbarRegistry;\n readonly setHighlighted: (value: unknown) => void;\n}\n\ninterface ToolbarItemState {\n readonly disabled: boolean;\n readonly value: unknown;\n}\n\nconst toolbarRootMixin = /* @__PURE__ */ createMixin(\n (context: MixinContext, state: ToolbarState) => ({\n \"aria-orientation\": state.orientation,\n bind: bindPart(context, state.registry, state.registry.bindContainer),\n \"data-orientation\": state.orientation,\n mix: [\n on(\"click\", (event) => {\n if (state.registry.itemAt(event.target)?.disabled === true) {\n event.preventDefault();\n }\n }),\n on(\"focusin\", (event) => {\n const item = state.registry.itemAt(event.target);\n if (item !== undefined && !item.disabled) {\n state.setHighlighted(item.value);\n }\n }),\n on(\"keydown\", (event) => {\n if (state.registry.itemAt(event.target) === undefined) return;\n const moved = state.registry.moveFocus(event, {\n loop: state.loopFocus,\n orientation: state.orientation,\n skipDisabled: true,\n });\n if (moved !== undefined) state.setHighlighted(moved.value);\n }),\n ],\n role: \"toolbar\",\n }),\n);\n\nconst toolbarItemMixin = /* @__PURE__ */ createMixin(\n (context: MixinContext, state: ToolbarState, own: ToolbarItemState) => {\n const disabled = own.disabled || context.props.disabled === true;\n return {\n \"aria-disabled\": disabled ? \"true\" : undefined,\n bind: bindPart(context, state.registry, (node, signal) =>\n state.registry.bindItem(node, signal, own.value, disabled),\n ),\n \"data-disabled\": disabled ? \"\" : undefined,\n \"data-fig-toolbar-item\": \"\",\n disabled:\n context.type === \"button\" && disabled ? true : context.props.disabled,\n tabindex:\n state.highlighted === null\n ? disabled\n ? -1\n : context.props.tabindex\n : sameValue(state.highlighted, own.value)\n ? 0\n : -1,\n type:\n context.type === \"button\"\n ? (context.props.type ?? \"button\")\n : undefined,\n };\n },\n);\n\n/** Coordinates one roving tab stop and arrow movement across a toolbar. */\nexport function useToolbar<Value = unknown>(\n options: ToolbarOptions = {},\n): ToolbarParts<Value> {\n const { loopFocus = true, orientation = \"horizontal\" } = options;\n const requestReconcile = useRegistrationReconcile();\n const registry = useMemo(\n () =>\n createComposite({\n container: '[role=\"toolbar\"]',\n item: \"[data-fig-toolbar-item]\",\n name: \"toolbar\",\n registrationChanged: requestReconcile,\n }),\n [],\n );\n const [highlighted, setHighlightedState] = useState<{\n readonly value: unknown;\n }>(() => ({ value: null }));\n const setHighlighted = useStableEvent((next: unknown) => {\n if (!sameValue(highlighted.value, next)) {\n setHighlightedState({ value: next });\n }\n });\n\n useBeforePaint(() => {\n const current = registry.item(highlighted.value);\n if (current !== undefined && !current.disabled) return;\n setHighlighted(\n registry.items().find((item) => !item.disabled)?.value ?? null,\n );\n });\n\n const state: ToolbarState = {\n highlighted: highlighted.value,\n loopFocus,\n orientation,\n registry,\n setHighlighted,\n };\n\n return {\n item: (value, itemOptions = {}) =>\n toolbarItemMixin(state, {\n disabled: itemOptions.disabled === true,\n value,\n }),\n root: () => toolbarRootMixin(state),\n };\n}\n\n/** {@link useToolbar} as a render-callback component. */\nexport function Toolbar<Value = unknown>(props: ToolbarProps<Value>): FigNode {\n return props.children(useToolbar<Value>(props));\n}\n"],"mappings":"gPAuDA,MAAM,EAAmC,GACtC,EAAuB,KAAyB,CAC/C,mBAAoB,EAAM,YAC1B,KAAM,EAAS,EAAS,EAAM,SAAU,EAAM,SAAS,aAAa,EACpE,mBAAoB,EAAM,YAC1B,IAAK,CACH,EAAG,QAAU,GAAU,CACjB,EAAM,SAAS,OAAO,EAAM,MAAM,CAAC,EAAE,WAAa,IACpD,EAAM,eAAe,CAEzB,CAAC,EACD,EAAG,UAAY,GAAU,CACvB,IAAM,EAAO,EAAM,SAAS,OAAO,EAAM,MAAM,EAC3C,IAAS,IAAA,IAAa,CAAC,EAAK,UAC9B,EAAM,eAAe,EAAK,KAAK,CAEnC,CAAC,EACD,EAAG,UAAY,GAAU,CACvB,GAAI,EAAM,SAAS,OAAO,EAAM,MAAM,IAAM,IAAA,GAAW,OACvD,IAAM,EAAQ,EAAM,SAAS,UAAU,EAAO,CAC5C,KAAM,EAAM,UACZ,YAAa,EAAM,YACnB,aAAc,EAChB,CAAC,EACG,IAAU,IAAA,IAAW,EAAM,eAAe,EAAM,KAAK,CAC3D,CAAC,CACH,EACA,KAAM,SACR,EACF,EAEM,EAAmC,GACtC,EAAuB,EAAqB,IAA0B,CACrE,IAAM,EAAW,EAAI,UAAY,EAAQ,MAAM,WAAa,GAC5D,MAAO,CACL,gBAAiB,EAAW,OAAS,IAAA,GACrC,KAAM,EAAS,EAAS,EAAM,UAAW,EAAM,IAC7C,EAAM,SAAS,SAAS,EAAM,EAAQ,EAAI,MAAO,CAAQ,CAC3D,EACA,gBAAiB,EAAW,GAAK,IAAA,GACjC,wBAAyB,GACzB,SACE,EAAQ,OAAS,UAAY,EAAW,GAAO,EAAQ,MAAM,SAC/D,SACE,EAAM,cAAgB,KAClB,EACE,GACA,EAAQ,MAAM,SAChB,EAAU,EAAM,YAAa,EAAI,KAAK,EACpC,EACA,GACR,KACE,EAAQ,OAAS,SACZ,EAAQ,MAAM,MAAQ,SACvB,IAAA,EACR,CACF,CACF,EAGA,SAAgB,EACd,EAA0B,CAAC,EACN,CACrB,GAAM,CAAE,YAAY,GAAM,cAAc,cAAiB,EACnD,EAAmB,EAAyB,EAC5C,EAAW,MAEb,EAAgB,CACd,UAAW,mBACX,KAAM,0BACN,KAAM,UACN,oBAAqB,CACvB,CAAC,EACH,CAAC,CACH,EACM,CAAC,EAAa,GAAuB,OAEjC,CAAE,MAAO,IAAK,EAAE,EACpB,EAAiB,EAAgB,GAAkB,CAClD,EAAU,EAAY,MAAO,CAAI,GACpC,EAAoB,CAAE,MAAO,CAAK,CAAC,CAEvC,CAAC,EAED,MAAqB,CACnB,IAAM,EAAU,EAAS,KAAK,EAAY,KAAK,EAC3C,IAAY,IAAA,IAAa,CAAC,EAAQ,UACtC,EACE,EAAS,MAAM,CAAC,CAAC,KAAM,GAAS,CAAC,EAAK,QAAQ,CAAC,EAAE,OAAS,IAC5D,CACF,CAAC,EAED,IAAM,EAAsB,CAC1B,YAAa,EAAY,MACzB,YACA,cACA,WACA,gBACF,EAEA,MAAO,CACL,MAAO,EAAO,EAAc,CAAC,IAC3B,EAAiB,EAAO,CACtB,SAAU,EAAY,WAAa,GACnC,OACF,CAAC,EACH,SAAY,EAAiB,CAAK,CACpC,CACF,CAGA,SAAgB,EAAyB,EAAqC,CAC5E,OAAO,EAAM,SAAS,EAAkB,CAAK,CAAC,CAChD"}
@@ -0,0 +1,2 @@
1
+ import{a as e,n as t}from"./parts-I0jdefyp.js";import{l as n}from"./diagnostics-BkQV0MaT.js";import{n as r,t as i}from"./popup-state-BUYfC2nG.js";import{t as a}from"./hover-HF7JXkR2.js";import{createMixin as o,useBeforePaint as s,useId as c,useMemo as l,useStableEvent as u}from"@bgub/fig";import{on as d}from"@bgub/fig-dom";const f=o((e,n)=>({"aria-describedby":g(e.props[`aria-describedby`],n.tooltipId),bind:t(e,n.bindTrigger,n.bindTrigger),"data-open":n.open?``:void 0,mix:[d(`focusin`,e=>{!n.disabled&&e.currentTarget instanceof Element&&n.schedule(!0,e,e.currentTarget,0)}),d(`focusout`,e=>{e.currentTarget instanceof Element&&n.schedule(!1,e,e.currentTarget,0)}),d(`pointerenter`,e=>{!n.disabled&&e.pointerType===`mouse`&&e.currentTarget instanceof Element&&n.schedule(!0,e,e.currentTarget,-1)}),d(`pointerleave`,e=>{e.pointerType===`mouse`&&e.currentTarget instanceof Element&&n.schedule(!1,e,e.currentTarget,-1)}),d(`keydown`,e=>{e.defaultPrevented||e.key!==`Escape`||!(e.currentTarget instanceof Element)||(n.schedule(void 0,e,e.currentTarget,0),n.requestOpen(!1,e,e.currentTarget))})]})),p=o((e,r)=>(n(e,r.tooltipId,`tooltip`),{bind:t(e,r.bindTrigger,r.bindPopup),"data-open":r.open?``:void 0,id:r.tooltipId,mix:[d(`beforetoggle`,e=>{r.nativeToggle(e)}),d(`toggle`,e=>{r.nativeToggle(e)}),d(`pointerenter`,e=>{e.pointerType===`mouse`&&e.currentTarget instanceof Element&&r.schedule(void 0,e,e.currentTarget,0)}),d(`pointerleave`,e=>{e.pointerType===`mouse`&&e.currentTarget instanceof Element&&r.schedule(!1,e,e.currentTarget,-1)})],popover:e.props.popover??`auto`,role:`tooltip`}));function m(t={}){let{closeDelay:n=0,delay:o=500,disabled:d=!1}=t,m=e(),h=l(()=>r(m,`tooltip`),[]),{getOpen:g,open:_,requestOpen:v,setOpen:y,nativeToggle:b}=i({...t,requestReconcile:m}),x=c(),S=t.id??`${x}-tooltip`,C=`--fig-tooltip-${x.replaceAll(/[^\w-]/g,`-`)}`,w=l(a,[]),T=u((e,t,r,i,a)=>{if(w.cancel(),e===void 0||e&&d)return;let s=i===-1?e?o:n:i,c=h.anchor();w.schedule(e,()=>{(!e||h.anchor()===c)&&v(e,t,r)},s,a)});s(()=>{d&&w.cancelOpening(),h.sync(g(),C)});let E={bindPopup:h.bindPopup,bindTrigger:h.bindAnchor,disabled:d,open:_,requestOpen:v,nativeToggle:b,schedule:(e,t,n,r)=>{let i=h.anchor();(t.type===`pointerenter`||t.type===`pointerleave`)&&i?.contains(i.ownerDocument.activeElement)||T(e,t,n,r)},tooltipId:S};return{open:_,setOpen:y,tooltip:()=>p(E),trigger:()=>f(E)}}function h(e){return e.children(m(e))}function g(e,t){return[...new Set([...e?.split(/\s+/)??[],t])].filter(Boolean).join(` `)}export{h as Tooltip,m as useTooltip};
2
+ //# sourceMappingURL=tooltip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tooltip.js","names":[],"sources":["../src/tooltip/tooltip.tsx"],"sourcesContent":["import {\n createMixin,\n type FigNode,\n type MixinContext,\n type MixinDescriptor,\n useBeforePaint,\n useId,\n useMemo,\n useStableEvent,\n} from \"@bgub/fig\";\nimport { on } from \"@bgub/fig-dom\";\nimport { createAnchoredPopup } from \"../internal/anchored-popup.ts\";\nimport { createHoverIntent } from \"../internal/hover.ts\";\nimport { expectPopupId } from \"../internal/diagnostics.ts\";\nimport type {\n OpenChangeDetails,\n OpenChangeHandler,\n} from \"../internal/open-state.ts\";\nimport { usePopupState } from \"../internal/popup-state.ts\";\nimport { bindPart } from \"../internal/parts.ts\";\nimport { useRegistrationReconcile } from \"../internal/reconcile.ts\";\n\nexport type TooltipOpenChangeDetails = OpenChangeDetails;\nexport type TooltipOpenChangeHandler = OpenChangeHandler;\n\nexport interface TooltipOptions {\n /** Delay before pointer intent opens the tooltip. Defaults to 500ms. */\n delay?: number;\n /** Delay before pointer exit closes the tooltip. Defaults to 0ms. */\n closeDelay?: number;\n defaultOpen?: boolean;\n disabled?: boolean;\n id?: string;\n onOpenChange?: TooltipOpenChangeHandler;\n open?: boolean;\n}\n\nexport interface TooltipParts {\n readonly open: boolean;\n setOpen(open: boolean): void;\n tooltip(): MixinDescriptor;\n trigger(): MixinDescriptor;\n}\n\nexport interface TooltipProps extends TooltipOptions {\n children: (tooltip: TooltipParts) => FigNode;\n}\n\ninterface TooltipState {\n readonly bindPopup: (node: HTMLElement, signal: AbortSignal) => void;\n readonly bindTrigger: (node: HTMLElement, signal: AbortSignal) => void;\n readonly disabled: boolean;\n readonly nativeToggle: (event: Event) => void;\n readonly open: boolean;\n readonly requestOpen: (\n open: boolean,\n event: Event,\n trigger: Element | undefined,\n ) => boolean;\n readonly schedule: (\n open: boolean | undefined,\n event: Event,\n trigger: Element,\n delay: number,\n ) => void;\n readonly tooltipId: string;\n}\n\nconst tooltipTriggerMixin = /* @__PURE__ */ createMixin(\n (context: MixinContext, state: TooltipState) => ({\n \"aria-describedby\": references(\n context.props[\"aria-describedby\"],\n state.tooltipId,\n ),\n bind: bindPart(context, state.bindTrigger, state.bindTrigger),\n \"data-open\": state.open ? \"\" : undefined,\n mix: [\n on(\"focusin\", (event) => {\n if (!state.disabled && event.currentTarget instanceof Element) {\n state.schedule(true, event, event.currentTarget, 0);\n }\n }),\n on(\"focusout\", (event) => {\n if (event.currentTarget instanceof Element) {\n state.schedule(false, event, event.currentTarget, 0);\n }\n }),\n on(\"pointerenter\", (event) => {\n if (\n !state.disabled &&\n event.pointerType === \"mouse\" &&\n event.currentTarget instanceof Element\n ) {\n state.schedule(true, event, event.currentTarget, -1);\n }\n }),\n on(\"pointerleave\", (event) => {\n if (\n event.pointerType === \"mouse\" &&\n event.currentTarget instanceof Element\n ) {\n state.schedule(false, event, event.currentTarget, -1);\n }\n }),\n on(\"keydown\", (event) => {\n if (\n event.defaultPrevented ||\n event.key !== \"Escape\" ||\n !(event.currentTarget instanceof Element)\n ) {\n return;\n }\n state.schedule(undefined, event, event.currentTarget, 0);\n state.requestOpen(false, event, event.currentTarget);\n }),\n ],\n }),\n);\n\nconst tooltipMixin = /* @__PURE__ */ createMixin(\n (context: MixinContext, state: TooltipState) => {\n expectPopupId(context, state.tooltipId, \"tooltip\");\n return {\n bind: bindPart(context, state.bindTrigger, state.bindPopup),\n \"data-open\": state.open ? \"\" : undefined,\n id: state.tooltipId,\n mix: [\n on(\"beforetoggle\", (event) => {\n state.nativeToggle(event);\n }),\n on(\"toggle\", (event) => {\n state.nativeToggle(event);\n }),\n on(\"pointerenter\", (event) => {\n if (\n event.pointerType === \"mouse\" &&\n event.currentTarget instanceof Element\n ) {\n state.schedule(undefined, event, event.currentTarget, 0);\n }\n }),\n on(\"pointerleave\", (event) => {\n if (\n event.pointerType === \"mouse\" &&\n event.currentTarget instanceof Element\n ) {\n state.schedule(false, event, event.currentTarget, -1);\n }\n }),\n ],\n popover: context.props.popover ?? \"auto\",\n role: \"tooltip\",\n };\n },\n);\n\n/** Coordinates one non-interactive tooltip over caller-owned hosts. */\nexport function useTooltip(options: TooltipOptions = {}): TooltipParts {\n const { closeDelay = 0, delay = 500, disabled = false } = options;\n const requestReconcile = useRegistrationReconcile();\n const registry = useMemo(\n () => createAnchoredPopup(requestReconcile, \"tooltip\"),\n [],\n );\n const { getOpen, open, requestOpen, setOpen, nativeToggle } = usePopupState({\n ...options,\n requestReconcile,\n });\n const id = useId();\n const tooltipId = options.id ?? `${id}-tooltip`;\n const anchorName = `--fig-tooltip-${id.replaceAll(/[^\\w-]/g, \"-\")}`;\n const intent = useMemo(createHoverIntent, []);\n\n const schedule = useStableEvent(\n (\n next: boolean | undefined,\n event: Event,\n trigger: Element,\n requestedDelay: number,\n signal: AbortSignal,\n ) => {\n intent.cancel();\n if (next === undefined || (next && disabled)) return;\n const wait =\n requestedDelay === -1 ? (next ? delay : closeDelay) : requestedDelay;\n const anchor = registry.anchor();\n intent.schedule(\n next,\n () => {\n // Same-host rebinding preserves intent; replacement invalidates it.\n if (!next || registry.anchor() === anchor)\n requestOpen(next, event, trigger);\n },\n wait,\n signal,\n );\n },\n );\n\n useBeforePaint(() => {\n if (disabled) intent.cancelOpening();\n registry.sync(getOpen(), anchorName);\n });\n\n const state: TooltipState = {\n bindPopup: registry.bindPopup,\n bindTrigger: registry.bindAnchor,\n disabled,\n open,\n requestOpen,\n nativeToggle,\n schedule: (next, event, trigger, wait) => {\n const anchor = registry.anchor();\n // Ignore hover before invoking the stable event, which aborts any\n // pending focus timer even when its callback returns immediately.\n if (\n (event.type === \"pointerenter\" || event.type === \"pointerleave\") &&\n anchor?.contains(anchor.ownerDocument.activeElement)\n )\n return;\n schedule(next, event, trigger, wait);\n },\n tooltipId,\n };\n\n return {\n open,\n setOpen,\n tooltip: () => tooltipMixin(state),\n trigger: () => tooltipTriggerMixin(state),\n };\n}\n\n/** {@link useTooltip} as a render-callback component. */\nexport function Tooltip(props: TooltipProps): FigNode {\n return props.children(useTooltip(props));\n}\n\nfunction references(authored: string | undefined, generated: string): string {\n return [...new Set([...(authored?.split(/\\s+/) ?? []), generated])]\n .filter(Boolean)\n .join(\" \");\n}\n"],"mappings":"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"}