@raxrai/stylelab-ui 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/dist/index.cjs +23 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +6 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.mjs +23 -12
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -49,6 +49,8 @@ export default function RootLayout({ children }) {
|
|
|
49
49
|
}
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
Optional props: `storageKey` (default `"stylelab-theme"`) for the localStorage key; `persistTheme` (default `true`). When `persistTheme={false}`, the provider does not read or write localStorage (theme is fixed to `defaultTheme` unless you change it in memory).
|
|
53
|
+
|
|
52
54
|
### 3. Use components
|
|
53
55
|
|
|
54
56
|
```tsx
|
|
@@ -191,6 +193,7 @@ Implements focus trap and scroll lock when open.
|
|
|
191
193
|
| `items` | `{ value, label, disabled? }[]` | — | Options |
|
|
192
194
|
| `value` / `defaultValue` | `string` | — | Controlled / initial |
|
|
193
195
|
| `onValueChange`| `(value: string) => void` | — | Selection callback |
|
|
196
|
+
| `disabled` | `boolean` | `false` | When true, does not open; trigger is non-interactive |
|
|
194
197
|
|
|
195
198
|
Keyboard: Enter/Space to open, Arrows to move, Enter to select, Escape to close.
|
|
196
199
|
|
|
@@ -212,6 +215,14 @@ import { cn, useFocusTrap, useClickOutside, useKeyboardNavigation, getNextListIn
|
|
|
212
215
|
|
|
213
216
|
## Changelog
|
|
214
217
|
|
|
218
|
+
### [0.3.2]
|
|
219
|
+
|
|
220
|
+
- **ThemeProvider** — Optional `persistTheme` prop (default `true`). When `persistTheme={false}`, the provider does not read from or write to localStorage: theme stays at `defaultTheme` unless changed in memory. Use for fixed-theme pages (e.g. landing, docs) or nested providers where only the inner tree should persist. Optional `storageKey` (default `"stylelab-theme"`) still applies when `persistTheme` is true.
|
|
221
|
+
|
|
222
|
+
### [0.3.1]
|
|
223
|
+
|
|
224
|
+
- **Dropdown** — `disabled` prop: when true, the dropdown does not open and the trigger is non-interactive (opacity, cursor, no popover). Use for empty or loading state.
|
|
225
|
+
|
|
215
226
|
### [0.2.0]
|
|
216
227
|
|
|
217
228
|
- **Navbar** — Optional responsive API: pass `logo`, `items` (`{ href, label }[]`), and `right` (e.g. theme switcher). On small screens nav links become a hamburger menu that opens as an overlay (no layout shift), with the theme’s navbar background; glass and clay use an opaque panel so content doesn’t show through. Desktop shows full nav row. Menu closes on link click or outside click. You can still pass `children` for a custom layout. Exported types: `NavbarItem`, `NavbarProps`.
|
package/dist/index.cjs
CHANGED
|
@@ -341,12 +341,14 @@ function getStoredTheme(storageKey, defaultTheme) {
|
|
|
341
341
|
function ThemeProvider({
|
|
342
342
|
children,
|
|
343
343
|
defaultTheme = "minimal",
|
|
344
|
-
storageKey = "stylelab-theme"
|
|
344
|
+
storageKey = "stylelab-theme",
|
|
345
|
+
persistTheme = true
|
|
345
346
|
}) {
|
|
346
347
|
const [theme, setThemeState] = (0, import_react.useState)(defaultTheme);
|
|
347
348
|
(0, import_react.useEffect)(() => {
|
|
349
|
+
if (!persistTheme) return;
|
|
348
350
|
setThemeState((prev) => getStoredTheme(storageKey, prev));
|
|
349
|
-
}, [storageKey]);
|
|
351
|
+
}, [storageKey, persistTheme]);
|
|
350
352
|
(0, import_react.useEffect)(() => {
|
|
351
353
|
if (typeof document !== "undefined") {
|
|
352
354
|
try {
|
|
@@ -361,12 +363,12 @@ function ThemeProvider({
|
|
|
361
363
|
if (typeof document !== "undefined") {
|
|
362
364
|
try {
|
|
363
365
|
document.documentElement.dataset.stylelabTheme = next;
|
|
364
|
-
localStorage.setItem(storageKey, next);
|
|
366
|
+
if (persistTheme) localStorage.setItem(storageKey, next);
|
|
365
367
|
} catch (_) {
|
|
366
368
|
}
|
|
367
369
|
}
|
|
368
370
|
},
|
|
369
|
-
[storageKey]
|
|
371
|
+
[storageKey, persistTheme]
|
|
370
372
|
);
|
|
371
373
|
const value = (0, import_react.useMemo)(
|
|
372
374
|
() => ({ theme, setTheme }),
|
|
@@ -4993,6 +4995,7 @@ function Dropdown({
|
|
|
4993
4995
|
value: controlledValue,
|
|
4994
4996
|
defaultValue,
|
|
4995
4997
|
onValueChange,
|
|
4998
|
+
disabled = false,
|
|
4996
4999
|
className,
|
|
4997
5000
|
style,
|
|
4998
5001
|
theme: themeProp,
|
|
@@ -5028,10 +5031,13 @@ function Dropdown({
|
|
|
5028
5031
|
});
|
|
5029
5032
|
}, []);
|
|
5030
5033
|
(0, import_react15.useLayoutEffect)(() => {
|
|
5031
|
-
if (open) {
|
|
5034
|
+
if (open && !disabled) {
|
|
5032
5035
|
updatePosition();
|
|
5033
5036
|
}
|
|
5034
|
-
}, [open, updatePosition, items.length]);
|
|
5037
|
+
}, [open, disabled, updatePosition, items.length]);
|
|
5038
|
+
(0, import_react15.useEffect)(() => {
|
|
5039
|
+
if (disabled && open) setOpen(false);
|
|
5040
|
+
}, [disabled, open]);
|
|
5035
5041
|
(0, import_react15.useEffect)(() => {
|
|
5036
5042
|
if (!open) return;
|
|
5037
5043
|
const onScrollOrResize = () => updatePosition();
|
|
@@ -5074,6 +5080,7 @@ function Dropdown({
|
|
|
5074
5080
|
}, [open, displayValue, items]);
|
|
5075
5081
|
const handleKeyDown = (0, import_react15.useCallback)(
|
|
5076
5082
|
(e) => {
|
|
5083
|
+
if (disabled) return;
|
|
5077
5084
|
if (!open) {
|
|
5078
5085
|
if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
|
|
5079
5086
|
e.preventDefault();
|
|
@@ -5106,15 +5113,17 @@ function Dropdown({
|
|
|
5106
5113
|
selectIndex(highlightedIndex);
|
|
5107
5114
|
}
|
|
5108
5115
|
},
|
|
5109
|
-
[open, highlightedIndex, items.length, selectIndex]
|
|
5116
|
+
[disabled, open, highlightedIndex, items.length, selectIndex]
|
|
5110
5117
|
);
|
|
5111
5118
|
const selectedLabel = displayValue ? items.find((i) => i.value === displayValue)?.label ?? displayValue : placeholder;
|
|
5112
5119
|
const defaultTrigger = /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
5113
5120
|
"span",
|
|
5114
5121
|
{
|
|
5115
5122
|
className: cn(
|
|
5116
|
-
"inline-flex min-w-[8rem]
|
|
5117
|
-
|
|
5123
|
+
"inline-flex min-w-[8rem] items-center justify-between rounded-lg border border-transparent px-3 py-2 text-sm font-medium transition-colors",
|
|
5124
|
+
!disabled && "cursor-pointer",
|
|
5125
|
+
!disabled && itemHighlightClass,
|
|
5126
|
+
disabled && "cursor-not-allowed opacity-60",
|
|
5118
5127
|
classNames.trigger
|
|
5119
5128
|
),
|
|
5120
5129
|
children: [
|
|
@@ -5124,7 +5133,7 @@ function Dropdown({
|
|
|
5124
5133
|
}
|
|
5125
5134
|
);
|
|
5126
5135
|
const trigger = triggerProp ?? defaultTrigger;
|
|
5127
|
-
const popover = open ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
5136
|
+
const popover = open && !disabled ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
5128
5137
|
"ul",
|
|
5129
5138
|
{
|
|
5130
5139
|
ref: listRef,
|
|
@@ -5170,11 +5179,13 @@ function Dropdown({
|
|
|
5170
5179
|
{
|
|
5171
5180
|
ref: triggerRef,
|
|
5172
5181
|
role: "button",
|
|
5173
|
-
tabIndex: 0,
|
|
5182
|
+
tabIndex: disabled ? -1 : 0,
|
|
5174
5183
|
"aria-haspopup": "listbox",
|
|
5175
5184
|
"aria-expanded": open,
|
|
5185
|
+
"aria-disabled": disabled,
|
|
5176
5186
|
"aria-activedescendant": open && items[highlightedIndex] ? `dropdown-option-${items[highlightedIndex].value}` : void 0,
|
|
5177
|
-
onClick: () => setOpen((o) => !o),
|
|
5187
|
+
onClick: () => !disabled && setOpen((o) => !o),
|
|
5188
|
+
className: disabled ? "cursor-not-allowed" : void 0,
|
|
5178
5189
|
children: trigger
|
|
5179
5190
|
}
|
|
5180
5191
|
),
|