@neasg/design-system 0.4.11 → 0.4.13
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 +147 -5
- package/dist/avatar-profile-popover.d.ts +34 -0
- package/dist/avatar-profile-popover.js +27 -0
- package/dist/badge.d.ts +1 -1
- package/dist/button.d.ts +1 -1
- package/dist/card.d.ts +2 -0
- package/dist/card.js +72 -3
- package/dist/command-search.d.ts +47 -2
- package/dist/command-search.js +173 -24
- package/dist/command.d.ts +2 -0
- package/dist/command.js +2 -2
- package/dist/dialog-primitive.js +1 -1
- package/dist/draggable-tabs.d.ts +3 -0
- package/dist/draggable-tabs.js +4 -0
- package/dist/editable-table.js +2 -2
- package/dist/guidance-tip.d.ts +26 -0
- package/dist/guidance-tip.js +162 -0
- package/dist/index.d.ts +15 -6
- package/dist/index.js +6 -2
- package/dist/jump-target-highlight.d.ts +13 -0
- package/dist/jump-target-highlight.js +95 -0
- package/dist/layout.d.ts +27 -5
- package/dist/layout.js +128 -34
- package/dist/message-item.js +9 -10
- package/dist/notification.js +1 -1
- package/dist/page-layout.d.ts +22 -0
- package/dist/page-layout.js +38 -0
- package/dist/page-section.js +1 -1
- package/dist/popover-menu.js +2 -2
- package/dist/routing-timeline.js +1 -1
- package/dist/styles.css +16 -0
- package/dist/table-column-visibility.d.ts +2 -1
- package/dist/table-column-visibility.js +9 -8
- package/dist/table-toolbar.d.ts +2 -1
- package/dist/table-toolbar.js +7 -2
- package/dist/table.d.ts +31 -2
- package/dist/table.js +298 -23
- package/dist/tabs.d.ts +2 -1
- package/dist/tabs.js +8 -5
- package/dist/theme-switcher.d.ts +1 -1
- package/dist/theme-switcher.js +4 -6
- package/dist/theme.d.ts +336 -77
- package/dist/theme.js +269 -55
- package/package.json +1 -1
package/dist/command-search.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
+
import { ArrowRight, ArrowUpRight, SlidersHorizontal } from "lucide-react";
|
|
5
|
+
import { Badge } from "./badge";
|
|
4
6
|
import { Button } from "./button";
|
|
5
7
|
import { Command, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, } from "./command";
|
|
6
8
|
import { EmptyState } from "./empty-state";
|
|
7
9
|
import { SearchIcon } from "./animated-icons/search";
|
|
10
|
+
import { XIcon } from "./animated-icons/x";
|
|
11
|
+
import { CountBadge } from "./count-badge";
|
|
8
12
|
import { DialogRoot, DialogSurface, DialogTitle } from "./dialog-primitive";
|
|
9
13
|
import { FilterToggle } from "./filter-toggle";
|
|
10
14
|
import { cn } from "./lib/utils";
|
|
@@ -15,15 +19,128 @@ import { SearchHighlight } from "./use-search-highlight";
|
|
|
15
19
|
function CommandSearchEmptyIcon({ className }) {
|
|
16
20
|
return _jsx(SearchIcon, { className: className, size: 20 });
|
|
17
21
|
}
|
|
22
|
+
const commandSearchItemToneClasses = {
|
|
23
|
+
accent: "border-status-accent-foreground/20 bg-status-accent text-status-accent-foreground",
|
|
24
|
+
destructive: "border-status-destructive-foreground/20 bg-status-destructive text-status-destructive-foreground",
|
|
25
|
+
info: "border-status-info-foreground/20 bg-status-info text-status-info-foreground",
|
|
26
|
+
muted: "border-border bg-muted text-muted-foreground",
|
|
27
|
+
success: "border-status-success-foreground/20 bg-status-success text-status-success-foreground",
|
|
28
|
+
warning: "border-status-warning-foreground/20 bg-status-warning text-status-warning-foreground",
|
|
29
|
+
};
|
|
18
30
|
function getCommandSearchItemValue(item) {
|
|
19
|
-
var _a, _b;
|
|
20
|
-
const
|
|
31
|
+
var _a, _b, _c, _d;
|
|
32
|
+
const detailValues = (_b = (_a = item.details) === null || _a === void 0 ? void 0 : _a.flatMap((detail) => [detail.label, detail.value])) !== null && _b !== void 0 ? _b : [];
|
|
33
|
+
const fallbackValue = [
|
|
34
|
+
item.title,
|
|
35
|
+
item.subtitle,
|
|
36
|
+
item.description,
|
|
37
|
+
...detailValues,
|
|
38
|
+
...((_c = item.keywords) !== null && _c !== void 0 ? _c : []),
|
|
39
|
+
]
|
|
21
40
|
.filter(Boolean)
|
|
22
41
|
.join(" ");
|
|
23
|
-
return (
|
|
42
|
+
return (_d = item.value) !== null && _d !== void 0 ? _d : fallbackValue;
|
|
43
|
+
}
|
|
44
|
+
function getCommandSearchSectionActionValue(section) {
|
|
45
|
+
if (!section.action) {
|
|
46
|
+
return section.heading;
|
|
47
|
+
}
|
|
48
|
+
if (section.action.value) {
|
|
49
|
+
return section.action.value;
|
|
50
|
+
}
|
|
51
|
+
const label = typeof section.action.label === "string"
|
|
52
|
+
? section.action.label
|
|
53
|
+
: "Show all results";
|
|
54
|
+
return `${section.heading} ${label}`;
|
|
55
|
+
}
|
|
56
|
+
function handleCommandSearchItemKeyDown(event, onSelect) {
|
|
57
|
+
if (event.key !== "Enter" && event.key !== " ") {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
event.preventDefault();
|
|
61
|
+
event.stopPropagation();
|
|
62
|
+
onSelect();
|
|
63
|
+
}
|
|
64
|
+
function isCommandSearchTitleDetail(detail) {
|
|
65
|
+
return detail.showLabel === false && Boolean(detail.variant);
|
|
66
|
+
}
|
|
67
|
+
function getNormalizedCommandSearchDetailLabel(detail) {
|
|
68
|
+
return detail.label.toLowerCase().replace(/\.$/, "");
|
|
69
|
+
}
|
|
70
|
+
function isCommandSearchCompanyDetail(detail) {
|
|
71
|
+
const label = getNormalizedCommandSearchDetailLabel(detail);
|
|
72
|
+
return (label === "company name" ||
|
|
73
|
+
label === "uen" ||
|
|
74
|
+
label === "postal code");
|
|
75
|
+
}
|
|
76
|
+
const commandSearchDetailGroupPriority = {
|
|
77
|
+
identity: 0,
|
|
78
|
+
summary: 1,
|
|
79
|
+
};
|
|
80
|
+
function getCommandSearchDetailGroup(detail) {
|
|
81
|
+
if (detail.group)
|
|
82
|
+
return detail.group;
|
|
83
|
+
const label = getNormalizedCommandSearchDetailLabel(detail);
|
|
84
|
+
if (label === "company name" ||
|
|
85
|
+
label === "case number" ||
|
|
86
|
+
label === "licence no" ||
|
|
87
|
+
label === "inspection no" ||
|
|
88
|
+
label === "uen" ||
|
|
89
|
+
label === "postal code") {
|
|
90
|
+
return "identity";
|
|
91
|
+
}
|
|
92
|
+
if (label === "active licences" || label === "in progress cases") {
|
|
93
|
+
return "summary";
|
|
94
|
+
}
|
|
95
|
+
return "metadata";
|
|
96
|
+
}
|
|
97
|
+
function getGroupedCommandSearchDetails(details) {
|
|
98
|
+
const groups = new Map();
|
|
99
|
+
details.forEach((detail, index) => {
|
|
100
|
+
const key = getCommandSearchDetailGroup(detail);
|
|
101
|
+
const group = groups.get(key);
|
|
102
|
+
if (group) {
|
|
103
|
+
group.details.push(detail);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
groups.set(key, { key, details: [detail], order: index });
|
|
107
|
+
});
|
|
108
|
+
return Array.from(groups.values()).sort((a, b) => {
|
|
109
|
+
var _a, _b;
|
|
110
|
+
const priorityA = (_a = commandSearchDetailGroupPriority[a.key]) !== null && _a !== void 0 ? _a : 2;
|
|
111
|
+
const priorityB = (_b = commandSearchDetailGroupPriority[b.key]) !== null && _b !== void 0 ? _b : 2;
|
|
112
|
+
return priorityA - priorityB || a.order - b.order;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
function CommandSearchDetailBadge({ detail, query, className, }) {
|
|
116
|
+
var _a;
|
|
117
|
+
return (_jsxs(Badge, { variant: (_a = detail.variant) !== null && _a !== void 0 ? _a : "muted", bordered: detail.bordered, "data-slot": "command-search-detail", className: cn("max-w-full justify-start text-left", className), title: detail.showLabel === false
|
|
118
|
+
? detail.value
|
|
119
|
+
: `${detail.label}: ${detail.value}`, children: [detail.showLabel === false ? null : (_jsx("span", { className: "shrink-0", children: detail.label })), _jsx("span", { className: cn("min-w-0 truncate", !detail.variant && "text-foreground"), children: _jsx(SearchHighlight, { text: detail.value, query: query }) })] }));
|
|
120
|
+
}
|
|
121
|
+
function CommandSearchResultItem({ item, query, }) {
|
|
122
|
+
var _a, _b, _c, _d;
|
|
123
|
+
const Icon = item.icon;
|
|
124
|
+
const tone = (_a = item.tone) !== null && _a !== void 0 ? _a : "muted";
|
|
125
|
+
const details = (_b = item.details) !== null && _b !== void 0 ? _b : [];
|
|
126
|
+
const titleDetails = details.filter(isCommandSearchTitleDetail);
|
|
127
|
+
const metadataDetails = details.filter((detail) => !isCommandSearchTitleDetail(detail));
|
|
128
|
+
const companyDetails = metadataDetails.filter(isCommandSearchCompanyDetail);
|
|
129
|
+
const recordDetails = metadataDetails.filter((detail) => !isCommandSearchCompanyDetail(detail));
|
|
130
|
+
const recordDetailGroups = getGroupedCommandSearchDetails(recordDetails);
|
|
131
|
+
const hasDetails = metadataDetails.length > 0;
|
|
132
|
+
const hasRecordDetails = recordDetails.length > 0;
|
|
133
|
+
const hasCompanyDetails = companyDetails.length > 0;
|
|
134
|
+
return (_jsxs(_Fragment, { children: [Icon ? (_jsx("span", { className: cn("mt-[calc(var(--space-1)/2)] flex size-[var(--space-8)] shrink-0 items-center justify-center rounded-control border", commandSearchItemToneClasses[tone]), "aria-hidden": "true", children: _jsx(Icon, { className: "h-3.5 w-3.5" }) })) : null, _jsxs("span", { className: "min-w-0 flex-1 space-y-[var(--space-2)]", children: [_jsxs("span", { "data-slot": "command-search-title-line", className: "flex min-w-0 items-center gap-[var(--space-2)]", children: [_jsx(Typography, { as: "span", variant: "label", className: "min-w-0 truncate", children: _jsx(SearchHighlight, { text: item.title, query: query }) }), titleDetails.map((detail) => (_jsx(CommandSearchDetailBadge, { detail: detail, query: query, className: "shrink-0" }, `${detail.label}-${detail.value}`)))] }), !hasDetails && item.subtitle ? (_jsx(Typography, { as: "span", variant: "caption", className: "block truncate text-muted-foreground", children: _jsx(SearchHighlight, { text: item.subtitle, query: query }) })) : null, !hasDetails && item.description ? (_jsx(Typography, { as: "span", variant: "caption", className: "block line-clamp-2 text-muted-foreground", children: _jsx(SearchHighlight, { text: item.description, query: query }) })) : null, hasDetails ? (_jsxs("span", { "data-slot": "command-search-details-lines", className: "flex min-w-0 flex-col gap-[calc(var(--space-1)+0.125rem)] overflow-hidden", children: [hasRecordDetails ? (_jsx("span", { "data-slot": "command-search-record-details-line", className: "flex min-w-0 items-center gap-[var(--space-1)] overflow-hidden", children: recordDetailGroups.map((group) => (_jsx("span", { "data-slot": "command-search-detail-group", "data-group": group.key, className: "flex min-w-0 shrink items-center gap-[var(--space-1)]", children: group.details.map((detail) => (_jsx(CommandSearchDetailBadge, { detail: detail, query: query, className: "min-w-0 shrink" }, `${detail.label}-${detail.value}`))) }, group.key))) })) : null, hasCompanyDetails ? (_jsx("span", { "data-slot": "command-search-company-details-line", "data-group": "company", className: "flex min-w-0 items-center gap-[var(--space-1)] overflow-hidden", children: companyDetails.map((detail) => (_jsx(CommandSearchDetailBadge, { detail: detail, query: query, className: "min-w-0 shrink" }, `${detail.label}-${detail.value}`))) })) : null] })) : null] }), _jsxs("span", { "data-slot": "command-search-result-affordance", className: "ml-auto flex size-[var(--space-8)] shrink-0 self-center items-center justify-center rounded-control text-muted-foreground opacity-0 transition-opacity group-data-[selected=true]:opacity-100 group-focus-within:opacity-100 group-hover:opacity-100", children: [(_c = item.affordance) !== null && _c !== void 0 ? _c : (_jsx(ArrowUpRight, { className: "size-[var(--space-4)]", "aria-hidden": "true" })), _jsx("span", { className: "sr-only", children: (_d = item.affordanceLabel) !== null && _d !== void 0 ? _d : "Open result" })] })] }));
|
|
24
135
|
}
|
|
25
136
|
function CommandSearchSkeleton({ rows = 4 }) {
|
|
26
|
-
return (_jsx("div", { className: "space-y-[var(--space-1)] p-[var(--space-1)]", children: Array.from({ length: rows }).map((_, index) => (_jsxs("div", { className: "space-
|
|
137
|
+
return (_jsx("div", { "data-slot": "command-search-skeleton", className: "space-y-[var(--space-1)] p-[var(--space-1)]", children: Array.from({ length: rows }).map((_, index) => (_jsxs("div", { "data-slot": "command-search-skeleton-row", className: "flex min-h-[var(--space-8)] items-start gap-[var(--space-2)] rounded-sm px-[var(--space-2)] py-[var(--space-3)]", children: [_jsx(Skeleton, { "data-slot": "command-search-skeleton-icon", className: "mt-[calc(var(--space-1)/2)] size-[var(--space-8)] shrink-0 rounded-control" }), _jsxs("div", { className: "min-w-0 flex-1 space-y-[var(--space-2)]", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-[var(--space-2)]", children: [_jsx(Skeleton, { className: "h-[var(--space-4)] w-36 max-w-[55%]" }), _jsx(Skeleton, { className: "h-[var(--space-5)] w-16 shrink-0 rounded-md" })] }), _jsx("div", { className: "flex min-w-0 items-center gap-[var(--space-1)] overflow-hidden", children: _jsx(Skeleton, { className: "h-[var(--space-5)] w-28 shrink rounded-md" }) }), _jsxs("div", { className: "flex min-w-0 items-center gap-[var(--space-1)] overflow-hidden", children: [_jsx(Skeleton, { className: "h-[var(--space-5)] w-40 max-w-[45%] shrink rounded-md" }), _jsx(Skeleton, { className: "h-[var(--space-5)] w-24 shrink rounded-md" }), _jsx(Skeleton, { className: "h-[var(--space-5)] w-24 shrink rounded-md" })] })] }), _jsx(Skeleton, { "data-slot": "command-search-skeleton-affordance", className: "ml-auto size-[var(--space-8)] shrink-0 self-center rounded-control opacity-60" })] }, index))) }));
|
|
138
|
+
}
|
|
139
|
+
function CommandSearchEmptyState({ query, resultsMinHeight, }) {
|
|
140
|
+
const normalizedQuery = query.trim();
|
|
141
|
+
return (_jsx(EmptyState, { "data-slot": "command-search-empty-state", icon: CommandSearchEmptyIcon, style: { minHeight: resultsMinHeight }, message: normalizedQuery ? "No results found" : "Start typing to search", description: normalizedQuery
|
|
142
|
+
? `No results found for "${query}".`
|
|
143
|
+
: "Results will appear here as you type." }));
|
|
27
144
|
}
|
|
28
145
|
function useControllableOpen({ open, defaultOpen, onOpenChange, }) {
|
|
29
146
|
const [internalOpen, setInternalOpen] = React.useState(defaultOpen !== null && defaultOpen !== void 0 ? defaultOpen : false);
|
|
@@ -37,7 +154,7 @@ function useControllableOpen({ open, defaultOpen, onOpenChange, }) {
|
|
|
37
154
|
}, [isControlled, onOpenChange]);
|
|
38
155
|
return [currentOpen, setOpen];
|
|
39
156
|
}
|
|
40
|
-
function CommandSearch({ value, onValueChange, sections, children, className, isCollapsed = false, open, defaultOpen, onOpenChange, triggerVariant = "default", triggerPlaceholder = "Search...", inputPlaceholder = "Search...", shortcutKey = "k", shortcutLabel, allFilter, filters = [], loading = false, loadingRows = 4, onShowAllResults, showAllResultsLabel, triggerClassName, dialogClassName, resultsClassName, ...props }) {
|
|
157
|
+
function CommandSearch({ value, onValueChange, sections, children, className, isCollapsed = false, open, defaultOpen, onOpenChange, triggerVariant = "default", triggerPlaceholder = "Search...", inputPlaceholder = "Search...", shortcutKey = "k", shortcutPlatform = "mac", shortcutLabel, allFilter, filters = [], subFilters = [], allowMultiSelect = true, loading = false, loadingRows = 4, resultsMinHeight = 384, resultsMaxHeight = 560, onShowAllResults, showAllResultsLabel, triggerClassName, dialogClassName, resultsClassName, ...props }) {
|
|
41
158
|
const [isOpen, setIsOpen] = useControllableOpen({
|
|
42
159
|
open,
|
|
43
160
|
defaultOpen,
|
|
@@ -53,7 +170,10 @@ function CommandSearch({ value, onValueChange, sections, children, className, is
|
|
|
53
170
|
: showAllResultsLabel !== null && showAllResultsLabel !== void 0 ? showAllResultsLabel : (hasResults
|
|
54
171
|
? `Show all results for "${normalizedValue}"`
|
|
55
172
|
: `Search all records for "${normalizedValue}"`);
|
|
56
|
-
const
|
|
173
|
+
const shortcutKeyLabel = shortcutKey.toUpperCase();
|
|
174
|
+
const resolvedLabel = shortcutLabel !== null && shortcutLabel !== void 0 ? shortcutLabel : (shortcutPlatform === "windows"
|
|
175
|
+
? `Ctrl ${shortcutKeyLabel}`
|
|
176
|
+
: `⌘${shortcutKeyLabel}`);
|
|
57
177
|
React.useEffect(() => {
|
|
58
178
|
const handleKeyDown = (event) => {
|
|
59
179
|
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === shortcutKey.toLowerCase()) {
|
|
@@ -72,29 +192,58 @@ function CommandSearch({ value, onValueChange, sections, children, className, is
|
|
|
72
192
|
const preserveInputFocus = React.useCallback((event) => {
|
|
73
193
|
event.preventDefault();
|
|
74
194
|
}, []);
|
|
75
|
-
const selectFilter = React.useCallback((
|
|
76
|
-
|
|
195
|
+
const selectFilter = React.useCallback((filter) => {
|
|
196
|
+
var _a;
|
|
197
|
+
(_a = filter.onSelect) === null || _a === void 0 ? void 0 : _a.call(filter, {
|
|
198
|
+
value: filter.value,
|
|
199
|
+
selected: Boolean(filter.selected),
|
|
200
|
+
allowMultiSelect,
|
|
201
|
+
});
|
|
77
202
|
window.setTimeout(() => { var _a; return (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus(); }, 0);
|
|
78
|
-
}, []);
|
|
203
|
+
}, [allowMultiSelect]);
|
|
204
|
+
const clearSearch = React.useCallback(() => {
|
|
205
|
+
onValueChange("");
|
|
206
|
+
window.setTimeout(() => { var _a; return (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus(); }, 0);
|
|
207
|
+
}, [onValueChange]);
|
|
79
208
|
return (_jsxs("div", { className: className, ...props, children: [_jsxs(Button, { type: "button", onClick: () => setIsOpen(true), variant: "ghost", size: isCollapsed ? "icon" : "default", className: cn(triggerVariant === "sidebar" && isCollapsed
|
|
80
|
-
? "h-control min-h-control w-control min-w-control max-w-control shrink-0 rounded-control border-none bg-transparent p-0 !font-normal text-muted-foreground hover:bg-card hover:text-foreground focus-visible:bg-card focus-visible:ring-ring/30"
|
|
209
|
+
? "mx-auto flex h-control min-h-control w-control min-w-control max-w-control shrink-0 gap-0 rounded-control border-none bg-transparent p-[var(--space-0)] !font-normal text-muted-foreground hover:bg-card hover:text-foreground focus-visible:bg-card focus-visible:ring-ring/30"
|
|
81
210
|
: triggerVariant === "sidebar"
|
|
82
|
-
? "h-control w-full min-w-[14rem] justify-start gap-2 rounded-control border border-[hsl(var(--layout-sidebar-border))] bg-card px-2
|
|
211
|
+
? "h-control w-full min-w-[14rem] justify-start gap-[var(--space-2)] rounded-control border border-[hsl(var(--layout-sidebar-border))] bg-card px-[var(--space-2)] !font-normal text-muted-foreground shadow-none hover:bg-card hover:text-foreground focus-visible:border-ring focus-visible:bg-card focus-visible:ring-ring/30"
|
|
83
212
|
: isCollapsed
|
|
84
|
-
? "h-control min-h-control w-control min-w-control max-w-control shrink-0 rounded-control p-0 !font-normal text-muted-foreground hover:bg-card hover:text-foreground"
|
|
85
|
-
: "h-control w-full min-w-[14rem] justify-start gap-2 rounded-control border border-border bg-card px-2
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
213
|
+
? "h-control min-h-control w-control min-w-control max-w-control shrink-0 rounded-control p-[var(--space-0)] !font-normal text-muted-foreground hover:bg-card hover:text-foreground"
|
|
214
|
+
: "h-control w-full min-w-[14rem] justify-start gap-[var(--space-2)] rounded-control border border-border bg-card px-[var(--space-2)] !font-normal text-muted-foreground hover:bg-card hover:text-foreground", triggerClassName), children: [_jsx(SearchIcon, { className: "shrink-0", size: 16 }), !isCollapsed ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "flex-1 text-left text-sm", children: triggerPlaceholder }), _jsx("kbd", { className: "hidden h-[var(--space-5)] items-center gap-[var(--space-1)] rounded border border-border bg-muted px-[var(--space-1)] font-mono text-[11px] text-muted-foreground sm:inline-flex", children: resolvedLabel })] })) : null] }), _jsx(DialogRoot, { open: isOpen, onOpenChange: setIsOpen, children: _jsxs(DialogSurface, { className: cn("w-[calc(100vw-var(--space-8))] max-w-[60rem] gap-[var(--space-0)] overflow-hidden p-[var(--space-0)] sm:w-[min(72vw,60rem)]", dialogClassName), "aria-describedby": undefined, hideCloseButton: true, children: [_jsx(DialogTitle, { className: "sr-only", children: inputPlaceholder }), _jsxs(Command, { shouldFilter: false, className: "rounded-none border-0", children: [_jsx(CommandInput, { ref: inputRef, value: value, onValueChange: onValueChange, placeholder: inputPlaceholder, wrapperClassName: "border-b px-[var(--space-3)] py-[var(--space-2)]", iconClassName: "hidden", className: "h-control border-0 bg-transparent px-[var(--space-3)] py-[var(--space-0)] pr-[calc(var(--space-12)+var(--space-6))] text-sm shadow-none focus-visible:border-transparent focus-visible:ring-0", endAdornment: _jsxs(_Fragment, { children: [value ? (_jsx("button", { type: "button", "aria-label": "Clear search", onMouseDown: preserveInputFocus, onClick: clearSearch, className: "flex size-[var(--space-6)] items-center justify-center rounded-sm text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: _jsx(XIcon, { size: 14, "aria-hidden": "true" }) })) : null, _jsx("kbd", { className: "inline-flex h-[var(--space-5)] items-center rounded border border-border bg-muted px-[var(--space-1)] font-mono text-[11px] leading-none text-muted-foreground", "aria-hidden": "true", children: "Esc" })] }) }), allFilter || filters.length ? (_jsxs("div", { role: "group", "aria-label": "Search filters", "data-multiselectable": allowMultiSelect ? "true" : "false", className: "flex flex-wrap items-center gap-[var(--space-1)] border-b px-[var(--space-3)] py-[var(--space-2)]", children: [allFilter ? (_jsxs(FilterToggle, { selected: allFilter.selected, "aria-pressed": Boolean(allFilter.selected), onMouseDown: preserveInputFocus, onClick: () => selectFilter(allFilter), children: [allFilter.label, typeof allFilter.count === "number" ? (_jsx(CountBadge, { count: allFilter.count, hideWhenZero: true, size: "sm", variant: allFilter.selected ? "default" : "muted" })) : null] })) : null, filters.map((filter) => (_jsxs(FilterToggle, { selected: filter.selected, "aria-pressed": Boolean(filter.selected), onMouseDown: preserveInputFocus, onClick: () => selectFilter(filter), children: [filter.label, typeof filter.count === "number" ? (_jsx(CountBadge, { count: filter.count, hideWhenZero: true, size: "sm", variant: filter.selected ? "default" : "muted" })) : null] }, filter.value)))] })) : null, subFilters.length ? (_jsxs("div", { role: "group", "aria-label": "Search sub-filters", "data-multiselectable": allowMultiSelect ? "true" : "false", className: "flex flex-wrap items-center gap-[var(--space-1)] border-b bg-muted/30 px-[var(--space-3)] py-[var(--space-2)]", children: [_jsx(SlidersHorizontal, { "data-slot": "command-search-sub-filter-icon", className: "mr-[var(--space-1)] h-3.5 w-3.5 shrink-0 text-muted-foreground", "aria-hidden": "true" }), subFilters.map((filter) => (_jsxs(FilterToggle, { selected: filter.selected, "aria-pressed": Boolean(filter.selected), onMouseDown: preserveInputFocus, onClick: () => selectFilter(filter), children: [filter.label, typeof filter.count === "number" ? (_jsx(CountBadge, { count: filter.count, hideWhenZero: true, size: "sm", variant: filter.selected ? "default" : "muted" })) : null] }, filter.value)))] })) : null, _jsx(CommandList, { "aria-busy": loading || undefined, className: cn("p-[var(--space-1)]", resultsClassName), style: {
|
|
215
|
+
minHeight: resultsMinHeight,
|
|
216
|
+
maxHeight: resultsMaxHeight,
|
|
217
|
+
}, children: loading ? (_jsx(CommandSearchSkeleton, { rows: loadingRows })) : children !== undefined ? children : sections ? (visibleSections.length ? (visibleSections.map((section, index) => {
|
|
218
|
+
var _a, _b;
|
|
219
|
+
return (_jsxs(React.Fragment, { children: [index > 0 ? (_jsx(CommandSeparator, { alwaysRender: true, className: "-mx-[var(--space-1)]" })) : null, _jsxs(CommandGroup, { heading: section.heading, children: [section.items.map((item) => (_jsx(CommandItem, { value: getCommandSearchItemValue(item), disabled: item.disabled, tabIndex: item.disabled ? -1 : 0, className: "group min-h-[var(--space-8)] w-full items-start gap-[var(--space-2)] px-[var(--space-2)] py-[var(--space-3)] text-left font-normal whitespace-normal focus-visible:z-10 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring", onSelect: () => {
|
|
220
|
+
var _a;
|
|
221
|
+
(_a = item.onSelect) === null || _a === void 0 ? void 0 : _a.call(item);
|
|
222
|
+
if (item.closeOnSelect !== false) {
|
|
223
|
+
setIsOpen(false);
|
|
224
|
+
}
|
|
225
|
+
}, onKeyDown: (event) => handleCommandSearchItemKeyDown(event, () => {
|
|
226
|
+
var _a;
|
|
227
|
+
(_a = item.onSelect) === null || _a === void 0 ? void 0 : _a.call(item);
|
|
228
|
+
if (item.closeOnSelect !== false) {
|
|
229
|
+
setIsOpen(false);
|
|
230
|
+
}
|
|
231
|
+
}), children: _jsx(CommandSearchResultItem, { item: item, query: value }) }, item.id))), section.action ? (_jsx(CommandItem, { value: getCommandSearchSectionActionValue(section), tabIndex: 0, className: "group min-h-[var(--space-8)] w-full items-center px-[var(--space-0)] py-[var(--space-0)] text-left font-normal whitespace-normal text-muted-foreground focus-visible:z-10 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring data-[selected=true]:bg-transparent data-[selected=true]:text-muted-foreground", onSelect: () => {
|
|
232
|
+
var _a, _b, _c;
|
|
233
|
+
(_b = (_a = section.action) === null || _a === void 0 ? void 0 : _a.onSelect) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
234
|
+
if (((_c = section.action) === null || _c === void 0 ? void 0 : _c.closeOnSelect) !== false) {
|
|
235
|
+
setIsOpen(false);
|
|
236
|
+
}
|
|
237
|
+
}, onKeyDown: (event) => handleCommandSearchItemKeyDown(event, () => {
|
|
238
|
+
var _a, _b, _c;
|
|
239
|
+
(_b = (_a = section.action) === null || _a === void 0 ? void 0 : _a.onSelect) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
240
|
+
if (((_c = section.action) === null || _c === void 0 ? void 0 : _c.closeOnSelect) !== false) {
|
|
241
|
+
setIsOpen(false);
|
|
242
|
+
}
|
|
243
|
+
}), children: _jsx(Button, { asChild: true, variant: "link", size: "compact", className: "w-full justify-start text-left font-medium group-data-[selected=true]:underline", children: _jsxs("span", { children: [_jsx("span", { className: "min-w-0 truncate", children: section.action.label }), _jsxs("span", { className: "inline-flex shrink-0 items-center justify-center text-primary", children: [(_a = section.action.affordance) !== null && _a !== void 0 ? _a : (_jsx(ArrowRight, { className: "size-[var(--space-4)]", "aria-hidden": "true" })), _jsx("span", { className: "sr-only", children: (_b = section.action.affordanceLabel) !== null && _b !== void 0 ? _b : "Show all results in group" })] })] }) }) }, `${section.key}-action`)) : null] })] }, section.key));
|
|
244
|
+
})) : (_jsx(CommandSearchEmptyState, { query: value, resultsMinHeight: resultsMinHeight }))) : (_jsx(CommandSearchEmptyState, { query: value, resultsMinHeight: resultsMinHeight })) }), shouldShowAllResults ? (_jsx(PopoverMenuFooter, { inset: "flush", children: _jsxs(PopoverMenuFooterAction, { className: "group", onClick: () => {
|
|
96
245
|
onShowAllResults === null || onShowAllResults === void 0 ? void 0 : onShowAllResults(normalizedValue);
|
|
97
246
|
setIsOpen(false);
|
|
98
|
-
}, children: _jsx("span", { className: "truncate", children: resolvedShowAllResultsLabel }) }) })) : null] })] }) })] }));
|
|
247
|
+
}, children: [_jsx("span", { className: "min-w-0 truncate", children: resolvedShowAllResultsLabel }), _jsx(ArrowRight, { className: "size-[var(--space-4)] shrink-0 transition-transform duration-150 group-hover:translate-x-[var(--space-1)] group-focus-visible:translate-x-[var(--space-1)]", "aria-hidden": "true" })] }) })) : null] })] }) })] }));
|
|
99
248
|
}
|
|
100
249
|
export { CommandSearch };
|
package/dist/command.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ interface CommandInputProps extends React.ComponentPropsWithoutRef<typeof Comman
|
|
|
21
21
|
wrapperClassName?: string;
|
|
22
22
|
iconClassName?: string;
|
|
23
23
|
iconSize?: number;
|
|
24
|
+
endAdornment?: React.ReactNode;
|
|
25
|
+
endAdornmentClassName?: string;
|
|
24
26
|
}
|
|
25
27
|
declare const CommandInput: React.ForwardRefExoticComponent<CommandInputProps & React.RefAttributes<HTMLInputElement>>;
|
|
26
28
|
declare const CommandList: React.ForwardRefExoticComponent<Omit<{
|
package/dist/command.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Command as CommandPrimitive } from "cmdk";
|
|
5
5
|
import { inputControlClassName } from "./input-control";
|
|
@@ -7,7 +7,7 @@ import { cn } from "./lib/utils";
|
|
|
7
7
|
import { SearchInputShell } from "./search-input-shell";
|
|
8
8
|
const Command = React.forwardRef(({ className, ...props }, ref) => (_jsx(CommandPrimitive, { ref: ref, className: cn("flex h-full w-full flex-col overflow-hidden rounded-overlay bg-popover text-popover-foreground", className), ...props })));
|
|
9
9
|
Command.displayName = CommandPrimitive.displayName;
|
|
10
|
-
const CommandInput = React.forwardRef(({ className, wrapperClassName, iconClassName, iconSize = 16, ...props }, ref) => (_jsx("div", { "cmdk-input-wrapper": "", className: cn("border-b p-4", wrapperClassName), children:
|
|
10
|
+
const CommandInput = React.forwardRef(({ className, wrapperClassName, iconClassName, iconSize = 16, endAdornment, endAdornmentClassName, ...props }, ref) => (_jsx("div", { "cmdk-input-wrapper": "", className: cn("border-b p-4", wrapperClassName), children: _jsxs(SearchInputShell, { iconClassName: iconClassName, iconSize: iconSize, children: [_jsx(CommandPrimitive.Input, { ref: ref, className: cn(inputControlClassName, "bg-background pl-9 pr-3", className), ...props }), endAdornment ? (_jsx("div", { "data-slot": "command-input-end-adornment", className: cn("absolute right-[var(--space-3)] top-1/2 flex -translate-y-1/2 items-center gap-[var(--space-1)]", endAdornmentClassName), children: endAdornment })) : null] }) })));
|
|
11
11
|
CommandInput.displayName = CommandPrimitive.Input.displayName;
|
|
12
12
|
const CommandList = React.forwardRef(({ className, ...props }, ref) => (_jsx(CommandPrimitive.List, { ref: ref, className: cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className), ...props })));
|
|
13
13
|
CommandList.displayName = CommandPrimitive.List.displayName;
|
package/dist/dialog-primitive.js
CHANGED
|
@@ -9,7 +9,7 @@ const DialogRoot = DialogPrimitive.Root;
|
|
|
9
9
|
const DialogTrigger = DialogPrimitive.Trigger;
|
|
10
10
|
const DialogPortal = DialogPrimitive.Portal;
|
|
11
11
|
const DialogClose = DialogPrimitive.Close;
|
|
12
|
-
const DialogOverlay = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Overlay, { ref: ref, className: cn("fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", className), ...props })));
|
|
12
|
+
const DialogOverlay = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Overlay, { ref: ref, "data-slot": "dialog-overlay", className: cn("fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", className), ...props })));
|
|
13
13
|
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|
14
14
|
const DialogSurface = React.forwardRef(({ className, children, hideCloseButton, ...props }, ref) => (_jsxs(DialogPortal, { children: [_jsx(DialogOverlay, {}), _jsxs(DialogPrimitive.Content, { ref: ref, className: cn("fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-3 border bg-background p-4 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-overlay", className), ...props, children: [children, !hideCloseButton ? (_jsxs(DialogPrimitive.Close, { className: "absolute right-2 top-2 flex h-6 w-6 cursor-pointer items-center justify-center rounded-control text-muted-foreground ring-offset-background transition-colors hover:bg-accent hover:text-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none", children: [_jsx(XIcon, { size: 16 }), _jsx("span", { className: "sr-only", children: "Close" })] })) : null] })] })));
|
|
15
15
|
DialogSurface.displayName = DialogPrimitive.Content.displayName;
|
package/dist/draggable-tabs.d.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type BadgeProps } from "./badge";
|
|
3
|
+
import { type GuidanceTipProps } from "./guidance-tip";
|
|
3
4
|
import { type TooltipProps } from "./tooltip";
|
|
4
5
|
export interface DraggableTabBadge {
|
|
5
6
|
label: React.ReactNode;
|
|
6
7
|
variant?: BadgeProps["variant"];
|
|
7
8
|
bordered?: BadgeProps["bordered"];
|
|
8
9
|
}
|
|
10
|
+
export type DraggableTabGuidance = Omit<GuidanceTipProps, "trigger">;
|
|
9
11
|
export interface DraggableTabItem {
|
|
10
12
|
id: string;
|
|
11
13
|
label: React.ReactNode;
|
|
@@ -17,6 +19,7 @@ export interface DraggableTabItem {
|
|
|
17
19
|
tooltip?: React.ReactNode;
|
|
18
20
|
tooltipContentClassName?: string;
|
|
19
21
|
tooltipSide?: TooltipProps["side"];
|
|
22
|
+
guidance?: DraggableTabGuidance;
|
|
20
23
|
onSelect?: () => void;
|
|
21
24
|
onClose?: () => void;
|
|
22
25
|
className?: string;
|
package/dist/draggable-tabs.js
CHANGED
|
@@ -5,6 +5,7 @@ import { SortableContext, horizontalListSortingStrategy, sortableKeyboardCoordin
|
|
|
5
5
|
import { CSS } from "@dnd-kit/utilities";
|
|
6
6
|
import { XIcon } from "./animated-icons/x";
|
|
7
7
|
import { Badge } from "./badge";
|
|
8
|
+
import { GuidanceTip } from "./guidance-tip";
|
|
8
9
|
import { cn } from "./lib/utils";
|
|
9
10
|
import { Tooltip } from "./tooltip";
|
|
10
11
|
function TabButton({ item, dragHandleProps, }) {
|
|
@@ -26,6 +27,9 @@ function TabButton({ item, dragHandleProps, }) {
|
|
|
26
27
|
event.stopPropagation();
|
|
27
28
|
(_a = item.onClose) === null || _a === void 0 ? void 0 : _a.call(item);
|
|
28
29
|
}, className: cn("cursor-pointer rounded p-1 text-muted-foreground hover:text-foreground", item.closeButtonClassName), "aria-label": "Close tab", children: _jsx(XIcon, { size: 12 }) })) : null] }));
|
|
30
|
+
if (item.guidance) {
|
|
31
|
+
return _jsx(GuidanceTip, { ...item.guidance, trigger: content });
|
|
32
|
+
}
|
|
29
33
|
if (!item.tooltip) {
|
|
30
34
|
return content;
|
|
31
35
|
}
|
package/dist/editable-table.js
CHANGED
|
@@ -33,7 +33,7 @@ function setNestedValue(obj, path, value) {
|
|
|
33
33
|
current[parts[parts.length - 1]] = value;
|
|
34
34
|
return result;
|
|
35
35
|
}
|
|
36
|
-
function EditableTable({ value, onChange, columns, isEditing = false, className, emptyPlaceholder = "Enter text
|
|
36
|
+
function EditableTable({ value, onChange, columns, isEditing = false, className, emptyPlaceholder = "Enter text", checkable = false, checkableHeader, activeField = "active", addRowLabel = "Add Row", renderRowActions, }) {
|
|
37
37
|
const [editingCell, setEditingCell] = React.useState(null);
|
|
38
38
|
const [editValue, setEditValue] = React.useState("");
|
|
39
39
|
const inputRef = React.useRef(null);
|
|
@@ -130,7 +130,7 @@ function EditableTable({ value, onChange, columns, isEditing = false, className,
|
|
|
130
130
|
const isEmptyCell = isEmptyCellValue(cellValue);
|
|
131
131
|
const isEditingCell = (editingCell === null || editingCell === void 0 ? void 0 : editingCell.rowIndex) === rowIndex &&
|
|
132
132
|
(editingCell === null || editingCell === void 0 ? void 0 : editingCell.colPath) === column.path;
|
|
133
|
-
return (_jsx(TableCell, { className: cn("whitespace-normal", isEditing && "cursor-pointer hover:bg-muted/50"), width: column.width, minWidth: getColumnMinWidth(column), maxWidth: getColumnMaxWidth(column), onClick: () => handleCellClick(rowIndex, column.path), children: isEditingCell ? (
|
|
133
|
+
return (_jsx(TableCell, { className: cn("whitespace-normal", isEditing && "cursor-pointer hover:bg-muted/50"), width: column.width, minWidth: getColumnMinWidth(column), maxWidth: getColumnMaxWidth(column), onClick: () => handleCellClick(rowIndex, column.path), children: isEditingCell ? (_jsxs("div", { className: "relative min-h-8 min-w-0", children: [_jsx("span", { "aria-hidden": "true", className: "block min-h-8 whitespace-normal break-words opacity-0", style: { maxWidth: getColumnMaxWidth(column) }, children: editValue || emptyPlaceholder }), _jsx(InputControl, { ref: inputRef, value: editValue, placeholder: emptyPlaceholder, onChange: (event) => setEditValue(event.target.value), onClick: (event) => event.stopPropagation(), onBlur: commitEdit, onKeyDown: handleKeyDown, className: "absolute inset-x-0 top-0 h-8 w-full px-2 py-1 text-sm" })] })) : (_jsx("span", { className: cn("flex min-h-8 items-center whitespace-normal break-words", isEmptyCell
|
|
134
134
|
? "text-muted-foreground"
|
|
135
135
|
: undefined), style: { maxWidth: getColumnMaxWidth(column) }, children: isEmptyCell && isEditing
|
|
136
136
|
? emptyPlaceholder
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { type ButtonProps } from "./button";
|
|
3
|
+
import { type PopoverProps } from "./popover";
|
|
4
|
+
export interface GuidanceTipAction {
|
|
5
|
+
label?: React.ReactNode;
|
|
6
|
+
onClick?: ButtonProps["onClick"];
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
loading?: boolean;
|
|
9
|
+
loadingText?: React.ReactNode;
|
|
10
|
+
ariaLabel?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface GuidanceTipProps extends Omit<PopoverProps, "children" | "contentClassName" | "surface" | "title"> {
|
|
13
|
+
title: React.ReactNode;
|
|
14
|
+
description: React.ReactNode;
|
|
15
|
+
step?: number;
|
|
16
|
+
totalSteps?: number;
|
|
17
|
+
stepLabel?: React.ReactNode;
|
|
18
|
+
previousAction?: GuidanceTipAction;
|
|
19
|
+
nextAction?: GuidanceTipAction;
|
|
20
|
+
dismissAction?: GuidanceTipAction;
|
|
21
|
+
highlightTrigger?: boolean;
|
|
22
|
+
focusOverlay?: boolean;
|
|
23
|
+
contentClassName?: string;
|
|
24
|
+
}
|
|
25
|
+
declare function GuidanceTip({ title, description, step, totalSteps, stepLabel, previousAction, nextAction, dismissAction, highlightTrigger, focusOverlay, contentClassName, trigger, open, defaultOpen, onOpenChange, align, side, sideOffset, onKeyDown, ...popoverProps }: GuidanceTipProps): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
export { GuidanceTip };
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { createPortal } from "react-dom";
|
|
5
|
+
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
6
|
+
import { Button } from "./button";
|
|
7
|
+
import { cn } from "./lib/utils";
|
|
8
|
+
import { Popover } from "./popover";
|
|
9
|
+
const triggerHighlightClassName = "rounded-control ring-[calc(var(--space-1)/2)] ring-[hsl(var(--ring))] ring-offset-background [--tw-ring-offset-width:var(--space-1)] shadow-[0_0_var(--space-10)_hsl(var(--ring)/0.55)]";
|
|
10
|
+
const useIsomorphicLayoutEffect = typeof window === "undefined" ? React.useEffect : React.useLayoutEffect;
|
|
11
|
+
function setRef(ref, value) {
|
|
12
|
+
if (!ref) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (typeof ref === "function") {
|
|
16
|
+
ref(value);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
ref.current = value;
|
|
20
|
+
}
|
|
21
|
+
function composeRefs(...refs) {
|
|
22
|
+
return (value) => {
|
|
23
|
+
refs.forEach((ref) => setRef(ref, value));
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function getStepLabel({ stepLabel, step, totalSteps, }) {
|
|
27
|
+
if (stepLabel !== undefined) {
|
|
28
|
+
return stepLabel;
|
|
29
|
+
}
|
|
30
|
+
if (typeof step === "number" && typeof totalSteps === "number") {
|
|
31
|
+
return `Step ${step} of ${totalSteps}`;
|
|
32
|
+
}
|
|
33
|
+
if (typeof step === "number") {
|
|
34
|
+
return `Step ${step}`;
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
function isDoneActionLabel(label) {
|
|
39
|
+
return typeof label === "string" && label.trim().toLowerCase() === "done";
|
|
40
|
+
}
|
|
41
|
+
function isEditableKeyboardTarget(target) {
|
|
42
|
+
if (!(target instanceof HTMLElement)) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
return (target.isContentEditable ||
|
|
46
|
+
target.matches("input, select, textarea, [role='textbox']"));
|
|
47
|
+
}
|
|
48
|
+
function canClickActionButton(button) {
|
|
49
|
+
return !(!button ||
|
|
50
|
+
button.disabled ||
|
|
51
|
+
button.getAttribute("aria-disabled") === "true");
|
|
52
|
+
}
|
|
53
|
+
function getGuidanceTrigger({ trigger, highlightTrigger, triggerRef, }) {
|
|
54
|
+
if (!highlightTrigger || !React.isValidElement(trigger)) {
|
|
55
|
+
return trigger;
|
|
56
|
+
}
|
|
57
|
+
const triggerElement = trigger;
|
|
58
|
+
return React.cloneElement(triggerElement, {
|
|
59
|
+
"data-guidance-highlight": "true",
|
|
60
|
+
ref: composeRefs(triggerElement.ref, triggerRef),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function GuidanceTriggerHighlight({ enabled, focusOverlay, targetRef, }) {
|
|
64
|
+
var _a, _b;
|
|
65
|
+
const [rect, setRect] = React.useState(null);
|
|
66
|
+
const updateRect = React.useCallback(() => {
|
|
67
|
+
const element = targetRef.current;
|
|
68
|
+
setRect(element && enabled ? element.getBoundingClientRect() : null);
|
|
69
|
+
}, [enabled, targetRef]);
|
|
70
|
+
useIsomorphicLayoutEffect(() => {
|
|
71
|
+
var _a;
|
|
72
|
+
if (!enabled) {
|
|
73
|
+
setRect(null);
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
const element = targetRef.current;
|
|
77
|
+
if (!element) {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
const ownerWindow = (_a = element.ownerDocument.defaultView) !== null && _a !== void 0 ? _a : window;
|
|
81
|
+
let frame = 0;
|
|
82
|
+
const requestUpdate = () => {
|
|
83
|
+
ownerWindow.cancelAnimationFrame(frame);
|
|
84
|
+
frame = ownerWindow.requestAnimationFrame(updateRect);
|
|
85
|
+
};
|
|
86
|
+
const resizeObserver = typeof ResizeObserver === "undefined"
|
|
87
|
+
? null
|
|
88
|
+
: new ResizeObserver(requestUpdate);
|
|
89
|
+
updateRect();
|
|
90
|
+
resizeObserver === null || resizeObserver === void 0 ? void 0 : resizeObserver.observe(element);
|
|
91
|
+
ownerWindow.addEventListener("resize", requestUpdate);
|
|
92
|
+
ownerWindow.addEventListener("scroll", requestUpdate, true);
|
|
93
|
+
return () => {
|
|
94
|
+
ownerWindow.cancelAnimationFrame(frame);
|
|
95
|
+
resizeObserver === null || resizeObserver === void 0 ? void 0 : resizeObserver.disconnect();
|
|
96
|
+
ownerWindow.removeEventListener("resize", requestUpdate);
|
|
97
|
+
ownerWindow.removeEventListener("scroll", requestUpdate, true);
|
|
98
|
+
};
|
|
99
|
+
}, [enabled, targetRef, updateRect]);
|
|
100
|
+
if (!enabled || !rect || typeof document === "undefined") {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
const ownerDocument = (_b = (_a = targetRef.current) === null || _a === void 0 ? void 0 : _a.ownerDocument) !== null && _b !== void 0 ? _b : document;
|
|
104
|
+
return createPortal(_jsx("div", { "aria-hidden": "true", "data-guidance-highlight-overlay": "true", className: cn("pointer-events-none fixed z-40", triggerHighlightClassName), style: {
|
|
105
|
+
left: rect.left,
|
|
106
|
+
top: rect.top,
|
|
107
|
+
width: rect.width,
|
|
108
|
+
height: rect.height,
|
|
109
|
+
boxShadow: focusOverlay
|
|
110
|
+
? "0 0 0 9999px rgb(0 0 0 / 0.8), 0 0 var(--space-10) hsl(var(--ring) / 0.55)"
|
|
111
|
+
: "0 0 var(--space-10) hsl(var(--ring) / 0.55)",
|
|
112
|
+
} }), ownerDocument.body);
|
|
113
|
+
}
|
|
114
|
+
function GuidanceTip({ title, description, step, totalSteps, stepLabel, previousAction, nextAction, dismissAction, highlightTrigger = true, focusOverlay = false, contentClassName, trigger, open, defaultOpen, onOpenChange, align = "start", side = "bottom", sideOffset = 0, onKeyDown, ...popoverProps }) {
|
|
115
|
+
var _a, _b, _c;
|
|
116
|
+
const [internalOpen, setInternalOpen] = React.useState(defaultOpen !== null && defaultOpen !== void 0 ? defaultOpen : false);
|
|
117
|
+
const previousButtonRef = React.useRef(null);
|
|
118
|
+
const nextButtonRef = React.useRef(null);
|
|
119
|
+
const triggerHighlightRef = React.useRef(null);
|
|
120
|
+
const isControlled = open !== undefined;
|
|
121
|
+
const isOpen = isControlled ? open : internalOpen;
|
|
122
|
+
const resolvedStepLabel = getStepLabel({ stepLabel, step, totalSteps });
|
|
123
|
+
const showActions = Boolean(previousAction || nextAction || dismissAction);
|
|
124
|
+
const isFinalStep = typeof step === "number" &&
|
|
125
|
+
typeof totalSteps === "number" &&
|
|
126
|
+
step >= totalSteps;
|
|
127
|
+
const showNextActionIcon = !isFinalStep && !isDoneActionLabel(nextAction === null || nextAction === void 0 ? void 0 : nextAction.label);
|
|
128
|
+
const resolvedTrigger = getGuidanceTrigger({
|
|
129
|
+
trigger,
|
|
130
|
+
highlightTrigger: highlightTrigger && isOpen,
|
|
131
|
+
triggerRef: triggerHighlightRef,
|
|
132
|
+
});
|
|
133
|
+
const handleOpenChange = React.useCallback((nextOpen) => {
|
|
134
|
+
if (!isControlled) {
|
|
135
|
+
setInternalOpen(nextOpen);
|
|
136
|
+
}
|
|
137
|
+
onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(nextOpen);
|
|
138
|
+
}, [isControlled, onOpenChange]);
|
|
139
|
+
const handleKeyDown = React.useCallback((event) => {
|
|
140
|
+
onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(event);
|
|
141
|
+
if (event.defaultPrevented ||
|
|
142
|
+
event.altKey ||
|
|
143
|
+
event.ctrlKey ||
|
|
144
|
+
event.metaKey ||
|
|
145
|
+
isEditableKeyboardTarget(event.target)) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (event.key !== "ArrowRight" && event.key !== "ArrowLeft") {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const actionButton = event.key === "ArrowRight"
|
|
152
|
+
? nextButtonRef.current
|
|
153
|
+
: previousButtonRef.current;
|
|
154
|
+
event.preventDefault();
|
|
155
|
+
event.stopPropagation();
|
|
156
|
+
if (canClickActionButton(actionButton)) {
|
|
157
|
+
actionButton.click();
|
|
158
|
+
}
|
|
159
|
+
}, [onKeyDown]);
|
|
160
|
+
return (_jsxs(_Fragment, { children: [_jsx(Popover, { open: isOpen, onOpenChange: handleOpenChange, trigger: resolvedTrigger, align: align, side: side, sideOffset: sideOffset, onKeyDown: handleKeyDown, contentClassName: cn("w-[calc(var(--space-12)*6)] max-w-[calc(100vw-var(--space-8))] p-[var(--space-3)]", "data-[side=bottom]:mt-[var(--space-3)] data-[side=left]:mr-[var(--space-3)] data-[side=right]:ml-[var(--space-3)] data-[side=top]:mb-[var(--space-3)]", contentClassName), ...popoverProps, children: _jsxs("div", { className: "space-y-[var(--space-2)]", children: [_jsxs("div", { className: "space-y-[var(--space-1)]", children: [resolvedStepLabel ? (_jsx("p", { className: "text-xs font-medium text-muted-foreground", children: resolvedStepLabel })) : null, _jsx("p", { className: "text-sm font-medium text-foreground", children: title }), _jsx("p", { className: "text-sm text-muted-foreground", children: description })] }), showActions ? (_jsxs("div", { className: cn("flex items-center gap-[var(--space-2)] pt-[var(--space-1)]", previousAction || dismissAction ? "justify-between" : "justify-end"), children: [previousAction || dismissAction ? (_jsxs("div", { className: "flex items-center gap-[var(--space-2)]", children: [dismissAction ? (_jsx(Button, { type: "button", variant: "ghost", size: "compact", onClick: dismissAction.onClick, disabled: dismissAction.disabled, loading: dismissAction.loading, loadingText: dismissAction.loadingText, "aria-label": dismissAction.ariaLabel, children: (_a = dismissAction.label) !== null && _a !== void 0 ? _a : "Skip" })) : null, previousAction ? (_jsxs(Button, { ref: previousButtonRef, type: "button", variant: "secondary", size: "compact", onClick: previousAction.onClick, disabled: previousAction.disabled, loading: previousAction.loading, loadingText: previousAction.loadingText, "aria-label": previousAction.ariaLabel, children: [_jsx(ChevronLeft, { className: "h-[var(--space-4)] w-[var(--space-4)]" }), (_b = previousAction.label) !== null && _b !== void 0 ? _b : "Previous"] })) : null] })) : null, nextAction ? (_jsxs(Button, { ref: nextButtonRef, type: "button", size: "compact", onClick: nextAction.onClick, disabled: nextAction.disabled, loading: nextAction.loading, loadingText: nextAction.loadingText, "aria-label": nextAction.ariaLabel, children: [(_c = nextAction.label) !== null && _c !== void 0 ? _c : "Next", showNextActionIcon ? (_jsx(ChevronRight, { className: "h-[var(--space-4)] w-[var(--space-4)]" })) : null] })) : null] })) : null] }) }), _jsx(GuidanceTriggerHighlight, { enabled: highlightTrigger && isOpen, focusOverlay: focusOverlay, targetRef: triggerHighlightRef })] }));
|
|
161
|
+
}
|
|
162
|
+
export { GuidanceTip };
|