@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
@@ -3,9 +3,15 @@ import { objectWithoutProperties as _objectWithoutProperties, objectSpread2 as _
3
3
  import { jsxs, jsx } from 'react/jsx-runtime';
4
4
  import React from 'react';
5
5
  import { cn } from '../../utils/cn.js';
6
+ import { TOUCH_TARGET } from '../../utils/touchTarget.js';
6
7
  import { ChevronLeft, ChevronRight } from 'lucide-react';
7
8
 
8
9
  var _excluded = ["currentPage", "totalPages", "onPageChange", "showFirstLast", "showPrevNext", "maxVisiblePages", "className"];
10
+ // Shared button chrome: dark-safe surface, focus-visible ring, 44px target on coarse pointers.
11
+ var BUTTON_BASE = cn('inline-flex items-center justify-center rounded-md border px-3 py-2 text-sm font-medium', 'transition-colors duration-fast motion-reduce:transition-none', 'outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-gray-900', TOUCH_TARGET);
12
+ var BUTTON_IDLE = 'border-gray-300 bg-white hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-100 dark:hover:bg-gray-800';
13
+ var BUTTON_ACTIVE = 'border-primary-500 bg-primary-50 text-primary-600 dark:bg-primary-950 dark:text-primary-300';
14
+ var DISABLED = 'disabled:opacity-50 disabled:cursor-not-allowed';
9
15
  var Pagination = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
10
16
  var _ref$currentPage = _ref.currentPage,
11
17
  currentPage = _ref$currentPage === void 0 ? 1 : _ref$currentPage,
@@ -45,37 +51,46 @@ var Pagination = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
45
51
  }
46
52
  };
47
53
  if (totalPages <= 1) return null;
