@neasg/design-system 0.4.113 → 0.4.118

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/button.d.ts CHANGED
@@ -10,6 +10,8 @@ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElemen
10
10
  * Use this for links or custom interactive elements that should look like a button.
11
11
  */
12
12
  asChild?: boolean;
13
+ /** Allow the label to wrap within its container and the button to grow in height. */
14
+ wrap?: boolean;
13
15
  /**
14
16
  * Shows a loading indicator, disables interaction, and sets `aria-busy`.
15
17
  * When used with `asChild`, the component also prevents click handling and sets `aria-disabled`.
package/dist/button.js CHANGED
@@ -102,7 +102,7 @@ function attachAnimatedIconRefs(node, refs) {
102
102
  }
103
103
  return node;
104
104
  }
105
- const Button = React.forwardRef(({ className, variant, size, asChild = false, loading = false, loadingText, loadingIndicator, disabled, children, onClick, onMouseEnter, onMouseLeave, onFocus, onBlur, tabIndex, ...props }, ref) => {
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
106
  const Comp = asChild ? Slot : "button";
107
107
  const isDisabled = disabled || loading;
108
108
  const loadingLabel = loadingText !== null && loadingText !== void 0 ? loadingText : children;
@@ -147,7 +147,9 @@ const Button = React.forwardRef(({ className, variant, size, asChild = false, lo
147
147
  stopAnimatedIcons();
148
148
  onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
149
149
  };
150
- return (_jsx(Comp, { "data-slot": "button", className: cn(buttonVariants({ variant, size, 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 }));
150
+ 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
+ ? "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 }));
151
153
  });
152
154
  Button.displayName = "Button";
153
155
  export { Button, buttonVariants };
package/dist/card.d.ts CHANGED
@@ -13,6 +13,8 @@ export interface CardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "t
13
13
  accent?: "primary";
14
14
  /** Optional visual affordance shown in the top-right corner, such as a filter icon. */
15
15
  icon?: React.ReactNode;
16
+ /** Interactive action beside the card title. */
17
+ headerAction?: React.ReactNode;
16
18
  /** Replaces title/stat/subMetric with skeleton placeholders while data is loading. */
17
19
  loading?: boolean;
18
20
  }
package/dist/card.js CHANGED
@@ -55,7 +55,7 @@ function attachCardIconRef(node, ref) {
55
55
  ref: mergeRefs(node.ref, refCallback),
56
56
  });
57
57
  }
