@minimalstuff/ui 3.2.0 → 4.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sonny
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/index.d.ts CHANGED
@@ -53,7 +53,6 @@ export declare type BadgeSize = ControlSize;
53
53
 
54
54
  export declare type BadgeVariant = 'solid' | 'outline' | 'subtle';
55
55
 
56
- /** Chrome shared by every text-entry control (Input, Textarea, Select, Combobox). */
57
56
  export declare const BASE_INPUT_STYLES = "w-full border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed";
58
57
 
59
58
  export declare function Button(buttonProps: Readonly<ButtonProps>): JSX.Element;
@@ -122,7 +121,7 @@ export declare interface CardProps {
122
121
  className?: string;
123
122
  }
124
123
 
125
- export declare function CharacterCount({ current, min, max, showMin, showMax, unstyled, className, }: Readonly<CharacterCountProps>): JSX.Element;
124
+ export declare function CharacterCount({ current, min, max, showMin, showMax, unstyled, id, className, }: Readonly<CharacterCountProps>): JSX.Element;
126
125
 
127
126
  export declare interface CharacterCountProps {
128
127
  current: number;
@@ -131,10 +130,33 @@ export declare interface CharacterCountProps {
131
130
  showMin?: boolean;
132
131
  showMax?: boolean;
133
132
  unstyled?: boolean;
133
+ id?: string;
134
134
  className?: string;
135
135
  }
136
136
 
137
- export declare function Checkbox({ label, description, error, radius, unstyled, className, wrapperClassName, checked, defaultChecked, onChange, id, ...props }: Readonly<CheckboxProps>): JSX.Element;
137
+ export declare function Checkbox({ label, description, error, radius, unstyled, fullWidth, variant, className, wrapperClassName, checked, defaultChecked, onChange, id, 'aria-describedby': callerDescribedBy, ...props }: Readonly<CheckboxProps>): JSX.Element;
138
+
139
+ export declare type CheckboxOption = ChoiceOption;
140
+
141
+ 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;
142
+
143
+ export declare interface CheckboxOptionsProps extends Omit<ComponentPropsWithRef<'fieldset'>, 'onChange'> {
144
+ options: string[] | CheckboxOption[];
145
+ orientation?: 'vertical' | 'horizontal';
146
+ values?: readonly string[];
147
+ defaultValues?: readonly string[];
148
+ onChange?: (values: readonly string[]) => void;
149
+ name?: string;
150
+ label?: string | React.ReactNode;
151
+ error?: string;
152
+ required?: boolean;
153
+ requiredLabel?: string;
154
+ radius?: Radius;
155
+ unstyled?: boolean;
156
+ className?: string;
157
+ wrapperClassName?: string;
158
+ id?: string;
159
+ }
138
160
 
139
161
  export declare interface CheckboxProps extends Omit<ComponentPropsWithRef<'input'>, 'type' | 'className'> {
140
162
  label?: string | React.ReactNode;
@@ -142,17 +164,28 @@ export declare interface CheckboxProps extends Omit<ComponentPropsWithRef<'input
142
164
  error?: string;
143
165
  radius?: Radius;
144
166
  unstyled?: boolean;
167
+ fullWidth?: boolean;
168
+ variant?: 'inline' | 'card';
145
169
  className?: string;
146
170
  wrapperClassName?: string;
147
171
  }
148
172
 
173
+ /** One choice in a `RadioOptions` or `CheckboxOptions` list. */
174
+ export declare interface ChoiceOption {
175
+ value: string;
176
+ label: string;
177
+ description?: string;
178
+ icon?: string;
179
+ disabled?: boolean;
180
+ }
181
+
149
182
  export declare function ClientOnly({ children, fallback }: Readonly<ClientOnlyProps>): string | number | bigint | boolean | JSX.Element | Iterable< ReactNode> | Promise<string | number | bigint | boolean | ReactPortal | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable< ReactNode> | null | undefined> | null;
150
183
 
151
184
  export declare interface ClientOnlyProps extends React.PropsWithChildren {
152
185
  fallback?: React.ReactNode;
153
186
  }
154
187
 
155
- export declare function Combobox({ options, label, error, placeholder, noResultsText, clearLabel, radius, unstyled, disabled, required, className, wrapperClassName, value, defaultValue, onChange, id, }: Readonly<ComboboxProps>): JSX.Element;
188
+ export declare function Combobox({ options, label, 'aria-label': ariaLabel, error, placeholder, noResultsText, clearLabel, radius, unstyled, disabled, required, className, wrapperClassName, value, defaultValue, onChange, id, }: Readonly<ComboboxProps>): JSX.Element;
156
189
 
157
190
  export declare interface ComboboxOption {
158
191
  value: string;
@@ -162,6 +195,7 @@ export declare interface ComboboxOption {
162
195
  export declare interface ComboboxProps {
163
196
  options: ComboboxOption[];
164
197
  label?: string | ReactNode;
198
+ 'aria-label'?: string;
165
199
  error?: string;
166
200
  placeholder?: string;
167
201
  noResultsText?: string;
@@ -202,10 +236,16 @@ export declare type ConfirmModalResponse = boolean;
202
236
  * `MenuSeparator`s — at the cursor position. Right-click again elsewhere on
203
237
  * the wrapped area to reposition it.
204
238
  *
239
+ * Keyboard-only users reach it via Shift+F10 or the Menu key instead of a
240
+ * right-click, but only once the wrapped area (or something inside it) can
241
+ * hold focus — give `children` a focusable element, or a `tabIndex={0}` plus
242
+ * a `role` and `aria-label` on the wrapper itself, or Shift+F10 has nothing
243
+ * to fire on. See `context_menu.stories.tsx`'s `Default` story.
244
+ *
205
245
  * For a menu anchored to a specific button (e.g. a kebab icon) instead of a
206
246
  * right-click, use `Menu`.
207
247
  */
208
- export declare function ContextMenu({ children, items, radius, className, ref, }: Readonly<ContextMenuProps>): JSX.Element;
248
+ export declare function ContextMenu({ children, items, radius, className, ref, 'aria-label': ariaLabel, }: Readonly<ContextMenuProps>): JSX.Element;
209
249
 
210
250
  export declare interface ContextMenuProps {
211
251
  /** The area that opens the menu on right-click. */
@@ -215,6 +255,8 @@ export declare interface ContextMenuProps {
215
255
  radius?: Radius;
216
256
  className?: string;
217
257
  ref?: Ref<HTMLDivElement>;
258
+ /** Names the menu for assistive tech, since there is no trigger element to label it from. */
259
+ 'aria-label'?: string;
218
260
  }
219
261
 
220
262
  /** The size scale every inline control shares (Button, IconButton, Badge, Kbd). */
@@ -267,7 +309,7 @@ declare type IconButtonStyle = {
267
309
  radius?: Radius;
268
310
  };
269
311
 
270
- export declare function Input({ label, error, showCharCount, minLength, maxLength, radius, unstyled, className, wrapperClassName, value, defaultValue, onChange, id, ...props }: Readonly<InputProps>): JSX.Element;
312
+ export declare function Input({ label, error, showCharCount, minLength, maxLength, radius, unstyled, className, wrapperClassName, value, defaultValue, onChange, id, 'aria-describedby': callerDescribedBy, ...props }: Readonly<InputProps>): JSX.Element;
271
313
 
272
314
  export declare interface InputProps extends ComponentPropsWithRef<'input'> {
273
315
  label?: string | ReactNode;
@@ -295,20 +337,27 @@ export declare type KbdSize = ControlSize;
295
337
 
296
338
  /**
297
339
  * A dropdown anchored to `trigger` (e.g. a kebab `IconButton`): click to
298
- * open, click again/click outside/Escape to close. `children` is made of
299
- * `MenuItem`s, optionally wrapped in `MenuGroup`s and split by
300
- * `MenuSeparator`s.
340
+ * open, click again/click outside/Escape to close, or ArrowDown/ArrowUp on
341
+ * the closed trigger to open focused on the first/last item respectively.
342
+ * `children` is made of `MenuItem`s, optionally wrapped in `MenuGroup`s and
343
+ * split by `MenuSeparator`s.
301
344
  *
302
345
  * For "right-click anywhere on this area" instead of a specific trigger
303
346
  * button, use `ContextMenu`.
304
347
  *
305
- * Opening a `Modal` from a `MenuItem`'s `onClick` is safe: the menu returns
306
- * focus to `trigger` synchronously during the click, then the modal mounts a
307
- * render later and takes focus from there. That ordering is what makes the
308
- * modal record `trigger` as its restore target, so focus lands back on the
309
- * trigger when the modal closes. The menu panel outlives the click by one
310
- * exit animation, but the modal sits above it and owns Escape (a document
311
- * listener), so the fading panel is inert. See `menu_with_modal.test.tsx`.
348
+ * Safe to nest inside a `Modal`: Escape on the open menu calls
349
+ * `event.preventDefault()` before closing it (see `MenuSurface`), so the
350
+ * modal's own Escape handler — which ignores an already-`defaultPrevented`
351
+ * event — leaves the dialog open and only the menu closes.
352
+ *
353
+ * Opening a `Modal` from a `MenuItem`'s `onClick` is also safe: the menu
354
+ * returns focus to `trigger` synchronously during the click, then the modal
355
+ * mounts a render later and takes focus from there. That ordering is what
356
+ * makes the modal record `trigger` as its restore target, so focus lands
357
+ * back on the trigger when the modal closes. The menu panel outlives the
358
+ * click by one exit animation, but the modal sits above it and owns Escape
359
+ * (a document listener), so the fading panel is inert. See
360
+ * `menu_with_modal.test.tsx`.
312
361
  */
313
362
  export declare function Menu({ trigger, children, side, align, radius, className, ref, }: Readonly<MenuProps>): JSX.Element;
314
363
 
@@ -448,6 +497,8 @@ export declare interface ModalProps {
448
497
  className?: string;
449
498
  dismissible?: boolean;
450
499
  closeLabel?: string;
500
+ /** Names the dialog when there is no `title`; ignored when `title` is set since the title labels it. */
501
+ 'aria-label'?: string;
451
502
  }
452
503
 
453
504
  export declare function ModalProvider(): JSX.Element;
@@ -456,15 +507,56 @@ declare type ModalRenderProp = ReactNode | ((close: () => void) => ReactNode);
456
507
 
457
508
  export declare type ModalSize = 'sm' | 'md' | 'lg' | 'xl';
458
509
 
459
- export declare interface RadioOption {
510
+ /**
511
+ * Multi-select combobox: a trigger button showing the selected options as
512
+ * pills opens a searchable dropdown of checkbox-style options. Selecting an
513
+ * option never closes the dropdown, unlike the single-select `Combobox`.
514
+ */
515
+ export declare function MultiCombobox({ options, label, '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;
516
+
517
+ export declare interface MultiComboboxOption {
460
518
  value: string;
461
519
  label: string;
462
- description?: string;
463
- icon?: string;
520
+ }
521
+
522
+ export declare interface MultiComboboxProps {
523
+ options: readonly MultiComboboxOption[];
524
+ label?: string | ReactNode;
525
+ 'aria-label'?: string;
526
+ error?: string;
527
+ placeholder?: string;
528
+ searchPlaceholder?: string;
529
+ noResultsText?: string;
530
+ clearLabel?: string;
531
+ clearText?: string;
532
+ maxSelectedValues?: number;
533
+ isOptionDisabled?: (optionValue: string, selectedValues: readonly string[]) => boolean;
534
+ maxDisplayedValues?: number;
535
+ formatOverflowCount?: (hiddenCount: number) => string;
536
+ formatSelectedCount?: (selectedCount: number) => string;
537
+ formatMaxSelectedHint?: (maxSelectedValues: number) => string;
538
+ radius?: Radius;
539
+ unstyled?: boolean;
464
540
  disabled?: boolean;
541
+ required?: boolean;
542
+ className?: string;
543
+ wrapperClassName?: string;
544
+ values?: readonly string[];
545
+ defaultValues?: readonly string[];
546
+ onChange?: (values: readonly string[]) => void;
547
+ /**
548
+ * Fires with the final selection whenever the user is done editing:
549
+ * when the dropdown closes (Escape, outside click, focus leaving the
550
+ * wrapper), and when the selection is cleared from the trigger's X
551
+ * button while the dropdown is closed.
552
+ */
553
+ onDropdownClose?: (values: readonly string[]) => void;
554
+ id?: string;
465
555
  }
466
556
 
467
- export declare function RadioOptions({ options, orientation, value, defaultValue, onChange, name: nameProp, label, error, required, disabled, radius, unstyled, className, wrapperClassName, id, ...props }: Readonly<RadioOptionsProps>): JSX.Element;
557
+ export declare type RadioOption = ChoiceOption;
558
+
559
+ 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;
468
560
 
469
561
  export declare interface RadioOptionsProps extends Omit<ComponentPropsWithRef<'fieldset'>, 'onChange'> {
470
562
  options: string[] | RadioOption[];
@@ -485,7 +577,7 @@ export declare interface RadioOptionsProps extends Omit<ComponentPropsWithRef<'f
485
577
 
486
578
  declare type Radius = 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full';
487
579
 
488
- export declare function Select({ options, label, error, placeholder, radius, unstyled, className, wrapperClassName, value, defaultValue, onChange, id, ...props }: Readonly<SelectProps>): JSX.Element;
580
+ export declare function Select({ options, label, error, placeholder, radius, unstyled, className, wrapperClassName, value, defaultValue, onChange, id, 'aria-describedby': callerDescribedBy, ...props }: Readonly<SelectProps>): JSX.Element;
489
581
 
490
582
  export declare interface SelectOption {
491
583
  value: string;
@@ -543,13 +635,14 @@ export declare function splitIntoHighlightSegments(
543
635
  return segments;
544
636
  }
545
637
 
546
- export declare function Switch({ label, description, error, unstyled, className, wrapperClassName, checked, defaultChecked, onChange, id, ...props }: Readonly<SwitchProps>): JSX.Element;
638
+ export declare function Switch({ label, description, error, unstyled, fullWidth, className, wrapperClassName, checked, defaultChecked, onChange, id, 'aria-describedby': callerDescribedBy, ...props }: Readonly<SwitchProps>): JSX.Element;
547
639
 
548
640
  export declare interface SwitchProps extends Omit<ComponentPropsWithRef<'input'>, 'type' | 'className'> {
549
641
  label?: string | ReactNode;
550
642
  description?: string | ReactNode;
551
643
  error?: string;
552
644
  unstyled?: boolean;
645
+ fullWidth?: boolean;
553
646
  className?: string;
554
647
  wrapperClassName?: string;
555
648
  }
@@ -561,21 +654,31 @@ export declare interface TabItem {
561
654
  icon?: string;
562
655
  }
563
656
 
564
- export declare function Tabs({ items, value, defaultIndex, radius, unstyled, className, tabListClassName, panelClassName, onChange, }: Readonly<TabsProps>): JSX.Element;
657
+ export declare function Tabs({ items, value, defaultIndex, variant, size, radius, fullWidth, unstyled, animated, className, tabListClassName, panelClassName, onChange, }: Readonly<TabsProps>): JSX.Element;
565
658
 
566
659
  export declare interface TabsProps {
567
660
  items: TabItem[];
568
661
  value?: number;
569
662
  defaultIndex?: number;
663
+ variant?: TabsVariant;
664
+ size?: ControlSize;
665
+ /**
666
+ * Only visibly rounds the `segmented` variant's track and tabs. The `line`
667
+ * variant has no filled surface to round, so radius is a no-op there.
668
+ */
570
669
  radius?: Radius;
670
+ fullWidth?: boolean;
571
671
  unstyled?: boolean;
672
+ animated?: boolean;
572
673
  className?: string;
573
674
  tabListClassName?: string;
574
675
  panelClassName?: string;
575
676
  onChange?: (index: number) => void;
576
677
  }
577
678
 
578
- export declare function Textarea({ label, error, showCharCount, minLength, maxLength, radius, unstyled, className, wrapperClassName, value, defaultValue, onChange, id, ...props }: Readonly<TextareaProps>): JSX.Element;
679
+ export declare type TabsVariant = 'line' | 'segmented';
680
+
681
+ export declare function Textarea({ label, error, showCharCount, minLength, maxLength, radius, unstyled, className, wrapperClassName, value, defaultValue, onChange, id, 'aria-describedby': callerDescribedBy, ...props }: Readonly<TextareaProps>): JSX.Element;
579
682
 
580
683
  export declare interface TextareaProps extends ComponentPropsWithRef<'textarea'> {
581
684
  label?: string | ReactNode;
@@ -591,11 +694,9 @@ export declare interface TextareaProps extends ComponentPropsWithRef<'textarea'>
591
694
 
592
695
  export declare type Theme = 'light' | 'dark' | 'system';
593
696
 
594
- declare type Theme_2 = 'light' | 'dark' | 'system';
595
-
596
697
  declare interface ThemeStore {
597
- theme: Theme_2;
598
- setTheme: (theme: Theme_2) => void;
698
+ theme: Theme;
699
+ setTheme: (theme: Theme) => void;
599
700
  }
600
701
 
601
702
  export declare function ThemeToggle({ isTransitionEnabled, transitionDuration, transitionEasing, size, radius, variant, getAriaLabel, }: Readonly<ThemeToggleProps>): JSX.Element;
@@ -607,9 +708,15 @@ export declare interface ThemeToggleProps {
607
708
  size?: 'sm' | 'md' | 'lg';
608
709
  radius?: Radius;
609
710
  variant?: ButtonVariant;
610
- getAriaLabel?: (theme: Theme_2) => string;
711
+ getAriaLabel?: (theme: Theme) => string;
611
712
  }
612
713
 
714
+ /**
715
+ * The trigger (`children`) must be focusable, otherwise the tooltip is
716
+ * unreachable by keyboard. Shows on hover and focus; Escape dismisses it.
717
+ * `temporaryContent` is announced to screen readers through a polite status
718
+ * region.
719
+ */
613
720
  export declare function Tooltip({ content, children, position, showOnClick, temporaryContent, temporaryDuration, disabled, radius, onTemporaryShow, className, ref, }: Readonly<TooltipProps>): JSX.Element;
614
721
 
615
722
  export declare type TooltipPosition = 'top' | 'bottom' | 'left' | 'right';