@janbox/storefront-ui 2.0.29 → 2.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +681 -0
- package/dist/lib/accordion/README.md +81 -0
- package/dist/lib/avatar/README.md +74 -0
- package/dist/lib/badge/README.md +58 -0
- package/dist/lib/box/README.md +69 -0
- package/dist/lib/breadcrumbs/README.md +70 -0
- package/dist/lib/button/README.md +115 -0
- package/dist/lib/cascader/README.md +128 -0
- package/dist/lib/checkbox/README.md +74 -0
- package/dist/lib/checkbox/checkbox.js +107 -12
- package/dist/lib/checkbox/types.d.ts +2 -2
- package/dist/lib/chip/README.md +72 -0
- package/dist/lib/collapse/README.md +78 -0
- package/dist/lib/container/README.md +59 -0
- package/dist/lib/count-up/README.md +52 -0
- package/dist/lib/countdown-timer/README.md +77 -0
- package/dist/lib/date-picker/README.md +94 -0
- package/dist/lib/date-picker/date-picker.js +2 -1
- package/dist/lib/dialog/README.md +109 -0
- package/dist/lib/drawer/README.md +97 -0
- package/dist/lib/filter-panel/README.md +146 -0
- package/dist/lib/flag/README.md +58 -0
- package/dist/lib/flexbox/README.md +59 -0
- package/dist/lib/floating/README.md +109 -0
- package/dist/lib/form-helper-text/README.md +54 -0
- package/dist/lib/form-label/README.md +50 -0
- package/dist/lib/grid/README.md +72 -0
- package/dist/lib/highlight-words/README.md +64 -0
- package/dist/lib/highlight-words/highlight-words.js +1 -2
- package/dist/lib/icon/README.md +69 -0
- package/dist/lib/icon-button/README.md +92 -0
- package/dist/lib/image/README.md +80 -0
- package/dist/lib/input/README.md +118 -0
- package/dist/lib/input-mask/README.md +88 -0
- package/dist/lib/input-number/README.md +92 -0
- package/dist/lib/input-range/README.md +85 -0
- package/dist/lib/lightbox/README.md +107 -0
- package/dist/lib/linear-progress/README.md +54 -0
- package/dist/lib/link/README.md +67 -0
- package/dist/lib/loading/README.md +65 -0
- package/dist/lib/marquee/README.md +83 -0
- package/dist/lib/menu/README.md +92 -0
- package/dist/lib/multiple-select/README.md +108 -0
- package/dist/lib/nav-link/README.md +61 -0
- package/dist/lib/notifications/README.md +103 -0
- package/dist/lib/otp-input/README.md +71 -0
- package/dist/lib/pagination/README.md +84 -0
- package/dist/lib/phone-input/README.md +80 -0
- package/dist/lib/popover/README.md +93 -0
- package/dist/lib/price-label/README.md +78 -0
- package/dist/lib/primitive/README.md +80 -0
- package/dist/lib/progress/README.md +50 -0
- package/dist/lib/radio-button/README.md +89 -0
- package/dist/lib/radio-button/radio-button.js +98 -7
- package/dist/lib/ripple-effect/README.md +66 -0
- package/dist/lib/select/README.md +124 -0
- package/dist/lib/star-rating/README.md +67 -0
- package/dist/lib/stepper/README.md +99 -0
- package/dist/lib/suspense-query/README.md +78 -0
- package/dist/lib/swiper/README.md +99 -0
- package/dist/lib/switch/README.md +73 -0
- package/dist/lib/switch/switch.d.ts +1 -1
- package/dist/lib/switch/switch.js +110 -11
- package/dist/lib/table/README.md +115 -0
- package/dist/lib/tabs/README.md +103 -0
- package/dist/lib/text/README.md +59 -0
- package/dist/lib/textarea/README.md +76 -0
- package/dist/lib/time-picker/README.md +100 -0
- package/dist/lib/tooltip/README.md +106 -0
- package/dist/lib/unordered-list/README.md +85 -0
- package/dist/lib/video/README.md +88 -0
- package/package.json +5 -5
- package/dist/lib/checkbox/checkbox.module.scss.js +0 -23
- package/dist/lib/radio-button/radio-button.module.scss.js +0 -17
- package/dist/lib/switch/switch.module.scss.js +0 -14
- package/dist/style.css +0 -823
|
@@ -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)
|