@olundot/ui 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/dist/index.d.ts +72 -2
- package/dist/index.js +456 -90
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# @olundot/ui
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- de4b5fe: Add `TreeView` — a WAI-ARIA tree (APG tree pattern, single-select) for hierarchical navigation such as folder/classification browsers.
|
|
8
|
+
|
|
9
|
+
- Data-driven `nodes` with a `renderItem` render-prop so consumers own item content (icon + label/link) while the DS owns tree semantics.
|
|
10
|
+
- Generic over `T extends TreeNode`; controlled `selectedId` and controlled/uncontrolled expansion (`expandedIds` / `defaultExpandedIds` + `onExpandedChange`), `onSelect`.
|
|
11
|
+
- Optional `selectionAriaCurrent` puts `aria-current` (e.g. `"page"`) on the selected node for navigation trees, alongside `aria-selected`.
|
|
12
|
+
- Full keyboard support: ArrowUp/Down move, ArrowRight/Left expand·collapse·move, Home/End, Enter selects, Space toggles a parent's expansion (selects a leaf), with roving tabindex (one Tab stop).
|
|
13
|
+
- Roles `tree`/`treeitem`/`group`, `aria-expanded`/`aria-selected`/`aria-level`, required `aria-label`; token-driven hover/selected (subtle fill)/focus with token-based depth indentation; light + dark.
|
|
14
|
+
- Works when item content contains a focusable link (give inner links `tabIndex={-1}`; activation preserved on pointer).
|
|
15
|
+
|
|
16
|
+
Scope (v1): single-select + expand/collapse + keyboard + a11y. Multi-select, async/lazy loading, drag-drop and inline rename are intentionally out of scope and can be added without breaking the API.
|
|
17
|
+
|
|
18
|
+
New exports: `TreeView`, `treeViewVariants`, `treeItemRowVariants`, `TreeNode`, `TreeItemState`, `TreeViewProps`, `TreeItemRowVariantProps`.
|
|
19
|
+
|
|
20
|
+
## 0.5.0
|
|
21
|
+
|
|
22
|
+
### Minor Changes
|
|
23
|
+
|
|
24
|
+
- Add the shared OLUN theme transition contract and theme helper API.
|
|
25
|
+
|
|
26
|
+
- `@olundot/tokens` now exposes a 300ms default theme transition token contract.
|
|
27
|
+
- `@olundot/ui` now exports `getOlunThemeState`, `setOlunTheme`, and `useOlunTheme` for synchronized light/dark theme controls.
|
|
28
|
+
|
|
29
|
+
### Patch Changes
|
|
30
|
+
|
|
31
|
+
- Updated dependencies
|
|
32
|
+
- @olundot/tokens@0.10.0
|
|
33
|
+
|
|
3
34
|
## 0.4.0
|
|
4
35
|
|
|
5
36
|
### Minor Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -9,10 +9,30 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
|
9
9
|
|
|
10
10
|
declare function cn(...inputs: ClassValue[]): string;
|
|
11
11
|
|
|
12
|
+
type OlunTheme = "light" | "dark";
|
|
13
|
+
type OlunThemeDuration = "instant" | "fast" | "base" | "slow" | "slower" | "slowest";
|
|
14
|
+
interface OlunThemeState {
|
|
15
|
+
theme: OlunTheme;
|
|
16
|
+
resolvedTheme: OlunTheme;
|
|
17
|
+
storedTheme: OlunTheme | null;
|
|
18
|
+
source: "attribute" | "stored" | "system" | "default";
|
|
19
|
+
}
|
|
20
|
+
interface SetOlunThemeOptions {
|
|
21
|
+
duration?: OlunThemeDuration;
|
|
22
|
+
}
|
|
23
|
+
interface UseOlunThemeValue extends OlunThemeState {
|
|
24
|
+
mounted: boolean;
|
|
25
|
+
setTheme: (nextTheme: OlunTheme, options?: SetOlunThemeOptions) => void;
|
|
26
|
+
toggleTheme: (options?: SetOlunThemeOptions) => void;
|
|
27
|
+
}
|
|
28
|
+
declare function getOlunThemeState(): OlunThemeState;
|
|
29
|
+
declare function setOlunTheme(nextTheme: OlunTheme, options?: SetOlunThemeOptions): void;
|
|
30
|
+
declare function useOlunTheme(): UseOlunThemeValue;
|
|
31
|
+
|
|
12
32
|
type SurfaceLevel = "surface" | "canvas" | "raised";
|
|
13
33
|
|
|
14
34
|
declare const buttonVariants: (props?: ({
|
|
15
|
-
variant?: "primary" | "secondary" | "destructive" | "outline" | "ghost" | "danger" | "emergency" | "subtle" | "
|
|
35
|
+
variant?: "primary" | "secondary" | "destructive" | "default" | "outline" | "ghost" | "danger" | "emergency" | "subtle" | "link" | null | undefined;
|
|
16
36
|
size?: "xs" | "sm" | "md" | "lg" | "xl" | "default" | "icon" | "icon-xs" | null | undefined;
|
|
17
37
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
18
38
|
type ButtonVariant = NonNullable<VariantProps<typeof buttonVariants>['variant']>;
|
|
@@ -1301,6 +1321,56 @@ interface ProgressProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
1301
1321
|
}
|
|
1302
1322
|
declare function Progress({ value, max, status, width, height, className, style, ...props }: ProgressProps): react_jsx_runtime.JSX.Element;
|
|
1303
1323
|
|
|
1324
|
+
/**
|
|
1325
|
+
* A single node in a TreeView. Consumers extend this shape with their own
|
|
1326
|
+
* fields (title, href, icon, …) and make the component generic over that type.
|
|
1327
|
+
* Only `id` (stable, unique within the tree) is required by the DS; children
|
|
1328
|
+
* are read through `getChildren` (default: `node.children`).
|
|
1329
|
+
*/
|
|
1330
|
+
interface TreeNode {
|
|
1331
|
+
id: string;
|
|
1332
|
+
children?: TreeNode[];
|
|
1333
|
+
}
|
|
1334
|
+
/** State handed to `renderItem` so the consumer can reflect tree status. */
|
|
1335
|
+
interface TreeItemState {
|
|
1336
|
+
expanded: boolean;
|
|
1337
|
+
selected: boolean;
|
|
1338
|
+
/** 1-based ARIA level (roots are level 1). */
|
|
1339
|
+
depth: number;
|
|
1340
|
+
hasChildren: boolean;
|
|
1341
|
+
}
|
|
1342
|
+
interface TreeViewProps<T extends TreeNode> extends Omit<React.HTMLAttributes<HTMLUListElement>, "onSelect" | "role"> {
|
|
1343
|
+
/** Root nodes of the tree. */
|
|
1344
|
+
nodes: T[];
|
|
1345
|
+
/** Controlled "current" node (single-select). */
|
|
1346
|
+
selectedId?: string;
|
|
1347
|
+
/** When set, the selected node also carries this `aria-current` value. Use
|
|
1348
|
+
* `"page"` for navigation trees so assistive tech announces the current
|
|
1349
|
+
* location (e.g. the open folder). Default: no `aria-current`. */
|
|
1350
|
+
selectionAriaCurrent?: React.AriaAttributes["aria-current"];
|
|
1351
|
+
/** Initial expanded node ids (uncontrolled expansion). */
|
|
1352
|
+
defaultExpandedIds?: string[];
|
|
1353
|
+
/** Controlled expanded node ids. When set, expansion is fully controlled. */
|
|
1354
|
+
expandedIds?: string[];
|
|
1355
|
+
/** Fires whenever the expanded set changes (controlled or uncontrolled). */
|
|
1356
|
+
onExpandedChange?: (ids: string[]) => void;
|
|
1357
|
+
/** Fires when a node is activated (Enter/Space or click). */
|
|
1358
|
+
onSelect?: (id: string, node: T) => void;
|
|
1359
|
+
/** Render the item's content (icon + label/link). Receives node state. */
|
|
1360
|
+
renderItem: (node: T, state: TreeItemState) => React.ReactNode;
|
|
1361
|
+
/** Read a node's children. Default: `node.children`. */
|
|
1362
|
+
getChildren?: (node: T) => T[] | undefined;
|
|
1363
|
+
/** Required: the tree must be labelled (WAI-ARIA tree). */
|
|
1364
|
+
"aria-label": string;
|
|
1365
|
+
className?: string;
|
|
1366
|
+
}
|
|
1367
|
+
declare const treeViewVariants: (props?: class_variance_authority_types.ClassProp | undefined) => string;
|
|
1368
|
+
declare const treeItemRowVariants: (props?: ({
|
|
1369
|
+
selected?: boolean | null | undefined;
|
|
1370
|
+
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
1371
|
+
type TreeItemRowVariantProps = VariantProps<typeof treeItemRowVariants>;
|
|
1372
|
+
declare function TreeView<T extends TreeNode>({ nodes, selectedId, selectionAriaCurrent, defaultExpandedIds, expandedIds, onExpandedChange, onSelect, renderItem, getChildren, className, "aria-label": ariaLabel, ...rest }: TreeViewProps<T>): react_jsx_runtime.JSX.Element;
|
|
1373
|
+
|
|
1304
1374
|
/**
|
|
1305
1375
|
* OLUN 브랜드 로고.
|
|
1306
1376
|
*
|
|
@@ -1349,4 +1419,4 @@ interface OrganizationLogoProps extends Omit<SVGProps<SVGSVGElement>, "width" |
|
|
|
1349
1419
|
}
|
|
1350
1420
|
declare function OrganizationLogo({ organization, size, className, style, role, "aria-label": ariaLabel, "aria-hidden": ariaHidden, ...props }: OrganizationLogoProps): react_jsx_runtime.JSX.Element;
|
|
1351
1421
|
|
|
1352
|
-
export { ActionFooter, type ActionFooterAction, type ActionFooterProps, type ActionFooterStatus, Alert, type AlertProps, type AlertVariant, Avatar, AvatarFallback, type AvatarFallbackProps, AvatarImage, Badge, type BadgeProps, type BadgeSize, type BadgeTone, type BadgeVariant, Button, type ButtonProps, type ButtonSize, type ButtonVariant, Card, CardContent, CardDescription, CardFooter, CardHeader, type CardProps, CardProse, CardTitle, type CardVariant, type CardVariantOptions, Checkbox, type CheckboxCheckedState, CheckboxGroup, type CheckboxGroupAppearance, type CheckboxGroupColumns, CheckboxGroupContext, type CheckboxGroupContextValue, type CheckboxGroupDensity, type CheckboxGroupOrientation, type CheckboxGroupProps, type CheckboxProps, type CheckboxSize, Chip, type ChipProps, type ChipSize, type ChipTone, type ChipVariant, ConfirmDialog, type ConfirmDialogProps, CopyButton, type CopyButtonProps, DataTable, type DataTableBulkAction, type DataTableColumn, type DataTableProps, type DataTableSort, DetailLayout, type DetailLayoutProps, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogRawContent, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, type EmptyStateProps, type EmptyStateSize, type EmptyStateTone, type EmptyStateVariantOptions, ErrorState, type ErrorStateProps, FileUploadZone, type FileUploadZoneProps, FilterToolbar, type FilterToolbarProps, FormField, type FormFieldProps, type FormFieldRenderProps, FormLayout, type FormLayoutProps, type FormLayoutSection, FormModal, type FormModalProps, IconButton, type IconButtonBadgeTone, type IconButtonProps, type IconButtonSize, type IconButtonVariant, InfoModal, type InfoModalProps, InlineAlert, type InlineAlertProps, Input, type InputProps, type InputSize, type InputSizeLegacy, type InputState, Label, type LabelProps, ModeToggle, type ModeToggleOption, type ModeToggleProps, NotificationBadge, type NotificationBadgePlacement, type NotificationBadgeProps, type NotificationBadgeSize, OTPField, type OTPFieldProps, type OTPFieldSize, OlunLogo, OlunSymbol, OrganizationLogo, type OrganizationLogoId, type OrganizationLogoProps, PageHeader, type PageHeaderProps, Pagination, type PaginationProps, type PaginationSize, PermissionContext, type PermissionEvaluator, type PermissionFallback, PermissionGuard, type PermissionGuardProps, PermissionProvider, type PermissionProviderProps, type PermissionScope, type PermissionState, Popover, PopoverAnchor, PopoverArrow, PopoverClose, PopoverContent, type PopoverContentProps, type PopoverProps, PopoverTrigger, Progress, type ProgressProps, type ProgressStatus, Radio, RadioGroup, type RadioGroupAppearance, type RadioGroupColumns, type RadioGroupDensity, type RadioGroupOrientation, type RadioGroupProps, type RadioProps, ScrollArea, ScrollBar, SearchField, type SearchFieldProps, type SearchFieldSize, Select, type SelectAppearance, type SelectOption, type SelectProps, type SelectSize, type SelectState, type SelectVariant, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetDrawer, type SheetDrawerProps, SheetFooter, SheetHeader, type SheetMotionConfig, type SheetMotionDuration, type SheetMotionEasing, type SheetMotionType, SheetTitle, SheetTrigger, Skeleton, type SkeletonProps, SkeletonRow, type SkeletonRowColumn, type SkeletonRowProps, SkeletonText, type SkeletonTextProps, Spinner, type SpinnerProps, StatusBadge, type StatusBadgeProps, type StatusBadgeStatus, type SurfaceLevel, Switch, type SwitchProps, type TabItem, Tabs, TabsContent, type TabsProps, type TabsVariant, Textarea, type TextareaProps, type TextareaSize, type TextareaSizeLegacy, type TextareaState, ThemeFab, type ThemeFabProps, ThemeToggle, type ToastAction, type ToastItem, type ToastOptions, ToastProvider, ToastRoot, type ToastVariant, ToastViewport, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TwoTierBadge, type TwoTierBadgeIntent, type TwoTierBadgeProps, WizardLayout, type WizardLayoutProps, type WizardStep, type WizardStepState, actionFooterStatusVariants, actionFooterVariants, alertVariants, badgeDotVariants, badgeVariants, buttonVariants, cardVariants, checkboxBoxVariants, checkboxFieldVariants, checkboxGroupBoxVariants, checkboxGroupItemVariants, checkboxGroupVariants, checkboxRootVariants, checkboxVariants, chipCountVariants, chipVariants, cn, dataTableRowVariants, dataTableVariants, emptyStateIconVariants, emptyStateVariants, errorStateVariants, evaluatePermission, fileUploadIconVariants, fileUploadZoneVariants, formFieldControlStackClassName, formFieldDescriptionClassName, formFieldErrorClassName, formFieldLabelClassName, formFieldRequiredClassName, formFieldVariants, getToastDuration, iconButtonBadgeVariants, iconButtonVariants, inlineAlertVariants, inputMessageVariants, inputVariants, limitToasts, modeToggleItemVariants, modeToggleVariants, notificationBadgeVariants, otpCellVariants, otpFieldGroupVariants, otpFieldMessageVariants, otpFieldVariants, paginationControlVariants, paginationVariants, radioControlVariants, radioGroupVariants, radioVariants, searchFieldInputVariants, searchFieldVariants, selectMessageVariants, selectVariants, skeletonVariants, spinnerVariants, statusBadgeDotVariants, statusBadgeVariants, tabsBadgeVariants, tabsListVariants, tabsRootVariants, tabsTriggerVariants, textareaMessageVariants, textareaVariants, toast, toastVariants, toastViewportVariants, twoTierBadgeLineVariants, twoTierBadgeVariants, usePermission };
|
|
1422
|
+
export { ActionFooter, type ActionFooterAction, type ActionFooterProps, type ActionFooterStatus, Alert, type AlertProps, type AlertVariant, Avatar, AvatarFallback, type AvatarFallbackProps, AvatarImage, Badge, type BadgeProps, type BadgeSize, type BadgeTone, type BadgeVariant, Button, type ButtonProps, type ButtonSize, type ButtonVariant, Card, CardContent, CardDescription, CardFooter, CardHeader, type CardProps, CardProse, CardTitle, type CardVariant, type CardVariantOptions, Checkbox, type CheckboxCheckedState, CheckboxGroup, type CheckboxGroupAppearance, type CheckboxGroupColumns, CheckboxGroupContext, type CheckboxGroupContextValue, type CheckboxGroupDensity, type CheckboxGroupOrientation, type CheckboxGroupProps, type CheckboxProps, type CheckboxSize, Chip, type ChipProps, type ChipSize, type ChipTone, type ChipVariant, ConfirmDialog, type ConfirmDialogProps, CopyButton, type CopyButtonProps, DataTable, type DataTableBulkAction, type DataTableColumn, type DataTableProps, type DataTableSort, DetailLayout, type DetailLayoutProps, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogRawContent, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, type EmptyStateProps, type EmptyStateSize, type EmptyStateTone, type EmptyStateVariantOptions, ErrorState, type ErrorStateProps, FileUploadZone, type FileUploadZoneProps, FilterToolbar, type FilterToolbarProps, FormField, type FormFieldProps, type FormFieldRenderProps, FormLayout, type FormLayoutProps, type FormLayoutSection, FormModal, type FormModalProps, IconButton, type IconButtonBadgeTone, type IconButtonProps, type IconButtonSize, type IconButtonVariant, InfoModal, type InfoModalProps, InlineAlert, type InlineAlertProps, Input, type InputProps, type InputSize, type InputSizeLegacy, type InputState, Label, type LabelProps, ModeToggle, type ModeToggleOption, type ModeToggleProps, NotificationBadge, type NotificationBadgePlacement, type NotificationBadgeProps, type NotificationBadgeSize, OTPField, type OTPFieldProps, type OTPFieldSize, OlunLogo, OlunSymbol, type OlunTheme, type OlunThemeDuration, type OlunThemeState, OrganizationLogo, type OrganizationLogoId, type OrganizationLogoProps, PageHeader, type PageHeaderProps, Pagination, type PaginationProps, type PaginationSize, PermissionContext, type PermissionEvaluator, type PermissionFallback, PermissionGuard, type PermissionGuardProps, PermissionProvider, type PermissionProviderProps, type PermissionScope, type PermissionState, Popover, PopoverAnchor, PopoverArrow, PopoverClose, PopoverContent, type PopoverContentProps, type PopoverProps, PopoverTrigger, Progress, type ProgressProps, type ProgressStatus, Radio, RadioGroup, type RadioGroupAppearance, type RadioGroupColumns, type RadioGroupDensity, type RadioGroupOrientation, type RadioGroupProps, type RadioProps, ScrollArea, ScrollBar, SearchField, type SearchFieldProps, type SearchFieldSize, Select, type SelectAppearance, type SelectOption, type SelectProps, type SelectSize, type SelectState, type SelectVariant, Separator, type SetOlunThemeOptions, Sheet, SheetClose, SheetContent, SheetDescription, SheetDrawer, type SheetDrawerProps, SheetFooter, SheetHeader, type SheetMotionConfig, type SheetMotionDuration, type SheetMotionEasing, type SheetMotionType, SheetTitle, SheetTrigger, Skeleton, type SkeletonProps, SkeletonRow, type SkeletonRowColumn, type SkeletonRowProps, SkeletonText, type SkeletonTextProps, Spinner, type SpinnerProps, StatusBadge, type StatusBadgeProps, type StatusBadgeStatus, type SurfaceLevel, Switch, type SwitchProps, type TabItem, Tabs, TabsContent, type TabsProps, type TabsVariant, Textarea, type TextareaProps, type TextareaSize, type TextareaSizeLegacy, type TextareaState, ThemeFab, type ThemeFabProps, ThemeToggle, type ToastAction, type ToastItem, type ToastOptions, ToastProvider, ToastRoot, type ToastVariant, ToastViewport, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type TreeItemRowVariantProps, type TreeItemState, type TreeNode, TreeView, type TreeViewProps, TwoTierBadge, type TwoTierBadgeIntent, type TwoTierBadgeProps, type UseOlunThemeValue, WizardLayout, type WizardLayoutProps, type WizardStep, type WizardStepState, actionFooterStatusVariants, actionFooterVariants, alertVariants, badgeDotVariants, badgeVariants, buttonVariants, cardVariants, checkboxBoxVariants, checkboxFieldVariants, checkboxGroupBoxVariants, checkboxGroupItemVariants, checkboxGroupVariants, checkboxRootVariants, checkboxVariants, chipCountVariants, chipVariants, cn, dataTableRowVariants, dataTableVariants, emptyStateIconVariants, emptyStateVariants, errorStateVariants, evaluatePermission, fileUploadIconVariants, fileUploadZoneVariants, formFieldControlStackClassName, formFieldDescriptionClassName, formFieldErrorClassName, formFieldLabelClassName, formFieldRequiredClassName, formFieldVariants, getOlunThemeState, getToastDuration, iconButtonBadgeVariants, iconButtonVariants, inlineAlertVariants, inputMessageVariants, inputVariants, limitToasts, modeToggleItemVariants, modeToggleVariants, notificationBadgeVariants, otpCellVariants, otpFieldGroupVariants, otpFieldMessageVariants, otpFieldVariants, paginationControlVariants, paginationVariants, radioControlVariants, radioGroupVariants, radioVariants, searchFieldInputVariants, searchFieldVariants, selectMessageVariants, selectVariants, setOlunTheme, skeletonVariants, spinnerVariants, statusBadgeDotVariants, statusBadgeVariants, tabsBadgeVariants, tabsListVariants, tabsRootVariants, tabsTriggerVariants, textareaMessageVariants, textareaVariants, toast, toastVariants, toastViewportVariants, treeItemRowVariants, treeViewVariants, twoTierBadgeLineVariants, twoTierBadgeVariants, useOlunTheme, usePermission };
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,165 @@ function cn(...inputs) {
|
|
|
14
14
|
return twMerge(clsx(inputs));
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
// src/lib/theme.ts
|
|
18
|
+
import { useCallback, useEffect, useState } from "react";
|
|
19
|
+
var STORAGE_KEY = "olun-theme";
|
|
20
|
+
var LEGACY_STORAGE_KEY = "olun-theme-mode";
|
|
21
|
+
var TRANSITION_ATTR = "olunThemeTransition";
|
|
22
|
+
var TRANSITION_DURATION_PROPERTY = "--olun-theme-transition-duration";
|
|
23
|
+
var durationMsByToken = {
|
|
24
|
+
instant: 0,
|
|
25
|
+
fast: 150,
|
|
26
|
+
base: 200,
|
|
27
|
+
slow: 300,
|
|
28
|
+
slower: 400,
|
|
29
|
+
slowest: 600
|
|
30
|
+
};
|
|
31
|
+
var transitionCleanupTimer;
|
|
32
|
+
var defaultThemeState = {
|
|
33
|
+
theme: "light",
|
|
34
|
+
resolvedTheme: "light",
|
|
35
|
+
storedTheme: null,
|
|
36
|
+
source: "default"
|
|
37
|
+
};
|
|
38
|
+
function isOlunTheme(value) {
|
|
39
|
+
return value === "light" || value === "dark";
|
|
40
|
+
}
|
|
41
|
+
function readStoredTheme() {
|
|
42
|
+
if (typeof window === "undefined") return null;
|
|
43
|
+
try {
|
|
44
|
+
const saved = window.localStorage.getItem(STORAGE_KEY);
|
|
45
|
+
if (isOlunTheme(saved)) return saved;
|
|
46
|
+
const legacy = window.localStorage.getItem(LEGACY_STORAGE_KEY);
|
|
47
|
+
return isOlunTheme(legacy) ? legacy : null;
|
|
48
|
+
} catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function resolveSystemTheme() {
|
|
53
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
54
|
+
return "light";
|
|
55
|
+
}
|
|
56
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
57
|
+
}
|
|
58
|
+
function getReducedMotionDurationMs(duration) {
|
|
59
|
+
if (typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
|
60
|
+
return durationMsByToken.fast;
|
|
61
|
+
}
|
|
62
|
+
return durationMsByToken[duration];
|
|
63
|
+
}
|
|
64
|
+
function getOlunThemeState() {
|
|
65
|
+
if (typeof document !== "undefined") {
|
|
66
|
+
const attr = document.documentElement.getAttribute("data-theme");
|
|
67
|
+
if (isOlunTheme(attr)) {
|
|
68
|
+
return {
|
|
69
|
+
theme: attr,
|
|
70
|
+
resolvedTheme: attr,
|
|
71
|
+
storedTheme: readStoredTheme(),
|
|
72
|
+
source: "attribute"
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const storedTheme = readStoredTheme();
|
|
77
|
+
if (storedTheme) {
|
|
78
|
+
return {
|
|
79
|
+
theme: storedTheme,
|
|
80
|
+
resolvedTheme: storedTheme,
|
|
81
|
+
storedTheme,
|
|
82
|
+
source: "stored"
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (typeof window !== "undefined") {
|
|
86
|
+
const systemTheme = resolveSystemTheme();
|
|
87
|
+
return {
|
|
88
|
+
theme: systemTheme,
|
|
89
|
+
resolvedTheme: systemTheme,
|
|
90
|
+
storedTheme: null,
|
|
91
|
+
source: "system"
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return defaultThemeState;
|
|
95
|
+
}
|
|
96
|
+
function setOlunTheme(nextTheme, options = {}) {
|
|
97
|
+
if (typeof document === "undefined") return;
|
|
98
|
+
const duration = options.duration ?? "slow";
|
|
99
|
+
const root = document.documentElement;
|
|
100
|
+
if (transitionCleanupTimer !== void 0) {
|
|
101
|
+
window.clearTimeout(transitionCleanupTimer);
|
|
102
|
+
transitionCleanupTimer = void 0;
|
|
103
|
+
}
|
|
104
|
+
if (duration !== "instant") {
|
|
105
|
+
root.dataset[TRANSITION_ATTR] = "true";
|
|
106
|
+
root.style.setProperty(
|
|
107
|
+
TRANSITION_DURATION_PROPERTY,
|
|
108
|
+
`var(--duration-${duration})`
|
|
109
|
+
);
|
|
110
|
+
void root.offsetWidth;
|
|
111
|
+
}
|
|
112
|
+
root.setAttribute("data-theme", nextTheme);
|
|
113
|
+
root.setAttribute("data-theme-mode", nextTheme);
|
|
114
|
+
try {
|
|
115
|
+
window.localStorage.setItem(STORAGE_KEY, nextTheme);
|
|
116
|
+
window.localStorage.removeItem(LEGACY_STORAGE_KEY);
|
|
117
|
+
} catch {
|
|
118
|
+
}
|
|
119
|
+
if (duration !== "instant") {
|
|
120
|
+
const cleanupDelay = getReducedMotionDurationMs(duration) + 80;
|
|
121
|
+
transitionCleanupTimer = window.setTimeout(() => {
|
|
122
|
+
delete root.dataset[TRANSITION_ATTR];
|
|
123
|
+
root.style.removeProperty(TRANSITION_DURATION_PROPERTY);
|
|
124
|
+
transitionCleanupTimer = void 0;
|
|
125
|
+
}, cleanupDelay);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function useOlunTheme() {
|
|
129
|
+
const [state, setState] = useState(defaultThemeState);
|
|
130
|
+
const [mounted, setMounted] = useState(false);
|
|
131
|
+
useEffect(() => {
|
|
132
|
+
setMounted(true);
|
|
133
|
+
const sync = () => setState(getOlunThemeState());
|
|
134
|
+
sync();
|
|
135
|
+
const observer = new MutationObserver(sync);
|
|
136
|
+
observer.observe(document.documentElement, {
|
|
137
|
+
attributes: true,
|
|
138
|
+
attributeFilter: ["data-theme"]
|
|
139
|
+
});
|
|
140
|
+
const onStorage = (event) => {
|
|
141
|
+
if (event.key === STORAGE_KEY || event.key === LEGACY_STORAGE_KEY) sync();
|
|
142
|
+
};
|
|
143
|
+
window.addEventListener("storage", onStorage);
|
|
144
|
+
return () => {
|
|
145
|
+
observer.disconnect();
|
|
146
|
+
window.removeEventListener("storage", onStorage);
|
|
147
|
+
};
|
|
148
|
+
}, []);
|
|
149
|
+
const setTheme = useCallback(
|
|
150
|
+
(nextTheme, options) => {
|
|
151
|
+
setState((current) => ({
|
|
152
|
+
theme: nextTheme,
|
|
153
|
+
resolvedTheme: nextTheme,
|
|
154
|
+
storedTheme: nextTheme,
|
|
155
|
+
source: "attribute"
|
|
156
|
+
}));
|
|
157
|
+
setOlunTheme(nextTheme, options);
|
|
158
|
+
},
|
|
159
|
+
[]
|
|
160
|
+
);
|
|
161
|
+
const toggleTheme = useCallback(
|
|
162
|
+
(options) => {
|
|
163
|
+
const nextTheme = state.resolvedTheme === "dark" ? "light" : "dark";
|
|
164
|
+
setTheme(nextTheme, options);
|
|
165
|
+
},
|
|
166
|
+
[setTheme, state.resolvedTheme]
|
|
167
|
+
);
|
|
168
|
+
return {
|
|
169
|
+
...state,
|
|
170
|
+
mounted,
|
|
171
|
+
setTheme,
|
|
172
|
+
toggleTheme
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
17
176
|
// src/components/ui/button.tsx
|
|
18
177
|
import * as React from "react";
|
|
19
178
|
import { cva } from "class-variance-authority";
|
|
@@ -7728,10 +7887,275 @@ function Progress({
|
|
|
7728
7887
|
);
|
|
7729
7888
|
}
|
|
7730
7889
|
|
|
7731
|
-
// src/components/
|
|
7890
|
+
// src/components/ui/tree-view.tsx
|
|
7891
|
+
import * as React41 from "react";
|
|
7892
|
+
import { ChevronRight as ChevronRight2 } from "lucide-react";
|
|
7893
|
+
import { cva as cva29 } from "class-variance-authority";
|
|
7732
7894
|
import { jsx as jsx51, jsxs as jsxs44 } from "react/jsx-runtime";
|
|
7895
|
+
var treeViewVariants = cva29(
|
|
7896
|
+
"m-0 list-none p-[var(--space-0-5)] text-body-sm text-foreground"
|
|
7897
|
+
);
|
|
7898
|
+
var treeItemRowVariants = cva29(
|
|
7899
|
+
[
|
|
7900
|
+
"flex min-h-[var(--touch-target-min)] w-full cursor-pointer select-none items-center gap-[var(--space-1)]",
|
|
7901
|
+
"rounded-[var(--radius-sm)] py-[var(--space-1)] pr-[var(--space-1-5)]",
|
|
7902
|
+
"text-text-secondary transition-colors duration-[var(--duration-fast)] ease-[var(--ease-standard)] motion-reduce:transition-none",
|
|
7903
|
+
"hover:bg-[var(--action-hover)] hover:text-foreground",
|
|
7904
|
+
// Roving focus lives on the treeitem <li> (named group/treeitem); the ring is
|
|
7905
|
+
// painted on the visual row so it never wraps the nested child group.
|
|
7906
|
+
"group-focus-visible/treeitem:outline-none group-focus-visible/treeitem:ring-2 group-focus-visible/treeitem:ring-inset group-focus-visible/treeitem:ring-[var(--focus-ring)]"
|
|
7907
|
+
].join(" "),
|
|
7908
|
+
{
|
|
7909
|
+
variants: {
|
|
7910
|
+
selected: {
|
|
7911
|
+
// Selected = subtle fill (action-selected), matching nav selected state.
|
|
7912
|
+
// Never a left border (OLUN selected-state doctrine).
|
|
7913
|
+
true: "bg-[var(--action-selected)] font-medium text-foreground",
|
|
7914
|
+
false: ""
|
|
7915
|
+
}
|
|
7916
|
+
},
|
|
7917
|
+
defaultVariants: {
|
|
7918
|
+
selected: false
|
|
7919
|
+
}
|
|
7920
|
+
}
|
|
7921
|
+
);
|
|
7922
|
+
function defaultGetChildren(node) {
|
|
7923
|
+
return node.children;
|
|
7924
|
+
}
|
|
7925
|
+
function flattenVisibleNodes(nodes, getChildren, expandedSet, depth = 1, parentId = null, acc = []) {
|
|
7926
|
+
for (const node of nodes) {
|
|
7927
|
+
const children = getChildren(node);
|
|
7928
|
+
const hasChildren = Array.isArray(children) && children.length > 0;
|
|
7929
|
+
const expanded = hasChildren && expandedSet.has(node.id);
|
|
7930
|
+
acc.push({ node, id: node.id, depth, hasChildren, expanded, parentId });
|
|
7931
|
+
if (expanded && children) {
|
|
7932
|
+
flattenVisibleNodes(children, getChildren, expandedSet, depth + 1, node.id, acc);
|
|
7933
|
+
}
|
|
7934
|
+
}
|
|
7935
|
+
return acc;
|
|
7936
|
+
}
|
|
7937
|
+
function getTreeViewKeyAction(key, items, activeId) {
|
|
7938
|
+
if (items.length === 0 || activeId === null) return null;
|
|
7939
|
+
const index = items.findIndex((item) => item.id === activeId);
|
|
7940
|
+
if (index === -1) return null;
|
|
7941
|
+
const current = items[index];
|
|
7942
|
+
switch (key) {
|
|
7943
|
+
case "ArrowDown": {
|
|
7944
|
+
const next = items[index + 1];
|
|
7945
|
+
return next ? { type: "focus", id: next.id } : null;
|
|
7946
|
+
}
|
|
7947
|
+
case "ArrowUp": {
|
|
7948
|
+
const prev = items[index - 1];
|
|
7949
|
+
return prev ? { type: "focus", id: prev.id } : null;
|
|
7950
|
+
}
|
|
7951
|
+
case "Home":
|
|
7952
|
+
return { type: "focus", id: items[0].id };
|
|
7953
|
+
case "End":
|
|
7954
|
+
return { type: "focus", id: items[items.length - 1].id };
|
|
7955
|
+
case "ArrowRight": {
|
|
7956
|
+
if (current.hasChildren && !current.expanded) {
|
|
7957
|
+
return { type: "expand", id: current.id };
|
|
7958
|
+
}
|
|
7959
|
+
if (current.hasChildren && current.expanded) {
|
|
7960
|
+
const child = items[index + 1];
|
|
7961
|
+
return child && child.parentId === current.id ? { type: "focus", id: child.id } : null;
|
|
7962
|
+
}
|
|
7963
|
+
return null;
|
|
7964
|
+
}
|
|
7965
|
+
case "ArrowLeft": {
|
|
7966
|
+
if (current.hasChildren && current.expanded) {
|
|
7967
|
+
return { type: "collapse", id: current.id };
|
|
7968
|
+
}
|
|
7969
|
+
return current.parentId ? { type: "focus", id: current.parentId } : null;
|
|
7970
|
+
}
|
|
7971
|
+
case "Enter":
|
|
7972
|
+
return { type: "select", id: current.id };
|
|
7973
|
+
case " ":
|
|
7974
|
+
if (current.hasChildren) {
|
|
7975
|
+
return current.expanded ? { type: "collapse", id: current.id } : { type: "expand", id: current.id };
|
|
7976
|
+
}
|
|
7977
|
+
return { type: "select", id: current.id };
|
|
7978
|
+
default:
|
|
7979
|
+
return null;
|
|
7980
|
+
}
|
|
7981
|
+
}
|
|
7982
|
+
function TreeView({
|
|
7983
|
+
nodes,
|
|
7984
|
+
selectedId,
|
|
7985
|
+
selectionAriaCurrent,
|
|
7986
|
+
defaultExpandedIds,
|
|
7987
|
+
expandedIds,
|
|
7988
|
+
onExpandedChange,
|
|
7989
|
+
onSelect,
|
|
7990
|
+
renderItem,
|
|
7991
|
+
getChildren = defaultGetChildren,
|
|
7992
|
+
className,
|
|
7993
|
+
"aria-label": ariaLabel,
|
|
7994
|
+
...rest
|
|
7995
|
+
}) {
|
|
7996
|
+
const isExpandedControlled = expandedIds !== void 0;
|
|
7997
|
+
const [internalExpanded, setInternalExpanded] = React41.useState(
|
|
7998
|
+
() => defaultExpandedIds ?? []
|
|
7999
|
+
);
|
|
8000
|
+
const expandedList = isExpandedControlled ? expandedIds : internalExpanded;
|
|
8001
|
+
const expandedSet = React41.useMemo(() => new Set(expandedList), [expandedList]);
|
|
8002
|
+
const items = React41.useMemo(
|
|
8003
|
+
() => flattenVisibleNodes(nodes, getChildren, expandedSet),
|
|
8004
|
+
[nodes, getChildren, expandedSet]
|
|
8005
|
+
);
|
|
8006
|
+
const [focusedId, setFocusedId] = React41.useState(null);
|
|
8007
|
+
const visibleIds = items.map((item) => item.id);
|
|
8008
|
+
const activeId = focusedId && visibleIds.includes(focusedId) ? focusedId : selectedId && visibleIds.includes(selectedId) ? selectedId : visibleIds[0] ?? null;
|
|
8009
|
+
const itemRefs = React41.useRef(/* @__PURE__ */ new Map());
|
|
8010
|
+
const commitExpanded = React41.useCallback(
|
|
8011
|
+
(next) => {
|
|
8012
|
+
if (!isExpandedControlled) setInternalExpanded(next);
|
|
8013
|
+
onExpandedChange?.(next);
|
|
8014
|
+
},
|
|
8015
|
+
[isExpandedControlled, onExpandedChange]
|
|
8016
|
+
);
|
|
8017
|
+
const setExpanded = React41.useCallback(
|
|
8018
|
+
(id, expand) => {
|
|
8019
|
+
const has = expandedSet.has(id);
|
|
8020
|
+
if (expand && !has) {
|
|
8021
|
+
commitExpanded([...expandedList, id]);
|
|
8022
|
+
} else if (!expand && has) {
|
|
8023
|
+
commitExpanded(expandedList.filter((value) => value !== id));
|
|
8024
|
+
}
|
|
8025
|
+
},
|
|
8026
|
+
[commitExpanded, expandedList, expandedSet]
|
|
8027
|
+
);
|
|
8028
|
+
const focusItem = React41.useCallback((id) => {
|
|
8029
|
+
setFocusedId(id);
|
|
8030
|
+
itemRefs.current.get(id)?.focus();
|
|
8031
|
+
}, []);
|
|
8032
|
+
const handleKeyDown = (event) => {
|
|
8033
|
+
const target = event.target;
|
|
8034
|
+
if (target.getAttribute("role") !== "treeitem") return;
|
|
8035
|
+
const targetId = target.getAttribute("data-id") ?? activeId;
|
|
8036
|
+
const action = getTreeViewKeyAction(event.key, items, targetId);
|
|
8037
|
+
if (!action) return;
|
|
8038
|
+
event.preventDefault();
|
|
8039
|
+
switch (action.type) {
|
|
8040
|
+
case "focus":
|
|
8041
|
+
focusItem(action.id);
|
|
8042
|
+
break;
|
|
8043
|
+
case "expand":
|
|
8044
|
+
setExpanded(action.id, true);
|
|
8045
|
+
break;
|
|
8046
|
+
case "collapse":
|
|
8047
|
+
setExpanded(action.id, false);
|
|
8048
|
+
break;
|
|
8049
|
+
case "select": {
|
|
8050
|
+
const item = items.find((entry) => entry.id === action.id);
|
|
8051
|
+
if (item) onSelect?.(item.id, item.node);
|
|
8052
|
+
break;
|
|
8053
|
+
}
|
|
8054
|
+
}
|
|
8055
|
+
};
|
|
8056
|
+
function renderLevel(levelNodes, depth) {
|
|
8057
|
+
return levelNodes.map((node) => {
|
|
8058
|
+
const children = getChildren(node);
|
|
8059
|
+
const hasChildren = Array.isArray(children) && children.length > 0;
|
|
8060
|
+
const expanded = hasChildren && expandedSet.has(node.id);
|
|
8061
|
+
const selected = node.id === selectedId;
|
|
8062
|
+
const isActive = node.id === activeId;
|
|
8063
|
+
const itemState = { expanded, selected, depth, hasChildren };
|
|
8064
|
+
return /* @__PURE__ */ jsxs44(
|
|
8065
|
+
"li",
|
|
8066
|
+
{
|
|
8067
|
+
ref: (element) => {
|
|
8068
|
+
if (element) itemRefs.current.set(node.id, element);
|
|
8069
|
+
else itemRefs.current.delete(node.id);
|
|
8070
|
+
},
|
|
8071
|
+
role: "treeitem",
|
|
8072
|
+
"data-slot": "treeitem",
|
|
8073
|
+
"data-id": node.id,
|
|
8074
|
+
"aria-level": depth,
|
|
8075
|
+
"aria-expanded": hasChildren ? expanded : void 0,
|
|
8076
|
+
"aria-selected": selected ? true : void 0,
|
|
8077
|
+
"aria-current": selected && selectionAriaCurrent ? selectionAriaCurrent : void 0,
|
|
8078
|
+
"data-selected": selected ? "true" : void 0,
|
|
8079
|
+
tabIndex: isActive ? 0 : -1,
|
|
8080
|
+
className: "group/treeitem block outline-none",
|
|
8081
|
+
onFocus: (event) => {
|
|
8082
|
+
if (event.target === event.currentTarget) setFocusedId(node.id);
|
|
8083
|
+
},
|
|
8084
|
+
onClick: (event) => {
|
|
8085
|
+
event.stopPropagation();
|
|
8086
|
+
onSelect?.(node.id, node);
|
|
8087
|
+
},
|
|
8088
|
+
children: [
|
|
8089
|
+
/* @__PURE__ */ jsxs44(
|
|
8090
|
+
"span",
|
|
8091
|
+
{
|
|
8092
|
+
"data-slot": "treeitem-row",
|
|
8093
|
+
style: {
|
|
8094
|
+
paddingInlineStart: `calc(var(--space-1) + var(--space-2) * ${depth - 1})`
|
|
8095
|
+
},
|
|
8096
|
+
className: treeItemRowVariants({ selected }),
|
|
8097
|
+
children: [
|
|
8098
|
+
hasChildren ? /* @__PURE__ */ jsx51(
|
|
8099
|
+
"span",
|
|
8100
|
+
{
|
|
8101
|
+
"data-slot": "treeitem-toggle",
|
|
8102
|
+
"aria-hidden": "true",
|
|
8103
|
+
onClick: (event) => {
|
|
8104
|
+
event.stopPropagation();
|
|
8105
|
+
setExpanded(node.id, !expanded);
|
|
8106
|
+
},
|
|
8107
|
+
className: "flex size-[var(--space-2)] shrink-0 items-center justify-center text-text-tertiary",
|
|
8108
|
+
children: /* @__PURE__ */ jsx51(
|
|
8109
|
+
ChevronRight2,
|
|
8110
|
+
{
|
|
8111
|
+
"aria-hidden": "true",
|
|
8112
|
+
"data-slot": "treeitem-chevron",
|
|
8113
|
+
className: cn(
|
|
8114
|
+
"size-[16px] transition-transform duration-[var(--duration-fast)] ease-[var(--ease-standard)] motion-reduce:transition-none",
|
|
8115
|
+
expanded && "rotate-90"
|
|
8116
|
+
),
|
|
8117
|
+
strokeWidth: 2
|
|
8118
|
+
}
|
|
8119
|
+
)
|
|
8120
|
+
}
|
|
8121
|
+
) : /* @__PURE__ */ jsx51(
|
|
8122
|
+
"span",
|
|
8123
|
+
{
|
|
8124
|
+
"data-slot": "treeitem-spacer",
|
|
8125
|
+
"aria-hidden": "true",
|
|
8126
|
+
className: "size-[var(--space-2)] shrink-0"
|
|
8127
|
+
}
|
|
8128
|
+
),
|
|
8129
|
+
/* @__PURE__ */ jsx51("span", { className: "flex min-w-0 flex-1 items-center gap-[var(--space-1)]", children: renderItem(node, itemState) })
|
|
8130
|
+
]
|
|
8131
|
+
}
|
|
8132
|
+
),
|
|
8133
|
+
hasChildren && expanded && children ? /* @__PURE__ */ jsx51("ul", { role: "group", "data-slot": "tree-group", className: "m-0 list-none p-0", children: renderLevel(children, depth + 1) }) : null
|
|
8134
|
+
]
|
|
8135
|
+
},
|
|
8136
|
+
node.id
|
|
8137
|
+
);
|
|
8138
|
+
});
|
|
8139
|
+
}
|
|
8140
|
+
return /* @__PURE__ */ jsx51(
|
|
8141
|
+
"ul",
|
|
8142
|
+
{
|
|
8143
|
+
role: "tree",
|
|
8144
|
+
"data-slot": "tree",
|
|
8145
|
+
"aria-label": ariaLabel,
|
|
8146
|
+
"aria-multiselectable": false,
|
|
8147
|
+
className: cn(treeViewVariants(), className),
|
|
8148
|
+
onKeyDown: handleKeyDown,
|
|
8149
|
+
...rest,
|
|
8150
|
+
children: renderLevel(nodes, 1)
|
|
8151
|
+
}
|
|
8152
|
+
);
|
|
8153
|
+
}
|
|
8154
|
+
|
|
8155
|
+
// src/components/branding/olun-logo.tsx
|
|
8156
|
+
import { jsx as jsx52, jsxs as jsxs45 } from "react/jsx-runtime";
|
|
7733
8157
|
function OlunSymbol({ size = 28, className }) {
|
|
7734
|
-
return /* @__PURE__ */
|
|
8158
|
+
return /* @__PURE__ */ jsxs45(
|
|
7735
8159
|
"svg",
|
|
7736
8160
|
{
|
|
7737
8161
|
width: size,
|
|
@@ -7742,7 +8166,7 @@ function OlunSymbol({ size = 28, className }) {
|
|
|
7742
8166
|
className,
|
|
7743
8167
|
"aria-hidden": "true",
|
|
7744
8168
|
children: [
|
|
7745
|
-
/* @__PURE__ */
|
|
8169
|
+
/* @__PURE__ */ jsx52(
|
|
7746
8170
|
"circle",
|
|
7747
8171
|
{
|
|
7748
8172
|
cx: "108",
|
|
@@ -7754,7 +8178,7 @@ function OlunSymbol({ size = 28, className }) {
|
|
|
7754
8178
|
style: { color: "var(--text-primary)" }
|
|
7755
8179
|
}
|
|
7756
8180
|
),
|
|
7757
|
-
/* @__PURE__ */
|
|
8181
|
+
/* @__PURE__ */ jsx52("circle", { cx: "185", cy: "175", r: "22", fill: "var(--accent-solid)" })
|
|
7758
8182
|
]
|
|
7759
8183
|
}
|
|
7760
8184
|
);
|
|
@@ -7765,7 +8189,7 @@ function OlunLogo({
|
|
|
7765
8189
|
showSymbol = true,
|
|
7766
8190
|
className
|
|
7767
8191
|
}) {
|
|
7768
|
-
return /* @__PURE__ */
|
|
8192
|
+
return /* @__PURE__ */ jsxs45(
|
|
7769
8193
|
"div",
|
|
7770
8194
|
{
|
|
7771
8195
|
className,
|
|
@@ -7775,8 +8199,8 @@ function OlunLogo({
|
|
|
7775
8199
|
gap: "var(--space-1-5)"
|
|
7776
8200
|
},
|
|
7777
8201
|
children: [
|
|
7778
|
-
showSymbol && /* @__PURE__ */
|
|
7779
|
-
/* @__PURE__ */
|
|
8202
|
+
showSymbol && /* @__PURE__ */ jsx52(OlunSymbol, { size: symbolSize }),
|
|
8203
|
+
/* @__PURE__ */ jsxs45(
|
|
7780
8204
|
"span",
|
|
7781
8205
|
{
|
|
7782
8206
|
style: {
|
|
@@ -7788,7 +8212,7 @@ function OlunLogo({
|
|
|
7788
8212
|
},
|
|
7789
8213
|
children: [
|
|
7790
8214
|
"OLUN",
|
|
7791
|
-
/* @__PURE__ */
|
|
8215
|
+
/* @__PURE__ */ jsx52("span", { style: { color: "var(--accent-fg)" }, children: "." })
|
|
7792
8216
|
]
|
|
7793
8217
|
}
|
|
7794
8218
|
)
|
|
@@ -7798,46 +8222,15 @@ function OlunLogo({
|
|
|
7798
8222
|
}
|
|
7799
8223
|
|
|
7800
8224
|
// src/components/branding/theme-toggle.tsx
|
|
7801
|
-
import { useEffect as useEffect4, useState as useState13 } from "react";
|
|
7802
8225
|
import { Sun, Moon } from "lucide-react";
|
|
7803
|
-
import { jsx as
|
|
8226
|
+
import { jsx as jsx53 } from "react/jsx-runtime";
|
|
7804
8227
|
function ThemeToggle({ className }) {
|
|
7805
|
-
const
|
|
7806
|
-
|
|
7807
|
-
|
|
7808
|
-
const attr = document.documentElement.getAttribute("data-theme");
|
|
7809
|
-
if (attr === "light" || attr === "dark") return attr;
|
|
7810
|
-
const stored = localStorage.getItem("olun-theme");
|
|
7811
|
-
if (stored === "light" || stored === "dark") return stored;
|
|
7812
|
-
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
7813
|
-
};
|
|
7814
|
-
setTheme(resolve());
|
|
7815
|
-
const sync = () => setTheme(resolve());
|
|
7816
|
-
const observer = new MutationObserver(sync);
|
|
7817
|
-
observer.observe(document.documentElement, {
|
|
7818
|
-
attributes: true,
|
|
7819
|
-
attributeFilter: ["data-theme"]
|
|
7820
|
-
});
|
|
7821
|
-
const onStorage = (e) => {
|
|
7822
|
-
if (e.key === "olun-theme") sync();
|
|
7823
|
-
};
|
|
7824
|
-
window.addEventListener("storage", onStorage);
|
|
7825
|
-
return () => {
|
|
7826
|
-
observer.disconnect();
|
|
7827
|
-
window.removeEventListener("storage", onStorage);
|
|
7828
|
-
};
|
|
7829
|
-
}, []);
|
|
7830
|
-
function toggle() {
|
|
7831
|
-
const next = theme === "dark" ? "light" : "dark";
|
|
7832
|
-
setTheme(next);
|
|
7833
|
-
document.documentElement.setAttribute("data-theme", next);
|
|
7834
|
-
localStorage.setItem("olun-theme", next);
|
|
7835
|
-
}
|
|
7836
|
-
const Icon = theme === "dark" ? Moon : Sun;
|
|
7837
|
-
return /* @__PURE__ */ jsx52(
|
|
8228
|
+
const { resolvedTheme, toggleTheme } = useOlunTheme();
|
|
8229
|
+
const Icon = resolvedTheme === "dark" ? Moon : Sun;
|
|
8230
|
+
return /* @__PURE__ */ jsx53(
|
|
7838
8231
|
"button",
|
|
7839
8232
|
{
|
|
7840
|
-
onClick:
|
|
8233
|
+
onClick: () => toggleTheme(),
|
|
7841
8234
|
className: cn(
|
|
7842
8235
|
"inline-flex size-[36px] items-center justify-center rounded-sm",
|
|
7843
8236
|
"text-text-secondary hover:bg-[var(--action-hover)] hover:text-foreground",
|
|
@@ -7845,54 +8238,21 @@ function ThemeToggle({ className }) {
|
|
|
7845
8238
|
className
|
|
7846
8239
|
),
|
|
7847
8240
|
"aria-label": "\uD14C\uB9C8 \uC804\uD658",
|
|
7848
|
-
title:
|
|
7849
|
-
children: /* @__PURE__ */
|
|
8241
|
+
title: resolvedTheme === "dark" ? "\uB77C\uC774\uD2B8 \uBAA8\uB4DC\uB85C" : "\uB2E4\uD06C \uBAA8\uB4DC\uB85C",
|
|
8242
|
+
children: /* @__PURE__ */ jsx53(Icon, { className: "size-[18px]", strokeWidth: 1.75 })
|
|
7850
8243
|
}
|
|
7851
8244
|
);
|
|
7852
8245
|
}
|
|
7853
8246
|
|
|
7854
8247
|
// src/components/branding/theme-fab.tsx
|
|
7855
|
-
import { useEffect as useEffect5, useState as useState14 } from "react";
|
|
7856
8248
|
import { Sun as Sun2, Moon as Moon2 } from "lucide-react";
|
|
7857
|
-
import { jsx as
|
|
8249
|
+
import { jsx as jsx54 } from "react/jsx-runtime";
|
|
7858
8250
|
function ThemeFab({ variant = "square" } = {}) {
|
|
7859
|
-
const
|
|
7860
|
-
const [mounted, setMounted] = useState14(false);
|
|
7861
|
-
useEffect5(() => {
|
|
7862
|
-
setMounted(true);
|
|
7863
|
-
const resolve = () => {
|
|
7864
|
-
const attr = document.documentElement.getAttribute("data-theme");
|
|
7865
|
-
if (attr === "light" || attr === "dark") return attr;
|
|
7866
|
-
const stored = localStorage.getItem("olun-theme");
|
|
7867
|
-
if (stored === "light" || stored === "dark") return stored;
|
|
7868
|
-
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
7869
|
-
};
|
|
7870
|
-
setTheme(resolve());
|
|
7871
|
-
const sync = () => setTheme(resolve());
|
|
7872
|
-
const observer = new MutationObserver(sync);
|
|
7873
|
-
observer.observe(document.documentElement, {
|
|
7874
|
-
attributes: true,
|
|
7875
|
-
attributeFilter: ["data-theme"]
|
|
7876
|
-
});
|
|
7877
|
-
const onStorage = (e) => {
|
|
7878
|
-
if (e.key === "olun-theme") sync();
|
|
7879
|
-
};
|
|
7880
|
-
window.addEventListener("storage", onStorage);
|
|
7881
|
-
return () => {
|
|
7882
|
-
observer.disconnect();
|
|
7883
|
-
window.removeEventListener("storage", onStorage);
|
|
7884
|
-
};
|
|
7885
|
-
}, []);
|
|
7886
|
-
function toggle() {
|
|
7887
|
-
const next = theme === "dark" ? "light" : "dark";
|
|
7888
|
-
setTheme(next);
|
|
7889
|
-
document.documentElement.setAttribute("data-theme", next);
|
|
7890
|
-
localStorage.setItem("olun-theme", next);
|
|
7891
|
-
}
|
|
8251
|
+
const { mounted, resolvedTheme, toggleTheme } = useOlunTheme();
|
|
7892
8252
|
if (!mounted) {
|
|
7893
8253
|
return null;
|
|
7894
8254
|
}
|
|
7895
|
-
const Icon =
|
|
8255
|
+
const Icon = resolvedTheme === "dark" ? Moon2 : Sun2;
|
|
7896
8256
|
const isCircle = variant === "circle";
|
|
7897
8257
|
const baseStyle = {
|
|
7898
8258
|
position: "fixed",
|
|
@@ -7926,13 +8286,13 @@ function ThemeFab({ variant = "square" } = {}) {
|
|
|
7926
8286
|
WebkitBackdropFilter: "blur(16px)",
|
|
7927
8287
|
zIndex: 9999
|
|
7928
8288
|
};
|
|
7929
|
-
return /* @__PURE__ */
|
|
8289
|
+
return /* @__PURE__ */ jsx54(
|
|
7930
8290
|
"button",
|
|
7931
8291
|
{
|
|
7932
|
-
onClick:
|
|
8292
|
+
onClick: () => toggleTheme(),
|
|
7933
8293
|
"aria-label": "\uD14C\uB9C8 \uC804\uD658",
|
|
7934
|
-
"aria-pressed":
|
|
7935
|
-
title:
|
|
8294
|
+
"aria-pressed": resolvedTheme === "light",
|
|
8295
|
+
title: resolvedTheme === "dark" ? "\uB77C\uC774\uD2B8 \uBAA8\uB4DC\uB85C" : "\uB2E4\uD06C \uBAA8\uB4DC\uB85C",
|
|
7936
8296
|
className: "transition-colors motion-reduce:transition-none",
|
|
7937
8297
|
style: { ...baseStyle, ...isCircle ? circleStyle : squareStyle },
|
|
7938
8298
|
onMouseEnter: (e) => {
|
|
@@ -7951,7 +8311,7 @@ function ThemeFab({ variant = "square" } = {}) {
|
|
|
7951
8311
|
e.currentTarget.style.color = "var(--text-secondary)";
|
|
7952
8312
|
}
|
|
7953
8313
|
},
|
|
7954
|
-
children: /* @__PURE__ */
|
|
8314
|
+
children: /* @__PURE__ */ jsx54(
|
|
7955
8315
|
Icon,
|
|
7956
8316
|
{
|
|
7957
8317
|
size: isCircle ? 22 : 16,
|
|
@@ -7963,7 +8323,7 @@ function ThemeFab({ variant = "square" } = {}) {
|
|
|
7963
8323
|
}
|
|
7964
8324
|
|
|
7965
8325
|
// src/components/logos/organization-logo.tsx
|
|
7966
|
-
import { jsx as
|
|
8326
|
+
import { jsx as jsx55 } from "react/jsx-runtime";
|
|
7967
8327
|
var organizationLogoRegistry = {
|
|
7968
8328
|
jbnu: {
|
|
7969
8329
|
label: "\uC804\uBD81\uB300\uD559\uAD50 \uC5E0\uBE14\uB7FC",
|
|
@@ -7992,7 +8352,7 @@ function OrganizationLogo({
|
|
|
7992
8352
|
}) {
|
|
7993
8353
|
const asset = organizationLogoRegistry[organization];
|
|
7994
8354
|
const isDecorative = ariaHidden === true || ariaHidden === "true";
|
|
7995
|
-
return /* @__PURE__ */
|
|
8355
|
+
return /* @__PURE__ */ jsx55(
|
|
7996
8356
|
"svg",
|
|
7997
8357
|
{
|
|
7998
8358
|
viewBox: asset.viewBox,
|
|
@@ -8010,7 +8370,7 @@ function OrganizationLogo({
|
|
|
8010
8370
|
...style
|
|
8011
8371
|
},
|
|
8012
8372
|
...props,
|
|
8013
|
-
children: asset.paths.map((path) => /* @__PURE__ */
|
|
8373
|
+
children: asset.paths.map((path) => /* @__PURE__ */ jsx55("path", { d: path }, path))
|
|
8014
8374
|
}
|
|
8015
8375
|
);
|
|
8016
8376
|
}
|
|
@@ -8127,6 +8487,7 @@ export {
|
|
|
8127
8487
|
TooltipContent,
|
|
8128
8488
|
TooltipProvider,
|
|
8129
8489
|
TooltipTrigger,
|
|
8490
|
+
TreeView,
|
|
8130
8491
|
TwoTierBadge,
|
|
8131
8492
|
WizardLayout,
|
|
8132
8493
|
actionFooterStatusVariants,
|
|
@@ -8160,6 +8521,7 @@ export {
|
|
|
8160
8521
|
formFieldLabelClassName,
|
|
8161
8522
|
formFieldRequiredClassName,
|
|
8162
8523
|
formFieldVariants,
|
|
8524
|
+
getOlunThemeState,
|
|
8163
8525
|
getToastDuration,
|
|
8164
8526
|
iconButtonBadgeVariants,
|
|
8165
8527
|
iconButtonVariants,
|
|
@@ -8183,6 +8545,7 @@ export {
|
|
|
8183
8545
|
searchFieldVariants,
|
|
8184
8546
|
selectMessageVariants,
|
|
8185
8547
|
selectVariants,
|
|
8548
|
+
setOlunTheme,
|
|
8186
8549
|
skeletonVariants,
|
|
8187
8550
|
spinnerVariants,
|
|
8188
8551
|
statusBadgeDotVariants,
|
|
@@ -8196,7 +8559,10 @@ export {
|
|
|
8196
8559
|
toast,
|
|
8197
8560
|
toastVariants,
|
|
8198
8561
|
toastViewportVariants,
|
|
8562
|
+
treeItemRowVariants,
|
|
8563
|
+
treeViewVariants,
|
|
8199
8564
|
twoTierBadgeLineVariants,
|
|
8200
8565
|
twoTierBadgeVariants,
|
|
8566
|
+
useOlunTheme,
|
|
8201
8567
|
usePermission
|
|
8202
8568
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@olundot/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "OLUN. shared React UI components built on OLUN design tokens",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"homepage": "https://olun-design-system.vercel.app",
|
|
15
15
|
"repository": {
|
|
16
16
|
"type": "git",
|
|
17
|
-
"url": "https://github.com/
|
|
17
|
+
"url": "https://github.com/olundot/olun-design-system",
|
|
18
18
|
"directory": "packages/ui"
|
|
19
19
|
},
|
|
20
20
|
"type": "module",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"lucide-react": "^1.8.0",
|
|
45
45
|
"radix-ui": "^1.4.3",
|
|
46
46
|
"tailwind-merge": "^3.5.0",
|
|
47
|
-
"@olundot/tokens": "^0.
|
|
47
|
+
"@olundot/tokens": "^0.10.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"react": ">=18",
|