58
- const Card = React.forwardRef(({ title, description, variant = "default", size = "default", stat, subMetric, footer, active, accent, icon, loading = false, children, className, onClick, onKeyDown, onMouseEnter, onMouseLeave, onFocus, onBlur, role, tabIndex, "aria-pressed": ariaPressed, ...props }, ref) => {
58
+ const Card = React.forwardRef(({ title, description, variant = "default", size = "default", stat, subMetric, footer, active, accent, icon, headerAction, loading = false, children, className, onClick, onKeyDown, onMouseEnter, onMouseLeave, onFocus, onBlur, role, tabIndex, "aria-pressed": ariaPressed, ...props }, ref) => {
59
59
  const interactive = Boolean(onClick);
60
60
  const hasStat = stat !== undefined;
61
61
  const iconRef = React.useRef(null);
@@ -111,7 +111,7 @@ const Card = React.forwardRef(({ title, description, variant = "default", size =
111
111
  "border-[hsl(var(--card-border))] shadow-[inset_4px_0_0_0_hsl(var(--primary))]", interactive &&
112
112
  "cursor-pointer transition-[border-color,background-color,box-shadow,transform] duration-150 ease-out hover:border-primary/50 hover:bg-primary/5 hover:shadow-md motion-safe:hover:-translate-y-0.5 motion-safe:active:translate-y-0 motion-safe:active:scale-[0.99] focus-visible:border-primary/50 focus-visible:bg-primary/5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background", className), ...props, children: [_jsxs("div", { className: cn("relative flex flex-col gap-2", size === "default" && "p-4", size === "compact" && "p-3", showIcon && size === "default" && "pr-12", showIcon && size === "compact" && "pr-10"), children: [showIcon ? (_jsx("span", { "aria-hidden": "true", "data-slot": "card-icon", className: cn("pointer-events-none absolute right-3 top-3 inline-flex size-7 items-center justify-center rounded-control border border-[hsl(var(--card-border))] bg-background/80 text-muted-foreground shadow-sm transition-[background-color,color,transform] duration-150 ease-out group-hover/card:scale-105 group-hover/card:bg-highlight group-hover/card:text-highlight-foreground group-focus-visible/card:bg-highlight group-focus-visible/card:text-highlight-foreground motion-reduce:transition-colors [&_svg]:size-3.5", size === "compact" && "right-2.5 top-2.5 size-6 [&_svg]:size-3", active &&
113
113
  "border-primary/30 bg-highlight text-highlight-foreground", variant === "gradient" &&
114
- "border-white/25 bg-white/10 text-current group-hover/card:bg-white/20 group-hover/card:text-current group-focus-visible/card:bg-white/20 group-focus-visible/card:text-current"), children: renderedIcon })) : null, loading ? (_jsx(Skeleton, { className: "h-3.5 w-24" })) : (renderedTitle), loading
114
+ "border-white/25 bg-white/10 text-current group-hover/card:bg-white/20 group-hover/card:text-current group-focus-visible/card:bg-white/20 group-focus-visible/card:text-current"), children: renderedIcon })) : null, loading ? (_jsx(Skeleton, { className: "h-3.5 w-24" })) : (headerAction ? (_jsxs("div", { className: "flex items-start justify-between gap-3", children: [renderedTitle, _jsx("div", { className: "shrink-0", children: headerAction })] })) : renderedTitle), loading
115
115
  ? description != null && _jsx(Skeleton, { className: "h-5 w-40" })
116
116
  : renderedDescription, loading ? (stat !== undefined && _jsx(Skeleton, { className: "h-8 w-20" })) : stat ? (_jsx(Typography, { as: "p", variant: "title", color: variant === "gradient" ? "inherit" : undefined, children: stat })) : null, loading ? (subMetric !== undefined && _jsx(Skeleton, { className: "h-5 w-28" })) : subMetric ? (_jsx(Typography, { as: "p", variant: "caption", color: variant === "gradient" ? "inherit" : undefined, className: variant === "gradient" ? "opacity-80" : undefined, children: subMetric })) : null] }), children ? (_jsx("div", { className: cn(size === "default" && "p-4 pt-0", size === "compact" && "p-3 pt-0"), children: children })) : null, footer ? (_jsx("div", { className: cn("flex items-center", size === "default" && "p-4 pt-0", size === "compact" && "p-3 pt-0"), children: footer })) : null] }));
117
117
  });
package/dist/checklist.js CHANGED
@@ -6,6 +6,7 @@ import { Camera, ChevronDown, ClipboardList, LoaderCircle, MessageSquareText, Tr
6
6
  import { Button } from "./button";
7
7
  import { Select } from "./select";
8
8
  import { Textarea } from "./textarea";
9
+ import { Tooltip } from "./tooltip";
9
10
  import { cn } from "./lib/utils";
10
11
  export function ChecklistPanel({ title, action, children }) {
11
12
  return _jsxs("div", { "data-slot": "checklist-panel", className: "rounded-xl border border-border bg-card p-4 shadow-xs", children: [_jsxs("div", { className: "mb-4 flex items-center justify-between gap-4", children: [_jsx("h3", { className: "text-base font-medium text-card-foreground", children: title }), action] }), children] });
@@ -19,11 +20,11 @@ export function ChecklistSelection({ title, description, typeOptions, subtypeOpt
19
20
  }
20
21
  export function ChecklistQuestion({ label, options, value, onValueChange, disabled, remarks, onRemarksChange, remarksLimit = 500, remarksMaxLength, remarksRequired = false, evidence, evidenceCount = 0 }) {
21
22
  const name = React.useId();
22
- React.useEffect(() => { if (remarksRequired)
23
- setShowRemarks(true); }, [remarksRequired, value]);
24
- const [showRemarks, setShowRemarks] = React.useState(Boolean(remarks));
23
+ const [showRemarks, setShowRemarks] = React.useState(false);
25
24
  const [showEvidence, setShowEvidence] = React.useState(false);
26
- return _jsxs("div", { "data-slot": "checklist-question", className: "rounded-xl border border-border bg-card p-4 shadow-xs", children: [_jsx("p", { id: `${name}-label`, className: "mb-4 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(Button, { variant: "ghost", size: "compactIcon", "aria-label": "Add remarks", disabled: disabled, onClick: () => setShowRemarks(true), children: _jsx(MessageSquareText, { "aria-hidden": "true", size: 16 }) }), _jsx(Button, { variant: "ghost", size: "compactIcon", "aria-label": "Add photos", disabled: disabled, onClick: () => setShowEvidence((open) => !open), children: _jsx(Camera, { "aria-hidden": "true", size: 16 }) })] })] }), showRemarks || remarks ? _jsx("div", { className: "mt-2 space-y-2", children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx("div", { className: "min-w-0 flex-1", children: _jsx(Textarea, { label: "Remarks", hideLabel: true, placeholder: "Type...", value: remarks, disabled: disabled, rows: 2, onChange: (event) => onRemarksChange(event.target.value), maxWords: remarksMaxLength === undefined ? remarksLimit : undefined, maxLength: remarksMaxLength, showCount: remarksMaxLength !== undefined }) }), _jsx(Button, { variant: "ghost", size: "compactIcon", "aria-label": "Remove remarks", disabled: disabled, onClick: () => { onRemarksChange(""); setShowRemarks(false); }, children: _jsx(Trash2, { "aria-hidden": "true", size: 16 }) })] }) }) : null, (showEvidence || evidenceCount > 0) && evidence ? _jsx("div", { className: "mt-2", children: evidence }) : null] });
25
+ const remarksToggleLabel = showRemarks ? "Hide remarks" : "Show remarks";
26
+ 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] })] }), showEvidence && evidence ? _jsx("div", { id: `${name}-evidence`, children: evidence }) : null, showRemarks ? _jsx("div", { id: `${name}-remarks`, children: _jsx(Textarea, { label: "Remarks", hideLabel: true, placeholder: "Type...", value: remarks, disabled: disabled, "aria-required": remarksRequired, rows: 5, onChange: (event) => onRemarksChange(event.target.value), maxWords: remarksMaxLength === undefined ? remarksLimit : undefined, maxLength: remarksMaxLength, showCount: remarksMaxLength !== undefined }) }) : null] });
27
28
  }
