@prmichaelsen/agentbase-ux 0.0.2
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 +62 -0
- package/dist/__tests__/setup.d.ts +2 -0
- package/dist/__tests__/setup.d.ts.map +1 -0
- package/dist/__tests__/test-utils.d.ts +6 -0
- package/dist/__tests__/test-utils.d.ts.map +1 -0
- package/dist/components/Button.d.ts +19 -0
- package/dist/components/Button.d.ts.map +1 -0
- package/dist/components/Button.test.d.ts +2 -0
- package/dist/components/Button.test.d.ts.map +1 -0
- package/dist/components/ConfirmationModal.d.ts +17 -0
- package/dist/components/ConfirmationModal.d.ts.map +1 -0
- package/dist/components/ConfirmationModal.test.d.ts +2 -0
- package/dist/components/ConfirmationModal.test.d.ts.map +1 -0
- package/dist/components/MenuItem.d.ts +17 -0
- package/dist/components/MenuItem.d.ts.map +1 -0
- package/dist/components/MenuItem.test.d.ts +2 -0
- package/dist/components/MenuItem.test.d.ts.map +1 -0
- package/dist/components/Modal.d.ts +18 -0
- package/dist/components/Modal.d.ts.map +1 -0
- package/dist/components/Modal.test.d.ts +2 -0
- package/dist/components/Modal.test.d.ts.map +1 -0
- package/dist/components/Paginator.d.ts +14 -0
- package/dist/components/Paginator.d.ts.map +1 -0
- package/dist/components/Paginator.test.d.ts +2 -0
- package/dist/components/Paginator.test.d.ts.map +1 -0
- package/dist/components/Popover.d.ts +18 -0
- package/dist/components/Popover.d.ts.map +1 -0
- package/dist/components/SlideOverPanel.d.ts +12 -0
- package/dist/components/SlideOverPanel.d.ts.map +1 -0
- package/dist/components/SlideOverPanel.test.d.ts +2 -0
- package/dist/components/SlideOverPanel.test.d.ts.map +1 -0
- package/dist/components/Slider.d.ts +36 -0
- package/dist/components/Slider.d.ts.map +1 -0
- package/dist/components/Slider.test.d.ts +2 -0
- package/dist/components/Slider.test.d.ts.map +1 -0
- package/dist/components/ToggleSwitch.d.ts +17 -0
- package/dist/components/ToggleSwitch.d.ts.map +1 -0
- package/dist/components/ToggleSwitch.test.d.ts +2 -0
- package/dist/components/ToggleSwitch.test.d.ts.map +1 -0
- package/dist/hooks/useDebounce.d.ts +2 -0
- package/dist/hooks/useDebounce.d.ts.map +1 -0
- package/dist/hooks/useDebounce.test.d.ts +2 -0
- package/dist/hooks/useDebounce.test.d.ts.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +746 -0
- package/dist/index.js.map +7 -0
- package/dist/lib/theming.d.ts +61 -0
- package/dist/lib/theming.d.ts.map +1 -0
- package/dist/lib/theming.test.d.ts +2 -0
- package/dist/lib/theming.test.d.ts.map +1 -0
- package/dist/tokens.css +88 -0
- package/dist/utils/clipboard.d.ts +9 -0
- package/dist/utils/clipboard.d.ts.map +1 -0
- package/dist/utils/clipboard.test.d.ts +2 -0
- package/dist/utils/clipboard.test.d.ts.map +1 -0
- package/dist/utils/format-time.d.ts +6 -0
- package/dist/utils/format-time.d.ts.map +1 -0
- package/dist/utils/format-time.test.d.ts +2 -0
- package/dist/utils/format-time.test.d.ts.map +1 -0
- package/package.json +43 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,746 @@
|
|
|
1
|
+
// src/lib/theming.tsx
|
|
2
|
+
import { createContext, useContext } from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
var darkTheme = {
|
|
5
|
+
// Layout
|
|
6
|
+
page: "bg-bg-page text-text-primary",
|
|
7
|
+
sidebar: "bg-bg-sidebar border-r border-border-default",
|
|
8
|
+
card: "bg-bg-card border border-border-default rounded-lg",
|
|
9
|
+
elevated: "bg-bg-elevated",
|
|
10
|
+
hover: "hover:bg-bg-hover",
|
|
11
|
+
active: "bg-bg-active",
|
|
12
|
+
border: "border border-border-default",
|
|
13
|
+
borderSubtle: "border border-border-subtle",
|
|
14
|
+
input: "bg-bg-input border border-border-default text-text-primary placeholder:text-text-muted",
|
|
15
|
+
inputFocus: "focus:border-brand-primary focus:ring-1 focus:ring-brand-primary/30",
|
|
16
|
+
// Text
|
|
17
|
+
textPrimary: "text-text-primary",
|
|
18
|
+
textSecondary: "text-text-secondary",
|
|
19
|
+
textMuted: "text-text-muted",
|
|
20
|
+
// Buttons
|
|
21
|
+
buttonPrimary: "bg-brand-primary text-white hover:bg-brand-primary/90",
|
|
22
|
+
buttonSecondary: "bg-brand-secondary text-white hover:bg-brand-secondary/90",
|
|
23
|
+
buttonGhost: "bg-transparent text-text-secondary hover:bg-bg-hover hover:text-text-primary",
|
|
24
|
+
buttonDanger: "bg-brand-danger text-white hover:bg-brand-danger/90",
|
|
25
|
+
buttonSuccess: "bg-brand-success text-white hover:bg-brand-success/90",
|
|
26
|
+
buttonWarning: "bg-brand-warning text-white hover:bg-brand-warning/90",
|
|
27
|
+
// Overlay
|
|
28
|
+
overlay: "bg-black/50 backdrop-blur-sm",
|
|
29
|
+
// Toggle
|
|
30
|
+
toggleOn: "bg-brand-primary",
|
|
31
|
+
toggleOff: "bg-bg-elevated",
|
|
32
|
+
// Slider
|
|
33
|
+
sliderTrack: "bg-bg-elevated",
|
|
34
|
+
sliderFill: "bg-brand-primary",
|
|
35
|
+
// Tabs
|
|
36
|
+
tabActive: "bg-brand-primary text-white",
|
|
37
|
+
tabInactive: "text-text-secondary hover:bg-bg-hover hover:text-text-primary",
|
|
38
|
+
// Menu
|
|
39
|
+
menuItem: "text-text-secondary hover:text-text-primary hover:bg-bg-hover",
|
|
40
|
+
menuItemHover: "bg-bg-hover text-text-primary",
|
|
41
|
+
menuItemDanger: "text-brand-danger hover:bg-brand-danger/10",
|
|
42
|
+
// Messages
|
|
43
|
+
messageSelf: "bg-brand-primary/20 border-l-2 border-brand-primary/50",
|
|
44
|
+
messageOther: "bg-bg-card",
|
|
45
|
+
messageAgent: "bg-brand-accent/10 border-l-2 border-brand-accent/50",
|
|
46
|
+
messageSystem: "bg-bg-elevated text-text-muted text-sm italic",
|
|
47
|
+
// Status
|
|
48
|
+
statusOnline: "bg-brand-success",
|
|
49
|
+
statusOffline: "bg-text-muted",
|
|
50
|
+
statusAway: "bg-brand-warning",
|
|
51
|
+
// Badges
|
|
52
|
+
badgeDefault: "bg-bg-elevated text-text-secondary",
|
|
53
|
+
badgeSuccess: "bg-brand-success/15 text-brand-success",
|
|
54
|
+
badgeWarning: "bg-brand-warning/15 text-brand-warning",
|
|
55
|
+
badgeDanger: "bg-brand-danger/15 text-brand-danger",
|
|
56
|
+
badgeInfo: "bg-brand-info/15 text-brand-info",
|
|
57
|
+
// Notifications
|
|
58
|
+
notificationBadge: "bg-brand-danger text-white",
|
|
59
|
+
notificationUnread: "bg-brand-primary/5",
|
|
60
|
+
notificationRead: "bg-bg-card"
|
|
61
|
+
};
|
|
62
|
+
var lightTheme = {
|
|
63
|
+
// Layout
|
|
64
|
+
page: "bg-bg-page text-text-primary",
|
|
65
|
+
sidebar: "bg-bg-sidebar border-r border-border-default",
|
|
66
|
+
card: "bg-bg-card border border-border-default rounded-lg shadow-sm",
|
|
67
|
+
elevated: "bg-bg-elevated",
|
|
68
|
+
hover: "hover:bg-bg-hover",
|
|
69
|
+
active: "bg-bg-active",
|
|
70
|
+
border: "border border-border-default",
|
|
71
|
+
borderSubtle: "border border-border-subtle",
|
|
72
|
+
input: "bg-bg-input border border-border-default text-text-primary placeholder:text-text-muted",
|
|
73
|
+
inputFocus: "focus:border-brand-primary focus:ring-1 focus:ring-brand-primary/30",
|
|
74
|
+
// Text
|
|
75
|
+
textPrimary: "text-text-primary",
|
|
76
|
+
textSecondary: "text-text-secondary",
|
|
77
|
+
textMuted: "text-text-muted",
|
|
78
|
+
// Buttons
|
|
79
|
+
buttonPrimary: "bg-brand-primary text-white hover:bg-brand-primary/90",
|
|
80
|
+
buttonSecondary: "bg-brand-secondary text-white hover:bg-brand-secondary/90",
|
|
81
|
+
buttonGhost: "bg-transparent text-text-secondary hover:bg-bg-hover hover:text-text-primary",
|
|
82
|
+
buttonDanger: "bg-brand-danger text-white hover:bg-brand-danger/90",
|
|
83
|
+
buttonSuccess: "bg-brand-success text-white hover:bg-brand-success/90",
|
|
84
|
+
buttonWarning: "bg-brand-warning text-white hover:bg-brand-warning/90",
|
|
85
|
+
// Overlay
|
|
86
|
+
overlay: "bg-black/40 backdrop-blur-sm",
|
|
87
|
+
// Toggle
|
|
88
|
+
toggleOn: "bg-brand-primary",
|
|
89
|
+
toggleOff: "bg-border-default",
|
|
90
|
+
// Slider
|
|
91
|
+
sliderTrack: "bg-bg-elevated",
|
|
92
|
+
sliderFill: "bg-brand-primary",
|
|
93
|
+
// Tabs
|
|
94
|
+
tabActive: "bg-brand-primary text-white",
|
|
95
|
+
tabInactive: "text-text-secondary hover:bg-bg-hover hover:text-text-primary",
|
|
96
|
+
// Menu
|
|
97
|
+
menuItem: "text-text-secondary hover:text-text-primary hover:bg-bg-hover",
|
|
98
|
+
menuItemHover: "bg-bg-hover text-text-primary",
|
|
99
|
+
menuItemDanger: "text-brand-danger hover:bg-brand-danger/10",
|
|
100
|
+
// Messages
|
|
101
|
+
messageSelf: "bg-brand-primary/10 border-l-2 border-brand-primary/40",
|
|
102
|
+
messageOther: "bg-bg-card",
|
|
103
|
+
messageAgent: "bg-brand-accent/5 border-l-2 border-brand-accent/40",
|
|
104
|
+
messageSystem: "bg-bg-elevated text-text-muted text-sm italic",
|
|
105
|
+
// Status
|
|
106
|
+
statusOnline: "bg-brand-success",
|
|
107
|
+
statusOffline: "bg-text-muted",
|
|
108
|
+
statusAway: "bg-brand-warning",
|
|
109
|
+
// Badges
|
|
110
|
+
badgeDefault: "bg-bg-elevated text-text-secondary",
|
|
111
|
+
badgeSuccess: "bg-brand-success/15 text-brand-success",
|
|
112
|
+
badgeWarning: "bg-brand-warning/15 text-brand-warning",
|
|
113
|
+
badgeDanger: "bg-brand-danger/15 text-brand-danger",
|
|
114
|
+
badgeInfo: "bg-brand-info/15 text-brand-info",
|
|
115
|
+
// Notifications
|
|
116
|
+
notificationBadge: "bg-brand-danger text-white",
|
|
117
|
+
notificationUnread: "bg-brand-primary/5",
|
|
118
|
+
notificationRead: "bg-bg-card"
|
|
119
|
+
};
|
|
120
|
+
var themes = { dark: darkTheme, light: lightTheme };
|
|
121
|
+
var ThemingContext = createContext(darkTheme);
|
|
122
|
+
function ThemingProvider({
|
|
123
|
+
theme = "dark",
|
|
124
|
+
children
|
|
125
|
+
}) {
|
|
126
|
+
return /* @__PURE__ */ jsx(ThemingContext.Provider, { value: themes[theme], children: /* @__PURE__ */ jsx("div", { "data-theme": theme, className: themes[theme].page, children }) });
|
|
127
|
+
}
|
|
128
|
+
function useTheme() {
|
|
129
|
+
return useContext(ThemingContext);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// src/components/Button.tsx
|
|
133
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
134
|
+
var sizeStyles = {
|
|
135
|
+
sm: "px-3 py-1.5 text-sm",
|
|
136
|
+
md: "px-4 py-2 text-base",
|
|
137
|
+
lg: "px-6 py-3 text-lg"
|
|
138
|
+
};
|
|
139
|
+
function Button({
|
|
140
|
+
variant = "primary",
|
|
141
|
+
size = "md",
|
|
142
|
+
icon,
|
|
143
|
+
children,
|
|
144
|
+
fullWidth = false,
|
|
145
|
+
className = "",
|
|
146
|
+
disabled = false,
|
|
147
|
+
...props
|
|
148
|
+
}) {
|
|
149
|
+
const t = useTheme();
|
|
150
|
+
const variantStyles = {
|
|
151
|
+
primary: t.buttonPrimary,
|
|
152
|
+
secondary: t.buttonSecondary,
|
|
153
|
+
danger: t.buttonDanger,
|
|
154
|
+
success: t.buttonSuccess,
|
|
155
|
+
ghost: t.buttonGhost
|
|
156
|
+
};
|
|
157
|
+
const combinedClassName = [
|
|
158
|
+
"font-medium rounded-lg transition-all flex items-center justify-center gap-2",
|
|
159
|
+
variantStyles[variant],
|
|
160
|
+
sizeStyles[size],
|
|
161
|
+
fullWidth ? "w-full" : "",
|
|
162
|
+
disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer",
|
|
163
|
+
className
|
|
164
|
+
].filter(Boolean).join(" ");
|
|
165
|
+
return /* @__PURE__ */ jsxs("button", { className: combinedClassName, disabled, ...props, children: [
|
|
166
|
+
icon && /* @__PURE__ */ jsx2("span", { className: "flex-shrink-0", children: icon }),
|
|
167
|
+
children
|
|
168
|
+
] });
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// src/components/Modal.tsx
|
|
172
|
+
import { useEffect } from "react";
|
|
173
|
+
import { createPortal } from "react-dom";
|
|
174
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
175
|
+
var maxWidthClasses = {
|
|
176
|
+
sm: "max-w-sm",
|
|
177
|
+
md: "max-w-md",
|
|
178
|
+
lg: "max-w-lg",
|
|
179
|
+
xl: "max-w-xl",
|
|
180
|
+
"2xl": "max-w-2xl"
|
|
181
|
+
};
|
|
182
|
+
function Modal({
|
|
183
|
+
isOpen,
|
|
184
|
+
onClose,
|
|
185
|
+
children,
|
|
186
|
+
title,
|
|
187
|
+
style,
|
|
188
|
+
maxWidth = "md",
|
|
189
|
+
persistent = false
|
|
190
|
+
}) {
|
|
191
|
+
const t = useTheme();
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
if (persistent) return;
|
|
194
|
+
const handleEscapeKey = (event) => {
|
|
195
|
+
if (event.key === "Escape" && isOpen) onClose();
|
|
196
|
+
};
|
|
197
|
+
document.addEventListener("keydown", handleEscapeKey);
|
|
198
|
+
if (isOpen) {
|
|
199
|
+
document.body.style.overflow = "hidden";
|
|
200
|
+
} else {
|
|
201
|
+
document.body.style.overflow = "";
|
|
202
|
+
}
|
|
203
|
+
return () => {
|
|
204
|
+
document.removeEventListener("keydown", handleEscapeKey);
|
|
205
|
+
document.body.style.overflow = "";
|
|
206
|
+
};
|
|
207
|
+
}, [isOpen, onClose, persistent]);
|
|
208
|
+
const handleBackdropClick = (e) => {
|
|
209
|
+
if (e.target === e.currentTarget) onClose();
|
|
210
|
+
};
|
|
211
|
+
if (!isOpen) return null;
|
|
212
|
+
if (typeof document === "undefined") return null;
|
|
213
|
+
return createPortal(
|
|
214
|
+
/* @__PURE__ */ jsx3(
|
|
215
|
+
"div",
|
|
216
|
+
{
|
|
217
|
+
className: `fixed inset-0 z-[55] flex items-center justify-center overflow-hidden ${t.overlay}`,
|
|
218
|
+
onClick: persistent ? void 0 : handleBackdropClick,
|
|
219
|
+
style: { width: "100vw", height: "100vh", top: 0, left: 0, margin: 0, paddingTop: "env(safe-area-inset-top)" },
|
|
220
|
+
children: /* @__PURE__ */ jsxs2(
|
|
221
|
+
"div",
|
|
222
|
+
{
|
|
223
|
+
className: `relative max-h-[90vh] w-full ${maxWidthClasses[maxWidth]} overflow-auto rounded-xl ${t.card} p-6 shadow-xl m-4`,
|
|
224
|
+
onClick: (e) => e.stopPropagation(),
|
|
225
|
+
style: { margin: "1rem", ...style || {} },
|
|
226
|
+
children: [
|
|
227
|
+
!persistent && /* @__PURE__ */ jsx3(
|
|
228
|
+
"button",
|
|
229
|
+
{
|
|
230
|
+
type: "button",
|
|
231
|
+
className: `absolute top-4 right-4 z-10 inline-flex h-8 w-8 items-center justify-center rounded-full ${t.textMuted} ${t.hover} transition-colors`,
|
|
232
|
+
onClick: onClose,
|
|
233
|
+
"aria-label": "Close modal",
|
|
234
|
+
children: /* @__PURE__ */ jsx3("svg", { className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
|
|
235
|
+
}
|
|
236
|
+
),
|
|
237
|
+
title && /* @__PURE__ */ jsx3("div", { className: "mb-4 pr-8", children: /* @__PURE__ */ jsx3("h3", { className: `text-xl font-bold ${t.textPrimary}`, children: title }) }),
|
|
238
|
+
/* @__PURE__ */ jsx3("div", { children })
|
|
239
|
+
]
|
|
240
|
+
}
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
),
|
|
244
|
+
document.body
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// src/components/ConfirmationModal.tsx
|
|
249
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
250
|
+
var iconPaths = {
|
|
251
|
+
danger: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z",
|
|
252
|
+
warning: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z",
|
|
253
|
+
info: "M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
254
|
+
};
|
|
255
|
+
function ConfirmationModal({
|
|
256
|
+
isOpen,
|
|
257
|
+
onClose,
|
|
258
|
+
onConfirm,
|
|
259
|
+
title,
|
|
260
|
+
message,
|
|
261
|
+
confirmText = "Confirm",
|
|
262
|
+
cancelText = "Cancel",
|
|
263
|
+
variant = "danger",
|
|
264
|
+
isLoading = false
|
|
265
|
+
}) {
|
|
266
|
+
const t = useTheme();
|
|
267
|
+
const variantStyles = {
|
|
268
|
+
danger: { icon: "bg-brand-danger", button: t.buttonDanger },
|
|
269
|
+
warning: { icon: "bg-brand-warning", button: t.buttonWarning },
|
|
270
|
+
info: { icon: "bg-brand-info", button: t.buttonSecondary }
|
|
271
|
+
};
|
|
272
|
+
const styles = variantStyles[variant];
|
|
273
|
+
return /* @__PURE__ */ jsx4(Modal, { isOpen, onClose: isLoading ? () => {
|
|
274
|
+
} : onClose, children: /* @__PURE__ */ jsxs3("div", { className: "w-full", children: [
|
|
275
|
+
/* @__PURE__ */ jsx4("div", { className: "mb-6 flex justify-center", children: /* @__PURE__ */ jsx4("div", { className: `flex h-16 w-16 items-center justify-center rounded-full ${styles.icon}`, children: /* @__PURE__ */ jsx4("svg", { className: "h-8 w-8 text-white", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx4("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: iconPaths[variant] }) }) }) }),
|
|
276
|
+
/* @__PURE__ */ jsx4("h3", { className: `mb-4 text-xl font-bold text-center ${t.textPrimary}`, children: title }),
|
|
277
|
+
/* @__PURE__ */ jsx4("div", { className: `mb-6 text-center whitespace-pre-line ${t.textSecondary}`, children: message }),
|
|
278
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex gap-3", children: [
|
|
279
|
+
/* @__PURE__ */ jsx4(
|
|
280
|
+
"button",
|
|
281
|
+
{
|
|
282
|
+
onClick: onClose,
|
|
283
|
+
disabled: isLoading,
|
|
284
|
+
className: `flex-1 px-4 py-2 rounded-lg transition-colors ${t.buttonGhost} ${t.border} disabled:opacity-50 disabled:cursor-not-allowed`,
|
|
285
|
+
children: cancelText
|
|
286
|
+
}
|
|
287
|
+
),
|
|
288
|
+
/* @__PURE__ */ jsx4(
|
|
289
|
+
"button",
|
|
290
|
+
{
|
|
291
|
+
onClick: onConfirm,
|
|
292
|
+
disabled: isLoading,
|
|
293
|
+
className: `flex-1 px-4 py-2 rounded-lg transition-colors ${styles.button} disabled:opacity-50 disabled:cursor-not-allowed`,
|
|
294
|
+
children: isLoading ? "Processing..." : confirmText
|
|
295
|
+
}
|
|
296
|
+
)
|
|
297
|
+
] })
|
|
298
|
+
] }) });
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// src/components/MenuItem.tsx
|
|
302
|
+
import { Loader2 } from "lucide-react";
|
|
303
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
304
|
+
function MenuItem({ icon: Icon, label, onClick, disabled, danger, loading, className, suffix }) {
|
|
305
|
+
const t = useTheme();
|
|
306
|
+
const colorClasses = danger ? t.menuItemDanger : t.menuItem;
|
|
307
|
+
return /* @__PURE__ */ jsxs4(
|
|
308
|
+
"button",
|
|
309
|
+
{
|
|
310
|
+
onClick,
|
|
311
|
+
disabled: disabled || loading,
|
|
312
|
+
className: `w-full flex items-center gap-2 px-3 py-2 text-sm rounded-lg transition-colors ${colorClasses} disabled:opacity-50${className ? ` ${className}` : ""}`,
|
|
313
|
+
children: [
|
|
314
|
+
loading ? /* @__PURE__ */ jsx5(Loader2, { className: "w-3.5 h-3.5 animate-spin" }) : /* @__PURE__ */ jsx5(Icon, { className: "w-3.5 h-3.5" }),
|
|
315
|
+
label,
|
|
316
|
+
suffix && /* @__PURE__ */ jsx5("span", { className: "ml-auto", children: suffix })
|
|
317
|
+
]
|
|
318
|
+
}
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// src/components/Paginator.tsx
|
|
323
|
+
import { useState, useRef, useEffect as useEffect2, useCallback } from "react";
|
|
324
|
+
import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from "lucide-react";
|
|
325
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
326
|
+
function Paginator({
|
|
327
|
+
currentPage,
|
|
328
|
+
totalPages,
|
|
329
|
+
onPageChange,
|
|
330
|
+
siblings = 2
|
|
331
|
+
}) {
|
|
332
|
+
const t = useTheme();
|
|
333
|
+
const [editValue, setEditValue] = useState(String(currentPage));
|
|
334
|
+
const [editing, setEditing] = useState(false);
|
|
335
|
+
const inputRef = useRef(null);
|
|
336
|
+
useEffect2(() => {
|
|
337
|
+
if (!editing) setEditValue(String(currentPage));
|
|
338
|
+
}, [currentPage, editing]);
|
|
339
|
+
const clamp = useCallback((page) => Math.max(1, Math.min(totalPages, page)), [totalPages]);
|
|
340
|
+
const commitEdit = useCallback(() => {
|
|
341
|
+
setEditing(false);
|
|
342
|
+
const parsed = parseInt(editValue, 10);
|
|
343
|
+
if (!isNaN(parsed)) {
|
|
344
|
+
const clamped = clamp(parsed);
|
|
345
|
+
onPageChange(clamped);
|
|
346
|
+
setEditValue(String(clamped));
|
|
347
|
+
} else {
|
|
348
|
+
setEditValue(String(currentPage));
|
|
349
|
+
}
|
|
350
|
+
}, [editValue, clamp, onPageChange, currentPage]);
|
|
351
|
+
const handleKeyDown = useCallback((e) => {
|
|
352
|
+
if (e.key === "Enter") {
|
|
353
|
+
commitEdit();
|
|
354
|
+
inputRef.current?.blur();
|
|
355
|
+
} else if (e.key === "Escape") {
|
|
356
|
+
setEditing(false);
|
|
357
|
+
setEditValue(String(currentPage));
|
|
358
|
+
inputRef.current?.blur();
|
|
359
|
+
}
|
|
360
|
+
}, [commitEdit, currentPage]);
|
|
361
|
+
const pages = [];
|
|
362
|
+
const start = Math.max(1, currentPage - siblings);
|
|
363
|
+
const end = Math.min(totalPages, currentPage + siblings);
|
|
364
|
+
for (let i = start; i <= end; i++) {
|
|
365
|
+
if (i !== currentPage) pages.push(i);
|
|
366
|
+
}
|
|
367
|
+
const before = pages.filter((p) => p < currentPage);
|
|
368
|
+
const after = pages.filter((p) => p > currentPage);
|
|
369
|
+
const navBtn = `p-1 ${t.textMuted} ${t.hover} disabled:opacity-30 disabled:cursor-not-allowed transition-colors rounded`;
|
|
370
|
+
const pageBtn = `px-1.5 py-0.5 text-xs rounded transition-colors ${t.textMuted} ${t.hover}`;
|
|
371
|
+
if (totalPages <= 1) return null;
|
|
372
|
+
return /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-center gap-0.5", children: [
|
|
373
|
+
/* @__PURE__ */ jsx6("button", { onClick: () => onPageChange(1), disabled: currentPage <= 1, className: navBtn, "aria-label": "First page", children: /* @__PURE__ */ jsx6(ChevronsLeft, { className: "w-4 h-4" }) }),
|
|
374
|
+
/* @__PURE__ */ jsx6("button", { onClick: () => onPageChange(clamp(currentPage - 1)), disabled: currentPage <= 1, className: navBtn, "aria-label": "Previous page", children: /* @__PURE__ */ jsx6(ChevronLeft, { className: "w-4 h-4" }) }),
|
|
375
|
+
before.map((p) => /* @__PURE__ */ jsx6("button", { onClick: () => onPageChange(p), className: pageBtn, children: p }, p)),
|
|
376
|
+
/* @__PURE__ */ jsx6(
|
|
377
|
+
"input",
|
|
378
|
+
{
|
|
379
|
+
ref: inputRef,
|
|
380
|
+
type: "text",
|
|
381
|
+
inputMode: "numeric",
|
|
382
|
+
value: editing ? editValue : String(currentPage),
|
|
383
|
+
onChange: (e) => setEditValue(e.target.value),
|
|
384
|
+
onFocus: () => {
|
|
385
|
+
setEditing(true);
|
|
386
|
+
setTimeout(() => inputRef.current?.select(), 0);
|
|
387
|
+
},
|
|
388
|
+
onBlur: commitEdit,
|
|
389
|
+
onKeyDown: handleKeyDown,
|
|
390
|
+
className: `w-8 text-center text-xs font-medium rounded-md py-0.5 outline-none ${t.tabActive} ${t.inputFocus}`,
|
|
391
|
+
"aria-label": "Current page"
|
|
392
|
+
}
|
|
393
|
+
),
|
|
394
|
+
after.map((p) => /* @__PURE__ */ jsx6("button", { onClick: () => onPageChange(p), className: pageBtn, children: p }, p)),
|
|
395
|
+
/* @__PURE__ */ jsx6("button", { onClick: () => onPageChange(clamp(currentPage + 1)), disabled: currentPage >= totalPages, className: navBtn, "aria-label": "Next page", children: /* @__PURE__ */ jsx6(ChevronRight, { className: "w-4 h-4" }) }),
|
|
396
|
+
/* @__PURE__ */ jsx6("button", { onClick: () => onPageChange(totalPages), disabled: currentPage >= totalPages, className: navBtn, "aria-label": "Last page", children: /* @__PURE__ */ jsx6(ChevronsRight, { className: "w-4 h-4" }) })
|
|
397
|
+
] });
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// src/components/Popover.tsx
|
|
401
|
+
import { useEffect as useEffect3, useRef as useRef2, useState as useState2 } from "react";
|
|
402
|
+
import { createPortal as createPortal2 } from "react-dom";
|
|
403
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
404
|
+
function Popover({ open, anchorRef, onClose, children, className, anchor = "below" }) {
|
|
405
|
+
const t = useTheme();
|
|
406
|
+
const popoverRef = useRef2(null);
|
|
407
|
+
const [adjustedStyle, setAdjustedStyle] = useState2({});
|
|
408
|
+
useEffect3(() => {
|
|
409
|
+
if (!open) return;
|
|
410
|
+
function handlePointerDown(e) {
|
|
411
|
+
const target = e.target;
|
|
412
|
+
if (popoverRef.current && !popoverRef.current.contains(target) && anchorRef.current && !anchorRef.current.contains(target)) {
|
|
413
|
+
onClose();
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
function handleKeyDown(e) {
|
|
417
|
+
if (e.key === "Escape") onClose();
|
|
418
|
+
}
|
|
419
|
+
document.addEventListener("pointerdown", handlePointerDown);
|
|
420
|
+
document.addEventListener("touchstart", handlePointerDown);
|
|
421
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
422
|
+
return () => {
|
|
423
|
+
document.removeEventListener("pointerdown", handlePointerDown);
|
|
424
|
+
document.removeEventListener("touchstart", handlePointerDown);
|
|
425
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
426
|
+
};
|
|
427
|
+
}, [open, onClose, anchorRef]);
|
|
428
|
+
useEffect3(() => {
|
|
429
|
+
if (!open || !popoverRef.current || !anchorRef.current) return;
|
|
430
|
+
const anchorRect = anchorRef.current.getBoundingClientRect();
|
|
431
|
+
const popoverRect = popoverRef.current.getBoundingClientRect();
|
|
432
|
+
const padding = 16;
|
|
433
|
+
const style2 = { position: "absolute", zIndex: 50 };
|
|
434
|
+
let left = anchorRect.left + window.scrollX;
|
|
435
|
+
if (left + popoverRect.width > window.innerWidth - padding) {
|
|
436
|
+
left = Math.max(padding, window.innerWidth - popoverRect.width - padding);
|
|
437
|
+
}
|
|
438
|
+
if (left < padding) left = padding;
|
|
439
|
+
style2.left = left;
|
|
440
|
+
const spaceBelow = window.innerHeight - anchorRect.bottom;
|
|
441
|
+
const spaceAbove = anchorRect.top;
|
|
442
|
+
if (anchor === "below") {
|
|
443
|
+
if (spaceBelow >= popoverRect.height + padding || spaceBelow >= spaceAbove) {
|
|
444
|
+
style2.top = anchorRect.bottom + window.scrollY;
|
|
445
|
+
} else {
|
|
446
|
+
style2.bottom = document.documentElement.scrollHeight - anchorRect.top - window.scrollY;
|
|
447
|
+
}
|
|
448
|
+
} else {
|
|
449
|
+
if (spaceAbove >= popoverRect.height + padding || spaceAbove >= spaceBelow) {
|
|
450
|
+
style2.bottom = document.documentElement.scrollHeight - anchorRect.top - window.scrollY;
|
|
451
|
+
} else {
|
|
452
|
+
style2.top = anchorRect.bottom + window.scrollY;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
setAdjustedStyle(style2);
|
|
456
|
+
}, [open, anchor, anchorRef]);
|
|
457
|
+
if (!open || !anchorRef.current) return null;
|
|
458
|
+
const rect = anchorRef.current.getBoundingClientRect();
|
|
459
|
+
const initialStyle = {
|
|
460
|
+
position: "absolute",
|
|
461
|
+
left: rect.left + window.scrollX,
|
|
462
|
+
zIndex: 50,
|
|
463
|
+
visibility: adjustedStyle.left != null ? "visible" : "hidden",
|
|
464
|
+
...anchor === "above" ? { bottom: document.documentElement.scrollHeight - rect.top - window.scrollY } : { top: rect.bottom + window.scrollY }
|
|
465
|
+
};
|
|
466
|
+
const style = adjustedStyle.left != null ? adjustedStyle : initialStyle;
|
|
467
|
+
const defaultClasses = `${t.elevated} ${t.border} shadow-lg rounded-lg`;
|
|
468
|
+
return createPortal2(
|
|
469
|
+
/* @__PURE__ */ jsx7("div", { ref: popoverRef, style, className: className ?? defaultClasses, children }),
|
|
470
|
+
document.body
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// src/components/Slider.tsx
|
|
475
|
+
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
476
|
+
function fillStyle(value, min, max) {
|
|
477
|
+
const pct = max === min ? 0 : (value - min) / (max - min) * 100;
|
|
478
|
+
return {
|
|
479
|
+
background: `linear-gradient(90deg, var(--color-brand-primary) 0%, var(--color-brand-secondary) ${pct}%, var(--color-bg-elevated) ${pct}%)`
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
function Slider(props) {
|
|
483
|
+
const t = useTheme();
|
|
484
|
+
const { disabled, className } = props;
|
|
485
|
+
if (props.options) {
|
|
486
|
+
const { options, labels, value: value2, onChange: onChange2 } = props;
|
|
487
|
+
const index = options.indexOf(value2);
|
|
488
|
+
const currentIndex = index === -1 ? 0 : index;
|
|
489
|
+
const getLabel = (option) => {
|
|
490
|
+
if (typeof labels === "function") return labels(option);
|
|
491
|
+
return String(option);
|
|
492
|
+
};
|
|
493
|
+
return /* @__PURE__ */ jsxs6("div", { className, children: [
|
|
494
|
+
/* @__PURE__ */ jsx8(
|
|
495
|
+
"input",
|
|
496
|
+
{
|
|
497
|
+
type: "range",
|
|
498
|
+
min: 0,
|
|
499
|
+
max: options.length - 1,
|
|
500
|
+
step: 1,
|
|
501
|
+
value: currentIndex,
|
|
502
|
+
onChange: (e) => onChange2(options[parseInt(e.target.value)]),
|
|
503
|
+
className: "w-full slider-styled",
|
|
504
|
+
style: fillStyle(currentIndex, 0, options.length - 1),
|
|
505
|
+
disabled
|
|
506
|
+
}
|
|
507
|
+
),
|
|
508
|
+
labels && /* @__PURE__ */ jsx8("div", { className: `flex justify-between text-xs mt-1 ${t.textMuted}`, children: options.map((option) => /* @__PURE__ */ jsx8("span", { children: getLabel(option) }, String(option))) })
|
|
509
|
+
] });
|
|
510
|
+
}
|
|
511
|
+
const { min, max, step, value, onChange } = props;
|
|
512
|
+
return /* @__PURE__ */ jsx8("div", { className, children: /* @__PURE__ */ jsx8(
|
|
513
|
+
"input",
|
|
514
|
+
{
|
|
515
|
+
type: "range",
|
|
516
|
+
min,
|
|
517
|
+
max,
|
|
518
|
+
step,
|
|
519
|
+
value,
|
|
520
|
+
onChange: (e) => onChange(parseFloat(e.target.value)),
|
|
521
|
+
className: "w-full slider-styled",
|
|
522
|
+
style: fillStyle(value, min, max),
|
|
523
|
+
disabled
|
|
524
|
+
}
|
|
525
|
+
) });
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// src/components/SlideOverPanel.tsx
|
|
529
|
+
import { useEffect as useEffect4, useState as useState3 } from "react";
|
|
530
|
+
import { Fragment, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
531
|
+
function SlideOverPanel({ open, onClose, children }) {
|
|
532
|
+
const t = useTheme();
|
|
533
|
+
const [mounted, setMounted] = useState3(false);
|
|
534
|
+
const [visible, setVisible] = useState3(false);
|
|
535
|
+
useEffect4(() => {
|
|
536
|
+
if (open) {
|
|
537
|
+
setMounted(true);
|
|
538
|
+
requestAnimationFrame(() => {
|
|
539
|
+
requestAnimationFrame(() => setVisible(true));
|
|
540
|
+
});
|
|
541
|
+
} else {
|
|
542
|
+
setVisible(false);
|
|
543
|
+
const timer = setTimeout(() => setMounted(false), 200);
|
|
544
|
+
return () => clearTimeout(timer);
|
|
545
|
+
}
|
|
546
|
+
}, [open]);
|
|
547
|
+
if (!mounted) return null;
|
|
548
|
+
return /* @__PURE__ */ jsxs7(Fragment, { children: [
|
|
549
|
+
/* @__PURE__ */ jsx9(
|
|
550
|
+
"div",
|
|
551
|
+
{
|
|
552
|
+
className: `fixed inset-0 z-20 transition-opacity duration-200 ${t.overlay} ${visible ? "opacity-100" : "opacity-0"}`,
|
|
553
|
+
onClick: onClose
|
|
554
|
+
}
|
|
555
|
+
),
|
|
556
|
+
/* @__PURE__ */ jsx9(
|
|
557
|
+
"div",
|
|
558
|
+
{
|
|
559
|
+
className: `fixed top-0 right-0 bottom-0 w-72 ${t.sidebar} overflow-y-auto z-30 transition-transform duration-200 ${visible ? "translate-x-0" : "translate-x-full"}`,
|
|
560
|
+
style: { paddingTop: "env(safe-area-inset-top)" },
|
|
561
|
+
children
|
|
562
|
+
}
|
|
563
|
+
)
|
|
564
|
+
] });
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// src/components/ToggleSwitch.tsx
|
|
568
|
+
import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
569
|
+
var sizeConfig = {
|
|
570
|
+
sm: { track: "w-9 h-5", knob: "w-4 h-4", knobTranslate: "translate-x-4" },
|
|
571
|
+
md: { track: "w-11 h-6", knob: "w-5 h-5", knobTranslate: "translate-x-5" },
|
|
572
|
+
lg: { track: "w-14 h-7", knob: "w-6 h-6", knobTranslate: "translate-x-7" }
|
|
573
|
+
};
|
|
574
|
+
function ToggleSwitch({
|
|
575
|
+
checked,
|
|
576
|
+
onChange,
|
|
577
|
+
size = "md",
|
|
578
|
+
label,
|
|
579
|
+
description,
|
|
580
|
+
disabled = false,
|
|
581
|
+
id,
|
|
582
|
+
...props
|
|
583
|
+
}) {
|
|
584
|
+
const t = useTheme();
|
|
585
|
+
const config = sizeConfig[size];
|
|
586
|
+
const handleClick = () => {
|
|
587
|
+
if (!disabled) onChange(!checked);
|
|
588
|
+
};
|
|
589
|
+
const handleKeyDown = (e) => {
|
|
590
|
+
if (e.key === " " || e.key === "Enter") {
|
|
591
|
+
e.preventDefault();
|
|
592
|
+
if (!disabled) onChange(!checked);
|
|
593
|
+
}
|
|
594
|
+
};
|
|
595
|
+
const toggle = /* @__PURE__ */ jsx10(
|
|
596
|
+
"button",
|
|
597
|
+
{
|
|
598
|
+
type: "button",
|
|
599
|
+
role: "switch",
|
|
600
|
+
"aria-checked": checked,
|
|
601
|
+
"aria-label": label,
|
|
602
|
+
id,
|
|
603
|
+
disabled,
|
|
604
|
+
onClick: handleClick,
|
|
605
|
+
onKeyDown: handleKeyDown,
|
|
606
|
+
className: [
|
|
607
|
+
"relative inline-flex items-center rounded-full transition-all duration-200 flex-shrink-0",
|
|
608
|
+
"focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-primary focus-visible:ring-offset-2",
|
|
609
|
+
config.track,
|
|
610
|
+
checked ? t.toggleOn : t.toggleOff,
|
|
611
|
+
disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"
|
|
612
|
+
].join(" "),
|
|
613
|
+
...props,
|
|
614
|
+
children: /* @__PURE__ */ jsx10(
|
|
615
|
+
"span",
|
|
616
|
+
{
|
|
617
|
+
className: [
|
|
618
|
+
"inline-flex items-center justify-center rounded-full bg-white shadow-sm transition-transform duration-200",
|
|
619
|
+
config.knob,
|
|
620
|
+
checked ? config.knobTranslate : "translate-x-0.5"
|
|
621
|
+
].join(" ")
|
|
622
|
+
}
|
|
623
|
+
)
|
|
624
|
+
}
|
|
625
|
+
);
|
|
626
|
+
if (!label) return toggle;
|
|
627
|
+
return /* @__PURE__ */ jsxs8("div", { className: ["flex items-center justify-between gap-3", disabled ? "opacity-50" : ""].join(" "), children: [
|
|
628
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex flex-col min-w-0", children: [
|
|
629
|
+
/* @__PURE__ */ jsx10(
|
|
630
|
+
"label",
|
|
631
|
+
{
|
|
632
|
+
htmlFor: id,
|
|
633
|
+
className: [
|
|
634
|
+
"text-sm font-medium",
|
|
635
|
+
t.textPrimary,
|
|
636
|
+
disabled ? "cursor-not-allowed" : "cursor-pointer"
|
|
637
|
+
].join(" "),
|
|
638
|
+
onClick: handleClick,
|
|
639
|
+
children: label
|
|
640
|
+
}
|
|
641
|
+
),
|
|
642
|
+
description && /* @__PURE__ */ jsx10("span", { className: `text-xs mt-0.5 ${t.textMuted}`, children: description })
|
|
643
|
+
] }),
|
|
644
|
+
toggle
|
|
645
|
+
] });
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// src/hooks/useDebounce.ts
|
|
649
|
+
import { useState as useState4, useEffect as useEffect5 } from "react";
|
|
650
|
+
function useDebounce(value, delay) {
|
|
651
|
+
const [debouncedValue, setDebouncedValue] = useState4(value);
|
|
652
|
+
useEffect5(() => {
|
|
653
|
+
const handler = setTimeout(() => {
|
|
654
|
+
setDebouncedValue(value);
|
|
655
|
+
}, delay);
|
|
656
|
+
return () => {
|
|
657
|
+
clearTimeout(handler);
|
|
658
|
+
};
|
|
659
|
+
}, [value, delay]);
|
|
660
|
+
return debouncedValue;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// src/utils/format-time.ts
|
|
664
|
+
function formatExactTime(dateString) {
|
|
665
|
+
const date = new Date(dateString);
|
|
666
|
+
const time = date.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit", hour12: true });
|
|
667
|
+
const weekday = date.toLocaleDateString("en-US", { weekday: "short" });
|
|
668
|
+
const month = date.getMonth() + 1;
|
|
669
|
+
const day = date.getDate();
|
|
670
|
+
const year = date.getFullYear().toString().slice(-2);
|
|
671
|
+
return `${time} ${weekday} ${month}/${day}/${year}`;
|
|
672
|
+
}
|
|
673
|
+
function getRelativeTime(dateString) {
|
|
674
|
+
const date = new Date(dateString);
|
|
675
|
+
const now = /* @__PURE__ */ new Date();
|
|
676
|
+
const diffMs = now.getTime() - date.getTime();
|
|
677
|
+
const diffMins = Math.floor(diffMs / 6e4);
|
|
678
|
+
const diffHours = Math.floor(diffMs / 36e5);
|
|
679
|
+
const diffDays = Math.floor(diffMs / 864e5);
|
|
680
|
+
if (diffMins < 1) return "Just now";
|
|
681
|
+
if (diffMins < 60) return `${diffMins}m ago`;
|
|
682
|
+
if (diffHours < 24) return `${diffHours}h ago`;
|
|
683
|
+
if (diffDays < 7) return `${diffDays}d ago`;
|
|
684
|
+
if (diffDays < 30) {
|
|
685
|
+
const weeks = Math.floor(diffDays / 7);
|
|
686
|
+
return `${weeks}w ago`;
|
|
687
|
+
}
|
|
688
|
+
return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// src/utils/clipboard.ts
|
|
692
|
+
async function shareOrCopyUrl(url) {
|
|
693
|
+
try {
|
|
694
|
+
if (navigator.share) {
|
|
695
|
+
await navigator.share({ url });
|
|
696
|
+
return "shared";
|
|
697
|
+
}
|
|
698
|
+
} catch (err) {
|
|
699
|
+
if (err?.name === "AbortError") return "cancelled";
|
|
700
|
+
}
|
|
701
|
+
const copied = await copyToClipboard(url);
|
|
702
|
+
return copied ? "copied" : "failed";
|
|
703
|
+
}
|
|
704
|
+
async function copyToClipboard(text) {
|
|
705
|
+
try {
|
|
706
|
+
if (navigator.clipboard?.writeText) {
|
|
707
|
+
await navigator.clipboard.writeText(text);
|
|
708
|
+
return true;
|
|
709
|
+
}
|
|
710
|
+
} catch {
|
|
711
|
+
}
|
|
712
|
+
const textarea = document.createElement("textarea");
|
|
713
|
+
textarea.value = text;
|
|
714
|
+
textarea.style.position = "fixed";
|
|
715
|
+
textarea.style.opacity = "0";
|
|
716
|
+
document.body.appendChild(textarea);
|
|
717
|
+
textarea.select();
|
|
718
|
+
try {
|
|
719
|
+
document.execCommand("copy");
|
|
720
|
+
return true;
|
|
721
|
+
} catch {
|
|
722
|
+
return false;
|
|
723
|
+
} finally {
|
|
724
|
+
document.body.removeChild(textarea);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
export {
|
|
728
|
+
Button,
|
|
729
|
+
ConfirmationModal,
|
|
730
|
+
MenuItem,
|
|
731
|
+
Modal,
|
|
732
|
+
Paginator,
|
|
733
|
+
Popover,
|
|
734
|
+
SlideOverPanel,
|
|
735
|
+
Slider,
|
|
736
|
+
ThemingProvider,
|
|
737
|
+
ToggleSwitch,
|
|
738
|
+
copyToClipboard,
|
|
739
|
+
formatExactTime,
|
|
740
|
+
getRelativeTime,
|
|
741
|
+
shareOrCopyUrl,
|
|
742
|
+
themes,
|
|
743
|
+
useDebounce,
|
|
744
|
+
useTheme
|
|
745
|
+
};
|
|
746
|
+
//# sourceMappingURL=index.js.map
|