hazo_ui 2.8.1 → 2.11.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/CHANGE_LOG.md +319 -0
- package/README.md +350 -0
- package/SETUP_CHECKLIST.md +418 -0
- package/dist/index.cjs +497 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +212 -1
- package/dist/index.d.ts +212 -1
- package/dist/index.js +470 -13
- package/dist/index.js.map +1 -1
- package/dist/styles.css +32 -0
- package/package.json +7 -3
package/CHANGE_LOG.md
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [2.11.0] - 2026-05-16
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **Button inline-style fallback rendering as transparent** — the v2-era inline-style fallback used bare `var(--primary)` etc., which resolved to invalid CSS when consumers (per shadcn convention) store CSS variables as raw HSL channels (e.g. `--primary: 222.2 47.4% 11.2%`). All `Button` variants (default, destructive, outline, secondary) now wrap their fallback values in `hsl(...)`, matching the documented theme convention and Tailwind preset. Buttons rendered as transparent rectangles with text-only labels before this fix.
|
|
12
|
+
- `destructive` variant's inline-style `color` switched from hardcoded `"white"` to `hsl(var(--destructive-foreground))` for theme consistency.
|
|
13
|
+
|
|
14
|
+
### Notes
|
|
15
|
+
- No API changes — pure visual fix. Consumers on the standard shadcn theme convention will see Buttons render correctly without any further changes.
|
|
16
|
+
- Consumers who set CSS variables as full color values (hex/rgb) instead of HSL channels should override via the `style` prop (this was never the documented convention).
|
|
17
|
+
|
|
18
|
+
## [2.10.0] - 2026-05-16
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
- **State primitives sub-module** — `Skeleton`, `SkeletonCircle`, `SkeletonBar`, `SkeletonRect`, `SkeletonGroup` with shimmer animation (respects `prefers-reduced-motion`).
|
|
22
|
+
- `EmptyState` component (icon + title + description + action).
|
|
23
|
+
- `ErrorBanner` component with `severity: 'warning' | 'error'`, auto icon, optional title/action/dismiss.
|
|
24
|
+
- `ErrorPage` component for full-page error fallback (title, description, errorCode, correlationId, actions).
|
|
25
|
+
- `LoadingTimeout` wrapper with 5s/15s/30s escalation (silent → gentle → firm → expired).
|
|
26
|
+
- `ProgressiveImage` (grey → blur LQIP → sharp).
|
|
27
|
+
- `HazoUiToaster` + `successToast(opts)` + `errorToast(opts)` imperative helpers — wraps `sonner`.
|
|
28
|
+
- `useLoadingState()` hook — `{ isLoading, setLoading, withLoading }`.
|
|
29
|
+
- `useErrorDisplay()` hook — passive `{ error, setError, clearError }`.
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
- Added `sonner ^2.0.7` to dependencies.
|
|
33
|
+
- Added `@keyframes hazo-shimmer` and `.hazo-shimmer` utility class to `src/styles/hazo-ui.css`.
|
|
34
|
+
|
|
35
|
+
### Notes
|
|
36
|
+
- All new components use shadcn semantic tokens (`bg-muted`, `bg-destructive`, etc.) and direct utility classes for amber tones (no `--warning` token assumed).
|
|
37
|
+
- Components are exported flat & unprefixed from the top-level barrel — matches the existing `Button`/`Spinner`/`Card` convention.
|
|
38
|
+
|
|
39
|
+
## [2.9.0] - 2026-05-12
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
- **Drawer primitive** (`src/components/ui/drawer.tsx`): vaul-backed bottom sheet component following the shadcn/ui Drawer pattern. Exports `Drawer`, `DrawerTrigger`, `DrawerPortal`, `DrawerOverlay`, `DrawerContent`, `DrawerHeader`, `DrawerFooter`, `DrawerTitle`, `DrawerDescription`, `DrawerClose`. Uses dynamic viewport units (`max-h-[96dvh]`) so iOS Safari's address-bar toggling doesn't crop the sheet.
|
|
43
|
+
- **`useMediaQuery` hook** (`src/hooks/use_media_query.ts`): SSR-safe React hook returning whether a CSS media query matches. Re-renders on match changes. Use to fork mobile/desktop rendering (e.g. swap `Dialog` ↔ `Drawer` at `(max-width: 640px)`).
|
|
44
|
+
- **New dependency**: `vaul@^1.1.2` (~6 KB gz, MIT). React/react-dom peers are already covered by hazo_ui's existing peer range.
|
|
45
|
+
|
|
46
|
+
### Notes for consumers
|
|
47
|
+
- The Drawer is intended for mobile bottom sheets. On desktop, prefer `Dialog`. Pattern:
|
|
48
|
+
```typescript
|
|
49
|
+
import { Drawer, Dialog, useMediaQuery } from "hazo_ui";
|
|
50
|
+
const is_mobile = useMediaQuery("(max-width: 640px)");
|
|
51
|
+
const Shell = is_mobile ? Drawer : Dialog;
|
|
52
|
+
```
|
|
53
|
+
- Snap points, side-anchored drawers (`top`/`left`/`right`), and nested drawers are not in scope for this primitive — pass through to vaul's API directly if needed in the meantime.
|
|
54
|
+
|
|
55
|
+
## [2.8.0] - 2026-05-11
|
|
56
|
+
|
|
57
|
+
### Added
|
|
58
|
+
- **shadcn/ui Primitive Re-exports (9 new primitives)**: AlertDialog, ButtonGroup, Card, Collapsible, ScrollArea, Separator, Spinner, Toggle, ToggleGroup
|
|
59
|
+
- All new primitives follow the existing shadcn/ui pattern and are re-exported from the package root
|
|
60
|
+
- Enables sibling hazo_* packages to import these UI primitives from a single source without managing separate Radix UI dependencies
|
|
61
|
+
- **6 new Radix UI dependencies**: `@radix-ui/react-alert-dialog`, `@radix-ui/react-collapsible`, `@radix-ui/react-scroll-area`, `@radix-ui/react-separator`, `@radix-ui/react-toggle`, `@radix-ui/react-toggle-group`
|
|
62
|
+
|
|
63
|
+
### Fixed
|
|
64
|
+
- **CardTitle ref type**: Corrected generic type from `HTMLParagraphElement` to `HTMLHeadingElement` — the rendered element is `<h3>` so the ref must be typed accordingly
|
|
65
|
+
|
|
66
|
+
## [2.7.3] - 2026-05-10
|
|
67
|
+
|
|
68
|
+
### Changed
|
|
69
|
+
- **BREAKING (build/styling)**: Migrated internal build pipeline to **Tailwind CSS v4** (`tailwindcss@^4.2.4`).
|
|
70
|
+
- Replaced `tailwindcss-animate` with `tw-animate-css@^1.4.0`
|
|
71
|
+
- Replaced PostCSS plugins `tailwindcss + autoprefixer` with single `@tailwindcss/postcss` plugin (Lightning CSS handles vendor prefixing)
|
|
72
|
+
- Updated `src/styles/globals.css` to use Tailwind v4 entry: `@import "tailwindcss"` + `@import "tw-animate-css"` (replaces `@tailwind base/components/utilities`)
|
|
73
|
+
- Updated `tailwind.preset.js` to be v4-compatible (removed `tailwindcss-animate` plugin import)
|
|
74
|
+
- Bumped canonical workspace versions: `clsx@^2.1.1`, `tailwind-merge@^3.5.0`, `lucide-react@^0.553.0`, `typescript@^5.7.2`, `@types/react@^18.3.3`
|
|
75
|
+
- Dev-app migrated to Tailwind v4 mirror of the library configuration
|
|
76
|
+
- `HazoUiTextbox` / `HazoUiTextarea` test pages updated to use object-form `color` prop (`{ bg, fg, border }`) for prefixes — aligns with the production pill color model
|
|
77
|
+
|
|
78
|
+
### Migration Notes for Consumers
|
|
79
|
+
- If you use the published `tailwind.preset.js`, no API changes are required.
|
|
80
|
+
- If you previously installed `tailwindcss-animate` for hazo_ui, replace it with `tw-animate-css` and load via `@import "tw-animate-css";` in your CSS.
|
|
81
|
+
- Tailwind v4 consumers must keep the `@source "../node_modules/hazo_ui/dist";` directive (already documented).
|
|
82
|
+
|
|
83
|
+
## [2.7.2] - 2026-03-31
|
|
84
|
+
|
|
85
|
+
### Added
|
|
86
|
+
- **HazoUiPillRadio**: Pill-shaped radio selection component
|
|
87
|
+
- Optional icons (react-icons compatible)
|
|
88
|
+
- Customizable accent colors per option
|
|
89
|
+
- Size variants (sm / md / lg)
|
|
90
|
+
- Horizontal or vertical layout
|
|
91
|
+
- Equal-width option for uniform pill widths
|
|
92
|
+
- Auto-fit vertical layout
|
|
93
|
+
|
|
94
|
+
## [2.7.1] - 2026-03-27
|
|
95
|
+
|
|
96
|
+
### Changed
|
|
97
|
+
- **BREAKING**: Renamed `DialogVariant` value `'danger'` to `'destructive'` in HazoUiDialog for consistency with HazoUiConfirmDialog
|
|
98
|
+
- Extracted shared `ANIMATION_PRESETS` and `resolve_animation_classes` into `src/lib/animations.ts` (used by both dialog components)
|
|
99
|
+
- Removed dead `example_component` placeholder from source
|
|
100
|
+
|
|
101
|
+
### Added
|
|
102
|
+
- Re-exported `ANIMATION_PRESETS` and `resolve_animation_classes` from package root for consumer use
|
|
103
|
+
- Added `.superpowers` and `design` to `.npmignore`
|
|
104
|
+
|
|
105
|
+
### Removed
|
|
106
|
+
- Unnecessary `esbuild` devDependency (tsup bundles its own)
|
|
107
|
+
|
|
108
|
+
## [2.7.0] - 2026-03-26
|
|
109
|
+
|
|
110
|
+
### Added
|
|
111
|
+
- **HazoUiConfirmDialog**: Compact confirmation dialog with variant-driven styling
|
|
112
|
+
- Variant system: default, destructive, warning, info, success
|
|
113
|
+
- Accent top border colored by variant
|
|
114
|
+
- Async `onConfirm` with auto-loading state
|
|
115
|
+
- Configurable buttons (confirm + optional cancel)
|
|
116
|
+
- ReactNode children or simple description string
|
|
117
|
+
- Focus management: cancel button receives focus for destructive/warning variants
|
|
118
|
+
- **shadcn/ui Primitive Re-exports**: All shadcn/ui base components now re-exported from hazo_ui
|
|
119
|
+
- Accordion, Button, Calendar, Checkbox, Command, DropdownMenu, HoverCard, Input, Label, Popover, RadioGroup, Select, Switch, Tabs, Textarea, Tooltip
|
|
120
|
+
- Enables sibling hazo_* packages to import UI primitives from a single source
|
|
121
|
+
- New Radix UI dependencies: `@radix-ui/react-accordion`, `@radix-ui/react-checkbox`, `@radix-ui/react-dropdown-menu`, `@radix-ui/react-hover-card`
|
|
122
|
+
|
|
123
|
+
## [2.6.6] - 2026-03-25
|
|
124
|
+
|
|
125
|
+
### Fixed
|
|
126
|
+
- Dialog title overlapping close button and not word-wrapping
|
|
127
|
+
|
|
128
|
+
## [2.6.5] - 2026-03-24
|
|
129
|
+
|
|
130
|
+
### Added
|
|
131
|
+
- `fixedSize` prop on HazoUiDialog — when true, `sizeHeight` becomes a fixed height instead of maxHeight
|
|
132
|
+
|
|
133
|
+
## [2.6.4] - 2026-03-23
|
|
134
|
+
|
|
135
|
+
### Fixed
|
|
136
|
+
- Button styling in dialog footer
|
|
137
|
+
|
|
138
|
+
## [2.6.3] - 2026-03-22
|
|
139
|
+
|
|
140
|
+
### Added
|
|
141
|
+
- Dialog `variant` prop with preset color themes (info, success, warning, destructive)
|
|
142
|
+
- Global `hazo_ui_config` system for theming buttons and headers
|
|
143
|
+
- Per-component color override props
|
|
144
|
+
|
|
145
|
+
## [2.6.2] - 2026-03-21
|
|
146
|
+
|
|
147
|
+
### Added
|
|
148
|
+
- Compositional Dialog API: re-exported shadcn/ui Dialog primitives as `HazoUiDialogRoot`, `HazoUiDialogContent`, etc. for complex layouts
|
|
149
|
+
|
|
150
|
+
## [2.6.0] - 2026-03-20
|
|
151
|
+
|
|
152
|
+
### Added
|
|
153
|
+
- **HazoUiDialog**: Standardized dialog component with comprehensive features
|
|
154
|
+
- Standardized header/footer layout with customizable content area
|
|
155
|
+
- Flexible animation system (9 presets + custom Tailwind classes)
|
|
156
|
+
- Responsive sizing with viewport-relative dimensions
|
|
157
|
+
- Background overlay customization
|
|
158
|
+
- CSS variable-based theming
|
|
159
|
+
- Header bar mode with colored full-width header
|
|
160
|
+
- Loading state and custom footer content support
|
|
161
|
+
|
|
162
|
+
## [2.5.2] - 2026-03-19
|
|
163
|
+
|
|
164
|
+
### Added
|
|
165
|
+
- Documentation updates for HazoUiDialog
|
|
166
|
+
|
|
167
|
+
## [2.5.1] - 2026-03-18
|
|
168
|
+
|
|
169
|
+
### Added
|
|
170
|
+
- `instance_id` prop documentation for command components
|
|
171
|
+
|
|
172
|
+
## [2.5.0] - 2026-03-17
|
|
173
|
+
|
|
174
|
+
### Added
|
|
175
|
+
- Multiple simultaneous command instances support
|
|
176
|
+
|
|
177
|
+
## [2.4.0] - 2026-03-15
|
|
178
|
+
|
|
179
|
+
### Added
|
|
180
|
+
- **HazoUiCommand**: Headless command/mention system for Tiptap
|
|
181
|
+
- Command pill rendering with prefix-based colors
|
|
182
|
+
- Popover suggestion menu
|
|
183
|
+
- Multiple prefix support (@, #, /, etc.)
|
|
184
|
+
- `parse_commands_from_text` and `text_to_tiptap_content` utilities
|
|
185
|
+
- **HazoUiTextbox**: Single-line input with command pill support
|
|
186
|
+
- **HazoUiTextarea**: Multi-line input with command pill support
|
|
187
|
+
|
|
188
|
+
## [2.3.0] - 2026-03-10
|
|
189
|
+
|
|
190
|
+
### Added
|
|
191
|
+
- **HazoUiRte**: Rich Text Editor component based on Tiptap v3
|
|
192
|
+
- Variable insertion system
|
|
193
|
+
- File attachment support
|
|
194
|
+
- HTML and plain text output modes
|
|
195
|
+
- Comprehensive toolbar with formatting options
|
|
196
|
+
- Font size and font family extensions
|
|
197
|
+
- Tailwind preset type declarations (`tailwind.preset.d.ts`)
|
|
198
|
+
|
|
199
|
+
### Fixed
|
|
200
|
+
- SSR hydration issues
|
|
201
|
+
|
|
202
|
+
## [2.2.0] - 2025-12-17
|
|
203
|
+
|
|
204
|
+
### Added
|
|
205
|
+
- **HazoUiMultiFilterDialog**: Added optional `title` prop (default: "Filter") to customize dialog title
|
|
206
|
+
- **HazoUiMultiFilterDialog**: Added optional `description` prop (default: "Add multiple fields to filter by...") to customize dialog description
|
|
207
|
+
- **HazoUiMultiSortDialog**: Added optional `title` prop (default: "Sort") to customize dialog title
|
|
208
|
+
- **HazoUiMultiSortDialog**: Added optional `description` prop (default: "Add multiple fields to sort by...") to customize dialog description
|
|
209
|
+
- **CommandItem**: Added hover styling (`hover:bg-accent hover:text-accent-foreground cursor-pointer`) for better user experience
|
|
210
|
+
- **Documentation**: Added troubleshooting entries for dialog backdrop and dropdown styling issues
|
|
211
|
+
|
|
212
|
+
**Design Decision**: The `title` and `description` props were added to provide better customization flexibility for different use cases. For example, a product filtering dialog might want to display "Filter Products" instead of the generic "Filter", making the interface more contextual and user-friendly. These props maintain backward compatibility by providing sensible defaults.
|
|
213
|
+
|
|
214
|
+
## [2.1.2] - 2025-12-17
|
|
215
|
+
|
|
216
|
+
### Added
|
|
217
|
+
- Color support enhancements
|
|
218
|
+
|
|
219
|
+
## [2.1.0] - 2025-12-17
|
|
220
|
+
|
|
221
|
+
### Added
|
|
222
|
+
- **HazoUiFlexInput**: New enhanced input component with type validation, character restrictions, and error messaging
|
|
223
|
+
- Supports multiple input types: mixed (text), numeric, alpha (letters only), and email
|
|
224
|
+
- Real-time character filtering for numeric and alpha types
|
|
225
|
+
- Validation on blur with clear error messages
|
|
226
|
+
- Numeric constraints: min/max value validation and decimal precision control
|
|
227
|
+
- Length constraints: configurable minimum and maximum character lengths
|
|
228
|
+
- Custom regex pattern support
|
|
229
|
+
- Optional format guide helper text
|
|
230
|
+
- Fully typed TypeScript interfaces
|
|
231
|
+
|
|
232
|
+
**Design Decision**: HazoUiFlexInput extends the shadcn Input component to provide comprehensive validation without requiring external form libraries. The validation-on-blur approach prevents disruptive real-time error messages while users are typing, improving the overall user experience.
|
|
233
|
+
|
|
234
|
+
## [2.0.0] - 2025-12-17
|
|
235
|
+
|
|
236
|
+
### Added
|
|
237
|
+
- **HazoUiFlexRadio**: New flexible radio button/icon selection component
|
|
238
|
+
- Support for single and multi-selection modes
|
|
239
|
+
- Layout options: horizontal or vertical
|
|
240
|
+
- Style variants: radio button style or icon-only button style
|
|
241
|
+
- Integration with react-icons library (supports 10+ icon sets: fa, md, hi, bi, ai, bs, fi, io, ri, tb)
|
|
242
|
+
- Label control with show/hide options
|
|
243
|
+
- Tooltips with 1-second delay
|
|
244
|
+
- Fully controlled component with value/onChange pattern
|
|
245
|
+
- TypeScript support with complete type definitions
|
|
246
|
+
- Accessibility features using Radix UI primitives
|
|
247
|
+
|
|
248
|
+
**Design Decision**: HazoUiFlexRadio was designed to provide maximum flexibility for both traditional radio button interfaces and modern icon-based selection patterns. The dual-mode support (single/multi selection) reduces the need for separate checkbox implementations.
|
|
249
|
+
|
|
250
|
+
## [1.0.0] - 2025-12-17
|
|
251
|
+
|
|
252
|
+
### Added
|
|
253
|
+
- **HazoUiMultiFilterDialog**: Multi-field filtering component
|
|
254
|
+
- Support for text, number, combobox, boolean, and date field types
|
|
255
|
+
- Operator support for number and date fields (equals, greater than, less than, etc.)
|
|
256
|
+
- Dynamic field addition and removal
|
|
257
|
+
- Built-in field validation for text length, number ranges, and decimal precision
|
|
258
|
+
- Visual feedback with active filters tooltip
|
|
259
|
+
- Clear all filters functionality
|
|
260
|
+
- Responsive design for mobile and desktop
|
|
261
|
+
- TypeScript support with complete interfaces
|
|
262
|
+
- Accessibility using Radix UI primitives
|
|
263
|
+
|
|
264
|
+
- **HazoUiMultiSortDialog**: Multi-field sorting component with drag-and-drop
|
|
265
|
+
- Multiple sort fields with priority ordering
|
|
266
|
+
- Drag-and-drop reordering using @dnd-kit
|
|
267
|
+
- Direction toggle for each field (ascending/descending)
|
|
268
|
+
- Visual feedback during drag operations
|
|
269
|
+
- Clear all sorts functionality
|
|
270
|
+
- Tooltip display of active sort configuration
|
|
271
|
+
- Keyboard navigation support
|
|
272
|
+
- Responsive design
|
|
273
|
+
- TypeScript support
|
|
274
|
+
- Accessibility features
|
|
275
|
+
|
|
276
|
+
- **Base UI Components**: Shadcn/ui components integration
|
|
277
|
+
- Button, Dialog, Command, Popover, Select, Input, Label, Switch, Tooltip, Calendar
|
|
278
|
+
- Tailwind CSS theming with CSS variable support
|
|
279
|
+
- Light and dark mode support
|
|
280
|
+
|
|
281
|
+
- **Build Configuration**:
|
|
282
|
+
- tsup bundler for ESM + CJS outputs
|
|
283
|
+
- TypeScript declarations generation
|
|
284
|
+
- Tree-shakeable exports
|
|
285
|
+
- "use client" directive for Next.js compatibility
|
|
286
|
+
|
|
287
|
+
- **Development Tools**:
|
|
288
|
+
- Storybook integration for component development
|
|
289
|
+
- Next.js dev app for integration testing
|
|
290
|
+
- TypeScript strict mode
|
|
291
|
+
- ESLint configuration
|
|
292
|
+
|
|
293
|
+
**Design Decision**: The library was built on shadcn/ui and Radix UI primitives to ensure accessibility, maintainability, and consistency with modern React patterns. The choice of @dnd-kit for drag-and-drop provides excellent accessibility support compared to alternatives like react-beautiful-dnd.
|
|
294
|
+
|
|
295
|
+
## [Unreleased]
|
|
296
|
+
|
|
297
|
+
### Planned
|
|
298
|
+
- Additional field types for filtering (time, datetime, multi-select)
|
|
299
|
+
- Preset filter configurations for common use cases
|
|
300
|
+
- Export/import filter configurations
|
|
301
|
+
- Advanced sort options (null handling, case sensitivity)
|
|
302
|
+
- Performance optimizations for large datasets
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## Version History Reference
|
|
307
|
+
|
|
308
|
+
- **2.7.3** - Tailwind v4 migration (internal build), tw-animate-css, canonical version sync
|
|
309
|
+
- **2.7.2** - HazoUiPillRadio component
|
|
310
|
+
- **2.7.1** - Rename 'danger' to 'destructive', extract shared animations, shadcn/ui re-exports
|
|
311
|
+
- **2.7.0** - HazoUiConfirmDialog, shadcn/ui primitive re-exports
|
|
312
|
+
- **2.6.x** - HazoUiDialog variants, compositional API, fixedSize, bug fixes
|
|
313
|
+
- **2.5.x** - Multiple command instances, dialog docs
|
|
314
|
+
- **2.4.0** - HazoUiCommand, HazoUiTextbox, HazoUiTextarea
|
|
315
|
+
- **2.3.0** - HazoUiRte rich text editor
|
|
316
|
+
- **2.2.0** - Customizable dialog titles and descriptions
|
|
317
|
+
- **2.1.0** - HazoUiFlexInput component
|
|
318
|
+
- **2.0.0** - HazoUiFlexRadio component
|
|
319
|
+
- **1.0.0** - Initial release with filter and sort dialogs
|
package/README.md
CHANGED
|
@@ -202,6 +202,22 @@ The following components support both global config and prop-level color overrid
|
|
|
202
202
|
|
|
203
203
|
- **[HazoUiConfirmDialog](#hazouiconfirmdialog)** - A compact, opinionated confirmation dialog with accent top border, variant system (destructive, warning, info, success), async loading support, and configurable buttons. Perfect for delete confirmations, unsaved changes warnings, and simple acknowledgments.
|
|
204
204
|
|
|
205
|
+
- **[Drawer](#drawer)** - A `vaul`-backed bottom sheet primitive for mobile UIs. Pair with `useMediaQuery` to swap between `Dialog` and `Drawer` based on viewport width.
|
|
206
|
+
|
|
207
|
+
### State Primitives (v2.10.0)
|
|
208
|
+
|
|
209
|
+
Lightweight, opinionated components for the four ubiquitous async states: **loading**, **empty**, **error**, and **success**.
|
|
210
|
+
|
|
211
|
+
- **[Skeleton](#skeleton)** family (`Skeleton`, `SkeletonCircle`, `SkeletonBar`, `SkeletonRect`, `SkeletonGroup`) - Shimmer placeholders. Respects `prefers-reduced-motion`.
|
|
212
|
+
- **[EmptyState](#emptystate)** - Icon + title + description + CTA for empty lists, search misses, no-data screens.
|
|
213
|
+
- **[ErrorBanner](#errorbanner)** - Inline `warning` or `error` strip with optional title, action button, and dismiss.
|
|
214
|
+
- **[ErrorPage](#errorpage)** - Full-page error fallback with title, description, error code tag, correlation id, and CTAs.
|
|
215
|
+
- **[LoadingTimeout](#loadingtimeout)** - Wraps a loading region and escalates messaging at 5s / 15s / 30s thresholds before showing a retry banner.
|
|
216
|
+
- **[ProgressiveImage](#progressiveimage)** - Three-stage image render: grey placeholder → blurred LQIP → sharp final image.
|
|
217
|
+
- **[HazoUiToaster + toast helpers](#toasts-hazouitoaster--successtoast--errortoast)** - `sonner`-backed toaster with `successToast()` and `errorToast()` imperative helpers.
|
|
218
|
+
- **[useLoadingState](#useloadingstate)** hook - `{ isLoading, setLoading, withLoading }` with an async wrapper.
|
|
219
|
+
- **[useErrorDisplay](#useerrordisplay)** hook - Passive `{ error, setError, clearError }` that coerces `Error` instances to strings.
|
|
220
|
+
|
|
205
221
|
### shadcn/ui Primitive Re-exports
|
|
206
222
|
|
|
207
223
|
All shadcn/ui base components are re-exported from hazo_ui, so sibling hazo_* packages (and consumers) can import UI primitives from a single source without installing shadcn/ui separately:
|
|
@@ -2965,6 +2981,340 @@ function FormDialog() {
|
|
|
2965
2981
|
| `footerClassName` | `string` | - | Footer CSS classes |
|
|
2966
2982
|
| `showCloseButton` | `boolean` | `true` | Show X close button |
|
|
2967
2983
|
|
|
2984
|
+
## Drawer
|
|
2985
|
+
|
|
2986
|
+
A vaul-backed bottom sheet primitive. Intended for mobile UIs; on desktop, prefer `Dialog`.
|
|
2987
|
+
|
|
2988
|
+
```typescript
|
|
2989
|
+
import {
|
|
2990
|
+
Drawer,
|
|
2991
|
+
DrawerTrigger,
|
|
2992
|
+
DrawerContent,
|
|
2993
|
+
DrawerHeader,
|
|
2994
|
+
DrawerTitle,
|
|
2995
|
+
DrawerDescription,
|
|
2996
|
+
DrawerFooter,
|
|
2997
|
+
DrawerClose,
|
|
2998
|
+
useMediaQuery,
|
|
2999
|
+
} from "hazo_ui";
|
|
3000
|
+
|
|
3001
|
+
function Example() {
|
|
3002
|
+
const is_mobile = useMediaQuery("(max-width: 640px)");
|
|
3003
|
+
|
|
3004
|
+
if (!is_mobile) {
|
|
3005
|
+
// Render a Dialog instead on desktop
|
|
3006
|
+
return null;
|
|
3007
|
+
}
|
|
3008
|
+
|
|
3009
|
+
return (
|
|
3010
|
+
<Drawer>
|
|
3011
|
+
<DrawerTrigger asChild>
|
|
3012
|
+
<button>Open</button>
|
|
3013
|
+
</DrawerTrigger>
|
|
3014
|
+
<DrawerContent>
|
|
3015
|
+
<DrawerHeader>
|
|
3016
|
+
<DrawerTitle>Title</DrawerTitle>
|
|
3017
|
+
<DrawerDescription>Optional description</DrawerDescription>
|
|
3018
|
+
</DrawerHeader>
|
|
3019
|
+
<DrawerFooter>
|
|
3020
|
+
<DrawerClose asChild>
|
|
3021
|
+
<button>Close</button>
|
|
3022
|
+
</DrawerClose>
|
|
3023
|
+
</DrawerFooter>
|
|
3024
|
+
</DrawerContent>
|
|
3025
|
+
</Drawer>
|
|
3026
|
+
);
|
|
3027
|
+
}
|
|
3028
|
+
```
|
|
3029
|
+
|
|
3030
|
+
**Behavior:**
|
|
3031
|
+
- Drag the grab handle downward to dismiss
|
|
3032
|
+
- ESC closes
|
|
3033
|
+
- Click overlay to dismiss
|
|
3034
|
+
- `max-h-[96dvh]` keeps the sheet usable on iOS Safari when the address bar toggles
|
|
3035
|
+
- Focus trap, `aria-labelledby`, and background `aria-hidden` are wired automatically by vaul
|
|
3036
|
+
|
|
3037
|
+
**Not yet supported (deferred):** snap points, side-anchored drawers, nested drawers.
|
|
3038
|
+
|
|
3039
|
+
---
|
|
3040
|
+
|
|
3041
|
+
## State Primitives
|
|
3042
|
+
|
|
3043
|
+
Components and hooks for the four ubiquitous async states. All are exported flat (unprefixed) from the package root — `import { Skeleton, EmptyState, ErrorBanner, ErrorPage, LoadingTimeout, ProgressiveImage, HazoUiToaster, successToast, errorToast, useLoadingState, useErrorDisplay } from "hazo_ui"`.
|
|
3044
|
+
|
|
3045
|
+
### Skeleton
|
|
3046
|
+
|
|
3047
|
+
Shimmer placeholders for loading content. The shimmer animation respects `prefers-reduced-motion` (renders as a static grey block instead).
|
|
3048
|
+
|
|
3049
|
+
```tsx
|
|
3050
|
+
import { Skeleton, SkeletonCircle, SkeletonBar, SkeletonRect, SkeletonGroup } from "hazo_ui";
|
|
3051
|
+
|
|
3052
|
+
<SkeletonGroup label="Loading user profile">
|
|
3053
|
+
<div className="flex items-center gap-3">
|
|
3054
|
+
<SkeletonCircle size={40} />
|
|
3055
|
+
<div className="flex-1 space-y-2">
|
|
3056
|
+
<SkeletonBar width="60%" height={14} />
|
|
3057
|
+
<SkeletonBar width="40%" height={10} />
|
|
3058
|
+
</div>
|
|
3059
|
+
</div>
|
|
3060
|
+
<SkeletonRect height={120} radius={8} />
|
|
3061
|
+
</SkeletonGroup>
|
|
3062
|
+
```
|
|
3063
|
+
|
|
3064
|
+
| Variant | Props |
|
|
3065
|
+
|---|---|
|
|
3066
|
+
| `Skeleton` | All standard `<div>` props. Base shimmer block. |
|
|
3067
|
+
| `SkeletonCircle` | `size?: number` (default 40), `className?: string` |
|
|
3068
|
+
| `SkeletonBar` | `width?: number \| string` (default "100%"), `height?: number` (default 12), `className?: string` |
|
|
3069
|
+
| `SkeletonRect` | `width?`, `height?`, `radius?: number \| string` (default 6), `className?: string` |
|
|
3070
|
+
| `SkeletonGroup` | `label?: string` (default "Loading content"), `children: ReactNode` — wraps a region with `role="status"` + `aria-busy` + visually-hidden label. |
|
|
3071
|
+
|
|
3072
|
+
### EmptyState
|
|
3073
|
+
|
|
3074
|
+
Standardized empty-list / no-data / no-results display.
|
|
3075
|
+
|
|
3076
|
+
```tsx
|
|
3077
|
+
import { EmptyState, Button } from "hazo_ui";
|
|
3078
|
+
import { InboxIcon } from "lucide-react";
|
|
3079
|
+
|
|
3080
|
+
<EmptyState
|
|
3081
|
+
icon={<InboxIcon />}
|
|
3082
|
+
title="No messages yet"
|
|
3083
|
+
description="When you receive a message, it'll show up here."
|
|
3084
|
+
action={<Button onClick={onCompose}>Send your first message</Button>}
|
|
3085
|
+
size="md"
|
|
3086
|
+
/>
|
|
3087
|
+
```
|
|
3088
|
+
|
|
3089
|
+
| Prop | Type | Default | Description |
|
|
3090
|
+
|---|---|---|---|
|
|
3091
|
+
| `title` | `string` | **required** | Main heading |
|
|
3092
|
+
| `icon` | `ReactNode` | — | Icon element (recommended 48×48) |
|
|
3093
|
+
| `description` | `ReactNode` | — | Secondary description |
|
|
3094
|
+
| `action` | `ReactNode` | — | CTA region (typically a `<Button>`) |
|
|
3095
|
+
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | Visual size for inline cards (`sm`) vs full pages (`lg`) |
|
|
3096
|
+
| `className` | `string` | — | Additional classes |
|
|
3097
|
+
|
|
3098
|
+
### ErrorBanner
|
|
3099
|
+
|
|
3100
|
+
Inline error or warning strip. `role="alert"`, with `aria-live="assertive"` for errors / `"polite"` for warnings.
|
|
3101
|
+
|
|
3102
|
+
```tsx
|
|
3103
|
+
import { ErrorBanner, Button } from "hazo_ui";
|
|
3104
|
+
|
|
3105
|
+
<ErrorBanner
|
|
3106
|
+
severity="error"
|
|
3107
|
+
title="Couldn't save your changes"
|
|
3108
|
+
message="We hit a network error. Your draft is safe locally."
|
|
3109
|
+
action={<Button size="sm" onClick={onRetry}>Retry</Button>}
|
|
3110
|
+
onDismiss={() => setBannerVisible(false)}
|
|
3111
|
+
/>
|
|
3112
|
+
|
|
3113
|
+
<ErrorBanner severity="warning" message="Your session expires in 2 minutes." />
|
|
3114
|
+
```
|
|
3115
|
+
|
|
3116
|
+
| Prop | Type | Default | Description |
|
|
3117
|
+
|---|---|---|---|
|
|
3118
|
+
| `message` | `ReactNode` | **required** | Body text |
|
|
3119
|
+
| `severity` | `"warning" \| "error"` | `"error"` | Drives colour & icon |
|
|
3120
|
+
| `title` | `string` | — | Bold heading above the message |
|
|
3121
|
+
| `icon` | `ReactNode` | auto | Override the auto-selected `AlertTriangle` / `OctagonAlert` |
|
|
3122
|
+
| `action` | `ReactNode` | — | CTA region (typically a `<Button>`) |
|
|
3123
|
+
| `onDismiss` | `() => void` | — | When provided, renders a dismiss X button |
|
|
3124
|
+
| `className` | `string` | — | Additional classes |
|
|
3125
|
+
|
|
3126
|
+
### ErrorPage
|
|
3127
|
+
|
|
3128
|
+
Full-page error fallback, ideal for route-level error boundaries.
|
|
3129
|
+
|
|
3130
|
+
```tsx
|
|
3131
|
+
import { ErrorPage, Button } from "hazo_ui";
|
|
3132
|
+
|
|
3133
|
+
<ErrorPage
|
|
3134
|
+
title="Something went wrong"
|
|
3135
|
+
description="We couldn't load this page. The issue has been reported."
|
|
3136
|
+
errorCode="500"
|
|
3137
|
+
correlationId="req_a8f3c12e4d"
|
|
3138
|
+
actions={
|
|
3139
|
+
<>
|
|
3140
|
+
<Button onClick={onRetry}>Try again</Button>
|
|
3141
|
+
<Button variant="outline" onClick={onGoHome}>Go home</Button>
|
|
3142
|
+
</>
|
|
3143
|
+
}
|
|
3144
|
+
/>
|
|
3145
|
+
```
|
|
3146
|
+
|
|
3147
|
+
| Prop | Type | Default | Description |
|
|
3148
|
+
|---|---|---|---|
|
|
3149
|
+
| `title` | `string` | `"Something went wrong"` | Main heading |
|
|
3150
|
+
| `description` | `ReactNode` | — | Explanation paragraph(s) |
|
|
3151
|
+
| `errorCode` | `string` | — | Short symbolic code rendered as a tag (`"500"`, `"NOT_FOUND"`) |
|
|
3152
|
+
| `correlationId` | `string` | — | Correlation id (typically from `hazo_logs`) — rendered in a copyable mono block |
|
|
3153
|
+
| `actions` | `ReactNode` | — | CTA region |
|
|
3154
|
+
| `illustration` | `ReactNode` | `<OctagonAlert />` | Override the default icon |
|
|
3155
|
+
| `className` | `string` | — | Additional classes |
|
|
3156
|
+
|
|
3157
|
+
### LoadingTimeout
|
|
3158
|
+
|
|
3159
|
+
Wraps a loading region and escalates messaging if the load takes too long. Four phases:
|
|
3160
|
+
|
|
3161
|
+
| Phase | When | What renders |
|
|
3162
|
+
|---|---|---|
|
|
3163
|
+
| `silent` | 0 – 5s | The provided `skeleton` (no message) |
|
|
3164
|
+
| `gentle` | 5s – 15s | Skeleton + "Loading {label}…" |
|
|
3165
|
+
| `firm` | 15s – 30s | Skeleton + "Still working on it — almost there." |
|
|
3166
|
+
| `expired` | 30s+ | `<ErrorBanner severity="error">` with a "Try again" button |
|
|
3167
|
+
|
|
3168
|
+
```tsx
|
|
3169
|
+
import { LoadingTimeout, SkeletonGroup, SkeletonBar } from "hazo_ui";
|
|
3170
|
+
|
|
3171
|
+
<LoadingTimeout
|
|
3172
|
+
active={isLoading}
|
|
3173
|
+
label="dashboard"
|
|
3174
|
+
onRetry={refetch}
|
|
3175
|
+
skeleton={
|
|
3176
|
+
<SkeletonGroup>
|
|
3177
|
+
<SkeletonBar width="80%" />
|
|
3178
|
+
<SkeletonBar width="60%" />
|
|
3179
|
+
</SkeletonGroup>
|
|
3180
|
+
}
|
|
3181
|
+
>
|
|
3182
|
+
<Dashboard data={data} />
|
|
3183
|
+
</LoadingTimeout>
|
|
3184
|
+
```
|
|
3185
|
+
|
|
3186
|
+
| Prop | Type | Default | Description |
|
|
3187
|
+
|---|---|---|---|
|
|
3188
|
+
| `active` | `boolean` | **required** | When `true`, runs the timeout escalation; when `false`, renders `children` |
|
|
3189
|
+
| `children` | `ReactNode` | — | Content shown when `active` is `false` |
|
|
3190
|
+
| `skeleton` | `ReactNode` | — | Placeholder rendered during the `silent`/`gentle`/`firm` phases |
|
|
3191
|
+
| `onRetry` | `() => void` | — | Called when the user clicks Retry in the `expired` phase |
|
|
3192
|
+
| `thresholds` | `{ gentle?, firm?, expired? }` | `{5000, 15000, 30000}` | Override timeout thresholds (ms) |
|
|
3193
|
+
| `label` | `string` | `"content"` | Used in gentle/firm/expired messages |
|
|
3194
|
+
| `className` | `string` | — | Additional classes |
|
|
3195
|
+
|
|
3196
|
+
### ProgressiveImage
|
|
3197
|
+
|
|
3198
|
+
Three-stage image render: grey placeholder → blurred low-quality image (`lqip`) → sharp final image. Sets a stable container box so layout doesn't shift.
|
|
3199
|
+
|
|
3200
|
+
```tsx
|
|
3201
|
+
import { ProgressiveImage } from "hazo_ui";
|
|
3202
|
+
|
|
3203
|
+
<ProgressiveImage
|
|
3204
|
+
src="/photos/large.jpg"
|
|
3205
|
+
lqip="data:image/jpeg;base64,/9j/4AAQ…"
|
|
3206
|
+
alt="Sunset over the lake"
|
|
3207
|
+
width={400}
|
|
3208
|
+
height={300}
|
|
3209
|
+
fit="cover"
|
|
3210
|
+
loading="lazy"
|
|
3211
|
+
/>
|
|
3212
|
+
```
|
|
3213
|
+
|
|
3214
|
+
| Prop | Type | Default | Description |
|
|
3215
|
+
|---|---|---|---|
|
|
3216
|
+
| `src` | `string` | **required** | Final image src |
|
|
3217
|
+
| `alt` | `string` | **required** | Alt text (empty string allowed for decorative) |
|
|
3218
|
+
| `width` | `number \| string` | **required** | Container width (px or any CSS length) |
|
|
3219
|
+
| `height` | `number \| string` | **required** | Container height |
|
|
3220
|
+
| `lqip` | `string` | — | Low-quality placeholder (data URL or tiny image URL) |
|
|
3221
|
+
| `loading` | `"eager" \| "lazy"` | `"lazy"` | Native loading attribute |
|
|
3222
|
+
| `fit` | `"cover" \| "contain" \| "fill" \| "none" \| "scale-down"` | `"cover"` | Object-fit for the final image |
|
|
3223
|
+
| `onLoad` | `() => void` | — | Called when the final image loads |
|
|
3224
|
+
| `onError` | `() => void` | — | Called if the final image errors |
|
|
3225
|
+
| `className` | `string` | — | Additional classes |
|
|
3226
|
+
|
|
3227
|
+
### Toasts (HazoUiToaster + successToast / errorToast)
|
|
3228
|
+
|
|
3229
|
+
A `sonner`-backed toaster plus two opinionated imperative helpers.
|
|
3230
|
+
|
|
3231
|
+
**Mount the toaster once** near the root of your app:
|
|
3232
|
+
|
|
3233
|
+
```tsx
|
|
3234
|
+
import { HazoUiToaster } from "hazo_ui";
|
|
3235
|
+
|
|
3236
|
+
export default function RootLayout({ children }) {
|
|
3237
|
+
return (
|
|
3238
|
+
<>
|
|
3239
|
+
{children}
|
|
3240
|
+
<HazoUiToaster position="bottom-right" />
|
|
3241
|
+
</>
|
|
3242
|
+
);
|
|
3243
|
+
}
|
|
3244
|
+
```
|
|
3245
|
+
|
|
3246
|
+
**Fire toasts imperatively from anywhere:**
|
|
3247
|
+
|
|
3248
|
+
```tsx
|
|
3249
|
+
import { successToast, errorToast } from "hazo_ui";
|
|
3250
|
+
|
|
3251
|
+
await save();
|
|
3252
|
+
successToast({ title: "Saved", description: "Your changes have been published." });
|
|
3253
|
+
|
|
3254
|
+
try { await sync(); } catch (e) {
|
|
3255
|
+
errorToast({
|
|
3256
|
+
title: "Sync failed",
|
|
3257
|
+
description: "Check your connection and try again.",
|
|
3258
|
+
action: { label: "Retry", onClick: () => sync() },
|
|
3259
|
+
});
|
|
3260
|
+
}
|
|
3261
|
+
```
|
|
3262
|
+
|
|
3263
|
+
`HazoUiToaster` props:
|
|
3264
|
+
|
|
3265
|
+
| Prop | Type | Default |
|
|
3266
|
+
|---|---|---|
|
|
3267
|
+
| `position` | `"top-left" \| "top-right" \| "bottom-left" \| "bottom-right" \| "top-center" \| "bottom-center"` | `"bottom-right"` |
|
|
3268
|
+
| `closeButton` | `boolean` | `true` |
|
|
3269
|
+
| `visibleToasts` | `number` | `5` |
|
|
3270
|
+
|
|
3271
|
+
`ToastOptions` (for `successToast` / `errorToast`):
|
|
3272
|
+
|
|
3273
|
+
| Prop | Type | Default |
|
|
3274
|
+
|---|---|---|
|
|
3275
|
+
| `title` | `string` | **required** |
|
|
3276
|
+
| `description` | `string` | — |
|
|
3277
|
+
| `duration` | `number` (ms) | `3000` success / `5000` error |
|
|
3278
|
+
| `action` | `{ label: string; onClick: () => void }` | — |
|
|
3279
|
+
|
|
3280
|
+
Also exports `rawToast` (re-export of sonner's `toast`) for advanced use cases.
|
|
3281
|
+
|
|
3282
|
+
### useLoadingState
|
|
3283
|
+
|
|
3284
|
+
Hook that returns a controlled loading flag plus an async wrapper.
|
|
3285
|
+
|
|
3286
|
+
```tsx
|
|
3287
|
+
import { useLoadingState } from "hazo_ui";
|
|
3288
|
+
|
|
3289
|
+
const { isLoading, setLoading, withLoading } = useLoadingState(false);
|
|
3290
|
+
|
|
3291
|
+
async function onSubmit() {
|
|
3292
|
+
await withLoading(async () => {
|
|
3293
|
+
await api.save(data);
|
|
3294
|
+
});
|
|
3295
|
+
}
|
|
3296
|
+
|
|
3297
|
+
return <Button disabled={isLoading} onClick={onSubmit}>{isLoading ? "Saving…" : "Save"}</Button>;
|
|
3298
|
+
```
|
|
3299
|
+
|
|
3300
|
+
Returns `{ isLoading: boolean; setLoading: (v: boolean) => void; withLoading: <T>(fn: () => Promise<T>) => Promise<T> }`. `withLoading` sets the flag to `true` before the call and clears it in a `finally` block — even on errors.
|
|
3301
|
+
|
|
3302
|
+
### useErrorDisplay
|
|
3303
|
+
|
|
3304
|
+
Passive error state. Coerces `Error` instances to their `.message`, strings pass through, anything else is `String()`-cast.
|
|
3305
|
+
|
|
3306
|
+
```tsx
|
|
3307
|
+
import { useErrorDisplay, ErrorBanner } from "hazo_ui";
|
|
3308
|
+
|
|
3309
|
+
const { error, setError, clearError } = useErrorDisplay();
|
|
3310
|
+
|
|
3311
|
+
try { await save(); } catch (e) { setError(e); }
|
|
3312
|
+
|
|
3313
|
+
return error ? <ErrorBanner message={error} onDismiss={clearError} /> : null;
|
|
3314
|
+
```
|
|
3315
|
+
|
|
3316
|
+
Returns `{ error: string | null; setError: (v: unknown) => void; clearError: () => void }`. Pass `null` (or any nullish value) to `setError` to clear.
|
|
3317
|
+
|
|
2968
3318
|
---
|
|
2969
3319
|
|
|
2970
3320
|
## Troubleshooting
|