@mucsi96/react-ui 0.4.0 → 0.6.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 CHANGED
@@ -44,6 +44,46 @@ the font to the document root, so applications need no font-family declarations
44
44
  or token overrides. No font downloads are needed.
45
45
  The optional stylesheet does not import fonts or reset page layouts.
46
46
 
47
+ ## Application layout
48
+
49
+ ```tsx
50
+ import { AppHeader, AppHeaderActions, AppMain } from '@mucsi96/react-ui';
51
+
52
+ <AppHeader>
53
+ <a href="/">My application</a>
54
+ <AppHeaderActions>
55
+ <nav aria-label="Main navigation">{/* Application links */}</nav>
56
+ <ProfileMenu />
57
+ </AppHeaderActions>
58
+ </AppHeader>
59
+ <AppMain id="main-content">{/* Page content */}</AppMain>
60
+ ```
61
+
62
+ `AppHeader` renders a native `header` with a width-constrained inner flex row.
63
+ Its safe-area top padding sits outside the row's minimum height, so iOS status
64
+ bars do not compress controls. Children wrap as needed. `AppHeaderActions` is a
65
+ wrapping, end-aligned `div`; make any nested navigation groups wrap as well.
66
+ `AppMain` renders a native `main` with matching horizontal gutters, page spacing,
67
+ and bottom safe-area padding. All three accept native element props, including
68
+ `className`, `style`, and React 19 `ref`. They use ordinary document flow.
69
+
70
+ Set these inherited CSS variables on a common ancestor to customize the layout:
71
+
72
+ | Variable | Default | Purpose |
73
+ | ------------------------ | ------- | ---------------------------------------------------- |
74
+ | `--bt-app-max-width` | `90rem` | Header row and main maximum width, including gutters |
75
+ | `--bt-app-gutter` | `1rem` | Minimum horizontal and bottom page padding |
76
+ | `--bt-app-header-height` | `4rem` | Minimum header row height, excluding top safe area |
77
+ | `--bt-app-header-gap` | `1rem` | Space between header children and actions |
78
+ | `--bt-app-content-gap` | `3rem` | Main content top padding |
79
+
80
+ The host document should reset the body margin and include
81
+ `<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">`
82
+ for edge-to-edge safe-area handling. The library does not modify document metadata
83
+ or global layout. Keep routing, profile fetching, navigation labels, and menu
84
+ contents in the application; compose profile menus with `MenuTrigger` and
85
+ `Button variant="avatar"`.
86
+
47
87
  ## Buttons
48
88
 
49
89
  ```tsx
@@ -79,6 +119,12 @@ const options = [{ id: 'apple', label: 'Apple' }, { id: 'pear', label: 'Pear' }]
79
119
  <Select label="Fruit" options={options} />
80
120
  <ComboBox label="Search fruits" options={options} />
81
121
  <Checkbox defaultSelected>Subscribe</Checkbox>
122
+ <NumberField label="Daily limit" name="limit" minValue={1} maxValue={100} step={1} isRequired />
123
+ <CheckboxGroup label="Card types" name="types" isRequired>
124
+ <Checkbox value="vocabulary">Vocabulary</Checkbox>
125
+ <Checkbox value="grammar">Grammar</Checkbox>
126
+ </CheckboxGroup>
127
+ <MultiSelect label="Fruits" options={options} />
82
128
  <RadioGroup label="Size" defaultValue="medium">
83
129
  <Radio value="small">Small</Radio>
84
130
  <Radio value="medium">Medium</Radio>
@@ -90,6 +136,22 @@ Labels, descriptions, and validation messages are associated with the input auto
90
136
 
91
137
  Select options have `{ id, label, isDisabled? }`; React Aria handles selection, typeahead, and filtering. Checkbox and switch controlled state uses `isSelected`/`onChange`; radio group state uses `value`/`onChange`. React Aria form `name` props support native form submissions.
92
138
 
139
+ `NumberField` uses numeric `value`/`onChange` or `defaultValue`, with `minValue`,
140
+ `maxValue`, `step`, locale-aware `formatOptions`, and increment/decrement buttons.
141
+ An empty field reports `NaN`; validate before persisting rather than substituting
142
+ zero. React Aria commits and clamps typed values on blur. Use `isRequired` to
143
+ prevent empty native form submission. It supports the same `appearance`,
144
+ `description`, `error`, and `validationBehavior` API as the other fields.
145
+
146
+ `CheckboxGroup` owns a string-array `value`/`onChange` or `defaultValue`; each
147
+ child `Checkbox` supplies a `value`. Group labels, descriptions, required
148
+ validation, and errors are associated automatically. `name` submits each selected
149
+ value as a separate form entry. `MultiSelect` provides the same compact field as
150
+ `Select` with `selectionMode="multiple"` fixed internally. Use its React Aria
151
+ `value`/`onChange` or `defaultValue` array API for selection; options remain open
152
+ while selecting, and Escape returns focus to the trigger. Disabled options cannot
153
+ be selected. Both controls accept `description`, `error`, and `isRequired`.
154
+
93
155
  ### Field and button sizing
94
156
 
95
157
  Fields default to `appearance="filled"`, with always-visible floating labels and
@@ -177,6 +239,62 @@ For nested actions, compose `SubmenuTrigger` with a `MenuItem` and another `Menu
177
239
 
