@dreamtree-org/twreact-ui 1.3.10 → 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 (93) hide show
  1. package/README.md +30 -0
  2. package/ai-skills/dreamtree-ui.md +12 -7
  3. package/dist/ai-schema.json +137 -21
  4. package/dist/charts/index.js +195 -66
  5. package/dist/components/charts/CartesianChart.d.ts.map +1 -1
  6. package/dist/components/charts/ChartFrame.d.ts.map +1 -1
  7. package/dist/components/charts/useChartLayout.d.ts +25 -0
  8. package/dist/components/charts/useChartLayout.d.ts.map +1 -0
  9. package/dist/components/core/Button.d.ts.map +1 -1
  10. package/dist/components/core/Checkbox.d.ts.map +1 -1
  11. package/dist/components/core/ColorPicker.d.ts.map +1 -1
  12. package/dist/components/core/Input.d.ts.map +1 -1
  13. package/dist/components/core/Loader.d.ts.map +1 -1
  14. package/dist/components/core/LocationPicker.d.ts.map +1 -1
  15. package/dist/components/core/ProgressBar.d.ts.map +1 -1
  16. package/dist/components/core/Radio.d.ts.map +1 -1
  17. package/dist/components/core/Rate.d.ts.map +1 -1
  18. package/dist/components/core/RoundedTag.d.ts.map +1 -1
  19. package/dist/components/core/Skeleton.d.ts.map +1 -1
  20. package/dist/components/core/Slider.d.ts.map +1 -1
  21. package/dist/components/core/SpeechToText.d.ts.map +1 -1
  22. package/dist/components/core/Switch.d.ts.map +1 -1
  23. package/dist/components/core/Tabs.d.ts +2 -5
  24. package/dist/components/core/Tabs.d.ts.map +1 -1
  25. package/dist/components/core/Textarea.d.ts.map +1 -1
  26. package/dist/components/navigation/Breadcrumbs.d.ts.map +1 -1
  27. package/dist/components/navigation/FootNav.d.ts.map +1 -1
  28. package/dist/components/utility/Avatar.d.ts +8 -0
  29. package/dist/components/utility/Avatar.d.ts.map +1 -1
  30. package/dist/components/utility/AvatarGroup.d.ts +4 -0
  31. package/dist/components/utility/AvatarGroup.d.ts.map +1 -0
  32. package/dist/components/utility/Badge.d.ts.map +1 -1
  33. package/dist/components/utility/Card.d.ts.map +1 -1
  34. package/dist/components/utility/Carousel.d.ts.map +1 -1
  35. package/dist/components/utility/FileUpload.d.ts.map +1 -1
  36. package/dist/components/utility/Pagination.d.ts.map +1 -1
  37. package/dist/components/utility/Sortable.d.ts.map +1 -1
  38. package/dist/components/utility/Stepper.d.ts.map +1 -1
  39. package/dist/esm/components/blocks/EmptyState.js +1 -1
  40. package/dist/esm/components/blocks/ErrorState.js +1 -1
  41. package/dist/esm/components/blocks/LoadingState.js +4 -4
  42. package/dist/esm/components/blocks/StatCard.js +6 -6
  43. package/dist/esm/components/blocks/StatusPill.js +5 -8
  44. package/dist/esm/components/charts/CartesianChart.js +85 -62
  45. package/dist/esm/components/charts/ChartFrame.js +12 -5
  46. package/dist/esm/components/charts/useChartLayout.js +105 -0
  47. package/dist/esm/components/core/Button.js +49 -25
  48. package/dist/esm/components/core/Checkbox.js +71 -45
  49. package/dist/esm/components/core/ColorPicker.js +31 -42
  50. package/dist/esm/components/core/Input.js +89 -22
  51. package/dist/esm/components/core/Loader.js +30 -44
  52. package/dist/esm/components/core/LocationPicker.js +82 -16
  53. package/dist/esm/components/core/ProgressBar.js +8 -12
  54. package/dist/esm/components/core/Radio.js +10 -12
  55. package/dist/esm/components/core/Rate.js +72 -4
  56. package/dist/esm/components/core/RoundedTag.js +5 -5
  57. package/dist/esm/components/core/Skeleton.js +13 -14
  58. package/dist/esm/components/core/Slider.js +24 -6
  59. package/dist/esm/components/core/SpeechToText.js +68 -16
  60. package/dist/esm/components/core/Switch.js +88 -16
  61. package/dist/esm/components/core/Tabs.js +180 -33
  62. package/dist/esm/components/core/Textarea.js +10 -2
  63. package/dist/esm/components/navigation/Breadcrumbs.js +97 -35
  64. package/dist/esm/components/navigation/FootNav.js +102 -13
  65. package/dist/esm/components/utility/Avatar.js +14 -12
  66. package/dist/esm/components/utility/AvatarGroup.js +56 -0
  67. package/dist/esm/components/utility/Badge.js +43 -23
  68. package/dist/esm/components/utility/Card.js +32 -8
  69. package/dist/esm/components/utility/Carousel.js +139 -29
  70. package/dist/esm/components/utility/FileUpload.js +106 -31
  71. package/dist/esm/components/utility/Pagination.js +31 -13
  72. package/dist/esm/components/utility/Sortable.js +6 -2
  73. package/dist/esm/components/utility/Stepper.js +110 -47
  74. package/dist/esm/hooks/useSliderValues.js +8 -2
  75. package/dist/esm/hooks/useSortable.js +107 -5
  76. package/dist/esm/index.js +1 -0
  77. package/dist/esm/utils/touchTarget.js +4 -1
  78. package/dist/hooks/useSliderValues.d.ts +6 -2
  79. package/dist/hooks/useSliderValues.d.ts.map +1 -1
  80. package/dist/hooks/useSortable.d.ts +11 -1
  81. package/dist/hooks/useSortable.d.ts.map +1 -1
  82. package/dist/index.css +1 -1
  83. package/dist/index.d.ts +3 -2
  84. package/dist/index.esm.css +1 -1
  85. package/dist/index.esm.js +2341 -1191
  86. package/dist/index.js +2341 -1190
  87. package/dist/types/charts.d.ts +3 -1
  88. package/dist/types/charts.d.ts.map +1 -1
  89. package/dist/types/index.d.ts +56 -7
  90. package/dist/types/index.d.ts.map +1 -1
  91. package/doc/README.md +1 -0
  92. package/mcp/catalog.snapshot.json +279 -80
  93. package/package.json +1 -1
