@neasg/design-system 0.4.118 → 0.4.132
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/alert.js +3 -1
- package/dist/button.d.ts +3 -1
- package/dist/button.js +4 -3
- package/dist/checkbox.js +2 -2
- package/dist/checklist.js +4 -2
- package/dist/dialog.d.ts +5 -1
- package/dist/dialog.js +5 -5
- package/dist/dropdown-button.js +1 -0
- package/dist/empty-state.d.ts +3 -1
- package/dist/empty-state.js +2 -2
- package/dist/file-upload-status-row.d.ts +14 -0
- package/dist/file-upload-status-row.js +53 -0
- package/dist/file-upload-status-toast.d.ts +3 -1
- package/dist/file-upload-status-toast.js +8 -47
- package/dist/file-upload.d.ts +3 -1
- package/dist/file-upload.js +11 -26
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/inline.d.ts +3 -0
- package/dist/inline.js +5 -0
- package/dist/label-value-grid.js +3 -1
- package/dist/popover.d.ts +2 -1
- package/dist/popover.js +2 -2
- package/dist/rich-text-editor.js +3 -3
- package/dist/routing-action-panel.d.ts +3 -1
- package/dist/routing-action-panel.js +5 -3
- package/dist/routing-timeline.js +1 -1
- package/dist/table.d.ts +3 -0
- package/dist/table.js +4 -3
- package/dist/typography.d.ts +1 -1
- package/dist/typography.js +5 -3
- package/package.json +1 -1
package/dist/alert.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
|
+
import { CircleAlert } from "lucide-react";
|
|
3
4
|
import { cva } from "class-variance-authority";
|
|
4
5
|
import { Button } from "./button";
|
|
5
6
|
import { Checkbox } from "./checkbox";
|
|
@@ -20,7 +21,8 @@ const alertVariants = cva("relative flex w-full items-start gap-2 rounded-surfac
|
|
|
20
21
|
});
|
|
21
22
|
const AlertAction = React.forwardRef(({ className, type = "button", ...props }, ref) => (_jsx(Button, { ref: ref, type: type, variant: "link", size: "compact", className: cn("h-auto min-h-6 px-0 text-inherit hover:text-inherit", className), ...props })));
|
|
22
23
|
AlertAction.displayName = "AlertAction";
|
|
23
|
-
const Alert = React.forwardRef(({ variant, title, description, icon
|
|
24
|
+
const Alert = React.forwardRef(({ variant, title, description, icon, action, className, children, role = "alert", ...props }, ref) => {
|
|
25
|
+
const Icon = icon !== null && icon !== void 0 ? icon : (variant === "destructive" ? CircleAlert : undefined);
|
|
24
26
|
const body = description !== null && description !== void 0 ? description : children;
|
|
25
27
|
const hasStackedContent = Boolean(title);
|
|
26
28
|
return (_jsxs("div", { ref: ref, role: role, className: cn(alertVariants({ variant }), !hasStackedContent && "items-center", className), ...props, children: [Icon ? (_jsx(Icon, { className: cn("h-4 w-4 shrink-0", hasStackedContent && "mt-0.5") })) : null, _jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-1", children: [title ? (_jsx(Typography, { as: "p", variant: "bodySm", color: "inherit", className: "font-bold", children: title })) : null, body ? (_jsx(Typography, { as: "div", variant: "caption", color: "inherit", className: "opacity-95", children: body })) : null] }), action ? (_jsx("div", { className: cn("shrink-0 text-inherit [&_a]:text-inherit [&_button]:text-inherit [&_button:hover]:text-inherit", hasStackedContent ? "self-start" : "self-center"), children: action })) : null] }));
|
package/dist/button.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
3
3
|
declare const buttonVariants: (props?: ({
|
|
4
|
-
variant?: "link" | "default" | "warning" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
|
|
4
|
+
variant?: "link" | "default" | "warning" | "destructive" | "outline" | "secondary" | "ghost" | "destructiveGhost" | null | undefined;
|
|
5
5
|
size?: "default" | "sm" | "compact" | "comfortable" | "icon" | "compactIcon" | null | undefined;
|
|
6
6
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
7
|
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
@@ -12,6 +12,8 @@ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElemen
|
|
|
12
12
|
asChild?: boolean;
|
|
13
13
|
/** Allow the label to wrap within its container and the button to grow in height. */
|
|
14
14
|
wrap?: boolean;
|
|
15
|
+
/** Align the button content within its available width. */
|
|
16
|
+
contentAlign?: "center" | "start";
|
|
15
17
|
/**
|
|
16
18
|
* Shows a loading indicator, disables interaction, and sets `aria-busy`.
|
|
17
19
|
* When used with `asChild`, the component also prevents click handling and sets `aria-disabled`.
|
package/dist/button.js
CHANGED
|
@@ -12,6 +12,7 @@ const buttonVariants = cva("inline-flex cursor-pointer items-center justify-cent
|
|
|
12
12
|
outline: "border border-[hsl(var(--button-outline-border))] bg-[hsl(var(--button-outline-background))] text-foreground hover:bg-[hsl(var(--button-outline-hover-background))] hover:text-accent-foreground",
|
|
13
13
|
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
14
14
|
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
15
|
+
destructiveGhost: "text-destructive hover:bg-destructive/10 hover:text-destructive focus-visible:ring-destructive",
|
|
15
16
|
link: "text-interactive underline-offset-4 hover:text-interactive hover:underline",
|
|
16
17
|
},
|
|
17
18
|
size: {
|
|
@@ -102,7 +103,7 @@ function attachAnimatedIconRefs(node, refs) {
|
|
|
102
103
|
}
|
|
103
104
|
return node;
|
|
104
105
|
}
|
|
105
|
-
const Button = React.forwardRef(({ className, variant, size, asChild = false, wrap = false, loading = false, loadingText, loadingIndicator, disabled, children, onClick, onMouseEnter, onMouseLeave, onFocus, onBlur, tabIndex, ...props }, ref) => {
|
|
106
|
+
const Button = React.forwardRef(({ className, variant, size, asChild = false, wrap = false, contentAlign = "center", loading = false, loadingText, loadingIndicator, disabled, children, onClick, onMouseEnter, onMouseLeave, onFocus, onBlur, tabIndex, ...props }, ref) => {
|
|
106
107
|
const Comp = asChild ? Slot : "button";
|
|
107
108
|
const isDisabled = disabled || loading;
|
|
108
109
|
const loadingLabel = loadingText !== null && loadingText !== void 0 ? loadingText : children;
|
|
@@ -112,7 +113,7 @@ const Button = React.forwardRef(({ className, variant, size, asChild = false, wr
|
|
|
112
113
|
hasTextLikeContent(children)) {
|
|
113
114
|
throw new Error('NEA Button `size="icon"` and `size="compactIcon"` only accepts icon components or SVG content. Do not pass text or emoji.');
|
|
114
115
|
}
|
|
115
|
-
const content = loading ? (loadingLabel ? (_jsxs("span", { className: "inline-flex items-center gap-2", children: [loadingIndicator !== null && loadingIndicator !== void 0 ? loadingIndicator : _jsx(DefaultLoadingIndicator, {}), _jsx("span", { children: attachAnimatedIconRefs(loadingLabel, animatedIconRefs) })] })) : (loadingIndicator !== null && loadingIndicator !== void 0 ? loadingIndicator : _jsx(DefaultLoadingIndicator, {}))) : (attachAnimatedIconRefs(children, animatedIconRefs));
|
|
116
|
+
const content = loading ? (loadingLabel ? (_jsxs("span", { className: "inline-flex items-center gap-2", children: [loadingIndicator !== null && loadingIndicator !== void 0 ? loadingIndicator : _jsx(DefaultLoadingIndicator, {}), _jsx("span", { className: "inline-flex items-center gap-2", children: attachAnimatedIconRefs(loadingLabel, animatedIconRefs) })] })) : (loadingIndicator !== null && loadingIndicator !== void 0 ? loadingIndicator : _jsx(DefaultLoadingIndicator, {}))) : (attachAnimatedIconRefs(children, animatedIconRefs));
|
|
116
117
|
const startAnimatedIcons = () => {
|
|
117
118
|
animatedIconRefs.current.forEach((icon) => { var _a; return (_a = icon.startAnimation) === null || _a === void 0 ? void 0 : _a.call(icon); });
|
|
118
119
|
};
|
|
@@ -149,7 +150,7 @@ const Button = React.forwardRef(({ className, variant, size, asChild = false, wr
|
|
|
149
150
|
};
|
|
150
151
|
return (_jsx(Comp, { "data-slot": "button", className: cn(buttonVariants({ variant, size }), wrap && "h-auto max-w-full whitespace-normal break-words py-1.5", wrap && (size === "sm" || size === "compact" || size === "compactIcon"
|
|
151
152
|
? "min-h-8"
|
|
152
|
-
: size === "comfortable" ? "min-h-[46px]" : "min-h-control"), className), ref: ref, "aria-busy": loading || undefined, "aria-disabled": isDisabled || undefined, "data-loading": loading ? "" : undefined, "data-disabled": isDisabled ? "" : undefined, onClick: handleClick, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onFocus: handleFocus, onBlur: handleBlur, tabIndex: isDisabled && asChild ? -1 : tabIndex, disabled: !asChild ? isDisabled : undefined, ...props, children: content }));
|
|
153
|
+
: size === "comfortable" ? "min-h-[46px]" : "min-h-control"), contentAlign === "start" && "justify-start text-left", className), ref: ref, "aria-busy": loading || undefined, "aria-disabled": isDisabled || undefined, "data-loading": loading ? "" : undefined, "data-disabled": isDisabled ? "" : undefined, onClick: handleClick, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onFocus: handleFocus, onBlur: handleBlur, tabIndex: isDisabled && asChild ? -1 : tabIndex, disabled: !asChild ? isDisabled : undefined, ...props, children: content }));
|
|
153
154
|
});
|
|
154
155
|
Button.displayName = "Button";
|
|
155
156
|
export { Button, buttonVariants };
|
package/dist/checkbox.js
CHANGED
|
@@ -24,9 +24,9 @@ const Checkbox = React.forwardRef(({ className, indeterminate = false, size = "m
|
|
|
24
24
|
innerRef.current.indeterminate = indeterminate;
|
|
25
25
|
}
|
|
26
26
|
}, [indeterminate]);
|
|
27
|
-
const box = (_jsxs("span", { className: cn("relative inline-
|
|
27
|
+
const box = (_jsxs("span", { className: cn("relative inline-flex shrink-0 items-center justify-center rounded-sm border bg-background align-middle text-primary-foreground transition-colors", sizeClasses[size], (checked || indeterminate) && !disabled
|
|
28
28
|
? "border-primary bg-primary"
|
|
29
|
-
: "border-input", disabled && "cursor-not-allowed opacity-50", label && alignToStart && startAlignedOffsetClasses[size], !label && className), children: [_jsx("input", { ref: innerRef, type: "checkbox", id: inputId, checked: checked, disabled: disabled, className: "absolute inset-0 h-full w-full cursor-pointer appearance-none opacity-0 disabled:cursor-not-allowed", ...props }), indeterminate ? (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round", className: "pointer-events-none
|
|
29
|
+
: "border-input", disabled && "cursor-not-allowed opacity-50", label && alignToStart && startAlignedOffsetClasses[size], !label && className), children: [_jsx("input", { ref: innerRef, type: "checkbox", id: inputId, checked: checked, disabled: disabled, className: "absolute inset-0 h-full w-full cursor-pointer appearance-none opacity-0 disabled:cursor-not-allowed", ...props }), indeterminate ? (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round", className: "pointer-events-none h-3 w-3", "aria-hidden": "true", children: _jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" }) })) : checked ? (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round", strokeLinejoin: "round", className: "pointer-events-none h-3 w-3", "aria-hidden": "true", children: _jsx("polyline", { points: "20 6 9 17 4 12" }) })) : null] }));
|
|
30
30
|
if (!label)
|
|
31
31
|
return box;
|
|
32
32
|
return (_jsxs("label", { htmlFor: inputId, className: cn("inline-flex cursor-pointer gap-2 text-sm", alignToStart ? "items-start" : "items-center", disabled && "cursor-not-allowed opacity-50", className), children: [box, _jsxs("div", { className: "flex min-w-0 flex-col gap-0.5 select-none", children: [_jsx(Typography, { as: "span", variant: "bodySm", children: label }), description ? (_jsx(Typography, { as: "p", variant: "caption", color: "muted", children: description })) : null] })] }));
|
package/dist/checklist.js
CHANGED
|
@@ -16,15 +16,17 @@ export function ChecklistSection({ title, progress, action, children, defaultOpe
|
|
|
16
16
|
}
|
|
17
17
|
export function ChecklistSelection({ title, description, typeOptions, subtypeOptions, type, subtype, onTypeChange, onSubtypeChange, disabled, loading, action }) {
|
|
18
18
|
const Icon = loading ? LoaderCircle : ClipboardList;
|
|
19
|
-
return _jsxs("div", { "data-slot": "checklist-selection", "aria-busy": loading, className: "flex flex-col items-center gap-4 rounded-lg bg-muted/30 p-6 text-center", children: [_jsx(Icon, { "aria-hidden": "true", className: cn("size-8", loading && "animate-spin") }), _jsxs("div", { className: "space-y-2", children: [_jsx("p", { className: "text-sm font-medium", children: title }), _jsx("p", { className: "text-sm text-muted-foreground", children: description })] }), _jsxs("div", { className: "
|
|
19
|
+
return _jsxs("div", { "data-slot": "checklist-selection", "aria-busy": loading, className: "flex flex-col items-center gap-4 rounded-lg bg-muted/30 p-6 text-center", children: [_jsx(Icon, { "aria-hidden": "true", className: cn("size-8", loading && "animate-spin") }), _jsxs("div", { className: "space-y-2", children: [_jsx("p", { className: "text-sm font-medium", children: title }), _jsx("p", { className: "text-sm text-muted-foreground", children: description })] }), _jsxs("div", { className: "grid w-full max-w-md grid-cols-1 gap-[var(--layout-page-control-gap)] sm:grid-cols-2", children: [_jsx(Select, { label: "Type", hideLabel: true, placeholder: "Type", options: typeOptions, value: type, onValueChange: onTypeChange, disabled: disabled || loading }), _jsx(Select, { label: "Sub-Type", hideLabel: true, placeholder: "Sub-Type", options: subtypeOptions, value: subtype, onValueChange: onSubtypeChange, disabled: disabled || loading || !type })] }), action] });
|
|
20
20
|
}
|
|
21
21
|
export function ChecklistQuestion({ label, options, value, onValueChange, disabled, remarks, onRemarksChange, remarksLimit = 500, remarksMaxLength, remarksRequired = false, evidence, evidenceCount = 0 }) {
|
|
22
22
|
const name = React.useId();
|
|
23
23
|
const [showRemarks, setShowRemarks] = React.useState(false);
|
|
24
24
|
const [showEvidence, setShowEvidence] = React.useState(false);
|
|
25
|
+
const remarksVisible = disabled ? Boolean(remarks.trim()) : showRemarks;
|
|
26
|
+
const evidenceVisible = disabled ? evidenceCount > 0 : showEvidence;
|
|
25
27
|
const remarksToggleLabel = showRemarks ? "Hide remarks" : "Show remarks";
|
|
26
28
|
const evidenceToggleLabel = showEvidence ? "Hide photos" : "Show photos";
|
|
27
|
-
return _jsxs("div", { "data-slot": "checklist-question", className: "space-y-4 rounded-xl border border-border bg-card p-4 shadow-xs", children: [_jsx("p", { id: `${name}-label`, className: "text-sm font-medium", children: label }), _jsxs("div", { className: "flex flex-wrap items-center gap-6", children: [_jsx("fieldset", { disabled: disabled, "aria-labelledby": `${name}-label`, className: cn("flex min-w-0 border-0 p-0", disabled && "opacity-50"), children: options.map((option, index) => _jsxs("label", { className: cn("relative cursor-pointer border border-input px-2.5 py-2 text-sm font-medium focus-within:z-10 focus-within:ring-2 focus-within:ring-ring", index > 0 && "-ml-px", index === 0 && "rounded-l-md", index === options.length - 1 && "rounded-r-md", disabled && "cursor-not-allowed", value === option.value ? option.tone === "negative" ? "bg-destructive/20" : option.tone === "positive" ? "bg-primary/20" : "bg-muted" : "bg-background"), children: [_jsx("input", { className: "absolute inset-0 size-full cursor-inherit opacity-0", type: "radio", name: name, value: option.value, checked: value === option.value, onChange: () => onValueChange(option.value) }), option.label] }, option.value)) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Tooltip, { content: remarksToggleLabel, children: _jsx(Button, { type: "button", variant: "ghost", size: "compactIcon", "aria-label": remarksToggleLabel, "aria-expanded": showRemarks, "aria-controls": `${name}-remarks`, onClick: () => setShowRemarks((open) => !open), children: _jsx(MessageSquareText, { "aria-hidden": "true", size: 16 }) }) }), evidence ? _jsx(Tooltip, { content: `${evidenceToggleLabel}${evidenceCount > 0 ? ` (${evidenceCount})` : ""}`, children: _jsx(Button, { type: "button", variant: "ghost", size: "compactIcon", "aria-label": evidenceToggleLabel, "aria-expanded": showEvidence, "aria-controls": `${name}-evidence`, onClick: () => setShowEvidence((open) => !open), children: _jsx(Camera, { "aria-hidden": "true", size: 16 }) }) }) : null] })] }),
|
|
29
|
+
return _jsxs("div", { "data-slot": "checklist-question", className: "space-y-4 rounded-xl border border-border bg-card p-4 shadow-xs", children: [_jsx("p", { id: `${name}-label`, className: "text-sm font-medium", children: label }), _jsxs("div", { className: "flex flex-wrap items-center gap-6", children: [_jsx("fieldset", { disabled: disabled, "aria-labelledby": `${name}-label`, className: cn("flex min-w-0 border-0 p-0", disabled && "opacity-50"), children: options.map((option, index) => _jsxs("label", { className: cn("relative cursor-pointer border border-input px-2.5 py-2 text-sm font-medium focus-within:z-10 focus-within:ring-2 focus-within:ring-ring", index > 0 && "-ml-px", index === 0 && "rounded-l-md", index === options.length - 1 && "rounded-r-md", disabled && "cursor-not-allowed", value === option.value ? option.tone === "negative" ? "bg-destructive/20" : option.tone === "positive" ? "bg-primary/20" : "bg-muted" : "bg-background"), children: [_jsx("input", { className: "absolute inset-0 size-full cursor-inherit opacity-0", type: "radio", name: name, value: option.value, checked: value === option.value, onChange: () => onValueChange(option.value) }), option.label] }, option.value)) }), !disabled ? _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Tooltip, { content: remarksToggleLabel, children: _jsx(Button, { type: "button", variant: "ghost", size: "compactIcon", "aria-label": remarksToggleLabel, "aria-expanded": showRemarks, "aria-controls": `${name}-remarks`, onClick: () => setShowRemarks((open) => !open), children: _jsx(MessageSquareText, { "aria-hidden": "true", size: 16 }) }) }), evidence ? _jsx(Tooltip, { content: `${evidenceToggleLabel}${evidenceCount > 0 ? ` (${evidenceCount})` : ""}`, children: _jsx(Button, { type: "button", variant: "ghost", size: "compactIcon", "aria-label": evidenceToggleLabel, "aria-expanded": showEvidence, "aria-controls": `${name}-evidence`, onClick: () => setShowEvidence((open) => !open), children: _jsx(Camera, { "aria-hidden": "true", size: 16 }) }) }) : null] }) : null] }), evidenceVisible && evidence ? _jsx("div", { id: `${name}-evidence`, children: evidence }) : null, remarksVisible ? _jsx("div", { id: `${name}-remarks`, children: disabled ? _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-sm font-medium", children: "Remarks" }), _jsx("p", { className: "whitespace-pre-wrap text-sm text-muted-foreground", children: remarks })] }) : _jsx(Textarea, { label: "Remarks", hideLabel: true, placeholder: "Type...", value: remarks, "aria-required": remarksRequired, rows: 5, onChange: (event) => onRemarksChange(event.target.value), maxWords: remarksMaxLength === undefined ? remarksLimit : undefined, maxLength: remarksMaxLength, showCount: remarksMaxLength !== undefined }) }) : null] });
|
|
28
30
|
}
|
|
29
31
|
export function ChecklistAttachments({ items, onRemove }) {
|
|
30
32
|
return _jsx("div", { className: "flex flex-wrap gap-2", children: items.map((item) => _jsxs("div", { className: "relative", children: [_jsx("a", { href: item.url, target: "_blank", rel: "noreferrer", children: _jsx("img", { src: item.url, alt: item.name, className: "size-20 rounded-md border border-border object-cover" }) }), onRemove ? _jsx(Button, { variant: "outline", size: "compactIcon", "aria-label": `Remove ${item.name}`, onClick: () => onRemove(item.key), children: _jsx(Trash2, { "aria-hidden": "true", size: 16 }) }) : null] }, item.key)) });
|
package/dist/dialog.d.ts
CHANGED
|
@@ -10,6 +10,10 @@ export interface DialogAction extends Omit<ButtonProps, "children"> {
|
|
|
10
10
|
type DialogRenderable = React.ReactNode | ((helpers: DialogRenderHelpers) => React.ReactNode);
|
|
11
11
|
export interface DialogProps {
|
|
12
12
|
variant?: "default" | "confirmation" | "routingConfirmation";
|
|
13
|
+
/** Standard dialogs are 512px wide; larger sizes are for complex editors. */
|
|
14
|
+
size?: "standard" | "large" | "extraLarge" | "full";
|
|
15
|
+
/** Optional minimum height for dialogs whose content changes between steps. */
|
|
16
|
+
minHeight?: React.CSSProperties["minHeight"];
|
|
13
17
|
open?: boolean;
|
|
14
18
|
defaultOpen?: boolean;
|
|
15
19
|
onOpenChange?: (open: boolean) => void;
|
|
@@ -27,5 +31,5 @@ export interface DialogProps {
|
|
|
27
31
|
/** Whether a pointer interaction outside the dialog closes it. */
|
|
28
32
|
closeOnOutsideClick?: boolean;
|
|
29
33
|
}
|
|
30
|
-
declare function Dialog({ variant, open, defaultOpen, onOpenChange, modal, trigger, title, description, children, footer, primaryAction, secondaryAction, contentClassName, bodyClassName, hideCloseButton, closeOnOutsideClick, }: DialogProps): import("react/jsx-runtime").JSX.Element;
|
|
34
|
+
declare function Dialog({ variant, size, minHeight, open, defaultOpen, onOpenChange, modal, trigger, title, description, children, footer, primaryAction, secondaryAction, contentClassName, bodyClassName, hideCloseButton, closeOnOutsideClick, }: DialogProps): import("react/jsx-runtime").JSX.Element;
|
|
31
35
|
export { Dialog };
|
package/dist/dialog.js
CHANGED
|
@@ -32,7 +32,7 @@ function renderDialogAction({ action, close, defaultVariant, }) {
|
|
|
32
32
|
}
|
|
33
33
|
}, children: label }));
|
|
34
34
|
}
|
|
35
|
-
function Dialog({ variant = "default", open, defaultOpen, onOpenChange, modal, trigger, title, description, children, footer, primaryAction, secondaryAction, contentClassName, bodyClassName, hideCloseButton = false, closeOnOutsideClick = false, }) {
|
|
35
|
+
function Dialog({ variant = "default", size = "standard", minHeight, open, defaultOpen, onOpenChange, modal, trigger, title, description, children, footer, primaryAction, secondaryAction, contentClassName, bodyClassName, hideCloseButton = false, closeOnOutsideClick = false, }) {
|
|
36
36
|
const [isOpen, setIsOpen] = useControllableOpen({
|
|
37
37
|
open,
|
|
38
38
|
defaultOpen,
|
|
@@ -46,14 +46,14 @@ function Dialog({ variant = "default", open, defaultOpen, onOpenChange, modal, t
|
|
|
46
46
|
const hasFooter = Boolean(customFooter || primaryAction || secondaryAction);
|
|
47
47
|
const isCompactConfirmation = variant === "confirmation";
|
|
48
48
|
const isRoutingConfirmation = variant === "routingConfirmation";
|
|
49
|
-
return (_jsxs(DialogRoot, { open: isOpen, onOpenChange: setIsOpen, modal: modal, children: [trigger ? _jsx(DialogTrigger, { asChild: true, children: trigger }) : null, _jsxs(DialogSurface, { ...(!description ? { "aria-describedby": undefined } : {}), className: cn(isCompactConfirmation &&
|
|
50
|
-
"
|
|
51
|
-
"
|
|
49
|
+
return (_jsxs(DialogRoot, { open: isOpen, onOpenChange: setIsOpen, modal: modal, children: [trigger ? _jsx(DialogTrigger, { asChild: true, children: trigger }) : null, _jsxs(DialogSurface, { ...(!description ? { "aria-describedby": undefined } : {}), style: minHeight !== undefined ? { minHeight } : undefined, className: cn(isCompactConfirmation &&
|
|
50
|
+
"gap-0 overflow-hidden rounded-xl p-0", isRoutingConfirmation &&
|
|
51
|
+
"gap-0 rounded-overlay p-[var(--space-8)]", minHeight !== undefined && "flex flex-col", "w-[calc(100vw-32px)]", { standard: "max-w-[512px]", large: "max-w-3xl", extraLarge: "max-w-5xl", full: "max-w-[96rem]" }[size], contentClassName), hideCloseButton: hideCloseButton || isCompactConfirmation || isRoutingConfirmation, onPointerDownOutside: (event) => {
|
|
52
52
|
if (!closeOnOutsideClick) {
|
|
53
53
|
event.preventDefault();
|
|
54
54
|
}
|
|
55
55
|
}, children: [_jsxs("div", { className: cn(isCompactConfirmation && "flex items-start gap-4 p-4"), children: [isCompactConfirmation ? (_jsx("div", { className: "rounded-lg bg-muted p-2", children: _jsx(CircleAlert, { "aria-hidden": "true", className: "size-6" }) })) : null, _jsxs(DialogHeader, { className: cn(isRoutingConfirmation && "gap-3 space-y-0"), children: [_jsx(DialogTitle, { className: cn(isRoutingConfirmation && "text-2xl leading-8"), children: title }), description ? (_jsx(DialogDescription, { className: cn(isRoutingConfirmation &&
|
|
56
|
-
"text-base leading-6 text-foreground"), children: description })) : null] })] }), body ? (_jsx("div", { className: cn("grid gap-4", isRoutingConfirmation && "mt-3 gap-[var(--space-8)]", bodyClassName), children: body })) : null, hasFooter ? (_jsx(DialogFooter, { className: cn(isCompactConfirmation && "border-t bg-muted/30 p-4", isRoutingConfirmation && "mt-[var(--space-8)] gap-3"), children: customFooter !== null && customFooter !== void 0 ? customFooter : (_jsxs(_Fragment, { children: [secondaryAction
|
|
56
|
+
"text-base leading-6 text-foreground"), children: description })) : null] })] }), body ? (_jsx("div", { className: cn("grid gap-4", minHeight !== undefined && "flex-1", isRoutingConfirmation && "mt-3 gap-[var(--space-8)]", bodyClassName), children: body })) : null, hasFooter ? (_jsx(DialogFooter, { className: cn(isCompactConfirmation && "border-t bg-muted/30 p-4", isRoutingConfirmation && "mt-[var(--space-8)] gap-3", minHeight !== undefined && "mt-auto"), children: customFooter !== null && customFooter !== void 0 ? customFooter : (_jsxs(_Fragment, { children: [secondaryAction
|
|
57
57
|
? renderDialogAction({
|
|
58
58
|
action: secondaryAction,
|
|
59
59
|
close: helpers.close,
|
package/dist/dropdown-button.js
CHANGED
|
@@ -12,6 +12,7 @@ const splitButtonDividerClassNames = {
|
|
|
12
12
|
outline: "before:bg-[hsl(var(--button-outline-border))]",
|
|
13
13
|
secondary: "before:bg-secondary-foreground/20",
|
|
14
14
|
ghost: "before:bg-border",
|
|
15
|
+
destructiveGhost: "before:bg-destructive/30",
|
|
15
16
|
link: "before:bg-interactive/25",
|
|
16
17
|
};
|
|
17
18
|
function DropdownButton({ label, leadingIcon, children, className, contentClassName, dropdownAriaLabel, menuWidth = "trigger", align = "end", alignOffset, avoidCollisions, collisionPadding, defaultOpen, disabled, hideWhenDetached, loading, loadingIndicator, loadingText, modal, onClick, onOpenChange, open, side, sideOffset = 4, split: splitProp, sticky, type = "button", variant, size, ...buttonProps }) {
|
package/dist/empty-state.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import * as React from "react";
|
|
|
2
2
|
export interface EmptyStateProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
3
3
|
/** Muted panel treatment for section empty states. */
|
|
4
4
|
variant?: "default" | "muted";
|
|
5
|
+
/** Controls spacing between the icon, copy, and action. */
|
|
6
|
+
density?: "default" | "compact";
|
|
5
7
|
message: React.ReactNode;
|
|
6
8
|
description?: React.ReactNode;
|
|
7
9
|
icon?: React.ComponentType<{
|
|
@@ -9,5 +11,5 @@ export interface EmptyStateProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
9
11
|
}>;
|
|
10
12
|
action?: React.ReactNode;
|
|
11
13
|
}
|
|
12
|
-
declare function EmptyState({ variant, message, description, icon: Icon, action, className, ...props }: EmptyStateProps): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
declare function EmptyState({ variant, density, message, description, icon: Icon, action, className, ...props }: EmptyStateProps): import("react/jsx-runtime").JSX.Element;
|
|
13
15
|
export { EmptyState };
|
package/dist/empty-state.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from "./lib/utils";
|
|
3
3
|
import { Typography } from "./typography";
|
|
4
|
-
function EmptyState({ variant = "default", message, description, icon: Icon, action, className, ...props }) {
|
|
5
|
-
return (_jsxs("div", { className: cn("flex flex-col items-center justify-center whitespace-normal px-6 py-10 text-center", variant === "muted" && "rounded-lg bg-muted/30", className), ...props, children: [Icon ? (_jsx(Icon, { className: "
|
|
4
|
+
function EmptyState({ variant = "default", density = "default", message, description, icon: Icon, action, className, ...props }) {
|
|
5
|
+
return (_jsxs("div", { className: cn("flex flex-col items-center justify-center whitespace-normal px-6 py-10 text-center", variant === "muted" && "rounded-lg bg-muted/30", className), ...props, children: [Icon ? (_jsx(Icon, { className: cn("h-10 w-10 stroke-[1.5] text-muted-foreground/70", density === "compact" ? "mb-2" : "mb-4") })) : null, _jsxs("div", { className: "max-w-full space-y-1.5", children: [_jsx(Typography, { as: "p", variant: "bodySm", className: "font-semibold", children: message }), description ? (_jsx(Typography, { as: "p", variant: "caption", className: "mx-auto max-w-md", children: description })) : null] }), action ? _jsx("div", { className: density === "compact" ? "pt-4" : "pt-6", children: action }) : null] }));
|
|
6
6
|
}
|
|
7
7
|
export { EmptyState };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { FileUploadStatus } from "./use-file-upload";
|
|
2
|
+
export declare function FileUploadStatusRow({ file, onCancel, onRemove, disabled, }: {
|
|
3
|
+
file: {
|
|
4
|
+
name: string;
|
|
5
|
+
type?: string;
|
|
6
|
+
size?: number | string;
|
|
7
|
+
status?: FileUploadStatus;
|
|
8
|
+
progress?: number;
|
|
9
|
+
error?: string;
|
|
10
|
+
};
|
|
11
|
+
onCancel?: () => void;
|
|
12
|
+
onRemove?: () => void;
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { AlertCircle, CheckCircle2, File, FileText, ImageIcon, Trash2, X } from "lucide-react";
|
|
4
|
+
import { Button } from "./button";
|
|
5
|
+
import { cn } from "./lib/utils";
|
|
6
|
+
import { Progress } from "./progress";
|
|
7
|
+
import { Typography } from "./typography";
|
|
8
|
+
function formatUploadFileSize(size) {
|
|
9
|
+
if (typeof size === "string")
|
|
10
|
+
return size;
|
|
11
|
+
if (typeof size !== "number")
|
|
12
|
+
return "";
|
|
13
|
+
if (size === 0)
|
|
14
|
+
return "0 B";
|
|
15
|
+
const k = 1024;
|
|
16
|
+
const sizes = ["B", "KB", "MB", "GB"];
|
|
17
|
+
const i = Math.min(Math.floor(Math.log(size) / Math.log(k)), sizes.length - 1);
|
|
18
|
+
return `${parseFloat((size / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
|
|
19
|
+
}
|
|
20
|
+
function getStatusIcon(status, type) {
|
|
21
|
+
if (status === "complete") {
|
|
22
|
+
return (_jsx(CheckCircle2, { "aria-hidden": "true", className: "size-5 text-[hsl(var(--status-success-foreground))]" }));
|
|
23
|
+
}
|
|
24
|
+
if (status === "error") {
|
|
25
|
+
return (_jsx(AlertCircle, { "aria-hidden": "true", className: "size-5 text-[hsl(var(--status-destructive-foreground))]" }));
|
|
26
|
+
}
|
|
27
|
+
const Icon = (type === null || type === void 0 ? void 0 : type.startsWith("image/")) ? ImageIcon : type === "application/pdf" ? FileText : File;
|
|
28
|
+
return (_jsx(Icon, { "aria-hidden": "true", className: "size-5 text-muted-foreground" }));
|
|
29
|
+
}
|
|
30
|
+
export function FileUploadStatusRow({ file, onCancel, onRemove, disabled = false, }) {
|
|
31
|
+
var _a;
|
|
32
|
+
const isUploading = file.status === "uploading";
|
|
33
|
+
const isPending = file.status === "pending";
|
|
34
|
+
const isComplete = file.status === "complete";
|
|
35
|
+
const isError = file.status === "error";
|
|
36
|
+
const sizeLabel = formatUploadFileSize(file.size);
|
|
37
|
+
const canCancel = (isUploading || isPending) && onCancel;
|
|
38
|
+
const canRemove = !isUploading && onRemove;
|
|
39
|
+
const action = canCancel ? "cancel" : canRemove ? "remove" : null;
|
|
40
|
+
return (_jsxs("li", { className: cn("rounded-control border bg-background px-2.5 py-2", isError && "border-destructive/80 bg-destructive/5"), children: [_jsxs("div", { className: cn("grid min-h-7 items-start gap-x-2", action
|
|
41
|
+
? "grid-cols-[1.5rem_minmax(0,1fr)_auto_1.75rem]"
|
|
42
|
+
: "grid-cols-[1.5rem_minmax(0,1fr)_auto]"), children: [_jsx("span", { className: "flex h-7 w-6 shrink-0 items-center justify-center", children: getStatusIcon(file.status, file.type) }), _jsxs("div", { className: "flex min-w-0 flex-col gap-1 py-1", children: [_jsx(Typography, { as: "p", variant: "label", className: cn("truncate", isComplete && "text-[hsl(var(--status-success-foreground))]"), title: file.name, children: file.name }), isError ? (_jsx(Typography, { as: "p", variant: "caption", className: "break-words text-xs leading-4 text-destructive", children: (_a = file.error) !== null && _a !== void 0 ? _a : "Upload failed" })) : null] }), sizeLabel ? (_jsx(Typography, { as: "span", variant: "bodySm", className: "col-start-3 mt-1.5 whitespace-nowrap text-xs leading-4 text-muted-foreground", children: sizeLabel })) : null, action ? (_jsx(Button, { type: "button", variant: "ghost", size: "compactIcon", className: "col-start-4 h-7 w-7 shrink-0", disabled: disabled, onClick: (event) => {
|
|
43
|
+
event.stopPropagation();
|
|
44
|
+
if (action === "cancel") {
|
|
45
|
+
onCancel === null || onCancel === void 0 ? void 0 : onCancel();
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
onRemove === null || onRemove === void 0 ? void 0 : onRemove();
|
|
49
|
+
}
|
|
50
|
+
}, "aria-label": action === "cancel"
|
|
51
|
+
? `Cancel upload for ${file.name}`
|
|
52
|
+
: `Remove ${file.name}`, children: action === "cancel" ? (_jsx(X, { "aria-hidden": "true" })) : (_jsx(Trash2, { "aria-hidden": "true" })) })) : null] }), (isUploading || isPending) ? (_jsx(Progress, { className: "mt-2", value: file.progress, size: "sm", label: `Uploading ${file.name}` })) : null] }));
|
|
53
|
+
}
|
|
@@ -17,6 +17,8 @@ export interface FileUploadStatusToastProps {
|
|
|
17
17
|
className?: string;
|
|
18
18
|
onCancel?: (file: FileUploadStatusToastFile) => void;
|
|
19
19
|
onRemove?: (file: FileUploadStatusToastFile) => void;
|
|
20
|
+
/** Show remove actions when onRemove is provided. Does not hide cancel. */
|
|
21
|
+
showRemoveButton?: boolean;
|
|
20
22
|
onDismiss?: () => void;
|
|
21
23
|
}
|
|
22
24
|
export interface ShowFileUploadStatusToastOptions extends FileUploadStatusToastProps {
|
|
@@ -24,5 +26,5 @@ export interface ShowFileUploadStatusToastOptions extends FileUploadStatusToastP
|
|
|
24
26
|
duration?: number;
|
|
25
27
|
dismissible?: boolean;
|
|
26
28
|
}
|
|
27
|
-
export declare function FileUploadStatusToast({ files, uploadProgress, title, description, className, onCancel, onRemove, onDismiss, }: FileUploadStatusToastProps): import("react/jsx-runtime").JSX.Element;
|
|
29
|
+
export declare function FileUploadStatusToast({ files, uploadProgress, title, description, className, onCancel, onRemove, showRemoveButton, onDismiss, }: FileUploadStatusToastProps): import("react/jsx-runtime").JSX.Element;
|
|
28
30
|
export declare function showFileUploadStatusToast({ id, duration, dismissible, onDismiss, ...props }: ShowFileUploadStatusToastOptions): string | number;
|
|
@@ -1,24 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
3
|
+
import { X } from "lucide-react";
|
|
4
|
+
import { FileUploadStatusRow } from "./file-upload-status-row";
|
|
4
5
|
import { Button } from "./button";
|
|
5
6
|
import { cn } from "./lib/utils";
|
|
6
|
-
import { Progress } from "./progress";
|
|
7
7
|
import { toast } from "./toaster";
|
|
8
8
|
import { Typography } from "./typography";
|
|
9
9
|
const DEFAULT_TOAST_ID = "file-upload-status";
|
|
10
|
-
function formatUploadFileSize(size) {
|
|
11
|
-
if (typeof size === "string")
|
|
12
|
-
return size;
|
|
13
|
-
if (typeof size !== "number")
|
|
14
|
-
return "";
|
|
15
|
-
if (size === 0)
|
|
16
|
-
return "0 B";
|
|
17
|
-
const k = 1024;
|
|
18
|
-
const sizes = ["B", "KB", "MB", "GB"];
|
|
19
|
-
const i = Math.min(Math.floor(Math.log(size) / Math.log(k)), sizes.length - 1);
|
|
20
|
-
return `${parseFloat((size / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
|
|
21
|
-
}
|
|
22
10
|
function getProgress(uploadProgress, fileName) {
|
|
23
11
|
if (!uploadProgress)
|
|
24
12
|
return undefined;
|
|
@@ -34,6 +22,7 @@ function normalizeFiles({ files, uploadProgress, }) {
|
|
|
34
22
|
const result = (_a = progress === null || progress === void 0 ? void 0 : progress.result) !== null && _a !== void 0 ? _a : file.result;
|
|
35
23
|
return {
|
|
36
24
|
...file,
|
|
25
|
+
name: file.name,
|
|
37
26
|
progress: (_c = (_b = progress === null || progress === void 0 ? void 0 : progress.progress) !== null && _b !== void 0 ? _b : file.progress) !== null && _c !== void 0 ? _c : 0,
|
|
38
27
|
status: (_e = (_d = progress === null || progress === void 0 ? void 0 : progress.status) !== null && _d !== void 0 ? _d : file.status) !== null && _e !== void 0 ? _e : "pending",
|
|
39
28
|
error: (_f = progress === null || progress === void 0 ? void 0 : progress.error) !== null && _f !== void 0 ? _f : file.error,
|
|
@@ -72,43 +61,15 @@ function getDefaultDescription(files) {
|
|
|
72
61
|
].filter(Boolean);
|
|
73
62
|
return parts.length > 0 ? parts.join(" / ") : undefined;
|
|
74
63
|
}
|
|
75
|
-
function
|
|
76
|
-
if (status === "complete") {
|
|
77
|
-
return (_jsx(CheckCircle2, { "aria-hidden": "true", className: "h-4 w-4 text-[hsl(var(--status-success-foreground))]" }));
|
|
78
|
-
}
|
|
79
|
-
if (status === "error") {
|
|
80
|
-
return (_jsx(AlertCircle, { "aria-hidden": "true", className: "h-4 w-4 text-[hsl(var(--status-destructive-foreground))]" }));
|
|
81
|
-
}
|
|
82
|
-
return (_jsx(Paperclip, { "aria-hidden": "true", className: "h-4 w-4 text-muted-foreground" }));
|
|
83
|
-
}
|
|
84
|
-
function FileUploadStatusRow({ file, onCancel, onRemove, }) {
|
|
85
|
-
var _a;
|
|
86
|
-
const isUploading = file.status === "uploading";
|
|
87
|
-
const isPending = file.status === "pending";
|
|
88
|
-
const isComplete = file.status === "complete";
|
|
89
|
-
const isError = file.status === "error";
|
|
90
|
-
const sizeLabel = formatUploadFileSize(file.size);
|
|
91
|
-
const canCancel = (isUploading || isPending) && onCancel;
|
|
92
|
-
const canRemove = (isComplete || isError) && onRemove;
|
|
93
|
-
const action = canCancel ? "cancel" : canRemove ? "remove" : null;
|
|
94
|
-
return (_jsxs("li", { className: cn("rounded-control border bg-background px-2.5 py-2", isError && "border-destructive/80 bg-destructive/5"), children: [_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: "mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center", children: getStatusIcon(file.status) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx(Typography, { as: "p", variant: "label", className: cn("truncate", isComplete && "text-[hsl(var(--status-success-foreground))]"), title: file.name, children: file.name }), isError ? (_jsx(Typography, { as: "p", variant: "bodySm", className: "mt-0.5 text-destructive", children: (_a = file.error) !== null && _a !== void 0 ? _a : "Upload failed" })) : null] }), sizeLabel ? (_jsx(Typography, { as: "span", variant: "bodySm", className: "shrink-0 text-muted-foreground", children: sizeLabel })) : null, action ? (_jsx(Button, { type: "button", variant: "ghost", size: "compactIcon", className: "h-7 w-7 shrink-0", onClick: () => {
|
|
95
|
-
if (action === "cancel") {
|
|
96
|
-
onCancel === null || onCancel === void 0 ? void 0 : onCancel(file);
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
onRemove === null || onRemove === void 0 ? void 0 : onRemove(file);
|
|
100
|
-
}
|
|
101
|
-
}, "aria-label": action === "cancel"
|
|
102
|
-
? `Cancel upload for ${file.name}`
|
|
103
|
-
: `Remove ${file.name}`, children: action === "cancel" ? (_jsx(X, { "aria-hidden": "true" })) : (_jsx(Trash2, { "aria-hidden": "true" })) })) : null] }), (isUploading || isPending) ? (_jsx(Progress, { className: "mt-2", value: file.progress, size: "sm", label: `Uploading ${file.name}` })) : null] }));
|
|
104
|
-
}
|
|
105
|
-
export function FileUploadStatusToast({ files, uploadProgress, title, description, className, onCancel, onRemove, onDismiss, }) {
|
|
64
|
+
export function FileUploadStatusToast({ files, uploadProgress, title, description, className, onCancel, onRemove, showRemoveButton = true, onDismiss, }) {
|
|
106
65
|
const normalizedFiles = normalizeFiles({ files, uploadProgress });
|
|
107
66
|
const resolvedTitle = title !== null && title !== void 0 ? title : getDefaultTitle(normalizedFiles);
|
|
108
67
|
const resolvedDescription = description !== null && description !== void 0 ? description : getDefaultDescription(normalizedFiles);
|
|
109
|
-
return (_jsxs("section", { className: cn("w-[min(40rem,calc(100vw-2rem))] overflow-hidden rounded-overlay border border-border bg-background text-foreground shadow-overlay", className), "aria-label": "File upload status", children: [_jsxs("div", { className: "flex items-start gap-3 border-b px-3 py-2.5", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx(Typography, { as: "p", variant: "label", className: "truncate", children: resolvedTitle }), resolvedDescription ? (_jsx(Typography, { as: "p", variant: "caption", className: "mt-0.5", children: resolvedDescription })) : null] }), onDismiss ? (_jsx(Button, { type: "button", variant: "ghost", size: "compactIcon", className: "h-7 w-7 shrink-0", onClick: onDismiss, "aria-label": "Dismiss file upload status", children: _jsx(X, { "aria-hidden": "true" }) })) : null] }), _jsx("ul", { className: "max-h-80 space-y-2 overflow-y-auto p-2.5", children: normalizedFiles.map((file, index) => {
|
|
68
|
+
return (_jsxs("section", { className: cn("w-[min(40rem,calc(100vw-2rem))] overflow-hidden rounded-overlay border border-border bg-background text-foreground shadow-overlay", className), "aria-label": "File upload status", children: [_jsxs("div", { className: "flex items-start gap-3 border-b px-3 py-2.5", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx(Typography, { as: "p", variant: "label", className: "truncate", children: resolvedTitle }), resolvedDescription ? (_jsx(Typography, { as: "p", variant: "caption", className: "mt-0.5 text-xs leading-4", children: resolvedDescription })) : null] }), onDismiss ? (_jsx(Button, { type: "button", variant: "ghost", size: "compactIcon", className: "h-7 w-7 shrink-0", onClick: onDismiss, "aria-label": "Dismiss file upload status", children: _jsx(X, { "aria-hidden": "true" }) })) : null] }), _jsx("ul", { className: "max-h-80 space-y-2 overflow-y-auto p-2.5", children: normalizedFiles.map((file, index) => {
|
|
110
69
|
var _a;
|
|
111
|
-
return (_jsx(FileUploadStatusRow, { file: file, onCancel: onCancel
|
|
70
|
+
return (_jsx(FileUploadStatusRow, { file: file, onCancel: onCancel ? () => onCancel(file) : undefined, onRemove: showRemoveButton && onRemove && (file.status === "complete" || file.status === "error")
|
|
71
|
+
? () => onRemove(file)
|
|
72
|
+
: undefined }, `${file.name}-${(_a = file.size) !== null && _a !== void 0 ? _a : "unknown"}-${index}`));
|
|
112
73
|
}) })] }));
|
|
113
74
|
}
|
|
114
75
|
export function showFileUploadStatusToast({ id = DEFAULT_TOAST_ID, duration = Number.POSITIVE_INFINITY, dismissible = true, onDismiss, ...props }) {
|
package/dist/file-upload.d.ts
CHANGED
|
@@ -6,7 +6,9 @@ export interface FileUploadProps {
|
|
|
6
6
|
onFilesChange: (files: File[]) => void;
|
|
7
7
|
value?: File[];
|
|
8
8
|
disabled?: boolean;
|
|
9
|
+
/** Show the remove action on file rows. */
|
|
10
|
+
showRemoveButton?: boolean;
|
|
9
11
|
className?: string;
|
|
10
12
|
uploadProgress?: Map<string, FileUploadProgress>;
|
|
11
13
|
}
|
|
12
|
-
export declare function FileUpload({ accept, maxSize, maxFiles, onFilesChange, value, disabled, className, uploadProgress, }: FileUploadProps): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export declare function FileUpload({ accept, maxSize, maxFiles, onFilesChange, value, disabled, showRemoveButton, className, uploadProgress, }: FileUploadProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/file-upload.js
CHANGED
|
@@ -2,14 +2,9 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { useCallback, useState } from "react";
|
|
5
|
-
import { File as FileIcon, ImageIcon } from "lucide-react";
|
|
6
|
-
import { CheckIcon } from "./animated-icons/check";
|
|
7
|
-
import { FileTextIcon } from "./animated-icons/file-text";
|
|
8
5
|
import { UploadIcon } from "./animated-icons/upload";
|
|
9
|
-
import { XIcon } from "./animated-icons/x";
|
|
10
|
-
import { Button } from "./button";
|
|
11
6
|
import { cn } from "./lib/utils";
|
|
12
|
-
import {
|
|
7
|
+
import { FileUploadStatusRow } from "./file-upload-status-row";
|
|
13
8
|
import { Typography } from "./typography";
|
|
14
9
|
const DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
15
10
|
function formatFileSize(bytes) {
|
|
@@ -20,16 +15,7 @@ function formatFileSize(bytes) {
|
|
|
20
15
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
21
16
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i];
|
|
22
17
|
}
|
|
23
|
-
function
|
|
24
|
-
if (file.type.startsWith("image/")) {
|
|
25
|
-
return _jsx(ImageIcon, { className: "size-6 shrink-0 text-muted-foreground" });
|
|
26
|
-
}
|
|
27
|
-
if (file.type === "application/pdf") {
|
|
28
|
-
return _jsx(FileTextIcon, { className: "shrink-0 text-muted-foreground", size: 24 });
|
|
29
|
-
}
|
|
30
|
-
return _jsx(FileIcon, { className: "size-6 shrink-0 text-muted-foreground" });
|
|
31
|
-
}
|
|
32
|
-
export function FileUpload({ accept, maxSize = DEFAULT_MAX_FILE_SIZE, maxFiles = 10, onFilesChange, value = [], disabled = false, className, uploadProgress, }) {
|
|
18
|
+
export function FileUpload({ accept, maxSize = DEFAULT_MAX_FILE_SIZE, maxFiles = 10, onFilesChange, value = [], disabled = false, showRemoveButton = true, className, uploadProgress, }) {
|
|
33
19
|
const [isDragOver, setIsDragOver] = useState(false);
|
|
34
20
|
const [error, setError] = useState(null);
|
|
35
21
|
const inputRef = React.useRef(null);
|
|
@@ -92,16 +78,15 @@ export function FileUpload({ accept, maxSize = DEFAULT_MAX_FILE_SIZE, maxFiles =
|
|
|
92
78
|
const fileInput = (_jsx("input", { ref: inputRef, type: "file", accept: accept, multiple: maxFiles > 1, onChange: handleInputChange, disabled: disabled, className: "hidden" }));
|
|
93
79
|
return (_jsxs("div", { className: cn("space-y-2", className), children: [fileInput, _jsxs("div", { onDrop: handleDrop, onDragOver: handleDragOver, onDragLeave: handleDragLeave, onClick: openFilePicker, className: cn("relative flex cursor-pointer flex-col items-center justify-center gap-1.5 rounded-surface border border-dashed p-4 transition-colors", isDragOver && !disabled
|
|
94
80
|
? "border-primary bg-primary/5"
|
|
95
|
-
: "border-muted-foreground/25 hover:border-muted-foreground/50", disabled && "cursor-not-allowed opacity-50"), children: [_jsx(UploadIcon, { className: cn(isDragOver ? "text-interactive" : "text-muted-foreground"), size: 28 }), _jsxs("div", { className: "text-center", children: [_jsx(Typography, { as: "p", variant: "label", children: isDragOver ? "Drop files here" : "Drag & drop files here" }), _jsxs(Typography, { as: "p", variant: "caption", className: "mt-1", children: ["or click to browse (max ", formatFileSize(maxSize), " each)"] })] })] }), error ? (_jsx(Typography, { as: "p", variant: "bodySm", className: "text-destructive", children: error })) : null, value.length > 0 ? (_jsx("
|
|
96
|
-
var _a;
|
|
81
|
+
: "border-muted-foreground/25 hover:border-muted-foreground/50", disabled && "cursor-not-allowed opacity-50"), children: [_jsx(UploadIcon, { className: cn(isDragOver ? "text-interactive" : "text-muted-foreground"), size: 28 }), _jsxs("div", { className: "text-center", children: [_jsx(Typography, { as: "p", variant: "label", children: isDragOver ? "Drop files here" : "Drag & drop files here" }), _jsxs(Typography, { as: "p", variant: "caption", className: "mt-1", children: ["or click to browse (max ", formatFileSize(maxSize), " each)"] })] })] }), error ? (_jsx(Typography, { as: "p", variant: "bodySm", className: "text-destructive", children: error })) : null, value.length > 0 ? (_jsx("ul", { className: "space-y-2", children: value.map((file, index) => {
|
|
97
82
|
const progress = uploadProgress === null || uploadProgress === void 0 ? void 0 : uploadProgress.get(file.name);
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
83
|
+
return (_jsx(FileUploadStatusRow, { file: {
|
|
84
|
+
name: file.name,
|
|
85
|
+
type: file.type,
|
|
86
|
+
size: file.size,
|
|
87
|
+
status: progress === null || progress === void 0 ? void 0 : progress.status,
|
|
88
|
+
progress: progress === null || progress === void 0 ? void 0 : progress.progress,
|
|
89
|
+
error: progress === null || progress === void 0 ? void 0 : progress.error,
|
|
90
|
+
}, disabled: disabled, onRemove: showRemoveButton ? () => removeFile(index) : undefined }, `${file.name}-${file.size}-${index}`));
|
|
106
91
|
}) })) : null] }));
|
|
107
92
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export type { AvatarProps, AvatarSize, AvatarProfileProps, AvatarRootProps, } fr
|
|
|
3
3
|
export { AvatarProfilePopover, AvatarProfilePopoverContent, AvatarProfilePopoverHeader, AvatarProfilePopoverSection, AvatarProfilePopoverFooter, AvatarProfilePopoverFooterAction, } from "./avatar-profile-popover";
|
|
4
4
|
export type { AvatarProfilePopoverProps, AvatarProfilePopoverContentProps, AvatarProfilePopoverHeaderProps, AvatarProfilePopoverSectionProps, AvatarProfilePopoverFooterProps, AvatarProfilePopoverFooterActionProps, } from "./avatar-profile-popover";
|
|
5
5
|
export * from "./animated-icons";
|
|
6
|
+
export { Inline } from "./inline";
|
|
6
7
|
export { Alert, AlertAction, AlertConsent, alertVariants, } from "./alert";
|
|
7
8
|
export type { AlertActionProps, AlertConsentProps, AlertProps, } from "./alert";
|
|
8
9
|
export { AlertStack } from "./alert-stack";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { Avatar, AvatarProfile, AvatarRoot, AvatarImage, } from "./avatar";
|
|
2
2
|
export { AvatarProfilePopover, AvatarProfilePopoverContent, AvatarProfilePopoverHeader, AvatarProfilePopoverSection, AvatarProfilePopoverFooter, AvatarProfilePopoverFooterAction, } from "./avatar-profile-popover";
|
|
3
3
|
export * from "./animated-icons";
|
|
4
|
+
export { Inline } from "./inline";
|
|
4
5
|
export { Alert, AlertAction, AlertConsent, alertVariants, } from "./alert";
|
|
5
6
|
export { AlertStack } from "./alert-stack";
|
|
6
7
|
export { ActivityCard, ActivityCardActor, ActivityCardContent, } from "./activity-card";
|
package/dist/inline.d.ts
ADDED
package/dist/inline.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/** A compact horizontal group with design-system spacing. */
|
|
3
|
+
export function Inline({ children, ...props }) {
|
|
4
|
+
return _jsx("div", { ...props, "data-slot": "inline", className: "flex items-center gap-[var(--space-2)]", children: children });
|
|
5
|
+
}
|
package/dist/label-value-grid.js
CHANGED
|
@@ -66,7 +66,9 @@ function LabelValueGrid({ items, columns = 4, density = "default", className, it
|
|
|
66
66
|
}, [columns, density, resolvedMinColumnWidth]);
|
|
67
67
|
return (_jsx("div", { ref: containerRef, className: cn("grid items-start", density === "compact"
|
|
68
68
|
? "auto-rows-auto gap-x-4 gap-y-3"
|
|
69
|
-
:
|
|
69
|
+
: resolvedColumns === 1
|
|
70
|
+
? "auto-rows-auto gap-x-6 gap-y-6"
|
|
71
|
+
: "auto-rows-fr gap-x-6 gap-y-6", className), style: {
|
|
70
72
|
...style,
|
|
71
73
|
gridTemplateColumns: `repeat(${resolvedColumns}, minmax(0, 1fr))`,
|
|
72
74
|
}, ...props, children: resolvedItems.map((item, index) => {
|
package/dist/popover.d.ts
CHANGED
|
@@ -13,7 +13,8 @@ export interface PopoverProps extends Pick<PopoverRootProps, "defaultOpen" | "mo
|
|
|
13
13
|
children: React.ReactNode;
|
|
14
14
|
/** Visible popover heading. Also labels the popover for assistive technology. */
|
|
15
15
|
title?: React.ReactNode;
|
|
16
|
-
|
|
16
|
+
/** List surfaces scroll at the overlay edge and stay within the available viewport height. */
|
|
17
|
+
surface?: "panel" | "menu" | "message" | "list";
|
|
17
18
|
/** Controls whether Tab stays within the popover or closes it and continues in the page Tab order. */
|
|
18
19
|
tabBehavior?: PopoverTabBehavior;
|
|
19
20
|
contentClassName?: string;
|
package/dist/popover.js
CHANGED
|
@@ -79,7 +79,7 @@ function Popover({ defaultOpen, modal, onOpenChange, open, trigger, virtualAncho
|
|
|
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: [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"
|
|
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", surface === "list" && "max-h-[min(18rem,var(--radix-popover-content-available-height))] overflow-y-auto overscroll-contain", 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
|
}
|
|
@@ -89,5 +89,5 @@ function PopoverTitle({ className, children, ...props }) {
|
|
|
89
89
|
export { Popover, PopoverTitle };
|
|
90
90
|
/** Read-only information rows for a popover, without implying menu actions. */
|
|
91
91
|
export function PopoverList({ items }) {
|
|
92
|
-
return (_jsx("ul", { "data-slot": "popover-list", className: "
|
|
92
|
+
return (_jsx("ul", { "data-slot": "popover-list", className: "space-y-3 text-sm text-foreground", children: items.map((item, index) => (_jsxs("li", { className: "flex items-start gap-2", children: [_jsx("span", { "aria-hidden": "true", className: "mt-2 size-1 shrink-0 rounded-full bg-muted-foreground" }), _jsx("span", { className: "min-w-0 break-words", children: item })] }, index))) }));
|
|
93
93
|
}
|
package/dist/rich-text-editor.js
CHANGED
|
@@ -122,7 +122,7 @@ export function replaceTemplateVariables(content, context) {
|
|
|
122
122
|
return result;
|
|
123
123
|
}
|
|
124
124
|
function ToolbarIconButton({ active, label, onClick, children, }) {
|
|
125
|
-
return (_jsx(Button, { type: "button", variant: "ghost", size: "
|
|
125
|
+
return (_jsx(Button, { type: "button", variant: "ghost", size: "compactIcon", className: cn(active && "bg-muted"), onClick: onClick, "aria-label": label, title: label, children: children }));
|
|
126
126
|
}
|
|
127
127
|
function getTemplateCategory(template) {
|
|
128
128
|
return template.category || DEFAULT_TEMPLATE_CATEGORY;
|
|
@@ -359,7 +359,7 @@ export function RichTextEditor({ variant = "default", placeholder = "Type messag
|
|
|
359
359
|
void handleSend();
|
|
360
360
|
}, children: [_jsx(SendIcon, { size: 16 }), isSending ? sendingLabel : sendLabel] }));
|
|
361
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-[
|
|
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-[148px] 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-[148px]", isDragging && "border-2 border-primary bg-primary/5", className), children: [header ? (_jsx("div", { "data-slot": "message-composer-header", className: "flex min-h-[32px] items-center px-[10px] py-[4px] 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 })] }) })) : 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
363
|
linkMenuPosition &&
|
|
364
364
|
typeof document !== "undefined" &&
|
|
365
365
|
createPortal(_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-[99]", onClick: () => {
|
|
@@ -370,7 +370,7 @@ export function RichTextEditor({ variant = "default", placeholder = "Type messag
|
|
|
370
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
371
|
top: linkMenuPosition.top,
|
|
372
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",
|
|
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", 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: "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: "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: "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
374
|
var _a;
|
|
375
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
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, renderSendAction({ compact: true })] })] })] }));
|
|
@@ -16,6 +16,8 @@ export interface RoutingActionPanelProps extends Omit<DashboardActionPanelProps,
|
|
|
16
16
|
onRouteValueChange?: (value: string) => void;
|
|
17
17
|
/** Workflow-specific fields rendered before the route target. */
|
|
18
18
|
supplementaryFields?: React.ReactNode;
|
|
19
|
+
/** Read-only review context in the scrollable body, above routing activity. */
|
|
20
|
+
context?: React.ReactNode;
|
|
19
21
|
remarks: string;
|
|
20
22
|
remarksDescription?: React.ReactNode;
|
|
21
23
|
remarksPlaceholder?: string;
|
|
@@ -34,5 +36,5 @@ export interface RoutingActionPanelProps extends Omit<DashboardActionPanelProps,
|
|
|
34
36
|
* Consumers provide workflow data and actions; the component owns the complete
|
|
35
37
|
* visual composition so different records render routing consistently.
|
|
36
38
|
*/
|
|
37
|
-
declare function RoutingActionPanel({ items, isLoading, loadingLabel, emptyMessage, emptyDescription, routeOptions, routeValue, routePlaceholder, routeDescription, routeDisabled, onRouteValueChange, supplementaryFields, remarks, remarksDescription, remarksPlaceholder, remarksRequired, remarksRows, validationMessages, onRemarksChange, submitLabel, submitDisabled, submitLoading, onSubmit, ...panelProps }: RoutingActionPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
39
|
+
declare function RoutingActionPanel({ items, isLoading, loadingLabel, emptyMessage, emptyDescription, routeOptions, routeValue, routePlaceholder, routeDescription, routeDisabled, onRouteValueChange, supplementaryFields, context, remarks, remarksDescription, remarksPlaceholder, remarksRequired, remarksRows, validationMessages, onRemarksChange, submitLabel, submitDisabled, submitLoading, onSubmit, ...panelProps }: RoutingActionPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
38
40
|
export { RoutingActionPanel };
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Route } from "lucide-react";
|
|
4
4
|
import { Button } from "./button";
|
|
5
|
+
import { EmptyState } from "./empty-state";
|
|
5
6
|
import { DashboardActionPanel, DashboardActionPanelEmptyState, DashboardRightPanelBody, } from "./layout";
|
|
6
7
|
import { RoutingTimeline, } from "./routing-timeline";
|
|
7
8
|
import { Select } from "./select";
|
|
@@ -12,8 +13,9 @@ import { Textarea } from "./textarea";
|
|
|
12
13
|
* Consumers provide workflow data and actions; the component owns the complete
|
|
13
14
|
* visual composition so different records render routing consistently.
|
|
14
15
|
*/
|
|
15
|
-
function RoutingActionPanel({ items, isLoading = false, loadingLabel = "Loading routing activity", emptyMessage = "No routing activity yet", emptyDescription = "Workflow routes will appear here.", routeOptions, routeValue, routePlaceholder = "Select Officer Role", routeDescription, routeDisabled = false, onRouteValueChange, supplementaryFields, remarks, remarksDescription, remarksPlaceholder = "Type remarks", remarksRequired = false, remarksRows = 2, validationMessages = [], onRemarksChange, submitLabel = "Route", submitDisabled = false, submitLoading = false, onSubmit, ...panelProps }) {
|
|
16
|
-
const
|
|
17
|
-
|
|
16
|
+
function RoutingActionPanel({ items, isLoading = false, loadingLabel = "Loading routing activity", emptyMessage = "No routing activity yet", emptyDescription = "Workflow routes will appear here.", routeOptions, routeValue, routePlaceholder = "Select Officer Role", routeDescription, routeDisabled = false, onRouteValueChange, supplementaryFields, context, remarks, remarksDescription, remarksPlaceholder = "Type remarks", remarksRequired = false, remarksRows = 2, validationMessages = [], onRemarksChange, submitLabel = "Route", submitDisabled = false, submitLoading = false, onSubmit, ...panelProps }) {
|
|
17
|
+
const RoutingEmptyState = context ? EmptyState : DashboardActionPanelEmptyState;
|
|
18
|
+
const footer = (_jsxs("div", { "data-slot": "routing-action-panel-form", className: "grid gap-[var(--space-4)]", children: [supplementaryFields, _jsx(Select, { label: "Route To", options: routeOptions, value: routeValue, placeholder: routePlaceholder, disabled: routeDisabled || submitLoading, description: routeDescription, onValueChange: onRouteValueChange }), _jsx(Textarea, { label: "Remarks", value: remarks, onChange: (event) => onRemarksChange(event.target.value), placeholder: remarksPlaceholder, description: remarksDescription, disabled: routeDisabled || submitLoading, required: remarksRequired, rows: remarksRows }), validationMessages.length > 0 ? (_jsxs("details", { className: "text-sm text-muted-foreground", children: [_jsxs("summary", { className: "cursor-pointer font-medium text-foreground", children: [validationMessages.length, " required ", validationMessages.length === 1 ? "item" : "items", " remaining \u2014 view details"] }), _jsx("ul", { className: "mt-2 max-h-40 list-disc space-y-1 overflow-y-auto pl-5", children: validationMessages.map((message, index) => _jsx("li", { children: message }, index)) })] })) : null, _jsx("div", { className: "flex justify-end", children: _jsxs(Button, { type: "button", size: "default", disabled: routeDisabled || submitDisabled || validationMessages.length > 0, loading: submitLoading, loadingText: submitLabel, onClick: onSubmit, children: [_jsx(Route, { "aria-hidden": "true", className: "size-4" }), submitLabel] }) })] }));
|
|
19
|
+
return (_jsx(DashboardActionPanel, { headerSpacing: "comfortable", ...panelProps, children: _jsxs(DashboardRightPanelBody, { spacing: "comfortable", topInset: "none", footerVariant: "separated", footer: footer, children: [context ? (_jsx("div", { "data-slot": "routing-action-panel-context", className: "mb-[var(--space-4)] grid min-w-0 gap-[var(--space-3)] whitespace-pre-wrap [overflow-wrap:anywhere]", children: context })) : null, isLoading ? (_jsx("div", { "data-slot": "routing-action-panel-loading", className: context ? "flex items-center justify-center py-[var(--space-4)]" : "absolute inset-0 flex items-center justify-center", children: _jsx(Spinner, { size: "md", label: loadingLabel }) })) : items.length === 0 ? (_jsx(RoutingEmptyState, { icon: Route, message: emptyMessage, description: emptyDescription })) : (_jsx(RoutingTimeline, { density: "compact", items: items }))] }) }));
|
|
18
20
|
}
|
|
19
21
|
export { RoutingActionPanel };
|
package/dist/routing-timeline.js
CHANGED
|
@@ -87,7 +87,7 @@ function RoutingTimeline({ items, emptyMessage = "No routing activity yet.", den
|
|
|
87
87
|
? "gap-[var(--space-3)]"
|
|
88
88
|
: "gap-[24px]"), children: [item.from ? (_jsx(RoutingTimelineHandoffEndpoint, { value: item.from, label: item.fromLabel })) : null, item.from && item.to ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "sr-only", children: "to" }), _jsx(ArrowRight, { "aria-hidden": "true", "data-slot": "routing-timeline-handoff-arrow", className: cn("shrink-0 text-foreground", density === "compact" ? "size-4" : "size-6") })] })) : null, item.to ? (_jsx(RoutingTimelineHandoffEndpoint, { value: item.to, label: item.toLabel })) : null] })) : null, remark ? (_jsxs("div", { "data-slot": "routing-timeline-remark", className: cn("bg-muted", density === "compact"
|
|
89
89
|
? "rounded-control p-[var(--space-2)]"
|
|
90
|
-
: "rounded-md p-[12px]"), children: [detailLabel ? (_jsx(Typography, { as: "p", variant: "bodySm", className: "font-semibold text-instructional", children: detailLabel })) : null, _jsx(Typography, { as: "div", variant: "bodySm", className: cn(detailLabel &&
|
|
90
|
+
: "rounded-md p-[12px]"), children: [detailLabel ? (_jsx(Typography, { as: "p", variant: "bodySm", className: "font-semibold text-instructional", children: detailLabel })) : null, _jsx(Typography, { as: "div", variant: "bodySm", className: cn("whitespace-pre-wrap [overflow-wrap:anywhere]", detailLabel &&
|
|
91
91
|
(density === "compact"
|
|
92
92
|
? "mt-[var(--space-1)]"
|
|
93
93
|
: "mt-[4px]")), children: remark })] })) : null] })) : null }) }, item.id));
|
package/dist/table.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ declare const TableCell: React.ForwardRefExoticComponent<React.TdHTMLAttributes<
|
|
|
29
29
|
wrap?: boolean;
|
|
30
30
|
treatWidthAsMinWidth?: boolean;
|
|
31
31
|
containOverflow?: boolean;
|
|
32
|
+
preserveButtonPadding?: boolean;
|
|
32
33
|
} & React.RefAttributes<HTMLTableCellElement>>;
|
|
33
34
|
declare const TableRowHeader: React.ForwardRefExoticComponent<React.ThHTMLAttributes<HTMLTableCellElement> & {
|
|
34
35
|
width?: string;
|
|
@@ -93,6 +94,8 @@ export interface TableColumn<Row> {
|
|
|
93
94
|
skeleton?: TableSkeletonConfig;
|
|
94
95
|
headerClassName?: string;
|
|
95
96
|
cellClassName?: string;
|
|
97
|
+
/** Keeps each design-system button's own horizontal padding in body cells. */
|
|
98
|
+
preserveButtonPadding?: boolean;
|
|
96
99
|
/** Renders this column's body cells as semantic row headers. */
|
|
97
100
|
rowHeader?: boolean;
|
|
98
101
|
/** Pins the column to the left or right edge while the table scrolls horizontally. */
|
package/dist/table.js
CHANGED
|
@@ -171,7 +171,8 @@ const TableHead = React.forwardRef(({ className, style, width, minWidth, maxWidt
|
|
|
171
171
|
treatWidthAsMinWidth,
|
|
172
172
|
}), ...props })));
|
|
173
173
|
TableHead.displayName = "TableHead";
|
|
174
|
-
const TableCell = React.forwardRef(({ className, style, width, minWidth, maxWidth, wrap = false, treatWidthAsMinWidth, containOverflow, ...props }, ref) => (_jsx("td", { ref: ref, className: cn("h-12 px-4 py-[4px] align-middle text-card-foreground [&:has([role=checkbox])]:pr-0
|
|
174
|
+
const TableCell = React.forwardRef(({ className, style, width, minWidth, maxWidth, wrap = false, treatWidthAsMinWidth, containOverflow, preserveButtonPadding = false, ...props }, ref) => (_jsx("td", { ref: ref, className: cn("h-12 px-4 py-[4px] align-middle text-card-foreground [&:has([role=checkbox])]:pr-0", !preserveButtonPadding &&
|
|
175
|
+
"[&:not([data-slot=table-empty-cell])_[data-slot=button]]:px-0", wrap ? "whitespace-normal" : "whitespace-nowrap", overflowContainmentClassName({ containOverflow, wrap }), className), style: getTableColumnSizingStyle({
|
|
175
176
|
style,
|
|
176
177
|
width,
|
|
177
178
|
minWidth,
|
|
@@ -742,11 +743,11 @@ function Table({ accessibleLabel, columns, rows, getRowKey, emptyState = (_jsx(T
|
|
|
742
743
|
const Cell = column.rowHeader
|
|
743
744
|
? TableRowHeader
|
|
744
745
|
: TableCell;
|
|
745
|
-
return (_jsx(Cell, { width: columnWidth, minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled), maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled), wrap: column.wrap, treatWidthAsMinWidth: !isColumnResizingEnabled, containOverflow: isColumnResizingEnabled, className: cn(alignClassName(column.align), stickyClassName(column.sticky, "cell", getStickyShadowVisibility(column.sticky, stickyScrollState)), column.cellClassName), children: value }, column.key));
|
|
746
|
+
return (_jsx(Cell, { width: columnWidth, minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled), maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled), wrap: column.wrap, treatWidthAsMinWidth: !isColumnResizingEnabled, containOverflow: isColumnResizingEnabled, preserveButtonPadding: column.preserveButtonPadding, className: cn(alignClassName(column.align), stickyClassName(column.sticky, "cell", getStickyShadowVisibility(column.sticky, stickyScrollState)), column.cellClassName), children: value }, column.key));
|
|
746
747
|
})] }), isExpanded ? (_jsx(TableRow, { id: expandedContentId, "data-slot": "table-expanded-row", className: "bg-[hsl(var(--table-row-striped-background))] hover:bg-[hsl(var(--table-row-striped-background))]", children: _jsx(TableCell, { colSpan: Math.max(visibleColumns.length +
|
|
747
748
|
(hasRowIndicatorColumn ? 1 : 0) +
|
|
748
749
|
(hasExpansionColumn ? 1 : 0), 1), className: "h-auto whitespace-normal p-4", children: rowExpansion === null || rowExpansion === void 0 ? void 0 : rowExpansion.renderExpandedContent(row, rowIndex) }) })) : null] }, rowKey));
|
|
749
|
-
})) : (_jsx(TableRow, { className: "cursor-default hover:bg-card", children: _jsx(TableCell, { colSpan: Math.max(visibleColumns.length +
|
|
750
|
+
})) : (_jsx(TableRow, { className: "cursor-default hover:bg-card", children: _jsx(TableCell, { "data-slot": "table-empty-cell", colSpan: Math.max(visibleColumns.length +
|
|
750
751
|
(hasRowIndicatorColumn ? 1 : 0) +
|
|
751
752
|
(hasExpansionColumn ? 1 : 0), 1), className: "p-6", children: resolvedEmptyState }) })) })] }), _jsx("div", { role: "status", className: "sr-only", children: loading ? loadingLabel : null })] }));
|
|
752
753
|
const reorderableContainer = reorderableColumns ? (_jsx(DndContext, { sensors: columnReorderSensors, collisionDetection: closestCenter, onDragEnd: handleColumnDragEnd, children: _jsx(SortableContext, { items: reorderableColumnIds, strategy: horizontalListSortingStrategy, children: container }) })) : (container);
|
package/dist/typography.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export interface TypographyProps extends React.HTMLAttributes<HTMLElement>, Vari
|
|
|
7
7
|
as?: keyof React.JSX.IntrinsicElements;
|
|
8
8
|
asChild?: boolean;
|
|
9
9
|
/** Override the built-in text colour. Use "inherit" to inherit from a parent container. */
|
|
10
|
-
color?: "inherit" | "foreground" | "muted";
|
|
10
|
+
color?: "inherit" | "foreground" | "muted" | "primary";
|
|
11
11
|
}
|
|
12
12
|
declare const Typography: React.ForwardRefExoticComponent<TypographyProps & React.RefAttributes<HTMLElement>>;
|
|
13
13
|
export { Typography, typographyVariants };
|
package/dist/typography.js
CHANGED
|
@@ -39,9 +39,11 @@ const Typography = React.forwardRef(({ as, asChild = false, className, variant,
|
|
|
39
39
|
? "text-inherit"
|
|
40
40
|
: color === "foreground"
|
|
41
41
|
? "text-foreground"
|
|
42
|
-
: color === "
|
|
43
|
-
? "text-
|
|
44
|
-
:
|
|
42
|
+
: color === "primary"
|
|
43
|
+
? "text-primary"
|
|
44
|
+
: color === "muted"
|
|
45
|
+
? "text-instructional"
|
|
46
|
+
: undefined;
|
|
45
47
|
return (_jsx(Comp, { ref: ref, className: cn(typographyVariants({ variant: resolvedVariant }), colorClass, className), ...props }));
|
|
46
48
|
});
|
|
47
49
|
Typography.displayName = "Typography";
|