178
240
  Dialog focus is trapped while open and restored to the trigger on close. Escape and outside interaction dismiss by default; `isDismissable={false}` disables outside dismissal, and `isKeyboardDismissDisabled` disables Escape. Use `role="alertdialog"` for important confirmations. Dialogs also support controlled `isOpen`/`onOpenChange` without a `DialogTrigger`. Forms and action layouts are composed as ordinary children. Tooltips accept a single pressable child, `content`, optional `placement`, and `delay`.
179
241
 
242
+ Use `size="small" | "medium" | "large"` (24, 32, or 48rem; medium by default)
243
+ to size the outer modal, or `maxWidth` for a CSS width override. All sizes are
244
+ constrained to the viewport with safe-area gutters. The optional `footer` node
245
+ or `({ close }) => ReactNode` keeps actions and the title visible while the body
246
+ scrolls, including in a reduced visual viewport. `className` still styles the
247
+ inner dialog. Keep the form in the body and connect footer submit buttons with
248
+ native `form`/`id` attributes:
249
+
250
+ ```tsx
251
+ <Dialog
252
+ title="Edit source"
253
+ size="large"
254
+ footer={({ close }) => (
255
+ <>
256
+ <Button variant="text" onPress={close}>
257
+ Cancel
258
+ </Button>
259
+ <Button type="submit" form="source-form">
260
+ Save
261
+ </Button>
262
+ </>
263
+ )}
264
+ >
265
+ <form id="source-form" onSubmit={save}>
266
+ <TextField label="Source name" isRequired />
267
+ </form>
268
+ </Dialog>
269
+ ```
270
+
271
+ ## Tabs and empty states
272
+
273
+ ```tsx
274
+ <Tabs defaultSelectedKey="items">
275
+ <TabList aria-label="Collection views">
276
+ <Tab id="items">Items</Tab>
277
+ <Tab id="summary">Summary</Tab>
278
+ </TabList>
279
+ <TabPanel id="items">
280
+ <EmptyState
281
+ title="No items yet"
282
+ description="Create an item to get started."
283
+ action={<Button onPress={create}>Add item</Button>}
284
+ />
285
+ </TabPanel>
286
+ <TabPanel id="summary">Summary content</TabPanel>
287
+ </Tabs>
288
+ ```
289
+
290
+ Tabs support controlled `selectedKey`/`onSelectionChange`, disabled tabs,
291
+ `orientation="vertical"`, and React Aria's keyboard activation and focus
292
+ behavior. Match tab and panel IDs. Panels unmount by default; use
293
+ `shouldForceMount` on `TabPanel` to preserve state in an inactive, hidden panel.
294
+ `EmptyState` owns no business behavior: supply the title, description, action,
295
+ and optional children. Its heading defaults to level 2; set `headingLevel` to
296
+ match the surrounding document.
297
+
180
298
  ## Notifications
