@minimalstuff/ui 5.1.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 +114 -5
- package/dist/minimalstuff-ui.css +1 -1
- package/dist/minimalstuff-ui.js +1639 -1144
- 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;
|
|
@@ -370,8 +385,24 @@ export declare interface CopyButtonProps {
|
|
|
370
385
|
*/
|
|
371
386
|
export declare type DismissGuard = () => boolean | Promise<boolean>;
|
|
372
387
|
|
|
388
|
+
/**
|
|
389
|
+
* Asks the topmost open dialog (`Modal`, `ConfirmModal`, `Drawer`) to close,
|
|
390
|
+
* exactly as an Escape press would: dismiss guards are honoured, and a
|
|
391
|
+
* dialog with `dismissible={false}` stays open. Only the topmost dialog is
|
|
392
|
+
* affected, and a dialog already closing does not count.
|
|
393
|
+
*
|
|
394
|
+
* Call it first from a hardware back button handler (for example Capacitor's
|
|
395
|
+
* `backButton` listener on Android) and navigate back only when it returns
|
|
396
|
+
* `false`.
|
|
397
|
+
*
|
|
398
|
+
* @returns `true` when a dialog was open and the press was consumed (even if
|
|
399
|
+
* it stayed open because it is not dismissible or a guard refused), `false`
|
|
400
|
+
* when no dialog is open.
|
|
401
|
+
*/
|
|
402
|
+
export declare function dismissTopmostModal(): boolean;
|
|
403
|
+
|
|
373
404
|
/** A modal side panel with the same focus handling, stacking and footer slot as `Modal`. */
|
|
374
|
-
export declare function Drawer({ isOpen, onClose, size, side, radius, dismissible, ...dialogProps }: Readonly<DrawerProps>): JSX.Element | null;
|
|
405
|
+
export declare function Drawer({ isOpen, onClose, size, side, radius, dismissible, onExited, ...dialogProps }: Readonly<DrawerProps>): JSX.Element | null;
|
|
375
406
|
|
|
376
407
|
export declare interface DrawerProps {
|
|
377
408
|
isOpen: boolean;
|
|
@@ -397,6 +428,8 @@ export declare interface DrawerProps {
|
|
|
397
428
|
/** `false` hides the close button and blocks Escape and backdrop dismissal. */
|
|
398
429
|
dismissible?: boolean;
|
|
399
430
|
closeLabel?: string;
|
|
431
|
+
/** Called once per close, when the exit has finished and the panel is unmounted. Not called when the drawer reopens during the exit, nor on an initial render with `isOpen={false}`. */
|
|
432
|
+
onExited?: () => void;
|
|
400
433
|
/** Wins over the first-tabbable rule for where focus lands on open; may point at an element in the body or in the footer. */
|
|
401
434
|
initialFocusRef?: RefObject<HTMLElement | null>;
|
|
402
435
|
}
|
|
@@ -509,6 +542,15 @@ export declare interface KbdProps {
|
|
|
509
542
|
|
|
510
543
|
export declare type KbdSize = ControlSize;
|
|
511
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
|
+
|
|
512
554
|
/**
|
|
513
555
|
* A dropdown anchored to `trigger` (e.g. a kebab `IconButton`): click to
|
|
514
556
|
* open, click again/click outside/Escape to close, or ArrowDown/ArrowUp on
|
|
@@ -707,11 +749,15 @@ export declare interface ModalFooterProps {
|
|
|
707
749
|
|
|
708
750
|
export declare type ModalHeight = 'auto' | 'fixed';
|
|
709
751
|
|
|
752
|
+
export declare type ModalPhoneLayout = 'center' | 'sheet';
|
|
753
|
+
|
|
710
754
|
export declare type ModalProps = {
|
|
711
755
|
children: ModalRenderProp;
|
|
712
756
|
size?: ModalSize;
|
|
713
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. */
|
|
714
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;
|
|
715
761
|
radius?: Radius;
|
|
716
762
|
className?: string;
|
|
717
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. */
|
|
@@ -741,7 +787,7 @@ export declare type ModalSize = 'sm' | 'md' | 'lg' | 'xl';
|
|
|
741
787
|
* pills opens a searchable dropdown of checkbox-style options. Selecting an
|
|
742
788
|
* option never closes the dropdown, unlike the single-select `Combobox`.
|
|
743
789
|
*/
|
|
744
|
-
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;
|
|
745
791
|
|
|
746
792
|
export declare interface MultiComboboxOption {
|
|
747
793
|
value: string;
|
|
@@ -749,6 +795,7 @@ export declare interface MultiComboboxOption {
|
|
|
749
795
|
}
|
|
750
796
|
|
|
751
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'`. */
|
|
752
799
|
options: readonly MultiComboboxOption[];
|
|
753
800
|
/**
|
|
754
801
|
* Decides whether an option matches the text typed in the search field.
|
|
@@ -777,6 +824,19 @@ export declare interface MultiComboboxProps {
|
|
|
777
824
|
required?: boolean;
|
|
778
825
|
className?: string;
|
|
779
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;
|
|
780
840
|
values?: readonly string[];
|
|
781
841
|
defaultValues?: readonly string[];
|
|
782
842
|
onChange?: (values: readonly string[]) => void;
|
|
@@ -790,6 +850,8 @@ export declare interface MultiComboboxProps {
|
|
|
790
850
|
id?: string;
|
|
791
851
|
}
|
|
792
852
|
|
|
853
|
+
export declare type OptionLabelOverflow = 'wrap' | 'wrap-active' | 'truncate';
|
|
854
|
+
|
|
793
855
|
export declare type RadioOption = ChoiceOption;
|
|
794
856
|
|
|
795
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;
|
|
@@ -937,7 +999,7 @@ export declare function splitIntoHighlightSegments(
|
|
|
937
999
|
return segments;
|
|
938
1000
|
}
|
|
939
1001
|
|
|
940
|
-
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;
|
|
941
1003
|
|
|
942
1004
|
export declare interface SwitchProps extends Omit<ComponentPropsWithRef<'input'>, 'type' | 'className'> {
|
|
943
1005
|
label?: string | ReactNode;
|
|
@@ -1034,7 +1096,10 @@ export declare type Theme = 'light' | 'dark' | 'system';
|
|
|
1034
1096
|
* first paint, so a dark-mode visitor never sees a light flash. It mirrors
|
|
1035
1097
|
* `useThemeStore`: the `dark` class is set for a stored `dark`, or for a
|
|
1036
1098
|
* stored `system` (or a missing, invalid or unreadable value) while the OS
|
|
1037
|
-
* 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.
|
|
1038
1103
|
*/
|
|
1039
1104
|
export declare const THEME_INIT_SCRIPT: string;
|
|
1040
1105
|
|
|
@@ -1237,6 +1302,43 @@ export declare const useGlobalHotkeysStore: UseBoundStore<StoreApi<GlobalHotkeys
|
|
|
1237
1302
|
|
|
1238
1303
|
export declare const useIsClient: () => boolean;
|
|
1239
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
|
+
|
|
1240
1342
|
export declare const useThemeStore: UseBoundStore<StoreApi<ThemeStore>>;
|
|
1241
1343
|
|
|
1242
1344
|
/** Wraps `Component` so it only renders once the client has mounted. */
|
|
@@ -1250,3 +1352,10 @@ declare module 'vitest' {
|
|
|
1250
1352
|
interface Assertion<T = unknown> extends AxeMatchers {}
|
|
1251
1353
|
interface AsymmetricMatchersContaining extends AxeMatchers {}
|
|
1252
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
|
+
}
|