@neasg/design-system 0.4.33 → 0.4.95
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/dist/activity-card.d.ts +29 -0
- package/dist/activity-card.js +65 -0
- package/dist/autocomplete.d.ts +84 -0
- package/dist/autocomplete.js +267 -0
- package/dist/avatar.d.ts +3 -1
- package/dist/avatar.js +27 -10
- package/dist/badge.js +1 -1
- package/dist/button.d.ts +1 -1
- package/dist/button.js +1 -0
- package/dist/calendar.js +1 -1
- package/dist/card.d.ts +1 -1
- package/dist/card.js +1 -1
- package/dist/compound-search-input.d.ts +8 -0
- package/dist/compound-search-input.js +9 -0
- package/dist/date-input.js +3 -3
- package/dist/dialog.d.ts +3 -1
- package/dist/dialog.js +6 -2
- package/dist/draggable-tabs.js +9 -8
- package/dist/index.d.ts +12 -4
- package/dist/index.js +6 -2
- package/dist/label-value-grid.js +6 -4
- package/dist/layout.d.ts +39 -6
- package/dist/layout.js +118 -42
- package/dist/lib/utils.js +1 -0
- package/dist/message-item.d.ts +23 -3
- package/dist/message-item.js +115 -16
- package/dist/notification.d.ts +14 -3
- package/dist/notification.js +32 -7
- package/dist/popover-menu.d.ts +4 -2
- package/dist/popover-menu.js +65 -5
- package/dist/popover.d.ts +5 -2
- package/dist/popover.js +3 -3
- package/dist/rich-text-editor.d.ts +2 -1
- package/dist/rich-text-editor.js +28 -3
- package/dist/routing-timeline.d.ts +7 -2
- package/dist/routing-timeline.js +67 -29
- package/dist/search-input.d.ts +4 -0
- package/dist/search-input.js +2 -2
- package/dist/select-primitive.js +1 -1
- package/dist/select.d.ts +3 -1
- package/dist/select.js +9 -4
- package/dist/status-dot.d.ts +13 -0
- package/dist/status-dot.js +19 -0
- package/dist/styles.css +0 -19
- package/dist/table-column-filter.js +7 -5
- package/dist/table.d.ts +9 -1
- package/dist/table.js +30 -12
- package/dist/tabs.d.ts +1 -1
- package/dist/tabs.js +6 -4
- package/dist/textarea.d.ts +1 -0
- package/dist/textarea.js +3 -2
- package/dist/theme-switcher.d.ts +3 -1
- package/dist/theme-switcher.js +8 -2
- package/package.json +5 -1
package/dist/notification.d.ts
CHANGED
|
@@ -25,6 +25,14 @@ export interface NotificationItemData {
|
|
|
25
25
|
id: string;
|
|
26
26
|
title: React.ReactNode;
|
|
27
27
|
description?: React.ReactNode;
|
|
28
|
+
/** Person or system responsible for the event. */
|
|
29
|
+
actor?: React.ReactNode;
|
|
30
|
+
/** Optional status or category displayed in an activity-card header. */
|
|
31
|
+
badge?: React.ReactNode;
|
|
32
|
+
/** Per-item activity-card density. Falls back to the list density. */
|
|
33
|
+
activityDensity?: "compact" | "default";
|
|
34
|
+
/** `plain` matches activity events whose semantic glyph needs no tone tile. */
|
|
35
|
+
iconAppearance?: "plain" | "toned";
|
|
28
36
|
meta?: React.ReactNode;
|
|
29
37
|
timestamp?: NotificationTimestamp;
|
|
30
38
|
dateTime?: string;
|
|
@@ -38,18 +46,21 @@ export interface NotificationItemData {
|
|
|
38
46
|
export interface NotificationItemProps extends Omit<React.HTMLAttributes<HTMLElement>, "children" | "onSelect" | "title"> {
|
|
39
47
|
notification: NotificationItemData;
|
|
40
48
|
density?: "compact" | "default";
|
|
49
|
+
variant?: "row" | "card";
|
|
41
50
|
labelLines?: 1 | 2;
|
|
42
51
|
descriptionLines?: 1 | 2 | 3;
|
|
43
52
|
unreadLabel?: React.ReactNode;
|
|
53
|
+
actorLabel?: React.ReactNode;
|
|
44
54
|
onSelect?: (notification: NotificationItemData) => void;
|
|
45
55
|
}
|
|
46
56
|
export interface NotificationListProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
|
|
47
57
|
notifications: NotificationItemData[];
|
|
48
58
|
density?: "compact" | "default";
|
|
49
|
-
variant?: "bordered" | "plain";
|
|
59
|
+
variant?: "bordered" | "plain" | "cards";
|
|
50
60
|
labelLines?: 1 | 2;
|
|
51
61
|
descriptionLines?: 1 | 2 | 3;
|
|
52
62
|
unreadLabel?: React.ReactNode;
|
|
63
|
+
actorLabel?: React.ReactNode;
|
|
53
64
|
emptyState?: React.ReactNode;
|
|
54
65
|
itemClassName?: string;
|
|
55
66
|
onNotificationClick?: (notification: NotificationItemData) => void;
|
|
@@ -83,7 +94,7 @@ export interface NotificationTriggerProps extends Omit<ButtonProps, "children">
|
|
|
83
94
|
}
|
|
84
95
|
declare function formatNotificationTimestamp(timestamp: NotificationTimestamp, { now, locale }?: FormatNotificationTimestampOptions): string;
|
|
85
96
|
declare const NotificationTrigger: React.ForwardRefExoticComponent<NotificationTriggerProps & React.RefAttributes<HTMLButtonElement>>;
|
|
86
|
-
declare function NotificationItem({ notification, density, labelLines, descriptionLines, unreadLabel, className, onClick, onSelect, ...props }: NotificationItemProps): import("react/jsx-runtime").JSX.Element;
|
|
87
|
-
declare function NotificationList({ notifications, density, variant, labelLines, descriptionLines, unreadLabel, emptyState, itemClassName, className, onNotificationClick, ...props }: NotificationListProps): import("react/jsx-runtime").JSX.Element;
|
|
97
|
+
declare function NotificationItem({ notification, density, variant, labelLines, descriptionLines, unreadLabel, actorLabel, className, onClick, onSelect, ...props }: NotificationItemProps): import("react/jsx-runtime").JSX.Element;
|
|
98
|
+
declare function NotificationList({ notifications, density, variant, labelLines, descriptionLines, unreadLabel, actorLabel, emptyState, itemClassName, className, onNotificationClick, ...props }: NotificationListProps): import("react/jsx-runtime").JSX.Element;
|
|
88
99
|
declare function NotificationPopover({ trigger, notifications, title, filter, defaultFilter, onFilterChange, showFilterTabs, height, minHeight, maxHeight, emptyState, listClassName, itemClassName, unreadLabel, markAllAsReadLabel, onNotificationClick, onMarkAllAsRead, contentClassName, align, ...popoverProps }: NotificationPopoverProps): import("react/jsx-runtime").JSX.Element;
|
|
89
100
|
export { formatNotificationTimestamp, NotificationItem, NotificationList, NotificationPopover, NotificationTrigger, };
|
package/dist/notification.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Bell, ClipboardCheck, FileText, MessageSquare, UserCheck, } from "lucide-react";
|
|
5
|
+
import { ActivityCard, ActivityCardActor, ActivityCardContent, } from "./activity-card";
|
|
5
6
|
import { Button } from "./button";
|
|
6
7
|
import { CountBadge } from "./count-badge";
|
|
7
8
|
import { EmptyState } from "./empty-state";
|
|
@@ -118,8 +119,8 @@ function getNotificationTone(notification) {
|
|
|
118
119
|
var _a, _b;
|
|
119
120
|
return ((_a = notification.tone) !== null && _a !== void 0 ? _a : notificationKindTones[(_b = notification.kind) !== null && _b !== void 0 ? _b : "system"]);
|
|
120
121
|
}
|
|
121
|
-
function NotificationItem({ notification, density = "default", labelLines = 2, descriptionLines = 2, unreadLabel = "Unread", className, onClick, onSelect, ...props }) {
|
|
122
|
-
var _a, _b, _c;
|
|
122
|
+
function NotificationItem({ notification, density = "default", variant = "row", labelLines = 2, descriptionLines = 2, unreadLabel = "Unread", actorLabel = "By", className, onClick, onSelect, ...props }) {
|
|
123
|
+
var _a, _b, _c, _d;
|
|
123
124
|
const Icon = getNotificationIcon(notification);
|
|
124
125
|
const tone = getNotificationTone(notification);
|
|
125
126
|
const isUnread = Boolean(notification.unread);
|
|
@@ -141,7 +142,29 @@ function NotificationItem({ notification, density = "default", labelLines = 2, d
|
|
|
141
142
|
onSelect === null || onSelect === void 0 ? void 0 : onSelect(notification);
|
|
142
143
|
}
|
|
143
144
|
};
|
|
144
|
-
|
|
145
|
+
if (variant === "card") {
|
|
146
|
+
const activityDensity = (_d = notification.activityDensity) !== null && _d !== void 0 ? _d : density;
|
|
147
|
+
const hasCardContent = notification.description !== undefined ||
|
|
148
|
+
notification.actor !== undefined ||
|
|
149
|
+
hasActions;
|
|
150
|
+
const activityElement = notification.href && !hasActions
|
|
151
|
+
? "a"
|
|
152
|
+
: isInteractive
|
|
153
|
+
? "button"
|
|
154
|
+
: "article";
|
|
155
|
+
const unreadIndicator = isUnread && unreadLabel !== null ? (_jsx("span", { "data-slot": "notification-unread-indicator", className: "block size-2.5 rounded-full bg-primary ring-2 ring-background", children: _jsx("span", { className: "sr-only", children: unreadLabel }) })) : null;
|
|
156
|
+
const cardTrailing = notification.badge || unreadIndicator ? (_jsxs("span", { className: "flex items-center gap-[var(--space-2)]", children: [notification.badge, unreadIndicator] })) : null;
|
|
157
|
+
return (_jsx(ActivityCard, { as: activityElement, "data-slot": "notification-item", "data-unread": isUnread ? "" : undefined, density: activityDensity, icon: _jsx(Icon, { className: activityDensity === "compact" ? "size-3.5" : "size-[24px]" }), iconTone: notification.iconAppearance === "plain" ? undefined : tone, title: notification.title, titleLines: labelLines, meta: meta, trailing: cardTrailing, href: notification.href, className: cn("group relative w-full text-left outline-none transition-colors", isUnread && "bg-primary/[0.045]", hasHoverSurface && "hover:bg-accent/70", isInteractive &&
|
|
158
|
+
"cursor-pointer focus-visible:z-10 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset", className), onClick: isInteractive ? handleClick : undefined, ...props, children: hasCardContent ? (_jsxs(ActivityCardContent, { density: activityDensity, children: [notification.description !== undefined ? (_jsx(Typography, { as: "div", variant: "bodySm", className: cn("text-left", isUnread ? "text-foreground/75" : "text-muted-foreground", descriptionLineClasses[descriptionLines]), children: notification.description })) : null, notification.actor !== undefined ? (_jsx(ActivityCardActor, { actor: notification.actor, label: actorLabel })) : null, hasActions ? (_jsx("div", { className: "flex flex-wrap gap-2", children: actions.map((action, index) => {
|
|
159
|
+
var _a, _b;
|
|
160
|
+
return (_jsx(Button, { type: "button", variant: (_a = action.variant) !== null && _a !== void 0 ? _a : (index === 0 ? "outline" : "default"), size: "sm", disabled: action.disabled, className: cn("min-w-24", action.className), onClick: (event) => {
|
|
161
|
+
var _a;
|
|
162
|
+
event.stopPropagation();
|
|
163
|
+
(_a = action.onClick) === null || _a === void 0 ? void 0 : _a.call(action, notification, event);
|
|
164
|
+
}, children: action.label }, (_b = action.id) !== null && _b !== void 0 ? _b : index));
|
|
165
|
+
}) })) : null] })) : null }));
|
|
166
|
+
}
|
|
167
|
+
const content = (_jsxs(_Fragment, { children: [_jsx("span", { "aria-hidden": "true", className: cn("mt-0.5 flex shrink-0 items-center justify-center rounded-control border", density === "compact" ? "h-7 w-7" : "h-8 w-8", notificationToneClasses[tone]), children: _jsx(Icon, { className: density === "compact" ? "h-3.5 w-3.5" : "h-4 w-4" }) }), _jsxs("span", { className: "min-w-0 flex-1", children: [_jsxs("span", { className: "grid min-w-0 grid-cols-[minmax(0,1fr)_auto] items-start gap-3", children: [_jsx(Typography, { as: "span", variant: "bodySm", className: cn("min-w-0 text-left text-foreground", isUnread ? "font-semibold" : "font-medium", labelLineClasses[labelLines]), children: notification.title }), meta || (isUnread && unreadLabel !== null) ? (_jsxs("span", { className: "flex min-w-0 shrink-0 items-center justify-end gap-1.5 text-right", children: [meta ? (_jsx(Typography, { as: "span", variant: "caption", className: "shrink-0 whitespace-nowrap text-muted-foreground/70", children: meta })) : null, isUnread && unreadLabel !== null ? (_jsx("span", { "data-slot": "notification-unread-indicator", className: "mt-1 h-2.5 w-2.5 shrink-0 rounded-full bg-primary ring-2 ring-background", children: _jsx("span", { className: "sr-only", children: unreadLabel }) })) : null] })) : null] }), notification.description !== undefined ? (_jsx(Typography, { as: "span", variant: "caption", className: cn("mt-1.5 block text-left", isUnread ? "text-foreground/75" : "text-muted-foreground", descriptionLineClasses[descriptionLines]), children: notification.description })) : null, notification.actor !== undefined ? (_jsxs(Typography, { as: "span", variant: "bodySm", className: "mt-2 block text-left font-semibold text-foreground", children: [actorLabel, " ", notification.actor] })) : null, hasActions ? (_jsx("span", { className: "mt-4 flex flex-wrap gap-2", children: actions.map((action, index) => {
|
|
145
168
|
var _a, _b;
|
|
146
169
|
return (_jsx(Button, { type: "button", variant: (_a = action.variant) !== null && _a !== void 0 ? _a : (index === 0 ? "outline" : "default"), size: "sm", disabled: action.disabled, className: cn("min-w-24", action.className), onClick: (event) => {
|
|
147
170
|
var _a;
|
|
@@ -149,7 +172,7 @@ function NotificationItem({ notification, density = "default", labelLines = 2, d
|
|
|
149
172
|
(_a = action.onClick) === null || _a === void 0 ? void 0 : _a.call(action, notification, event);
|
|
150
173
|
}, children: action.label }, (_b = action.id) !== null && _b !== void 0 ? _b : index));
|
|
151
174
|
}) })) : null] })] }));
|
|
152
|
-
const itemClassName = cn("group relative flex w-full min-w-0 items-start gap-3 overflow-hidden
|
|
175
|
+
const itemClassName = cn("group relative flex w-full min-w-0 items-start gap-3 overflow-hidden bg-card text-left text-card-foreground outline-none transition-colors", "border-b border-border/70 last:border-b-0", density === "compact" ? "px-3 py-3" : "px-4 py-4", isUnread && "bg-primary/[0.045]", hasHoverSurface && "hover:bg-accent/70", isInteractive &&
|
|
153
176
|
"cursor-pointer focus-visible:z-10 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset", className);
|
|
154
177
|
if (notification.href && !hasActions) {
|
|
155
178
|
return (_jsx("a", { href: notification.href, "data-slot": "notification-item", "data-unread": isUnread ? "" : undefined, className: itemClassName, onClick: handleClick, ...props, children: content }));
|
|
@@ -159,11 +182,13 @@ function NotificationItem({ notification, density = "default", labelLines = 2, d
|
|
|
159
182
|
}
|
|
160
183
|
return (_jsx("div", { "data-slot": "notification-item", "data-unread": isUnread ? "" : undefined, className: itemClassName, ...props, children: content }));
|
|
161
184
|
}
|
|
162
|
-
function NotificationList({ notifications, density = "default", variant = "bordered", labelLines = 2, descriptionLines = 2, unreadLabel = "Unread", emptyState, itemClassName, className, onNotificationClick, ...props }) {
|
|
185
|
+
function NotificationList({ notifications, density = "default", variant = "bordered", labelLines = 2, descriptionLines = 2, unreadLabel = "Unread", actorLabel = "By", emptyState, itemClassName, className, onNotificationClick, ...props }) {
|
|
163
186
|
const hasNotifications = notifications.length > 0;
|
|
164
187
|
const resolvedEmptyState = emptyState !== null && emptyState !== void 0 ? emptyState : (_jsx(EmptyState, { message: "No notifications", description: "New updates will appear here.", icon: Bell }));
|
|
165
|
-
return (_jsx("div", { "data-slot": "notification-list", className: cn("min-w-0
|
|
166
|
-
?
|
|
188
|
+
return (_jsx("div", { "data-slot": "notification-list", className: cn("min-w-0 text-card-foreground", !hasNotifications && "flex min-h-full flex-col", variant === "cards"
|
|
189
|
+
? "space-y-[var(--space-4)] bg-transparent"
|
|
190
|
+
: "overflow-hidden bg-card", variant === "bordered" && "rounded-surface border border-border/70", className), ...props, children: hasNotifications
|
|
191
|
+
? notifications.map((notification) => (_jsx(NotificationItem, { notification: notification, density: density, variant: variant === "cards" ? "card" : "row", labelLines: labelLines, descriptionLines: descriptionLines, unreadLabel: unreadLabel, actorLabel: actorLabel, className: itemClassName, onSelect: onNotificationClick }, notification.id)))
|
|
167
192
|
: (_jsx("div", { className: "flex min-h-0 flex-1 flex-col justify-center", children: resolvedEmptyState })) }));
|
|
168
193
|
}
|
|
169
194
|
function NotificationPopover({ trigger, notifications, title = "Notifications", filter, defaultFilter = "all", onFilterChange, showFilterTabs = true, height = "h-[36rem]", minHeight = "min-h-[36rem]", maxHeight = "max-h-[36rem]", emptyState, listClassName, itemClassName, unreadLabel = "Unread", markAllAsReadLabel = "Mark all as read", onNotificationClick, onMarkAllAsRead, contentClassName, align = "end", ...popoverProps }) {
|
package/dist/popover-menu.d.ts
CHANGED
|
@@ -17,8 +17,10 @@ export interface PopoverMenuSectionProps extends React.HTMLAttributes<HTMLDivEle
|
|
|
17
17
|
declare function PopoverMenuSection({ className, children, ...props }: PopoverMenuSectionProps): import("react/jsx-runtime").JSX.Element;
|
|
18
18
|
export interface PopoverMenuItemProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
19
19
|
active?: boolean;
|
|
20
|
+
/** Communicates destructive actions without changing menu-item sizing. */
|
|
21
|
+
tone?: "default" | "destructive";
|
|
20
22
|
}
|
|
21
|
-
declare function PopoverMenuItem({ className, children, active, type, ...props }: PopoverMenuItemProps): import("react/jsx-runtime").JSX.Element;
|
|
23
|
+
declare function PopoverMenuItem({ className, children, active, tone, type, ...props }: PopoverMenuItemProps): import("react/jsx-runtime").JSX.Element;
|
|
22
24
|
export interface PopoverMenuCheckboxItemProps extends Omit<CheckboxProps, "className" | "label"> {
|
|
23
25
|
description?: React.ReactNode;
|
|
24
26
|
className?: string;
|
|
@@ -52,7 +54,7 @@ declare const PopoverMenuSearch: React.ForwardRefExoticComponent<Omit<PopoverMen
|
|
|
52
54
|
export interface PopoverMenuScrollAreaProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
53
55
|
maxHeight?: string;
|
|
54
56
|
}
|
|
55
|
-
declare function PopoverMenuScrollArea({ className, children, maxHeight, ...props }: PopoverMenuScrollAreaProps): import("react/jsx-runtime").JSX.Element;
|
|
57
|
+
declare function PopoverMenuScrollArea({ className, children, maxHeight, onScroll, ...props }: PopoverMenuScrollAreaProps): import("react/jsx-runtime").JSX.Element;
|
|
56
58
|
export interface PopoverMenuViewProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
57
59
|
direction?: "forward" | "backward" | "none";
|
|
58
60
|
viewKey?: React.Key;
|
package/dist/popover-menu.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import { Check } from "lucide-react";
|
|
4
|
+
import { Check, ChevronUp } from "lucide-react";
|
|
5
|
+
import { ChevronDownIcon } from "./animated-icons/chevron-down";
|
|
5
6
|
import { Button } from "./button";
|
|
6
7
|
import { Checkbox } from "./checkbox";
|
|
7
8
|
import { Input } from "./input";
|
|
@@ -20,9 +21,10 @@ function PopoverMenuHeader({ className, children, ...props }) {
|
|
|
20
21
|
function PopoverMenuSection({ className, children, ...props }) {
|
|
21
22
|
return (_jsx("div", { className: cn("space-y-0", className), ...props, children: children }));
|
|
22
23
|
}
|
|
23
|
-
function PopoverMenuItem({ className, children, active, type = "button", ...props }) {
|
|
24
|
+
function PopoverMenuItem({ className, children, active, tone = "default", type = "button", ...props }) {
|
|
24
25
|
return (_jsxs(Button, { type: type, variant: "ghost", "data-slot": "popover-menu-item", className: cn("h-auto min-h-8 w-full justify-start gap-2 px-2 py-2 text-left font-normal whitespace-normal aria-[selected=true]:bg-highlight aria-[selected=true]:text-highlight-foreground aria-[selected=true]:[--muted-foreground:var(--instructional-foreground)] aria-[selected=true]:hover:bg-highlight/80 aria-[selected=true]:hover:text-highlight-foreground aria-[selected=true]:focus-visible:bg-highlight/80 aria-[selected=true]:focus-visible:text-highlight-foreground", popoverMenuItemFocusClassName, active &&
|
|
25
|
-
"bg-highlight text-highlight-foreground [--muted-foreground:var(--instructional-foreground)] hover:bg-highlight/80 hover:text-highlight-foreground focus-visible:bg-highlight/80 focus-visible:text-highlight-foreground",
|
|
26
|
+
"bg-highlight text-highlight-foreground [--muted-foreground:var(--instructional-foreground)] hover:bg-highlight/80 hover:text-highlight-foreground focus-visible:bg-highlight/80 focus-visible:text-highlight-foreground", tone === "destructive" &&
|
|
27
|
+
"text-destructive hover:bg-destructive/10 hover:text-destructive focus-visible:bg-destructive/10 focus-visible:text-destructive", className), ...props, children: [children, active ? _jsx(Check, { "aria-hidden": "true", className: "ml-auto h-4 w-4 shrink-0" }) : null] }));
|
|
26
28
|
}
|
|
27
29
|
function PopoverMenuCheckboxItem({ className, children, checked = false, description, disabled, onChange, readOnly, ...props }) {
|
|
28
30
|
return (_jsxs("label", { className: cn("flex h-auto min-h-8 w-full cursor-pointer items-start gap-1.5 rounded-sm px-2 py-2 text-left text-popover-foreground outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus-within:bg-accent focus-within:text-accent-foreground", popoverMenuItemFocusWithinClassName, checked &&
|
|
@@ -52,8 +54,66 @@ const PopoverMenuSearch = React.forwardRef(({ "aria-label": ariaLabel, className
|
|
|
52
54
|
return (_jsx("div", { className: cn("relative -mx-1 -mt-1 mb-1 px-1 py-1 after:absolute after:inset-x-0 after:bottom-0 after:h-px after:bg-border after:content-['']", containerClassName), children: _jsx(Input, { ref: ref, "data-popover-menu-search": "true", label: (_b = (_a = label !== null && label !== void 0 ? label : ariaLabel) !== null && _a !== void 0 ? _a : placeholder) !== null && _b !== void 0 ? _b : "Search", hideLabel: true, className: "gap-0", inputClassName: className, "aria-label": ariaLabel, placeholder: placeholder, ...props }) }));
|
|
53
55
|
});
|
|
54
56
|
PopoverMenuSearch.displayName = "PopoverMenuSearch";
|
|
55
|
-
function
|
|
56
|
-
|
|
57
|
+
function PopoverMenuScrollButton({ direction, onAutoScroll, }) {
|
|
58
|
+
const autoScrollTimerRef = React.useRef(null);
|
|
59
|
+
const clearAutoScrollTimer = React.useCallback(() => {
|
|
60
|
+
if (autoScrollTimerRef.current === null)
|
|
61
|
+
return;
|
|
62
|
+
window.clearInterval(autoScrollTimerRef.current);
|
|
63
|
+
autoScrollTimerRef.current = null;
|
|
64
|
+
}, []);
|
|
65
|
+
const startAutoScroll = React.useCallback(() => {
|
|
66
|
+
if (autoScrollTimerRef.current !== null)
|
|
67
|
+
return;
|
|
68
|
+
autoScrollTimerRef.current = window.setInterval(() => onAutoScroll(direction), 50);
|
|
69
|
+
}, [direction, onAutoScroll]);
|
|
70
|
+
React.useEffect(() => clearAutoScrollTimer, [clearAutoScrollTimer]);
|
|
71
|
+
return (_jsx("div", { "aria-hidden": "true", "data-slot": `popover-menu-scroll-${direction}-button`, className: "-mx-1 flex shrink-0 cursor-pointer items-center justify-center py-1", onPointerDown: startAutoScroll, onPointerMove: startAutoScroll, onPointerLeave: clearAutoScrollTimer, children: direction === "up" ? (_jsx(ChevronUp, { className: "h-4 w-4" })) : (_jsx(ChevronDownIcon, { size: 16 })) }));
|
|
72
|
+
}
|
|
73
|
+
function PopoverMenuScrollArea({ className, children, maxHeight = "max-h-64", onScroll, ...props }) {
|
|
74
|
+
const scrollAreaRef = React.useRef(null);
|
|
75
|
+
const [canScrollUp, setCanScrollUp] = React.useState(false);
|
|
76
|
+
const [canScrollDown, setCanScrollDown] = React.useState(false);
|
|
77
|
+
const updateScrollControls = React.useCallback(() => {
|
|
78
|
+
const scrollArea = scrollAreaRef.current;
|
|
79
|
+
if (!scrollArea)
|
|
80
|
+
return;
|
|
81
|
+
const maxScroll = scrollArea.scrollHeight - scrollArea.clientHeight;
|
|
82
|
+
setCanScrollUp(scrollArea.scrollTop > 0);
|
|
83
|
+
setCanScrollDown(maxScroll > 0 && Math.ceil(scrollArea.scrollTop) < maxScroll);
|
|
84
|
+
}, []);
|
|
85
|
+
useIsomorphicLayoutEffect(() => {
|
|
86
|
+
const scrollArea = scrollAreaRef.current;
|
|
87
|
+
if (!scrollArea)
|
|
88
|
+
return;
|
|
89
|
+
updateScrollControls();
|
|
90
|
+
const animationFrame = window.requestAnimationFrame(updateScrollControls);
|
|
91
|
+
const resizeObserver = typeof ResizeObserver === "undefined"
|
|
92
|
+
? null
|
|
93
|
+
: new ResizeObserver(updateScrollControls);
|
|
94
|
+
resizeObserver === null || resizeObserver === void 0 ? void 0 : resizeObserver.observe(scrollArea);
|
|
95
|
+
const mutationObserver = new MutationObserver(updateScrollControls);
|
|
96
|
+
mutationObserver.observe(scrollArea, { childList: true, subtree: true });
|
|
97
|
+
return () => {
|
|
98
|
+
window.cancelAnimationFrame(animationFrame);
|
|
99
|
+
resizeObserver === null || resizeObserver === void 0 ? void 0 : resizeObserver.disconnect();
|
|
100
|
+
mutationObserver.disconnect();
|
|
101
|
+
};
|
|
102
|
+
}, [children, updateScrollControls]);
|
|
103
|
+
const handleScroll = React.useCallback((event) => {
|
|
104
|
+
onScroll === null || onScroll === void 0 ? void 0 : onScroll(event);
|
|
105
|
+
updateScrollControls();
|
|
106
|
+
}, [onScroll, updateScrollControls]);
|
|
107
|
+
const handleAutoScroll = React.useCallback((direction) => {
|
|
108
|
+
const scrollArea = scrollAreaRef.current;
|
|
109
|
+
if (!scrollArea)
|
|
110
|
+
return;
|
|
111
|
+
const firstItem = scrollArea.querySelector('[data-slot="popover-menu-item"]');
|
|
112
|
+
const scrollStep = (firstItem === null || firstItem === void 0 ? void 0 : firstItem.offsetHeight) || 32;
|
|
113
|
+
scrollArea.scrollTop += direction === "up" ? -scrollStep : scrollStep;
|
|
114
|
+
updateScrollControls();
|
|
115
|
+
}, [updateScrollControls]);
|
|
116
|
+
return (_jsxs("div", { "data-slot": "popover-menu-scroll-viewport", className: cn(maxHeight, "flex min-h-0 flex-col overflow-hidden"), children: [canScrollUp ? (_jsx(PopoverMenuScrollButton, { direction: "up", onAutoScroll: handleAutoScroll })) : null, _jsx("div", { ref: scrollAreaRef, className: cn("-mx-1 min-h-0 flex-1 space-y-1 overflow-y-auto overflow-x-hidden px-1", className), onScroll: handleScroll, ...props, children: children }), canScrollDown ? (_jsx(PopoverMenuScrollButton, { direction: "down", onAutoScroll: handleAutoScroll })) : null] }));
|
|
57
117
|
}
|
|
58
118
|
function PopoverMenuView({ children, className, direction = "forward", viewKey = "default", ...props }) {
|
|
59
119
|
const [activeViewKey, setActiveViewKey] = React.useState(viewKey);
|
package/dist/popover.d.ts
CHANGED
|
@@ -5,12 +5,15 @@ import { type TooltipProps } from "./tooltip";
|
|
|
5
5
|
import { Typography } from "./typography";
|
|
6
6
|
type PopoverRootProps = React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Root>;
|
|
7
7
|
type PopoverContentProps = React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>;
|
|
8
|
+
type PopoverAnchorProps = React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Anchor>;
|
|
8
9
|
export interface PopoverProps extends Pick<PopoverRootProps, "defaultOpen" | "modal" | "onOpenChange" | "open">, Omit<PopoverContentProps, "children" | "className" | "title"> {
|
|
9
10
|
trigger: React.ReactElement;
|
|
11
|
+
/** Positions the popover from a specific element while retaining `trigger` for interaction and focus. */
|
|
12
|
+
virtualAnchorRef?: PopoverAnchorProps["virtualRef"];
|
|
10
13
|
children: React.ReactNode;
|
|
11
14
|
/** Visible popover heading. Also labels the popover for assistive technology. */
|
|
12
15
|
title?: React.ReactNode;
|
|
13
|
-
surface?: "panel" | "menu";
|
|
16
|
+
surface?: "panel" | "menu" | "message";
|
|
14
17
|
/** Controls whether Tab stays within the popover or closes it and continues in the page Tab order. */
|
|
15
18
|
tabBehavior?: PopoverTabBehavior;
|
|
16
19
|
contentClassName?: string;
|
|
@@ -19,7 +22,7 @@ export interface PopoverProps extends Pick<PopoverRootProps, "defaultOpen" | "mo
|
|
|
19
22
|
triggerTooltipSideOffset?: TooltipProps["sideOffset"];
|
|
20
23
|
triggerTooltipClassName?: TooltipProps["contentClassName"];
|
|
21
24
|
}
|
|
22
|
-
declare function Popover({ defaultOpen, modal, onOpenChange, open, trigger, children, title, surface, tabBehavior, contentClassName, triggerTooltip, triggerTooltipSide, triggerTooltipSideOffset, triggerTooltipClassName, align, sideOffset, ...contentProps }: PopoverProps): import("react/jsx-runtime").JSX.Element;
|
|
25
|
+
declare function Popover({ defaultOpen, modal, onOpenChange, open, trigger, virtualAnchorRef, children, title, surface, tabBehavior, contentClassName, triggerTooltip, triggerTooltipSide, triggerTooltipSideOffset, triggerTooltipClassName, align, sideOffset, style, ...contentProps }: PopoverProps): import("react/jsx-runtime").JSX.Element;
|
|
23
26
|
export interface PopoverTitleProps extends Omit<React.ComponentPropsWithoutRef<typeof Typography>, "as" | "asChild" | "variant" | "color"> {
|
|
24
27
|
}
|
|
25
28
|
declare function PopoverTitle({ className, children, ...props }: PopoverTitleProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/popover.js
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cn } from "./lib/utils";
|
|
5
|
-
import { PopoverContent, PopoverRoot, PopoverTrigger, } from "./popover-primitive";
|
|
5
|
+
import { PopoverAnchor, PopoverContent, PopoverRoot, PopoverTrigger, } from "./popover-primitive";
|
|
6
6
|
import { usePopoverTabExit, } from "./popover-focus";
|
|
7
7
|
import { Tooltip } from "./tooltip";
|
|
8
8
|
import { Typography } from "./typography";
|
|
9
|
-
function Popover({ defaultOpen, modal, onOpenChange, open, trigger, children, title, surface = "panel", tabBehavior = "contain", contentClassName, triggerTooltip, triggerTooltipSide, triggerTooltipSideOffset, triggerTooltipClassName, align = "center", sideOffset = 4, ...contentProps }) {
|
|
9
|
+
function Popover({ defaultOpen, modal, onOpenChange, open, trigger, virtualAnchorRef, children, title, surface = "panel", tabBehavior = "contain", contentClassName, triggerTooltip, triggerTooltipSide, triggerTooltipSideOffset, triggerTooltipClassName, align = "center", sideOffset = 4, style, ...contentProps }) {
|
|
10
10
|
const generatedTitleId = React.useId();
|
|
11
11
|
const titleId = title ? `popover-title-${generatedTitleId}` : undefined;
|
|
12
12
|
const [internalOpen, setInternalOpen] = React.useState(defaultOpen !== null && defaultOpen !== void 0 ? defaultOpen : false);
|
|
@@ -79,7 +79,7 @@ function Popover({ defaultOpen, modal, onOpenChange, open, trigger, children, ti
|
|
|
79
79
|
contentOnCloseAutoFocus === null || contentOnCloseAutoFocus === void 0 ? void 0 : contentOnCloseAutoFocus(event);
|
|
80
80
|
handleCloseAutoFocus(event);
|
|
81
81
|
}, [contentOnCloseAutoFocus, handleCloseAutoFocus]);
|
|
82
|
-
return (_jsxs(PopoverRoot, { modal: modal, onOpenChange: setOpen, open: currentOpen, children: [triggerTooltip ? (_jsx(Tooltip, { content: triggerTooltip, side: triggerTooltipSide, sideOffset: triggerTooltipSideOffset, contentClassName: triggerTooltipClassName, children: triggerElement })) : (triggerElement), _jsxs(PopoverContent, { ref: contentRef, align: align, sideOffset: sideOffset, "aria-labelledby": titleId !== null && titleId !== void 0 ? titleId : contentProps["aria-labelledby"], className: cn(surface === "menu" && "overflow-hidden p-1", contentClassName), ...contentProps, onCloseAutoFocus: handleContentCloseAutoFocus, onKeyDown: handleContentKeyDown, onKeyDownCapture: handleContentKeyDownCapture, children: [title ? (_jsx(PopoverTitle, { id: titleId, className: cn(surface === "menu"
|
|
82
|
+
return (_jsxs(PopoverRoot, { modal: modal, onOpenChange: setOpen, open: currentOpen, children: [virtualAnchorRef ? (_jsx(PopoverAnchor, { virtualRef: virtualAnchorRef })) : null, triggerTooltip ? (_jsx(Tooltip, { content: triggerTooltip, side: triggerTooltipSide, sideOffset: triggerTooltipSideOffset, contentClassName: triggerTooltipClassName, children: triggerElement })) : (triggerElement), _jsxs(PopoverContent, { ref: contentRef, align: align, sideOffset: sideOffset, "aria-labelledby": titleId !== null && titleId !== void 0 ? titleId : contentProps["aria-labelledby"], className: cn(surface === "menu" && "overflow-hidden p-1", surface === "message" && "overflow-hidden p-0", contentClassName), style: style, ...contentProps, onCloseAutoFocus: handleContentCloseAutoFocus, onKeyDown: handleContentKeyDown, onKeyDownCapture: handleContentKeyDownCapture, children: [title ? (_jsx(PopoverTitle, { id: titleId, className: cn(surface === "menu"
|
|
83
83
|
? "px-2 py-1.5"
|
|
84
84
|
: "-mx-4 -mt-4 mb-4 border-b px-4 py-3"), children: title })) : null, children] })] }));
|
|
85
85
|
}
|
|
@@ -25,6 +25,7 @@ export interface RichTextEditorSendAction {
|
|
|
25
25
|
disabled?: boolean;
|
|
26
26
|
}
|
|
27
27
|
export interface RichTextEditorProps {
|
|
28
|
+
variant?: "default" | "message";
|
|
28
29
|
placeholder?: string;
|
|
29
30
|
editorAriaLabel?: string;
|
|
30
31
|
header?: React.ReactNode;
|
|
@@ -53,4 +54,4 @@ export interface RichTextEditorProps {
|
|
|
53
54
|
export declare function formatFileSize(bytes: number): string;
|
|
54
55
|
export declare function sanitizeRichTextHtml(html: string): string;
|
|
55
56
|
export declare function replaceTemplateVariables(content: string, context: TemplateContext): string;
|
|
56
|
-
export declare function RichTextEditor({ placeholder, editorAriaLabel, header, isExpanded, onExpandToggle, onSend, className, maxFileSize, maxFileSizeLabel, acceptedFileTypes, acceptedFileTypesLabel, isSending, showTemplates, templates, templateContext, templateCategoryLabels, templateCategoryOrder, templateCreateHref, templateCreateLabel, onFileRejected, colorPalette, sendLabel, sendingLabel, sendActions, }: RichTextEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
57
|
+
export declare function RichTextEditor({ variant, placeholder, editorAriaLabel, header, isExpanded, onExpandToggle, onSend, className, maxFileSize, maxFileSizeLabel, acceptedFileTypes, acceptedFileTypesLabel, isSending, showTemplates, templates, templateContext, templateCategoryLabels, templateCategoryOrder, templateCreateHref, templateCreateLabel, onFileRejected, colorPalette, sendLabel, sendingLabel, sendActions, }: RichTextEditorProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/rich-text-editor.js
CHANGED
|
@@ -11,6 +11,7 @@ import { TextStyle } from "@tiptap/extension-text-style";
|
|
|
11
11
|
import { Color } from "@tiptap/extension-color";
|
|
12
12
|
import Highlight from "@tiptap/extension-highlight";
|
|
13
13
|
import Underline from "@tiptap/extension-underline";
|
|
14
|
+
import { Baseline, Reply } from "lucide-react";
|
|
14
15
|
import { BoldIcon, ChevronDownIcon, HighlighterIcon, ItalicIcon, LinkIcon, ListIcon, ListOrderedIcon, MaximizeIcon, PaletteIcon, PaperclipIcon, PlusIcon, SendIcon, StrikethroughIcon, UnderlineIcon, XIcon, } from "./animated-icons";
|
|
15
16
|
import { Button } from "./button";
|
|
16
17
|
import { DropdownButton } from "./dropdown-button";
|
|
@@ -19,6 +20,7 @@ import { inputControlPaddingClassName } from "./input-control";
|
|
|
19
20
|
import { Popover } from "./popover";
|
|
20
21
|
import { PopoverMenuItem, PopoverMenuSection } from "./popover-menu";
|
|
21
22
|
import { Tooltip } from "./tooltip";
|
|
23
|
+
import { Typography } from "./typography";
|
|
22
24
|
import { cn } from "./lib/utils";
|
|
23
25
|
const DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
24
26
|
const DEFAULT_MAX_FILE_SIZE_LABEL = "10MB";
|
|
@@ -125,7 +127,8 @@ function ToolbarIconButton({ active, label, onClick, children, }) {
|
|
|
125
127
|
function getTemplateCategory(template) {
|
|
126
128
|
return template.category || DEFAULT_TEMPLATE_CATEGORY;
|
|
127
129
|
}
|
|
128
|
-
export function RichTextEditor({ placeholder = "Type message here...", editorAriaLabel = "Message body", header, isExpanded = false, onExpandToggle, onSend, className, maxFileSize = DEFAULT_MAX_FILE_SIZE, maxFileSizeLabel = DEFAULT_MAX_FILE_SIZE_LABEL, acceptedFileTypes = DEFAULT_ACCEPTED_FILE_TYPES, acceptedFileTypesLabel = DEFAULT_ACCEPTED_FILE_TYPES_LABEL, isSending = false, showTemplates = true, templates = [], templateContext = {}, templateCategoryLabels = {}, templateCategoryOrder, templateCreateHref, templateCreateLabel = "Create new template", onFileRejected, colorPalette = DEFAULT_COLOR_PALETTE, sendLabel = "Send", sendingLabel = "Sending...", sendActions, }) {
|
|
130
|
+
export function RichTextEditor({ variant = "default", placeholder = "Type message here...", editorAriaLabel = "Message body", header, isExpanded = false, onExpandToggle, onSend, className, maxFileSize = DEFAULT_MAX_FILE_SIZE, maxFileSizeLabel = DEFAULT_MAX_FILE_SIZE_LABEL, acceptedFileTypes = DEFAULT_ACCEPTED_FILE_TYPES, acceptedFileTypesLabel = DEFAULT_ACCEPTED_FILE_TYPES_LABEL, isSending = false, showTemplates = true, templates = [], templateContext = {}, templateCategoryLabels = {}, templateCategoryOrder, templateCreateHref, templateCreateLabel = "Create new template", onFileRejected, colorPalette = DEFAULT_COLOR_PALETTE, sendLabel = "Send", sendingLabel = "Sending...", sendActions, }) {
|
|
131
|
+
var _a;
|
|
129
132
|
const [isEmpty, setIsEmpty] = React.useState(true);
|
|
130
133
|
const [attachments, setAttachments] = React.useState([]);
|
|
131
134
|
const [isDragging, setIsDragging] = React.useState(false);
|
|
@@ -133,6 +136,7 @@ export function RichTextEditor({ placeholder = "Type message here...", editorAri
|
|
|
133
136
|
const [linkUrl, setLinkUrl] = React.useState("");
|
|
134
137
|
const [linkMenuPosition, setLinkMenuPosition] = React.useState(null);
|
|
135
138
|
const [templatePopoverOpen, setTemplatePopoverOpen] = React.useState(false);
|
|
139
|
+
const [isFormattingOpen, setIsFormattingOpen] = React.useState(false);
|
|
136
140
|
const fileInputRef = React.useRef(null);
|
|
137
141
|
const dropZoneRef = React.useRef(null);
|
|
138
142
|
const linkInputRef = React.useRef(null);
|
|
@@ -192,7 +196,9 @@ export function RichTextEditor({ placeholder = "Type message here...", editorAri
|
|
|
192
196
|
role: "textbox",
|
|
193
197
|
"aria-multiline": "true",
|
|
194
198
|
"aria-label": editorAriaLabel,
|
|
195
|
-
class: cn("prose prose-sm max-w-none focus:outline-none", "
|
|
199
|
+
class: cn("prose prose-sm max-w-none focus:outline-none", variant === "message"
|
|
200
|
+
? "min-h-[48px] px-[10px] py-[8px] text-sm"
|
|
201
|
+
: cn("min-h-[120px]", inputControlPaddingClassName)),
|
|
196
202
|
},
|
|
197
203
|
},
|
|
198
204
|
onUpdate: ({ editor }) => {
|
|
@@ -352,6 +358,25 @@ export function RichTextEditor({ placeholder = "Type message here...", editorAri
|
|
|
352
358
|
}, children: action.label }, action.id))) }) })) : (_jsxs(Button, { type: "button", className: "w-full", disabled: sendDisabled, onClick: () => {
|
|
353
359
|
void handleSend();
|
|
354
360
|
}, children: [_jsx(SendIcon, { size: 16 }), isSending ? sendingLabel : sendLabel] }));
|
|
361
|
+
if (variant === "message") {
|
|
362
|
+
return (_jsxs("div", { "data-slot": "message-composer", ref: dropZoneRef, onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, onDragOver: handleDragOver, onDrop: handleDrop, className: cn("box-border flex min-h-[164px] flex-col overflow-hidden rounded-[8px] border border-input bg-background transition-all focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/50", !isExpanded && attachments.length === 0 && "h-[164px]", isDragging && "border-2 border-primary bg-primary/5", className), children: [header ? (_jsxs("div", { "data-slot": "message-composer-header", className: "flex min-h-[48px] items-center justify-between px-[10px] py-[8px] text-muted-foreground", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-[8px]", children: [_jsx(Reply, { "aria-hidden": "true", className: "size-[16px] shrink-0" }), _jsx(Typography, { as: "span", variant: "caption", className: "min-w-0 truncate font-medium text-muted-foreground", "data-slot": "message-composer-header-label", children: header })] }), onExpandToggle ? (_jsx(Tooltip, { content: isExpanded ? "Collapse editor" : "Expand editor", side: "top", children: _jsx(Button, { type: "button", variant: "ghost", size: "compactIcon", className: "h-[28px] w-[28px] shrink-0", onClick: onExpandToggle, "aria-label": isExpanded ? "Collapse editor" : "Expand editor", children: _jsx(MaximizeIcon, { size: 16 }) }) })) : null] })) : null, _jsx("div", { "data-slot": "message-composer-editor", className: cn("min-h-[64px]", isExpanded && "min-h-[200px]"), children: _jsx(EditorContent, { editor: editor, className: cn("min-h-[64px] text-sm", isExpanded && "min-h-[200px]", "[&_.ProseMirror]:min-h-[64px] [&_.ProseMirror]:outline-none", isExpanded && "[&_.ProseMirror]:min-h-[200px]", "[&_.ProseMirror_p.is-editor-empty:first-child::before]:float-left", "[&_.ProseMirror_p.is-editor-empty:first-child::before]:h-0", "[&_.ProseMirror_p.is-editor-empty:first-child::before]:pointer-events-none", "[&_.ProseMirror_p.is-editor-empty:first-child::before]:text-muted-foreground", "[&_.ProseMirror_p.is-editor-empty:first-child::before]:content-[attr(data-placeholder)]") }) }), attachments.length > 0 ? (_jsx("div", { className: "space-y-[8px] px-[10px] pb-[8px]", children: attachments.map((attachment, index) => (_jsxs("div", { className: "flex w-full items-center gap-[8px] rounded-md border bg-muted px-[12px] py-[8px] text-sm", children: [_jsx(PaperclipIcon, { size: 14, className: "shrink-0 text-muted-foreground" }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: attachment.name }), _jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: formatFileSize(attachment.size) }), _jsx("button", { type: "button", onClick: () => removeAttachment(index), className: "shrink-0 text-muted-foreground hover:text-foreground", "aria-label": `Remove ${attachment.name}`, children: _jsx(XIcon, { size: 14 }) })] }, `${attachment.name}-${attachment.size}-${index}`))) })) : null, _jsx("input", { ref: fileInputRef, type: "file", accept: acceptedFileTypes, multiple: true, onChange: handleFileSelect, className: "hidden" }), linkPopoverOpen &&
|
|
363
|
+
linkMenuPosition &&
|
|
364
|
+
typeof document !== "undefined" &&
|
|
365
|
+
createPortal(_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-[99]", onClick: () => {
|
|
366
|
+
setLinkPopoverOpen(false);
|
|
367
|
+
setLinkUrl("");
|
|
368
|
+
setLinkMenuPosition(null);
|
|
369
|
+
editor === null || editor === void 0 ? void 0 : editor.chain().focus().run();
|
|
370
|
+
} }), _jsx("div", { ref: linkMenuRef, className: "fixed z-[100] w-80 animate-in fade-in-0 zoom-in-95 rounded-md border bg-popover p-3 shadow-md", style: {
|
|
371
|
+
top: linkMenuPosition.top,
|
|
372
|
+
left: linkMenuPosition.left,
|
|
373
|
+
}, children: _jsxs("div", { className: "flex items-end gap-2", children: [_jsx(Input, { ref: linkInputRef, label: "URL", id: "message-link-url", type: "url", placeholder: "https://example.com", value: linkUrl, onChange: (event) => setLinkUrl(event.target.value), onKeyDown: handleLinkKeyDown, inputClassName: "h-8 text-sm", autoFocus: true }), _jsx(Button, { type: "button", className: "h-8 px-3", onClick: handleLinkSubmit, disabled: !linkUrl.trim(), children: "Add" })] }) })] }), document.body), isFormattingOpen ? (_jsxs("div", { className: "flex flex-wrap items-center gap-[4px] border-t border-border px-[10px] py-[8px]", children: [_jsx(ToolbarIconButton, { label: "Bold", active: editor === null || editor === void 0 ? void 0 : editor.isActive("bold"), onClick: () => editor === null || editor === void 0 ? void 0 : editor.chain().focus().toggleBold().run(), children: _jsx(BoldIcon, { size: 14 }) }), _jsx(ToolbarIconButton, { label: "Italic", active: editor === null || editor === void 0 ? void 0 : editor.isActive("italic"), onClick: () => editor === null || editor === void 0 ? void 0 : editor.chain().focus().toggleItalic().run(), children: _jsx(ItalicIcon, { size: 14 }) }), _jsx(ToolbarIconButton, { label: "Underline", active: editor === null || editor === void 0 ? void 0 : editor.isActive("underline"), onClick: () => editor === null || editor === void 0 ? void 0 : editor.chain().focus().toggleUnderline().run(), children: _jsx(UnderlineIcon, { size: 14 }) }), _jsx(ToolbarIconButton, { label: "Bullet list", active: editor === null || editor === void 0 ? void 0 : editor.isActive("bulletList"), onClick: () => editor === null || editor === void 0 ? void 0 : editor.chain().focus().toggleBulletList().run(), children: _jsx(ListIcon, { size: 14 }) }), _jsx(ToolbarIconButton, { label: "Ordered list", active: editor === null || editor === void 0 ? void 0 : editor.isActive("orderedList"), onClick: () => editor === null || editor === void 0 ? void 0 : editor.chain().focus().toggleOrderedList().run(), children: _jsx(ListOrderedIcon, { size: 14 }) })] })) : null, _jsxs("div", { "data-slot": "message-composer-toolbar", className: "flex min-h-[52px] items-center justify-between px-[10px] py-[8px]", children: [_jsxs("div", { className: "flex items-center gap-[8px]", children: [_jsx(Tooltip, { content: `Attach files (max ${maxFileSizeLabel}) - ${acceptedFileTypesLabel}`, side: "top", contentClassName: "max-w-[240px] text-center", children: _jsx(Button, { type: "button", variant: "ghost", size: "compactIcon", className: "h-[28px] w-[28px]", onClick: () => { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, "aria-label": "Attach files", children: _jsx(PaperclipIcon, { size: 16 }) }) }), _jsxs("div", { className: "inline-flex h-[32px] overflow-hidden rounded-control border border-input bg-background", children: [_jsx(Button, { type: "button", variant: "ghost", size: "compactIcon", className: "h-[32px] w-[32px] rounded-none border-r border-input", onClick: () => editor === null || editor === void 0 ? void 0 : editor.chain().focus().toggleBold().run(), "aria-pressed": (_a = editor === null || editor === void 0 ? void 0 : editor.isActive("bold")) !== null && _a !== void 0 ? _a : false, "aria-label": "Bold", children: _jsx(BoldIcon, { size: 16 }) }), _jsx(Button, { type: "button", variant: "ghost", size: "compactIcon", className: "h-[32px] w-[32px] rounded-none border-r border-input", onClick: openLinkMenu, "aria-label": "Add link", children: _jsx(LinkIcon, { size: 16 }) }), _jsx(Button, { type: "button", variant: "ghost", size: "compactIcon", className: "h-[32px] w-[32px] rounded-none", onClick: () => setIsFormattingOpen((open) => !open), "aria-expanded": isFormattingOpen, "aria-label": "Formatting options", children: _jsx(Baseline, { "aria-hidden": "true", className: "size-[16px]" }) })] })] }), _jsxs("div", { className: "flex items-center gap-[8px]", children: [showTemplates && (templates.length > 0 || templateCreateHref) ? (_jsxs(Popover, { open: templatePopoverOpen, onOpenChange: setTemplatePopoverOpen, align: "end", contentClassName: "w-96 p-0", trigger: _jsxs(Button, { type: "button", variant: "outline", size: "sm", children: ["Templates", _jsx(ChevronDownIcon, { size: 16 })] }), children: [_jsx("div", { className: "max-h-80 overflow-y-auto", children: templatesByCategory.map(({ category, templates }) => {
|
|
374
|
+
var _a;
|
|
375
|
+
return (_jsxs("div", { children: [_jsx("div", { className: "sticky top-0 bg-muted px-3 py-2 text-sm font-semibold text-foreground", children: (_a = templateCategoryLabels[category]) !== null && _a !== void 0 ? _a : category }), templates.map((template) => (_jsxs("button", { type: "button", className: "w-full border-b border-border/50 px-3 py-2 text-left text-sm transition-colors last:border-0 hover:bg-muted", onClick: () => applyTemplate(template), children: [_jsx("div", { className: "truncate font-medium", children: template.name }), template.description ? (_jsx("div", { className: "mt-0.5 truncate text-xs text-muted-foreground", children: template.description })) : null] }, template.id)))] }, category));
|
|
376
|
+
}) }), templateCreateHref ? (_jsx("div", { className: "border-t p-2", children: _jsxs("a", { href: templateCreateHref, target: "_blank", rel: "noopener noreferrer", className: "flex w-full items-center justify-center gap-1.5 rounded-md px-3 py-2 text-sm text-interactive transition-colors hover:bg-muted", children: [_jsx(PlusIcon, { size: 16 }), templateCreateLabel] }) })) : null] })) : null, _jsx(Button, { type: "button", size: "sm", disabled: isSending || !onSend, "aria-label": isSending ? sendingLabel : sendLabel, onClick: () => {
|
|
377
|
+
void handleSend();
|
|
378
|
+
}, children: isSending ? sendingLabel : sendLabel })] })] })] }));
|
|
379
|
+
}
|
|
355
380
|
return (_jsxs("div", { ref: dropZoneRef, onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, onDragOver: handleDragOver, onDrop: handleDrop, className: cn("overflow-hidden rounded-control border bg-background transition-all focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/50", isExpanded && "shadow-md", isDragging && "border-2 border-primary bg-primary/5", className), children: [header && (_jsxs("div", { className: cn("flex items-center justify-between border-b bg-muted dark:bg-muted/30", inputControlPaddingClassName), children: [_jsx("div", { className: "flex-1", children: header }), _jsx("div", { className: "flex items-center gap-2", children: onExpandToggle && (_jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "h-6 w-6", onClick: onExpandToggle, "aria-label": "Expand editor", children: _jsx(MaximizeIcon, { size: 14 }) })) })] })), _jsxs("div", { className: cn("bg-background transition-all", isExpanded ? "min-h-[200px]" : "min-h-[120px]"), children: [_jsx(EditorContent, { editor: editor, className: cn("text-sm", "[&_.ProseMirror]:min-h-[120px] [&_.ProseMirror]:outline-none", "[&_.ProseMirror_p.is-editor-empty:first-child::before]:text-muted-foreground", "[&_.ProseMirror_p.is-editor-empty:first-child::before]:content-[attr(data-placeholder)]", "[&_.ProseMirror_p.is-editor-empty:first-child::before]:float-left", "[&_.ProseMirror_p.is-editor-empty:first-child::before]:pointer-events-none", "[&_.ProseMirror_p.is-editor-empty:first-child::before]:h-0", "[&_.ProseMirror_ul]:my-2 [&_.ProseMirror_ul]:list-disc [&_.ProseMirror_ul]:pl-6", "[&_.ProseMirror_ol]:my-2 [&_.ProseMirror_ol]:list-decimal [&_.ProseMirror_ol]:pl-6", "[&_.ProseMirror_li]:my-1", "[&_.ProseMirror_ul_ul]:list-[circle] [&_.ProseMirror_ul_ul_ul]:list-[square]") }), linkPopoverOpen &&
|
|
356
381
|
linkMenuPosition &&
|
|
357
382
|
typeof document !== "undefined" &&
|
|
@@ -387,7 +412,7 @@ export function RichTextEditor({ placeholder = "Type message here...", editorAri
|
|
|
387
412
|
else {
|
|
388
413
|
editor === null || editor === void 0 ? void 0 : editor.chain().focus().setHighlight({ color: color.value }).run();
|
|
389
414
|
}
|
|
390
|
-
}, children: color.value === "" && (_jsx("span", { className: "text-xs text-muted-foreground", children: "A" })) }, `bg-${color.name}`))) }) }), _jsx(ToolbarIconButton, { label: "Bullet list", active: editor === null || editor === void 0 ? void 0 : editor.isActive("bulletList"), onClick: () => editor === null || editor === void 0 ? void 0 : editor.chain().focus().toggleBulletList().run(), children: _jsx(ListIcon, { size: 14 }) }), _jsx(ToolbarIconButton, { label: "Ordered list", active: editor === null || editor === void 0 ? void 0 : editor.isActive("orderedList"), onClick: () => editor === null || editor === void 0 ? void 0 : editor.chain().focus().toggleOrderedList().run(), children: _jsx(ListOrderedIcon, { size: 14 }) }), _jsx(Tooltip, { content: `Attach files (max ${maxFileSizeLabel}) - ${acceptedFileTypesLabel}`, side: "top", contentClassName: "max-w-[240px] text-center", children: _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "h-8 w-8", onClick: () => { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, "aria-label": "Attach files", children: _jsx(PaperclipIcon, { size: 14 }) }) }), _jsx(ToolbarIconButton, { label: "Add link", active: editor === null || editor === void 0 ? void 0 : editor.isActive("link"), onClick: openLinkMenu, children: _jsx(LinkIcon, { size: 14 }) }), showTemplates && templates.length > 0 && (_jsx(_Fragment, { children: _jsxs(Popover, { open: templatePopoverOpen, onOpenChange: setTemplatePopoverOpen, align: "end", contentClassName: "w-96 p-0", trigger: _jsxs(Button, { type: "button", variant: "ghost", className: "h-8 gap-1 text-muted-foreground", children: ["Template", _jsx(ChevronDownIcon, { size: 14 })] }), children: [_jsx("div", { className: "max-h-80 overflow-y-auto", children: templatesByCategory.map(({ category, templates }) => {
|
|
415
|
+
}, children: color.value === "" && (_jsx("span", { className: "text-xs text-muted-foreground", children: "A" })) }, `bg-${color.name}`))) }) }), _jsx(ToolbarIconButton, { label: "Bullet list", active: editor === null || editor === void 0 ? void 0 : editor.isActive("bulletList"), onClick: () => editor === null || editor === void 0 ? void 0 : editor.chain().focus().toggleBulletList().run(), children: _jsx(ListIcon, { size: 14 }) }), _jsx(ToolbarIconButton, { label: "Ordered list", active: editor === null || editor === void 0 ? void 0 : editor.isActive("orderedList"), onClick: () => editor === null || editor === void 0 ? void 0 : editor.chain().focus().toggleOrderedList().run(), children: _jsx(ListOrderedIcon, { size: 14 }) }), _jsx(Tooltip, { content: `Attach files (max ${maxFileSizeLabel}) - ${acceptedFileTypesLabel}`, side: "top", contentClassName: "max-w-[240px] text-center", children: _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "h-8 w-8", onClick: () => { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, "aria-label": "Attach files", children: _jsx(PaperclipIcon, { size: 14 }) }) }), _jsx(ToolbarIconButton, { label: "Add link", active: editor === null || editor === void 0 ? void 0 : editor.isActive("link"), onClick: openLinkMenu, children: _jsx(LinkIcon, { size: 14 }) }), showTemplates && (templates.length > 0 || templateCreateHref) && (_jsx(_Fragment, { children: _jsxs(Popover, { open: templatePopoverOpen, onOpenChange: setTemplatePopoverOpen, align: "end", contentClassName: "w-96 p-0", trigger: _jsxs(Button, { type: "button", variant: "ghost", className: "h-8 gap-1 text-muted-foreground", children: ["Template", _jsx(ChevronDownIcon, { size: 14 })] }), children: [_jsx("div", { className: "max-h-80 overflow-y-auto", children: templatesByCategory.map(({ category, templates }) => {
|
|
391
416
|
var _a;
|
|
392
417
|
return (_jsxs("div", { children: [_jsx("div", { className: "sticky top-0 bg-muted px-3 py-2 text-sm font-semibold text-foreground", children: (_a = templateCategoryLabels[category]) !== null && _a !== void 0 ? _a : category }), templates.map((template) => (_jsxs("button", { type: "button", className: "w-full border-b border-border/50 px-3 py-2 text-left text-sm transition-colors last:border-0 hover:bg-muted", onClick: () => applyTemplate(template), children: [_jsx("div", { className: "truncate font-medium", children: template.name }), template.description && (_jsx("div", { className: "mt-0.5 truncate text-xs text-muted-foreground", children: template.description }))] }, template.id)))] }, category));
|
|
393
418
|
}) }), templateCreateHref && (_jsx("div", { className: "border-t p-2", children: _jsxs("a", { href: templateCreateHref, target: "_blank", rel: "noopener noreferrer", className: "flex w-full items-center justify-center gap-1.5 rounded-md px-3 py-2 text-sm text-interactive transition-colors hover:bg-muted", children: [_jsx(PlusIcon, { size: 16 }), templateCreateLabel] }) }))] }) }))] }) }), _jsx("div", { className: "bg-muted px-3 pb-2 pt-1 dark:bg-background", children: sendAction })] }));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
export type RoutingTimelineItemKind = "assigned" | "reassigned" | "returned" | "routed" | "system";
|
|
2
|
+
export type RoutingTimelineItemKind = "assigned" | "reassigned" | "returned" | "routed" | "message-received" | "message-sent" | "system";
|
|
3
3
|
export interface RoutingTimelineItem {
|
|
4
4
|
id: string;
|
|
5
5
|
kind?: RoutingTimelineItemKind;
|
|
@@ -8,15 +8,20 @@ export interface RoutingTimelineItem {
|
|
|
8
8
|
actor?: React.ReactNode;
|
|
9
9
|
remark?: React.ReactNode;
|
|
10
10
|
description?: React.ReactNode;
|
|
11
|
+
detailLabel?: React.ReactNode;
|
|
11
12
|
from?: React.ReactNode;
|
|
12
13
|
to?: React.ReactNode;
|
|
13
14
|
fromLabel?: React.ReactNode;
|
|
14
15
|
toLabel?: React.ReactNode;
|
|
15
16
|
badgeLabel?: React.ReactNode;
|
|
17
|
+
badge?: React.ReactNode;
|
|
18
|
+
icon?: React.ReactNode;
|
|
16
19
|
}
|
|
17
20
|
export interface RoutingTimelineProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
|
|
18
21
|
items: RoutingTimelineItem[];
|
|
19
22
|
emptyMessage?: React.ReactNode;
|
|
23
|
+
/** Reduces typography, icon, padding, and event spacing for constrained panels. */
|
|
24
|
+
density?: "default" | "compact";
|
|
20
25
|
}
|
|
21
|
-
declare function RoutingTimeline({ items, emptyMessage, className, ...props }: RoutingTimelineProps): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
declare function RoutingTimeline({ items, emptyMessage, density, className, ...props }: RoutingTimelineProps): import("react/jsx-runtime").JSX.Element;
|
|
22
27
|
export { RoutingTimeline };
|