@weasel-js/ui 1.7.1 → 1.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/dist/chunks/{BandEditor-Dd0egAgE.js → BandEditor-B4jUAHQ8.js} +13 -13
  2. package/dist/chunks/BandEditor-B4jUAHQ8.js.map +1 -0
  3. package/dist/chunks/{ColorField-BLs9fbeq.js → ColorField-BwlelY26.js} +4 -4
  4. package/dist/chunks/ColorField-BwlelY26.js.map +1 -0
  5. package/dist/chunks/{ComboBox-CuTLRmUg.js → ComboBox-DKln_mOd.js} +11 -11
  6. package/dist/chunks/ComboBox-DKln_mOd.js.map +1 -0
  7. package/dist/chunks/{CurveEditor-jVBEqqdv.js → CurveEditor-0EE00D6s.js} +2 -2
  8. package/dist/chunks/{CurveEditor-jVBEqqdv.js.map → CurveEditor-0EE00D6s.js.map} +1 -1
  9. package/dist/chunks/{DataGrid-BklgBqS0.js → DataGrid-CmHJe57v.js} +22 -17
  10. package/dist/chunks/DataGrid-CmHJe57v.js.map +1 -0
  11. package/dist/chunks/DetailList-BpU50XVe.js +54 -0
  12. package/dist/chunks/DetailList-BpU50XVe.js.map +1 -0
  13. package/dist/chunks/{DetentSlider-Bh7EpMqT.js → DetentSlider-DNTAk8xP.js} +2 -2
  14. package/dist/chunks/{DetentSlider-Bh7EpMqT.js.map → DetentSlider-DNTAk8xP.js.map} +1 -1
  15. package/dist/chunks/{FallthroughDiagram-C7BUe8eU.js → FallthroughDiagram-CS1IhQaU.js} +14 -14
  16. package/dist/chunks/FallthroughDiagram-CS1IhQaU.js.map +1 -0
  17. package/dist/chunks/{Field-DsBlnFKf.js → Field-DiX_WByH.js} +2 -2
  18. package/dist/chunks/{Field-DsBlnFKf.js.map → Field-DiX_WByH.js.map} +1 -1
  19. package/dist/chunks/{FillStrokeSwatch-CgsYQgew.js → FillStrokeSwatch-C4h3rUFM.js} +2 -2
  20. package/dist/chunks/FillStrokeSwatch-C4h3rUFM.js.map +1 -0
  21. package/dist/chunks/{FontFamilySelect-CGJ5OIOy.js → FontFamilySelect-DEvMXyZd.js} +2 -2
  22. package/dist/chunks/{FontFamilySelect-CGJ5OIOy.js.map → FontFamilySelect-DEvMXyZd.js.map} +1 -1
  23. package/dist/chunks/{FontWeightSelect-RbcXYJPS.js → FontWeightSelect-BLQ_LMJF.js} +2 -2
  24. package/dist/chunks/{FontWeightSelect-RbcXYJPS.js.map → FontWeightSelect-BLQ_LMJF.js.map} +1 -1
  25. package/dist/chunks/{GradientEditor-DypXzMc-.js → GradientEditor-DD2OjQtn.js} +4 -4
  26. package/dist/chunks/{GradientEditor-DypXzMc-.js.map → GradientEditor-DD2OjQtn.js.map} +1 -1
  27. package/dist/chunks/{InlineRange-P01ABSzo.js → InlineRange-DPjIsJB3.js} +3 -3
  28. package/dist/chunks/InlineRange-DPjIsJB3.js.map +1 -0
  29. package/dist/chunks/{Input-CzwvCZs_.js → Input-D7SDKbST.js} +2 -2
  30. package/dist/chunks/{Input-CzwvCZs_.js.map → Input-D7SDKbST.js.map} +1 -1
  31. package/dist/chunks/{Jog-DfNUYejm.js → Jog-BrkzdGV2.js} +6 -6
  32. package/dist/chunks/Jog-BrkzdGV2.js.map +1 -0
  33. package/dist/chunks/{LayerList-CsDqwYh-.js → LayerList-BqeWFuGc.js} +3 -3
  34. package/dist/chunks/{LayerList-CsDqwYh-.js.map → LayerList-BqeWFuGc.js.map} +1 -1
  35. package/dist/chunks/{ListEditor-BnGzqERm.js → ListEditor-OQDzG4A5.js} +2 -2
  36. package/dist/chunks/{ListEditor-BnGzqERm.js.map → ListEditor-OQDzG4A5.js.map} +1 -1
  37. package/dist/chunks/{MenuButton-L9f5vido.js → MenuButton-TAFg88Lp.js} +2 -2
  38. package/dist/chunks/{MenuButton-L9f5vido.js.map → MenuButton-TAFg88Lp.js.map} +1 -1
  39. package/dist/chunks/{MeshEditor-GbqqiPk8.js → MeshEditor-DWpvg8H6.js} +2 -2
  40. package/dist/chunks/{MeshEditor-GbqqiPk8.js.map → MeshEditor-DWpvg8H6.js.map} +1 -1
  41. package/dist/chunks/{NumberField-DLXXgh48.js → NumberField-DS0WSiin.js} +7 -7
  42. package/dist/chunks/NumberField-DS0WSiin.js.map +1 -0
  43. package/dist/chunks/{PaintField-C0hoXc0Y.js → PaintField-BdmQM53v.js} +2 -2
  44. package/dist/chunks/{PaintField-C0hoXc0Y.js.map → PaintField-BdmQM53v.js.map} +1 -1
  45. package/dist/chunks/{PaintInput-jQRxM95Y.js → PaintInput-DaQMeBsI.js} +4 -4
  46. package/dist/chunks/{PaintInput-jQRxM95Y.js.map → PaintInput-DaQMeBsI.js.map} +1 -1
  47. package/dist/chunks/{PatternPicker-CaMYXzhF.js → PatternPicker-B3HcMZWg.js} +2 -2
  48. package/dist/chunks/{PatternPicker-CaMYXzhF.js.map → PatternPicker-B3HcMZWg.js.map} +1 -1
  49. package/dist/chunks/{Plot2D-BHNbgCAl.js → Plot2D-BrvQS0dh.js} +7 -7
  50. package/dist/chunks/Plot2D-BrvQS0dh.js.map +1 -0
  51. package/dist/chunks/{PointPlotter-BZ_vk6gV.js → PointPlotter-BBXpQ_QD.js} +2 -2
  52. package/dist/chunks/{PointPlotter-BZ_vk6gV.js.map → PointPlotter-BBXpQ_QD.js.map} +1 -1
  53. package/dist/chunks/{Prefs-CkyldX-e.js → Prefs-BnVEyfWO.js} +76 -66
  54. package/dist/chunks/Prefs-BnVEyfWO.js.map +1 -0
  55. package/dist/chunks/{Properties-D15c8NP-.js → Properties-BoG2MMHR.js} +24 -24
  56. package/dist/chunks/{Properties-D15c8NP-.js.map → Properties-BoG2MMHR.js.map} +1 -1
  57. package/dist/chunks/{PropertyField-CV_Gw57S.js → PropertyField-PMrqBA0n.js} +243 -186
  58. package/dist/chunks/PropertyField-PMrqBA0n.js.map +1 -0
  59. package/dist/chunks/{RadioGroup-4dbzqvGy.js → RadioGroup-B70_M-gT.js} +2 -2
  60. package/dist/chunks/{RadioGroup-4dbzqvGy.js.map → RadioGroup-B70_M-gT.js.map} +1 -1
  61. package/dist/chunks/{RangeSlider-BxokZViJ.js → RangeSlider-DO5TZVk7.js} +8 -8
  62. package/dist/chunks/RangeSlider-DO5TZVk7.js.map +1 -0
  63. package/dist/chunks/{Select-BppLsJTz.js → Select-ouV8O6cp.js} +19 -19
  64. package/dist/chunks/Select-ouV8O6cp.js.map +1 -0
  65. package/dist/chunks/{SelectionPanel-Cm1qu-ZX.js → SelectionPanel-COZ6z9_P.js} +2 -2
  66. package/dist/chunks/{SelectionPanel-Cm1qu-ZX.js.map → SelectionPanel-COZ6z9_P.js.map} +1 -1
  67. package/dist/chunks/{Slider-Cl5RCeZ7.js → Slider-DSrlMEq8.js} +19 -19
  68. package/dist/chunks/Slider-DSrlMEq8.js.map +1 -0
  69. package/dist/chunks/{StatusBar-Ca9Dvhhq.js → StatusBar-BEOfOX0D.js} +6 -6
  70. package/dist/chunks/StatusBar-BEOfOX0D.js.map +1 -0
  71. package/dist/chunks/{SwatchStrip-Cw0l8Om7.js → SwatchStrip-Bi9iPLoh.js} +2 -2
  72. package/dist/chunks/{SwatchStrip-Cw0l8Om7.js.map → SwatchStrip-Bi9iPLoh.js.map} +1 -1
  73. package/dist/chunks/{Timeline-pqnYk5cV.js → Timeline-sMCH2fah.js} +99 -84
  74. package/dist/chunks/Timeline-sMCH2fah.js.map +1 -0
  75. package/dist/chunks/{TokenPanel-DAkL6vwQ.js → TokenPanel-WtKGY2Db.js} +29 -29
  76. package/dist/chunks/TokenPanel-WtKGY2Db.js.map +1 -0
  77. package/dist/chunks/{ToolOptionsBar-COsTJ1cu.js → ToolOptionsBar-BJKwnemU.js} +2 -2
  78. package/dist/chunks/{ToolOptionsBar-COsTJ1cu.js.map → ToolOptionsBar-BJKwnemU.js.map} +1 -1
  79. package/dist/chunks/{createKeyframeLayer-BTa4LyJE.js → createKeyframeLayer-DSVf13Su.js} +2 -2
  80. package/dist/chunks/{createKeyframeLayer-BTa4LyJE.js.map → createKeyframeLayer-DSVf13Su.js.map} +1 -1
  81. package/dist/chunks/field.module-B27pWv0i.js +15 -0
  82. package/dist/chunks/{field.module-Bh7BTKMh.js.map → field.module-B27pWv0i.js.map} +1 -1
  83. package/dist/chunks/{model-B1-gMBIW.js → model-B6zuQg6b.js} +2 -2
  84. package/dist/chunks/{model-B1-gMBIW.js.map → model-B6zuQg6b.js.map} +1 -1
  85. package/dist/components/BandEditor/index.js +1 -1
  86. package/dist/components/ColorField/index.js +1 -1
  87. package/dist/components/ComboBox/index.js +1 -1
  88. package/dist/components/CurveEditor/index.js +2 -2
  89. package/dist/components/DataGrid/DataGrid.d.ts +3 -0
  90. package/dist/components/DataGrid/DataGrid.d.ts.map +1 -1
  91. package/dist/components/DataGrid/index.js +1 -1
  92. package/dist/components/DetailList/DetailList.d.ts +1 -1
  93. package/dist/components/DetailList/DetailList.d.ts.map +1 -1
  94. package/dist/components/DetailList/index.js +1 -1
  95. package/dist/components/DetentSlider/index.js +1 -1
  96. package/dist/components/FallthroughDiagram/index.js +1 -1
  97. package/dist/components/Field/index.js +1 -1
  98. package/dist/components/FillStrokeSwatch/FillStrokeSwatch.d.ts +5 -2
  99. package/dist/components/FillStrokeSwatch/FillStrokeSwatch.d.ts.map +1 -1
  100. package/dist/components/FillStrokeSwatch/index.js +1 -1
  101. package/dist/components/FontFamilySelect/index.js +1 -1
  102. package/dist/components/FontWeightSelect/index.js +1 -1
  103. package/dist/components/GradientEditor/index.js +1 -1
  104. package/dist/components/InlineRange/index.js +1 -1
  105. package/dist/components/Input/index.js +1 -1
  106. package/dist/components/Jog/index.js +1 -1
  107. package/dist/components/LayerList/index.js +1 -1
  108. package/dist/components/ListEditor/index.js +1 -1
  109. package/dist/components/MenuButton/index.js +1 -1
  110. package/dist/components/MeshEditor/index.js +1 -1
  111. package/dist/components/NumberField/index.js +1 -1
  112. package/dist/components/PaintField/index.js +1 -1
  113. package/dist/components/PaintInput/index.js +1 -1
  114. package/dist/components/PatternPicker/index.js +1 -1
  115. package/dist/components/Plot2D/index.js +1 -1
  116. package/dist/components/PointPlotter/index.js +1 -1
  117. package/dist/components/Prefs/PrefsForm.d.ts +14 -0
  118. package/dist/components/Prefs/PrefsForm.d.ts.map +1 -1
  119. package/dist/components/Prefs/PrefsRow.d.ts +4 -0
  120. package/dist/components/Prefs/PrefsRow.d.ts.map +1 -1
  121. package/dist/components/Prefs/index.js +1 -1
  122. package/dist/components/Properties/PropertyField.d.ts.map +1 -1
  123. package/dist/components/Properties/PropertyPanel.d.ts +18 -1
  124. package/dist/components/Properties/PropertyPanel.d.ts.map +1 -1
  125. package/dist/components/Properties/index.d.ts +2 -2
  126. package/dist/components/Properties/index.d.ts.map +1 -1
  127. package/dist/components/Properties/index.js +3 -3
  128. package/dist/components/RadioGroup/index.js +1 -1
  129. package/dist/components/RangeSlider/index.js +1 -1
  130. package/dist/components/Select/index.js +1 -1
  131. package/dist/components/SelectionPanel/index.js +2 -2
  132. package/dist/components/Slider/index.js +1 -1
  133. package/dist/components/StatusBar/StatusBar.d.ts +1 -1
  134. package/dist/components/StatusBar/index.js +1 -1
  135. package/dist/components/SwatchStrip/index.js +1 -1
  136. package/dist/components/Timeline/Transport.d.ts.map +1 -1
  137. package/dist/components/Timeline/index.js +1 -1
  138. package/dist/components/TokenPanel/index.js +1 -1
  139. package/dist/components/ToolOptionsBar/index.js +1 -1
  140. package/dist/index.js +75 -75
  141. package/dist/index.js.map +1 -1
  142. package/dist/style.css +1 -1
  143. package/package.json +9 -9
  144. package/dist/chunks/BandEditor-Dd0egAgE.js.map +0 -1
  145. package/dist/chunks/ColorField-BLs9fbeq.js.map +0 -1
  146. package/dist/chunks/ComboBox-CuTLRmUg.js.map +0 -1
  147. package/dist/chunks/DataGrid-BklgBqS0.js.map +0 -1
  148. package/dist/chunks/DetailList-DFtqdOJj.js +0 -52
  149. package/dist/chunks/DetailList-DFtqdOJj.js.map +0 -1
  150. package/dist/chunks/FallthroughDiagram-C7BUe8eU.js.map +0 -1
  151. package/dist/chunks/FillStrokeSwatch-CgsYQgew.js.map +0 -1
  152. package/dist/chunks/InlineRange-P01ABSzo.js.map +0 -1
  153. package/dist/chunks/Jog-DfNUYejm.js.map +0 -1
  154. package/dist/chunks/NumberField-DLXXgh48.js.map +0 -1
  155. package/dist/chunks/Plot2D-BHNbgCAl.js.map +0 -1
  156. package/dist/chunks/Prefs-CkyldX-e.js.map +0 -1
  157. package/dist/chunks/PropertyField-CV_Gw57S.js.map +0 -1
  158. package/dist/chunks/RangeSlider-BxokZViJ.js.map +0 -1
  159. package/dist/chunks/Select-BppLsJTz.js.map +0 -1
  160. package/dist/chunks/Slider-Cl5RCeZ7.js.map +0 -1
  161. package/dist/chunks/StatusBar-Ca9Dvhhq.js.map +0 -1
  162. package/dist/chunks/Timeline-pqnYk5cV.js.map +0 -1
  163. package/dist/chunks/TokenPanel-DAkL6vwQ.js.map +0 -1
  164. package/dist/chunks/field.module-Bh7BTKMh.js +0 -15