54
+ // Below `sm` the trail collapses to "‹ Page 3 of 10 ›" — only when there are
55
+ // prev/next buttons to drive it; otherwise the full layout shows everywhere.
56
+ var compact = showPrevNext;
57
+ var fullOnly = compact ? 'hidden sm:inline-flex' : '';
48
58
  return jsxs("nav", _objectSpread2(_objectSpread2({
49
59
  ref: ref,
50
- className: cn('flex items-center justify-center space-x-1', className),
60
+ className: cn('flex flex-wrap items-center justify-center gap-1', className),
51
61
  "aria-label": "Pagination"
52
62
  }, props), {}, {
53
- children: [showFirstLast && currentPage > 1 && jsx("button", {
54
- className: "rounded-md border border-gray-300 bg-white px-3 py-2 text-sm font-medium hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2",
63
+ children: [showFirstLast && visiblePages[0] > 1 && jsx("button", {
64
+ type: "button",
65
+ className: cn(BUTTON_BASE, BUTTON_IDLE, fullOnly),
55
66
  onClick: function onClick() {
56
67
  return handlePageClick(1);
57
68
  },
58
69
  "aria-label": "Go to first page",
59
70
  children: "1"
60
71
  }), showPrevNext && jsx("button", {
61
- className: "rounded-md border border-gray-300 bg-white px-3 py-2 text-sm font-medium hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed",
72
+ type: "button",
73
+ className: cn(BUTTON_BASE, BUTTON_IDLE, DISABLED),
62
74
  onClick: function onClick() {
63
75
  return handlePageClick(currentPage - 1);
64
76
  },
65
77
  disabled: currentPage === 1,
66
78
  "aria-label": "Go to previous page",
67
79
  children: jsx(ChevronLeft, {
68
- className: "h-4 w-4"
80
+ className: "h-4 w-4",
81
+ "aria-hidden": "true"
69
82
  })
83
+ }), compact && jsxs("span", {
84
+ className: "px-2 text-sm font-medium tabular-nums text-foreground sm:hidden",
85
+ "data-pagination-compact": "",
86
+ children: ["Page ", currentPage, " of ", totalPages]
70
87
  }), showFirstLast && visiblePages[0] > 2 && jsx("span", {
71
- className: "px-3 py-2 text-sm text-primary-500",
88
+ className: cn('px-3 py-2 text-sm text-primary-500 dark:text-primary-400', fullOnly),
72
89
  children: "..."
73
90
  }), visiblePages.map(function (page) {
74
91
  return jsx("button", {
75
- className: cn('rounded-md border px-3 py-2 text-sm font-medium focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2', {
76
- 'border-primary-500 bg-primary-50 text-primary-600': page === currentPage,
77
- 'border-gray-300 bg-white hover:bg-gray-50': page !== currentPage
78
- }),
92
+ type: "button",
93
+ className: cn(BUTTON_BASE, page === currentPage ? BUTTON_ACTIVE : BUTTON_IDLE, fullOnly),
79
94
  onClick: function onClick() {
80
95
  return handlePageClick(page);
81
96
  },
@@ -84,20 +99,23 @@ var Pagination = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
84
99
  children: page
85
100
  }, page);
86
101
  }), showFirstLast && visiblePages[visiblePages.length - 1] < totalPages - 1 && jsx("span", {
87
- className: "px-3 py-2 text-sm text-primary-500",
102
+ className: cn('px-3 py-2 text-sm text-primary-500 dark:text-primary-400', fullOnly),
88
103
  children: "..."
89
104
  }), showPrevNext && jsx("button", {
90
- className: "rounded-md border border-gray-300 bg-white px-3 py-2 text-sm font-medium hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed",
105
+ type: "button",
106
+ className: cn(BUTTON_BASE, BUTTON_IDLE, DISABLED),
91
107
  onClick: function onClick() {
92
108
  return handlePageClick(currentPage + 1);
93
109
  },
94
110
  disabled: currentPage === totalPages,
95
111
  "aria-label": "Go to next page",
96
112
  children: jsx(ChevronRight, {
97
- className: "h-4 w-4"
113
+ className: "h-4 w-4",
114
+ "aria-hidden": "true"
98
115
  })
99
- }), showFirstLast && currentPage < totalPages && jsx("button", {
100
- className: "rounded-md border border-gray-300 bg-white px-3 py-2 text-sm font-medium hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2",
116
+ }), showFirstLast && visiblePages[visiblePages.length - 1] < totalPages && jsx("button", {
117
+ type: "button",
118
+ className: cn(BUTTON_BASE, BUTTON_IDLE, fullOnly),
101
119
  onClick: function onClick() {
102
120
  return handlePageClick(totalPages);
103
121
  },
@@ -137,6 +137,10 @@ var Sortable = /*#__PURE__*/forwardRef(function (_ref4, ref) {
137
137
  var isGrabbed = keyboard.grabbed === key;
138
138
  var isDragging = pointer.dragging === key;
139
139
  var startDrag = disabled ? undefined : pointer.startFor(key);
140
+ // Whole-item drags wait for a long-press on touch so the page still scrolls.
141
+ var startItemDrag = disabled || handle ? undefined : pointer.startFor(key, {
142
+ longPress: true
143
+ });
140
144
  return jsx(ItemTag, {
141
145
  ref: function ref(node) {
142
146
  if (node) nodes.current.set(key, node);else nodes.current["delete"](key);
@@ -151,8 +155,8 @@ var Sortable = /*#__PURE__*/forwardRef(function (_ref4, ref) {
151
155
  onBlur: function onBlur(e) {
152
156
  if (isGrabbed && e.relatedTarget && !e.currentTarget.contains(e.relatedTarget)) keyboard.cancel();
153
157
  },
154
- onPointerDown: handle ? undefined : startDrag,
155
- className: cn("rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 dark:focus:ring-offset-gray-900", !disabled && !handle && "cursor-grab touch-none select-none", (isGrabbed || isDragging) && "relative z-10 shadow-lg ring-2 ring-primary-500 opacity-90", itemClassName),
158
+ onPointerDown: startItemDrag,
159
+ className: cn("rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 dark:focus:ring-offset-gray-900", !disabled && !handle && "cursor-grab select-none [-webkit-touch-callout:none]", (isGrabbed || isDragging) && "relative z-10 shadow-lg ring-2 ring-primary-500 opacity-90", itemClassName),
156
160
  children: renderItem ? renderItem(item, {
157
161
  index: index,
158
162
  originalIndex: originalIndex,
@@ -3,18 +3,75 @@ import { objectWithoutProperties as _objectWithoutProperties, objectSpread2 as _
3
3
  import { jsx, jsxs } from 'react/jsx-runtime';
4
4
  import React from 'react';
5
5
  import { cn } from '../../utils/cn.js';
6
+ import { TOUCH_TARGET } from '../../utils/touchTarget.js';
6
7
  import { Check } from 'lucide-react';
7
8
 
8
9
  var _excluded = ["steps", "currentStep", "orientation", "variant", "onStepClick", "className"];
10
+ // Layout classes per orientation. "auto" stacks vertically below `sm` and lays
11
+ // out horizontally from `sm` up. Every class is written out literally so
12
+ // Tailwind's content scan picks it up.
13
+ var LAYOUT = {
14
+ horizontal: {
15
+ nav: 'flex-row',
16
+ step: 'flex-col flex-1 min-w-0',
17
+ lastStep: 'flex-none',
18
+ track: 'flex-row items-center',
19
+ // Connectors flex to fill the gap between circles, so steps never overflow.
20
+ line: 'h-0.5 flex-1 min-w-4 mx-2',
21
+ content: 'mt-2 pr-2',
22
+ contentGap: ''
23
+ },
24
+ vertical: {
25
+ nav: 'flex-col',
26
+ step: 'flex-row gap-4',
27
+ lastStep: '',
28
+ track: 'flex-col items-center',
29
+ line: 'w-0.5 flex-1 min-h-8 my-1',
30
+ content: 'pt-1',
31
+ contentGap: 'pb-8'
32
+ },
33
+ auto: {
34
+ nav: 'flex-col sm:flex-row',
35
+ step: 'flex-row gap-4 sm:flex-col sm:gap-0 sm:flex-1 sm:min-w-0',
36
+ lastStep: 'sm:flex-none',
37
+ track: 'flex-col items-center sm:flex-row',
38
+ line: 'w-0.5 flex-1 min-h-8 my-1 sm:h-0.5 sm:w-auto sm:min-h-0 sm:min-w-4 sm:my-0 sm:mx-2',
39
+ content: 'pt-1 sm:pt-0 sm:mt-2 sm:pr-2',
40
+ contentGap: 'pb-8 sm:pb-0'
41
+ }
42
+ };
43
+ var FOCUS_RING = 'outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-gray-900';
44
+ var statusColors = {
45
+ completed: {
46
+ circle: 'bg-primary-600 text-white',
47
+ line: 'bg-primary-600',
48
+ text: 'text-primary-600 dark:text-primary-400',
49
+ dot: 'w-2 bg-primary-600'
50
+ },
51
+ current: {
52
+ circle: 'bg-primary-600 text-white',
53
+ line: 'bg-gray-300 dark:bg-gray-700',
54
+ text: 'text-primary-600 dark:text-primary-400',
55
+ // The current dot stretches into a pill.
56
+ dot: 'w-6 bg-primary-600'
57
+ },
58
+ upcoming: {
59
+ circle: 'bg-gray-300 text-gray-700 dark:bg-gray-700 dark:text-gray-200',
60
+ line: 'bg-gray-300 dark:bg-gray-700',
61
+ text: 'text-muted',
62
+ dot: 'w-2 bg-gray-300 dark:bg-gray-600'
63
+ }
64
+ };
9
65
  var Stepper = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
10
66
  var _ref$steps = _ref.steps,
11
67
  steps = _ref$steps === void 0 ? [] : _ref$steps,
12
68
  _ref$currentStep = _ref.currentStep,
13
69
  currentStep = _ref$currentStep === void 0 ? 0 : _ref$currentStep,
14
70
  _ref$orientation = _ref.orientation,
15
- orientation = _ref$orientation === void 0 ? 'horizontal' : _ref$orientation;
16
- _ref.variant;
17
- var onStepClick = _ref.onStepClick,
71
+ orientation = _ref$orientation === void 0 ? 'horizontal' : _ref$orientation,
72
+ _ref$variant = _ref.variant,
73
+ variant = _ref$variant === void 0 ? 'default' : _ref$variant,
74
+ onStepClick = _ref.onStepClick,
18
75
  className = _ref.className,
19
76
  props = _objectWithoutProperties(_ref, _excluded);
20
77
  var getStepStatus = function getStepStatus(stepIndex) {
@@ -22,73 +79,79 @@ var Stepper = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
22
79
  if (stepIndex === currentStep) return 'current';
23
80
  return 'upcoming';
24
81
  };
25
- var statusColors = {
26
- completed: {
27
- circle: 'bg-primary-600 text-white',
28
- line: 'bg-primary-600',
29
- text: 'text-primary-600'
30
- },
31
- current: {
32
- circle: 'bg-primary-600 text-white',
33
- line: 'bg-gray-300',
34
- text: 'text-primary-600'
35
- },
36
- upcoming: {
37
- circle: 'bg-gray-300 text-gray-700',
38
- line: 'bg-gray-300',
39
- text: 'text-muted'
40
- }
82
+ var stepLabel = function stepLabel(step, stepIndex, status) {
83
+ var _step$title;
84
+ // `label` is accepted as an alias for `title` (the docs used `label`).
85
+ var stepTitle = (_step$title = step.title) !== null && _step$title !== void 0 ? _step$title : step.label;
86
+ // A completed step shows only a check icon, so the button needs a name.
87
+ return "Step ".concat(stepIndex + 1).concat(stepTitle ? ": ".concat(stepTitle) : '').concat(status === 'completed' ? ' (completed)' : '');
41
88
  };
89
+ var isClickable = function isClickable(stepIndex) {
90
+ return Boolean(onStepClick) && stepIndex <= currentStep;
91
+ };
92
+ // Compact progress dots: no titles, just one dot per step (current = pill).
93
+ if (variant === 'dots') {
94
+ return jsx("nav", _objectSpread2(_objectSpread2({
95
+ ref: ref,
96
+ className: cn('flex flex-row items-center gap-1', className),
97
+ "aria-label": "Progress",
98
+ "data-variant": "dots"
99
+ }, props), {}, {
100
+ children: steps.map(function (step, stepIndex) {
101
+ var status = getStepStatus(stepIndex);
102
+ return jsx("button", {
103
+ type: "button",
104
+ className: cn('inline-flex items-center justify-center rounded-full p-1', FOCUS_RING, isClickable(stepIndex) ? cn('cursor-pointer', TOUCH_TARGET) : 'cursor-default'),
105
+ onClick: function onClick() {
106
+ return onStepClick === null || onStepClick === void 0 ? void 0 : onStepClick(step, stepIndex);
107
+ },
108
+ disabled: !isClickable(stepIndex),
109
+ "aria-current": status === 'current' ? 'step' : undefined,
110
+ "aria-label": stepLabel(step, stepIndex, status),
111
+ children: jsx("span", {
112
+ "aria-hidden": "true",
113
+ className: cn('block h-2 rounded-full transition-[width,background-color] duration-base ease-standard motion-reduce:transition-none', statusColors[status].dot)
114
+ })
115
+ }, step.id || stepIndex);
116
+ })
117
+ }));
118
+ }
119
+ var layout = LAYOUT[orientation] || LAYOUT.horizontal;
42
120
  return jsx("nav", _objectSpread2(_objectSpread2({
43
121
  ref: ref,
44
- className: cn('flex', {
45
- 'flex-col': orientation === 'vertical',
46
- 'flex-row': orientation === 'horizontal'
47
- }, className),
122
+ className: cn('flex', layout.nav, className),
48
123
  "aria-label": "Progress"
49
124
  }, props), {}, {
50
125
  children: steps.map(function (step, stepIndex) {
51
- var _step$title;
126
+ var _step$title2;
52
127
  var status = getStepStatus(stepIndex);
53
128
  var isLast = stepIndex === steps.length - 1;
54
129
  var colors = statusColors[status];
55
- // `label` is accepted as an alias for `title` (the docs used `label`).
56
- var stepTitle = (_step$title = step.title) !== null && _step$title !== void 0 ? _step$title : step.label;
130
+ var stepTitle = (_step$title2 = step.title) !== null && _step$title2 !== void 0 ? _step$title2 : step.label;
57
131
  return jsxs("div", {
58
- className: cn('flex items-center', {
59
- 'flex-col': orientation === 'horizontal',
60
- 'flex-row': orientation === 'vertical',
61
- 'mb-8': orientation === 'vertical' && !isLast,
62
- 'mr-8': orientation === 'horizontal' && !isLast
63
- }),
132
+ className: cn('flex', layout.step, isLast && layout.lastStep),
64
133
  children: [jsxs("div", {
65
- className: "flex items-center",
134
+ className: cn('flex', layout.track),
66
135
  children: [jsx("button", {
67
- className: cn('flex h-8 w-8 items-center justify-center rounded-full text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2', colors.circle, {
68
- 'cursor-pointer': onStepClick && stepIndex <= currentStep,
69
- 'cursor-default': !onStepClick || stepIndex > currentStep
70
- }),
136
+ type: "button",
137
+ className: cn('flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-sm font-medium transition-colors duration-fast motion-reduce:transition-none', FOCUS_RING, colors.circle, isClickable(stepIndex) ? cn('cursor-pointer', TOUCH_TARGET) : 'cursor-default'),
71
138
  onClick: function onClick() {
72
139
  return onStepClick === null || onStepClick === void 0 ? void 0 : onStepClick(step, stepIndex);
73
140
  },
74
- disabled: !onStepClick || stepIndex > currentStep,
141
+ disabled: !isClickable(stepIndex),
75
142
  "aria-current": status === 'current' ? 'step' : undefined,
76
- "aria-label": "Step ".concat(stepIndex + 1).concat(stepTitle ? ": ".concat(stepTitle) : '').concat(status === 'completed' ? ' (completed)' : ''),
143
+ "aria-label": stepLabel(step, stepIndex, status),
77
144
  children: status === 'completed' ? jsx(Check, {
78
145
  className: "h-4 w-4",
79
146
  "aria-hidden": "true"
80
147
  }) : stepIndex + 1
81
148
  }), !isLast && jsx("div", {
82
- className: cn('bg-gray-300', {
83
- 'h-0.5 w-8': orientation === 'horizontal',
84
- 'h-8 w-0.5': orientation === 'vertical'
85
- })
149
+ "aria-hidden": "true",
150
+ "data-stepper-connector": "",
151
+ className: cn('transition-colors duration-base motion-reduce:transition-none', layout.line, colors.line)
86
152
  })]
87
153
  }), jsxs("div", {
88
- className: cn('flex flex-col', {
89
- 'ml-4': orientation === 'vertical',
90
- 'mt-2': orientation === 'horizontal'
91
- }),
154
+ className: cn('flex min-w-0 flex-col', layout.content, !isLast && layout.contentGap),
92
155
  children: [jsx("span", {
93
156
  className: cn('text-sm font-medium', colors.text),
94
157
  children: stepTitle
@@ -3,8 +3,7 @@ import { useEffect } from 'react';
3
3
  import { useLatestRef } from './useLatestRef.js';
4
4
 
5
5
  // Internal (not exported from the package). Shared focus management for the
6
- // overlay primitives (Drawer, Popover, Tour). Dialog keeps its own copy so its
7
- // behavior is untouched.
6
+ // overlay primitives (Drawer, Dialog, Popover, Tour, Sidebar, AppShell).
8
7
  var FOCUSABLE_SELECTOR = ["a[href]", "button:not([disabled])", "textarea:not([disabled])", "input:not([disabled])", "select:not([disabled])", '[tabindex]:not([tabindex="-1"])'].join(",");
9
8
  var getFocusable = function getFocusable(root) {
10
9
  return root ? Array.from(root.querySelectorAll(FOCUSABLE_SELECTOR)).filter(function (el) {
@@ -17,7 +16,16 @@ var layerStack = [];
17
16
  var isTopLayer = function isTopLayer(token) {
18
17
  return layerStack[layerStack.length - 1] === token;
19
18
  };
19
+ // A responsive drawer (Sidebar's `md:hidden`, AppShell's `lg:hidden`) can be
20
+ // "open" while CSS hides it on wide viewports; there is nothing to trap then.
21
+ var isDisplayNone = function isDisplayNone(el) {
22
+ for (var node = el; node && node.nodeType === 1; node = node.parentElement) {
23
+ if (window.getComputedStyle(node).display === "none") return true;
24
+ }
25
+ return false;
26
+ };
20
27
  function cycleFocus(e, panel) {
28
+ if (isDisplayNone(panel)) return;
21
29
  var focusables = getFocusable(panel);
22
30
  if (focusables.length === 0) {
23
31
  e.preventDefault();
@@ -91,13 +91,17 @@ function useSliderValues(_ref3) {
91
91
  setThumb: setThumb
92
92
  };
93
93
  }
94
- /** Pointer drag on the track: picks the nearest thumb and follows the pointer. */
94
+ /**
95
+ * Pointer drag on the track: picks the nearest thumb and follows the pointer.
96
+ * `onActiveChange(index | null)` reports which thumb is being dragged.
97
+ */
95
98
  function useSliderPointer(_ref4) {
96
99
  var trackRef = _ref4.trackRef,
97
100
  values = _ref4.values,
98
101
  setThumb = _ref4.setThumb,
99
102
  bounds = _ref4.bounds,
100
- disabled = _ref4.disabled;
103
+ disabled = _ref4.disabled,
104
+ onActiveChange = _ref4.onActiveChange;
101
105
  var active = useRef(null);
102
106
  var toValue = function toValue(clientX) {
103
107
  var rect = trackRef.current.getBoundingClientRect();
@@ -112,6 +116,7 @@ function useSliderPointer(_ref4) {
112
116
  return Math.abs(v - raw) < Math.abs(values[best] - raw) || v === values[best] && raw > v ? i : best;
113
117
  }, 0);
114
118
  active.current = nearest;
119
+ onActiveChange === null || onActiveChange === void 0 || onActiveChange(nearest);
115
120
  (_e$currentTarget$setP = (_e$currentTarget = e.currentTarget).setPointerCapture) === null || _e$currentTarget$setP === void 0 || _e$currentTarget$setP.call(_e$currentTarget, e.pointerId);
116
121
  setThumb(nearest, raw);
117
122
  (_e$currentTarget$quer = e.currentTarget.querySelectorAll('[role="slider"]')[nearest]) === null || _e$currentTarget$quer === void 0 || _e$currentTarget$quer.focus();
@@ -123,6 +128,7 @@ function useSliderPointer(_ref4) {
123
128
  if (active.current === null) return;
124
129
  setThumb(active.current, toValue(e.clientX), true);
125
130
  active.current = null;
131
+ onActiveChange === null || onActiveChange === void 0 || onActiveChange(null);
126
132
  };
127
133
  return {
128
134
  onPointerDown: onPointerDown,
@@ -91,7 +91,45 @@ function useSortableKeyboard(_ref) {
91
91
  cancel: cancel
92
92
  };
93
93
  }
94
+ // Mouse / pen: a drag starts once the pointer has moved this far, so clicks inside
95
+ // items still work.
94
96
  var DRAG_THRESHOLD = 4;
97
+ // Touch: a drag starts after holding still this long, so a swipe over the list
98
+ // still scrolls the page. Moving further than LONG_PRESS_TOLERANCE first cancels it.
99
+ var LONG_PRESS_DELAY = 250;
100
+ var LONG_PRESS_TOLERANCE = 8;
101
+ // While dragging within this many px of a scroll container's (or the viewport's)
102
+ // edge, the container scrolls; speed grows toward the edge up to AUTO_SCROLL_MAX px/frame.
103
+ var AUTO_SCROLL_EDGE = 40;
104
+ var AUTO_SCROLL_MAX = 16;
105
+ var isScrollable = function isScrollable(el, axis) {
106
+ var style = window.getComputedStyle(el);
107
+ var overflow = axis === "horizontal" ? style.overflowX : style.overflowY;
108
+ if (!/(auto|scroll|overlay)/.test(overflow)) return false;
109
+ return axis === "horizontal" ? el.scrollWidth > el.clientWidth : el.scrollHeight > el.clientHeight;
110
+ };
111
+ /** Nearest scrollable ancestor along `axis`, or `null` for the window. */
112
+ function getScrollParent(node, axis) {
113
+ var el = node === null || node === void 0 ? void 0 : node.parentElement;
114
+ while (el && el !== document.body && el !== document.documentElement) {
115
+ if (isScrollable(el, axis)) return el;
116
+ el = el.parentElement;
117
+ }
118
+ return null;
119
+ }
120
+ /** Scroll speed (px/frame, signed) for a coordinate near the [start, end] edges. */
121
+ function edgeSpeed(pos, start, end) {
122
+ var edge = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : AUTO_SCROLL_EDGE;
123
+ if (pos < start + edge) return -Math.ceil(AUTO_SCROLL_MAX * Math.min(1, (start + edge - pos) / edge));
124
+ if (pos > end - edge) return Math.ceil(AUTO_SCROLL_MAX * Math.min(1, (pos - (end - edge)) / edge));
125
+ return 0;
126
+ }
127
+ var raf = function raf(cb) {
128
+ return typeof window.requestAnimationFrame === "function" ? window.requestAnimationFrame(cb) : setTimeout(cb, 16);
129
+ };
130
+ var caf = function caf(id) {
131
+ return typeof window.cancelAnimationFrame === "function" ? window.cancelAnimationFrame(id) : clearTimeout(id);
132
+ };
95
133
  function useSortablePointer(_ref2) {
96
134
  var keys = _ref2.keys,
97
135
  setDraft = _ref2.setDraft,
@@ -134,7 +172,12 @@ function useSortablePointer(_ref2) {
134
172
  });
135
173
  return index === -1 ? others.length : index;
136
174
  };
175
+ // `longPress`: on touch, wait LONG_PRESS_DELAY before picking the item up (used
176
+ // when the whole item is the drag source, so the list still scrolls on swipe).
137
177
  var startFor = function startFor(key) {
178
+ var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
179
+ _ref3$longPress = _ref3.longPress,
180
+ longPress = _ref3$longPress === void 0 ? false : _ref3$longPress;
138
181
  return function (e) {
139
182
  if (disabled || e.button > 0) return;
140
183
  var start = {
@@ -142,18 +185,59 @@ function useSortablePointer(_ref2) {
142
185
  y: e.clientY
143
186
  };
144
187
  var from = latest.current.keys.indexOf(key);
188
+ var waitForPress = longPress && e.pointerType === "touch";
145
189
  var active = false;
190
+ var point = start;
191
+ var pressTimer = null;
192
+ var frame = null;
193
+ var scrollTarget = function scrollTarget() {
194
+ return getScrollParent(latest.current.nodeFor(key), latest.current.orientation);
195
+ };
196
+ var _autoScroll = function autoScroll() {
197
+ frame = null;
198
+ if (!active) return;
199
+ var axis = latest.current.orientation;
200
+ var container = scrollTarget();
201
+ var rect = container ? container.getBoundingClientRect() : {
202
+ top: 0,
203
+ left: 0,
204
+ bottom: window.innerHeight,
205
+ right: window.innerWidth
206
+ };
207
+ var speed = axis === "horizontal" ? edgeSpeed(point.x, rect.left, rect.right) : edgeSpeed(point.y, rect.top, rect.bottom);
208
+ if (!speed) return;
209
+ var dx = axis === "horizontal" ? speed : 0;
210
+ var dy = axis === "horizontal" ? 0 : speed;
211
+ if (container) {
212
+ container.scrollLeft += dx;
213
+ container.scrollTop += dy;
214
+ } else if (typeof window.scrollBy === "function") {
215
+ window.scrollBy(dx, dy);
216
+ }
217
+ setDraft(moveKey(latest.current.keys, key, targetIndex(point, key)));
218
+ frame = raf(_autoScroll);
219
+ };
220
+ var activate = function activate() {
221
+ active = true;
222
+ setDragging(key);
223
+ };
146
224
  var onMove = function onMove(ev) {
147
- var point = {
225
+ point = {
148
226
  x: ev.clientX,
149
227
  y: ev.clientY
150
228
  };
151
- if (!active && Math.hypot(point.x - start.x, point.y - start.y) < DRAG_THRESHOLD) return;
229
+ var distance = Math.hypot(point.x - start.x, point.y - start.y);
152
230
  if (!active) {
153
- active = true;
154
- setDragging(key);
231
+ if (waitForPress) {
232
+ // Finger moved before the long-press fired: it's a scroll, not a drag.
233
+ if (distance > LONG_PRESS_TOLERANCE) end();
234
+ return;
235
+ }
236
+ if (distance < DRAG_THRESHOLD) return;
237
+ activate();
155
238
  }
156
239
  setDraft(moveKey(latest.current.keys, key, targetIndex(point, key)));
240
+ if (frame === null) frame = raf(_autoScroll);
157
241
  };
158
242
  var onUp = function onUp() {
159
243
  end();
@@ -163,18 +247,36 @@ function useSortablePointer(_ref2) {
163
247
  };
164
248
  var onCancel = function onCancel() {
165
249
  end();
250
+ if (!active) return;
166
251
  setDragging(null);
167
252
  setDraft(null);
168
253
  };
254
+ // Once a touch drag is live, stop the browser from scrolling (and from firing
255
+ // pointercancel); before that, let the page scroll normally.
256
+ var onTouchMove = function onTouchMove(ev) {
257
+ if (active && ev.cancelable) ev.preventDefault();
258
+ };
259
+ var onContextMenu = function onContextMenu(ev) {
260
+ if (waitForPress) ev.preventDefault();
261
+ };
169
262
  end();
263
+ if (waitForPress) pressTimer = setTimeout(activate, LONG_PRESS_DELAY);
170
264
  window.addEventListener("pointermove", onMove);
171
265
  window.addEventListener("pointerup", onUp);
172
266
  window.addEventListener("pointercancel", onCancel);
267
+ window.addEventListener("touchmove", onTouchMove, {
268
+ passive: false
269
+ });
270
+ window.addEventListener("contextmenu", onContextMenu);
173
271
  session.current = {
174
272
  detach: function detach() {
273
+ if (pressTimer !== null) clearTimeout(pressTimer);
274
+ if (frame !== null) caf(frame);
175
275
  window.removeEventListener("pointermove", onMove);
176
276
  window.removeEventListener("pointerup", onUp);
177
277
  window.removeEventListener("pointercancel", onCancel);
278
+ window.removeEventListener("touchmove", onTouchMove);
279
+ window.removeEventListener("contextmenu", onContextMenu);
178
280
  }
179
281
  };
180
282
  };
@@ -185,4 +287,4 @@ function useSortablePointer(_ref2) {
185
287
  };
186
288
  }
187
289
 
188
- export { DEFAULT_SORTABLE_MESSAGES, moveKey, useSortableKeyboard, useSortablePointer };
290
+ export { AUTO_SCROLL_EDGE, DEFAULT_SORTABLE_MESSAGES, DRAG_THRESHOLD, LONG_PRESS_DELAY, LONG_PRESS_TOLERANCE, edgeSpeed, getScrollParent, moveKey, useSortableKeyboard, useSortablePointer };
package/dist/esm/index.js CHANGED
@@ -42,6 +42,7 @@ export { default as Drawer } from './components/feedback/Drawer.js';
42
42
  export { default as Tour } from './components/feedback/Tour.js';
43
43
  export { default as Badge } from './components/utility/Badge.js';
44
44
  export { default as Avatar } from './components/utility/Avatar.js';
45
+ export { default as AvatarGroup } from './components/utility/AvatarGroup.js';
45
46
  export { default as Card } from './components/utility/Card.js';
46
47
  export { default as Pagination } from './components/utility/Pagination.js';
47
48
  export { default as Stepper } from './components/utility/Stepper.js';
@@ -2,5 +2,8 @@
2
2
  // touch target (WCAG 2.5.5, Apple HIG) on coarse pointers only, so desktop
3
3
  // layouts keep their compact size. Pair with inline-flex centering.
4
4
  var TOUCH_TARGET = "[@media(pointer:coarse)]:min-h-11 [@media(pointer:coarse)]:min-w-11";
5
+ // Internal (not exported). Native-feeling press: a slight shrink while pressed,
6
+ // off under prefers-reduced-motion. Add to pressables (buttons, tiles, tab items).
7
+ var PRESS_FEEDBACK = "active:scale-[0.97] motion-reduce:active:scale-100 transition-transform duration-fast";
5
8
 
6
- export { TOUCH_TARGET };
9
+ export { PRESS_FEEDBACK, TOUCH_TARGET };
@@ -1 +1 @@
1
- {"version":3,"file":"useFocusTrap.d.ts","sourceRoot":"","sources":["../../src/hooks/useFocusTrap.js"],"names":[],"mappings":"AAgDA;;;;GAIG;AACH;;;;;;SAmCC;AAjFD,wCAOY;AAEL,+CAKC"}
1
+ {"version":3,"file":"useFocusTrap.d.ts","sourceRoot":"","sources":["../../src/hooks/useFocusTrap.js"],"names":[],"mappings":"AAyDA;;;;GAIG;AACH;;;;;;SAmCC;AA3FD,wCAOY;AAEL,+CAKC"}
@@ -26,13 +26,17 @@ export function useSliderValues({ value, defaultValue, onChange, onChangeEnd, bo
26
26
  isRange: boolean;
27
27
  setThumb: (index: any, raw: any, commit?: boolean) => void;
28
28
  };
29
- /** Pointer drag on the track: picks the nearest thumb and follows the pointer. */
30
- export function useSliderPointer({ trackRef, values, setThumb, bounds, disabled }: {
29
+ /**
30
+ * Pointer drag on the track: picks the nearest thumb and follows the pointer.
31
+ * `onActiveChange(index | null)` reports which thumb is being dragged.
32
+ */
33
+ export function useSliderPointer({ trackRef, values, setThumb, bounds, disabled, onActiveChange }: {
31
34
  trackRef: any;
32
35
  values: any;
33
36
  setThumb: any;
34
37
  bounds: any;
35
38
  disabled: any;
39
+ onActiveChange: any;
36
40
  }): {
37
41
  onPointerDown: (e: any) => void;
38
42
  onPointerMove: (e: any) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"useSliderValues.d.ts","sourceRoot":"","sources":["../../src/hooks/useSliderValues.js"],"names":[],"mappings":"AAQA;;;;WAGC;AAID,kFAAkF;AAClF;;;;;QAmBC;AAED;;;;GAIG;AACH;;;;;;;;;;EAqBC;AAED,kFAAkF;AAClF;;;;;;;;;;;EA8BC;AAnFM,8DAAsF"}
1
+ {"version":3,"file":"useSliderValues.d.ts","sourceRoot":"","sources":["../../src/hooks/useSliderValues.js"],"names":[],"mappings":"AAQA;;;;WAGC;AAID,kFAAkF;AAClF;;;;;QAmBC;AAED;;;;GAIG;AACH;;;;;;;;;;EAqBC;AAED;;;GAGG;AACH;;;;;;;;;;;;EAgCC;AAxFM,8DAAsF"}
@@ -11,6 +11,10 @@ export function useSortableKeyboard({ keys, setDraft, commit, announce, labelFor
11
11
  keyDownFor: (key: any) => (e: any) => void;
12
12
  cancel: () => void;
13
13
  };
14
+ /** Nearest scrollable ancestor along `axis`, or `null` for the window. */
15
+ export function getScrollParent(node: any, axis: any): any;
16
+ /** Scroll speed (px/frame, signed) for a coordinate near the [start, end] edges. */
17
+ export function edgeSpeed(pos: any, start: any, end: any, edge?: number): number;
14
18
  export function useSortablePointer({ keys, setDraft, commit, nodeFor, orientation, disabled }: {
15
19
  keys: any;
16
20
  setDraft: any;
@@ -20,7 +24,9 @@ export function useSortablePointer({ keys, setDraft, commit, nodeFor, orientatio
20
24
  disabled: any;
21
25
  }): {
22
26
  dragging: null;
23
- startFor: (key: any) => (e: any) => void;
27
+ startFor: (key: any, { longPress }?: {
28
+ longPress?: boolean | undefined;
29
+ }) => (e: any) => void;
24
30
  };
25
31
  export namespace DEFAULT_SORTABLE_MESSAGES {
26
32
  let instructions: string;
@@ -29,4 +35,8 @@ export namespace DEFAULT_SORTABLE_MESSAGES {
29
35
  function dropped(label: any, position: any, total: any): string;
30
36
  function cancelled(label: any, position: any): string;
31
37
  }
38
+ export const DRAG_THRESHOLD: 4;
39
+ export const LONG_PRESS_DELAY: 250;
40
+ export const LONG_PRESS_TOLERANCE: 8;
41
+ export const AUTO_SCROLL_EDGE: 40;
32
42
  //# sourceMappingURL=useSortable.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useSortable.d.ts","sourceRoot":"","sources":["../../src/hooks/useSortable.js"],"names":[],"mappings":"AAOA,gEAIC;AAcD;;;;;;;;;+BAK+B,MAAC;;EAqC/B;AAID;;;;;;;;;6BAsB6B,MAAC;EAuC7B;;;IApHU,gEAAmF;IACrF,8DAAiF;IAC/E,gEAAmF;IACjF,sDAAoF"}
1
+ {"version":3,"file":"useSortable.d.ts","sourceRoot":"","sources":["../../src/hooks/useSortable.js"],"names":[],"mappings":"AAOA,gEAIC;AAcD;;;;;;;;;+BAK+B,MAAC;;EAqC/B;AAqBD,0EAA0E;AAC1E,2DAOC;AAED,oFAAoF;AACpF,iFAIC;AAOD;;;;;;;;;;;WAwByD,MAAC;EAgGzD;;;IAtNU,gEAAmF;IACrF,8DAAiF;IAC/E,gEAAmF;IACjF,sDAAoF;;AAoDjG,6BAA8B,CAAC,CAAC;AAGhC,+BAAgC,GAAG,CAAC;AACpC,mCAAoC,CAAC,CAAC;AAGtC,+BAAgC,EAAE,CAAC"}