@neasg/design-system 0.1.3 → 0.2.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/README.md +84 -9
- package/dist/animated-icons/arrow-down.d.ts +10 -0
- package/dist/animated-icons/arrow-down.js +54 -0
- package/dist/animated-icons/arrow-up.d.ts +10 -0
- package/dist/animated-icons/arrow-up.js +54 -0
- package/dist/animated-icons/bell.d.ts +10 -0
- package/dist/animated-icons/bell.js +42 -0
- package/dist/animated-icons/bold.d.ts +10 -0
- package/dist/animated-icons/bold.js +45 -0
- package/dist/animated-icons/book-open.d.ts +10 -0
- package/dist/animated-icons/book-open.js +55 -0
- package/dist/animated-icons/building.d.ts +10 -0
- package/dist/animated-icons/building.js +46 -0
- package/dist/animated-icons/check.d.ts +10 -0
- package/dist/animated-icons/check.js +55 -0
- package/dist/animated-icons/chevron-down.d.ts +10 -0
- package/dist/animated-icons/chevron-down.js +42 -0
- package/dist/animated-icons/chevron-left.d.ts +10 -0
- package/dist/animated-icons/chevron-left.js +42 -0
- package/dist/animated-icons/chevron-right.d.ts +10 -0
- package/dist/animated-icons/chevron-right.js +42 -0
- package/dist/animated-icons/chevron-up.d.ts +10 -0
- package/dist/animated-icons/chevron-up.js +42 -0
- package/dist/animated-icons/clipboard-check.d.ts +10 -0
- package/dist/animated-icons/clipboard-check.js +56 -0
- package/dist/animated-icons/download.d.ts +10 -0
- package/dist/animated-icons/download.js +47 -0
- package/dist/animated-icons/external-link.d.ts +10 -0
- package/dist/animated-icons/external-link.js +59 -0
- package/dist/animated-icons/eye.d.ts +10 -0
- package/dist/animated-icons/eye.js +41 -0
- package/dist/animated-icons/file-text.d.ts +11 -0
- package/dist/animated-icons/file-text.js +89 -0
- package/dist/animated-icons/filter.d.ts +10 -0
- package/dist/animated-icons/filter.js +78 -0
- package/dist/animated-icons/folder-open.d.ts +10 -0
- package/dist/animated-icons/folder-open.js +46 -0
- package/dist/animated-icons/form.d.ts +10 -0
- package/dist/animated-icons/form.js +56 -0
- package/dist/animated-icons/git-branch.d.ts +10 -0
- package/dist/animated-icons/git-branch.js +56 -0
- package/dist/animated-icons/grip-horizontal.d.ts +10 -0
- package/dist/animated-icons/grip-horizontal.js +70 -0
- package/dist/animated-icons/grip-vertical.d.ts +10 -0
- package/dist/animated-icons/grip-vertical.js +67 -0
- package/dist/animated-icons/grip.d.ts +10 -0
- package/dist/animated-icons/grip.js +72 -0
- package/dist/animated-icons/highlighter.d.ts +10 -0
- package/dist/animated-icons/highlighter.js +46 -0
- package/dist/animated-icons/history.d.ts +10 -0
- package/dist/animated-icons/history.js +80 -0
- package/dist/animated-icons/index.d.ts +100 -0
- package/dist/animated-icons/index.js +50 -0
- package/dist/animated-icons/italic.d.ts +10 -0
- package/dist/animated-icons/italic.js +45 -0
- package/dist/animated-icons/link.d.ts +10 -0
- package/dist/animated-icons/link.js +51 -0
- package/dist/animated-icons/list-ordered.d.ts +10 -0
- package/dist/animated-icons/list-ordered.js +46 -0
- package/dist/animated-icons/list.d.ts +10 -0
- package/dist/animated-icons/list.js +56 -0
- package/dist/animated-icons/loader-pinwheel.d.ts +10 -0
- package/dist/animated-icons/loader-pinwheel.js +51 -0
- package/dist/animated-icons/logout.d.ts +10 -0
- package/dist/animated-icons/logout.js +44 -0
- package/dist/animated-icons/maximize.d.ts +10 -0
- package/dist/animated-icons/maximize.js +45 -0
- package/dist/animated-icons/message-square.d.ts +10 -0
- package/dist/animated-icons/message-square.js +56 -0
- package/dist/animated-icons/palette.d.ts +10 -0
- package/dist/animated-icons/palette.js +45 -0
- package/dist/animated-icons/paperclip.d.ts +10 -0
- package/dist/animated-icons/paperclip.js +45 -0
- package/dist/animated-icons/pencil.d.ts +10 -0
- package/dist/animated-icons/pencil.js +46 -0
- package/dist/animated-icons/plus.d.ts +10 -0
- package/dist/animated-icons/plus.js +42 -0
- package/dist/animated-icons/search.d.ts +10 -0
- package/dist/animated-icons/search.js +44 -0
- package/dist/animated-icons/send.d.ts +10 -0
- package/dist/animated-icons/send.js +46 -0
- package/dist/animated-icons/settings.d.ts +10 -0
- package/dist/animated-icons/settings.js +42 -0
- package/dist/animated-icons/shield.d.ts +10 -0
- package/dist/animated-icons/shield.js +45 -0
- package/dist/animated-icons/strikethrough.d.ts +10 -0
- package/dist/animated-icons/strikethrough.js +45 -0
- package/dist/animated-icons/timer.d.ts +10 -0
- package/dist/animated-icons/timer.js +70 -0
- package/dist/animated-icons/trash.d.ts +10 -0
- package/dist/animated-icons/trash.js +56 -0
- package/dist/animated-icons/trending-down.d.ts +10 -0
- package/dist/animated-icons/trending-down.js +86 -0
- package/dist/animated-icons/trending-up.d.ts +10 -0
- package/dist/animated-icons/trending-up.js +86 -0
- package/dist/animated-icons/underline.d.ts +10 -0
- package/dist/animated-icons/underline.js +45 -0
- package/dist/animated-icons/upload.d.ts +10 -0
- package/dist/animated-icons/upload.js +47 -0
- package/dist/animated-icons/users.d.ts +10 -0
- package/dist/animated-icons/users.js +45 -0
- package/dist/animated-icons/x.d.ts +10 -0
- package/dist/animated-icons/x.js +45 -0
- package/dist/avatar.d.ts +32 -0
- package/dist/avatar.js +21 -0
- package/dist/badge.d.ts +4 -3
- package/dist/badge.js +7 -12
- package/dist/breadcrumb.d.ts +30 -0
- package/dist/breadcrumb.js +32 -0
- package/dist/button.d.ts +2 -2
- package/dist/button.js +86 -8
- package/dist/command-search.d.ts +44 -0
- package/dist/command-search.js +73 -0
- package/dist/command.d.ts +77 -0
- package/dist/command.js +24 -0
- package/dist/count-badge.d.ts +10 -0
- package/dist/count-badge.js +34 -0
- package/dist/dialog-primitive.d.ts +22 -0
- package/dist/dialog-primitive.js +23 -0
- package/dist/dialog.d.ts +28 -0
- package/dist/dialog.js +60 -0
- package/dist/draggable-tabs.d.ts +27 -0
- package/dist/draggable-tabs.js +61 -0
- package/dist/drawer.d.ts +30 -0
- package/dist/drawer.js +77 -0
- package/dist/editable-table.d.ts +31 -0
- package/dist/editable-table.js +122 -0
- package/dist/email-input.d.ts +16 -0
- package/dist/email-input.js +55 -0
- package/dist/empty-state.d.ts +11 -0
- package/dist/empty-state.js +7 -0
- package/dist/field.d.ts +6 -0
- package/dist/field.js +18 -0
- package/dist/filter-toggle.d.ts +12 -0
- package/dist/filter-toggle.js +23 -0
- package/dist/index.d.ts +53 -4
- package/dist/index.js +31 -4
- package/dist/input-control.d.ts +4 -0
- package/dist/input-control.js +7 -0
- package/dist/input-validation.d.ts +9 -0
- package/dist/input-validation.js +75 -0
- package/dist/input.d.ts +16 -0
- package/dist/input.js +22 -0
- package/dist/label-value-grid.d.ts +13 -0
- package/dist/label-value-grid.js +61 -0
- package/dist/label-value.d.ts +11 -0
- package/dist/label-value.js +19 -0
- package/dist/layout.d.ts +55 -0
- package/dist/layout.js +140 -0
- package/dist/otp-input.d.ts +21 -0
- package/dist/otp-input.js +60 -0
- package/dist/page-header.d.ts +9 -0
- package/dist/page-header.js +6 -0
- package/dist/pagination.d.ts +12 -0
- package/dist/pagination.js +24 -0
- package/dist/phone-input.d.ts +36 -0
- package/dist/phone-input.js +135 -0
- package/dist/right-drawer.d.ts +2 -0
- package/dist/right-drawer.js +1 -0
- package/dist/search-input-shell.d.ts +8 -0
- package/dist/search-input-shell.js +9 -0
- package/dist/search-input.d.ts +20 -0
- package/dist/search-input.js +73 -0
- package/dist/select-primitive.d.ts +17 -0
- package/dist/select-primitive.js +33 -0
- package/dist/select.d.ts +31 -0
- package/dist/select.js +97 -0
- package/dist/styles.css +111 -1
- package/dist/table-toolbar.d.ts +7 -0
- package/dist/table-toolbar.js +7 -0
- package/dist/table.d.ts +87 -3
- package/dist/table.js +102 -5
- package/dist/tabs.d.ts +24 -7
- package/dist/tabs.js +22 -26
- package/dist/tailwind-preset.d.ts +4 -0
- package/dist/tailwind-preset.js +97 -0
- package/dist/textarea.d.ts +16 -0
- package/dist/textarea.js +23 -0
- package/dist/theme.d.ts +259 -0
- package/dist/theme.js +118 -0
- package/dist/tooltip.d.ts +15 -3
- package/dist/tooltip.js +9 -4
- package/dist/typography.d.ts +11 -0
- package/dist/typography.js +41 -0
- package/dist/use-search-highlight.d.ts +13 -0
- package/dist/use-search-highlight.js +46 -0
- package/package.json +124 -3
package/dist/input.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { InputControl } from "./input-control";
|
|
3
|
+
export interface InputProps extends Omit<React.ComponentProps<typeof InputControl>, "className"> {
|
|
4
|
+
label: React.ReactNode;
|
|
5
|
+
description?: React.ReactNode;
|
|
6
|
+
error?: React.ReactNode;
|
|
7
|
+
invalid?: boolean;
|
|
8
|
+
hideLabel?: boolean;
|
|
9
|
+
className?: string;
|
|
10
|
+
inputClassName?: string;
|
|
11
|
+
labelClassName?: string;
|
|
12
|
+
descriptionClassName?: string;
|
|
13
|
+
errorClassName?: string;
|
|
14
|
+
}
|
|
15
|
+
declare const Input: React.ForwardRefExoticComponent<Omit<InputProps, "ref"> & React.RefAttributes<HTMLInputElement>>;
|
|
16
|
+
export { Input };
|
package/dist/input.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { Field, FieldDescription, FieldError, FieldLabel } from "./field";
|
|
4
|
+
import { InputControl } from "./input-control";
|
|
5
|
+
import { cn } from "./lib/utils";
|
|
6
|
+
const Input = React.forwardRef(({ label, description, error, invalid = false, hideLabel = false, className, inputClassName, labelClassName, descriptionClassName, errorClassName, id, disabled, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy, ...props }, ref) => {
|
|
7
|
+
const generatedId = React.useId();
|
|
8
|
+
const inputId = id !== null && id !== void 0 ? id : `input-${generatedId}`;
|
|
9
|
+
const errorId = error ? `${inputId}-error` : undefined;
|
|
10
|
+
const showDescription = Boolean(description) && !error;
|
|
11
|
+
const descriptionId = showDescription ? `${inputId}-description` : undefined;
|
|
12
|
+
const isInvalid = invalid ||
|
|
13
|
+
ariaInvalid === true ||
|
|
14
|
+
ariaInvalid === "true" ||
|
|
15
|
+
Boolean(error);
|
|
16
|
+
const describedBy = [ariaDescribedBy, descriptionId, errorId]
|
|
17
|
+
.filter(Boolean)
|
|
18
|
+
.join(" ") || undefined;
|
|
19
|
+
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 }), _jsx(InputControl, { ref: ref, id: inputId, disabled: disabled, "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": describedBy, className: inputClassName, ...props }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
|
|
20
|
+
});
|
|
21
|
+
Input.displayName = "Input";
|
|
22
|
+
export { Input };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { type LabelValueProps } from "./label-value";
|
|
3
|
+
export interface LabelValueGridItem extends LabelValueProps {
|
|
4
|
+
id?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface LabelValueGridProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
7
|
+
items: LabelValueGridItem[];
|
|
8
|
+
columns?: 1 | 2 | 3 | 4;
|
|
9
|
+
itemClassName?: string;
|
|
10
|
+
minColumnWidth?: number;
|
|
11
|
+
}
|
|
12
|
+
declare function LabelValueGrid({ items, columns, className, itemClassName, minColumnWidth, style, ...props }: LabelValueGridProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export { LabelValueGrid };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { LabelValue } from "./label-value";
|
|
5
|
+
import { cn } from "./lib/utils";
|
|
6
|
+
function getResponsiveColumnCount(width, maxColumns, minColumnWidth, gap) {
|
|
7
|
+
const nextColumns = Math.floor((width + gap) / (minColumnWidth + gap));
|
|
8
|
+
return Math.max(1, Math.min(maxColumns, nextColumns));
|
|
9
|
+
}
|
|
10
|
+
function getAvailableWidth(element) {
|
|
11
|
+
var _a, _b, _c, _d, _e, _f;
|
|
12
|
+
return Math.max(element.getBoundingClientRect().width, element.clientWidth, element.offsetWidth, (_b = (_a = element.parentElement) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().width) !== null && _b !== void 0 ? _b : 0, (_d = (_c = element.parentElement) === null || _c === void 0 ? void 0 : _c.clientWidth) !== null && _d !== void 0 ? _d : 0, (_f = (_e = element.parentElement) === null || _e === void 0 ? void 0 : _e.offsetWidth) !== null && _f !== void 0 ? _f : 0);
|
|
13
|
+
}
|
|
14
|
+
function LabelValueGrid({ items, columns = 4, className, itemClassName, minColumnWidth = 192, style, ...props }) {
|
|
15
|
+
const containerRef = React.useRef(null);
|
|
16
|
+
const [resolvedColumns, setResolvedColumns] = React.useState(columns);
|
|
17
|
+
React.useLayoutEffect(() => {
|
|
18
|
+
const element = containerRef.current;
|
|
19
|
+
if (!element)
|
|
20
|
+
return;
|
|
21
|
+
const GAP_PX = 24;
|
|
22
|
+
let frameId = null;
|
|
23
|
+
const updateColumns = () => {
|
|
24
|
+
const width = getAvailableWidth(element);
|
|
25
|
+
if (width <= 0) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
setResolvedColumns(getResponsiveColumnCount(width, columns, minColumnWidth, GAP_PX));
|
|
29
|
+
};
|
|
30
|
+
const requestUpdate = () => {
|
|
31
|
+
if (frameId !== null) {
|
|
32
|
+
window.cancelAnimationFrame(frameId);
|
|
33
|
+
}
|
|
34
|
+
frameId = window.requestAnimationFrame(updateColumns);
|
|
35
|
+
};
|
|
36
|
+
requestUpdate();
|
|
37
|
+
const observer = new ResizeObserver(() => {
|
|
38
|
+
requestUpdate();
|
|
39
|
+
});
|
|
40
|
+
observer.observe(element);
|
|
41
|
+
if (element.parentElement) {
|
|
42
|
+
observer.observe(element.parentElement);
|
|
43
|
+
}
|
|
44
|
+
window.addEventListener("resize", requestUpdate);
|
|
45
|
+
return () => {
|
|
46
|
+
if (frameId !== null) {
|
|
47
|
+
window.cancelAnimationFrame(frameId);
|
|
48
|
+
}
|
|
49
|
+
window.removeEventListener("resize", requestUpdate);
|
|
50
|
+
observer.disconnect();
|
|
51
|
+
};
|
|
52
|
+
}, [columns, minColumnWidth]);
|
|
53
|
+
return (_jsx("div", { ref: containerRef, className: cn("grid auto-rows-fr items-start gap-x-6 gap-y-3", className), style: {
|
|
54
|
+
...style,
|
|
55
|
+
gridTemplateColumns: `repeat(${resolvedColumns}, minmax(0, 1fr))`,
|
|
56
|
+
}, ...props, children: items.map((item, index) => {
|
|
57
|
+
var _a;
|
|
58
|
+
return (_jsx(LabelValue, { ...item, className: cn("min-w-0", itemClassName, item.className) }, (_a = item.id) !== null && _a !== void 0 ? _a : `${item.label}-${index}`));
|
|
59
|
+
}) }));
|
|
60
|
+
}
|
|
61
|
+
export { LabelValueGrid };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export interface LabelValueProps {
|
|
3
|
+
label: string;
|
|
4
|
+
value: React.ReactNode;
|
|
5
|
+
className?: string;
|
|
6
|
+
labelClassName?: string;
|
|
7
|
+
hideIfEmpty?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function isEmptyValue(value: React.ReactNode): boolean;
|
|
10
|
+
declare function LabelValue({ label, value, className, labelClassName, hideIfEmpty, }: LabelValueProps): import("react/jsx-runtime").JSX.Element | null;
|
|
11
|
+
export { LabelValue };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "./lib/utils";
|
|
3
|
+
export function isEmptyValue(value) {
|
|
4
|
+
if (value === null || value === undefined || value === "")
|
|
5
|
+
return true;
|
|
6
|
+
if (typeof value === "string") {
|
|
7
|
+
const trimmed = value.trim().toLowerCase();
|
|
8
|
+
return trimmed === "" || trimmed === "n/a" || trimmed === "-";
|
|
9
|
+
}
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
function LabelValue({ label, value, className, labelClassName, hideIfEmpty = false, }) {
|
|
13
|
+
const empty = isEmptyValue(value);
|
|
14
|
+
if (hideIfEmpty && empty) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
return (_jsxs("div", { className: cn("min-w-0 h-full space-y-1", className), children: [_jsx("p", { className: cn("text-sm font-semibold text-foreground", 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
|
+
export { LabelValue };
|
package/dist/layout.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { type CountBadgeProps } from "./count-badge";
|
|
3
|
+
type SidebarContextType = {
|
|
4
|
+
isCollapsed: boolean;
|
|
5
|
+
toggle: () => void;
|
|
6
|
+
};
|
|
7
|
+
export declare function SidebarProvider({ children }: {
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export declare function useSidebar(): SidebarContextType;
|
|
11
|
+
export interface DashboardLayoutProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
12
|
+
}
|
|
13
|
+
declare function DashboardLayout({ className, children, ...props }: DashboardLayoutProps): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export interface DashboardSidebarProps extends React.HTMLAttributes<HTMLElement> {
|
|
15
|
+
logo?: React.ReactNode | ((context: {
|
|
16
|
+
isCollapsed: boolean;
|
|
17
|
+
}) => React.ReactNode);
|
|
18
|
+
search?: React.ReactNode | ((context: {
|
|
19
|
+
isCollapsed: boolean;
|
|
20
|
+
}) => React.ReactNode);
|
|
21
|
+
navGroups?: DashboardSidebarNavGroup[];
|
|
22
|
+
footerItems?: DashboardSidebarNavItem[];
|
|
23
|
+
footer?: React.ReactNode | ((context: {
|
|
24
|
+
isCollapsed: boolean;
|
|
25
|
+
}) => React.ReactNode);
|
|
26
|
+
toggleAriaLabel?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface DashboardSidebarNavItem {
|
|
29
|
+
id: string;
|
|
30
|
+
label: string;
|
|
31
|
+
icon: React.ElementType;
|
|
32
|
+
href?: string;
|
|
33
|
+
target?: string;
|
|
34
|
+
rel?: string;
|
|
35
|
+
active?: boolean;
|
|
36
|
+
disabled?: boolean;
|
|
37
|
+
wip?: boolean;
|
|
38
|
+
badgeCount?: number;
|
|
39
|
+
badgeVariant?: CountBadgeProps["variant"];
|
|
40
|
+
badgePulse?: boolean;
|
|
41
|
+
trailing?: React.ReactNode;
|
|
42
|
+
onClick?: () => void;
|
|
43
|
+
onHoverStart?: () => void;
|
|
44
|
+
onHoverEnd?: () => void;
|
|
45
|
+
}
|
|
46
|
+
export interface DashboardSidebarNavGroup {
|
|
47
|
+
label?: string;
|
|
48
|
+
items: DashboardSidebarNavItem[];
|
|
49
|
+
}
|
|
50
|
+
declare function DashboardSidebar({ className, children, logo, search, navGroups, footerItems, footer, toggleAriaLabel, ...props }: DashboardSidebarProps): import("react/jsx-runtime").JSX.Element;
|
|
51
|
+
export interface DashboardContentProps extends React.HTMLAttributes<HTMLElement> {
|
|
52
|
+
surfaceClassName?: string;
|
|
53
|
+
}
|
|
54
|
+
declare function DashboardContent({ className, surfaceClassName, children, ...props }: DashboardContentProps): import("react/jsx-runtime").JSX.Element;
|
|
55
|
+
export { DashboardLayout, DashboardSidebar, DashboardContent };
|
package/dist/layout.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
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 { ChevronLeftIcon } from "./animated-icons/chevron-left";
|
|
5
|
+
import { ChevronRightIcon } from "./animated-icons/chevron-right";
|
|
6
|
+
import { Badge } from "./badge";
|
|
7
|
+
import { Button } from "./button";
|
|
8
|
+
import { CountBadge } from "./count-badge";
|
|
9
|
+
import { Tooltip } from "./tooltip";
|
|
10
|
+
import { cn } from "./lib/utils";
|
|
11
|
+
const COLLAPSE_BREAKPOINT = 1280;
|
|
12
|
+
const SidebarContext = React.createContext(undefined);
|
|
13
|
+
export function SidebarProvider({ children }) {
|
|
14
|
+
const [isCollapsed, setIsCollapsed] = React.useState(false);
|
|
15
|
+
const manualCollapsedRef = React.useRef(false);
|
|
16
|
+
const toggle = () => {
|
|
17
|
+
setIsCollapsed((prev) => {
|
|
18
|
+
const next = !prev;
|
|
19
|
+
manualCollapsedRef.current = next;
|
|
20
|
+
return next;
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
React.useEffect(() => {
|
|
24
|
+
const handleResize = () => {
|
|
25
|
+
if (window.innerWidth < COLLAPSE_BREAKPOINT) {
|
|
26
|
+
setIsCollapsed(true);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
setIsCollapsed(manualCollapsedRef.current);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
handleResize();
|
|
33
|
+
window.addEventListener("resize", handleResize);
|
|
34
|
+
return () => window.removeEventListener("resize", handleResize);
|
|
35
|
+
}, []);
|
|
36
|
+
return (_jsx(SidebarContext.Provider, { value: { isCollapsed, toggle }, children: children }));
|
|
37
|
+
}
|
|
38
|
+
export function useSidebar() {
|
|
39
|
+
const context = React.useContext(SidebarContext);
|
|
40
|
+
if (!context) {
|
|
41
|
+
throw new Error("useSidebar must be used within a SidebarProvider");
|
|
42
|
+
}
|
|
43
|
+
return context;
|
|
44
|
+
}
|
|
45
|
+
function DashboardLayout({ className, children, ...props }) {
|
|
46
|
+
return (_jsx("div", { className: cn("flex min-h-screen bg-[hsl(var(--layout-shell-background))]", className), ...props, children: children }));
|
|
47
|
+
}
|
|
48
|
+
function SidebarShell({ isCollapsed, className, children, ...props }) {
|
|
49
|
+
return (_jsx("aside", { className: cn("fixed left-0 top-0 z-30 flex h-screen 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
|
|
50
|
+
? "w-[var(--layout-sidebar-collapsed-width)]"
|
|
51
|
+
: "w-[var(--layout-sidebar-width)]", className), ...props, children: children }));
|
|
52
|
+
}
|
|
53
|
+
function SidebarHeader({ className, children, }) {
|
|
54
|
+
return _jsx("div", { className: cn("shrink-0", className), children: children });
|
|
55
|
+
}
|
|
56
|
+
function SidebarContent({ className, children, }) {
|
|
57
|
+
return _jsx("div", { className: cn("flex-1 overflow-y-auto", className), children: children });
|
|
58
|
+
}
|
|
59
|
+
function SidebarFooter({ className, children, }) {
|
|
60
|
+
return _jsx("div", { className: cn("shrink-0", className), children: children });
|
|
61
|
+
}
|
|
62
|
+
function SidebarGroup({ className, children, }) {
|
|
63
|
+
return _jsx("div", { className: className, children: children });
|
|
64
|
+
}
|
|
65
|
+
function SidebarGroupLabel({ className, children, }) {
|
|
66
|
+
return (_jsx("p", { className: cn("px-3 py-2 text-xs font-semibold uppercase tracking-wider text-muted-foreground", className), children: children }));
|
|
67
|
+
}
|
|
68
|
+
function SidebarMenu({ className, children, }) {
|
|
69
|
+
return _jsx("ul", { className: cn("space-y-1", className), children: children });
|
|
70
|
+
}
|
|
71
|
+
function SidebarMenuItem({ className, children, }) {
|
|
72
|
+
return _jsx("li", { className: className, children: children });
|
|
73
|
+
}
|
|
74
|
+
function SidebarMenuButton({ active = false, collapsed = false, disabled = false, className, asChild = false, children, ...props }) {
|
|
75
|
+
return (_jsx(Button, { asChild: asChild, variant: "ghost", disabled: disabled, className: cn("h-auto w-full justify-start gap-3 rounded-lg py-2 text-sm font-medium shadow-none", collapsed ? "justify-center px-3" : "px-3", disabled
|
|
76
|
+
? "text-muted-foreground/50 hover:bg-transparent hover:text-muted-foreground/50"
|
|
77
|
+
: active
|
|
78
|
+
? "bg-white text-foreground hover:bg-white hover:text-foreground"
|
|
79
|
+
: "text-muted-foreground hover:bg-white hover:text-foreground", className), ...props, children: children }));
|
|
80
|
+
}
|
|
81
|
+
function renderSidebarSlot(slot, isCollapsed) {
|
|
82
|
+
if (typeof slot === "function") {
|
|
83
|
+
return slot({ isCollapsed });
|
|
84
|
+
}
|
|
85
|
+
return slot !== null && slot !== void 0 ? slot : null;
|
|
86
|
+
}
|
|
87
|
+
function DashboardSidebarItem({ item, isCollapsed, }) {
|
|
88
|
+
const Icon = item.icon;
|
|
89
|
+
const iconRef = React.useRef(null);
|
|
90
|
+
const showWipBadge = item.disabled || item.wip;
|
|
91
|
+
const handleMouseEnter = () => {
|
|
92
|
+
var _a, _b, _c;
|
|
93
|
+
if (!item.disabled) {
|
|
94
|
+
(_b = (_a = iconRef.current) === null || _a === void 0 ? void 0 : _a.startAnimation) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
95
|
+
(_c = item.onHoverStart) === null || _c === void 0 ? void 0 : _c.call(item);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
const handleMouseLeave = () => {
|
|
99
|
+
var _a, _b, _c;
|
|
100
|
+
if (!item.disabled) {
|
|
101
|
+
(_b = (_a = iconRef.current) === null || _a === void 0 ? void 0 : _a.stopAnimation) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
102
|
+
(_c = item.onHoverEnd) === null || _c === void 0 ? void 0 : _c.call(item);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
const content = (_jsxs(_Fragment, { children: [_jsxs("div", { className: "relative flex h-5 w-5 shrink-0 items-center justify-center", children: [_jsx(Icon, { ref: iconRef, size: 16 }), isCollapsed && item.badgeCount !== undefined ? (_jsx(CountBadge, { count: item.badgeCount, variant: item.badgeVariant, size: "sm", pulse: item.badgePulse, className: "absolute -right-2 -top-2" })) : null] }), !isCollapsed ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "flex-1 truncate text-left", children: item.label }), showWipBadge ? _jsx(Badge, { variant: "muted", children: "WIP" }) : null, item.trailing, item.badgeCount !== undefined ? (_jsx(CountBadge, { count: item.badgeCount, variant: item.badgeVariant, size: "md", pulse: item.badgePulse })) : null] })) : null] }));
|
|
106
|
+
const button = (_jsx(SidebarMenuButton, { onClick: item.onClick, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onFocus: handleMouseEnter, onBlur: handleMouseLeave, disabled: item.disabled, active: item.active, collapsed: isCollapsed, "aria-current": item.active ? "page" : undefined, children: content }));
|
|
107
|
+
const interactive = item.href && !item.disabled ? (_jsx(SidebarMenuButton, { asChild: true, onClick: item.onClick, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onFocus: handleMouseEnter, onBlur: handleMouseLeave, active: item.active, collapsed: isCollapsed, "aria-current": item.active ? "page" : undefined, children: _jsx("a", { href: item.href, target: item.target, rel: item.rel, children: content }) })) : (button);
|
|
108
|
+
if (isCollapsed) {
|
|
109
|
+
return (_jsx(Tooltip, { side: "right", content: _jsxs("p", { children: [item.label, showWipBadge ? " (WIP)" : ""] }), children: interactive }));
|
|
110
|
+
}
|
|
111
|
+
return interactive;
|
|
112
|
+
}
|
|
113
|
+
function DashboardSidebar({ className, children, logo, search, navGroups, footerItems, footer, toggleAriaLabel = "Toggle sidebar", ...props }) {
|
|
114
|
+
var _a;
|
|
115
|
+
const { isCollapsed, toggle } = useSidebar();
|
|
116
|
+
const hasStructuredSidebar = logo !== undefined ||
|
|
117
|
+
search !== undefined ||
|
|
118
|
+
navGroups !== undefined ||
|
|
119
|
+
footerItems !== undefined ||
|
|
120
|
+
footer !== undefined;
|
|
121
|
+
if (!hasStructuredSidebar) {
|
|
122
|
+
return (_jsx(SidebarShell, { isCollapsed: isCollapsed, className: className, ...props, children: children }));
|
|
123
|
+
}
|
|
124
|
+
return (_jsxs(SidebarShell, { isCollapsed: isCollapsed, className: className, ...props, children: [_jsxs(SidebarHeader, { className: cn("flex h-14 items-center gap-2", isCollapsed ? "justify-center px-2" : "justify-between px-3"), children: [renderSidebarSlot(logo, isCollapsed), _jsx(Button, { variant: "ghost", size: "icon", "aria-label": toggleAriaLabel, className: "h-7 w-7 shrink-0", onClick: toggle, children: isCollapsed ? (_jsx(ChevronRightIcon, { size: 16 })) : (_jsx(ChevronLeftIcon, { size: 16 })) })] }), search ? (_jsx(SidebarHeader, { className: "px-2 pb-3 pt-2", children: renderSidebarSlot(search, isCollapsed) })) : null, search || ((_a = navGroups === null || navGroups === void 0 ? void 0 : navGroups.length) !== null && _a !== void 0 ? _a : 0) > 0 ? (_jsx(SidebarHeader, { className: "mx-2 mb-2 border-t" })) : null, (navGroups === null || navGroups === void 0 ? void 0 : navGroups.length) ? (_jsx(SidebarContent, { className: "p-2", children: _jsx("nav", { "aria-label": "Sidebar navigation", children: navGroups.map((group, groupIndex) => {
|
|
125
|
+
var _a;
|
|
126
|
+
return (_jsxs(SidebarGroup, { children: [isCollapsed && groupIndex > 0 ? (_jsx("div", { className: "mx-2 my-2 border-t" })) : null, _jsxs("div", { className: cn(!isCollapsed && groupIndex > 0 && "mt-4"), children: [!isCollapsed && group.label ? (_jsx(SidebarGroupLabel, { children: group.label })) : null, _jsx(SidebarMenu, { children: group.items.map((item) => (_jsx(SidebarMenuItem, { children: _jsx(DashboardSidebarItem, { item: item, isCollapsed: isCollapsed }) }, item.id))) })] })] }, `${(_a = group.label) !== null && _a !== void 0 ? _a : "group"}-${groupIndex}`));
|
|
127
|
+
}) }) })) : null, (footerItems === null || footerItems === void 0 ? void 0 : footerItems.length) ? (_jsx(SidebarFooter, { className: "px-2 py-1", children: _jsx(SidebarMenu, { children: footerItems.map((item) => (_jsx(SidebarMenuItem, { children: _jsx(DashboardSidebarItem, { item: item, isCollapsed: isCollapsed }) }, item.id))) }) })) : null, footer ? _jsx(SidebarFooter, { className: "mx-2 mt-2 border-t pt-2" }) : null, footer ? (_jsx(SidebarFooter, { children: renderSidebarSlot(footer, isCollapsed) })) : null] }));
|
|
128
|
+
}
|
|
129
|
+
function DashboardContent({ className, surfaceClassName, children, ...props }) {
|
|
130
|
+
const { isCollapsed, toggle } = useSidebar();
|
|
131
|
+
const handleContentClick = React.useCallback(() => {
|
|
132
|
+
if (!isCollapsed && window.innerWidth < COLLAPSE_BREAKPOINT) {
|
|
133
|
+
toggle();
|
|
134
|
+
}
|
|
135
|
+
}, [isCollapsed, toggle]);
|
|
136
|
+
return (_jsx("main", { onClick: handleContentClick, className: cn("flex min-w-0 flex-1 flex-col bg-[hsl(var(--layout-shell-background))] pb-[var(--layout-content-padding)] pl-[var(--layout-content-gap-start)] pr-[var(--layout-content-padding)] pt-[var(--layout-content-padding)] transition-all duration-300 md:pb-[var(--layout-content-padding-md)] md:pl-[var(--layout-content-gap-start-md)] md:pr-[var(--layout-content-padding-md)] md:pt-[var(--layout-content-padding-md)] lg:pb-[var(--layout-content-padding-lg)] lg:pl-[var(--layout-content-gap-start-lg)] lg:pr-[var(--layout-content-padding-lg)] lg:pt-[var(--layout-content-padding-lg)]", isCollapsed
|
|
137
|
+
? "ml-[var(--layout-sidebar-collapsed-width)]"
|
|
138
|
+
: "ml-[var(--layout-sidebar-width)]", className), ...props, children: _jsx("div", { className: cn("flex flex-1 flex-col gap-[var(--layout-surface-gap)] overflow-x-clip rounded-[var(--radius-lg)] bg-background p-[var(--layout-surface-padding)] shadow-sm md:p-[var(--layout-surface-padding-md)] lg:p-[var(--layout-surface-padding-lg)]", surfaceClassName), children: children }) }));
|
|
139
|
+
}
|
|
140
|
+
export { DashboardLayout, DashboardSidebar, DashboardContent };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { OTPInput } from "input-otp";
|
|
3
|
+
export interface OtpInputProps extends Omit<React.ComponentProps<typeof OTPInput>, "children" | "className" | "containerClassName" | "maxLength" | "onChange" | "pasteTransformer" | "render" | "value"> {
|
|
4
|
+
label: React.ReactNode;
|
|
5
|
+
value: string;
|
|
6
|
+
onValueChange: (value: string) => void;
|
|
7
|
+
description?: React.ReactNode;
|
|
8
|
+
error?: React.ReactNode;
|
|
9
|
+
invalid?: boolean;
|
|
10
|
+
hideLabel?: boolean;
|
|
11
|
+
onClearError?: () => void;
|
|
12
|
+
length?: number;
|
|
13
|
+
className?: string;
|
|
14
|
+
groupClassName?: string;
|
|
15
|
+
slotClassName?: string;
|
|
16
|
+
labelClassName?: string;
|
|
17
|
+
descriptionClassName?: string;
|
|
18
|
+
errorClassName?: string;
|
|
19
|
+
}
|
|
20
|
+
declare const OtpInput: React.ForwardRefExoticComponent<Omit<OtpInputProps, "ref"> & React.RefAttributes<HTMLInputElement>>;
|
|
21
|
+
export { OtpInput };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { OTPInput, REGEXP_ONLY_DIGITS, } from "input-otp";
|
|
5
|
+
import { Field, FieldDescription, FieldError, FieldLabel } from "./field";
|
|
6
|
+
import { validateOtp } from "./input-validation";
|
|
7
|
+
import { cn } from "./lib/utils";
|
|
8
|
+
function OtpSlot({ slot, invalid, className, }) {
|
|
9
|
+
var _a, _b;
|
|
10
|
+
return (_jsxs("div", { "data-slot": "otp-slot", className: cn("relative flex size-9 items-center justify-center rounded-md border bg-transparent text-sm font-medium shadow-sm 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
|
+
(invalid
|
|
12
|
+
? "border-destructive ring-[3px] ring-destructive/20"
|
|
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] }));
|
|
14
|
+
}
|
|
15
|
+
const OtpInput = React.forwardRef(({ label, value, onValueChange, description, error: externalError, invalid = false, hideLabel = false, onClearError, length = 6, className, groupClassName, slotClassName, labelClassName, descriptionClassName, errorClassName, id, disabled, onBlur, autoComplete = "one-time-code", inputMode = "numeric", pattern = REGEXP_ONLY_DIGITS, pushPasswordManagerStrategy = "none", "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy, ...props }, ref) => {
|
|
16
|
+
const [internalError, setInternalError] = React.useState();
|
|
17
|
+
const [touched, setTouched] = React.useState(false);
|
|
18
|
+
const validate = React.useMemo(() => validateOtp(length, "Verification code"), [length]);
|
|
19
|
+
const generatedId = React.useId();
|
|
20
|
+
const inputId = id !== null && id !== void 0 ? id : `otp-input-${generatedId}`;
|
|
21
|
+
const displayError = externalError !== null && externalError !== void 0 ? externalError : internalError;
|
|
22
|
+
const errorId = displayError ? `${inputId}-error` : undefined;
|
|
23
|
+
const showDescription = Boolean(description) && !displayError;
|
|
24
|
+
const descriptionId = showDescription ? `${inputId}-description` : undefined;
|
|
25
|
+
const isInvalid = invalid ||
|
|
26
|
+
ariaInvalid === true ||
|
|
27
|
+
ariaInvalid === "true" ||
|
|
28
|
+
Boolean(displayError);
|
|
29
|
+
const describedBy = [ariaDescribedBy, descriptionId, errorId]
|
|
30
|
+
.filter(Boolean)
|
|
31
|
+
.join(" ") || undefined;
|
|
32
|
+
const handleChange = React.useCallback((nextValue) => {
|
|
33
|
+
onValueChange(nextValue);
|
|
34
|
+
if (externalError) {
|
|
35
|
+
onClearError === null || onClearError === void 0 ? void 0 : onClearError();
|
|
36
|
+
}
|
|
37
|
+
if (touched) {
|
|
38
|
+
setInternalError(validate(nextValue));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (internalError) {
|
|
42
|
+
setInternalError(undefined);
|
|
43
|
+
}
|
|
44
|
+
}, [
|
|
45
|
+
externalError,
|
|
46
|
+
internalError,
|
|
47
|
+
onClearError,
|
|
48
|
+
onValueChange,
|
|
49
|
+
touched,
|
|
50
|
+
validate,
|
|
51
|
+
]);
|
|
52
|
+
const handleBlur = React.useCallback((event) => {
|
|
53
|
+
setTouched(true);
|
|
54
|
+
setInternalError(validate(value));
|
|
55
|
+
onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
|
|
56
|
+
}, [onBlur, validate, value]);
|
|
57
|
+
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 }), _jsx(OTPInput, { ref: ref, id: inputId, value: value, onChange: handleChange, onBlur: handleBlur, disabled: disabled, maxLength: length, autoComplete: autoComplete, inputMode: inputMode, pattern: pattern, pushPasswordManagerStrategy: pushPasswordManagerStrategy, "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": describedBy, containerClassName: cn("flex w-full items-center gap-2", groupClassName), render: ({ slots }) => slots.map((slot, index) => (_jsx(OtpSlot, { slot: slot, invalid: isInvalid, className: slotClassName }, `${inputId}-${index}`))), ...props }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: displayError })] }));
|
|
58
|
+
});
|
|
59
|
+
OtpInput.displayName = "OtpInput";
|
|
60
|
+
export { OtpInput };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export interface PageHeaderProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
|
|
3
|
+
title: React.ReactNode;
|
|
4
|
+
subtitle?: React.ReactNode;
|
|
5
|
+
breadcrumbs?: React.ReactNode;
|
|
6
|
+
children?: React.ReactNode;
|
|
7
|
+
}
|
|
8
|
+
declare function PageHeader({ title, subtitle, breadcrumbs, children, className, ...props }: PageHeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export { PageHeader };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Typography } from "./typography";
|
|
3
|
+
function PageHeader({ title, subtitle, breadcrumbs, children, className, ...props }) {
|
|
4
|
+
return (_jsx("div", { className: className, ...props, children: _jsxs("div", { className: "flex items-center justify-between gap-4", children: [_jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [breadcrumbs ? _jsx("div", { className: "mb-1", children: breadcrumbs }) : null, _jsx("div", { className: "flex items-center gap-3", children: _jsx(Typography, { as: "h2", variant: "title", children: title }) }), subtitle ? (_jsx(Typography, { as: "p", variant: "caption", children: subtitle })) : null] }), children] }) }));
|
|
5
|
+
}
|
|
6
|
+
export { PageHeader };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export interface PaginationProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
3
|
+
currentPage: number;
|
|
4
|
+
totalPages: number;
|
|
5
|
+
totalItems: number;
|
|
6
|
+
itemsPerPage?: number;
|
|
7
|
+
onPageChange: (page: number) => void;
|
|
8
|
+
onItemsPerPageChange?: (itemsPerPage: number) => void;
|
|
9
|
+
itemsPerPageOptions?: number[];
|
|
10
|
+
}
|
|
11
|
+
declare function Pagination({ currentPage, totalPages, totalItems, itemsPerPage, onPageChange, onItemsPerPageChange, itemsPerPageOptions, className, ...props }: PaginationProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export { Pagination };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { ChevronsLeft, ChevronsRight } from "lucide-react";
|
|
4
|
+
import { ChevronLeftIcon } from "./animated-icons/chevron-left";
|
|
5
|
+
import { ChevronRightIcon } from "./animated-icons/chevron-right";
|
|
6
|
+
import { Button } from "./button";
|
|
7
|
+
import { SelectContent, SelectItem, SelectRoot, SelectTrigger, SelectValue, } from "./select-primitive";
|
|
8
|
+
import { cn } from "./lib/utils";
|
|
9
|
+
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
|
+
PaginationNav.displayName = "PaginationNav";
|
|
11
|
+
const PaginationContent = React.forwardRef(({ className, ...props }, ref) => (_jsx("ul", { ref: ref, className: cn("flex flex-row items-center gap-1", className), ...props })));
|
|
12
|
+
PaginationContent.displayName = "PaginationContent";
|
|
13
|
+
const PaginationItem = React.forwardRef(({ className, ...props }, ref) => (_jsx("li", { ref: ref, className: cn("", className), ...props })));
|
|
14
|
+
PaginationItem.displayName = "PaginationItem";
|
|
15
|
+
function Pagination({ currentPage, totalPages, totalItems, itemsPerPage = 5, onPageChange, onItemsPerPageChange, itemsPerPageOptions = [5, 10, 20, 50, 100], className, ...props }) {
|
|
16
|
+
const normalizedTotalPages = totalPages || 1;
|
|
17
|
+
const startItem = totalItems === 0 ? 0 : (currentPage - 1) * itemsPerPage + 1;
|
|
18
|
+
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("span", { className: "text-sm text-muted-foreground", children: "Rows per page" }), _jsxs(SelectRoot, { value: itemsPerPage.toString(), onValueChange: (value) => {
|
|
20
|
+
onItemsPerPageChange(Number(value));
|
|
21
|
+
onPageChange(1);
|
|
22
|
+
}, 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" }) }) })] }) })] })] }));
|
|
23
|
+
}
|
|
24
|
+
export { Pagination };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { InputControl } from "./input-control";
|
|
3
|
+
import { type Validator } from "./input-validation";
|
|
4
|
+
export interface CountryCode {
|
|
5
|
+
code: string;
|
|
6
|
+
dialCode: string;
|
|
7
|
+
name: string;
|
|
8
|
+
}
|
|
9
|
+
export declare const DEFAULT_COUNTRY_CODES: CountryCode[];
|
|
10
|
+
export interface PhoneInputProps extends Omit<React.ComponentProps<typeof InputControl>, "className" | "defaultValue" | "onChange" | "type" | "value"> {
|
|
11
|
+
label: React.ReactNode;
|
|
12
|
+
value: string;
|
|
13
|
+
onValueChange: (value: string) => void;
|
|
14
|
+
countryCode?: string;
|
|
15
|
+
onCountryCodeChange?: (code: string) => void;
|
|
16
|
+
error?: string;
|
|
17
|
+
invalid?: boolean;
|
|
18
|
+
description?: React.ReactNode;
|
|
19
|
+
hideLabel?: boolean;
|
|
20
|
+
onClearError?: () => void;
|
|
21
|
+
validators?: Validator[];
|
|
22
|
+
fieldName?: string;
|
|
23
|
+
validateOnChange?: boolean;
|
|
24
|
+
countryCodes?: CountryCode[];
|
|
25
|
+
defaultCountryCode?: string;
|
|
26
|
+
className?: string;
|
|
27
|
+
inputClassName?: string;
|
|
28
|
+
triggerClassName?: string;
|
|
29
|
+
contentClassName?: string;
|
|
30
|
+
labelClassName?: string;
|
|
31
|
+
descriptionClassName?: string;
|
|
32
|
+
errorClassName?: string;
|
|
33
|
+
countryCodeAriaLabel?: string;
|
|
34
|
+
}
|
|
35
|
+
declare function PhoneInput({ label, value, onValueChange, countryCode, onCountryCodeChange, error: externalError, invalid, description, hideLabel, onClearError, validators, required, fieldName, validateOnChange, countryCodes, defaultCountryCode, className, inputClassName, triggerClassName, contentClassName, labelClassName, descriptionClassName, errorClassName, countryCodeAriaLabel, id, disabled, onBlur, autoComplete, inputMode, placeholder, "aria-describedby": ariaDescribedBy, ...props }: PhoneInputProps): import("react/jsx-runtime").JSX.Element;
|
|
36
|
+
export { PhoneInput };
|