@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
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Primitive
|
|
2
|
+
|
|
3
|
+
Một polymorphic base component cho phép render HTML element động với hỗ trợ responsive styling qua `sx` prop. Đây là nền tảng để xây dựng các component khác trong design system, cho phép thay đổi loại element qua `as` prop trong khi vẫn duy trì khả năng styling nhất quán.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Primitive } from '@janbox/storefront-ui';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Default | Description |
|
|
14
|
+
|------|------|---------|-------------|
|
|
15
|
+
| `as` | `HTMLElementType` | `div` | Loại HTML element cần render (e.g., `'div'`, `'button'`, `'span'`, `'a'`). |
|
|
16
|
+
| `sx` | `ToResponsiveProps<StyledCSS>` | - | Responsive style object hỗ trợ breakpoint-specific styles (`sm`, `md`, `lg`). Nhận Emotion CSS properties với responsive variants. |
|
|
17
|
+
| `className` | `string` | - | CSS class names bổ sung cho element. |
|
|
18
|
+
| `children` | `React.ReactNode` | - | Child elements render bên trong component. |
|
|
19
|
+
|
|
20
|
+
## Examples
|
|
21
|
+
|
|
22
|
+
### Basic usage — render mặc định là div
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
<Primitive sx={{ padding: 16, backgroundColor: 'lightblue' }}>
|
|
26
|
+
Default div element
|
|
27
|
+
</Primitive>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Render as button với responsive styles
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
<Primitive
|
|
34
|
+
as="button"
|
|
35
|
+
sx={{
|
|
36
|
+
padding: 12,
|
|
37
|
+
fontSize: 14,
|
|
38
|
+
sm: { padding: 16, fontSize: 16 },
|
|
39
|
+
md: { padding: 20, fontSize: 18 }
|
|
40
|
+
}}
|
|
41
|
+
onClick={() => console.log('clicked')}
|
|
42
|
+
>
|
|
43
|
+
Responsive Button
|
|
44
|
+
</Primitive>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Render as anchor link với custom styles
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
<Primitive
|
|
51
|
+
as="a"
|
|
52
|
+
href="https://example.com"
|
|
53
|
+
sx={{
|
|
54
|
+
color: 'blue',
|
|
55
|
+
textDecoration: 'none',
|
|
56
|
+
'&:hover': { textDecoration: 'underline' }
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
Click me
|
|
60
|
+
</Primitive>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Related Components
|
|
64
|
+
|
|
65
|
+
- [Box](../box/README.md)
|
|
66
|
+
- [Flexbox](../flexbox/README.md)
|
|
67
|
+
- [Grid](../grid/README.md)
|
|
68
|
+
- [TableRow](../tablerow/README.md)
|
|
69
|
+
- [ListItem](../listitem/README.md)
|
|
70
|
+
|
|
71
|
+
## Dependencies
|
|
72
|
+
|
|
73
|
+
- `@emotion/react`
|
|
74
|
+
- `react`
|
|
75
|
+
|
|
76
|
+
## See Also
|
|
77
|
+
|
|
78
|
+
- [Component Source](./index.ts)
|
|
79
|
+
- [Storybook Stories](./primitive.stories.tsx)
|
|
80
|
+
- [Main README](../../README.md)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Progress
|
|
2
|
+
|
|
3
|
+
A progress bar component that visually represents a completion percentage. Supports both horizontal and vertical orientations with smooth animated fill transitions.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Progress } from '@janbox/storefront-ui';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Default | Description |
|
|
14
|
+
|------|------|---------|-------------|
|
|
15
|
+
| `value*` | `number` | - | The progress percentage (0–100). Controls the width (horizontal) or height (vertical) of the filled indicator. |
|
|
16
|
+
| `direction` | `'horizontal' \| 'vertical'` | `'horizontal'` | Orientation of the progress bar. |
|
|
17
|
+
| `activeProps` | `ComponentProps<'div'>` | `{}` | Props forwarded to the inner indicator element (the filled portion). Useful for custom styling or event handlers on the fill bar. |
|
|
18
|
+
| `children` | `React.ReactNode` | - | Optional content rendered inside the active/fill indicator element. |
|
|
19
|
+
|
|
20
|
+
*Required props
|
|
21
|
+
|
|
22
|
+
## Examples
|
|
23
|
+
|
|
24
|
+
### Default horizontal at 50%
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
<Progress value={50} style={{ width: 400 }} />
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Full completion
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
<Progress value={100} style={{ width: 400 }} />
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Vertical progress bar at 60%
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
<Progress value={60} direction="vertical" style={{ width: 8, height: 200 }} />
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Dependencies
|
|
43
|
+
|
|
44
|
+
- @emotion/react
|
|
45
|
+
|
|
46
|
+
## See Also
|
|
47
|
+
|
|
48
|
+
- [Component Source](./index.ts)
|
|
49
|
+
- [Storybook Stories](./progress.stories.tsx)
|
|
50
|
+
- [Main README](../../README.md)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# RadioButton
|
|
2
|
+
|
|
3
|
+
Component radio input được styled tùy chỉnh, render một label wrapper với checkmark visual. Hỗ trợ color variants cho brand và semantic theming, và chấp nhận tất cả các HTML input[type="radio"] attributes chuẩn.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { RadioButton } from '@janbox/storefront-ui';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Default | Description |
|
|
14
|
+
|------|------|---------|-------------|
|
|
15
|
+
| `color` | `ColorVariant` | `'primary'` | Điều khiển màu accent của radio button. Map tới design token color shades qua getColorShadesByVariant. |
|
|
16
|
+
| `containerProps` | `PrimitiveProps<JSX.IntrinsicElements['label']>` | - | Props được forward tới outer label element (Primitive as='label'). Hỗ trợ sx cho responsive styles và tất cả standard label HTML attributes. |
|
|
17
|
+
| `children` | `React.ReactNode` | - | Nội dung label được render bên cạnh radio button checkmark. |
|
|
18
|
+
| `className` | `string` | - | CSS class names bổ sung được apply vào inner input element. |
|
|
19
|
+
| `disabled` | `boolean` | `false` | Disable radio input, ngăn chặn interaction. |
|
|
20
|
+
| `checked` | `boolean` | - | Controlled checked state của radio input. |
|
|
21
|
+
| `defaultChecked` | `boolean` | - | Uncontrolled initial checked state của radio input. |
|
|
22
|
+
| `onChange` | `React.ChangeEventHandler<HTMLInputElement>` | - | Change event handler được gọi khi radio selection thay đổi. |
|
|
23
|
+
| `name` | `string` | - | Nhóm các radio buttons có cùng name để chỉ một button có thể được chọn tại một thời điểm. |
|
|
24
|
+
| `value` | `string \| number \| readonly string[]` | - | Value được liên kết với radio button này khi submit trong form. |
|
|
25
|
+
| `ref` | `React.Ref<HTMLInputElement>` | - | Ref được forward tới underlying input element. |
|
|
26
|
+
|
|
27
|
+
## Examples
|
|
28
|
+
|
|
29
|
+
### Default radio button
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
<RadioButton onChange={handleChange} />
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Grouped radio buttons với color và label
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
<>
|
|
39
|
+
<RadioButton name="delivery" value="standard" color="primary" defaultChecked onChange={handleChange}>
|
|
40
|
+
Giao hàng tiêu chuẩn
|
|
41
|
+
</RadioButton>
|
|
42
|
+
<RadioButton name="delivery" value="express" color="primary" onChange={handleChange}>
|
|
43
|
+
Giao hàng nhanh
|
|
44
|
+
</RadioButton>
|
|
45
|
+
</>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Error state và disabled
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
<>
|
|
52
|
+
<RadioButton color="red" defaultChecked onChange={handleChange}>
|
|
53
|
+
Tùy chọn không hợp lệ
|
|
54
|
+
</RadioButton>
|
|
55
|
+
<RadioButton disabled defaultChecked onChange={handleChange}>
|
|
56
|
+
Tùy chọn không khả dụng
|
|
57
|
+
</RadioButton>
|
|
58
|
+
</>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Sử dụng containerProps để customize label
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
<RadioButton
|
|
65
|
+
name="payment"
|
|
66
|
+
value="card"
|
|
67
|
+
color="blue"
|
|
68
|
+
containerProps={{
|
|
69
|
+
sx: { padding: '8px', border: '1px solid #ddd', borderRadius: '4px' }
|
|
70
|
+
}}
|
|
71
|
+
>
|
|
72
|
+
Thanh toán qua thẻ
|
|
73
|
+
</RadioButton>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Related Components
|
|
77
|
+
|
|
78
|
+
- [Primitive](../primitive/README.md)
|
|
79
|
+
- [Checkbox](../checkbox/README.md)
|
|
80
|
+
|
|
81
|
+
## Dependencies
|
|
82
|
+
|
|
83
|
+
- @emotion/react
|
|
84
|
+
|
|
85
|
+
## See Also
|
|
86
|
+
|
|
87
|
+
- [Component Source](./radio-button.tsx)
|
|
88
|
+
- [Storybook Stories](./radio-button.stories.ts)
|
|
89
|
+
- [Types](./types.ts)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
2
|
import { getRadioButtonProps } from "./helpers.js";
|
|
3
|
-
import classes from "./radio-button.module.scss.js";
|
|
4
3
|
import { getColorShadesByVariant } from "../../utils/index.js";
|
|
5
|
-
import
|
|
4
|
+
import "lodash-es";
|
|
5
|
+
import { getColorVar } from "../../theme/palette.js";
|
|
6
6
|
import { Primitive } from "../primitive/primitive.js";
|
|
7
7
|
const RadioButton = ({ ref, ..._props }) => {
|
|
8
8
|
const { children, color = "primary", className, containerProps, ...props } = getRadioButtonProps(_props);
|
|
@@ -11,17 +11,108 @@ const RadioButton = ({ ref, ..._props }) => {
|
|
|
11
11
|
Primitive,
|
|
12
12
|
{
|
|
13
13
|
...containerProps,
|
|
14
|
-
className: clsx(classes.radio_button__container, containerProps == null ? void 0 : containerProps.className),
|
|
15
14
|
sx: {
|
|
16
15
|
"--radio-button-color": main,
|
|
16
|
+
display: "flex",
|
|
17
|
+
width: "fit-content",
|
|
18
|
+
maxWidth: "100%",
|
|
19
|
+
alignItems: "center",
|
|
20
|
+
gap: "0.5rem",
|
|
21
|
+
cursor: "pointer",
|
|
22
|
+
"&:has(input:disabled)": {
|
|
23
|
+
cursor: "default",
|
|
24
|
+
pointerEvents: "none"
|
|
25
|
+
},
|
|
26
|
+
'&:hover [data-radio-button-part="main"]': {
|
|
27
|
+
"--radio-button-checkmark-color": "var(--radio-button-color)",
|
|
28
|
+
'& [data-radio-button-part="checkmark"]': {
|
|
29
|
+
boxShadow: "0 1px 3px 0 currentColor, 0 1px 2px -1px currentColor"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
17
32
|
...containerProps == null ? void 0 : containerProps.sx
|
|
18
33
|
},
|
|
19
34
|
as: "label",
|
|
20
35
|
children: [
|
|
21
|
-
/* @__PURE__ */ jsxs(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
36
|
+
/* @__PURE__ */ jsxs(
|
|
37
|
+
Primitive,
|
|
38
|
+
{
|
|
39
|
+
as: "span",
|
|
40
|
+
"data-radio-button-part": "main",
|
|
41
|
+
sx: {
|
|
42
|
+
"--radio-button-checkmark-color": getColorVar("neutral.600"),
|
|
43
|
+
"--radio-button-dot-scale": 0,
|
|
44
|
+
position: "relative",
|
|
45
|
+
display: "flex",
|
|
46
|
+
borderRadius: 9999,
|
|
47
|
+
padding: "0.125rem",
|
|
48
|
+
width: "1.5rem",
|
|
49
|
+
height: "1.5rem",
|
|
50
|
+
flexShrink: 0,
|
|
51
|
+
"&:has(input:checked)": {
|
|
52
|
+
"--radio-button-checkmark-color": "var(--radio-button-color)",
|
|
53
|
+
"--radio-button-dot-scale": 1
|
|
54
|
+
},
|
|
55
|
+
"&:has(input:disabled)": {
|
|
56
|
+
"--radio-button-checkmark-color": getColorVar("neutral.300")
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
children: [
|
|
60
|
+
/* @__PURE__ */ jsx(
|
|
61
|
+
Primitive,
|
|
62
|
+
{
|
|
63
|
+
...props,
|
|
64
|
+
className,
|
|
65
|
+
type: "radio",
|
|
66
|
+
ref,
|
|
67
|
+
as: "input",
|
|
68
|
+
sx: {
|
|
69
|
+
width: 0,
|
|
70
|
+
height: 0,
|
|
71
|
+
opacity: 0,
|
|
72
|
+
display: "none"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
),
|
|
76
|
+
/* @__PURE__ */ jsx(
|
|
77
|
+
Primitive,
|
|
78
|
+
{
|
|
79
|
+
as: "span",
|
|
80
|
+
"data-radio-button-part": "checkmark",
|
|
81
|
+
sx: {
|
|
82
|
+
color: "var(--radio-button-checkmark-color)",
|
|
83
|
+
width: "100%",
|
|
84
|
+
height: "100%",
|
|
85
|
+
borderWidth: 1,
|
|
86
|
+
borderStyle: "solid",
|
|
87
|
+
borderColor: "currentColor",
|
|
88
|
+
borderRadius: 9999,
|
|
89
|
+
position: "relative",
|
|
90
|
+
"&::after": {
|
|
91
|
+
content: '""',
|
|
92
|
+
position: "absolute",
|
|
93
|
+
top: 0,
|
|
94
|
+
right: 0,
|
|
95
|
+
bottom: 0,
|
|
96
|
+
left: 0,
|
|
97
|
+
borderWidth: 2,
|
|
98
|
+
borderStyle: "solid",
|
|
99
|
+
backgroundClip: "content-box",
|
|
100
|
+
width: "100%",
|
|
101
|
+
height: "100%",
|
|
102
|
+
borderRadius: 9999,
|
|
103
|
+
transform: "scale(var(--radio-button-dot-scale))",
|
|
104
|
+
transitionProperty: "transform",
|
|
105
|
+
transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)",
|
|
106
|
+
transitionDuration: "300ms",
|
|
107
|
+
borderColor: "transparent",
|
|
108
|
+
backgroundColor: "currentColor"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
)
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
),
|
|
25
116
|
children
|
|
26
117
|
]
|
|
27
118
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# RippleEffect
|
|
2
|
+
|
|
3
|
+
A presentational component that renders a Material-style ripple animation on pointer interactions. It attaches itself to its parent element and is used internally by interactive components like Button and IconButton.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { RippleEffect } from '@janbox/storefront-ui';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Default | Description |
|
|
14
|
+
|------|------|---------|-------------|
|
|
15
|
+
| `enabled` | `boolean` | `true` | Whether to enable the ripple effect. When false, the component renders nothing and no event listeners are attached. |
|
|
16
|
+
| `color` | `string` | `'currentColor'` | CSS color value for the ripple circle. Accepts any valid CSS color string. |
|
|
17
|
+
| `duration` | `number` | `400` | Duration of the ripple animation in milliseconds. |
|
|
18
|
+
| `opacity` | `number` | `0.2` | Starting opacity of the ripple circle before it fades out. |
|
|
19
|
+
| `sizeMultiplier` | `number` | `1` | Multiplier applied to the computed ripple radius. Values greater than 1 make the ripple larger relative to the parent element. |
|
|
20
|
+
|
|
21
|
+
## Examples
|
|
22
|
+
|
|
23
|
+
### Basic usage inside a button
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import { RippleEffect } from '@janbox/storefront-ui';
|
|
27
|
+
|
|
28
|
+
const MyButton = () => (
|
|
29
|
+
<button style={{ position: 'relative', overflow: 'hidden' }}>
|
|
30
|
+
Click me
|
|
31
|
+
<RippleEffect />
|
|
32
|
+
</button>
|
|
33
|
+
);
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Custom color and duration
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
import { RippleEffect } from '@janbox/storefront-ui';
|
|
40
|
+
|
|
41
|
+
const PrimaryButton = () => (
|
|
42
|
+
<button style={{ position: 'relative', overflow: 'hidden', backgroundColor: '#1976d2', color: '#fff' }}>
|
|
43
|
+
Submit
|
|
44
|
+
<RippleEffect color="#ffffff" opacity={0.3} duration={600} />
|
|
45
|
+
</button>
|
|
46
|
+
);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Disabled ripple conditionally
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
import { RippleEffect } from '@janbox/storefront-ui';
|
|
53
|
+
|
|
54
|
+
const ToggleButton = ({ disabled }: { disabled: boolean }) => (
|
|
55
|
+
<button style={{ position: 'relative', overflow: 'hidden' }} disabled={disabled}>
|
|
56
|
+
Toggle
|
|
57
|
+
<RippleEffect enabled={!disabled} />
|
|
58
|
+
</button>
|
|
59
|
+
);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## See Also
|
|
63
|
+
|
|
64
|
+
- [Component Source](./index.ts)
|
|
65
|
+
- [Storybook Stories](./rippleeffect.stories.tsx)
|
|
66
|
+
- [Main README](../../README.md)
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Select
|
|
2
|
+
|
|
3
|
+
A dropdown select component that supports searchable filtering, virtualized option lists, controlled/uncontrolled state, and responsive sizing. It is built on top of InputMask and Floating UI, making it suitable for forms that need single-value selection from a list of options.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Select } from '@janbox/storefront-ui';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Default | Description |
|
|
14
|
+
|------|------|---------|-------------|
|
|
15
|
+
| `options*` | `O[]` | - | Array of option objects to display in the dropdown list. |
|
|
16
|
+
| `value` | `V \| null` | - | Controlled selected value. |
|
|
17
|
+
| `defaultValue` | `V` | - | Default selected value for uncontrolled usage. |
|
|
18
|
+
| `onChange` | `(value: V, option: O) => void` | - | Callback fired when the user selects an option. Receives both the selected value and the full option object. |
|
|
19
|
+
| `placeholder` | `string` | - | Placeholder text shown when no option is selected. |
|
|
20
|
+
| `searchable` | `boolean` | - | Enables a search input inside the dropdown. Defaults to `true` automatically when options count exceeds `displayRowCount`. |
|
|
21
|
+
| `optionLabel` | `keyof O \| ((option: O) => React.ReactNode)` | `'label'` | Key or function to derive the display label from an option. |
|
|
22
|
+
| `optionValue` | `keyof O \| ((option: O) => V)` | `'value'` | Key or function to derive the value from an option. |
|
|
23
|
+
| `optionSearchLabel` | `keyof O \| Array<keyof O> \| ((option: O) => string \| string[])` | - | Key, array of keys, or function used to extract searchable text from an option. Falls back to `optionLabel` if not provided. |
|
|
24
|
+
| `selectedRenderer` | `(option: O) => React.ReactNode` | - | Custom render function for the selected option displayed in the trigger button. |
|
|
25
|
+
| `placement` | `Placement` | `'bottom-start'` | Floating UI placement for the dropdown panel. |
|
|
26
|
+
| `open` | `boolean` | - | Controlled open state of the dropdown. |
|
|
27
|
+
| `onOpenChange` | `UseFloatingOptions['onOpenChange']` | - | Callback fired when the dropdown open state changes. |
|
|
28
|
+
| `error` | `boolean` | - | Puts the trigger input into an error visual state. |
|
|
29
|
+
| `helperText` | `string` | - | Helper or validation message displayed below the select trigger. |
|
|
30
|
+
| `displayRowCount` | `number` | `5` | Maximum number of rows visible in the dropdown list before scrolling. Use `Infinity` to show all options. |
|
|
31
|
+
| `lazy` | `boolean` | `true` | When `true`, uses windowed/virtualized rendering for the option list. |
|
|
32
|
+
| `size` | `'xs' \| 'sm' \| 'md' \| 'lg'` | `'md'` | Size variant for the trigger input and option rows. Supports responsive breakpoint overrides via `sm`/`md`/`lg` props. |
|
|
33
|
+
| `disabled` | `boolean` | - | Disables the select trigger, preventing interaction. |
|
|
34
|
+
| `containerProps` | `PrimitiveProps<JSX.IntrinsicElements['div']>` | - | Props forwarded to the outer container `Primitive` div. |
|
|
35
|
+
| `listProps` | `HTMLAttributes<HTMLDivElement>` | - | Props forwarded to the `VirtualizedList` container div. |
|
|
36
|
+
| `inputProps` | `InputProps` | - | Props forwarded to the search `Input` rendered inside the dropdown when `searchable` is `true`. |
|
|
37
|
+
| `css` | `Interpolation` | - | Emotion CSS interpolation applied to the `InputMask` trigger element. |
|
|
38
|
+
| `sx` | `ToResponsiveProps<StyledCSS>` | - | Responsive sx prop for inline styles via Emotion (inherited from `InputMaskProps`). |
|
|
39
|
+
|
|
40
|
+
*Required props
|
|
41
|
+
|
|
42
|
+
## Examples
|
|
43
|
+
|
|
44
|
+
### Default
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
const options = [
|
|
48
|
+
{ label: 'Apple', value: 'apple' },
|
|
49
|
+
{ label: 'Banana', value: 'banana' },
|
|
50
|
+
{ label: 'Cherry', value: 'cherry' },
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
<Select
|
|
54
|
+
options={options}
|
|
55
|
+
placeholder="Select a fruit..."
|
|
56
|
+
onChange={(value, option) => console.log(value, option)}
|
|
57
|
+
/>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Searchable with error state
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
const options = [
|
|
64
|
+
{ label: 'Apple', value: 'apple' },
|
|
65
|
+
{ label: 'Banana', value: 'banana' },
|
|
66
|
+
{ label: 'Cherry', value: 'cherry' },
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
<Select
|
|
70
|
+
options={options}
|
|
71
|
+
placeholder="Search and select..."
|
|
72
|
+
searchable
|
|
73
|
+
error
|
|
74
|
+
helperText="Please select an option"
|
|
75
|
+
onChange={(value) => console.log(value)}
|
|
76
|
+
/>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Controlled with custom optionLabel and selectedRenderer
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
type Product = { id: number; name: string; price: number };
|
|
83
|
+
|
|
84
|
+
const products: Product[] = [
|
|
85
|
+
{ id: 1, name: 'Shirt', price: 29 },
|
|
86
|
+
{ id: 2, name: 'Pants', price: 59 },
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
<Select<number, Product>
|
|
90
|
+
options={products}
|
|
91
|
+
optionValue="id"
|
|
92
|
+
optionLabel="name"
|
|
93
|
+
optionSearchLabel="name"
|
|
94
|
+
value={selectedId}
|
|
95
|
+
selectedRenderer={(p) => `${p.name} — $${p.price}`}
|
|
96
|
+
onChange={(id) => setSelectedId(id)}
|
|
97
|
+
size="lg"
|
|
98
|
+
placeholder="Pick a product"
|
|
99
|
+
/>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Related Components
|
|
103
|
+
|
|
104
|
+
- [InputMask](../input-mask/README.md)
|
|
105
|
+
- [Input](../input/README.md)
|
|
106
|
+
- [Floating](../floating/README.md)
|
|
107
|
+
- [FloatingTrigger](../floatingtrigger/README.md)
|
|
108
|
+
- [FloatingContent](../floatingcontent/README.md)
|
|
109
|
+
- [FormHelperText](../formhelpertext/README.md)
|
|
110
|
+
- [VirtualizedList](../virtualizedlist/README.md)
|
|
111
|
+
- [Primitive](../primitive/README.md)
|
|
112
|
+
|
|
113
|
+
## Dependencies
|
|
114
|
+
|
|
115
|
+
- `@floating-ui/react`
|
|
116
|
+
- `@tanstack/react-virtual`
|
|
117
|
+
- `lodash-es`
|
|
118
|
+
- `@emotion/react`
|
|
119
|
+
|
|
120
|
+
## See Also
|
|
121
|
+
|
|
122
|
+
- [Component Source](./index.ts)
|
|
123
|
+
- [Storybook Stories](./select.stories.ts)
|
|
124
|
+
- [Main README](../../README.md)
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# StarRating
|
|
2
|
+
|
|
3
|
+
Component SVG một ngôi sao hiển thị rating theo tỷ lệ phần trăm bằng kỹ thuật clip-path fill. Dùng để hiển thị điểm đánh giá sản phẩm hoặc review score, hỗ trợ giá trị thập phân và color variants.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { StarRating } from '@janbox/storefront-ui';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Default | Description |
|
|
14
|
+
|------|------|---------|-------------|
|
|
15
|
+
| `rating*` | `number` | - | Giá trị rating hiện tại. Kiểm soát mức độ tô màu ngôi sao qua clip-path. Hỗ trợ giá trị thập phân (VD: 2.5). |
|
|
16
|
+
| `maxRating` | `number` | 5 | Giá trị rating tối đa. Dùng để tính phần trăm tô màu của ngôi sao. |
|
|
17
|
+
| `size` | `number` | 24 | Chiều rộng và chiều cao của SVG tính bằng pixel. ViewBox nội bộ luôn là 24x24 nên giá trị này scale output. |
|
|
18
|
+
| `colorVariant` | `ColorVariant` | `"secondary"` | Color variant dùng cho cả stroke và fill của ngôi sao. Ánh xạ tới design token color shades qua `getColorShadesByVariant`. |
|
|
19
|
+
| `className` | `string` | - | CSS class name tùy chọn áp dụng lên root SVG element. |
|
|
20
|
+
|
|
21
|
+
*Props bắt buộc
|
|
22
|
+
|
|
23
|
+
## Examples
|
|
24
|
+
|
|
25
|
+
### Mặc định (partial fill)
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
<StarRating rating={3} />
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Nửa ngôi sao với kích thước tùy chỉnh
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
<StarRating rating={2.5} size={32} />
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Max rating tùy chỉnh và color variant
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
<StarRating rating={7} maxRating={10} colorVariant="primary" />
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Ngôi sao đầy và rỗng
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
{/* Đầy hoàn toàn */}
|
|
47
|
+
<StarRating rating={5} />
|
|
48
|
+
|
|
49
|
+
{/* Rỗng hoàn toàn */}
|
|
50
|
+
<StarRating rating={0} />
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Related Components
|
|
54
|
+
|
|
55
|
+
- [Icon](../icon/README.md)
|
|
56
|
+
|
|
57
|
+
## Dependencies
|
|
58
|
+
|
|
59
|
+
- react (forwardRef, useId)
|
|
60
|
+
- @/utils (getColorShadesByVariant)
|
|
61
|
+
- @/types (ColorVariant)
|
|
62
|
+
|
|
63
|
+
## See Also
|
|
64
|
+
|
|
65
|
+
- [Component Source](./index.ts)
|
|
66
|
+
- [Storybook Stories](./star-rating.stories.tsx)
|
|
67
|
+
- [Main README](../../README.md)
|