@ahrowe/ui 0.1.5 → 0.1.7
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/dist/index.cjs +4 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1831 -1164
- package/dist/index.mjs.map +1 -1
- package/dist/style.css +1 -1
- package/dist/types/package/common/iconPicker/iconPicker.d.ts +2 -21
- package/dist/types/package/common/iconPicker/iconPicker.types.d.ts +23 -0
- package/dist/types/package/common/iconPicker/index.d.ts +1 -2
- package/dist/types/package/common/inputDropdown/index.d.ts +1 -0
- package/dist/types/package/common/inputDropdown/inputDropdown.d.ts +2 -0
- package/dist/types/package/common/inputDropdown/inputDropdown.types.d.ts +21 -0
- package/dist/types/package/common/stepper/index.d.ts +1 -2
- package/dist/types/package/common/stepper/stepper.d.ts +2 -25
- package/dist/types/package/common/stepper/stepper.types.d.ts +21 -0
- package/dist/types/package/common/virtualList/index.d.ts +2 -0
- package/dist/types/package/common/virtualList/virtualList.d.ts +14 -0
- package/dist/types/package/common/virtualList/virtualList.types.d.ts +71 -0
- package/dist/types/package/common/wizard/index.d.ts +1 -2
- package/dist/types/package/common/wizard/wizard.d.ts +2 -27
- package/dist/types/package/common/wizard/wizard.types.d.ts +12 -0
- package/dist/types/package/index.d.ts +53 -37
- package/docs/CLAUDE.md +7 -1
- package/docs/DatePicker.md +77 -0
- package/docs/IconPicker.md +55 -0
- package/docs/InputDropdown.md +52 -0
- package/docs/RoomViewer.md +39 -0
- package/docs/ThemeProvider.md +82 -0
- package/docs/Toast.md +67 -0
- package/docs/VirtualList.md +108 -0
- package/package.json +10 -2
- package/dist/types/package/common/icon/Icon.d.ts +0 -10
- package/dist/types/package/common/icon/index.d.ts +0 -2
- package/dist/types/package/common/icon/svg/can.d.ts +0 -2
- package/dist/types/package/common/inputDropdownModern/index.d.ts +0 -2
- package/dist/types/package/common/inputDropdownModern/inputDropdownModern.d.ts +0 -35
- package/dist/types/package/common/roleManager/index.d.ts +0 -2
- package/dist/types/package/common/roleManager/roleManager.container.d.ts +0 -2
- package/dist/types/package/common/roleManager/roleManager.d.ts +0 -22
package/docs/CLAUDE.md
CHANGED
|
@@ -111,14 +111,16 @@ Slot keys per component are documented in each component's doc file below.
|
|
|
111
111
|
@Chip.md
|
|
112
112
|
@ColorPicker.md
|
|
113
113
|
@ConfirmModal.md
|
|
114
|
+
@DatePicker.md
|
|
114
115
|
@Dropdown.md
|
|
115
116
|
@EmptyState.md
|
|
116
117
|
@Fab.md
|
|
117
118
|
@FloatingMenu.md
|
|
118
|
-
@
|
|
119
|
+
@IconPicker.md
|
|
119
120
|
@IdleManager.md
|
|
120
121
|
@InfiniteBlock.md
|
|
121
122
|
@Input.md
|
|
123
|
+
@InputDropdown.md
|
|
122
124
|
@InteractableDiv.md
|
|
123
125
|
@KanbanBoard.md
|
|
124
126
|
@Loading.md
|
|
@@ -129,10 +131,14 @@ Slot keys per component are documented in each component's doc file below.
|
|
|
129
131
|
@ProgressBar.md
|
|
130
132
|
@Ripple.md
|
|
131
133
|
@RoomDrawer.md
|
|
134
|
+
@RoomViewer.md
|
|
132
135
|
@ScrollbarProvider.md
|
|
133
136
|
@SearchInput.md
|
|
134
137
|
@Stepper.md
|
|
135
138
|
@TenorPicker.md
|
|
136
139
|
@Textarea.md
|
|
140
|
+
@ThemeProvider.md
|
|
141
|
+
@Toast.md
|
|
137
142
|
@Tooltip.md
|
|
143
|
+
@VirtualList.md
|
|
138
144
|
@Wizard.md
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# DatePicker
|
|
2
|
+
|
|
3
|
+
**When to use:** Date selection input — booking forms, birth date fields, date range pickers, deadline selectors. Renders as a text input with a floating label by default; can also be inline or modal.
|
|
4
|
+
|
|
5
|
+
**Import:** `import { DatePicker } from '@ahrowe/ui'`
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { DatePicker, FormValidator } from '@ahrowe/ui';
|
|
9
|
+
|
|
10
|
+
// Controlled input with floating label
|
|
11
|
+
<DatePicker
|
|
12
|
+
label="Date of birth"
|
|
13
|
+
selected={date}
|
|
14
|
+
onSelect={(date) => setDate(date)}
|
|
15
|
+
/>
|
|
16
|
+
|
|
17
|
+
// With FormValidator
|
|
18
|
+
const dateValidator = new FormValidator<Date | null>(null);
|
|
19
|
+
<DatePicker label="Start date" formValidator={dateValidator} />
|
|
20
|
+
|
|
21
|
+
// Inline calendar (no input field)
|
|
22
|
+
<DatePicker
|
|
23
|
+
selected={date}
|
|
24
|
+
onSelect={setDate}
|
|
25
|
+
isInline
|
|
26
|
+
noInput
|
|
27
|
+
/>
|
|
28
|
+
|
|
29
|
+
// Inside a modal (calendar opens in a Modal overlay)
|
|
30
|
+
<DatePicker
|
|
31
|
+
label="Schedule date"
|
|
32
|
+
selected={date}
|
|
33
|
+
onSelect={setDate}
|
|
34
|
+
isModal
|
|
35
|
+
/>
|
|
36
|
+
|
|
37
|
+
// With min/max constraints and a clear button
|
|
38
|
+
<DatePicker
|
|
39
|
+
label="Appointment"
|
|
40
|
+
selected={date}
|
|
41
|
+
onSelect={setDate}
|
|
42
|
+
minDate={new Date()}
|
|
43
|
+
maxDate={endOfYear}
|
|
44
|
+
showClearButton
|
|
45
|
+
/>
|
|
46
|
+
|
|
47
|
+
// Date range (highlight start–end)
|
|
48
|
+
<DatePicker
|
|
49
|
+
label="From"
|
|
50
|
+
selected={startDate}
|
|
51
|
+
onSelect={setStartDate}
|
|
52
|
+
startDate={startDate}
|
|
53
|
+
endDate={endDate}
|
|
54
|
+
/>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Key props:**
|
|
58
|
+
|
|
59
|
+
| Prop | Type | Description |
|
|
60
|
+
|------|------|-------------|
|
|
61
|
+
| `label` | `string` | Floating label on the input |
|
|
62
|
+
| `selected` | `Date` | Currently selected date |
|
|
63
|
+
| `onSelect` | `(date: Date \| null) => void` | Called when a date is picked |
|
|
64
|
+
| `onChange` | `(date: Date \| null) => void` | Alternative change callback |
|
|
65
|
+
| `formValidator` | `FormValidator` | Connects to validation |
|
|
66
|
+
| `isRequired` | `boolean` | |
|
|
67
|
+
| `isValid` | `boolean` | Manual valid state |
|
|
68
|
+
| `errorMessage` | `string` | Manual error message |
|
|
69
|
+
| `noInput` | `boolean` | Hide the text input (show calendar only) |
|
|
70
|
+
| `noHeader` | `boolean` | Hide the month/year navigation header |
|
|
71
|
+
| `isModal` | `boolean` | Open calendar in a Modal overlay |
|
|
72
|
+
| `isInline` | `boolean` | Render calendar always-visible inline |
|
|
73
|
+
| `minDate` | `Date \| null` | Earliest selectable date |
|
|
74
|
+
| `maxDate` | `Date \| null` | Latest selectable date |
|
|
75
|
+
| `showClearButton` | `boolean` | Show a clear/reset button |
|
|
76
|
+
| `startDate` | `Date` | Range highlight start |
|
|
77
|
+
| `endDate` | `Date` | Range highlight end |
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# IconPicker
|
|
2
|
+
|
|
3
|
+
**When to use:** Pick one item from a visual icon/image grid — category selectors, emoji-style pickers, avatar selectors, anything where each option is best represented by an icon or image rather than text alone.
|
|
4
|
+
|
|
5
|
+
**Import:** `import { IconPicker } from '@ahrowe/ui'`
|
|
6
|
+
**Types:** `import type { IconPickerItem, IconPickerProps } from '@ahrowe/ui'`
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { IconPicker, FormValidator } from '@ahrowe/ui';
|
|
10
|
+
import type { IconPickerItem } from '@ahrowe/ui';
|
|
11
|
+
import { faHouse, faCar, faTree } from '@fortawesome/free-solid-svg-icons';
|
|
12
|
+
|
|
13
|
+
const items: IconPickerItem[] = [
|
|
14
|
+
{ id: 'house', label: 'House', icon: faHouse },
|
|
15
|
+
{ id: 'car', label: 'Car', icon: faCar },
|
|
16
|
+
{ id: 'tree', label: 'Tree', icon: faTree },
|
|
17
|
+
{ id: 'custom', label: 'Custom', customIcon: <img src="/logo.png" alt="" /> },
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
// Controlled
|
|
21
|
+
<IconPicker
|
|
22
|
+
label="Category"
|
|
23
|
+
data={items}
|
|
24
|
+
selectedId={selectedId}
|
|
25
|
+
onChange={(id) => setSelectedId(id)}
|
|
26
|
+
/>
|
|
27
|
+
|
|
28
|
+
// With FormValidator
|
|
29
|
+
const categoryValidator = new FormValidator<string | null>(null);
|
|
30
|
+
<IconPicker label="Category" data={items} formValidator={categoryValidator} isRequired />
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**IconPickerItem:**
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
interface IconPickerItem {
|
|
37
|
+
id: string;
|
|
38
|
+
label: string;
|
|
39
|
+
icon?: IconDefinition; // FontAwesome icon
|
|
40
|
+
customIcon?: React.ReactNode; // custom element (overrides icon)
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Key props:**
|
|
45
|
+
|
|
46
|
+
| Prop | Type | Description |
|
|
47
|
+
|------|------|-------------|
|
|
48
|
+
| `data` | `IconPickerItem[]` | Items to display in the grid |
|
|
49
|
+
| `selectedId` | `string \| null` | Currently selected item id |
|
|
50
|
+
| `label` | `string` | Floating label |
|
|
51
|
+
| `onChange` | `(id: string) => void` | Called when selection changes |
|
|
52
|
+
| `formValidator` | `FormValidator` | Connects to validation |
|
|
53
|
+
| `isRequired` | `boolean` | |
|
|
54
|
+
|
|
55
|
+
**Slots:** `root` `label` `itemContainer` `item`
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# InputDropdown
|
|
2
|
+
|
|
3
|
+
**When to use:** Combobox-style input — the user can type to filter and also pick from a dropdown list. Use over `Dropdown` when free-text entry or search-to-filter behaviour is needed.
|
|
4
|
+
|
|
5
|
+
**Import:** `import { InputDropdown } from '@ahrowe/ui'`
|
|
6
|
+
**Types:** `import type { InputDropdownItem, InputDropdownProps } from '@ahrowe/ui'`
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { InputDropdown } from '@ahrowe/ui';
|
|
10
|
+
import type { InputDropdownItem } from '@ahrowe/ui';
|
|
11
|
+
|
|
12
|
+
const items: InputDropdownItem[] = [
|
|
13
|
+
{ value: 'react', label: 'React' },
|
|
14
|
+
{ value: 'vue', label: 'Vue' },
|
|
15
|
+
{ value: 'svelte', label: 'Svelte' },
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
// Controlled
|
|
19
|
+
<InputDropdown
|
|
20
|
+
label="Framework"
|
|
21
|
+
value={selected}
|
|
22
|
+
items={items}
|
|
23
|
+
onSelect={(value) => setSelected(value)}
|
|
24
|
+
/>
|
|
25
|
+
|
|
26
|
+
// With FormValidator
|
|
27
|
+
const frameworkValidator = new FormValidator('');
|
|
28
|
+
<InputDropdown label="Framework" items={items} formValidator={frameworkValidator} />
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**InputDropdownItem:**
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
interface InputDropdownItem {
|
|
35
|
+
value: string;
|
|
36
|
+
label?: string; // displayed text — falls back to value if omitted
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Key props:**
|
|
41
|
+
|
|
42
|
+
| Prop | Type | Description |
|
|
43
|
+
|------|------|-------------|
|
|
44
|
+
| `label` | `string` | Floating label |
|
|
45
|
+
| `value` | `string` | Controlled value |
|
|
46
|
+
| `items` | `InputDropdownItem[]` | Dropdown options |
|
|
47
|
+
| `onSelect` | `(value: string) => void` | Called when an item is picked from the list |
|
|
48
|
+
| `onChange` | `(value: unknown) => void` | Called on every text change |
|
|
49
|
+
| `placeholder` | `string` | |
|
|
50
|
+
| `formValidator` | `FormValidator` | Connects to validation |
|
|
51
|
+
|
|
52
|
+
**Slots:** `root` `dropdown` `item`
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# RoomViewer
|
|
2
|
+
|
|
3
|
+
**When to use:** Display read-only SVG floor plans with a floor selector — venue maps, seating layouts, building directories. Pair with `RoomDrawer` when you also need editing capability.
|
|
4
|
+
|
|
5
|
+
**Import:** `import { RoomViewer } from '@ahrowe/ui'`
|
|
6
|
+
**Types:** `import type { RoomViewerFloor, RoomViewerProps } from '@ahrowe/ui'`
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { RoomViewer } from '@ahrowe/ui';
|
|
10
|
+
import type { RoomViewerFloor } from '@ahrowe/ui';
|
|
11
|
+
|
|
12
|
+
const floors: RoomViewerFloor[] = [
|
|
13
|
+
{
|
|
14
|
+
label: 'Ground Floor',
|
|
15
|
+
data: <GroundFloorSvg />,
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
label: 'First Floor',
|
|
19
|
+
data: <FirstFloorSvg />,
|
|
20
|
+
},
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
<RoomViewer data={floors} />
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**RoomViewerFloor:**
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
interface RoomViewerFloor {
|
|
30
|
+
label: string; // Tab/selector label shown above the plan
|
|
31
|
+
data: React.ReactNode; // SVG floor plan element
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Key props:**
|
|
36
|
+
|
|
37
|
+
| Prop | Type | Description |
|
|
38
|
+
|------|------|-------------|
|
|
39
|
+
| `data` | `RoomViewerFloor[]` | Floor definitions to display |
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# ThemeProvider
|
|
2
|
+
|
|
3
|
+
**When to use:** Required wrapper for the entire component library. Injects all CSS custom properties as inline styles so every component inside can use theme variables. Place it at or near the root of your app.
|
|
4
|
+
|
|
5
|
+
**Import:** `import { ThemeProvider } from '@ahrowe/ui'`
|
|
6
|
+
**Types:** `import type { Theme, ThemeVariables } from '@ahrowe/ui'`
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { ThemeProvider } from '@ahrowe/ui';
|
|
10
|
+
import '@ahrowe/ui/style.css'; // add once in your entry point
|
|
11
|
+
|
|
12
|
+
// Minimal — uses the built-in default theme
|
|
13
|
+
<ThemeProvider>
|
|
14
|
+
<App />
|
|
15
|
+
</ThemeProvider>
|
|
16
|
+
|
|
17
|
+
// With a custom theme
|
|
18
|
+
import type { Theme } from '@ahrowe/ui';
|
|
19
|
+
|
|
20
|
+
const brandTheme: Theme = {
|
|
21
|
+
id: 'brand',
|
|
22
|
+
variables: {
|
|
23
|
+
'--primary-color': '#6200ea',
|
|
24
|
+
'--primary-lighter': '#b388ff',
|
|
25
|
+
'--primary-accent': '#7c4dff',
|
|
26
|
+
'--background': '#ffffff',
|
|
27
|
+
'--text-color': '#212121',
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
<ThemeProvider themes={[brandTheme]} currentThemeId="brand">
|
|
32
|
+
<App />
|
|
33
|
+
</ThemeProvider>
|
|
34
|
+
|
|
35
|
+
// Extending the built-in theme (only override specific variables)
|
|
36
|
+
const darkTheme: Theme = {
|
|
37
|
+
id: 'dark',
|
|
38
|
+
baseThemeId: 'default', // inherit everything not listed here
|
|
39
|
+
variables: {
|
|
40
|
+
'--background': '#121212',
|
|
41
|
+
'--background-accent': '#1e1e1e',
|
|
42
|
+
'--text-color': '#ffffff',
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
<ThemeProvider themes={[darkTheme]} currentThemeId="dark">
|
|
47
|
+
<App />
|
|
48
|
+
</ThemeProvider>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Theme type:**
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
interface Theme {
|
|
55
|
+
id: string;
|
|
56
|
+
baseThemeId?: string; // inherit from another theme before applying variables
|
|
57
|
+
variables: ThemeVariables;
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Key CSS variables** (see `package/common/themeProvider/theme.types.ts` for the full list):
|
|
62
|
+
|
|
63
|
+
| Variable | Purpose |
|
|
64
|
+
|----------|---------|
|
|
65
|
+
| `--primary-color` / `--primary-lighter` / `--primary-accent` | Brand colours |
|
|
66
|
+
| `--background` | Page/surface background |
|
|
67
|
+
| `--background-accent` / `--background-accent-light` | Raised surfaces, cards |
|
|
68
|
+
| `--text-color` / `--text-dark` | Primary and secondary text |
|
|
69
|
+
| `--border-color` | Borders and dividers |
|
|
70
|
+
| `--error-color` / `--success-color` / `--warn-color` / `--info-color` | Semantic colours |
|
|
71
|
+
| `--default-border-radius` | Border radii |
|
|
72
|
+
| `--card-shadow` | Elevation shadows |
|
|
73
|
+
|
|
74
|
+
**Key props:**
|
|
75
|
+
|
|
76
|
+
| Prop | Type | Description |
|
|
77
|
+
|------|------|-------------|
|
|
78
|
+
| `children` | `ReactNode` | Your app content (required) |
|
|
79
|
+
| `themes` | `Theme[]` | Theme definitions — defaults to built-in default theme |
|
|
80
|
+
| `currentThemeId` | `string` | Active theme id — must match one of the `themes[].id` values |
|
|
81
|
+
|
|
82
|
+
**Note:** Without `ThemeProvider`, all CSS variables are undefined and components will be unstyled.
|
package/docs/Toast.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Toast
|
|
2
|
+
|
|
3
|
+
**When to use:** Non-blocking notifications — save confirmations, error alerts, info messages, warnings. Toasts appear in a corner of the screen and auto-dismiss after a configurable duration.
|
|
4
|
+
|
|
5
|
+
**Import:** `import { ToastProvider, showToast } from '@ahrowe/ui'`
|
|
6
|
+
**Types:** `import type { ToastOptions, ToastType, ToastPosition } from '@ahrowe/ui'`
|
|
7
|
+
|
|
8
|
+
**Setup:** Wrap your app with `ToastProvider` (inside `ThemeProvider`):
|
|
9
|
+
|
|
10
|
+
```tsx
|
|
11
|
+
import { ThemeProvider, ToastProvider } from '@ahrowe/ui';
|
|
12
|
+
|
|
13
|
+
<ThemeProvider>
|
|
14
|
+
<ToastProvider position="bottom-right" duration={3000}>
|
|
15
|
+
<App />
|
|
16
|
+
</ToastProvider>
|
|
17
|
+
</ThemeProvider>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Usage:** Call `showToast` anywhere — no component hierarchy required:
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
import { showToast } from '@ahrowe/ui';
|
|
24
|
+
|
|
25
|
+
// Simple message
|
|
26
|
+
showToast('Profile saved!');
|
|
27
|
+
|
|
28
|
+
// With type
|
|
29
|
+
showToast('Something went wrong.', { type: 'error' });
|
|
30
|
+
showToast('3 items updated.', { type: 'info' });
|
|
31
|
+
showToast('Disk space is low.', { type: 'warn' });
|
|
32
|
+
|
|
33
|
+
// Persistent (no auto-dismiss)
|
|
34
|
+
showToast('Processing…', { duration: 0, showCloseButton: true });
|
|
35
|
+
|
|
36
|
+
// With progress bar showing remaining time
|
|
37
|
+
showToast('Uploading file…', {
|
|
38
|
+
duration: 5000,
|
|
39
|
+
showRemainingTime: true,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Override position per-toast
|
|
43
|
+
showToast('Copied!', { position: 'top-center', duration: 1500 });
|
|
44
|
+
|
|
45
|
+
// With slot overrides
|
|
46
|
+
showToast('Done', {
|
|
47
|
+
type: 'info',
|
|
48
|
+
classNames: { root: 'my-toast' },
|
|
49
|
+
styles: { progressBar: { backgroundColor: '#6200ea' } },
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**ToastProviderProps** (default config for all toasts):
|
|
54
|
+
|
|
55
|
+
| Prop | Type | Default | Description |
|
|
56
|
+
|------|------|---------|-------------|
|
|
57
|
+
| `position` | `ToastPosition` | `'bottom-right'` | Screen position |
|
|
58
|
+
| `duration` | `number` | `3000` | Auto-dismiss delay in ms; `0` = persistent |
|
|
59
|
+
| `showRemainingTime` | `boolean` | `false` | Show a progress bar draining to zero |
|
|
60
|
+
| `showCloseButton` | `boolean` | `false` | Show × button |
|
|
61
|
+
| `allowStacking` | `boolean` | `true` | Allow multiple toasts simultaneously |
|
|
62
|
+
|
|
63
|
+
**ToastPosition:** `'top-left'` `'top-center'` `'top-right'` `'bottom-left'` `'bottom-center'` `'bottom-right'`
|
|
64
|
+
|
|
65
|
+
**ToastType:** `'default'` `'info'` `'error'` `'warn'`
|
|
66
|
+
|
|
67
|
+
**Slots:** `root` `icon` `content` `closeButton` `progressBar`
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# VirtualList
|
|
2
|
+
|
|
3
|
+
**When to use:** Efficiently render large lists or tables — thousands of rows rendered with a virtualised scroll window so only visible rows are in the DOM. Supports column definitions, row selection, multi-select, column visibility toggling, and infinite scroll.
|
|
4
|
+
|
|
5
|
+
**Import:** `import { VirtualList } from '@ahrowe/ui'`
|
|
6
|
+
**Types:** `import type { VirtualListProps, VirtualListColumn } from '@ahrowe/ui'`
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { VirtualList } from '@ahrowe/ui';
|
|
10
|
+
import type { VirtualListColumn } from '@ahrowe/ui';
|
|
11
|
+
|
|
12
|
+
type User = { id: string; name: string; email: string; role: string };
|
|
13
|
+
|
|
14
|
+
// Simple list mode (no columns)
|
|
15
|
+
<VirtualList
|
|
16
|
+
items={users}
|
|
17
|
+
renderRow={(user) => (
|
|
18
|
+
<div className="row">{user.name} — {user.email}</div>
|
|
19
|
+
)}
|
|
20
|
+
height={500}
|
|
21
|
+
/>
|
|
22
|
+
|
|
23
|
+
// Table mode with column definitions
|
|
24
|
+
const columns: VirtualListColumn<User>[] = [
|
|
25
|
+
{
|
|
26
|
+
key: 'name',
|
|
27
|
+
label: 'Name',
|
|
28
|
+
width: { type: 'flex', weight: 2 },
|
|
29
|
+
renderCell: (user) => <strong>{user.name}</strong>,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
key: 'email',
|
|
33
|
+
label: 'Email',
|
|
34
|
+
width: { type: 'flex' },
|
|
35
|
+
renderCell: (user) => user.email,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
key: 'role',
|
|
39
|
+
label: 'Role',
|
|
40
|
+
width: { type: 'fixed', px: 120 },
|
|
41
|
+
renderCell: (user) => user.role,
|
|
42
|
+
defaultHidden: true,
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
<VirtualList
|
|
47
|
+
items={users}
|
|
48
|
+
columns={columns}
|
|
49
|
+
getItemKey={(user) => user.id}
|
|
50
|
+
selectedKey={selectedId}
|
|
51
|
+
onRowClick={(user) => setSelectedId(user.id)}
|
|
52
|
+
height={600}
|
|
53
|
+
/>
|
|
54
|
+
|
|
55
|
+
// Infinite scroll
|
|
56
|
+
<VirtualList
|
|
57
|
+
items={items}
|
|
58
|
+
columns={columns}
|
|
59
|
+
onLoadMore={async () => { await fetchNextPage(); }}
|
|
60
|
+
isLoading={isInitialLoading}
|
|
61
|
+
height="100%"
|
|
62
|
+
/>
|
|
63
|
+
|
|
64
|
+
// Multi-select
|
|
65
|
+
<VirtualList
|
|
66
|
+
items={items}
|
|
67
|
+
columns={columns}
|
|
68
|
+
multiSelect
|
|
69
|
+
selectedKeys={selectedKeys}
|
|
70
|
+
onSelectionChange={(keys) => setSelectedKeys(keys)}
|
|
71
|
+
height={500}
|
|
72
|
+
/>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Column width types:**
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
{ type: 'fixed'; px: number } // fixed pixel width
|
|
79
|
+
{ type: 'flex'; weight?: number } // flex, weight defaults to 1
|
|
80
|
+
{ type: 'fit' } // shrink to content
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Key props:**
|
|
84
|
+
|
|
85
|
+
| Prop | Type | Description |
|
|
86
|
+
|------|------|-------------|
|
|
87
|
+
| `items` | `T[]` | Data array (required) |
|
|
88
|
+
| `renderRow` | `(item, index) => ReactNode` | Row render fn — list mode (no columns) |
|
|
89
|
+
| `columns` | `VirtualListColumn<T>[]` | Column definitions — enables table/header mode |
|
|
90
|
+
| `height` | `number \| string` | Scroll viewport height (default `'100%'`) |
|
|
91
|
+
| `estimatedRowHeight` | `number` | Estimated row height before measurement (default `40`) |
|
|
92
|
+
| `overscan` | `number` | Extra rows rendered outside the viewport (default `3`) |
|
|
93
|
+
| `rowGap` | `number` | Gap in px between rows (default `0`) |
|
|
94
|
+
| `showDivider` | `boolean` | Row divider lines (default `true`) |
|
|
95
|
+
| `getItemKey` | `(item, index) => string \| number` | Stable key per item |
|
|
96
|
+
| `selectedKey` | `string \| number \| null` | Controlled single-select key |
|
|
97
|
+
| `onRowClick` | `(item, index) => void` | Row click handler |
|
|
98
|
+
| `multiSelect` | `boolean` | Enable checkbox multi-select column |
|
|
99
|
+
| `selectedKeys` | `Set<string \| number>` | Controlled multi-select keys |
|
|
100
|
+
| `onSelectionChange` | `(keys) => void` | Multi-select change callback |
|
|
101
|
+
| `showColumnToggle` | `boolean` | Show gear button to show/hide columns (default `true` when columns present) |
|
|
102
|
+
| `visibleColumnKeys` | `string[]` | Controlled visible column keys |
|
|
103
|
+
| `onVisibleColumnsChange` | `(keys) => void` | Column visibility change callback |
|
|
104
|
+
| `onLoadMore` | `() => Promise<void>` | Triggered near the bottom — append items in the handler |
|
|
105
|
+
| `loadMoreThreshold` | `number` | Distance from bottom that triggers `onLoadMore` (default `100`) |
|
|
106
|
+
| `isLoading` | `boolean` | Replaces list body with a full-height spinner |
|
|
107
|
+
|
|
108
|
+
**Slots:** `root` `header` `headerCell` `headerToggle` `togglePopover` `toggleItem` `body` `row` `cell` `selectCell` `loadingIndicator`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ahrowe/ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -43,12 +43,16 @@
|
|
|
43
43
|
"dist",
|
|
44
44
|
"docs"
|
|
45
45
|
],
|
|
46
|
+
"lint-staged": {
|
|
47
|
+
"*.{ts,tsx}": "vitest related --run"
|
|
48
|
+
},
|
|
46
49
|
"scripts": {
|
|
47
50
|
"dev": "vite",
|
|
48
51
|
"build": "vite build",
|
|
49
52
|
"build:lib": "vite build --config vite.lib.config.ts",
|
|
50
53
|
"build:mcp": "vite build --config vite.mcp.config.ts",
|
|
51
54
|
"preview": "vite preview",
|
|
55
|
+
"gen-barrel": "tsx scripts/gen-barrel.ts",
|
|
52
56
|
"gc": "bash src/scripts/generateComponent.sh $INIT_CWD process.argv",
|
|
53
57
|
"lint": "eslint .",
|
|
54
58
|
"lint:fix": "eslint . --fix",
|
|
@@ -58,7 +62,8 @@
|
|
|
58
62
|
"verify-package": "bash src/scripts/verify-package.sh",
|
|
59
63
|
"test": "vitest run",
|
|
60
64
|
"test:watch": "vitest",
|
|
61
|
-
"test:ui": "vitest --ui"
|
|
65
|
+
"test:ui": "vitest --ui",
|
|
66
|
+
"prepare": "husky"
|
|
62
67
|
},
|
|
63
68
|
"peerDependencies": {
|
|
64
69
|
"@fortawesome/fontawesome-svg-core": ">=6.0.0",
|
|
@@ -99,8 +104,10 @@
|
|
|
99
104
|
"eslint-config-prettier": "^10.1.8",
|
|
100
105
|
"eslint-plugin-react": "^7.37.5",
|
|
101
106
|
"eslint-plugin-react-hooks": "^7.1.1",
|
|
107
|
+
"husky": "^9.1.7",
|
|
102
108
|
"jiti": "^2.7.0",
|
|
103
109
|
"jsdom": "^29.1.1",
|
|
110
|
+
"lint-staged": "^17.0.5",
|
|
104
111
|
"postcss": "^8.5.15",
|
|
105
112
|
"postcss-nested": "^7.0.2",
|
|
106
113
|
"prettier": "^3.8.3",
|
|
@@ -108,6 +115,7 @@
|
|
|
108
115
|
"react-dom": "^19.2.6",
|
|
109
116
|
"react-element-to-jsx-string": "^17.0.1",
|
|
110
117
|
"react-syntax-highlighter": "^16.1.1",
|
|
118
|
+
"tsx": "^4.22.3",
|
|
111
119
|
"typescript": "^6.0.3",
|
|
112
120
|
"typescript-eslint": "^8.60.0",
|
|
113
121
|
"vite": "^8.0.14",
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { ValidatableComponent } from '../../services/formValidation';
|
|
2
|
-
export default InputDropdownModern;
|
|
3
|
-
declare class InputDropdownModern extends ValidatableComponent<import('../../services/formValidation/validatableComponent').ValidatableComponentProps, import('../../services/formValidation/validatableComponent').ValidatableComponentState> {
|
|
4
|
-
static propTypes: {
|
|
5
|
-
label: any;
|
|
6
|
-
value: any;
|
|
7
|
-
formValidator: any;
|
|
8
|
-
items: any;
|
|
9
|
-
className: any;
|
|
10
|
-
onSelect: any;
|
|
11
|
-
placeholder: any;
|
|
12
|
-
};
|
|
13
|
-
static defaultProps: {
|
|
14
|
-
label: string;
|
|
15
|
-
value: string;
|
|
16
|
-
formValidator: null;
|
|
17
|
-
items: never[];
|
|
18
|
-
className: string;
|
|
19
|
-
onSelect: () => null;
|
|
20
|
-
placeholder: string;
|
|
21
|
-
};
|
|
22
|
-
constructor(props: any);
|
|
23
|
-
state: {
|
|
24
|
-
isMenuShown: boolean;
|
|
25
|
-
highlightedIndex: null;
|
|
26
|
-
currItems: any;
|
|
27
|
-
lastVal: string;
|
|
28
|
-
};
|
|
29
|
-
getItemsForValue: (value: any) => any;
|
|
30
|
-
getValueFromProps: (props: any) => any;
|
|
31
|
-
componentDidUpdate(prevProps: any, prevState: any): void;
|
|
32
|
-
hideMenuTimeout: any;
|
|
33
|
-
onKeyDown: (event: any) => void;
|
|
34
|
-
render(): import("react/jsx-runtime").JSX.Element;
|
|
35
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { default as React } from 'react';
|
|
2
|
-
export default RoleManager;
|
|
3
|
-
declare class RoleManager extends React.Component<any, any, any> {
|
|
4
|
-
static propTypes: {
|
|
5
|
-
children: any;
|
|
6
|
-
anyRequiredRoles: any;
|
|
7
|
-
allRequiredRoles: any;
|
|
8
|
-
invalidRoleContent: any;
|
|
9
|
-
user: any;
|
|
10
|
-
household: any;
|
|
11
|
-
};
|
|
12
|
-
static defaultProps: {
|
|
13
|
-
children: null;
|
|
14
|
-
anyRequiredRoles: never[];
|
|
15
|
-
allRequiredRoles: never[];
|
|
16
|
-
invalidRoleContent: null;
|
|
17
|
-
};
|
|
18
|
-
constructor(props: any);
|
|
19
|
-
state: {};
|
|
20
|
-
checkIsRoleSufficient: () => any;
|
|
21
|
-
render(): import("react/jsx-runtime").JSX.Element;
|
|
22
|
-
}
|