@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.
- package/README.md +692 -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/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/marquee/marquee/marquee.js +1 -1
- 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/dist/style.css +1 -244
- 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
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useRef, useEffect } from "react";
|
|
3
3
|
import { useMergeRefs } from "@floating-ui/react";
|
|
4
|
-
import classes from "./checkbox.module.scss.js";
|
|
5
4
|
import { SvgLoader } from "../../components/svg-loader.js";
|
|
6
5
|
import CheckIcon from "../../assets/svg/done-check.svg.js";
|
|
7
6
|
import MinusIcon from "../../assets/svg/minus.svg.js";
|
|
8
7
|
import { getColorShadesByVariant } from "../../utils/index.js";
|
|
9
|
-
import
|
|
8
|
+
import "lodash-es";
|
|
9
|
+
import { getColorVar } from "../../theme/palette.js";
|
|
10
10
|
import { Primitive } from "../primitive/primitive.js";
|
|
11
11
|
const Checkbox = ({
|
|
12
12
|
children,
|
|
@@ -17,7 +17,7 @@ const Checkbox = ({
|
|
|
17
17
|
containerProps,
|
|
18
18
|
...props
|
|
19
19
|
}) => {
|
|
20
|
-
const { main
|
|
20
|
+
const { main } = getColorShadesByVariant(color);
|
|
21
21
|
const inputRef = useRef(null);
|
|
22
22
|
const mergedRefs = useMergeRefs([ref, inputRef]);
|
|
23
23
|
useEffect(() => {
|
|
@@ -31,20 +31,115 @@ const Checkbox = ({
|
|
|
31
31
|
{
|
|
32
32
|
as: "label",
|
|
33
33
|
...containerProps,
|
|
34
|
-
className: clsx(classes.checkbox__container, containerProps == null ? void 0 : containerProps.className),
|
|
35
34
|
sx: {
|
|
36
35
|
"--checkbox-color": main,
|
|
37
|
-
"
|
|
36
|
+
display: "flex",
|
|
37
|
+
alignItems: "center",
|
|
38
|
+
gap: "0.5rem",
|
|
39
|
+
cursor: "pointer",
|
|
40
|
+
width: "fit-content",
|
|
41
|
+
maxWidth: "100%",
|
|
42
|
+
"&:has(input:disabled)": {
|
|
43
|
+
cursor: "default",
|
|
44
|
+
pointerEvents: "none"
|
|
45
|
+
},
|
|
46
|
+
"&:hover .checkbox__main": {
|
|
47
|
+
"--checkbox-checkmark-color": "var(--checkbox-color)",
|
|
48
|
+
"& .checkbox__checkmark": {
|
|
49
|
+
boxShadow: "0 1px 3px 0 currentColor, 0 1px 2px -1px currentColor"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
38
52
|
...containerProps == null ? void 0 : containerProps.sx
|
|
39
53
|
},
|
|
40
54
|
children: [
|
|
41
|
-
/* @__PURE__ */ jsxs(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
55
|
+
/* @__PURE__ */ jsxs(
|
|
56
|
+
Primitive,
|
|
57
|
+
{
|
|
58
|
+
as: "span",
|
|
59
|
+
className: "checkbox__main",
|
|
60
|
+
sx: {
|
|
61
|
+
"--checkbox-checkmark-color": getColorVar("neutral.600"),
|
|
62
|
+
"--checkbox-checkmark-background": "transparent",
|
|
63
|
+
"--checkbox-icon-checked-opacity": 0,
|
|
64
|
+
"--checkbox-icon-indeterminate-opacity": 0,
|
|
65
|
+
position: "relative",
|
|
66
|
+
display: "inline-flex",
|
|
67
|
+
padding: "0.125rem",
|
|
68
|
+
width: "1.5rem",
|
|
69
|
+
height: "1.5rem",
|
|
70
|
+
flexShrink: 0,
|
|
71
|
+
"&:has(input:checked), &:has(input:indeterminate)": {
|
|
72
|
+
"--checkbox-checkmark-color": "var(--checkbox-color)",
|
|
73
|
+
"--checkbox-checkmark-background": "currentColor"
|
|
74
|
+
},
|
|
75
|
+
"&:has(input:checked)": {
|
|
76
|
+
"--checkbox-icon-checked-opacity": 1
|
|
77
|
+
},
|
|
78
|
+
"&:has(input:indeterminate)": {
|
|
79
|
+
"--checkbox-icon-indeterminate-opacity": 1
|
|
80
|
+
},
|
|
81
|
+
"&:has(input:disabled)": {
|
|
82
|
+
"--checkbox-checkmark-color": getColorVar("neutral.300")
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
children: [
|
|
86
|
+
/* @__PURE__ */ jsx(
|
|
87
|
+
Primitive,
|
|
88
|
+
{
|
|
89
|
+
...props,
|
|
90
|
+
type: "checkbox",
|
|
91
|
+
className,
|
|
92
|
+
ref: mergedRefs,
|
|
93
|
+
as: "input",
|
|
94
|
+
sx: {
|
|
95
|
+
width: 0,
|
|
96
|
+
height: 0,
|
|
97
|
+
opacity: 0,
|
|
98
|
+
display: "none"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
),
|
|
102
|
+
/* @__PURE__ */ jsxs(
|
|
103
|
+
Primitive,
|
|
104
|
+
{
|
|
105
|
+
as: "span",
|
|
106
|
+
className: "checkbox__checkmark",
|
|
107
|
+
sx: {
|
|
108
|
+
position: "relative",
|
|
109
|
+
color: "var(--checkbox-checkmark-color)",
|
|
110
|
+
backgroundColor: "var(--checkbox-checkmark-background)",
|
|
111
|
+
width: "100%",
|
|
112
|
+
height: "100%",
|
|
113
|
+
borderWidth: 1,
|
|
114
|
+
borderStyle: "solid",
|
|
115
|
+
borderColor: "currentColor",
|
|
116
|
+
borderRadius: "0.25rem",
|
|
117
|
+
"& [data-checkbox-icon]": {
|
|
118
|
+
color: "white",
|
|
119
|
+
position: "absolute",
|
|
120
|
+
top: "50%",
|
|
121
|
+
left: "50%",
|
|
122
|
+
transform: "translate(-50%, -50%)",
|
|
123
|
+
transitionProperty: "opacity",
|
|
124
|
+
transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)",
|
|
125
|
+
transitionDuration: "150ms"
|
|
126
|
+
},
|
|
127
|
+
'& [data-checkbox-icon="checked"]': {
|
|
128
|
+
opacity: "var(--checkbox-icon-checked-opacity)"
|
|
129
|
+
},
|
|
130
|
+
'& [data-checkbox-icon="indeterminate"]': {
|
|
131
|
+
opacity: "var(--checkbox-icon-indeterminate-opacity)"
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
children: [
|
|
135
|
+
/* @__PURE__ */ jsx(SvgLoader, { "data-checkbox-icon": "checked", width: 18, height: 18, source: CheckIcon }),
|
|
136
|
+
/* @__PURE__ */ jsx(SvgLoader, { "data-checkbox-icon": "indeterminate", width: 18, height: 18, source: MinusIcon })
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
)
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
),
|
|
48
143
|
children
|
|
49
144
|
]
|
|
50
145
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { JSX } from 'react';
|
|
2
2
|
import { ColorVariant, ShallowMerge } from '../../types';
|
|
3
3
|
import { PrimitiveProps } from '../primitive';
|
|
4
|
-
export type CheckboxProps = ShallowMerge<JSX.IntrinsicElements['input'], {
|
|
4
|
+
export type CheckboxProps = PrimitiveProps<ShallowMerge<JSX.IntrinsicElements['input'], {
|
|
5
5
|
color?: ColorVariant;
|
|
6
6
|
/**
|
|
7
7
|
* using this prop will make the checkbox indeterminate
|
|
@@ -10,4 +10,4 @@ export type CheckboxProps = ShallowMerge<JSX.IntrinsicElements['input'], {
|
|
|
10
10
|
*/
|
|
11
11
|
indeterminate?: boolean;
|
|
12
12
|
containerProps?: PrimitiveProps<JSX.IntrinsicElements['label']>;
|
|
13
|
-
}>;
|
|
13
|
+
}>, 'input'>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Chip
|
|
2
|
+
|
|
3
|
+
A compact label component used to display short status indicators, tags, or categorical information. Supports multiple color variants and responsive size options with a bordered, filled appearance based on the chosen color.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Chip } from '@janbox/storefront-ui';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Default | Description |
|
|
14
|
+
|------|------|---------|-------------|
|
|
15
|
+
| `color` | `ColorVariant` | `primary` | Color variant controlling the background (light shade) and border/text (main shade). Accepts `'primary' \| 'secondary' \| 'green' \| 'red' \| 'orange' \| 'blue' \| 'neutral'`. |
|
|
16
|
+
| `size` | `SizeVariant` | `md` | Base size of the chip controlling height, padding, font size, and border radius. Accepts `'xs' \| 'sm' \| 'md' \| 'lg'`. |
|
|
17
|
+
| `sm` | `ChipResponsiveProps` | - | Responsive size override applied at the sm breakpoint (768px+). Accepts `{ size?: SizeVariant }`. |
|
|
18
|
+
| `md` | `ChipResponsiveProps` | - | Responsive size override applied at the md breakpoint (1280px+). Accepts `{ size?: SizeVariant }`. |
|
|
19
|
+
| `lg` | `ChipResponsiveProps` | - | Responsive size override applied at the lg breakpoint (1680px+). Accepts `{ size?: SizeVariant }`. |
|
|
20
|
+
| `sx` | `ToResponsiveProps<StyledCSS>` | - | Escape-hatch for custom Emotion CSS styles, including responsive overrides via `sm`/`md`/`lg` keys. |
|
|
21
|
+
| `children` | `React.ReactNode` | - | Content rendered inside the chip, typically a short label string. |
|
|
22
|
+
|
|
23
|
+
## Examples
|
|
24
|
+
|
|
25
|
+
### Color variants
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import { Chip } from '@janbox/storefront-ui';
|
|
29
|
+
|
|
30
|
+
// Primary brand chip
|
|
31
|
+
<Chip color="primary">New</Chip>
|
|
32
|
+
|
|
33
|
+
// Success / status chip
|
|
34
|
+
<Chip color="green">In Stock</Chip>
|
|
35
|
+
|
|
36
|
+
// Error / warning chip
|
|
37
|
+
<Chip color="red">Out of Stock</Chip>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Size variants
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
import { Chip } from '@janbox/storefront-ui';
|
|
44
|
+
|
|
45
|
+
<div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
|
|
46
|
+
<Chip size="xs" color="primary">XS</Chip>
|
|
47
|
+
<Chip size="sm" color="primary">SM</Chip>
|
|
48
|
+
<Chip size="md" color="primary">MD</Chip>
|
|
49
|
+
<Chip size="lg" color="primary">LG</Chip>
|
|
50
|
+
</div>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Responsive size
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import { Chip } from '@janbox/storefront-ui';
|
|
57
|
+
|
|
58
|
+
// Renders xs on mobile, sm on tablet, md on desktop
|
|
59
|
+
<Chip size="xs" sm={{ size: 'sm' }} md={{ size: 'md' }} color="orange">
|
|
60
|
+
Sale
|
|
61
|
+
</Chip>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Dependencies
|
|
65
|
+
|
|
66
|
+
- `@emotion/react`
|
|
67
|
+
|
|
68
|
+
## See Also
|
|
69
|
+
|
|
70
|
+
- [Component Source](./index.ts)
|
|
71
|
+
- [Storybook Stories](./chip.stories.tsx)
|
|
72
|
+
- [Main README](../../README.md)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Collapse
|
|
2
|
+
|
|
3
|
+
An animated collapse/expand container that smoothly transitions content in and out using height (vertical) or width (horizontal) animation. Useful for accordions, expandable sections, and any show/hide content patterns.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Collapse } from '@janbox/storefront-ui';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Default | Description |
|
|
14
|
+
|------|------|---------|-------------|
|
|
15
|
+
| `open` | `boolean` | true | Controls whether the content is expanded (true) or collapsed (false). |
|
|
16
|
+
| `collapsedSize` | `number` | 0 | The size in pixels to collapse to when closed. Defaults to 0 (fully hidden). Use a non-zero value to show a partial peek of the content. |
|
|
17
|
+
| `orientation` | `'horizontal' \| 'vertical'` | 'vertical' | The axis along which the collapse animation occurs. 'vertical' animates height; 'horizontal' animates width. |
|
|
18
|
+
| `transition` | `AnimationProps['transition']` | - | Motion transition config passed to the underlying motion.div. Merged with the default duration of 0.15s. |
|
|
19
|
+
| `children` | `React.ReactNode` | - | Content to show or hide. |
|
|
20
|
+
|
|
21
|
+
## Examples
|
|
22
|
+
|
|
23
|
+
### Basic toggle
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
const [open, setOpen] = useState(false);
|
|
27
|
+
|
|
28
|
+
<>
|
|
29
|
+
<button onClick={() => setOpen(v => !v)}>Toggle</button>
|
|
30
|
+
<Collapse open={open}>
|
|
31
|
+
<div style={{ padding: 16 }}>
|
|
32
|
+
<p>Hidden content revealed on toggle.</p>
|
|
33
|
+
</div>
|
|
34
|
+
</Collapse>
|
|
35
|
+
</>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Partial collapse (peek)
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
const [open, setOpen] = useState(false);
|
|
42
|
+
|
|
43
|
+
<>
|
|
44
|
+
<button onClick={() => setOpen(v => !v)}>Show more</button>
|
|
45
|
+
<Collapse open={open} collapsedSize={40}>
|
|
46
|
+
<div style={{ padding: 16 }}>
|
|
47
|
+
<p>First line always visible</p>
|
|
48
|
+
<p>Second line revealed when expanded</p>
|
|
49
|
+
<p>Third line revealed when expanded</p>
|
|
50
|
+
</div>
|
|
51
|
+
</Collapse>
|
|
52
|
+
</>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Horizontal collapse
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
const [open, setOpen] = useState(true);
|
|
59
|
+
|
|
60
|
+
<div style={{ display: 'flex' }}>
|
|
61
|
+
<Collapse open={open} orientation="horizontal">
|
|
62
|
+
<div style={{ padding: 16, whiteSpace: 'nowrap', background: '#f0f0f0' }}>
|
|
63
|
+
Sidebar content
|
|
64
|
+
</div>
|
|
65
|
+
</Collapse>
|
|
66
|
+
<main>Main content</main>
|
|
67
|
+
</div>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Dependencies
|
|
71
|
+
|
|
72
|
+
- motion/react
|
|
73
|
+
|
|
74
|
+
## See Also
|
|
75
|
+
|
|
76
|
+
- [Component Source](./index.ts)
|
|
77
|
+
- [Storybook Stories](./collapse.stories.tsx)
|
|
78
|
+
- [Main README](../../README.md)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Container
|
|
2
|
+
|
|
3
|
+
Wrapper layout responsive giúp giới hạn chiều rộng nội dung theo breakpoint và tự động xử lý padding ngang. Component căn giữa nội dung trong viewport bằng cách tính padding hai bên theo breakpoint hiện tại, đồng thời hỗ trợ căn chỉnh vị trí để nội dung có thể tràn ra cạnh trái hoặc phải.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Container } from '@janbox/storefront-ui';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Default | Description |
|
|
14
|
+
|------|------|---------|-------------|
|
|
15
|
+
| `position` | `ContainerPosition` | `"center"` | Kiểm soát căn chỉnh ngang và padding của container. `'center'` áp dụng padding đều hai bên, `'start'` bỏ padding trái (nội dung tràn ra cạnh trái), `'end'` bỏ padding phải (nội dung tràn ra cạnh phải). |
|
|
16
|
+
| `sm` | `{ position?: ContainerPosition }` | - | Responsive override tại breakpoint `sm` (min-width: 768px). Nhận giá trị `position` để thay đổi căn chỉnh ở màn hình tablet. |
|
|
17
|
+
| `md` | `{ position?: ContainerPosition }` | - | Responsive override tại breakpoint `md` (min-width: 1280px). Nhận giá trị `position` để thay đổi căn chỉnh ở màn hình desktop. |
|
|
18
|
+
| `lg` | `{ position?: ContainerPosition }` | - | Responsive override tại breakpoint `lg` (min-width: 1680px). Nhận giá trị `position` để thay đổi căn chỉnh ở màn hình large desktop. |
|
|
19
|
+
| `sx` | `ToResponsiveProps<StyledCSS>` | - | Escape hatch để truyền custom Emotion CSS vào wrapper div ngoài cùng. Hỗ trợ responsive keys (`sm`, `md`, `lg`). |
|
|
20
|
+
| `children` | `React.ReactNode` | - | Nội dung render bên trong container. |
|
|
21
|
+
| `...rest` | `JSX.IntrinsicElements['div']` | - | Tất cả HTML div attributes tiêu chuẩn (`className`, `style`, `onClick`, v.v.) được forward xuống div ngoài cùng. |
|
|
22
|
+
|
|
23
|
+
## Examples
|
|
24
|
+
|
|
25
|
+
### Container căn giữa mặc định
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
<Container style={{ background: '#f0f0f0', minHeight: 100 }}>
|
|
29
|
+
Page content constrained to breakpoint width
|
|
30
|
+
</Container>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Căn trái (bleed left)
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
<Container position="start">
|
|
37
|
+
{/* Bỏ padding trái — nội dung kéo dài ra cạnh viewport bên trái */}
|
|
38
|
+
<HeroImage />
|
|
39
|
+
</Container>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Responsive position — căn giữa trên mobile, căn trái trên desktop
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
<Container position="center" md={{ position: 'start' }}>
|
|
46
|
+
<SidebarPanel />
|
|
47
|
+
</Container>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Dependencies
|
|
51
|
+
|
|
52
|
+
- @emotion/react
|
|
53
|
+
- lodash-es
|
|
54
|
+
|
|
55
|
+
## See Also
|
|
56
|
+
|
|
57
|
+
- [Component Source](./index.ts)
|
|
58
|
+
- [Storybook Stories](./container.stories.tsx)
|
|
59
|
+
- [Main README](../../README.md)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# CountUp
|
|
2
|
+
|
|
3
|
+
Component đếm số có animation, tự động đếm từ giá trị bắt đầu đến giá trị kết thúc một cách mượt mà. Sử dụng `motion/react` cho animation và tự động format số theo locale. Animation kích hoạt khi component xuất hiện trong viewport.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { CountUp } from '@janbox/storefront-ui';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Default | Description |
|
|
14
|
+
|------|------|---------|-------------|
|
|
15
|
+
| `to*` | `number` | - | Số đích cần đếm tới |
|
|
16
|
+
| `from` | `number` | 0 | Số bắt đầu đếm |
|
|
17
|
+
| `duration` | `number` | 2 | Thời gian animation tính bằng giây |
|
|
18
|
+
| `formatNumberOptions` | `FormatNumberOptions` | - | Tùy chọn format số (mở rộng từ `Intl.NumberFormatOptions` với thuộc tính `lng`). Kiểm soát số chữ số thập phân, format tiền tệ và các tùy chọn hiển thị số theo locale |
|
|
19
|
+
| `ref` | `React.Ref<HTMLSpanElement>` | - | Ref tới phần tử span bên dưới |
|
|
20
|
+
|
|
21
|
+
*Prop bắt buộc
|
|
22
|
+
|
|
23
|
+
## Examples
|
|
24
|
+
|
|
25
|
+
### Đếm cơ bản
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
<CountUp to={1000} style={{ fontSize: 32, fontWeight: 'bold' }} />
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Đếm từ giá trị cụ thể với duration tùy chỉnh
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
<CountUp from={500} to={1000} duration={3} style={{ fontSize: 24 }} />
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Số thập phân với animation chậm
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
<CountUp from={0} to={99.99} duration={5} style={{ fontSize: 32, fontWeight: 'bold' }} />
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Dependencies
|
|
44
|
+
|
|
45
|
+
- motion/react
|
|
46
|
+
- @floating-ui/react
|
|
47
|
+
|
|
48
|
+
## See Also
|
|
49
|
+
|
|
50
|
+
- [Component Source](./index.ts)
|
|
51
|
+
- [Storybook Stories](./countup.stories.tsx)
|
|
52
|
+
- [Main README](../../README.md)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# CountdownTimer
|
|
2
|
+
|
|
3
|
+
A headless countdown timer component that counts down to a target date and exposes the remaining time via a render prop. It delegates all UI rendering to the consumer through the `children` function, making it fully customizable for any display format.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { CountdownTimer } from '@janbox/storefront-ui';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Default | Description |
|
|
14
|
+
|------|------|---------|-------------|
|
|
15
|
+
| `date` | `Date \| string` | `new Date()` | The target date/time to count down to. Accepts a Date object or a date string parseable by dayjs. |
|
|
16
|
+
| `children` | `(timeDiff: TimeDiff) => React.ReactNode` | - | Render prop that receives the current time difference object and returns the UI to display. TimeDiff includes years, months, weeks, days, hours, minutes, seconds, their total counterparts (e.g. totalSeconds), and isFinished. |
|
|
17
|
+
| `onFinish` | `() => void` | - | Callback fired once when the countdown reaches zero (isFinished becomes true). |
|
|
18
|
+
| `omit` | `Partial<Record<'years' \| 'months' \| 'weeks' \| 'days' \| 'hours' \| 'minutes' \| 'seconds', boolean>>` | `{}` | Units to omit from the cascading time calculation. When a unit is omitted its value rolls over into the next smaller unit instead of being subtracted from the running total. |
|
|
19
|
+
|
|
20
|
+
## Examples
|
|
21
|
+
|
|
22
|
+
### Basic HH:MM:SS display
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
<CountdownTimer date={new Date(Date.now() + 60 * 60 * 1000)}>
|
|
26
|
+
{({ hours, minutes, seconds }) => (
|
|
27
|
+
<div style={{ fontSize: 24, fontFamily: 'monospace' }}>
|
|
28
|
+
{String(hours).padStart(2, '0')}:{String(minutes).padStart(2, '0')}:{String(seconds).padStart(2, '0')}
|
|
29
|
+
</div>
|
|
30
|
+
)}
|
|
31
|
+
</CountdownTimer>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Finished state with color change
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
<CountdownTimer
|
|
38
|
+
date={new Date(Date.now() + 30 * 1000)}
|
|
39
|
+
onFinish={() => console.log('Timer done!')}
|
|
40
|
+
>
|
|
41
|
+
{({ minutes, seconds, isFinished }) => (
|
|
42
|
+
<div style={{ color: isFinished ? 'red' : 'inherit', fontSize: 24 }}>
|
|
43
|
+
{isFinished
|
|
44
|
+
? "Time's up!"
|
|
45
|
+
: `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`}
|
|
46
|
+
</div>
|
|
47
|
+
)}
|
|
48
|
+
</CountdownTimer>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Days / Hours / Minutes breakdown
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
<CountdownTimer date={new Date(Date.now() + 2 * 24 * 60 * 60 * 1000)}>
|
|
55
|
+
{({ days, hours, minutes }) => (
|
|
56
|
+
<div style={{ display: 'flex', gap: 16 }}>
|
|
57
|
+
<div><strong>{days}</strong> days</div>
|
|
58
|
+
<div><strong>{hours}</strong> hours</div>
|
|
59
|
+
<div><strong>{minutes}</strong> minutes</div>
|
|
60
|
+
</div>
|
|
61
|
+
)}
|
|
62
|
+
</CountdownTimer>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Related Components
|
|
66
|
+
|
|
67
|
+
- [useCountdownTimer](../usecountdowntimer/README.md)
|
|
68
|
+
|
|
69
|
+
## Dependencies
|
|
70
|
+
|
|
71
|
+
- dayjs
|
|
72
|
+
|
|
73
|
+
## See Also
|
|
74
|
+
|
|
75
|
+
- [Component Source](./index.ts)
|
|
76
|
+
- [Storybook Stories](./countdown-timer.stories.tsx)
|
|
77
|
+
- [Main README](../../README.md)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# DatePicker
|
|
2
|
+
|
|
3
|
+
A controlled/uncontrolled date picker component that renders a trigger input with a floating calendar popup. Supports optional time selection, date range constraints, clearable state, error/helper text, and responsive sizing.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { DatePicker } from '@janbox/storefront-ui';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Default | Description |
|
|
14
|
+
|------|------|---------|-------------|
|
|
15
|
+
| `value` | `Date \| null` | - | Controlled selected date value. |
|
|
16
|
+
| `defaultValue` | `Date` | - | Uncontrolled initial selected date. |
|
|
17
|
+
| `onChange` | `(date?: Date) => void` | - | Callback fired when the selected date changes. |
|
|
18
|
+
| `placeholder` | `string` | - | Placeholder text shown in the trigger input when no date is selected. |
|
|
19
|
+
| `format` | `string` | `MM/DD/YYYY` | dayjs format string for displaying the selected date. Defaults to `'MM/DD/YYYY'`, or `'MM/DD/YYYY HH:mm'` when `timePicker` is true. |
|
|
20
|
+
| `minDate` | `Date` | - | Minimum selectable date. Dates before this value are disabled in the calendar. |
|
|
21
|
+
| `maxDate` | `Date` | - | Maximum selectable date. Dates after this value are disabled in the calendar. |
|
|
22
|
+
| `timePicker` | `boolean` | `false` | When true, renders a TimePicker below the calendar to allow time selection alongside the date. |
|
|
23
|
+
| `clearable` | `boolean` | `false` | When true and a date is selected, shows a clear (X) icon in the trigger instead of the calendar icon. |
|
|
24
|
+
| `onClear` | `() => void` | - | Callback fired when the clear icon is clicked. |
|
|
25
|
+
| `icon` | `React.ReactNode` | - | Custom icon to render in the trigger suffix area, overriding the default calendar/clear icon. |
|
|
26
|
+
| `open` | `boolean` | - | Controlled open state of the calendar popup. |
|
|
27
|
+
| `onOpenChange` | `UseFloatingOptions['onOpenChange']` | - | Callback fired when the popup open state changes. |
|
|
28
|
+
| `placement` | `Placement` | `bottom-end` | Floating UI placement for the calendar popup. |
|
|
29
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | `md` | Size variant for the trigger input. Responsive: also accepts `sm`, `md`, `lg` breakpoint overrides. |
|
|
30
|
+
| `error` | `boolean` | `false` | Applies error styling to the trigger input. |
|
|
31
|
+
| `helperText` | `string` | - | Helper or error message text rendered below the trigger input via FormHelperText. |
|
|
32
|
+
| `disabled` | `boolean` | `false` | Disables interaction with the trigger input. |
|
|
33
|
+
| `containerProps` | `PropsWithSx<JSX.IntrinsicElements['div']>` | - | Props passed to the outer wrapper div, including `sx` for style overrides. |
|
|
34
|
+
| `sx` | `ToResponsiveProps<StyledCSS>` | - | Responsive Emotion CSS style overrides applied to the outer container. |
|
|
35
|
+
|
|
36
|
+
## Examples
|
|
37
|
+
|
|
38
|
+
### Basic date picker with placeholder
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
<DatePicker
|
|
42
|
+
placeholder="Select date..."
|
|
43
|
+
onChange={(date) => console.log(date)}
|
|
44
|
+
/>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Controlled value with min/max constraints
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
<DatePicker
|
|
51
|
+
value={selectedDate}
|
|
52
|
+
minDate={new Date('2024-01-01')}
|
|
53
|
+
maxDate={new Date('2024-12-31')}
|
|
54
|
+
placeholder="Select date..."
|
|
55
|
+
onChange={(date) => setSelectedDate(date)}
|
|
56
|
+
/>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Date and time picker with clearable trigger and error state
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
<DatePicker
|
|
63
|
+
value={selectedDate}
|
|
64
|
+
timePicker
|
|
65
|
+
clearable
|
|
66
|
+
format="DD/MM/YYYY HH:mm"
|
|
67
|
+
error={!selectedDate}
|
|
68
|
+
helperText={!selectedDate ? 'Date is required' : undefined}
|
|
69
|
+
onClear={() => setSelectedDate(null)}
|
|
70
|
+
onChange={(date) => setSelectedDate(date)}
|
|
71
|
+
/>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Related Components
|
|
75
|
+
|
|
76
|
+
- [TimePicker](../timepicker/README.md)
|
|
77
|
+
- [InputMask](../inputmask/README.md)
|
|
78
|
+
- [Floating](../floating/README.md)
|
|
79
|
+
- [FloatingContent](../floatingcontent/README.md)
|
|
80
|
+
- [FloatingTrigger](../floatingtrigger/README.md)
|
|
81
|
+
- [FormHelperText](../formhelpertext/README.md)
|
|
82
|
+
- [Icon](../icon/README.md)
|
|
83
|
+
|
|
84
|
+
## Dependencies
|
|
85
|
+
|
|
86
|
+
- @floating-ui/react
|
|
87
|
+
- dayjs
|
|
88
|
+
- react-date-range
|
|
89
|
+
|
|
90
|
+
## See Also
|
|
91
|
+
|
|
92
|
+
- [Component Source](./index.ts)
|
|
93
|
+
- [Storybook Stories](./datepicker.stories.tsx)
|
|
94
|
+
- [Main README](../../README.md)
|