@neasg/design-system 0.2.1 → 0.3.0
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/badge.d.ts +3 -1
- package/dist/badge.js +13 -2
- package/dist/button.d.ts +1 -1
- package/dist/calendar.d.ts +8 -0
- package/dist/calendar.js +80 -0
- package/dist/card.d.ts +14 -0
- package/dist/card.js +9 -0
- package/dist/command-search.js +3 -2
- package/dist/date-input.d.ts +22 -0
- package/dist/date-input.js +61 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +6 -0
- package/dist/input-control.d.ts +1 -1
- package/dist/input-control.js +1 -1
- package/dist/label-value.js +2 -1
- package/dist/layout.js +2 -2
- package/dist/multi-select.d.ts +29 -0
- package/dist/multi-select.js +77 -0
- package/dist/otp-input.js +1 -1
- package/dist/pagination.js +2 -1
- package/dist/popover.d.ts +7 -0
- package/dist/popover.js +11 -0
- package/dist/select-primitive.d.ts +3 -1
- package/dist/select-primitive.js +3 -2
- package/dist/select.d.ts +1 -0
- package/dist/select.js +1 -1
- package/dist/tab-with-dropdown.d.ts +18 -0
- package/dist/tab-with-dropdown.js +70 -0
- package/dist/textarea.js +1 -1
- package/dist/toaster.d.ts +13 -0
- package/dist/toaster.js +18 -0
- package/dist/typography.d.ts +2 -0
- package/dist/typography.js +9 -2
- package/package.json +5 -1
package/dist/badge.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ declare const badgeVariants: (props?: ({
|
|
|
9
9
|
* surface the full label via `title`.
|
|
10
10
|
*/
|
|
11
11
|
export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {
|
|
12
|
+
/** Show a visible border matching the badge colour. */
|
|
13
|
+
bordered?: boolean;
|
|
12
14
|
}
|
|
13
|
-
declare function Badge({ className, variant, children, ...props }: BadgeProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
declare function Badge({ className, variant, bordered, children, ...props }: BadgeProps): import("react/jsx-runtime").JSX.Element;
|
|
14
16
|
export { Badge, badgeVariants };
|
package/dist/badge.js
CHANGED
|
@@ -19,12 +19,23 @@ const badgeVariants = cva("inline-flex max-w-[200px] items-center justify-center
|
|
|
19
19
|
variant: "secondary",
|
|
20
20
|
},
|
|
21
21
|
});
|
|
22
|
-
function Badge({ className, variant, children, ...props }) {
|
|
22
|
+
function Badge({ className, variant, bordered = false, children, ...props }) {
|
|
23
23
|
const textContent = typeof children === "string"
|
|
24
24
|
? children
|
|
25
25
|
: React.Children.toArray(children)
|
|
26
26
|
.filter((child) => typeof child === "string")
|
|
27
27
|
.join("");
|
|
28
|
-
|
|
28
|
+
const resolvedVariant = variant !== null && variant !== void 0 ? variant : "secondary";
|
|
29
|
+
const borderedClasses = {
|
|
30
|
+
secondary: "border-secondary-foreground/30",
|
|
31
|
+
muted: "border-muted-foreground/30",
|
|
32
|
+
destructive: "border-[hsl(var(--status-destructive-foreground))]/30",
|
|
33
|
+
outline: "border-foreground/30",
|
|
34
|
+
success: "border-[hsl(var(--status-success-foreground))]/30",
|
|
35
|
+
warning: "border-[hsl(var(--status-warning-foreground))]/30",
|
|
36
|
+
info: "border-[hsl(var(--status-info-foreground))]/30",
|
|
37
|
+
accent: "border-[hsl(var(--status-accent-foreground))]/30",
|
|
38
|
+
};
|
|
39
|
+
return (_jsx("div", { className: cn(badgeVariants({ variant }), bordered && borderedClasses[resolvedVariant], className), title: textContent || undefined, ...props, children: children }));
|
|
29
40
|
}
|
|
30
41
|
export { Badge, badgeVariants };
|
package/dist/button.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
3
3
|
declare const buttonVariants: (props?: ({
|
|
4
4
|
variant?: "link" | "default" | "outline" | "secondary" | "destructive" | "ghost" | null | undefined;
|
|
5
|
-
size?: "default" | "sm" | "
|
|
5
|
+
size?: "default" | "sm" | "lg" | "icon" | null | undefined;
|
|
6
6
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
7
|
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
8
8
|
/**
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { DayButton, DayPicker } from "react-day-picker";
|
|
3
|
+
import { Button } from "./button";
|
|
4
|
+
declare function CalendarDayButton({ className, day, modifiers, children, disabled, onClick, onFocus, onBlur, onKeyDown, onMouseEnter, onMouseLeave, tabIndex, type, "aria-label": ariaLabel, "aria-pressed": ariaPressed, "aria-selected": ariaSelected, }: React.ComponentProps<typeof DayButton>): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
declare function Calendar({ className, classNames, showOutsideDays, captionLayout, buttonVariant, formatters, components, ...props }: React.ComponentProps<typeof DayPicker> & {
|
|
6
|
+
buttonVariant?: React.ComponentProps<typeof Button>["variant"];
|
|
7
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export { Calendar, CalendarDayButton };
|
package/dist/calendar.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { DayPicker, getDefaultClassNames } from "react-day-picker";
|
|
5
|
+
import { buttonVariants } from "./button";
|
|
6
|
+
import { cn } from "./lib/utils";
|
|
7
|
+
function CalendarDayButton({ className, day, modifiers, children, disabled, onClick, onFocus, onBlur, onKeyDown, onMouseEnter, onMouseLeave, tabIndex, type, "aria-label": ariaLabel, "aria-pressed": ariaPressed, "aria-selected": ariaSelected, }) {
|
|
8
|
+
const defaultClassNames = getDefaultClassNames();
|
|
9
|
+
const ref = React.useRef(null);
|
|
10
|
+
React.useEffect(() => {
|
|
11
|
+
var _a;
|
|
12
|
+
if (modifiers.focused)
|
|
13
|
+
(_a = ref.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
14
|
+
}, [modifiers.focused]);
|
|
15
|
+
return (_jsx("button", { ref: ref, type: type, disabled: disabled, onClick: onClick, onFocus: onFocus, onBlur: onBlur, onKeyDown: onKeyDown, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, tabIndex: tabIndex, "aria-label": ariaLabel, "aria-pressed": ariaPressed, "aria-selected": ariaSelected, "data-day": day.date.toLocaleDateString(), "data-selected-single": modifiers.selected &&
|
|
16
|
+
!modifiers.range_start &&
|
|
17
|
+
!modifiers.range_end &&
|
|
18
|
+
!modifiers.range_middle, "data-range-start": modifiers.range_start, "data-range-end": modifiers.range_end, "data-range-middle": modifiers.range_middle, className: cn("inline-flex cursor-pointer items-center justify-center rounded-md text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", "data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 aspect-square w-full min-w-[--cell-size] font-normal leading-none data-[range-end=true]:rounded-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px]", defaultClassNames.day, className), children: children }));
|
|
19
|
+
}
|
|
20
|
+
function Calendar({ className, classNames, showOutsideDays = true, captionLayout = "label", buttonVariant = "ghost", formatters, components, ...props }) {
|
|
21
|
+
const defaultClassNames = getDefaultClassNames();
|
|
22
|
+
return (_jsx(DayPicker, { showOutsideDays: showOutsideDays, className: cn("bg-background group/calendar p-3 [--cell-size:2rem] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent", String.raw `rtl:**:[.rdp-button\_next>svg]:rotate-180`, String.raw `rtl:**:[.rdp-button\_previous>svg]:rotate-180`, className), captionLayout: captionLayout, formatters: {
|
|
23
|
+
formatMonthDropdown: (date) => date.toLocaleString("default", { month: "short" }),
|
|
24
|
+
...formatters,
|
|
25
|
+
}, classNames: {
|
|
26
|
+
root: cn("w-fit", defaultClassNames.root),
|
|
27
|
+
months: cn("relative flex flex-col gap-4 md:flex-row", defaultClassNames.months),
|
|
28
|
+
month: cn("flex w-full flex-col gap-4", defaultClassNames.month),
|
|
29
|
+
nav: cn("absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1", defaultClassNames.nav),
|
|
30
|
+
button_previous: cn(buttonVariants({ variant: buttonVariant }), "h-[--cell-size] w-[--cell-size] select-none p-0 aria-disabled:opacity-50", defaultClassNames.button_previous),
|
|
31
|
+
button_next: cn(buttonVariants({ variant: buttonVariant }), "h-[--cell-size] w-[--cell-size] select-none p-0 aria-disabled:opacity-50", defaultClassNames.button_next),
|
|
32
|
+
month_caption: cn("flex h-[--cell-size] w-full items-center justify-center px-[--cell-size]", defaultClassNames.month_caption),
|
|
33
|
+
dropdowns: cn("flex h-[--cell-size] w-full items-center justify-center gap-1.5 text-sm font-medium", defaultClassNames.dropdowns),
|
|
34
|
+
dropdown_root: cn("has-focus:border-ring border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] relative rounded-md border", defaultClassNames.dropdown_root),
|
|
35
|
+
dropdown: cn("bg-popover absolute inset-0 opacity-0", defaultClassNames.dropdown),
|
|
36
|
+
caption_label: cn("select-none font-medium", captionLayout === "label"
|
|
37
|
+
? "text-sm"
|
|
38
|
+
: "[&>svg]:text-muted-foreground flex h-8 items-center gap-1 rounded-md pl-2 pr-1 text-sm [&>svg]:size-3.5", defaultClassNames.caption_label),
|
|
39
|
+
table: "w-full border-collapse",
|
|
40
|
+
weekdays: cn("flex", defaultClassNames.weekdays),
|
|
41
|
+
weekday: cn("text-muted-foreground flex-1 select-none rounded-md text-[0.8rem] font-normal", defaultClassNames.weekday),
|
|
42
|
+
week: cn("mt-2 flex w-full", defaultClassNames.week),
|
|
43
|
+
week_number_header: cn("w-[--cell-size] select-none", defaultClassNames.week_number_header),
|
|
44
|
+
week_number: cn("text-muted-foreground select-none text-[0.8rem]", defaultClassNames.week_number),
|
|
45
|
+
day: cn("group/day relative aspect-square h-full w-full select-none p-0 text-center [&:first-child[data-selected=true]_button]:rounded-l-md [&:last-child[data-selected=true]_button]:rounded-r-md", defaultClassNames.day),
|
|
46
|
+
range_start: cn("bg-accent rounded-l-md", defaultClassNames.range_start),
|
|
47
|
+
range_middle: cn("rounded-none", defaultClassNames.range_middle),
|
|
48
|
+
range_end: cn("bg-accent rounded-r-md", defaultClassNames.range_end),
|
|
49
|
+
today: cn("bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none", defaultClassNames.today),
|
|
50
|
+
outside: cn("text-muted-foreground aria-selected:text-muted-foreground", defaultClassNames.outside),
|
|
51
|
+
disabled: cn("text-muted-foreground opacity-50", defaultClassNames.disabled),
|
|
52
|
+
hidden: cn("invisible", defaultClassNames.hidden),
|
|
53
|
+
...classNames,
|
|
54
|
+
}, components: {
|
|
55
|
+
Root: ({ className: rootClassName, rootRef, ...rootProps }) => (_jsx("div", { "data-slot": "calendar", ref: rootRef, className: cn(rootClassName), ...rootProps })),
|
|
56
|
+
Chevron: ({ className: chevronClassName, orientation }) => {
|
|
57
|
+
const svgProps = {
|
|
58
|
+
className: cn("size-4", chevronClassName),
|
|
59
|
+
viewBox: "0 0 24 24",
|
|
60
|
+
fill: "none",
|
|
61
|
+
stroke: "currentColor",
|
|
62
|
+
strokeWidth: 2,
|
|
63
|
+
strokeLinecap: "round",
|
|
64
|
+
strokeLinejoin: "round",
|
|
65
|
+
"aria-hidden": true,
|
|
66
|
+
};
|
|
67
|
+
if (orientation === "left") {
|
|
68
|
+
return _jsx("svg", { ...svgProps, children: _jsx("path", { d: "m15 18-6-6 6-6" }) });
|
|
69
|
+
}
|
|
70
|
+
if (orientation === "right") {
|
|
71
|
+
return _jsx("svg", { ...svgProps, children: _jsx("path", { d: "m9 18 6-6-6-6" }) });
|
|
72
|
+
}
|
|
73
|
+
return _jsx("svg", { ...svgProps, children: _jsx("path", { d: "m6 9 6 6 6-6" }) });
|
|
74
|
+
},
|
|
75
|
+
DayButton: CalendarDayButton,
|
|
76
|
+
WeekNumber: ({ children, ...weekNumberProps }) => (_jsx("td", { ...weekNumberProps, children: _jsx("div", { className: "flex size-[--cell-size] items-center justify-center text-center", children: children }) })),
|
|
77
|
+
...components,
|
|
78
|
+
}, ...props }));
|
|
79
|
+
}
|
|
80
|
+
export { Calendar, CalendarDayButton };
|
package/dist/card.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export interface CardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
|
|
3
|
+
title: React.ReactNode;
|
|
4
|
+
description?: React.ReactNode;
|
|
5
|
+
variant?: "default" | "elevated" | "gradient";
|
|
6
|
+
/** Large value displayed above the title — use for stat/metric cards. */
|
|
7
|
+
stat?: React.ReactNode;
|
|
8
|
+
/** Small supporting text below the stat or body — use for sub-metrics. */
|
|
9
|
+
subMetric?: React.ReactNode;
|
|
10
|
+
footer?: React.ReactNode;
|
|
11
|
+
active?: boolean;
|
|
12
|
+
}
|
|
13
|
+
declare const Card: React.ForwardRefExoticComponent<CardProps & React.RefAttributes<HTMLDivElement>>;
|
|
14
|
+
export { Card };
|
package/dist/card.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "./lib/utils";
|
|
5
|
+
import { Typography } from "./typography";
|
|
6
|
+
const Card = React.forwardRef(({ title, description, variant = "default", stat, subMetric, footer, active, children, className, onClick, ...props }, ref) => (_jsxs("div", { ref: ref, onClick: onClick, className: cn("rounded-xl border text-card-foreground", variant === "default" && "bg-card", variant === "elevated" && "bg-card shadow-sm", variant === "gradient" &&
|
|
7
|
+
"border-primary/20 bg-gradient-to-br from-primary to-primary/80 text-primary-foreground", active && "border-primary bg-primary/10 ring-1 ring-primary/20", onClick && "cursor-pointer transition-colors hover:border-primary", className), ...props, children: [_jsxs("div", { className: "flex flex-col gap-2 p-6", children: [_jsx(Typography, { as: "p", variant: "label", color: variant === "gradient" ? "inherit" : "muted", className: variant === "gradient" ? "opacity-80" : undefined, children: title }), description ? (_jsx(Typography, { as: "p", variant: "caption", color: variant === "gradient" ? "inherit" : undefined, className: variant === "gradient" ? "opacity-70" : undefined, children: description })) : null, stat ? (_jsx(Typography, { as: "p", variant: "title", color: variant === "gradient" ? "inherit" : undefined, children: stat })) : null, 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: "p-6 pt-0", children: children }) : null, footer ? (_jsx("div", { className: "flex items-center p-6 pt-0", children: footer })) : null] })));
|
|
8
|
+
Card.displayName = "Card";
|
|
9
|
+
export { Card };
|
package/dist/command-search.js
CHANGED
|
@@ -8,6 +8,7 @@ import { SearchIcon } from "./animated-icons/search";
|
|
|
8
8
|
import { DialogRoot, DialogSurface } from "./dialog-primitive";
|
|
9
9
|
import { FilterToggle } from "./filter-toggle";
|
|
10
10
|
import { cn } from "./lib/utils";
|
|
11
|
+
import { Typography } from "./typography";
|
|
11
12
|
import { SearchHighlight } from "./use-search-highlight";
|
|
12
13
|
function CommandSearchEmptyIcon({ className }) {
|
|
13
14
|
return _jsx(SearchIcon, { className: className, size: 20 });
|
|
@@ -58,13 +59,13 @@ function CommandSearch({ value, onValueChange, sections, children, className, is
|
|
|
58
59
|
}, [isOpen]);
|
|
59
60
|
return (_jsxs("div", { className: className, ...props, children: [_jsxs(Button, { type: "button", onClick: () => setIsOpen(true), variant: "ghost", className: cn(isCollapsed
|
|
60
61
|
? "h-10 w-full justify-center rounded-lg px-3 !font-normal text-muted-foreground hover:bg-white hover:text-foreground"
|
|
61
|
-
: "h-10 w-full min-w-[14rem] justify-start gap-3 rounded-lg border border-gray-200 bg-white px-3 !font-normal text-muted-foreground
|
|
62
|
+
: "h-10 w-full min-w-[14rem] justify-start gap-3 rounded-lg border border-gray-200 bg-white px-3 !font-normal text-muted-foreground hover:bg-white hover:text-foreground"), children: [_jsx(SearchIcon, { className: "shrink-0", size: 16 }), !isCollapsed ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "flex-1 text-left text-sm", children: triggerPlaceholder }), _jsx("kbd", { className: "hidden h-5 items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] text-muted-foreground sm:inline-flex", children: resolvedLabel })] })) : null] }), _jsx(DialogRoot, { open: isOpen, onOpenChange: setIsOpen, children: _jsx(DialogSurface, { className: cn("max-w-xl gap-0 overflow-hidden p-0 sm:max-w-xl", dialogClassName), hideCloseButton: true, children: _jsxs(Command, { shouldFilter: false, className: "rounded-none border-0", children: [_jsx(CommandInput, { ref: inputRef, value: value, onValueChange: onValueChange, placeholder: inputPlaceholder, className: "text-sm" }), allFilter || filters.length ? (_jsxs("div", { className: "flex items-center gap-1 border-b px-3 py-2", children: [allFilter ? (_jsx(FilterToggle, { selected: allFilter.selected, onClick: allFilter.onSelect, children: allFilter.label })) : null, filters.map((filter) => (_jsx(FilterToggle, { selected: filter.selected, onClick: filter.onSelect, children: filter.label }, filter.value)))] })) : null, _jsx(CommandList, { className: cn("max-h-[400px] p-1", resultsClassName), children: children !== undefined ? children : sections ? (visibleSections.length ? (visibleSections.map((section, index) => (_jsxs(React.Fragment, { children: [index > 0 ? _jsx(CommandSeparator, { alwaysRender: true }) : null, _jsx(CommandGroup, { heading: section.heading, children: section.items.map((item) => (_jsx(CommandItem, { value: getCommandSearchItemValue(item), disabled: item.disabled, onSelect: () => {
|
|
62
63
|
var _a;
|
|
63
64
|
(_a = item.onSelect) === null || _a === void 0 ? void 0 : _a.call(item);
|
|
64
65
|
if (item.closeOnSelect !== false) {
|
|
65
66
|
setIsOpen(false);
|
|
66
67
|
}
|
|
67
|
-
}, children: _jsxs("div", { className: "min-w-0", children: [_jsx("p",
|
|
68
|
+
}, children: _jsxs("div", { className: "min-w-0", children: [_jsx(Typography, { as: "p", variant: "label", className: "truncate", children: _jsx(SearchHighlight, { text: item.title, query: value }) }), item.subtitle ? (_jsx(Typography, { as: "p", variant: "caption", className: "truncate", children: _jsx(SearchHighlight, { text: item.subtitle, query: value }) })) : null] }) }, item.id))) })] }, section.key)))) : (_jsx(EmptyState, { icon: CommandSearchEmptyIcon, message: normalizedValue ? "No results found" : "Start typing to search", description: normalizedValue
|
|
68
69
|
? `No results found for "${value}".`
|
|
69
70
|
: "Results will appear here as you type." }))) : (_jsx(EmptyState, { icon: CommandSearchEmptyIcon, message: normalizedValue ? "No results found" : "Start typing to search", description: normalizedValue
|
|
70
71
|
? `No results found for "${value}".`
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export interface DateInputProps {
|
|
3
|
+
label: React.ReactNode;
|
|
4
|
+
value: string;
|
|
5
|
+
onValueChange: (value: string) => void;
|
|
6
|
+
description?: React.ReactNode;
|
|
7
|
+
error?: React.ReactNode;
|
|
8
|
+
invalid?: boolean;
|
|
9
|
+
hideLabel?: boolean;
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
minDate?: Date;
|
|
13
|
+
maxDate?: Date;
|
|
14
|
+
className?: string;
|
|
15
|
+
triggerClassName?: string;
|
|
16
|
+
labelClassName?: string;
|
|
17
|
+
descriptionClassName?: string;
|
|
18
|
+
errorClassName?: string;
|
|
19
|
+
id?: string;
|
|
20
|
+
}
|
|
21
|
+
declare const DateInput: React.ForwardRefExoticComponent<DateInputProps & React.RefAttributes<HTMLButtonElement>>;
|
|
22
|
+
export { DateInput };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { format, parse } from "date-fns";
|
|
5
|
+
import { Calendar } from "./calendar";
|
|
6
|
+
import { inputControlClassName } from "./input-control";
|
|
7
|
+
import { Field, FieldDescription, FieldError, FieldLabel } from "./field";
|
|
8
|
+
import { Popover, PopoverContent, PopoverTrigger } from "./popover";
|
|
9
|
+
import { cn } from "./lib/utils";
|
|
10
|
+
function parseDateString(dateStr) {
|
|
11
|
+
if (!(dateStr === null || dateStr === void 0 ? void 0 : dateStr.trim()))
|
|
12
|
+
return undefined;
|
|
13
|
+
try {
|
|
14
|
+
const nativeDate = new Date(dateStr);
|
|
15
|
+
if (!isNaN(nativeDate.getTime()))
|
|
16
|
+
return nativeDate;
|
|
17
|
+
const ddmmyyyyMatch = dateStr.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})/);
|
|
18
|
+
if (ddmmyyyyMatch) {
|
|
19
|
+
const [, day, month, year] = ddmmyyyyMatch;
|
|
20
|
+
const date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
|
|
21
|
+
if (!isNaN(date.getTime()))
|
|
22
|
+
return date;
|
|
23
|
+
}
|
|
24
|
+
const date = parse(dateStr, "yyyy-MM-dd", new Date());
|
|
25
|
+
if (!isNaN(date.getTime()))
|
|
26
|
+
return date;
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function formatISODate(date) {
|
|
34
|
+
return format(date, "yyyy-MM-dd");
|
|
35
|
+
}
|
|
36
|
+
const DateInput = React.forwardRef(({ label, value, onValueChange, description, error, invalid = false, hideLabel = false, placeholder = "Pick a date", disabled = false, minDate, maxDate, className, triggerClassName, labelClassName, descriptionClassName, errorClassName, id, }, ref) => {
|
|
37
|
+
const [open, setOpen] = React.useState(false);
|
|
38
|
+
const generatedId = React.useId();
|
|
39
|
+
const inputId = id !== null && id !== void 0 ? id : `date-input-${generatedId}`;
|
|
40
|
+
const errorId = error ? `${inputId}-error` : undefined;
|
|
41
|
+
const showDescription = Boolean(description) && !error;
|
|
42
|
+
const descriptionId = showDescription ? `${inputId}-description` : undefined;
|
|
43
|
+
const isInvalid = invalid || Boolean(error);
|
|
44
|
+
const selectedDate = React.useMemo(() => parseDateString(value), [value]);
|
|
45
|
+
const displayValue = selectedDate
|
|
46
|
+
? format(selectedDate, "d MMM yyyy")
|
|
47
|
+
: undefined;
|
|
48
|
+
const handleSelect = (date) => {
|
|
49
|
+
onValueChange(date ? formatISODate(date) : "");
|
|
50
|
+
setOpen(false);
|
|
51
|
+
};
|
|
52
|
+
return (_jsxs(Field, { className: className, "data-disabled": disabled ? "true" : undefined, "data-invalid": isInvalid ? "true" : undefined, children: [_jsx(FieldLabel, { htmlFor: inputId, className: cn(hideLabel && "sr-only", labelClassName), children: label }), _jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("div", { className: "relative w-full", children: [_jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", style: { width: 14, height: 14 }, "aria-hidden": "true", children: [_jsx("rect", { width: "18", height: "18", x: "3", y: "4", rx: "2", ry: "2" }), _jsx("line", { x1: "16", x2: "16", y1: "2", y2: "6" }), _jsx("line", { x1: "8", x2: "8", y1: "2", y2: "6" }), _jsx("line", { x1: "3", x2: "21", y1: "10", y2: "10" })] }), _jsx("button", { ref: ref, type: "button", id: inputId, disabled: disabled, "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": [descriptionId, errorId].filter(Boolean).join(" ") || undefined, className: cn(inputControlClassName, "cursor-pointer pl-9 text-left", !selectedDate && "text-muted-foreground", isInvalid && "border-destructive focus-visible:ring-destructive", triggerClassName), children: displayValue !== null && displayValue !== void 0 ? displayValue : _jsx("span", { children: placeholder }) })] }) }), _jsx(PopoverContent, { className: "w-auto p-0", align: "start", children: _jsx(Calendar, { mode: "single", selected: selectedDate, onSelect: handleSelect, disabled: (date) => {
|
|
53
|
+
if (minDate && date < minDate)
|
|
54
|
+
return true;
|
|
55
|
+
if (maxDate && date > maxDate)
|
|
56
|
+
return true;
|
|
57
|
+
return false;
|
|
58
|
+
}, defaultMonth: selectedDate, autoFocus: true }) })] }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
|
|
59
|
+
});
|
|
60
|
+
DateInput.displayName = "DateInput";
|
|
61
|
+
export { DateInput };
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,12 @@ export { Avatar, AvatarProfile, AvatarRoot, AvatarImage, AvatarFallback, } from
|
|
|
2
2
|
export type { AvatarProps, AvatarProfileProps, AvatarRootProps, } from "./avatar";
|
|
3
3
|
export * from "./animated-icons";
|
|
4
4
|
export { Badge, badgeVariants } from "./badge";
|
|
5
|
+
export { Card } from "./card";
|
|
6
|
+
export type { CardProps } from "./card";
|
|
5
7
|
export type { BadgeProps } from "./badge";
|
|
8
|
+
export { Calendar, CalendarDayButton } from "./calendar";
|
|
9
|
+
export { DateInput } from "./date-input";
|
|
10
|
+
export type { DateInputProps } from "./date-input";
|
|
6
11
|
export { CountBadge } from "./count-badge";
|
|
7
12
|
export type { CountBadgeProps } from "./count-badge";
|
|
8
13
|
export { EmptyState } from "./empty-state";
|
|
@@ -15,6 +20,8 @@ export { Input } from "./input";
|
|
|
15
20
|
export type { InputProps } from "./input";
|
|
16
21
|
export { Select } from "./select";
|
|
17
22
|
export type { SelectOption, SelectProps } from "./select";
|
|
23
|
+
export { MultiSelect } from "./multi-select";
|
|
24
|
+
export type { MultiSelectOption, MultiSelectProps } from "./multi-select";
|
|
18
25
|
export { DEFAULT_COUNTRY_CODES, PhoneInput, } from "./phone-input";
|
|
19
26
|
export type { CountryCode, PhoneInputProps, } from "./phone-input";
|
|
20
27
|
export { OtpInput } from "./otp-input";
|
|
@@ -56,5 +63,9 @@ export type { TableToolbarProps } from "./table-toolbar";
|
|
|
56
63
|
export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption, TableRowSkeleton, TABLE_SKELETON_PRESETS, } from "./table";
|
|
57
64
|
export type { TableColumn, TableProps, TableSortDirection, TableSortingProps, TableSkeletonConfig, TableRowSkeletonProps, } from "./table";
|
|
58
65
|
export { Tabs, TabsRoot, type TabsItem, type TabsProps, TabsList, TabsTrigger, TabsContent, useTabsContext, } from "./tabs";
|
|
66
|
+
export { TabWithDropdown } from "./tab-with-dropdown";
|
|
67
|
+
export type { TabWithDropdownProps, TabDropdownSection } from "./tab-with-dropdown";
|
|
59
68
|
export { Tooltip, TooltipRoot, TooltipTrigger, TooltipContent, TooltipProvider, type TooltipProps, } from "./tooltip";
|
|
69
|
+
export { Toaster, toast } from "./toaster";
|
|
70
|
+
export type { ToasterProps } from "./toaster";
|
|
60
71
|
export { cn } from "./lib/utils";
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
export { Avatar, AvatarProfile, AvatarRoot, AvatarImage, AvatarFallback, } from "./avatar";
|
|
2
2
|
export * from "./animated-icons";
|
|
3
3
|
export { Badge, badgeVariants } from "./badge";
|
|
4
|
+
export { Card } from "./card";
|
|
5
|
+
export { Calendar, CalendarDayButton } from "./calendar";
|
|
6
|
+
export { DateInput } from "./date-input";
|
|
4
7
|
export { CountBadge } from "./count-badge";
|
|
5
8
|
export { EmptyState } from "./empty-state";
|
|
6
9
|
export { EditableTable } from "./editable-table";
|
|
7
10
|
export { EmailInput } from "./email-input";
|
|
8
11
|
export { Input } from "./input";
|
|
9
12
|
export { Select } from "./select";
|
|
13
|
+
export { MultiSelect } from "./multi-select";
|
|
10
14
|
export { DEFAULT_COUNTRY_CODES, PhoneInput, } from "./phone-input";
|
|
11
15
|
export { OtpInput } from "./otp-input";
|
|
12
16
|
export { LabelValue, isEmptyValue, } from "./label-value";
|
|
@@ -31,5 +35,7 @@ export { Pagination } from "./pagination";
|
|
|
31
35
|
export { TableToolbar } from "./table-toolbar";
|
|
32
36
|
export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption, TableRowSkeleton, TABLE_SKELETON_PRESETS, } from "./table";
|
|
33
37
|
export { Tabs, TabsRoot, TabsList, TabsTrigger, TabsContent, useTabsContext, } from "./tabs";
|
|
38
|
+
export { TabWithDropdown } from "./tab-with-dropdown";
|
|
34
39
|
export { Tooltip, TooltipRoot, TooltipTrigger, TooltipContent, TooltipProvider, } from "./tooltip";
|
|
40
|
+
export { Toaster, toast } from "./toaster";
|
|
35
41
|
export { cn } from "./lib/utils";
|
package/dist/input-control.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
declare const inputControlClassName = "h-10 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base
|
|
2
|
+
declare const inputControlClassName = "h-10 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-8 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-[invalid=true]:border-destructive aria-[invalid=true]:ring-destructive/20 md:text-sm";
|
|
3
3
|
declare const InputControl: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref"> & React.RefAttributes<HTMLInputElement>>;
|
|
4
4
|
export { InputControl, inputControlClassName };
|
package/dist/input-control.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { cn } from "./lib/utils";
|
|
4
|
-
const inputControlClassName = "h-10 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base
|
|
4
|
+
const inputControlClassName = "h-10 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-8 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-[invalid=true]:border-destructive aria-[invalid=true]:ring-destructive/20 md:text-sm";
|
|
5
5
|
const InputControl = React.forwardRef(({ className, type = "text", ...props }, ref) => (_jsx("input", { ref: ref, type: type, "data-slot": "input", className: cn(inputControlClassName, className), ...props })));
|
|
6
6
|
InputControl.displayName = "InputControl";
|
|
7
7
|
export { InputControl, inputControlClassName };
|
package/dist/label-value.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from "./lib/utils";
|
|
3
|
+
import { Typography } from "./typography";
|
|
3
4
|
export function isEmptyValue(value) {
|
|
4
5
|
if (value === null || value === undefined || value === "")
|
|
5
6
|
return true;
|
|
@@ -14,6 +15,6 @@ function LabelValue({ label, value, className, labelClassName, hideIfEmpty = fal
|
|
|
14
15
|
if (hideIfEmpty && empty) {
|
|
15
16
|
return null;
|
|
16
17
|
}
|
|
17
|
-
return (_jsxs("div", { className: cn("min-w-0 h-full space-y-1", className), children: [_jsx(
|
|
18
|
+
return (_jsxs("div", { className: cn("min-w-0 h-full space-y-1", className), children: [_jsx(Typography, { as: "p", variant: "label", className: labelClassName, children: label }), _jsx("div", { className: cn("min-w-0 break-words text-sm text-foreground", empty && "italic text-muted-foreground/60"), children: empty ? "-" : value })] }));
|
|
18
19
|
}
|
|
19
20
|
export { LabelValue };
|
package/dist/layout.js
CHANGED
|
@@ -69,7 +69,7 @@ function DashboardLayout({ className, masthead, prototypeBanner, children, style
|
|
|
69
69
|
observer.observe(node);
|
|
70
70
|
return () => observer.disconnect();
|
|
71
71
|
}, [masthead, prototypeBanner]);
|
|
72
|
-
return (_jsxs("div", { className: cn("flex min-h-screen flex-col bg-[hsl(var(--layout-shell-background))]", className), style: layoutStyle, ...props, children: [masthead || prototypeBanner ? (_jsxs("div", { ref: chromeRef, className: "shrink-0", children: [masthead, prototypeBanner] })) : null, _jsx("div", { className: "flex min-h-0 flex-1", children: children })] }));
|
|
72
|
+
return (_jsxs("div", { className: cn("flex min-h-screen flex-col bg-[hsl(var(--layout-shell-background))]", className), style: layoutStyle, ...props, children: [masthead || prototypeBanner ? (_jsxs("div", { ref: chromeRef, className: "sticky top-0 z-40 shrink-0", children: [masthead, prototypeBanner] })) : null, _jsx("div", { className: "flex min-h-0 flex-1", children: children })] }));
|
|
73
73
|
}
|
|
74
74
|
function SidebarShell({ isCollapsed, className, children, ...props }) {
|
|
75
75
|
return (_jsx("aside", { className: cn("fixed left-0 top-[var(--layout-top-offset)] z-30 flex h-[calc(100dvh-var(--layout-top-offset))] shrink-0 flex-col overflow-hidden bg-[hsl(var(--layout-shell-background))] pb-2 pl-[var(--layout-sidebar-padding-inline)] pt-[var(--layout-sidebar-padding-top)] transition-all duration-300 ease-in-out", isCollapsed
|
|
@@ -98,7 +98,7 @@ function SidebarMenuItem({ className, children, }) {
|
|
|
98
98
|
return _jsx("li", { className: className, children: children });
|
|
99
99
|
}
|
|
100
100
|
function SidebarMenuButton({ active = false, collapsed = false, disabled = false, className, asChild = false, children, ...props }) {
|
|
101
|
-
return (_jsx(Button, { asChild: asChild, variant: "ghost", disabled: disabled, className: cn("h-
|
|
101
|
+
return (_jsx(Button, { asChild: asChild, variant: "ghost", disabled: disabled, className: cn("h-10 w-full justify-start gap-3 rounded-lg text-sm font-medium shadow-none", collapsed ? "justify-center px-3" : "px-3", disabled
|
|
102
102
|
? "text-muted-foreground/50 hover:bg-transparent hover:text-muted-foreground/50"
|
|
103
103
|
: active
|
|
104
104
|
? "bg-white text-foreground hover:bg-white hover:text-foreground"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export interface MultiSelectOption {
|
|
3
|
+
label: string;
|
|
4
|
+
value: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface MultiSelectProps {
|
|
9
|
+
label: React.ReactNode;
|
|
10
|
+
options: MultiSelectOption[];
|
|
11
|
+
value: string[];
|
|
12
|
+
onValueChange: (value: string[]) => void;
|
|
13
|
+
placeholder?: string;
|
|
14
|
+
description?: React.ReactNode;
|
|
15
|
+
error?: React.ReactNode;
|
|
16
|
+
invalid?: boolean;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
hideLabel?: boolean;
|
|
19
|
+
searchable?: boolean;
|
|
20
|
+
searchPlaceholder?: string;
|
|
21
|
+
className?: string;
|
|
22
|
+
triggerClassName?: string;
|
|
23
|
+
labelClassName?: string;
|
|
24
|
+
descriptionClassName?: string;
|
|
25
|
+
errorClassName?: string;
|
|
26
|
+
id?: string;
|
|
27
|
+
}
|
|
28
|
+
declare function MultiSelect({ label, options, value, onValueChange, placeholder, description, error, invalid, disabled, hideLabel, searchable, searchPlaceholder, className, triggerClassName, labelClassName, descriptionClassName, errorClassName, id, }: MultiSelectProps): import("react/jsx-runtime").JSX.Element;
|
|
29
|
+
export { MultiSelect };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { XIcon } from "./animated-icons/x";
|
|
5
|
+
import { Badge } from "./badge";
|
|
6
|
+
import { Button } from "./button";
|
|
7
|
+
import { Field, FieldDescription, FieldError, FieldLabel } from "./field";
|
|
8
|
+
import { InputControl } from "./input-control";
|
|
9
|
+
import { cn } from "./lib/utils";
|
|
10
|
+
import { Typography } from "./typography";
|
|
11
|
+
function MultiSelect({ label, options, value, onValueChange, placeholder = "Select options", description, error, invalid = false, disabled = false, hideLabel = false, searchable = false, searchPlaceholder = "Search...", className, triggerClassName, labelClassName, descriptionClassName, errorClassName, id, }) {
|
|
12
|
+
const generatedId = React.useId();
|
|
13
|
+
const selectId = id !== null && id !== void 0 ? id : `multi-select-${generatedId}`;
|
|
14
|
+
const errorId = error ? `${selectId}-error` : undefined;
|
|
15
|
+
const showDescription = Boolean(description) && !error;
|
|
16
|
+
const descriptionId = showDescription ? `${selectId}-description` : undefined;
|
|
17
|
+
const isInvalid = invalid || Boolean(error);
|
|
18
|
+
const [open, setOpen] = React.useState(false);
|
|
19
|
+
const [searchQuery, setSearchQuery] = React.useState("");
|
|
20
|
+
const containerRef = React.useRef(null);
|
|
21
|
+
const searchInputRef = React.useRef(null);
|
|
22
|
+
const normalizedQuery = searchQuery.trim().toLowerCase();
|
|
23
|
+
const filteredOptions = searchable && normalizedQuery
|
|
24
|
+
? options.filter((opt) => {
|
|
25
|
+
var _a, _b;
|
|
26
|
+
return opt.label.toLowerCase().includes(normalizedQuery) ||
|
|
27
|
+
((_b = (_a = opt.description) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(normalizedQuery)) !== null && _b !== void 0 ? _b : false);
|
|
28
|
+
})
|
|
29
|
+
: options;
|
|
30
|
+
const selectedLabels = value
|
|
31
|
+
.map((v) => options.find((opt) => opt.value === v))
|
|
32
|
+
.filter(Boolean);
|
|
33
|
+
const toggleOption = React.useCallback((optionValue) => {
|
|
34
|
+
const next = value.includes(optionValue)
|
|
35
|
+
? value.filter((v) => v !== optionValue)
|
|
36
|
+
: [...value, optionValue];
|
|
37
|
+
onValueChange(next);
|
|
38
|
+
}, [value, onValueChange]);
|
|
39
|
+
const removeOption = React.useCallback((optionValue) => {
|
|
40
|
+
onValueChange(value.filter((v) => v !== optionValue));
|
|
41
|
+
}, [value, onValueChange]);
|
|
42
|
+
React.useEffect(() => {
|
|
43
|
+
if (!open) {
|
|
44
|
+
setSearchQuery("");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (searchable) {
|
|
48
|
+
const frame = window.requestAnimationFrame(() => {
|
|
49
|
+
var _a;
|
|
50
|
+
(_a = searchInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
51
|
+
});
|
|
52
|
+
return () => window.cancelAnimationFrame(frame);
|
|
53
|
+
}
|
|
54
|
+
}, [open, searchable]);
|
|
55
|
+
React.useEffect(() => {
|
|
56
|
+
const handleClickOutside = (event) => {
|
|
57
|
+
if (containerRef.current &&
|
|
58
|
+
!containerRef.current.contains(event.target)) {
|
|
59
|
+
setOpen(false);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
if (open) {
|
|
63
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
64
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
65
|
+
}
|
|
66
|
+
}, [open]);
|
|
67
|
+
return (_jsxs(Field, { className: className, "data-disabled": disabled ? "true" : undefined, "data-invalid": isInvalid ? "true" : undefined, children: [_jsx(FieldLabel, { htmlFor: selectId, className: cn(hideLabel && "sr-only", labelClassName), children: label }), _jsxs("div", { ref: containerRef, className: "relative", children: [_jsx("button", { type: "button", id: selectId, disabled: disabled, onClick: () => setOpen((prev) => !prev), "aria-expanded": open, "aria-haspopup": "listbox", "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": [descriptionId, errorId].filter(Boolean).join(" ") || undefined, className: cn("flex min-h-10 w-full flex-wrap items-center gap-1.5 rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background md:text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", isInvalid ? "border-destructive" : "border-input", triggerClassName), children: selectedLabels.length > 0 ? (selectedLabels.map((opt) => (_jsxs(Badge, { variant: "secondary", className: "gap-1 pr-1", children: [opt.label, _jsx(Button, { variant: "ghost", size: "icon", className: "h-4 w-4 p-0 hover:bg-transparent", onClick: (e) => {
|
|
68
|
+
e.stopPropagation();
|
|
69
|
+
removeOption(opt.value);
|
|
70
|
+
}, "aria-label": `Remove ${opt.label}`, children: _jsx(XIcon, { size: 10 }) })] }, opt.value)))) : (_jsx("span", { className: "text-muted-foreground", children: placeholder })) }), open ? (_jsxs("div", { className: "absolute z-50 mt-1 w-full rounded-md border bg-popover shadow-md", children: [searchable ? (_jsx("div", { className: "border-b p-2", children: _jsx(InputControl, { ref: searchInputRef, value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), placeholder: searchPlaceholder, className: "h-8 border-0 bg-transparent px-2 py-0 text-sm shadow-none focus-visible:ring-0", onKeyDown: (e) => e.stopPropagation() }) })) : null, _jsx("div", { role: "listbox", "aria-label": typeof label === "string" ? label : "Options", "aria-multiselectable": "true", className: "max-h-64 overflow-y-auto p-1", children: filteredOptions.length > 0 ? (filteredOptions.map((option) => {
|
|
71
|
+
const isSelected = value.includes(option.value);
|
|
72
|
+
return (_jsxs("button", { type: "button", role: "option", "aria-selected": isSelected, disabled: option.disabled, onClick: () => toggleOption(option.value), className: cn("flex h-10 w-full cursor-pointer items-center gap-2 rounded-sm px-2 text-left text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50", isSelected && "bg-accent"), children: [_jsx("div", { className: cn("mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-sm border", isSelected
|
|
73
|
+
? "border-primary bg-primary text-primary-foreground"
|
|
74
|
+
: "border-input"), children: isSelected ? (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round", strokeLinejoin: "round", className: "h-3 w-3", children: _jsx("polyline", { points: "20 6 9 17 4 12" }) })) : null }), _jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { children: option.label }), option.description ? (_jsx(Typography, { as: "span", variant: "caption", children: option.description })) : null] })] }, option.value));
|
|
75
|
+
})) : (_jsx("div", { className: "px-3 py-6 text-center text-sm text-muted-foreground", children: "No options found." })) })] })) : null] }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
|
|
76
|
+
}
|
|
77
|
+
export { MultiSelect };
|
package/dist/otp-input.js
CHANGED
|
@@ -7,7 +7,7 @@ import { validateOtp } from "./input-validation";
|
|
|
7
7
|
import { cn } from "./lib/utils";
|
|
8
8
|
function OtpSlot({ slot, invalid, className, }) {
|
|
9
9
|
var _a, _b;
|
|
10
|
-
return (_jsxs("div", { "data-slot": "otp-slot", className: cn("relative flex size-10 items-center justify-center rounded-md border bg-transparent text-sm font-medium
|
|
10
|
+
return (_jsxs("div", { "data-slot": "otp-slot", className: cn("relative flex size-10 items-center justify-center rounded-md border bg-transparent text-sm font-medium transition-[border-color,box-shadow,color] outline-none group-data-[disabled=true]/field:cursor-not-allowed group-data-[disabled=true]/field:bg-muted/30 group-data-[disabled=true]/field:text-muted-foreground group-data-[disabled=true]/field:opacity-50", invalid ? "border-destructive" : "border-input", slot.isActive &&
|
|
11
11
|
(invalid
|
|
12
12
|
? "border-destructive ring-[3px] ring-destructive/20"
|
|
13
13
|
: "border-ring ring-[3px] ring-ring/50"), className), children: [_jsx("span", { className: cn("pointer-events-none", !slot.char && slot.placeholderChar ? "text-muted-foreground" : ""), children: (_b = (_a = slot.char) !== null && _a !== void 0 ? _a : slot.placeholderChar) !== null && _b !== void 0 ? _b : "" }), slot.hasFakeCaret ? (_jsx("span", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 flex items-center justify-center", children: _jsx("span", { className: "h-4 w-px animate-pulse bg-foreground" }) })) : null] }));
|
package/dist/pagination.js
CHANGED
|
@@ -6,6 +6,7 @@ import { ChevronRightIcon } from "./animated-icons/chevron-right";
|
|
|
6
6
|
import { Button } from "./button";
|
|
7
7
|
import { SelectContent, SelectItem, SelectRoot, SelectTrigger, SelectValue, } from "./select-primitive";
|
|
8
8
|
import { cn } from "./lib/utils";
|
|
9
|
+
import { Typography } from "./typography";
|
|
9
10
|
const PaginationNav = React.forwardRef(({ className, ...props }, ref) => (_jsx("nav", { ref: ref, role: "navigation", "aria-label": "pagination", className: cn("flex w-full justify-center", className), ...props })));
|
|
10
11
|
PaginationNav.displayName = "PaginationNav";
|
|
11
12
|
const PaginationContent = React.forwardRef(({ className, ...props }, ref) => (_jsx("ul", { ref: ref, className: cn("flex flex-row items-center gap-1", className), ...props })));
|
|
@@ -16,7 +17,7 @@ function Pagination({ currentPage, totalPages, totalItems, itemsPerPage = 5, onP
|
|
|
16
17
|
const normalizedTotalPages = totalPages || 1;
|
|
17
18
|
const startItem = totalItems === 0 ? 0 : (currentPage - 1) * itemsPerPage + 1;
|
|
18
19
|
const endItem = Math.min(currentPage * itemsPerPage, totalItems);
|
|
19
|
-
return (_jsxs("div", { className: cn("flex items-center justify-between gap-6", className), ...props, children: [_jsxs("div", { className: "text-sm text-muted-foreground", children: ["Showing ", startItem, "-", endItem, " out of ", totalItems] }), _jsxs("div", { className: "flex items-center gap-6", children: [onItemsPerPageChange ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(
|
|
20
|
+
return (_jsxs("div", { className: cn("flex items-center justify-between gap-6", className), ...props, children: [_jsxs("div", { className: "text-sm text-muted-foreground", children: ["Showing ", startItem, "-", endItem, " out of ", totalItems] }), _jsxs("div", { className: "flex items-center gap-6", children: [onItemsPerPageChange ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Typography, { as: "span", variant: "caption", children: "Rows per page" }), _jsxs(SelectRoot, { value: itemsPerPage.toString(), onValueChange: (value) => {
|
|
20
21
|
onItemsPerPageChange(Number(value));
|
|
21
22
|
onPageChange(1);
|
|
22
23
|
}, children: [_jsx(SelectTrigger, { className: "h-8 w-[70px] text-sm", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: itemsPerPageOptions.map((option) => (_jsx(SelectItem, { value: option.toString(), children: option }, option))) })] })] })) : null, _jsxs("div", { className: "text-sm text-muted-foreground", children: ["Page ", currentPage, " of ", normalizedTotalPages] }), _jsx(PaginationNav, { className: "mx-0 w-auto justify-start", children: _jsxs(PaginationContent, { children: [_jsx(PaginationItem, { children: _jsx(Button, { type: "button", variant: "outline", size: "icon", className: "h-8 w-8", onClick: () => onPageChange(1), disabled: currentPage === 1, "aria-label": "Go to first page", children: _jsx(ChevronsLeft, { className: "h-4 w-4" }) }) }), _jsx(PaginationItem, { children: _jsx(Button, { type: "button", variant: "outline", size: "icon", className: "h-8 w-8", onClick: () => onPageChange(Math.max(1, currentPage - 1)), disabled: currentPage === 1, "aria-label": "Go to previous page", children: _jsx(ChevronLeftIcon, { size: 16 }) }) }), _jsx(PaginationItem, { children: _jsx(Button, { type: "button", variant: "outline", size: "icon", className: "h-8 w-8", onClick: () => onPageChange(Math.min(normalizedTotalPages, currentPage + 1)), disabled: currentPage === normalizedTotalPages || totalPages === 0, "aria-label": "Go to next page", children: _jsx(ChevronRightIcon, { size: 16 }) }) }), _jsx(PaginationItem, { children: _jsx(Button, { type: "button", variant: "outline", size: "icon", className: "h-8 w-8", onClick: () => onPageChange(normalizedTotalPages), disabled: currentPage === normalizedTotalPages || totalPages === 0, "aria-label": "Go to last page", children: _jsx(ChevronsRight, { className: "h-4 w-4" }) }) })] }) })] })] }));
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
3
|
+
declare const Popover: React.FC<PopoverPrimitive.PopoverProps>;
|
|
4
|
+
declare const PopoverTrigger: React.ForwardRefExoticComponent<PopoverPrimitive.PopoverTriggerProps & React.RefAttributes<HTMLButtonElement>>;
|
|
5
|
+
declare const PopoverAnchor: React.ForwardRefExoticComponent<PopoverPrimitive.PopoverAnchorProps & React.RefAttributes<HTMLDivElement>>;
|
|
6
|
+
declare const PopoverContent: React.ForwardRefExoticComponent<Omit<PopoverPrimitive.PopoverContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
7
|
+
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };
|
package/dist/popover.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
5
|
+
import { cn } from "./lib/utils";
|
|
6
|
+
const Popover = PopoverPrimitive.Root;
|
|
7
|
+
const PopoverTrigger = PopoverPrimitive.Trigger;
|
|
8
|
+
const PopoverAnchor = PopoverPrimitive.Anchor;
|
|
9
|
+
const PopoverContent = React.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => (_jsx(PopoverPrimitive.Portal, { children: _jsx(PopoverPrimitive.Content, { ref: ref, align: align, sideOffset: sideOffset, className: cn("z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", className), ...props }) })));
|
|
10
|
+
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
|
11
|
+
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };
|
|
@@ -12,6 +12,8 @@ interface SelectContentProps extends React.ComponentPropsWithoutRef<typeof Selec
|
|
|
12
12
|
}
|
|
13
13
|
declare const SelectContent: React.ForwardRefExoticComponent<SelectContentProps & React.RefAttributes<HTMLDivElement>>;
|
|
14
14
|
declare const SelectLabel: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectLabelProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
15
|
-
declare const SelectItem: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectItemProps & React.RefAttributes<HTMLDivElement>, "ref"> &
|
|
15
|
+
declare const SelectItem: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
|
|
16
|
+
description?: React.ReactNode;
|
|
17
|
+
} & React.RefAttributes<HTMLDivElement>>;
|
|
16
18
|
declare const SelectSeparator: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectSeparatorProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
17
19
|
export { SelectContent, SelectGroup, SelectItem, SelectLabel, SelectRoot, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, };
|
package/dist/select-primitive.js
CHANGED
|
@@ -4,6 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
5
5
|
import { Check, ChevronDown, ChevronUp } from "lucide-react";
|
|
6
6
|
import { cn } from "./lib/utils";
|
|
7
|
+
import { Typography } from "./typography";
|
|
7
8
|
function SelectRoot(props) {
|
|
8
9
|
return _jsx(SelectPrimitive.Root, { "data-slot": "select", ...props });
|
|
9
10
|
}
|
|
@@ -13,7 +14,7 @@ function SelectGroup(props) {
|
|
|
13
14
|
function SelectValue(props) {
|
|
14
15
|
return _jsx(SelectPrimitive.Value, { "data-slot": "select-value", ...props });
|
|
15
16
|
}
|
|
16
|
-
const SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => (_jsxs(SelectPrimitive.Trigger, { ref: ref, "data-slot": "select-trigger", className: cn("flex h-10 w-full cursor-pointer items-center justify-between gap-2 whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-
|
|
17
|
+
const SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => (_jsxs(SelectPrimitive.Trigger, { ref: ref, "data-slot": "select-trigger", className: cn("flex h-10 w-full cursor-pointer items-center justify-between gap-2 whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-base transition-[color,box-shadow] md:text-sm outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-[invalid=true]:border-destructive aria-[invalid=true]:ring-destructive/20 data-[placeholder]:text-muted-foreground [&>span]:line-clamp-1 [&_svg]:pointer-events-none [&_svg]:shrink-0", className), ...props, children: [children, _jsx(SelectPrimitive.Icon, { asChild: true, children: _jsx(ChevronDown, { className: "h-4 w-4 shrink-0 opacity-50" }) })] })));
|
|
17
18
|
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
18
19
|
const SelectScrollUpButton = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.ScrollUpButton, { ref: ref, "data-slot": "select-scroll-up-button", className: cn("flex cursor-pointer items-center justify-center py-1", className), ...props, children: _jsx(ChevronUp, { className: "h-4 w-4" }) })));
|
|
19
20
|
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
|
@@ -26,7 +27,7 @@ const SelectContent = React.forwardRef(({ className, children, header, position
|
|
|
26
27
|
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
27
28
|
const SelectLabel = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.Label, { ref: ref, "data-slot": "select-label", className: cn("px-2 py-1.5 text-xs text-muted-foreground", className), ...props })));
|
|
28
29
|
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
29
|
-
const SelectItem = React.forwardRef(({ className, children, ...props }, ref) => (_jsxs(SelectPrimitive.Item, { ref: ref, "data-slot": "select-item", className: cn("relative flex w-full cursor-pointer select-none items-center gap-2 rounded-sm
|
|
30
|
+
const SelectItem = React.forwardRef(({ className, children, description, ...props }, ref) => (_jsxs(SelectPrimitive.Item, { ref: ref, "data-slot": "select-item", className: cn("relative flex h-10 w-full cursor-pointer select-none items-center gap-2 rounded-sm pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className), ...props, children: [_jsx("span", { "data-slot": "select-item-indicator", className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: _jsx(SelectPrimitive.ItemIndicator, { children: _jsx(Check, { className: "h-4 w-4" }) }) }), _jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx(SelectPrimitive.ItemText, { className: "break-words font-normal", children: children }), description ? (_jsx(Typography, { as: "span", variant: "caption", children: description })) : null] })] })));
|
|
30
31
|
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
31
32
|
const SelectSeparator = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.Separator, { ref: ref, "data-slot": "select-separator", className: cn("-mx-1 my-1 h-px bg-border", className), ...props })));
|
|
32
33
|
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
package/dist/select.d.ts
CHANGED
package/dist/select.js
CHANGED
|
@@ -91,7 +91,7 @@ function Select({ label, options, placeholder = "Select an option", description,
|
|
|
91
91
|
event.stopPropagation();
|
|
92
92
|
} })) : undefined, viewportClassName: searchable ? "max-h-64" : undefined, children: hasVisibleOptions ? (optionGroups.map((group, groupIndex) => {
|
|
93
93
|
var _a;
|
|
94
|
-
return (_jsxs(React.Fragment, { children: [_jsxs(SelectGroup, { children: [group.group ? (_jsx(SelectLabel, { children: _jsx(SearchHighlight, { text: group.group, query: searchQuery }) })) : null, group.options.map((option) => (_jsx(SelectItem, { value: option.value, disabled: option.disabled, textValue: getOptionTextValue(option), children: renderHighlightedLabel(option.label, searchQuery) }, option.value)))] }), groupIndex < optionGroups.length - 1 ? _jsx(SelectSeparator, {}) : null] }, (_a = group.group) !== null && _a !== void 0 ? _a : `group-${groupIndex}`));
|
|
94
|
+
return (_jsxs(React.Fragment, { children: [_jsxs(SelectGroup, { children: [group.group ? (_jsx(SelectLabel, { children: _jsx(SearchHighlight, { text: group.group, query: searchQuery }) })) : null, group.options.map((option) => (_jsx(SelectItem, { value: option.value, disabled: option.disabled, textValue: getOptionTextValue(option), description: option.description, children: renderHighlightedLabel(option.label, searchQuery) }, option.value)))] }), groupIndex < optionGroups.length - 1 ? _jsx(SelectSeparator, {}) : null] }, (_a = group.group) !== null && _a !== void 0 ? _a : `group-${groupIndex}`));
|
|
95
95
|
})) : (_jsx("div", { className: "px-3 py-6 text-sm text-muted-foreground", children: searchEmptyMessage })) })] }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
|
|
96
96
|
}
|
|
97
97
|
export { Select };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface TabDropdownSection {
|
|
2
|
+
id: string;
|
|
3
|
+
label: string;
|
|
4
|
+
}
|
|
5
|
+
export interface TabWithDropdownProps {
|
|
6
|
+
/** Tab value — must match a TabsContent value. */
|
|
7
|
+
value: string;
|
|
8
|
+
/** Tab label shown in the trigger. */
|
|
9
|
+
label: string;
|
|
10
|
+
/** Dropdown sections shown on hover. */
|
|
11
|
+
sections: TabDropdownSection[];
|
|
12
|
+
/** Called when a section is selected. Receives the section id. */
|
|
13
|
+
onSectionSelect?: (sectionId: string) => void;
|
|
14
|
+
/** Scroll offset in pixels when scrolling to a section. */
|
|
15
|
+
scrollOffset?: number;
|
|
16
|
+
}
|
|
17
|
+
declare function TabWithDropdown({ value, label, sections, onSectionSelect, scrollOffset, }: TabWithDropdownProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
export { TabWithDropdown };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as ReactDOM from "react-dom";
|
|
5
|
+
import { ChevronDownIcon } from "./animated-icons/chevron-down";
|
|
6
|
+
import { cn } from "./lib/utils";
|
|
7
|
+
import { TabsTrigger, useTabsContext } from "./tabs";
|
|
8
|
+
function TabWithDropdown({ value, label, sections, onSectionSelect, scrollOffset = 200, }) {
|
|
9
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
10
|
+
const [dropdownPosition, setDropdownPosition] = React.useState({
|
|
11
|
+
top: 0,
|
|
12
|
+
left: 0,
|
|
13
|
+
});
|
|
14
|
+
const triggerRef = React.useRef(null);
|
|
15
|
+
const timeoutRef = React.useRef(null);
|
|
16
|
+
const tabsContext = useTabsContext();
|
|
17
|
+
const updatePosition = React.useCallback(() => {
|
|
18
|
+
if (triggerRef.current) {
|
|
19
|
+
const rect = triggerRef.current.getBoundingClientRect();
|
|
20
|
+
setDropdownPosition({
|
|
21
|
+
top: rect.bottom + 8,
|
|
22
|
+
left: rect.left,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}, []);
|
|
26
|
+
const handleMouseEnter = () => {
|
|
27
|
+
if (timeoutRef.current)
|
|
28
|
+
clearTimeout(timeoutRef.current);
|
|
29
|
+
updatePosition();
|
|
30
|
+
setIsOpen(true);
|
|
31
|
+
};
|
|
32
|
+
const handleMouseLeave = () => {
|
|
33
|
+
timeoutRef.current = setTimeout(() => setIsOpen(false), 150);
|
|
34
|
+
};
|
|
35
|
+
const handleDropdownMouseEnter = () => {
|
|
36
|
+
if (timeoutRef.current)
|
|
37
|
+
clearTimeout(timeoutRef.current);
|
|
38
|
+
};
|
|
39
|
+
const handleDropdownMouseLeave = () => {
|
|
40
|
+
timeoutRef.current = setTimeout(() => setIsOpen(false), 150);
|
|
41
|
+
};
|
|
42
|
+
const handleSectionClick = (sectionId) => {
|
|
43
|
+
if (onSectionSelect) {
|
|
44
|
+
onSectionSelect(sectionId);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
// Default: switch tab and scroll to the section element
|
|
48
|
+
const doScroll = () => {
|
|
49
|
+
const element = document.getElementById(sectionId);
|
|
50
|
+
if (!element)
|
|
51
|
+
return;
|
|
52
|
+
const top = element.getBoundingClientRect().top + window.scrollY - scrollOffset;
|
|
53
|
+
window.scrollTo({ top, behavior: "smooth" });
|
|
54
|
+
};
|
|
55
|
+
if (tabsContext && tabsContext.value !== value) {
|
|
56
|
+
tabsContext.setValue(value);
|
|
57
|
+
setTimeout(doScroll, 50);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
doScroll();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
setIsOpen(false);
|
|
64
|
+
};
|
|
65
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { ref: triggerRef, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: _jsxs(TabsTrigger, { value: value, className: "flex items-center gap-1", children: [label, sections.length > 0 ? (_jsx(ChevronDownIcon, { className: cn("transition-transform duration-200", isOpen && "rotate-180"), size: 12 })) : null] }) }), isOpen &&
|
|
66
|
+
sections.length > 0 &&
|
|
67
|
+
typeof document !== "undefined" &&
|
|
68
|
+
ReactDOM.createPortal(_jsx("div", { className: "fixed z-[100] rounded-md border bg-popover p-1 shadow-lg animate-in fade-in-0 zoom-in-95", style: { top: dropdownPosition.top, left: dropdownPosition.left }, onMouseEnter: handleDropdownMouseEnter, onMouseLeave: handleDropdownMouseLeave, children: sections.map((section) => (_jsx("button", { type: "button", onClick: () => handleSectionClick(section.id), className: "block w-full rounded-sm px-3 py-2 text-left text-sm transition-colors hover:bg-accent hover:text-accent-foreground", children: section.label }, section.id))) }), document.body)] }));
|
|
69
|
+
}
|
|
70
|
+
export { TabWithDropdown };
|
package/dist/textarea.js
CHANGED
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { Field, FieldDescription, FieldError, FieldLabel } from "./field";
|
|
4
4
|
import { cn } from "./lib/utils";
|
|
5
|
-
const TextareaControl = React.forwardRef(({ className, ...props }, ref) => (_jsx("textarea", { ref: ref, "data-slot": "textarea", className: cn("flex min-h-20 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base
|
|
5
|
+
const TextareaControl = React.forwardRef(({ className, ...props }, ref) => (_jsx("textarea", { ref: ref, "data-slot": "textarea", className: cn("flex min-h-20 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-[invalid=true]:border-destructive aria-[invalid=true]:ring-destructive/20 md:text-sm", className), ...props })));
|
|
6
6
|
TextareaControl.displayName = "TextareaControl";
|
|
7
7
|
const Textarea = React.forwardRef(({ label, description, error, invalid = false, hideLabel = false, className, textareaClassName, labelClassName, descriptionClassName, errorClassName, id, disabled, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy, ...props }, ref) => {
|
|
8
8
|
const generatedId = React.useId();
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { toast } from "sonner";
|
|
2
|
+
export interface ToasterProps {
|
|
3
|
+
/** Position of the toast container. */
|
|
4
|
+
position?: "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right";
|
|
5
|
+
/** Maximum number of toasts visible at once. */
|
|
6
|
+
visibleToasts?: number;
|
|
7
|
+
/** Whether to show the close button on each toast. */
|
|
8
|
+
closeButton?: boolean;
|
|
9
|
+
/** Duration in milliseconds before auto-dismiss. */
|
|
10
|
+
duration?: number;
|
|
11
|
+
}
|
|
12
|
+
declare function Toaster({ position, visibleToasts, closeButton, duration, }: ToasterProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export { Toaster, toast };
|
package/dist/toaster.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Toaster as SonnerToaster, toast } from "sonner";
|
|
4
|
+
function Toaster({ position = "bottom-right", visibleToasts = 3, closeButton = true, duration = 4000, }) {
|
|
5
|
+
return (_jsx(SonnerToaster, { position: position, visibleToasts: visibleToasts, closeButton: closeButton, duration: duration, toastOptions: {
|
|
6
|
+
classNames: {
|
|
7
|
+
toast: "group border border-border bg-background text-foreground shadow-md rounded-lg p-4 flex items-start gap-3",
|
|
8
|
+
title: "text-sm font-medium",
|
|
9
|
+
description: "text-sm text-muted-foreground",
|
|
10
|
+
closeButton: "text-muted-foreground hover:text-foreground",
|
|
11
|
+
success: "border-[hsl(var(--status-success-foreground))]/20",
|
|
12
|
+
error: "border-destructive/20",
|
|
13
|
+
warning: "border-[hsl(var(--status-warning-foreground))]/20",
|
|
14
|
+
info: "border-[hsl(var(--status-info-foreground))]/20",
|
|
15
|
+
},
|
|
16
|
+
} }));
|
|
17
|
+
}
|
|
18
|
+
export { Toaster, toast };
|
package/dist/typography.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ declare const typographyVariants: (props?: ({
|
|
|
6
6
|
export interface TypographyProps extends React.HTMLAttributes<HTMLElement>, VariantProps<typeof typographyVariants> {
|
|
7
7
|
as?: keyof React.JSX.IntrinsicElements;
|
|
8
8
|
asChild?: boolean;
|
|
9
|
+
/** Override the built-in text colour. Use "inherit" to inherit from a parent container. */
|
|
10
|
+
color?: "inherit" | "foreground" | "muted";
|
|
9
11
|
}
|
|
10
12
|
declare const Typography: React.ForwardRefExoticComponent<TypographyProps & React.RefAttributes<HTMLElement>>;
|
|
11
13
|
export { Typography, typographyVariants };
|
package/dist/typography.js
CHANGED
|
@@ -30,12 +30,19 @@ const defaultElementByVariant = {
|
|
|
30
30
|
caption: "span",
|
|
31
31
|
eyebrow: "span",
|
|
32
32
|
};
|
|
33
|
-
const Typography = React.forwardRef(({ as, asChild = false, className, variant, ...props }, ref) => {
|
|
33
|
+
const Typography = React.forwardRef(({ as, asChild = false, className, variant, color, ...props }, ref) => {
|
|
34
34
|
const resolvedVariant = (variant !== null && variant !== void 0 ? variant : "body");
|
|
35
35
|
const Comp = asChild
|
|
36
36
|
? Slot
|
|
37
37
|
: as !== null && as !== void 0 ? as : defaultElementByVariant[resolvedVariant];
|
|
38
|
-
|
|
38
|
+
const colorClass = color === "inherit"
|
|
39
|
+
? "text-inherit"
|
|
40
|
+
: color === "foreground"
|
|
41
|
+
? "text-foreground"
|
|
42
|
+
: color === "muted"
|
|
43
|
+
? "text-muted-foreground"
|
|
44
|
+
: undefined;
|
|
45
|
+
return (_jsx(Comp, { ref: ref, className: cn(typographyVariants({ variant: resolvedVariant }), colorClass, className), ...props }));
|
|
39
46
|
});
|
|
40
47
|
Typography.displayName = "Typography";
|
|
41
48
|
export { Typography, typographyVariants };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neasg/design-system",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "NEA shared design system primitives, theme tokens, and Storybook docs.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"@govtechsg/sgds-web-component": "^3.8.1",
|
|
37
37
|
"@radix-ui/react-avatar": "^1.1.11",
|
|
38
38
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
39
|
+
"@radix-ui/react-popover": "^1.1.15",
|
|
39
40
|
"@radix-ui/react-select": "^2.2.6",
|
|
40
41
|
"@radix-ui/react-slot": "^1.2.4",
|
|
41
42
|
"@radix-ui/react-tabs": "^1.1.13",
|
|
@@ -43,10 +44,13 @@
|
|
|
43
44
|
"class-variance-authority": "^0.7.0",
|
|
44
45
|
"clsx": "^2.1.1",
|
|
45
46
|
"cmdk": "^1.1.1",
|
|
47
|
+
"date-fns": "^4.1.0",
|
|
46
48
|
"input-otp": "^1.4.2",
|
|
47
49
|
"libphonenumber-js": "^1.12.41",
|
|
48
50
|
"lucide-react": "^0.555.0",
|
|
49
51
|
"motion": "^12.33.0",
|
|
52
|
+
"react-day-picker": "^9.14.0",
|
|
53
|
+
"sonner": "^2.0.7",
|
|
50
54
|
"tailwind-merge": "^2.3.0",
|
|
51
55
|
"tailwindcss-animate": "^1.0.7"
|
|
52
56
|
},
|