@minimalstuff/ui 5.0.1 → 5.1.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 +21 -0
- package/dist/index.d.ts +125 -11
- package/dist/minimalstuff-ui.css +1 -1
- package/dist/minimalstuff-ui.js +1637 -1511
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,10 +42,31 @@ export function Root() {
|
|
|
42
42
|
}
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
+
For French, pass the bundled preset: `<UIProvider labels={FRENCH_UI_LABELS}>` (import `FRENCH_UI_LABELS` from `@minimalstuff/ui`).
|
|
46
|
+
|
|
45
47
|
### Dark mode
|
|
46
48
|
|
|
47
49
|
Dark styles apply when `<html>` has the `dark` class. `useThemeStore` (and the `ThemeToggle` built on it) sets that class from the `light`, `dark` or `system` preference and keeps it in `localStorage`.
|
|
48
50
|
|
|
51
|
+
To avoid a light flash before your bundle runs, inline `THEME_INIT_SCRIPT` in `<head>`, before the stylesheet. It reads the same `localStorage` key as the store and sets the `dark` class the same way.
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
import { THEME_INIT_SCRIPT } from '@minimalstuff/ui';
|
|
55
|
+
|
|
56
|
+
<head>
|
|
57
|
+
<script dangerouslySetInnerHTML={{ __html: THEME_INIT_SCRIPT }} />
|
|
58
|
+
<link rel="stylesheet" href="/style.css" />
|
|
59
|
+
</head>;
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
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
|
+
|
|
64
|
+
```bash
|
|
65
|
+
node --input-type=module -e "import { THEME_INIT_SCRIPT } from '@minimalstuff/ui'; import { createHash } from 'node:crypto'; console.log(createHash('sha256').update(THEME_INIT_SCRIPT).digest('base64'))"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The hash changes if a release changes the script: recompute it when you upgrade.
|
|
69
|
+
|
|
49
70
|
### Modals and drawers lock page scroll
|
|
50
71
|
|
|
51
72
|
While a `Modal` or `Drawer` is open, page scroll is locked on `<html>`. The scrollbar width this frees is compensated with right padding on `<html>`, so the page does not shift. That padding does not move `position: fixed` elements: anchor right-aligned ones with `right: var(--scrollbar-compensation, 0px)`. Paint the page background on `<html>` or `<body>` rather than only on a wrapper element, or the padding shows the root's background as a strip on the right.
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,27 @@ import { RefObject } from 'react';
|
|
|
13
13
|
import { StoreApi } from 'zustand';
|
|
14
14
|
import { UseBoundStore } from 'zustand';
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* A thin application bar with a `start` slot on the left, a centered `title`
|
|
18
|
+
* and an `end` slot on the right. It is a plain `<header>` (the `banner`
|
|
19
|
+
* landmark when it is not nested in another sectioning element), not a
|
|
20
|
+
* WAI-ARIA toolbar: put your own links and buttons in the slots, or use
|
|
21
|
+
* `Toolbar` for a group of controls with arrow-key navigation. Its minimum
|
|
22
|
+
* height grows with enlarged text and its horizontal padding honours the
|
|
23
|
+
* device safe area.
|
|
24
|
+
*/
|
|
25
|
+
export declare function AppBar(appBarProps: Readonly<AppBarProps>): JSX.Element;
|
|
26
|
+
|
|
27
|
+
export declare type AppBarProps = Omit<ComponentPropsWithRef<'header'>, 'title'> & {
|
|
28
|
+
start?: ReactNode;
|
|
29
|
+
title?: ReactNode;
|
|
30
|
+
end?: ReactNode;
|
|
31
|
+
startClassName?: string;
|
|
32
|
+
titleClassName?: string;
|
|
33
|
+
endClassName?: string;
|
|
34
|
+
unstyled?: boolean;
|
|
35
|
+
};
|
|
36
|
+
|
|
16
37
|
/**
|
|
17
38
|
* The picture of a person, or the initial of `name` when there is none.
|
|
18
39
|
* Labelled with the full `name` so screen readers announce the person rather
|
|
@@ -64,6 +85,31 @@ export declare const BASE_INPUT_STYLES = "w-full border border-gray-300 dark:bor
|
|
|
64
85
|
|
|
65
86
|
export declare function Button(buttonProps: Readonly<ButtonProps>): JSX.Element;
|
|
66
87
|
|
|
88
|
+
/**
|
|
89
|
+
* The exact class string `Button` renders, for styling another element as a
|
|
90
|
+
* button, typically a router `<Link>` or an `<a>`. Keyboard focus ring included.
|
|
91
|
+
* For a selected link, add `pressed` and `aria-current`, not `aria-pressed`.
|
|
92
|
+
*/
|
|
93
|
+
export declare function buttonClassName(options?: Readonly<ButtonClassNameOptions>): string;
|
|
94
|
+
|
|
95
|
+
export declare type ButtonClassNameOptions = {
|
|
96
|
+
variant?: ButtonVariant;
|
|
97
|
+
color?: ButtonColor;
|
|
98
|
+
size?: ControlSize;
|
|
99
|
+
radius?: Radius;
|
|
100
|
+
fullWidth?: boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Applies the pressed look, for a real `<button>` toggle paired with
|
|
103
|
+
* `aria-pressed`. Never put `aria-pressed` on a link (not allowed on role
|
|
104
|
+
* link): on an `<a>`, pair the selected look with `aria-current`, for
|
|
105
|
+
* example `aria-current="page"`.
|
|
106
|
+
*/
|
|
107
|
+
pressed?: boolean;
|
|
108
|
+
/** Keeps only the layout classes, like `Button`'s `unstyled` prop. */
|
|
109
|
+
unstyled?: boolean;
|
|
110
|
+
className?: string;
|
|
111
|
+
};
|
|
112
|
+
|
|
67
113
|
declare type ButtonColor =
|
|
68
114
|
| 'primary'
|
|
69
115
|
| 'neutral'
|
|
@@ -85,6 +131,11 @@ export declare type ButtonProps = Omit<ComponentPropsWithRef<'button'>, 'childre
|
|
|
85
131
|
* the tab order, exposed as `aria-disabled`.
|
|
86
132
|
*/
|
|
87
133
|
loading?: boolean;
|
|
134
|
+
/**
|
|
135
|
+
* Turns the button into a toggle: sets `aria-pressed` and a pressed look (dark fill and inset ring).
|
|
136
|
+
* Leave it `undefined` for a regular button (no attribute).
|
|
137
|
+
*/
|
|
138
|
+
pressed?: boolean;
|
|
88
139
|
startIcon?: string;
|
|
89
140
|
endIcon?: string;
|
|
90
141
|
} & ButtonStyle;
|
|
@@ -153,7 +204,7 @@ export declare type CheckboxOption = ChoiceOption;
|
|
|
153
204
|
export declare function CheckboxOptions({ options, orientation, values, defaultValues, onChange, name: nameProp, label, error, required, requiredLabel, disabled, radius, unstyled, className, wrapperClassName, id, 'aria-describedby': callerDescribedBy, ...props }: Readonly<CheckboxOptionsProps>): JSX.Element;
|
|
154
205
|
|
|
155
206
|
export declare interface CheckboxOptionsProps extends Omit<ComponentPropsWithRef<'fieldset'>, 'onChange'> {
|
|
156
|
-
options: string[] | CheckboxOption[];
|
|
207
|
+
options: readonly string[] | readonly CheckboxOption[];
|
|
157
208
|
orientation?: 'vertical' | 'horizontal';
|
|
158
209
|
values?: readonly string[];
|
|
159
210
|
defaultValues?: readonly string[];
|
|
@@ -193,13 +244,13 @@ export declare interface ChoiceOption {
|
|
|
193
244
|
disabled?: boolean;
|
|
194
245
|
}
|
|
195
246
|
|
|
196
|
-
export declare function ClientOnly({ children, fallback }: Readonly<ClientOnlyProps>): string | number | bigint | boolean |
|
|
247
|
+
export declare function ClientOnly({ children, fallback }: Readonly<ClientOnlyProps>): string | number | bigint | boolean | Iterable< ReactNode> | Promise<string | number | bigint | boolean | ReactPortal | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable< ReactNode> | null | undefined> | JSX.Element | null;
|
|
197
248
|
|
|
198
249
|
export declare interface ClientOnlyProps extends React.PropsWithChildren {
|
|
199
250
|
fallback?: React.ReactNode;
|
|
200
251
|
}
|
|
201
252
|
|
|
202
|
-
export declare function Combobox({ options, label, description, 'aria-label': ariaLabel, error, placeholder, noResultsText, clearLabel, radius, unstyled, disabled, required, className, wrapperClassName, value, defaultValue, onChange, id, }: Readonly<ComboboxProps>): JSX.Element;
|
|
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;
|
|
203
254
|
|
|
204
255
|
export declare interface ComboboxOption {
|
|
205
256
|
value: string;
|
|
@@ -207,7 +258,13 @@ export declare interface ComboboxOption {
|
|
|
207
258
|
}
|
|
208
259
|
|
|
209
260
|
export declare interface ComboboxProps {
|
|
210
|
-
options: ComboboxOption[];
|
|
261
|
+
options: readonly ComboboxOption[];
|
|
262
|
+
/**
|
|
263
|
+
* Decides whether an option matches the text typed in the field. Replaces
|
|
264
|
+
* the default case-insensitive "label contains the query" match, for
|
|
265
|
+
* example to ignore accents.
|
|
266
|
+
*/
|
|
267
|
+
filterOption?: (option: ComboboxOption, query: string) => boolean;
|
|
211
268
|
label?: string | ReactNode;
|
|
212
269
|
description?: string | ReactNode;
|
|
213
270
|
'aria-label'?: string;
|
|
@@ -289,14 +346,20 @@ export declare interface ContextMenuProps {
|
|
|
289
346
|
/** The size scale every inline control shares (Button, IconButton, Badge, Kbd). */
|
|
290
347
|
declare type ControlSize = 'xs' | 'sm' | 'md' | 'lg';
|
|
291
348
|
|
|
292
|
-
export declare function CopyButton({ value, children, onError, }: Readonly<CopyButtonProps>): JSX.Element;
|
|
349
|
+
export declare function CopyButton({ value, children, label, onError, }: Readonly<CopyButtonProps>): JSX.Element;
|
|
293
350
|
|
|
294
351
|
export declare interface CopyButtonProps {
|
|
295
352
|
value: string;
|
|
296
|
-
|
|
353
|
+
/**
|
|
354
|
+
* Render prop for a custom control. Omit it to get the default icon button,
|
|
355
|
+
* which swaps its copy icon for a check while `copied`.
|
|
356
|
+
*/
|
|
357
|
+
children?: (props: {
|
|
297
358
|
copied: boolean;
|
|
298
359
|
copy: () => void | Promise<void>;
|
|
299
360
|
}) => ReactNode;
|
|
361
|
+
/** Accessible name of the default icon button. Defaults to `labels.copy`. */
|
|
362
|
+
label?: string;
|
|
300
363
|
onError?: (error: unknown) => void;
|
|
301
364
|
}
|
|
302
365
|
|
|
@@ -342,6 +405,9 @@ export declare type DrawerSide = 'right' | 'left';
|
|
|
342
405
|
|
|
343
406
|
export declare type DrawerSize = 'sm' | 'md' | 'lg';
|
|
344
407
|
|
|
408
|
+
/** French translation of every `UILabels` entry, ready for `<UIProvider labels={FRENCH_UI_LABELS}>`. */
|
|
409
|
+
export declare const FRENCH_UI_LABELS: UILabels;
|
|
410
|
+
|
|
345
411
|
declare interface GlobalHotkeysStore {
|
|
346
412
|
globalHotkeysEnabled: boolean;
|
|
347
413
|
setGlobalHotkeysEnabled: (value: boolean) => void;
|
|
@@ -383,6 +449,11 @@ export declare type IconButtonProps = ComponentPropsWithRef<'button'> & {
|
|
|
383
449
|
size?: ControlSize;
|
|
384
450
|
children?: ReactNode;
|
|
385
451
|
tooltip?: IconButtonTooltip;
|
|
452
|
+
/**
|
|
453
|
+
* Turns the button into a toggle: sets `aria-pressed` and a pressed look (dark fill and inset ring).
|
|
454
|
+
* Leave it `undefined` for a regular button (no attribute).
|
|
455
|
+
*/
|
|
456
|
+
pressed?: boolean;
|
|
386
457
|
} & IconButtonStyle;
|
|
387
458
|
|
|
388
459
|
declare type IconButtonStyle = {
|
|
@@ -670,7 +741,7 @@ export declare type ModalSize = 'sm' | 'md' | 'lg' | 'xl';
|
|
|
670
741
|
* pills opens a searchable dropdown of checkbox-style options. Selecting an
|
|
671
742
|
* option never closes the dropdown, unlike the single-select `Combobox`.
|
|
672
743
|
*/
|
|
673
|
-
export declare function MultiCombobox({ options, 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;
|
|
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;
|
|
674
745
|
|
|
675
746
|
export declare interface MultiComboboxOption {
|
|
676
747
|
value: string;
|
|
@@ -679,6 +750,12 @@ export declare interface MultiComboboxOption {
|
|
|
679
750
|
|
|
680
751
|
export declare interface MultiComboboxProps {
|
|
681
752
|
options: readonly MultiComboboxOption[];
|
|
753
|
+
/**
|
|
754
|
+
* Decides whether an option matches the text typed in the search field.
|
|
755
|
+
* Replaces the default case-insensitive "label contains the query" match,
|
|
756
|
+
* for example to ignore accents.
|
|
757
|
+
*/
|
|
758
|
+
filterOption?: (option: MultiComboboxOption, query: string) => boolean;
|
|
682
759
|
label?: string | ReactNode;
|
|
683
760
|
description?: string | ReactNode;
|
|
684
761
|
'aria-label'?: string;
|
|
@@ -718,7 +795,7 @@ export declare type RadioOption = ChoiceOption;
|
|
|
718
795
|
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;
|
|
719
796
|
|
|
720
797
|
export declare interface RadioOptionsProps extends Omit<ComponentPropsWithRef<'fieldset'>, 'onChange'> {
|
|
721
|
-
options: string[] | RadioOption[];
|
|
798
|
+
options: readonly string[] | readonly RadioOption[];
|
|
722
799
|
orientation?: 'vertical' | 'horizontal';
|
|
723
800
|
value?: string;
|
|
724
801
|
defaultValue?: string;
|
|
@@ -744,7 +821,7 @@ export declare interface SelectOption {
|
|
|
744
821
|
}
|
|
745
822
|
|
|
746
823
|
export declare interface SelectProps extends Omit<ComponentPropsWithRef<'select'>, 'children'> {
|
|
747
|
-
options: SelectOption[];
|
|
824
|
+
options: readonly SelectOption[];
|
|
748
825
|
label?: string | ReactNode;
|
|
749
826
|
description?: string | ReactNode;
|
|
750
827
|
error?: string;
|
|
@@ -755,6 +832,28 @@ export declare interface SelectProps extends Omit<ComponentPropsWithRef<'select'
|
|
|
755
832
|
wrapperClassName?: string;
|
|
756
833
|
}
|
|
757
834
|
|
|
835
|
+
/**
|
|
836
|
+
* A loading indicator exposed as a `role="status"` live region. It uses the
|
|
837
|
+
* icon `Button` shows while loading; under `prefers-reduced-motion` it swaps
|
|
838
|
+
* to a static icon.
|
|
839
|
+
*
|
|
840
|
+
* The region is empty on first render and filled right after mount, because
|
|
841
|
+
* many screen readers ignore a live region inserted already filled. The most
|
|
842
|
+
* robust setup is still a persistent `role="status"` wrapper in your own
|
|
843
|
+
* markup that stays mounted while a `Spinner` appears and disappears inside it.
|
|
844
|
+
*/
|
|
845
|
+
export declare function Spinner(spinnerProps: Readonly<SpinnerProps>): JSX.Element;
|
|
846
|
+
|
|
847
|
+
export declare type SpinnerProps = Omit<ComponentPropsWithRef<'span'>, 'children'> & {
|
|
848
|
+
size?: ControlSize;
|
|
849
|
+
/** Visible text next to the icon. Without it, `labels.loading` is announced instead. */
|
|
850
|
+
label?: string;
|
|
851
|
+
/** Strips the built-in layout, text size and icon size; size the icon with `iconClassName`. */
|
|
852
|
+
unstyled?: boolean;
|
|
853
|
+
iconClassName?: string;
|
|
854
|
+
labelClassName?: string;
|
|
855
|
+
};
|
|
856
|
+
|
|
758
857
|
/**
|
|
759
858
|
* A primary action joined to a chevron `Menu` trigger that offers secondary
|
|
760
859
|
* variants of that action. Both halves share variant, color, size and radius:
|
|
@@ -869,7 +968,7 @@ export declare function Tabs({ items, value, defaultIndex, variant, orientation,
|
|
|
869
968
|
export declare type TabsOrientation = 'horizontal' | 'vertical';
|
|
870
969
|
|
|
871
970
|
export declare interface TabsProps {
|
|
872
|
-
items: TabItem[];
|
|
971
|
+
items: readonly TabItem[];
|
|
873
972
|
value?: number;
|
|
874
973
|
defaultIndex?: number;
|
|
875
974
|
variant?: TabsVariant;
|
|
@@ -930,6 +1029,15 @@ export declare interface TextareaProps extends ComponentPropsWithRef<'textarea'>
|
|
|
930
1029
|
|
|
931
1030
|
export declare type Theme = 'light' | 'dark' | 'system';
|
|
932
1031
|
|
|
1032
|
+
/**
|
|
1033
|
+
* A self-contained script that applies the saved theme to `<html>` before the
|
|
1034
|
+
* first paint, so a dark-mode visitor never sees a light flash. It mirrors
|
|
1035
|
+
* `useThemeStore`: the `dark` class is set for a stored `dark`, or for a
|
|
1036
|
+
* stored `system` (or a missing, invalid or unreadable value) while the OS
|
|
1037
|
+
* prefers dark. Inline it in `<head>` before the stylesheet.
|
|
1038
|
+
*/
|
|
1039
|
+
export declare const THEME_INIT_SCRIPT: string;
|
|
1040
|
+
|
|
933
1041
|
declare interface ThemeStore {
|
|
934
1042
|
theme: Theme;
|
|
935
1043
|
setTheme: (theme: Theme) => void;
|
|
@@ -1060,8 +1168,14 @@ export declare interface UILabels {
|
|
|
1060
1168
|
required: string;
|
|
1061
1169
|
/** Screen-reader announcement of `CopyButton` after a successful copy. */
|
|
1062
1170
|
copied: string;
|
|
1063
|
-
/** Screen-reader text added to a `Button` while it is loading. */
|
|
1171
|
+
/** Screen-reader text added to a `Button` while it is loading, and announced by `Spinner` without a visible label. */
|
|
1064
1172
|
loading: string;
|
|
1173
|
+
/**
|
|
1174
|
+
* Accessible name of the default `CopyButton` icon button. Optional so that
|
|
1175
|
+
* existing complete label objects keep compiling: a translated label set
|
|
1176
|
+
* must add it, otherwise the English "Copy" is used.
|
|
1177
|
+
*/
|
|
1178
|
+
copy?: string;
|
|
1065
1179
|
/** Screen-reader announcement of `CharacterCount` near and at the limit. */
|
|
1066
1180
|
charactersRemaining: (remainingCount: number) => string;
|
|
1067
1181
|
/** Accessible name of `ThemeToggle` for the current theme. */
|