@weasel-js/ui 1.1.0 → 1.3.0-pre.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{ActionBar-BvPEd_GW.js → ActionBar-BTev_ZZl.js} +21 -21
- package/dist/chunks/ActionBar-BTev_ZZl.js.map +1 -0
- package/dist/chunks/{ActionsBar-9aR5N3lU.js → ActionsBar-gTuCtkv8.js} +4 -4
- package/dist/chunks/{ActionsBar-9aR5N3lU.js.map → ActionsBar-gTuCtkv8.js.map} +1 -1
- package/dist/chunks/{Badge-DZQqAV4d.js → Badge-9dPfvYOk.js} +715 -707
- package/dist/chunks/Badge-9dPfvYOk.js.map +1 -0
- package/dist/chunks/{BandEditor-D3i6l299.js → BandEditor-DKbcHG2_.js} +46 -46
- package/dist/chunks/BandEditor-DKbcHG2_.js.map +1 -0
- package/dist/chunks/{Button-9QNIYugK.js → Button-CIu40Epu.js} +28 -28
- package/dist/chunks/Button-CIu40Epu.js.map +1 -0
- package/dist/chunks/{Callout-NSOjza6G.js → Callout-CHs98djt.js} +40 -39
- package/dist/chunks/Callout-CHs98djt.js.map +1 -0
- package/dist/chunks/Checkbox-BLEw-wEb.js +47 -0
- package/dist/chunks/Checkbox-BLEw-wEb.js.map +1 -0
- package/dist/chunks/ColorField-D1t-BdSa.js +60 -0
- package/dist/chunks/ColorField-D1t-BdSa.js.map +1 -0
- package/dist/chunks/{ComboBox-ONQISwtI.js → ComboBox-cvdKolWv.js} +28 -28
- package/dist/chunks/ComboBox-cvdKolWv.js.map +1 -0
- package/dist/chunks/{CurveEditor-SmwwvrsA.js → CurveEditor-BaA-PabB.js} +116 -116
- package/dist/chunks/CurveEditor-BaA-PabB.js.map +1 -0
- package/dist/chunks/DataGrid-D15I779H.js +210 -0
- package/dist/chunks/DataGrid-D15I779H.js.map +1 -0
- package/dist/chunks/Dialog-DC1x46Sq.js +59 -0
- package/dist/chunks/Dialog-DC1x46Sq.js.map +1 -0
- package/dist/chunks/{Field-VQwmrIna.js → Field-BjFl_yng.js} +6 -6
- package/dist/chunks/Field-BjFl_yng.js.map +1 -0
- package/dist/chunks/GradientEditor-BJRD0n1g.js +308 -0
- package/dist/chunks/GradientEditor-BJRD0n1g.js.map +1 -0
- package/dist/chunks/InlineRange-C6UYKxLD.js +24 -0
- package/dist/chunks/InlineRange-C6UYKxLD.js.map +1 -0
- package/dist/chunks/Input-DhWBgpRx.js +55 -0
- package/dist/chunks/Input-DhWBgpRx.js.map +1 -0
- package/dist/chunks/{Keycaps-CFX7UAj8.js → Keycaps-BhCKdyws.js} +15 -15
- package/dist/chunks/Keycaps-BhCKdyws.js.map +1 -0
- package/dist/chunks/NumberField-ZdVa4bpJ.js +59 -0
- package/dist/chunks/NumberField-ZdVa4bpJ.js.map +1 -0
- package/dist/chunks/{OptionsBar-CHvH4n4u.js → OptionsBar-DahCI4Aq.js} +4 -4
- package/dist/chunks/{OptionsBar-CHvH4n4u.js.map → OptionsBar-DahCI4Aq.js.map} +1 -1
- package/dist/chunks/PaintInput-Cggm9OIl.js +205 -0
- package/dist/chunks/PaintInput-Cggm9OIl.js.map +1 -0
- package/dist/chunks/PatternPicker-B63SlDoF.js +93 -0
- package/dist/chunks/PatternPicker-B63SlDoF.js.map +1 -0
- package/dist/chunks/{Plot2D-CEnhzodm.js → Plot2D-sPXTw_-l.js} +27 -27
- package/dist/chunks/{Plot2D-CEnhzodm.js.map → Plot2D-sPXTw_-l.js.map} +1 -1
- package/dist/chunks/{PointPlotter-BUIse9c4.js → PointPlotter-CLd9u5Bq.js} +2 -2
- package/dist/chunks/{PointPlotter-BUIse9c4.js.map → PointPlotter-CLd9u5Bq.js.map} +1 -1
- package/dist/chunks/{Powerline-Dd9Ukhcl.js → Powerline-DhQJYaUg.js} +2 -2
- package/dist/chunks/{Powerline-Dd9Ukhcl.js.map → Powerline-DhQJYaUg.js.map} +1 -1
- package/dist/chunks/Prefs-BSJ0lMmJ.js +300 -0
- package/dist/chunks/Prefs-BSJ0lMmJ.js.map +1 -0
- package/dist/chunks/RadioGroup-vtwjRiUm.js +49 -0
- package/dist/chunks/RadioGroup-vtwjRiUm.js.map +1 -0
- package/dist/chunks/RangeSlider-B-n_7AgJ.js +52 -0
- package/dist/chunks/RangeSlider-B-n_7AgJ.js.map +1 -0
- package/dist/chunks/{ResizeHandle-BOpmtOOj.js → ResizeHandle--ZExnMfa.js} +8 -8
- package/dist/chunks/{ResizeHandle-BOpmtOOj.js.map → ResizeHandle--ZExnMfa.js.map} +1 -1
- package/dist/chunks/{Select-CHsuQCp0.js → Select--BVTi-j0.js} +32 -32
- package/dist/chunks/Select--BVTi-j0.js.map +1 -0
- package/dist/chunks/SelectionPanel-C7kygTFE.js +446 -0
- package/dist/chunks/SelectionPanel-C7kygTFE.js.map +1 -0
- package/dist/chunks/{Sidebar-DP_nyJze.js → Sidebar-CtwBdr4n.js} +1 -1
- package/dist/chunks/{Sidebar-DP_nyJze.js.map → Sidebar-CtwBdr4n.js.map} +1 -1
- package/dist/chunks/SidebarPanel-AeSc7wNR.js +59 -0
- package/dist/chunks/SidebarPanel-AeSc7wNR.js.map +1 -0
- package/dist/chunks/{Slider-tGbqoAZH.js → Slider-cxWR47gi.js} +39 -39
- package/dist/chunks/Slider-cxWR47gi.js.map +1 -0
- package/dist/chunks/{StatusBar-DBO0E0Yd.js → StatusBar-D_wu-SZh.js} +6 -6
- package/dist/chunks/StatusBar-D_wu-SZh.js.map +1 -0
- package/dist/chunks/Switch-CtZwp5zm.js +27 -0
- package/dist/chunks/Switch-CtZwp5zm.js.map +1 -0
- package/dist/chunks/Tabs-lB0IB4TX.js +41 -0
- package/dist/chunks/Tabs-lB0IB4TX.js.map +1 -0
- package/dist/chunks/{Toast-DFw4IrD_.js → Toast-hr9BYsho.js} +31 -31
- package/dist/chunks/{Toast-DFw4IrD_.js.map → Toast-hr9BYsho.js.map} +1 -1
- package/dist/chunks/{ToggleBar-BP1zFcvg.js → ToggleBar-BxgWq-wO.js} +11 -10
- package/dist/chunks/ToggleBar-BxgWq-wO.js.map +1 -0
- package/dist/chunks/{ToolButton-ClPz5L6j.js → ToolButton-CJrPjbsj.js} +16 -16
- package/dist/chunks/ToolButton-CJrPjbsj.js.map +1 -0
- package/dist/chunks/{ToolGroup-BTIwd4U4.js → ToolGroup-D_lDzkvU.js} +1 -1
- package/dist/chunks/{ToolGroup-BTIwd4U4.js.map → ToolGroup-D_lDzkvU.js.map} +1 -1
- package/dist/chunks/{ToolOptionsBar-JvmY7iNe.js → ToolOptionsBar-BA6Sb3U2.js} +4 -4
- package/dist/chunks/ToolOptionsBar-BA6Sb3U2.js.map +1 -0
- package/dist/chunks/{ToolPalette-DAiVhdtV.js → ToolPalette-TMvkaJwr.js} +24 -24
- package/dist/chunks/ToolPalette-TMvkaJwr.js.map +1 -0
- package/dist/chunks/Tooltip-Di7vxbFa.js +40 -0
- package/dist/chunks/Tooltip-Di7vxbFa.js.map +1 -0
- package/dist/chunks/{keyGlyph-BDH5fqkO.js → keyGlyph-DNpLxF8u.js} +1 -1
- package/dist/chunks/{keyGlyph-BDH5fqkO.js.map → keyGlyph-DNpLxF8u.js.map} +1 -1
- package/dist/chunks/segmentedControl.module-D4b7x19O.js +11 -0
- package/dist/chunks/segmentedControl.module-D4b7x19O.js.map +1 -0
- package/dist/chunks/{useRovingTabIndex-DBZtq4IH.js → useRovingTabIndex-CQz8EiKh.js} +1 -1
- package/dist/chunks/{useRovingTabIndex-DBZtq4IH.js.map → useRovingTabIndex-CQz8EiKh.js.map} +1 -1
- package/dist/components/ActionBar/index.js +1 -1
- package/dist/components/ActionsBar/index.js +1 -1
- package/dist/components/Badge/Badge.d.ts.map +1 -1
- package/dist/components/Badge/bases/index.d.ts.map +1 -1
- package/dist/components/Badge/effects/index.d.ts.map +1 -1
- package/dist/components/Badge/index.js +1 -1
- package/dist/components/Badge/shapes/index.d.ts.map +1 -1
- package/dist/components/BandEditor/index.js +1 -1
- package/dist/components/Button/index.js +1 -1
- package/dist/components/Callout/Callout.d.ts.map +1 -1
- package/dist/components/Callout/index.js +1 -1
- package/dist/components/Checkbox/Checkbox.d.ts +1 -1
- package/dist/components/Checkbox/index.js +1 -1
- package/dist/components/ColorField/ColorField.d.ts.map +1 -1
- package/dist/components/ColorField/index.js +1 -1
- package/dist/components/ComboBox/index.js +1 -1
- package/dist/components/CurveEditor/LayeredCurveEditor.d.ts +1 -1
- package/dist/components/CurveEditor/LayeredCurveEditor.d.ts.map +1 -1
- package/dist/components/CurveEditor/createFunctionLayer.d.ts +1 -2
- package/dist/components/CurveEditor/createFunctionLayer.d.ts.map +1 -1
- package/dist/components/CurveEditor/index.js +1 -1
- package/dist/components/DataGrid/index.js +1 -1
- package/dist/components/Dialog/index.js +1 -1
- package/dist/components/Field/index.js +1 -1
- package/dist/components/GradientEditor/SceneGradientHandles.d.ts +41 -0
- package/dist/components/GradientEditor/SceneGradientHandles.d.ts.map +1 -0
- package/dist/components/GradientEditor/index.d.ts +1 -0
- package/dist/components/GradientEditor/index.d.ts.map +1 -1
- package/dist/components/GradientEditor/index.js +2 -2
- package/dist/components/InlineRange/InlineRange.d.ts +17 -0
- package/dist/components/InlineRange/InlineRange.d.ts.map +1 -0
- package/dist/components/InlineRange/index.d.ts +2 -0
- package/dist/components/InlineRange/index.d.ts.map +1 -0
- package/dist/components/InlineRange/index.js +2 -0
- package/dist/components/Input/Input.d.ts +1 -1
- package/dist/components/Input/index.js +1 -1
- package/dist/components/Keycaps/index.js +2 -2
- package/dist/components/NumberField/NumberField.d.ts +1 -1
- package/dist/components/NumberField/index.js +1 -1
- package/dist/components/OptionsBar/index.js +1 -1
- package/dist/components/PaintInput/PaintInput.d.ts +40 -0
- package/dist/components/PaintInput/PaintInput.d.ts.map +1 -0
- package/dist/components/PaintInput/index.d.ts +2 -0
- package/dist/components/PaintInput/index.d.ts.map +1 -0
- package/dist/components/PaintInput/index.js +2 -0
- package/dist/components/PatternPicker/PatternPicker.d.ts +31 -0
- package/dist/components/PatternPicker/PatternPicker.d.ts.map +1 -0
- package/dist/components/PatternPicker/index.d.ts +2 -0
- package/dist/components/PatternPicker/index.d.ts.map +1 -0
- package/dist/components/PatternPicker/index.js +2 -0
- package/dist/components/Plot2D/Plot2D.d.ts.map +1 -1
- package/dist/components/Plot2D/index.js +1 -1
- package/dist/components/PointPlotter/index.js +1 -1
- package/dist/components/Powerline/index.js +2 -2
- package/dist/components/Prefs/PrefsForm.d.ts.map +1 -1
- package/dist/components/Prefs/index.d.ts +1 -1
- package/dist/components/Prefs/index.d.ts.map +1 -1
- package/dist/components/Prefs/index.js +1 -1
- package/dist/components/Prefs/schema.d.ts +4 -93
- package/dist/components/Prefs/schema.d.ts.map +1 -1
- package/dist/components/RadioGroup/index.js +1 -1
- package/dist/components/RangeSlider/index.js +1 -1
- package/dist/components/ResizeHandle/index.js +1 -1
- package/dist/components/Select/index.js +1 -1
- package/dist/components/SelectionPanel/SelectionPanel.d.ts +32 -1
- package/dist/components/SelectionPanel/SelectionPanel.d.ts.map +1 -1
- package/dist/components/SelectionPanel/index.js +1 -1
- package/dist/components/Sidebar/index.js +1 -1
- package/dist/components/SidebarPanel/index.js +1 -1
- package/dist/components/Slider/index.js +1 -1
- package/dist/components/StatusBar/index.js +1 -1
- package/dist/components/Switch/Switch.d.ts +1 -1
- package/dist/components/Switch/index.js +1 -1
- package/dist/components/Tabs/index.js +1 -1
- package/dist/components/Toast/index.js +1 -1
- package/dist/components/ToggleBar/ToggleBar.d.ts +1 -1
- package/dist/components/ToggleBar/ToggleBar.d.ts.map +1 -1
- package/dist/components/ToggleBar/index.js +1 -1
- package/dist/components/ToolButton/index.js +1 -1
- package/dist/components/ToolGroup/index.js +1 -1
- package/dist/components/ToolOptionsBar/index.js +1 -1
- package/dist/components/ToolPalette/index.js +1 -1
- package/dist/components/Tooltip/index.js +1 -1
- package/dist/components/paintValue.d.ts +8 -0
- package/dist/components/paintValue.d.ts.map +1 -0
- package/dist/icons/Icon.d.ts +17 -0
- package/dist/icons/Icon.d.ts.map +1 -0
- package/dist/icons/index.d.ts +50 -0
- package/dist/icons/index.d.ts.map +1 -0
- package/dist/icons/paths.d.ts +71 -0
- package/dist/icons/paths.d.ts.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +184 -48
- package/dist/index.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +4 -3
- package/dist/chunks/ActionBar-BvPEd_GW.js.map +0 -1
- package/dist/chunks/Badge-DZQqAV4d.js.map +0 -1
- package/dist/chunks/BandEditor-D3i6l299.js.map +0 -1
- package/dist/chunks/Button-9QNIYugK.js.map +0 -1
- package/dist/chunks/Callout-NSOjza6G.js.map +0 -1
- package/dist/chunks/Checkbox-KQ5L8APP.js +0 -47
- package/dist/chunks/Checkbox-KQ5L8APP.js.map +0 -1
- package/dist/chunks/ColorField-ykpiD3XU.js +0 -60
- package/dist/chunks/ColorField-ykpiD3XU.js.map +0 -1
- package/dist/chunks/ComboBox-ONQISwtI.js.map +0 -1
- package/dist/chunks/CurveEditor-SmwwvrsA.js.map +0 -1
- package/dist/chunks/DataGrid-DZQ4Chbd.js +0 -210
- package/dist/chunks/DataGrid-DZQ4Chbd.js.map +0 -1
- package/dist/chunks/Dialog-CqwdJv_t.js +0 -59
- package/dist/chunks/Dialog-CqwdJv_t.js.map +0 -1
- package/dist/chunks/Field-VQwmrIna.js.map +0 -1
- package/dist/chunks/GradientEditor-Bg6SX64V.js +0 -280
- package/dist/chunks/GradientEditor-Bg6SX64V.js.map +0 -1
- package/dist/chunks/Input-DpkwYtUY.js +0 -55
- package/dist/chunks/Input-DpkwYtUY.js.map +0 -1
- package/dist/chunks/Keycaps-CFX7UAj8.js.map +0 -1
- package/dist/chunks/NumberField-BbzI0GtU.js +0 -59
- package/dist/chunks/NumberField-BbzI0GtU.js.map +0 -1
- package/dist/chunks/Prefs-BMR9wMiZ.js +0 -238
- package/dist/chunks/Prefs-BMR9wMiZ.js.map +0 -1
- package/dist/chunks/RadioGroup-CIpHr3g6.js +0 -49
- package/dist/chunks/RadioGroup-CIpHr3g6.js.map +0 -1
- package/dist/chunks/RangeSlider-CaDogGTj.js +0 -52
- package/dist/chunks/RangeSlider-CaDogGTj.js.map +0 -1
- package/dist/chunks/Select-CHsuQCp0.js.map +0 -1
- package/dist/chunks/SelectionPanel-D_wk_8ZN.js +0 -303
- package/dist/chunks/SelectionPanel-D_wk_8ZN.js.map +0 -1
- package/dist/chunks/SidebarPanel-BVTQ1ZyM.js +0 -59
- package/dist/chunks/SidebarPanel-BVTQ1ZyM.js.map +0 -1
- package/dist/chunks/Slider-tGbqoAZH.js.map +0 -1
- package/dist/chunks/StatusBar-DBO0E0Yd.js.map +0 -1
- package/dist/chunks/Switch-JyVSSAzC.js +0 -27
- package/dist/chunks/Switch-JyVSSAzC.js.map +0 -1
- package/dist/chunks/Tabs-Df0sDomx.js +0 -41
- package/dist/chunks/Tabs-Df0sDomx.js.map +0 -1
- package/dist/chunks/ToggleBar-BP1zFcvg.js.map +0 -1
- package/dist/chunks/ToolButton-ClPz5L6j.js.map +0 -1
- package/dist/chunks/ToolOptionsBar-JvmY7iNe.js.map +0 -1
- package/dist/chunks/ToolPalette-DAiVhdtV.js.map +0 -1
- package/dist/chunks/Tooltip-DXJbdR5B.js +0 -40
- package/dist/chunks/Tooltip-DXJbdR5B.js.map +0 -1
- package/dist/chunks/segmentedControl.module-DCyuDbj1.js +0 -11
- package/dist/chunks/segmentedControl.module-DCyuDbj1.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Plot2D-CEnhzodm.js","names":[],"sources":["../../src/dlog.ts","../../src/components/Plot2D/geometry.ts","../../src/components/Plot2D/Plot2D.module.css","../../src/components/Plot2D/Plot2D.tsx"],"sourcesContent":["/**\n * Namespaced, opt-in console tracing. Off by default; production builds\n * may strip the calls entirely depending on the bundler.\n *\n * Enable in the browser:\n *\n * localStorage.setItem('weasel.debug', '*') // all namespaces\n * localStorage.setItem('weasel.debug', 'curve-editor') // just curve editor\n * localStorage.setItem('weasel.debug', 'curve-editor,layer-stack')\n *\n * Each component picks a stable namespace and calls `dlog(namespace, ...)`.\n * Useful for tracing pointer/event flows that are hard to capture\n * post-hoc (drag handlers, gesture sequences, selection mutations).\n *\n * The flag is read once and cached for the page session — reload to\n * pick up changes. Cheap hot-path checks (one nullish-coalesce + Set\n * lookup) so leaving calls in place is fine.\n *\n * Mirrors `src/debug/flag.ts` in the kit. Two impls live here because\n * weasel-ui doesn't depend on the kit, but both read the same\n * localStorage keys so behavior is uniform. See the kit copy for the\n * canonical list of kit-level namespaces.\n *\n * Known weasel-ui namespaces:\n * - plot2d — Plot2D mount / unmount\n * - curve-editor — CurveEditor pointer flow\n */\n\nconst KEYS = ['weasel.debug', 'weaseldraw.debug'] as const;\n\ninterface Cached {\n all: boolean;\n set: Set<string>;\n}\n\nlet cached: Cached | null = null;\n\nfunction read(): Cached {\n const empty: Cached = { all: false, set: new Set() };\n if (typeof localStorage === 'undefined') return empty;\n try {\n for (const k of KEYS) {\n const v = localStorage.getItem(k);\n if (v == null || v === '') continue;\n if (v === '1' || v === 'true' || v === '*') return { all: true, set: new Set() };\n return { all: false, set: new Set(v.split(/[\\s,]+/).filter(Boolean)) };\n }\n return empty;\n } catch {\n return empty;\n }\n}\n\nfunction get(): Cached {\n if (cached === null) cached = read();\n return cached;\n}\n\n/** True when `namespace` (or `*`) is enabled. */\nexport function isDebugEnabled(namespace: string): boolean {\n const c = get();\n return c.all || c.set.has(namespace);\n}\n\n/** Conditional `console.debug` — no-op unless `namespace` (or `*`) is enabled.\n * Each call is prefixed with `[namespace]` so the source is searchable. */\nexport function dlog(namespace: string, ...args: unknown[]): void {\n if (!isDebugEnabled(namespace)) return;\n // eslint-disable-next-line no-console\n console.debug(`[${namespace}]`, ...args);\n}\n","/**\n * Coordinate transforms (model ↔ plot) for Plot2D. Pure / deterministic —\n * no DOM, no React, no state.\n *\n * Model space: caller-defined xRange × yRange. Y axis goes UP.\n * Plot space: 0..width × 0..height in CSS pixels. Y axis goes DOWN (SVG).\n */\n\nexport interface Point {\n x: number;\n y: number;\n}\n\n/**\n * The caller-defined extent of model space, the coordinate system a plot's\n * data lives in.\n */\nexport interface ModelRange {\n xMin: number;\n xMax: number;\n yMin: number;\n yMax: number;\n}\n\n/** The plot's drawing area in CSS pixels. */\nexport interface PlotSize {\n width: number;\n height: number;\n}\n\n/**\n * Maps a model-space point into plot space, flipping the y axis so model y\n * grows upward while SVG y grows downward.\n */\nexport function modelToPlot(p: Point, m: ModelRange, plot: PlotSize): Point {\n const xFrac = (p.x - m.xMin) / (m.xMax - m.xMin);\n const yFrac = (p.y - m.yMin) / (m.yMax - m.yMin);\n return {\n x: xFrac * plot.width,\n // Invert y so model y=0 sits at plot bottom (SVG y grows downward).\n y: (1 - yFrac) * plot.height,\n };\n}\n\n/** The inverse of {@link modelToPlot}. */\nexport function plotToModel(p: Point, m: ModelRange, plot: PlotSize): Point {\n const xFrac = p.x / plot.width;\n const yFrac = 1 - p.y / plot.height;\n return {\n x: m.xMin + xFrac * (m.xMax - m.xMin),\n y: m.yMin + yFrac * (m.yMax - m.yMin),\n };\n}\n",".root {\n /* Component-local vars layered over theme tokens. Consumers override\n * either the plot-* var (component scope) or the wzl-* token (app\n * scope). */\n --plot-bg: var(--wzl-surface-sunken);\n --plot-grid: var(--wzl-line);\n /* Axes get full foreground contrast — they're the plot frame, the\n * most prominent structural element after caller-rendered content. */\n --plot-axis: var(--wzl-fg);\n\n display: block;\n /* Caller-rendered marks at the very edge (e.g. CurveEditor anchors)\n * extend past the SVG box by their radius — let them paint over the\n * host page instead of being half-clipped. */\n overflow: visible;\n background: var(--plot-bg);\n user-select: none;\n touch-action: none;\n outline: none;\n}\n\n/* Keyboard-only focus ring. Caller opts in via `tabIndex`. */\n.root:focus-visible {\n outline: 2px solid var(--wzl-focus-ring);\n outline-offset: 2px;\n}\n\n.grid {\n stroke: var(--plot-grid);\n stroke-width: 1;\n fill: none;\n}\n\n.axis {\n stroke: var(--plot-axis);\n stroke-width: var(--wzl-line-width);\n fill: none;\n}\n","import {\n forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef,\n type CSSProperties,\n type KeyboardEvent as ReactKeyboardEvent,\n type PointerEvent as ReactPointerEvent,\n type ReactNode,\n} from 'react';\nimport {\n modelToPlot, plotToModel,\n type ModelRange, type Point,\n} from './geometry';\nimport s from './Plot2D.module.css';\nimport { dlog } from '../../dlog';\n\n/** Background grid configuration for a {@link Plot2D}. */\nexport interface GridSettings {\n /** Number of evenly-spaced internal grid lines per axis (excluding\n * the edges). Applied to both x and y. Default 3. */\n divisions?: number;\n /** Stroke color override. When omitted, uses `var(--plot-grid)`. */\n color?: string;\n}\n\n/** Axis-line configuration for a {@link Plot2D}. */\nexport interface AxesSettings {\n /** Stroke color override. When omitted, uses `var(--plot-axis)`. */\n color?: string;\n}\n\n/** A pointer position given in both of the plot's coordinate systems. */\nexport interface Plot2DCoords {\n plot: Point;\n model: Point;\n}\n\n/** Props for {@link Plot2D}. */\nexport interface Plot2DProps {\n width: number;\n height: number;\n /** Model-space x range. Default [0, 1]. */\n xRange?: readonly [number, number];\n /** Model-space y range. Default [0, 1]. */\n yRange?: readonly [number, number];\n /** Background grid. `false` / `null` / omitted = no grid. Pass `{}`\n * for default (3 divisions per axis) or populated GridSettings. */\n grid?: GridSettings | false | null;\n /** Axis lines. `false` / `null` = no axes. Omitted = default-styled\n * axes (on). Pass AxesSettings to customize. */\n axes?: AxesSettings | false | null;\n /** Forwarded to the underlying svg. Consumer opts into focus this way. */\n tabIndex?: number;\n className?: string;\n style?: CSSProperties;\n /** Pointer down on the SVG. Receives both plot- and model-space coords\n * pre-computed so consumers don't repeat the rect/transform dance. */\n onPointerDown?: (e: ReactPointerEvent<SVGSVGElement>, coords: Plot2DCoords) => void;\n onKeyDown?: (e: ReactKeyboardEvent<SVGSVGElement>) => void;\n children?: ReactNode;\n}\n\n/**\n * Imperative handle on a {@link Plot2D}: the SVG element, its size, and the\n * coordinate conversions, including ones that start from a raw DOM event so a\n * drag tracked on `window` can still map back into the plot.\n */\nexport interface Plot2DHandle {\n readonly svg: SVGSVGElement | null;\n plotToModel(pt: Point): Point;\n modelToPlot(pt: Point): Point;\n /** Convert a DOM event's clientX/clientY (e.g. from a window-attached\n * pointermove during drag) to plot-space. */\n clientToPlot(e: { clientX: number; clientY: number }): Point;\n /** Convenience: clientToPlot → plotToModel. */\n clientToModel(e: { clientX: number; clientY: number }): Point;\n readonly width: number;\n readonly height: number;\n}\n\n/**\n * An SVG plotting surface with an optional grid and axes. It draws the frame\n * and owns the model-space to plot-space mapping; the plotted content is\n * whatever children are passed, positioned in plot space.\n */\nexport const Plot2D = forwardRef<Plot2DHandle, Plot2DProps>(function Plot2D(props, ref) {\n const {\n width, height,\n xRange = [0, 1],\n yRange = [0, 1],\n grid,\n axes,\n tabIndex,\n className,\n style,\n onPointerDown,\n onKeyDown,\n children,\n } = props;\n\n const svgRef = useRef<SVGSVGElement | null>(null);\n\n const modelRange: ModelRange = useMemo(\n () => ({ xMin: xRange[0], xMax: xRange[1], yMin: yRange[0], yMax: yRange[1] }),\n [xRange, yRange],\n );\n\n const plotSize = useMemo(() => ({ width, height }), [width, height]);\n\n const clientToPlot = useCallback((e: { clientX: number; clientY: number }): Point => {\n const rect = svgRef.current?.getBoundingClientRect();\n const left = rect?.left ?? 0;\n const top = rect?.top ?? 0;\n return { x: e.clientX - left, y: e.clientY - top };\n }, []);\n\n useEffect(() => {\n dlog('plot2d', 'mount', { width, height });\n return () => dlog('plot2d', 'unmount');\n }, []);\n useImperativeHandle(ref, () => ({\n get svg() { return svgRef.current; },\n plotToModel: (pt: Point) => plotToModel(pt, modelRange, plotSize),\n modelToPlot: (pt: Point) => modelToPlot(pt, modelRange, plotSize),\n clientToPlot,\n clientToModel: (e) => plotToModel(clientToPlot(e), modelRange, plotSize),\n get width() { return plotSize.width; },\n get height() { return plotSize.height; },\n }), [modelRange, plotSize, clientToPlot]);\n\n const handlePointerDown = useCallback((e: ReactPointerEvent<SVGSVGElement>) => {\n if (!onPointerDown) return;\n const plot = clientToPlot(e);\n const model = plotToModel(plot, modelRange, plotSize);\n onPointerDown(e, { plot, model });\n }, [onPointerDown, clientToPlot, modelRange, plotSize]);\n\n const cls = [s.root, className].filter(Boolean).join(' ');\n\n return (\n <svg\n ref={svgRef}\n className={cls}\n style={style}\n width={width}\n height={height}\n viewBox={`0 0 ${width} ${height}`}\n role=\"img\"\n tabIndex={tabIndex}\n onPointerDown={onPointerDown ? handlePointerDown : undefined}\n onKeyDown={onKeyDown}\n >\n {grid && (() => {\n const divisions = grid.divisions ?? 3;\n const stroke = grid.color;\n // Evenly-spaced internal divisions, excluding the edges (which\n // belong to the axes). For divisions=3, fractions are 1/4, 2/4, 3/4.\n const fractions: number[] = [];\n for (let i = 1; i <= divisions; i++) fractions.push(i / (divisions + 1));\n return (\n <g>\n {fractions.map((f) => (\n <line\n key={`gx-${f}`}\n data-plot-element=\"grid\"\n className={s.grid}\n stroke={stroke}\n x1={f * width} x2={f * width}\n y1={0} y2={height}\n />\n ))}\n {fractions.map((f) => (\n <line\n key={`gy-${f}`}\n data-plot-element=\"grid\"\n className={s.grid}\n stroke={stroke}\n x1={0} x2={width}\n y1={f * height} y2={f * height}\n />\n ))}\n </g>\n );\n })()}\n {axes !== false && axes !== null && (() => {\n const stroke = axes?.color;\n return (\n <g>\n <line\n data-plot-element=\"axis\"\n className={s.axis}\n stroke={stroke}\n x1={0} x2={width}\n y1={height} y2={height}\n />\n <line\n data-plot-element=\"axis\"\n className={s.axis}\n stroke={stroke}\n x1={0} x2={0}\n y1={0} y2={height}\n />\n </g>\n );\n })()}\n {children}\n </svg>\n );\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Plot2D-sPXTw_-l.js","names":[],"sources":["../../src/dlog.ts","../../src/components/Plot2D/geometry.ts","../../src/components/Plot2D/Plot2D.module.css","../../src/components/Plot2D/Plot2D.tsx"],"sourcesContent":["/**\n * Namespaced, opt-in console tracing. Off by default; production builds\n * may strip the calls entirely depending on the bundler.\n *\n * Enable in the browser:\n *\n * localStorage.setItem('weasel.debug', '*') // all namespaces\n * localStorage.setItem('weasel.debug', 'curve-editor') // just curve editor\n * localStorage.setItem('weasel.debug', 'curve-editor,layer-stack')\n *\n * Each component picks a stable namespace and calls `dlog(namespace, ...)`.\n * Useful for tracing pointer/event flows that are hard to capture\n * post-hoc (drag handlers, gesture sequences, selection mutations).\n *\n * The flag is read once and cached for the page session — reload to\n * pick up changes. Cheap hot-path checks (one nullish-coalesce + Set\n * lookup) so leaving calls in place is fine.\n *\n * Mirrors `src/debug/flag.ts` in the kit. Two impls live here because\n * weasel-ui doesn't depend on the kit, but both read the same\n * localStorage keys so behavior is uniform. See the kit copy for the\n * canonical list of kit-level namespaces.\n *\n * Known weasel-ui namespaces:\n * - plot2d — Plot2D mount / unmount\n * - curve-editor — CurveEditor pointer flow\n */\n\nconst KEYS = ['weasel.debug', 'weaseldraw.debug'] as const;\n\ninterface Cached {\n all: boolean;\n set: Set<string>;\n}\n\nlet cached: Cached | null = null;\n\nfunction read(): Cached {\n const empty: Cached = { all: false, set: new Set() };\n if (typeof localStorage === 'undefined') return empty;\n try {\n for (const k of KEYS) {\n const v = localStorage.getItem(k);\n if (v == null || v === '') continue;\n if (v === '1' || v === 'true' || v === '*') return { all: true, set: new Set() };\n return { all: false, set: new Set(v.split(/[\\s,]+/).filter(Boolean)) };\n }\n return empty;\n } catch {\n return empty;\n }\n}\n\nfunction get(): Cached {\n if (cached === null) cached = read();\n return cached;\n}\n\n/** True when `namespace` (or `*`) is enabled. */\nexport function isDebugEnabled(namespace: string): boolean {\n const c = get();\n return c.all || c.set.has(namespace);\n}\n\n/** Conditional `console.debug` — no-op unless `namespace` (or `*`) is enabled.\n * Each call is prefixed with `[namespace]` so the source is searchable. */\nexport function dlog(namespace: string, ...args: unknown[]): void {\n if (!isDebugEnabled(namespace)) return;\n // eslint-disable-next-line no-console\n console.debug(`[${namespace}]`, ...args);\n}\n","/**\n * Coordinate transforms (model ↔ plot) for Plot2D. Pure / deterministic —\n * no DOM, no React, no state.\n *\n * Model space: caller-defined xRange × yRange. Y axis goes UP.\n * Plot space: 0..width × 0..height in CSS pixels. Y axis goes DOWN (SVG).\n */\n\nexport interface Point {\n x: number;\n y: number;\n}\n\n/**\n * The caller-defined extent of model space, the coordinate system a plot's\n * data lives in.\n */\nexport interface ModelRange {\n xMin: number;\n xMax: number;\n yMin: number;\n yMax: number;\n}\n\n/** The plot's drawing area in CSS pixels. */\nexport interface PlotSize {\n width: number;\n height: number;\n}\n\n/**\n * Maps a model-space point into plot space, flipping the y axis so model y\n * grows upward while SVG y grows downward.\n */\nexport function modelToPlot(p: Point, m: ModelRange, plot: PlotSize): Point {\n const xFrac = (p.x - m.xMin) / (m.xMax - m.xMin);\n const yFrac = (p.y - m.yMin) / (m.yMax - m.yMin);\n return {\n x: xFrac * plot.width,\n // Invert y so model y=0 sits at plot bottom (SVG y grows downward).\n y: (1 - yFrac) * plot.height,\n };\n}\n\n/** The inverse of {@link modelToPlot}. */\nexport function plotToModel(p: Point, m: ModelRange, plot: PlotSize): Point {\n const xFrac = p.x / plot.width;\n const yFrac = 1 - p.y / plot.height;\n return {\n x: m.xMin + xFrac * (m.xMax - m.xMin),\n y: m.yMin + yFrac * (m.yMax - m.yMin),\n };\n}\n",".root {\n /* Component-local vars layered over theme tokens. Consumers override\n * either the plot-* var (component scope) or the wzl-* token (app\n * scope). */\n --plot-bg: var(--wzl-surface-sunken);\n --plot-grid: var(--wzl-line);\n /* Axes get full foreground contrast — they're the plot frame, the\n * most prominent structural element after caller-rendered content. */\n --plot-axis: var(--wzl-fg);\n\n display: block;\n /* Caller-rendered marks at the very edge (e.g. CurveEditor anchors)\n * extend past the SVG box by their radius — let them paint over the\n * host page instead of being half-clipped. */\n overflow: visible;\n background: var(--plot-bg);\n user-select: none;\n touch-action: none;\n outline: none;\n}\n\n/* Keyboard-only focus ring. Caller opts in via `tabIndex`. */\n.root:focus-visible {\n outline: 2px solid var(--wzl-focus-ring);\n outline-offset: 2px;\n}\n\n.grid {\n stroke: var(--plot-grid);\n stroke-width: 1;\n fill: none;\n}\n\n.axis {\n stroke: var(--plot-axis);\n stroke-width: var(--wzl-line-width);\n fill: none;\n}\n","import {\n forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef,\n type CSSProperties,\n type KeyboardEvent as ReactKeyboardEvent,\n type PointerEvent as ReactPointerEvent,\n type ReactNode,\n} from 'react';\nimport {\n modelToPlot, plotToModel,\n type ModelRange, type Point,\n} from './geometry';\nimport s from './Plot2D.module.css';\nimport { dlog } from '../../dlog';\n\n/** Background grid configuration for a {@link Plot2D}. */\nexport interface GridSettings {\n /** Number of evenly-spaced internal grid lines per axis (excluding\n * the edges). Applied to both x and y. Default 3. */\n divisions?: number;\n /** Stroke color override. When omitted, uses `var(--plot-grid)`. */\n color?: string;\n}\n\n/** Axis-line configuration for a {@link Plot2D}. */\nexport interface AxesSettings {\n /** Stroke color override. When omitted, uses `var(--plot-axis)`. */\n color?: string;\n}\n\n/** A pointer position given in both of the plot's coordinate systems. */\nexport interface Plot2DCoords {\n plot: Point;\n model: Point;\n}\n\n/** Props for {@link Plot2D}. */\nexport interface Plot2DProps {\n width: number;\n height: number;\n /** Model-space x range. Default [0, 1]. */\n xRange?: readonly [number, number];\n /** Model-space y range. Default [0, 1]. */\n yRange?: readonly [number, number];\n /** Background grid. `false` / `null` / omitted = no grid. Pass `{}`\n * for default (3 divisions per axis) or populated GridSettings. */\n grid?: GridSettings | false | null;\n /** Axis lines. `false` / `null` = no axes. Omitted = default-styled\n * axes (on). Pass AxesSettings to customize. */\n axes?: AxesSettings | false | null;\n /** Forwarded to the underlying svg. Consumer opts into focus this way. */\n tabIndex?: number;\n className?: string;\n style?: CSSProperties;\n /** Pointer down on the SVG. Receives both plot- and model-space coords\n * pre-computed so consumers don't repeat the rect/transform dance. */\n onPointerDown?: (e: ReactPointerEvent<SVGSVGElement>, coords: Plot2DCoords) => void;\n onKeyDown?: (e: ReactKeyboardEvent<SVGSVGElement>) => void;\n children?: ReactNode;\n}\n\n/**\n * Imperative handle on a {@link Plot2D}: the SVG element, its size, and the\n * coordinate conversions, including ones that start from a raw DOM event so a\n * drag tracked on `window` can still map back into the plot.\n */\nexport interface Plot2DHandle {\n readonly svg: SVGSVGElement | null;\n plotToModel(pt: Point): Point;\n modelToPlot(pt: Point): Point;\n /** Convert a DOM event's clientX/clientY (e.g. from a window-attached\n * pointermove during drag) to plot-space. */\n clientToPlot(e: { clientX: number; clientY: number }): Point;\n /** Convenience: clientToPlot → plotToModel. */\n clientToModel(e: { clientX: number; clientY: number }): Point;\n readonly width: number;\n readonly height: number;\n}\n\n/**\n * An SVG plotting surface with an optional grid and axes. It draws the frame\n * and owns the model-space to plot-space mapping; the plotted content is\n * whatever children are passed, positioned in plot space.\n */\nexport const Plot2D = forwardRef<Plot2DHandle, Plot2DProps>(function Plot2D(props, ref) {\n const {\n width, height,\n xRange = [0, 1],\n yRange = [0, 1],\n grid,\n axes,\n tabIndex,\n className,\n style,\n onPointerDown,\n onKeyDown,\n children,\n } = props;\n\n const svgRef = useRef<SVGSVGElement | null>(null);\n\n const modelRange: ModelRange = useMemo(\n () => ({ xMin: xRange[0], xMax: xRange[1], yMin: yRange[0], yMax: yRange[1] }),\n [xRange, yRange],\n );\n\n const plotSize = useMemo(() => ({ width, height }), [width, height]);\n\n const clientToPlot = useCallback((e: { clientX: number; clientY: number }): Point => {\n const rect = svgRef.current?.getBoundingClientRect();\n const left = rect?.left ?? 0;\n const top = rect?.top ?? 0;\n return { x: e.clientX - left, y: e.clientY - top };\n }, []);\n\n useEffect(() => {\n dlog('plot2d', 'mount', { width, height });\n return () => dlog('plot2d', 'unmount');\n // Mount/unmount trace: re-running on a resize would log a spurious mount.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n useImperativeHandle(ref, () => ({\n get svg() { return svgRef.current; },\n plotToModel: (pt: Point) => plotToModel(pt, modelRange, plotSize),\n modelToPlot: (pt: Point) => modelToPlot(pt, modelRange, plotSize),\n clientToPlot,\n clientToModel: (e) => plotToModel(clientToPlot(e), modelRange, plotSize),\n get width() { return plotSize.width; },\n get height() { return plotSize.height; },\n }), [modelRange, plotSize, clientToPlot]);\n\n const handlePointerDown = useCallback((e: ReactPointerEvent<SVGSVGElement>) => {\n if (!onPointerDown) return;\n const plot = clientToPlot(e);\n const model = plotToModel(plot, modelRange, plotSize);\n onPointerDown(e, { plot, model });\n }, [onPointerDown, clientToPlot, modelRange, plotSize]);\n\n const cls = [s.root, className].filter(Boolean).join(' ');\n\n return (\n <svg\n ref={svgRef}\n className={cls}\n style={style}\n width={width}\n height={height}\n viewBox={`0 0 ${width} ${height}`}\n role=\"img\"\n tabIndex={tabIndex}\n onPointerDown={onPointerDown ? handlePointerDown : undefined}\n onKeyDown={onKeyDown}\n >\n {grid && (() => {\n const divisions = grid.divisions ?? 3;\n const stroke = grid.color;\n // Evenly-spaced internal divisions, excluding the edges (which\n // belong to the axes). For divisions=3, fractions are 1/4, 2/4, 3/4.\n const fractions: number[] = [];\n for (let i = 1; i <= divisions; i++) fractions.push(i / (divisions + 1));\n return (\n <g>\n {fractions.map((f) => (\n <line\n key={`gx-${f}`}\n data-plot-element=\"grid\"\n className={s.grid}\n stroke={stroke}\n x1={f * width} x2={f * width}\n y1={0} y2={height}\n />\n ))}\n {fractions.map((f) => (\n <line\n key={`gy-${f}`}\n data-plot-element=\"grid\"\n className={s.grid}\n stroke={stroke}\n x1={0} x2={width}\n y1={f * height} y2={f * height}\n />\n ))}\n </g>\n );\n })()}\n {axes !== false && axes !== null && (() => {\n const stroke = axes?.color;\n return (\n <g>\n <line\n data-plot-element=\"axis\"\n className={s.axis}\n stroke={stroke}\n x1={0} x2={width}\n y1={height} y2={height}\n />\n <line\n data-plot-element=\"axis\"\n className={s.axis}\n stroke={stroke}\n x1={0} x2={0}\n y1={0} y2={height}\n />\n </g>\n );\n })()}\n {children}\n </svg>\n );\n});\n"],"mappings":";;;AA4BA,IAAM,IAAO,CAAC,gBAAgB,mBAAmB,EAO7C,IAAwB;AAE5B,SAAS,IAAe;CACtB,IAAM,IAAgB;EAAE,KAAK;EAAO,qBAAK,IAAI,KAAK;EAAE;AACpD,KAAI,OAAO,eAAiB,IAAa,QAAO;AAChD,KAAI;AACF,OAAK,IAAM,KAAK,GAAM;GACpB,IAAM,IAAI,aAAa,QAAQ,EAAE;AAC7B,cAAK,QAAQ,MAAM,IAEvB,QADI,MAAM,OAAO,MAAM,UAAU,MAAM,MAAY;IAAE,KAAK;IAAM,qBAAK,IAAI,KAAK;IAAE,GACzE;IAAE,KAAK;IAAO,KAAK,IAAI,IAAI,EAAE,MAAM,SAAS,CAAC,OAAO,QAAQ,CAAC;IAAE;;AAExE,SAAO;SACD;AACN,SAAO;;;AAIX,SAAS,IAAc;AAErB,QADI,MAAW,SAAM,IAAS,GAAM,GAC7B;;AAIT,SAAgB,EAAe,GAA4B;CACzD,IAAM,IAAI,GAAK;AACf,QAAO,EAAE,OAAO,EAAE,IAAI,IAAI,EAAU;;AAKtC,SAAgB,EAAK,GAAmB,GAAG,GAAuB;AAC3D,GAAe,EAAU,IAE9B,QAAQ,MAAM,IAAI,EAAU,IAAI,GAAG,EAAK;;;;ACnC1C,SAAgB,EAAY,GAAU,GAAe,GAAuB;CAC1E,IAAM,KAAS,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,OACrC,KAAS,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE;AAC3C,QAAO;EACL,GAAG,IAAQ,EAAK;EAEhB,IAAI,IAAI,KAAS,EAAK;EACvB;;AAIH,SAAgB,EAAY,GAAU,GAAe,GAAuB;CAC1E,IAAM,IAAQ,EAAE,IAAI,EAAK,OACnB,IAAQ,IAAI,EAAE,IAAI,EAAK;AAC7B,QAAO;EACL,GAAG,EAAE,OAAO,KAAS,EAAE,OAAO,EAAE;EAChC,GAAG,EAAE,OAAO,KAAS,EAAE,OAAO,EAAE;EACjC;;;;;;GEgCU,IAAS,EAAsC,SAAgB,GAAO,GAAK;CACtF,IAAM,EACJ,UAAO,WACP,YAAS,CAAC,GAAG,EAAE,EACf,YAAS,CAAC,GAAG,EAAE,EACf,SACA,SACA,aACA,cACA,UACA,kBACA,cACA,gBACE,GAEE,IAAS,EAA6B,KAAK,EAE3C,IAAyB,SACtB;EAAE,MAAM,EAAO;EAAI,MAAM,EAAO;EAAI,MAAM,EAAO;EAAI,MAAM,EAAO;EAAI,GAC7E,CAAC,GAAQ,EAAO,CACjB,EAEK,IAAW,SAAe;EAAE;EAAO;EAAQ,GAAG,CAAC,GAAO,EAAO,CAAC,EAE9D,IAAe,GAAa,MAAmD;EACnF,IAAM,IAAO,EAAO,SAAS,uBAAuB,EAC9C,IAAO,GAAM,QAAQ,GACrB,IAAM,GAAM,OAAO;AACzB,SAAO;GAAE,GAAG,EAAE,UAAU;GAAM,GAAG,EAAE,UAAU;GAAK;IACjD,EAAE,CAAC;AAQN,CANA,SACE,EAAK,UAAU,SAAS;EAAE;EAAO;EAAQ,CAAC,QAC7B,EAAK,UAAU,UAAU,GAGrC,EAAE,CAAC,EACN,EAAoB,UAAY;EAC9B,IAAI,MAAM;AAAE,UAAO,EAAO;;EAC1B,cAAc,MAAc,EAAY,GAAI,GAAY,EAAS;EACjE,cAAc,MAAc,EAAY,GAAI,GAAY,EAAS;EACjE;EACA,gBAAgB,MAAM,EAAY,EAAa,EAAE,EAAE,GAAY,EAAS;EACxE,IAAI,QAAQ;AAAE,UAAO,EAAS;;EAC9B,IAAI,SAAS;AAAE,UAAO,EAAS;;EAChC,GAAG;EAAC;EAAY;EAAU;EAAa,CAAC;CAEzC,IAAM,IAAoB,GAAa,MAAwC;AAC7E,MAAI,CAAC,EAAe;EACpB,IAAM,IAAO,EAAa,EAAE;AAE5B,IAAc,GAAG;GAAE;GAAM,OADX,EAAY,GAAM,GAAY,EACnB;GAAO,CAAC;IAChC;EAAC;EAAe;EAAc;EAAY;EAAS,CAAC;AAIvD,QACE,kBAAC,OAAD;EACE,KAAK;EACL,WALQ,CAAC,EAAE,MAAM,EAAU,CAAC,OAAO,QAAQ,CAAC,KAAK,IAKtC;EACJ;EACA;EACC;EACR,SAAS,OAAO,EAAM,GAAG;EACzB,MAAK;EACK;EACV,eAAe,IAAgB,IAAoB,KAAA;EACxC;YAVb;GAYG,YAAe;IACd,IAAM,IAAY,EAAK,aAAa,GAC9B,IAAS,EAAK,OAGd,IAAsB,EAAE;AAC9B,SAAK,IAAI,IAAI,GAAG,KAAK,GAAW,IAAK,GAAU,KAAK,KAAK,IAAY,GAAG;AACxE,WACE,kBAAC,KAAD,EAAA,UAAA,CACG,EAAU,KAAK,MACd,kBAAC,QAAD;KAEE,qBAAkB;KAClB,WAAW,EAAE;KACL;KACR,IAAI,IAAI;KAAO,IAAI,IAAI;KACvB,IAAI;KAAG,IAAI;KACX,EANK,MAAM,IAMX,CACF,EACD,EAAU,KAAK,MACd,kBAAC,QAAD;KAEE,qBAAkB;KAClB,WAAW,EAAE;KACL;KACR,IAAI;KAAG,IAAI;KACX,IAAI,IAAI;KAAQ,IAAI,IAAI;KACxB,EANK,MAAM,IAMX,CACF,CACA,EAAA,CAAA;OAEJ;GACH,MAAS,MAAS,MAAS,eAAe;IACzC,IAAM,IAAS,GAAM;AACrB,WACE,kBAAC,KAAD,EAAA,UAAA,CACE,kBAAC,QAAD;KACE,qBAAkB;KAClB,WAAW,EAAE;KACL;KACR,IAAI;KAAG,IAAI;KACX,IAAI;KAAQ,IAAI;KAChB,CAAA,EACF,kBAAC,QAAD;KACE,qBAAkB;KAClB,WAAW,EAAE;KACL;KACR,IAAI;KAAG,IAAI;KACX,IAAI;KAAG,IAAI;KACX,CAAA,CACA,EAAA,CAAA;OAEJ;GACH;GACG;;EAER"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as e } from "./CurveEditor-
|
|
1
|
+
import { n as e } from "./CurveEditor-BaA-PabB.js";
|
|
2
2
|
import { jsx as t } from "react/jsx-runtime";
|
|
3
3
|
//#region src/components/PointPlotter/PointPlotter.tsx
|
|
4
4
|
function n(n) {
|
|
@@ -13,4 +13,4 @@ function n(n) {
|
|
|
13
13
|
//#endregion
|
|
14
14
|
export { n as t };
|
|
15
15
|
|
|
16
|
-
//# sourceMappingURL=PointPlotter-
|
|
16
|
+
//# sourceMappingURL=PointPlotter-CLd9u5Bq.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PointPlotter-
|
|
1
|
+
{"version":3,"file":"PointPlotter-CLd9u5Bq.js","names":[],"sources":["../../src/components/PointPlotter/PointPlotter.tsx"],"sourcesContent":["import type { CSSProperties, ReactNode } from 'react';\nimport {\n CurveEditor,\n type AnchorRenderProps,\n type ControlPoint,\n type CurveEditorProps,\n} from '../CurveEditor';\nimport type { GridSettings, AxesSettings } from '../Plot2D';\n\n/** Props for {@link PointPlotter}. */\nexport interface PointPlotterProps {\n /** Plotted points; caller-owned. */\n value: readonly ControlPoint[];\n /** Fires every frame during drag with the live in-flight value. */\n onInput: (next: ControlPoint[]) => void;\n /** Fires once per discrete user action (drag-end, add, delete) with\n * the new value and the value at gesture start. Wire history here. */\n onChange?: (next: ControlPoint[], prev: readonly ControlPoint[]) => void;\n /** Model-space x range. Default [0, 1]. */\n xRange?: readonly [number, number];\n /** Model-space y range. Default [0, 1]. */\n yRange?: readonly [number, number];\n width: number;\n height: number;\n grid?: GridSettings | false | null;\n axes?: AxesSettings | false | null;\n /** Minimum allowed point count. User-initiated deletion is refused\n * while `value.length <= minPoints`. */\n minPoints?: number;\n /** Maximum allowed point count. User-initiated insertion is refused\n * while `value.length >= maxPoints`. */\n maxPoints?: number;\n /** How new points are added. Default `'click-empty'`. Set to `'never'`\n * to disable insertion entirely. */\n addPointMode?: 'click-empty' | 'never';\n /** Built-in undo/redo via keyboard when the component has focus.\n * Default `true`. See `CurveEditor.history` for details. */\n history?: boolean;\n /** Custom per-anchor renderer. See `CurveEditor.renderAnchor`. */\n renderAnchor?: (info: AnchorRenderProps) => ReactNode;\n /** Extra SVG content beneath the anchors. See `CurveEditor.decorations`. */\n decorations?: ReactNode;\n className?: string;\n style?: CSSProperties;\n}\n\n/**\n * 2D point plotter — a CurveEditor with the curve hidden. Anchors,\n * drag, shift+click delete, right-click delete, and click-empty insert\n * are inherited from CurveEditor verbatim, so the two components share\n * gesture semantics and stay in sync as CurveEditor evolves.\n *\n * Differences from CurveEditor: no curve `<path>` drawn, no fill, no\n * endpoint pinning, no interpolation, default `addPointMode` is\n * `'click-empty'` (CurveEditor defaults to `'click-curve'`, which\n * doesn't make sense without a curve to hit-test against).\n */\nexport function PointPlotter(props: PointPlotterProps) {\n const curveProps: CurveEditorProps = {\n ...props,\n addPointMode: props.addPointMode ?? 'click-empty',\n curve: false,\n fill: false,\n endpoints: 'free',\n };\n return <CurveEditor {...curveProps} />;\n}\n"],"mappings":";;;AAyDA,SAAgB,EAAa,GAA0B;AAQrD,QAAO,kBAAC,GAAD;EANL,GAAG;EACH,cAAc,EAAM,gBAAgB;EACpC,OAAO;EACP,MAAM;EACN,WAAW;EAEyB,CAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as e } from "./Badge-
|
|
1
|
+
import { t as e } from "./Badge-9dPfvYOk.js";
|
|
2
2
|
import { jsx as t } from "react/jsx-runtime";
|
|
3
3
|
var n = {
|
|
4
4
|
row: "_row_q3wun_1",
|
|
@@ -33,4 +33,4 @@ function r({ segments: r, startCap: i = "flat", size: a, variant: o, depth: s, g
|
|
|
33
33
|
//#endregion
|
|
34
34
|
export { r as t };
|
|
35
35
|
|
|
36
|
-
//# sourceMappingURL=Powerline-
|
|
36
|
+
//# sourceMappingURL=Powerline-DhQJYaUg.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Powerline-
|
|
1
|
+
{"version":3,"file":"Powerline-DhQJYaUg.js","names":[],"sources":["../../src/components/Powerline/Powerline.module.css","../../src/components/Powerline/Powerline.tsx"],"sourcesContent":[".row {\n display: inline-flex;\n align-items: stretch;\n gap: var(--powerline-gap, 0.2em);\n}\n\n.matrix {\n display: grid;\n gap: 12px;\n}\n\n/* Reset Badge's opinionated text defaults — Powerline segments should pick up\n * the surrounding text style instead of forcing uppercase / 10px / extra\n * tracking. Consumers can still override via className on Powerline. */\n.segment {\n font-size: inherit;\n text-transform: none;\n letter-spacing: normal;\n}\n","import type { CSSProperties, ReactNode } from 'react';\nimport { Badge } from '../Badge/Badge';\nimport type { BadgeSize, BadgeTone, BadgeVariant } from '../Badge/types';\nimport type { EdgeCap } from '../Badge/bases/edgeProfiles';\nimport s from './Powerline.module.css';\n\n/**\n * One segment of a {@link Powerline}. Giving it `onClick` or `href` makes it\n * interactive, the same way it does on a `Badge`.\n */\nexport interface PowerlineSegment {\n text: ReactNode;\n /** Cap on this segment's right edge. Next segment's left edge adopts the same profile. */\n endCap?: EdgeCap;\n tone?: BadgeTone;\n variant?: BadgeVariant;\n size?: BadgeSize;\n onClick?: () => void;\n href?: string;\n 'aria-label'?: string;\n}\n\n/** Props for {@link Powerline}. */\nexport interface PowerlineProps {\n segments: PowerlineSegment[];\n /** Left edge of the first segment. Defaults to 'flat'. */\n startCap?: EdgeCap;\n /** Default size for every segment (per-segment `size` wins). */\n size?: BadgeSize;\n /** Default variant for every segment (per-segment `variant` wins). */\n variant?: BadgeVariant;\n /** Protrusion depth in CSS px, passed through to every segment's base. */\n depth?: number;\n /** Visible gap between adjacent segments. Number → px; string → literal CSS length.\n * Default: `0.2em` (scales with the row's font size). Pass `0` for flush. */\n gap?: number | string;\n className?: string;\n 'aria-label'?: string;\n}\n\n/**\n * A row of chevron-linked badge segments, in the style of a shell powerline\n * prompt. Each segment's `endCap` shapes both its own right edge and the next\n * segment's left edge, so the run reads as one continuous chain.\n *\n * Built for showing a path through a sequence of steps — the inspector uses\n * it to display how input routes from tool to gesture to action to target.\n */\nexport function Powerline({\n segments,\n startCap = 'flat',\n size,\n variant,\n depth,\n gap,\n className,\n ...rest\n}: PowerlineProps) {\n const cls = [s.row, className].filter(Boolean).join(' ');\n const style: CSSProperties | undefined = gap !== undefined\n ? { ['--powerline-gap' as never]: typeof gap === 'number' ? `${gap}px` : gap }\n : undefined;\n return (\n <span className={cls} style={style} aria-label={rest['aria-label']}>\n {segments.map((seg, i) => {\n const leftEdge: EdgeCap = i === 0 ? startCap : (segments[i - 1].endCap ?? 'flat');\n const rightEdge: EdgeCap = seg.endCap ?? 'flat';\n return (\n <Badge\n key={i}\n base=\"powerline\"\n baseParams={{ leftEdge, rightEdge, ...(depth !== undefined && { depth }) }}\n tone={seg.tone}\n variant={seg.variant ?? variant}\n size={seg.size ?? size}\n onClick={seg.onClick}\n href={seg.href}\n aria-label={seg['aria-label']}\n className={s.segment}\n >\n {seg.text}\n </Badge>\n );\n })}\n </span>\n );\n}\n"],"mappings":";;;;;;;;;ACgDA,SAAgB,EAAU,EACxB,aACA,cAAW,QACX,SACA,YACA,UACA,QACA,cACA,GAAG,KACc;AAKjB,QACE,kBAAC,QAAD;EAAM,WALI,CAAC,EAAE,KAAK,EAAU,CAAC,OAAO,QAAQ,CAAC,KAAK,IAKjC;EAAY,OAJU,MAAQ,KAAA,IAE7C,KAAA,IADA,EAAG,mBAA6B,OAAO,KAAQ,WAAW,GAAG,EAAI,MAAM,GAAK;EAG1C,cAAY,EAAK;YAClD,EAAS,KAAK,GAAK,MAIhB,kBAAC,GAAD;GAEE,MAAK;GACL,YAAY;IAAE,UANQ,MAAM,IAAI,IAAY,EAAS,IAAI,GAAG,UAAU;IAM9C,WALD,EAAI,UAAU;IAKF,GAAI,MAAU,KAAA,KAAa,EAAE,UAAO;IAAG;GAC1E,MAAM,EAAI;GACV,SAAS,EAAI,WAAW;GACxB,MAAM,EAAI,QAAQ;GAClB,SAAS,EAAI;GACb,MAAM,EAAI;GACV,cAAY,EAAI;GAChB,WAAW,EAAE;aAEZ,EAAI;GACC,EAZD,EAYC,CAEV;EACG,CAAA"}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { n as e, t } from "./Tooltip-Di7vxbFa.js";
|
|
2
|
+
import { t as n } from "./ColorField-D1t-BdSa.js";
|
|
3
|
+
import { t as r } from "./Checkbox-BLEw-wEb.js";
|
|
4
|
+
import { t as i } from "./Input-DhWBgpRx.js";
|
|
5
|
+
import { t as a } from "./NumberField-ZdVa4bpJ.js";
|
|
6
|
+
import { n as o, t as s } from "./RadioGroup-vtwjRiUm.js";
|
|
7
|
+
import { t as c } from "./RangeSlider-B-n_7AgJ.js";
|
|
8
|
+
import { t as l } from "./Select--BVTi-j0.js";
|
|
9
|
+
import { t as u } from "./Switch-CtZwp5zm.js";
|
|
10
|
+
import { t as d } from "./Dialog-DC1x46Sq.js";
|
|
11
|
+
import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
12
|
+
import { isBuiltinToolPref as h } from "@weasel-js/core";
|
|
13
|
+
import { useMemo as g } from "react";
|
|
14
|
+
import { Focusable as _ } from "react-aria-components";
|
|
15
|
+
//#region src/components/paintValue.ts
|
|
16
|
+
function v(e) {
|
|
17
|
+
if (typeof e != "object" || !e) return;
|
|
18
|
+
let t = e;
|
|
19
|
+
if (!(t.fill !== void 0 && t.fill !== "solid")) return typeof t.color == "string" ? t.color : void 0;
|
|
20
|
+
}
|
|
21
|
+
function y(e) {
|
|
22
|
+
if (!(typeof e != "object" || !e || !("paint" in e))) return v(e.paint);
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/components/Prefs/schema.ts
|
|
26
|
+
function b(e) {
|
|
27
|
+
return "kind" in e;
|
|
28
|
+
}
|
|
29
|
+
function x(e, t) {
|
|
30
|
+
let n = e;
|
|
31
|
+
for (let e of t.split(".")) {
|
|
32
|
+
if (typeof n != "object" || !n) return;
|
|
33
|
+
n = n[e];
|
|
34
|
+
}
|
|
35
|
+
return n;
|
|
36
|
+
}
|
|
37
|
+
function S(e, t) {
|
|
38
|
+
if (b(e)) return e.hidden && !t ? null : e;
|
|
39
|
+
let n = e, r = {};
|
|
40
|
+
for (let [e, i] of Object.entries(n.children)) {
|
|
41
|
+
let n = S(i, t);
|
|
42
|
+
n && (r[e] = n);
|
|
43
|
+
}
|
|
44
|
+
return Object.keys(r).length === 0 ? null : {
|
|
45
|
+
...e,
|
|
46
|
+
children: r
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
var C = {
|
|
50
|
+
columns: "_columns_i4hcs_5",
|
|
51
|
+
column: "_column_i4hcs_5",
|
|
52
|
+
panel: "_panel_i4hcs_23",
|
|
53
|
+
subpanel: "_subpanel_i4hcs_33",
|
|
54
|
+
groupHeader: "_groupHeader_i4hcs_44",
|
|
55
|
+
groupTitle: "_groupTitle_i4hcs_57",
|
|
56
|
+
groupDesc: "_groupDesc_i4hcs_72",
|
|
57
|
+
rows: "_rows_i4hcs_79",
|
|
58
|
+
row: "_row_i4hcs_79",
|
|
59
|
+
rowLabel: "_rowLabel_i4hcs_95",
|
|
60
|
+
help: "_help_i4hcs_104",
|
|
61
|
+
rowControl: "_rowControl_i4hcs_128",
|
|
62
|
+
textarea: "_textarea_i4hcs_135",
|
|
63
|
+
unrenderable: "_unrenderable_i4hcs_147",
|
|
64
|
+
titleRow: "_titleRow_i4hcs_154",
|
|
65
|
+
objectLeaf: "_objectLeaf_i4hcs_168",
|
|
66
|
+
objectRow: "_objectRow_i4hcs_175",
|
|
67
|
+
objectLabel: "_objectLabel_i4hcs_182",
|
|
68
|
+
objectGroup: "_objectGroup_i4hcs_187"
|
|
69
|
+
};
|
|
70
|
+
//#endregion
|
|
71
|
+
//#region src/components/Prefs/PrefsForm.tsx
|
|
72
|
+
function w(e) {
|
|
73
|
+
let { schema: t, values: n, onChange: r, renderers: i, showHidden: a = !1, className: o } = e, s = g(() => S(t, a), [t, a]), c = {
|
|
74
|
+
values: n,
|
|
75
|
+
onChange: r,
|
|
76
|
+
renderers: i
|
|
77
|
+
};
|
|
78
|
+
return /* @__PURE__ */ p("div", {
|
|
79
|
+
className: [C.columns, o].filter(Boolean).join(" "),
|
|
80
|
+
children: Object.entries(s?.children ?? {}).map(([e, t]) => /* @__PURE__ */ p("div", {
|
|
81
|
+
className: C.column,
|
|
82
|
+
children: b(t) ? /* @__PURE__ */ p(E, {
|
|
83
|
+
ctx: c,
|
|
84
|
+
path: e,
|
|
85
|
+
pref: t
|
|
86
|
+
}) : /* @__PURE__ */ p(T, {
|
|
87
|
+
ctx: c,
|
|
88
|
+
group: t,
|
|
89
|
+
path: e,
|
|
90
|
+
depth: 0
|
|
91
|
+
})
|
|
92
|
+
}, e))
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function T({ ctx: e, group: t, path: n, depth: r }) {
|
|
96
|
+
return /* @__PURE__ */ m("div", {
|
|
97
|
+
className: r === 0 ? C.panel : C.subpanel,
|
|
98
|
+
children: [/* @__PURE__ */ m("div", {
|
|
99
|
+
className: C.groupHeader,
|
|
100
|
+
children: [/* @__PURE__ */ p("h3", {
|
|
101
|
+
className: C.groupTitle,
|
|
102
|
+
children: t.name
|
|
103
|
+
}), t.description !== void 0 && /* @__PURE__ */ p("p", {
|
|
104
|
+
className: C.groupDesc,
|
|
105
|
+
children: t.description
|
|
106
|
+
})]
|
|
107
|
+
}), /* @__PURE__ */ p("div", {
|
|
108
|
+
className: C.rows,
|
|
109
|
+
children: Object.entries(t.children).map(([t, i]) => {
|
|
110
|
+
let a = `${n}.${t}`;
|
|
111
|
+
return b(i) ? /* @__PURE__ */ p(E, {
|
|
112
|
+
ctx: e,
|
|
113
|
+
path: a,
|
|
114
|
+
pref: i
|
|
115
|
+
}, t) : /* @__PURE__ */ p(T, {
|
|
116
|
+
ctx: e,
|
|
117
|
+
group: i,
|
|
118
|
+
path: a,
|
|
119
|
+
depth: r + 1
|
|
120
|
+
}, t);
|
|
121
|
+
})
|
|
122
|
+
})]
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
function E({ ctx: n, path: r, pref: i }) {
|
|
126
|
+
let a = x(n.values, r), o = {
|
|
127
|
+
path: r,
|
|
128
|
+
pref: i,
|
|
129
|
+
value: a === void 0 ? i.default : a,
|
|
130
|
+
setValue: (e) => n.onChange(r, e)
|
|
131
|
+
}, s = n.renderers?.[i.kind], c = s ? s(o) : D(o);
|
|
132
|
+
return s && c === null ? null : i.block ? /* @__PURE__ */ p(f, { children: c }) : /* @__PURE__ */ m("label", {
|
|
133
|
+
className: C.row,
|
|
134
|
+
children: [/* @__PURE__ */ m("span", {
|
|
135
|
+
className: C.rowLabel,
|
|
136
|
+
children: [i.name, i.description ? /* @__PURE__ */ m(e, { children: [/* @__PURE__ */ p(_, { children: /* @__PURE__ */ p("button", {
|
|
137
|
+
type: "button",
|
|
138
|
+
className: C.help,
|
|
139
|
+
"aria-label": `About ${i.name}`,
|
|
140
|
+
children: "ⓘ"
|
|
141
|
+
}) }), /* @__PURE__ */ p(t, { children: i.description })] }) : null]
|
|
142
|
+
}), /* @__PURE__ */ p("span", {
|
|
143
|
+
className: C.rowControl,
|
|
144
|
+
children: c
|
|
145
|
+
})]
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
function D(e, t) {
|
|
149
|
+
let { pref: d, value: f, setValue: g } = e;
|
|
150
|
+
if (!h(d)) return /* @__PURE__ */ m("span", {
|
|
151
|
+
className: C.unrenderable,
|
|
152
|
+
children: [
|
|
153
|
+
"(",
|
|
154
|
+
d.kind,
|
|
155
|
+
": no renderer)"
|
|
156
|
+
]
|
|
157
|
+
});
|
|
158
|
+
switch (d.kind) {
|
|
159
|
+
case "boolean": {
|
|
160
|
+
let e = !!f;
|
|
161
|
+
return d.control === "switch" ? /* @__PURE__ */ p(u, {
|
|
162
|
+
isSelected: e,
|
|
163
|
+
onChange: g,
|
|
164
|
+
"aria-label": d.name
|
|
165
|
+
}) : /* @__PURE__ */ p(r, {
|
|
166
|
+
isSelected: e,
|
|
167
|
+
onChange: g,
|
|
168
|
+
"aria-label": d.name
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
case "number": {
|
|
172
|
+
let e = typeof f == "number" && Number.isFinite(f) ? f : 0;
|
|
173
|
+
return d.control === "slider" ? /* @__PURE__ */ p(c, {
|
|
174
|
+
value: e,
|
|
175
|
+
onChange: (e) => g(typeof e == "number" ? e : e[0]),
|
|
176
|
+
minValue: d.min,
|
|
177
|
+
maxValue: d.max,
|
|
178
|
+
step: d.step ?? 1,
|
|
179
|
+
"aria-label": d.name
|
|
180
|
+
}) : /* @__PURE__ */ p(a, {
|
|
181
|
+
value: e,
|
|
182
|
+
onChange: g,
|
|
183
|
+
minValue: d.min,
|
|
184
|
+
maxValue: d.max,
|
|
185
|
+
step: d.step ?? 1,
|
|
186
|
+
"aria-label": d.name
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
case "string": {
|
|
190
|
+
let e = typeof f == "string" ? f : "";
|
|
191
|
+
return d.control === "textarea" ? /* @__PURE__ */ p("textarea", {
|
|
192
|
+
className: C.textarea,
|
|
193
|
+
value: e,
|
|
194
|
+
onChange: (e) => g(e.target.value),
|
|
195
|
+
"aria-label": d.name,
|
|
196
|
+
rows: 3
|
|
197
|
+
}) : /* @__PURE__ */ p(i, {
|
|
198
|
+
value: e,
|
|
199
|
+
onChange: g,
|
|
200
|
+
"aria-label": d.name
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
case "enum": {
|
|
204
|
+
let e = d.encoding, n = e ? e.read(f, t) : typeof f == "string" ? f : d.default, r = (n) => g(e ? e.write(n, t) : n);
|
|
205
|
+
return d.control === "radio" ? /* @__PURE__ */ p(o, {
|
|
206
|
+
value: n ?? null,
|
|
207
|
+
onChange: r,
|
|
208
|
+
"aria-label": d.name,
|
|
209
|
+
children: d.options.map((e) => /* @__PURE__ */ p(s, {
|
|
210
|
+
value: e.value,
|
|
211
|
+
isDisabled: e.disabled,
|
|
212
|
+
children: e.label
|
|
213
|
+
}, e.value))
|
|
214
|
+
}) : /* @__PURE__ */ p(l, {
|
|
215
|
+
options: d.options.map((e) => ({
|
|
216
|
+
value: e.value,
|
|
217
|
+
label: e.label,
|
|
218
|
+
isDisabled: e.disabled
|
|
219
|
+
})),
|
|
220
|
+
selectedKey: n ?? null,
|
|
221
|
+
onSelectionChange: r,
|
|
222
|
+
"aria-label": d.name
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
case "color": return /* @__PURE__ */ p(n, {
|
|
226
|
+
value: typeof f == "string" ? f : d.default,
|
|
227
|
+
alpha: d.alpha,
|
|
228
|
+
onChange: g,
|
|
229
|
+
"aria-label": d.name
|
|
230
|
+
});
|
|
231
|
+
case "paint": return /* @__PURE__ */ p(n, {
|
|
232
|
+
value: v(f) ?? v(d.default),
|
|
233
|
+
alpha: d.alpha,
|
|
234
|
+
onChange: (e) => g({
|
|
235
|
+
fill: "solid",
|
|
236
|
+
color: e
|
|
237
|
+
}),
|
|
238
|
+
"aria-label": d.name
|
|
239
|
+
});
|
|
240
|
+
case "object": {
|
|
241
|
+
let t = d, n = typeof f == "object" && f ? f : void 0, r = (i) => {
|
|
242
|
+
let a = [];
|
|
243
|
+
for (let [o, s] of Object.entries(i)) {
|
|
244
|
+
if (!b(s)) {
|
|
245
|
+
let e = r(s.children);
|
|
246
|
+
if (e.length === 0) continue;
|
|
247
|
+
a.push(/* @__PURE__ */ p("h4", {
|
|
248
|
+
className: C.objectGroup,
|
|
249
|
+
children: s.name
|
|
250
|
+
}, `group:${o}`), ...e);
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
a.push(/* @__PURE__ */ m("label", {
|
|
254
|
+
className: C.objectRow,
|
|
255
|
+
children: [/* @__PURE__ */ p("span", {
|
|
256
|
+
className: C.objectLabel,
|
|
257
|
+
children: s.name
|
|
258
|
+
}), D({
|
|
259
|
+
path: `${e.path}.${o}`,
|
|
260
|
+
pref: s,
|
|
261
|
+
value: n?.[o],
|
|
262
|
+
setValue: (e) => {
|
|
263
|
+
g({
|
|
264
|
+
...n ?? t.fromScalar?.(f) ?? {},
|
|
265
|
+
[o]: e
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}, n)]
|
|
269
|
+
}, o));
|
|
270
|
+
}
|
|
271
|
+
return a;
|
|
272
|
+
};
|
|
273
|
+
return /* @__PURE__ */ p("div", {
|
|
274
|
+
className: C.objectLeaf,
|
|
275
|
+
children: r(t.children)
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
default: throw Error(`PrefsForm: no control for built-in pref kind "${d.kind}"`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
//#endregion
|
|
282
|
+
//#region src/components/Prefs/PrefsDialog.tsx
|
|
283
|
+
function O(e) {
|
|
284
|
+
let { isOpen: t, onOpenChange: n, title: r, headerExtra: i, dialogClassName: a, ...o } = e;
|
|
285
|
+
return /* @__PURE__ */ p(d, {
|
|
286
|
+
isOpen: t,
|
|
287
|
+
onOpenChange: n,
|
|
288
|
+
"aria-label": typeof r == "string" ? r : o.schema.name,
|
|
289
|
+
className: a,
|
|
290
|
+
title: i === void 0 ? r ?? o.schema.name : /* @__PURE__ */ m("span", {
|
|
291
|
+
className: C.titleRow,
|
|
292
|
+
children: [/* @__PURE__ */ p("span", { children: r ?? o.schema.name }), i]
|
|
293
|
+
}),
|
|
294
|
+
children: /* @__PURE__ */ p(w, { ...o })
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
//#endregion
|
|
298
|
+
export { S as a, x as i, w as n, v as o, b as r, y as s, O as t };
|
|
299
|
+
|
|
300
|
+
//# sourceMappingURL=Prefs-BSJ0lMmJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Prefs-BSJ0lMmJ.js","names":[],"sources":["../../src/components/paintValue.ts","../../src/components/Prefs/schema.ts","../../src/components/Prefs/Prefs.module.css","../../src/components/Prefs/PrefsForm.tsx","../../src/components/Prefs/PrefsDialog.tsx"],"sourcesContent":["// Reading and writing the paint objects a property control may be pointed at.\n//\n// `data.fill` is a `FillStyle` and `data.stroke` a `Stroke`, so a control that\n// assumes either is a hex string reads `undefined` off it and writes a bare\n// string back over it. These helpers are what keep a control honest about the\n// shape it is holding.\n\n/** The color of a solid paint, or `undefined` for anything else — including a\n * value that isn't a paint at all. `fill` is optional on the solid member of\n * the union, so the tag alone can't decide it. */\nexport function solidColorOf(value: unknown): string | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const paint = value as { fill?: string; color?: unknown };\n if (paint.fill !== undefined && paint.fill !== 'solid') return undefined;\n return typeof paint.color === 'string' ? paint.color : undefined;\n}\n\n/** The single color a stroke shows, or `undefined` when it has none to show —\n * a gradient or pattern stroke, or no stroke at all. */\nexport function strokeColorOf(value: unknown): string | undefined {\n if (typeof value !== 'object' || value === null || !('paint' in value)) return undefined;\n return solidColorOf((value as { paint: unknown }).paint);\n}\n","// Preference-schema vocabulary for PrefsForm / PrefsDialog / SelectionPanel:\n// core's `ToolPref*` family under this package's names. Never re-declare them\n// here — this was a hand-kept structural copy until it drifted, and the\n// independence from `@weasel-js/core` it bought is long gone anyway.\n\nexport type {\n ToolPref as BuiltinPref,\n ToolPrefBoolean as PrefBoolean,\n ToolPrefBooleanControl as PrefBooleanControl,\n ToolPrefColor as PrefColor,\n ToolPrefCustom as PrefCustom,\n ToolPrefEnum as PrefEnum,\n ToolPrefEnumControl as PrefEnumControl,\n ToolPrefEnumEncoding as PrefEnumEncoding,\n ToolPrefGroup as PrefGroup,\n ToolPrefKind as PrefKind,\n ToolPrefLeaf as PrefLeaf,\n ToolPrefNumber as PrefNumber,\n ToolPrefNumberControl as PrefNumberControl,\n ToolPrefNumberUnit as PrefNumberUnit,\n ToolPrefObject as PrefObject,\n ToolPrefPaint as PrefPaint,\n ToolPrefString as PrefString,\n ToolPrefStringControl as PrefStringControl,\n} from '@weasel-js/core';\n\nimport type { ToolPrefGroup, ToolPrefLeaf } from '@weasel-js/core';\n\n/** Distinguishes a leaf from a group while walking a schema tree. */\nexport function isPrefLeaf(node: ToolPrefLeaf | ToolPrefGroup): node is ToolPrefLeaf {\n return 'kind' in node;\n}\n\n/** Get the value at a dotted path inside a nested value tree. Returns\n * `undefined` when a segment is missing or hits a non-object. */\nexport function prefValueAtPath(values: unknown, path: string): unknown {\n let cur: unknown = values;\n for (const seg of path.split('.')) {\n if (cur == null || typeof cur !== 'object') return undefined;\n cur = (cur as Record<string, unknown>)[seg];\n }\n return cur;\n}\n\n/**\n * Recursively drop `hidden` leaves (unless `showHidden`), pruning groups\n * that end up empty. Returns null when the entire subtree is hidden.\n */\nexport function visiblePrefSubtree<T extends ToolPrefLeaf | ToolPrefGroup>(\n node: T,\n showHidden: boolean,\n): T | null {\n if (isPrefLeaf(node)) return node.hidden && !showHidden ? null : node;\n const group = node as ToolPrefGroup;\n const children: Record<string, ToolPrefLeaf | ToolPrefGroup> = {};\n for (const [key, child] of Object.entries(group.children)) {\n const kept = visiblePrefSubtree(child, showHidden);\n if (kept) children[key] = kept;\n }\n if (Object.keys(children).length === 0) return null;\n return { ...node, children };\n}\n","/* Ported from WeaselDraw's wd-prefs-* rules (apps/draw/src/app.css),\n * generalized onto theme tokens. Column width is overridable via\n * --wzl-prefs-column-width at any ancestor scope. */\n\n.columns {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n align-items: flex-start;\n gap: 12px;\n overflow-y: auto;\n padding-right: 4px;\n}\n\n.column {\n flex: 0 0 var(--wzl-prefs-column-width, 300px);\n width: var(--wzl-prefs-column-width, 300px);\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.panel {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 10px;\n background: var(--wzl-surface-sunken);\n border: 1px solid var(--wzl-border);\n border-radius: var(--wzl-radius-sm);\n}\n\n.subpanel {\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin-top: 6px;\n padding: 8px 8px 8px 10px;\n background: var(--wzl-surface-sunken);\n border-left: 2px solid var(--wzl-border);\n border-radius: var(--wzl-radius-sm);\n}\n\n.groupHeader {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding-bottom: 4px;\n border-bottom: 1px solid var(--wzl-border);\n}\n\n.subpanel .groupHeader {\n border-bottom: none;\n padding-bottom: 2px;\n}\n\n.groupTitle {\n margin: 0;\n font-size: var(--wzl-font-size-sm);\n font-weight: var(--wzl-font-weight-bold);\n color: var(--wzl-fg);\n letter-spacing: 0.04em;\n text-transform: uppercase;\n}\n\n.subpanel .groupTitle {\n font-size: var(--wzl-font-size-sm);\n text-transform: none;\n letter-spacing: 0;\n}\n\n.groupDesc {\n margin: 0;\n font-size: var(--wzl-font-size-sm);\n color: var(--wzl-fg-subtle);\n line-height: 1.35;\n}\n\n.rows {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n font-size: var(--wzl-font-size-sm);\n color: var(--wzl-fg);\n cursor: default;\n}\n\n.rowLabel {\n flex: 1 1 auto;\n min-width: 0;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n/* Description affordance — quiet until hovered/focused. */\n.help {\n flex: 0 0 auto;\n padding: 0;\n border: none;\n background: none;\n color: var(--wzl-fg-subtle);\n font-size: var(--wzl-font-size-sm);\n line-height: 1;\n cursor: help;\n border-radius: var(--wzl-radius-sm);\n}\n\n.help:hover {\n color: var(--wzl-fg);\n}\n\n.help:focus-visible {\n outline: 2px solid var(--wzl-focus-ring);\n outline-offset: 1px;\n color: var(--wzl-fg);\n}\n\n/* Control slot — fixed width so the label can take the rest of the row.\n * Kit form controls render as block-level flex columns that fill it. */\n.rowControl {\n flex: 0 0 110px;\n min-width: 0;\n display: flex;\n align-items: center;\n}\n\n.textarea {\n width: 100%;\n padding: 4px 8px;\n background: var(--wzl-surface-sunken);\n color: var(--wzl-fg);\n border: 1px solid var(--wzl-border);\n border-radius: var(--wzl-radius-sm);\n font: inherit;\n font-size: var(--wzl-font-size-sm);\n resize: vertical;\n}\n\n.unrenderable {\n font-family: var(--wzl-font-mono);\n font-size: var(--wzl-font-size-sm);\n color: var(--wzl-fg-subtle);\n}\n\n/* PrefsDialog header row: title text + optional headerExtra chrome. */\n.titleRow {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n min-width: 0;\n}\n\n.titleRow > span:first-child {\n flex: 1;\n min-width: 0;\n}\n\n/* An object leaf renders its fields as its own rows. */\n.objectLeaf {\n display: flex;\n flex-direction: column;\n gap: 6px;\n inline-size: 100%;\n}\n\n.objectRow {\n display: grid;\n grid-template-columns: 96px 1fr;\n gap: 8px;\n align-items: center;\n}\n\n.objectLabel {\n font-size: var(--wzl-font-size-sm);\n opacity: 0.75;\n}\n\n.objectGroup {\n margin: 4px 0 0;\n font-size: var(--wzl-font-size-xs);\n font-weight: var(--wzl-font-weight-bold);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n opacity: 0.55;\n}\n","import { useMemo, type ReactNode } from 'react';\nimport { Focusable } from 'react-aria-components';\nimport { Checkbox } from '../Checkbox';\nimport { ColorField } from '../ColorField';\nimport { solidColorOf } from '../paintValue';\nimport { Input } from '../Input';\nimport { NumberField } from '../NumberField';\nimport { RadioGroup, Radio } from '../RadioGroup';\nimport { RangeSlider } from '../RangeSlider';\nimport { Select } from '../Select';\nimport { Switch } from '../Switch';\nimport { Tooltip, TooltipTrigger } from '../Tooltip';\nimport { isBuiltinToolPref } from '@weasel-js/core';\nimport {\n isPrefLeaf,\n prefValueAtPath,\n visiblePrefSubtree,\n type PrefGroup,\n type PrefLeaf,\n} from './schema';\nimport s from './Prefs.module.css';\n\n/** What a {@link PrefRenderer} is given for the leaf it is rendering. */\nexport interface PrefRenderContext {\n /** Dotted path of the leaf within the schema root. */\n path: string;\n /** The schema node. App renderers narrow this to their own kind shape. */\n pref: PrefLeaf;\n /** Current value — `values` at `path`, falling back to `pref.default`. */\n value: unknown;\n setValue: (value: unknown) => void;\n}\n\n/**\n * Renders the control cell for one preference leaf. Returning `null`\n * collapses the row.\n */\nexport type PrefRenderer = (ctx: PrefRenderContext) => ReactNode;\n\n/** Props for {@link PrefsForm}. */\nexport interface PrefsFormProps {\n /** Root of the schema tree. Core `ToolPrefGroup`s assign structurally. */\n schema: PrefGroup;\n /** Nested value tree (shape mirrors the schema). Sparse is fine —\n * missing leaves fall back to their schema `default`. */\n values?: unknown;\n /** Change callback with the leaf's dotted path. Values apply live;\n * there is no dirty/commit state. */\n onChange: (path: string, value: unknown) => void;\n /**\n * Per-kind renderers for app-defined kinds. Entries also override the\n * built-in kinds when keys collide. A renderer owns the control cell\n * (the label/tooltip row chrome stays with the form, unless the leaf\n * sets `block`); returning `null` collapses the row entirely. Unknown\n * kinds with no renderer show a labeled placeholder instead of\n * crashing.\n */\n renderers?: Record<string, PrefRenderer>;\n /** Reveal `hidden` leaves (dev tooling). Default false. */\n showHidden?: boolean;\n className?: string;\n}\n\n/**\n * Schema-driven preferences form. Top-level groups render as columns,\n * nested groups as indented sub-panels, leaves as label + control rows.\n * Storage-agnostic: pair with `PrefsDialog` for the modal composition,\n * and persist however the app likes via `onChange`.\n */\nexport function PrefsForm(props: PrefsFormProps) {\n const { schema, values, onChange, renderers, showHidden = false, className } = props;\n const visibleRoot = useMemo(\n () => visiblePrefSubtree(schema, showHidden),\n [schema, showHidden],\n );\n const ctx: WalkCtx = { values, onChange, renderers };\n return (\n <div className={[s.columns, className].filter(Boolean).join(' ')}>\n {Object.entries(visibleRoot?.children ?? {}).map(([key, child]) => (\n <div key={key} className={s.column}>\n {isPrefLeaf(child) ? (\n // Top-level leaves are unusual but legal — give each its own\n // column for symmetry with grouped siblings.\n <PrefRow ctx={ctx} path={key} pref={child} />\n ) : (\n <GroupBody ctx={ctx} group={child} path={key} depth={0} />\n )}\n </div>\n ))}\n </div>\n );\n}\n\ninterface WalkCtx {\n values: unknown;\n onChange: (path: string, value: unknown) => void;\n renderers?: Record<string, PrefRenderer>;\n}\n\nfunction GroupBody({ ctx, group, path, depth }: {\n ctx: WalkCtx;\n group: PrefGroup;\n path: string;\n depth: number;\n}) {\n return (\n <div className={depth === 0 ? s.panel : s.subpanel}>\n <div className={s.groupHeader}>\n <h3 className={s.groupTitle}>{group.name}</h3>\n {group.description !== undefined && (\n <p className={s.groupDesc}>{group.description}</p>\n )}\n </div>\n <div className={s.rows}>\n {Object.entries(group.children).map(([key, child]) => {\n const childPath = `${path}.${key}`;\n return isPrefLeaf(child) ? (\n <PrefRow key={key} ctx={ctx} path={childPath} pref={child} />\n ) : (\n <GroupBody key={key} ctx={ctx} group={child} path={childPath} depth={depth + 1} />\n );\n })}\n </div>\n </div>\n );\n}\n\nfunction PrefRow({ ctx, path, pref }: { ctx: WalkCtx; path: string; pref: PrefLeaf }) {\n const stored = prefValueAtPath(ctx.values, path);\n const renderCtx: PrefRenderContext = {\n path,\n pref,\n value: stored !== undefined ? stored : pref.default,\n setValue: (v) => ctx.onChange(path, v),\n };\n\n const custom = ctx.renderers?.[pref.kind];\n const control = custom ? custom(renderCtx) : renderBuiltin(renderCtx);\n if (custom && control === null) return null;\n\n // `block` leaves own their chrome (embedded editors with their own\n // header) — no label/tooltip row.\n if (pref.block) return <>{control}</>;\n\n return (\n <label className={s.row}>\n <span className={s.rowLabel}>\n {pref.name}\n {pref.description ? (\n // Help affordance carries the description tooltip. A tooltip\n // trigger must be interactive (keyboard-reachable), so this is\n // a real button — a bare label span would be neither focusable\n // nor announced.\n <TooltipTrigger>\n <Focusable>\n <button type=\"button\" className={s.help} aria-label={`About ${pref.name}`}>\n ⓘ\n </button>\n </Focusable>\n <Tooltip>{pref.description}</Tooltip>\n </TooltipTrigger>\n ) : null}\n </span>\n <span className={s.rowControl}>{control}</span>\n </label>\n );\n}\n\nfunction renderBuiltin(\n ctx: PrefRenderContext,\n // The object a nested leaf is a field of — what an enum `encoding` reads\n // and writes against. Undefined for a top-level leaf, which has none.\n siblings?: Record<string, unknown>,\n): ReactNode {\n const { pref, value, setValue } = ctx;\n if (!isBuiltinToolPref(pref)) {\n // App-defined kind with no `renderers` entry: labeled placeholder, not a\n // crash — a missing wiring should be visible and recoverable.\n return <span className={s.unrenderable}>({pref.kind}: no renderer)</span>;\n }\n switch (pref.kind) {\n case 'boolean': {\n const checked = Boolean(value);\n return pref.control === 'switch' ? (\n <Switch isSelected={checked} onChange={setValue} aria-label={pref.name} />\n ) : (\n <Checkbox isSelected={checked} onChange={setValue} aria-label={pref.name} />\n );\n }\n case 'number': {\n const n = typeof value === 'number' && Number.isFinite(value) ? value : 0;\n if (pref.control === 'slider') {\n return (\n <RangeSlider\n value={n}\n onChange={(v) => setValue(typeof v === 'number' ? v : v[0])}\n minValue={pref.min}\n maxValue={pref.max}\n step={pref.step ?? 1}\n aria-label={pref.name}\n />\n );\n }\n return (\n <NumberField\n value={n}\n onChange={setValue}\n minValue={pref.min}\n maxValue={pref.max}\n step={pref.step ?? 1}\n aria-label={pref.name}\n />\n );\n }\n case 'string': {\n const text = typeof value === 'string' ? value : '';\n if (pref.control === 'textarea') {\n return (\n <textarea\n className={s.textarea}\n value={text}\n onChange={(e) => setValue(e.target.value)}\n aria-label={pref.name}\n rows={3}\n />\n );\n }\n return <Input value={text} onChange={setValue} aria-label={pref.name} />;\n }\n case 'enum': {\n const encoding = pref.encoding;\n // An encoded leaf stores something other than the option string (a dash\n // array), so reading the raw value selects nothing and writing one\n // replaces the stored form with the option string.\n const option = encoding\n ? encoding.read(value, siblings)\n : typeof value === 'string'\n ? value\n : pref.default;\n const choose = (next: string): void =>\n setValue(encoding ? encoding.write(next, siblings) : next);\n if (pref.control === 'radio') {\n return (\n <RadioGroup value={option ?? null} onChange={choose} aria-label={pref.name}>\n {pref.options.map((o) => (\n <Radio key={o.value} value={o.value} isDisabled={o.disabled}>\n {o.label}\n </Radio>\n ))}\n </RadioGroup>\n );\n }\n return (\n <Select<string>\n options={pref.options.map((o) => ({\n value: o.value,\n label: o.label,\n isDisabled: o.disabled,\n }))}\n selectedKey={option ?? null}\n onSelectionChange={choose}\n aria-label={pref.name}\n />\n );\n }\n case 'color': {\n const hex = typeof value === 'string' ? value : pref.default;\n return (\n <ColorField\n value={hex}\n alpha={pref.alpha}\n onChange={setValue}\n aria-label={pref.name}\n />\n );\n }\n case 'paint': {\n // A whole `FillStyle`. A gradient or pattern has no single color to\n // show, so the field goes blank rather than claiming one, and an edit\n // writes a whole solid union member rather than grafting a `color` key\n // onto the gradient.\n const solid = solidColorOf(value) ?? solidColorOf(pref.default);\n return (\n <ColorField\n value={solid}\n alpha={pref.alpha}\n onChange={(color) => setValue({ fill: 'solid', color })}\n aria-label={pref.name}\n />\n );\n }\n case 'object': {\n // One value with its fields hanging off it: each child renders its own\n // control and commits the parent object whole.\n const p = pref;\n const held = typeof value === 'object' && value !== null\n ? (value as Record<string, unknown>)\n : undefined;\n const objectRows = (children: Record<string, PrefLeaf | PrefGroup>): ReactNode[] => {\n const out: ReactNode[] = [];\n for (const [key, child] of Object.entries(children)) {\n if (!isPrefLeaf(child)) {\n const inner = objectRows(child.children);\n if (inner.length === 0) continue;\n out.push(<h4 key={`group:${key}`} className={s.objectGroup}>{child.name}</h4>, ...inner);\n continue;\n }\n out.push(\n <label key={key} className={s.objectRow}>\n <span className={s.objectLabel}>{child.name}</span>\n {renderBuiltin({\n path: `${ctx.path}.${key}`,\n pref: child,\n value: held?.[key],\n setValue: (v) => {\n const base = held ?? p.fromScalar?.(value) ?? {};\n setValue({ ...base, [key]: v });\n },\n }, held)}\n </label>,\n );\n }\n return out;\n };\n return <div className={s.objectLeaf}>{objectRows(p.children)}</div>;\n }\n default: {\n // Not reachable while every built-in kind has an arm — and a new kind\n // that lacks one is a compile error here, never a blank row.\n const _exhaustive: never = pref;\n throw new Error(\n `PrefsForm: no control for built-in pref kind \"${(_exhaustive as { kind: string }).kind}\"`,\n );\n }\n }\n}\n","import type { ReactNode } from 'react';\nimport { Dialog } from '../Dialog';\nimport { PrefsForm, type PrefsFormProps } from './PrefsForm';\nimport s from './Prefs.module.css';\n\n/**\n * Props for {@link PrefsDialog} — everything {@link PrefsForm} takes, plus\n * the dialog's own open state and chrome.\n */\nexport interface PrefsDialogProps extends PrefsFormProps {\n isOpen: boolean;\n onOpenChange: (open: boolean) => void;\n /** Dialog heading. Defaults to the schema root's `name`. */\n title?: ReactNode;\n /** Extra chrome rendered inline with the title (e.g. a dev-mode\n * \"Show hidden\" switch). */\n headerExtra?: ReactNode;\n /** Class applied to the dialog's modal box. */\n dialogClassName?: string;\n}\n\n/**\n * The kit's preferences dialog: `Dialog` + `PrefsForm`. Changes apply\n * live through `onChange` — there is no OK/Cancel staging. For custom\n * placement (sidebar, popover, inline page), compose `PrefsForm`\n * directly; this wrapper is intentionally thin.\n */\nexport function PrefsDialog(props: PrefsDialogProps) {\n const { isOpen, onOpenChange, title, headerExtra, dialogClassName, ...form } = props;\n return (\n <Dialog\n isOpen={isOpen}\n onOpenChange={onOpenChange}\n aria-label={typeof title === 'string' ? title : form.schema.name}\n className={dialogClassName}\n title={\n headerExtra !== undefined ? (\n <span className={s.titleRow}>\n <span>{title ?? form.schema.name}</span>\n {headerExtra}\n </span>\n ) : (\n title ?? form.schema.name\n )\n }\n >\n <PrefsForm {...form} />\n </Dialog>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { n as e } from "./Field-BjFl_yng.js";
|
|
2
|
+
import { jsx as t, jsxs as n } from "react/jsx-runtime";
|
|
3
|
+
import { FieldError as r, Label as i, Radio as a, RadioGroup as o, Text as s } from "react-aria-components";
|
|
4
|
+
var c = {
|
|
5
|
+
group: "_group_1nkc4_1",
|
|
6
|
+
legend: "_legend_1nkc4_14",
|
|
7
|
+
radio: "_radio_1nkc4_20",
|
|
8
|
+
dot: "_dot_1nkc4_36"
|
|
9
|
+
};
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/components/RadioGroup/RadioGroup.tsx
|
|
12
|
+
function l(a) {
|
|
13
|
+
let { label: l, description: u, errorMessage: d, children: f, className: p, ...m } = a;
|
|
14
|
+
return /* @__PURE__ */ n(o, {
|
|
15
|
+
...m,
|
|
16
|
+
className: [
|
|
17
|
+
c.group,
|
|
18
|
+
e.root,
|
|
19
|
+
p
|
|
20
|
+
].filter(Boolean).join(" "),
|
|
21
|
+
children: [
|
|
22
|
+
l !== void 0 && /* @__PURE__ */ t(i, {
|
|
23
|
+
className: c.legend,
|
|
24
|
+
children: l
|
|
25
|
+
}),
|
|
26
|
+
f,
|
|
27
|
+
u !== void 0 && /* @__PURE__ */ t(s, {
|
|
28
|
+
slot: "description",
|
|
29
|
+
className: e.hint,
|
|
30
|
+
children: u
|
|
31
|
+
}),
|
|
32
|
+
/* @__PURE__ */ t(r, {
|
|
33
|
+
className: e.error,
|
|
34
|
+
children: d
|
|
35
|
+
})
|
|
36
|
+
]
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function u({ children: e, className: r, ...i }) {
|
|
40
|
+
return /* @__PURE__ */ n(a, {
|
|
41
|
+
...i,
|
|
42
|
+
className: [c.radio, r].filter(Boolean).join(" "),
|
|
43
|
+
children: [/* @__PURE__ */ t("span", { className: c.dot }), e !== void 0 && /* @__PURE__ */ t("span", { children: e })]
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { l as n, u as t };
|
|
48
|
+
|
|
49
|
+
//# sourceMappingURL=RadioGroup-vtwjRiUm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RadioGroup-vtwjRiUm.js","names":[],"sources":["../../src/components/RadioGroup/RadioGroup.module.css","../../src/components/RadioGroup/RadioGroup.tsx"],"sourcesContent":[".group {\n display: flex;\n flex-direction: column;\n gap: 6px;\n font-family: var(--wzl-font-ui);\n color: var(--wzl-fg);\n}\n\n.group[data-orientation=\"horizontal\"] {\n flex-direction: row;\n gap: 16px;\n}\n\n.legend {\n font-size: var(--wzl-font-size-sm);\n font-weight: var(--wzl-font-weight-medium);\n color: var(--wzl-fg);\n}\n\n.radio {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n font-size: var(--wzl-font-size);\n color: var(--wzl-fg);\n user-select: none;\n -webkit-user-select: none;\n}\n\n.radio[data-disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.dot {\n position: relative;\n flex: 0 0 auto;\n width: 14px;\n height: 14px;\n background: var(--wzl-surface-sunken);\n border: 1px solid var(--wzl-border-strong);\n border-radius: 50%;\n transition: border-color var(--wzl-motion-fast), box-shadow var(--wzl-motion-fast);\n}\n\n.radio[data-focus-visible] .dot {\n box-shadow: 0 0 0 2px var(--wzl-focus-ring);\n}\n\n.radio[data-hovered]:not([data-disabled]) .dot {\n border-color: var(--wzl-fg-muted);\n}\n\n.radio[data-selected] .dot {\n border-color: var(--wzl-accent-strong);\n}\n\n.dot::after {\n content: '';\n position: absolute;\n inset: 3px;\n border-radius: 50%;\n background: var(--wzl-accent-strong);\n transform: scale(0);\n transition: transform var(--wzl-motion-fast);\n}\n\n.radio[data-selected] .dot::after {\n transform: scale(1);\n}\n","import type { ReactNode } from 'react';\nimport {\n RadioGroup as RACRadioGroup,\n Radio as RACRadio,\n Label,\n Text,\n FieldError,\n type RadioGroupProps as RACRadioGroupProps,\n type RadioProps as RACRadioProps,\n type ValidationResult,\n} from 'react-aria-components';\nimport { fieldClasses } from '../Field/Field';\nimport s from './RadioGroup.module.css';\n\n/** Props for {@link RadioGroup}, on top of React Aria's `RadioGroup` props. */\nexport type RadioGroupProps = Omit<RACRadioGroupProps, 'children' | 'className'> & {\n label?: ReactNode;\n description?: ReactNode;\n errorMessage?: ReactNode | ((v: ValidationResult) => ReactNode);\n children?: ReactNode;\n className?: string;\n};\n\n/**\n * A set of mutually exclusive choices, wrapping React Aria's RadioGroup and\n * supplying the same label / description / errorMessage slots as `Input`.\n * Holds {@link Radio} children.\n */\nexport function RadioGroup(props: RadioGroupProps) {\n const { label, description, errorMessage, children, className, ...rest } = props;\n return (\n <RACRadioGroup\n {...rest}\n className={[s.group, fieldClasses.root, className].filter(Boolean).join(' ')}\n >\n {label !== undefined && <Label className={s.legend}>{label}</Label>}\n {children}\n {description !== undefined && (\n <Text slot=\"description\" className={fieldClasses.hint}>\n {description}\n </Text>\n )}\n <FieldError className={fieldClasses.error}>{errorMessage}</FieldError>\n </RACRadioGroup>\n );\n}\n\n/** Props for {@link Radio}, on top of React Aria's `Radio` props. */\nexport type RadioProps = Omit<RACRadioProps, 'className' | 'children'> & {\n children?: ReactNode;\n className?: string;\n};\n\n/** One choice in a {@link RadioGroup}. The label is passed as children. */\nexport function Radio({ children, className, ...rest }: RadioProps) {\n return (\n <RACRadio {...rest} className={[s.radio, className].filter(Boolean).join(' ')}>\n <span className={s.dot} />\n {children !== undefined && <span>{children}</span>}\n </RACRadio>\n );\n}\n"],"mappings":";;;;;;;;;;;AC4BA,SAAgB,EAAW,GAAwB;CACjD,IAAM,EAAE,UAAO,gBAAa,iBAAc,aAAU,cAAW,GAAG,MAAS;AAC3E,QACE,kBAAC,GAAD;EACE,GAAI;EACJ,WAAW;GAAC,EAAE;GAAO,EAAa;GAAM;GAAU,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;YAF9E;GAIG,MAAU,KAAA,KAAa,kBAAC,GAAD;IAAO,WAAW,EAAE;cAAS;IAAc,CAAA;GAClE;GACA,MAAgB,KAAA,KACf,kBAAC,GAAD;IAAM,MAAK;IAAc,WAAW,EAAa;cAC9C;IACI,CAAA;GAET,kBAAC,GAAD;IAAY,WAAW,EAAa;cAAQ;IAA0B,CAAA;GACxD;;;AAWpB,SAAgB,EAAM,EAAE,aAAU,cAAW,GAAG,KAAoB;AAClE,QACE,kBAAC,GAAD;EAAU,GAAI;EAAM,WAAW,CAAC,EAAE,OAAO,EAAU,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;YAA7E,CACE,kBAAC,QAAD,EAAM,WAAW,EAAE,KAAO,CAAA,EACzB,MAAa,KAAA,KAAa,kBAAC,QAAD,EAAO,aAAgB,CAAA,CACzC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Fragment as e, jsx as t, jsxs as n } from "react/jsx-runtime";
|
|
2
|
+
import { Label as r, Slider as i, SliderOutput as a, SliderThumb as o, SliderTrack as s } from "react-aria-components";
|
|
3
|
+
var c = {
|
|
4
|
+
slider: "_slider_cno0k_1",
|
|
5
|
+
head: "_head_cno0k_17",
|
|
6
|
+
label: "_label_cno0k_24",
|
|
7
|
+
output: "_output_cno0k_30",
|
|
8
|
+
track: "_track_cno0k_36",
|
|
9
|
+
fill: "_fill_cno0k_50",
|
|
10
|
+
thumb: "_thumb_cno0k_59"
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/components/RangeSlider/RangeSlider.tsx
|
|
14
|
+
function l(l) {
|
|
15
|
+
let { label: u, showOutput: d, formatOutput: f, className: p, ...m } = l, h = d ?? u !== void 0;
|
|
16
|
+
return /* @__PURE__ */ n(i, {
|
|
17
|
+
...m,
|
|
18
|
+
className: [c.slider, p].filter(Boolean).join(" "),
|
|
19
|
+
children: [(u !== void 0 || h) && /* @__PURE__ */ n("div", {
|
|
20
|
+
className: c.head,
|
|
21
|
+
children: [u !== void 0 && /* @__PURE__ */ t(r, {
|
|
22
|
+
className: c.label,
|
|
23
|
+
children: u
|
|
24
|
+
}), h && /* @__PURE__ */ t(a, {
|
|
25
|
+
className: c.output,
|
|
26
|
+
children: ({ state: e }) => {
|
|
27
|
+
let t = e.values.length === 1 ? e.values[0] : e.values;
|
|
28
|
+
return f ? f(t) : Array.isArray(t) ? t.join(" – ") : String(t);
|
|
29
|
+
}
|
|
30
|
+
})]
|
|
31
|
+
}), /* @__PURE__ */ t(s, {
|
|
32
|
+
className: c.track,
|
|
33
|
+
children: ({ state: r }) => {
|
|
34
|
+
let i = r.values.length > 1 ? r.getThumbPercent(0) * 100 : 0, a = r.getThumbPercent(r.values.length - 1) * 100;
|
|
35
|
+
return /* @__PURE__ */ n(e, { children: [/* @__PURE__ */ t("div", {
|
|
36
|
+
className: c.fill,
|
|
37
|
+
style: {
|
|
38
|
+
left: `${i}%`,
|
|
39
|
+
right: `${100 - a}%`
|
|
40
|
+
}
|
|
41
|
+
}), r.values.map((e, n) => /* @__PURE__ */ t(o, {
|
|
42
|
+
index: n,
|
|
43
|
+
className: c.thumb
|
|
44
|
+
}, n))] });
|
|
45
|
+
}
|
|
46
|
+
})]
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
export { l as t };
|
|
51
|
+
|
|
52
|
+
//# sourceMappingURL=RangeSlider-B-n_7AgJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RangeSlider-B-n_7AgJ.js","names":[],"sources":["../../src/components/RangeSlider/RangeSlider.module.css","../../src/components/RangeSlider/RangeSlider.tsx"],"sourcesContent":[".slider {\n display: flex;\n flex-direction: column;\n width: 100%;\n gap: 4px;\n font-family: var(--wzl-font-ui);\n color: var(--wzl-fg);\n}\n\n.slider[data-orientation=\"vertical\"] {\n flex-direction: row;\n align-items: stretch;\n width: 28px;\n height: 160px;\n}\n\n.head {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 8px;\n}\n\n.label {\n font-size: var(--wzl-font-size-sm);\n font-weight: var(--wzl-font-weight-medium);\n color: var(--wzl-fg);\n}\n\n.output {\n font-size: var(--wzl-font-size-sm);\n color: var(--wzl-fg-subtle);\n font-variant-numeric: tabular-nums;\n}\n\n.track {\n position: relative;\n height: 6px;\n min-width: 80px;\n background: var(--wzl-surface-sunken);\n border: 1px solid var(--wzl-border);\n border-radius: var(--wzl-radius-sm);\n}\n\n.slider[data-orientation=\"vertical\"] .track {\n width: 6px;\n height: 100%;\n}\n\n.fill {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n background: var(--wzl-accent-strong);\n border-radius: var(--wzl-radius-sm);\n}\n\n.thumb {\n width: 14px;\n height: 14px;\n background: var(--wzl-fg-muted);\n border: 1px solid var(--wzl-border-strong);\n border-radius: 50%;\n cursor: ew-resize;\n outline: none;\n transition: box-shadow var(--wzl-motion-fast);\n}\n\n.slider[data-orientation=\"vertical\"] .thumb {\n cursor: ns-resize;\n}\n\n.thumb[data-focus-visible] {\n box-shadow: 0 0 0 2px var(--wzl-focus-ring);\n}\n\n.thumb[data-dragging] {\n background: var(--wzl-accent-strong);\n}\n\n.slider[data-disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n","import type { ReactNode } from 'react';\nimport {\n Slider as RACSlider,\n SliderOutput,\n SliderTrack,\n SliderThumb,\n Label,\n type SliderProps as RACSliderProps,\n} from 'react-aria-components';\nimport s from './RangeSlider.module.css';\n\n/** Props for {@link RangeSlider}, on top of React Aria's `Slider` props. */\nexport type RangeSliderProps = Omit<RACSliderProps, 'children' | 'className'> & {\n label?: ReactNode;\n /** Show the current value next to the label. Defaults to true when a\n * label is supplied. */\n showOutput?: boolean;\n /** Format the rendered value(s). Useful for units (`v => `${v}%``). */\n formatOutput?: (v: number | number[]) => ReactNode;\n className?: string;\n};\n\n/**\n * Linear range slider on React Aria. Single-value by default; pass an array\n * to `value` / `defaultValue` for multi-thumb. Distinct from the kit's\n * canvas-scrub `Slider`, which is shaped for tool-canvas interactions.\n */\nexport function RangeSlider(props: RangeSliderProps) {\n const { label, showOutput, formatOutput, className, ...rest } = props;\n const wantOutput = showOutput ?? label !== undefined;\n return (\n <RACSlider {...rest} className={[s.slider, className].filter(Boolean).join(' ')}>\n {(label !== undefined || wantOutput) && (\n <div className={s.head}>\n {label !== undefined && <Label className={s.label}>{label}</Label>}\n {wantOutput && (\n <SliderOutput className={s.output}>\n {({ state }) => {\n const v = state.values.length === 1 ? state.values[0] : state.values;\n return formatOutput ? formatOutput(v) : Array.isArray(v) ? v.join(' – ') : String(v);\n }}\n </SliderOutput>\n )}\n </div>\n )}\n <SliderTrack className={s.track}>\n {({ state }) => {\n // Single-thumb: fill from start to thumb. Multi-thumb: fill\n // between adjacent thumbs (simplest = between first and last).\n const fillStart = state.values.length > 1 ? state.getThumbPercent(0) * 100 : 0;\n const fillEnd = state.getThumbPercent(state.values.length - 1) * 100;\n return (\n <>\n <div\n className={s.fill}\n style={{ left: `${fillStart}%`, right: `${100 - fillEnd}%` }}\n />\n {state.values.map((_, i) => (\n <SliderThumb key={i} index={i} className={s.thumb} />\n ))}\n </>\n );\n }}\n </SliderTrack>\n </RACSlider>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AC2BA,SAAgB,EAAY,GAAyB;CACnD,IAAM,EAAE,UAAO,eAAY,iBAAc,cAAW,GAAG,MAAS,GAC1D,IAAa,KAAc,MAAU,KAAA;AAC3C,QACE,kBAAC,GAAD;EAAW,GAAI;EAAM,WAAW,CAAC,EAAE,QAAQ,EAAU,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;YAA/E,EACI,MAAU,KAAA,KAAa,MACvB,kBAAC,OAAD;GAAK,WAAW,EAAE;aAAlB,CACG,MAAU,KAAA,KAAa,kBAAC,GAAD;IAAO,WAAW,EAAE;cAAQ;IAAc,CAAA,EACjE,KACC,kBAAC,GAAD;IAAc,WAAW,EAAE;eACvB,EAAE,eAAY;KACd,IAAM,IAAI,EAAM,OAAO,WAAW,IAAI,EAAM,OAAO,KAAK,EAAM;AAC9D,YAAO,IAAe,EAAa,EAAE,GAAG,MAAM,QAAQ,EAAE,GAAG,EAAE,KAAK,MAAM,GAAG,OAAO,EAAE;;IAEzE,CAAA,CAEb;MAER,kBAAC,GAAD;GAAa,WAAW,EAAE;cACtB,EAAE,eAAY;IAGd,IAAM,IAAY,EAAM,OAAO,SAAS,IAAI,EAAM,gBAAgB,EAAE,GAAG,MAAM,GACvE,IAAU,EAAM,gBAAgB,EAAM,OAAO,SAAS,EAAE,GAAG;AACjE,WACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;KACE,WAAW,EAAE;KACb,OAAO;MAAE,MAAM,GAAG,EAAU;MAAI,OAAO,GAAG,MAAM,EAAQ;MAAI;KAC5D,CAAA,EACD,EAAM,OAAO,KAAK,GAAG,MACpB,kBAAC,GAAD;KAAqB,OAAO;KAAG,WAAW,EAAE;KAAS,EAAnC,EAAmC,CACrD,CACD,EAAA,CAAA;;GAGK,CAAA,CACJ"}
|