181
299
 
182
300
  ```tsx
@@ -247,6 +365,20 @@ This is a semantic HTML table. Columns with `compare` have three-state sorting (
247
365
 
248
366
  All loading animations respect reduced motion. `Card` supports `outlined` and `elevated`; `Badge` accepts semantic tones and `neutral`. `Heading` supports semantic levels 1–6, `Text` supports `muted`, and `Code` provides inline monospace styling.
249
367
 
368
+ Compose stable placeholders with `Skeleton` (`variant="text" | "image" | "row"`,
369
+ optional `width`/`height` CSS values). Each shape is decorative and hidden from
370
+ assistive technology. Wrap a set in `SkeletonGroup label="Loading collection"`
371
+ for one visually hidden status label. Place row placeholders inside table cells,
372
+ not directly inside a `tbody`. Animations stop under reduced motion.
373
+
374
+ ```tsx
375
+ <SkeletonGroup label="Loading collection">
376
+ <Skeleton variant="image" />
377
+ <Skeleton width="70%" />
378
+ <Skeleton variant="row" />
379
+ </SkeletonGroup>
380
+ ```
381
+
250
382
  ## Customize with CSS Modules
251
383
 
252
384
  ```css
@@ -0,0 +1,7 @@
1
+ import type { ComponentPropsWithRef } from 'react';
2
+ export type AppHeaderProps = ComponentPropsWithRef<'header'>;
3
+ export type AppHeaderActionsProps = ComponentPropsWithRef<'div'>;
4
+ export type AppMainProps = ComponentPropsWithRef<'main'>;
5
+ export declare function AppHeader({ children, className, ...props }: AppHeaderProps): import("react").JSX.Element;
6
+ export declare function AppHeaderActions({ className, ...props }: AppHeaderActionsProps): import("react").JSX.Element;
7
+ export declare function AppMain({ className, ...props }: AppMainProps): import("react").JSX.Element;
@@ -1,4 +1,4 @@
1
- import type { ReactNode } from 'react';
1
+ import type { CSSProperties, ReactNode } from 'react';
2
2
  import type { ModalOverlayProps } from 'react-aria-components';
3
3
  export { DialogTrigger } from 'react-aria-components';
4
4
  export interface DialogProps extends Omit<ModalOverlayProps, 'children' | 'className'> {
@@ -9,5 +9,10 @@ export interface DialogProps extends Omit<ModalOverlayProps, 'children' | 'class
9
9
  }) => ReactNode);
10
10
  role?: 'dialog' | 'alertdialog';
11
11
  className?: string;
12
+ size?: 'small' | 'medium' | 'large';
13
+ maxWidth?: CSSProperties['maxWidth'];
14
+ footer?: ReactNode | ((props: {
15
+ close: () => void;
16
+ }) => ReactNode);
12
17
  }
13
- export declare function Dialog({ title, description, children, role, className, isDismissable, ...props }: DialogProps): import("react").JSX.Element;
18
+ export declare function Dialog({ title, description, children, role, className, size, maxWidth, footer, isDismissable, ...props }: DialogProps): import("react").JSX.Element;
@@ -0,0 +1,9 @@
1
+ import type { ComponentPropsWithRef, ReactNode } from 'react';
2
+ import type { HeadingProps } from '../surface/surface';
3
+ export interface EmptyStateProps extends Omit<ComponentPropsWithRef<'section'>, 'title'> {
4
+ title: string;
5
+ description?: ReactNode;
6
+ action?: ReactNode;
7
+ headingLevel?: HeadingProps['level'];
8
+ }
9
+ export declare function EmptyState({ title, description, action, headingLevel, children, className, ...props }: EmptyStateProps): import("react").JSX.Element;
@@ -1,10 +1,18 @@
1
- import type { CheckboxProps as AriaCheckboxProps, RadioGroupProps as AriaRadioGroupProps, RadioProps as AriaRadioProps, SwitchProps as AriaSwitchProps } from 'react-aria-components';
1
+ import type { CheckboxProps as AriaCheckboxProps, CheckboxGroupProps as AriaCheckboxGroupProps, RadioGroupProps as AriaRadioGroupProps, RadioProps as AriaRadioProps, SwitchProps as AriaSwitchProps } from 'react-aria-components';
2
2
  import type { ReactNode } from 'react';
