@raydenui/ui 0.9.7 → 0.10.1
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 +124 -49
- package/dist/Table-DCNSxua1.d.cts +31 -0
- package/dist/Table-DCNSxua1.d.ts +31 -0
- package/dist/blocks/ApplicationShellBlock.cjs +157 -0
- package/dist/blocks/ApplicationShellBlock.js +154 -0
- package/dist/blocks/CheckoutReviewBlock.cjs +16 -0
- package/dist/blocks/CheckoutReviewBlock.js +13 -0
- package/dist/blocks/CommandPaletteBlock.cjs +334 -0
- package/dist/blocks/CommandPaletteBlock.js +331 -0
- package/dist/blocks/CreateAccountBlock.cjs +147 -0
- package/dist/blocks/CreateAccountBlock.js +143 -0
- package/dist/blocks/EmptyStateBlock.cjs +19 -0
- package/dist/blocks/EmptyStateBlock.js +16 -0
- package/dist/blocks/FeatureOverviewBlock.cjs +54 -0
- package/dist/blocks/FeatureOverviewBlock.js +51 -0
- package/dist/blocks/HeaderBlock.cjs +230 -0
- package/dist/blocks/HeaderBlock.js +227 -0
- package/dist/blocks/KpiOverviewBlock.cjs +63 -0
- package/dist/blocks/KpiOverviewBlock.js +60 -0
- package/dist/blocks/LoginBlock.cjs +66 -0
- package/dist/blocks/LoginBlock.js +63 -0
- package/dist/blocks/NotificationsBlock.cjs +17 -0
- package/dist/blocks/NotificationsBlock.js +14 -0
- package/dist/blocks/PageHeaderBlock.cjs +121 -0
- package/dist/blocks/PageHeaderBlock.js +118 -0
- package/dist/blocks/PricingPlansBlock.cjs +146 -0
- package/dist/blocks/PricingPlansBlock.js +143 -0
- package/dist/blocks/ProductCollectionBlock.cjs +37 -0
- package/dist/blocks/ProductCollectionBlock.js +34 -0
- package/dist/blocks/ProductDetailBlock.cjs +30 -0
- package/dist/blocks/ProductDetailBlock.js +27 -0
- package/dist/blocks/ProductHeroBlock.cjs +83 -0
- package/dist/blocks/ProductHeroBlock.js +80 -0
- package/dist/blocks/ProfileSettingsBlock.cjs +130 -0
- package/dist/blocks/ProfileSettingsBlock.js +126 -0
- package/dist/blocks/QuickSendBlock.cjs +11 -0
- package/dist/blocks/QuickSendBlock.js +8 -0
- package/dist/blocks/RecentTransactionsBlock.cjs +13 -0
- package/dist/blocks/RecentTransactionsBlock.js +10 -0
- package/dist/blocks/SearchableTableBlock.cjs +91 -0
- package/dist/blocks/SearchableTableBlock.js +88 -0
- package/dist/blocks/ShoppingCartBlock.cjs +16 -0
- package/dist/blocks/ShoppingCartBlock.js +13 -0
- package/dist/blocks/SiteFooterBlock.cjs +69 -0
- package/dist/blocks/SiteFooterBlock.js +66 -0
- package/dist/blocks/TableBlock.cjs +20 -0
- package/dist/blocks/TableBlock.js +17 -0
- package/dist/blocks/TaskListBlock.cjs +198 -0
- package/dist/blocks/TaskListBlock.js +195 -0
- package/dist/blocks/WorkspaceSwitcherBlock.cjs +291 -0
- package/dist/blocks/WorkspaceSwitcherBlock.js +288 -0
- package/dist/blocks/commerce.cjs +44 -0
- package/dist/blocks/commerce.js +35 -0
- package/dist/blocks/index.cjs +53 -0
- package/dist/blocks/index.js +24 -0
- package/dist/blocks.d.cts +1820 -0
- package/dist/blocks.d.ts +1820 -0
- package/dist/chart.cjs +13 -0
- package/dist/chart.d.cts +59 -0
- package/dist/chart.d.ts +59 -0
- package/dist/chart.js +5 -0
- package/dist/components/Accordion/Accordion.cjs +94 -0
- package/dist/components/Accordion/Accordion.js +91 -0
- package/dist/components/Accordion/index.cjs +8 -0
- package/dist/components/Accordion/index.js +1 -0
- package/dist/components/ActivityFeed/ActivityContent.cjs +40 -0
- package/dist/components/ActivityFeed/ActivityContent.js +37 -0
- package/dist/components/ActivityFeed/ActivityItem.cjs +19 -0
- package/dist/components/ActivityFeed/ActivityItem.js +16 -0
- package/dist/components/ActivityFeed/index.cjs +7 -0
- package/dist/components/ActivityFeed/index.js +2 -0
- package/dist/components/Alert/Alert.cjs +42 -0
- package/dist/components/Alert/Alert.js +39 -0
- package/dist/components/Alert/index.cjs +5 -0
- package/dist/components/Alert/index.js +1 -0
- package/dist/components/Avatar/Avatar.cjs +101 -0
- package/dist/components/Avatar/Avatar.js +98 -0
- package/dist/components/Avatar/index.cjs +6 -0
- package/dist/components/Avatar/index.js +1 -0
- package/dist/components/Badge/Badge.cjs +53 -0
- package/dist/components/Badge/Badge.js +50 -0
- package/dist/components/Badge/index.cjs +5 -0
- package/dist/components/Badge/index.js +1 -0
- package/dist/components/Banner/Banner.cjs +83 -0
- package/dist/components/Banner/Banner.js +80 -0
- package/dist/components/Banner/index.cjs +5 -0
- package/dist/components/Banner/index.js +1 -0
- package/dist/components/Breadcrumb/Breadcrumb.cjs +31 -0
- package/dist/components/Breadcrumb/Breadcrumb.js +28 -0
- package/dist/components/Breadcrumb/index.cjs +5 -0
- package/dist/components/Breadcrumb/index.js +1 -0
- package/dist/components/Button/Button.cjs +88 -0
- package/dist/components/Button/Button.js +85 -0
- package/dist/components/Button/index.cjs +5 -0
- package/dist/components/Button/index.js +1 -0
- package/dist/components/ButtonGroup/ButtonGroup.cjs +8 -0
- package/dist/components/ButtonGroup/ButtonGroup.js +5 -0
- package/dist/components/ButtonGroup/ButtonGroupItem.cjs +15 -0
- package/dist/components/ButtonGroup/ButtonGroupItem.js +12 -0
- package/dist/components/ButtonGroup/index.cjs +7 -0
- package/dist/components/ButtonGroup/index.js +2 -0
- package/dist/components/Card/Card.cjs +94 -0
- package/dist/components/Card/Card.js +91 -0
- package/dist/components/Card/index.cjs +9 -0
- package/dist/components/Card/index.js +1 -0
- package/dist/components/Chart/Chart.cjs +160 -0
- package/dist/components/Chart/Chart.js +153 -0
- package/dist/components/Chart/index.cjs +10 -0
- package/dist/components/Chart/index.js +2 -0
- package/dist/components/Chart/theme.cjs +67 -0
- package/dist/components/Chart/theme.js +62 -0
- package/dist/components/Chip/Chip.cjs +18 -0
- package/dist/components/Chip/Chip.js +15 -0
- package/dist/components/Chip/index.cjs +5 -0
- package/dist/components/Chip/index.js +1 -0
- package/dist/components/Counter/Counter.cjs +74 -0
- package/dist/components/Counter/Counter.js +71 -0
- package/dist/components/Counter/index.cjs +6 -0
- package/dist/components/Counter/index.js +1 -0
- package/dist/components/DatePicker/DatePicker.cjs +343 -0
- package/dist/components/DatePicker/DatePicker.js +340 -0
- package/dist/components/DatePicker/index.cjs +5 -0
- package/dist/components/DatePicker/index.js +1 -0
- package/dist/components/Divider/Divider.cjs +31 -0
- package/dist/components/Divider/Divider.js +28 -0
- package/dist/components/Divider/index.cjs +5 -0
- package/dist/components/Divider/index.js +1 -0
- package/dist/components/DropdownMenu/DropdownMenu.cjs +108 -0
- package/dist/components/DropdownMenu/DropdownMenu.js +105 -0
- package/dist/components/DropdownMenu/index.cjs +11 -0
- package/dist/components/DropdownMenu/index.js +1 -0
- package/dist/components/EmptyStateIllustration/EmptyStateIllustration.cjs +42 -0
- package/dist/components/EmptyStateIllustration/EmptyStateIllustration.js +39 -0
- package/dist/components/EmptyStateIllustration/illustrations.cjs +3322 -0
- package/dist/components/EmptyStateIllustration/illustrations.js +3319 -0
- package/dist/components/EmptyStateIllustration/index.cjs +7 -0
- package/dist/components/EmptyStateIllustration/index.js +2 -0
- package/dist/components/FileUpload/FileUpload.cjs +116 -0
- package/dist/components/FileUpload/FileUpload.js +113 -0
- package/dist/components/FileUpload/FileUploadDropZone.cjs +58 -0
- package/dist/components/FileUpload/FileUploadDropZone.js +55 -0
- package/dist/components/FileUpload/FileUploadIcons.cjs +55 -0
- package/dist/components/FileUpload/FileUploadIcons.js +50 -0
- package/dist/components/FileUpload/FileUploadItem.cjs +34 -0
- package/dist/components/FileUpload/FileUploadItem.js +31 -0
- package/dist/components/FileUpload/index.cjs +9 -0
- package/dist/components/FileUpload/index.js +3 -0
- package/dist/components/FormControl/Checkbox.cjs +27 -0
- package/dist/components/FormControl/Checkbox.js +24 -0
- package/dist/components/FormControl/Radio.cjs +15 -0
- package/dist/components/FormControl/Radio.js +12 -0
- package/dist/components/FormControl/Toggle.cjs +15 -0
- package/dist/components/FormControl/Toggle.js +12 -0
- package/dist/components/FormControl/index.cjs +9 -0
- package/dist/components/FormControl/index.js +3 -0
- package/dist/components/Icon/Icon.cjs +70 -0
- package/dist/components/Icon/Icon.js +34 -0
- package/dist/components/Icon/catalog.cjs +1672 -0
- package/dist/components/Icon/catalog.js +1669 -0
- package/dist/components/Icon/icons.cjs +5621 -0
- package/dist/components/Icon/icons.js +5610 -0
- package/dist/components/Icon/index.cjs +8 -0
- package/dist/components/Icon/index.js +2 -0
- package/dist/components/Input/Input.cjs +64 -0
- package/dist/components/Input/Input.js +61 -0
- package/dist/components/Input/index.cjs +5 -0
- package/dist/components/Input/index.js +1 -0
- package/dist/components/MetricsCard/MetricsCard.cjs +107 -0
- package/dist/components/MetricsCard/MetricsCard.js +104 -0
- package/dist/components/MetricsCard/index.cjs +5 -0
- package/dist/components/MetricsCard/index.js +1 -0
- package/dist/components/Modal/Modal.cjs +126 -0
- package/dist/components/Modal/Modal.js +123 -0
- package/dist/components/Modal/index.cjs +5 -0
- package/dist/components/Modal/index.js +1 -0
- package/dist/components/Pagination/Pagination.cjs +42 -0
- package/dist/components/Pagination/Pagination.js +39 -0
- package/dist/components/Pagination/index.cjs +5 -0
- package/dist/components/Pagination/index.js +1 -0
- package/dist/components/ProgressBar/ProgressBar.cjs +34 -0
- package/dist/components/ProgressBar/ProgressBar.js +31 -0
- package/dist/components/ProgressBar/index.cjs +5 -0
- package/dist/components/ProgressBar/index.js +1 -0
- package/dist/components/ProgressCircle/ProgressCircle.cjs +34 -0
- package/dist/components/ProgressCircle/ProgressCircle.js +31 -0
- package/dist/components/ProgressCircle/index.cjs +5 -0
- package/dist/components/ProgressCircle/index.js +1 -0
- package/dist/components/Select/Select.cjs +84 -0
- package/dist/components/Select/Select.js +80 -0
- package/dist/components/Select/SelectOption.cjs +26 -0
- package/dist/components/Select/SelectOption.js +23 -0
- package/dist/components/Select/index.cjs +7 -0
- package/dist/components/Select/index.js +2 -0
- package/dist/components/SidebarMenu/SidebarMenu.cjs +85 -0
- package/dist/components/SidebarMenu/SidebarMenu.js +81 -0
- package/dist/components/SidebarMenu/SidebarMenuItem.cjs +126 -0
- package/dist/components/SidebarMenu/SidebarMenuItem.js +122 -0
- package/dist/components/SidebarMenu/SidebarMenuSection.cjs +14 -0
- package/dist/components/SidebarMenu/SidebarMenuSection.js +11 -0
- package/dist/components/SidebarMenu/index.cjs +12 -0
- package/dist/components/SidebarMenu/index.js +4 -0
- package/dist/components/Slider/Slider.cjs +161 -0
- package/dist/components/Slider/Slider.js +158 -0
- package/dist/components/Slider/index.cjs +6 -0
- package/dist/components/Slider/index.js +1 -0
- package/dist/components/Spinner/Spinner.cjs +93 -0
- package/dist/components/Spinner/Spinner.js +90 -0
- package/dist/components/Spinner/index.cjs +5 -0
- package/dist/components/Spinner/index.js +1 -0
- package/dist/components/Stepper/Stepper.cjs +82 -0
- package/dist/components/Stepper/Stepper.js +79 -0
- package/dist/components/Stepper/index.cjs +7 -0
- package/dist/components/Stepper/index.js +1 -0
- package/dist/components/Table/Table.cjs +25 -0
- package/dist/components/Table/Table.js +22 -0
- package/dist/components/Table/index.cjs +10 -0
- package/dist/components/Table/index.js +1 -0
- package/dist/components/Table/useTableSelection.cjs +60 -0
- package/dist/components/Table/useTableSelection.js +57 -0
- package/dist/components/Tabs/Tab.cjs +70 -0
- package/dist/components/Tabs/Tab.js +67 -0
- package/dist/components/Tabs/Tabs.cjs +133 -0
- package/dist/components/Tabs/Tabs.js +129 -0
- package/dist/components/Tabs/index.cjs +7 -0
- package/dist/components/Tabs/index.js +2 -0
- package/dist/components/Tooltip/Tooltip.cjs +101 -0
- package/dist/components/Tooltip/Tooltip.js +98 -0
- package/dist/components/Tooltip/index.cjs +5 -0
- package/dist/components/Tooltip/index.js +1 -0
- package/dist/context/ThemeContext.cjs +97 -0
- package/dist/context/ThemeContext.js +92 -0
- package/dist/fonts/README.md +13 -0
- package/dist/fonts/hanken-grotesk-cyrillic-ext-italic.woff2 +0 -0
- package/dist/fonts/hanken-grotesk-cyrillic-ext-normal.woff2 +0 -0
- package/dist/fonts/hanken-grotesk-latin-ext-italic.woff2 +0 -0
- package/dist/fonts/hanken-grotesk-latin-ext-normal.woff2 +0 -0
- package/dist/fonts/hanken-grotesk-latin-italic.woff2 +0 -0
- package/dist/fonts/hanken-grotesk-latin-normal.woff2 +0 -0
- package/dist/fonts/hanken-grotesk-vietnamese-italic.woff2 +0 -0
- package/dist/fonts/hanken-grotesk-vietnamese-normal.woff2 +0 -0
- package/dist/fonts/hankengrotesk-LICENSE.txt +94 -0
- package/dist/fonts/manrope-LICENSE.txt +93 -0
- package/dist/fonts/manrope-cyrillic-ext-normal.woff2 +0 -0
- package/dist/fonts/manrope-cyrillic-normal.woff2 +0 -0
- package/dist/fonts/manrope-greek-normal.woff2 +0 -0
- package/dist/fonts/manrope-latin-ext-normal.woff2 +0 -0
- package/dist/fonts/manrope-latin-normal.woff2 +0 -0
- package/dist/fonts/manrope-vietnamese-normal.woff2 +0 -0
- package/dist/fonts.css +148 -0
- package/dist/hooks/form/index.cjs +11 -0
- package/dist/hooks/form/index.js +3 -0
- package/dist/hooks/form/useRaydenFormControl.cjs +81 -0
- package/dist/hooks/form/useRaydenFormControl.js +76 -0
- package/dist/hooks/form/useRaydenInput.cjs +38 -0
- package/dist/hooks/form/useRaydenInput.js +35 -0
- package/dist/hooks/form/useRaydenSelect.cjs +45 -0
- package/dist/hooks/form/useRaydenSelect.js +42 -0
- package/dist/hooks/index.cjs +17 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useBodyScrollLock.cjs +24 -0
- package/dist/hooks/useBodyScrollLock.js +21 -0
- package/dist/hooks/useCollisionAwareSide.cjs +72 -0
- package/dist/hooks/useCollisionAwareSide.js +69 -0
- package/dist/hooks/useControllableValue.cjs +16 -0
- package/dist/hooks/useControllableValue.js +13 -0
- package/dist/hooks/useDismissableLayer.cjs +100 -0
- package/dist/hooks/useDismissableLayer.js +97 -0
- package/dist/hooks/usePopupList.cjs +101 -0
- package/dist/hooks/usePopupList.js +98 -0
- package/dist/icons-8yD7I5jO.d.cts +430 -0
- package/dist/icons-8yD7I5jO.d.ts +430 -0
- package/dist/icons.cjs +2111 -418
- package/dist/icons.d.cts +1256 -429
- package/dist/icons.d.ts +1256 -429
- package/dist/icons.js +2089 -1
- package/dist/index.cjs +128 -15046
- package/dist/index.d.cts +697 -662
- package/dist/index.d.ts +697 -662
- package/dist/index.js +43 -14962
- package/dist/motion/Collapse.cjs +28 -0
- package/dist/motion/Collapse.js +25 -0
- package/dist/motion/MotionProvider.cjs +43 -0
- package/dist/motion/MotionProvider.js +38 -0
- package/dist/motion/Pressable.cjs +46 -0
- package/dist/motion/Pressable.js +43 -0
- package/dist/motion/Reveal.cjs +19 -0
- package/dist/motion/Reveal.js +16 -0
- package/dist/motion/SharedLayout.cjs +46 -0
- package/dist/motion/SharedLayout.js +43 -0
- package/dist/motion/index.cjs +19 -0
- package/dist/motion/index.js +6 -0
- package/dist/motion/presence.cjs +58 -0
- package/dist/motion/presence.js +55 -0
- package/dist/motion/presets.cjs +88 -0
- package/dist/motion/presets.js +84 -0
- package/dist/motion.d.cts +51 -0
- package/dist/motion.d.ts +51 -0
- package/dist/preset.cjs +356 -195
- package/dist/preset.d.cts +111 -32
- package/dist/preset.d.ts +111 -32
- package/dist/preset.js +353 -187
- package/dist/presets-Ba5QndHg.d.cts +144 -0
- package/dist/presets-Ba5QndHg.d.ts +144 -0
- package/dist/styles.css +1 -1
- package/dist/utils/cn.cjs +38 -0
- package/dist/utils/cn.js +35 -0
- package/dist/utils/resolveIcon.cjs +18 -0
- package/dist/utils/resolveIcon.js +15 -0
- package/package.json +79 -21
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useBodyScrollLock } from "../../hooks/useBodyScrollLock.js";
|
|
3
|
+
import { forwardRef, useCallback, useEffect, useId, useLayoutEffect, useRef, useState, } from "react";
|
|
4
|
+
import { createPortal } from "react-dom";
|
|
5
|
+
import { cn } from "../../utils/cn.js";
|
|
6
|
+
import { resolveIcon } from "../../utils/resolveIcon.js";
|
|
7
|
+
import { Button } from "../Button/index.js";
|
|
8
|
+
import { useMotionPresence } from "../../motion/presence.js";
|
|
9
|
+
const useBrowserLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
|
|
10
|
+
/* ─── Icons ────────────────────────────────────────────────────────────── */
|
|
11
|
+
function CloseIcon({ className }) {
|
|
12
|
+
return (_jsx("svg", { className: className, viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("path", { d: "M9 3L3 9M3 3l6 6" }) }));
|
|
13
|
+
}
|
|
14
|
+
function BoxIcon({ className }) {
|
|
15
|
+
return (_jsxs("svg", { className: className, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.2, strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M2.67 4.67L8 7.33l5.33-2.66M8 14V7.33" }), _jsx("path", { d: "M13.33 10.67V5.33a1.33 1.33 0 00-.66-1.15L8.67 2a1.33 1.33 0 00-1.34 0L3.33 4.18A1.33 1.33 0 002.67 5.33v5.34a1.33 1.33 0 00.66 1.15L7.33 14a1.33 1.33 0 001.34 0l4-2.18a1.33 1.33 0 00.66-1.15z" })] }));
|
|
16
|
+
}
|
|
17
|
+
/* ─── Size Config ──────────────────────────────────────────────────────── */
|
|
18
|
+
const sizeClasses = {
|
|
19
|
+
sm: "w-full max-w-[400px]",
|
|
20
|
+
md: "w-full max-w-[500px]",
|
|
21
|
+
lg: "w-full max-w-[640px]",
|
|
22
|
+
};
|
|
23
|
+
/* ─── Modal ────────────────────────────────────────────────────────────── */
|
|
24
|
+
export const Modal = forwardRef(({ open, motion = false, onClose, size = "sm", title, description, icon, showClose = true,
|
|
25
|
+
// Defaulted only when there is something for it to do; an inert "Save"
|
|
26
|
+
// used to render whenever the footer appeared.
|
|
27
|
+
primaryLabel: primaryLabelProp, onPrimaryClick, secondaryLabel, onSecondaryClick, footerOrientation = "horizontal", closeOnOverlay = true, closeOnEscape = true, blur = true, children, className, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, ...rest }, ref) => {
|
|
28
|
+
const overlayRef = useRef(null);
|
|
29
|
+
const anchorRef = useRef(null);
|
|
30
|
+
const panelRef = useRef(null);
|
|
31
|
+
const titleId = useId();
|
|
32
|
+
const descriptionId = useId();
|
|
33
|
+
// The portal target only exists in a browser; defer it so an initially
|
|
34
|
+
// open modal can be server-rendered without touching document.
|
|
35
|
+
const [mounted, setMounted] = useState(false);
|
|
36
|
+
useEffect(() => setMounted(true), []);
|
|
37
|
+
const { present, preset } = useMotionPresence(open, panelRef, motion, "overlay.pop", mounted);
|
|
38
|
+
// Native modal dialogs contain focus and make the background inert.
|
|
39
|
+
useBrowserLayoutEffect(() => {
|
|
40
|
+
const dialog = overlayRef.current;
|
|
41
|
+
if (!present || !dialog)
|
|
42
|
+
return;
|
|
43
|
+
const trigger = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
44
|
+
dialog.showModal();
|
|
45
|
+
return () => {
|
|
46
|
+
dialog.close();
|
|
47
|
+
if (trigger?.isConnected)
|
|
48
|
+
trigger.focus({ preventScroll: true });
|
|
49
|
+
};
|
|
50
|
+
}, [present, mounted]);
|
|
51
|
+
// ── #30: the portal escapes the provider, so its theme class is copied in.
|
|
52
|
+
// Copying once on open left an open dialog stuck in the old theme when the
|
|
53
|
+
// app switched; observe the source element and re-sync on every change.
|
|
54
|
+
useBrowserLayoutEffect(() => {
|
|
55
|
+
const dialog = overlayRef.current;
|
|
56
|
+
if (!present || !dialog)
|
|
57
|
+
return;
|
|
58
|
+
const source = anchorRef.current?.closest(".rayden-light, .dark");
|
|
59
|
+
if (!source)
|
|
60
|
+
return;
|
|
61
|
+
const sync = () => {
|
|
62
|
+
const isLight = source.classList.contains("rayden-light");
|
|
63
|
+
dialog.classList.toggle("rayden-light", isLight);
|
|
64
|
+
dialog.classList.toggle("dark", !isLight);
|
|
65
|
+
};
|
|
66
|
+
sync();
|
|
67
|
+
const observer = new MutationObserver(sync);
|
|
68
|
+
observer.observe(source, { attributes: true, attributeFilter: ["class"] });
|
|
69
|
+
// The theme may also move to a different ancestor, e.g. the documentElement.
|
|
70
|
+
if (source !== document.documentElement)
|
|
71
|
+
observer.observe(document.documentElement, {
|
|
72
|
+
attributes: true,
|
|
73
|
+
attributeFilter: ["class"],
|
|
74
|
+
});
|
|
75
|
+
return () => observer.disconnect();
|
|
76
|
+
}, [present, mounted]);
|
|
77
|
+
useBodyScrollLock(present && mounted);
|
|
78
|
+
const handleOverlayClick = useCallback((e) => {
|
|
79
|
+
if (open && closeOnOverlay && e.target === overlayRef.current)
|
|
80
|
+
onClose();
|
|
81
|
+
}, [open, closeOnOverlay, onClose]);
|
|
82
|
+
if (!present || !mounted)
|
|
83
|
+
return null;
|
|
84
|
+
const primaryLabel = primaryLabelProp ?? (onPrimaryClick ? "Save" : undefined);
|
|
85
|
+
const showPrimary = !!primaryLabel;
|
|
86
|
+
const showSecondary = !!secondaryLabel;
|
|
87
|
+
const isVertical = footerOrientation === "vertical";
|
|
88
|
+
const modal = (_jsx("dialog", { ref: overlayRef, "data-rayden-motion": motion ? preset : undefined, "data-state": open ? "open" : "closed", "aria-label": ariaLabel ?? (!title && !ariaLabelledBy ? "Dialog" : undefined), "aria-labelledby": ariaLabelledBy ?? (title ? titleId : undefined), "aria-describedby": ariaDescribedBy ?? (description ? descriptionId : undefined), className: cn("fixed inset-0 m-0 h-dvh w-screen max-h-none max-w-none border-0 bg-transparent p-4 open:flex items-center justify-center backdrop:bg-black/60", blur && "backdrop:backdrop-blur-md"), onCancel: (event) => {
|
|
89
|
+
event.preventDefault();
|
|
90
|
+
if (open && closeOnEscape)
|
|
91
|
+
onClose();
|
|
92
|
+
}, onKeyDown: (event) => {
|
|
93
|
+
if (event.key !== "Tab")
|
|
94
|
+
return;
|
|
95
|
+
const dialog = event.currentTarget;
|
|
96
|
+
const focusable = Array.from(dialog.querySelectorAll('a[href], button, input, select, textarea, [tabindex], [contenteditable="true"]')).filter((element) => element.tabIndex >= 0 &&
|
|
97
|
+
!element.matches(":disabled") &&
|
|
98
|
+
!element.closest("[inert]") &&
|
|
99
|
+
element.getClientRects().length > 0 &&
|
|
100
|
+
getComputedStyle(element).visibility !== "hidden");
|
|
101
|
+
const first = focusable[0];
|
|
102
|
+
const last = focusable[focusable.length - 1];
|
|
103
|
+
if (!first || !last) {
|
|
104
|
+
event.preventDefault();
|
|
105
|
+
dialog.firstElementChild?.focus();
|
|
106
|
+
}
|
|
107
|
+
else if (!focusable.includes(document.activeElement) ||
|
|
108
|
+
(event.shiftKey ? document.activeElement === first : document.activeElement === last)) {
|
|
109
|
+
event.preventDefault();
|
|
110
|
+
(event.shiftKey ? last : first).focus();
|
|
111
|
+
}
|
|
112
|
+
}, onClick: handleOverlayClick, children: _jsxs("div", { ref: (element) => {
|
|
113
|
+
panelRef.current = element;
|
|
114
|
+
if (typeof ref === "function")
|
|
115
|
+
ref(element);
|
|
116
|
+
else if (ref)
|
|
117
|
+
ref.current = element;
|
|
118
|
+
}, tabIndex: -1, className: cn("relative flex max-h-[calc(100dvh-2rem)] flex-col bg-surface rounded-xl shadow-soft-xl overflow-hidden", sizeClasses[size], className), ...rest, children: [(title || showClose) && (_jsxs("div", { className: "flex items-start gap-2 pt-5 pb-2 px-4", children: [_jsxs("div", { className: "flex flex-1 gap-4 items-start min-w-0", children: [icon !== undefined ? (_jsx("span", { className: "shrink-0 flex items-center justify-center p-2.5 bg-primary-50 rounded-[20px]", children: _jsx("span", { className: "size-4", children: resolveIcon(icon, "sm") }) })) : title ? (_jsx("span", { className: "shrink-0 flex items-center justify-center p-2.5 bg-primary-50 rounded-[20px]", children: _jsx(BoxIcon, { className: "size-4 text-primary-400" }) })) : null, _jsxs("div", { className: "flex flex-col flex-1 min-w-0", children: [title && (_jsx("h2", { id: titleId, className: "text-xl font-semibold text-on-surface-body leading-[1.2] tracking-[-0.4px]", children: title })), description && !!(title || showClose) && (_jsx("p", { id: descriptionId, className: "text-sm text-on-surface-muted leading-[1.45]", children: description }))] })] }), showClose && (_jsx("button", { type: "button", onClick: onClose, "aria-label": "Close modal", className: "shrink-0 flex items-center justify-center p-2 bg-grey-75 text-grey-700 rounded-xl hover:bg-grey-200 transition-colors cursor-pointer focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-action-primary-text", children: _jsx(CloseIcon, { className: "size-3" }) }))] })), description && !title && !showClose && (_jsx("p", { id: descriptionId, className: "px-4 pt-5 text-sm text-on-surface-muted leading-[1.45]", children: description })), children && _jsx("div", { className: "min-h-0 flex-1 px-4 py-4 overflow-y-auto", children: children }), (showPrimary || showSecondary) && (_jsxs("div", { className: cn("py-5", isVertical
|
|
119
|
+
? "flex flex-col gap-5 items-center px-4"
|
|
120
|
+
: "flex items-center justify-end gap-3 px-4"), children: [showSecondary && !isVertical && (_jsx(Button, { variant: "secondary", onClick: onSecondaryClick ?? onClose, type: "button", children: secondaryLabel })), showPrimary && (_jsx(Button, { type: "button", onClick: onPrimaryClick, className: isVertical ? "w-full" : undefined, children: primaryLabel })), showSecondary && isVertical && (_jsx(Button, { variant: "secondary", onClick: onSecondaryClick ?? onClose, type: "button", className: "w-full", children: secondaryLabel }))] }))] }) }));
|
|
121
|
+
return (_jsxs(_Fragment, { children: [_jsx("span", { ref: anchorRef, hidden: true }), createPortal(modal, document.body)] }));
|
|
122
|
+
});
|
|
123
|
+
Modal.displayName = "Modal";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Modal } from "./Modal.js";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Pagination = Pagination;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const cn_1 = require("../../utils/cn.cjs");
|
|
6
|
+
const Icon_1 = require("../Icon/index.cjs");
|
|
7
|
+
function range(start, end) {
|
|
8
|
+
const arr = [];
|
|
9
|
+
for (let i = start; i <= end; i++)
|
|
10
|
+
arr.push(i);
|
|
11
|
+
return arr;
|
|
12
|
+
}
|
|
13
|
+
function getPageNumbers(currentPage, totalPages, siblingCount) {
|
|
14
|
+
const totalNumbers = siblingCount * 2 + 5; // siblings + first + last + current + 2 dots
|
|
15
|
+
if (totalPages <= totalNumbers) {
|
|
16
|
+
return range(1, totalPages);
|
|
17
|
+
}
|
|
18
|
+
const leftSiblingIndex = Math.max(currentPage - siblingCount, 1);
|
|
19
|
+
const rightSiblingIndex = Math.min(currentPage + siblingCount, totalPages);
|
|
20
|
+
const showLeftDots = leftSiblingIndex > 2;
|
|
21
|
+
const showRightDots = rightSiblingIndex < totalPages - 1;
|
|
22
|
+
if (!showLeftDots && showRightDots) {
|
|
23
|
+
const leftItemCount = 3 + 2 * siblingCount;
|
|
24
|
+
const leftRange = range(1, leftItemCount);
|
|
25
|
+
return [...leftRange, "...", totalPages];
|
|
26
|
+
}
|
|
27
|
+
if (showLeftDots && !showRightDots) {
|
|
28
|
+
const rightItemCount = 3 + 2 * siblingCount;
|
|
29
|
+
const rightRange = range(totalPages - rightItemCount + 1, totalPages);
|
|
30
|
+
return [1, "...", ...rightRange];
|
|
31
|
+
}
|
|
32
|
+
const middleRange = range(leftSiblingIndex, rightSiblingIndex);
|
|
33
|
+
return [1, "...", ...middleRange, "...", totalPages];
|
|
34
|
+
}
|
|
35
|
+
const ChevronLeft = () => (0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: "chevron-left", size: "md" });
|
|
36
|
+
const ChevronRight = () => (0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: "chevron-right", size: "md" });
|
|
37
|
+
function Pagination({ currentPage, totalPages, onPageChange, showPrevNext = true, siblingCount = 1, className, ...rest }) {
|
|
38
|
+
const pages = getPageNumbers(currentPage, totalPages, siblingCount);
|
|
39
|
+
return ((0, jsx_runtime_1.jsx)("nav", { className: (0, cn_1.cn)("rayden-pagination min-w-0", className), "aria-label": "Pagination", ...rest, children: (0, jsx_runtime_1.jsxs)("div", { className: "flex w-full items-center justify-center gap-2", children: [showPrevNext && ((0, jsx_runtime_1.jsxs)("button", { type: "button", onClick: () => onPageChange(currentPage - 1), disabled: currentPage <= 1, className: "inline-flex min-h-11 shrink-0 items-center gap-2 rounded-lg focus-visible:outline-2 focus-visible:outline-action-primary-text border border-control-border bg-surface px-3 py-2 text-sm font-semibold text-on-surface-body cursor-pointer hover:bg-grey-50 disabled:opacity-50 disabled:cursor-not-allowed", "aria-label": "Previous page", children: [(0, jsx_runtime_1.jsx)(ChevronLeft, {}), (0, jsx_runtime_1.jsx)("span", { className: "rayden-pagination-word", children: "Previous" })] })), (0, jsx_runtime_1.jsx)("div", { className: (0, cn_1.cn)("items-center justify-center gap-1 flex-wrap", showPrevNext ? "rayden-pagination-pages" : "flex"), children: pages.map((page, i) => page === "..." ? ((0, jsx_runtime_1.jsx)("span", { className: "flex items-center justify-center size-6 text-sm text-on-surface-muted", children: "..." }, `dots-${i}`)) : ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => onPageChange(page), className: (0, cn_1.cn)("flex items-center justify-center min-w-9 h-11 px-2 rounded-md text-sm cursor-pointer", page === currentPage
|
|
40
|
+
? "border border-action-primary-text text-action-primary-text font-medium"
|
|
41
|
+
: "text-on-surface-secondary hover:bg-grey-100"), "aria-label": `Page ${page}`, "aria-current": page === currentPage ? "page" : undefined, children: page }, page))) }), showPrevNext && ((0, jsx_runtime_1.jsxs)("span", { className: "rayden-pagination-compact text-sm text-on-surface-body", "aria-live": "polite", children: ["Page ", currentPage, " of ", totalPages] })), showPrevNext && ((0, jsx_runtime_1.jsxs)("button", { type: "button", onClick: () => onPageChange(currentPage + 1), disabled: currentPage >= totalPages, className: "inline-flex min-h-11 shrink-0 items-center gap-2 rounded-lg focus-visible:outline-2 focus-visible:outline-action-primary-text border border-control-border bg-surface px-3 py-2 text-sm font-semibold text-on-surface-body cursor-pointer hover:bg-grey-50 disabled:opacity-50 disabled:cursor-not-allowed", "aria-label": "Next page", children: [(0, jsx_runtime_1.jsx)("span", { className: "rayden-pagination-word", children: "Next" }), (0, jsx_runtime_1.jsx)(ChevronRight, {})] }))] }) }));
|
|
42
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../../utils/cn.js";
|
|
3
|
+
import { Icon } from "../Icon/index.js";
|
|
4
|
+
function range(start, end) {
|
|
5
|
+
const arr = [];
|
|
6
|
+
for (let i = start; i <= end; i++)
|
|
7
|
+
arr.push(i);
|
|
8
|
+
return arr;
|
|
9
|
+
}
|
|
10
|
+
function getPageNumbers(currentPage, totalPages, siblingCount) {
|
|
11
|
+
const totalNumbers = siblingCount * 2 + 5; // siblings + first + last + current + 2 dots
|
|
12
|
+
if (totalPages <= totalNumbers) {
|
|
13
|
+
return range(1, totalPages);
|
|
14
|
+
}
|
|
15
|
+
const leftSiblingIndex = Math.max(currentPage - siblingCount, 1);
|
|
16
|
+
const rightSiblingIndex = Math.min(currentPage + siblingCount, totalPages);
|
|
17
|
+
const showLeftDots = leftSiblingIndex > 2;
|
|
18
|
+
const showRightDots = rightSiblingIndex < totalPages - 1;
|
|
19
|
+
if (!showLeftDots && showRightDots) {
|
|
20
|
+
const leftItemCount = 3 + 2 * siblingCount;
|
|
21
|
+
const leftRange = range(1, leftItemCount);
|
|
22
|
+
return [...leftRange, "...", totalPages];
|
|
23
|
+
}
|
|
24
|
+
if (showLeftDots && !showRightDots) {
|
|
25
|
+
const rightItemCount = 3 + 2 * siblingCount;
|
|
26
|
+
const rightRange = range(totalPages - rightItemCount + 1, totalPages);
|
|
27
|
+
return [1, "...", ...rightRange];
|
|
28
|
+
}
|
|
29
|
+
const middleRange = range(leftSiblingIndex, rightSiblingIndex);
|
|
30
|
+
return [1, "...", ...middleRange, "...", totalPages];
|
|
31
|
+
}
|
|
32
|
+
const ChevronLeft = () => _jsx(Icon, { name: "chevron-left", size: "md" });
|
|
33
|
+
const ChevronRight = () => _jsx(Icon, { name: "chevron-right", size: "md" });
|
|
34
|
+
export function Pagination({ currentPage, totalPages, onPageChange, showPrevNext = true, siblingCount = 1, className, ...rest }) {
|
|
35
|
+
const pages = getPageNumbers(currentPage, totalPages, siblingCount);
|
|
36
|
+
return (_jsx("nav", { className: cn("rayden-pagination min-w-0", className), "aria-label": "Pagination", ...rest, children: _jsxs("div", { className: "flex w-full items-center justify-center gap-2", children: [showPrevNext && (_jsxs("button", { type: "button", onClick: () => onPageChange(currentPage - 1), disabled: currentPage <= 1, className: "inline-flex min-h-11 shrink-0 items-center gap-2 rounded-lg focus-visible:outline-2 focus-visible:outline-action-primary-text border border-control-border bg-surface px-3 py-2 text-sm font-semibold text-on-surface-body cursor-pointer hover:bg-grey-50 disabled:opacity-50 disabled:cursor-not-allowed", "aria-label": "Previous page", children: [_jsx(ChevronLeft, {}), _jsx("span", { className: "rayden-pagination-word", children: "Previous" })] })), _jsx("div", { className: cn("items-center justify-center gap-1 flex-wrap", showPrevNext ? "rayden-pagination-pages" : "flex"), children: pages.map((page, i) => page === "..." ? (_jsx("span", { className: "flex items-center justify-center size-6 text-sm text-on-surface-muted", children: "..." }, `dots-${i}`)) : (_jsx("button", { type: "button", onClick: () => onPageChange(page), className: cn("flex items-center justify-center min-w-9 h-11 px-2 rounded-md text-sm cursor-pointer", page === currentPage
|
|
37
|
+
? "border border-action-primary-text text-action-primary-text font-medium"
|
|
38
|
+
: "text-on-surface-secondary hover:bg-grey-100"), "aria-label": `Page ${page}`, "aria-current": page === currentPage ? "page" : undefined, children: page }, page))) }), showPrevNext && (_jsxs("span", { className: "rayden-pagination-compact text-sm text-on-surface-body", "aria-live": "polite", children: ["Page ", currentPage, " of ", totalPages] })), showPrevNext && (_jsxs("button", { type: "button", onClick: () => onPageChange(currentPage + 1), disabled: currentPage >= totalPages, className: "inline-flex min-h-11 shrink-0 items-center gap-2 rounded-lg focus-visible:outline-2 focus-visible:outline-action-primary-text border border-control-border bg-surface px-3 py-2 text-sm font-semibold text-on-surface-body cursor-pointer hover:bg-grey-50 disabled:opacity-50 disabled:cursor-not-allowed", "aria-label": "Next page", children: [_jsx("span", { className: "rayden-pagination-word", children: "Next" }), _jsx(ChevronRight, {})] }))] }) }));
|
|
39
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Pagination = void 0;
|
|
4
|
+
var Pagination_1 = require("./Pagination.cjs");
|
|
5
|
+
Object.defineProperty(exports, "Pagination", { enumerable: true, get: function () { return Pagination_1.Pagination; } });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Pagination } from "./Pagination.js";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ProgressBar = ProgressBar;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const cn_1 = require("../../utils/cn.cjs");
|
|
7
|
+
const SEGMENT_COUNT = 10;
|
|
8
|
+
const trackHeight = {
|
|
9
|
+
sm: "h-1",
|
|
10
|
+
md: "h-2",
|
|
11
|
+
lg: "h-3",
|
|
12
|
+
};
|
|
13
|
+
const clampValue = (value) => Math.min(100, Math.max(0, value));
|
|
14
|
+
function ProgressBar({ value = 0, size = "lg", type = "basic", label, metadata, showPercentage = true, percentagePosition = "bottom", className, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, ...rest }) {
|
|
15
|
+
const clamped = clampValue(value);
|
|
16
|
+
const generatedId = (0, react_1.useId)();
|
|
17
|
+
const labelId = label ? `${generatedId}-label` : undefined;
|
|
18
|
+
// Naming and value semantics belong on the same element as the progressbar
|
|
19
|
+
// role. Spreading them onto the wrapper left every bar unnamed, and the
|
|
20
|
+
// visible label had no id to point at.
|
|
21
|
+
const namedBy = ariaLabelledBy ?? labelId;
|
|
22
|
+
const progressAria = {
|
|
23
|
+
role: "progressbar",
|
|
24
|
+
"aria-valuenow": clamped,
|
|
25
|
+
"aria-valuemin": 0,
|
|
26
|
+
"aria-valuemax": 100,
|
|
27
|
+
"aria-labelledby": namedBy,
|
|
28
|
+
"aria-label": namedBy ? undefined : ariaLabel,
|
|
29
|
+
"aria-describedby": ariaDescribedBy,
|
|
30
|
+
};
|
|
31
|
+
const filledSegments = Math.round((clamped / 100) * SEGMENT_COUNT);
|
|
32
|
+
const metadataItems = Array.isArray(metadata) ? metadata : metadata ? [metadata] : [];
|
|
33
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)("flex flex-col gap-2 w-full", className), ...rest, children: [(label || (showPercentage && percentagePosition === "top")) && ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [label && ((0, jsx_runtime_1.jsx)("span", { id: labelId, className: "flex-1 text-sm font-medium text-grey-900 leading-[1.45]", children: label })), showPercentage && percentagePosition === "top" && ((0, jsx_runtime_1.jsxs)("span", { className: "text-sm font-semibold text-grey-500 leading-[1.45]", children: [Math.round(clamped), "%"] }))] })), type === "basic" ? ((0, jsx_runtime_1.jsx)("div", { className: (0, cn_1.cn)("w-full rounded-full bg-grey-200", trackHeight[size]), ...progressAria, children: (0, jsx_runtime_1.jsx)("div", { className: (0, cn_1.cn)("rounded-full bg-primary-400 transition-all duration-300", trackHeight[size]), style: { width: `${clamped}%` } }) })) : ((0, jsx_runtime_1.jsx)("div", { className: (0, cn_1.cn)("flex gap-0.5 w-full", trackHeight[size]), ...progressAria, children: Array.from({ length: SEGMENT_COUNT }, (_, i) => ((0, jsx_runtime_1.jsx)("div", { className: (0, cn_1.cn)("flex-1 rounded-full transition-colors duration-300", i < filledSegments ? "bg-primary-400" : "bg-grey-100") }, i))) })), (metadataItems.length > 0 || (showPercentage && percentagePosition === "bottom")) && ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [metadataItems.length > 0 && ((0, jsx_runtime_1.jsx)("div", { className: "flex flex-1 items-center gap-1", children: metadataItems.map((item, i) => ((0, jsx_runtime_1.jsxs)("span", { className: "contents", children: [i > 0 && (0, jsx_runtime_1.jsx)("span", { className: "text-xs text-grey-500 leading-[1.45]", children: "\u2022" }), (0, jsx_runtime_1.jsx)("span", { className: "text-sm font-medium text-grey-500 leading-[1.45]", children: item })] }, i))) })), showPercentage && percentagePosition === "bottom" && ((0, jsx_runtime_1.jsxs)("span", { className: "flex-1 text-sm font-semibold text-grey-500 text-right leading-[1.45]", children: [Math.round(clamped), "%"] }))] }))] }));
|
|
34
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useId } from "react";
|
|
3
|
+
import { cn } from "../../utils/cn.js";
|
|
4
|
+
const SEGMENT_COUNT = 10;
|
|
5
|
+
const trackHeight = {
|
|
6
|
+
sm: "h-1",
|
|
7
|
+
md: "h-2",
|
|
8
|
+
lg: "h-3",
|
|
9
|
+
};
|
|
10
|
+
const clampValue = (value) => Math.min(100, Math.max(0, value));
|
|
11
|
+
export function ProgressBar({ value = 0, size = "lg", type = "basic", label, metadata, showPercentage = true, percentagePosition = "bottom", className, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, ...rest }) {
|
|
12
|
+
const clamped = clampValue(value);
|
|
13
|
+
const generatedId = useId();
|
|
14
|
+
const labelId = label ? `${generatedId}-label` : undefined;
|
|
15
|
+
// Naming and value semantics belong on the same element as the progressbar
|
|
16
|
+
// role. Spreading them onto the wrapper left every bar unnamed, and the
|
|
17
|
+
// visible label had no id to point at.
|
|
18
|
+
const namedBy = ariaLabelledBy ?? labelId;
|
|
19
|
+
const progressAria = {
|
|
20
|
+
role: "progressbar",
|
|
21
|
+
"aria-valuenow": clamped,
|
|
22
|
+
"aria-valuemin": 0,
|
|
23
|
+
"aria-valuemax": 100,
|
|
24
|
+
"aria-labelledby": namedBy,
|
|
25
|
+
"aria-label": namedBy ? undefined : ariaLabel,
|
|
26
|
+
"aria-describedby": ariaDescribedBy,
|
|
27
|
+
};
|
|
28
|
+
const filledSegments = Math.round((clamped / 100) * SEGMENT_COUNT);
|
|
29
|
+
const metadataItems = Array.isArray(metadata) ? metadata : metadata ? [metadata] : [];
|
|
30
|
+
return (_jsxs("div", { className: cn("flex flex-col gap-2 w-full", className), ...rest, children: [(label || (showPercentage && percentagePosition === "top")) && (_jsxs("div", { className: "flex items-center gap-2", children: [label && (_jsx("span", { id: labelId, className: "flex-1 text-sm font-medium text-grey-900 leading-[1.45]", children: label })), showPercentage && percentagePosition === "top" && (_jsxs("span", { className: "text-sm font-semibold text-grey-500 leading-[1.45]", children: [Math.round(clamped), "%"] }))] })), type === "basic" ? (_jsx("div", { className: cn("w-full rounded-full bg-grey-200", trackHeight[size]), ...progressAria, children: _jsx("div", { className: cn("rounded-full bg-primary-400 transition-all duration-300", trackHeight[size]), style: { width: `${clamped}%` } }) })) : (_jsx("div", { className: cn("flex gap-0.5 w-full", trackHeight[size]), ...progressAria, children: Array.from({ length: SEGMENT_COUNT }, (_, i) => (_jsx("div", { className: cn("flex-1 rounded-full transition-colors duration-300", i < filledSegments ? "bg-primary-400" : "bg-grey-100") }, i))) })), (metadataItems.length > 0 || (showPercentage && percentagePosition === "bottom")) && (_jsxs("div", { className: "flex items-center justify-between", children: [metadataItems.length > 0 && (_jsx("div", { className: "flex flex-1 items-center gap-1", children: metadataItems.map((item, i) => (_jsxs("span", { className: "contents", children: [i > 0 && _jsx("span", { className: "text-xs text-grey-500 leading-[1.45]", children: "\u2022" }), _jsx("span", { className: "text-sm font-medium text-grey-500 leading-[1.45]", children: item })] }, i))) })), showPercentage && percentagePosition === "bottom" && (_jsxs("span", { className: "flex-1 text-sm font-semibold text-grey-500 text-right leading-[1.45]", children: [Math.round(clamped), "%"] }))] }))] }));
|
|
31
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ProgressBar = void 0;
|
|
4
|
+
var ProgressBar_1 = require("./ProgressBar.cjs");
|
|
5
|
+
Object.defineProperty(exports, "ProgressBar", { enumerable: true, get: function () { return ProgressBar_1.ProgressBar; } });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ProgressBar } from "./ProgressBar.js";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ProgressCircle = ProgressCircle;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const cn_1 = require("../../utils/cn.cjs");
|
|
6
|
+
const sizeConfig = {
|
|
7
|
+
xs: { dimension: 48, strokeWidth: 4, fontSize: 12 },
|
|
8
|
+
sm: { dimension: 56, strokeWidth: 5, fontSize: 12 },
|
|
9
|
+
md: { dimension: 72, strokeWidth: 6, fontSize: 14 },
|
|
10
|
+
lg: { dimension: 90, strokeWidth: 7, fontSize: 16 },
|
|
11
|
+
xl: { dimension: 120, strokeWidth: 8, fontSize: 18 },
|
|
12
|
+
};
|
|
13
|
+
const SEGMENT_COUNT = 10;
|
|
14
|
+
const SEGMENT_GAP_DEGREES = 6;
|
|
15
|
+
const clampValue = (value) => Math.min(100, Math.max(0, value));
|
|
16
|
+
function ProgressCircle({ value = 0, size = "md", variant = "default", showText = true, className, label, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ...rest }) {
|
|
17
|
+
const clamped = clampValue(value);
|
|
18
|
+
const config = sizeConfig[size];
|
|
19
|
+
const radius = (config.dimension - config.strokeWidth) / 2;
|
|
20
|
+
const circumference = 2 * Math.PI * radius;
|
|
21
|
+
if (variant === "segmented") {
|
|
22
|
+
const filledSegments = Math.round((clamped / 100) * SEGMENT_COUNT);
|
|
23
|
+
const totalGapDegrees = SEGMENT_COUNT * SEGMENT_GAP_DEGREES;
|
|
24
|
+
const segmentDegrees = (360 - totalGapDegrees) / SEGMENT_COUNT;
|
|
25
|
+
const segmentLength = (segmentDegrees / 360) * circumference;
|
|
26
|
+
const gapLength = (SEGMENT_GAP_DEGREES / 360) * circumference;
|
|
27
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)("relative inline-flex items-center justify-center", className), style: { width: config.dimension, height: config.dimension }, role: "progressbar", "aria-valuenow": clamped, "aria-valuemin": 0, "aria-valuemax": 100, "aria-labelledby": ariaLabelledBy, "aria-label": ariaLabelledBy ? undefined : (label ?? ariaLabel ?? "Progress"), ...rest, children: [(0, jsx_runtime_1.jsx)("svg", { width: config.dimension, height: config.dimension, className: "-rotate-90", children: Array.from({ length: SEGMENT_COUNT }, (_, i) => {
|
|
28
|
+
const segmentOffset = i * ((segmentLength + gapLength) / circumference) * circumference;
|
|
29
|
+
return ((0, jsx_runtime_1.jsx)("circle", { cx: config.dimension / 2, cy: config.dimension / 2, r: radius, fill: "none", stroke: i < filledSegments ? "#F56630" : "#F0F2F5", strokeWidth: config.strokeWidth, strokeLinecap: "round", strokeDasharray: `${segmentLength} ${circumference - segmentLength}`, strokeDashoffset: -segmentOffset, className: "transition-colors duration-300" }, i));
|
|
30
|
+
}) }), showText && ((0, jsx_runtime_1.jsxs)("span", { className: "absolute font-semibold text-grey-900", style: { fontSize: config.fontSize }, children: [Math.round(clamped), "%"] }))] }));
|
|
31
|
+
}
|
|
32
|
+
const offset = circumference - (clamped / 100) * circumference;
|
|
33
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)("relative inline-flex items-center justify-center", className), style: { width: config.dimension, height: config.dimension }, role: "progressbar", "aria-valuenow": clamped, "aria-valuemin": 0, "aria-valuemax": 100, "aria-labelledby": ariaLabelledBy, "aria-label": ariaLabelledBy ? undefined : (label ?? ariaLabel ?? "Progress"), ...rest, children: [(0, jsx_runtime_1.jsxs)("svg", { width: config.dimension, height: config.dimension, className: "-rotate-90", children: [(0, jsx_runtime_1.jsx)("circle", { cx: config.dimension / 2, cy: config.dimension / 2, r: radius, fill: "none", stroke: "#F0F2F5", strokeWidth: config.strokeWidth }), (0, jsx_runtime_1.jsx)("circle", { cx: config.dimension / 2, cy: config.dimension / 2, r: radius, fill: "none", stroke: "#F56630", strokeWidth: config.strokeWidth, strokeLinecap: "round", strokeDasharray: circumference, strokeDashoffset: offset, className: "transition-all duration-300" })] }), showText && ((0, jsx_runtime_1.jsxs)("span", { className: "absolute font-semibold text-grey-900", style: { fontSize: config.fontSize }, children: [Math.round(clamped), "%"] }))] }));
|
|
34
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../../utils/cn.js";
|
|
3
|
+
const sizeConfig = {
|
|
4
|
+
xs: { dimension: 48, strokeWidth: 4, fontSize: 12 },
|
|
5
|
+
sm: { dimension: 56, strokeWidth: 5, fontSize: 12 },
|
|
6
|
+
md: { dimension: 72, strokeWidth: 6, fontSize: 14 },
|
|
7
|
+
lg: { dimension: 90, strokeWidth: 7, fontSize: 16 },
|
|
8
|
+
xl: { dimension: 120, strokeWidth: 8, fontSize: 18 },
|
|
9
|
+
};
|
|
10
|
+
const SEGMENT_COUNT = 10;
|
|
11
|
+
const SEGMENT_GAP_DEGREES = 6;
|
|
12
|
+
const clampValue = (value) => Math.min(100, Math.max(0, value));
|
|
13
|
+
export function ProgressCircle({ value = 0, size = "md", variant = "default", showText = true, className, label, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ...rest }) {
|
|
14
|
+
const clamped = clampValue(value);
|
|
15
|
+
const config = sizeConfig[size];
|
|
16
|
+
const radius = (config.dimension - config.strokeWidth) / 2;
|
|
17
|
+
const circumference = 2 * Math.PI * radius;
|
|
18
|
+
if (variant === "segmented") {
|
|
19
|
+
const filledSegments = Math.round((clamped / 100) * SEGMENT_COUNT);
|
|
20
|
+
const totalGapDegrees = SEGMENT_COUNT * SEGMENT_GAP_DEGREES;
|
|
21
|
+
const segmentDegrees = (360 - totalGapDegrees) / SEGMENT_COUNT;
|
|
22
|
+
const segmentLength = (segmentDegrees / 360) * circumference;
|
|
23
|
+
const gapLength = (SEGMENT_GAP_DEGREES / 360) * circumference;
|
|
24
|
+
return (_jsxs("div", { className: cn("relative inline-flex items-center justify-center", className), style: { width: config.dimension, height: config.dimension }, role: "progressbar", "aria-valuenow": clamped, "aria-valuemin": 0, "aria-valuemax": 100, "aria-labelledby": ariaLabelledBy, "aria-label": ariaLabelledBy ? undefined : (label ?? ariaLabel ?? "Progress"), ...rest, children: [_jsx("svg", { width: config.dimension, height: config.dimension, className: "-rotate-90", children: Array.from({ length: SEGMENT_COUNT }, (_, i) => {
|
|
25
|
+
const segmentOffset = i * ((segmentLength + gapLength) / circumference) * circumference;
|
|
26
|
+
return (_jsx("circle", { cx: config.dimension / 2, cy: config.dimension / 2, r: radius, fill: "none", stroke: i < filledSegments ? "#F56630" : "#F0F2F5", strokeWidth: config.strokeWidth, strokeLinecap: "round", strokeDasharray: `${segmentLength} ${circumference - segmentLength}`, strokeDashoffset: -segmentOffset, className: "transition-colors duration-300" }, i));
|
|
27
|
+
}) }), showText && (_jsxs("span", { className: "absolute font-semibold text-grey-900", style: { fontSize: config.fontSize }, children: [Math.round(clamped), "%"] }))] }));
|
|
28
|
+
}
|
|
29
|
+
const offset = circumference - (clamped / 100) * circumference;
|
|
30
|
+
return (_jsxs("div", { className: cn("relative inline-flex items-center justify-center", className), style: { width: config.dimension, height: config.dimension }, role: "progressbar", "aria-valuenow": clamped, "aria-valuemin": 0, "aria-valuemax": 100, "aria-labelledby": ariaLabelledBy, "aria-label": ariaLabelledBy ? undefined : (label ?? ariaLabel ?? "Progress"), ...rest, children: [_jsxs("svg", { width: config.dimension, height: config.dimension, className: "-rotate-90", children: [_jsx("circle", { cx: config.dimension / 2, cy: config.dimension / 2, r: radius, fill: "none", stroke: "#F0F2F5", strokeWidth: config.strokeWidth }), _jsx("circle", { cx: config.dimension / 2, cy: config.dimension / 2, r: radius, fill: "none", stroke: "#F56630", strokeWidth: config.strokeWidth, strokeLinecap: "round", strokeDasharray: circumference, strokeDashoffset: offset, className: "transition-all duration-300" })] }), showText && (_jsxs("span", { className: "absolute font-semibold text-grey-900", style: { fontSize: config.fontSize }, children: [Math.round(clamped), "%"] }))] }));
|
|
31
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ProgressCircle = void 0;
|
|
4
|
+
var ProgressCircle_1 = require("./ProgressCircle.cjs");
|
|
5
|
+
Object.defineProperty(exports, "ProgressCircle", { enumerable: true, get: function () { return ProgressCircle_1.ProgressCircle; } });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ProgressCircle } from "./ProgressCircle.js";
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Select = void 0;
|
|
4
|
+
exports.useSelectContext = useSelectContext;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
const useControllableValue_1 = require("../../hooks/useControllableValue.cjs");
|
|
8
|
+
const usePopupList_1 = require("../../hooks/usePopupList.cjs");
|
|
9
|
+
const cn_1 = require("../../utils/cn.cjs");
|
|
10
|
+
const useCollisionAwareSide_1 = require("../../hooks/useCollisionAwareSide.cjs");
|
|
11
|
+
const resolveIcon_1 = require("../../utils/resolveIcon.cjs");
|
|
12
|
+
const Icon_1 = require("../Icon/index.cjs");
|
|
13
|
+
const SelectContext = (0, react_1.createContext)(null);
|
|
14
|
+
function useSelectContext() {
|
|
15
|
+
const ctx = (0, react_1.useContext)(SelectContext);
|
|
16
|
+
if (!ctx)
|
|
17
|
+
throw new Error("SelectOption must be used within <Select>");
|
|
18
|
+
return ctx;
|
|
19
|
+
}
|
|
20
|
+
function findSelectedOption(children, value) {
|
|
21
|
+
let found = null;
|
|
22
|
+
react_1.Children.forEach(children, (child) => {
|
|
23
|
+
if (found)
|
|
24
|
+
return;
|
|
25
|
+
if ((0, react_1.isValidElement)(child) && child.props.value === value) {
|
|
26
|
+
found = {
|
|
27
|
+
label: child.props.children,
|
|
28
|
+
icon: child.props.icon,
|
|
29
|
+
avatar: child.props.avatar,
|
|
30
|
+
statusColor: child.props.statusColor,
|
|
31
|
+
description: child.props.description,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
else if ((0, react_1.isValidElement)(child) && child.props.children) {
|
|
35
|
+
found = findSelectedOption(child.props.children, value);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
return found;
|
|
39
|
+
}
|
|
40
|
+
exports.Select = (0, react_1.forwardRef)(({ value: controlledValue, defaultValue = "", onValueChange, placeholder = "Select an option", label, helperText, error, disabled = false, children, className, wrapperClassName, ...rest }, ref) => {
|
|
41
|
+
const [value, setValue] = (0, useControllableValue_1.useControllableValue)(controlledValue, defaultValue, onValueChange);
|
|
42
|
+
const [open, setOpen] = (0, react_1.useState)(false);
|
|
43
|
+
const hasError = error !== undefined && error !== false;
|
|
44
|
+
const bottomText = hasError ? (typeof error === "string" ? error : undefined) : helperText;
|
|
45
|
+
const listboxId = (0, react_1.useId)();
|
|
46
|
+
const labelId = (0, react_1.useId)();
|
|
47
|
+
const helperId = (0, react_1.useId)();
|
|
48
|
+
const containerRef = (0, react_1.useRef)(null);
|
|
49
|
+
const triggerRef = (0, react_1.useRef)(null);
|
|
50
|
+
const listRef = (0, react_1.useRef)(null);
|
|
51
|
+
// The listbox always opened downward, so near the bottom edge it ran off-screen.
|
|
52
|
+
const listSide = (0, useCollisionAwareSide_1.useCollisionAwareSide)("bottom", open, triggerRef, listRef);
|
|
53
|
+
const { close, onTriggerKeyDown, onListKeyDown } = (0, usePopupList_1.usePopupList)({
|
|
54
|
+
open,
|
|
55
|
+
setOpen,
|
|
56
|
+
containerRef,
|
|
57
|
+
triggerRef,
|
|
58
|
+
listRef,
|
|
59
|
+
role: "option",
|
|
60
|
+
selected: true,
|
|
61
|
+
});
|
|
62
|
+
const onSelect = (0, react_1.useCallback)((val) => {
|
|
63
|
+
close(true);
|
|
64
|
+
setValue(val);
|
|
65
|
+
}, [close, setValue]);
|
|
66
|
+
// Find selected option info for trigger display
|
|
67
|
+
const selectedOption = value ? findSelectedOption(children, value) : null;
|
|
68
|
+
// Resolve leading element for trigger
|
|
69
|
+
const triggerLeading = selectedOption ? (selectedOption.avatar ? (selectedOption.avatar) : selectedOption.statusColor ? ((0, jsx_runtime_1.jsx)("span", { className: "shrink-0 size-2 rounded-full border-[1.5px] border-white", style: { backgroundColor: selectedOption.statusColor } })) : selectedOption.icon ? ((0, jsx_runtime_1.jsx)("span", { className: "shrink-0 size-5", children: (0, resolveIcon_1.resolveIcon)(selectedOption.icon, "md") })) : null) : null;
|
|
70
|
+
return ((0, jsx_runtime_1.jsxs)("div", { ref: (node) => {
|
|
71
|
+
containerRef.current = node;
|
|
72
|
+
if (typeof ref === "function")
|
|
73
|
+
ref(node);
|
|
74
|
+
else if (ref)
|
|
75
|
+
ref.current = node;
|
|
76
|
+
}, ...rest, className: (0, cn_1.cn)("relative flex flex-col gap-2 w-full", wrapperClassName), children: [label && ((0, jsx_runtime_1.jsx)("label", { id: labelId, className: "text-sm font-medium text-on-surface leading-[1.45]", children: label })), (0, jsx_runtime_1.jsxs)("button", { ref: triggerRef, type: "button", role: "combobox", "aria-expanded": open, "aria-haspopup": "listbox", "aria-controls": listboxId, "aria-labelledby": label ? labelId : undefined, "aria-describedby": bottomText ? helperId : undefined, "aria-invalid": hasError || undefined, disabled: disabled, onKeyDown: onTriggerKeyDown, onClick: () => !disabled && setOpen(!open), className: (0, cn_1.cn)("flex h-14 w-full items-center gap-3 rounded-md border px-4 transition-colors", disabled
|
|
77
|
+
? "bg-grey-100 border-control-border-disabled cursor-not-allowed"
|
|
78
|
+
: hasError
|
|
79
|
+
? "bg-surface border-error-400"
|
|
80
|
+
: open
|
|
81
|
+
? "bg-surface border-[#1671D9]"
|
|
82
|
+
: "bg-surface border-control-border hover:border-[#B6D8FF]", className), children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-1 items-center gap-2 min-w-0", children: [triggerLeading, (0, jsx_runtime_1.jsx)("span", { className: (0, cn_1.cn)("truncate text-body-sm", selectedOption ? "text-on-surface" : "text-on-surface-muted"), children: selectedOption ? selectedOption.label : placeholder })] }), (0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: "chevron-down", size: "md", "aria-hidden": "true", className: (0, cn_1.cn)("shrink-0 transition-transform", open && "rotate-180", disabled ? "text-grey-300" : "text-on-surface-muted") })] }), bottomText && ((0, jsx_runtime_1.jsx)("p", { id: helperId, className: (0, cn_1.cn)("text-sm leading-[1.45]", hasError ? "text-feedback-error" : "text-on-surface-muted"), children: bottomText })), open && ((0, jsx_runtime_1.jsx)(SelectContext.Provider, { value: { value, onSelect, open }, children: (0, jsx_runtime_1.jsx)("div", { ref: listRef, id: listboxId, role: "listbox", onKeyDown: onListKeyDown, className: (0, cn_1.cn)("absolute left-0 right-0 z-50 max-h-[320px] overflow-y-auto rounded-lg border border-grey-200 bg-surface py-1 shadow-[0px_3px_2px_-2px_rgba(0,0,0,0.06),0px_5px_3px_-2px_rgba(0,0,0,0.02)]", listSide === "top" ? "bottom-full mb-2" : "top-full mt-2"), children: children }) }))] }));
|
|
83
|
+
});
|
|
84
|
+
exports.Select.displayName = "Select";
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useCallback, useContext, useId, useRef, useState, forwardRef, Children, isValidElement, } from "react";
|
|
3
|
+
import { useControllableValue } from "../../hooks/useControllableValue.js";
|
|
4
|
+
import { usePopupList } from "../../hooks/usePopupList.js";
|
|
5
|
+
import { cn } from "../../utils/cn.js";
|
|
6
|
+
import { useCollisionAwareSide } from "../../hooks/useCollisionAwareSide.js";
|
|
7
|
+
import { resolveIcon } from "../../utils/resolveIcon.js";
|
|
8
|
+
import { Icon } from "../Icon/index.js";
|
|
9
|
+
const SelectContext = createContext(null);
|
|
10
|
+
export function useSelectContext() {
|
|
11
|
+
const ctx = useContext(SelectContext);
|
|
12
|
+
if (!ctx)
|
|
13
|
+
throw new Error("SelectOption must be used within <Select>");
|
|
14
|
+
return ctx;
|
|
15
|
+
}
|
|
16
|
+
function findSelectedOption(children, value) {
|
|
17
|
+
let found = null;
|
|
18
|
+
Children.forEach(children, (child) => {
|
|
19
|
+
if (found)
|
|
20
|
+
return;
|
|
21
|
+
if (isValidElement(child) && child.props.value === value) {
|
|
22
|
+
found = {
|
|
23
|
+
label: child.props.children,
|
|
24
|
+
icon: child.props.icon,
|
|
25
|
+
avatar: child.props.avatar,
|
|
26
|
+
statusColor: child.props.statusColor,
|
|
27
|
+
description: child.props.description,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
else if (isValidElement(child) && child.props.children) {
|
|
31
|
+
found = findSelectedOption(child.props.children, value);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
return found;
|
|
35
|
+
}
|
|
36
|
+
export const Select = forwardRef(({ value: controlledValue, defaultValue = "", onValueChange, placeholder = "Select an option", label, helperText, error, disabled = false, children, className, wrapperClassName, ...rest }, ref) => {
|
|
37
|
+
const [value, setValue] = useControllableValue(controlledValue, defaultValue, onValueChange);
|
|
38
|
+
const [open, setOpen] = useState(false);
|
|
39
|
+
const hasError = error !== undefined && error !== false;
|
|
40
|
+
const bottomText = hasError ? (typeof error === "string" ? error : undefined) : helperText;
|
|
41
|
+
const listboxId = useId();
|
|
42
|
+
const labelId = useId();
|
|
43
|
+
const helperId = useId();
|
|
44
|
+
const containerRef = useRef(null);
|
|
45
|
+
const triggerRef = useRef(null);
|
|
46
|
+
const listRef = useRef(null);
|
|
47
|
+
// The listbox always opened downward, so near the bottom edge it ran off-screen.
|
|
48
|
+
const listSide = useCollisionAwareSide("bottom", open, triggerRef, listRef);
|
|
49
|
+
const { close, onTriggerKeyDown, onListKeyDown } = usePopupList({
|
|
50
|
+
open,
|
|
51
|
+
setOpen,
|
|
52
|
+
containerRef,
|
|
53
|
+
triggerRef,
|
|
54
|
+
listRef,
|
|
55
|
+
role: "option",
|
|
56
|
+
selected: true,
|
|
57
|
+
});
|
|
58
|
+
const onSelect = useCallback((val) => {
|
|
59
|
+
close(true);
|
|
60
|
+
setValue(val);
|
|
61
|
+
}, [close, setValue]);
|
|
62
|
+
// Find selected option info for trigger display
|
|
63
|
+
const selectedOption = value ? findSelectedOption(children, value) : null;
|
|
64
|
+
// Resolve leading element for trigger
|
|
65
|
+
const triggerLeading = selectedOption ? (selectedOption.avatar ? (selectedOption.avatar) : selectedOption.statusColor ? (_jsx("span", { className: "shrink-0 size-2 rounded-full border-[1.5px] border-white", style: { backgroundColor: selectedOption.statusColor } })) : selectedOption.icon ? (_jsx("span", { className: "shrink-0 size-5", children: resolveIcon(selectedOption.icon, "md") })) : null) : null;
|
|
66
|
+
return (_jsxs("div", { ref: (node) => {
|
|
67
|
+
containerRef.current = node;
|
|
68
|
+
if (typeof ref === "function")
|
|
69
|
+
ref(node);
|
|
70
|
+
else if (ref)
|
|
71
|
+
ref.current = node;
|
|
72
|
+
}, ...rest, className: cn("relative flex flex-col gap-2 w-full", wrapperClassName), children: [label && (_jsx("label", { id: labelId, className: "text-sm font-medium text-on-surface leading-[1.45]", children: label })), _jsxs("button", { ref: triggerRef, type: "button", role: "combobox", "aria-expanded": open, "aria-haspopup": "listbox", "aria-controls": listboxId, "aria-labelledby": label ? labelId : undefined, "aria-describedby": bottomText ? helperId : undefined, "aria-invalid": hasError || undefined, disabled: disabled, onKeyDown: onTriggerKeyDown, onClick: () => !disabled && setOpen(!open), className: cn("flex h-14 w-full items-center gap-3 rounded-md border px-4 transition-colors", disabled
|
|
73
|
+
? "bg-grey-100 border-control-border-disabled cursor-not-allowed"
|
|
74
|
+
: hasError
|
|
75
|
+
? "bg-surface border-error-400"
|
|
76
|
+
: open
|
|
77
|
+
? "bg-surface border-[#1671D9]"
|
|
78
|
+
: "bg-surface border-control-border hover:border-[#B6D8FF]", className), children: [_jsxs("div", { className: "flex flex-1 items-center gap-2 min-w-0", children: [triggerLeading, _jsx("span", { className: cn("truncate text-body-sm", selectedOption ? "text-on-surface" : "text-on-surface-muted"), children: selectedOption ? selectedOption.label : placeholder })] }), _jsx(Icon, { name: "chevron-down", size: "md", "aria-hidden": "true", className: cn("shrink-0 transition-transform", open && "rotate-180", disabled ? "text-grey-300" : "text-on-surface-muted") })] }), bottomText && (_jsx("p", { id: helperId, className: cn("text-sm leading-[1.45]", hasError ? "text-feedback-error" : "text-on-surface-muted"), children: bottomText })), open && (_jsx(SelectContext.Provider, { value: { value, onSelect, open }, children: _jsx("div", { ref: listRef, id: listboxId, role: "listbox", onKeyDown: onListKeyDown, className: cn("absolute left-0 right-0 z-50 max-h-[320px] overflow-y-auto rounded-lg border border-grey-200 bg-surface py-1 shadow-[0px_3px_2px_-2px_rgba(0,0,0,0.06),0px_5px_3px_-2px_rgba(0,0,0,0.02)]", listSide === "top" ? "bottom-full mb-2" : "top-full mt-2"), children: children }) }))] }));
|
|
79
|
+
});
|
|
80
|
+
Select.displayName = "Select";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SelectOption = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const cn_1 = require("../../utils/cn.cjs");
|
|
7
|
+
const resolveIcon_1 = require("../../utils/resolveIcon.cjs");
|
|
8
|
+
const Select_1 = require("./Select.cjs");
|
|
9
|
+
exports.SelectOption = (0, react_1.forwardRef)(({ value, icon, avatar, statusColor, description, disabled = false, children, className, onClick, ...rest }, ref) => {
|
|
10
|
+
const ctx = (0, Select_1.useSelectContext)();
|
|
11
|
+
const isSelected = ctx.value === value;
|
|
12
|
+
const handleClick = (e) => {
|
|
13
|
+
if (disabled)
|
|
14
|
+
return;
|
|
15
|
+
onClick?.(e);
|
|
16
|
+
if (!e.defaultPrevented)
|
|
17
|
+
ctx.onSelect(value);
|
|
18
|
+
};
|
|
19
|
+
const resolvedIcon = (0, resolveIcon_1.resolveIcon)(icon, "md");
|
|
20
|
+
// Determine leading element
|
|
21
|
+
const leading = avatar ? ((0, jsx_runtime_1.jsx)("span", { className: "shrink-0 size-5", children: avatar })) : statusColor ? ((0, jsx_runtime_1.jsx)("span", { className: "shrink-0 size-2 rounded-full border-[1.5px] border-white", style: { backgroundColor: statusColor } })) : resolvedIcon ? ((0, jsx_runtime_1.jsx)("span", { className: (0, cn_1.cn)("shrink-0 size-5", disabled && "opacity-50"), children: resolvedIcon })) : null;
|
|
22
|
+
return ((0, jsx_runtime_1.jsxs)("button", { ref: ref, type: "button", role: "option", tabIndex: -1, "aria-selected": isSelected, disabled: disabled, onClick: handleClick, className: (0, cn_1.cn)("flex w-full items-center px-4 py-2 text-left text-body-sm outline-none transition-colors", statusColor ? "gap-2" : "gap-3", disabled
|
|
23
|
+
? "bg-grey-100 text-grey-500 cursor-not-allowed"
|
|
24
|
+
: "text-grey-900 hover:bg-grey-50 focus:bg-grey-100 focus:font-medium", className), ...rest, children: [(0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)("flex flex-1 items-center", statusColor ? "gap-2" : "gap-3"), children: [leading, (0, jsx_runtime_1.jsx)("span", { className: "shrink-0", children: children }), description && (0, jsx_runtime_1.jsx)("span", { className: "text-grey-500 truncate", children: description })] }), isSelected && !disabled && ((0, jsx_runtime_1.jsx)("span", { className: "ml-2 shrink-0", children: (0, jsx_runtime_1.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", className: "text-[#1671D9]", children: [(0, jsx_runtime_1.jsx)("circle", { cx: "8", cy: "8", r: "8", fill: "currentColor" }), (0, jsx_runtime_1.jsx)("path", { d: "M11.5 5.5L6.5 10.5L4.5 8.5", stroke: "white", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round" })] }) }))] }));
|
|
25
|
+
});
|
|
26
|
+
exports.SelectOption.displayName = "SelectOption";
|