@janbox/storefront-builder 1.0.3 → 1.0.5
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 +477 -0
- package/dist/editor/lib/index.d.ts +3 -3
- package/dist/editor/lib/sidebar/index.d.ts +1 -1
- package/dist/editor/lib/sidebar/insert-action.d.ts +2 -2
- package/dist/editor/ui/dialog/dialog/helpers.d.ts +1 -1
- package/dist/editor/ui/dialog/dialog-close/dialog-close.d.ts +1 -1
- package/dist/editor.js +326 -313
- package/dist/lib/accordion/README.md +39 -0
- package/dist/lib/accordion-content/README.md +36 -0
- package/dist/lib/accordion-group/README.md +106 -0
- package/dist/lib/accordion-summary/README.md +37 -0
- package/dist/lib/box/README.md +107 -0
- package/dist/lib/button/README.md +94 -0
- package/dist/lib/cell/README.md +119 -0
- package/dist/lib/countdown-timer/README.md +157 -0
- package/dist/lib/flex-item/README.md +111 -0
- package/dist/lib/flexbox/README.md +120 -0
- package/dist/lib/grid/README.md +133 -0
- package/dist/lib/heading/README.md +53 -0
- package/dist/lib/icon/README.md +98 -0
- package/dist/lib/image/README.md +107 -0
- package/dist/lib/link/README.md +111 -0
- package/dist/lib/list-item/README.md +31 -0
- package/dist/lib/marquee/README.md +111 -0
- package/dist/lib/marquee-item/README.md +31 -0
- package/dist/lib/paragraph/README.md +82 -0
- package/dist/lib/root/README.md +33 -0
- package/dist/lib/swiper/README.md +108 -0
- package/dist/lib/swiper-slide/README.md +32 -0
- package/dist/lib/tab/README.md +35 -0
- package/dist/lib/tab-content/README.md +35 -0
- package/dist/lib/tab-list/README.md +35 -0
- package/dist/lib/tab-panel/README.md +35 -0
- package/dist/lib/tabs/README.md +90 -0
- package/dist/lib/text/README.md +90 -0
- package/dist/lib/unknown/README.md +48 -0
- package/dist/lib/unordered-list/README.md +82 -0
- package/dist/lib/video/README.md +91 -0
- package/lib/accordion/README.md +39 -0
- package/lib/accordion-content/README.md +36 -0
- package/lib/accordion-group/README.md +106 -0
- package/lib/accordion-summary/README.md +37 -0
- package/lib/box/README.md +107 -0
- package/lib/button/README.md +94 -0
- package/lib/cell/README.md +119 -0
- package/lib/countdown-timer/README.md +157 -0
- package/lib/flex-item/README.md +111 -0
- package/lib/flexbox/README.md +120 -0
- package/lib/grid/README.md +133 -0
- package/lib/heading/README.md +53 -0
- package/lib/icon/README.md +98 -0
- package/lib/image/README.md +107 -0
- package/lib/link/README.md +111 -0
- package/lib/list-item/README.md +31 -0
- package/lib/marquee/README.md +111 -0
- package/lib/marquee-item/README.md +31 -0
- package/lib/paragraph/README.md +82 -0
- package/lib/root/README.md +33 -0
- package/lib/swiper/README.md +108 -0
- package/lib/swiper-slide/README.md +32 -0
- package/lib/tab/README.md +35 -0
- package/lib/tab-content/README.md +35 -0
- package/lib/tab-list/README.md +35 -0
- package/lib/tab-panel/README.md +35 -0
- package/lib/tabs/README.md +90 -0
- package/lib/text/README.md +90 -0
- package/lib/unknown/README.md +48 -0
- package/lib/unordered-list/README.md +82 -0
- package/lib/video/README.md +91 -0
- package/package.json +5 -9
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# BoxNode
|
|
2
|
+
|
|
3
|
+
Block container đa năng (render ra `div`). Là canvas node — chứa bất kỳ node con nào. Dùng để nhóm các element, tạo section, hay làm wrapper có background/border.
|
|
4
|
+
|
|
5
|
+
## Resolved name
|
|
6
|
+
|
|
7
|
+
`Box` (displayName: `Block`)
|
|
8
|
+
|
|
9
|
+
## Configurable props
|
|
10
|
+
|
|
11
|
+
### Top-level props
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Description |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `container` | `boolean` | Bật max-width container constraint |
|
|
16
|
+
|
|
17
|
+
### `sx` props
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
sx?: {
|
|
21
|
+
// size
|
|
22
|
+
width?: number | string
|
|
23
|
+
height?: number | string
|
|
24
|
+
|
|
25
|
+
// spacing
|
|
26
|
+
paddingTop?: number
|
|
27
|
+
paddingRight?: number
|
|
28
|
+
paddingBottom?: number
|
|
29
|
+
paddingLeft?: number
|
|
30
|
+
marginTop?: number
|
|
31
|
+
marginRight?: number
|
|
32
|
+
marginBottom?: number
|
|
33
|
+
marginLeft?: number
|
|
34
|
+
|
|
35
|
+
// border
|
|
36
|
+
borderStyle?: string
|
|
37
|
+
borderColor?: string
|
|
38
|
+
borderTopWidth?: number
|
|
39
|
+
borderRightWidth?: number
|
|
40
|
+
borderBottomWidth?: number
|
|
41
|
+
borderLeftWidth?: number
|
|
42
|
+
borderTopLeftRadius?: number | string
|
|
43
|
+
borderTopRightRadius?: number | string
|
|
44
|
+
borderBottomRightRadius?: number | string
|
|
45
|
+
borderBottomLeftRadius?: number | string
|
|
46
|
+
|
|
47
|
+
// background
|
|
48
|
+
backgroundColor?: string
|
|
49
|
+
backgroundImage?: string
|
|
50
|
+
backgroundPosition?: string
|
|
51
|
+
backgroundRepeat?: string
|
|
52
|
+
backgroundSize?: string
|
|
53
|
+
backgroundAttachment?: string
|
|
54
|
+
background?: string
|
|
55
|
+
|
|
56
|
+
// responsive overrides
|
|
57
|
+
sm?: { /* same keys above */ }
|
|
58
|
+
md?: { /* same keys above */ }
|
|
59
|
+
lg?: { /* same keys above */ }
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Usage
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { BoxNode } from '@janbox/storefront-builder';
|
|
67
|
+
|
|
68
|
+
// Wrapper đơn giản
|
|
69
|
+
<BoxNode sx={{ padding: 16 }}>
|
|
70
|
+
<TextNode>Hello</TextNode>
|
|
71
|
+
</BoxNode>
|
|
72
|
+
|
|
73
|
+
// Section có background
|
|
74
|
+
<BoxNode
|
|
75
|
+
sx={{
|
|
76
|
+
backgroundColor: '#f5f5f5',
|
|
77
|
+
paddingTop: 48,
|
|
78
|
+
paddingBottom: 48,
|
|
79
|
+
}}
|
|
80
|
+
>
|
|
81
|
+
...
|
|
82
|
+
</BoxNode>
|
|
83
|
+
|
|
84
|
+
// Container với responsive padding
|
|
85
|
+
<BoxNode
|
|
86
|
+
container
|
|
87
|
+
sx={{
|
|
88
|
+
paddingTop: 24,
|
|
89
|
+
paddingBottom: 24,
|
|
90
|
+
md: {
|
|
91
|
+
paddingTop: 48,
|
|
92
|
+
paddingBottom: 48,
|
|
93
|
+
},
|
|
94
|
+
}}
|
|
95
|
+
>
|
|
96
|
+
...
|
|
97
|
+
</BoxNode>
|
|
98
|
+
|
|
99
|
+
// Box ẩn trên mobile, hiện trên desktop — dùng DisplayOn (qua editor)
|
|
100
|
+
// Có thể kết hợp với sx.width/height để tạo spacer cố định
|
|
101
|
+
<BoxNode sx={{ width: '100%', height: 32, md: { height: 64 } }} />
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Rules
|
|
105
|
+
|
|
106
|
+
- **Canvas**: yes — chứa bất kỳ node con nào.
|
|
107
|
+
- **canMoveIn**: không giới hạn node type.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# ButtonNode
|
|
2
|
+
|
|
3
|
+
CTA button node dùng component `Button` từ `@janbox/storefront-ui`. Có thể render như button thường hoặc link button qua prop `anchor`.
|
|
4
|
+
|
|
5
|
+
## Resolved name
|
|
6
|
+
|
|
7
|
+
`Button`
|
|
8
|
+
|
|
9
|
+
## Configurable props
|
|
10
|
+
|
|
11
|
+
### Top-level props
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Description |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `dangerouslySetInnerHTML.__html` | `string` | Text hiển thị trong button |
|
|
16
|
+
| `color` | `ColorVariant` | Màu theme của button |
|
|
17
|
+
| `variant` | `ButtonVariant` | Kiểu hiển thị của button |
|
|
18
|
+
| `size` | `ButtonSizeVariant` | Kích thước button |
|
|
19
|
+
| `anchor.to` | `string \| { pathname: string }` | Link destination |
|
|
20
|
+
| `anchor.target` | `'_self' \| '_blank' \| '_parent' \| '_top'` | Cách mở link |
|
|
21
|
+
|
|
22
|
+
`size` hỗ trợ responsive: `size="sm"` xs, `md={{ size: 'lg' }}` desktop.
|
|
23
|
+
|
|
24
|
+
**`ColorVariant`:** `'primary' | 'secondary' | 'green' | 'red' | 'orange' | 'blue' | 'neutral'`
|
|
25
|
+
|
|
26
|
+
**`ButtonVariant`:** `'contained' | 'outlined' | 'text'`
|
|
27
|
+
|
|
28
|
+
**`ButtonSizeVariant`:** `'sm' | 'md' | 'lg'`
|
|
29
|
+
|
|
30
|
+
### `sx` props
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
sx?: {
|
|
34
|
+
// size
|
|
35
|
+
width?: number | string
|
|
36
|
+
|
|
37
|
+
// spacing (margin only)
|
|
38
|
+
marginTop?: number
|
|
39
|
+
marginRight?: number
|
|
40
|
+
marginBottom?: number
|
|
41
|
+
marginLeft?: number
|
|
42
|
+
|
|
43
|
+
// responsive overrides
|
|
44
|
+
sm?: { /* same keys above */ }
|
|
45
|
+
md?: { /* same keys above */ }
|
|
46
|
+
lg?: { /* same keys above */ }
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Usage
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
import { ButtonNode } from '@janbox/storefront-builder';
|
|
54
|
+
|
|
55
|
+
// Button cơ bản
|
|
56
|
+
<ButtonNode
|
|
57
|
+
color="primary"
|
|
58
|
+
variant="contained"
|
|
59
|
+
size="md"
|
|
60
|
+
dangerouslySetInnerHTML={{ __html: 'Buy now' }}
|
|
61
|
+
/>
|
|
62
|
+
|
|
63
|
+
// Link button
|
|
64
|
+
<ButtonNode
|
|
65
|
+
color="primary"
|
|
66
|
+
variant="contained"
|
|
67
|
+
size="lg"
|
|
68
|
+
anchor={{ to: '/checkout', target: '_self' }}
|
|
69
|
+
dangerouslySetInnerHTML={{ __html: 'Checkout' }}
|
|
70
|
+
/>
|
|
71
|
+
|
|
72
|
+
// Responsive size + width
|
|
73
|
+
<ButtonNode
|
|
74
|
+
color="orange"
|
|
75
|
+
variant="outlined"
|
|
76
|
+
size="sm"
|
|
77
|
+
md={{ size: 'lg' }}
|
|
78
|
+
sx={{
|
|
79
|
+
width: '100%',
|
|
80
|
+
marginTop: 16,
|
|
81
|
+
md: {
|
|
82
|
+
width: 'fit-content',
|
|
83
|
+
marginTop: 24,
|
|
84
|
+
},
|
|
85
|
+
}}
|
|
86
|
+
dangerouslySetInnerHTML={{ __html: 'View products' }}
|
|
87
|
+
/>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Rules
|
|
91
|
+
|
|
92
|
+
- **Canvas**: no — không chứa node con.
|
|
93
|
+
- Khi editor đang bật, `anchor` bị unset lúc render để tránh navigate trong canvas.
|
|
94
|
+
- Nội dung button được chỉnh qua `dangerouslySetInnerHTML.__html` trong inspector.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# CellNode
|
|
2
|
+
|
|
3
|
+
Một ô (column) trong `GridNode`. Hỗ trợ `colSpan` để chiếm nhiều cột. Là canvas node — chứa bất kỳ node con nào.
|
|
4
|
+
|
|
5
|
+
## Resolved name
|
|
6
|
+
|
|
7
|
+
`Cell`
|
|
8
|
+
|
|
9
|
+
## Configurable props
|
|
10
|
+
|
|
11
|
+
### Top-level props
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Description |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `colSpan` | `number` | Số cột mà cell chiếm (`grid-column: span colSpan`) |
|
|
16
|
+
|
|
17
|
+
`colSpan` hỗ trợ responsive: `colSpan={1}` cho mobile, `md={{ colSpan: 2 }}` cho desktop.
|
|
18
|
+
|
|
19
|
+
### `sx` props
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
sx?: {
|
|
23
|
+
// size
|
|
24
|
+
height?: number | string
|
|
25
|
+
|
|
26
|
+
// order (thứ tự hiển thị trong grid)
|
|
27
|
+
order?: number | string
|
|
28
|
+
|
|
29
|
+
// spacing
|
|
30
|
+
paddingTop?: number
|
|
31
|
+
paddingRight?: number
|
|
32
|
+
paddingBottom?: number
|
|
33
|
+
paddingLeft?: number
|
|
34
|
+
marginTop?: number
|
|
35
|
+
marginRight?: number
|
|
36
|
+
marginBottom?: number
|
|
37
|
+
marginLeft?: number
|
|
38
|
+
|
|
39
|
+
// border
|
|
40
|
+
borderStyle?: string
|
|
41
|
+
borderColor?: string
|
|
42
|
+
borderTopWidth?: number
|
|
43
|
+
borderRightWidth?: number
|
|
44
|
+
borderBottomWidth?: number
|
|
45
|
+
borderLeftWidth?: number
|
|
46
|
+
borderTopLeftRadius?: number | string
|
|
47
|
+
borderTopRightRadius?: number | string
|
|
48
|
+
borderBottomRightRadius?: number | string
|
|
49
|
+
borderBottomLeftRadius?: number | string
|
|
50
|
+
|
|
51
|
+
// background
|
|
52
|
+
backgroundColor?: string
|
|
53
|
+
backgroundImage?: string
|
|
54
|
+
backgroundPosition?: string
|
|
55
|
+
backgroundRepeat?: string
|
|
56
|
+
backgroundSize?: string
|
|
57
|
+
backgroundAttachment?: string
|
|
58
|
+
background?: string
|
|
59
|
+
|
|
60
|
+
// responsive overrides
|
|
61
|
+
sm?: { /* same keys above */ }
|
|
62
|
+
md?: { /* same keys above */ }
|
|
63
|
+
lg?: { /* same keys above */ }
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Usage
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import { GridNode, CellNode } from '@janbox/storefront-builder';
|
|
71
|
+
|
|
72
|
+
// Cell mặc định (1 cột)
|
|
73
|
+
<GridNode cols={3}>
|
|
74
|
+
<CellNode>Content A</CellNode>
|
|
75
|
+
<CellNode>Content B</CellNode>
|
|
76
|
+
<CellNode>Content C</CellNode>
|
|
77
|
+
</GridNode>
|
|
78
|
+
|
|
79
|
+
// colSpan: cell chiếm 2/3 cột, cell còn lại chiếm 1/3
|
|
80
|
+
<GridNode cols={3}>
|
|
81
|
+
<CellNode colSpan={2}>Wide content</CellNode>
|
|
82
|
+
<CellNode colSpan={1}>Narrow content</CellNode>
|
|
83
|
+
</GridNode>
|
|
84
|
+
|
|
85
|
+
// colSpan responsive: full-width mobile, 2 cột trên desktop
|
|
86
|
+
<GridNode cols={1} md={{ cols: 3 }}>
|
|
87
|
+
<CellNode colSpan={1} md={{ colSpan: 2 }}>
|
|
88
|
+
Wide on desktop
|
|
89
|
+
</CellNode>
|
|
90
|
+
<CellNode colSpan={1}>
|
|
91
|
+
Normal
|
|
92
|
+
</CellNode>
|
|
93
|
+
</GridNode>
|
|
94
|
+
|
|
95
|
+
// sx responsive
|
|
96
|
+
<CellNode
|
|
97
|
+
colSpan={1}
|
|
98
|
+
sx={{
|
|
99
|
+
paddingTop: 12,
|
|
100
|
+
paddingBottom: 12,
|
|
101
|
+
backgroundColor: '#fff',
|
|
102
|
+
md: {
|
|
103
|
+
paddingTop: 24,
|
|
104
|
+
paddingBottom: 24,
|
|
105
|
+
},
|
|
106
|
+
}}
|
|
107
|
+
>
|
|
108
|
+
...
|
|
109
|
+
</CellNode>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Rules
|
|
113
|
+
|
|
114
|
+
- **Canvas**: yes — chứa bất kỳ node con nào.
|
|
115
|
+
- **Parent**: phải là `GridNode`.
|
|
116
|
+
|
|
117
|
+
## Related nodes
|
|
118
|
+
|
|
119
|
+
- [`GridNode`](../grid/README.md) — parent bắt buộc của `CellNode`
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# CountdownTimerNode
|
|
2
|
+
|
|
3
|
+
Đồng hồ đếm ngược đến một mốc thời gian. Hiển thị các time units (days, hours, minutes, seconds...) kèm caption.
|
|
4
|
+
|
|
5
|
+
## Resolved name
|
|
6
|
+
|
|
7
|
+
`CountdownTimer`
|
|
8
|
+
|
|
9
|
+
## Configurable props
|
|
10
|
+
|
|
11
|
+
### Top-level props
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Description |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `date` | `Date` | Thời điểm đích để đếm ngược đến |
|
|
16
|
+
| `units` | `{ value: TimeUnit; caption?: string }[]` | Danh sách time units hiển thị, theo thứ tự |
|
|
17
|
+
| `hideAfterExpired` | `boolean` | Ẩn toàn bộ component sau khi hết giờ |
|
|
18
|
+
|
|
19
|
+
**`TimeUnit`:** `'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds'`
|
|
20
|
+
|
|
21
|
+
### `unitItemProps.sx` — Styling cho mỗi unit box
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
unitItemProps?: {
|
|
25
|
+
sx?: {
|
|
26
|
+
width?: number | string
|
|
27
|
+
rowGap?: number // khoảng cách giữa số và caption
|
|
28
|
+
paddingTop?: number
|
|
29
|
+
paddingRight?: number
|
|
30
|
+
paddingBottom?: number
|
|
31
|
+
paddingLeft?: number
|
|
32
|
+
backgroundColor?: string
|
|
33
|
+
borderTopLeftRadius?: number | string
|
|
34
|
+
borderTopRightRadius?: number | string
|
|
35
|
+
borderBottomLeftRadius?: number | string
|
|
36
|
+
borderBottomRightRadius?: number | string
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### `timeProps` — Styling cho số đếm
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
timeProps?: {
|
|
45
|
+
sizeVariant?: TypographySizeVariant // responsive
|
|
46
|
+
sx?: {
|
|
47
|
+
color?: string
|
|
48
|
+
fontWeight?: number | string
|
|
49
|
+
fontStyle?: 'normal' | 'italic'
|
|
50
|
+
textDecorationLine?: 'none' | 'underline' | 'line-through'
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### `captionProps` — Styling cho caption (label dưới số)
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
captionProps?: {
|
|
59
|
+
sizeVariant?: TypographySizeVariant // responsive
|
|
60
|
+
sx?: {
|
|
61
|
+
color?: string
|
|
62
|
+
fontWeight?: number | string
|
|
63
|
+
fontStyle?: 'normal' | 'italic'
|
|
64
|
+
textDecorationLine?: 'none' | 'underline' | 'line-through'
|
|
65
|
+
textTransform?: 'none' | 'uppercase' | 'lowercase' | 'capitalize'
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### `sx` props (outer container)
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
sx?: {
|
|
74
|
+
width?: number | string
|
|
75
|
+
flexWrap?: 'nowrap' | 'wrap' | 'wrap-reverse'
|
|
76
|
+
|
|
77
|
+
// spacing
|
|
78
|
+
paddingTop?: number
|
|
79
|
+
paddingRight?: number
|
|
80
|
+
paddingBottom?: number
|
|
81
|
+
paddingLeft?: number
|
|
82
|
+
marginTop?: number
|
|
83
|
+
marginRight?: number
|
|
84
|
+
marginBottom?: number
|
|
85
|
+
marginLeft?: number
|
|
86
|
+
|
|
87
|
+
// border
|
|
88
|
+
borderStyle?: string
|
|
89
|
+
borderColor?: string
|
|
90
|
+
borderTopWidth?: number
|
|
91
|
+
borderRightWidth?: number
|
|
92
|
+
borderBottomWidth?: number
|
|
93
|
+
borderLeftWidth?: number
|
|
94
|
+
borderTopLeftRadius?: number | string
|
|
95
|
+
borderTopRightRadius?: number | string
|
|
96
|
+
borderBottomRightRadius?: number | string
|
|
97
|
+
borderBottomLeftRadius?: number | string
|
|
98
|
+
|
|
99
|
+
// background
|
|
100
|
+
backgroundColor?: string
|
|
101
|
+
backgroundImage?: string
|
|
102
|
+
backgroundPosition?: string
|
|
103
|
+
backgroundRepeat?: string
|
|
104
|
+
backgroundSize?: string
|
|
105
|
+
backgroundAttachment?: string
|
|
106
|
+
background?: string
|
|
107
|
+
|
|
108
|
+
// responsive overrides
|
|
109
|
+
sm?: { /* same keys above */ }
|
|
110
|
+
md?: { /* same keys above */ }
|
|
111
|
+
lg?: { /* same keys above */ }
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Usage
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
import { CountdownTimerNode } from '@janbox/storefront-builder';
|
|
119
|
+
|
|
120
|
+
// Đếm ngược đến ngày sale
|
|
121
|
+
<CountdownTimerNode
|
|
122
|
+
date={new Date('2026-12-31T23:59:59')}
|
|
123
|
+
units={[
|
|
124
|
+
{ value: 'days', caption: 'Days' },
|
|
125
|
+
{ value: 'hours', caption: 'Hours' },
|
|
126
|
+
{ value: 'minutes', caption: 'Mins' },
|
|
127
|
+
{ value: 'seconds', caption: 'Secs' },
|
|
128
|
+
]}
|
|
129
|
+
hideAfterExpired={false}
|
|
130
|
+
sx={{ columnGap: 8 }}
|
|
131
|
+
unitItemProps={{
|
|
132
|
+
sx: {
|
|
133
|
+
width: 64,
|
|
134
|
+
paddingTop: 8,
|
|
135
|
+
paddingBottom: 8,
|
|
136
|
+
backgroundColor: '#fa8c16',
|
|
137
|
+
borderTopLeftRadius: 8,
|
|
138
|
+
borderTopRightRadius: 8,
|
|
139
|
+
borderBottomLeftRadius: 8,
|
|
140
|
+
borderBottomRightRadius: 8,
|
|
141
|
+
},
|
|
142
|
+
}}
|
|
143
|
+
timeProps={{
|
|
144
|
+
sizeVariant: '2xl',
|
|
145
|
+
sx: { color: '#ffffff', fontWeight: 700 },
|
|
146
|
+
}}
|
|
147
|
+
captionProps={{
|
|
148
|
+
sizeVariant: 'xs',
|
|
149
|
+
sx: { color: '#ffffff', textTransform: 'uppercase' },
|
|
150
|
+
}}
|
|
151
|
+
/>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Rules
|
|
155
|
+
|
|
156
|
+
- **Canvas**: no — không chứa node con.
|
|
157
|
+
- Khi editor đang bật, component hiển thị placeholder nếu `units` rỗng hoặc đã hết giờ với `hideAfterExpired: true`.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# FlexItemNode
|
|
2
|
+
|
|
3
|
+
Một item trong `FlexboxNode`. Kiểm soát kích thước, grow/shrink, thứ tự của từng item trong flex container.
|
|
4
|
+
|
|
5
|
+
## Resolved name
|
|
6
|
+
|
|
7
|
+
`FlexItem`
|
|
8
|
+
|
|
9
|
+
## Configurable props
|
|
10
|
+
|
|
11
|
+
Tất cả props nằm trong `sx`. Không có top-level prop riêng.
|
|
12
|
+
|
|
13
|
+
### `sx` props
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
sx?: {
|
|
17
|
+
// flex item behavior
|
|
18
|
+
flexGrow?: number // auto grow: 1 = bật, undefined = tắt
|
|
19
|
+
flexShrink?: number // auto shrink: 0 = tắt, undefined = bật
|
|
20
|
+
|
|
21
|
+
// size
|
|
22
|
+
width?: number | string
|
|
23
|
+
height?: number | string
|
|
24
|
+
|
|
25
|
+
// order (thứ tự hiển thị)
|
|
26
|
+
order?: number | string
|
|
27
|
+
|
|
28
|
+
// spacing
|
|
29
|
+
paddingTop?: number
|
|
30
|
+
paddingRight?: number
|
|
31
|
+
paddingBottom?: number
|
|
32
|
+
paddingLeft?: number
|
|
33
|
+
marginTop?: number
|
|
34
|
+
marginRight?: number
|
|
35
|
+
marginBottom?: number
|
|
36
|
+
marginLeft?: number
|
|
37
|
+
|
|
38
|
+
// border
|
|
39
|
+
borderStyle?: string
|
|
40
|
+
borderColor?: string
|
|
41
|
+
borderTopWidth?: number
|
|
42
|
+
borderRightWidth?: number
|
|
43
|
+
borderBottomWidth?: number
|
|
44
|
+
borderLeftWidth?: number
|
|
45
|
+
borderTopLeftRadius?: number | string
|
|
46
|
+
borderTopRightRadius?: number | string
|
|
47
|
+
borderBottomRightRadius?: number | string
|
|
48
|
+
borderBottomLeftRadius?: number | string
|
|
49
|
+
|
|
50
|
+
// background
|
|
51
|
+
backgroundColor?: string
|
|
52
|
+
backgroundImage?: string
|
|
53
|
+
backgroundPosition?: string
|
|
54
|
+
backgroundRepeat?: string
|
|
55
|
+
backgroundSize?: string
|
|
56
|
+
backgroundAttachment?: string
|
|
57
|
+
background?: string
|
|
58
|
+
|
|
59
|
+
// responsive overrides
|
|
60
|
+
sm?: { /* same keys above */ }
|
|
61
|
+
md?: { /* same keys above */ }
|
|
62
|
+
lg?: { /* same keys above */ }
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Usage
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
import { FlexboxNode, FlexItemNode } from '@janbox/storefront-builder';
|
|
70
|
+
|
|
71
|
+
// Item cố định width, item còn lại grow để fill
|
|
72
|
+
<FlexboxNode sx={{ flexDirection: 'row', columnGap: 16 }}>
|
|
73
|
+
<FlexItemNode sx={{ width: 200 }}>Sidebar</FlexItemNode>
|
|
74
|
+
<FlexItemNode sx={{ flexGrow: 1 }}>Main content</FlexItemNode>
|
|
75
|
+
</FlexboxNode>
|
|
76
|
+
|
|
77
|
+
// Item không shrink khi container hẹp
|
|
78
|
+
<FlexboxNode sx={{ flexDirection: 'row' }}>
|
|
79
|
+
<FlexItemNode sx={{ flexShrink: 0, width: 120 }}>Fixed</FlexItemNode>
|
|
80
|
+
<FlexItemNode sx={{ flexGrow: 1 }}>Flexible</FlexItemNode>
|
|
81
|
+
</FlexboxNode>
|
|
82
|
+
|
|
83
|
+
// Responsive: full width mobile, 50% desktop
|
|
84
|
+
<FlexboxNode sx={{ flexDirection: 'column', md: { flexDirection: 'row' } }}>
|
|
85
|
+
<FlexItemNode
|
|
86
|
+
sx={{
|
|
87
|
+
width: '100%',
|
|
88
|
+
md: { width: '50%' },
|
|
89
|
+
}}
|
|
90
|
+
>
|
|
91
|
+
Left
|
|
92
|
+
</FlexItemNode>
|
|
93
|
+
<FlexItemNode
|
|
94
|
+
sx={{
|
|
95
|
+
width: '100%',
|
|
96
|
+
md: { width: '50%' },
|
|
97
|
+
}}
|
|
98
|
+
>
|
|
99
|
+
Right
|
|
100
|
+
</FlexItemNode>
|
|
101
|
+
</FlexboxNode>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Rules
|
|
105
|
+
|
|
106
|
+
- **Canvas**: yes — chứa bất kỳ node con nào.
|
|
107
|
+
- **Parent**: phải là `FlexboxNode`.
|
|
108
|
+
|
|
109
|
+
## Related nodes
|
|
110
|
+
|
|
111
|
+
- [`FlexboxNode`](../flexbox/README.md) — parent bắt buộc của `FlexItemNode`
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# FlexboxNode
|
|
2
|
+
|
|
3
|
+
Flex container node (`display: flex`). Chỉ chấp nhận `FlexItemNode` làm con trực tiếp.
|
|
4
|
+
|
|
5
|
+
## Resolved name
|
|
6
|
+
|
|
7
|
+
`Flexbox`
|
|
8
|
+
|
|
9
|
+
## Configurable props
|
|
10
|
+
|
|
11
|
+
### Top-level props
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Description |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `container` | `boolean` | Bật max-width container constraint |
|
|
16
|
+
|
|
17
|
+
### `sx` props
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
sx?: {
|
|
21
|
+
// flex layout
|
|
22
|
+
flexDirection?: 'row' | 'column'
|
|
23
|
+
flexWrap?: 'nowrap' | 'wrap' | 'wrap-reverse'
|
|
24
|
+
justifyContent?: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly'
|
|
25
|
+
alignItems?: 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline'
|
|
26
|
+
rowGap?: number
|
|
27
|
+
columnGap?: number
|
|
28
|
+
|
|
29
|
+
// size
|
|
30
|
+
width?: number | string
|
|
31
|
+
height?: number | string
|
|
32
|
+
|
|
33
|
+
// spacing
|
|
34
|
+
paddingTop?: number
|
|
35
|
+
paddingRight?: number
|
|
36
|
+
paddingBottom?: number
|
|
37
|
+
paddingLeft?: number
|
|
38
|
+
marginTop?: number
|
|
39
|
+
marginRight?: number
|
|
40
|
+
marginBottom?: number
|
|
41
|
+
marginLeft?: number
|
|
42
|
+
|
|
43
|
+
// border
|
|
44
|
+
borderStyle?: string
|
|
45
|
+
borderColor?: string
|
|
46
|
+
borderTopWidth?: number
|
|
47
|
+
borderRightWidth?: number
|
|
48
|
+
borderBottomWidth?: number
|
|
49
|
+
borderLeftWidth?: number
|
|
50
|
+
borderTopLeftRadius?: number | string
|
|
51
|
+
borderTopRightRadius?: number | string
|
|
52
|
+
borderBottomRightRadius?: number | string
|
|
53
|
+
borderBottomLeftRadius?: number | string
|
|
54
|
+
|
|
55
|
+
// background
|
|
56
|
+
backgroundColor?: string
|
|
57
|
+
backgroundImage?: string
|
|
58
|
+
backgroundPosition?: string
|
|
59
|
+
backgroundRepeat?: string
|
|
60
|
+
backgroundSize?: string
|
|
61
|
+
backgroundAttachment?: string
|
|
62
|
+
background?: string
|
|
63
|
+
|
|
64
|
+
// responsive overrides
|
|
65
|
+
sm?: { /* same keys above */ }
|
|
66
|
+
md?: { /* same keys above */ }
|
|
67
|
+
lg?: { /* same keys above */ }
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Usage
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
import { FlexboxNode, FlexItemNode } from '@janbox/storefront-builder';
|
|
75
|
+
|
|
76
|
+
// Row ngang, căn giữa dọc
|
|
77
|
+
<FlexboxNode sx={{ flexDirection: 'row', alignItems: 'center', columnGap: 16 }}>
|
|
78
|
+
<FlexItemNode>Item A</FlexItemNode>
|
|
79
|
+
<FlexItemNode>Item B</FlexItemNode>
|
|
80
|
+
</FlexboxNode>
|
|
81
|
+
|
|
82
|
+
// Đổi direction theo breakpoint: column trên mobile, row trên desktop
|
|
83
|
+
<FlexboxNode
|
|
84
|
+
sx={{
|
|
85
|
+
flexDirection: 'column',
|
|
86
|
+
rowGap: 12,
|
|
87
|
+
md: {
|
|
88
|
+
flexDirection: 'row',
|
|
89
|
+
columnGap: 24,
|
|
90
|
+
alignItems: 'center',
|
|
91
|
+
},
|
|
92
|
+
}}
|
|
93
|
+
>
|
|
94
|
+
<FlexItemNode>Item A</FlexItemNode>
|
|
95
|
+
<FlexItemNode>Item B</FlexItemNode>
|
|
96
|
+
</FlexboxNode>
|
|
97
|
+
|
|
98
|
+
// Space-between, wrap
|
|
99
|
+
<FlexboxNode
|
|
100
|
+
sx={{
|
|
101
|
+
flexDirection: 'row',
|
|
102
|
+
flexWrap: 'wrap',
|
|
103
|
+
justifyContent: 'space-between',
|
|
104
|
+
rowGap: 16,
|
|
105
|
+
}}
|
|
106
|
+
>
|
|
107
|
+
<FlexItemNode>Item A</FlexItemNode>
|
|
108
|
+
<FlexItemNode>Item B</FlexItemNode>
|
|
109
|
+
<FlexItemNode>Item C</FlexItemNode>
|
|
110
|
+
</FlexboxNode>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Rules
|
|
114
|
+
|
|
115
|
+
- **Canvas**: yes.
|
|
116
|
+
- **canMoveIn**: chỉ chấp nhận `FlexItemNode`.
|
|
117
|
+
|
|
118
|
+
## Related nodes
|
|
119
|
+
|
|
120
|
+
- [`FlexItemNode`](../flex-item/README.md) — con trực tiếp duy nhất của `FlexboxNode`
|