28
29
  export function ChecklistAttachments({ items, onRemove }) {
29
30
  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
@@ -9,7 +9,7 @@ export interface DialogAction extends Omit<ButtonProps, "children"> {
9
9
  }
10
10
  type DialogRenderable = React.ReactNode | ((helpers: DialogRenderHelpers) => React.ReactNode);
11
11
  export interface DialogProps {
12
- variant?: "default" | "confirmation";
12
+ variant?: "default" | "confirmation" | "routingConfirmation";
13
13
  open?: boolean;
14
14
  defaultOpen?: boolean;
15
15
  onOpenChange?: (open: boolean) => void;
package/dist/dialog.js CHANGED
@@ -44,11 +44,16 @@ function Dialog({ variant = "default", open, defaultOpen, onOpenChange, modal, t
44
44
  const body = resolveDialogRenderable(children, helpers);
45
45
  const customFooter = resolveDialogRenderable(footer, helpers);
46
46
  const hasFooter = Boolean(customFooter || primaryAction || secondaryAction);
47
- 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(variant === "confirmation" && "max-w-sm gap-0 overflow-hidden rounded-xl p-0", contentClassName), hideCloseButton: hideCloseButton || variant === "confirmation", onPointerDownOutside: (event) => {
47
+ const isCompactConfirmation = variant === "confirmation";
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
+ "max-w-sm gap-0 overflow-hidden rounded-xl p-0", isRoutingConfirmation &&
51
+ "max-w-[40rem] gap-0 rounded-overlay p-[var(--space-8)]", contentClassName), hideCloseButton: hideCloseButton || isCompactConfirmation || isRoutingConfirmation, onPointerDownOutside: (event) => {
48
52
  if (!closeOnOutsideClick) {
49
53
  event.preventDefault();
50
54
  }
51
- }, children: [_jsxs("div", { className: cn(variant === "confirmation" && "flex items-start gap-4 p-4"), children: [variant === "confirmation" ? _jsx("div", { className: "rounded-lg bg-muted p-2", children: _jsx(CircleAlert, { "aria-hidden": "true", className: "size-6" }) }) : null, _jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: title }), description ? _jsx(DialogDescription, { children: description }) : null] })] }), body ? _jsx("div", { className: cn("grid gap-4", bodyClassName), children: body }) : null, hasFooter ? (_jsx(DialogFooter, { className: variant === "confirmation" ? "border-t bg-muted/30 p-4" : undefined, children: customFooter !== null && customFooter !== void 0 ? customFooter : (_jsxs(_Fragment, { children: [secondaryAction
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
52
57
  ? renderDialogAction({
53
58
  action: secondaryAction,
54
59
  close: helpers.close,
@@ -4,7 +4,7 @@ import { Button } from "./button";
4
4
  import { TableAddRow } from "./table-add-row";
5
5
  import { TrashIcon } from "./animated-icons/trash";
6
6
  import { Checkbox } from "./checkbox";
7
- import { EmptyState } from "./empty-state";
7
+ import { TableEmptyState } from "./table-empty-state";
8
8
  import { InputControl } from "./input-control";
9
9
  import { TableRoot as Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "./table";
10
10
  import { cn } from "./lib/utils";
@@ -121,7 +121,7 @@ function EditableTable({ value, onChange, columns, isEditing = false, className,
121
121
  const checkableColumnWidth = checkableHeader ? "9rem" : "48px";
122
122
  return (_jsxs("div", { className: cn("space-y-2", className), children: [_jsx("div", { className: "overflow-x-auto rounded-surface border", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [checkable ? (_jsx(TableHead, { width: checkableColumnWidth, minWidth: checkableColumnWidth, children: checkableHeader })) : null, columns.map((column, index) => (_jsx(TableHead, { width: column.width, minWidth: getColumnMinWidth(column), children: _jsx("div", { className: "whitespace-nowrap", children: column.header }) }, `header-${index}`))), hasActionsColumn ? (_jsx(TableHead, { width: renderRowActions ? undefined : "72px", minWidth: renderRowActions ? "120px" : "72px" })) : null] }) }), _jsx(TableBody, { children: value.length === 0 ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: columns.length +
123
123
  (checkable ? 1 : 0) +
124
- (hasActionsColumn ? 1 : 0), className: "p-0", children: _jsx(EmptyState, { message: "No data available", description: "There are no rows to display in this table yet.", className: "border-0 bg-transparent px-0 py-[var(--space-6)]" }) }) })) : (value.map((row, rowIndex) => (_jsxs(TableRow, { className: rowIndex % 2 === 0
124
+ (hasActionsColumn ? 1 : 0), className: "p-0", children: _jsx(TableEmptyState, {}) }) })) : (value.map((row, rowIndex) => (_jsxs(TableRow, { className: rowIndex % 2 === 0
125
125
  ? "bg-[hsl(var(--table-row-striped-background))]"
126
126
  : undefined, children: [checkable ? (_jsx(TableCell, { width: checkableColumnWidth, minWidth: checkableColumnWidth, children: _jsx("div", { className: "flex items-center", onClick: (event) => event.stopPropagation(), children: _jsx(Checkbox, { checked: Boolean(row[activeField]), disabled: !isEditing, onChange: isEditing
127
127
  ? (event) => handleToggleRow(rowIndex, event.currentTarget.checked)
@@ -1,5 +1,7 @@
1
1
  import * as React from "react";
2
2
  export interface EmptyStateProps extends React.HTMLAttributes<HTMLDivElement> {
3
+ /** Muted panel treatment for section empty states. */
4
+ variant?: "default" | "muted";
3
5
  message: React.ReactNode;
4
6
  description?: React.ReactNode;
5
7
  icon?: React.ComponentType<{
@@ -7,5 +9,5 @@ export interface EmptyStateProps extends React.HTMLAttributes<HTMLDivElement> {
7
9
  }>;
8
10
  action?: React.ReactNode;
9
11
  }
10
- declare function EmptyState({ message, description, icon: Icon, action, className, ...props }: EmptyStateProps): import("react/jsx-runtime").JSX.Element;
12
+ declare function EmptyState({ variant, message, description, icon: Icon, action, className, ...props }: EmptyStateProps): import("react/jsx-runtime").JSX.Element;
11
13
  export { EmptyState };
@@ -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({ message, description, icon: Icon, action, className, ...props }) {
5
- return (_jsxs("div", { className: cn("flex flex-col items-center justify-center px-6 py-10 text-center", className), ...props, children: [Icon ? (_jsx(Icon, { className: "mb-4 h-10 w-10 stroke-[1.5] text-muted-foreground/70" })) : null, _jsxs("div", { className: "space-y-1.5", children: [_jsx(Typography, { as: "p", variant: "bodySm", className: "font-semibold", children: message }), description ? (_jsx(Typography, { as: "p", variant: "caption", className: "max-w-md", children: description })) : null] }), action ? _jsx("div", { className: "pt-6", children: action }) : null] }));
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: "mb-4 h-10 w-10 stroke-[1.5] text-muted-foreground/70" })) : 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: "pt-6", children: action }) : null] }));
6
6
  }
7
7
  export { EmptyState };
@@ -22,12 +22,12 @@ function formatFileSize(bytes) {
22
22
  }
23
23
  function getFileIcon(file) {
24
24
  if (file.type.startsWith("image/")) {
25
- return _jsx(ImageIcon, { className: "h-4 w-4 text-muted-foreground" });
25
+ return _jsx(ImageIcon, { className: "size-6 shrink-0 text-muted-foreground" });
26
26
  }
27
27
  if (file.type === "application/pdf") {
28
- return _jsx(FileTextIcon, { className: "text-muted-foreground", size: 16 });
28
+ return _jsx(FileTextIcon, { className: "shrink-0 text-muted-foreground", size: 24 });
29
29
  }
30
- return _jsx(FileIcon, { className: "h-4 w-4 text-muted-foreground" });
30
+ return _jsx(FileIcon, { className: "size-6 shrink-0 text-muted-foreground" });
31
31
  }
32
32
  export function FileUpload({ accept, maxSize = DEFAULT_MAX_FILE_SIZE, maxFiles = 10, onFilesChange, value = [], disabled = false, className, uploadProgress, }) {
33
33
  const [isDragOver, setIsDragOver] = useState(false);
package/dist/index.d.ts CHANGED
@@ -40,6 +40,7 @@ export { CountBadge } from "./count-badge";
40
40
  export type { CountBadgeProps } from "./count-badge";
41
41
  export { StatusDot } from "./status-dot";
42
42
  export type { StatusDotProps, StatusDotSize, StatusDotVariant, } from "./status-dot";
43
+ export { TableEmptyState } from "./table-empty-state";
43
44
  export { EmptyState } from "./empty-state";
44
45
  export type { EmptyStateProps } from "./empty-state";
45
46
  export { EditableTable } from "./editable-table";
@@ -171,3 +172,4 @@ export { ChecklistAttachments, ChecklistPanel, ChecklistSection, ChecklistSelect
171
172
  export type { ChecklistAttachmentsProps, ChecklistPanelProps, ChecklistSectionProps, ChecklistSelectionProps, ChecklistQuestionProps, ChecklistQuestionOption } from "./checklist";
172
173
  export { TableAddRow, type TableAddRowProps } from "./table-add-row";
173
174
  export { PopoverList, type PopoverListProps } from "./popover";
175
+ export { StatusBanner, type StatusBannerProps } from "./status-banner";
package/dist/index.js CHANGED
@@ -20,6 +20,7 @@ export { ThemeSwitcher } from "./theme-switcher";
20
20
  export { DateInput } from "./date-input";
21
21
  export { CountBadge } from "./count-badge";
22
22
  export { StatusDot } from "./status-dot";
23
+ export { TableEmptyState } from "./table-empty-state";
23
24
  export { EmptyState } from "./empty-state";
24
25
  export { EditableTable } from "./editable-table";
25
26
  export { EditButton } from "./edit-button";
@@ -90,3 +91,4 @@ export { parseDateTime, formatDateTime } from "./lib/date-utils";
90
91
  export { ChecklistAttachments, ChecklistPanel, ChecklistSection, ChecklistSelection, ChecklistQuestion } from "./checklist";
91
92
  export { TableAddRow } from "./table-add-row";
92
93
  export { PopoverList } from "./popover";
94
+ export { StatusBanner } from "./status-banner";
@@ -0,0 +1,15 @@
1
+ import * as React from "react";
2
+ export interface StatusBannerProps extends React.HTMLAttributes<HTMLDivElement> {
3
+ label: string;
4
+ detail?: React.ReactNode;
5
+ icon?: React.ComponentType<{
6
+ className?: string;
7
+ "aria-hidden"?: React.AriaAttributes["aria-hidden"];
8
+ }>;
9
+ trailingIcon?: React.ComponentType<{
10
+ className?: string;
11
+ "aria-hidden"?: React.AriaAttributes["aria-hidden"];
12
+ }>;
13
+ }
14
+ /** Compact primary-colour status strip for an ongoing activity. */
15
+ export declare function StatusBanner({ label, detail, icon: Icon, trailingIcon: TrailingIcon, className, ...props }: StatusBannerProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "./lib/utils";
3
+ /** Compact primary-colour status strip for an ongoing activity. */
4
+ export function StatusBanner({ label, detail, icon: Icon, trailingIcon: TrailingIcon, className, ...props }) {
5
+ return (_jsxs("div", { ...props, className: cn("flex w-full items-center justify-center gap-2 rounded-none border border-border bg-primary px-4 py-1.5 text-sm text-primary-foreground shadow-sm", className), children: [Icon ? _jsx(Icon, { className: "h-4 w-4 shrink-0", "aria-hidden": true }) : null, _jsx("span", { className: "font-medium", children: label }), detail, TrailingIcon ? (_jsx(TrailingIcon, { className: "h-4 w-4 shrink-0 opacity-50", "aria-hidden": true })) : null] }));
6
+ }
@@ -0,0 +1,3 @@
1
+ import { type EmptyStateProps } from "./empty-state";
2
+ /** Shared empty-table presentation, with an icon and explanatory copy by default. */
3
+ export declare function TableEmptyState({ message, description, icon, ...props }: Partial<EmptyStateProps>): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Inbox } from "lucide-react";
3
+ import { EmptyState } from "./empty-state";
4
+ /** Shared empty-table presentation, with an icon and explanatory copy by default. */
5
+ export function TableEmptyState({ message = "No data available", description = "There are no rows to display in this table yet.", icon = Inbox, ...props }) {
6
+ return _jsx(EmptyState, { ...props, message: message, description: description, icon: icon });
7
+ }
package/dist/table.js CHANGED
@@ -6,7 +6,7 @@ import { CSS } from "@dnd-kit/utilities";
6
6
  import { ArrowUpDown, ChevronRight } from "lucide-react";
7
7
  import { ArrowDownIcon } from "./animated-icons/arrow-down";
8
8
  import { ArrowUpIcon } from "./animated-icons/arrow-up";
9
- import { EmptyState } from "./empty-state";
9
+ import { TableEmptyState } from "./table-empty-state";
10
10
  import { Pagination } from "./pagination";
11
11
  import { Skeleton } from "./skeleton";
12
12
  import { TableColumnFilterControl, } from "./table-column-filter";
@@ -399,7 +399,7 @@ function getColumnPairResizeRange(column, nextColumn, columnWidths) {
399
399
  maxWidth: Math.round(currentWidth + maxDelta),
400
400
  };
401
401
  }
402
- function Table({ accessibleLabel, columns, rows, getRowKey, emptyState = (_jsx(EmptyState, { message: "No data available", description: "There are no rows to display in this table yet.", className: "border-0 bg-transparent px-0 py-6" })), toolbar, caption, className, containerClassName, tableClassName, rowClassName, rowTone, maxHeight, minHeight, sorting, pagination, loading = false, loadingLabel = "Loading table data", loadingRows = 5, visibleColumnKeys, resizableColumns = false, columnResizeMode = "pair", columnWidths, defaultColumnWidths, onColumnWidthsChange, renderColumnResizeHandle, tableLayout, centerEmptyState = false, emptyStateMinHeight = DEFAULT_CENTERED_EMPTY_STATE_MIN_HEIGHT, striped = false, rowIndicator, rowExpansion, reorderableColumns = false, onColumnOrderChange, onScroll, style, ...props }) {
402
+ function Table({ accessibleLabel, columns, rows, getRowKey, emptyState = (_jsx(TableEmptyState, {})), toolbar, caption, className, containerClassName, tableClassName, rowClassName, rowTone, maxHeight, minHeight, sorting, pagination, loading = false, loadingLabel = "Loading table data", loadingRows = 5, visibleColumnKeys, resizableColumns = false, columnResizeMode = "pair", columnWidths, defaultColumnWidths, onColumnWidthsChange, renderColumnResizeHandle, tableLayout, centerEmptyState = false, emptyStateMinHeight = DEFAULT_CENTERED_EMPTY_STATE_MIN_HEIGHT, striped = false, rowIndicator, rowExpansion, reorderableColumns = false, onColumnOrderChange, onScroll, style, ...props }) {
403
403
  var _a;
404
404
  const visibleColumns = getVisibleColumns(columns, visibleColumnKeys);
405
405
  const hasRowIndicatorColumn = Boolean(rowIndicator);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neasg/design-system",
3
- "version": "0.4.113",
3
+ "version": "0.4.118",
4
4
  "description": "NEA shared design system primitives, theme tokens, and Storybook docs.",
5
5
  "repository": {
6
6
  "type": "git",