@dreamtree-org/twreact-ui 1.3.9 → 1.3.11

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 (128) hide show
  1. package/README.md +32 -0
  2. package/ai-skills/dreamtree-ui.md +12 -7
  3. package/dist/ai-schema.json +156 -29
  4. package/dist/charts/index.js +195 -66
  5. package/dist/components/blocks/AppShell.d.ts.map +1 -1
  6. package/dist/components/charts/CartesianChart.d.ts.map +1 -1
  7. package/dist/components/charts/ChartFrame.d.ts.map +1 -1
  8. package/dist/components/charts/useChartLayout.d.ts +25 -0
  9. package/dist/components/charts/useChartLayout.d.ts.map +1 -0
  10. package/dist/components/core/Accordion.d.ts.map +1 -1
  11. package/dist/components/core/Button.d.ts.map +1 -1
  12. package/dist/components/core/Checkbox.d.ts.map +1 -1
  13. package/dist/components/core/ColorPicker.d.ts.map +1 -1
  14. package/dist/components/core/DatePicker.d.ts.map +1 -1
  15. package/dist/components/core/Form.d.ts.map +1 -1
  16. package/dist/components/core/Input.d.ts.map +1 -1
  17. package/dist/components/core/Loader.d.ts.map +1 -1
  18. package/dist/components/core/LocationPicker.d.ts.map +1 -1
  19. package/dist/components/core/PriceRangePicker.d.ts +0 -29
  20. package/dist/components/core/PriceRangePicker.d.ts.map +1 -1
  21. package/dist/components/core/ProgressBar.d.ts.map +1 -1
  22. package/dist/components/core/Radio.d.ts.map +1 -1
  23. package/dist/components/core/Rate.d.ts.map +1 -1
  24. package/dist/components/core/RoundedTag.d.ts.map +1 -1
  25. package/dist/components/core/Skeleton.d.ts.map +1 -1
  26. package/dist/components/core/Slider.d.ts.map +1 -1
  27. package/dist/components/core/SpeechToText.d.ts.map +1 -1
  28. package/dist/components/core/Switch.d.ts.map +1 -1
  29. package/dist/components/core/Table/ColumnMenu.d.ts.map +1 -1
  30. package/dist/components/core/Table/Pagination.d.ts.map +1 -1
  31. package/dist/components/core/Table/TableRow.d.ts.map +1 -1
  32. package/dist/components/core/Table/helpers.d.ts +2 -0
  33. package/dist/components/core/Table/helpers.d.ts.map +1 -1
  34. package/dist/components/core/Tabs.d.ts +2 -5
  35. package/dist/components/core/Tabs.d.ts.map +1 -1
  36. package/dist/components/core/TextToSpeech.d.ts +2 -7
  37. package/dist/components/core/TextToSpeech.d.ts.map +1 -1
  38. package/dist/components/core/Textarea.d.ts.map +1 -1
  39. package/dist/components/core/Tooltip.d.ts.map +1 -1
  40. package/dist/components/feedback/Alert.d.ts.map +1 -1
  41. package/dist/components/feedback/Dialog.d.ts.map +1 -1
  42. package/dist/components/feedback/Toast.d.ts.map +1 -1
  43. package/dist/components/navigation/Breadcrumbs.d.ts.map +1 -1
  44. package/dist/components/navigation/FootNav.d.ts.map +1 -1
  45. package/dist/components/navigation/Sidebar.d.ts.map +1 -1
  46. package/dist/components/utility/Avatar.d.ts +8 -0
  47. package/dist/components/utility/Avatar.d.ts.map +1 -1
  48. package/dist/components/utility/AvatarGroup.d.ts +4 -0
  49. package/dist/components/utility/AvatarGroup.d.ts.map +1 -0
  50. package/dist/components/utility/Badge.d.ts.map +1 -1
  51. package/dist/components/utility/Card.d.ts.map +1 -1
  52. package/dist/components/utility/Carousel.d.ts.map +1 -1
  53. package/dist/components/utility/FileUpload.d.ts.map +1 -1
  54. package/dist/components/utility/Pagination.d.ts.map +1 -1
  55. package/dist/components/utility/Sortable.d.ts.map +1 -1
  56. package/dist/components/utility/Stepper.d.ts.map +1 -1
  57. package/dist/esm/components/blocks/AppShell.js +24 -18
  58. package/dist/esm/components/blocks/EmptyState.js +1 -1
  59. package/dist/esm/components/blocks/ErrorState.js +1 -1
  60. package/dist/esm/components/blocks/LoadingState.js +4 -4
  61. package/dist/esm/components/blocks/StatCard.js +6 -6
  62. package/dist/esm/components/blocks/StatusPill.js +5 -8
  63. package/dist/esm/components/charts/CartesianChart.js +85 -62
  64. package/dist/esm/components/charts/ChartFrame.js +12 -5
  65. package/dist/esm/components/charts/useChartLayout.js +105 -0
  66. package/dist/esm/components/core/Accordion.js +40 -9
  67. package/dist/esm/components/core/Button.js +49 -25
  68. package/dist/esm/components/core/Checkbox.js +71 -45
  69. package/dist/esm/components/core/ColorPicker.js +49 -48
  70. package/dist/esm/components/core/DatePicker.js +97 -58
  71. package/dist/esm/components/core/Form.js +35 -8
  72. package/dist/esm/components/core/Input.js +89 -20
  73. package/dist/esm/components/core/Loader.js +30 -44
  74. package/dist/esm/components/core/LocationPicker.js +82 -16
  75. package/dist/esm/components/core/PriceRangePicker.js +205 -218
  76. package/dist/esm/components/core/ProgressBar.js +8 -12
  77. package/dist/esm/components/core/Radio.js +43 -15
  78. package/dist/esm/components/core/Rate.js +109 -50
  79. package/dist/esm/components/core/RoundedTag.js +27 -10
  80. package/dist/esm/components/core/Skeleton.js +13 -14
  81. package/dist/esm/components/core/Slider.js +24 -6
  82. package/dist/esm/components/core/SpeechToText.js +68 -16
  83. package/dist/esm/components/core/Switch.js +88 -16
  84. package/dist/esm/components/core/Table/ColumnMenu.js +2 -4
  85. package/dist/esm/components/core/Table/Pagination.js +15 -18
  86. package/dist/esm/components/core/Table/TableRow.js +2 -2
  87. package/dist/esm/components/core/Table/helpers.js +33 -1
  88. package/dist/esm/components/core/Tabs.js +185 -37
  89. package/dist/esm/components/core/TextToSpeech.js +121 -28
  90. package/dist/esm/components/core/Textarea.js +10 -2
  91. package/dist/esm/components/core/Tooltip.js +100 -28
  92. package/dist/esm/components/feedback/Alert.js +21 -11
  93. package/dist/esm/components/feedback/Dialog.js +44 -100
  94. package/dist/esm/components/feedback/Toast.js +24 -5
  95. package/dist/esm/components/navigation/Breadcrumbs.js +97 -35
  96. package/dist/esm/components/navigation/FootNav.js +102 -13
  97. package/dist/esm/components/navigation/Sidebar.js +13 -61
  98. package/dist/esm/components/utility/Avatar.js +37 -22
  99. package/dist/esm/components/utility/AvatarGroup.js +56 -0
  100. package/dist/esm/components/utility/Badge.js +43 -23
  101. package/dist/esm/components/utility/Card.js +32 -8
  102. package/dist/esm/components/utility/Carousel.js +165 -38
  103. package/dist/esm/components/utility/FileUpload.js +106 -31
  104. package/dist/esm/components/utility/Pagination.js +33 -15
  105. package/dist/esm/components/utility/Sortable.js +6 -2
  106. package/dist/esm/components/utility/Stepper.js +110 -47
  107. package/dist/esm/hooks/useFocusTrap.js +10 -2
  108. package/dist/esm/hooks/useSliderValues.js +8 -2
  109. package/dist/esm/hooks/useSortable.js +107 -5
  110. package/dist/esm/index.js +1 -0
  111. package/dist/esm/utils/touchTarget.js +4 -1
  112. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  113. package/dist/hooks/useSliderValues.d.ts +6 -2
  114. package/dist/hooks/useSliderValues.d.ts.map +1 -1
  115. package/dist/hooks/useSortable.d.ts +11 -1
  116. package/dist/hooks/useSortable.d.ts.map +1 -1
  117. package/dist/index.css +1 -1
  118. package/dist/index.d.ts +3 -2
  119. package/dist/index.esm.css +1 -1
  120. package/dist/index.esm.js +3734 -2174
  121. package/dist/index.js +3732 -2171
  122. package/dist/types/charts.d.ts +3 -1
  123. package/dist/types/charts.d.ts.map +1 -1
  124. package/dist/types/index.d.ts +64 -9
  125. package/dist/types/index.d.ts.map +1 -1
  126. package/doc/README.md +1 -0
  127. package/mcp/catalog.snapshot.json +350 -121
  128. package/package.json +1 -1
