@neasg/design-system 0.4.11 → 0.4.12

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.
@@ -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;
@@ -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
  }
@@ -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...", checkable = false, checkableHeader, activeField = "active", addRowLabel = "Add Row", renderRowActions, }) {
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 ? (_jsx("div", { className: "flex min-h-8 items-center", children: _jsx(InputControl, { ref: inputRef, value: editValue, placeholder: emptyPlaceholder, onChange: (event) => setEditValue(event.target.value), onClick: (event) => event.stopPropagation(), onBlur: commitEdit, onKeyDown: handleKeyDown, className: "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
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 };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export { Avatar, AvatarProfile, AvatarRoot, AvatarImage, } from "./avatar";
2
2
  export type { AvatarProps, AvatarSize, AvatarProfileProps, AvatarRootProps, } from "./avatar";
3
+ export { AvatarProfilePopover, AvatarProfilePopoverContent, AvatarProfilePopoverHeader, AvatarProfilePopoverSection, AvatarProfilePopoverFooter, AvatarProfilePopoverFooterAction, } from "./avatar-profile-popover";
4
+ export type { AvatarProfilePopoverProps, AvatarProfilePopoverContentProps, AvatarProfilePopoverHeaderProps, AvatarProfilePopoverSectionProps, AvatarProfilePopoverFooterProps, AvatarProfilePopoverFooterActionProps, } from "./avatar-profile-popover";
3
5
  export * from "./animated-icons";
4
6
  export { Alert, AlertAction, AlertConsent, alertVariants } from "./alert";
5
7
  export type { AlertActionProps, AlertConsentProps, AlertProps } from "./alert";
@@ -63,7 +65,7 @@ export type { LinkProps } from "./link";
63
65
  export { Dialog } from "./dialog";
64
66
  export type { DialogAction, DialogProps } from "./dialog";
65
67
  export { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator, CommandShortcut, } from "./command";
66
- export { CommandSearch, type CommandSearchFilter, type CommandSearchItem, type CommandSearchProps, type CommandSearchSection, type CommandSearchShowAllResultsLabel, } from "./command-search";
68
+ export { CommandSearch, type CommandSearchFilter, type CommandSearchFilterSelectDetails, type CommandSearchItem, type CommandSearchItemDetail, type CommandSearchProps, type CommandSearchSection, type CommandSearchSectionAction, type CommandSearchShowAllResultsLabel, type CommandSearchShortcutPlatform, } from "./command-search";
67
69
  export { FileUpload } from "./file-upload";
68
70
  export type { FileUploadProps } from "./file-upload";
69
71
  export { FileUploadStatusToast, showFileUploadStatusToast, } from "./file-upload-status-toast";
@@ -90,8 +92,12 @@ export { SectionNav } from "./section-nav";
90
92
  export type { SectionNavItem, SectionNavProps } from "./section-nav";
91
93
  export { GovtMasthead } from "./govt-masthead";
92
94
  export type { GovtMastheadProps } from "./govt-masthead";
95
+ export { GuidanceTip } from "./guidance-tip";
96
+ export type { GuidanceTipAction, GuidanceTipProps } from "./guidance-tip";
93
97
  export { SearchHighlight, useSearchHighlight, } from "./use-search-highlight";
94
98
  export type { SearchHighlightProps, SearchHighlightSegment, } from "./use-search-highlight";
99
+ export { clearJumpTargetHighlights, highlightJumpTarget, jumpTargetHighlightAttribute, jumpTargetHighlightClassName, jumpTargetHighlightDurationMs, jumpTargetHighlightIterations, jumpTargetHighlightSelector, } from "./jump-target-highlight";
100
+ export type { HighlightJumpTargetOptions, JumpTargetHighlightRoot, } from "./jump-target-highlight";
95
101
  export { useErrorShake } from "./use-error-shake";
96
102
  export type { UseErrorShakeOptions } from "./use-error-shake";
97
103
  export { useStickySentinel } from "./use-sticky-sentinel";
@@ -100,12 +106,14 @@ export { useViewportThreshold } from "./use-viewport-threshold";
100
106
  export type { UseViewportThresholdOptions, UseViewportThresholdReturn, } from "./use-viewport-threshold";
101
107
  export { Textarea } from "./textarea";
102
108
  export type { TextareaProps } from "./textarea";
103
- export { DraggableTabs, type DraggableTabBadge, type DraggableTabItem, type DraggableTabsProps, } from "./draggable-tabs";
109
+ export { DraggableTabs, type DraggableTabBadge, type DraggableTabGuidance, type DraggableTabItem, type DraggableTabsProps, } from "./draggable-tabs";
104
110
  export { Drawer, } from "./drawer";
105
111
  export type { DrawerAction, DrawerProps, DrawerSide } from "./drawer";
106
- export { DashboardContent, DashboardLayout, DashboardRightPanel, DashboardRightPanelBody, DashboardRightPanelHeader, DashboardRightPanelSection, DashboardRightPanelTabs, DashboardSidebar, DashboardSidebarProfile, SidebarProvider, useSidebar, } from "./layout";
107
- export type { DashboardContentProps, DashboardLayoutProps, DashboardRightPanelBodyProps, DashboardRightPanelHeaderProps, DashboardRightPanelMode, DashboardRightPanelProps, DashboardRightPanelSectionProps, DashboardRightPanelTabsProps, DashboardSidebarNavDropdownItem, DashboardSidebarNavGroup, DashboardSidebarNavItem, DashboardSidebarNavLinkItem, DashboardSidebarProfileProps, } from "./layout";
112
+ export { DashboardContent, DashboardLayout, DashboardRightPanel, DashboardRightPanelBody, DashboardRightPanelHeader, DashboardRightPanelSection, DashboardRightPanelTabs, DashboardSidebar, DashboardSidebarProfile, DashboardSidebarProfilePopover, SidebarProvider, useSidebar, } from "./layout";
113
+ export type { DashboardContentProps, DashboardLayoutProps, DashboardRightPanelBodyProps, DashboardRightPanelHeaderProps, DashboardRightPanelMode, DashboardRightPanelProps, DashboardRightPanelSectionProps, DashboardRightPanelTabsProps, DashboardSidebarNavDropdownItem, DashboardSidebarNavGroup, DashboardSidebarNavItem, DashboardSidebarNavLinkItem, DashboardSidebarProfileProps, DashboardSidebarProfilePopoverProps, } from "./layout";
108
114
  export { PageHeader } from "./page-header";
115
+ export { PageLayout, PageLayoutContext, PageLayoutControls, PageLayoutHeader, PageLayoutSection, PageLayoutStack, PageLayoutTitle, pageLayoutGapClassNames, pageLayoutStackGapClassNames, } from "./page-layout";
116
+ export type { PageLayoutGap, PageLayoutProps, PageLayoutStackGap, PageLayoutStackProps, PageLayoutTitleProps, } from "./page-layout";
109
117
  export { WorkspaceHeader } from "./workspace-header";
110
118
  export type { WorkspaceHeaderProps } from "./workspace-header";
111
119
  export { PageSection, PageSectionGroup } from "./page-section";
@@ -123,7 +131,7 @@ export type { TableColumnVisibilityMenuProps } from "./table-column-visibility";
123
131
  export { useTableSort } from "./use-table-sort";
124
132
  export type { UseTableSortOptions, UseTableSortReturn, } from "./use-table-sort";
125
133
  export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption, TableRowSkeleton, TABLE_SKELETON_PRESETS, } from "./table";
126
- export type { TableColumn, TableProps, TableRowTone, TableSortDirection, TableSortingProps, TableSkeletonConfig, TableRowSkeletonProps, } from "./table";
134
+ export type { TableColumn, TableColumnWidths, TableProps, TableRowTone, TableSortDirection, TableSortingProps, TableSkeletonConfig, TableRowSkeletonProps, } from "./table";
127
135
  export { Tabs, TabsRoot, TabWithDropdown, type TabsItem, type TabsItemLabel, type TabsItemLabelProps, type TabsProps, TabsList, TabsTrigger, TabsContent, type TabWithDropdownProps, useTabsContext, type TabDropdownSection, } from "./tabs";
128
136
  export { Tooltip, TooltipRoot, TooltipTrigger, TooltipContent, TooltipProvider, type TooltipProps, } from "./tooltip";
129
137
  export { Toaster, toast } from "./toaster";
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { Avatar, AvatarProfile, AvatarRoot, AvatarImage, } from "./avatar";
2
+ export { AvatarProfilePopover, AvatarProfilePopoverContent, AvatarProfilePopoverHeader, AvatarProfilePopoverSection, AvatarProfilePopoverFooter, AvatarProfilePopoverFooterAction, } from "./avatar-profile-popover";
2
3
  export * from "./animated-icons";
3
4
  export { Alert, AlertAction, AlertConsent, alertVariants } from "./alert";
4
5
  export { Badge, badgeVariants } from "./badge";
@@ -46,15 +47,18 @@ export { PopoverMenuContent, PopoverMenuCheckboxItem, PopoverMenuHeader, Popover
46
47
  export { SearchInput } from "./search-input";
47
48
  export { SectionNav } from "./section-nav";
48
49
  export { GovtMasthead } from "./govt-masthead";
50
+ export { GuidanceTip } from "./guidance-tip";
49
51
  export { SearchHighlight, useSearchHighlight, } from "./use-search-highlight";
52
+ export { clearJumpTargetHighlights, highlightJumpTarget, jumpTargetHighlightAttribute, jumpTargetHighlightClassName, jumpTargetHighlightDurationMs, jumpTargetHighlightIterations, jumpTargetHighlightSelector, } from "./jump-target-highlight";
50
53
  export { useErrorShake } from "./use-error-shake";
51
54
  export { useStickySentinel } from "./use-sticky-sentinel";
52
55
  export { useViewportThreshold } from "./use-viewport-threshold";
53
56
  export { Textarea } from "./textarea";
54
57
  export { DraggableTabs, } from "./draggable-tabs";
55
58
  export { Drawer, } from "./drawer";
56
- export { DashboardContent, DashboardLayout, DashboardRightPanel, DashboardRightPanelBody, DashboardRightPanelHeader, DashboardRightPanelSection, DashboardRightPanelTabs, DashboardSidebar, DashboardSidebarProfile, SidebarProvider, useSidebar, } from "./layout";
59
+ export { DashboardContent, DashboardLayout, DashboardRightPanel, DashboardRightPanelBody, DashboardRightPanelHeader, DashboardRightPanelSection, DashboardRightPanelTabs, DashboardSidebar, DashboardSidebarProfile, DashboardSidebarProfilePopover, SidebarProvider, useSidebar, } from "./layout";
57
60
  export { PageHeader } from "./page-header";
61
+ export { PageLayout, PageLayoutContext, PageLayoutControls, PageLayoutHeader, PageLayoutSection, PageLayoutStack, PageLayoutTitle, pageLayoutGapClassNames, pageLayoutStackGapClassNames, } from "./page-layout";
58
62
  export { WorkspaceHeader } from "./workspace-header";
59
63
  export { PageSection, PageSectionGroup } from "./page-section";
60
64
  export { Typography, typographyVariants } from "./typography";
@@ -0,0 +1,13 @@
1
+ export declare const jumpTargetHighlightAttribute = "data-jump-target-highlight";
2
+ export declare const jumpTargetHighlightSelector = "[data-jump-target-highlight=\"true\"]";
3
+ export declare const jumpTargetHighlightDurationMs = 420;
4
+ export declare const jumpTargetHighlightIterations = 2;
5
+ export declare const jumpTargetHighlightClassName = "transition-[background-color,box-shadow,border-color] duration-150 data-[jump-target-highlight=true]:border-ring/40 data-[jump-target-highlight=true]:bg-primary/5 data-[jump-target-highlight=true]:shadow-[0_0_0_2px_hsl(var(--ring)_/_0.16)] motion-reduce:transition-none";
6
+ export type JumpTargetHighlightRoot = Document | DocumentFragment | Element;
7
+ export interface HighlightJumpTargetOptions {
8
+ root?: JumpTargetHighlightRoot;
9
+ durationMs?: number;
10
+ iterations?: number;
11
+ }
12
+ export declare function clearJumpTargetHighlights(root?: JumpTargetHighlightRoot): void;
13
+ export declare function highlightJumpTarget(target: HTMLElement | null | undefined, { root, durationMs, iterations, }?: HighlightJumpTargetOptions): () => void;
@@ -0,0 +1,95 @@
1
+ export const jumpTargetHighlightAttribute = "data-jump-target-highlight";
2
+ export const jumpTargetHighlightSelector = `[${jumpTargetHighlightAttribute}="true"]`;
3
+ export const jumpTargetHighlightDurationMs = 420;
4
+ export const jumpTargetHighlightIterations = 2;
5
+ export const jumpTargetHighlightClassName = "transition-[background-color,box-shadow,border-color] duration-150 data-[jump-target-highlight=true]:border-ring/40 data-[jump-target-highlight=true]:bg-primary/5 data-[jump-target-highlight=true]:shadow-[0_0_0_2px_hsl(var(--ring)_/_0.16)] motion-reduce:transition-none";
6
+ const jumpTargetHighlightIdDataKey = "jumpTargetHighlightId";
7
+ const activeJumpTargetHighlights = new WeakMap();
8
+ function getJumpTargetHighlightTotalMs(durationMs, iterations) {
9
+ return durationMs * iterations;
10
+ }
11
+ function prefersReducedMotion(ownerWindow) {
12
+ return (typeof ownerWindow.matchMedia === "function" &&
13
+ ownerWindow.matchMedia("(prefers-reduced-motion: reduce)").matches);
14
+ }
15
+ function clearJumpTargetElement(element) {
16
+ const activeHighlight = activeJumpTargetHighlights.get(element);
17
+ if (activeHighlight) {
18
+ activeJumpTargetHighlights.delete(element);
19
+ if (activeHighlight.timeoutId !== undefined) {
20
+ activeHighlight.ownerWindow.clearTimeout(activeHighlight.timeoutId);
21
+ }
22
+ if (activeHighlight.animation) {
23
+ activeHighlight.animation.onfinish = null;
24
+ activeHighlight.animation.oncancel = null;
25
+ activeHighlight.animation.cancel();
26
+ }
27
+ }
28
+ element.removeAttribute(jumpTargetHighlightAttribute);
29
+ delete element.dataset[jumpTargetHighlightIdDataKey];
30
+ }
31
+ export function clearJumpTargetHighlights(root) {
32
+ const highlightRoot = root !== null && root !== void 0 ? root : (typeof document === "undefined" ? undefined : document);
33
+ if (!highlightRoot) {
34
+ return;
35
+ }
36
+ highlightRoot
37
+ .querySelectorAll(jumpTargetHighlightSelector)
38
+ .forEach(clearJumpTargetElement);
39
+ }
40
+ export function highlightJumpTarget(target, { root, durationMs = jumpTargetHighlightDurationMs, iterations = jumpTargetHighlightIterations, } = {}) {
41
+ if (!target) {
42
+ return () => undefined;
43
+ }
44
+ const ownerWindow = target.ownerDocument.defaultView;
45
+ if (!ownerWindow) {
46
+ return () => undefined;
47
+ }
48
+ clearJumpTargetHighlights(root !== null && root !== void 0 ? root : target.ownerDocument);
49
+ const highlightId = `${Date.now()}`;
50
+ const clearHighlight = () => {
51
+ if (target.dataset[jumpTargetHighlightIdDataKey] !== highlightId) {
52
+ return;
53
+ }
54
+ clearJumpTargetElement(target);
55
+ };
56
+ target.dataset[jumpTargetHighlightIdDataKey] = highlightId;
57
+ target.setAttribute(jumpTargetHighlightAttribute, "true");
58
+ if (!prefersReducedMotion(ownerWindow) &&
59
+ typeof target.animate === "function") {
60
+ const animation = target.animate([
61
+ {
62
+ backgroundColor: "hsl(var(--primary) / 0.04)",
63
+ borderColor: "hsl(var(--ring) / 0.35)",
64
+ boxShadow: "0 0 0 2px hsl(var(--ring) / 0.12)",
65
+ },
66
+ {
67
+ backgroundColor: "hsl(var(--primary) / 0.12)",
68
+ borderColor: "hsl(var(--ring) / 0.6)",
69
+ boxShadow: "0 0 0 3px hsl(var(--ring) / 0.24)",
70
+ },
71
+ {
72
+ backgroundColor: "hsl(var(--primary) / 0.04)",
73
+ borderColor: "hsl(var(--ring) / 0.35)",
74
+ boxShadow: "0 0 0 2px hsl(var(--ring) / 0.12)",
75
+ },
76
+ ], {
77
+ duration: durationMs,
78
+ easing: "ease-in-out",
79
+ iterations,
80
+ });
81
+ activeJumpTargetHighlights.set(target, {
82
+ animation,
83
+ ownerWindow,
84
+ });
85
+ animation.onfinish = clearHighlight;
86
+ animation.oncancel = clearHighlight;
87
+ return clearHighlight;
88
+ }
89
+ const timeoutId = ownerWindow.setTimeout(clearHighlight, getJumpTargetHighlightTotalMs(durationMs, iterations));
90
+ activeJumpTargetHighlights.set(target, {
91
+ ownerWindow,
92
+ timeoutId,
93
+ });
94
+ return clearHighlight;
95
+ }
package/dist/layout.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { type AvatarProfileProps } from "./avatar";
3
+ import { type AvatarProfilePopoverProps } from "./avatar-profile-popover";
3
4
  import { type CountBadgeProps } from "./count-badge";
4
5
  import { type TabsProps } from "./tabs";
5
6
  export type DashboardRightPanelMode = "responsive" | "persistent";
@@ -32,6 +33,14 @@ export interface DashboardLayoutProps extends React.HTMLAttributes<HTMLDivElemen
32
33
  rightPanelMode?: DashboardRightPanelMode;
33
34
  /** Allows users to drag-resize a persistent right panel. */
34
35
  rightPanelResizable?: boolean;
36
+ /** Shows an edge control that collapses and reopens a persistent right panel. */
37
+ rightPanelCollapsible?: boolean;
38
+ /** Controlled collapsed state for a persistent right panel. */
39
+ rightPanelCollapsed?: boolean;
40
+ /** Initial collapsed state for an uncontrolled persistent right panel. */
41
+ defaultRightPanelCollapsed?: boolean;
42
+ /** Called when the persistent right panel collapse state changes. */
43
+ onRightPanelCollapsedChange?: (collapsed: boolean) => void;
35
44
  /** Initial persistent right panel width in pixels when resizing is enabled. */
36
45
  rightPanelDefaultWidth?: number;
37
46
  /** Minimum persistent right panel width in pixels when resizing is enabled. */
@@ -46,7 +55,7 @@ export interface DashboardLayoutProps extends React.HTMLAttributes<HTMLDivElemen
46
55
  /** Initial sidebar state on desktop before the user toggles it. */
47
56
  defaultSidebarCollapsed?: boolean;
48
57
  }
49
- declare function DashboardLayout({ className, masthead, prototypeBanner, sidebar, rightPanel, surfaceClassName, contentClassName, rightPanelClassName, rightPanelFallbackTrigger, rightPanelMode, rightPanelResizable, rightPanelDefaultWidth, rightPanelMinWidth, rightPanelMaxWidth, rightPanelAutoCollapseSidebarBreakpoint, defaultSidebarCollapsed, children, style, ...props }: DashboardLayoutProps): import("react/jsx-runtime").JSX.Element;
58
+ declare function DashboardLayout({ className, masthead, prototypeBanner, sidebar, rightPanel, surfaceClassName, contentClassName, rightPanelClassName, rightPanelFallbackTrigger, rightPanelMode, rightPanelResizable, rightPanelCollapsible, rightPanelCollapsed, defaultRightPanelCollapsed, onRightPanelCollapsedChange, rightPanelDefaultWidth, rightPanelMinWidth, rightPanelMaxWidth, rightPanelAutoCollapseSidebarBreakpoint, defaultSidebarCollapsed, children, style, ...props }: DashboardLayoutProps): import("react/jsx-runtime").JSX.Element;
50
59
  export interface DashboardSidebarProps extends React.HTMLAttributes<HTMLElement> {
51
60
  logo?: React.ReactNode | ((context: {
52
61
  isCollapsed: boolean;
@@ -99,12 +108,21 @@ export interface DashboardSidebarNavGroup {
99
108
  label?: string;
100
109
  items: DashboardSidebarNavItem[];
101
110
  }
111
+ type DashboardSidebarProfileStatus = "online" | "busy" | "offline" | "none";
112
+ type DashboardSidebarProfilePopoverPlacementProps = Pick<AvatarProfilePopoverProps, "align" | "alignOffset" | "avoidCollisions" | "collisionPadding" | "defaultOpen" | "modal" | "onOpenChange" | "open" | "side" | "sideOffset">;
102
113
  export interface DashboardSidebarProfileProps extends Omit<AvatarProfileProps, "className" | "avatarClassName" | "fallbackClassName" | "avatarOverlay" | "imageClassName" | "contentClassName" | "nameClassName" | "roleClassName" | "topInfoClassName" | "bottomInfoClassName" | "rolePlacement"> {
103
114
  isCollapsed?: boolean;
104
115
  rolePlacement?: "above" | "below";
105
- status?: "online" | "busy" | "offline" | "none";
116
+ status?: DashboardSidebarProfileStatus;
106
117
  }
107
- declare function DashboardSidebarProfile({ isCollapsed, status, name, role, rolePlacement, size, ...props }: DashboardSidebarProfileProps): import("react/jsx-runtime").JSX.Element;
118
+ export interface DashboardSidebarProfilePopoverProps extends Omit<DashboardSidebarProfileProps, "children">, DashboardSidebarProfilePopoverPlacementProps {
119
+ children: React.ReactNode;
120
+ contentClassName?: string;
121
+ triggerAriaLabel?: string;
122
+ triggerClassName?: string;
123
+ }
124
+ declare function DashboardSidebarProfile({ isCollapsed, ...props }: DashboardSidebarProfileProps): import("react/jsx-runtime").JSX.Element;
125
+ declare function DashboardSidebarProfilePopover({ align, alignOffset, avoidCollisions, children, collisionPadding, contentClassName, defaultOpen, modal, onOpenChange, open, side, sideOffset, triggerAriaLabel, triggerClassName, ...profileProps }: DashboardSidebarProfilePopoverProps): import("react/jsx-runtime").JSX.Element;
108
126
  declare function DashboardSidebar({ className, children, logo, search, navGroups, footerItems, footer, toggleAriaLabel, ...props }: DashboardSidebarProps): import("react/jsx-runtime").JSX.Element;
109
127
  export interface DashboardContentProps extends React.HTMLAttributes<HTMLElement> {
110
128
  hasSidebar?: boolean;
@@ -113,6 +131,10 @@ export interface DashboardContentProps extends React.HTMLAttributes<HTMLElement>
113
131
  rightPanelFallbackTrigger?: React.ReactNode;
114
132
  rightPanelMode?: DashboardRightPanelMode;
115
133
  rightPanelResizable?: boolean;
134
+ rightPanelCollapsible?: boolean;
135
+ rightPanelCollapsed?: boolean;
136
+ defaultRightPanelCollapsed?: boolean;
137
+ onRightPanelCollapsedChange?: (collapsed: boolean) => void;
116
138
  rightPanelDefaultWidth?: number;
117
139
  rightPanelMinWidth?: number;
118
140
  rightPanelMaxWidth?: number;
@@ -148,5 +170,5 @@ declare function DashboardRightPanelTabs({ className, listWrapperClassName, pane
148
170
  declare function DashboardRightPanel({ className, ...props }: DashboardRightPanelProps): import("react/jsx-runtime").JSX.Element;
149
171
  declare function DashboardRightPanelHeader({ className, title, subtitle, badge, action, children, ...props }: DashboardRightPanelHeaderProps): import("react/jsx-runtime").JSX.Element;
150
172
  declare function DashboardRightPanelBody({ className, contentClassName, footer, footerClassName, children, ...props }: DashboardRightPanelBodyProps): import("react/jsx-runtime").JSX.Element;
151
- declare function DashboardContent({ className, hasSidebar, rightPanel, rightPanelClassName, rightPanelFallbackTrigger, rightPanelMode, rightPanelResizable, rightPanelDefaultWidth, rightPanelMinWidth, rightPanelMaxWidth, rightPanelAutoCollapseSidebarBreakpoint, surfaceClassName, children, ...props }: DashboardContentProps): import("react/jsx-runtime").JSX.Element;
152
- export { DashboardLayout, DashboardSidebar, DashboardSidebarProfile, DashboardContent, DashboardRightPanel, DashboardRightPanelHeader, DashboardRightPanelBody, DashboardRightPanelSection, DashboardRightPanelTabs, };
173
+ declare function DashboardContent({ className, hasSidebar, rightPanel, rightPanelClassName, rightPanelFallbackTrigger, rightPanelMode, rightPanelResizable, rightPanelCollapsible, rightPanelCollapsed, defaultRightPanelCollapsed, onRightPanelCollapsedChange, rightPanelDefaultWidth, rightPanelMinWidth, rightPanelMaxWidth, rightPanelAutoCollapseSidebarBreakpoint, surfaceClassName, children, ...props }: DashboardContentProps): import("react/jsx-runtime").JSX.Element;
174
+ export { DashboardLayout, DashboardSidebar, DashboardSidebarProfile, DashboardSidebarProfilePopover, DashboardContent, DashboardRightPanel, DashboardRightPanelHeader, DashboardRightPanelBody, DashboardRightPanelSection, DashboardRightPanelTabs, };