@gouno/ui 0.3.2
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/CHANGELOG.md +120 -0
- package/LICENSE +21 -0
- package/README.md +144 -0
- package/dist/base.css +239 -0
- package/dist/bootstrap.js +22 -0
- package/dist/components/primitives/alert-dialog.d.ts +18 -0
- package/dist/components/primitives/alert-dialog.js +43 -0
- package/dist/components/primitives/alert.d.ts +9 -0
- package/dist/components/primitives/alert.js +25 -0
- package/dist/components/primitives/avatar.d.ts +14 -0
- package/dist/components/primitives/avatar.js +42 -0
- package/dist/components/primitives/badge.d.ts +9 -0
- package/dist/components/primitives/badge.js +25 -0
- package/dist/components/primitives/button.d.ts +10 -0
- package/dist/components/primitives/button.js +36 -0
- package/dist/components/primitives/checkbox.d.ts +4 -0
- package/dist/components/primitives/checkbox.js +10 -0
- package/dist/components/primitives/dialog.d.ts +20 -0
- package/dist/components/primitives/dialog.js +37 -0
- package/dist/components/primitives/dropdown-menu.d.ts +25 -0
- package/dist/components/primitives/dropdown-menu.js +51 -0
- package/dist/components/primitives/empty.d.ts +11 -0
- package/dist/components/primitives/empty.js +33 -0
- package/dist/components/primitives/field.d.ts +24 -0
- package/dist/components/primitives/field.js +75 -0
- package/dist/components/primitives/input.d.ts +3 -0
- package/dist/components/primitives/input.js +7 -0
- package/dist/components/primitives/label.d.ts +4 -0
- package/dist/components/primitives/label.js +8 -0
- package/dist/components/primitives/popover.d.ts +16 -0
- package/dist/components/primitives/popover.js +31 -0
- package/dist/components/primitives/radio-group.d.ts +5 -0
- package/dist/components/primitives/radio-group.js +13 -0
- package/dist/components/primitives/select.d.ts +15 -0
- package/dist/components/primitives/select.js +38 -0
- package/dist/components/primitives/separator.d.ts +4 -0
- package/dist/components/primitives/separator.js +8 -0
- package/dist/components/primitives/sheet.d.ts +17 -0
- package/dist/components/primitives/sheet.js +41 -0
- package/dist/components/primitives/skeleton.d.ts +2 -0
- package/dist/components/primitives/skeleton.js +6 -0
- package/dist/components/primitives/switch.d.ts +6 -0
- package/dist/components/primitives/switch.js +8 -0
- package/dist/components/primitives/table.d.ts +28 -0
- package/dist/components/primitives/table.js +44 -0
- package/dist/components/primitives/tabs.d.ts +11 -0
- package/dist/components/primitives/tabs.js +30 -0
- package/dist/components/primitives/textarea.d.ts +3 -0
- package/dist/components/primitives/textarea.js +7 -0
- package/dist/components/primitives/tooltip.d.ts +13 -0
- package/dist/components/primitives/tooltip.js +24 -0
- package/dist/core/affix.d.ts +14 -0
- package/dist/core/affix.js +29 -0
- package/dist/core/alert.d.ts +54 -0
- package/dist/core/alert.js +56 -0
- package/dist/core/anchor.d.ts +16 -0
- package/dist/core/anchor.js +31 -0
- package/dist/core/app.d.ts +5 -0
- package/dist/core/app.js +5 -0
- package/dist/core/aspect-ratio.d.ts +6 -0
- package/dist/core/aspect-ratio.js +6 -0
- package/dist/core/autocomplete.d.ts +24 -0
- package/dist/core/autocomplete.js +139 -0
- package/dist/core/avatar.d.ts +26 -0
- package/dist/core/avatar.js +20 -0
- package/dist/core/badge.d.ts +16 -0
- package/dist/core/badge.js +22 -0
- package/dist/core/breadcrumb.d.ts +55 -0
- package/dist/core/breadcrumb.js +69 -0
- package/dist/core/button.d.ts +49 -0
- package/dist/core/button.js +66 -0
- package/dist/core/calendar.d.ts +48 -0
- package/dist/core/calendar.js +179 -0
- package/dist/core/card.d.ts +30 -0
- package/dist/core/card.js +12 -0
- package/dist/core/carousel.d.ts +49 -0
- package/dist/core/carousel.js +166 -0
- package/dist/core/cascader.d.ts +19 -0
- package/dist/core/cascader.js +45 -0
- package/dist/core/code-block.d.ts +16 -0
- package/dist/core/code-block.js +47 -0
- package/dist/core/collapse.d.ts +49 -0
- package/dist/core/collapse.js +96 -0
- package/dist/core/color-picker.d.ts +9 -0
- package/dist/core/color-picker.js +8 -0
- package/dist/core/config-provider.d.ts +10 -0
- package/dist/core/config-provider.js +14 -0
- package/dist/core/control-types.d.ts +2 -0
- package/dist/core/control-types.js +7 -0
- package/dist/core/date-picker.d.ts +13 -0
- package/dist/core/date-picker.js +14 -0
- package/dist/core/date-range-picker.d.ts +26 -0
- package/dist/core/date-range-picker.js +10 -0
- package/dist/core/descriptions.d.ts +41 -0
- package/dist/core/descriptions.js +133 -0
- package/dist/core/drawer.d.ts +9 -0
- package/dist/core/drawer.js +58 -0
- package/dist/core/empty.d.ts +8 -0
- package/dist/core/empty.js +5 -0
- package/dist/core/feedback.d.ts +5 -0
- package/dist/core/feedback.js +5 -0
- package/dist/core/field.d.ts +15 -0
- package/dist/core/field.js +19 -0
- package/dist/core/float-button.d.ts +13 -0
- package/dist/core/float-button.js +22 -0
- package/dist/core/form.d.ts +19 -0
- package/dist/core/form.js +56 -0
- package/dist/core/icon-button.d.ts +7 -0
- package/dist/core/icon-button.js +4 -0
- package/dist/core/icon.d.ts +17 -0
- package/dist/core/icon.js +35 -0
- package/dist/core/image.d.ts +76 -0
- package/dist/core/image.js +189 -0
- package/dist/core/index.d.ts +79 -0
- package/dist/core/index.js +77 -0
- package/dist/core/input-number.d.ts +24 -0
- package/dist/core/input-number.js +70 -0
- package/dist/core/input-otp.d.ts +14 -0
- package/dist/core/input-otp.js +76 -0
- package/dist/core/input.d.ts +14 -0
- package/dist/core/input.js +51 -0
- package/dist/core/kbd.d.ts +8 -0
- package/dist/core/kbd.js +11 -0
- package/dist/core/layout-primitives.d.ts +11 -0
- package/dist/core/layout-primitives.js +8 -0
- package/dist/core/layout.d.ts +71 -0
- package/dist/core/layout.js +119 -0
- package/dist/core/list.d.ts +37 -0
- package/dist/core/list.js +35 -0
- package/dist/core/locale.d.ts +48 -0
- package/dist/core/locale.js +32 -0
- package/dist/core/mentions.d.ts +10 -0
- package/dist/core/mentions.js +112 -0
- package/dist/core/menu.d.ts +79 -0
- package/dist/core/menu.js +265 -0
- package/dist/core/message.d.ts +16 -0
- package/dist/core/message.js +36 -0
- package/dist/core/modal.d.ts +40 -0
- package/dist/core/modal.js +67 -0
- package/dist/core/notification.d.ts +15 -0
- package/dist/core/notification.js +39 -0
- package/dist/core/page-layout.d.ts +6 -0
- package/dist/core/page-layout.js +7 -0
- package/dist/core/pagination.d.ts +28 -0
- package/dist/core/pagination.js +69 -0
- package/dist/core/popconfirm.d.ts +13 -0
- package/dist/core/popconfirm.js +42 -0
- package/dist/core/progress.d.ts +6 -0
- package/dist/core/progress.js +8 -0
- package/dist/core/public-props.d.ts +82 -0
- package/dist/core/public-props.js +1 -0
- package/dist/core/qrcode.d.ts +14 -0
- package/dist/core/qrcode.js +17 -0
- package/dist/core/rate.d.ts +11 -0
- package/dist/core/rate.js +48 -0
- package/dist/core/result.d.ts +12 -0
- package/dist/core/result.js +19 -0
- package/dist/core/segmented.d.ts +29 -0
- package/dist/core/segmented.js +38 -0
- package/dist/core/select.d.ts +27 -0
- package/dist/core/select.js +143 -0
- package/dist/core/selection-controls.d.ts +12 -0
- package/dist/core/selection-controls.js +23 -0
- package/dist/core/separator.d.ts +24 -0
- package/dist/core/separator.js +17 -0
- package/dist/core/slider.d.ts +2 -0
- package/dist/core/slider.js +6 -0
- package/dist/core/spin.d.ts +7 -0
- package/dist/core/spin.js +7 -0
- package/dist/core/spinner.d.ts +3 -0
- package/dist/core/spinner.js +6 -0
- package/dist/core/splitter.d.ts +44 -0
- package/dist/core/splitter.js +208 -0
- package/dist/core/statistic.d.ts +12 -0
- package/dist/core/statistic.js +6 -0
- package/dist/core/steps.d.ts +54 -0
- package/dist/core/steps.js +106 -0
- package/dist/core/tabs.d.ts +40 -0
- package/dist/core/tabs.js +80 -0
- package/dist/core/tag.d.ts +19 -0
- package/dist/core/tag.js +29 -0
- package/dist/core/textarea.d.ts +8 -0
- package/dist/core/textarea.js +14 -0
- package/dist/core/time-picker.d.ts +9 -0
- package/dist/core/time-picker.js +8 -0
- package/dist/core/timeline.d.ts +37 -0
- package/dist/core/timeline.js +48 -0
- package/dist/core/tour.d.ts +18 -0
- package/dist/core/tour.js +51 -0
- package/dist/core/transfer.d.ts +16 -0
- package/dist/core/transfer.js +46 -0
- package/dist/core/tree-select.d.ts +19 -0
- package/dist/core/tree-select.js +19 -0
- package/dist/core/tree.d.ts +89 -0
- package/dist/core/tree.js +386 -0
- package/dist/core/typography.d.ts +20 -0
- package/dist/core/typography.js +12 -0
- package/dist/core/upload.d.ts +20 -0
- package/dist/core/upload.js +72 -0
- package/dist/core/watermark.d.ts +10 -0
- package/dist/core/watermark.js +21 -0
- package/dist/fonts/inter-LICENSE +93 -0
- package/dist/fonts/inter-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/jetbrains-mono-LICENSE +93 -0
- package/dist/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
- package/dist/gouno/app-shell.d.ts +22 -0
- package/dist/gouno/app-shell.js +27 -0
- package/dist/gouno/index.d.ts +4 -0
- package/dist/gouno/index.js +4 -0
- package/dist/gouno/page-container.d.ts +4 -0
- package/dist/gouno/page-container.js +6 -0
- package/dist/gouno/page-header.d.ts +8 -0
- package/dist/gouno/page-header.js +6 -0
- package/dist/hooks/use-overlay-body.d.ts +6 -0
- package/dist/hooks/use-overlay-body.js +18 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.js +5 -0
- package/dist/patterns/bulk-action-bar.d.ts +8 -0
- package/dist/patterns/bulk-action-bar.js +7 -0
- package/dist/patterns/index.d.ts +5 -0
- package/dist/patterns/index.js +5 -0
- package/dist/theme/index.d.ts +2 -0
- package/dist/theme/index.js +2 -0
- package/dist/theme/provider.d.ts +22 -0
- package/dist/theme/provider.js +76 -0
- package/dist/theme/theme-toggle.d.ts +6 -0
- package/dist/theme/theme-toggle.js +10 -0
- package/dist/tokens.css +164 -0
- package/package.json +111 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useCallback, useContext, useEffect, useRef, useState, } from "react";
|
|
3
|
+
const NotificationContext = createContext(null);
|
|
4
|
+
const DEFAULT_DURATION = 4500;
|
|
5
|
+
function resolveDuration(duration) {
|
|
6
|
+
if (duration === undefined)
|
|
7
|
+
return DEFAULT_DURATION;
|
|
8
|
+
if (!Number.isFinite(duration) || duration <= 0)
|
|
9
|
+
return DEFAULT_DURATION;
|
|
10
|
+
return duration;
|
|
11
|
+
}
|
|
12
|
+
export function NotificationProvider({ children, }) {
|
|
13
|
+
const [items, setItems] = useState([]);
|
|
14
|
+
const nextId = useRef(0);
|
|
15
|
+
const timers = useRef(new Map());
|
|
16
|
+
const open = useCallback((notice) => {
|
|
17
|
+
nextId.current += 1;
|
|
18
|
+
const id = nextId.current;
|
|
19
|
+
setItems((current) => [...current, { ...notice, id }]);
|
|
20
|
+
const timer = setTimeout(() => {
|
|
21
|
+
timers.current.delete(id);
|
|
22
|
+
setItems((current) => current.filter((item) => item.id !== id));
|
|
23
|
+
}, resolveDuration(notice.duration));
|
|
24
|
+
timers.current.set(id, timer);
|
|
25
|
+
}, []);
|
|
26
|
+
useEffect(() => () => {
|
|
27
|
+
for (const timer of timers.current.values())
|
|
28
|
+
clearTimeout(timer);
|
|
29
|
+
timers.current.clear();
|
|
30
|
+
}, []);
|
|
31
|
+
return (_jsxs(NotificationContext.Provider, { value: open, children: [children, _jsx("div", { "data-slot": "notification-region", className: "fixed right-4 top-4 z-[100] flex w-80 max-w-[calc(100vw-2rem)] flex-col gap-2", children: items.map((item) => (_jsxs("section", { "data-slot": "notification", role: "status", "aria-atomic": "true", className: "rounded-lg border bg-popover p-4 shadow-overlay", children: [_jsx("strong", { children: item.title }), item.description ? (_jsx("div", { className: "mt-1 text-sm text-muted-foreground", children: item.description })) : null] }, item.id))) })] }));
|
|
32
|
+
}
|
|
33
|
+
export function useNotification() {
|
|
34
|
+
const open = useContext(NotificationContext);
|
|
35
|
+
if (!open) {
|
|
36
|
+
throw new Error("useNotification must be used inside NotificationProvider");
|
|
37
|
+
}
|
|
38
|
+
return { open };
|
|
39
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { HTMLAttributes } from "react";
|
|
2
|
+
export declare const Layout: ({ className, ...props }: HTMLAttributes<HTMLDivElement>) => import("react").JSX.Element;
|
|
3
|
+
export declare const LayoutHeader: ({ className, ...props }: HTMLAttributes<HTMLElement>) => import("react").JSX.Element;
|
|
4
|
+
export declare const LayoutSider: ({ className, ...props }: HTMLAttributes<HTMLElement>) => import("react").JSX.Element;
|
|
5
|
+
export declare const LayoutContent: ({ className, ...props }: HTMLAttributes<HTMLElement>) => import("react").JSX.Element;
|
|
6
|
+
export declare const LayoutFooter: ({ className, ...props }: HTMLAttributes<HTMLElement>) => import("react").JSX.Element;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/utils.js";
|
|
3
|
+
export const Layout = ({ className, ...props }) => _jsx("div", { ...props, className: cn("flex min-h-0 min-w-0 flex-1 flex-col bg-background", className) });
|
|
4
|
+
export const LayoutHeader = ({ className, ...props }) => _jsx("header", { ...props, className: cn("flex min-h-14 items-center border-b px-5", className) });
|
|
5
|
+
export const LayoutSider = ({ className, ...props }) => _jsx("aside", { ...props, className: cn("w-60 shrink-0 border-r bg-sidebar p-4", className) });
|
|
6
|
+
export const LayoutContent = ({ className, ...props }) => _jsx("main", { ...props, className: cn("min-w-0 flex-1 p-5", className) });
|
|
7
|
+
export const LayoutFooter = ({ className, ...props }) => _jsx("footer", { ...props, className: cn("border-t px-5 py-4 text-sm text-muted-foreground", className) });
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { PaginationLocale } from "./locale.js";
|
|
2
|
+
import { type ReactNode } from "react";
|
|
3
|
+
export interface PaginationProps {
|
|
4
|
+
locale?: Partial<PaginationLocale>;
|
|
5
|
+
page?: number;
|
|
6
|
+
defaultPage?: number;
|
|
7
|
+
total: number;
|
|
8
|
+
pageSize?: number;
|
|
9
|
+
defaultPageSize?: number;
|
|
10
|
+
onChange?: (page: number, pageSize: number) => void;
|
|
11
|
+
onShowSizeChange?: (page: number, pageSize: number) => void;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
ariaLabel?: string;
|
|
14
|
+
showSizeChanger?: boolean;
|
|
15
|
+
pageSizeOptions?: number[];
|
|
16
|
+
showQuickJumper?: boolean;
|
|
17
|
+
showTotal?: (total: number, range: [number, number]) => ReactNode;
|
|
18
|
+
hideOnSinglePage?: boolean;
|
|
19
|
+
simple?: boolean;
|
|
20
|
+
showLessItems?: boolean;
|
|
21
|
+
size?: "small" | "middle";
|
|
22
|
+
align?: "start" | "center" | "end";
|
|
23
|
+
className?: string;
|
|
24
|
+
itemRender?: (page: number, type: "page" | "prev" | "next", originalElement: ReactNode) => ReactNode;
|
|
25
|
+
prevText?: ReactNode;
|
|
26
|
+
nextText?: ReactNode;
|
|
27
|
+
}
|
|
28
|
+
export declare function Pagination({ locale, page, defaultPage, total, pageSize, defaultPageSize, onChange, onShowSizeChange, disabled, ariaLabel, showSizeChanger, pageSizeOptions, showQuickJumper, showTotal, hideOnSinglePage, simple, showLessItems, size, align, className, itemRender, prevText, nextText, }: PaginationProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useComponentLocale } from "./config-provider.js";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { Button } from "./button.js";
|
|
5
|
+
import { cn } from "../lib/utils.js";
|
|
6
|
+
const positiveInteger = (value, fallback) => Number.isFinite(value) && value > 0 ? Math.max(1, Math.floor(value)) : fallback;
|
|
7
|
+
function pageItems(current, pages, less) {
|
|
8
|
+
const radius = less ? 1 : 2;
|
|
9
|
+
if (pages <= radius * 2 + 5)
|
|
10
|
+
return Array.from({ length: pages }, (_, i) => i + 1);
|
|
11
|
+
const start = Math.max(2, Math.min(current - radius, pages - radius * 2 - 1));
|
|
12
|
+
const end = Math.min(pages - 1, Math.max(current + radius, radius * 2 + 2));
|
|
13
|
+
const items = [1];
|
|
14
|
+
if (start > 2)
|
|
15
|
+
items.push("before");
|
|
16
|
+
for (let i = start; i <= end; i++)
|
|
17
|
+
items.push(i);
|
|
18
|
+
if (end < pages - 1)
|
|
19
|
+
items.push("after");
|
|
20
|
+
items.push(pages);
|
|
21
|
+
return items;
|
|
22
|
+
}
|
|
23
|
+
export function Pagination({ locale, page, defaultPage = 1, total, pageSize, defaultPageSize = 10, onChange, onShowSizeChange, disabled = false, ariaLabel, showSizeChanger = false, pageSizeOptions = [10, 20, 50, 100], showQuickJumper = false, showTotal, hideOnSinglePage = false, simple = false, showLessItems = false, size = "middle", align = "start", className, itemRender, prevText, nextText, }) {
|
|
24
|
+
const text = useComponentLocale("pagination", locale);
|
|
25
|
+
const previous = prevText === undefined ? text.prevText : prevText;
|
|
26
|
+
const next = nextText === undefined ? text.nextText : nextText;
|
|
27
|
+
const previousLabel = prevText !== undefined ? (typeof prevText === "string" ? prevText : undefined) : text.prevText;
|
|
28
|
+
const nextLabel = nextText !== undefined ? (typeof nextText === "string" ? nextText : undefined) : text.nextText;
|
|
29
|
+
const [internalPage, setInternalPage] = useState(defaultPage);
|
|
30
|
+
const [internalSize, setInternalSize] = useState(defaultPageSize);
|
|
31
|
+
const [jump, setJump] = useState("");
|
|
32
|
+
const count = Number.isFinite(total) ? Math.max(0, Math.floor(total)) : 0;
|
|
33
|
+
const resolvedSize = positiveInteger(pageSize ?? internalSize, 10);
|
|
34
|
+
const pages = Math.max(1, Math.ceil(count / resolvedSize));
|
|
35
|
+
const current = Math.min(pages, positiveInteger(page ?? internalPage, 1));
|
|
36
|
+
const range = count ? [(current - 1) * resolvedSize + 1, Math.min(current * resolvedSize, count)] : [0, 0];
|
|
37
|
+
const options = [...new Set([...pageSizeOptions.filter(n => Number.isInteger(n) && n > 0), resolvedSize])].sort((a, b) => a - b);
|
|
38
|
+
const update = (next, nextSize = resolvedSize) => {
|
|
39
|
+
if (disabled)
|
|
40
|
+
return;
|
|
41
|
+
const bounded = Math.min(Math.max(1, Math.ceil(count / nextSize)), positiveInteger(next, 1));
|
|
42
|
+
if (bounded === current && nextSize === resolvedSize)
|
|
43
|
+
return;
|
|
44
|
+
if (page === undefined)
|
|
45
|
+
setInternalPage(bounded);
|
|
46
|
+
if (pageSize === undefined)
|
|
47
|
+
setInternalSize(nextSize);
|
|
48
|
+
onChange?.(bounded, nextSize);
|
|
49
|
+
};
|
|
50
|
+
const submitJump = () => {
|
|
51
|
+
if (jump.trim() && Number.isFinite(Number(jump)))
|
|
52
|
+
update(Number(jump));
|
|
53
|
+
setJump("");
|
|
54
|
+
};
|
|
55
|
+
const button = (target, type, label, unavailable = false) => (_jsx(Button, { size: size === "small" ? "small" : "middle", variant: type === "page" && target === current ? "solid" : "outline", color: type === "page" && target === current ? "primary" : "default", disabled: disabled || unavailable, "aria-label": type === "page" ? text.pageLabel(target) : type === "prev" ? previousLabel : nextLabel, "aria-current": type === "page" && target === current ? "page" : undefined, onClick: () => update(target), children: itemRender ? itemRender(target, type, label) : label }, `${type}-${target}`));
|
|
56
|
+
if (hideOnSinglePage && pages === 1)
|
|
57
|
+
return null;
|
|
58
|
+
return (_jsxs("nav", { "aria-label": ariaLabel ?? text.ariaLabel, className: cn("flex flex-wrap items-center gap-2", align === "center" && "justify-center", align === "end" && "justify-end", className), children: [showTotal && _jsx("span", { className: "text-sm text-muted-foreground", children: showTotal(count, range) }), button(current - 1, "prev", previous, current <= 1), simple ? _jsxs("span", { className: "px-2 text-sm tabular-nums", "aria-live": "polite", children: [current, " / ", pages] }) : pageItems(current, pages, showLessItems).map(item => typeof item === "number"
|
|
59
|
+
? button(item, "page", item)
|
|
60
|
+
: _jsx(Button, { size: "small", variant: "text", disabled: disabled, "aria-label": item === "before" ? text.jumpBackwardLabel : text.jumpForwardLabel, onClick: () => update(current + (item === "before" ? -5 : 5)), children: "\u2026" }, item)), button(current + 1, "next", next, current >= pages), !simple && _jsxs("span", { className: "sr-only", "aria-live": "polite", children: [current, " / ", pages] }), showSizeChanger && _jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx("span", { className: "sr-only", children: text.pageSizeLabel }), _jsx("select", { className: "min-h-9 rounded-md border bg-background px-2 text-foreground", disabled: disabled, value: resolvedSize, onChange: event => {
|
|
61
|
+
const nextSize = Number(event.target.value);
|
|
62
|
+
const nextPage = Math.min(current, Math.max(1, Math.ceil(count / nextSize)));
|
|
63
|
+
update(nextPage, nextSize);
|
|
64
|
+
onShowSizeChange?.(nextPage, nextSize);
|
|
65
|
+
}, children: options.map(option => _jsx("option", { value: option, children: text.pageSizeText(option) }, option)) })] }), showQuickJumper && _jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx("span", { children: text.jumpText }), _jsx("input", { "aria-label": text.jumpLabel, className: "min-h-9 w-16 rounded-md border bg-background px-2 text-foreground", type: "number", min: 1, max: pages, disabled: disabled, value: jump, onChange: event => setJump(event.target.value), onBlur: submitJump, onKeyDown: event => { if (event.key === "Enter") {
|
|
66
|
+
event.preventDefault();
|
|
67
|
+
submitJump();
|
|
68
|
+
} } })] })] }));
|
|
69
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type HTMLAttributes, type ReactElement } from "react";
|
|
2
|
+
export interface PopconfirmProps extends Omit<HTMLAttributes<HTMLSpanElement>, "children" | "title"> {
|
|
3
|
+
title: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
children: ReactElement;
|
|
6
|
+
okText: string;
|
|
7
|
+
cancelText: string;
|
|
8
|
+
danger?: boolean;
|
|
9
|
+
onConfirm?: () => void | Promise<void>;
|
|
10
|
+
onCancel?: () => void;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare const Popconfirm: import("react").ForwardRefExoticComponent<PopconfirmProps & import("react").RefAttributes<HTMLSpanElement>>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useState, } from "react";
|
|
3
|
+
import { AlertDialog, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "../components/primitives/alert-dialog.js";
|
|
4
|
+
import { Button } from "./button.js";
|
|
5
|
+
export const Popconfirm = forwardRef(function Popconfirm({ title, description, children, okText, cancelText, danger = false, onConfirm, onCancel, disabled = false, onClick, className, ...props }, ref) {
|
|
6
|
+
const [open, setOpen] = useState(false);
|
|
7
|
+
const [busy, setBusy] = useState(false);
|
|
8
|
+
const handleOpenChange = (nextOpen) => {
|
|
9
|
+
if (busy && !nextOpen)
|
|
10
|
+
return;
|
|
11
|
+
setOpen(nextOpen);
|
|
12
|
+
};
|
|
13
|
+
const handleTriggerClick = (event) => {
|
|
14
|
+
onClick?.(event);
|
|
15
|
+
if (disabled || busy || event.defaultPrevented)
|
|
16
|
+
return;
|
|
17
|
+
setOpen(true);
|
|
18
|
+
};
|
|
19
|
+
const handleCancel = () => {
|
|
20
|
+
if (busy)
|
|
21
|
+
return;
|
|
22
|
+
setOpen(false);
|
|
23
|
+
onCancel?.();
|
|
24
|
+
};
|
|
25
|
+
const handleConfirm = async () => {
|
|
26
|
+
if (busy)
|
|
27
|
+
return;
|
|
28
|
+
setBusy(true);
|
|
29
|
+
try {
|
|
30
|
+
await onConfirm?.();
|
|
31
|
+
setOpen(false);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// Keep the confirmation context visible. Product feedback belongs to
|
|
35
|
+
// the caller's onConfirm implementation rather than Core.
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
setBusy(false);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
return (_jsxs(AlertDialog, { open: open, onOpenChange: handleOpenChange, children: [_jsx("span", { ...props, ref: ref, "data-slot": "popconfirm-trigger", "data-disabled": disabled || undefined, "aria-disabled": disabled || props["aria-disabled"] || undefined, className: className, onClick: handleTriggerClick, children: children }), _jsxs(AlertDialogContent, { "data-slot": "popconfirm-content", "aria-busy": busy || undefined, children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: title }), description ? (_jsx(AlertDialogDescription, { children: description })) : null] }), _jsxs(AlertDialogFooter, { children: [_jsx(Button, { disabled: busy, onClick: handleCancel, children: cancelText }), _jsx(Button, { loading: busy, variant: "solid", color: danger ? "error" : "primary", onClick: handleConfirm, children: okText })] })] })] }));
|
|
42
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { HTMLAttributes } from "react";
|
|
2
|
+
export interface ProgressProps extends HTMLAttributes<HTMLDivElement> {
|
|
3
|
+
value?: number;
|
|
4
|
+
max?: number;
|
|
5
|
+
}
|
|
6
|
+
export declare function Progress({ value, max, className, ...props }: ProgressProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/utils.js";
|
|
3
|
+
export function Progress({ value = 0, max = 100, className, ...props }) {
|
|
4
|
+
const normalizedMax = Number.isFinite(max) && max > 0 ? max : 100;
|
|
5
|
+
const normalizedValue = Number.isFinite(value) ? Math.min(normalizedMax, Math.max(0, value)) : 0;
|
|
6
|
+
const percent = (normalizedValue / normalizedMax) * 100;
|
|
7
|
+
return (_jsx("div", { role: "progressbar", "aria-valuenow": normalizedValue, "aria-valuemin": 0, "aria-valuemax": normalizedMax, ...props, "data-slot": "progress", className: cn("h-2 w-full overflow-hidden rounded-full bg-muted", className), children: _jsx("div", { "data-slot": "progress-indicator", className: "h-full bg-primary transition-all", style: { width: `${percent}%` } }) }));
|
|
8
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { ComponentProps } from "react";
|
|
2
|
+
import type { ChoiceButton, IconButtonLink, NavigationProvider } from "./button.js";
|
|
3
|
+
import type { Icon } from "./icon.js";
|
|
4
|
+
import type { SearchField } from "./input.js";
|
|
5
|
+
import type { FieldGroup, FieldLabel, FieldLegend, FieldSet, FormField } from "./field.js";
|
|
6
|
+
import type { Checkbox, CheckboxField, CheckboxGroup, Radio, Switch } from "./selection-controls.js";
|
|
7
|
+
import type { FormActions, FormGrid, FormLayout, OverlayForm } from "./form.js";
|
|
8
|
+
import type { Tab, TabList, TabPanel } from "./tabs.js";
|
|
9
|
+
import type { Skeleton } from "./feedback.js";
|
|
10
|
+
import type { Container } from "./layout-primitives.js";
|
|
11
|
+
import type { Divider, Grid } from "./layout.js";
|
|
12
|
+
import type { Breadcrumb } from "./breadcrumb.js";
|
|
13
|
+
import type { Calendar } from "./calendar.js";
|
|
14
|
+
import type { Carousel } from "./carousel.js";
|
|
15
|
+
import type { Slider } from "./slider.js";
|
|
16
|
+
import type { Rate } from "./rate.js";
|
|
17
|
+
import type { Segmented } from "./segmented.js";
|
|
18
|
+
import type { Collapse } from "./collapse.js";
|
|
19
|
+
import type { Popover, PopoverAnchor, PopoverTrigger } from "../components/primitives/popover.js";
|
|
20
|
+
import type { Tooltip, TooltipProvider, TooltipTrigger } from "../components/primitives/tooltip.js";
|
|
21
|
+
import type { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "../components/primitives/dropdown-menu.js";
|
|
22
|
+
import type { Tree } from "./tree.js";
|
|
23
|
+
import type { Layout, LayoutContent, LayoutFooter, LayoutHeader, LayoutSider } from "./page-layout.js";
|
|
24
|
+
import type { InputOTP } from "./input-otp.js";
|
|
25
|
+
/**
|
|
26
|
+
* Exact, runtime-derived props for public components whose implementation is a
|
|
27
|
+
* thin wrapper or compound primitive and therefore does not need a separate
|
|
28
|
+
* handwritten props contract. These aliases are type-only and cannot add a
|
|
29
|
+
* runtime dependency edge or drift away from the component implementation.
|
|
30
|
+
*/
|
|
31
|
+
export type IconButtonLinkProps = ComponentProps<typeof IconButtonLink>;
|
|
32
|
+
export type ChoiceButtonProps = ComponentProps<typeof ChoiceButton>;
|
|
33
|
+
export type NavigationProviderProps = ComponentProps<typeof NavigationProvider>;
|
|
34
|
+
export type IconProps = ComponentProps<typeof Icon>;
|
|
35
|
+
export type SearchFieldProps = ComponentProps<typeof SearchField>;
|
|
36
|
+
export type FormFieldProps = ComponentProps<typeof FormField>;
|
|
37
|
+
export type FieldGroupProps = ComponentProps<typeof FieldGroup>;
|
|
38
|
+
export type FieldSetProps = ComponentProps<typeof FieldSet>;
|
|
39
|
+
export type FieldLegendProps = ComponentProps<typeof FieldLegend>;
|
|
40
|
+
export type FieldLabelProps = ComponentProps<typeof FieldLabel>;
|
|
41
|
+
export type CheckboxProps = ComponentProps<typeof Checkbox>;
|
|
42
|
+
export type RadioProps = ComponentProps<typeof Radio>;
|
|
43
|
+
export type SwitchProps = ComponentProps<typeof Switch>;
|
|
44
|
+
export type CheckboxFieldProps = ComponentProps<typeof CheckboxField>;
|
|
45
|
+
export type CheckboxGroupProps = ComponentProps<typeof CheckboxGroup>;
|
|
46
|
+
export type FormLayoutProps = ComponentProps<typeof FormLayout>;
|
|
47
|
+
export type FormGridProps = ComponentProps<typeof FormGrid>;
|
|
48
|
+
export type FormActionsProps = ComponentProps<typeof FormActions>;
|
|
49
|
+
export type OverlayFormProps = ComponentProps<typeof OverlayForm>;
|
|
50
|
+
export type TabListProps = ComponentProps<typeof TabList>;
|
|
51
|
+
export type TabProps = ComponentProps<typeof Tab>;
|
|
52
|
+
export type TabPanelProps = ComponentProps<typeof TabPanel>;
|
|
53
|
+
export type SkeletonProps = ComponentProps<typeof Skeleton>;
|
|
54
|
+
export type ContainerProps = ComponentProps<typeof Container>;
|
|
55
|
+
export type DividerProps = ComponentProps<typeof Divider>;
|
|
56
|
+
export type GridProps = ComponentProps<typeof Grid>;
|
|
57
|
+
export type BreadcrumbProps = ComponentProps<typeof Breadcrumb>;
|
|
58
|
+
export type CalendarProps = ComponentProps<typeof Calendar>;
|
|
59
|
+
export type CarouselProps = ComponentProps<typeof Carousel>;
|
|
60
|
+
export type SliderProps = ComponentProps<typeof Slider>;
|
|
61
|
+
export type RateProps = ComponentProps<typeof Rate>;
|
|
62
|
+
export type SegmentedProps = ComponentProps<typeof Segmented>;
|
|
63
|
+
export type CollapseProps = ComponentProps<typeof Collapse>;
|
|
64
|
+
export type PopoverProps = ComponentProps<typeof Popover>;
|
|
65
|
+
export type PopoverTriggerProps = ComponentProps<typeof PopoverTrigger>;
|
|
66
|
+
export type PopoverAnchorProps = ComponentProps<typeof PopoverAnchor>;
|
|
67
|
+
export type TooltipProps = ComponentProps<typeof Tooltip>;
|
|
68
|
+
export type TooltipTriggerProps = ComponentProps<typeof TooltipTrigger>;
|
|
69
|
+
export type TooltipProviderProps = ComponentProps<typeof TooltipProvider>;
|
|
70
|
+
export type DropdownMenuProps = ComponentProps<typeof DropdownMenu>;
|
|
71
|
+
export type DropdownMenuTriggerProps = ComponentProps<typeof DropdownMenuTrigger>;
|
|
72
|
+
export type DropdownMenuContentProps = ComponentProps<typeof DropdownMenuContent>;
|
|
73
|
+
export type DropdownMenuItemProps = ComponentProps<typeof DropdownMenuItem>;
|
|
74
|
+
export type DropdownMenuSeparatorProps = ComponentProps<typeof DropdownMenuSeparator>;
|
|
75
|
+
export type DropdownMenuLabelProps = ComponentProps<typeof DropdownMenuLabel>;
|
|
76
|
+
export type TreeProps = ComponentProps<typeof Tree>;
|
|
77
|
+
export type LayoutProps = ComponentProps<typeof Layout>;
|
|
78
|
+
export type LayoutHeaderProps = ComponentProps<typeof LayoutHeader>;
|
|
79
|
+
export type LayoutSiderProps = ComponentProps<typeof LayoutSider>;
|
|
80
|
+
export type LayoutContentProps = ComponentProps<typeof LayoutContent>;
|
|
81
|
+
export type LayoutFooterProps = ComponentProps<typeof LayoutFooter>;
|
|
82
|
+
export type InputOTPProps = ComponentProps<typeof InputOTP>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type CanvasHTMLAttributes } from "react";
|
|
2
|
+
export type QRCodeErrorLevel = "L" | "M" | "Q" | "H";
|
|
3
|
+
export interface QRCodeProps extends Omit<CanvasHTMLAttributes<HTMLCanvasElement>, "children" | "color" | "height" | "width"> {
|
|
4
|
+
value: string;
|
|
5
|
+
/** Rendered canvas width and height in CSS pixels. */
|
|
6
|
+
size?: number;
|
|
7
|
+
/** QR foreground color accepted by the renderer. */
|
|
8
|
+
color?: string;
|
|
9
|
+
/** QR background color accepted by the renderer. */
|
|
10
|
+
background?: string;
|
|
11
|
+
/** QR error-correction level. */
|
|
12
|
+
errorLevel?: QRCodeErrorLevel;
|
|
13
|
+
}
|
|
14
|
+
export declare const QRCode: import("react").ForwardRefExoticComponent<QRCodeProps & import("react").RefAttributes<HTMLCanvasElement>>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useEffect, useImperativeHandle, useRef, } from "react";
|
|
3
|
+
import QRCodeLib from "qrcode";
|
|
4
|
+
export const QRCode = forwardRef(function QRCode({ value, size = 160, color = "#000000", background = "#ffffff", errorLevel = "M", role = "img", ...props }, ref) {
|
|
5
|
+
const canvasRef = useRef(null);
|
|
6
|
+
useImperativeHandle(ref, () => canvasRef.current);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (!canvasRef.current)
|
|
9
|
+
return;
|
|
10
|
+
void QRCodeLib.toCanvas(canvasRef.current, value, {
|
|
11
|
+
width: size,
|
|
12
|
+
color: { dark: color, light: background },
|
|
13
|
+
errorCorrectionLevel: errorLevel,
|
|
14
|
+
});
|
|
15
|
+
}, [background, color, errorLevel, size, value]);
|
|
16
|
+
return (_jsx("canvas", { ...props, ref: canvasRef, "data-slot": "qrcode", width: size, height: size, role: role }));
|
|
17
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type HTMLAttributes } from "react";
|
|
2
|
+
interface RateRuntimeProps extends Omit<HTMLAttributes<HTMLDivElement>, "children" | "onChange" | "role" | "aria-disabled"> {
|
|
3
|
+
value?: number;
|
|
4
|
+
defaultValue?: number;
|
|
5
|
+
count?: number;
|
|
6
|
+
allowClear?: boolean;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
onChange?: (value: number) => void;
|
|
9
|
+
}
|
|
10
|
+
export declare const Rate: import("react").ForwardRefExoticComponent<RateRuntimeProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useRef, useState, } from "react";
|
|
3
|
+
import { cn } from "../lib/utils.js";
|
|
4
|
+
export const Rate = forwardRef(function Rate({ value, defaultValue = 0, count = 5, allowClear = true, disabled = false, onChange, className, ...props }, ref) {
|
|
5
|
+
const [inner, setInner] = useState(defaultValue);
|
|
6
|
+
const itemRefs = useRef([]);
|
|
7
|
+
const itemCount = Math.max(0, Math.floor(count));
|
|
8
|
+
const current = value ?? inner;
|
|
9
|
+
const focusValue = current >= 1 && current <= itemCount ? Math.round(current) : 1;
|
|
10
|
+
const commit = (next, allowToggle = true) => {
|
|
11
|
+
const result = allowToggle && allowClear && next === current ? 0 : next;
|
|
12
|
+
if (value === undefined)
|
|
13
|
+
setInner(result);
|
|
14
|
+
onChange?.(result);
|
|
15
|
+
};
|
|
16
|
+
const move = (from, delta) => {
|
|
17
|
+
if (itemCount === 0)
|
|
18
|
+
return;
|
|
19
|
+
const next = ((from - 1 + delta + itemCount) % itemCount) + 1;
|
|
20
|
+
commit(next, false);
|
|
21
|
+
itemRefs.current[next - 1]?.focus();
|
|
22
|
+
};
|
|
23
|
+
const handleKeyDown = (event, itemValue) => {
|
|
24
|
+
if (event.key === "ArrowRight" || event.key === "ArrowDown") {
|
|
25
|
+
event.preventDefault();
|
|
26
|
+
move(itemValue, 1);
|
|
27
|
+
}
|
|
28
|
+
else if (event.key === "ArrowLeft" || event.key === "ArrowUp") {
|
|
29
|
+
event.preventDefault();
|
|
30
|
+
move(itemValue, -1);
|
|
31
|
+
}
|
|
32
|
+
else if (event.key === "Home" && itemCount > 0) {
|
|
33
|
+
event.preventDefault();
|
|
34
|
+
commit(1, false);
|
|
35
|
+
itemRefs.current[0]?.focus();
|
|
36
|
+
}
|
|
37
|
+
else if (event.key === "End" && itemCount > 0) {
|
|
38
|
+
event.preventDefault();
|
|
39
|
+
commit(itemCount, false);
|
|
40
|
+
itemRefs.current[itemCount - 1]?.focus();
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
return (_jsx("div", { ...props, ref: ref, role: "radiogroup", "aria-disabled": disabled || undefined, "data-slot": "rate", className: cn("inline-flex gap-1", className), children: Array.from({ length: itemCount }, (_, index) => index + 1).map((itemValue) => (_jsx("button", { ref: (node) => {
|
|
44
|
+
itemRefs.current[itemValue - 1] = node;
|
|
45
|
+
}, type: "button", role: "radio", "aria-checked": itemValue === current, "aria-label": String(itemValue), tabIndex: !disabled && itemValue === focusValue ? 0 : -1, disabled: disabled, "data-slot": "rate-item", "data-value": itemValue, onClick: () => commit(itemValue), onKeyDown: (event) => handleKeyDown(event, itemValue), className: cn("inline-flex size-9 items-center justify-center rounded-md text-2xl outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", itemValue <= current
|
|
46
|
+
? "text-warning"
|
|
47
|
+
: "text-muted-foreground/40"), children: _jsx("span", { "aria-hidden": "true", children: "\u2605" }) }, itemValue))) }));
|
|
48
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from "react";
|
|
2
|
+
import type { HeadingLevel } from "./typography.js";
|
|
3
|
+
export type ResultStatus = "success" | "error" | "info" | "warning";
|
|
4
|
+
export interface ResultProps extends Omit<HTMLAttributes<HTMLElement>, "title"> {
|
|
5
|
+
status?: ResultStatus;
|
|
6
|
+
title: ReactNode;
|
|
7
|
+
description?: ReactNode;
|
|
8
|
+
extra?: ReactNode;
|
|
9
|
+
children?: ReactNode;
|
|
10
|
+
headingLevel?: HeadingLevel;
|
|
11
|
+
}
|
|
12
|
+
export declare function Result({ status, title, description, extra, children, headingLevel, className, ...props }: ResultProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { CircleCheck, CircleX, Info, TriangleAlert } from "lucide-react";
|
|
3
|
+
import { cn } from "../lib/utils.js";
|
|
4
|
+
const statusIcons = {
|
|
5
|
+
success: _jsx(CircleCheck, { "aria-hidden": "true" }),
|
|
6
|
+
error: _jsx(CircleX, { "aria-hidden": "true" }),
|
|
7
|
+
info: _jsx(Info, { "aria-hidden": "true" }),
|
|
8
|
+
warning: _jsx(TriangleAlert, { "aria-hidden": "true" }),
|
|
9
|
+
};
|
|
10
|
+
const statusClassName = {
|
|
11
|
+
success: "bg-success-subtle text-success",
|
|
12
|
+
error: "bg-danger-subtle text-destructive",
|
|
13
|
+
info: "bg-info-subtle text-info",
|
|
14
|
+
warning: "bg-warning-subtle text-warning",
|
|
15
|
+
};
|
|
16
|
+
export function Result({ status = "info", title, description, extra, children, headingLevel = 2, className, ...props }) {
|
|
17
|
+
const Title = `h${headingLevel}`;
|
|
18
|
+
return (_jsxs("section", { ...props, "data-slot": "result", "data-status": status, className: cn("flex flex-col items-center gap-3 p-8 text-center", className), children: [_jsx("div", { "data-slot": "result-icon", "aria-hidden": "true", className: cn("flex size-12 items-center justify-center rounded-full [&_svg]:size-7", statusClassName[status]), children: statusIcons[status] }), _jsx(Title, { "data-slot": "result-title", className: cn("font-semibold tracking-tight text-foreground", headingLevel === 1 ? "text-2xl" : "text-xl"), children: title }), description != null ? (_jsx("div", { "data-slot": "result-description", className: "max-w-xl text-sm text-muted-foreground", children: description })) : null, extra != null ? _jsx("div", { "data-slot": "result-extra", children: extra }) : null, children != null ? (_jsx("div", { "data-slot": "result-content", className: "w-full", children: children })) : null] }));
|
|
19
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type HTMLAttributes, type ReactElement, type ReactNode, type Ref } from "react";
|
|
2
|
+
import type { ControlSize } from "./control-types.js";
|
|
3
|
+
type SegmentedValue = string | number;
|
|
4
|
+
type SegmentedOrientation = "horizontal" | "vertical";
|
|
5
|
+
type SegmentedShape = "default" | "round";
|
|
6
|
+
type SegmentedOption<T extends SegmentedValue> = {
|
|
7
|
+
value: T;
|
|
8
|
+
label?: ReactNode;
|
|
9
|
+
icon?: ReactNode;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
className?: string;
|
|
12
|
+
};
|
|
13
|
+
type SegmentedPropsInternal<T extends SegmentedValue> = Omit<HTMLAttributes<HTMLDivElement>, "defaultValue" | "onChange"> & {
|
|
14
|
+
options: readonly (T | SegmentedOption<T>)[];
|
|
15
|
+
value?: T;
|
|
16
|
+
defaultValue?: T;
|
|
17
|
+
onChange?: (value: T) => void;
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
block?: boolean;
|
|
20
|
+
orientation?: SegmentedOrientation;
|
|
21
|
+
size?: ControlSize;
|
|
22
|
+
shape?: SegmentedShape;
|
|
23
|
+
name?: string;
|
|
24
|
+
};
|
|
25
|
+
type SegmentedComponent = <T extends SegmentedValue = string>(props: SegmentedPropsInternal<T> & {
|
|
26
|
+
ref?: Ref<HTMLDivElement>;
|
|
27
|
+
}) => ReactElement | null;
|
|
28
|
+
export declare const Segmented: SegmentedComponent;
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useId, useMemo, useState, } from "react";
|
|
3
|
+
import { cn } from "../lib/utils.js";
|
|
4
|
+
function normalizeOption(option) {
|
|
5
|
+
if (typeof option === "string" || typeof option === "number") {
|
|
6
|
+
return { value: option, label: String(option) };
|
|
7
|
+
}
|
|
8
|
+
return option;
|
|
9
|
+
}
|
|
10
|
+
const sizeClasses = {
|
|
11
|
+
small: "h-7 px-2 text-sm",
|
|
12
|
+
middle: "h-8 px-3 text-sm",
|
|
13
|
+
large: "h-10 px-4 text-base",
|
|
14
|
+
};
|
|
15
|
+
function SegmentedInner({ options, value, defaultValue, onChange, disabled = false, block = false, orientation = "horizontal", size = "middle", shape = "default", name, className, ...props }, ref) {
|
|
16
|
+
const normalized = useMemo(() => options.map(normalizeOption), [options]);
|
|
17
|
+
const firstEnabledValue = normalized.find((option) => !option.disabled)?.value;
|
|
18
|
+
const [internalValue, setInternalValue] = useState(() => defaultValue ?? firstEnabledValue);
|
|
19
|
+
const currentValue = value ?? internalValue ?? firstEnabledValue;
|
|
20
|
+
const generatedId = useId().replace(/:/g, "");
|
|
21
|
+
const groupName = name ?? `gouno-segmented-${generatedId}`;
|
|
22
|
+
const select = (nextValue) => {
|
|
23
|
+
if (Object.is(nextValue, currentValue))
|
|
24
|
+
return;
|
|
25
|
+
if (value === undefined)
|
|
26
|
+
setInternalValue(nextValue);
|
|
27
|
+
onChange?.(nextValue);
|
|
28
|
+
};
|
|
29
|
+
return (_jsx("div", { ...props, ref: ref, role: "radiogroup", "aria-orientation": orientation, "aria-disabled": disabled || undefined, "data-slot": "segmented", "data-size": size, "data-shape": shape, "data-orientation": orientation, className: cn("inline-flex max-w-full items-stretch gap-0.5 bg-muted p-0.5 text-muted-foreground", orientation === "vertical" && "flex-col", block && "w-full", shape === "round" ? "rounded-full" : "rounded-lg", className), children: normalized.map((option) => {
|
|
30
|
+
const optionDisabled = disabled || option.disabled;
|
|
31
|
+
const checked = Object.is(currentValue, option.value);
|
|
32
|
+
const accessibleName = typeof option.label === "string" || typeof option.label === "number"
|
|
33
|
+
? String(option.label)
|
|
34
|
+
: String(option.value);
|
|
35
|
+
return (_jsxs("label", { "data-slot": "segmented-item", "data-disabled": optionDisabled ? "true" : undefined, className: cn("relative min-w-0 select-none", block && "flex-1", orientation === "vertical" && "w-full", optionDisabled ? "cursor-not-allowed opacity-50" : "cursor-pointer", option.className), children: [_jsx("input", { className: "peer sr-only", type: "radio", name: groupName, value: String(option.value), checked: checked, "aria-checked": checked, disabled: optionDisabled, "aria-label": accessibleName, onChange: () => select(option.value) }), _jsxs("span", { "data-slot": "segmented-label", className: cn("flex min-w-0 items-center justify-center gap-1.5 whitespace-nowrap font-medium transition-[background-color,color,box-shadow]", "peer-checked:bg-background peer-checked:text-foreground peer-checked:shadow-control", "peer-focus-visible:outline-none peer-focus-visible:ring-2 peer-focus-visible:ring-ring peer-focus-visible:ring-offset-1 peer-focus-visible:ring-offset-muted", !optionDisabled && "hover:text-foreground", shape === "round" ? "rounded-full" : "rounded-md", sizeClasses[size]), children: [option.icon ? _jsx("span", { "data-slot": "segmented-icon", "aria-hidden": "true", className: "flex shrink-0 items-center [&_svg]:size-4", children: option.icon }) : null, option.label !== undefined ? _jsx("span", { className: "min-w-0 truncate", children: option.label }) : null] })] }, `${typeof option.value}:${String(option.value)}`));
|
|
36
|
+
}) }));
|
|
37
|
+
}
|
|
38
|
+
export const Segmented = forwardRef(SegmentedInner);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { SelectLocale } from "./locale.js";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { type ControlSize } from "./control-types.js";
|
|
4
|
+
export type SelectMode = "single" | "multiple" | "tags";
|
|
5
|
+
export interface SelectOption {
|
|
6
|
+
value: string;
|
|
7
|
+
label: React.ReactNode;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface SelectProps extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, "size" | "value" | "defaultValue" | "onChange" | "multiple"> {
|
|
11
|
+
locale?: Partial<SelectLocale>;
|
|
12
|
+
size?: ControlSize;
|
|
13
|
+
status?: "error" | "warning";
|
|
14
|
+
loading?: boolean;
|
|
15
|
+
placeholder?: string;
|
|
16
|
+
allowClear?: boolean;
|
|
17
|
+
onClear?: () => void;
|
|
18
|
+
mode?: SelectMode;
|
|
19
|
+
showSearch?: boolean;
|
|
20
|
+
optionFilterProp?: "label" | "value";
|
|
21
|
+
maxTagCount?: number;
|
|
22
|
+
value?: string | readonly string[];
|
|
23
|
+
defaultValue?: string | readonly string[];
|
|
24
|
+
onChange?: (value: string | string[], option: SelectOption | SelectOption[]) => void;
|
|
25
|
+
onSearch?: (value: string) => void;
|
|
26
|
+
}
|
|
27
|
+
export declare const Select: React.ForwardRefExoticComponent<SelectProps & React.RefAttributes<HTMLSelectElement>>;
|