@owlmeans/web-panel 0.1.18-rc.74 → 0.1.18-rc.76

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 (177) hide show
  1. package/README.md +89 -7
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/web-panel/SKILL.md +207 -7
  4. package/build/@/components/ui/popover.d.ts +8 -0
  5. package/build/@/components/ui/popover.d.ts.map +1 -0
  6. package/build/@/components/ui/popover.js +17 -0
  7. package/build/@/components/ui/popover.js.map +1 -0
  8. package/build/components/picker/color-picker.d.ts +9 -0
  9. package/build/components/picker/color-picker.d.ts.map +1 -0
  10. package/build/components/picker/color-picker.js +9 -0
  11. package/build/components/picker/color-picker.js.map +1 -0
  12. package/build/components/picker/consts.local.d.ts +76 -0
  13. package/build/components/picker/consts.local.d.ts.map +1 -0
  14. package/build/components/picker/consts.local.js +213 -0
  15. package/build/components/picker/consts.local.js.map +1 -0
  16. package/build/components/picker/dot.d.ts +8 -0
  17. package/build/components/picker/dot.d.ts.map +1 -0
  18. package/build/components/picker/dot.js +10 -0
  19. package/build/components/picker/dot.js.map +1 -0
  20. package/build/components/picker/grid.d.ts +17 -0
  21. package/build/components/picker/grid.d.ts.map +1 -0
  22. package/build/components/picker/grid.js +73 -0
  23. package/build/components/picker/grid.js.map +1 -0
  24. package/build/components/picker/i18n/be.json +60 -0
  25. package/build/components/picker/i18n/de.json +60 -0
  26. package/build/components/picker/i18n/en.json +60 -0
  27. package/build/components/picker/i18n/es.json +60 -0
  28. package/build/components/picker/i18n/fr.json +60 -0
  29. package/build/components/picker/i18n/pl.json +60 -0
  30. package/build/components/picker/i18n/ru.json +60 -0
  31. package/build/components/picker/i18n/uk.json +60 -0
  32. package/build/components/picker/i18n.d.ts +2 -0
  33. package/build/components/picker/i18n.d.ts.map +1 -0
  34. package/build/components/picker/i18n.js +18 -0
  35. package/build/components/picker/i18n.js.map +1 -0
  36. package/build/components/picker/icon-color-picker.d.ts +14 -0
  37. package/build/components/picker/icon-color-picker.d.ts.map +1 -0
  38. package/build/components/picker/icon-color-picker.js +15 -0
  39. package/build/components/picker/icon-color-picker.js.map +1 -0
  40. package/build/components/picker/icon-picker.d.ts +8 -0
  41. package/build/components/picker/icon-picker.d.ts.map +1 -0
  42. package/build/components/picker/icon-picker.js +8 -0
  43. package/build/components/picker/icon-picker.js.map +1 -0
  44. package/build/components/picker/index.d.ts +11 -0
  45. package/build/components/picker/index.d.ts.map +1 -0
  46. package/build/components/picker/index.js +10 -0
  47. package/build/components/picker/index.js.map +1 -0
  48. package/build/components/picker/panel.d.ts +9 -0
  49. package/build/components/picker/panel.d.ts.map +1 -0
  50. package/build/components/picker/panel.js +23 -0
  51. package/build/components/picker/panel.js.map +1 -0
  52. package/build/components/picker/root.d.ts +13 -0
  53. package/build/components/picker/root.d.ts.map +1 -0
  54. package/build/components/picker/root.js +55 -0
  55. package/build/components/picker/root.js.map +1 -0
  56. package/build/components/picker/roving.d.ts +8 -0
  57. package/build/components/picker/roving.d.ts.map +1 -0
  58. package/build/components/picker/roving.js +62 -0
  59. package/build/components/picker/roving.js.map +1 -0
  60. package/build/components/picker/surface.d.ts +18 -0
  61. package/build/components/picker/surface.d.ts.map +1 -0
  62. package/build/components/picker/surface.js +36 -0
  63. package/build/components/picker/surface.js.map +1 -0
  64. package/build/components/picker/swatches/custom.d.ts +10 -0
  65. package/build/components/picker/swatches/custom.d.ts.map +1 -0
  66. package/build/components/picker/swatches/custom.js +65 -0
  67. package/build/components/picker/swatches/custom.js.map +1 -0
  68. package/build/components/picker/swatches.d.ts +16 -0
  69. package/build/components/picker/swatches.d.ts.map +1 -0
  70. package/build/components/picker/swatches.js +35 -0
  71. package/build/components/picker/swatches.js.map +1 -0
  72. package/build/components/picker/text.d.ts +5 -0
  73. package/build/components/picker/text.d.ts.map +1 -0
  74. package/build/components/picker/text.js +29 -0
  75. package/build/components/picker/text.js.map +1 -0
  76. package/build/components/picker/tile.d.ts +11 -0
  77. package/build/components/picker/tile.d.ts.map +1 -0
  78. package/build/components/picker/tile.js +19 -0
  79. package/build/components/picker/tile.js.map +1 -0
  80. package/build/components/picker/translate.d.ts +9 -0
  81. package/build/components/picker/translate.d.ts.map +1 -0
  82. package/build/components/picker/translate.js +13 -0
  83. package/build/components/picker/translate.js.map +1 -0
  84. package/build/components/picker/trigger.d.ts +12 -0
  85. package/build/components/picker/trigger.d.ts.map +1 -0
  86. package/build/components/picker/trigger.js +29 -0
  87. package/build/components/picker/trigger.js.map +1 -0
  88. package/build/components/picker/types.d.ts +114 -0
  89. package/build/components/picker/types.d.ts.map +1 -0
  90. package/build/components/picker/types.js +2 -0
  91. package/build/components/picker/types.js.map +1 -0
  92. package/build/components/picker/types.local.d.ts +143 -0
  93. package/build/components/picker/types.local.d.ts.map +1 -0
  94. package/build/components/picker/types.local.js +2 -0
  95. package/build/components/picker/types.local.js.map +1 -0
  96. package/build/picker/icons/consts.d.ts +9 -0
  97. package/build/picker/icons/consts.d.ts.map +1 -0
  98. package/build/picker/icons/consts.js +264 -0
  99. package/build/picker/icons/consts.js.map +1 -0
  100. package/build/picker/icons/types.d.ts +18 -0
  101. package/build/picker/icons/types.d.ts.map +1 -0
  102. package/build/picker/icons/types.js +2 -0
  103. package/build/picker/icons/types.js.map +1 -0
  104. package/build/picker/icons.d.ts +4 -0
  105. package/build/picker/icons.d.ts.map +1 -0
  106. package/build/picker/icons.js +54 -0
  107. package/build/picker/icons.js.map +1 -0
  108. package/build/picker/index.d.ts +11 -0
  109. package/build/picker/index.d.ts.map +1 -0
  110. package/build/picker/index.js +10 -0
  111. package/build/picker/index.js.map +1 -0
  112. package/build/picker/utils/color/types.d.ts +35 -0
  113. package/build/picker/utils/color/types.d.ts.map +1 -0
  114. package/build/picker/utils/color/types.js +2 -0
  115. package/build/picker/utils/color/types.js.map +1 -0
  116. package/build/picker/utils/color.d.ts +4 -0
  117. package/build/picker/utils/color.d.ts.map +1 -0
  118. package/build/picker/utils/color.js +96 -0
  119. package/build/picker/utils/color.js.map +1 -0
  120. package/build/picker/vocabulary/consts.d.ts +26 -0
  121. package/build/picker/vocabulary/consts.d.ts.map +1 -0
  122. package/build/picker/vocabulary/consts.js +336 -0
  123. package/build/picker/vocabulary/consts.js.map +1 -0
  124. package/build/picker/vocabulary/types.d.ts +67 -0
  125. package/build/picker/vocabulary/types.d.ts.map +1 -0
  126. package/build/picker/vocabulary/types.js +2 -0
  127. package/build/picker/vocabulary/types.js.map +1 -0
  128. package/build/picker/vocabulary.d.ts +11 -0
  129. package/build/picker/vocabulary.d.ts.map +1 -0
  130. package/build/picker/vocabulary.js +83 -0
  131. package/build/picker/vocabulary.js.map +1 -0
  132. package/package.json +37 -20
  133. package/src/@/components/ui/popover.tsx +47 -0
  134. package/src/components/picker/color-picker.tsx +15 -0
  135. package/src/components/picker/consts.local.ts +266 -0
  136. package/src/components/picker/dot.tsx +21 -0
  137. package/src/components/picker/grid.tsx +138 -0
  138. package/src/components/picker/i18n/be.json +60 -0
  139. package/src/components/picker/i18n/de.json +60 -0
  140. package/src/components/picker/i18n/en.json +60 -0
  141. package/src/components/picker/i18n/es.json +60 -0
  142. package/src/components/picker/i18n/fr.json +60 -0
  143. package/src/components/picker/i18n/pl.json +60 -0
  144. package/src/components/picker/i18n/ru.json +60 -0
  145. package/src/components/picker/i18n/uk.json +60 -0
  146. package/src/components/picker/i18n.ts +19 -0
  147. package/src/components/picker/icon-color-picker.tsx +25 -0
  148. package/src/components/picker/icon-picker.tsx +13 -0
  149. package/src/components/picker/index.ts +11 -0
  150. package/src/components/picker/panel.tsx +61 -0
  151. package/src/components/picker/root.tsx +81 -0
  152. package/src/components/picker/roving.ts +70 -0
  153. package/src/components/picker/surface.tsx +62 -0
  154. package/src/components/picker/swatches/custom.tsx +110 -0
  155. package/src/components/picker/swatches.tsx +67 -0
  156. package/src/components/picker/text.ts +46 -0
  157. package/src/components/picker/tile.tsx +35 -0
  158. package/src/components/picker/translate.ts +15 -0
  159. package/src/components/picker/trigger.tsx +54 -0
  160. package/src/components/picker/types.local.ts +154 -0
  161. package/src/components/picker/types.ts +125 -0
  162. package/src/picker/icons/consts.ts +309 -0
  163. package/src/picker/icons/types.ts +18 -0
  164. package/src/picker/icons.ts +70 -0
  165. package/src/picker/index.ts +11 -0
  166. package/src/picker/utils/color/types.ts +37 -0
  167. package/src/picker/utils/color.ts +119 -0
  168. package/src/picker/vocabulary/consts.ts +347 -0
  169. package/src/picker/vocabulary/types.ts +72 -0
  170. package/src/picker/vocabulary.ts +111 -0
  171. package/tests/context.ts +1 -1
  172. package/tests/harness/index.html +1 -0
  173. package/tests/harness/mount.tsx +13 -2
  174. package/tests/harness/picker.tsx +110 -0
  175. package/tests/picker-shots.spec.ts +174 -0
  176. package/tests/picker-vocabulary.spec.ts +217 -0
  177. package/tests/picker.spec.ts +333 -0
