@minimalstuff/ui 5.2.0 → 5.3.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/README.md +24 -0
- package/dist/index.d.ts +95 -4
- package/dist/minimalstuff-ui.css +1 -1
- package/dist/minimalstuff-ui.js +1610 -1124
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -59,6 +59,28 @@ import { THEME_INIT_SCRIPT } from '@minimalstuff/ui';
|
|
|
59
59
|
</head>;
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
+
#### Status bar and `theme-color`
|
|
63
|
+
|
|
64
|
+
To make the browser or installed PWA status bar follow the theme, add a `theme-color` meta with a `data-light` and a `data-dark` color. `THEME_INIT_SCRIPT` and `useThemeStore` copy the matching one into `content` every time the theme is applied (init, `setTheme`, OS change while on `system`, another tab). A meta without both attributes is left alone, and without such a meta nothing changes.
|
|
65
|
+
|
|
66
|
+
```html
|
|
67
|
+
<head>
|
|
68
|
+
<meta
|
|
69
|
+
name="theme-color"
|
|
70
|
+
content="#ffffff"
|
|
71
|
+
data-light="#ffffff"
|
|
72
|
+
data-dark="#0b0b0b"
|
|
73
|
+
/>
|
|
74
|
+
<script>
|
|
75
|
+
/* THEME_INIT_SCRIPT */
|
|
76
|
+
</script>
|
|
77
|
+
</head>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
- Put the meta before the script in `<head>`.
|
|
81
|
+
- Remove any other `theme-color` meta, notably ones with a `media` attribute: the first matching one wins.
|
|
82
|
+
- Keep the meta in the static HTML. A meta recreated by a `<head>` manager (Inertia `<Head>`, react-helmet) is not resynced.
|
|
83
|
+
|
|
62
84
|
With a strict Content-Security-Policy, either add the script's SHA-256 hash to `script-src` (`'sha256-<hash>'`), or write `THEME_INIT_SCRIPT` to a file at build time and load it with `<script src>`. Compute the hash with:
|
|
63
85
|
|
|
64
86
|
```bash
|
|
@@ -88,6 +110,8 @@ pnpm verify
|
|
|
88
110
|
|
|
89
111
|
`pnpm test:stories` runs the stories in a real browser and needs Playwright's Chromium once: `pnpm exec playwright install chromium`.
|
|
90
112
|
|
|
113
|
+
To change the library from a project that uses it, see [docs/developing_from_a_consumer.md](docs/developing_from_a_consumer.md).
|
|
114
|
+
|
|
91
115
|
Releases follow [docs/releasing.md](docs/releasing.md). Changes are listed in the [CHANGELOG](CHANGELOG.md).
|
|
92
116
|
|
|
93
117
|
## Report a bug
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { ReactElement } from 'react';
|
|
|
9
9
|
import { ReactNode } from 'react';
|
|
10
10
|
import { ReactPortal } from 'react';
|
|
11
11
|
import { Ref } from 'react';
|
|
12
|
+
import { RefCallback } from 'react';
|
|
12
13
|
import { RefObject } from 'react';
|
|
13
14
|
import { StoreApi } from 'zustand';
|
|
14
15
|
import { UseBoundStore } from 'zustand';
|
|
@@ -250,7 +251,7 @@ export declare interface ClientOnlyProps extends React.PropsWithChildren {
|
|
|
250
251
|
fallback?: React.ReactNode;
|
|
251
252
|
}
|
|
252
253
|
|
|
253
|
-
export declare function Combobox({ options, filterOption, label, description, 'aria-label': ariaLabel, error, placeholder, noResultsText, clearLabel, radius, unstyled, disabled, required, className, wrapperClassName, value, defaultValue, onChange, id, }: Readonly<ComboboxProps>): JSX.Element;
|
|
254
|
+
export declare function Combobox({ options, filterOption, label, description, 'aria-label': ariaLabel, error, placeholder, noResultsText, clearLabel, radius, unstyled, disabled, required, className, wrapperClassName, dropdownClassName, optionLabelOverflow, value, defaultValue, onChange, id, }: Readonly<ComboboxProps>): JSX.Element;
|
|
254
255
|
|
|
255
256
|
export declare interface ComboboxOption {
|
|
256
257
|
value: string;
|
|
@@ -258,6 +259,7 @@ export declare interface ComboboxOption {
|
|
|
258
259
|
}
|
|
259
260
|
|
|
260
261
|
export declare interface ComboboxProps {
|
|
262
|
+
/** Long lists render faster when the array keeps the same identity between renders (memoize it), and when `optionLabelOverflow` is not `'wrap'`. */
|
|
261
263
|
options: readonly ComboboxOption[];
|
|
262
264
|
/**
|
|
263
265
|
* Decides whether an option matches the text typed in the field. Replaces
|
|
@@ -278,6 +280,19 @@ export declare interface ComboboxProps {
|
|
|
278
280
|
required?: boolean;
|
|
279
281
|
className?: string;
|
|
280
282
|
wrapperClassName?: string;
|
|
283
|
+
/**
|
|
284
|
+
* Classes for the dropdown panel, applied last so they can override its
|
|
285
|
+
* defaults, for example a width wider than the field when option labels are
|
|
286
|
+
* long (`w-96`, `min-w-80`). The panel is kept inside the viewport.
|
|
287
|
+
*/
|
|
288
|
+
dropdownClassName?: string;
|
|
289
|
+
/**
|
|
290
|
+
* How option labels that do not fit on one line are shown.
|
|
291
|
+
* - `'wrap'` (default): labels wrap; every option is rendered, so lists of thousands of options open slower.
|
|
292
|
+
* - `'wrap-active'`: labels are truncated with a native tooltip, except the active option, which wraps in full; long lists only render the visible options. Recommended for long lists.
|
|
293
|
+
* - `'truncate'`: every label is truncated with a native tooltip, the active one included; keyboard and zoom users can no longer read long labels (WCAG 1.4.10), use only for labels known to be short.
|
|
294
|
+
*/
|
|
295
|
+
optionLabelOverflow?: OptionLabelOverflow;
|
|
281
296
|
value?: string;
|
|
282
297
|
defaultValue?: string;
|
|
283
298
|
onChange?: (value: string) => void;
|
|
@@ -527,6 +542,15 @@ export declare interface KbdProps {
|
|
|
527
542
|
|
|
528
543
|
export declare type KbdSize = ControlSize;
|
|
529
544
|
|
|
545
|
+
export declare type KeyboardScrollableProps = {
|
|
546
|
+
ref: RefCallback<HTMLElement>;
|
|
547
|
+
tabIndex?: 0;
|
|
548
|
+
role?: 'region';
|
|
549
|
+
'aria-labelledby'?: string;
|
|
550
|
+
'aria-label'?: string;
|
|
551
|
+
'data-keyboard-scroll-region'?: true;
|
|
552
|
+
};
|
|
553
|
+
|
|
530
554
|
/**
|
|
531
555
|
* A dropdown anchored to `trigger` (e.g. a kebab `IconButton`): click to
|
|
532
556
|
* open, click again/click outside/Escape to close, or ArrowDown/ArrowUp on
|
|
@@ -725,11 +749,15 @@ export declare interface ModalFooterProps {
|
|
|
725
749
|
|
|
726
750
|
export declare type ModalHeight = 'auto' | 'fixed';
|
|
727
751
|
|
|
752
|
+
export declare type ModalPhoneLayout = 'center' | 'sheet';
|
|
753
|
+
|
|
728
754
|
export declare type ModalProps = {
|
|
729
755
|
children: ModalRenderProp;
|
|
730
756
|
size?: ModalSize;
|
|
731
757
|
/** `fixed` gives the dialog a stable height so it does not jump when content changes; the content area stops scrolling and a child with `flex-1 min-h-0 overflow-y-auto` scrolls on its own. */
|
|
732
758
|
height?: ModalHeight;
|
|
759
|
+
/** `sheet` turns the dialog into a bottom sheet below the `sm` breakpoint (full width, flush with the bottom edge, only the top corners rounded, at most 85svh tall, bottom padding clearing the safe area, sliding up on open and down on close; plain fade under reduced motion). From `sm` up it renders as `center`. With `height="fixed"` the sheet is 85svh tall on phones. `unstyled` keeps the sheet position and size but drops radius and the slide. */
|
|
760
|
+
phoneLayout?: ModalPhoneLayout;
|
|
733
761
|
radius?: Radius;
|
|
734
762
|
className?: string;
|
|
735
763
|
/** Applied to the `role=dialog` element. A `transform`, `filter` or `backdrop-filter` on the open panel makes it the containing block of `position: fixed` descendants, so Menus opened inside it get clipped. Keep these off the open state. Carries `data-state="open"` or `"closed"`, so `data-[state=closed]:` variants can animate it. Put your own transitions behind `motion-reduce:` variants, since `unstyled` drops the library's reduced-motion handling along with its animations. */
|
|
@@ -759,7 +787,7 @@ export declare type ModalSize = 'sm' | 'md' | 'lg' | 'xl';
|
|
|
759
787
|
* pills opens a searchable dropdown of checkbox-style options. Selecting an
|
|
760
788
|
* option never closes the dropdown, unlike the single-select `Combobox`.
|
|
761
789
|
*/
|
|
762
|
-
export declare function MultiCombobox({ options, filterOption, label, description, 'aria-label': ariaLabel, error, placeholder, searchPlaceholder, noResultsText, clearLabel, clearText, maxSelectedValues, isOptionDisabled, maxDisplayedValues, formatOverflowCount, formatSelectedCount, formatMaxSelectedHint, radius, unstyled, disabled, required, className, wrapperClassName, values, defaultValues, onChange, onDropdownClose, id, }: Readonly<MultiComboboxProps>): ReactElement;
|
|
790
|
+
export declare function MultiCombobox({ options, filterOption, label, description, 'aria-label': ariaLabel, error, placeholder, searchPlaceholder, noResultsText, clearLabel, clearText, maxSelectedValues, isOptionDisabled, maxDisplayedValues, formatOverflowCount, formatSelectedCount, formatMaxSelectedHint, radius, unstyled, disabled, required, className, wrapperClassName, dropdownClassName, optionLabelOverflow, values, defaultValues, onChange, onDropdownClose, id, }: Readonly<MultiComboboxProps>): ReactElement;
|
|
763
791
|
|
|
764
792
|
export declare interface MultiComboboxOption {
|
|
765
793
|
value: string;
|
|
@@ -767,6 +795,7 @@ export declare interface MultiComboboxOption {
|
|
|
767
795
|
}
|
|
768
796
|
|
|
769
797
|
export declare interface MultiComboboxProps {
|
|
798
|
+
/** Long lists render faster when the array keeps the same identity between renders (memoize it), and when `optionLabelOverflow` is not `'wrap'`. */
|
|
770
799
|
options: readonly MultiComboboxOption[];
|
|
771
800
|
/**
|
|
772
801
|
* Decides whether an option matches the text typed in the search field.
|
|
@@ -795,6 +824,19 @@ export declare interface MultiComboboxProps {
|
|
|
795
824
|
required?: boolean;
|
|
796
825
|
className?: string;
|
|
797
826
|
wrapperClassName?: string;
|
|
827
|
+
/**
|
|
828
|
+
* Classes for the dropdown panel, applied last so they can override its
|
|
829
|
+
* defaults, for example a width wider than the field when option labels are
|
|
830
|
+
* long (`w-96`, `min-w-80`). The panel is kept inside the viewport.
|
|
831
|
+
*/
|
|
832
|
+
dropdownClassName?: string;
|
|
833
|
+
/**
|
|
834
|
+
* How option labels that do not fit on one line are shown.
|
|
835
|
+
* - `'wrap'` (default): labels wrap; every option is rendered, so lists of thousands of options open slower.
|
|
836
|
+
* - `'wrap-active'`: labels are truncated with a native tooltip, except the active option, which wraps in full; long lists only render the visible options. Recommended for long lists.
|
|
837
|
+
* - `'truncate'`: every label is truncated with a native tooltip, the active one included; keyboard and zoom users can no longer read long labels (WCAG 1.4.10), use only for labels known to be short.
|
|
838
|
+
*/
|
|
839
|
+
optionLabelOverflow?: OptionLabelOverflow;
|
|
798
840
|
values?: readonly string[];
|
|
799
841
|
defaultValues?: readonly string[];
|
|
800
842
|
onChange?: (values: readonly string[]) => void;
|
|
@@ -808,6 +850,8 @@ export declare interface MultiComboboxProps {
|
|
|
808
850
|
id?: string;
|
|
809
851
|
}
|
|
810
852
|
|
|
853
|
+
export declare type OptionLabelOverflow = 'wrap' | 'wrap-active' | 'truncate';
|
|
854
|
+
|
|
811
855
|
export declare type RadioOption = ChoiceOption;
|
|
812
856
|
|
|
813
857
|
export declare function RadioOptions({ options, orientation, value, defaultValue, onChange, name: nameProp, label, error, required, disabled, radius, unstyled, className, wrapperClassName, id, 'aria-describedby': callerDescribedBy, ...props }: Readonly<RadioOptionsProps>): JSX.Element;
|
|
@@ -955,7 +999,7 @@ export declare function splitIntoHighlightSegments(
|
|
|
955
999
|
return segments;
|
|
956
1000
|
}
|
|
957
1001
|
|
|
958
|
-
export declare function Switch({ label, description, error, unstyled, fullWidth, className, wrapperClassName, checked, defaultChecked, onChange, id, 'aria-describedby': callerDescribedBy, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, ...props }: Readonly<SwitchProps>): JSX.Element;
|
|
1002
|
+
export declare function Switch({ label, description, error, unstyled, fullWidth, className, wrapperClassName, checked, defaultChecked, onChange, id, 'aria-describedby': callerDescribedBy, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, ref, ...props }: Readonly<SwitchProps>): JSX.Element;
|
|
959
1003
|
|
|
960
1004
|
export declare interface SwitchProps extends Omit<ComponentPropsWithRef<'input'>, 'type' | 'className'> {
|
|
961
1005
|
label?: string | ReactNode;
|
|
@@ -1052,7 +1096,10 @@ export declare type Theme = 'light' | 'dark' | 'system';
|
|
|
1052
1096
|
* first paint, so a dark-mode visitor never sees a light flash. It mirrors
|
|
1053
1097
|
* `useThemeStore`: the `dark` class is set for a stored `dark`, or for a
|
|
1054
1098
|
* stored `system` (or a missing, invalid or unreadable value) while the OS
|
|
1055
|
-
* prefers dark.
|
|
1099
|
+
* prefers dark. Every `<meta name="theme-color" data-light="..." data-dark="...">`
|
|
1100
|
+
* also gets its `content` set from `data-dark` or `data-light`; it must come
|
|
1101
|
+
* before this script in `<head>`. Inline the script in `<head>` before the
|
|
1102
|
+
* stylesheet.
|
|
1056
1103
|
*/
|
|
1057
1104
|
export declare const THEME_INIT_SCRIPT: string;
|
|
1058
1105
|
|
|
@@ -1255,6 +1302,43 @@ export declare const useGlobalHotkeysStore: UseBoundStore<StoreApi<GlobalHotkeys
|
|
|
1255
1302
|
|
|
1256
1303
|
export declare const useIsClient: () => boolean;
|
|
1257
1304
|
|
|
1305
|
+
/**
|
|
1306
|
+
* Makes a scroll container reachable by keyboard. When the container overflows
|
|
1307
|
+
* and holds nothing tabbable, `scrollableProps` gives it a tab stop, a
|
|
1308
|
+
* `region` role and an accessible name, and `focusClassName` a visible focus
|
|
1309
|
+
* outline. Otherwise only the `ref` is returned. Re-evaluated on resize and on
|
|
1310
|
+
* content changes; a container mounted after the first render is observed too.
|
|
1311
|
+
*
|
|
1312
|
+
* To combine `scrollableProps.ref` with your own ref, use a ref callback that
|
|
1313
|
+
* calls both.
|
|
1314
|
+
*
|
|
1315
|
+
* @example
|
|
1316
|
+
* const { scrollableProps, focusClassName } = useKeyboardScrollable({
|
|
1317
|
+
* label: 'Release notes',
|
|
1318
|
+
* })
|
|
1319
|
+
* return (
|
|
1320
|
+
* <div {...scrollableProps} className={cn('h-64 overflow-y-auto', focusClassName)}>
|
|
1321
|
+
* {content}
|
|
1322
|
+
* </div>
|
|
1323
|
+
* )
|
|
1324
|
+
*/
|
|
1325
|
+
export declare function useKeyboardScrollable(options: Readonly<UseKeyboardScrollableOptions>): UseKeyboardScrollableReturn;
|
|
1326
|
+
|
|
1327
|
+
/** A scroll region must be named, exactly once: by `label` or by `labelledBy`. */
|
|
1328
|
+
export declare type UseKeyboardScrollableOptions = {
|
|
1329
|
+
label: string;
|
|
1330
|
+
labelledBy?: never;
|
|
1331
|
+
} | {
|
|
1332
|
+
labelledBy: string;
|
|
1333
|
+
label?: never;
|
|
1334
|
+
};
|
|
1335
|
+
|
|
1336
|
+
export declare type UseKeyboardScrollableReturn = {
|
|
1337
|
+
scrollableProps: KeyboardScrollableProps;
|
|
1338
|
+
/** Focus outline classes to add to the container's `className`; `undefined` when the container is not a tab stop. */
|
|
1339
|
+
focusClassName: string | undefined;
|
|
1340
|
+
};
|
|
1341
|
+
|
|
1258
1342
|
export declare const useThemeStore: UseBoundStore<StoreApi<ThemeStore>>;
|
|
1259
1343
|
|
|
1260
1344
|
/** Wraps `Component` so it only renders once the client has mounted. */
|
|
@@ -1268,3 +1352,10 @@ declare module 'vitest' {
|
|
|
1268
1352
|
interface Assertion<T = unknown> extends AxeMatchers {}
|
|
1269
1353
|
interface AsymmetricMatchersContaining extends AxeMatchers {}
|
|
1270
1354
|
}
|
|
1355
|
+
|
|
1356
|
+
declare module 'vitest/browser' {
|
|
1357
|
+
interface BrowserCommands {
|
|
1358
|
+
/** Moves the real mouse cursor to a point outside the story document. */
|
|
1359
|
+
movePointerOutOfStory: () => Promise<void>;
|
|
1360
|
+
}
|
|
1361
|
+
}
|