@dreamtree-org/twreact-ui 1.3.9 → 1.3.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/ai-schema.json +19 -8
- package/dist/components/blocks/AppShell.d.ts.map +1 -1
- package/dist/components/core/Accordion.d.ts.map +1 -1
- package/dist/components/core/ColorPicker.d.ts.map +1 -1
- package/dist/components/core/DatePicker.d.ts.map +1 -1
- package/dist/components/core/Form.d.ts.map +1 -1
- package/dist/components/core/Input.d.ts.map +1 -1
- package/dist/components/core/PriceRangePicker.d.ts +0 -29
- package/dist/components/core/PriceRangePicker.d.ts.map +1 -1
- package/dist/components/core/Radio.d.ts.map +1 -1
- package/dist/components/core/Rate.d.ts.map +1 -1
- package/dist/components/core/RoundedTag.d.ts.map +1 -1
- package/dist/components/core/Table/ColumnMenu.d.ts.map +1 -1
- package/dist/components/core/Table/Pagination.d.ts.map +1 -1
- package/dist/components/core/Table/TableRow.d.ts.map +1 -1
- package/dist/components/core/Table/helpers.d.ts +2 -0
- package/dist/components/core/Table/helpers.d.ts.map +1 -1
- package/dist/components/core/Tabs.d.ts.map +1 -1
- package/dist/components/core/TextToSpeech.d.ts +2 -7
- package/dist/components/core/TextToSpeech.d.ts.map +1 -1
- package/dist/components/core/Tooltip.d.ts.map +1 -1
- package/dist/components/feedback/Alert.d.ts.map +1 -1
- package/dist/components/feedback/Dialog.d.ts.map +1 -1
- package/dist/components/feedback/Toast.d.ts.map +1 -1
- package/dist/components/navigation/Sidebar.d.ts.map +1 -1
- package/dist/components/utility/Avatar.d.ts.map +1 -1
- package/dist/components/utility/Carousel.d.ts.map +1 -1
- package/dist/esm/components/blocks/AppShell.js +24 -18
- package/dist/esm/components/core/Accordion.js +40 -9
- package/dist/esm/components/core/ColorPicker.js +19 -7
- package/dist/esm/components/core/DatePicker.js +97 -58
- package/dist/esm/components/core/Form.js +35 -8
- package/dist/esm/components/core/Input.js +3 -1
- package/dist/esm/components/core/PriceRangePicker.js +205 -218
- package/dist/esm/components/core/Radio.js +36 -6
- package/dist/esm/components/core/Rate.js +41 -50
- package/dist/esm/components/core/RoundedTag.js +25 -8
- package/dist/esm/components/core/Table/ColumnMenu.js +2 -4
- package/dist/esm/components/core/Table/Pagination.js +15 -18
- package/dist/esm/components/core/Table/TableRow.js +2 -2
- package/dist/esm/components/core/Table/helpers.js +33 -1
- package/dist/esm/components/core/Tabs.js +6 -5
- package/dist/esm/components/core/TextToSpeech.js +121 -28
- package/dist/esm/components/core/Tooltip.js +100 -28
- package/dist/esm/components/feedback/Alert.js +21 -11
- package/dist/esm/components/feedback/Dialog.js +44 -100
- package/dist/esm/components/feedback/Toast.js +24 -5
- package/dist/esm/components/navigation/Sidebar.js +13 -61
- package/dist/esm/components/utility/Avatar.js +23 -10
- package/dist/esm/components/utility/Carousel.js +34 -17
- package/dist/esm/components/utility/Pagination.js +2 -2
- package/dist/esm/hooks/useFocusTrap.js +10 -2
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.esm.css +1 -1
- package/dist/index.esm.js +1760 -1350
- package/dist/index.js +1759 -1349
- package/dist/types/index.d.ts +8 -2
- package/dist/types/index.d.ts.map +1 -1
- package/mcp/catalog.snapshot.json +82 -52
- package/package.json +1 -1
|
@@ -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 { jsx, jsxs } from 'react/jsx-runtime';
|
|
4
|
-
import { forwardRef, useState } from 'react';
|
|
4
|
+
import { forwardRef, useState, useRef } from 'react';
|
|
5
5
|
import { cn } from '../../utils/cn.js';
|
|
6
|
+
import { TOUCH_TARGET } from '../../utils/touchTarget.js';
|
|
6
7
|
|
|
7
8
|
var _excluded = ["count", "value", "defaultValue", "readOnly", "icon", "toggledIcon", "activeColor", "inactiveColor", "size", "onChange", "onClick", "text", "className", "id", "name"];
|
|
8
9
|
function StarFilled(_ref) {
|
|
@@ -69,45 +70,39 @@ var Rate = /*#__PURE__*/forwardRef(function Rate(_ref3, ref) {
|
|
|
69
70
|
_useState4 = _slicedToArray(_useState3, 2),
|
|
70
71
|
hoverValue = _useState4[0],
|
|
71
72
|
setHoverValue = _useState4[1];
|
|
72
|
-
var
|
|
73
|
-
_useState6 = _slicedToArray(_useState5, 2),
|
|
74
|
-
focusValue = _useState6[0],
|
|
75
|
-
setFocusValue = _useState6[1];
|
|
73
|
+
var starRefs = useRef([]);
|
|
76
74
|
var isControlled = value != null;
|
|
77
75
|
var selectedValue = isControlled ? value : internalValue;
|
|
78
|
-
|
|
76
|
+
// Hover preview (pointer devices only) wins over the committed value.
|
|
77
|
+
var displayValue = hoverValue != null ? hoverValue : selectedValue;
|
|
78
|
+
// Roving tabindex (DTUI-23): the group is a single tab stop — the star
|
|
79
|
+
// equal to the value, or the first star when nothing is selected.
|
|
80
|
+
var tabStop = selectedValue >= 1 ? Math.min(count, selectedValue) : 1;
|
|
79
81
|
var InactiveIcon = icon !== null && icon !== void 0 ? icon : jsx(StarOutline, {
|
|
80
82
|
size: size
|
|
81
83
|
});
|
|
82
84
|
var ActiveIcon = toggledIcon !== null && toggledIcon !== void 0 ? toggledIcon : jsx(StarFilled, {
|
|
83
85
|
size: size
|
|
84
86
|
});
|
|
85
|
-
var
|
|
86
|
-
if (readOnly) return;
|
|
87
|
+
var setValue = function setValue(n) {
|
|
87
88
|
if (!isControlled) setInternalValue(n);
|
|
88
89
|
if (typeof onChange === "function") onChange(n);
|
|
89
|
-
if (typeof onClick === "function") onClick(n);
|
|
90
90
|
};
|
|
91
|
-
var
|
|
91
|
+
var commit = function commit(n) {
|
|
92
92
|
if (readOnly) return;
|
|
93
|
-
|
|
93
|
+
setValue(n);
|
|
94
|
+
if (typeof onClick === "function") onClick(n);
|
|
94
95
|
};
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
// Touch taps emit pointer/compat-mouse events with no matching "leave",
|
|
97
|
+
// which used to leave the preview stuck. Only real hover devices preview.
|
|
98
|
+
var handlePointerHover = function handlePointerHover(e, n) {
|
|
99
|
+
if (readOnly || e.pointerType === "touch") return;
|
|
97
100
|
setHoverValue(n);
|
|
98
101
|
};
|
|
99
|
-
var
|
|
102
|
+
var clearHover = function clearHover() {
|
|
100
103
|
if (readOnly) return;
|
|
101
104
|
setHoverValue(null);
|
|
102
105
|
};
|
|
103
|
-
var handleFocus = function handleFocus(n) {
|
|
104
|
-
if (readOnly) return;
|
|
105
|
-
setFocusValue(n);
|
|
106
|
-
};
|
|
107
|
-
var handleBlur = function handleBlur() {
|
|
108
|
-
if (readOnly) return;
|
|
109
|
-
setFocusValue(null);
|
|
110
|
-
};
|
|
111
106
|
var handleKeyDown = function handleKeyDown(e, n) {
|
|
112
107
|
if (readOnly) return;
|
|
113
108
|
if (e.key === "Enter" || e.key === " ") {
|
|
@@ -115,22 +110,16 @@ var Rate = /*#__PURE__*/forwardRef(function Rate(_ref3, ref) {
|
|
|
115
110
|
commit(n);
|
|
116
111
|
return;
|
|
117
112
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (
|
|
127
|
-
|
|
128
|
-
setFocusValue(function (prev) {
|
|
129
|
-
var base = prev != null ? prev : selectedValue || 0;
|
|
130
|
-
return Math.min(count, base + 1);
|
|
131
|
-
});
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
113
|
+
var next = null;
|
|
114
|
+
if (e.key === "ArrowRight" || e.key === "ArrowUp") next = n + 1;else if (e.key === "ArrowLeft" || e.key === "ArrowDown") next = n - 1;else if (e.key === "Home") next = 1;else if (e.key === "End") next = count;
|
|
115
|
+
if (next == null) return;
|
|
116
|
+
e.preventDefault();
|
|
117
|
+
next = Math.max(1, Math.min(count, next));
|
|
118
|
+
// Radio-group semantics: arrows move real focus AND the selection.
|
|
119
|
+
setHoverValue(null);
|
|
120
|
+
var target = starRefs.current[next - 1];
|
|
121
|
+
if (target) target.focus();
|
|
122
|
+
if (next !== selectedValue) setValue(next);
|
|
134
123
|
};
|
|
135
124
|
return jsxs("div", _objectSpread2(_objectSpread2({
|
|
136
125
|
ref: ref,
|
|
@@ -141,41 +130,43 @@ var Rate = /*#__PURE__*/forwardRef(function Rate(_ref3, ref) {
|
|
|
141
130
|
children: [jsx("div", {
|
|
142
131
|
role: "radiogroup",
|
|
143
132
|
"aria-label": name || "Rating",
|
|
133
|
+
"aria-readonly": readOnly ? "true" : undefined,
|
|
144
134
|
className: "inline-flex gap-1",
|
|
145
|
-
|
|
135
|
+
onPointerLeave: clearHover,
|
|
136
|
+
onPointerCancel: clearHover,
|
|
146
137
|
children: Array.from({
|
|
147
138
|
length: count
|
|
148
139
|
}, function (_, i) {
|
|
149
140
|
var starNumber = i + 1;
|
|
150
141
|
var filled = starNumber <= (displayValue || 0);
|
|
142
|
+
var checked = starNumber === selectedValue;
|
|
151
143
|
var color = filled ? activeColor : inactiveColor;
|
|
152
144
|
// Token classes unless a colour override is passed, so brands and dark mode apply (STUDIO-59).
|
|
153
145
|
var colorClass = color ? "" : filled ? " text-warning-400" : " text-gray-200";
|
|
154
146
|
return jsx("button", {
|
|
147
|
+
ref: function ref(el) {
|
|
148
|
+
starRefs.current[i] = el;
|
|
149
|
+
},
|
|
155
150
|
type: "button",
|
|
156
151
|
role: "radio",
|
|
157
|
-
"aria-checked":
|
|
152
|
+
"aria-checked": checked,
|
|
158
153
|
"aria-label": "".concat(starNumber, " star").concat(starNumber > 1 ? "s" : ""),
|
|
159
|
-
tabIndex: readOnly ?
|
|
154
|
+
tabIndex: !readOnly && starNumber === tabStop ? 0 : -1,
|
|
160
155
|
onClick: function onClick() {
|
|
161
156
|
return commit(starNumber);
|
|
162
157
|
},
|
|
163
158
|
onKeyDown: function onKeyDown(e) {
|
|
164
159
|
return handleKeyDown(e, starNumber);
|
|
165
160
|
},
|
|
166
|
-
|
|
167
|
-
return
|
|
168
|
-
},
|
|
169
|
-
onMouseMove: function onMouseMove() {
|
|
170
|
-
return handleMouseMove(starNumber);
|
|
161
|
+
onPointerEnter: function onPointerEnter(e) {
|
|
162
|
+
return handlePointerHover(e, starNumber);
|
|
171
163
|
},
|
|
172
|
-
|
|
173
|
-
return
|
|
164
|
+
onPointerMove: function onPointerMove(e) {
|
|
165
|
+
return handlePointerHover(e, starNumber);
|
|
174
166
|
},
|
|
175
|
-
onBlur: handleBlur,
|
|
176
167
|
disabled: readOnly,
|
|
177
168
|
title: "".concat(starNumber, " of ").concat(count),
|
|
178
|
-
className: "p-0 m-0 bg-transparent border-0 inline-flex items-center justify-center leading-0 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-
|
|
169
|
+
className: cn("p-0 m-0 bg-transparent border-0 inline-flex items-center justify-center leading-0 rounded", "focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-primary-500", "transition-colors motion-reduce:transition-none", TOUCH_TARGET, colorClass.trim()),
|
|
179
170
|
style: color ? {
|
|
180
171
|
color: color
|
|
181
172
|
} : undefined,
|
|
@@ -3,8 +3,9 @@ import { objectWithoutProperties as _objectWithoutProperties, objectSpread2 as _
|
|
|
3
3
|
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
4
4
|
import { forwardRef } from 'react';
|
|
5
5
|
import { cn } from '../../utils/cn.js';
|
|
6
|
+
import { TOUCH_TARGET } from '../../utils/touchTarget.js';
|
|
6
7
|
|
|
7
|
-
var _excluded = ["label", "avatarSrc", "avatarAlt", "initials", "size", "onRemove", "onClick", "className", "closeClass", "ariaLabel", "avatarPosition", "avatarClassname", "initialClassname"];
|
|
8
|
+
var _excluded = ["label", "avatarSrc", "avatarAlt", "initials", "size", "onRemove", "onClick", "className", "closeClass", "ariaLabel", "onKeyDown", "avatarPosition", "avatarClassname", "initialClassname"];
|
|
8
9
|
var RoundedTag = /*#__PURE__*/forwardRef(function RoundedTag(_ref, ref) {
|
|
9
10
|
var label = _ref.label,
|
|
10
11
|
_ref$avatarSrc = _ref.avatarSrc,
|
|
@@ -23,8 +24,8 @@ var RoundedTag = /*#__PURE__*/forwardRef(function RoundedTag(_ref, ref) {
|
|
|
23
24
|
className = _ref$className === void 0 ? "" : _ref$className,
|
|
24
25
|
_ref$closeClass = _ref.closeClass,
|
|
25
26
|
closeClass = _ref$closeClass === void 0 ? "" : _ref$closeClass,
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
ariaLabel = _ref.ariaLabel,
|
|
28
|
+
onKeyDown = _ref.onKeyDown,
|
|
28
29
|
_ref$avatarPosition = _ref.avatarPosition,
|
|
29
30
|
avatarPosition = _ref$avatarPosition === void 0 ? "left" : _ref$avatarPosition,
|
|
30
31
|
_ref$avatarClassname = _ref.avatarClassname,
|
|
@@ -63,6 +64,19 @@ var RoundedTag = /*#__PURE__*/forwardRef(function RoundedTag(_ref, ref) {
|
|
|
63
64
|
e && e.stopPropagation();
|
|
64
65
|
onRemove && onRemove({});
|
|
65
66
|
}
|
|
67
|
+
var clickable = typeof _onClick === "function";
|
|
68
|
+
// Enter / Space activate a clickable tag like a native button. Keys from
|
|
69
|
+
// nested controls (the remove button) are left alone.
|
|
70
|
+
function handleKeyDown(e) {
|
|
71
|
+
onKeyDown && onKeyDown(e);
|
|
72
|
+
if (!clickable || e.defaultPrevented || e.target !== e.currentTarget) return;
|
|
73
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
74
|
+
e.preventDefault();
|
|
75
|
+
_onClick(e);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
var labelText = typeof label === "string" || typeof label === "number" ? String(label) : "";
|
|
79
|
+
var removeLabel = labelText ? "Remove ".concat(labelText) : "Remove";
|
|
66
80
|
var s = sizes[size] || sizes.md;
|
|
67
81
|
var paddingClass = showAvatar ? s.paddingWithAvatar : s.paddingNoAvatar;
|
|
68
82
|
var gapClass = showAvatar ? s.gapWithAvatar : s.gapNoAvatar;
|
|
@@ -83,22 +97,25 @@ var RoundedTag = /*#__PURE__*/forwardRef(function RoundedTag(_ref, ref) {
|
|
|
83
97
|
});
|
|
84
98
|
return jsxs("div", _objectSpread2(_objectSpread2({
|
|
85
99
|
ref: ref,
|
|
86
|
-
className: cn("inline-flex items-center rounded-full bg-gray-100 text-primary-800", paddingClass, gapClass, s.text, className),
|
|
87
|
-
role: "group",
|
|
100
|
+
className: cn("inline-flex items-center rounded-full bg-gray-100 text-primary-800", paddingClass, gapClass, s.text, clickable && "cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-1", className),
|
|
101
|
+
role: clickable ? "button" : "group",
|
|
102
|
+
tabIndex: clickable ? 0 : undefined,
|
|
88
103
|
"aria-label": ariaLabel,
|
|
89
104
|
onClick: function onClick(e) {
|
|
90
105
|
return _onClick && _onClick(e);
|
|
91
|
-
}
|
|
106
|
+
},
|
|
107
|
+
onKeyDown: handleKeyDown
|
|
92
108
|
}, rest), {}, {
|
|
93
109
|
children: [avatarPosition === "left" && AvatarNode, jsx("div", {
|
|
94
110
|
className: "truncate max-w-[10rem]",
|
|
95
111
|
children: label
|
|
96
112
|
}), avatarPosition === "right" && AvatarNode, onRemove && jsx("button", {
|
|
97
113
|
type: "button",
|
|
98
|
-
"aria-label":
|
|
114
|
+
"aria-label": removeLabel,
|
|
99
115
|
onClick: handleRemove,
|
|
100
|
-
className: "inline-flex items-center justify-center rounded-full p-1 hover:bg-gray-200 focus:outline-none "
|
|
116
|
+
className: cn("inline-flex items-center justify-center rounded-full p-1 hover:bg-gray-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500", TOUCH_TARGET, closeClass),
|
|
101
117
|
children: jsx("svg", {
|
|
118
|
+
"aria-hidden": "true",
|
|
102
119
|
className: "w-3 h-3",
|
|
103
120
|
viewBox: "0 0 24 24",
|
|
104
121
|
fill: "none",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { defineProperty as _defineProperty } from '../../../_virtual/_rollupPluginBabelHelpers.js';
|
|
3
2
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
3
|
import 'react';
|
|
5
4
|
import { cn } from '../../../utils/cn.js';
|
|
5
|
+
import { getHoverClass } from './helpers.js';
|
|
6
6
|
|
|
7
7
|
var DEFAULT_ROW_HOVER = "bg-background";
|
|
8
8
|
/**
|
|
@@ -32,9 +32,7 @@ function ColumnMenu(_ref) {
|
|
|
32
32
|
className: "max-h-56 overflow-auto",
|
|
33
33
|
children: columns.map(function (col) {
|
|
34
34
|
return jsxs("label", {
|
|
35
|
-
className: cn(
|
|
36
|
-
"flex cursor-pointer items-center gap-2 px-2 py-1.5 rounded": true
|
|
37
|
-
}, "hover:" + rowHover, true)),
|
|
35
|
+
className: cn("flex cursor-pointer items-center gap-2 px-2 py-1.5 rounded", getHoverClass(rowHover)),
|
|
38
36
|
children: [jsx("input", {
|
|
39
37
|
type: "checkbox",
|
|
40
38
|
checked: col.isVisible !== false,
|
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { defineProperty as _defineProperty } from '../../../_virtual/_rollupPluginBabelHelpers.js';
|
|
3
2
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
3
|
import 'react';
|
|
5
4
|
import { cn } from '../../../utils/cn.js';
|
|
5
|
+
import { TOUCH_TARGET } from '../../../utils/touchTarget.js';
|
|
6
|
+
import { getHoverClass } from './helpers.js';
|
|
6
7
|
import { ChevronsLeft, ChevronLeft, ChevronRight, ChevronsRight } from 'lucide-react';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Pagination controls component for the table
|
|
10
11
|
* Supports both header (icon-only) and footer (with page numbers) variants
|
|
11
12
|
*/
|
|
12
|
-
var
|
|
13
|
+
var NAV_BUTTON_BASE = "inline-flex items-center justify-center rounded-lg md:rounded-md border border-gray-300 disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500";
|
|
13
14
|
// Header pagination (compact, icon-based)
|
|
14
15
|
function PaginationHeader(_ref) {
|
|
15
|
-
var _theme$rowHover;
|
|
16
16
|
var currentPage = _ref.currentPage,
|
|
17
17
|
totalPages = _ref.totalPages,
|
|
18
18
|
onPageChange = _ref.onPageChange,
|
|
19
19
|
theme = _ref.theme;
|
|
20
|
-
var
|
|
20
|
+
var hoverClass = getHoverClass(theme === null || theme === void 0 ? void 0 : theme.rowHover);
|
|
21
21
|
return jsxs("div", {
|
|
22
22
|
className: "flex items-center gap-1",
|
|
23
23
|
children: [totalPages >= 4 && jsx("button", {
|
|
24
|
-
className: cn("
|
|
24
|
+
className: cn(NAV_BUTTON_BASE, "p-2", TOUCH_TARGET, hoverClass),
|
|
25
25
|
disabled: currentPage === 1,
|
|
26
26
|
onClick: function onClick() {
|
|
27
27
|
return onPageChange(1);
|
|
@@ -31,7 +31,7 @@ function PaginationHeader(_ref) {
|
|
|
31
31
|
className: "size-4 md:size-5 text-primary-800"
|
|
32
32
|
})
|
|
33
33
|
}), jsx("button", {
|
|
34
|
-
className: cn("
|
|
34
|
+
className: cn(NAV_BUTTON_BASE, "p-2", TOUCH_TARGET, hoverClass),
|
|
35
35
|
disabled: currentPage === 1,
|
|
36
36
|
onClick: function onClick() {
|
|
37
37
|
return onPageChange(Math.max(1, currentPage - 1));
|
|
@@ -41,7 +41,7 @@ function PaginationHeader(_ref) {
|
|
|
41
41
|
className: "size-4 md:size-5 text-primary-800"
|
|
42
42
|
})
|
|
43
43
|
}), jsx("button", {
|
|
44
|
-
className: cn("
|
|
44
|
+
className: cn(NAV_BUTTON_BASE, "p-2", TOUCH_TARGET, hoverClass),
|
|
45
45
|
disabled: currentPage === totalPages,
|
|
46
46
|
onClick: function onClick() {
|
|
47
47
|
return onPageChange(Math.min(totalPages, currentPage + 1));
|
|
@@ -51,7 +51,7 @@ function PaginationHeader(_ref) {
|
|
|
51
51
|
className: "size-4 md:size-5 text-primary-800"
|
|
52
52
|
})
|
|
53
53
|
}), totalPages >= 4 && jsx("button", {
|
|
54
|
-
className: cn("
|
|
54
|
+
className: cn(NAV_BUTTON_BASE, "p-2", TOUCH_TARGET, hoverClass),
|
|
55
55
|
disabled: currentPage === totalPages,
|
|
56
56
|
onClick: function onClick() {
|
|
57
57
|
return onPageChange(totalPages);
|
|
@@ -65,13 +65,12 @@ function PaginationHeader(_ref) {
|
|
|
65
65
|
}
|
|
66
66
|
// Footer pagination (with page numbers)
|
|
67
67
|
function PaginationFooter(_ref2) {
|
|
68
|
-
var _theme$rowHover2;
|
|
69
68
|
var currentPage = _ref2.currentPage,
|
|
70
69
|
totalPages = _ref2.totalPages,
|
|
71
70
|
onPageChange = _ref2.onPageChange,
|
|
72
71
|
isMobileView = _ref2.isMobileView,
|
|
73
72
|
theme = _ref2.theme;
|
|
74
|
-
var
|
|
73
|
+
var hoverClass = getHoverClass(theme === null || theme === void 0 ? void 0 : theme.rowHover);
|
|
75
74
|
var visiblePageCount = isMobileView ? 5 : 3;
|
|
76
75
|
return jsx("div", {
|
|
77
76
|
className: "p-4 border-t",
|
|
@@ -83,7 +82,7 @@ function PaginationFooter(_ref2) {
|
|
|
83
82
|
}), jsxs("div", {
|
|
84
83
|
className: "flex items-center gap-1 sm:gap-2",
|
|
85
84
|
children: [totalPages >= 4 && jsx("button", {
|
|
86
|
-
className: cn(
|
|
85
|
+
className: cn(NAV_BUTTON_BASE, isMobileView ? "p-2" : "px-4 py-2 text-sm", TOUCH_TARGET, hoverClass),
|
|
87
86
|
disabled: currentPage === 1,
|
|
88
87
|
onClick: function onClick() {
|
|
89
88
|
return onPageChange(1);
|
|
@@ -93,7 +92,7 @@ function PaginationFooter(_ref2) {
|
|
|
93
92
|
className: "size-4 text-primary-800"
|
|
94
93
|
}) : "First"
|
|
95
94
|
}), jsx("button", {
|
|
96
|
-
className: cn(
|
|
95
|
+
className: cn(NAV_BUTTON_BASE, isMobileView ? "p-2" : "px-4 py-2 text-sm", TOUCH_TARGET, hoverClass),
|
|
97
96
|
disabled: currentPage === 1,
|
|
98
97
|
onClick: function onClick() {
|
|
99
98
|
return onPageChange(Math.max(1, currentPage - 1));
|
|
@@ -118,10 +117,8 @@ function PaginationFooter(_ref2) {
|
|
|
118
117
|
pageNum = currentPage - 2 + i;
|
|
119
118
|
}
|
|
120
119
|
return jsx("button", {
|
|
121
|
-
className: cn("h-8 w-8 rounded-lg md:rounded-md text-sm",
|
|
122
|
-
|
|
123
|
-
"border border-gray-300": pageNum !== currentPage
|
|
124
|
-
}, "hover:" + rowHover, pageNum !== currentPage)),
|
|
120
|
+
className: cn("inline-flex items-center justify-center h-8 w-8 rounded-lg md:rounded-md text-sm focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500", TOUCH_TARGET, pageNum === currentPage ? "bg-primary-600 text-white" : ["border border-gray-300", hoverClass]),
|
|
121
|
+
"aria-current": pageNum === currentPage ? "page" : undefined,
|
|
125
122
|
onClick: function onClick() {
|
|
126
123
|
return onPageChange(pageNum);
|
|
127
124
|
},
|
|
@@ -129,7 +126,7 @@ function PaginationFooter(_ref2) {
|
|
|
129
126
|
}, pageNum);
|
|
130
127
|
})
|
|
131
128
|
}), jsx("button", {
|
|
132
|
-
className: cn(
|
|
129
|
+
className: cn(NAV_BUTTON_BASE, isMobileView ? "p-2" : "px-4 py-2 text-sm", TOUCH_TARGET, hoverClass),
|
|
133
130
|
disabled: currentPage === totalPages,
|
|
134
131
|
onClick: function onClick() {
|
|
135
132
|
return onPageChange(Math.min(totalPages, currentPage + 1));
|
|
@@ -139,7 +136,7 @@ function PaginationFooter(_ref2) {
|
|
|
139
136
|
className: "size-4 text-primary-800"
|
|
140
137
|
}) : "Next"
|
|
141
138
|
}), totalPages >= 4 && jsx("button", {
|
|
142
|
-
className: cn(
|
|
139
|
+
className: cn(NAV_BUTTON_BASE, isMobileView ? "p-2" : "px-4 py-2 text-sm", TOUCH_TARGET, hoverClass),
|
|
143
140
|
disabled: currentPage === totalPages,
|
|
144
141
|
onClick: function onClick() {
|
|
145
142
|
return onPageChange(totalPages);
|
|
@@ -5,7 +5,7 @@ import React from 'react';
|
|
|
5
5
|
import { cn } from '../../../utils/cn.js';
|
|
6
6
|
import { Trash2, MoreVertical } from 'lucide-react';
|
|
7
7
|
import ActionMenu from './ActionMenu.js';
|
|
8
|
-
import { getRowKey, resolveActions, rowHasDetails, getActionLabelText, getStripeProps } from './helpers.js';
|
|
8
|
+
import { getRowKey, resolveActions, rowHasDetails, getActionLabelText, getHoverClass, getStripeProps } from './helpers.js';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Desktop table row component
|
|
@@ -96,7 +96,7 @@ function TableRow(_ref) {
|
|
|
96
96
|
return jsxs(React.Fragment, {
|
|
97
97
|
children: [jsxs("tr", {
|
|
98
98
|
style: stripeProps.style,
|
|
99
|
-
className: cn(stripeProps.className, theme !== null && theme !== void 0 && theme.rowHover ?
|
|
99
|
+
className: cn(stripeProps.className, theme !== null && theme !== void 0 && theme.rowHover ? getHoverClass(theme.rowHover) : [], {
|
|
100
100
|
"cursor-pointer": !!onRowClick,
|
|
101
101
|
"bg-primary-50": isSelected,
|
|
102
102
|
"bg-primary-50/20": isRowActive && !isSelected,
|
|
@@ -135,5 +135,37 @@ var resolveActions = function resolveActions(actions, row, index) {
|
|
|
135
135
|
menuActions: menuActions
|
|
136
136
|
};
|
|
137
137
|
};
|
|
138
|
+
/**
|
|
139
|
+
* Tailwind only generates classes it can see as complete literals in source, so a
|
|
140
|
+
* runtime-built `"hover:" + token` is purged from the compiled CSS. Bare `rowHover`
|
|
141
|
+
* tokens (the shipped defaults, past and present) map to static literals here.
|
|
142
|
+
*/
|
|
143
|
+
var HOVER_BG_CLASSES = {
|
|
144
|
+
"bg-background": "hover:bg-background",
|
|
145
|
+
"bg-surface": "hover:bg-surface",
|
|
146
|
+
"bg-gray-50": "hover:bg-gray-50",
|
|
147
|
+
"bg-gray-100": "hover:bg-gray-100",
|
|
148
|
+
"bg-primary-50": "hover:bg-primary-50",
|
|
149
|
+
"bg-primary-100": "hover:bg-primary-100"
|
|
150
|
+
};
|
|
151
|
+
var DEFAULT_HOVER_CLASS = HOVER_BG_CLASSES["bg-background"];
|
|
152
|
+
/**
|
|
153
|
+
* Resolve `theme.rowHover` to a full hover class string.
|
|
154
|
+
* - A value that already contains a `hover:` variant is used as-is (preferred: pass
|
|
155
|
+
* the full literal, e.g. `"hover:bg-gray-100"`, so your Tailwind build sees it).
|
|
156
|
+
* - A bare token from the static map resolves to its literal.
|
|
157
|
+
* - Any other bare token falls back to a `hover:` prefix for backward compatibility;
|
|
158
|
+
* it only works if the consumer's own Tailwind build generates that class.
|
|
159
|
+
*/
|
|
160
|
+
var getHoverClass = function getHoverClass(rowHover) {
|
|
161
|
+
if (!rowHover || typeof rowHover !== "string") return DEFAULT_HOVER_CLASS;
|
|
162
|
+
var value = rowHover.trim();
|
|
163
|
+
if (!value) return DEFAULT_HOVER_CLASS;
|
|
164
|
+
if (/(^|\s)hover:/.test(value)) return value;
|
|
165
|
+
if (HOVER_BG_CLASSES[value]) return HOVER_BG_CLASSES[value];
|
|
166
|
+
return value.split(/\s+/).map(function (cls) {
|
|
167
|
+
return "hover:".concat(cls);
|
|
168
|
+
}).join(" ");
|
|
169
|
+
};
|
|
138
170
|
|
|
139
|
-
export { getActionLabelText, getRowKey, getStripeProps, mergeDefaultActions, resolveActions, rowHasDetails };
|
|
171
|
+
export { DEFAULT_HOVER_CLASS, getActionLabelText, getHoverClass, getRowKey, getStripeProps, mergeDefaultActions, resolveActions, rowHasDetails };
|
|
@@ -156,7 +156,7 @@ function Tab(_ref3) {
|
|
|
156
156
|
var idx = typeof index === "number" ? index : -1;
|
|
157
157
|
var selected = idx === activeIndex;
|
|
158
158
|
var id = tabId(baseId, idx);
|
|
159
|
-
var base = "inline-flex items-center gap-2 outline-none select-none";
|
|
159
|
+
var base = "inline-flex items-center gap-2 outline-none select-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-1";
|
|
160
160
|
var sizeClasses = {
|
|
161
161
|
sm: "px-2 py-1 text-sm",
|
|
162
162
|
md: "px-3 py-2 text-sm",
|
|
@@ -269,15 +269,16 @@ function Panel(_ref5) {
|
|
|
269
269
|
// Remember that the panel has been shown once (adjust-during-render pattern).
|
|
270
270
|
if (isActive && !wasActive) setWasActive(true);
|
|
271
271
|
if (!wasActive && !isActive) return null;
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
272
|
+
// Inactive panels are `hidden` (display: none), so a CSS *transition* can
|
|
273
|
+
// never run. A keyframe animation does replay each time the panel leaves
|
|
274
|
+
// display:none, so the active panel fades in on every switch.
|
|
275
|
+
var activeClasses = animated ? "animate-fade-in motion-reduce:animate-none" : "";
|
|
275
276
|
return jsx("div", {
|
|
276
277
|
id: panelId(baseId, index),
|
|
277
278
|
role: "tabpanel",
|
|
278
279
|
"aria-labelledby": tabId(baseId, index),
|
|
279
280
|
hidden: !isActive,
|
|
280
|
-
className: ""
|
|
281
|
+
className: cn("w-full", isActive && activeClasses, className),
|
|
281
282
|
children: children
|
|
282
283
|
});
|
|
283
284
|
}
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { slicedToArray as _slicedToArray } from '../../_virtual/_rollupPluginBabelHelpers.js';
|
|
3
|
-
import { jsx } from 'react/jsx-runtime';
|
|
4
|
-
import { useState } from 'react';
|
|
2
|
+
import { objectWithoutProperties as _objectWithoutProperties, slicedToArray as _slicedToArray, objectSpread2 as _objectSpread2 } from '../../_virtual/_rollupPluginBabelHelpers.js';
|
|
3
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
|
+
import { forwardRef, useState, useRef, useEffect, useCallback } from 'react';
|
|
5
|
+
import { Square, Volume2 } from 'lucide-react';
|
|
6
|
+
import { cn } from '../../utils/cn.js';
|
|
5
7
|
|
|
6
|
-
var
|
|
8
|
+
var _excluded = ["text", "rate", "pitch", "onSpeak", "onError", "renderButton", "disabled", "className"];
|
|
9
|
+
var UNSUPPORTED_MESSAGE = "Text-to-speech is not supported in this browser";
|
|
10
|
+
// `interrupted` / `canceled` fire when playback is stopped on purpose (our own
|
|
11
|
+
// cancel(), or another utterance taking over) — not failures worth surfacing.
|
|
12
|
+
var NON_FATAL_ERRORS = new Set(["interrupted", "canceled"]);
|
|
13
|
+
var detectSupport = function detectSupport() {
|
|
14
|
+
return typeof window !== "undefined" && "speechSynthesis" in window && !!window.speechSynthesis && typeof window.SpeechSynthesisUtterance !== "undefined";
|
|
15
|
+
};
|
|
16
|
+
var TextToSpeech = /*#__PURE__*/forwardRef(function (_ref, ref) {
|
|
7
17
|
var _ref$text = _ref.text,
|
|
8
18
|
text = _ref$text === void 0 ? "" : _ref$text,
|
|
9
19
|
_ref$rate = _ref.rate,
|
|
@@ -12,41 +22,124 @@ var TextToSpeech = function TextToSpeech(_ref) {
|
|
|
12
22
|
pitch = _ref$pitch === void 0 ? 0.5 : _ref$pitch,
|
|
13
23
|
_ref$onSpeak = _ref.onSpeak,
|
|
14
24
|
onSpeak = _ref$onSpeak === void 0 ? function () {} : _ref$onSpeak,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
_ref$onError = _ref.onError,
|
|
26
|
+
onError = _ref$onError === void 0 ? function () {} : _ref$onError,
|
|
27
|
+
renderButton = _ref.renderButton,
|
|
28
|
+
_ref$disabled = _ref.disabled,
|
|
29
|
+
disabled = _ref$disabled === void 0 ? false : _ref$disabled,
|
|
30
|
+
className = _ref.className,
|
|
31
|
+
rest = _objectWithoutProperties(_ref, _excluded);
|
|
32
|
+
var _useState = useState(false),
|
|
33
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
34
|
+
isSpeaking = _useState2[0],
|
|
35
|
+
setIsSpeaking = _useState2[1];
|
|
36
|
+
var _useState3 = useState(true),
|
|
20
37
|
_useState4 = _slicedToArray(_useState3, 2),
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
var
|
|
24
|
-
|
|
38
|
+
isSupported = _useState4[0],
|
|
39
|
+
setIsSupported = _useState4[1];
|
|
40
|
+
var isMountedRef = useRef(true);
|
|
41
|
+
var utteranceRef = useRef(null);
|
|
42
|
+
useEffect(function () {
|
|
43
|
+
setIsSupported(detectSupport());
|
|
44
|
+
}, []);
|
|
45
|
+
var stop = useCallback(function () {
|
|
46
|
+
if (detectSupport()) window.speechSynthesis.cancel();
|
|
47
|
+
utteranceRef.current = null;
|
|
48
|
+
setIsSpeaking(false);
|
|
49
|
+
}, []);
|
|
50
|
+
var speak = useCallback(function () {
|
|
51
|
+
if (!detectSupport()) {
|
|
52
|
+
setIsSupported(false);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
// Always speak the *current* `text` prop, not a value captured on mount.
|
|
56
|
+
var spokenText = text;
|
|
57
|
+
var utterance = new window.SpeechSynthesisUtterance(spokenText);
|
|
25
58
|
utterance.rate = rate;
|
|
26
59
|
utterance.pitch = pitch;
|
|
60
|
+
var isCurrent = function isCurrent() {
|
|
61
|
+
return isMountedRef.current && utteranceRef.current === utterance;
|
|
62
|
+
};
|
|
27
63
|
utterance.onstart = function () {
|
|
28
|
-
|
|
64
|
+
if (!isCurrent()) return;
|
|
65
|
+
onSpeak(spokenText);
|
|
29
66
|
setIsSpeaking(true);
|
|
30
67
|
};
|
|
31
68
|
utterance.onend = function () {
|
|
69
|
+
if (!isCurrent()) return;
|
|
70
|
+
utteranceRef.current = null;
|
|
32
71
|
setIsSpeaking(false);
|
|
33
72
|
};
|
|
34
|
-
|
|
35
|
-
|
|
73
|
+
utterance.onerror = function (event) {
|
|
74
|
+
if (!isCurrent()) return;
|
|
75
|
+
utteranceRef.current = null;
|
|
36
76
|
setIsSpeaking(false);
|
|
37
|
-
|
|
77
|
+
if (!NON_FATAL_ERRORS.has(event === null || event === void 0 ? void 0 : event.error)) {
|
|
78
|
+
onError(new Error((event === null || event === void 0 ? void 0 : event.error) || "Speech synthesis error"));
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
utteranceRef.current = utterance;
|
|
82
|
+
try {
|
|
38
83
|
window.speechSynthesis.speak(utterance);
|
|
84
|
+
} catch (err) {
|
|
85
|
+
utteranceRef.current = null;
|
|
86
|
+
setIsSpeaking(false);
|
|
87
|
+
onError(err);
|
|
39
88
|
}
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
89
|
+
}, [text, rate, pitch, onSpeak, onError]);
|
|
90
|
+
var isDisabled = disabled || !isSupported;
|
|
91
|
+
var handleClick = useCallback(function () {
|
|
92
|
+
if (isDisabled) return;
|
|
93
|
+
if (isSpeaking) stop();else speak();
|
|
94
|
+
}, [isDisabled, isSpeaking, speak, stop]);
|
|
95
|
+
// Stop our own speech when unmounting so it doesn't outlive the component.
|
|
96
|
+
useEffect(function () {
|
|
97
|
+
isMountedRef.current = true;
|
|
98
|
+
return function () {
|
|
99
|
+
isMountedRef.current = false;
|
|
100
|
+
if (utteranceRef.current && detectSupport()) {
|
|
101
|
+
utteranceRef.current = null;
|
|
102
|
+
window.speechSynthesis.cancel();
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
}, []);
|
|
106
|
+
if (typeof renderButton === "function") {
|
|
107
|
+
// speak/stop only touch refs when invoked from event handlers, never while
|
|
108
|
+
// renderButton itself is rendering.
|
|
109
|
+
// eslint-disable-next-line react-hooks/refs
|
|
110
|
+
return renderButton({
|
|
111
|
+
onClick: handleClick,
|
|
112
|
+
isSpeaking: isSpeaking,
|
|
113
|
+
isSupported: isSupported,
|
|
114
|
+
disabled: isDisabled,
|
|
115
|
+
speak: speak,
|
|
116
|
+
stop: stop
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return jsxs("button", _objectSpread2(_objectSpread2({
|
|
120
|
+
ref: ref,
|
|
121
|
+
type: "button",
|
|
122
|
+
onClick: handleClick,
|
|
123
|
+
disabled: isDisabled,
|
|
124
|
+
"aria-pressed": isSpeaking,
|
|
125
|
+
title: !isSupported ? UNSUPPORTED_MESSAGE : undefined,
|
|
126
|
+
className: cn("inline-flex items-center gap-2 rounded-md px-4 h-10 text-sm font-medium", "border border-gray-300 bg-transparent transition-colors motion-reduce:transition-none", "hover:bg-gray-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-primary-500", "disabled:opacity-50 disabled:cursor-not-allowed", {
|
|
127
|
+
"text-error-600 border-error-600": isSpeaking,
|
|
128
|
+
"text-gray-900": !isSpeaking
|
|
129
|
+
}, className)
|
|
130
|
+
}, rest), {}, {
|
|
131
|
+
children: [isSpeaking ? jsx(Square, {
|
|
132
|
+
className: "h-4 w-4",
|
|
133
|
+
"aria-hidden": "true"
|
|
134
|
+
}) : jsx(Volume2, {
|
|
135
|
+
className: "h-4 w-4",
|
|
136
|
+
"aria-hidden": "true"
|
|
137
|
+
}), isSpeaking ? "Stop" : "Speak", !isSupported && jsx("span", {
|
|
138
|
+
className: "sr-only",
|
|
139
|
+
children: " (".concat(UNSUPPORTED_MESSAGE, ")")
|
|
140
|
+
})]
|
|
141
|
+
}));
|
|
142
|
+
});
|
|
143
|
+
TextToSpeech.displayName = "TextToSpeech";
|
|
51
144
|
|
|
52
145
|
export { TextToSpeech as default };
|