package/README.md CHANGED
@@ -11,14 +11,14 @@ but never started.
11
11
  ## Installation
12
12
 
13
13
  ```sh
14
- bun add @owlmeans/web-panel@^0.1.18-rc.74
14
+ bun add @owlmeans/web-panel@^0.1.18-rc.76
15
15
  ```
16
16
 
17
17
  Peer requirements (the consuming app provides these): `react`, `react-dom`,
18
18
  `react-hook-form`, `ajv`, `tailwindcss@^4`, `lucide-react`, `clsx`,
19
19
  `tailwind-merge`, `class-variance-authority`, plus the radix primitives
20
- listed in `peerDependencies` (`alert-dialog`, `dialog`, `label`, `navigation-menu`, `progress`,
21
- `separator`, `slot`). Also declare `ajv-formats` next to `ajv`: the form model imports it, but no dependency
20
+ listed in `peerDependencies` (`alert-dialog`, `dialog`, `label`, `navigation-menu`, `popover`,
21
+ `progress`, `separator`, `slot` — keep `dialog` and `popover` on the same Radix generation). Also declare `ajv-formats` next to `ajv`: the form model imports it, but no dependency
22
22
  section of this manifest lists it.
23
23
 
24
24
  ## Concepts
@@ -38,8 +38,12 @@ section of this manifest lists it.
38
38
  with tailwind-merge, never substituted.
39
39
  - **Global overlays** — `PanelApp` renders its `children` and `SocketReloadDialog` beside the
40
40
  router, so a dialog survives navigation.
41
- - **`translate` props** — the navigation and login components take a
41
+ - **`translate` props** — the navigation, login and picker components take a
42
42
  `(key, defaultValue) => string` resolver as a prop instead of reading an i18n provider implicitly.
43
+ - **Icon + colour picker** — `@owlmeans/web-panel/picker` is the standard way a person chooses an
44
+ icon and/or a colour (24 palette keys, ~250 curated lucide icons, an opt-in custom colour) and the
45
+ way a chosen mark is rendered (`IconTile`, `ColorDot`). A popover on wide screens, a bottom sheet
46
+ on phones; its data and colour helper are React-free under `./picker/vocabulary`.
43
47
 
44
48
  ## Usage
45
49
 
@@ -60,8 +64,8 @@ The components paint with these CSS variables, defined inside `@theme` in the ap
60
64
  - `--animate-progress-indeterminate` + the `@keyframes progress-indeterminate`
61
65
  rule for the `Progress` component's indeterminate mode
62
66
 
63
- A working set is shipped at `src/@/globals.css` (for dev/test only). Toasts additionally read
64
- `--popover`, `--popover-foreground` and `--border`.
67
+ A working set is shipped at `src/@/globals.css` (for dev/test only). Toasts and the picker's
68
+ surfaces additionally read `--popover`, `--popover-foreground` and `--border`.
65
69
 
66
70
  Tailwind's scanner reads your CSS root plus its `@source` directives, and it excludes
67
71
  `node_modules`. Classes that exist only inside this package's components — the whole navigation
@@ -316,6 +320,36 @@ export const ConnectionBadge: FC = () => {
316
320
  The dialog's strings are the lib-tier `socket` namespace (`reload.title`, `reload.description`,
317
321
  `reload.action`), shipped in seven languages.
318
322
 
