@janbox/storefront-ui 2.0.29 → 2.0.31

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.
Files changed (76) hide show
  1. package/README.md +692 -0
  2. package/dist/lib/accordion/README.md +81 -0
  3. package/dist/lib/avatar/README.md +74 -0
  4. package/dist/lib/badge/README.md +58 -0
  5. package/dist/lib/box/README.md +69 -0
  6. package/dist/lib/breadcrumbs/README.md +70 -0
  7. package/dist/lib/button/README.md +115 -0
  8. package/dist/lib/cascader/README.md +128 -0
  9. package/dist/lib/checkbox/README.md +74 -0
  10. package/dist/lib/checkbox/checkbox.js +107 -12
  11. package/dist/lib/checkbox/types.d.ts +2 -2
  12. package/dist/lib/chip/README.md +72 -0
  13. package/dist/lib/collapse/README.md +78 -0
  14. package/dist/lib/container/README.md +59 -0
  15. package/dist/lib/count-up/README.md +52 -0
  16. package/dist/lib/countdown-timer/README.md +77 -0
  17. package/dist/lib/date-picker/README.md +94 -0
  18. package/dist/lib/dialog/README.md +109 -0
  19. package/dist/lib/drawer/README.md +97 -0
  20. package/dist/lib/filter-panel/README.md +146 -0
  21. package/dist/lib/flag/README.md +58 -0
  22. package/dist/lib/flexbox/README.md +59 -0
  23. package/dist/lib/floating/README.md +109 -0
  24. package/dist/lib/form-helper-text/README.md +54 -0
  25. package/dist/lib/form-label/README.md +50 -0
  26. package/dist/lib/grid/README.md +72 -0
  27. package/dist/lib/highlight-words/README.md +64 -0
  28. package/dist/lib/highlight-words/highlight-words.js +1 -2
  29. package/dist/lib/icon/README.md +69 -0
  30. package/dist/lib/icon-button/README.md +92 -0
  31. package/dist/lib/image/README.md +80 -0
  32. package/dist/lib/input/README.md +118 -0
  33. package/dist/lib/input-mask/README.md +88 -0
  34. package/dist/lib/input-number/README.md +92 -0
  35. package/dist/lib/input-range/README.md +85 -0
  36. package/dist/lib/lightbox/README.md +107 -0
  37. package/dist/lib/linear-progress/README.md +54 -0
  38. package/dist/lib/link/README.md +67 -0
  39. package/dist/lib/loading/README.md +65 -0
  40. package/dist/lib/marquee/README.md +83 -0
  41. package/dist/lib/marquee/marquee/marquee.js +1 -1
  42. package/dist/lib/menu/README.md +92 -0
  43. package/dist/lib/multiple-select/README.md +108 -0
  44. package/dist/lib/nav-link/README.md +61 -0
  45. package/dist/lib/notifications/README.md +103 -0
  46. package/dist/lib/otp-input/README.md +71 -0
  47. package/dist/lib/pagination/README.md +84 -0
  48. package/dist/lib/phone-input/README.md +80 -0
  49. package/dist/lib/popover/README.md +93 -0
  50. package/dist/lib/price-label/README.md +78 -0
  51. package/dist/lib/primitive/README.md +80 -0
  52. package/dist/lib/progress/README.md +50 -0
  53. package/dist/lib/radio-button/README.md +89 -0
  54. package/dist/lib/radio-button/radio-button.js +98 -7
  55. package/dist/lib/ripple-effect/README.md +66 -0
  56. package/dist/lib/select/README.md +124 -0
  57. package/dist/lib/star-rating/README.md +67 -0
  58. package/dist/lib/stepper/README.md +99 -0
  59. package/dist/lib/suspense-query/README.md +78 -0
  60. package/dist/lib/swiper/README.md +99 -0
  61. package/dist/lib/switch/README.md +73 -0
  62. package/dist/lib/switch/switch.d.ts +1 -1
  63. package/dist/lib/switch/switch.js +110 -11
  64. package/dist/lib/table/README.md +115 -0
  65. package/dist/lib/tabs/README.md +103 -0
  66. package/dist/lib/text/README.md +59 -0
  67. package/dist/lib/textarea/README.md +76 -0
  68. package/dist/lib/time-picker/README.md +100 -0
  69. package/dist/lib/tooltip/README.md +106 -0
  70. package/dist/lib/unordered-list/README.md +85 -0
  71. package/dist/lib/video/README.md +88 -0
  72. package/dist/style.css +1 -244
  73. package/package.json +5 -5
  74. package/dist/lib/checkbox/checkbox.module.scss.js +0 -23
  75. package/dist/lib/radio-button/radio-button.module.scss.js +0 -17
  76. package/dist/lib/switch/switch.module.scss.js +0 -14