3
3
  export interface CheckboxProps extends Omit<AriaCheckboxProps, 'children' | 'className'> {
4
4
  children: ReactNode;
5
5
  className?: string;
6
6
  }
7
7
  export declare function Checkbox({ children, className, ...props }: CheckboxProps): import("react").JSX.Element;
8
+ export interface CheckboxGroupProps extends Omit<AriaCheckboxGroupProps, 'children' | 'className'> {
9
+ label: string;
10
+ children: ReactNode;
11
+ description?: ReactNode;
12
+ error?: ReactNode;
13
+ className?: string;
14
+ }
15
+ export declare function CheckboxGroup({ label, children, description, error, className, ...props }: CheckboxGroupProps): import("react").JSX.Element;
8
16
  export interface RadioGroupProps extends Omit<AriaRadioGroupProps, 'children' | 'className'> {
9
17
  label: string;
10
18
  children: ReactNode;
@@ -0,0 +1,11 @@
1
+ import type { NumberFieldProps as AriaNumberFieldProps } from 'react-aria-components';
2
+ import type { ReactNode, Ref } from 'react';
3
+ export interface NumberFieldProps extends Omit<AriaNumberFieldProps, 'children' | 'className'> {
4
+ label: string;
5
+ description?: ReactNode;
6
+ error?: ReactNode;
7
+ appearance?: 'outlined' | 'filled';
8
+ className?: string;
9
+ ref?: Ref<HTMLDivElement>;
10
+ }
11
+ export declare function NumberField({ label, description, error, appearance, className, ref, ...props }: NumberFieldProps): import("react").JSX.Element;
@@ -14,10 +14,12 @@ interface FieldProps {
14
14
  appearance?: 'outlined' | 'filled';
15
15
  className?: string;
16
16
  }
17
- export interface SelectProps extends Omit<AriaSelectProps<SelectOption>, keyof FieldProps | 'children' | 'items'>, FieldProps {
17
+ export interface SelectProps<M extends 'single' | 'multiple' = 'single'> extends Omit<AriaSelectProps<SelectOption, M>, keyof FieldProps | 'children' | 'items'>, FieldProps {
18
18
  }
19
- export declare function Select({ label, options, description, error, placeholder, appearance, className, ...props }: SelectProps): import("react").JSX.Element;
19
+ export declare function Select<M extends 'single' | 'multiple' = 'single'>({ label, options, description, error, placeholder, appearance, className, ...props }: SelectProps<M>): import("react").JSX.Element;
20
20
  export interface ComboBoxProps extends Omit<AriaComboBoxProps<SelectOption>, keyof FieldProps | 'children' | 'items' | 'defaultItems'>, FieldProps {
21
21
  }
22
+ export type MultiSelectProps = Omit<SelectProps<'multiple'>, 'selectionMode' | 'selectedKey' | 'defaultSelectedKey' | 'onSelectionChange'>;
23
+ export declare function MultiSelect(props: MultiSelectProps): import("react").JSX.Element;
22
24
  export declare function ComboBox({ label, options, description, error, placeholder, appearance, className, ...props }: ComboBoxProps): import("react").JSX.Element;
23
25
  export {};
@@ -0,0 +1,11 @@
1
+ import type { ComponentPropsWithRef, CSSProperties } from 'react';
2
+ export interface SkeletonProps extends Omit<ComponentPropsWithRef<'div'>, 'children'> {
3
+ variant?: 'text' | 'image' | 'row';
4
+ width?: CSSProperties['width'];
5
+ height?: CSSProperties['height'];
6
+ }
7
+ export declare function Skeleton({ variant, width, height, className, style, ...props }: SkeletonProps): import("react").JSX.Element;
8
+ export interface SkeletonGroupProps extends ComponentPropsWithRef<'div'> {
9
+ label: string;
10
+ }
11
+ export declare function SkeletonGroup({ label, children, className, ...props }: SkeletonGroupProps): import("react").JSX.Element;
@@ -0,0 +1,17 @@
1
+ import type { TabsProps as AriaTabsProps, TabListProps as AriaTabListProps, TabProps as AriaTabProps, TabPanelProps as AriaTabPanelProps } from 'react-aria-components';
2
+ export interface TabsProps extends Omit<AriaTabsProps, 'className'> {
3
+ className?: string;
4
+ }
5
+ export interface TabListProps<T extends object> extends Omit<AriaTabListProps<T>, 'className'> {
6
+ className?: string;
7
+ }
8
+ export interface TabProps extends Omit<AriaTabProps, 'className'> {
9
+ className?: string;
10
+ }
11
+ export interface TabPanelProps extends Omit<AriaTabPanelProps, 'className'> {
12
+ className?: string;
13
+ }
14
+ export declare function Tabs({ className, ...props }: TabsProps): import("react").JSX.Element;
15
+ export declare function TabList<T extends object>({ className, ...props }: TabListProps<T>): import("react").JSX.Element;
16
+ export declare function Tab({ className, ...props }: TabProps): import("react").JSX.Element;
17
+ export declare function TabPanel({ className, ...props }: TabPanelProps): import("react").JSX.Element;
package/dist/index.d.ts CHANGED
@@ -1,11 +1,15 @@
1
+ export { AppHeader, AppHeaderActions, AppMain } from './components/app-layout/app-layout';
2
+ export type { AppHeaderProps, AppHeaderActionsProps, AppMainProps, } from './components/app-layout/app-layout';
1
3
  export { Button, LinkButton } from './components/button/button';
2
4
  export type { ButtonProps, ButtonVariant, LinkButtonProps } from './components/button/button';
3
5
  export { TextField } from './components/forms/text-field';
4
6
  export type { TextFieldProps } from './components/forms/text-field';
5
- export { Select, ComboBox } from './components/forms/select';
6
- export type { SelectProps, ComboBoxProps, SelectOption } from './components/forms/select';
7
- export { Checkbox, Radio, RadioGroup, Switch } from './components/forms/controls';
8
- export type { CheckboxProps, RadioProps, RadioGroupProps, SwitchProps, } from './components/forms/controls';
7
+ export { NumberField } from './components/forms/number-field';
8
+ export type { NumberFieldProps } from './components/forms/number-field';
9
+ export { Select, MultiSelect, ComboBox } from './components/forms/select';
10
+ export type { SelectProps, MultiSelectProps, ComboBoxProps, SelectOption, } from './components/forms/select';
11
+ export { Checkbox, CheckboxGroup, Radio, RadioGroup, Switch } from './components/forms/controls';
12
+ export type { CheckboxProps, CheckboxGroupProps, RadioProps, RadioGroupProps, SwitchProps, } from './components/forms/controls';
9
13
  export { DonutSlider } from './components/donut-slider/donut-slider';
10
14
  export type { DonutSliderProps } from './components/donut-slider/donut-slider';
11
15
  export { Menu, MenuItem, MenuPopover, MenuSeparator, MenuTrigger, SubmenuTrigger, } from './components/menu/menu';
@@ -25,3 +29,9 @@ export type { BarLoaderProps, ProgressBarProps } from './components/loader/loade
25
29
  export { Card, Badge, Heading, Text, Code, Divider } from './components/surface/surface';
26
30
  export type { CardProps, BadgeProps, HeadingProps, TextProps } from './components/surface/surface';
27
31
  export type { Tone } from './types';
32
+ export { Tabs, TabList, Tab, TabPanel } from './components/tabs/tabs';
33
+ export type { TabsProps, TabListProps, TabProps, TabPanelProps } from './components/tabs/tabs';
34
+ export { Skeleton, SkeletonGroup } from './components/skeleton/skeleton';
35
+ export type { SkeletonProps, SkeletonGroupProps } from './components/skeleton/skeleton';
36
+ export { EmptyState } from './components/empty-state/empty-state';
37
+ export type { EmptyStateProps } from './components/empty-state/empty-state';