323
+ ### 6. Icon and colour picker
324
+
325
+ A person choosing a type's mark — an icon and a colour — gets one control; the stored values are a
326
+ palette key (or `#rrggbb` with `customColors`) and a kebab-case lucide name:
327
+
328
+ ```tsx
329
+ import { useState, type FC } from 'react'
330
+ import { IconColorPicker, IconTile, usePickerTranslate } from '@owlmeans/web-panel/picker'
331
+ import type { IconColorChange } from '@owlmeans/web-panel/picker'
332
+
333
+ export const TypeMarkField: FC<{ initial: IconColorChange, onSave: (mark: IconColorChange) => void }> =
334
+ ({ initial, onSave }) => {
335
+ const translate = usePickerTranslate() // only under PanelApp / render()
336
+ const [mark, setMark] = useState(initial)
337
+
338
+ return <div className="flex items-center gap-3">
339
+ <IconColorPicker label="Mark" value={mark} onChange={setMark} translate={translate} />
340
+ <button type="button" onClick={() => onSave(mark)}>Save</button>
341
+ <IconTile icon={mark.icon} color={mark.color} size="sm" /> {/* how a list shows it */}
342
+ </div>
343
+ }
344
+ ```
345
+
346
+ `ColorPicker` and `IconPicker` edit one half; `ColorSwatches` and `IconGrid` are the same grids
347
+ inline, for a dialog the app already owns. Stored marks render with `IconTile` / `ColorDot`, or with
348
+ `pickerColorHelper.styleOf(color)` in custom markup. From 768px up the picker opens as a popover
349
+ (modal, so it works inside an app's own Dialog or Sheet), below that as a bottom sheet with 44px
350
+ targets and a Done button. Without `translate` every string is English; the packaged bundle covers
351
+ eight languages.
352
+
319
353
  ## API
320
354
 
321
355
  ### Context and mounting
@@ -423,6 +457,37 @@ consumer's CSS puts dark tokens under `.dark` AND under
423
457
  | `ConsentWidgetPresenceRecord`, `ConsentWidgetService`, `ConsentWidgetServiceAppend` | type | Service types |
424
458
  | `useConsent`, `useConsentCategory`, `consentStore`, `openConsent`, `isConsented`, `readConsent`, `writeConsent`, `clearConsent`, `consentBootstrapScript`, `DEFAULT_CONSENT_CATEGORIES`, `CONSENT_KEY`, `CONSENT_ESSENTIAL`, `CONSENT_ANALYTICS`, `CONSENT_MARKETING` and the consent types | re-export | `@owlmeans/web-consent` |
425
459
 
460
+ ### `@owlmeans/web-panel/picker`
461
+
462
+ Not re-exported by the root. Re-exports everything of `./picker/vocabulary` too.
463
+
464
+ | Symbol | Kind | Purpose |
465
+ |---|---|---|
466
+ | `IconColorPicker`, `IconColorPickerProps` | component, type | Icon + colour: `value?: { icon?, color? }`, `onChange({ icon?, color? })`, `colors?`, `icons?`, `customColors?`, `trigger?: 'button' \| 'tile'` |
467
+ | `ColorPicker`, `ColorPickerProps` | component, type | A colour: `value?`, `onChange(color)`, `colors?`, `customColors?`, `trigger?: 'button' \| 'dot'` |
468
+ | `IconPicker`, `IconPickerProps` | component, type | An icon: `value?`, `onChange(icon)`, `color?` (tint), `icons?`, `trigger?: 'button' \| 'tile'` |
469
+ | `PickerBaseProps` | type | Shared: `label?`, `placeholder?`, `disabled?`, `surface?: 'auto' \| 'popover' \| 'sheet'`, `translate?`, `className?`, `id?` |
470
+ | `ColorSwatches`, `IconGrid` (+ `…Props`) | component, type | The grids inline, for an app's own dialog or sheet |
471
+ | `IconTile`, `IconTileProps` | component, type | A stored mark: `icon?`, `color?`, `colors?`, `size?: 'xs' \| 'sm' \| 'md' \| 'lg'`, `variant?: 'tile' \| 'plain'`, `label?` |
472
+ | `ColorDot`, `ColorDotProps` | component, type | A stored colour as a dot in its ink: `color?`, `colors?`, `size?`, `label?` |
473
+ | `usePickerTranslate()`, `PickerTranslate` | hook, type | `(key, fallback) => string` over the lib-tier `picker` bundle — only under `PanelApp` / `render()` |
474
+ | `IconColorValue`, `IconColorChange` | type | The composite's value and its change (`undefined`, never `null`, for an unset half) |
475
+ | `PICKER_ICONS` | const | Catalogue name → statically imported lucide component |
476
+ | `pickerIconHelper` (`iconOf`, `has`, `search`, `byCategory`), `createPickerIconHelper`, `PickerIconHelper` | helper, type | Icon lookup with the `circle` fallback and catalogue search |
477
+
478
+ ### `@owlmeans/web-panel/picker/vocabulary`
479
+
480
+ React-free and icon-free — safe on a server or in a seed.
481
+
482
+ | Symbol | Kind | Purpose |
483
+ |---|---|---|
484
+ | `PICKER_COLOR_KEYS`, `PickerColorKey` | const, type | The 24 keys in hue order: red … rose, maroon, brown, olive, navy, slate, stone, graphite |
485
+ | `PICKER_COLORS`, `PickerColor` | const, type | `{ key, swatch, ink, soft, inkDark?, softDark?, label? }` — every ink 4.5:1 on its ground and the page in both modes |
486
+ | `PICKER_FALLBACK_COLOR`, `PICKER_FALLBACK_ICON` | const | `'slate'`, `'circle'` |
487
+ | `PICKER_ICON_ENTRIES`, `PICKER_ICON_NAMES`, `PICKER_ICON_CATEGORIES` | const | The catalogue: `{ name, category, keywords }`, its names, its ten categories |
488
+ | `PickerIconEntry`, `PickerIconCategory`, `ResolvedPickerColor` | type | |
489
+ | `pickerColorHelper` (`isHex`, `normalizeHex`, `resolve`, `styleOf`, `hashKey`), `createPickerColorHelper`, `PickerColorHelper` | helper, type | Validate, normalize and resolve a stored colour (a hex gets contrast-safe inks); `styleOf` → the `--pick-*` custom properties the parts read |
490
+
426
491
  ### Differences from `@owlmeans/mui-panel`
427
492
 
428
493
  The public names match the MUI implementation; these props and types differ:
@@ -478,6 +543,14 @@ The public names match the MUI implementation; these props and types differ:
478
543
  - **Never import `@/…` from package code or vendor its primitives in the app.** The `@` alias
479
544
  belongs to the consumer.
480
545
  - **Every override of a packaged string covers all seven languages** (`SUPPORTED_LNGS`).
546
+ - **A person-chosen colour or icon goes through `./picker`.** Never a `Select` or radio group of
547
+ colour names, a hand-made swatch row, or a class built from the stored value
548
+ (`` `text-${color}-600` ``); store the key and the icon name, never the label.
549
+ - **Keep `@radix-ui/react-popover` and `@radix-ui/react-dialog` on one Radix generation.** They
550
+ share the dismissable-layer and focus-scope stacks; mixed, a picker inside a Dialog loses focus
551
+ to the dialog and its clicks count as outside.
552
+ - **`usePickerTranslate()` needs `PanelApp` / `render()`.** Without an i18n instance it throws;
553
+ the picker parts themselves render English with no `translate`.
481
554
 
482
555
  ## Related packages
483
556
 
@@ -490,6 +563,15 @@ The public names match the MUI implementation; these props and types differ:
490
563
  - [`@owlmeans/web-router`](../web-router) — default OwlMeans routing plugin
491
564
  - [`@owlmeans/mui-panel`](../mui-panel) — legacy Material UI implementation of the same surface
492
565
 
566
+ ## Notes for coding agents
567
+
568
+ - Read the embedded `web-panel` skill first: its *Choosing a component* table maps every job to the
569
+ export, the import path and a correct one-liner, and lists the mistakes that recur in generated
570
+ code (private primitives, a missing `@source`, two `Toaster`s, `Form decorate` with an own submit).
571
+ - An icon or a colour a person chooses is edited with `@owlmeans/web-panel/picker`
572
+ (`IconColorPicker`, `ColorPicker`, `IconPicker`) and shown with `IconTile` / `ColorDot`; pass
573
+ `translate={usePickerTranslate()}` in an app rendered by `PanelApp`.
574
+
493
575
  <!-- owlmeans:agent-guidance:start -->
494
576
  ## Agent guidance
495
577
 
@@ -498,7 +580,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
498
580
  your project's skill store (`.agents/skills/`):
499
581
 
500
582
  ```sh
501
- npx @owlmeans/agent-skills@^0.1.18-rc.53
583
+ npx @owlmeans/agent-skills@^0.1.18-rc.54
502
584
  ```
503
585
 
504
586
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@owlmeans/web-panel",
4
- "version": "0.1.18-rc.74",
5
- "generatedAt": "2026-10-09T11:30:42.761Z",
4
+ "version": "0.1.18-rc.76",
5
+ "generatedAt": "2026-10-16T18:05:19.013Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: web-panel
3
- description: How to use @owlmeans/web-panel — base browser context factory (makeContext) with shadcn/Radix + Tailwind and the default OwlMeans router wired in, plus the two-layer navigation shell (NavLayout/TopNav/SideNav/Footer) and form/panel components. Auto-invoked when building a web app's makeContext, wiring its navigation or layout, or importing web panel components.
3
+ description: How to use @owlmeans/web-panel — base browser context factory (makeContext) with shadcn/Radix + Tailwind and the default OwlMeans router wired in, the two-layer navigation shell (NavLayout/TopNav/SideNav/Footer), form/panel components, and the standard icon + colour picker (`./picker`: IconColorPicker, ColorPicker, IconPicker, IconTile, ColorDot). Auto-invoked when building a web app's makeContext, wiring its navigation or layout, importing web panel components, or letting a person choose (or showing) an icon or a colour.
4
4
  user-invocable: false
5
5
  ---
6
6
  <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
@@ -8,7 +8,54 @@ user-invocable: false
8
8
  # @owlmeans/web-panel
9
9
 
10
10
  **Layer:** Web (React)
11
- **Install:** `"@owlmeans/web-panel": "^0.1.18-rc.74"` in `dependencies`
11
+ **Install:** `"@owlmeans/web-panel": "^0.1.18-rc.76"` in `dependencies`
12
+
13
+ ## Choosing a component
14
+
15
+ Pick the row for the job; the one-liner is the correct shape. Everything comes from the package
16
+ root unless the import column names a subpath.
17
+
18
+ | Job | Use | Import | Correct one-liner |
19
+ |---|---|---|---|
20
+ | The app's context | `makeContext` | root | `const context = makeContext<C, T>(cfg)` in `context.ts`, then `append*(context)` mixins |
21
+ | Mount the app | `render` (or `PanelApp` for a host that mounts itself) | root | `render(context)` |
22
+ | The application shell | `NavLayout` | root | `<NavLayout nav={navConfig} title="App" mobileMenu themeToggle>{children}</NavLayout>` |
23
+ | One piece of the shell | `TopNav` / `SideNav` / `MobileNav` / `Footer` / `ShellCredit` | root | `<Footer links={footerLinks} />` |
24
+ | Toasts | `Toaster` + `toast` from `sonner` | root | `<Toaster />` once, in the layout; `toast.success('Saved')` where the action ends |
25
+ | Light / dark switch | `ThemeToggle` / `useColorScheme`; head script from `./scheme` | root, `./scheme` | `<NavLayout themeToggle … />` |
26
+ | A link to a screen | `Link` | root | `<Link module={web.about} />` |
27
+ | A form | `Form`, `TextInput`, `SubmitButton`, `Button`, `ButtonSelector` | root | `<Form decorate validation={schema} onSubmit={save}><TextInput name="email" label /></Form>` |
28
+ | A panel, a heading, an error | `Block`, `Text`, `Status` | root | `<Status error={error} />` |
29
+ | An image upload | `ImageUploader` (react-dropzone options) | root | `<ImageUploader accept={{ 'image/*': [] }} onDrop={files => upload(files[0])} previewUrl={url} />` |
30
+ | "Connection lost" prompt | `SocketReloadDialog` (mounted by `PanelApp`) | root | `cfg.socket = { reloadDialog: true }` |
31
+ | The sign-in screen | `LoginScreen` / `LocalizedLoginScreen` / `appendLoginScreen` | root | `appendLoginScreen(context, { Logo })` |
32
+ | Cookie consent | `PanelCookieConsent`, `PanelConsentMenuWidget` | `./consent` | `<PanelCookieConsent />` as a `PanelApp` child |
33
+ | A person picks an icon AND a colour (a type's, a flow's, a label's mark) | `IconColorPicker` | `./picker` | `<IconColorPicker label="Mark" value={{ icon, color }} onChange={next => …} translate={usePickerTranslate()} />` |
34
+ | A person picks a colour only | `ColorPicker` | `./picker` | `<ColorPicker label="Colour" value={color} onChange={setColor} />` |
35
+ | A person picks an icon only | `IconPicker` | `./picker` | `<IconPicker label="Icon" value={icon} onChange={setIcon} color={color} />` |
36
+ | The same, inline in a dialog the app already owns | `ColorSwatches`, `IconGrid` | `./picker` | `<IconGrid value={icon} onChange={setIcon} color={color} />` |
37
+ | Show a chosen mark or colour (list, chip, header) | `IconTile`, `ColorDot` | `./picker` | `<IconTile icon={type.icon} color={type.tone} size="sm" />` |
38
+ | Palette, icon names, colour maths — no React | `PICKER_*`, `pickerColorHelper`, `pickerIconHelper` | `./picker/vocabulary` (`pickerIconHelper`, `PICKER_ICONS`: `./picker`) | `pickerColorHelper.styleOf(color)` |
39
+ | The current breakpoint | `useBreakPoint` / `useMapBreakpoint` | root | `useMapBreakpoint({ xs: 1, md: 2 }, 3)` |
40
+
41
+ **Common mistakes:**
42
+
43
+ - **Importing the package's private primitives.** `src/@/components/ui/*` (button, sheet, popover …)
44
+ and its `cn` are not exports; an app keeps its own shadcn components and never imports
45
+ `@owlmeans/web-panel/build/@/…` or expects `@/components/ui` to resolve to this package's copy.
46
+ - **A missing `@source`.** The app's Tailwind entry must scan this package's `src`
47
+ (`@source "../../../node_modules/@owlmeans/web-panel/src";`) or every class used only here — the
48
+ shell, the footer, the picker — silently never reaches the stylesheet.
49
+ - **Two `Toaster`s.** Mount it once, in the layout: two render every toast twice.
50
+ - **`Form decorate` plus an own submit button.** `decorate` already renders the `Card`, the root
51
+ error and a `SubmitButton` whenever `onSubmit` is given; without `decorate` there is no submit
52
+ button at all and the caller renders one.
53
+ - **A `Select`, radio group or hand-made swatch row for a colour or an icon a person chooses, or
54
+ `` `text-${color}-600` `` / `` `text-tone-${…}` `` built from a stored value.** A person-chosen colour
55
+ or icon is always edited with the `./picker` parts and rendered with `IconTile` / `ColorDot` (or
56
+ `pickerColorHelper.styleOf`); code-chosen tones (a status's meaning) stay the app's own classes.
57
+ - **`usePickerTranslate()` outside `PanelApp` / `render()`.** It needs the i18n instance and throws
58
+ without one; the picker parts themselves never read i18n (pass `translate`, or get English).
12
59
 
13
60
  ## Key Exports
14
61
 
@@ -47,6 +94,15 @@ user-invocable: false
47
94
  - `./scheme` — the React-free colour-scheme half: `COLOR_SCHEME_KEY`, `COLOR_SCHEME_EVENT`,
48
95
  `ColorSchemeChoice`, `colorSchemeHelper` (`readColorScheme`, `applyColorScheme`, `colorSchemeBootstrapScript`) — safe
49
96
  to import from a Node build script
97
+ - `./picker` — the standard icon + colour picker and its parts (`IconColorPicker`, `ColorPicker`,
98
+ `IconPicker`, `ColorSwatches`, `IconGrid`, `IconTile`, `ColorDot`, `usePickerTranslate`), the icon
99
+ components (`PICKER_ICONS`, `pickerIconHelper`) and everything of `./picker/vocabulary` — see
100
+ *Subpath: `./picker`* below. Not re-exported by the root, so only a bundle importing it carries
101
+ the icon catalogue
102
+ - `./picker/vocabulary` — the React-free half: the 24-colour palette (`PICKER_COLORS`,
103
+ `PICKER_COLOR_KEYS`), the icon catalogue's names, categories and search words
104
+ (`PICKER_ICON_ENTRIES`, `PICKER_ICON_NAMES`, `PICKER_ICON_CATEGORIES`), the fallbacks and
105
+ `pickerColorHelper` — safe on a server, in a seed or a Node build script
50
106
 
51
107
  ## Usage
52
108
 
@@ -582,6 +638,148 @@ unregistered state resource threw inside render ("Resource consent-widget-presen
582
638
  blanked the whole application. Pinned by `tests/consent.spec.ts` (`?consent=bare` / `?consent=menu`;
583
639
  the configuration by `?consentCfg=window|bar`, `?modeProp=` and `?consentGeo=<CC>|fail`).
584
640
 
641
+ ## Subpath: `./picker`
642
+
643
+ The standard editor and renderer of an icon and a colour **a person chooses** — a workcard type's
644
+ or a status flow's mark, a label, a category. One component family, so every application offers
645
+ the same 24 colours, the same curated icons and the same behaviour on a desktop and a phone.
646
+
647
+ ```ts
648
+ import {
649
+ IconColorPicker, ColorPicker, IconPicker, ColorSwatches, IconGrid, IconTile, ColorDot, usePickerTranslate,
650
+ pickerColorHelper, pickerIconHelper, PICKER_COLORS, PICKER_COLOR_KEYS,
651
+ } from '@owlmeans/web-panel/picker'
652
+ // Server, seed, build script — the data and the colour helper without React or icons:
653
+ // import { pickerColorHelper, PICKER_COLOR_KEYS } from '@owlmeans/web-panel/picker/vocabulary'
654
+ ```
655
+
656
+ ### Which part
657
+
658
+ | Part | When |
659
+ |---|---|
660
+ | `IconColorPicker` | Both halves of a mark. One trigger (the tile, "Rocket · Indigo", a chevron — or `trigger="tile"` for the tile alone) opens swatches above a searchable, categorized icon grid tinted in the chosen colour. Stays open for the other half; Esc, a click outside or (sheet) Done closes it |
661
+ | `ColorPicker` | A colour alone (`trigger="button"` or `"dot"`). Closes on a swatch. `colors` takes an application palette — e.g. status tones |
662
+ | `IconPicker` | An icon alone, tinted in `color`. Closes on a pick |
663
+ | `ColorSwatches`, `IconGrid` | Inline, for a dialog or sheet the application already owns — no trigger, no surface of their own |
664
+ | `IconTile` | A stored mark shown: the icon in the colour's ink on its ground (`variant="tile"`), or the ink alone (`"plain"`); `size` `xs`/`sm`/`md`/`lg`; `label` makes it an image, else decorative |
665
+ | `ColorDot` | A stored colour as a dot in its ink (`size` `xs`/`sm`/`md`) |
666
+ | `usePickerTranslate()` | The `translate` for an app rendered by `PanelApp` / `render()` — the packaged bundle in the current language |
667
+
668
+ ### Values
669
+
670
+ - **Colour**: a palette key — `red orange amber yellow lime green emerald teal cyan sky blue indigo
671
+ violet purple fuchsia pink rose maroon brown olive navy slate stone graphite` (`PICKER_COLOR_KEYS`,
672
+ hue order) — or, only with `customColors`, a lowercase `#rrggbb`. Never a label ("Indigo").
673
+ - **Icon**: a kebab-case lucide name from the catalogue (`PICKER_ICON_NAMES`, ~250 in ten categories,
674
+ a superset of the planning kits' icons). Never a component, never a label.
675
+ - `IconColorPicker` emits `onChange({ icon, color })` — both halves as they now stand; a half
676
+ never chosen stays `undefined`, never `null`. `value` tolerates `null`s and absent halves.
677
+ - An unknown stored value renders the fallback (`slate`, `circle`) and never throws, so a value
678
+ from an older palette or a typo never breaks a page.
679
+ - `customColors` (default `false`) adds a colour wheel (react-colorful) and a hex field: a full
680
+ `#rrggbb` commits while typing, `#rgb` / `abc` on Enter or blur, always normalized to lowercase six
681
+ digits; anything else marks the field `aria-invalid` and changes nothing. A custom colour's ink
682
+ and grounds are derived to keep 4.5:1 in both modes.
683
+
684
+ ### Surfaces
685
+
686
+ `surface="auto"` (default) opens a popover from 768px up and a bottom sheet below — decided when
687
+ it OPENS, kept until it closes. `surface="popover" | "sheet"` forces one. The popover is modal, so
688
+ inside an application's own Dialog or Sheet the wheel scrolls it and focus stays in it; it keeps
689
+ 12px off the edges and scrolls inside rather than growing past the viewport. The sheet has a grab
690
+ handle, a sticky header with the preview and a 44px Done button, `88dvh` at most and safe-area
691
+ padding. Below 768px and on coarse pointers every cell, chip, field and button is at least 44px;
692
+ the search field takes focus on open only for a fine pointer (no keyboard pops up on a phone).
693
+ Keyboard: arrows move in two dimensions through swatches and icons (one tab stop each), Home/End,
694
+ ArrowDown from the search into the grid and ArrowUp back, Enter/Space picks, Esc closes and focus
695
+ returns to the trigger. Every control is a `<button type="button">` with an `aria-label`, so a
696
+ pick never submits an enclosing form.
697
+
698
+ ### Strings
699
+
700
+ The parts never read i18n implicitly — rendered without an i18n instance they show English. Pass
701
+ `translate={usePickerTranslate()}` in an app rendered by `PanelApp` / `render()`: chrome, colour
702
+ names and category names come from the lib-tier `picker` bundle in eight languages (en, pl, ru, be,
703
+ uk, es, de, fr); icon names stay English. An app overrides a string at the App tier in the `lib`
704
+ namespace (`i18nHelper.addI18nApp('de', 'picker', { colors: { teal: 'Türkis' } }, { ns: LIB_NAMESPACE })`).
705
+ Keys: `label`, `icon`, `color`, `placeholder.{both,icon,color}`, `nothing`, `done`, `search`,
706
+ `searchPlaceholder`, `categoriesLabel`, `all`, `results` (`{count}`), `empty` (`{query}`), `clear`,
707
+ `custom`, `hex`, `invalidHex`, `colors.<key>`, `categories.<category>`. `label` and `placeholder`
708
+ props override per instance. Only the 24 palette names translate: an application palette passed as
709
+ `colors` shows its `label`s as given, so translate them before passing them in.
710
+
711
+ ### RIGHT and WRONG
712
+
713
+ ```tsx
714
+ import type { IconColorChange } from '@owlmeans/web-panel/picker'
715
+
716
+ // RIGHT — local state
717
+ const translate = usePickerTranslate()
718
+ const [mark, setMark] = useState<IconColorChange>({ icon: type.icon, color: type.tone })
719
+ <IconColorPicker label="Mark" value={mark} onChange={setMark} translate={translate} />
720
+
721
+ // RIGHT — react-hook-form: the field holds { icon, color }; the picker is the whole control
722
+ <Controller
723
+ name="mark" control={form.control}
724
+ render={({ field }) => <IconColorPicker
725
+ id="mark" label="Mark" value={field.value} onChange={field.onChange} disabled={field.disabled} translate={translate}
726
+ />}
727
+ />
728
+ // …and on save: { tone: values.mark.color, icon: values.mark.icon }
729
+
730
+ // WRONG — a Select of colour names, a hand-made swatch row, or a label stored instead of the key
731
+ <Select value={tone} onValueChange={setTone}>{PICKER_COLOR_KEYS.map(k => <SelectItem value={k}>{k}</SelectItem>)}</Select>
732
+ {['red', 'blue'].map(c => <button className={`bg-${c}-500`} onClick={() => setTone(c)} />)}
733
+ onChange={next => save({ tone: 'Indigo' })}
734
+ // WRONG — the picker hands over a value, not an event
735
+ <ColorPicker value={field.value} onChange={event => field.onChange(event.target.value)} />
736
+ ```
737
+
738
+ ### Rendering a stored mark
739
+
740
+ ```tsx
741
+ <IconTile icon={type.icon} color={type.tone} size="sm" /> // list row, chip, header
742
+ <IconTile icon={flow.icon} color={flow.tone} variant="plain" /> // the ink alone, inline with text
743
+ <ColorDot color={label.color} /> // a dot
744
+ <span style={pickerColorHelper.styleOf(card.color)} className="text-(--pick-ink)">…</span> // custom markup
745
+ ```
746
+
747
+ `pickerColorHelper.styleOf(value, colors?)` returns the custom properties `--pick-swatch`,
748
+ `--pick-ink`, `--pick-soft`, `--pick-ink-dark`, `--pick-soft-dark` (a plain string record — it goes
749
+ straight into `style`). The parts switch to the dark values themselves, through explicit
750
+ arbitrary variants on web-panel's scheme contract (`.dark` on `<html>`, or the system's dark
751
+ unless `.light`) — never through the app's `dark:` variant. A hand-written element using
752
+ `styleOf` reads `--pick-ink` / `--pick-ink-dark` itself. `pickerColorHelper.resolve(value)` gives
753
+ every field (and `custom: true` for a hex); `hashKey(seed)` gives a stable palette key for a record
754
+ that has none. Code-chosen tones (a status's meaning) stay the app's own classes; a colour a PERSON
755
+ chose always renders through these parts.
756
+
757
+ **DOM hooks** (stable, used by end-to-end suites): the trigger `data-picker-trigger`, the open
758
+ surface `data-picker-surface="popover|sheet"`, a swatch `data-picker-color="<key>"` (the custom
759
+ row `"custom"`, `data-selected` while a custom value is chosen), an icon cell
760
+ `data-picker-icon="<name>"`, a chip `data-picker-category`, the search field `data-picker-search`,
761
+ the hex field `data-picker-hex`, the sheet's Done `data-picker-done`; `IconTile` carries
762
+ `data-icon-tile`, `data-icon`, `data-color`, `ColorDot` `data-color-dot`. Picker parts never carry
763
+ `data-tone` or `data-slot="button"` — an application's own rules on those never reach them.
764
+
765
+ ### Consumer setup
766
+
767
+ - `@radix-ui/react-popover` is a peer, like the other Radix primitives: keep it on the SAME Radix
768
+ generation as `@radix-ui/react-dialog` (update them together). The two share the dismissable-layer
769
+ and focus-scope stacks; a popover from another generation inside a Dialog is treated as an outside
770
+ click and loses focus to the dialog. `react-colorful` is a dependency of this package.
771
+ - The `@source` line for this package's `src` (see *Consumer setup* below) is what brings the
772
+ picker's classes — including its `[.dark_&]:` and `pointer-coarse:` variants — into the stylesheet.
773
+ - Tokens read: `--popover`, `--popover-foreground`, `--border`, `--input`, `--ring`, `--accent`,
774
+ `--accent-foreground`, `--primary`, `--primary-foreground`, `--muted-foreground`, `--foreground`,
775
+ `--destructive`.
776
+
777
+ Pinned by `tests/picker.spec.ts` (harness `/picker`: `?custom=1`, `?inSheet=1`, `?bare=1`,
778
+ `?surface=…`, `?compact=1`, `?empty=1`, `?inline=1`, `?dark=1`) and
779
+ `tests/picker-vocabulary.spec.ts` (palette contrast and OKLab distance, hex handling, catalogue ↔
780
+ components). `PICKER_SHOTS_DIR=<dir> bun test tests/picker-shots.spec.ts` writes every scenario's
781
+ screenshot at 1280 and 375, light and dark.
782
+
585
783
  ## Consumer setup — package boundary and Tailwind
586
784
 
587
785
  `web-panel` ships its shadcn primitives and `cn` helper as private implementation files under its
@@ -591,8 +789,9 @@ fresh installation depend on unrelated files. `cn` is not exported: a consumer k
591
789
  class-name helper and never vendors or imports this package's UI primitives.
592
790
 
593
791
  Consumers still supply the package's peer dependencies: the Radix primitives (`alert-dialog`,
594
- `dialog`, `label`, `navigation-menu`, `progress`, `separator`, `slot`) plus React, Tailwind and the
595
- usual utility libraries. A consumer may have its own shadcn `@` alias, but it is unrelated to this package.
792
+ `dialog`, `label`, `navigation-menu`, `popover`, `progress`, `separator`, `slot` — `dialog` and
793
+ `popover` on the same Radix generation) plus React, Tailwind and the usual utility libraries. A
794
+ consumer may have its own shadcn `@` alias, but it is unrelated to this package.
596
795
 
597
796
  Then point Tailwind at the installed package's **`src`** directory. Its oxide scanner reads the CSS root
598
797
  plus `@source` directives only, and excludes `node_modules` — so classes that exist **only** inside
@@ -612,10 +811,11 @@ in a linked workspace, so it is the reliable scan target in both modes.
612
811
 
613
812
  - `@owlmeans/web-client`, `@owlmeans/client-panel`, `@owlmeans/client-i18n`, `@owlmeans/web-router`
614
813
  - `@owlmeans/client-socket` — `appendSocketStatus`, `useSocketStatus`, `useSocketRetry`, behind `SocketReloadDialog`
814
+ - `react-colorful` — the custom-colour wheel of `./picker` (`customColors`); `sonner` — toasts
615
815
  - Peers (app-provided): `react`, `react-dom`, `react-hook-form`, `tailwindcss`, `tailwind-merge`,
616
- `clsx`, `class-variance-authority`, `lucide-react`, `ajv`, and the `@radix-ui/react-*` primitives
617
- (`alert-dialog`, `dialog`, `label`, `navigation-menu`, `progress`, `separator`, `slot`). No MUI,
618
- no react-router.
816
+ `clsx`, `class-variance-authority`, `lucide-react` (`./picker` imports its ~250 catalogue icons
817
+ statically), `ajv`, and the `@radix-ui/react-*` primitives (`alert-dialog`, `dialog`, `label`,
818
+ `navigation-menu`, `popover`, `progress`, `separator`, `slot`). No MUI, no react-router.
619
819
  - `ajv-formats` is imported at module scope by the form model but is declared in no dependency
620
820
  section of the manifest, which lists `ajv` alone. An install that does not otherwise pull it in
621
821
  fails at import time, so declare `ajv-formats` next to `ajv` in the consuming application.
@@ -0,0 +1,8 @@
1
+ import * as React from 'react';
2
+ import * as PopoverPrimitive from '@radix-ui/react-popover';
3
+ declare function Popover({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Root>): React.JSX.Element;
4
+ declare function PopoverTrigger({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Trigger>): React.JSX.Element;
5
+ declare function PopoverContent({ className, align, sideOffset, ...props }: React.ComponentProps<typeof PopoverPrimitive.Content>): React.JSX.Element;
6
+ declare function PopoverAnchor({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Anchor>): React.JSX.Element;
7
+ export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };
8
+ //# sourceMappingURL=popover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"popover.d.ts","sourceRoot":"","sources":["../../../../src/@/components/ui/popover.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,gBAAgB,MAAM,yBAAyB,CAAA;AAI3D,iBAAS,OAAO,CAAC,EACf,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,IAAI,CAAC,qBAEpD;AAED,iBAAS,cAAc,CAAC,EACtB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,OAAO,CAAC,qBAEvD;AAED,iBAAS,cAAc,CAAC,EACtB,SAAS,EACT,KAAgB,EAChB,UAAc,EACd,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,OAAO,CAAC,qBAevD;AAED,iBAAS,aAAa,CAAC,EACrB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,MAAM,CAAC,qBAEtD;AAED,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,CAAA"}
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as PopoverPrimitive from '@radix-ui/react-popover';
3
+ import { cn } from '../../lib/utils.js';
4
+ function Popover({ ...props }) {
5
+ return _jsx(PopoverPrimitive.Root, { "data-slot": "popover", ...props });
6
+ }
7
+ function PopoverTrigger({ ...props }) {
8
+ return _jsx(PopoverPrimitive.Trigger, { "data-slot": "popover-trigger", ...props });
9
+ }
10
+ function PopoverContent({ className, align = 'center', sideOffset = 4, ...props }) {
11
+ return (_jsx(PopoverPrimitive.Portal, { children: _jsx(PopoverPrimitive.Content, { "data-slot": "popover-content", align: align, sideOffset: sideOffset, className: cn('bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden', className), ...props }) }));
12
+ }
13
+ function PopoverAnchor({ ...props }) {
14
+ return _jsx(PopoverPrimitive.Anchor, { "data-slot": "popover-anchor", ...props });
15
+ }
16
+ export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };
17
+ //# sourceMappingURL=popover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"popover.js","sourceRoot":"","sources":["../../../../src/@/components/ui/popover.tsx"],"names":[],"mappings":";AAEA,OAAO,KAAK,gBAAgB,MAAM,yBAAyB,CAAA;AAE3D,OAAO,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAA;AAEvC,SAAS,OAAO,CAAC,EACf,GAAG,KAAK,EAC2C;IACnD,OAAO,KAAC,gBAAgB,CAAC,IAAI,iBAAW,SAAS,KAAK,KAAK,GAAI,CAAA;AACjE,CAAC;AAED,SAAS,cAAc,CAAC,EACtB,GAAG,KAAK,EAC8C;IACtD,OAAO,KAAC,gBAAgB,CAAC,OAAO,iBAAW,iBAAiB,KAAK,KAAK,GAAI,CAAA;AAC5E,CAAC;AAED,SAAS,cAAc,CAAC,EACtB,SAAS,EACT,KAAK,GAAG,QAAQ,EAChB,UAAU,GAAG,CAAC,EACd,GAAG,KAAK,EAC8C;IACtD,OAAO,CACL,KAAC,gBAAgB,CAAC,MAAM,cACtB,KAAC,gBAAgB,CAAC,OAAO,iBACb,iBAAiB,EAC3B,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,EAAE,CACX,geAAge,EAChe,SAAS,CACV,KACG,KAAK,GACT,GACsB,CAC3B,CAAA;AACH,CAAC;AAED,SAAS,aAAa,CAAC,EACrB,GAAG,KAAK,EAC6C;IACrD,OAAO,KAAC,gBAAgB,CAAC,MAAM,iBAAW,gBAAgB,KAAK,KAAK,GAAI,CAAA;AAC1E,CAAC;AAED,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,CAAA"}
@@ -0,0 +1,9 @@
1
+ import type { FC } from 'react';
2
+ import type { ColorPickerProps } from './types.js';
3
+ /**
4
+ * The colour half alone: a trigger opening the swatches (plus a colour wheel and a hex field with
5
+ * `customColors`). A swatch commits its key and closes; the wheel and the field commit as they
6
+ * change and keep it open. Pass `colors` for an application palette — a status palette, say.
7
+ */
8
+ export declare const ColorPicker: FC<ColorPickerProps>;
9
+ //# sourceMappingURL=color-picker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color-picker.d.ts","sourceRoot":"","sources":["../../../src/components/picker/color-picker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAA;AAC/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAGlD;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,EAAE,CAAC,gBAAgB,CAK3C,CAAA"}
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { PickerRoot } from './root.js';
3
+ /**
4
+ * The colour half alone: a trigger opening the swatches (plus a colour wheel and a hex field with
5
+ * `customColors`). A swatch commits its key and closes; the wheel and the field commit as they
6
+ * change and keep it open. Pass `colors` for an application palette — a status palette, say.
7
+ */
8
+ export const ColorPicker = ({ value, onChange, trigger = 'button', customColors = false, ...base }) => _jsx(PickerRoot, { ...base, mode: "color", trigger: trigger, customColors: customColors, closeOnPick: true, color: value, onColor: onChange, onIcon: () => undefined });
9
+ //# sourceMappingURL=color-picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color-picker.js","sourceRoot":"","sources":["../../../src/components/picker/color-picker.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAEtC;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAyB,CAC/C,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,GAAG,QAAQ,EAAE,YAAY,GAAG,KAAK,EAAE,GAAG,IAAI,EAAE,EACtE,EAAE,CAAC,KAAC,UAAU,OACV,IAAI,EAAE,IAAI,EAAC,OAAO,EAAC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,QAAC,KAAK,EAAE,KAAK,EAC7F,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,SAAS,GAC1C,CAAA"}
@@ -0,0 +1,76 @@
1
+ import type { CSSProperties } from 'react';
2
+ import type { PickerDotSize, PickerTileSize } from './types.js';
3
+ import type { PickerLayout } from './types.local.js';
4
+ /** From this width up `surface="auto"` opens a popover; below it, a bottom sheet. */
5
+ export declare const POPOVER_QUERY = "(min-width: 768px)";
6
+ /** A mouse or trackpad — the search field may take focus on open without raising a keyboard. */
7
+ export declare const FINE_POINTER_QUERY = "(pointer: fine)";
8
+ /**
9
+ * The current mode's ink and ground, read from the five custom properties `styleOf` sets.
10
+ *
11
+ * Dark follows web-panel's colour-scheme contract — `.dark` on the document element, or the
12
+ * operating system's dark unless `.light` is there — through explicit arbitrary variants. Never the
13
+ * consumer's `dark:`: an application may define it any way it likes, or not at all. Every part that
14
+ * carries a colour's style puts this on the same element; children read `--pick-fg` / `--pick-bg`.
15
+ */
16
+ export declare const PICK_SCOPE: string;
17
+ /** A hairline inside a swatch, so a dark swatch keeps an edge on a dark surface and a pale one on a light. */
18
+ export declare const HAIRLINE: string;
19
+ export declare const TILE = "inline-flex shrink-0 items-center justify-center bg-(--pick-bg) text-(--pick-fg)";
20
+ export declare const TILE_SIZE: Record<PickerTileSize, string>;
21
+ export declare const PLAIN = "inline-flex shrink-0 items-center justify-center text-(--pick-fg)";
22
+ export declare const PLAIN_SIZE: Record<PickerTileSize, string>;
23
+ export declare const DOT = "inline-block shrink-0 rounded-full bg-(--pick-fg)";
24
+ export declare const DOT_SIZE: Record<PickerDotSize, string>;
25
+ export declare const TRIGGER_BUTTON: string;
26
+ export declare const TRIGGER_COMPACT: string;
27
+ export declare const TRIGGER_DOT = "rounded-full border border-input bg-transparent hover:bg-accent/60";
28
+ /** The empty tile of a trigger while nothing is chosen. */
29
+ export declare const TRIGGER_EMPTY: string;
30
+ export declare const CHEVRON: string;
31
+ export declare const POPOVER: string;
32
+ export declare const SHEET: string;
33
+ /** With an icon grid the surface keeps one height, so filtering never makes it jump. */
34
+ export declare const TALL: Record<'popover' | 'sheet', string>;
35
+ /** The trigger's swatch disc for a colour-only picker — the colour as it was picked. */
36
+ export declare const TRIGGER_DISC = "block size-4 shrink-0 rounded-full bg-(--pick-swatch)";
37
+ export declare const GRAB = "mx-auto mt-2 mb-1 h-1.5 w-10 shrink-0 rounded-full bg-muted-foreground/30";
38
+ export declare const HEADER: Record<'popover' | 'sheet', string>;
39
+ export declare const HEADER_TITLE = "truncate text-sm font-semibold leading-5";
40
+ export declare const HEADER_SUMMARY = "truncate text-xs leading-4 text-muted-foreground";
41
+ export declare const DONE: string;
42
+ export declare const BODY = "min-h-0 flex-1 overflow-y-auto overscroll-contain";
43
+ export declare const SECTION: Record<'popover' | 'sheet', string>;
44
+ export declare const SECTION_TITLE = "mb-2 text-xs font-medium text-muted-foreground";
45
+ export declare const SWATCHES: Record<PickerLayout, string>;
46
+ export declare const SWATCH: string;
47
+ export declare const SWATCH_DISC: string;
48
+ export declare const SWATCH_SELECTED = "ring-2 ring-foreground ring-offset-2 ring-offset-popover";
49
+ export declare const CUSTOM_ROW: string;
50
+ export declare const CUSTOM_DISC = "block size-5 shrink-0 rounded-full";
51
+ /** The colour wheel's mark while no custom colour is chosen. */
52
+ export declare const RAINBOW = "bg-[conic-gradient(from_180deg,#ef4444,#f59e0b,#eab308,#22c55e,#06b6d4,#3b82f6,#8b5cf6,#ec4899,#ef4444)]";
53
+ export declare const CUSTOM_PANEL = "mt-2 flex flex-col gap-2";
54
+ /** A full six-digit value — committed while typing; a three-digit one waits for Enter or blur. */
55
+ export declare const FULL_HEX: RegExp;
56
+ /** The wheel's colour while nothing custom is chosen yet — also the hex field's placeholder. */
57
+ export declare const CUSTOM_START = "#3b82f6";
58
+ /** react-colorful sizes itself with a stylesheet it injects; an inline size is the one that always wins. */
59
+ export declare const WHEEL_STYLE: CSSProperties;
60
+ export declare const FIELD: string;
61
+ export declare const HEX_FIELD = "font-mono";
62
+ export declare const FIELD_ERROR = "text-xs text-destructive";
63
+ /** The search field and the chips stay in view while the grid scrolls under them. */
64
+ export declare const ICON_TOOLS: Record<PickerLayout, string>;
65
+ export declare const SEARCH_WRAP = "relative";
66
+ export declare const SEARCH_ICON = "pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground";
67
+ export declare const SEARCH = "pl-8 [&::-webkit-search-cancel-button]:cursor-pointer";
68
+ /** One scrolling row of category chips, running to the surface's edges rather than its padding. */
69
+ export declare const CHIPS: Record<PickerLayout, string>;
70
+ export declare const CHIP: string;
71
+ export declare const ICONS: Record<PickerLayout, string>;
72
+ export declare const ICON_CELL: string;
73
+ export declare const ICON_GRID_PAD: Record<PickerLayout, string>;
74
+ export declare const EMPTY = "flex flex-col items-center gap-2 px-4 py-8 text-center text-sm text-muted-foreground";
75
+ export declare const LINK_BUTTON: string;
76
+ //# sourceMappingURL=consts.local.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consts.local.d.ts","sourceRoot":"","sources":["../../../src/components/picker/consts.local.ts"],"names":[],"mappings":"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"}