@@ -0,0 +1,109 @@
1
+ # Dialog
2
+
3
+ A modal dialog component built on top of the Floating primitive, used to display overlay content that requires user attention or interaction. It supports responsive sizing, a backdrop overlay with blur, scroll lock, and composable sub-components (DialogHeader, DialogBody, DialogFooter, DialogClose) for flexible layout.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { Dialog } from '@janbox/storefront-ui';
9
+ ```
10
+
11
+ ## Props
12
+
13
+ | Prop | Type | Default | Description |
14
+ |------|------|---------|-------------|
15
+ | `size` | `DialogSize (keyof typeof breakpoint \| number)` | `'sm'` | Controls the width of the dialog panel. Accepts breakpoint keys (`'xs' \| 'sm' \| 'md' \| 'lg'`) mapped to pixel values, or an explicit pixel number. `'xs'` maps to 480px; other keys use the breakpoint minWidth. |
16
+ | `sm` | `{ size?: DialogSize }` | - | Responsive override for the dialog size at the sm breakpoint (≥768px). |
17
+ | `md` | `{ size?: DialogSize }` | - | Responsive override for the dialog size at the md breakpoint (≥1280px). |
18
+ | `lg` | `{ size?: DialogSize }` | - | Responsive override for the dialog size at the lg breakpoint (≥1680px). |
19
+ | `open` | `boolean` | - | Controlled open state of the dialog. When provided, the component becomes controlled. |
20
+ | `onOpenChange` | `UseFloatingOptions['onOpenChange']` | - | Callback fired when the open state changes (e.g., when the user dismisses the dialog). |
21
+ | `placement` | `Placement` | - | Floating UI placement for the dialog panel. Inherited from FloatingProps. |
22
+ | `clickProps` | `UseClickProps` | - | Options passed to the floating-ui useClick interaction hook. |
23
+ | `dismissProps` | `UseDismissProps` | `{ enabled: true }` | Options for the floating-ui useDismiss hook. Dismiss on outside click/Escape is enabled by default. |
24
+ | `hoverProps` | `UseHoverProps` | - | Options passed to the floating-ui useHover interaction hook. |
25
+ | `focusProps` | `UseFocusProps` | - | Options passed to the floating-ui useFocus interaction hook. |
26
+ | `offsetOptions` | `OffsetOptions` | - | Offset configuration for floating-ui positioning. |
27
+ | `sizeOptions` | `SizeOptions` | - | Size middleware options for floating-ui. |
28
+ | `autoUpdateOptions` | `AutoUpdateOptions` | - | Options for floating-ui autoUpdate (controls when position recalculates). |
29
+ | `elements` | `UseFloatingOptions['elements']` | - | Custom reference/floating element overrides for floating-ui. |
30
+ | `ref` | `React.Ref<FloatingRef>` | - | Ref forwarded to the underlying floating-ui UseFloatingReturn object. |
31
+ | `children` | `React.ReactNode` | - | Dialog content. Should include a FloatingTrigger and one or more of DialogHeader, DialogBody, DialogFooter. |
32
+
33
+ ## Examples
34
+
35
+ ### Default dialog with header, body, and footer
36
+
37
+ ```tsx
38
+ <Dialog>
39
+ <FloatingTrigger>
40
+ <button>Open Dialog</button>
41
+ </FloatingTrigger>
42
+ <DialogHeader closeable>Dialog Title</DialogHeader>
43
+ <DialogBody>
44
+ <p>This is the dialog body content.</p>
45
+ </DialogBody>
46
+ <DialogFooter>
47
+ <button>Cancel</button>
48
+ <button>Confirm</button>
49
+ </DialogFooter>
50
+ </Dialog>
51
+ ```
52
+
53
+ ### Small dialog without footer
54
+
55
+ ```tsx
56
+ <Dialog size="sm">
57
+ <FloatingTrigger>
58
+ <button>Open Small Dialog</button>
59
+ </FloatingTrigger>
60
+ <DialogHeader closeable>Small Dialog</DialogHeader>
61
+ <DialogBody>
62
+ <p>Small dialog content.</p>
63
+ </DialogBody>
64
+ </Dialog>
65
+ ```
66
+
67
+ ### Controlled dialog with responsive sizing
68
+
69
+ ```tsx
70
+ const [open, setOpen] = useState(false);
71
+
72
+ <Dialog
73
+ open={open}
74
+ onOpenChange={setOpen}
75
+ size="xs"
76
+ md={{ size: 'md' }}
77
+ lg={{ size: 'lg' }}
78
+ >
79
+ <FloatingTrigger>
80
+ <button onClick={() => setOpen(true)}>Open</button>
81
+ </FloatingTrigger>
82
+ <DialogHeader closeable>Responsive Dialog</DialogHeader>
83
+ <DialogBody>
84
+ <p>Width changes at each breakpoint.</p>
85
+ </DialogBody>
86
+ </Dialog>
87
+ ```
88
+
89
+ ## Related Components
90
+
91
+ - [Floating](../floating/README.md)
92
+ - [FloatingContent](../floatingcontent/README.md)
93
+ - [FloatingClose](../floatingclose/README.md)
94
+ - [DialogHeader](../dialogheader/README.md)
95
+ - [DialogBody](../dialogbody/README.md)
96
+ - [DialogFooter](../dialogfooter/README.md)
97
+ - [DialogClose](../dialogclose/README.md)
98
+ - [IconButton](../iconbutton/README.md)
99
+ - [ScrollView](../scrollview/README.md)
100
+
101
+ ## Dependencies
102
+
103
+ - @floating-ui/react
104
+
105
+ ## See Also
106
+
107
+ - [Component Source](./index.ts)
108
+ - [Storybook Stories](./dialog.stories.tsx)
109
+ - [Main README](../../README.md)
@@ -0,0 +1,97 @@
1
+ # Drawer
2
+
3
+ A slide-in panel component that animates in from a configurable edge (left, right, top, or bottom) of the screen. Built on top of the Floating system, it provides overlay backdrop, scroll locking, and dismiss-on-click-outside behavior out of the box.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { Drawer } from '@janbox/storefront-ui';
9
+ ```
10
+
11
+ ## Props
12
+
13
+ | Prop | Type | Default | Description |
14
+ |------|------|---------|-------------|
15
+ | `placement` | `'left' \| 'right' \| 'top' \| 'bottom'` | `'left'` | The edge from which the drawer slides in. |
16
+ | `open` | `boolean` | - | Controlled open state of the drawer. |
17
+ | `onOpenChange` | `(open: boolean, event?: Event, reason?: OpenChangeReason) => void` | - | Callback fired when the open state changes (e.g. user dismisses the drawer). |
18
+ | `children` | `React.ReactNode` | - | Drawer content. Should include a `FloatingTrigger` to open the drawer and a `DrawerClose` to close it. The panel content (rendered inside the sliding pane) is placed as siblings to `FloatingTrigger`. |
19
+ | `clickProps` | `UseClickProps` | - | Options forwarded to `@floating-ui/react` `useClick` interaction hook. |
20
+ | `dismissProps` | `UseDismissProps` | - | Options forwarded to `@floating-ui/react` `useDismiss` hook. Dismiss on outside click is enabled by default. |
21
+ | `hoverProps` | `UseHoverProps` | - | Options forwarded to `@floating-ui/react` `useHover` interaction hook. |
22
+ | `focusProps` | `UseFocusProps` | - | Options forwarded to `@floating-ui/react` `useFocus` interaction hook. |
23
+ | `offsetOptions` | `OffsetOptions` | - | Offset distance between trigger and floating content, forwarded to `@floating-ui/react`. |
24
+ | `sizeOptions` | `SizeOptions` | - | Size middleware options forwarded to `@floating-ui/react`. |
25
+ | `autoUpdateOptions` | `AutoUpdateOptions` | - | Options for the `autoUpdate` utility from `@floating-ui/react`. |
26
+ | `elements` | `UseFloatingOptions['elements']` | - | Custom reference/floating elements override for the floating context. |
27
+ | `ref` | `React.Ref<FloatingRef>` | - | Ref forwarded to the underlying Floating context (`UseFloatingReturn`). |
28
+
29
+ ## Examples
30
+
31
+ ### Right-side drawer
32
+
33
+ ```tsx
34
+ <Drawer placement="right">
35
+ <FloatingTrigger>
36
+ <button>Open</button>
37
+ </FloatingTrigger>
38
+ <div style={{ padding: 24, width: 320 }}>
39
+ <DrawerClose>
40
+ <button>✕ Close</button>
41
+ </DrawerClose>
42
+ <h3>Settings</h3>
43
+ <p>Panel content goes here.</p>
44
+ </div>
45
+ </Drawer>
46
+ ```
47
+
48
+ ### Bottom drawer (mobile sheet pattern)
49
+
50
+ ```tsx
51
+ <Drawer placement="bottom">
52
+ <FloatingTrigger>
53
+ <button>Open Sheet</button>
54
+ </FloatingTrigger>
55
+ <div style={{ padding: 24 }}>
56
+ <DrawerClose>
57
+ <button>Dismiss</button>
58
+ </DrawerClose>
59
+ <p>Bottom sheet content.</p>
60
+ </div>
61
+ </Drawer>
62
+ ```
63
+
64
+ ### Controlled drawer
65
+
66
+ ```tsx
67
+ const [open, setOpen] = useState(false);
68
+
69
+ <>
70
+ <button onClick={() => setOpen(true)}>Open</button>
71
+ <Drawer placement="left" open={open} onOpenChange={setOpen}>
72
+ <div style={{ width: 280, padding: 24 }}>
73
+ <DrawerClose>
74
+ <button>Close</button>
75
+ </DrawerClose>
76
+ <p>Controlled drawer panel.</p>
77
+ </div>
78
+ </Drawer>
79
+ </>
80
+ ```
81
+
82
+ ## Related Components
83
+
84
+ - [Floating](../floating/README.md)
85
+ - [FloatingContent](../floatingcontent/README.md)
86
+ - [FloatingClose](../floatingclose/README.md)
87
+
88
+ ## Dependencies
89
+
90
+ - `motion/react`
91
+ - `@floating-ui/react`
92
+
93
+ ## See Also
94
+
95
+ - [Component Source](./index.ts)
96
+ - [Storybook Stories](./drawer.stories.tsx)
97
+ - [Main README](../../README.md)
@@ -0,0 +1,146 @@
1
+ # FilterPanel
2
+
3
+ Component bộ lọc có thể thu/mở, dùng trong giao diện lọc sản phẩm e-commerce. Hỗ trợ bốn loại field — checkbox, radio, price-range, và input-text — được render bên trong một toggle collapse có animation.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { FilterPanel } from '@janbox/storefront-ui';
9
+ ```
10
+
11
+ ## Props
12
+
13
+ | Prop | Type | Default | Description |
14
+ |------|------|---------|-------------|
15
+ | `label*` | `ReactNode` | - | Nhãn hiển thị trên nút toggle header. Nhận string hoặc React element bất kỳ. |
16
+ | `fieldType*` | `'checkbox' \| 'radio' \| 'price-range' \| 'input-text'` | - | Xác định loại control filter bên trong được render. Đây là discriminated union key, thay đổi loại props được chấp nhận. |
17
+ | `defaultOpen` | `boolean` | `true` | Panel có mở rộng mặc định hay không. |
18
+ | `className` | `string` | - | CSS class name bổ sung áp dụng lên root element. |
19
+ | `isLoading` | `boolean` | - | Khi `true`, render trạng thái loading bên trong panel. |
20
+ | `options` | `{ label: ReactNode; searchLabel?: string \| string[]; value: string; isChecked?: boolean }[]` | - | Danh sách các option có thể chọn. Bắt buộc khi `fieldType` là `'checkbox'` hoặc `'radio'`. |
21
+ | `value` | `string[] \| string \| { from?: number; to?: number; priceType?: string }` | - | Giá trị controlled. Kiểu phụ thuộc vào `fieldType`: `string[]` cho checkbox, `string` cho radio, object `{ from, to, priceType }` cho price-range. |
22
+ | `onChange` | `(e: React.ChangeEvent<HTMLInputElement>) => void` | - | Handler thay đổi dùng cho `checkbox` và `radio`. |
23
+ | `inputSearchProps` | `ShallowMerge<InputProps, { enabled?: boolean }>` | - | Props cho ô tìm kiếm tùy chọn hiển thị phía trên danh sách option. Có khi `fieldType` là `'checkbox'` hoặc `'radio'`. |
24
+ | `name` | `string` | - | HTML `name` attribute truyền xuống các checkbox. Có khi `fieldType` là `'checkbox'`. |
25
+ | `listProps` | `PropsWithoutRef<JSX.IntrinsicElements['div']>` | - | Props spread lên div container chứa danh sách option. Có khi `fieldType` là `'checkbox'`. |
26
+ | `fallbackValue` | `string` | - | Giá trị mặc định khi không có `value` nào được cung cấp. Có khi `fieldType` là `'radio'`. |
27
+ | `queryParam` | `object` | - | Cấu hình query parameter URL dùng để đồng bộ trạng thái filter với URL. Cấu trúc thay đổi theo `fieldType`. |
28
+ | `placeholder` | `{ from?: string; to?: string } \| string` | - | Placeholder cho các input field. Object `{ from, to }` cho price-range; string cho input-text. |
29
+ | `priceTypes` | `{ label: ReactNode; value: string; isDefault?: boolean }[]` | - | Các option radio để chọn loại giá (VD: giá hiện tại vs. giá gốc). Có khi `fieldType` là `'price-range'`. |
30
+ | `onPriceChange` | `InputRangeProps['onChange']` | - | Handler thay đổi cho các input from/to giá. Có khi `fieldType` là `'price-range'`. |
31
+ | `onPriceTypeChange` | `(e: React.ChangeEvent<HTMLInputElement>) => void` | - | Handler thay đổi cho radio chọn loại giá. Có khi `fieldType` là `'price-range'`. |
32
+
33
+ *Bắt buộc
34
+
35
+ ## Examples
36
+
37
+ ### Checkbox filter
38
+
39
+ ```tsx
40
+ <FilterPanel
41
+ label="Danh mục"
42
+ fieldType="checkbox"
43
+ options={[
44
+ { label: 'Điện tử', value: 'electronics' },
45
+ { label: 'Thời trang', value: 'clothing' },
46
+ { label: 'Sách', value: 'books' },
47
+ ]}
48
+ onChange={(e) => console.log(e.target.value, e.target.checked)}
49
+ />
50
+ ```
51
+
52
+ ### Checkbox filter với tìm kiếm
53
+
54
+ ```tsx
55
+ <FilterPanel
56
+ label="Thương hiệu"
57
+ fieldType="checkbox"
58
+ options={[
59
+ { label: 'Apple', value: 'apple', searchLabel: 'apple' },
60
+ { label: 'Samsung', value: 'samsung', searchLabel: 'samsung' },
61
+ { label: 'Sony', value: 'sony', searchLabel: 'sony' },
62
+ ]}
63
+ value={['apple']}
64
+ inputSearchProps={{ enabled: true, placeholder: 'Tìm thương hiệu...' }}
65
+ onChange={(e) => console.log(e.target.value, e.target.checked)}
66
+ />
67
+ ```
68
+
69
+ ### Radio filter
70
+
71
+ ```tsx
72
+ <FilterPanel
73
+ label="Sắp xếp theo"
74
+ fieldType="radio"
75
+ value="newest"
76
+ options={[
77
+ { label: 'Giá: Thấp đến Cao', value: 'price_asc' },
78
+ { label: 'Giá: Cao đến Thấp', value: 'price_desc' },
79
+ { label: 'Mới nhất', value: 'newest' },
80
+ ]}
81
+ onChange={(e) => console.log(e.target.value)}
82
+ />
83
+ ```
84
+
85
+ ### Price range filter
86
+
87
+ ```tsx
88
+ <FilterPanel
89
+ label="Khoảng giá"
90
+ fieldType="price-range"
91
+ value={{ from: 10, to: 500 }}
92
+ placeholder={{ from: 'Giá tối thiểu', to: 'Giá tối đa' }}
93
+ priceTypes={[
94
+ { label: 'Giá hiện tại', value: 'current_price', isDefault: true },
95
+ { label: 'Giá gốc', value: 'original_price' },
96
+ ]}
97
+ onPriceChange={(value) => console.log(value)}
98
+ onPriceTypeChange={(e) => console.log(e.target.value)}
99
+ />
100
+ ```
101
+
102
+ ### Input text filter
103
+
104
+ ```tsx
105
+ <FilterPanel
106
+ label="Tìm kiếm"
107
+ fieldType="input-text"
108
+ placeholder="Nhập từ khóa..."
109
+ value="áo thun"
110
+ />
111
+ ```
112
+
113
+ ### Panel đóng mặc định
114
+
115
+ ```tsx
116
+ <FilterPanel
117
+ label="Bộ lọc nâng cao"
118
+ fieldType="checkbox"
119
+ defaultOpen={false}
120
+ options={[
121
+ { label: 'Có khuyến mãi', value: 'on_sale' },
122
+ { label: 'Hàng mới về', value: 'new_arrival' },
123
+ ]}
124
+ onChange={(e) => console.log(e.target.value, e.target.checked)}
125
+ />
126
+ ```
127
+
128
+ ## Related Components
129
+
130
+ - [Collapse](../collapse/README.md)
131
+ - [Icon](../icon/README.md)
132
+ - [Checkbox](../checkbox/README.md)
133
+ - [Input](../input/README.md)
134
+ - [InputRange](../input-range/README.md)
135
+ - [RadioButton](../radio-button/README.md)
136
+ - [VirtualizedList](../virtualized-list/README.md)
137
+
138
+ ## Dependencies
139
+
140
+ - @emotion/react
141
+
142
+ ## See Also
143
+
144
+ - [Component Source](./index.ts)
145
+ - [Storybook Stories](./filter-panel.stories.tsx)
146
+ - [Main README](../../README.md)
@@ -0,0 +1,58 @@
1
+ # Flag
2
+
3
+ Hiển thị hình ảnh quốc kỳ từ flagcdn.com dựa trên mã quốc gia ISO 2 ký tự. Tự động fallback về icon địa cầu (hoặc fallback tùy chỉnh) khi không có mã quốc gia hoặc hình ảnh tải thất bại.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { Flag } from '@janbox/storefront-ui';
9
+ ```
10
+
11
+ ## Props
12
+
13
+ | Prop | Type | Default | Description |
14
+ |------|------|---------|-------------|
15
+ | `countryCode` | `string` | - | Mã quốc gia ISO 3166-1 alpha-2 (ví dụ: `'VN'`, `'JP'`, `'US'`). Dùng để tạo URL hình ảnh từ flagcdn.com. Nếu không truyền, hiển thị ảnh fallback. |
16
+ | `fallback` | `string` | EarthIcon (SVG địa cầu nội bộ) | URL hoặc giá trị hiển thị khi hình ảnh tải thất bại hoặc `countryCode` không được cung cấp. Mặc định là icon địa cầu tích hợp sẵn. |
17
+ | `alt` | `string` | - | Alt text cho thẻ `img`, kế thừa từ phần tử Image/img bên dưới. |
18
+ | `sx` | `ToResponsiveProps<StyledCSS>` | `{ width: 24, aspectRatio: '4 / 3', objectFit: 'contain' }` | Style override responsive qua Emotion sx prop. Mặc định: width 24px, tỉ lệ 4/3, objectFit contain. |
19
+ | `...rest` | `JSX.IntrinsicElements['img']` | - | Tất cả HTML img attributes tiêu chuẩn được forward xuống phần tử img (ví dụ: `className`, `style`, `onClick`, v.v.). |
20
+
21
+ ## Examples
22
+
23
+ ### Quốc kỳ cơ bản
24
+
25
+ ```tsx
26
+ <Flag countryCode="VN" alt="Vietnam" />
27
+ ```
28
+
29
+ ### Nhiều quốc kỳ cạnh nhau
30
+
31
+ ```tsx
32
+ <div style={{ display: 'flex', gap: 8 }}>
33
+ <Flag countryCode="JP" alt="Japan" />
34
+ <Flag countryCode="US" alt="United States" />
35
+ <Flag countryCode="VN" alt="Vietnam" />
36
+ </div>
37
+ ```
38
+
39
+ ### Kích thước tùy chỉnh và fallback cho mã không hợp lệ
40
+
41
+ ```tsx
42
+ <Flag
43
+ countryCode="UNKNOWN"
44
+ fallback="https://example.com/placeholder-flag.svg"
45
+ alt="Unknown country"
46
+ sx={{ width: 32 }}
47
+ />
48
+ ```
49
+
50
+ ## Related Components
51
+
52
+ - [Image](../image/README.md)
53
+
54
+ ## See Also
55
+
56
+ - [Component Source](./index.ts)
57
+ - [Storybook Stories](./flag.stories.tsx)
58
+ - [Main README](../../README.md)
@@ -0,0 +1,59 @@
1
+ # Flexbox
2
+
3
+ A layout component that renders a `div` with `display: flex` applied by default. It supports responsive `sx` prop overrides and an optional `container` prop that applies the project's responsive container styles for page-level layout use cases.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { Flexbox } from '@janbox/storefront-ui';
9
+ ```
10
+
11
+ ## Props
12
+
13
+ | Prop | Type | Default | Description |
14
+ |------|------|---------|-------------|
15
+ | `container` | `boolean` | undefined (false) | When true, applies the global `containerStyle` (responsive padding and max-width) on top of the flex display — useful for page-level section wrappers. |
16
+ | `sx` | `ToResponsiveProps<StyledCSS>` | { flexDirection: 'row', alignItems: 'flex-start', justifyContent: 'flex-start' } | Responsive style overrides via the Emotion-powered sx prop. Supports base styles plus `sm`, `md`, and `lg` breakpoint keys. Merged on top of the component defaults (`flexDirection: 'row'`, `alignItems: 'flex-start'`, `justifyContent: 'flex-start'`). |
17
+ | `children` | `React.ReactNode` | - | Child elements rendered inside the flex container — typically `FlexItem` components. |
18
+ | `ref` | `React.Ref<HTMLDivElement>` | - | Forwarded ref to the underlying `div` element. |
19
+ | `...rest` | `JSX.IntrinsicElements['div']` | - | All standard HTML div attributes (className, style, onClick, aria-*, data-*, etc.) are forwarded to the underlying element. |
20
+
21
+ ## Examples
22
+
23
+ ### Default row layout with gap
24
+
25
+ ```tsx
26
+ <Flexbox sx={{ gap: 8 }}>
27
+ <FlexItem style={{ padding: 8 }}>Item 1</FlexItem>
28
+ <FlexItem style={{ padding: 8 }}>Item 2</FlexItem>
29
+ <FlexItem style={{ padding: 8 }}>Item 3</FlexItem>
30
+ </Flexbox>
31
+ ```
32
+
33
+ ### Column layout
34
+
35
+ ```tsx
36
+ <Flexbox sx={{ flexDirection: 'column', gap: 8 }}>
37
+ <FlexItem>Top</FlexItem>
38
+ <FlexItem>Middle</FlexItem>
39
+ <FlexItem>Bottom</FlexItem>
40
+ </Flexbox>
41
+ ```
42
+
43
+ ### Centered content (both axes)
44
+
45
+ ```tsx
46
+ <Flexbox sx={{ justifyContent: 'center', alignItems: 'center' }} style={{ height: 200 }}>
47
+ <FlexItem>Centered</FlexItem>
48
+ </Flexbox>
49
+ ```
50
+
51
+ ## Related Components
52
+
53
+ - [Primitive](../primitive/README.md)
54
+
55
+ ## See Also
56
+
57
+ - [Component Source](./index.ts)
58
+ - [Storybook Stories](./flexbox.stories.tsx)
59
+ - [Main README](../../README.md)
@@ -0,0 +1,109 @@
1
+ # Floating
2
+
3
+ A compound component system for creating accessible floating UI elements such as tooltips, popovers, dropdowns, and menus. Built on top of @floating-ui/react, it manages positioning, interactions (click, hover, focus, dismiss), and portal rendering with enter/exit animations.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { Floating } from '@janbox/storefront-ui';
9
+ ```
10
+
11
+ ## Props
12
+
13
+ | Prop | Type | Default | Description |
14
+ |------|------|---------|-------------|
15
+ | `placement` | `Placement` | `'bottom'` | Vị trí của floating element so với trigger. Nhận bất kỳ giá trị `Placement` nào từ @floating-ui/react (VD: `'bottom'`, `'top'`, `'top-start'`, `'right'`, ...). |
16
+ | `open` | `boolean` | - | Controlled open state — kiểm soát trạng thái mở/đóng từ bên ngoài. |
17
+ | `onOpenChange` | `UseFloatingOptions['onOpenChange']` | - | Callback khi trạng thái open thay đổi. Nhận `(open: boolean, event?: Event, reason?: OpenChangeReason)`. |
18
+ | `elements` | `UseFloatingOptions['elements']` | - | DOM elements được quản lý bên ngoài (reference và floating). Dùng cho advanced use cases khi tự quản lý element refs. |
19
+ | `clickProps` | `UseClickProps` | `{ enabled: false }` | Cấu hình cho click interaction hook từ @floating-ui/react. Điều khiển việc click trigger có mở/đóng floating element không. |
20
+ | `dismissProps` | `UseDismissProps` | `{ enabled: false }` | Cấu hình cho dismiss interaction hook. Điều khiển việc click ngoài hoặc nhấn Escape có đóng floating element không. |
21
+ | `hoverProps` | `UseHoverProps` | `{ enabled: false }` | Cấu hình cho hover interaction hook. Điều khiển việc hover trigger có mở floating element không. |
22
+ | `focusProps` | `UseFocusProps` | `{ enabled: false }` | Cấu hình cho focus interaction hook. Điều khiển việc focus trigger có mở floating element không. |
23
+ | `offsetOptions` | `OffsetOptions` | `4` | Khoảng cách (px) giữa trigger và floating element. Nhận số hoặc `OffsetOptions` object. |
24
+ | `sizeOptions` | `SizeOptions` | - | Cấu hình cho size middleware. Component cung cấp sẵn `apply` function giới hạn height/width theo viewport khả dụng. |
25
+ | `autoUpdateOptions` | `AutoUpdateOptions` | - | Cấu hình cho `autoUpdate` từ @floating-ui/react. Kiểm soát các event kích hoạt tính lại vị trí (scroll, resize, ...). |
26
+ | `children` | `React.ReactNode` | - | Nội dung của compound component. Nên chứa `FloatingTrigger` và `FloatingContent` là children trực tiếp hoặc lồng nhau. |
27
+ | `ref` | `React.Ref<FloatingRef>` | - | Ref expose `UseFloatingReturn` object, cho phép truy cập floating context, refs, và positioning data. |
28
+
29
+ ## Examples
30
+
31
+ ### Controlled popover với click-to-open và dismiss
32
+
33
+ ```tsx
34
+ const [open, setOpen] = useState(false);
35
+
36
+ <Floating
37
+ open={open}
38
+ onOpenChange={setOpen}
39
+ clickProps={{ enabled: true }}
40
+ dismissProps={{ enabled: true }}
41
+ >
42
+ <FloatingTrigger>
43
+ <button>Open Popover</button>
44
+ </FloatingTrigger>
45
+ <FloatingContent>
46
+ <div style={{ padding: 16, background: 'white', border: '1px solid #ccc', borderRadius: 8 }}>
47
+ Popover content
48
+ </div>
49
+ </FloatingContent>
50
+ </Floating>
51
+ ```
52
+
53
+ ### Tooltip khi hover với placement top
54
+
55
+ ```tsx
56
+ <Floating placement="top" hoverProps={{ enabled: true }}>
57
+ <FloatingTrigger>
58
+ <button>Hover me</button>
59
+ </FloatingTrigger>
60
+ <FloatingContent>
61
+ <div style={{ padding: '4px 8px', background: '#333', color: '#fff', borderRadius: 4, fontSize: 12 }}>
62
+ Tooltip text
63
+ </div>
64
+ </FloatingContent>
65
+ </Floating>
66
+ ```
67
+
68
+ ### Dropdown menu với FloatingClose
69
+
70
+ ```tsx
71
+ const [open, setOpen] = useState(false);
72
+
73
+ <Floating
74
+ open={open}
75
+ onOpenChange={setOpen}
76
+ placement="bottom-start"
77
+ clickProps={{ enabled: true }}
78
+ dismissProps={{ enabled: true }}
79
+ >
80
+ <FloatingTrigger>
81
+ <button>Actions</button>
82
+ </FloatingTrigger>
83
+ <FloatingContent>
84
+ <ul style={{ listStyle: 'none', margin: 0, padding: '4px 0', background: 'white', border: '1px solid #eee', borderRadius: 8 }}>
85
+ <li>
86
+ <FloatingClose>
87
+ <button style={{ display: 'block', width: '100%', padding: '8px 16px', textAlign: 'left' }}>Edit</button>
88
+ </FloatingClose>
89
+ </li>
90
+ <li>
91
+ <FloatingClose>
92
+ <button style={{ display: 'block', width: '100%', padding: '8px 16px', textAlign: 'left' }}>Delete</button>
93
+ </FloatingClose>
94
+ </li>
95
+ </ul>
96
+ </FloatingContent>
97
+ </Floating>
98
+ ```
99
+
100
+ ## Dependencies
101
+
102
+ - @floating-ui/react
103
+ - motion/react
104
+
105
+ ## See Also
106
+
107
+ - [Component Source](./index.ts)
108
+ - [Storybook Stories](./floating.stories.tsx)
109
+ - [Main README](../../README.md)
@@ -0,0 +1,54 @@
1
+ # FormHelperText
2
+
3
+ Component hiển thị text hỗ trợ nhỏ bên dưới các form field, dùng để hiển thị hướng dẫn bổ sung hoặc thông báo lỗi validation. Tự động ẩn khi không có nội dung và đổi màu đỏ khi prop `error` được set.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { FormHelperText } from '@janbox/storefront-ui';
9
+ ```
10
+
11
+ ## Props
12
+
13
+ | Prop | Type | Default | Description |
14
+ |------|------|---------|-------------|
15
+ | `error` | `boolean` | `false` | Khi `true`, render helper text màu đỏ (`--color-red-600`) để chỉ trạng thái lỗi validation. |
16
+ | `children` | `React.ReactNode` | - | Nội dung helper text. Element tự ẩn (`display: none`) khi không có nội dung. |
17
+ | `sx` | `ToResponsiveProps<StyledCSS>` | - | Style override có responsive, truyền qua hệ thống `sx` prop của `Primitive`. |
18
+ | `as` | `React.ElementType` | `"p"` | Override element type (polymorphic), kế thừa từ `PrimitiveProps`. Mặc định là `p`. |
19
+ | `...rest` | `JSX.IntrinsicElements['p']` | - | Tất cả HTML attributes của thẻ `<p>` đều được forward xuống element bên dưới. |
20
+
21
+ ## Examples
22
+
23
+ ### Helper text mặc định
24
+
25
+ ```tsx
26
+ <FormHelperText>This is a helper text</FormHelperText>
27
+ ```
28
+
29
+ ### Trạng thái lỗi
30
+
31
+ ```tsx
32
+ <FormHelperText error>This field is required</FormHelperText>
33
+ ```
34
+
35
+ ### Kết hợp với input field
36
+
37
+ ```tsx
38
+ <div>
39
+ <input type="email" placeholder="Enter your email" />
40
+ <FormHelperText error={!isValid}>
41
+ {isValid ? 'We will never share your email.' : 'Please enter a valid email address.'}
42
+ </FormHelperText>
43
+ </div>
44
+ ```
45
+
46
+ ## Related Components
47
+
48
+ - [Primitive](../primitive/README.md)
49
+
50
+ ## See Also
51
+
52
+ - [Component Source](./index.ts)
53
+ - [Storybook Stories](./form-helper-text.stories.tsx)
54
+ - [Main README](../../README.md)
@@ -0,0 +1,50 @@
1
+ # FormLabel
2
+
3
+ A form label wrapper component that renders a label for form fields with optional required indicator. It accepts either a string (auto-wrapped in a `<label>` element) or custom React children, and displays a red asterisk when the field is required.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { FormLabel } from '@janbox/storefront-ui';
9
+ ```
10
+
11
+ ## Props
12
+
13
+ | Prop | Type | Default | Description |
14
+ |------|------|---------|-------------|
15
+ | `required` | `boolean` | undefined | When true, appends a red asterisk (*) after the label to indicate the associated field is required. |
16
+ | `children` | `React.ReactNode` | undefined | Label content. If a plain string is passed, it is automatically wrapped in a `<label>` element. Custom React nodes are rendered as-is. |
17
+ | `...props` | `JSX.IntrinsicElements['div']` | - | All standard HTML div attributes (className, style, id, onClick, etc.) are forwarded to the root div element. |
18
+
19
+ ## Examples
20
+
21
+ ### Default label
22
+
23
+ ```tsx
24
+ <FormLabel>Email address</FormLabel>
25
+ ```
26
+
27
+ ### Required field label
28
+
29
+ ```tsx
30
+ <FormLabel required>Password</FormLabel>
31
+ ```
32
+
33
+ ### With custom children and className
34
+
35
+ ```tsx
36
+ <FormLabel required className="custom-label">
37
+ <label htmlFor="username">Username</label>
38
+ </FormLabel>
39
+ ```
40
+
41
+ ## Dependencies
42
+
43
+ - lodash-es
44
+ - @emotion/react
45
+
46
+ ## See Also
47
+
48
+ - [Component Source](./index.ts)
49
+ - [Storybook Stories](./formlabel.stories.tsx)
50
+ - [Main README](../../README.md)