@@ -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
  }));
@@ -3,6 +3,7 @@ import { objectWithoutProperties as _objectWithoutProperties, slicedToArray as _
3
3
  import { jsxs, jsx } from 'react/jsx-runtime';
4
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,
@@ -224,8 +233,10 @@ var ColorPicker = /*#__PURE__*/forwardRef(function ColorPicker(props, ref) {
224
233
  children: label
225
234
  }), jsxs("div", {
226
235
  className: "flex ".concat(sizeMap.gap, " items-start flex-col"),
227
- children: [jsx("div", {
236
+ children: [swatches.length > 0 && jsx("div", {
228
237
  className: "flex flex-wrap ".concat(sizeMap.gap),
238
+ role: "group",
239
+ "aria-label": "Swatches",
229
240
  children: swatches.map(function (s, i) {
230
241
  var _parseColor;
231
242
  var c = (_parseColor = parseColor(s)) !== null && _parseColor !== void 0 ? _parseColor : {
@@ -234,38 +245,41 @@ var ColorPicker = /*#__PURE__*/forwardRef(function ColorPicker(props, ref) {
234
245
  b: 0,
235
246
  a: 1
236
247
  };
248
+ var selected = sameColor(c, internal);
237
249
  return jsx("button", {
238
250
  type: "button",
239
251
  "aria-label": "Select ".concat(s),
252
+ "aria-pressed": selected,
240
253
  disabled: disabled,
241
254
  onClick: function onClick() {
242
255
  return pushChange(c);
243
256
  },
244
- className: "rounded-md border border-gray-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-primary-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),
245
258
  style: {
246
259
  background: "rgba(".concat(c.r, ",").concat(c.g, ",").concat(c.b, ",").concat(c.a, ")")
247
260
  },
248
- title: s
261
+ title: s,
262
+ "data-selected": selected || undefined
249
263
  }, i);
250
264
  })
251
265
  }), jsxs("div", {
252
- className: "flex-1 min-w-[220px]",
266
+ className: "w-full sm:flex-1 sm:min-w-[220px]",
253
267
  children: [jsxs("div", {
254
- className: "flex items-center gap-3",
268
+ className: "flex flex-wrap items-center gap-3",
255
269
  children: [jsxs("div", {
256
270
  className: "flex items-center gap-2",
257
271
  children: [jsx("div", {
258
- 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",
259
273
  style: {
260
274
  background: "rgba(".concat(internal.r, ", ").concat(internal.g, ", ").concat(internal.b, ", ").concat(internal.a, ")")
261
275
  }
262
276
  }), jsxs("div", {
263
277
  className: "flex flex-col",
264
278
  children: [jsx("div", {
265
- className: "text-xs text-primary-500",
279
+ className: "text-xs text-primary-500 dark:text-primary-300",
266
280
  children: "Current"
267
281
  }), jsx("div", {
268
- className: "font-mono text-sm",
282
+ className: "font-mono text-sm text-foreground",
269
283
  children: rgbaToHex(internal, showAlpha)
270
284
  })]
271
285
  })]
@@ -282,7 +296,7 @@ var ColorPicker = /*#__PURE__*/forwardRef(function ColorPicker(props, ref) {
282
296
  className: "w-10 h-10 p-0 border-none bg-transparent",
283
297
  "aria-label": "Native color input"
284
298
  }), jsxs("div", {
285
- className: "flex-1",
299
+ className: "min-w-[8rem] flex-1",
286
300
  children: [jsx("span", {
287
301
  id: valueLabelId,
288
302
  className: "sr-only",
@@ -303,17 +317,21 @@ var ColorPicker = /*#__PURE__*/forwardRef(function ColorPicker(props, ref) {
303
317
  }));
304
318
  }
305
319
  },
306
- className: "w-full rounded-md border border-gray-200 px-2 py-1 font-mono",
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",
307
321
  "aria-labelledby": "".concat(labelId, " ").concat(valueLabelId)
308
322
  })]
309
323
  })]
310
324
  }), jsx("div", {
311
325
  className: "mt-3 space-y-2",
312
326
  children: showAlpha && jsx(Slider, {
313
- 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
+ },
314
332
  min: 0,
315
333
  max: 100,
316
- 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),
317
335
  onChange: function onChange(v) {
318
336
  pushChange(_objectSpread2(_objectSpread2({}, internal), {}, {
319
337
  a: clamp(v / 100, 0, 1)
@@ -327,34 +345,5 @@ var ColorPicker = /*#__PURE__*/forwardRef(function ColorPicker(props, ref) {
327
345
  }));
328
346
  });
329
347
  ColorPicker.displayName = "ColorPicker";
330
- function Slider(_ref4) {
331
- var label = _ref4.label,
332
- min = _ref4.min,
333
- max = _ref4.max,
334
- value = _ref4.value,
335
- _onChange = _ref4.onChange,
336
- disabled = _ref4.disabled;
337
- return jsxs("div", {
338
- className: "flex items-center gap-3",
339
- children: [jsx("div", {
340
- className: "w-36 text-sm text-primary-600",
341
- children: label
342
- }), jsx("input", {
343
- type: "range",
344
- min: min,
345
- max: max,
346
- value: value,
347
- disabled: disabled,
348
- onChange: function onChange(e) {
349
- return _onChange(Number(e.target.value));
350
- },
351
- className: "w-full h-2 rounded-lg appearance-none bg-gray-200",
352
- "aria-label": label
353
- }), jsx("div", {
354
- className: "w-14 text-right font-mono text-sm",
355
- children: value
356
- })]
357
- });
358
- }
359
348
 
360
349
  export { ColorPicker as default };
@@ -1,11 +1,52 @@
1
1
  'use client';
2
2
  import { objectWithoutProperties as _objectWithoutProperties, slicedToArray as _slicedToArray, objectSpread2 as _objectSpread2 } from '../../_virtual/_rollupPluginBabelHelpers.js';
3
- import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
4
4
  import { forwardRef, useId, useRef, useCallback, useState } from 'react';
5
5
  import { Search, EyeOff, Eye, X } from 'lucide-react';
6
6
  import { cn } from '../../utils/cn.js';
7
+ import { TOUCH_TARGET } from '../../utils/touchTarget.js';
7
8
 
8
- var _excluded = ["id", "className", "type", "label", "placeholder", "error", "success", "helperText", "disabled", "required", "clearable", "icon", "iconPosition", "onClear", "showCount", "maxLength", "readOnly", "onChange", "value", "defaultValue"];
9
+ var _excluded = ["id", "className", "type", "label", "placeholder", "error", "success", "helperText", "disabled", "required", "clearable", "icon", "iconPosition", "onClear", "showCount", "maxLength", "readOnly", "onChange", "value", "defaultValue", "size", "floatingLabel", "otp"];
10
+ // Field heights. `md` keeps its 40px desktop look and grows to the 44px touch
11
+ // target on coarse pointers (same rule as Select); `lg` is 44px everywhere.
12
+ var SIZE_CLASSES = {
13
+ sm: "h-8 text-xs",
14
+ md: "h-10 text-sm leading-10 [@media(pointer:coarse)]:h-11",
15
+ lg: "h-11 text-base"
16
+ };
17
+ // Taller fields for `floatingLabel`, with top padding so the value sits below
18
+ // the floated label.
19
+ var FLOATING_SIZE_CLASSES = {
20
+ sm: "h-10 pt-3 text-sm",
21
+ md: "h-12 pt-4 text-sm",
22
+ lg: "h-14 pt-5 text-base"
23
+ };
24
+ // Mobile keyboard / autofill hints for native types. Applied only when the
25
+ // caller did not pass the attribute themselves.
26
+ var TYPE_HINTS = {
27
+ tel: {
28
+ inputMode: "tel",
29
+ autoComplete: "tel"
30
+ },
31
+ email: {
32
+ inputMode: "email",
33
+ autoComplete: "email"
34
+ },
35
+ url: {
36
+ inputMode: "url",
37
+ autoComplete: "url"
38
+ },
39
+ search: {
40
+ inputMode: "search",
41
+ enterKeyHint: "search"
42
+ }
43
+ };
44
+ var OTP_HINTS = {
45
+ inputMode: "numeric",
46
+ autoComplete: "one-time-code"
47
+ };
48
+ // Password toggle / clear: real buttons that reach 44×44 on coarse pointers.
49
+ var ADORNMENT_BUTTON = cn("absolute right-1 top-1/2 -translate-y-1/2 inline-flex h-8 w-8 items-center justify-center rounded-md", "text-primary-500 hover:text-primary-700 dark:text-primary-400 dark:hover:text-primary-300", "transition-colors duration-fast ease-standard motion-reduce:transition-none", "focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500", "disabled:cursor-not-allowed disabled:opacity-50", TOUCH_TARGET);
9
50
  var Input = /*#__PURE__*/forwardRef(function (props, forwardedRef) {
10
51
  var id = props.id,
11
52
  className = props.className,
@@ -34,7 +75,25 @@ var Input = /*#__PURE__*/forwardRef(function (props, forwardedRef) {
34
75
  onChange = props.onChange,
35
76
  value = props.value,
36
77
  defaultValue = props.defaultValue,
78
+ _props$size = props.size,
79
+ sizeProp = _props$size === void 0 ? "md" : _props$size,
80
+ _props$floatingLabel = props.floatingLabel,
81
+ floatingLabel = _props$floatingLabel === void 0 ? false : _props$floatingLabel,
82
+ _props$otp = props.otp,
83
+ otp = _props$otp === void 0 ? false : _props$otp,
37
84
  rest = _objectWithoutProperties(props, _excluded);
85
+ // `size` is the field size (sm|md|lg). A numeric value is the native
86
+ // `<input size>` attribute and is passed through unchanged.
87
+ var size = SIZE_CLASSES[sizeProp] ? sizeProp : "md";
88
+ var nativeSize = SIZE_CLASSES[sizeProp] ? undefined : sizeProp;
89
+ var hints = otp ? OTP_HINTS : TYPE_HINTS[type];
90
+ var hintProps = {};
91
+ if (hints) {
92
+ Object.keys(hints).forEach(function (key) {
93
+ if (!(key in rest)) hintProps[key] = hints[key];
94
+ });
95
+ }
96
+ var isFloating = Boolean(floatingLabel && label);
38
97
  var internalId = useId();
39
98
  var inputId = id || "input-".concat(internalId);
40
99
  var isSearch = type === "search";
@@ -88,21 +147,22 @@ var Input = /*#__PURE__*/forwardRef(function (props, forwardedRef) {
88
147
  var leftHasIcon = Boolean(icon && iconPosition === "left") || isSearch;
89
148
  var rightHasIcon = Boolean(icon && iconPosition === "right") || isPassword || clearable && currentLength > 0;
90
149
  var paddingClass = cn(leftHasIcon ? "pl-10" : "pl-3", rightHasIcon ? "pr-12" : "pr-3");
91
- var baseClasses = cn("w-full rounded-md border bg-white text-sm placeholder-gray-400 disabled:opacity-50", "h-10 leading-10",
92
- // consistent height
93
- paddingClass, error ? "border-red-500 focus:ring-red-500" : "border-gray-300", success ? "border-green-500 focus:ring-green-500" : "",
94
- // Neutral state needs a visible keyboard focus indicator too.
95
- !error && !success && "focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500", className);
150
+ var baseClasses = cn("w-full rounded-md border bg-surface text-foreground placeholder-gray-400 dark:placeholder-gray-500", "transition-colors duration-fast ease-standard motion-reduce:transition-none", "disabled:cursor-not-allowed disabled:opacity-50", isFloating ? FLOATING_SIZE_CLASSES[size] : SIZE_CLASSES[size],
151
+ // The floated label replaces the placeholder until the field is focused.
152
+ isFloating && "peer placeholder-transparent focus:placeholder-gray-400 dark:focus:placeholder-gray-500", paddingClass, "focus:outline-none focus-visible:ring-2", error ? "border-error-500 focus-visible:ring-error-500 dark:border-error-400" : success ? "border-success-500 focus-visible:ring-success-500 dark:border-success-400" : "border-gray-300 focus-visible:ring-primary-500 dark:border-gray-600", className);
153
+ var labelContent = jsxs(Fragment, {
154
+ children: [label, required && jsx("span", {
155
+ className: "text-error-500 ml-1",
156
+ children: "*"
157
+ })]
158
+ });
96
159
  var inputType = isPassword && showPassword ? "text" : type;
97
160
  return jsxs("div", {
98
161
  className: "w-full",
99
- children: [label && jsxs("label", {
162
+ children: [label && !isFloating && jsx("label", {
100
163
  htmlFor: inputId,
101
- className: "block text-sm font-medium mb-1",
102
- children: [label, required && jsx("span", {
103
- className: "text-error-500 ml-1",
104
- children: "*"
105
- })]
164
+ className: "block text-sm font-medium mb-1 text-foreground",
165
+ children: labelContent
106
166
  }), jsxs("div", {
107
167
  className: "relative",
108
168
  children: [leftHasIcon && jsx("div", {
@@ -113,12 +173,13 @@ var Input = /*#__PURE__*/forwardRef(function (props, forwardedRef) {
113
173
  className: "text-primary-400",
114
174
  children: icon
115
175
  })
116
- }), jsx("input", _objectSpread2({
176
+ }), jsx("input", _objectSpread2(_objectSpread2({
117
177
  id: inputId,
118
178
  ref: setRefs,
119
179
  type: inputType,
120
180
  className: baseClasses,
121
- placeholder: placeholder,
181
+ // :placeholder-shown drives the floating label, so it needs a placeholder.
182
+ placeholder: isFloating ? placeholder || " " : placeholder,
122
183
  disabled: disabled,
123
184
  readOnly: readOnly,
124
185
  "aria-invalid": !!error,
@@ -126,13 +187,19 @@ var Input = /*#__PURE__*/forwardRef(function (props, forwardedRef) {
126
187
  maxLength: maxLength,
127
188
  onChange: handleChange,
128
189
  value: value,
129
- defaultValue: defaultValue
130
- }, rest)), icon && iconPosition === "right" && !isPassword && !(clearable && currentLength > 0) && jsx("div", {
190
+ defaultValue: defaultValue,
191
+ size: nativeSize
192
+ }, hintProps), rest)), isFloating && jsx("label", {
193
+ htmlFor: inputId,
194
+ "data-floating-label": "",
195
+ className: cn("pointer-events-none absolute top-1 origin-left text-xs font-medium text-muted", leftHasIcon ? "left-10" : "left-3", "transition-[top,transform,font-size,color] duration-fast ease-standard motion-reduce:transition-none", "peer-placeholder-shown:top-1/2 peer-placeholder-shown:-translate-y-1/2 peer-placeholder-shown:text-sm", "peer-focus:top-1 peer-focus:translate-y-0 peer-focus:text-xs peer-focus:text-primary-600 dark:peer-focus:text-primary-400"),
196
+ children: labelContent
197
+ }), icon && iconPosition === "right" && !isPassword && !(clearable && currentLength > 0) && jsx("div", {
131
198
  className: "absolute right-3 top-1/2 transform -translate-y-1/2 pointer-events-none text-primary-400",
132
199
  children: icon
133
200
  }), isPassword && jsx("button", {
134
201
  type: "button",
135
- className: "absolute right-3 top-1/2 transform -translate-y-1/2 text-primary-500",
202
+ className: ADORNMENT_BUTTON,
136
203
  onClick: function onClick() {
137
204
  return setShowPassword(function (s) {
138
205
  return !s;
@@ -147,7 +214,7 @@ var Input = /*#__PURE__*/forwardRef(function (props, forwardedRef) {
147
214
  })
148
215
  }), clearable && currentLength > 0 && !isPassword && jsx("button", {
149
216
  type: "button",
150
- className: "absolute right-3 top-1/2 transform -translate-y-1/2 text-primary-500",
217
+ className: ADORNMENT_BUTTON,
151
218
  onClick: handleClear,
152
219
  "aria-label": "Clear input",
153
220
  disabled: disabled,
@@ -157,12 +224,12 @@ var Input = /*#__PURE__*/forwardRef(function (props, forwardedRef) {
157
224
  })]
158
225
  }), error && jsx("p", {
159
226
  id: "".concat(inputId, "-error"),
160
- className: "mt-1 text-sm text-error-600",
227
+ className: "mt-1 text-sm text-error-600 dark:text-error-400",
161
228
  "aria-live": "polite",
162
229
  children: error
163
230
  }), !error && success && jsx("p", {
164
231
  id: "".concat(inputId, "-success"),
165
- className: "mt-1 text-sm text-success-600",
232
+ className: "mt-1 text-sm text-success-600 dark:text-success-400",
166
233
  "aria-live": "polite",
167
234
  children: success
168
235
  }), !error && !success && helperText && jsx("p", {
@@ -170,7 +237,7 @@ var Input = /*#__PURE__*/forwardRef(function (props, forwardedRef) {
170
237
  className: "mt-1 text-sm text-muted",
171
238
  children: helperText
172
239
  }), showCount && maxLength && jsxs("div", {
173
- className: "mt-1 text-right text-xs text-primary-500",
240
+ className: "mt-1 text-right text-xs text-primary-500 dark:text-primary-400",
174
241
  "aria-hidden": true,
175
242
  children: [currentLength, "/", maxLength]
176
243
  })]