@@ -1,9 +1,10 @@
1
1
  'use client';
2
2
  import { objectWithoutProperties as _objectWithoutProperties, slicedToArray as _slicedToArray, objectSpread2 as _objectSpread2, toConsumableArray as _toConsumableArray } from '../../_virtual/_rollupPluginBabelHelpers.js';
3
3
  import { jsx, jsxs } from 'react/jsx-runtime';
4
- import { forwardRef, useState, useRef, useEffect, useLayoutEffect } from 'react';
4
+ import { forwardRef, useState, useRef, useEffect, useId, useLayoutEffect } from 'react';
5
5
  import { cn } from '../../utils/cn.js';
6
6
  import { useLatestRef } from '../../hooks/useLatestRef.js';
7
+ import { useReducedMotion } from '../../hooks/useReducedMotion.js';
7
8
 
8
9
  var _excluded = ["items", "allowMultiple", "defaultOpenIndexes", "onChange", "className", "headerClassName", "contentClassName", "chevronPosition", "chevronIcon", "bordered", "rounded", "size", "transitionDuration"];
9
10
  /**
@@ -65,15 +66,45 @@ var Accordion = /*#__PURE__*/forwardRef(function Accordion(_ref, ref) {
65
66
  useEffect(function () {
66
67
  if (typeof onChangeRef.current === "function") onChangeRef.current(openIndexes);
67
68
  }, [openIndexes, onChangeRef]);
68
- // Size panels from their measured content before paint. Done imperatively
69
- // (not via the style prop) so render never reads refs / the DOM.
69
+ // Unique per instance so two accordions on one page never share ids.
70
+ var uid = useId();
71
+ var reducedMotion = useReducedMotion();
72
+ var duration = reducedMotion ? 0 : transitionDuration;
73
+ // Which panels were open on the previous layout pass (null = first mount).
74
+ var prevOpenRef = useRef(null);
75
+ // Size panels imperatively (not via the style prop) so render never reads
76
+ // refs / the DOM. An open panel settles on `max-height: none` so content
77
+ // that grows after opening is never clipped; closed panels are `inert` so
78
+ // Tab cannot land inside collapsed content.
70
79
  useLayoutEffect(function () {
80
+ var prevOpen = prevOpenRef.current;
81
+ var timers = [];
71
82
  panelRefs.current.forEach(function (panelEl, idx) {
72
83
  if (!panelEl) return;
73
84
  var open = openIndexes.includes(idx);
74
- panelEl.style.maxHeight = open ? panelEl.scrollHeight + "px" : "0px";
85
+ var wasOpen = prevOpen ? prevOpen.includes(idx) : open;
86
+ if (open) panelEl.removeAttribute("inert");else panelEl.setAttribute("inert", "");
87
+ if (open === wasOpen || duration === 0) {
88
+ panelEl.style.maxHeight = open ? "none" : "0px";
89
+ return;
90
+ }
91
+ if (open) {
92
+ panelEl.style.maxHeight = panelEl.scrollHeight + "px";
93
+ timers.push(setTimeout(function () {
94
+ if (panelEl.getAttribute("inert") === null) panelEl.style.maxHeight = "none";
95
+ }, duration));
96
+ } else {
97
+ // Collapse from a concrete height; `none` -> 0 cannot be transitioned.
98
+ panelEl.style.maxHeight = panelEl.scrollHeight + "px";
99
+ void panelEl.offsetHeight; // force reflow so the transition starts
100
+ panelEl.style.maxHeight = "0px";
101
+ }
75
102
  });
76
- }, [openIndexes, items]);
103
+ prevOpenRef.current = openIndexes;
104
+ return function () {
105
+ return timers.forEach(clearTimeout);
106
+ };
107
+ }, [openIndexes, items, duration]);
77
108
  var isOpen = function isOpen(index) {
78
109
  return openIndexes.includes(index);
79
110
  };