@@ -1 +0,0 @@
1
- {"version":3,"file":"Select-BppLsJTz.js","names":["Select","RACSelect","f","s","RACButton","RACPopover","RACListBox","listbox","_Fragment","RACListBoxSection","RACListBoxItem","useOverlayPortal","TriggerTooltip","segmentTooltipContent","field_module_default","listbox_module_default","Select_module_default","isGroup","entry","Select$1","variant","rest","props","portalContainer","triggerRef","useRef","pressRef","nudge","setNudge","useState","NO_NUDGE","Select","onSelectionChange","k","width","indicator","orientation","className","anchor","label","Label","Button","triggerId","SelectValue","isPlaceholder","placeholder","defaultChildren","optionLabels","options","children","l","i","description","Text","FieldError","errorMessage","Popover","popup","portalProps","AlignOverTrigger","ListBox","SelectSection","renderOption","CLICK_MS","EDGE","BELOW","markerRef","passes","useLayoutEffect","trigger","popover","value","applied","onMeasure","align","toEnd","box","seat","row","shown","over","lowest","next","useEffect","doc","onUp","press","pastDragThreshold","CheckMark","SelectItem","o","Children","isValidElement","c","withIcon","g","icon","Fragment","ListBoxSection","title","Header","textValueOf","explicit","ListBoxItem","textValue"],"sources":["../../src/components/Select/Select.module.css","../../src/components/Select/Select.tsx"],"sourcesContent":["/* `orientation='row'`: the shared layout sets the trigger beside its label; a\n fit one keeps its own width there rather than the fill width below. */\n.row.fit .trigger { width: auto; }\n\n.trigger {\n display: flex;\n width: 100%;\n align-items: center;\n gap: var(--wzl-space-3);\n /* Hooks, each defaulting to what the frame already draws: the visible box is\n this button, not the root a consumer's `className` lands on, so a surface\n that has to restyle it sets these rather than reaching in here. Together\n they draw the bare look too, for a surface that wants it from context\n rather than from `variant`: a transparent fill, no padding, the chevron\n hidden, the value underlined and an outline for focus. */\n padding: var(--wzl-select-pad, 0 var(--wzl-space-3) 0 var(--wzl-space-5));\n --_frame-h: var(--wzl-select-h);\n --_frame-bg: var(--wzl-select-bg);\n --_frame-border: var(--wzl-select-border);\n --_frame-fg: var(--wzl-select-fg, var(--wzl-fg));\n --_frame-focus-border: var(--wzl-select-focus-border);\n --_frame-focus-shadow: var(--wzl-select-focus-shadow);\n --_frame-focus-outline: var(--wzl-select-focus-outline);\n cursor: pointer;\n}\n\n.trigger[data-focus-visible] {\n outline-offset: 2px;\n}\n\n.value {\n flex: 1;\n min-width: 0;\n text-align: var(--wzl-select-align, left);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n /* The ellipsis needs `overflow: hidden`, which makes the line box a clip —\n the trigger's `line-height: 1` would cut this text's ascenders and\n descenders off. `normal` is the font's own leading, so it never can. */\n line-height: normal;\n /* The underline hook: none unless a surface or `indicator` turns it on. */\n text-decoration-line: var(--wzl-select-underline, none);\n text-decoration-style: dotted;\n text-decoration-thickness: 1px;\n text-decoration-color: var(--wzl-fg-subtle);\n text-underline-offset: 0.25em;\n transition: text-decoration-color var(--wzl-motion-fast);\n}\n\n.trigger:hover .value,\n.trigger[data-focus-visible] .value {\n text-decoration-style: solid;\n text-decoration-color: var(--wzl-fg-muted);\n}\n\n.select[data-open] .value {\n text-decoration-style: solid;\n text-decoration-color: var(--wzl-accent);\n}\n\n/* `variant='bare'`: no box. The focus ring moves to an outline, since there is no border to recolor. */\n.bare .trigger {\n padding: 0;\n background: transparent;\n border: var(--wzl-select-border, 1px solid transparent);\n}\n\n.bare .trigger[data-focus-visible] {\n border-color: transparent;\n box-shadow: none;\n outline: 1px solid var(--wzl-focus-ring);\n outline-offset: 2px;\n}\n\n.value[data-placeholder] {\n color: var(--wzl-fg-subtle);\n}\n\n.chevron {\n display: var(--wzl-select-chevron, block);\n width: 10px;\n height: 10px;\n color: var(--wzl-fg-muted);\n flex: 0 0 auto;\n}\n\n/* `indicator='underline'`: the value carries the affordance itself. A text\n decoration rather than a border, so it hugs the word and adds nothing to the\n box — the row's height and the value's width are the same either way. The\n rest of the underline's look sits on `.value`, shared with the hook. */\n.underlined .value {\n text-decoration-line: underline;\n}\n\n.value[data-placeholder] {\n text-decoration-color: var(--wzl-fg-subtle);\n}\n\n/* Nothing sits beside the value, so the row's gap and the padding kept for a\n caret are the value's to spend. */\n.underlined .trigger,\n.plain .trigger {\n gap: 0;\n}\n\n.bare.underlined .trigger,\n.bare.plain .trigger {\n padding: 0;\n}\n\n/* A list that hangs below its trigger takes the trigger's width, so the two\n read as one control. One that opens over it is already tied to the trigger\n by its selected row — widening it to match would only add blank rule beside\n the labels. `data-align` is set on exactly the ones that do. */\n.popover:not([data-align]) {\n min-width: var(--trigger-width);\n}\n\n.popover {\n max-height: inherit;\n outline: none;\n}\n\n.option {\n display: flex;\n align-items: center;\n gap: var(--wzl-space-4);\n padding: var(--wzl-space-2) var(--wzl-space-4);\n border-radius: var(--wzl-radius-sm);\n cursor: pointer;\n color: var(--wzl-fg);\n outline: none;\n}\n\n:is(.option, .section) + .section,\n.section + .option {\n margin-top: var(--wzl-space-2);\n padding-top: var(--wzl-space-2);\n border-top: 1px solid var(--wzl-line-subtle);\n}\n\n.sectionHeader {\n padding: var(--wzl-space-2) var(--wzl-space-4);\n font-size: var(--wzl-font-size-sm);\n font-weight: var(--wzl-font-weight-medium);\n color: var(--wzl-fg-subtle);\n}\n\n.optionLabel {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.optionIcon {\n display: inline-flex;\n vertical-align: middle;\n margin-inline-end: var(--wzl-space-3);\n}\n\n.optionIcon > svg {\n display: block;\n}\n\n/* A trigger that sets its value against its end edge gets a list that does the\n same, so the row that opened over the value stays on top of it. */\n.popover[data-align='end'] .option {\n justify-content: flex-end;\n}\n\n.option[data-focused] {\n background: var(--wzl-surface-hover);\n}\n\n.option[data-selected] {\n color: var(--wzl-fg);\n font-weight: var(--wzl-font-weight-medium);\n}\n\n.option[data-disabled] {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.check {\n width: 10px;\n height: 10px;\n flex: 0 0 auto;\n opacity: 0;\n}\n\n.option[data-selected] .check {\n opacity: 1;\n}\n\n.value .check {\n display: none;\n}\n\n/* `width='fit'`: a hidden stack of every option label shares the value's grid\n cell, so the trigger sizes to its widest option and never resizes on select. */\n.fit .trigger {\n display: grid;\n grid-template-columns: auto auto;\n}\n\n.fit.underlined .trigger,\n.fit.plain .trigger {\n grid-template-columns: auto;\n}\n\n.fit .value,\n.fit .sizer {\n grid-area: 1 / 1;\n}\n\n.fit .chevron {\n grid-area: 1 / 2;\n}\n\n.sizer {\n display: grid;\n height: 0;\n overflow: hidden;\n visibility: hidden;\n white-space: nowrap;\n}\n\n.sizer > * {\n grid-area: 1 / 1;\n}\n","import {\n Children,\n isValidElement,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type ReactNode,\n type RefObject,\n} from 'react';\nimport {\n Select as RACSelect,\n Label,\n Button as RACButton,\n SelectValue,\n Popover as RACPopover,\n ListBox as RACListBox,\n ListBoxItem as RACListBoxItem,\n ListBoxSection as RACListBoxSection,\n Header,\n Text,\n FieldError,\n type SelectProps as RACSelectProps,\n type ListBoxItemProps as RACListBoxItemProps,\n type ValidationResult,\n} from 'react-aria-components';\nimport { pastDragThreshold } from '@weasel-js/core';\nimport type { FieldOrientation } from '../Field/Field';\nimport { useOverlayPortal, type OverlayPortalProps } from '../../overlays/portalHost';\nimport f from '../field.module.css';\nimport listbox from '../listbox.module.css';\nimport s from './Select.module.css';\nimport { TriggerTooltip, segmentTooltipContent, type SegmentTooltipFields } from '../segmentTooltip';\n\n/** One option in a {@link Select}'s `options` list. */\nexport type SelectOption = {\n value: string;\n label: ReactNode;\n isDisabled?: boolean;\n /** A glyph drawn before `label`, in the list and in the trigger. */\n icon?: ReactNode;\n /**\n * Plain-text form of `label`, for type-to-select and screen readers.\n * Only needed when `label` isn't a bare string — a label built from\n * elements has no text React Aria can read off it. A string label\n * supplies this itself.\n */\n textValue?: string;\n};\n\n/** A titled group of options in a {@link Select}'s `options` list. */\nexport type SelectOptionGroup<T extends Key = string> = {\n title: ReactNode;\n options: ReadonlyArray<SelectOption & { value: T }>;\n};\n\ntype Key = string | number;\n\nfunction isGroup<T extends Key>(\n entry: (SelectOption & { value: T }) | SelectOptionGroup<T>,\n): entry is SelectOptionGroup<T> {\n return 'options' in entry;\n}\n\n/** How a {@link Select} trigger signals that it opens a list. */\nexport type SelectIndicator = 'chevron' | 'underline' | 'none';\n\n/** Where a {@link Select}'s list opens relative to its trigger. */\nexport type SelectPopup = 'over' | 'below';\n\n/**\n * Props for {@link Select}, on top of React Aria's `Select` props, with the\n * selection key narrowed to the option value type.\n */\nexport type SelectProps<T extends Key = string> = Omit<RACSelectProps<object>, 'children' | 'className' | 'selectedKey' | 'defaultSelectedKey' | 'onSelectionChange'> & {\n label?: ReactNode;\n /**\n * `'stacked'` (the default) puts the label above the trigger; `'row'` sets it\n * beside the trigger at its own width, with any description or error on a\n * line below. Same vocabulary as {@link Field}'s `orientation`.\n */\n orientation?: FieldOrientation;\n description?: ReactNode;\n errorMessage?: ReactNode | ((v: ValidationResult) => ReactNode);\n placeholder?: string;\n /**\n * Either pass `options` for a quick render, or `children` for full control.\n * An entry with `title` and `options` of its own is a titled section.\n */\n options?: ReadonlyArray<(SelectOption & { value: T }) | SelectOptionGroup<T>>;\n children?: ReactNode;\n selectedKey?: T | null;\n defaultSelectedKey?: T;\n onSelectionChange?: (key: T) => void;\n /**\n * `'fill'` (the default) takes the width of whatever row the select sits in.\n * `'fit'` sizes the trigger to its widest option, so it neither swallows a\n * toolbar's slack nor changes width as the selection moves.\n */\n width?: 'fill' | 'fit';\n /** `'bare'` drops the box, for a select set in a row of other chrome such as a property row. */\n variant?: 'field' | 'bare';\n /**\n * How the trigger says it opens a list. `'chevron'` draws the caret beside\n * the value; `'underline'` marks the value itself, dotted at rest and solid\n * while it is pointed at or open; `'none'` says nothing. Unset takes the\n * variant's own: a boxed select shows the caret, a bare one — set in a row\n * where 10px of caret is a tenth of the value's width — underlines.\n */\n indicator?: SelectIndicator;\n /**\n * Where the list opens. `'over'` (the default) puts the selected row on the\n * trigger, its label in the trigger's own text column, so choosing what is\n * already chosen moves nothing; `'below'` hangs it under the trigger.\n */\n popup?: SelectPopup;\n /**\n * Id for the trigger button. React Aria puts a plain `id` on the wrapper,\n * which is not labelable, so an outer `<label for>` needs this to reach a\n * control the label can actually own.\n */\n triggerId?: string;\n className?: string;\n} & SegmentTooltipFields & OverlayPortalProps;\n\n/**\n * Form select wrapping React Aria's Select. Pass either `options` for a\n * quick declarative render or `children` of `<SelectItem>` for control over\n * each row; `<SelectSection>` (or an `options` entry with a `title`) groups\n * rows under a heading.\n *\n * The selection key type is parameterized so consumers with a string-literal\n * union for `value` (e.g. `'r' | 'g' | 'b'`) get a typed `onSelectionChange`.\n *\n * `shortcut` puts a kit tooltip on the trigger reading `Name (⌘K)`, named by\n * `aria-label` or a string `label`, as `Button` does; `tooltip` replaces that\n * text.\n */\nexport function Select<T extends Key = string>(props: SelectProps<T>) {\n const {\n label,\n orientation = 'stacked',\n description,\n errorMessage,\n placeholder,\n options,\n children,\n selectedKey,\n defaultSelectedKey,\n onSelectionChange,\n width = 'fill',\n variant = 'field',\n indicator = variant === 'bare' ? 'underline' : 'chevron',\n popup = 'over',\n triggerId,\n className,\n portalContainer,\n shortcut,\n tooltip,\n ...rest\n } = props;\n\n const { anchor, portalProps } = useOverlayPortal(portalContainer);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const pressRef = useRef<Press | null>(null);\n const [nudge, setNudge] = useState(NO_NUDGE);\n\n return (\n <RACSelect\n {...rest}\n selectedKey={selectedKey}\n defaultSelectedKey={defaultSelectedKey}\n onSelectionChange={onSelectionChange ? (k) => onSelectionChange(k as T) : undefined}\n className={[\n f.field,\n f.control,\n s.select,\n width === 'fit' && `${f.fit} ${s.fit}`,\n variant === 'bare' && s.bare,\n indicator === 'underline' && s.underlined,\n indicator === 'none' && s.plain,\n orientation === 'row' && `${f.row} ${s.row}`,\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n >\n {anchor}\n {label !== undefined && <Label className={f.label}>{label}</Label>}\n <TriggerTooltip\n content={segmentTooltipContent({ tooltip, shortcut, ariaLabel: rest['aria-label'], label })}\n disabled={rest.isDisabled}\n >\n <RACButton\n id={triggerId}\n ref={triggerRef}\n className={`${f.frame} ${s.trigger}`}\n onPointerDown={(e) => {\n pressRef.current = { x: e.clientX, y: e.clientY, at: performance.now() };\n }}\n >\n <SelectValue className={s.value}>\n {({ defaultChildren, isPlaceholder }) =>\n isPlaceholder ? (placeholder ?? defaultChildren) : defaultChildren\n }\n </SelectValue>\n {width === 'fit' && (\n <span className={s.sizer} aria-hidden=\"true\">\n {placeholder !== undefined && <span>{placeholder}</span>}\n {optionLabels(options, children).map((l, i) => (\n <span key={i}>{l}</span>\n ))}\n </span>\n )}\n {indicator === 'chevron' && (\n <svg className={s.chevron} viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <path d=\"M2 4 L5 7 L8 4\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )}\n </RACButton>\n </TriggerTooltip>\n {description !== undefined && (\n <Text slot=\"description\" className={`${f.hint} ${f.below}`}>\n {description}\n </Text>\n )}\n <FieldError className={`${f.error} ${f.below}`}>{errorMessage}</FieldError>\n {/* `data-weasel-overlay` marks DOM that belongs to this control but\n renders in a portal, outside the subtree the trigger sits in. Any\n consumer reasoning about \"did focus leave my component?\" via\n `closest()` gets the wrong answer without it — a text editor whose\n font menu lives here would end its session the moment the menu is\n clicked. */}\n <RACPopover\n className={s.popover}\n data-weasel-overlay=\"\"\n placement=\"bottom start\"\n offset={popup === 'over' ? nudge.offset : undefined}\n crossOffset={popup === 'over' ? nudge.crossOffset : undefined}\n shouldFlip={popup !== 'over'}\n {...portalProps}\n >\n {popup === 'over' && (\n <AlignOverTrigger\n triggerRef={triggerRef}\n pressRef={pressRef}\n applied={nudge}\n onMeasure={setNudge}\n />\n )}\n <RACListBox className={`${listbox.surface} ${listbox.list}`}>\n {options !== undefined\n ? options.map((entry, i) =>\n isGroup(entry) ? (\n <SelectSection key={`section-${i}`} title={entry.title}>\n {entry.options.map(renderOption)}\n </SelectSection>\n ) : (\n renderOption(entry)\n ),\n )\n : children}\n </RACListBox>\n </RACPopover>\n </RACSelect>\n );\n}\n\ntype Nudge = { offset: number; crossOffset: number };\n\n/** Where and when the pointer went down on the trigger. */\ntype Press = { x: number; y: number; at: number };\n\n/** How long a press may last and how far it may travel and still be a click. */\nconst CLICK_MS = 500;\n\nconst NO_NUDGE: Nudge = { offset: 0, crossOffset: 0 };\n\n/** The gap kept between a list and the viewport edge — React Aria's own\n * `containerPadding` default. */\nconst EDGE = 12;\n\n/** React Aria's own resting place for a popover: hanging under its trigger. */\nconst BELOW: Nudge = { offset: 8, crossOffset: 0 };\n\n/**\n * Lands the list so its selected row covers the trigger: same text column,\n * same line. The shift is handed back as the popover's own `offset` and\n * `crossOffset` rather than a transform, so React Aria keeps owning the\n * position and still holds the list inside the viewport.\n *\n * Measuring needs the list rendered, and the correction is applied by setting\n * state from a layout effect — React finishes that pass before the browser\n * paints, so the list is never seen at the uncorrected position.\n */\nfunction AlignOverTrigger({\n triggerRef,\n pressRef,\n applied,\n onMeasure,\n}: {\n triggerRef: RefObject<HTMLButtonElement | null>;\n pressRef: RefObject<Press | null>;\n applied: Nudge;\n onMeasure: (next: Nudge) => void;\n}) {\n // React Aria positions the popover but hands out no ref to it, so the\n // element is reached from inside: this marker's own parent.\n const markerRef = useRef<HTMLSpanElement>(null);\n // A measurement that settles takes two passes — one to place the list, one\n // to confirm. A cap is what keeps a list whose own placement changes its\n // measurement from renegotiating forever.\n const passes = useRef(0);\n useLayoutEffect(() => {\n const trigger = triggerRef.current;\n const popover = markerRef.current?.closest<HTMLElement>('[data-weasel-overlay]');\n if (!trigger || !popover || passes.current > 3) return;\n const value = trigger.querySelector(`.${s.value}`);\n // Nothing chosen yet, so there is no row that belongs over the trigger —\n // and putting an arbitrary one under the pointer arms it. A select showing\n // its placeholder hangs its list below, like any other dropdown.\n const label = popover.querySelector(`[data-selected] .${s.optionLabel}`);\n if (!value || !label) {\n if (applied.offset !== BELOW.offset || applied.crossOffset !== BELOW.crossOffset)\n onMeasure(BELOW);\n return;\n }\n\n // The trigger's own text alignment decides which edge the two labels are\n // matched on, and the list follows it so the selected row reads as the\n // trigger with the others unfolded around it.\n const align = getComputedStyle(trigger).getPropertyValue('--wzl-select-align').trim();\n const toEnd = align === 'right' || align === 'end';\n popover.dataset.align = toEnd ? 'end' : 'start';\n\n // Stated against each element's own box rather than measured as a gap to\n // close: React Aria repositions after this effect, so what is on screen\n // now is a frame behind, and a correction read off it would be applied to\n // a position that has already moved — the shift compounds every pass.\n // `bottom start` puts the list's top at the trigger's bottom and their\n // left edges together, so the two insets below are the whole story.\n const box = popover.getBoundingClientRect();\n const seat = trigger.getBoundingClientRect();\n const row = label.getBoundingClientRect();\n const shown = value.getBoundingClientRect();\n // React Aria holds the list inside the viewport across the trigger but not\n // along it, and `offset` moves it along: the selected row sits on the\n // trigger only as far as the rest of the list still fits on screen.\n const over = shown.top + shown.height / 2 - seat.top - (row.top + row.height / 2 - box.top) - seat.height;\n const viewHeight = trigger.ownerDocument.documentElement.clientHeight;\n const lowest = viewHeight - EDGE - box.height - seat.bottom;\n const next = {\n offset: Math.max(EDGE - seat.bottom, Math.min(over, lowest)),\n crossOffset: toEnd\n ? shown.right - seat.left - (row.right - box.left)\n : shown.left - seat.left - (row.left - box.left),\n };\n if (Math.abs(next.offset - applied.offset) < 0.5 && Math.abs(next.crossOffset - applied.crossOffset) < 0.5)\n return;\n passes.current += 1;\n onMeasure(next);\n });\n\n // The list covers the trigger, so the release that ends the click opening it\n // lands on a row — and a row selects on release, which shuts the list again\n // before it has been seen. A click is a press and a release in the same\n // place: that one is the opening gesture and belongs to the trigger, and the\n // list keeps every release after it. Press, hold, drag and release still\n // picks a row, which is the gesture a list over its trigger is for.\n useEffect(() => {\n const doc = triggerRef.current?.ownerDocument;\n if (!doc) return;\n const onUp = (e: PointerEvent) => {\n doc.removeEventListener('pointerup', onUp, true);\n const press = pressRef.current;\n if (!press || performance.now() - press.at > CLICK_MS) return;\n if (pastDragThreshold({ clientX: press.x, clientY: press.y }, e)) return;\n e.preventDefault();\n e.stopPropagation();\n };\n doc.addEventListener('pointerup', onUp, true);\n return () => doc.removeEventListener('pointerup', onUp, true);\n }, [triggerRef, pressRef]);\n\n return <span ref={markerRef} hidden />;\n}\n\n/* `SelectValue` copies the selected row into the trigger, mark included; the\n trigger hides it, so the `width='fit'` sizer leaves it out. */\nfunction CheckMark() {\n return (\n <svg className={s.check} viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <polyline points=\"1.5,5 4,7.5 8.5,3\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n );\n}\n\nfunction renderOption<T extends Key>(o: SelectOption & { value: T }) {\n return (\n <SelectItem key={String(o.value)} id={o.value} isDisabled={o.isDisabled} icon={o.icon} textValue={o.textValue}>\n {o.label}\n </SelectItem>\n );\n}\n\n/**\n * The labels a `width='fit'` trigger measures itself against. In the children\n * form the label sits one element deep, inside a `SelectItem`, or two inside a\n * `SelectSection`.\n */\nfunction optionLabels(\n options: ReadonlyArray<SelectOption | SelectOptionGroup<Key>> | undefined,\n children: ReactNode,\n): ReactNode[] {\n if (options !== undefined)\n return options.flatMap((o) =>\n 'options' in o ? o.options.map((g) => withIcon(g.icon, g.label)) : [withIcon(o.icon, o.label)],\n );\n return Children.toArray(children).flatMap((c) => {\n if (!isValidElement<{ children?: ReactNode; icon?: ReactNode }>(c)) return [c];\n return c.type === SelectSection\n ? optionLabels(undefined, c.props.children)\n : [withIcon(c.props.icon, c.props.children)];\n });\n}\n\n/** A label led by its icon, as a row draws it. */\nfunction withIcon(icon: ReactNode, label: ReactNode): ReactNode {\n if (icon === undefined || icon === null) return label;\n return (\n <>\n <span className={s.optionIcon} aria-hidden=\"true\">{icon}</span>\n {label}\n </>\n );\n}\n\n/** Props for {@link SelectSection}. */\nexport type SelectSectionProps = {\n /** Heading shown above the section's rows; it also names the group for\n * screen readers. */\n title?: ReactNode;\n children?: ReactNode;\n className?: string;\n};\n\n/** A titled group of `SelectItem` rows in a {@link Select}'s list. */\nexport function SelectSection({ title, children, className }: SelectSectionProps) {\n return (\n <RACListBoxSection className={[s.section, className].filter(Boolean).join(' ')}>\n {title !== undefined && <Header className={s.sectionHeader}>{title}</Header>}\n {children}\n </RACListBoxSection>\n );\n}\n\n/** Props for {@link SelectItem}, on top of React Aria's `ListBoxItem` props. */\nexport type SelectItemProps = Omit<RACListBoxItemProps, 'className' | 'children'> & {\n children?: ReactNode;\n /** A glyph drawn before the label, in the list and in the trigger. */\n icon?: ReactNode;\n className?: string;\n};\n\n/**\n * Every row renders a check mark beside its label, so from React Aria's\n * side the children are never plain text and it can't derive the string\n * type-to-select and screen readers need — it warns once per row. A string\n * label already *is* that string, so derive it rather than making every\n * call site restate it; anything richer has to say what it reads as.\n */\nfunction textValueOf(children: ReactNode, explicit: string | undefined): string | undefined {\n if (explicit !== undefined) return explicit;\n return typeof children === 'string' || typeof children === 'number'\n ? String(children)\n : undefined;\n}\n\n/**\n * One row in a {@link Select}'s list, with a leading check mark when\n * selected. A string child supplies its own `textValue`; anything richer must\n * pass one so type-to-select and screen readers have something to read.\n */\nexport function SelectItem({ children, icon, className, textValue, ...rest }: SelectItemProps) {\n return (\n <RACListBoxItem\n {...rest}\n textValue={textValueOf(children, textValue)}\n className={[s.option, className].filter(Boolean).join(' ')}\n >\n <CheckMark />\n {/* A box around the label, not just text: landing the list over its\n trigger means measuring where this row's label sits. */}\n <span className={s.optionLabel}>{withIcon(icon, children)}</span>\n </RACListBoxItem>\n );\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"Slider-Cl5RCeZ7.js","names":["s","Slider_module_default","quantize","step","v","min","clamp","lo","hi","STOP_SNAP_PX","NO_CAPTURE","usableStops","stops","byValue","stop","s","max","seen","a","b","attract","f","toFraction","best","bestGap","tolerance","gap","stepStops","direction","first","count","i","defaultStep","clampOrdered","index","thumbs","resolveBounds","thumb","fallbackMin","fallbackMax","tuple","ctx","fractionToPosition","READOUT","decimal","Slider","props","stopList","useMemo","even","strict","trackRef","useRef","railRef","dragBufferRef","sessionRef","valueToFraction","useCallback","gaps","x","fractionToValue","c","valueAt","q","trackWidth","beginThumbDrag","buf","seed","t","droppedOff","openPointerSession","origin","down","rail","buffer","rect","ev","bLo","bHi","constraint","bandHeight","onInput","next","onChange","beginShiftAllDrag","startValues","dxFraction","anchorX","starts","df","dValue","allowedNeg","allowedPos","onThumbPointerDown","e","onThumbContextMenu","onTrackPointerDown","wantsMove","track","created","moved","onThumbKeyDown","stepSize","delta","stopDelta","snapTo","placement","renderReadout","display","slim","density","rootVars","trackHeight","labelMode","lastStop","labeledStops","stopLabelRow","rootClass","className","isNotched","customRender","cls","qty","ariaLabel"],"sources":["../../src/components/Slider/Slider.module.css","../../src/components/Slider/Slider.tsx"],"sourcesContent":[".root {\n /* The value span stops this far short of each end of the track. Half a\n * thumb puts an end thumb flush with the edge; `fractionToPosition` in\n * Slider.tsx is the same geometry for painters. */\n --rp-thumb-inset: calc(var(--rp-thumb-size, 14px) / 2);\n position: relative;\n user-select: none;\n -webkit-user-select: none;\n touch-action: none;\n}\n\n.row {\n display: flex;\n align-items: center;\n gap: var(--wzl-space-4);\n}\n\n.track {\n position: relative;\n flex: 1;\n min-width: 0;\n height: var(--rp-track-height, 24px);\n background: var(--wzl-surface-sunken);\n border: 1px solid var(--wzl-border);\n border-radius: var(--wzl-radius-sm);\n cursor: var(--rp-track-cursor, crosshair);\n /* No `overflow: hidden` here — thumbs at extreme values would get\n * clipped at the track edges. The gradient/track-paint clip is\n * applied to `.trackInner` instead, which lets thumbs spill freely. */\n}\n\n.overhang {\n --rp-thumb-inset: 0px;\n}\n\n.rail {\n position: absolute;\n inset: 0 var(--rp-thumb-inset);\n}\n\n.trackInner {\n position: absolute;\n inset: 0;\n border-radius: var(--wzl-radius-sm);\n overflow: hidden;\n}\n\n.thumb {\n position: absolute;\n /* Thumb extends 2px above + below the track for grabbability + visual\n * weight. Track itself stays its natural height; the thumb just spills. */\n top: -2px;\n bottom: -2px;\n width: var(--rp-thumb-size, 14px);\n margin-left: calc(var(--rp-thumb-size, 14px) / -2);\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--wzl-fg-muted) 70%, transparent);\n border: 1px solid var(--wzl-border-strong);\n border-radius: var(--wzl-radius-sm);\n cursor: ew-resize;\n /* Frosted-glass over the (often colorful) track. Cheap on modern GPUs;\n * gracefully degrades to just the partial-alpha background on engines\n * without backdrop-filter support. */\n backdrop-filter: blur(3px);\n -webkit-backdrop-filter: blur(3px);\n /* Subtle raise — sells the \"thumb is on top of the track\" hierarchy. */\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);\n font: var(--wzl-font-weight-medium) var(--wzl-font-size-xs)/var(--wzl-leading-tight) var(--wzl-font-numeric);\n font-variant-numeric: tabular-nums;\n color: var(--wzl-fg-inverse);\n /* White halo so labels stay legible over wildly varying gradient\n * backgrounds (e.g. labels 'T' / 'P' / 'B' over teal, hue 200° over\n * yellow-green, etc.). */\n text-shadow: 0 0 2px rgba(255, 255, 255, 0.7);\n}\n\n/* A slim slider's thumb is round and flush with the track, matching the\n property rows. The default thumb keeps its 2px spill and rounded-rect face. */\n.slim .thumb {\n top: 50%;\n bottom: auto;\n height: var(--rp-thumb-size);\n transform: translateY(-50%);\n border-radius: 50%;\n border: none;\n backdrop-filter: none;\n -webkit-backdrop-filter: none;\n box-shadow: none;\n background: color-mix(in srgb, var(--wzl-accent) var(--wzl-slider-thumb-mix), transparent);\n}\n\n.thumb:focus-visible {\n outline: 2px solid var(--wzl-accent);\n outline-offset: 1px;\n}\n\n.thumbActive {\n /* Visual marker for the dragging/focused thumb; subclass-overridable. */\n z-index: 1;\n}\n\n/* The row holds absolutely-positioned labels, so it has to state its own\n height: the text's, not the thumb's. It said 14px — the default thumb's\n size — which left 4px of dead space under the labels at either density. */\n.readoutsBelow {\n position: relative;\n margin-inline: var(--rp-thumb-inset);\n height: calc(var(--wzl-font-size-xs) * var(--wzl-leading-tight));\n margin-top: var(--wzl-space-2);\n}\n\n.readoutBelow {\n position: absolute;\n transform: translateX(-50%);\n font: var(--wzl-font-weight-medium) var(--wzl-font-size-xs)/var(--wzl-leading-tight) var(--wzl-font-mono);\n color: var(--wzl-fg-muted);\n white-space: nowrap;\n}\n\n.readoutInline {\n display: inline-block;\n margin-left: var(--wzl-space-4);\n font: var(--wzl-font-weight-medium) var(--wzl-font-size-sm)/var(--wzl-leading-tight) var(--wzl-font-mono);\n color: var(--wzl-fg-muted);\n vertical-align: middle;\n}\n\n.notched {\n background: var(--thumb-svg, none);\n background-size: 100% 100%;\n background-repeat: no-repeat;\n background-color: transparent;\n border: none;\n /* Notched thumbs need to disable the .thumb defaults that paint to\n * the bounding box: backdrop-filter and box-shadow both ignore the\n * SVG's polygon/notch and would render through the notch cutout.\n * Use filter: drop-shadow instead — it traces the actual rendered\n * alpha (i.e. the polygon shape including the cut). */\n backdrop-filter: none;\n -webkit-backdrop-filter: none;\n box-shadow: none;\n filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.25));\n /* Default notched SVG (down-pointing pentagon, matches the perceptual-color experiment). */\n --thumb-svg: url(\"data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 30' preserveAspectRatio='none'%3E%3Cpolygon points='0,0 2.52,0 7,10.3 11.48,0 14,0 14,30 0,30' fill='rgba(255,255,255,0.62)' stroke='rgba(0,0,0,0.55)' stroke-width='0.75' stroke-linejoin='miter'/%3E%3C/svg%3E\");\n}\n\n.ticks {\n position: absolute;\n inset: 0;\n /* A mark must never eat the track click that adds a thumb, nor the\n * pointerdown that starts a drag through it. */\n pointer-events: none;\n}\n\n.tick {\n position: absolute;\n top: var(--rp-tick-inset, 20%);\n bottom: var(--rp-tick-inset, 20%);\n width: var(--rp-tick-width, 1px);\n margin-left: calc(var(--rp-tick-width, 1px) / -2);\n border-radius: var(--wzl-radius-pill);\n background: var(--rp-tick-color, var(--wzl-border-strong));\n}\n\n/* The labels hang below the track out of flow, so the row reserves their\n * height and anything after it — the below-thumb readouts — clears them.\n * Padding, not margin: a margin would collapse into whatever follows. */\n.withStopLabels .row {\n padding-bottom: calc(var(--wzl-space-xs) + var(--wzl-font-size-xs) * var(--wzl-leading-tight));\n}\n\n.stopLabels {\n position: absolute;\n left: 0;\n right: 0;\n top: calc(100% + var(--wzl-space-xs));\n height: calc(var(--wzl-font-size-xs) * var(--wzl-leading-tight));\n pointer-events: none;\n}\n\n.stopLabel {\n position: absolute;\n transform: translateX(-50%);\n white-space: nowrap;\n font-family: var(--wzl-font-numeric);\n font-size: var(--wzl-font-size-xs);\n line-height: var(--wzl-leading-tight);\n font-variant-numeric: tabular-nums;\n color: var(--wzl-fg-subtle);\n}\n\n/* The end labels are pinned inside the track rather than centered on it, so a\n * wide one can't spill past the control and misalign whatever sits beside it. */\n.stopLabel[data-edge=\"start\"] {\n transform: none;\n}\n\n.stopLabel[data-edge=\"end\"] {\n transform: translateX(-100%);\n}\n\n.stopLabel[data-selected] {\n color: var(--wzl-fg);\n font-weight: var(--wzl-font-weight-medium);\n}\n","import { useCallback, useEffect, useMemo, useRef, type CSSProperties, type KeyboardEvent as ReactKeyboardEvent, type MouseEvent as ReactMouseEvent, type PointerEvent as ReactPointerEvent, type ReactElement, type ReactNode } from 'react';\nimport { openPointerSession, type PointerSession } from '@weasel-js/core';\nimport s from './Slider.module.css';\nimport { decimal, qty, type Display } from '@weasel-js/quantity';\n\n/**\n * Passed to a custom thumb renderer: the thumb box in CSS px, and whether\n * this thumb is the one being dragged.\n */\nexport type ThumbRenderCtx = {\n width: number;\n height: number;\n isActive: boolean;\n};\n\n/**\n * A thumb's appearance — one of the two built-in shapes, or a custom\n * renderer.\n */\nexport type ThumbShape =\n | 'round'\n | 'notched'\n | { render: (ctx: ThumbRenderCtx) => ReactNode };\n\n/**\n * One handle on a {@link Slider}. `bounds` narrows the range this particular\n * thumb may move within, either fixed or computed from the current thumb\n * list.\n */\nexport type Thumb = {\n value: number;\n label?: string;\n shape?: ThumbShape;\n bounds?: [number, number] | ((ctx: BoundsCtx) => [number, number]);\n /**\n * Spoken form of `value`, published as `aria-valuetext`. Required by ARIA\n * whenever `value` is not the quantity the user is choosing — an index into\n * a value list, a log-scaled position — since `aria-valuenow` alone then\n * announces a number that means nothing.\n */\n valueText?: string;\n};\n\n/**\n * Passed to a thumb's `bounds` function: the full thumb list and this thumb's\n * index in it, so a bound can be expressed relative to its neighbors.\n */\nexport type BoundsCtx = {\n thumbs: readonly Thumb[];\n index: number;\n};\n\n/**\n * Passed to `renderTrack`: the track's width in CSS px, a mapping from a\n * slider value to its 0..1 position along the value span, and the CSS\n * position (a `calc()` against the painted box's width) of a 0..1 fraction of\n * that span. A painter places everything that must line up with the thumbs\n * through `fractionToPosition`: under `thumbFit: 'inside'` the span stops half\n * a thumb short of each end of the track.\n */\nexport type TrackCtx = {\n trackWidth: number;\n valueToFraction: (v: number) => number;\n fractionToPosition: (f: number) => string;\n};\n\n/** A {@link Slider} stop that carries a label, drawn under the track at the stop. */\nexport type SliderStop = {\n value: number;\n label?: ReactNode;\n};\n\n/**\n * Props for {@link Slider}.\n *\n * `onInput` fires continuously through a drag; `onChange` fires once when it\n * ends and is the one to write to history.\n *\n * `stops` are attractors: a drag that passes within a few pixels of one lands\n * on it, and the arrow keys move stop to stop. `step` still quantizes the\n * values between them. Each one is drawn on the track as a mark; pass\n * `showStops: false` for a track whose own paint already reads as the stops.\n * A stop given as a {@link SliderStop} with a `label` gets that label drawn\n * under it; `stopLabels` narrows the row to the two ends or turns it off.\n *\n * `snap: 'strict'` makes every stop a detent the thumb must rest on: drags and\n * track presses land on the nearest one, and `step` no longer applies.\n * `spacing: 'even'` places the stops at equal intervals, runs the track from\n * the first stop to the last, and maps values linearly within each gap — so a\n * geometric list (0.25/0.5/1/2/4) gets usable room for every stop instead of\n * four crowded into the first fifth. Both need stops; with fewer than two,\n * `'even'` falls back to linear.\n *\n * `trackClick: 'move-nearest'` makes a press on bare track send the closest\n * thumb there, focus it, and continue as a drag. It is the default for a\n * single thumb. With several it defaults to `'none'`, because on a\n * multi-thumb editor a stray click would yank a stop the user was not aiming\n * at; `onAddThumb`, where it is set, keeps the track press.\n *\n * `constraint: 'ordered'` keeps thumbs from crossing each other. Supplying\n * `onAddThumb` makes a click on empty track create a thumb, and supplying\n * `onRemoveThumb` lets a right-click or a drag off the track remove one —\n * both callbacks can decline by returning `null`/`false`. `allowShiftAll`\n * makes shift-drag translate every thumb together.\n *\n * `thumbFit: 'inside'`, the default, runs the value span half a thumb short of\n * each end of the track, so a thumb at `min` or `max` sits flush with the\n * track's edge instead of hanging half over it — the layout a native range\n * input has. `'overhang'` runs the span edge to edge and centers an end thumb\n * on the edge.\n */\nexport type SliderProps<T extends Thumb = Thumb> = {\n thumbs: readonly T[];\n onInput: (next: T[]) => void;\n onChange?: (next: T[]) => void;\n min: number;\n max: number;\n step?: number;\n stops?: readonly (number | SliderStop)[];\n showStops?: boolean;\n stopLabels?: 'all' | 'ends' | 'none';\n snap?: 'magnetic' | 'strict';\n spacing?: 'linear' | 'even';\n trackClick?: 'none' | 'move-nearest';\n constraint?: 'free' | 'ordered';\n onAddThumb?: (atValue: number) => T | null;\n onRemoveThumb?: (index: number) => boolean;\n allowShiftAll?: boolean;\n renderTrack?: (ctx: TrackCtx) => ReactNode;\n thumbFit?: 'inside' | 'overhang';\n trackHeight?: number;\n /** `'slim'` drives the track and thumb from the kit's slider tokens, so a\n * Slider matches the property rows. `trackHeight` still wins if given. */\n density?: 'default' | 'slim';\n renderReadout?: (thumb: T, index: number) => ReactNode;\n /** How a thumb's value shows in the default readout and is spoken as its\n * `aria-valuetext` when the thumb gives no `valueText` of its own. Default:\n * three decimals shown, nothing spoken beyond `aria-valuenow`. */\n display?: Display;\n readoutPlacement?: 'none' | 'inline-after' | 'below-thumb';\n ariaLabel?: string;\n className?: string;\n};\n\nfunction quantize(v: number, step: number | undefined, min: number): number {\n if (step === undefined || step <= 0) return v;\n return Math.round((v - min) / step) * step + min;\n}\n\nfunction clamp(v: number, lo: number, hi: number): number {\n return Math.max(lo, Math.min(hi, v));\n}\n\n/** How close a drag has to come to a stop, in track pixels, to land on it. */\nconst STOP_SNAP_PX = 8;\n\n/** A custom `ThumbShape.render` may put interactive content inside the thumb,\n * and capture would retarget pointerup and kill the click on it. */\nconst NO_CAPTURE = { capture: false } as const;\n\n/** The stops that are actually reachable: inside the range, deduped, ascending.\n * A duplicate keeps the first label given for its value. */\nfunction usableStops(stops: SliderProps['stops'], min: number, max: number): SliderStop[] {\n if (!stops || stops.length === 0) return [];\n const byValue = new Map<number, SliderStop>();\n for (const stop of stops) {\n const s = typeof stop === 'number' ? { value: stop } : stop;\n if (s.value < min || s.value > max) continue;\n const seen = byValue.get(s.value);\n if (!seen || (seen.label === undefined && s.label !== undefined)) byValue.set(s.value, s);\n }\n return [...byValue.values()].sort((a, b) => a.value - b.value);\n}\n\n/** The stop nearest `v` along the track, or `v` itself when none lies within\n * `tolerance` (a track fraction). Measured on the track rather than in value\n * units so evenly spaced stops attract over the same distance. */\nfunction attract(v: number, stops: number[], tolerance: number, toFraction: (v: number) => number): number {\n const f = toFraction(v);\n let best = v;\n let bestGap = tolerance;\n for (const stop of stops) {\n const gap = Math.abs(toFraction(stop) - f);\n if (gap <= bestGap) {\n best = stop;\n bestGap = gap;\n }\n }\n return best;\n}\n\n/** The stop `count` places past `v` in `direction`, saturating at either end. */\nfunction stepStops(stops: number[], v: number, direction: 1 | -1, count: number): number {\n if (direction > 0) {\n const first = stops.findIndex(stop => stop > v);\n if (first === -1) return stops[stops.length - 1];\n return stops[Math.min(stops.length - 1, first + count - 1)];\n }\n let first = -1;\n for (let i = stops.length - 1; i >= 0; i--) {\n if (stops[i] < v) {\n first = i;\n break;\n }\n }\n if (first === -1) return stops[0];\n return stops[Math.max(0, first - (count - 1))];\n}\n\nfunction defaultStep(step: number | undefined, min: number, max: number): number {\n if (step !== undefined && step > 0) return step;\n return (max - min) / 100;\n}\n\n/** Keep a thumb inside its neighbors when `constraint` is `'ordered'`. */\nfunction clampOrdered(\n v: number,\n thumbs: readonly Thumb[],\n index: number,\n min: number,\n max: number,\n step: number | undefined,\n): number {\n const gap = step !== undefined && step > 0 ? step : (max - min) / 1000;\n const lower = index > 0 ? thumbs[index - 1].value + gap : min;\n const upper = index < thumbs.length - 1 ? thumbs[index + 1].value - gap : max;\n return clamp(v, lower, upper);\n}\n\nfunction resolveBounds(thumb: Thumb, ctx: BoundsCtx, fallbackMin: number, fallbackMax: number): [number, number] {\n if (!thumb.bounds) return [fallbackMin, fallbackMax];\n const tuple = typeof thumb.bounds === 'function' ? thumb.bounds(ctx) : thumb.bounds;\n return [tuple[0], tuple[1]];\n}\n\n/** Where fraction `f` of the value span falls in a box as wide as the track.\n * The rail element is laid out from the same `--rp-thumb-inset`. */\nfunction fractionToPosition(f: number): string {\n return `calc(var(--rp-thumb-inset) + ${f} * (100% - 2 * var(--rp-thumb-inset)))`;\n}\n\nconst READOUT = decimal({ places: 3 });\n\n/**\n * Multi-thumb slider over a shared track. The thumb list is fully controlled:\n * every change, live or committed, arrives as a whole new array.\n *\n * Thumbs are draggable, and arrow/Home/End move the focused thumb — those\n * keystrokes fire `onInput` and `onChange` together, since there is no\n * in-flight state to buffer.\n */\nexport function Slider<T extends Thumb = Thumb>(props: SliderProps<T>): ReactElement {\n const { thumbs, onInput, onChange, step, constraint, trackHeight, density, ariaLabel, className } = props;\n\n const stopList = useMemo(() => usableStops(props.stops, props.min, props.max), [props.stops, props.min, props.max]);\n const stops = useMemo(() => stopList.map(stop => stop.value), [stopList]);\n const even = props.spacing === 'even' && stops.length >= 2;\n const strict = props.snap === 'strict' && stops.length > 0;\n const min = even ? stops[0] : props.min;\n const max = even ? stops[stops.length - 1] : props.max;\n\n const trackRef = useRef<HTMLDivElement | null>(null);\n // The value span. Every pointer is measured against it, and thumbs, ticks\n // and stop labels are laid out inside it, so they agree by construction.\n const railRef = useRef<HTMLDivElement | null>(null);\n // In-flight thumb buffer during a drag; null when not dragging.\n const dragBufferRef = useRef<T[] | null>(null);\n const sessionRef = useRef<PointerSession | null>(null);\n\n useEffect(() => () => { sessionRef.current?.cancel(); }, []);\n\n const valueToFraction = useCallback(\n (v: number): number => {\n if (max === min) return 0;\n if (!even) return clamp((v - min) / (max - min), 0, 1);\n const gaps = stops.length - 1;\n const x = clamp(v, min, max);\n let i = 0;\n while (i < gaps - 1 && x > stops[i + 1]) i++;\n return (i + (x - stops[i]) / (stops[i + 1] - stops[i])) / gaps;\n },\n [min, max, even, stops],\n );\n\n const fractionToValue = useCallback(\n (f: number): number => {\n const c = clamp(f, 0, 1);\n if (!even) return min + c * (max - min);\n const gaps = stops.length - 1;\n const i = Math.min(gaps - 1, Math.floor(c * gaps));\n return stops[i] + (c * gaps - i) * (stops[i + 1] - stops[i]);\n },\n [min, max, even, stops],\n );\n\n /** The value a pointer at track fraction `f` chooses, before bounds. */\n const valueAt = useCallback(\n (f: number, trackWidth: number): number => {\n const v = fractionToValue(f);\n if (strict) return attract(v, stops, Infinity, valueToFraction);\n const q = clamp(quantize(v, step, min), min, max);\n return stops.length > 0 ? attract(q, stops, STOP_SNAP_PX / trackWidth, valueToFraction) : q;\n },\n [fractionToValue, valueToFraction, strict, stops, step, min, max],\n );\n\n // `seed` replaces the thumb list the drag starts from. A track press moves a\n // thumb before the drag begins, and without seeding, a release with no\n // movement would commit the stale buffer and undo that move.\n const beginThumbDrag = useCallback(\n (origin: Element, down: ReactPointerEvent, index: number, seed?: readonly T[]) => {\n sessionRef.current?.cancel();\n const buf: T[] = (seed ?? thumbs).map(t => ({ ...t }));\n dragBufferRef.current = buf;\n let droppedOff = false;\n\n const onMove = (ev: PointerEvent) => {\n const rail = railRef.current;\n const buffer = dragBufferRef.current;\n if (!rail || !buffer) return;\n const rect = rail.getBoundingClientRect();\n let v = valueAt((ev.clientX - rect.left) / rect.width, rect.width);\n\n const [bLo, bHi] = resolveBounds(buffer[index], { thumbs: buffer, index }, min, max);\n v = clamp(v, bLo, bHi);\n\n if (constraint === 'ordered') v = clampOrdered(v, buffer, index, min, max, step);\n\n // Drop-off detection: pointer exits the track vertically by more than trackHeight.\n const bandHeight = rect.height;\n if (props.onRemoveThumb) {\n if (ev.clientY < rect.top - bandHeight || ev.clientY > rect.bottom + bandHeight) {\n droppedOff = true;\n } else {\n droppedOff = false;\n }\n }\n\n buffer[index] = { ...buffer[index], value: v };\n onInput(buffer.map(t => ({ ...t })));\n };\n\n // A drag that ends without a release — cancelled pointer, lost capture,\n // an unmount — drops the buffer without committing.\n const onCancel = () => {\n sessionRef.current = null;\n dragBufferRef.current = null;\n };\n\n const onEnd = () => {\n sessionRef.current = null;\n const buffer = dragBufferRef.current;\n dragBufferRef.current = null;\n if (!buffer) return;\n\n if (droppedOff && props.onRemoveThumb) {\n const accepted = props.onRemoveThumb(index);\n if (accepted) {\n const next = buffer.filter((_, i) => i !== index).map(t => ({ ...t })) as T[];\n onInput(next);\n onChange?.(next);\n return;\n }\n }\n\n onChange?.(buffer.map(t => ({ ...t })));\n };\n\n sessionRef.current = openPointerSession(origin, down, { onMove, onEnd, onCancel }, NO_CAPTURE);\n },\n [thumbs, onInput, onChange, valueAt, min, max, step, constraint, props],\n );\n\n const beginShiftAllDrag = useCallback(\n (origin: Element, down: ReactPointerEvent, anchorX: number) => {\n sessionRef.current?.cancel();\n const buf: T[] = thumbs.map(t => ({ ...t }));\n const startValues = buf.map(t => t.value);\n dragBufferRef.current = buf;\n\n const onMove = (ev: PointerEvent) => {\n const rail = railRef.current;\n const buffer = dragBufferRef.current;\n if (!rail || !buffer) return;\n const rect = rail.getBoundingClientRect();\n const dxFraction = (ev.clientX - anchorX) / rect.width;\n // Evenly spaced gaps span different amounts of value, so equal value\n // offsets would pull the thumbs apart; move them by track distance.\n if (even) {\n const starts = startValues.map(valueToFraction);\n const df = clamp(dxFraction, -Math.min(...starts), 1 - Math.max(...starts));\n for (let i = 0; i < buffer.length; i++) {\n buffer[i] = { ...buffer[i], value: fractionToValue(starts[i] + df) };\n }\n onInput(buffer.map(t => ({ ...t })));\n return;\n }\n let dValue = dxFraction * (max - min);\n dValue = quantize(dValue, step, 0);\n\n // Clamp delta so no thumb leaves [min, max] (per-thumb bounds intentionally\n // not enforced — matches the experiment's hue-band shift-translate semantics).\n let allowedNeg = -Infinity;\n let allowedPos = Infinity;\n for (let i = 0; i < startValues.length; i++) {\n allowedNeg = Math.max(allowedNeg, min - startValues[i]);\n allowedPos = Math.min(allowedPos, max - startValues[i]);\n }\n dValue = clamp(dValue, allowedNeg, allowedPos);\n\n for (let i = 0; i < buffer.length; i++) {\n buffer[i] = { ...buffer[i], value: clamp(startValues[i] + dValue, min, max) };\n }\n onInput(buffer.map(t => ({ ...t })));\n };\n\n const onCancel = () => {\n sessionRef.current = null;\n dragBufferRef.current = null;\n };\n\n const onEnd = () => {\n sessionRef.current = null;\n const buffer = dragBufferRef.current;\n dragBufferRef.current = null;\n if (buffer) onChange?.(buffer.map(t => ({ ...t })));\n };\n\n sessionRef.current = openPointerSession(origin, down, { onMove, onEnd, onCancel }, NO_CAPTURE);\n },\n [thumbs, onInput, onChange, min, max, step, even, valueToFraction, fractionToValue],\n );\n\n const onThumbPointerDown = (index: number) => (e: ReactPointerEvent) => {\n // Only bail on explicit non-primary buttons (button > 0). jsdom's PointerEvent\n // leaves `button` undefined; treat that as primary so tests can drive drags.\n if (typeof e.button === 'number' && e.button > 0) return;\n // preventDefault below suppresses the focus the press would otherwise\n // give the thumb, and the arrow keys are on the thumb.\n (e.currentTarget as HTMLElement).focus?.();\n e.preventDefault();\n e.stopPropagation();\n const origin = e.currentTarget as HTMLElement;\n if (e.shiftKey && props.allowShiftAll) {\n beginShiftAllDrag(origin, e, e.clientX);\n } else {\n beginThumbDrag(origin, e, index);\n }\n };\n\n const onThumbContextMenu = (index: number) => (e: ReactMouseEvent) => {\n if (!props.onRemoveThumb) return;\n e.preventDefault();\n const accepted = props.onRemoveThumb(index);\n if (!accepted) return;\n const next = thumbs.filter((_, i) => i !== index).map(t => ({ ...t })) as T[];\n onInput(next);\n onChange?.(next);\n };\n\n const onTrackPointerDown = (e: ReactPointerEvent<HTMLDivElement>) => {\n if (typeof e.button === 'number' && e.button > 0) return;\n const trackClick = props.trackClick ?? (thumbs.length === 1 ? 'move-nearest' : 'none');\n const wantsMove = trackClick === 'move-nearest' && thumbs.length > 0;\n if (!props.onAddThumb && !wantsMove) return;\n // If the event originated on a thumb, the thumb's own handler ran first; this is a track click.\n if ((e.target as HTMLElement).closest(`.${s.thumb}`)) return;\n e.preventDefault();\n const track = trackRef.current;\n const rail = railRef.current;\n if (!track || !rail) return;\n const rect = rail.getBoundingClientRect();\n const v = valueAt((e.clientX - rect.left) / rect.width, rect.width);\n\n if (props.onAddThumb) {\n const created = props.onAddThumb(v);\n if (!created) return;\n const next = [...thumbs.map(t => ({ ...t })), created] as T[];\n onInput(next);\n onChange?.(next);\n return;\n }\n\n let index = 0;\n for (let i = 1; i < thumbs.length; i++) {\n if (Math.abs(thumbs[i].value - v) < Math.abs(thumbs[index].value - v)) index = i;\n }\n const next = thumbs.map(t => ({ ...t })) as T[];\n const [bLo, bHi] = resolveBounds(next[index], { thumbs: next, index }, min, max);\n let moved = clamp(v, bLo, bHi);\n if (constraint === 'ordered') moved = clampOrdered(moved, next, index, min, max, step);\n next[index] = { ...next[index], value: moved };\n onInput(next);\n // preventDefault above suppresses the focus the press would give; the arrow keys are on the thumb.\n track.querySelectorAll<HTMLElement>(`.${s.thumb}`)[index]?.focus();\n beginThumbDrag(track, e, index, next);\n };\n\n const onThumbKeyDown = (index: number) => (e: ReactKeyboardEvent) => {\n const stepSize = defaultStep(step, min, max);\n let delta = 0;\n // With stops, a keystroke moves by whole stops rather than by value.\n let stopDelta = 0;\n let snapTo: 'home' | 'end' | null = null;\n\n switch (e.key) {\n case 'ArrowRight':\n case 'ArrowUp':\n delta = e.shiftKey ? stepSize * 10 : stepSize;\n stopDelta = e.shiftKey ? 10 : 1;\n break;\n case 'ArrowLeft':\n case 'ArrowDown':\n delta = e.shiftKey ? -stepSize * 10 : -stepSize;\n stopDelta = e.shiftKey ? -10 : -1;\n break;\n case 'PageUp':\n delta = stepSize * 10;\n stopDelta = 10;\n break;\n case 'PageDown':\n delta = -stepSize * 10;\n stopDelta = -10;\n break;\n case 'Home':\n snapTo = 'home';\n break;\n case 'End':\n snapTo = 'end';\n break;\n default:\n return;\n }\n\n e.preventDefault();\n const next = thumbs.map(t => ({ ...t }));\n const [bLo, bHi] = resolveBounds(next[index], { thumbs: next, index }, min, max);\n const lo = Math.max(min, bLo);\n const hi = Math.min(max, bHi);\n let v: number;\n if (snapTo === 'home') v = lo;\n else if (snapTo === 'end') v = hi;\n else if (stops.length > 0) {\n v = stepStops(stops, next[index].value, stopDelta > 0 ? 1 : -1, Math.abs(stopDelta));\n } else {\n v = quantize(next[index].value + delta, step, min);\n }\n v = clamp(v, lo, hi);\n if (constraint === 'ordered') v = clampOrdered(v, next, index, lo, hi, step);\n next[index] = { ...next[index], value: v };\n onInput(next);\n onChange?.(next);\n };\n\n const placement = props.readoutPlacement ?? 'none';\n const renderReadout = props.renderReadout;\n const display = props.display;\n\n const slim = density === 'slim';\n const rootVars: Record<string, string> = {};\n if (slim) {\n rootVars['--rp-track-height'] = 'var(--wzl-slider-track-h)';\n rootVars['--rp-thumb-size'] = 'var(--wzl-slider-thumb-size)';\n }\n // An explicit trackHeight wins: a caller who named a number meant it.\n if (trackHeight !== undefined) rootVars['--rp-track-height'] = `${trackHeight}px`;\n\n const labelMode = props.stopLabels ?? 'all';\n const lastStop = stopList.length - 1;\n const labeledStops = labelMode === 'none'\n ? []\n : stopList\n .map((stop, i) => ({ stop, i }))\n .filter(({ stop, i }) => stop.label !== undefined && (labelMode === 'all' || i === 0 || i === lastStop));\n // Inside the track so it shares the track's width, not the row's — an inline\n // readout beside the track would otherwise shift every label off its stop.\n const stopLabelRow = labeledStops.length > 0 && (\n <div className={s.stopLabels} data-slider-stop-labels aria-hidden=\"true\">\n {labeledStops.map(({ stop, i }) => (\n <span\n key={stop.value}\n className={s.stopLabel}\n data-slider-stop-label\n data-selected={thumbs.some(t => t.value === stop.value) ? 'true' : undefined}\n data-edge={i === 0 ? 'start' : i === lastStop ? 'end' : undefined}\n style={{ left: `${valueToFraction(stop.value) * 100}%` }}\n >\n {stop.label}\n </span>\n ))}\n </div>\n );\n const rootClass = [s.root, slim && s.slim, props.thumbFit === 'overhang' && s.overhang, stopLabelRow && s.withStopLabels, className].filter(Boolean).join(' ');\n\n return (\n <div\n className={rootClass}\n style={Object.keys(rootVars).length > 0 ? (rootVars as CSSProperties) : undefined}\n >\n <div className={s.row}>\n <div className={s.track} ref={trackRef} onPointerDown={onTrackPointerDown}>\n {props.renderTrack && (\n <div className={s.trackInner}>\n {props.renderTrack({\n trackWidth: trackRef.current?.getBoundingClientRect().width ?? 0,\n valueToFraction,\n fractionToPosition,\n })}\n </div>\n )}\n <div className={s.rail} ref={railRef} data-slider-rail>\n {(props.showStops ?? true) && stops.length > 0 && (\n <div className={s.ticks} data-slider-ticks aria-hidden=\"true\">\n {stops.map(v => {\n const f = valueToFraction(v);\n return (\n <span\n key={v}\n className={s.tick}\n data-slider-tick\n data-fraction={String(f)}\n style={{ left: `${f * 100}%` }}\n />\n );\n })}\n </div>\n )}\n {stopLabelRow}\n {thumbs.map((thumb, i) => {\n const isNotched = thumb.shape === 'notched';\n const customRender = typeof thumb.shape === 'object' && thumb.shape !== null ? thumb.shape.render : null;\n const cls = `${s.thumb}${isNotched ? ` ${s.notched}` : ''}`;\n return (\n <div\n key={i}\n role=\"slider\"\n tabIndex={0}\n aria-orientation=\"horizontal\"\n aria-valuemin={min}\n aria-valuemax={max}\n aria-valuenow={thumb.value}\n aria-valuetext={thumb.valueText ?? (display ? qty(thumb.value, display).spoken : undefined)}\n aria-label={[ariaLabel, thumb.label].filter(Boolean).join(' ') || undefined}\n className={cls}\n style={{ left: `${valueToFraction(thumb.value) * 100}%` }}\n onPointerDown={onThumbPointerDown(i)}\n onKeyDown={onThumbKeyDown(i)}\n onContextMenu={onThumbContextMenu(i)}\n >\n {customRender ? customRender({ width: 14, height: 24, isActive: false }) : (thumb.label ?? '')}\n </div>\n );\n })}\n </div>\n </div>\n {placement === 'inline-after' && (\n <span data-readout=\"inline\" className={s.readoutInline}>\n {thumbs.map((t, i) => (\n <span key={i}>{i > 0 ? ' / ' : ''}{renderReadout ? renderReadout(t, i) : qty(t.value, display ?? READOUT).text}</span>\n ))}\n </span>\n )}\n </div>\n {placement === 'below-thumb' && (\n <div className={s.readoutsBelow}>\n {thumbs.map((t, i) => (\n <span\n key={i}\n data-readout=\"below\"\n className={s.readoutBelow}\n style={{ left: `${valueToFraction(t.value) * 100}%` }}\n >\n {renderReadout ? renderReadout(t, i) : qty(t.value, display ?? READOUT).text}\n </span>\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"StatusBar-Ca9Dvhhq.js","names":["s","StatusBar_module_default","StatusBar","props","className","ariaLabel","StatusBarItem","cls","muted","title","StatusBarSpacer"],"sources":["../../src/components/StatusBar/StatusBar.module.css","../../src/components/StatusBar/StatusBar.tsx"],"sourcesContent":[".bar {\n display: flex;\n align-items: center;\n gap: var(--wzl-space-8);\n flex-shrink: 0;\n padding: var(--wzl-space-2) var(--wzl-space-6);\n font-family: var(--wzl-font-mono);\n font-size: var(--wzl-font-size-sm);\n color: var(--wzl-fg-muted);\n background: var(--wzl-surface);\n border-top: 1px solid var(--wzl-border);\n}\n\n.item {\n white-space: nowrap;\n}\n\n.muted {\n opacity: 0.6;\n font-variant-numeric: tabular-nums;\n}\n\n.hasTitle {\n cursor: help;\n}\n\n.spacer {\n margin-left: auto;\n}\n","import type { ReactNode } from 'react';\nimport s from './StatusBar.module.css';\n\n/** Props for {@link StatusBar}. */\nexport interface StatusBarProps {\n /**\n * Accessible label for the bar. Deliberately not a live region — a status\n * bar's readouts change on every pointer move (zoom, cursor position,\n * selection count), and announcing each one would make a screen reader\n * unusable. Screen-reader users reach the same facts through the controls\n * that own them.\n */\n ariaLabel?: string;\n children?: ReactNode;\n className?: string;\n}\n\n/**\n * The thin readout strip along the bottom of an editor window: monospace,\n * one row, items separated by a fixed gap, with an optional spacer that\n * pushes everything after it to the trailing edge.\n *\n * Pure presentation — it holds whatever the app puts in it. Compose with\n * `<StatusBarItem>` and `<StatusBarSpacer>`.\n */\nexport function StatusBar(props: StatusBarProps) {\n const { ariaLabel, children, className } = props;\n return (\n <footer className={[s.bar, className].filter(Boolean).join(' ')} aria-label={ariaLabel}>\n {children}\n </footer>\n );\n}\n\n/** Props for {@link StatusBarItem}. */\nexport interface StatusBarItemProps {\n /**\n * Hover text. Items that carry one also get a `help` cursor, so the\n * affordance is discoverable rather than hidden behind a hover delay.\n */\n title?: string;\n /**\n * Dim the item and use tabular figures. For reference material that\n * shouldn't compete with live readouts — build stamps, hints, units.\n */\n muted?: boolean;\n children?: ReactNode;\n className?: string;\n}\n\n/** One readout in a `<StatusBar>`. */\nexport function StatusBarItem(props: StatusBarItemProps) {\n const { title, muted, children, className } = props;\n const cls = [s.item, muted && s.muted, title !== undefined && s.hasTitle, className]\n .filter(Boolean).join(' ');\n return <span className={cls} title={title}>{children}</span>;\n}\n\n/**\n * Splits a status bar into leading and trailing runs — everything after the\n * spacer is pushed to the trailing edge. Decorative, so it's hidden from the\n * accessibility tree.\n */\nexport function StatusBarSpacer() {\n return <span className={s.spacer} aria-hidden=\"true\" />;\n}\n"],"mappings":";;;;;;;;;;ACyBA,SAAgBE,EAAU,GAAuB;CAC/C,IAAM,EAAE,cAAW,aAAU,iBAAcC;CAC3C,OACE,kBAAC,UAAD;EAAQ,WAAW,CAACH,EAAE,KAAKI,CAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;EAAG,cAAYC;EAC1E;CACK,CAAA;AAEZ;AAmBA,SAAgBC,EAAc,GAA2B;CACvD,IAAM,EAAE,UAAO,UAAO,aAAU,iBAAcH,GACxCI,IAAM;EAACP,EAAE;EAAMQ,KAASR,EAAE;EAAOS,MAAU,KAAA,KAAaT,EAAE;EAAUI;CAAS,CAAC,CACjF,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;CAC3B,OAAO,kBAAC,QAAD;EAAM,WAAWG;EAAY;EAAQ;CAAe,CAAA;AAC7D;AAOA,SAAgBG,IAAkB;CAChC,OAAO,kBAAC,QAAD;EAAM,WAAWV,EAAE;EAAQ,eAAY;CAAQ,CAAA;AACxD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"Timeline-pqnYk5cV.js","names":["easingLabel","s","KEY_STEP_MS","s","s","_Fragment","s","s","st","s","PauseIcon","PlayIcon","createKeyframeLayer","applyKeyframeDrag","snapToNearest","DetentSlider","Timeline_module_default","bezierOf","spec","easingBezier","easingLabel","b","name","fn","EASINGS","sampleEasing","resolveEasing","out","count","last","i","EASING_NAMES","PREVIEW_SAMPLES","BEZIER_OF","DEFAULT_BEZIER","EasingPicker","props","bezier","value","label","named","selectValue","points","v","onChange","n","MIN_SPAN_MS","createTimeScale","span","window","perMs","widthPx","perPx","ms","px","toFraction","toPercent","spanFraction","spanPercent","clampWindow","w","bounds","from","zoomWindow","maxSpan","nextSpan","factor","frac","atMs","panWindow","byMs","tickTimes","step","niceStep","minSpacingPx","V_INSET_PCT","KEY_STEP_MS$1","valueRange","values","keys","k","lo","pad","LaneGraph","win","hostRef","useRef","size","setSize","useState","el","update","r","prev","ro","layer","useMemo","t","snapTimes","offset","committed","selection","selectedSegment","live","setLive","state","stateRef","useLatest","layers","onLayerChange","next","raw","onSelect","onSelectSegment","applied","onKeysInput","onLayerCommit","rawPrev","onKeysCommit","yLo","yHi","ticks","LayeredCurveEditor","KEY_STEP_MS","entryTimes","row","Lane","trackRef","sessionRef","drag","setDrag","pct","msAt","rect","clientX","snapPxToMs","width","scale","graph","mode","sampledKeys","onKeyPointerDown","e","at","ev","times","end","openPointerSession","onKeyInput","onKeyCommit","onKeyDown","expanded","onToggleExpand","DisclosureMark","onInsert","Fragment","idx","ZOOM_STEP","formatMs","Ruler","setWidth","widthOf","onPointerDown","onScrub","onWheel","playhead","RATE","multiplier","RATES","seconds","Transport","looping","loop","rates","rate","a","paused","onPlay","onPause","jsx","onLoopChange","qty","onRateChange","duration","isNumeric","track","buildLanes","walk","nextPath","path","key","depth","tracks","trackAtPath","list","found","samePath","withKeys","withEvents","replaceAtPath","rest","parent","tt","moveKey","sel","toMs","st","moved","keyIndex","et","events","insertKey","trackPath","sampleTrack","deleteKey","setKeyEasing","_drop","easing","setKeys","setKeyValue","Timeline","setExpanded","ownWindow","setOwnWindow","ownSelection","setOwnSelection","segmentSelection","setSegmentSelection","setWindow","setSelection","rows","toggle","onRootKeyDown","selectedTrack","selectedKey","segmentTrack","segmentKey","className","onInput","renderKeyEditor","AnimatedTimeline","bump","useReducer","handle","raf","useVisibleRaf","rafRef","useCallback"],"sources":["../../src/components/Timeline/Timeline.module.css","../../src/components/Timeline/easingSpec.ts","../../src/components/Timeline/EasingPicker.tsx","../../src/components/Timeline/timeScale.ts","../../src/components/Timeline/LaneGraph.tsx","../../src/components/Timeline/Lane.tsx","../../src/components/Timeline/Ruler.tsx","../../src/components/Timeline/Transport.tsx","../../src/components/Timeline/lanes.ts","../../src/components/Timeline/keys.ts","../../src/components/Timeline/Timeline.tsx","../../src/components/Timeline/AnimatedTimeline.tsx"],"sourcesContent":[".ruler {\n position: relative;\n height: var(--wzl-control-h);\n border-bottom: 1px solid var(--wzl-border);\n background: var(--wzl-surface-raised);\n cursor: ew-resize;\n user-select: none;\n overflow: hidden;\n}\n\n.tick {\n position: absolute;\n top: 0;\n transform: translateX(2px);\n color: var(--wzl-fg-muted);\n pointer-events: none;\n white-space: nowrap;\n}\n\n.tick::before {\n content: '';\n position: absolute;\n left: -2px;\n top: 0;\n bottom: 0;\n border-left: 1px solid var(--wzl-border);\n}\n\n.playhead {\n position: absolute;\n top: 0;\n bottom: 0;\n width: 1px;\n background: var(--wzl-accent-strong);\n pointer-events: none;\n}\n\n.lane {\n display: flex;\n align-items: stretch;\n min-height: var(--wzl-control-h-sm);\n border-bottom: 1px solid var(--wzl-line-subtle);\n}\n\n.laneLabel {\n /* Must match `.gutter`'s width exactly, or every key sits off the ruler. */\n box-sizing: border-box;\n flex: 0 0 var(--wzl-timeline-label-w, 120px);\n display: flex;\n align-items: center;\n gap: var(--wzl-space-2);\n padding-inline: var(--wzl-space-3);\n padding-inline-start: calc(var(--wzl-space-3) + var(--depth, 0) * var(--wzl-space-6));\n font-size: var(--wzl-font-size);\n color: var(--wzl-fg);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.lane[data-depth='1'] .laneLabel { --depth: 1; }\n.lane[data-depth='2'] .laneLabel { --depth: 2; }\n.lane[data-depth='3'] .laneLabel { --depth: 3; }\n\n.disclosure { display: inline-flex; flex: none; cursor: pointer; }\n\n.laneTrack {\n position: relative;\n flex: 1 1 auto;\n min-width: 0;\n background: var(--wzl-surface);\n}\n\n.key, .eventMark {\n position: absolute;\n top: 50%;\n width: var(--wzl-handle-size);\n height: var(--wzl-handle-size);\n margin-left: calc(var(--wzl-handle-size) / -2);\n background: var(--wzl-fg-muted);\n cursor: ew-resize;\n}\n\n.key { transform: translateY(-50%) rotate(45deg); }\n.eventMark { transform: translateY(-50%); border-radius: 50%; }\n\n.key[aria-current='true'], .eventMark[aria-current='true'] { background: var(--wzl-accent-strong); }\n.key:focus-visible, .eventMark:focus-visible { outline: 2px solid var(--wzl-focus-ring); outline-offset: 1px; }\n\n/* The key stays drawn where it was committed while its ghost tracks the\n pointer, so the drag reads as a move from somewhere to somewhere. */\n.key[data-dragging='true'], .eventMark[data-dragging='true'] { opacity: 0.4; }\n\n.keyGhost, .eventGhost {\n position: absolute;\n top: 50%;\n width: var(--wzl-handle-size);\n height: var(--wzl-handle-size);\n margin-left: calc(var(--wzl-handle-size) / -2);\n background: var(--wzl-accent-soft);\n border: 1px dashed var(--wzl-accent-strong);\n pointer-events: none;\n}\n\n.keyGhost { transform: translateY(-50%) rotate(45deg); }\n.eventGhost { transform: translateY(-50%); border-radius: 50%; }\n\n.lane[data-mode='graph'] { min-height: 72px; }\n\n/* A numeric lane in graph mode is a curve editor filling the track. */\n.graphHost {\n position: absolute;\n inset: 0;\n}\n\n/* Out-specifies the editor's own defaults: the lane keeps the track's ground\n and the timeline's accent line rather than a plot's. */\n.graphHost .graph {\n --plot-bg: transparent;\n --curve-line: var(--wzl-accent-strong);\n --curve-line-width: 1px;\n}\n\n/* A graph lane's value labels hang off the track's start edge into the label\n column, so the lane's own label ellipsizes before it reaches them. */\n.lane[data-graph='true'] .laneLabel {\n padding-inline-end: calc(var(--wzl-space-3) + var(--wzl-timeline-value-axis-w, 32px));\n}\n\n.nestedBar {\n position: absolute;\n top: 4px;\n bottom: 4px;\n background: var(--wzl-surface-sunken);\n border: 1px solid var(--wzl-border);\n border-radius: var(--wzl-radius-sm);\n}\n\n.transport {\n display: flex;\n align-items: center;\n gap: var(--wzl-space-4);\n padding: var(--wzl-space-2) var(--wzl-space-3);\n border-bottom: 1px solid var(--wzl-border);\n background: var(--wzl-surface-raised);\n}\n\n.transportButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: var(--wzl-icon-button-size);\n padding: var(--wzl-space-1) var(--wzl-space-3);\n /* Sits on `--wzl-surface-raised`, so a transparent fill and a border that\n close in value leave it reading as text rather than as a control. */\n background: var(--wzl-surface);\n border: 1px solid var(--wzl-border);\n border-radius: var(--wzl-radius-sm);\n color: var(--wzl-fg);\n font: inherit;\n cursor: pointer;\n}\n\n.transportButton:hover { border-color: var(--wzl-fg-muted); }\n\n.transportButton[aria-checked='true'] {\n background: var(--wzl-accent-soft);\n border-color: var(--wzl-accent-strong);\n color: var(--wzl-accent-fg);\n}\n\n/* The bar's chrome lives on the row, so controls beside `<Transport>` sit\n inside it rather than hanging off the panel's corner. `.transport` keeps its\n own chrome for standalone use. */\n.transportRow {\n display: flex;\n align-items: center;\n gap: var(--wzl-space-4);\n padding-inline-end: var(--wzl-space-3);\n border-bottom: 1px solid var(--wzl-border);\n background: var(--wzl-surface-raised);\n}\n\n.transportRow > .transport {\n flex: 1 1 auto;\n border-bottom: 0;\n background: transparent;\n}\n\n/* Last in the row, and growing leftward into the slack `margin-inline-start`\n leaves it: the playhead rewrites this every frame and gains a digit at 10s,\n so anything laid out after it would vibrate. */\n.time {\n margin-inline-start: auto;\n text-align: end;\n font-variant-numeric: tabular-nums;\n font-family: var(--wzl-font-numeric);\n color: var(--wzl-fg-muted);\n}\n.rate { display: inline-flex; align-items: center; gap: var(--wzl-space-3); color: var(--wzl-fg-muted); }\n.rateSlider { width: 88px; }\n\n/* The detents carry no labels in a bar this dense, so the chosen rate is said\n once, here, in a fixed-width cell so the controls beside it never shift. */\n.rateReadout {\n min-width: 3ch;\n font-variant-numeric: tabular-nums;\n font-family: var(--wzl-font-numeric);\n color: var(--wzl-fg);\n}\n\n.root {\n display: flex;\n flex-direction: column;\n min-height: 0;\n background: var(--wzl-surface);\n border: 1px solid var(--wzl-border);\n border-radius: var(--wzl-radius-sm);\n color: var(--wzl-fg);\n /* `--wzl-font-ui` is condensed, so it reads a size smaller than it measures.\n Everything below inherits this; override per rule only to go up. */\n font-family: var(--wzl-font-ui);\n font-size: var(--wzl-font-size-sm);\n line-height: var(--wzl-leading-snug);\n}\n\n.label { padding: var(--wzl-space-2) var(--wzl-space-3); color: var(--wzl-fg-muted); }\n\n.body { display: flex; align-items: stretch; }\n.gutter { box-sizing: border-box; flex: 0 0 var(--wzl-timeline-label-w, 120px); border-right: 1px solid var(--wzl-border); }\n.rulerWrap { flex: 1 1 auto; min-width: 0; }\n\n.lanes {\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n overflow-x: hidden;\n}\n\n.inspector {\n display: flex;\n align-items: center;\n gap: var(--wzl-space-4);\n padding: var(--wzl-space-2) var(--wzl-space-3);\n border-top: 1px solid var(--wzl-border);\n background: var(--wzl-surface-raised);\n}\n\n.segment {\n position: absolute;\n top: 0;\n bottom: 0;\n cursor: pointer;\n}\n\n.segment[aria-current='true'] { background: var(--wzl-accent-soft); }\n.segment:focus-visible { outline: 2px solid var(--wzl-focus-ring); outline-offset: -2px; }\n\n.easingPicker { display: inline-flex; align-items: center; gap: var(--wzl-space-3); }\n.easingLabel { font-family: var(--wzl-font-numeric); font-variant-numeric: tabular-nums; color: var(--wzl-fg-muted); }\n\n.easingPreview {\n width: 32px;\n height: 32px; /* not-a-control */\n fill: none;\n stroke: var(--wzl-accent-strong);\n stroke-width: 1;\n border: 1px solid var(--wzl-border);\n}\n\n","import { EASINGS, resolveEasing, type EasingName, type EasingSpec } from '@weasel-js/core';\n\nfunction bezierOf(spec: EasingSpec): readonly [number, number, number, number] | null {\n if (typeof spec === 'object' && spec !== null && 'bezier' in spec) return spec.bezier;\n return null;\n}\n\n/** Control points, for the graph view's draggable handles. `null` means this\n * spec has no handles to drag — the view offers to convert it to a bezier. */\nexport function easingBezier(spec?: EasingSpec): readonly [number, number, number, number] | null {\n if (spec === undefined) return null;\n return bezierOf(spec);\n}\n\n/** What the picker shows for a spec.\n *\n * A function is matched against the built-ins by reference so a consumer that\n * passed `easeOutBack` directly still reads back as `'easeOutBack'`. A wrapped\n * or hand-written function cannot be named and reads as `'custom'` — which is\n * why an editor writes names and control points rather than functions. */\nexport function easingLabel(spec?: EasingSpec): string {\n if (spec === undefined) return 'linear';\n if (typeof spec === 'string') return spec;\n const b = bezierOf(spec);\n if (b) return `cubic-bezier(${b.join(', ')})`;\n for (const [name, fn] of Object.entries(EASINGS)) {\n if (fn === spec) return name;\n }\n return 'custom';\n}\n\n/** `count` evenly spaced samples of the curve over 0..1, for drawing it. */\nexport function sampleEasing(spec: EasingSpec | undefined, count: number): number[] {\n const fn = resolveEasing(spec);\n const out = new Array<number>(count);\n const last = count - 1;\n for (let i = 0; i < count; i++) out[i] = fn(last === 0 ? 0 : i / last);\n return out;\n}\n\n/** Every built-in name, for the picker's list. */\nexport const EASING_NAMES = Object.keys(EASINGS) as EasingName[];\n","import type { ReactElement } from 'react';\nimport type { EasingSpec } from '@weasel-js/core';\nimport s from './Timeline.module.css';\nimport { EASING_NAMES, easingBezier, easingLabel, sampleEasing } from './easingSpec';\n\n/** Samples drawn in the inline curve preview. */\nconst PREVIEW_SAMPLES = 24;\n\n/** Control points for the named curves the picker can convert. A name the table\n * does not cover converts to the CSS default rather than refusing. */\nconst BEZIER_OF: Partial<Record<string, readonly [number, number, number, number]>> = {\n linear: [0, 0, 1, 1],\n easeInQuad: [0.11, 0, 0.5, 0],\n easeOutQuad: [0.5, 1, 0.89, 1],\n easeInOutQuad: [0.45, 0, 0.55, 1],\n easeInCubic: [0.32, 0, 0.67, 0],\n easeOutCubic: [0.33, 1, 0.68, 1],\n easeInOutCubic: [0.65, 0, 0.35, 1],\n easeInSine: [0.12, 0, 0.39, 0],\n easeOutSine: [0.61, 1, 0.88, 1],\n easeInOutSine: [0.37, 0, 0.63, 1],\n};\n\nconst DEFAULT_BEZIER: readonly [number, number, number, number] = [0.25, 0.1, 0.25, 1];\n\n/** Props for {@link EasingPicker}. `undefined` is linear. */\nexport interface EasingPickerProps {\n value: EasingSpec | undefined;\n onChange: (next: EasingSpec | undefined) => void;\n}\n\n/** A key's easing: a menu of the named curves, a button converting a named\n * curve to its cubic-bezier control points, and a plot of the curve. */\nexport function EasingPicker(props: EasingPickerProps): ReactElement {\n const { value, onChange } = props;\n const bezier = easingBezier(value);\n const label = easingLabel(value);\n const named = bezier === null && (EASING_NAMES as string[]).includes(label);\n const selectValue = bezier !== null ? 'custom' : named ? label : 'linear';\n\n const points = sampleEasing(value, PREVIEW_SAMPLES)\n .map((v, i) => `${(i / (PREVIEW_SAMPLES - 1)) * 100},${100 - v * 100}`)\n .join(' ');\n\n return (\n <div className={s.easingPicker}>\n <label>\n Easing\n <select\n value={selectValue}\n onChange={(e) => onChange(e.target.value === 'linear' ? undefined : e.target.value as EasingSpec)}\n >\n <option value=\"linear\">linear</option>\n {EASING_NAMES.filter((n) => n !== 'linear').map((n) => <option key={n} value={n}>{n}</option>)}\n {bezier !== null ? <option value=\"custom\">custom</option> : null}\n </select>\n </label>\n\n {bezier !== null ? <span className={s.easingLabel}>{label}</span> : (\n <button\n type=\"button\"\n className={s.transportButton}\n onClick={() => onChange({ bezier: BEZIER_OF[selectValue] ?? DEFAULT_BEZIER })}\n >\n Convert to bezier\n </button>\n )}\n\n <svg className={s.easingPreview} data-testid=\"easing-preview\" viewBox=\"0 0 100 100\" preserveAspectRatio=\"none\">\n <polyline points={points} vectorEffect=\"non-scaling-stroke\" />\n </svg>\n </div>\n );\n}\n","import { niceStep } from '../Plot2D/ticks';\n\n/** A visible span of timeline time, in ms. */\nexport interface TimeWindow {\n from: number;\n to: number;\n}\n\nexport interface TimeScale {\n window: TimeWindow;\n widthPx: number;\n toPx(ms: number): number;\n toMs(px: number): number;\n}\n\n/** Narrowest window the user can zoom to, in ms. Below this the playhead and\n * the keys it sits between stop being separable at any sane track width. */\nconst MIN_SPAN_MS = 1;\n\nexport function createTimeScale(window: TimeWindow, widthPx: number): TimeScale {\n const span = window.to - window.from;\n const perMs = span === 0 ? 0 : widthPx / span;\n const perPx = widthPx === 0 ? 0 : span / widthPx;\n return {\n window,\n widthPx,\n toPx: (ms) => (ms - window.from) * perMs,\n toMs: (px) => window.from + px * perPx,\n };\n}\n\n/** Where `ms` sits in the window, as a fraction. The width-free half of\n * `createTimeScale` — a CSS `left: %` needs no pixel width, and asking for one\n * is what drove three copies of this arithmetic into the lane and the ruler. */\nexport function toFraction(window: TimeWindow, ms: number): number {\n const span = window.to - window.from;\n return span === 0 ? 0 : (ms - window.from) / span;\n}\n\n/** `toFraction` as a CSS percentage. */\nexport function toPercent(window: TimeWindow, ms: number): string {\n return `${toFraction(window, ms) * 100}%`;\n}\n\n/** A duration as a fraction of the window. Distinct from `toFraction`, which\n * measures a position and so subtracts `window.from`. */\nexport function spanFraction(window: TimeWindow, ms: number): number {\n const span = window.to - window.from;\n return span === 0 ? 0 : ms / span;\n}\n\n/** A duration as a CSS percentage, for a width. */\nexport function spanPercent(window: TimeWindow, ms: number): string {\n return `${spanFraction(window, ms) * 100}%`;\n}\n\nfunction clampWindow(w: TimeWindow, bounds: TimeWindow): TimeWindow {\n const span = Math.min(w.to - w.from, bounds.to - bounds.from);\n let from = w.from;\n if (from < bounds.from) from = bounds.from;\n if (from + span > bounds.to) from = bounds.to - span;\n return { from, to: from + span };\n}\n\n/** Scale the window by `factor` about `atMs`, which stays at the same fraction\n * of the track. `factor < 1` zooms in. */\nexport function zoomWindow(\n w: TimeWindow, atMs: number, factor: number, bounds: TimeWindow,\n): TimeWindow {\n const span = w.to - w.from;\n const maxSpan = bounds.to - bounds.from;\n const nextSpan = Math.min(maxSpan, Math.max(MIN_SPAN_MS, span * factor));\n const frac = span === 0 ? 0.5 : (atMs - w.from) / span;\n return clampWindow({ from: atMs - frac * nextSpan, to: atMs + (1 - frac) * nextSpan }, bounds);\n}\n\n/** Shift the window by `byMs`, preserving its span. */\nexport function panWindow(w: TimeWindow, byMs: number, bounds: TimeWindow): TimeWindow {\n return clampWindow({ from: w.from + byMs, to: w.to + byMs }, bounds);\n}\n\n/** Minimum gap between time ticks, in px — the ruler's and the graph grid's. */\nexport const TICK_SPACING_PX = 64;\n\n/** Ruler tick times inside `w`, spaced at least `minSpacingPx` apart. */\nexport function tickTimes(w: TimeWindow, widthPx: number, minSpacingPx: number): number[] {\n const span = w.to - w.from;\n if (widthPx <= 0 || span <= 0) return [];\n const step = niceStep((minSpacingPx / widthPx) * span);\n const out: number[] = [];\n for (let t = Math.ceil(w.from / step) * step; t <= w.to + 1e-9; t += step) {\n out.push(Math.round(t / step) * step);\n }\n return out;\n}\n","import { useLayoutEffect, useMemo, useRef, useState, type ReactElement } from 'react';\nimport { useLatest, type Keyframe } from '@weasel-js/core';\nimport { LayeredCurveEditor } from '../CurveEditor/LayeredCurveEditor';\nimport {\n applyKeyframeDrag, createKeyframeLayer, type KeyframeLayerState,\n} from '../CurveEditor/createKeyframeLayer';\nimport s from './Timeline.module.css';\nimport { TICK_SPACING_PX, tickTimes, type TimeWindow } from './timeScale';\n\n/** Keeps the extreme keys inside the lane: at the very edge a key centers on\n * the lane's border and hangs half into its neighbor. As a percentage of\n * the lane's height, applied top and bottom. */\nconst V_INSET_PCT = 8;\n\n/** One arrow-key step, in ms; shift multiplies by ten. */\nconst KEY_STEP_MS = 10;\n\nexport interface LaneGraphProps {\n keys: readonly Keyframe<number>[];\n label: string;\n window: TimeWindow;\n /** Ruler time of this track's zero. */\n offset: number;\n /** Ruler times a dragged key snaps to. */\n snapTimes: readonly number[];\n selection: number | null;\n selectedSegment: number | null;\n onSelect: (keyIndex: number) => void;\n onSelectSegment?: (keyIndex: number) => void;\n /** Live during a key drag. */\n onKeysInput?: (keys: readonly Keyframe<number>[]) => void;\n /** Once per edit: a drag's end, a handle's release, a key press. */\n onKeysCommit: (keys: readonly Keyframe<number>[]) => void;\n}\n\n/** The value range a lane plots: its keys' extent, padded so no key sits on\n * the border. A flat track gets a unit span so it still has a middle. */\nfunction valueRange(keys: readonly Keyframe<number>[]): [number, number] {\n const values = keys.map((k) => k.value);\n const lo = Math.min(...values);\n const hi = Math.max(...values);\n const span = hi - lo || 1;\n const pad = (span * V_INSET_PCT) / (100 - 2 * V_INSET_PCT);\n return [lo - pad, lo + span + pad];\n}\n\n/** A numeric lane's graph: one keyframe layer on a curve editor, sized to the\n * lane and windowed to the ruler. */\nexport function LaneGraph(props: LaneGraphProps): ReactElement {\n const {\n keys, label, window: win, offset, snapTimes, selection, selectedSegment,\n onSelect, onSelectSegment, onKeysInput, onKeysCommit,\n } = props;\n\n const hostRef = useRef<HTMLDivElement | null>(null);\n const [size, setSize] = useState({ width: 0, height: 0 });\n useLayoutEffect(() => {\n const el = hostRef.current;\n if (!el) return;\n const update = (): void => {\n const r = el.getBoundingClientRect();\n setSize((prev) => (prev.width === r.width && prev.height === r.height ? prev : { width: r.width, height: r.height }));\n };\n update();\n if (typeof ResizeObserver === 'undefined') return;\n const ro = new ResizeObserver(update);\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n const layer = useMemo(() => createKeyframeLayer({\n id: 'keys',\n label,\n formatX: (t) => `${Math.round(t)} ms`,\n // The layer works in track time; the snap candidates are ruler times.\n snapX: snapTimes.map((t) => t - offset),\n xClamp: [0, Infinity],\n step: { x: KEY_STEP_MS },\n }), [label, snapTimes, offset]);\n\n const committed = useMemo<KeyframeLayerState>(\n () => ({ keys, selectedKey: selection, selectedSegment, drag: null }),\n [keys, selection, selectedSegment],\n );\n // Held only while a gesture is in flight, so a consumer echoing live input\n // back through `keys` cannot move the keys out from under the drag.\n const [live, setLive] = useState<KeyframeLayerState | null>(null);\n const state = live ?? committed;\n const stateRef = useLatest(state);\n\n const layers = useMemo(() => [{ layer, state: state as unknown }], [layer, state]);\n\n const onLayerChange = (_id: string, raw: unknown): void => {\n const next = raw as KeyframeLayerState;\n const prev = stateRef.current;\n if (next.selectedKey !== null && next.selectedKey !== prev.selectedKey) onSelect(next.selectedKey);\n if (next.selectedSegment !== null && next.selectedSegment !== prev.selectedSegment) onSelectSegment?.(next.selectedSegment);\n if (!next.drag) { setLive(null); return; }\n setLive(next);\n // A handle drag writes a fresh easing spec per move; publishing those\n // would grow the consumer's easing cache by one entry per pixel.\n if (next.drag.kind !== 'key') return;\n const applied = applyKeyframeDrag(next).keys;\n if (applied !== next.keys) onKeysInput?.(applied);\n };\n\n const onLayerCommit = (_id: string, raw: unknown, rawPrev: unknown): void => {\n setLive(null);\n const next = raw as KeyframeLayerState;\n if (next.keys !== (rawPrev as KeyframeLayerState).keys) onKeysCommit(next.keys);\n };\n\n const [yLo, yHi] = valueRange(state.keys);\n const ticks = size.width > 0 ? tickTimes(win, size.width, TICK_SPACING_PX) : [];\n\n return (\n <div ref={hostRef} className={s.graphHost}>\n {size.width > 0 && size.height > 0 ? (\n <LayeredCurveEditor\n className={s.graph}\n aria-label={`${label} graph`}\n layers={layers}\n onLayerChange={onLayerChange}\n onLayerCommit={onLayerCommit}\n width={size.width}\n height={size.height}\n xRange={[win.from - offset, win.to - offset]}\n yRange={[yLo, yHi]}\n grid={false}\n axes={false}\n // The ruler's ticks, carried into track time.\n xTicks={{ values: ticks.map((t) => t - offset), labels: false }}\n // Clears a key at the track's start edge, which hangs into the gutter.\n yTicks={{ labels: 'outside', gap: 10 }}\n history={false}\n />\n ) : null}\n </div>\n );\n}\n","import { useEffect, useRef, useState, type KeyboardEvent as ReactKeyboardEvent, type PointerEvent as ReactPointerEvent, type ReactElement } from 'react';\nimport { openPointerSession, type EventTrack, type Keyframe, type PointerSession, type SampledTrack, type TimelineTrack } from '@weasel-js/core';\nimport { DisclosureMark } from '../Disclosure';\nimport s from './Timeline.module.css';\nimport { createTimeScale, spanPercent, toPercent, type TimeWindow } from './timeScale';\nimport { SNAP_RADIUS_PX, snapToNearest } from '../../snap';\nimport { LaneGraph } from './LaneGraph';\nimport type { LaneRow } from './lanes';\n\n/** One arrow-key step, in ms; shift multiplies by ten. */\nconst KEY_STEP_MS = 10;\n\nexport interface LaneProps {\n row: LaneRow;\n window: TimeWindow;\n mode: 'dope' | 'graph';\n /** Index of the selected key on THIS row, or null. */\n selection: number | null;\n onSelect: (keyIndex: number) => void;\n /** Live during a dope-sheet drag. */\n onKeyInput: (keyIndex: number, toMs: number) => void;\n /** Once, at the end of a dope-sheet gesture. */\n onKeyCommit: (keyIndex: number, toMs: number) => void;\n /** Live during a graph-mode key drag: the row's whole key list. */\n onKeysInput?: (keys: readonly Keyframe<number>[]) => void;\n /** Once per graph-mode edit: the row's whole key list. */\n onKeysCommit?: (keys: readonly Keyframe<number>[]) => void;\n onInsert: (atMs: number) => void;\n onToggleExpand: () => void;\n expanded: boolean;\n /** Times a dragged key snaps to. */\n snapTimes: readonly number[];\n /** Index of the key a selected segment runs INTO, or null. */\n selectedSegment?: number | null;\n onSelectSegment?: (keyIndex: number) => void;\n}\n\nfunction entryTimes(row: LaneRow): number[] {\n if (row.kind === 'sampled') return (row.track as SampledTrack<unknown>).keys.map((k) => k.t);\n if (row.kind === 'event') return (row.track as EventTrack).events.map((e) => e.t);\n return [];\n}\n\nexport function Lane(props: LaneProps): ReactElement {\n const {\n row, window: win, mode, selection, onSelect, onKeyInput, onKeyCommit, onInsert, onToggleExpand, expanded, snapTimes,\n selectedSegment = null, onSelectSegment, onKeysInput, onKeysCommit,\n } = props;\n const trackRef = useRef<HTMLDivElement | null>(null);\n const sessionRef = useRef<PointerSession | null>(null);\n useEffect(() => () => { sessionRef.current?.cancel(); }, []);\n\n // Where the dragged key would land. Owned here rather than derived from the\n // tracks prop, so a drag previews whether or not the consumer wires\n // `onKeyInput` — the committed key stays put underneath as the origin.\n const [drag, setDrag] = useState<{ keyIndex: number; t: number } | null>(null);\n\n const pct = (ms: number): string => toPercent(win, ms + row.offset);\n\n const msAt = (clientX: number): number => {\n const rect = trackRef.current?.getBoundingClientRect();\n if (!rect) return win.from;\n return createTimeScale(win, rect.width).toMs(clientX - rect.left) - row.offset;\n };\n\n const snapPxToMs = (): number => {\n const width = trackRef.current?.getBoundingClientRect().width ?? 0;\n const scale = createTimeScale(win, width);\n return scale.toMs(SNAP_RADIUS_PX) - scale.toMs(0);\n };\n\n // Only a numeric sampled row has an honest value axis to graph.\n const graph = mode === 'graph' && row.numeric;\n const sampledKeys = row.kind === 'sampled' ? (row.track as SampledTrack<number>).keys : [];\n\n const onKeyPointerDown = (i: number) => (e: ReactPointerEvent<HTMLDivElement>): void => {\n if (e.button !== 0) return;\n e.stopPropagation();\n onSelect(i);\n\n // `snapTimes` are ruler times and `msAt` is track-local, so a nested row\n // has to cross into ruler space to snap and back out to commit.\n const at = (ev: { clientX: number; altKey: boolean }): number => {\n const raw = Math.max(0, msAt(ev.clientX));\n return ev.altKey ? raw : snapToNearest(raw + row.offset, snapTimes, snapPxToMs()) - row.offset;\n };\n\n setDrag({ keyIndex: i, t: times[i] });\n\n const end = (): void => {\n setDrag(null);\n sessionRef.current = null;\n };\n // No pointer capture — see `Ruler`, same idiom across the timeline.\n sessionRef.current?.cancel();\n sessionRef.current = openPointerSession(e.currentTarget, e, {\n onMove: (ev) => {\n setDrag({ keyIndex: i, t: at(ev) });\n onKeyInput(i, at(ev));\n },\n onEnd: (ev) => {\n onKeyCommit(i, at(ev));\n end();\n },\n onCancel: end,\n }, { capture: false });\n };\n\n const onKeyDown = (i: number, t: number) => (e: ReactKeyboardEvent<HTMLDivElement>): void => {\n const step = KEY_STEP_MS * (e.shiftKey ? 10 : 1);\n if (e.key === 'ArrowRight') { e.preventDefault(); onKeyCommit(i, t + step); }\n if (e.key === 'ArrowLeft') { e.preventDefault(); onKeyCommit(i, Math.max(0, t - step)); }\n };\n\n const times = entryTimes(row);\n\n return (\n <div className={s.lane} data-depth={row.depth} data-mode={mode} data-graph={graph ? 'true' : undefined}>\n <div className={s.laneLabel}>\n {row.kind === 'timeline' ? (\n <span\n role=\"button\"\n tabIndex={0}\n aria-expanded={expanded}\n data-testid=\"timeline-disclosure\"\n className={s.disclosure}\n onClick={onToggleExpand}\n onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onToggleExpand(); } }}\n >\n <DisclosureMark open={expanded} />\n </span>\n ) : null}\n {row.label}\n </div>\n <div\n className={s.laneTrack}\n ref={trackRef}\n data-testid=\"timeline-lane-track\"\n onDoubleClick={(e) => { if (row.kind === 'sampled') onInsert(Math.max(0, msAt(e.clientX))); }}\n >\n {graph ? (\n <LaneGraph\n keys={sampledKeys}\n label={row.label}\n window={win}\n offset={row.offset}\n snapTimes={snapTimes}\n selection={selection}\n selectedSegment={selectedSegment}\n onSelect={onSelect}\n onSelectSegment={onSelectSegment}\n onKeysInput={onKeysInput}\n onKeysCommit={(keys) => onKeysCommit?.(keys)}\n />\n ) : (\n <>\n {row.kind === 'timeline' ? (\n <div\n className={s.nestedBar}\n data-testid=\"timeline-nested\"\n style={{ left: pct(0), width: spanPercent(win, (row.track as TimelineTrack).timeline.duration ?? 0) }}\n />\n ) : null}\n {row.kind === 'sampled' ? sampledKeys.slice(1).map((k, idx) => {\n const i = idx + 1;\n return (\n <div\n key={`seg-${i}`}\n role=\"button\"\n tabIndex={0}\n aria-label={`${row.label} segment into ${Math.round(k.t)} ms`}\n aria-current={selectedSegment === i ? 'true' : undefined}\n data-testid=\"timeline-segment\"\n className={s.segment}\n style={{ left: pct(sampledKeys[i - 1].t), width: spanPercent(win, k.t - sampledKeys[i - 1].t) }}\n onClick={() => onSelectSegment?.(i)}\n onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onSelectSegment?.(i); } }}\n />\n );\n }) : null}\n {times.map((t, i) => (\n <div\n key={i}\n role=\"button\"\n tabIndex={0}\n aria-label={`${row.label} key at ${Math.round(t)} ms`}\n aria-current={selection === i ? 'true' : undefined}\n data-testid={row.kind === 'event' ? 'timeline-event' : 'timeline-key'}\n data-dragging={drag?.keyIndex === i ? 'true' : undefined}\n className={row.kind === 'event' ? s.eventMark : s.key}\n style={{ left: pct(t) }}\n onPointerDown={onKeyPointerDown(i)}\n onKeyDown={onKeyDown(i, t)}\n />\n ))}\n {drag ? (\n <div\n aria-hidden=\"true\"\n data-testid=\"timeline-key-ghost\"\n className={row.kind === 'event' ? s.eventGhost : s.keyGhost}\n style={{ left: pct(drag.t) }}\n />\n ) : null}\n </>\n )}\n </div>\n </div>\n );\n}\n","import { useEffect, useLayoutEffect, useRef, useState, type PointerEvent as ReactPointerEvent, type ReactElement, type WheelEvent as ReactWheelEvent } from 'react';\nimport { openPointerSession, type PointerSession } from '@weasel-js/core';\nimport s from './Timeline.module.css';\nimport { createTimeScale, panWindow, TICK_SPACING_PX, tickTimes, toPercent, zoomWindow, type TimeWindow } from './timeScale';\n\n/** One wheel notch's zoom factor. */\nconst ZOOM_STEP = 0.0015;\n\nexport interface RulerProps {\n window: TimeWindow;\n /** The full extent the window may cover — normally `{ from: 0, to: duration }`. */\n bounds: TimeWindow;\n playhead: number;\n onScrub: (t: number) => void;\n onWindowChange: (w: TimeWindow) => void;\n}\n\nfunction formatMs(ms: number): string {\n if (ms >= 1000) return `${(ms / 1000).toFixed(ms % 1000 === 0 ? 0 : 2)}s`;\n return `${Math.round(ms)}ms`;\n}\n\nexport function Ruler(props: RulerProps): ReactElement {\n const { window: win, bounds, playhead, onScrub, onWindowChange } = props;\n const trackRef = useRef<HTMLDivElement | null>(null);\n const sessionRef = useRef<PointerSession | null>(null);\n useEffect(() => () => { sessionRef.current?.cancel(); }, []);\n\n // Re-render once the track is mounted so ticks lay out against its real\n // width — `trackRef.current` is null on the render that first attaches it.\n const [width, setWidth] = useState(0);\n useLayoutEffect(() => {\n const el = trackRef.current;\n if (!el) return;\n const update = (): void => setWidth(el.getBoundingClientRect().width);\n update();\n if (typeof ResizeObserver === 'undefined') return;\n const ro = new ResizeObserver(update);\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n const widthOf = (): number => trackRef.current?.getBoundingClientRect().width ?? 0;\n const msAt = (clientX: number): number => {\n const rect = trackRef.current?.getBoundingClientRect();\n if (!rect) return win.from;\n const scale = createTimeScale(win, rect.width);\n const raw = scale.toMs(clientX - rect.left);\n return Math.min(win.to, Math.max(win.from, raw));\n };\n\n // `capture: false`: the session tracks the pointer off the ruler on its own,\n // and capture would retarget pointerup and kill the click on non-native\n // children of the scrubbed track.\n const onPointerDown = (e: ReactPointerEvent<HTMLDivElement>): void => {\n if (e.button !== 0) return;\n onScrub(msAt(e.clientX));\n\n sessionRef.current?.cancel();\n const end = (): void => { sessionRef.current = null; };\n sessionRef.current = openPointerSession(e.currentTarget, e, {\n onMove: (ev) => { onScrub(msAt(ev.clientX)); },\n onEnd: end,\n onCancel: end,\n }, { capture: false });\n };\n\n const onWheel = (e: ReactWheelEvent<HTMLDivElement>): void => {\n if (e.ctrlKey) {\n onWindowChange(zoomWindow(win, msAt(e.clientX), Math.exp(e.deltaY * ZOOM_STEP), bounds));\n } else {\n const width = widthOf();\n const perPx = width === 0 ? 0 : (win.to - win.from) / width;\n onWindowChange(panWindow(win, e.deltaY * perPx, bounds));\n }\n };\n\n const ticks = tickTimes(win, width, TICK_SPACING_PX);\n const pct = (ms: number): string => toPercent(win, ms);\n\n return (\n <div\n className={s.ruler}\n ref={trackRef}\n data-testid=\"timeline-ruler\"\n onPointerDown={onPointerDown}\n onWheel={onWheel}\n >\n {ticks.map((t) => (\n <span\n key={t}\n className={s.tick}\n data-testid=\"timeline-tick\"\n style={{ left: pct(t) }}\n >\n {formatMs(t)}\n </span>\n ))}\n <div\n className={s.playhead}\n data-testid=\"timeline-playhead\"\n style={{ left: pct(playhead) }}\n />\n </div>\n );\n}\n","import type { ReactElement } from 'react';\nimport { multiplier, qty } from '@weasel-js/quantity';\nimport { DetentSlider } from '../DetentSlider';\nimport { PauseIcon, PlayIcon } from '../../icons';\nimport s from './Timeline.module.css';\n\nconst RATE = multiplier({ symbol: 'x' });\n\n/** Playback rates the transport offers. */\nconst RATES = [0.25, 0.5, 1, 2, 4] as const;\n\n/** Props for {@link Transport}. Times are in ms. */\nexport interface TransportProps {\n paused: boolean;\n /** A `TimelineHandle`'s loop policy. The switch shows any value but\n * `false` or `0` as on, and toggles between `true` and `false`. */\n loop: boolean | number;\n /** Playback speed multiplier. A rate off the offered detents gets one of its own. */\n rate: number;\n playhead: number;\n duration: number;\n onPlay: () => void;\n onPause: () => void;\n onLoopChange: (loop: boolean | number) => void;\n onRateChange: (rate: number) => void;\n}\n\nconst seconds = (ms: number): string => `${(ms / 1000).toFixed(2)}s`;\n\n/** Play/pause, a loop switch, a rate slider, and the playhead over the\n * duration in seconds. Holds no state: every control reports through a callback. */\nexport function Transport(props: TransportProps): ReactElement {\n const { paused, loop, rate, playhead, duration, onPlay, onPause, onLoopChange, onRateChange } = props;\n const looping = loop !== false && loop !== 0;\n // The handle's scale is whatever anything holding it set, so an off-list rate\n // gets its own detent — `DetentSlider` would otherwise round it to the\n // nearest and label the thumb with a rate the timeline is not running at.\n const rates = RATES.includes(rate as typeof RATES[number])\n ? (RATES as readonly number[])\n : [...RATES, rate].sort((a, b) => a - b);\n\n return (\n <div className={s.transport}>\n <button\n type=\"button\"\n className={s.transportButton}\n aria-label={paused ? 'Play' : 'Pause'}\n onClick={paused ? onPlay : onPause}\n >\n {paused ? <PlayIcon size={14} /> : <PauseIcon size={14} />}\n </button>\n\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={looping}\n aria-label=\"Loop\"\n className={s.transportButton}\n onClick={() => onLoopChange(!looping)}\n >\n ⟲\n </button>\n\n <div className={s.rate}>\n Rate\n <DetentSlider\n ariaLabel=\"Rate\"\n items={rates.map((r) => ({ value: r, ariaLabel: qty(r, RATE).spoken }))}\n value={rate}\n onChange={onRateChange}\n formatLabel={(r) => qty(r, RATE).text}\n labels=\"none\"\n className={s.rateSlider}\n />\n <span className={s.rateReadout}>{qty(rate, RATE).text}</span>\n </div>\n\n <span className={s.time} data-testid=\"timeline-time\">\n {seconds(playhead)} / {seconds(duration)}\n </span>\n </div>\n );\n}\n","import type { SampledTrack, Track } from '@weasel-js/core';\n\nexport interface LaneRow {\n /** Stable across re-renders and edits: the dotted index path. */\n key: string;\n /** Index path into the track tree, outermost first. */\n path: number[];\n track: Track;\n label: string;\n /** Nesting depth; 0 for a top-level track. */\n depth: number;\n /** Accumulated `at` from every nested parent, in ms. A key drawn on this row\n * sits at `offset + key.t` on the ruler. */\n offset: number;\n kind: Track['kind'];\n /** True only for a sampled track whose every key value is a number — the\n * sole case where a value axis has an honest meaning. */\n numeric: boolean;\n}\n\nfunction isNumeric(track: Track): boolean {\n if (track.kind !== 'sampled') return false;\n const keys = (track as SampledTrack<unknown>).keys;\n return keys.length > 0 && keys.every((k) => typeof k.value === 'number');\n}\n\n/** Flatten the track tree to rows, descending only into expanded nested\n * timelines. `expanded` holds the `key` of each open nested row. */\nexport function buildLanes(\n tracks: readonly Track[],\n expanded: ReadonlySet<string>,\n): LaneRow[] {\n const out: LaneRow[] = [];\n const walk = (list: readonly Track[], path: number[], depth: number, offset: number): void => {\n list.forEach((track, i) => {\n const nextPath = [...path, i];\n const key = nextPath.join('.');\n out.push({\n key,\n path: nextPath,\n track,\n label: track.label ?? `${track.kind} ${i}`,\n depth,\n offset,\n kind: track.kind,\n numeric: isNumeric(track),\n });\n if (track.kind === 'timeline' && expanded.has(key)) {\n walk(track.timeline.tracks, nextPath, depth + 1, offset + track.at);\n }\n });\n };\n walk(tracks, [], 0, 0);\n return out;\n}\n\n/** The track at an index path, or `undefined` when the path leaves the tree. */\nexport function trackAtPath(tracks: readonly Track[], path: readonly number[]): Track | undefined {\n let list: readonly Track[] = tracks;\n let found: Track | undefined;\n for (const i of path) {\n found = list[i];\n if (!found) return undefined;\n list = found.kind === 'timeline' ? found.timeline.tracks : [];\n }\n return found;\n}\n","import { sampleTrack, type EasingSpec, type EventTrack, type Keyframe, type SampledTrack, type TimelineTrack, type Track } from '@weasel-js/core';\nimport { trackAtPath } from './lanes';\n\n/** One keyframe, addressed by its track's place in the tree and its index in that track's `keys`. */\nexport interface KeySelection {\n /** Index path into the track tree, outermost first — a `LaneRow.path`. A\n * top-level track is `[i]`; a track inside the nested timeline at `i` is\n * `[i, j]`. */\n trackPath: readonly number[];\n keyIndex: number;\n}\n\nexport interface KeyEdit {\n tracks: Track[];\n selection: KeySelection | null;\n}\n\n/** True when two index paths address the same track. */\nexport function samePath(a: readonly number[], b: readonly number[]): boolean {\n return a.length === b.length && a.every((n, i) => n === b[i]);\n}\n\n/** Shallow-clone one track, replacing only its key list. `onTick`, `fire`,\n * `interpolate` and `interpolator` survive as references, which is what lets a\n * running timeline take an edited track without rewiring. */\nfunction withKeys(track: Track, keys: Keyframe<unknown>[]): Track {\n return { ...(track as SampledTrack<unknown>), keys } as Track;\n}\n\nfunction withEvents(track: Track, events: EventTrack['events']): Track {\n return { ...(track as EventTrack), events } as Track;\n}\n\n/** Replace the track at `path`, cloning the timeline tracks along the way so\n * the caller's tree is untouched at every level, not just the top. */\nfunction replaceAtPath(tracks: readonly Track[], path: readonly number[], next: Track): Track[] {\n const out = tracks.slice();\n const [i, ...rest] = path;\n if (rest.length === 0) {\n out[i] = next;\n return out;\n }\n const parent = out[i];\n if (parent?.kind !== 'timeline') return out;\n const tt = parent as TimelineTrack;\n out[i] = {\n ...tt,\n timeline: { ...tt.timeline, tracks: replaceAtPath(tt.timeline.tracks, rest, next) },\n };\n return out;\n}\n\n/** Move a key or an event crossing to `toMs`, clamped at zero. `toMs` is in the\n * addressed track's own time, not the ruler's — a nested track's keys are\n * measured from its parent's `at`.\n *\n * Re-sorts, and reports the moved entry's new index. `sampleTrack` binary-\n * searches without sorting first, so a drag past a neighbour that left the list\n * unsorted would sample the wrong segment and raise nothing. */\nexport function moveKey(\n tracks: readonly Track[], sel: KeySelection, toMs: number,\n): KeyEdit {\n const track = trackAtPath(tracks, sel.trackPath);\n const t = Math.max(0, toMs);\n\n if (track?.kind === 'sampled') {\n const st = track as SampledTrack<unknown>;\n const moved = { ...st.keys[sel.keyIndex], t };\n const keys = st.keys.filter((_, i) => i !== sel.keyIndex);\n const at = keys.findIndex((k) => k.t > t);\n const keyIndex = at === -1 ? keys.length : at;\n keys.splice(keyIndex, 0, moved);\n return {\n tracks: replaceAtPath(tracks, sel.trackPath, withKeys(track, keys)),\n selection: { trackPath: sel.trackPath, keyIndex },\n };\n }\n\n if (track?.kind === 'event') {\n const et = track as EventTrack;\n const moved = { ...et.events[sel.keyIndex], t };\n const events = et.events.filter((_, i) => i !== sel.keyIndex);\n const at = events.findIndex((e) => e.t > t);\n const keyIndex = at === -1 ? events.length : at;\n events.splice(keyIndex, 0, moved);\n return {\n tracks: replaceAtPath(tracks, sel.trackPath, withEvents(track, events)),\n selection: { trackPath: sel.trackPath, keyIndex },\n };\n }\n\n return { tracks: tracks.slice(), selection: sel };\n}\n\n/** Insert a key at `atMs`, seeded with whatever the track already reads there\n * so inserting alone never changes the motion. */\nexport function insertKey(\n tracks: readonly Track[], trackPath: readonly number[], atMs: number,\n): KeyEdit {\n const track = trackAtPath(tracks, trackPath);\n if (track?.kind !== 'sampled') return { tracks: tracks.slice(), selection: null };\n\n const st = track as SampledTrack<unknown>;\n const t = Math.max(0, atMs);\n const value = st.keys.length === 0 ? 0 : sampleTrack(st, t);\n const keys = st.keys.slice();\n const at = keys.findIndex((k) => k.t > t);\n const keyIndex = at === -1 ? keys.length : at;\n keys.splice(keyIndex, 0, { t, value });\n return {\n tracks: replaceAtPath(tracks, trackPath, withKeys(track, keys)),\n selection: { trackPath, keyIndex },\n };\n}\n\n/** Remove a key, selecting the one before it. */\nexport function deleteKey(tracks: readonly Track[], sel: KeySelection): KeyEdit {\n const track = trackAtPath(tracks, sel.trackPath);\n if (track?.kind !== 'sampled') return { tracks: tracks.slice(), selection: sel };\n\n const st = track as SampledTrack<unknown>;\n const keys = st.keys.filter((_, i) => i !== sel.keyIndex);\n return {\n tracks: replaceAtPath(tracks, sel.trackPath, withKeys(track, keys)),\n selection: keys.length === 0\n ? null\n : { trackPath: sel.trackPath, keyIndex: Math.max(0, sel.keyIndex - 1) },\n };\n}\n\n/** Set the easing shaping the approach INTO the selected key. */\nexport function setKeyEasing(\n tracks: readonly Track[], sel: KeySelection, easing: EasingSpec | undefined,\n): Track[] {\n const track = trackAtPath(tracks, sel.trackPath);\n if (track?.kind !== 'sampled') return tracks.slice();\n\n const st = track as SampledTrack<unknown>;\n const keys = st.keys.slice();\n const { easing: _drop, ...rest } = keys[sel.keyIndex];\n keys[sel.keyIndex] = easing === undefined ? rest : { ...rest, easing };\n return replaceAtPath(tracks, sel.trackPath, withKeys(track, keys));\n}\n\n/** Replace a sampled track's whole key list, keeping its callbacks. */\nexport function setKeys(\n tracks: readonly Track[], trackPath: readonly number[], keys: readonly Keyframe<unknown>[],\n): Track[] {\n const track = trackAtPath(tracks, trackPath);\n if (track?.kind !== 'sampled') return tracks.slice();\n return replaceAtPath(tracks, trackPath, withKeys(track, keys.slice()));\n}\n\n/** Set the selected key's value, leaving its time alone. */\nexport function setKeyValue(\n tracks: readonly Track[], sel: KeySelection, value: unknown,\n): Track[] {\n const track = trackAtPath(tracks, sel.trackPath);\n if (track?.kind !== 'sampled') return tracks.slice();\n\n const st = track as SampledTrack<unknown>;\n const keys = st.keys.slice();\n keys[sel.keyIndex] = { ...keys[sel.keyIndex], value };\n return replaceAtPath(tracks, sel.trackPath, withKeys(track, keys));\n}\n\nexport { snapToNearest as snapTime } from '../../snap';\n","import { useMemo, useState, type KeyboardEvent as ReactKeyboardEvent, type ReactElement, type ReactNode } from 'react';\nimport type { EasingSpec, Keyframe, SampledTrack, Track } from '@weasel-js/core';\nimport s from './Timeline.module.css';\nimport { EasingPicker } from './EasingPicker';\nimport { Lane } from './Lane';\nimport { Ruler } from './Ruler';\nimport { Transport, type TransportProps } from './Transport';\nimport { buildLanes, trackAtPath } from './lanes';\nimport { deleteKey, insertKey, moveKey, samePath, setKeyEasing, setKeys, setKeyValue, type KeySelection } from './keys';\nimport type { TimeWindow } from './timeScale';\n\nexport type { KeySelection } from './keys';\nexport type { TimeWindow } from './timeScale';\n\n/** What `renderKeyEditor` is handed for the selected keyframe. */\nexport interface KeyEditorCtx<T = unknown> {\n key: Keyframe<T>;\n track: SampledTrack<T>;\n selection: KeySelection;\n /** Replace the selected key's value — `next`'s time and easing are ignored.\n * Routed through this component's `onChange`. */\n commit: (next: Keyframe<T>) => void;\n /** Set the easing shaping the approach into this key. */\n setEasing: (easing: EasingSpec | undefined) => void;\n}\n\n/** Props for {@link Timeline}. Times are in ms. `selection` and `window` are\n * controlled when given and held internally otherwise. */\nexport interface TimelineProps {\n tracks: readonly Track[];\n duration: number;\n playhead: number;\n\n /** Keys as marks on a row (`'dope'`, the default), or plotted as a value\n * curve (`'graph'`) on rows whose values are numbers. */\n mode?: 'dope' | 'graph';\n onModeChange?: (mode: 'dope' | 'graph') => void;\n\n /** Live during a drag — wire for preview, do not write to history. */\n onInput?: (next: Track[]) => void;\n /** Committed at gesture end: one call per gesture. */\n onChange: (next: Track[]) => void;\n onScrub: (t: number) => void;\n\n /** `false` hides the transport. */\n transport?: Omit<TransportProps, 'playhead' | 'duration'> | false;\n selection?: KeySelection | null;\n onSelect?: (sel: KeySelection | null) => void;\n renderKeyEditor?: (ctx: KeyEditorCtx) => ReactNode;\n\n window?: TimeWindow;\n onWindowChange?: (w: TimeWindow) => void;\n\n label?: ReactNode;\n className?: string;\n}\n\n/** A keyframe editor for a set of tracks: a ruler to scrub and zoom on, a\n * lane per track, and a transport. Every edit comes back through `onChange`\n * as a new track array; Delete or Backspace removes the selected key. */\nexport function Timeline(props: TimelineProps): ReactElement {\n const {\n tracks, duration, playhead,\n onInput, onChange, onScrub,\n renderKeyEditor, label, className,\n } = props;\n\n const [expanded, setExpanded] = useState<ReadonlySet<string>>(() => new Set());\n const [ownWindow, setOwnWindow] = useState<TimeWindow>({ from: 0, to: duration });\n const [ownSelection, setOwnSelection] = useState<KeySelection | null>(null);\n const [segmentSelection, setSegmentSelection] = useState<KeySelection | null>(null);\n\n const win = props.window ?? ownWindow;\n const setWindow = props.onWindowChange ?? setOwnWindow;\n const selection = props.selection !== undefined ? props.selection : ownSelection;\n const setSelection = props.onSelect ?? setOwnSelection;\n\n const bounds: TimeWindow = { from: 0, to: duration };\n const rows = useMemo(() => buildLanes(tracks, expanded), [tracks, expanded]);\n\n /** Every key time on every row — what a dragged key snaps to. */\n const snapTimes = useMemo(() => {\n const out = new Set<number>([0, duration]);\n for (const row of rows) {\n if (row.kind === 'sampled') for (const k of (row.track as SampledTrack<unknown>).keys) out.add(k.t + row.offset);\n }\n return [...out];\n }, [rows, duration]);\n\n const toggle = (key: string): void => {\n setExpanded((prev) => {\n const next = new Set(prev);\n if (next.has(key)) next.delete(key); else next.add(key);\n return next;\n });\n };\n\n const onRootKeyDown = (e: ReactKeyboardEvent<HTMLDivElement>): void => {\n if ((e.key === 'Delete' || e.key === 'Backspace') && selection) {\n e.preventDefault();\n const r = deleteKey(tracks, selection);\n onChange(r.tracks);\n setSelection(r.selection);\n }\n };\n\n const selectedTrack = selection ? trackAtPath(tracks, selection.trackPath) : undefined;\n const selectedKey = selectedTrack?.kind === 'sampled'\n ? (selectedTrack as SampledTrack<unknown>).keys[selection!.keyIndex]\n : undefined;\n\n const segmentTrack = segmentSelection ? trackAtPath(tracks, segmentSelection.trackPath) : undefined;\n const segmentKey = segmentTrack?.kind === 'sampled'\n ? (segmentTrack as SampledTrack<unknown>).keys[segmentSelection!.keyIndex]\n : undefined;\n\n return (\n <div\n className={[s.root, className].filter(Boolean).join(' ')}\n data-testid=\"timeline-root\"\n tabIndex={-1}\n onKeyDown={onRootKeyDown}\n >\n {label ? <div className={s.label}>{label}</div> : null}\n\n {props.transport === false ? null : (\n <div className={s.transportRow}>\n <Transport\n {...(props.transport ?? {\n paused: true, loop: false, rate: 1,\n onPlay: () => {}, onPause: () => {},\n onLoopChange: () => {}, onRateChange: () => {},\n })}\n playhead={playhead}\n duration={duration}\n />\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={(props.mode ?? 'dope') === 'graph'}\n aria-label=\"Graph mode\"\n className={s.transportButton}\n onClick={() => props.onModeChange?.((props.mode ?? 'dope') === 'graph' ? 'dope' : 'graph')}\n >\n Graph\n </button>\n </div>\n )}\n\n <div className={s.body}>\n <div className={s.gutter} />\n <div className={s.rulerWrap}>\n <Ruler\n window={win}\n bounds={bounds}\n playhead={playhead}\n onScrub={onScrub}\n onWindowChange={setWindow}\n />\n </div>\n </div>\n\n <div className={s.lanes}>\n {rows.map((row) => {\n const trackPath = row.path;\n return (\n <Lane\n key={row.key}\n row={row}\n window={win}\n mode={props.mode ?? 'dope'}\n selection={selection && samePath(selection.trackPath, trackPath) ? selection.keyIndex : null}\n expanded={expanded.has(row.key)}\n snapTimes={snapTimes}\n onToggleExpand={() => toggle(row.key)}\n onSelect={(keyIndex) => setSelection({ trackPath, keyIndex })}\n onKeyInput={(keyIndex, toMs) => {\n onInput?.(moveKey(tracks, { trackPath, keyIndex }, toMs).tracks);\n }}\n onKeyCommit={(keyIndex, toMs) => {\n const r = moveKey(tracks, { trackPath, keyIndex }, toMs);\n onChange(r.tracks);\n setSelection(r.selection);\n }}\n onKeysInput={(keys) => onInput?.(setKeys(tracks, trackPath, keys))}\n onKeysCommit={(keys) => onChange(setKeys(tracks, trackPath, keys))}\n onInsert={(atMs) => {\n const r = insertKey(tracks, trackPath, atMs);\n onChange(r.tracks);\n setSelection(r.selection);\n }}\n selectedSegment={segmentSelection && samePath(segmentSelection.trackPath, trackPath) ? segmentSelection.keyIndex : null}\n onSelectSegment={(keyIndex) => setSegmentSelection({ trackPath, keyIndex })}\n />\n );\n })}\n </div>\n\n {renderKeyEditor && selection && selectedKey && selectedTrack?.kind === 'sampled' ? (\n <div className={s.inspector}>\n {renderKeyEditor({\n key: selectedKey,\n track: selectedTrack as SampledTrack<unknown>,\n selection,\n commit: (next) => onChange(setKeyValue(tracks, selection, next.value)),\n setEasing: (easing) => onChange(setKeyEasing(tracks, selection, easing)),\n })}\n </div>\n ) : null}\n\n {segmentSelection && segmentKey ? (\n <div className={s.inspector}>\n <EasingPicker\n value={segmentKey.easing}\n onChange={(next) => onChange(setKeyEasing(tracks, segmentSelection, next))}\n />\n </div>\n ) : null}\n </div>\n );\n}\n","import { useCallback, useEffect, useReducer, type ReactElement } from 'react';\nimport { useLatest, useVisibleRaf, type TimelineHandle, type Track } from '@weasel-js/core';\nimport { Timeline, type TimelineProps } from './Timeline';\n\n/** Props for {@link AnimatedTimeline}: `<Timeline>`'s, less what it reads off `handle`. */\nexport interface AnimatedTimelineProps\n extends Omit<TimelineProps, 'tracks' | 'duration' | 'playhead' | 'onChange' | 'onScrub' | 'transport'> {\n handle: TimelineHandle;\n /** `false` hides the transport, as on `<Timeline>`. */\n transport?: false;\n}\n\n/** Binds `<Timeline>` to a live handle.\n *\n * `handle.tracks()` returns the timeline's own array, not a copy, so an edit\n * splices it in place inside `edit()`. Assigning a replacement array would\n * update nothing and raise nothing. */\nexport function AnimatedTimeline(props: AnimatedTimelineProps): ReactElement {\n const { handle, ...rest } = props;\n const [, bump] = useReducer((n: number) => n + 1, 0);\n\n // `subscribe` fires on `edit` only, so the playhead comes from the frame loop.\n useEffect(() => handle.subscribe(bump), [handle]);\n\n // `useVisibleRaf` returns a controller; a continuous loop re-requests from\n // inside its own frame, and nothing runs until something calls `request`.\n // No `onResume` here: this loop measures no elapsed time of its own, it only\n // reads the playhead the animator already advanced.\n const raf = useVisibleRaf(() => {\n bump();\n if (!handle.isPaused()) rafRef.current.request();\n });\n const rafRef = useLatest(raf);\n useEffect(() => {\n if (!handle.isPaused()) raf.request();\n return () => raf.cancel();\n }, [handle, raf]);\n\n const onChange = useCallback((next: Track[]) => {\n handle.edit(() => {\n const live = handle.tracks() as Track[];\n live.splice(0, live.length, ...next);\n });\n }, [handle]);\n\n const playhead = handle.time();\n const duration = handle.duration();\n\n return (\n <Timeline\n {...rest}\n tracks={handle.tracks()}\n duration={duration}\n playhead={playhead}\n onChange={onChange}\n onScrub={handle.seek}\n transport={props.transport === false ? false : {\n paused: handle.isPaused(),\n loop: handle.loop(),\n rate: handle.timeScale(),\n onPlay: () => {\n // `rearm` declines to revive a timeline at its duration, so resume\n // alone would do nothing. Play-at-end rewinds first.\n if (handle.time() >= duration) handle.seek(0);\n handle.resume();\n raf.request();\n bump();\n },\n onPause: () => { handle.pause(); bump(); },\n onLoopChange: (next) => { handle.setLoop(next); bump(); },\n onRateChange: (next) => { handle.setTimeScale(next); bump(); },\n }}\n />\n );\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"TokenPanel-DAkL6vwQ.js","names":["s","_Fragment","NumberField","Select","Input","Button","TooltipTrigger","Tooltip","Tooltip$1","TooltipTrigger$1","Button$1","Select$1","Input$1","NumberField$1","ToggleBar","HEX","RGB","COLOR_FUNCTION","KEYWORDS","browserSupports","value","parsesAsColor","v","supports","hexOf","digits","d","rgb","n","paint","serialized","onBlack","toHex","direct","browser","TokenPanel_module_default","round2","refitScale","first","amounts","scale","last","spans","kind","a","TOKEN_CATEGORIES","TYPE_SCALE","tokenCategory","token","AMOUNT","DIMENSION","DURATION","NUMBER","LENGTH","BEZIER","GRADIENT","LAYERS","PARTS","isShadow","parts","layer","lengths","part","rest","inferTokenType","splitUnit","match","bezierPoints","x1","y1","x2","y2","WEIGHTS","NUMBER_FORMAT","SCALAR","familyKindOf","itemsOf","counts","tokens","families","items","key","familySize","family","stepLabels","split","names","name","shared","segments","Curve","points","top","bottom","label","TokenControl","hex","useMemo","type","Fragment","amount","set","options","weight","event","TokenRow","memo","useCallback","onChange","b","Family","picked","setPicked","useState","t","group","Focusable","current","ScaleStep","showUnit","factor","onFactorChange","next","RULES","ScaleRuleControls","param","rule","base","onScaleChange","unit","Scale","labels","units","overridden","generator","at","i","j","f","Section","title","DisclosureRow","open","item","scales","TokenPanel","ownCollapsed","setOwnCollapsed","closed","collapsed","byCategory","sections","category","categorize","density","className","inSection","id","prev","onCollapsedChange"],"sources":["../../src/components/TokenPanel/color.ts","../../src/components/TokenPanel/TokenPanel.module.css","../../src/components/TokenPanel/tokenTypes.ts","../../src/components/TokenPanel/TokenPanel.tsx"],"sourcesContent":["const HEX = /^#(?:[\\da-f]{3,4}|[\\da-f]{6}|[\\da-f]{8})$/i;\nconst RGB = /^rgba?\\(\\s*(\\d{1,3})[\\s,]+(\\d{1,3})[\\s,]+(\\d{1,3})\\b/i;\nconst COLOR_FUNCTION = /^(?:rgba?|hsla?|hwb|lab|lch|oklab|oklch|color|color-mix)\\(.+\\)$/i;\nconst KEYWORDS = new Set([\n 'transparent',\n 'currentcolor',\n 'black',\n 'white',\n 'gray',\n 'silver',\n 'red',\n 'orange',\n 'yellow',\n 'gold',\n 'lime',\n 'green',\n 'teal',\n 'cyan',\n 'blue',\n 'navy',\n 'purple',\n 'magenta',\n 'pink',\n 'brown',\n]);\n\ntype Supports = ((value: string) => boolean) | null;\n\nfunction browserSupports(): Supports {\n return typeof CSS !== 'undefined' && typeof CSS.supports === 'function'\n ? (value) => CSS.supports('color', value)\n : null;\n}\n\n/** Whether `value` is a CSS color — the browser's answer where it has `CSS.supports`, a small parser's otherwise. */\nexport function parsesAsColor(value: string, supports: Supports = browserSupports()): boolean {\n const v = value.trim();\n // `CSS.supports` accepts any `var()` for any property.\n if (!v || v.includes('var(')) return false;\n if (supports) return supports(v);\n return HEX.test(v) || COLOR_FUNCTION.test(v) || KEYWORDS.has(v.toLowerCase());\n}\n\nfunction hexOf(v: string): string | null {\n if (HEX.test(v)) {\n const digits = v.slice(1).toLowerCase();\n return `#${digits.length <= 4 ? [...digits.slice(0, 3)].map((d) => d + d).join('') : digits.slice(0, 6)}`;\n }\n const rgb = RGB.exec(v);\n return rgb\n ? `#${rgb\n .slice(1, 4)\n .map((n) => Math.min(255, Number(n)).toString(16).padStart(2, '0'))\n .join('')}`\n : null;\n}\n\nlet paint: CanvasRenderingContext2D | null | undefined;\n\n/** The browser's serialization of a color, through a canvas fill style; null where it has none or rejects `value`. */\nfunction serialized(value: string): string | null {\n if (paint === undefined) {\n try {\n paint = document.createElement('canvas').getContext('2d');\n } catch {\n paint = null;\n }\n }\n if (!paint) return null;\n // A rejected value leaves the previous fill style, so two different ones tell a rejection apart.\n paint.fillStyle = '#000000';\n paint.fillStyle = value;\n const onBlack = String(paint.fillStyle);\n paint.fillStyle = '#ffffff';\n paint.fillStyle = value;\n return onBlack === String(paint.fillStyle) ? onBlack : null;\n}\n\n/** `value` as the `#rrggbb` a color input takes, alpha dropped; null when it cannot be converted. */\nexport function toHex(value: string): string | null {\n const v = value.trim();\n const direct = hexOf(v);\n if (direct) return direct;\n const browser = typeof document === 'undefined' ? null : serialized(v);\n return browser && browser !== v ? hexOf(browser) : null;\n}\n",".panel {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.section {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.section + .section {\n border-top: var(--wzl-border-w) solid var(--wzl-line-subtle);\n}\n\n.sectionHead {\n padding-block: var(--wzl-space-xs);\n color: var(--wzl-fg-muted);\n font-family: var(--wzl-font-display);\n font-size: var(--wzl-font-size-sm);\n letter-spacing: 0.08em;\n text-transform: uppercase;\n}\n\n.sectionBody {\n display: flex;\n flex-direction: column;\n gap: var(--wzl-space-sm);\n padding-block-end: var(--wzl-space-sm);\n}\n\n.row,\n.family {\n display: flex;\n flex-direction: column;\n gap: var(--wzl-space-1);\n min-width: 0;\n}\n\n.name {\n overflow: hidden;\n color: var(--wzl-fg-muted);\n font-family: var(--wzl-font-mono);\n font-size: var(--wzl-font-size-sm);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.edit {\n display: flex;\n align-items: center;\n gap: var(--wzl-space-xs);\n min-width: 0;\n}\n\n.field {\n --wzl-font-ui: var(--wzl-font-mono);\n flex: 1;\n min-width: 0;\n}\n\n.number {\n --wzl-font-ui: var(--wzl-font-mono);\n flex: 1;\n min-width: 0;\n}\n\n.unit {\n color: var(--wzl-fg-muted);\n font-family: var(--wzl-font-mono);\n font-size: var(--wzl-font-size-sm);\n}\n\n/* Padding inflates a native color input past its box in Chrome. */\n.swatchInput {\n box-sizing: border-box;\n flex-shrink: 0;\n width: 18px;\n height: 14px;\n padding: 0;\n background: transparent;\n border: 0;\n border-radius: var(--wzl-radius-sm);\n cursor: pointer;\n}\n\n.chip {\n flex-shrink: 0;\n width: 18px;\n height: 14px;\n background: var(--token-color);\n border-radius: var(--wzl-radius-sm);\n box-shadow: inset 0 0 0 1px var(--wzl-line);\n}\n\n.curve {\n flex-shrink: 0;\n width: 24px;\n /* Square easing-curve artwork: not-a-control */\n height: 24px;\n color: var(--wzl-fg);\n}\n\n.strip {\n display: flex;\n height: var(--wzl-control-h);\n overflow: hidden;\n border: var(--wzl-border-w) solid var(--wzl-line);\n border-radius: var(--wzl-radius-sm);\n}\n\n.swatch {\n flex: 1 1 0;\n min-width: 0;\n height: 100%;\n padding: 0;\n background: var(--token-color);\n border: 0;\n border-radius: 0;\n cursor: pointer;\n}\n\n.swatch[data-overridden] {\n box-shadow: inset 0 -3px 0 var(--wzl-accent);\n}\n\n.swatch[aria-pressed='true'],\n.swatch:focus-visible {\n position: relative;\n z-index: 1;\n outline: 2px solid var(--wzl-focus-ring);\n outline-offset: -2px;\n}\n\n/* A group of sizes: its name over a grid of steps, each labeled by what its name adds. */\n.scale {\n display: flex;\n flex-direction: column;\n gap: var(--wzl-space-1);\n min-width: 0;\n}\n\n.scaleHead {\n display: flex;\n align-items: center;\n gap: var(--wzl-space-xs);\n min-width: 0;\n}\n\n.steps {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(4rem, 1fr));\n gap: var(--wzl-space-1) var(--wzl-space-xs);\n}\n\n.rule {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--wzl-space-xs) var(--wzl-space-sm);\n min-width: 0;\n}\n\n.ruleLabel {\n color: var(--wzl-fg-subtle);\n font-family: var(--wzl-font-mono);\n font-size: var(--wzl-font-size-2xs);\n}\n\n.rule .number {\n flex: 0 1 5ch;\n font-variant-numeric: tabular-nums;\n font-family: var(--wzl-font-numeric);\n text-align: end;\n}\n\n.step {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.stepLabel {\n overflow: hidden;\n text-align: end;\n color: var(--wzl-fg-subtle);\n font-family: var(--wzl-font-mono);\n font-size: var(--wzl-font-size-2xs);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.step[data-overridden] .stepLabel {\n color: var(--wzl-accent-fg);\n}\n\n.stepEdit {\n display: flex;\n align-items: center;\n gap: var(--wzl-space-1);\n min-width: 0;\n}\n\n.step > .stepEdit + .stepEdit {\n margin-block-start: var(--wzl-space-1);\n}\n\n.stepEdit .number {\n font-variant-numeric: tabular-nums;\n font-family: var(--wzl-font-numeric);\n text-align: end;\n}\n\n/* Tight: a token to a line, with the names on a fixed rail so the values read\n down the panel as a column. A stacked row spends its height on the name and\n lets the field take the whole width, which a set of ninety cannot afford. */\n.tight .sectionBody {\n gap: var(--wzl-space-1);\n padding-block-end: var(--wzl-space-xs);\n}\n\n.tight .row {\n flex-direction: row;\n align-items: center;\n gap: var(--wzl-space-xs);\n}\n\n.tight .row > .name {\n flex: 0 0 11rem;\n}\n\n.tight .edit {\n flex: 1 1 auto;\n}\n\n.tight .field,\n.tight .number {\n flex: 0 1 16rem;\n font-variant-numeric: tabular-nums;\n font-family: var(--wzl-font-numeric);\n}\n\n.tight .unit {\n font-variant-numeric: tabular-nums;\n font-family: var(--wzl-font-numeric);\n}\n\n.tight .scale {\n display: grid;\n grid-template-columns: 11rem minmax(0, 1fr);\n align-items: start;\n column-gap: var(--wzl-space-xs);\n}\n\n/* Grid rather than a row, so a picked swatch's editor still spans the full width. */\n.tight .family {\n display: grid;\n grid-template-columns: 11rem minmax(0, 1fr);\n align-items: center;\n column-gap: var(--wzl-space-xs);\n row-gap: var(--wzl-space-1);\n}\n\n.tight .family > .row {\n grid-column: 1 / -1;\n}\n\n.tight .strip {\n height: 16px;\n}\n\n.tight .curve {\n width: 16px;\n height: 16px;\n}\n","import { parsesAsColor } from './color';\n\n/** A design token as `TokenPanel` edits it. */\nexport interface TokenEntry {\n /** The custom property, e.g. `--wzl-gray-50`. */\n name: string;\n /** A DTCG type name. A type the panel has no control for edits as text. */\n type: string;\n /** Tokens sharing a group sit together; a color group large enough draws as one row of swatches. */\n group: string;\n value: string;\n /** Whether `value` overrides the token's own, which offers Reset. */\n overridden?: boolean;\n description?: string;\n}\n\n/** How a scale's steps follow from its base: one multiplier per step, a constant ratio, or a constant step. */\nexport type TokenScaleRule =\n | { kind: 'factors'; factors: readonly number[] }\n | { kind: 'ratio'; ratio: number }\n | { kind: 'step'; step: number };\n\n/** A group whose steps are generated from a base, so the panel edits the base and the rule rather than each step. */\nexport interface TokenScale {\n /** The generated tokens, smallest first. Anything else in the group draws as an ordinary step. */\n tokens: readonly string[];\n base: number;\n rule: TokenScaleRule;\n}\n\nconst round2 = (n: number) => Math.round(n * 100) / 100;\n\n/**\n * The same scale under another rule, fitted to the amounts its steps have now.\n * A ratio or step counts from the first step, so switching to one moves the\n * base there.\n */\nexport function refitScale(scale: TokenScale, kind: TokenScaleRule['kind'], amounts: readonly number[]): TokenScale {\n const first = amounts[0] ?? scale.base;\n const last = amounts.at(-1) ?? first;\n const spans = Math.max(1, amounts.length - 1);\n switch (kind) {\n case 'factors':\n return { ...scale, rule: { kind, factors: amounts.map((a) => (scale.base ? round2(a / scale.base) : 1)) } };\n case 'ratio':\n return { ...scale, base: first, rule: { kind, ratio: first > 0 ? round2((last / first) ** (1 / spans)) : 1 } };\n case 'step':\n return { ...scale, base: first, rule: { kind, step: round2((last - first) / spans) } };\n }\n}\n\n/** A section of the token panel; `tokenCategory` files each token under one. */\nexport type TokenCategory ='color' | 'type' | 'size' | 'motion' | 'depth' | 'other';\n\n/** The panel's sections, in the order it draws them. */\nexport const TOKEN_CATEGORIES: readonly { id: TokenCategory; title: string }[] = [\n { id: 'color', title: 'Color' },\n { id: 'type', title: 'Type' },\n { id: 'size', title: 'Size' },\n { id: 'motion', title: 'Motion' },\n { id: 'depth', title: 'Depth' },\n { id: 'other', title: 'Other' },\n];\n\nconst TYPE_SCALE = /^(?:font|tracking|leading|letter|line-height)/;\n\n/** The section a token is filed under: by its type, and for a bare dimension or number by its group. */\nexport function tokenCategory(token: TokenEntry): TokenCategory {\n switch (token.type) {\n case 'color':\n return 'color';\n case 'fontFamily':\n case 'fontWeight':\n return 'type';\n case 'duration':\n case 'cubicBezier':\n return 'motion';\n case 'shadow':\n case 'gradient':\n return 'depth';\n case 'dimension':\n case 'number':\n if (TYPE_SCALE.test(token.group)) return 'type';\n if (token.type === 'number' && (token.group === 'z' || token.group === 'z-index')) return 'depth';\n return 'size';\n default:\n return 'other';\n }\n}\n\nconst AMOUNT = String.raw`-?(?:\\d+\\.?\\d*|\\.\\d+)`;\nconst DIMENSION = new RegExp(\n `^${AMOUNT}(?:px|rem|em|%|ch|ex|lh|rlh|vh|vw|vmin|vmax|svh|lvh|dvh|cqw|cqh|pt|pc|cm|mm|in|q)$`,\n 'i',\n);\nconst DURATION = new RegExp(`^${AMOUNT}m?s$`);\nconst NUMBER = new RegExp(`^${AMOUNT}$`);\nconst LENGTH = new RegExp(`^${AMOUNT}(?:px|rem|em)?$`);\nconst BEZIER = new RegExp(`^cubic-bezier\\\\(\\\\s*(${AMOUNT})\\\\s*,\\\\s*(${AMOUNT})\\\\s*,\\\\s*(${AMOUNT})\\\\s*,\\\\s*(${AMOUNT})\\\\s*\\\\)$`, 'i');\nconst GRADIENT = /^(?:repeating-)?(?:linear|radial|conic)-gradient\\(/i;\n/** Commas outside parentheses: a shadow's layers, not a color function's arguments. */\nconst LAYERS = /,(?![^(]*\\))/;\nconst PARTS = /[a-z-]+\\([^)]*\\)|\\S+/gi;\n\nfunction isShadow(value: string): boolean {\n return value.split(LAYERS).every((layer) => {\n const parts = layer.trim().replace(/^inset\\s+/i, '').match(PARTS) ?? [];\n const lengths = parts.filter((part) => LENGTH.test(part));\n const rest = parts.filter((part) => !LENGTH.test(part));\n return lengths.length >= 2 && lengths.length <= 4 && rest.length <= 1 && rest.every((part) => parsesAsColor(part));\n });\n}\n\n/** A DTCG type read off the shape of a value, for a token that arrives without one. `'string'` when nothing fits. */\nexport function inferTokenType(value: string): string {\n const v = value.trim();\n if (!v) return 'string';\n if (DURATION.test(v)) return 'duration';\n if (DIMENSION.test(v)) return 'dimension';\n if (NUMBER.test(v)) return 'number';\n if (BEZIER.test(v)) return 'cubicBezier';\n if (GRADIENT.test(v)) return 'gradient';\n if (parsesAsColor(v)) return 'color';\n if (isShadow(v)) return 'shadow';\n return 'string';\n}\n\n/** A number and its unit, or null for a value that is not one. */\nexport function splitUnit(value: string): { amount: number; unit: string } | null {\n const match = new RegExp(`^(${AMOUNT})([a-z%]*)$`, 'i').exec(value.trim());\n return match ? { amount: Number(match[1]), unit: match[2] ?? '' } : null;\n}\n\n/** The four control points of a `cubic-bezier()`, or null for any other easing. */\nexport function bezierPoints(value: string): readonly [number, number, number, number] | null {\n const match = BEZIER.exec(value.trim());\n if (!match) return null;\n const [x1, y1, x2, y2] = match.slice(1, 5).map(Number) as [number, number, number, number];\n return [x1, y1, x2, y2];\n}\n","import { type CSSProperties, memo, useCallback, useMemo, useState } from 'react';\nimport { Focusable } from 'react-aria-components';\nimport { Button } from '../Button';\nimport { DisclosureRow } from '../Disclosure';\nimport { Input } from '../Input';\nimport { NumberField } from '../NumberField';\nimport { Select } from '../Select';\nimport { ToggleBar, type ToggleBarItem } from '../ToggleBar';\nimport { Tooltip, TooltipTrigger } from '../Tooltip';\nimport { toHex } from './color';\nimport s from './TokenPanel.module.css';\nimport {\n bezierPoints,\n refitScale,\n splitUnit,\n TOKEN_CATEGORIES,\n type TokenCategory,\n type TokenEntry,\n type TokenScale,\n type TokenScaleRule,\n tokenCategory,\n} from './tokenTypes';\n\ntype OnChange = (name: string, value: string | null) => void;\n\n/** Props for `<TokenPanel>`. */\nexport interface TokenPanelProps {\n tokens: readonly TokenEntry[];\n /** A token's new value, or null to drop its override. Keep it stable: rows re-render only when it or their token changes. */\n onChange: OnChange;\n /** Sections held closed, by category. Omit to let the panel keep that state itself. */\n collapsed?: Readonly<Partial<Record<TokenCategory, boolean>>>;\n onCollapsedChange?: (category: TokenCategory, collapsed: boolean) => void;\n /** Groups generated from a base, by group name. Such a group edits its base and rule above its steps. */\n scales?: Readonly<Record<string, TokenScale>>;\n /** A scale's new base or rule. The panel does not regenerate the steps; the consumer does, through `onChange`. */\n onScaleChange?: (group: string, scale: TokenScale) => void;\n /** Files a token under a section. Defaults to `tokenCategory`. */\n categorize?: (token: TokenEntry) => TokenCategory;\n /** The fewest colors, or sizes, sharing a group that draw as one row of swatches or one grid of steps. Default 3. */\n familySize?: number;\n /**\n * Row layout. `tight` puts a token on one line — name, control, Reset — with the\n * names on a fixed rail so the values read down the panel as a column, and caps\n * the field rather than letting it span the panel. Default `normal` stacks the\n * control under its name.\n */\n density?: 'tight' | 'normal';\n className?: string;\n}\n\nconst WEIGHTS = ['100', '200', '300', '400', '500', '600', '700', '800', '900'];\nconst NUMBER_FORMAT = { maximumFractionDigits: 4, useGrouping: false } as const;\n\ntype FamilyKind = 'family' | 'scale';\ntype Item = { kind: 'row'; token: TokenEntry } | { kind: FamilyKind; group: string; tokens: TokenEntry[] };\n\nconst SCALAR = new Set(['dimension', 'duration', 'number']);\n\nfunction familyKindOf(token: TokenEntry): FamilyKind | null {\n if (token.type === 'color') return 'family';\n return SCALAR.has(token.type) && splitUnit(token.value) ? 'scale' : null;\n}\n\nfunction itemsOf(tokens: readonly TokenEntry[], familySize: number): Item[] {\n const counts = new Map<string, number>();\n for (const token of tokens) {\n const kind = familyKindOf(token);\n if (kind) counts.set(`${kind}:${token.group}`, (counts.get(`${kind}:${token.group}`) ?? 0) + 1);\n }\n const families = new Map<string, Extract<Item, { kind: FamilyKind }>>();\n const items: Item[] = [];\n for (const token of tokens) {\n const kind = familyKindOf(token);\n const key = `${kind}:${token.group}`;\n if (!kind || (counts.get(key) ?? 0) < familySize) {\n items.push({ kind: 'row', token });\n continue;\n }\n let family = families.get(key);\n if (!family) {\n family = { kind, group: token.group, tokens: [] };\n families.set(key, family);\n items.push(family);\n }\n family.tokens.push(token);\n }\n return items;\n}\n\n/** Each name's segments past those every name in the set shares; a name that is all shared keeps its last. */\nfunction stepLabels(names: readonly string[]): string[] {\n const split = names.map((name) => name.replace(/^--/, '').split('-'));\n let shared = 0;\n while (split.every((segments) => segments.length > shared && segments[shared] === split[0]?.[shared])) shared++;\n return split.map((segments) => segments.slice(shared).join('-') || (segments.at(-1) ?? ''));\n}\n\nfunction Curve({ points, label }: { points: readonly [number, number, number, number]; label: string }) {\n const [x1, y1, x2, y2] = points;\n // y is flipped so the curve rises, and the box grows to hold an overshoot.\n const top = Math.min(0, 1 - y1, 1 - y2);\n const bottom = Math.max(1, 1 - y1, 1 - y2);\n return (\n <svg className={s.curve} viewBox={`-0.1 ${top - 0.1} 1.2 ${bottom - top + 0.2}`} role=\"img\" aria-label={label}>\n <path d={`M0 1 C${x1} ${1 - y1} ${x2} ${1 - y2} 1 0`} fill=\"none\" stroke=\"currentColor\" strokeWidth={0.08} />\n </svg>\n );\n}\n\nfunction TokenControl({ token, set }: { token: TokenEntry; set: (value: string) => void }) {\n const { name, type, value } = token;\n const label = `${name} value`;\n const hex = useMemo(() => (type === 'color' ? toHex(value) : null), [type, value]);\n\n if (type === 'dimension' || type === 'duration' || type === 'number') {\n const split = splitUnit(value);\n if (split) {\n return (\n <>\n <NumberField\n className={s.number}\n aria-label={label}\n value={split.amount}\n hideSteppers\n formatOptions={NUMBER_FORMAT}\n onChange={(amount) => {\n if (Number.isFinite(amount)) set(`${amount}${split.unit}`);\n }}\n />\n {split.unit ? <span className={s.unit}>{split.unit}</span> : null}\n </>\n );\n }\n }\n\n if (type === 'fontWeight') {\n const options = (WEIGHTS.includes(value) ? WEIGHTS : [...WEIGHTS, value]).map((weight) => ({\n value: weight,\n label: weight,\n }));\n return <Select aria-label={name} width=\"fit\" selectedKey={value} options={options} onSelectionChange={set} />;\n }\n\n const points = type === 'cubicBezier' ? bezierPoints(value) : null;\n return (\n <>\n {points ? <Curve points={points} label={`${name} curve`} /> : null}\n <Input\n className={s.field}\n aria-label={label}\n value={value}\n onChange={set}\n leadingAdornment={\n type !== 'color' ? undefined : hex ? (\n <input\n type=\"color\"\n className={s.swatchInput}\n aria-label={`${name} color`}\n value={hex}\n onChange={(event) => set(event.target.value)}\n />\n ) : (\n <span className={s.chip} style={{ '--token-color': value } as CSSProperties} aria-hidden=\"true\" />\n )\n }\n />\n </>\n );\n}\n\nconst TokenRow = memo(\n function TokenRow({ token, onChange }: { token: TokenEntry; onChange: OnChange }) {\n const set = useCallback((value: string) => onChange(token.name, value), [onChange, token.name]);\n return (\n <div className={s.row} role=\"group\" aria-label={token.name}>\n <span className={s.name} title={token.description || token.name}>\n {token.name}\n </span>\n <div className={s.edit}>\n <TokenControl token={token} set={set} />\n {token.overridden ? (\n <Button variant=\"ghost\" size=\"sm\" ariaLabel={`Reset ${token.name}`} onClick={() => onChange(token.name, null)}>\n Reset\n </Button>\n ) : null}\n </div>\n </div>\n );\n },\n // Consumers rebuild their entries each render; a row only has to redraw when what it shows changed.\n (a, b) =>\n a.onChange === b.onChange &&\n a.token.name === b.token.name &&\n a.token.type === b.token.type &&\n a.token.value === b.token.value &&\n a.token.overridden === b.token.overridden &&\n a.token.description === b.token.description,\n);\n\nfunction Family({ group, tokens, onChange }: { group: string; tokens: readonly TokenEntry[]; onChange: OnChange }) {\n const [picked, setPicked] = useState<string | null>(null);\n const token = tokens.find((t) => t.name === picked) ?? null;\n return (\n <div className={s.family} role=\"group\" aria-label={group}>\n <span className={s.name}>{group}</span>\n <div className={s.strip}>\n {tokens.map((t) => (\n <TooltipTrigger key={t.name} delay={0}>\n <Focusable>\n <button\n type=\"button\"\n className={s.swatch}\n style={{ '--token-color': t.value } as CSSProperties}\n aria-label={t.name}\n aria-pressed={t.name === picked}\n data-overridden={t.overridden ? '' : undefined}\n onClick={() => setPicked((current) => (current === t.name ? null : t.name))}\n />\n </Focusable>\n <Tooltip>{t.name}</Tooltip>\n </TooltipTrigger>\n ))}\n </div>\n {token ? <TokenRow token={token} onChange={onChange} /> : null}\n </div>\n );\n}\n\nfunction ScaleStep({\n token,\n label,\n showUnit,\n factor,\n onChange,\n onFactorChange,\n}: {\n token: TokenEntry;\n label: string;\n showUnit: boolean;\n factor?: number;\n onChange: OnChange;\n onFactorChange?: (factor: number) => void;\n}) {\n const split = splitUnit(token.value);\n if (!split) return null;\n return (\n <div className={s.step} title={token.description || token.name} data-overridden={token.overridden ? '' : undefined}>\n <span className={s.stepLabel}>{label}</span>\n <span className={s.stepEdit}>\n <NumberField\n className={s.number}\n aria-label={`${token.name} value`}\n value={split.amount}\n hideSteppers\n formatOptions={NUMBER_FORMAT}\n onChange={(amount) => {\n if (Number.isFinite(amount)) onChange(token.name, `${amount}${split.unit}`);\n }}\n />\n {showUnit && split.unit ? <span className={s.unit}>{split.unit}</span> : null}\n </span>\n {factor !== undefined && onFactorChange ? (\n <span className={s.stepEdit}>\n <span className={s.unit}>×</span>\n <NumberField\n className={s.number}\n aria-label={`${token.name} factor`}\n value={factor}\n hideSteppers\n formatOptions={NUMBER_FORMAT}\n onChange={(next) => {\n if (Number.isFinite(next)) onFactorChange(next);\n }}\n />\n </span>\n ) : null}\n </div>\n );\n}\n\nconst RULES: readonly ToggleBarItem<TokenScaleRule['kind']>[] = [\n { value: 'factors', label: 'each', ariaLabel: 'factors' },\n { value: 'ratio', label: 'ratio' },\n { value: 'step', label: 'step' },\n];\n\nfunction ScaleRuleControls({\n group,\n scale,\n unit,\n amounts,\n onScaleChange,\n}: {\n group: string;\n scale: TokenScale;\n unit: string;\n amounts: readonly number[];\n onScaleChange: (scale: TokenScale) => void;\n}) {\n const { rule } = scale;\n const param =\n rule.kind === 'ratio'\n ? { key: 'ratio' as const, value: rule.ratio }\n : rule.kind === 'step'\n ? { key: 'step' as const, value: rule.step }\n : null;\n return (\n <div className={s.rule}>\n <span className={s.stepEdit}>\n <span className={s.ruleLabel}>base</span>\n <NumberField\n className={s.number}\n aria-label={`${group} base`}\n value={scale.base}\n hideSteppers\n formatOptions={NUMBER_FORMAT}\n onChange={(base) => {\n if (Number.isFinite(base)) onScaleChange({ ...scale, base });\n }}\n />\n {unit ? <span className={s.unit}>{unit}</span> : null}\n </span>\n <ToggleBar\n ariaLabel={`${group} rule`}\n size=\"sm\"\n items={RULES}\n value={rule.kind}\n onChange={(kind) => {\n if (kind && kind !== rule.kind) onScaleChange(refitScale(scale, kind, amounts));\n }}\n />\n {param ? (\n <span className={s.stepEdit}>\n {param.key === 'ratio' ? <span className={s.unit}>×</span> : <span className={s.unit}>+</span>}\n <NumberField\n className={s.number}\n aria-label={`${group} ${param.key}`}\n value={param.value}\n hideSteppers\n formatOptions={NUMBER_FORMAT}\n onChange={(value) => {\n if (Number.isFinite(value)) onScaleChange({ ...scale, rule: { kind: param.key, [param.key]: value } as TokenScaleRule });\n }}\n />\n </span>\n ) : null}\n </div>\n );\n}\n\nfunction Scale({\n group,\n tokens,\n generator,\n onChange,\n onScaleChange,\n}: {\n group: string;\n tokens: readonly TokenEntry[];\n generator?: TokenScale;\n onChange: OnChange;\n onScaleChange?: (group: string, scale: TokenScale) => void;\n}) {\n const labels = useMemo(() => stepLabels(tokens.map((t) => t.name)), [tokens]);\n const units = new Set(tokens.map((t) => splitUnit(t.value)?.unit ?? ''));\n const unit = units.size === 1 ? [...units][0] : null;\n const overridden = tokens.filter((t) => t.overridden);\n return (\n <div className={s.scale} role=\"group\" aria-label={group}>\n <span className={s.scaleHead}>\n <span className={s.name}>{group}</span>\n {unit ? <span className={s.unit}>{unit}</span> : null}\n {overridden.length > 0 ? (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n ariaLabel={`Reset ${group}`}\n onClick={() => {\n for (const t of overridden) onChange(t.name, null);\n }}\n >\n Reset\n </Button>\n ) : null}\n </span>\n {generator && onScaleChange ? (\n <ScaleRuleControls\n group={group}\n scale={generator}\n unit={unit ?? ''}\n amounts={generator.tokens.map(\n (name) => splitUnit(tokens.find((t) => t.name === name)?.value ?? '')?.amount ?? generator.base,\n )}\n onScaleChange={(next) => onScaleChange(group, next)}\n />\n ) : null}\n <div className={s.steps}>\n {tokens.map((t, i) => {\n const rule = generator?.rule;\n const at = generator ? generator.tokens.indexOf(t.name) : -1;\n const factor = rule?.kind === 'factors' && at >= 0 ? rule.factors[at] : undefined;\n return (\n <ScaleStep\n key={t.name}\n token={t}\n label={labels[i] ?? t.name}\n showUnit={unit === null}\n factor={factor}\n onChange={onChange}\n onFactorChange={\n generator && onScaleChange && rule?.kind === 'factors'\n ? (next) =>\n onScaleChange(group, {\n ...generator,\n rule: { kind: 'factors', factors: rule.factors.map((f, j) => (j === at ? next : f)) },\n })\n : undefined\n }\n />\n );\n })}\n </div>\n </div>\n );\n}\n\nfunction Section({\n title,\n open,\n onToggle,\n tokens,\n familySize,\n scales,\n onChange,\n onScaleChange,\n}: {\n title: string;\n open: boolean;\n onToggle: () => void;\n tokens: readonly TokenEntry[];\n familySize: number;\n scales?: Readonly<Record<string, TokenScale>>;\n onChange: OnChange;\n onScaleChange?: (group: string, scale: TokenScale) => void;\n}) {\n const items = useMemo(() => itemsOf(tokens, familySize), [tokens, familySize]);\n return (\n <section className={s.section} aria-label={title}>\n <DisclosureRow className={s.sectionHead} open={open} onToggle={onToggle} label={title}>\n <span aria-hidden=\"true\">{title}</span>\n </DisclosureRow>\n {open ? (\n <div className={s.sectionBody}>\n {items.map((item) =>\n item.kind === 'row' ? (\n <TokenRow key={item.token.name} token={item.token} onChange={onChange} />\n ) : item.kind === 'scale' ? (\n <Scale\n key={`scale:${item.group}`}\n group={item.group}\n tokens={item.tokens}\n generator={scales?.[item.group]}\n onChange={onChange}\n onScaleChange={onScaleChange}\n />\n ) : (\n <Family key={`family:${item.group}`} group={item.group} tokens={item.tokens} onChange={onChange} />\n ),\n )}\n </div>\n ) : null}\n </section>\n );\n}\n\n/**\n * Edits a set of design tokens by type: collapsible sections by category, a\n * color group drawn as one row of swatches sized to fit, a group of sizes drawn\n * as one grid of numbers labeled by step — with its base and rule above it when\n * `scales` says how it is generated — and a control for each\n * type — a number with its unit for dimensions and durations, the nine weights\n * for a font weight, a curve beside a `cubic-bezier()`, a swatch beside a color,\n * and text for everything else.\n */\nexport function TokenPanel({\n tokens,\n onChange,\n collapsed,\n onCollapsedChange,\n scales,\n onScaleChange,\n categorize = tokenCategory,\n familySize = 3,\n density = 'normal',\n className,\n}: TokenPanelProps) {\n const [ownCollapsed, setOwnCollapsed] = useState<Partial<Record<TokenCategory, boolean>>>({});\n const closed = collapsed ?? ownCollapsed;\n const byCategory = useMemo(() => {\n const sections = new Map<TokenCategory, TokenEntry[]>();\n for (const token of tokens) {\n const category = categorize(token);\n sections.set(category, [...(sections.get(category) ?? []), token]);\n }\n return sections;\n }, [tokens, categorize]);\n\n return (\n <div className={[s.panel, density === 'tight' && s.tight, className].filter(Boolean).join(' ')}>\n {TOKEN_CATEGORIES.map(({ id, title }) => {\n const inSection = byCategory.get(id);\n if (!inSection) return null;\n const open = !closed[id];\n return (\n <Section\n key={id}\n title={title}\n open={open}\n onToggle={() => {\n if (collapsed === undefined) setOwnCollapsed((prev) => ({ ...prev, [id]: open }));\n onCollapsedChange?.(id, open);\n }}\n tokens={inSection}\n familySize={familySize}\n scales={scales}\n onChange={onChange}\n onScaleChange={onScaleChange}\n />\n );\n })}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,15 +0,0 @@
1
- var e = {
2
- field: "_field_k7w94_5",
3
- label: "_label_k7w94_13",
4
- hint: "_hint_k7w94_20",
5
- error: "_error_k7w94_26",
6
- row: "_row_k7w94_32",
7
- control: "_control_k7w94_46",
8
- fit: "_fit_k7w94_58",
9
- frame: "_frame_k7w94_58",
10
- below: "_below_k7w94_59"
11
- };
12
- //#endregion
13
- export { e as t };
14
-
15
- //# sourceMappingURL=field.module-Bh7BTKMh.js.map