@herongxhr/ui-react 0.0.0-stage → 0.1.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.
@@ -0,0 +1 @@
1
+ export declare function withClass<State>(base: string, className?: string | ((state: State) => string | undefined)): string | ((state: State) => string);
@@ -0,0 +1,5 @@
1
+ export function withClass(base, className) {
2
+ return typeof className === 'function'
3
+ ? (state) => [base, className(state)].filter(Boolean).join(' ')
4
+ : [base, className].filter(Boolean).join(' ');
5
+ }
@@ -0,0 +1,22 @@
1
+ import type { ComponentProps } from 'react';
2
+ import { Dialog as BaseDialog } from '@base-ui/react/dialog';
3
+ export declare const DialogRoot: <Payload>(props: BaseDialog.Root.Props<Payload>) => import("react").JSX.Element;
4
+ export declare const DialogTrigger: BaseDialog.Trigger;
5
+ export declare const DialogPortal: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogPortalProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
6
+ export declare const DialogClose: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogCloseProps, "ref"> & import("react").RefAttributes<HTMLButtonElement>>;
7
+ export declare const DialogTitle: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogTitleProps, "ref"> & import("react").RefAttributes<HTMLHeadingElement>>;
8
+ export declare const DialogDescription: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogDescriptionProps, "ref"> & import("react").RefAttributes<HTMLParagraphElement>>;
9
+ export declare function DialogBackdrop({ className, ...props }: ComponentProps<typeof BaseDialog.Backdrop>): import("react").JSX.Element;
10
+ export declare function DialogViewport({ className, ...props }: ComponentProps<typeof BaseDialog.Viewport>): import("react").JSX.Element;
11
+ export declare function DialogPopup({ className, ...props }: ComponentProps<typeof BaseDialog.Popup>): import("react").JSX.Element;
12
+ export declare const Dialog: {
13
+ Root: <Payload>(props: BaseDialog.Root.Props<Payload>) => import("react").JSX.Element;
14
+ Trigger: BaseDialog.Trigger;
15
+ Portal: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogPortalProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
16
+ Close: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogCloseProps, "ref"> & import("react").RefAttributes<HTMLButtonElement>>;
17
+ Title: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogTitleProps, "ref"> & import("react").RefAttributes<HTMLHeadingElement>>;
18
+ Description: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogDescriptionProps, "ref"> & import("react").RefAttributes<HTMLParagraphElement>>;
19
+ Backdrop: typeof DialogBackdrop;
20
+ Viewport: typeof DialogViewport;
21
+ Popup: typeof DialogPopup;
22
+ };
package/dist/dialog.js ADDED
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Dialog as BaseDialog } from '@base-ui/react/dialog';
3
+ import { withClass } from './classes.js';
4
+ export const DialogRoot = BaseDialog.Root;
5
+ export const DialogTrigger = BaseDialog.Trigger;
6
+ export const DialogPortal = BaseDialog.Portal;
7
+ export const DialogClose = BaseDialog.Close;
8
+ export const DialogTitle = BaseDialog.Title;
9
+ export const DialogDescription = BaseDialog.Description;
10
+ export function DialogBackdrop({ className, ...props }) {
11
+ return _jsx(BaseDialog.Backdrop, { ...props, className: withClass('hrui-dialog-backdrop', className) });
12
+ }
13
+ export function DialogViewport({ className, ...props }) {
14
+ return _jsx(BaseDialog.Viewport, { ...props, className: withClass('hrui-dialog-viewport', className) });
15
+ }
16
+ export function DialogPopup({ className, ...props }) {
17
+ return _jsx(BaseDialog.Popup, { ...props, className: withClass('hrui-dialog-popup', className) });
18
+ }
19
+ export const Dialog = {
20
+ Root: DialogRoot, Trigger: DialogTrigger, Portal: DialogPortal, Close: DialogClose,
21
+ Title: DialogTitle, Description: DialogDescription, Backdrop: DialogBackdrop,
22
+ Viewport: DialogViewport, Popup: DialogPopup,
23
+ };
@@ -0,0 +1,4 @@
1
+ export * from './select.js';
2
+ export * from './dialog.js';
3
+ export * from './theme-select.js';
4
+ export { CSPProvider } from '@base-ui/react/csp-provider';
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from './select.js';
2
+ export * from './dialog.js';
3
+ export * from './theme-select.js';
4
+ export { CSPProvider } from '@base-ui/react/csp-provider';
@@ -0,0 +1,46 @@
1
+ import { type ComponentProps } from 'react';
2
+ import { Select as BaseSelect } from '@base-ui/react/select';
3
+ export declare const SelectRoot: typeof BaseSelect.Root;
4
+ export declare const SelectValue: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react/select").SelectValueProps, "ref"> & import("react").RefAttributes<HTMLSpanElement>>;
5
+ export declare const SelectPortal: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react/select").SelectPortalProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
6
+ export declare function SelectTrigger({ className, variant, ...props }: ComponentProps<typeof BaseSelect.Trigger> & {
7
+ variant?: 'field' | 'ghost';
8
+ }): import("react").JSX.Element;
9
+ export declare function SelectIcon({ className, ...props }: ComponentProps<typeof BaseSelect.Icon>): import("react").JSX.Element;
10
+ export declare function SelectPositioner({ className, ...props }: ComponentProps<typeof BaseSelect.Positioner>): import("react").JSX.Element;
11
+ export declare function SelectPopup({ className, ...props }: ComponentProps<typeof BaseSelect.Popup>): import("react").JSX.Element;
12
+ export declare function SelectList({ className, ...props }: ComponentProps<typeof BaseSelect.List>): import("react").JSX.Element;
13
+ export declare function SelectItem({ className, ...props }: ComponentProps<typeof BaseSelect.Item>): import("react").JSX.Element;
14
+ export declare function SelectItemText({ className, ...props }: ComponentProps<typeof BaseSelect.ItemText>): import("react").JSX.Element;
15
+ export declare function SelectItemIndicator({ className, ...props }: ComponentProps<typeof BaseSelect.ItemIndicator>): import("react").JSX.Element;
16
+ export declare const Select: {
17
+ Root: typeof BaseSelect.Root;
18
+ Trigger: typeof SelectTrigger;
19
+ Value: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react/select").SelectValueProps, "ref"> & import("react").RefAttributes<HTMLSpanElement>>;
20
+ Icon: typeof SelectIcon;
21
+ Portal: import("react").ForwardRefExoticComponent<Omit<import("@base-ui/react/select").SelectPortalProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
22
+ Positioner: typeof SelectPositioner;
23
+ Popup: typeof SelectPopup;
24
+ List: typeof SelectList;
25
+ Item: typeof SelectItem;
26
+ ItemText: typeof SelectItemText;
27
+ ItemIndicator: typeof SelectItemIndicator;
28
+ };
29
+ export interface Option {
30
+ value: string;
31
+ label: string;
32
+ disabled?: boolean;
33
+ }
34
+ export interface SelectControlProps {
35
+ label: string;
36
+ name?: string;
37
+ options: Option[];
38
+ value?: string;
39
+ defaultValue?: string;
40
+ onValueChange?: (value: string) => void;
41
+ required?: boolean;
42
+ disabled?: boolean;
43
+ className?: string;
44
+ labelledBy?: string;
45
+ }
46
+ export declare function SelectControl({ label, name, options, value, defaultValue, onValueChange, required, disabled, className, labelledBy }: SelectControlProps): import("react").JSX.Element;
package/dist/select.js ADDED
@@ -0,0 +1,41 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { Select as BaseSelect } from '@base-ui/react/select';
4
+ import { Check, ChevronDown } from 'lucide-react';
5
+ import { withClass } from './classes.js';
6
+ export const SelectRoot = BaseSelect.Root;
7
+ export const SelectValue = BaseSelect.Value;
8
+ export const SelectPortal = BaseSelect.Portal;
9
+ export function SelectTrigger({ className, variant = 'field', ...props }) {
10
+ return _jsx(BaseSelect.Trigger, { ...props, "data-variant": variant, className: withClass('hrui-select-trigger', className) });
11
+ }
12
+ export function SelectIcon({ className, ...props }) {
13
+ return _jsx(BaseSelect.Icon, { ...props, className: withClass('hrui-select-icon', className) });
14
+ }
15
+ export function SelectPositioner({ className, ...props }) {
16
+ return _jsx(BaseSelect.Positioner, { alignItemWithTrigger: false, side: "bottom", align: "start", sideOffset: 4, ...props, className: withClass('hrui-select-positioner', className) });
17
+ }
18
+ export function SelectPopup({ className, ...props }) {
19
+ return _jsx(BaseSelect.Popup, { ...props, className: withClass('hrui-select-popup', className) });
20
+ }
21
+ export function SelectList({ className, ...props }) {
22
+ return _jsx(BaseSelect.List, { ...props, className: withClass('hrui-select-list', className) });
23
+ }
24
+ export function SelectItem({ className, ...props }) {
25
+ return _jsx(BaseSelect.Item, { ...props, className: withClass('hrui-select-item', className) });
26
+ }
27
+ export function SelectItemText({ className, ...props }) {
28
+ return _jsx(BaseSelect.ItemText, { ...props, className: withClass('hrui-select-item-text', className) });
29
+ }
30
+ export function SelectItemIndicator({ className, ...props }) {
31
+ return _jsx(BaseSelect.ItemIndicator, { ...props, className: withClass('hrui-select-item-indicator', className) });
32
+ }
33
+ export const Select = {
34
+ Root: SelectRoot, Trigger: SelectTrigger, Value: SelectValue, Icon: SelectIcon,
35
+ Portal: SelectPortal, Positioner: SelectPositioner, Popup: SelectPopup,
36
+ List: SelectList, Item: SelectItem, ItemText: SelectItemText, ItemIndicator: SelectItemIndicator,
37
+ };
38
+ export function SelectControl({ label, name, options, value, defaultValue = '', onValueChange, required, disabled, className, labelledBy }) {
39
+ const [ownValue, setOwnValue] = useState(defaultValue);
40
+ return _jsxs(Select.Root, { items: options, name: name, value: value === undefined ? ownValue : value, required: required, disabled: disabled, onValueChange: (next) => { const nextValue = next ?? ''; setOwnValue(nextValue); onValueChange?.(nextValue); }, children: [_jsxs(Select.Trigger, { className: className, "aria-label": labelledBy ? undefined : label, "aria-labelledby": labelledBy, children: [_jsx(Select.Value, {}), _jsx(Select.Icon, { children: _jsx(ChevronDown, { "aria-hidden": "true" }) })] }), _jsx(Select.Portal, { children: _jsx(Select.Positioner, { children: _jsx(Select.Popup, { children: _jsx(Select.List, { children: options.map((option) => _jsxs(Select.Item, { value: option.value, label: option.label, disabled: option.disabled, children: [_jsx(Select.ItemText, { children: option.label }), _jsx(Select.ItemIndicator, { children: _jsx(Check, { "aria-hidden": "true" }) })] }, option.value)) }) }) }) })] });
41
+ }
@@ -0,0 +1,139 @@
1
+ /* Import @herongxhr/design-tokens/tokens.css before this file and the app brand adapter. */
2
+ .hrui-select-trigger,
3
+ .hrui-select-popup,
4
+ .hrui-select-list,
5
+ .hrui-select-item,
6
+ .hrui-dialog-viewport,
7
+ .hrui-dialog-popup { box-sizing: border-box; }
8
+ .hrui-select-trigger {
9
+ appearance: none;
10
+ display: inline-flex;
11
+ align-items: center;
12
+ justify-content: space-between;
13
+ gap: var(--hr-ui-space-3);
14
+ min-height: var(--hr-ui-control-height);
15
+ min-width: 0;
16
+ max-width: 100%;
17
+ padding: var(--hr-ui-space-2) var(--hr-ui-space-3);
18
+ border: var(--hr-ui-border-width) solid var(--hr-ui-color-border-strong);
19
+ border-radius: var(--hr-ui-radius-small);
20
+ background: var(--hr-ui-color-surface);
21
+ color: var(--hr-ui-color-text);
22
+ font: inherit;
23
+ text-align: left;
24
+ cursor: pointer;
25
+ outline: none;
26
+ -webkit-tap-highlight-color: transparent;
27
+ transition: background-color var(--hr-ui-transition-fast), color var(--hr-ui-transition-fast), border-color var(--hr-ui-transition-fast);
28
+ }
29
+ .hrui-select-trigger:hover:not([data-disabled]),
30
+ .hrui-select-trigger[data-popup-open] { background: var(--hr-ui-color-surface-raised); border-color: var(--hr-ui-color-border-hover); }
31
+ .hrui-select-trigger:focus-visible { outline: var(--hr-ui-focus-width-subtle) solid var(--hr-ui-color-focus); outline-offset: var(--hr-ui-focus-offset); }
32
+ .hrui-select-trigger[data-disabled] { opacity: var(--hr-ui-opacity-disabled); cursor: not-allowed; }
33
+ .hrui-select-trigger>span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
34
+ .hrui-select-trigger[data-variant="ghost"] {
35
+ gap: var(--hr-ui-space-2);
36
+ border-color: transparent;
37
+ border-radius: var(--hr-ui-radius-medium);
38
+ background: transparent;
39
+ color: var(--hr-ui-color-muted);
40
+ font-size: var(--hr-ui-font-small);
41
+ line-height: var(--hr-ui-leading-heading);
42
+ white-space: nowrap;
43
+ }
44
+ .hrui-select-trigger[data-variant="ghost"]:hover:not([data-disabled]),
45
+ .hrui-select-trigger[data-variant="ghost"][data-popup-open] { color: var(--hr-ui-color-text); background: var(--hr-ui-color-surface-raised); }
46
+ .hrui-select-trigger[data-variant="ghost"]:hover:not([data-disabled]):not([data-popup-open]) { border-color: transparent; }
47
+ .hrui-select-trigger[data-variant="ghost"][data-popup-open] { border-color: var(--hr-ui-color-border); }
48
+ .hrui-select-trigger>svg { width: var(--hr-ui-icon-small); height: var(--hr-ui-icon-small); flex-shrink: 0; }
49
+ .hrui-select-icon { display: inline-flex; flex-shrink: 0; margin-left: auto; color: var(--hr-ui-color-subtle); transition: transform var(--hr-ui-transition-fast) var(--hr-ui-motion-easing-standard); }
50
+ .hrui-select-icon[data-popup-open] { transform: rotate(180deg); }
51
+ .hrui-select-icon svg,
52
+ .hrui-select-item-indicator svg { width: var(--hr-ui-icon-medium); height: var(--hr-ui-icon-medium); }
53
+ .hrui-select-trigger svg,
54
+ .hrui-select-item svg { stroke-width: var(--hr-ui-icon-stroke); }
55
+ .hrui-select-trigger[data-variant="ghost"] .hrui-select-icon svg { width: var(--hr-ui-icon-small); height: var(--hr-ui-icon-small); }
56
+ .hrui-select-positioner { z-index: var(--hr-ui-layer-popover); outline: none; }
57
+ .hrui-select-popup {
58
+ min-width: min(var(--anchor-width), var(--available-width));
59
+ width: max-content;
60
+ max-width: min(var(--available-width), var(--hr-ui-select-max-width));
61
+ max-height: var(--available-height);
62
+ padding: var(--hr-ui-space-1);
63
+ border: var(--hr-ui-border-width) solid var(--hr-ui-color-border);
64
+ border-radius: var(--hr-ui-radius-small);
65
+ background: var(--hr-ui-color-surface);
66
+ color: var(--hr-ui-color-text);
67
+ box-shadow: var(--hr-ui-shadow-popover);
68
+ font: var(--hr-ui-font-body)/var(--hr-ui-leading-body) var(--hr-ui-font-sans);
69
+ overflow: hidden;
70
+ outline: none;
71
+ transform-origin: var(--transform-origin);
72
+ opacity: 1;
73
+ transform: translateY(0) scale(1);
74
+ transition: opacity var(--hr-ui-transition-fast) var(--hr-ui-motion-easing-standard), transform var(--hr-ui-transition-normal) var(--hr-ui-motion-easing-standard);
75
+ }
76
+ .hrui-select-popup[data-starting-style],
77
+ .hrui-select-popup[data-ending-style] { opacity: 0; transform: translateY(calc(-1 * var(--hr-ui-space-1))) scale(var(--hr-ui-motion-scale-enter)); }
78
+ .hrui-select-popup[data-ending-style] { transition-duration: var(--hr-ui-transition-fast); }
79
+ .hrui-select-list {
80
+ max-height: max(0px, min(var(--hr-ui-select-max-height), calc(var(--available-height) - var(--hr-ui-space-1) * 2 - var(--hr-ui-border-width) * 2)));
81
+ overflow-y: auto;
82
+ overscroll-behavior: contain;
83
+ padding: 0;
84
+ margin: 0;
85
+ outline: none;
86
+ }
87
+ .hrui-select-item {
88
+ display: flex;
89
+ align-items: center;
90
+ gap: var(--hr-ui-space-3);
91
+ min-height: var(--hr-ui-control-height);
92
+ padding: var(--hr-ui-space-2) var(--hr-ui-space-3);
93
+ border-radius: var(--hr-ui-radius-small);
94
+ color: var(--hr-ui-color-text);
95
+ cursor: default;
96
+ user-select: none;
97
+ outline: none;
98
+ transition: background-color var(--hr-ui-transition-fast), color var(--hr-ui-transition-fast);
99
+ }
100
+ .hrui-select-item[data-highlighted] { color: var(--hr-ui-color-highlight-text); background: var(--hr-ui-color-highlight); }
101
+ .hrui-select-item[data-disabled] { color: var(--hr-ui-color-muted); opacity: var(--hr-ui-opacity-disabled); }
102
+ .hrui-select-item>svg { width: var(--hr-ui-icon-medium); height: var(--hr-ui-icon-medium); flex-shrink: 0; }
103
+ .hrui-select-item-text { flex: 1; min-width: 0; overflow-wrap: anywhere; }
104
+ .hrui-select-item-indicator { display: inline-flex; margin-left: auto; color: var(--hr-ui-color-accent); }
105
+ .hrui-theme-control { position: relative; }
106
+ .hrui-theme-control .hrui-select-trigger { width: var(--hr-ui-control-width-theme); }
107
+ .hrui-theme-popup { width: var(--hr-ui-popover-width-small); border-radius: var(--hr-ui-radius-medium); font-size: var(--hr-ui-font-small); line-height: var(--hr-ui-leading-heading); }
108
+ .hrui-theme-popup .hrui-select-item { color: var(--hr-ui-color-muted); }
109
+ .hrui-theme-popup .hrui-select-item[data-highlighted],
110
+ .hrui-theme-popup .hrui-select-item[data-selected] { color: var(--hr-ui-color-text); }
111
+ .hrui-theme-popup .hrui-select-item-indicator svg { width: var(--hr-ui-icon-small); height: var(--hr-ui-icon-small); }
112
+ .hrui-theme-status:not([data-warning]) { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
113
+ .hrui-theme-status[data-warning] {
114
+ position: absolute;
115
+ top: 100%;
116
+ right: 0;
117
+ z-index: var(--hr-ui-layer-popover);
118
+ width: max-content;
119
+ max-width: min(var(--hr-ui-warning-max-width), calc(100vw - var(--hr-ui-space-6) * 2));
120
+ padding: var(--hr-ui-space-2) var(--hr-ui-space-3);
121
+ border-radius: var(--hr-ui-radius-small);
122
+ background: var(--hr-ui-color-warning-bg);
123
+ color: var(--hr-ui-color-warning);
124
+ font-size: var(--hr-ui-font-caption);
125
+ }
126
+ .hrui-dialog-backdrop { position: fixed; inset: 0; z-index: var(--hr-ui-layer-backdrop); background: var(--hr-ui-color-scrim); transition: opacity var(--hr-ui-transition-fast) var(--hr-ui-motion-easing-standard); }
127
+ .hrui-dialog-backdrop[data-starting-style],
128
+ .hrui-dialog-backdrop[data-ending-style] { opacity: 0; }
129
+ .hrui-dialog-viewport { position: fixed; inset: 0; z-index: var(--hr-ui-layer-dialog); display: flex; align-items: center; justify-content: center; padding: var(--hr-ui-space-4); overflow: auto; overscroll-behavior: contain; }
130
+ .hrui-dialog-popup { display: flex; flex-direction: column; flex-shrink: 0; margin: 0; overflow: hidden; outline: none; transition: opacity var(--hr-ui-transition-fast) var(--hr-ui-motion-easing-standard), transform var(--hr-ui-transition-normal) var(--hr-ui-motion-easing-standard); }
131
+ .hrui-dialog-popup[data-starting-style],
132
+ .hrui-dialog-popup[data-ending-style] { opacity: 0; transform: translateY(var(--hr-ui-space-2)) scale(var(--hr-ui-motion-scale-enter)); }
133
+ .hrui-dialog-popup[data-ending-style] { transition-duration: var(--hr-ui-transition-fast); }
134
+ /* This viewport breakpoint matches the existing compact admin layout. */
135
+ @media (max-width: 760px) { .hrui-dialog-viewport { padding: var(--hr-ui-space-2); } }
136
+ @media (prefers-reduced-motion: reduce) {
137
+ .hrui-select-trigger, .hrui-select-icon, .hrui-select-popup, .hrui-select-item,
138
+ .hrui-dialog-backdrop, .hrui-dialog-popup { transition: none; }
139
+ }
@@ -0,0 +1,7 @@
1
+ export type ThemeChoice = 'system' | 'light' | 'dark';
2
+ export interface ThemeSelectProps {
3
+ storageKey?: string;
4
+ className?: string;
5
+ defaultTheme?: ThemeChoice;
6
+ }
7
+ export declare function ThemeSelect({ storageKey, className, defaultTheme }: ThemeSelectProps): import("react").JSX.Element;
@@ -0,0 +1,50 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useId, useState } from 'react';
3
+ import { Check, ChevronDown, Monitor, Moon, Sun } from 'lucide-react';
4
+ import { Select } from './select.js';
5
+ const themeItems = [
6
+ { value: 'system', label: '跟随系统', icon: Monitor },
7
+ { value: 'light', label: '浅色', icon: Sun },
8
+ { value: 'dark', label: '深色', icon: Moon },
9
+ ];
10
+ function isTheme(value) { return value === 'system' || value === 'light' || value === 'dark'; }
11
+ export function ThemeSelect({ storageKey = 'herong-theme', className, defaultTheme = 'system' }) {
12
+ // Stable initial markup keeps server rendering and the hydration render equal.
13
+ // Each application's pre-paint theme adapter remains the source of truth.
14
+ const [theme, setTheme] = useState(defaultTheme);
15
+ const [status, setStatus] = useState({ message: '', warning: false });
16
+ const statusId = useId();
17
+ const CurrentIcon = themeItems.find((item) => item.value === theme).icon;
18
+ useEffect(() => {
19
+ const initial = document.documentElement.dataset.theme;
20
+ setTheme(isTheme(initial) ? initial : defaultTheme);
21
+ let storage;
22
+ try {
23
+ storage = window.localStorage;
24
+ }
25
+ catch { /* Storage can be unavailable while theme switching still works. */ }
26
+ const onStorage = (event) => {
27
+ if ((storage && event.storageArea !== storage) || (event.key !== storageKey && event.key !== null))
28
+ return;
29
+ const next = isTheme(event.newValue) ? event.newValue : defaultTheme;
30
+ document.documentElement.dataset.theme = next;
31
+ setTheme(next);
32
+ };
33
+ window.addEventListener('storage', onStorage);
34
+ return () => window.removeEventListener('storage', onStorage);
35
+ }, [storageKey, defaultTheme]);
36
+ const changeTheme = (value) => {
37
+ if (!isTheme(value))
38
+ return;
39
+ document.documentElement.dataset.theme = value;
40
+ setTheme(value);
41
+ try {
42
+ localStorage.setItem(storageKey, value);
43
+ setStatus({ message: '外观已保存', warning: false });
44
+ }
45
+ catch {
46
+ setStatus({ message: '外观已切换,但当前浏览器无法保存偏好。', warning: true });
47
+ }
48
+ };
49
+ return _jsxs("div", { className: ['hrui-theme-control', className].filter(Boolean).join(' '), "data-theme-choice": theme, children: [_jsxs(Select.Root, { items: themeItems, value: theme, onValueChange: changeTheme, children: [_jsxs(Select.Trigger, { variant: "ghost", "aria-label": "\u9875\u9762\u5916\u89C2", "aria-describedby": statusId, children: [_jsx(CurrentIcon, { "aria-hidden": "true" }), _jsx(Select.Value, {}), _jsx(Select.Icon, { children: _jsx(ChevronDown, { "aria-hidden": "true" }) })] }), _jsx(Select.Portal, { children: _jsx(Select.Positioner, { align: "end", sideOffset: 8, children: _jsx(Select.Popup, { className: "hrui-theme-popup", children: _jsx(Select.List, { children: themeItems.map(({ value, label, icon: Icon }) => _jsxs(Select.Item, { value: value, children: [_jsx(Icon, { "aria-hidden": "true" }), _jsx(Select.ItemText, { children: label }), _jsx(Select.ItemIndicator, { children: _jsx(Check, { "aria-hidden": "true" }) })] }, value)) }) }) }) })] }), _jsx("span", { id: statusId, className: "hrui-theme-status", "data-warning": status.warning || undefined, role: "status", "aria-live": "polite", children: status.message })] });
50
+ }
package/package.json CHANGED
@@ -1,6 +1,46 @@
1
1
  {
2
2
  "name": "@herongxhr/ui-react",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.1",
4
+ "license": "UNLICENSED",
5
+ "publishConfig": {
6
+ "registry": "https://registry.npmjs.org/",
7
+ "access": "public"
8
+ },
9
+ "description": "Shared accessible Base UI components for Herong applications.",
10
+ "type": "module",
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "types": "./dist/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.js"
19
+ },
20
+ "./styles.css": "./dist/styles.css"
21
+ },
22
+ "sideEffects": [
23
+ "**/*.css"
24
+ ],
25
+ "dependencies": {
26
+ "@base-ui/react": "1.9.0",
27
+ "lucide-react": "1.72.0"
28
+ },
29
+ "peerDependencies": {
30
+ "@herongxhr/design-tokens": "0.1.0",
31
+ "react": "^19.3.0",
32
+ "react-dom": "^19.3.0"
33
+ },
34
+ "devDependencies": {
35
+ "@types/react": "19.3.0",
36
+ "@types/react-dom": "19.3.0",
37
+ "react": "19.3.1",
38
+ "react-dom": "19.3.1",
39
+ "typescript": "6.0.3",
40
+ "@herongxhr/design-tokens": "0.1.0"
41
+ },
42
+ "scripts": {
43
+ "build": "tsc -p tsconfig.json && node scripts/copy-styles.mjs",
44
+ "check": "tsc -p tsconfig.json --noEmit"
45
+ }
6
46
  }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.