@@ -166,8 +197,8 @@ var Accordion = /*#__PURE__*/forwardRef(function Accordion(_ref, ref) {
166
197
  var _item$id;
167
198
  var id = (_item$id = item.id) !== null && _item$id !== void 0 ? _item$id : index;
168
199
  var open = isOpen(index);
169
- var headerId = "accordion-header-".concat(id);
170
- var panelId = "accordion-panel-".concat(id);
200
+ var headerId = "".concat(uid, "accordion-header-").concat(id);
201
+ var panelId = "".concat(uid, "accordion-panel-").concat(id);
171
202
  var leftIconEl = renderLeftIcon(item, index);
172
203
  return jsxs("div", {
173
204
  className: "bg-white",
@@ -187,7 +218,7 @@ var Accordion = /*#__PURE__*/forwardRef(function Accordion(_ref, ref) {
187
218
  return handleHeaderKeyDown(e, index);
188
219
  },
189
220
  type: "button",
190
- className: "w-full flex items-center ".concat(sizeMap[size].header, " justify-between focus:outline-none focus:ring-2 focus:ring-indigo-500 ").concat(headerClassName, " ").concat(item.disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"),
221
+ className: "w-full flex items-center ".concat(sizeMap[size].header, " justify-between focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 ").concat(headerClassName, " ").concat(item.disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"),
191
222
  children: jsxs("div", {
192
223
  className: "flex items-center gap-3 w-full",
193
224
  children: [chevronPosition === "left" ? jsx("span", {
@@ -242,7 +273,7 @@ var Accordion = /*#__PURE__*/forwardRef(function Accordion(_ref, ref) {
242
273
  return panelRefs.current[index] = el;
243
274
  },
244
275
  style: {
245
- transition: "max-height ".concat(transitionDuration, "ms ease"),
276
+ transition: duration ? "max-height ".concat(duration, "ms ease") : "none",
246
277
  overflow: "hidden"
247
278
  },
248
279
  className: "".concat(contentClassName, " ").concat(sizeMap[size].content),
@@ -1,11 +1,18 @@
1
1
  'use client';
2
- import { objectWithoutProperties as _objectWithoutProperties, objectSpread2 as _objectSpread2 } from '../../_virtual/_rollupPluginBabelHelpers.js';
3
- import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { objectWithoutProperties as _objectWithoutProperties, defineProperty as _defineProperty, objectSpread2 as _objectSpread2 } from '../../_virtual/_rollupPluginBabelHelpers.js';
3
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
4
4
  import { forwardRef } from 'react';
5
5
  import { cn } from '../../utils/cn.js';
6
+ import { TOUCH_TARGET, PRESS_FEEDBACK } from '../../utils/touchTarget.js';
6
7
  import { Loader2 } from 'lucide-react';
7
8
 
8
9
  var _excluded = ["className", "variant", "size", "disabled", "loading", "children", "leftIcon", "rightIcon", "fullWidth", "as", "onClick"];
10
+ // Square, icon-only sizes. Pair with an `aria-label` (there is no visible text).
11
+ var ICON_SIZES = {
12
+ 'icon-sm': 'h-8 w-8 p-0',
13
+ icon: 'h-10 w-10 p-0',
14
+ 'icon-lg': 'h-12 w-12 p-0'
15
+ };
9
16
  var Button = /*#__PURE__*/forwardRef(function (_ref, ref) {
10
17
  var className = _ref.className,
11
18
  _ref$variant = _ref.variant,
@@ -26,26 +33,37 @@ var Button = /*#__PURE__*/forwardRef(function (_ref, ref) {
26
33
  var Component = as || (props.href !== undefined ? 'a' : 'button');
27
34
  var isNativeButton = Component === 'button';
28
35
  var isDisabled = disabled || loading;
29
- var baseClasses = cn('inline-flex items-center justify-center rounded-md font-medium transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none aria-disabled:opacity-50 aria-disabled:pointer-events-none', {
30
- // Variants
31
- 'bg-primary-600 text-white hover:bg-primary-700 focus-visible:ring-primary-500': variant === 'primary',
32
- 'bg-secondary-100 text-secondary-900 hover:bg-secondary-200 focus-visible:ring-secondary-500': variant === 'secondary',
33
- 'bg-transparent border border-gray-300 hover:bg-gray-50 focus-visible:ring-primary-500 dark:border-gray-600 dark:text-gray-100 dark:hover:bg-gray-800 dark:focus-visible:ring-offset-gray-900': variant === 'outline',
34
- 'bg-transparent text-primary-600 hover:bg-primary-50 focus-visible:ring-primary-500': variant === 'ghost',
35
- 'bg-error-600 text-white hover:bg-error-700 focus-visible:ring-error-500': variant === 'destructive',
36
- 'bg-success-600 text-white hover:bg-success-700 focus-visible:ring-success-500': variant === 'success',
37
- 'bg-warning-600 text-white hover:bg-warning-700 focus-visible:ring-warning-500': variant === 'warning',
36
+ var isIconSize = Boolean(ICON_SIZES[size]);
37
+ var baseClasses = cn('relative inline-flex items-center justify-center rounded-md font-medium',
38
+ // Press shrink + an explicit transition for colours and the shrink;
39
+ // both are off under prefers-reduced-motion.
40
+ PRESS_FEEDBACK, 'transition duration-fast ease-standard motion-reduce:transition-none', 'focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-gray-900', 'disabled:opacity-50 disabled:pointer-events-none aria-disabled:opacity-50 aria-disabled:pointer-events-none', _defineProperty(_defineProperty({
41
+ // Variants (each with dark-mode colours).
42
+ 'bg-primary-600 text-white hover:bg-primary-700 focus-visible:ring-primary-500 dark:hover:bg-primary-500': variant === 'primary',
43
+ 'bg-secondary-100 text-secondary-900 hover:bg-secondary-200 focus-visible:ring-secondary-500 dark:bg-secondary-800 dark:text-secondary-100 dark:hover:bg-secondary-700': variant === 'secondary',
44
+ 'bg-transparent border border-gray-300 hover:bg-gray-50 focus-visible:ring-primary-500 dark:border-gray-600 dark:text-gray-100 dark:hover:bg-gray-800': variant === 'outline',
45
+ 'bg-transparent text-primary-600 hover:bg-primary-50 focus-visible:ring-primary-500 dark:text-primary-300 dark:hover:bg-primary-900/40': variant === 'ghost',
46
+ 'bg-error-600 text-white hover:bg-error-700 focus-visible:ring-error-500 dark:hover:bg-error-500': variant === 'destructive',
47
+ 'bg-success-600 text-white hover:bg-success-700 focus-visible:ring-success-500 dark:hover:bg-success-500': variant === 'success',
48
+ 'bg-warning-600 text-white hover:bg-warning-700 focus-visible:ring-warning-500 dark:hover:bg-warning-500': variant === 'warning',
38
49
  // Sizes — every value of the shared `Size` type gets a real height.
39
50
  'h-7 px-2 text-xs': size === 'xs',
40
51
  'h-8 px-3 text-xs': size === 'sm',
41
52
  'h-10 px-4 text-sm': size === 'md',
42
53
  'h-12 px-6 text-base': size === 'lg',
43
54
  'h-14 px-8 text-lg': size === 'xl',
44
- 'h-16 px-10 text-xl': size === '2xl',
45
- // Full width
46
- 'w-full': fullWidth
47
- }, className);
48
- return jsxs(Component, _objectSpread2(_objectSpread2(_objectSpread2({
55
+ 'h-16 px-10 text-xl': size === '2xl'
56
+ }, TOUCH_TARGET, size === 'xs' || size === 'sm' || size === 'icon-sm' || size === 'icon'), 'w-full', fullWidth), ICON_SIZES[size], className);
57
+ var content = jsxs(Fragment, {
58
+ children: [leftIcon && jsx("span", {
59
+ className: isIconSize ? undefined : 'mr-2',
60
+ children: leftIcon
61
+ }), children, rightIcon && jsx("span", {
62
+ className: isIconSize ? undefined : 'ml-2',
63
+ children: rightIcon
64
+ })]
65
+ });
66
+ return jsx(Component, _objectSpread2(_objectSpread2(_objectSpread2({
49
67
  ref: ref,
50
68
  className: baseClasses
51
69
  }, isNativeButton ? {
@@ -56,19 +74,25 @@ var Button = /*#__PURE__*/forwardRef(function (_ref, ref) {
56
74
  'aria-disabled': true,
57
75
  tabIndex: -1
58
76
  }), {}, {
77
+ "aria-busy": loading ? true : undefined,
59
78
  onClick: !isNativeButton && isDisabled ? function (e) {
60
79
  return e.preventDefault();
61
80
  } : onClick
62
81
  }, props), {}, {
63
- children: [loading && jsx(Loader2, {
64
- className: "mr-2 h-4 w-4 animate-spin"
65
- }), !loading && leftIcon && jsx("span", {
66
- className: "mr-2",
67
- children: leftIcon
68
- }), children, !loading && rightIcon && jsx("span", {
69
- className: "ml-2",
70
- children: rightIcon
71
- })]
82
+ children: loading ? jsxs(Fragment, {
83
+ children: [jsx("span", {
84
+ className: "inline-flex items-center opacity-0",
85
+ "data-slot": "label",
86
+ children: content
87
+ }), jsx("span", {
88
+ className: "absolute inset-0 flex items-center justify-center",
89
+ "data-slot": "spinner",
90
+ "aria-hidden": "true",
91
+ children: jsx(Loader2, {
92
+ className: "h-4 w-4 animate-spin motion-reduce:animate-none"
93
+ })
94
+ })]
95
+ }) : content
72
96
  }));
73
97
  });
74
98
  Button.displayName = 'Button';
@@ -2,7 +2,8 @@
2
2
  import { objectWithoutProperties as _objectWithoutProperties, slicedToArray as _slicedToArray, objectSpread2 as _objectSpread2 } from '../../_virtual/_rollupPluginBabelHelpers.js';
3
3
  import { jsxs, jsx } from 'react/jsx-runtime';
4
4
  import { forwardRef, useId, useRef, useState, useEffect, useCallback } from 'react';
5
- import { Check } from 'lucide-react';
5
+ import { Check, Minus } from 'lucide-react';
6
+ import { cn } from '../../utils/cn.js';
6
7
 
7
8
  var _excluded = ["id", "name", "label", "checked", "defaultChecked", "onChange", "disabled", "required", "className", "size", "labelPosition", "value", "ariaLabel", "variant", "indeterminate", "borderColor", "color", "iconClass", "checkedClass", "wrapperProps"];
8
9
  var SIZE_CLASSES = {
@@ -22,52 +23,71 @@ var SIZE_CLASSES = {
22
23
  icon: "w-4 h-4"
23
24
  }
24
25
  };
26
+ // `checked` paints the box from the native :checked state; `fill` is the same
27
+ // paint applied from React state for the indeterminate (mixed) state.
28
+ // blue / indigo / purple are raw-palette presets kept for compatibility
29
+ // (deprecated; removed in 2.0 — use the token names).
25
30
  var COLOR_PRESETS = {
26
31
  primary: {
27
- checked: "peer-checked:bg-primary-600 peer-checked:border-primary-600 peer-checked:text-white",
28
- ring: "peer-focus:ring-primary-300"
32
+ checked: "peer-checked:bg-primary-600 peer-checked:border-primary-600 peer-checked:text-white dark:peer-checked:bg-primary-500 dark:peer-checked:border-primary-500",
33
+ fill: "bg-primary-600 border-primary-600 text-white dark:bg-primary-500 dark:border-primary-500",
34
+ ring: "peer-focus-visible:ring-primary-500"
29
35
  },
30
36
  secondary: {
31
- checked: "peer-checked:bg-secondary-600 peer-checked:border-secondary-600 peer-checked:text-white",
32
- ring: "peer-focus:ring-secondary-300"
37
+ checked: "peer-checked:bg-secondary-600 peer-checked:border-secondary-600 peer-checked:text-white dark:peer-checked:bg-secondary-500 dark:peer-checked:border-secondary-500",
38
+ fill: "bg-secondary-600 border-secondary-600 text-white dark:bg-secondary-500 dark:border-secondary-500",
39
+ ring: "peer-focus-visible:ring-secondary-500"
33
40
  },
34
41
  success: {
35
- checked: "peer-checked:bg-success-600 peer-checked:border-success-600 peer-checked:text-white",
36
- ring: "peer-focus:ring-success-300"
42
+ checked: "peer-checked:bg-success-600 peer-checked:border-success-600 peer-checked:text-white dark:peer-checked:bg-success-500 dark:peer-checked:border-success-500",
43
+ fill: "bg-success-600 border-success-600 text-white dark:bg-success-500 dark:border-success-500",
44
+ ring: "peer-focus-visible:ring-success-500"
37
45
  },
38
46
  green: {
39
- checked: "peer-checked:bg-success-600 peer-checked:border-success-600 peer-checked:text-white",
40
- ring: "peer-focus:ring-success-300"
47
+ checked: "peer-checked:bg-success-600 peer-checked:border-success-600 peer-checked:text-white dark:peer-checked:bg-success-500 dark:peer-checked:border-success-500",
48
+ fill: "bg-success-600 border-success-600 text-white dark:bg-success-500 dark:border-success-500",
49
+ ring: "peer-focus-visible:ring-success-500"
41
50
  },
42
51
  error: {
43
- checked: "peer-checked:bg-error-600 peer-checked:border-error-600 peer-checked:text-white",
44
- ring: "peer-focus:ring-error-300"
52
+ checked: "peer-checked:bg-error-600 peer-checked:border-error-600 peer-checked:text-white dark:peer-checked:bg-error-500 dark:peer-checked:border-error-500",
53
+ fill: "bg-error-600 border-error-600 text-white dark:bg-error-500 dark:border-error-500",
54
+ ring: "peer-focus-visible:ring-error-500"
45
55
  },
46
56
  danger: {
47
- checked: "peer-checked:bg-error-600 peer-checked:border-error-600 peer-checked:text-white",
48
- ring: "peer-focus:ring-error-300"
57
+ checked: "peer-checked:bg-error-600 peer-checked:border-error-600 peer-checked:text-white dark:peer-checked:bg-error-500 dark:peer-checked:border-error-500",
58
+ fill: "bg-error-600 border-error-600 text-white dark:bg-error-500 dark:border-error-500",
59
+ ring: "peer-focus-visible:ring-error-500"
49
60
  },
50
61
  red: {
51
- checked: "peer-checked:bg-error-600 peer-checked:border-error-600 peer-checked:text-white",
52
- ring: "peer-focus:ring-error-300"
62
+ checked: "peer-checked:bg-error-600 peer-checked:border-error-600 peer-checked:text-white dark:peer-checked:bg-error-500 dark:peer-checked:border-error-500",
63
+ fill: "bg-error-600 border-error-600 text-white dark:bg-error-500 dark:border-error-500",
64
+ ring: "peer-focus-visible:ring-error-500"
53
65
  },
54
66
  warning: {
55
- checked: "peer-checked:bg-warning-600 peer-checked:border-warning-600 peer-checked:text-white",
56
- ring: "peer-focus:ring-warning-300"
67
+ checked: "peer-checked:bg-warning-600 peer-checked:border-warning-600 peer-checked:text-white dark:peer-checked:bg-warning-500 dark:peer-checked:border-warning-500",
68
+ fill: "bg-warning-600 border-warning-600 text-white dark:bg-warning-500 dark:border-warning-500",
69
+ ring: "peer-focus-visible:ring-warning-500"
57
70
  },
58
71
  blue: {
59
- checked: "peer-checked:bg-blue-600 peer-checked:border-blue-600 peer-checked:text-white",
60
- ring: "peer-focus:ring-blue-300"
72
+ checked: "peer-checked:bg-blue-600 peer-checked:border-blue-600 peer-checked:text-white dark:peer-checked:bg-blue-500 dark:peer-checked:border-blue-500",
73
+ fill: "bg-blue-600 border-blue-600 text-white dark:bg-blue-500 dark:border-blue-500",
74
+ ring: "peer-focus-visible:ring-blue-500"
61
75
  },
62
76
  indigo: {
63
- checked: "peer-checked:bg-indigo-600 peer-checked:border-indigo-600 peer-checked:text-white",
64
- ring: "peer-focus:ring-indigo-300"
77
+ checked: "peer-checked:bg-indigo-600 peer-checked:border-indigo-600 peer-checked:text-white dark:peer-checked:bg-indigo-500 dark:peer-checked:border-indigo-500",
78
+ fill: "bg-indigo-600 border-indigo-600 text-white dark:bg-indigo-500 dark:border-indigo-500",
79
+ ring: "peer-focus-visible:ring-indigo-500"
65
80
  },
66
81
  purple: {
67
- checked: "peer-checked:bg-purple-600 peer-checked:border-purple-600 peer-checked:text-white",
68
- ring: "peer-focus:ring-purple-300"
82
+ checked: "peer-checked:bg-purple-600 peer-checked:border-purple-600 peer-checked:text-white dark:peer-checked:bg-purple-500 dark:peer-checked:border-purple-500",
83
+ fill: "bg-purple-600 border-purple-600 text-white dark:bg-purple-500 dark:border-purple-500",
84
+ ring: "peer-focus-visible:ring-purple-500"
69
85
  }
70
86
  };
87
+ // The invisible native input is the hit area: the box size with a mouse,
88
+ // 44×44 centred on the box on coarse pointers.
89
+ var HIT_AREA = "absolute left-1/2 top-1/2 h-full w-full -translate-x-1/2 -translate-y-1/2 [@media(pointer:coarse)]:h-11 [@media(pointer:coarse)]:w-11";
90
+ var ICON_MOTION = "pointer-events-none absolute transition-[opacity,transform] duration-fast ease-standard motion-reduce:transition-none";
71
91
  var Checkbox = /*#__PURE__*/forwardRef(function (_ref, ref) {
72
92
  var id = _ref.id,
73
93
  name = _ref.name,
@@ -121,30 +141,28 @@ var Checkbox = /*#__PURE__*/forwardRef(function (_ref, ref) {
121
141
  if (typeof onChange === "function") onChange(e);
122
142
  }, [isControlled, onChange]);
123
143
  var sizeClass = SIZE_CLASSES[size] || SIZE_CLASSES.md;
124
- var wrapperCls = "inline-flex items-center ".concat(className);
125
- var boxOuterCls = "relative inline-flex items-center justify-center rounded-sm border transition-shadow ".concat(sizeClass.box, " ").concat(disabled ? "opacity-60 cursor-not-allowed" : "cursor-pointer hover:shadow-sm", " ");
126
- // Icon classes driven by React state (reliable)
127
- var iconBase = "pointer-events-none transform transition-all duration-150 ease-out";
128
- var iconShown = "opacity-100 scale-100";
129
- var iconHidden = "opacity-0 scale-75";
130
144
  var isPreset = COLOR_PRESETS[color];
131
- var colorClasses = isPreset ? "".concat(isPreset.checked, " peer-focus:ring-2 peer-focus:ring-offset-1 ").concat(isPreset.ring) : "peer-focus:ring-2 peer-focus:ring-offset-1 peer-focus:ring-primary-300";
132
- var customColorStyle = currentChecked && !isPreset && !checkedClass ? {
145
+ var isMixed = Boolean(indeterminate);
146
+ var isPainted = Boolean(currentChecked) || isMixed;
147
+ var customColorStyle = isPainted && !isPreset && !checkedClass ? {
133
148
  backgroundColor: color,
134
149
  borderColor: color,
135
150
  color: "#ffffff"
136
151
  } : {};
137
152
  // The outer box shows its own border unless a checked preset or custom colour paints it.
138
- var showBoxBorder = !(currentChecked && (isPreset || customColorStyle.borderColor));
153
+ var showBoxBorder = !(isPainted && (isPreset || customColorStyle.borderColor));
154
+ var iconState = function iconState(shown) {
155
+ return shown ? "opacity-100 scale-100" : "opacity-0 scale-50";
156
+ };
139
157
  return jsxs("div", _objectSpread2(_objectSpread2({
140
- className: wrapperCls
158
+ className: cn("inline-flex items-center", className)
141
159
  }, wrapperProps !== null && wrapperProps !== void 0 ? wrapperProps : {}), {}, {
142
160
  children: [label && labelPosition === "left" && jsx("label", {
143
161
  htmlFor: inputId,
144
- className: "".concat(sizeClass.label, " select-none mr-2"),
162
+ className: cn(sizeClass.label, "select-none mr-2 text-foreground"),
145
163
  children: label
146
164
  }), jsxs("div", {
147
- className: "".concat(boxOuterCls).concat(!disabled && showBoxBorder && !borderColor ? " border-gray-300" : ""),
165
+ className: cn("relative inline-flex shrink-0 items-center justify-center rounded-sm border transition-shadow duration-fast motion-reduce:transition-none", sizeClass.box, disabled ? "opacity-60 cursor-not-allowed" : "cursor-pointer hover:shadow-sm", !disabled && showBoxBorder && !borderColor && "border-gray-300 dark:border-gray-600"),
148
166
  style: disabled || !showBoxBorder || !borderColor ? {} : {
149
167
  borderColor: borderColor
150
168
  },
@@ -154,7 +172,7 @@ var Checkbox = /*#__PURE__*/forwardRef(function (_ref, ref) {
154
172
  ref: inputRef,
155
173
  name: name,
156
174
  type: "checkbox",
157
- className: "peer absolute inset-0 m-0 p-0 opacity-0 z-10",
175
+ className: cn("peer m-0 p-0 opacity-0 z-10", HIT_AREA, disabled ? "cursor-not-allowed" : "cursor-pointer"),
158
176
  // ensure we set checked only from the boolean (avoid undefined)
159
177
  checked: Boolean(currentChecked),
160
178
  onChange: handleChange,
@@ -162,23 +180,31 @@ var Checkbox = /*#__PURE__*/forwardRef(function (_ref, ref) {
162
180
  required: required,
163
181
  value: value,
164
182
  "aria-label": ariaLabel,
165
- "aria-checked": Boolean(currentChecked)
166
- }, props)), jsx("div", {
167
- className: "pointer-events-none z-0 flex items-center justify-center rounded-sm w-full h-full transition-colors duration-150 ease-in-out " + "".concat(colorClasses, " ") + "".concat(disabled ? "bg-gray-100 border-gray-400 text-primary-400" : "bg-white border-gray-500", " ").concat(checkedClass ? checkedClass : ""),
168
- style: currentChecked && checkedClass ? {
183
+ "aria-checked": isMixed ? "mixed" : Boolean(currentChecked)
184
+ }, props)), jsxs("div", {
185
+ className: cn("pointer-events-none relative z-0 flex items-center justify-center rounded-sm w-full h-full", "transition-colors duration-fast ease-standard motion-reduce:transition-none",
186
+ // Keyboard focus ring drawn on the visual box (the input is invisible).
187
+ "peer-focus-visible:ring-2 peer-focus-visible:ring-offset-1 dark:peer-focus-visible:ring-offset-gray-900", isPreset ? isPreset.ring : "peer-focus-visible:ring-primary-500", disabled ? "bg-gray-100 text-primary-400 dark:bg-gray-800" : "bg-surface", isPreset && isPreset.checked, isPreset && isMixed && !checkedClass && isPreset.fill, checkedClass),
188
+ style: isPainted && checkedClass ? {
169
189
  backgroundColor: "",
170
190
  borderColor: ""
171
191
  } : customColorStyle,
172
192
  "aria-hidden": "true",
173
- children: jsx(Check, {
174
- className: "".concat(sizeClass.icon, " ").concat(iconBase, " ").concat(currentChecked ? iconShown : iconHidden, " ").concat(iconClass || ""),
193
+ children: [jsx(Check, {
194
+ className: cn(sizeClass.icon, ICON_MOTION, iconState(currentChecked && !isMixed), iconClass),
175
195
  strokeWidth: 2.2,
176
- "aria-hidden": "true"
177
- })
196
+ "aria-hidden": "true",
197
+ "data-icon": "check"
198
+ }), jsx(Minus, {
199
+ className: cn(sizeClass.icon, ICON_MOTION, iconState(isMixed), iconClass),
200
+ strokeWidth: 2.6,
201
+ "aria-hidden": "true",
202
+ "data-icon": "indeterminate"
203
+ })]
178
204
  })]
179
205
  }), label && labelPosition === "right" && jsx("label", {
180
206
  htmlFor: inputId,
181
- className: "".concat(sizeClass.label, " select-none ml-2"),
207
+ className: cn(sizeClass.label, "select-none ml-2 text-foreground"),
182
208
  children: label
183
209
  })]
184
210
  }));
@@ -1,8 +1,9 @@
1
1
  'use client';
2
2
  import { objectWithoutProperties as _objectWithoutProperties, slicedToArray as _slicedToArray, objectSpread2 as _objectSpread2 } from '../../_virtual/_rollupPluginBabelHelpers.js';
3
3
  import { jsxs, jsx } from 'react/jsx-runtime';
4
- import { forwardRef, useMemo, useState } from 'react';
4
+ import { forwardRef, useId, useMemo, useState } from 'react';
5
5
  import { cn } from '../../utils/cn.js';
6
+ import Slider from './Slider.js';
6
7
 
7
8
  var _excluded = ["value", "defaultValue", "onChange", "swatches", "showAlpha", "size", "disabled", "className", "label", "id"];
8
9
  /**
@@ -121,6 +122,14 @@ function parseColor(input) {
121
122
  }
122
123
  return null;
123
124
  }
125
+ // Same colour (alpha compared to 3 decimals, like the output string).
126
+ function sameColor(x, y) {
127
+ var _x$a, _y$a;
128
+ if (!x || !y) return false;
129
+ var ax = +((_x$a = x.a) !== null && _x$a !== void 0 ? _x$a : 1).toFixed(3);
130
+ var ay = +((_y$a = y.a) !== null && _y$a !== void 0 ? _y$a : 1).toFixed(3);
131
+ return x.r === y.r && x.g === y.g && x.b === y.b && ax === ay;
132
+ }
124
133
  function rgbaToString(_ref2) {
125
134
  var r = _ref2.r,
126
135
  g = _ref2.g,
@@ -132,7 +141,7 @@ function rgbaToString(_ref2) {
132
141
  }
133
142
  // ---------- Component ----------
134
143
  var ColorPicker = /*#__PURE__*/forwardRef(function ColorPicker(props, ref) {
135
- var _internal$a, _internal$a2;
144
+ var _internal$a;
136
145
  var value = props.value,
137
146
  defaultValue = props.defaultValue,
138
147
  onChange = props.onChange,
@@ -150,6 +159,12 @@ var ColorPicker = /*#__PURE__*/forwardRef(function ColorPicker(props, ref) {
150
159
  label = _props$label === void 0 ? "Pick a color" : _props$label,
151
160
  id = props.id,
152
161
  rest = _objectWithoutProperties(props, _excluded);
162
+ // The visible <label> must point at a real control: the text value input
163
+ // takes the caller's `id` (or a generated one).
164
+ var autoId = useId();
165
+ var inputId = id !== null && id !== void 0 ? id : "".concat(autoId, "color-input");
166
+ var labelId = "".concat(inputId, "-label");
167
+ var valueLabelId = "".concat(inputId, "-value-label");
153
168
  var parsedControlled = useMemo(function () {
154
169
  return parseColor(value !== null && value !== void 0 ? value : null);
155
170
  }, [value]);
@@ -212,13 +227,16 @@ var ColorPicker = /*#__PURE__*/forwardRef(function ColorPicker(props, ref) {
212
227
  className: cn("inline-block", className)
213
228
  }, rest), {}, {
214
229
  children: [jsx("label", {
230
+ id: labelId,
215
231
  className: "block mb-2 ".concat(sizeMap.font),
216
- htmlFor: id,
232
+ htmlFor: inputId,
217
233
  children: label
218
234
  }), jsxs("div", {
219
235
  className: "flex ".concat(sizeMap.gap, " items-start flex-col"),
220
- children: [jsx("div", {
236
+ children: [swatches.length > 0 && jsx("div", {
221
237
  className: "flex flex-wrap ".concat(sizeMap.gap),
238
+ role: "group",
239
+ "aria-label": "Swatches",
222
240
  children: swatches.map(function (s, i) {
223
241
  var _parseColor;
224
242
  var c = (_parseColor = parseColor(s)) !== null && _parseColor !== void 0 ? _parseColor : {
@@ -227,38 +245,41 @@ var ColorPicker = /*#__PURE__*/forwardRef(function ColorPicker(props, ref) {
227
245
  b: 0,
228
246
  a: 1
229
247
  };
248
+ var selected = sameColor(c, internal);
230
249
  return jsx("button", {
231
250
  type: "button",
232
251
  "aria-label": "Select ".concat(s),
252
+ "aria-pressed": selected,
233
253
  disabled: disabled,
234
254
  onClick: function onClick() {
235
255
  return pushChange(c);
236
256
  },
237
- className: "rounded-md border border-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-1 focus:ring-indigo-500 ".concat(sizeMap.swatch, " overflow-hidden"),
257
+ className: cn("rounded-md border border-line overflow-hidden transition-shadow duration-fast motion-reduce:transition-none", "focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-primary-500 dark:focus-visible:ring-offset-gray-900", "disabled:cursor-not-allowed disabled:opacity-50", selected && "ring-2 ring-offset-2 ring-primary-500 dark:ring-offset-gray-900", sizeMap.swatch),
238
258
  style: {
239
259
  background: "rgba(".concat(c.r, ",").concat(c.g, ",").concat(c.b, ",").concat(c.a, ")")
240
260
  },
241
- title: s
261
+ title: s,
262
+ "data-selected": selected || undefined
242
263
  }, i);
243
264
  })
244
265
  }), jsxs("div", {
245
- className: "flex-1 min-w-[220px]",
266
+ className: "w-full sm:flex-1 sm:min-w-[220px]",
246
267
  children: [jsxs("div", {
247
- className: "flex items-center gap-3",
268
+ className: "flex flex-wrap items-center gap-3",
248
269
  children: [jsxs("div", {
249
270
  className: "flex items-center gap-2",
250
271
  children: [jsx("div", {
251
- className: "rounded-md border border-gray-200 overflow-hidden w-12 h-12 flex items-center justify-center",
272
+ className: "rounded-md border border-line overflow-hidden w-12 h-12 flex items-center justify-center",
252
273
  style: {
253
274
  background: "rgba(".concat(internal.r, ", ").concat(internal.g, ", ").concat(internal.b, ", ").concat(internal.a, ")")
254
275
  }
255
276
  }), jsxs("div", {
256
277
  className: "flex flex-col",
257
278
  children: [jsx("div", {
258
- className: "text-xs text-primary-500",
279
+ className: "text-xs text-primary-500 dark:text-primary-300",
259
280
  children: "Current"
260
281
  }), jsx("div", {
261
- className: "font-mono text-sm",
282
+ className: "font-mono text-sm text-foreground",
262
283
  children: rgbaToHex(internal, showAlpha)
263
284
  })]
264
285
  })]
@@ -274,9 +295,14 @@ var ColorPicker = /*#__PURE__*/forwardRef(function ColorPicker(props, ref) {
274
295
  },
275
296
  className: "w-10 h-10 p-0 border-none bg-transparent",
276
297
  "aria-label": "Native color input"
277
- }), jsx("div", {
278
- className: "flex-1",
279
- children: jsx("input", {
298
+ }), jsxs("div", {
299
+ className: "min-w-[8rem] flex-1",
300
+ children: [jsx("span", {
301
+ id: valueLabelId,
302
+ className: "sr-only",
303
+ children: "Color value"
304
+ }), jsx("input", {
305
+ id: inputId,
280
306
  type: "text",
281
307
  disabled: disabled,
282
308
  value: showAlpha ? rgbaToString(internal) : rgbaToHex(internal, showAlpha),
@@ -291,17 +317,21 @@ var ColorPicker = /*#__PURE__*/forwardRef(function ColorPicker(props, ref) {
291
317
  }));
292
318
  }
293
319
  },
294
- className: "w-full rounded-md border border-gray-200 px-2 py-1 font-mono",
295
- "aria-label": "Color value"
296
- })
320
+ className: "w-full rounded-md border border-line bg-surface px-2 py-1 font-mono text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 disabled:opacity-50",
321
+ "aria-labelledby": "".concat(labelId, " ").concat(valueLabelId)
322
+ })]
297
323
  })]
298
324
  }), jsx("div", {
299
325
  className: "mt-3 space-y-2",
300
326
  children: showAlpha && jsx(Slider, {
301
- label: "Alpha (".concat(Math.round(((_internal$a = internal.a) !== null && _internal$a !== void 0 ? _internal$a : 1) * 100), "%)"),
327
+ label: "Alpha",
328
+ showValue: true,
329
+ formatValue: function formatValue(v) {
330
+ return "".concat(v, "%");
331
+ },
302
332
  min: 0,
303
333
  max: 100,
304
- value: Math.round(((_internal$a2 = internal.a) !== null && _internal$a2 !== void 0 ? _internal$a2 : 1) * 100),
334
+ value: Math.round(((_internal$a = internal.a) !== null && _internal$a !== void 0 ? _internal$a : 1) * 100),
305
335
  onChange: function onChange(v) {
306
336
  pushChange(_objectSpread2(_objectSpread2({}, internal), {}, {
307
337
  a: clamp(v / 100, 0, 1)
@@ -315,34 +345,5 @@ var ColorPicker = /*#__PURE__*/forwardRef(function ColorPicker(props, ref) {
315
345
  }));
316
346
  });
317
347
  ColorPicker.displayName = "ColorPicker";
318
- function Slider(_ref4) {
319
- var label = _ref4.label,
320
- min = _ref4.min,
321
- max = _ref4.max,
322
- value = _ref4.value,
323
- _onChange = _ref4.onChange,
324
- disabled = _ref4.disabled;
325
- return jsxs("div", {
326
- className: "flex items-center gap-3",
327
- children: [jsx("div", {
328
- className: "w-36 text-sm text-primary-600",
329
- children: label
330
- }), jsx("input", {
331
- type: "range",
332
- min: min,
333
- max: max,
334
- value: value,
335
- disabled: disabled,
336
- onChange: function onChange(e) {
337
- return _onChange(Number(e.target.value));
338
- },
339
- className: "w-full h-2 rounded-lg appearance-none bg-gray-200",
340
- "aria-label": label
341
- }), jsx("div", {
342
- className: "w-14 text-right font-mono text-sm",
343
- children: value
344
- })]
345
- });
346
- }
347
348
 
348
349
  export { ColorPicker as default };