@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,99 @@
|
|
|
1
|
+
# Stepper
|
|
2
|
+
|
|
3
|
+
Component hiển thị tiến trình theo từng bước — dùng cho checkout flow, wizard, onboarding, hoặc bất kỳ quy trình nhiều bước nào.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Stepper } from '@janbox/storefront-ui';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
|
|
13
|
+
### StepperProps
|
|
14
|
+
|
|
15
|
+
| Prop | Type | Default | Description |
|
|
16
|
+
|------|------|---------|-------------|
|
|
17
|
+
| `items*` | `StepProps[]` | - | Danh sách các bước |
|
|
18
|
+
| `activeIndex` | `number` | `0` | Index của bước đang active |
|
|
19
|
+
| `direction` | `'horizontal' \| 'vertical'` | `'horizontal'` | Hướng hiển thị |
|
|
20
|
+
| `circleSize` | `number` | `32` | Kích thước circle của mỗi step (px) |
|
|
21
|
+
| `className` | `string` | - | Custom class name |
|
|
22
|
+
|
|
23
|
+
### StepProps
|
|
24
|
+
|
|
25
|
+
| Prop | Type | Default | Description |
|
|
26
|
+
|------|------|---------|-------------|
|
|
27
|
+
| `state` | `'done' \| 'active' \| 'normal'` | auto | Trạng thái của step (tự tính từ `activeIndex` nếu không set) |
|
|
28
|
+
| `content` | `ReactNode` | - | Nội dung label của step |
|
|
29
|
+
| `description` | `ReactNode` | - | Mô tả phụ bên dưới step |
|
|
30
|
+
| `size` | `number` | - | Override circle size cho từng step riêng lẻ |
|
|
31
|
+
|
|
32
|
+
*Required props
|
|
33
|
+
|
|
34
|
+
## Examples
|
|
35
|
+
|
|
36
|
+
### Cơ bản — Horizontal
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
import { Stepper } from '@janbox/storefront-ui';
|
|
40
|
+
|
|
41
|
+
const steps = [
|
|
42
|
+
{ content: 'Giỏ hàng' },
|
|
43
|
+
{ content: 'Thông tin' },
|
|
44
|
+
{ content: 'Thanh toán' },
|
|
45
|
+
{ content: 'Hoàn tất' },
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
<Stepper items={steps} activeIndex={1} />
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Với mô tả
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
const steps = [
|
|
55
|
+
{ content: 'Bước 1', description: 'Chọn sản phẩm' },
|
|
56
|
+
{ content: 'Bước 2', description: 'Nhập địa chỉ' },
|
|
57
|
+
{ content: 'Bước 3', description: 'Xác nhận đơn' },
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
<Stepper items={steps} activeIndex={2} />
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Vertical
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
<Stepper
|
|
67
|
+
items={steps}
|
|
68
|
+
activeIndex={1}
|
|
69
|
+
direction="vertical"
|
|
70
|
+
circleSize={28}
|
|
71
|
+
/>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Controlled — cập nhật bước theo user action
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
const [step, setStep] = useState(0);
|
|
78
|
+
|
|
79
|
+
<Stepper items={steps} activeIndex={step} />
|
|
80
|
+
|
|
81
|
+
<Button onClick={() => setStep(s => Math.min(s + 1, steps.length - 1))}>
|
|
82
|
+
Tiếp theo
|
|
83
|
+
</Button>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Notes
|
|
87
|
+
|
|
88
|
+
- State của từng step được tính tự động từ `activeIndex`: bước trước → `done`, bước hiện tại → `active`, bước sau → `normal`
|
|
89
|
+
- `Progress` component được dùng làm progress bar nền bên dưới các step
|
|
90
|
+
- `circleSize` ảnh hưởng đến vị trí căn giữa của progress bar
|
|
91
|
+
|
|
92
|
+
## Related Components
|
|
93
|
+
|
|
94
|
+
- [Progress](../progress/README.md)
|
|
95
|
+
|
|
96
|
+
## See Also
|
|
97
|
+
|
|
98
|
+
- [Component Source](./index.ts)
|
|
99
|
+
- [Main README](../../README.md)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# SuspenseQuery
|
|
2
|
+
|
|
3
|
+
A generic component that combines React Suspense and ErrorBoundary to handle async data fetching declaratively. It manages promise lifecycle with dep-based re-fetching, providing a render-prop children pattern to consume the resolved data.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { SuspenseQuery } from '@janbox/storefront-ui';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Default | Description |
|
|
14
|
+
|------|------|---------|-------------|
|
|
15
|
+
| `factory*` | `(...args: A) => Promise<R>` | - | Async function that returns a Promise. Called on mount and whenever deps change (deep equality check). Receives the spread deps array as arguments. |
|
|
16
|
+
| `children*` | `(data: R) => React.ReactNode` | - | Render-prop function called with the resolved data once the Promise resolves. Rendered inside Suspense so it only runs after data is available. |
|
|
17
|
+
| `deps` | `A` | [] | Dependency array passed as arguments to factory. Required when factory takes arguments; optional (defaults to []) when factory takes no arguments. Re-triggers factory on deep-equality change. |
|
|
18
|
+
| `LoadingComponent` | `ComponentType` | - | Optional component rendered as the Suspense fallback while the Promise is pending. If omitted, the fallback renders null. |
|
|
19
|
+
| `ErrorComponent` | `ComponentType<FallbackProps>` | DefaultErrorComponent (logs error, renders nothing) | Component rendered by the ErrorBoundary when the Promise rejects or a render error occurs. Receives the react-error-boundary FallbackProps (error, resetErrorBoundary). Defaults to a silent component that logs the error to console. |
|
|
20
|
+
|
|
21
|
+
*Required props
|
|
22
|
+
|
|
23
|
+
## Examples
|
|
24
|
+
|
|
25
|
+
### Basic usage — no arguments
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
const fetchUser = () => fetch('/api/me').then(res => res.json());
|
|
29
|
+
|
|
30
|
+
<SuspenseQuery factory={fetchUser}>
|
|
31
|
+
{(user) => <span>{user.name}</span>}
|
|
32
|
+
</SuspenseQuery>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### With deps and custom loading/error components
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
const fetchProduct = (id: string) => fetch(`/api/products/${id}`).then(r => r.json());
|
|
39
|
+
|
|
40
|
+
<SuspenseQuery
|
|
41
|
+
factory={fetchProduct}
|
|
42
|
+
deps={[productId]}
|
|
43
|
+
LoadingComponent={() => <Skeleton />}
|
|
44
|
+
ErrorComponent={({ error }) => <Alert color="red">{error.message}</Alert>}
|
|
45
|
+
>
|
|
46
|
+
{(product) => <ProductCard title={product.name} price={product.price} />}
|
|
47
|
+
</SuspenseQuery>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Multiple deps — re-fetches when any dep changes
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
const searchItems = (query: string, page: number) =>
|
|
54
|
+
fetch(`/api/search?q=${query}&page=${page}`).then(r => r.json());
|
|
55
|
+
|
|
56
|
+
<SuspenseQuery factory={searchItems} deps={[query, page]}>
|
|
57
|
+
{(result) => (
|
|
58
|
+
<ul>
|
|
59
|
+
{result.items.map(item => <li key={item.id}>{item.title}</li>)}
|
|
60
|
+
</ul>
|
|
61
|
+
)}
|
|
62
|
+
</SuspenseQuery>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Related Components
|
|
66
|
+
|
|
67
|
+
- [ErrorBoundary](../errorboundary/README.md)
|
|
68
|
+
|
|
69
|
+
## Dependencies
|
|
70
|
+
|
|
71
|
+
- react-error-boundary
|
|
72
|
+
- lodash-es
|
|
73
|
+
|
|
74
|
+
## See Also
|
|
75
|
+
|
|
76
|
+
- [Component Source](./index.ts)
|
|
77
|
+
- [Storybook Stories](./suspensequery.stories.tsx)
|
|
78
|
+
- [Main README](../../README.md)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Swiper
|
|
2
|
+
|
|
3
|
+
A responsive carousel/swiper component built on top of Embla Carousel, supporting touch gestures, wheel scroll, autoplay, and multiple paging styles. Used for displaying horizontally (or vertically) scrollable slide content such as product carousels and image galleries.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Swiper } from '@janbox/storefront-ui';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Default | Description |
|
|
14
|
+
|------|------|---------|-------------|
|
|
15
|
+
| `children` | `React.ReactNode` | - | Slide content — typically one or more SwiperSlide components. |
|
|
16
|
+
| `slidesPerView` | `number \| 'auto'` | - | Number of slides visible at once at the base (mobile) breakpoint. Use `'auto'` to let each slide define its own width. |
|
|
17
|
+
| `spaceBetween` | `number` | - | Gap in pixels between slides at the base (mobile) breakpoint. |
|
|
18
|
+
| `sm` | `{ slidesPerView?: number \| 'auto'; spaceBetween?: number }` | - | Override `slidesPerView` và `spaceBetween` tại breakpoint sm (≥768px). |
|
|
19
|
+
| `md` | `{ slidesPerView?: number \| 'auto'; spaceBetween?: number }` | - | Override `slidesPerView` và `spaceBetween` tại breakpoint md (≥1280px). |
|
|
20
|
+
| `lg` | `{ slidesPerView?: number \| 'auto'; spaceBetween?: number }` | - | Override `slidesPerView` và `spaceBetween` tại breakpoint lg (≥1680px). |
|
|
21
|
+
| `autoplayOptions` | `ShallowMerge<AutoplayOptionsType, { delay?: number }>` | `{ active: false, delay: 3000, stopOnFocusIn: false, stopOnInteraction: false, stopOnLastSnap: false }` | Embla Carousel Autoplay plugin options. Autoplay mặc định không kích hoạt. |
|
|
22
|
+
| `options` | `Partial<SwiperOptions>` | - | Raw Embla Carousel options (e.g. `loop`, `axis`, `align`). Truyền thẳng vào `useEmblaCarousel`. Default `align` là `'start'`. |
|
|
23
|
+
| `paging` | `{ type: 'dots'; color?: ColorVariant; size?: number; spaceBetween?: number; offset?: number } \| { type: 'side-arrows'; color?: ColorVariant; visibility?: 'auto' \| 'always'; anchorSelector?: string; size?: number; offsetX?: number \| 'center'; offsetY?: number \| 'center' }` | - | Cấu hình paging control. Dùng `'dots'` cho dải chấm tròn bên dưới slides, hoặc `'side-arrows'` cho nút prev/next ở hai bên. |
|
|
24
|
+
| `onApi` | `(api: EmblaCarouselType) => void` | - | Callback khi Embla Carousel API instance sẵn sàng, cho phép truy cập trực tiếp các carousel methods. |
|
|
25
|
+
| `sx` | `ToResponsiveProps<StyledCSS>` | - | Responsive style overrides áp dụng lên root container của swiper. |
|
|
26
|
+
| `ref` | `React.Ref<HTMLDivElement>` | - | Forwarded ref đến root container element (cũng được forward vào Embla's ref). |
|
|
27
|
+
|
|
28
|
+
## Examples
|
|
29
|
+
|
|
30
|
+
### Basic carousel with dot paging
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
<Swiper spaceBetween={20} paging={{ type: 'dots' }} sx={{ width: 300, sm: { width: 500 } }}>
|
|
34
|
+
<SwiperSlide style={{ width: 200, height: 200, background: 'orange' }}>Slide 1</SwiperSlide>
|
|
35
|
+
<SwiperSlide style={{ width: 200, height: 200, background: 'orange' }}>Slide 2</SwiperSlide>
|
|
36
|
+
<SwiperSlide style={{ width: 200, height: 200, background: 'orange' }}>Slide 3</SwiperSlide>
|
|
37
|
+
</Swiper>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Product carousel with side arrows and responsive slides per view
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
<Swiper
|
|
44
|
+
slidesPerView={2}
|
|
45
|
+
spaceBetween={16}
|
|
46
|
+
sm={{ slidesPerView: 3, spaceBetween: 20 }}
|
|
47
|
+
md={{ slidesPerView: 4, spaceBetween: 24 }}
|
|
48
|
+
paging={{
|
|
49
|
+
type: 'side-arrows',
|
|
50
|
+
anchorSelector: '.product-image',
|
|
51
|
+
visibility: 'auto',
|
|
52
|
+
color: 'primary',
|
|
53
|
+
}}
|
|
54
|
+
>
|
|
55
|
+
{products.map((p) => (
|
|
56
|
+
<SwiperSlide key={p.id}>
|
|
57
|
+
<img className="product-image" src={p.image} alt={p.name} />
|
|
58
|
+
<p>{p.name}</p>
|
|
59
|
+
</SwiperSlide>
|
|
60
|
+
))}
|
|
61
|
+
</Swiper>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Autoplay banner with loop enabled
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
<Swiper
|
|
68
|
+
slidesPerView={1}
|
|
69
|
+
options={{ loop: true }}
|
|
70
|
+
autoplayOptions={{ active: true, delay: 4000 }}
|
|
71
|
+
paging={{ type: 'dots', color: 'primary', offset: 12 }}
|
|
72
|
+
sx={{ width: '100%' }}
|
|
73
|
+
>
|
|
74
|
+
<SwiperSlide><img src="/banner1.jpg" alt="Banner 1" /></SwiperSlide>
|
|
75
|
+
<SwiperSlide><img src="/banner2.jpg" alt="Banner 2" /></SwiperSlide>
|
|
76
|
+
<SwiperSlide><img src="/banner3.jpg" alt="Banner 3" /></SwiperSlide>
|
|
77
|
+
</Swiper>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Related Components
|
|
81
|
+
|
|
82
|
+
- [SwiperSlide](../swiperslide/README.md)
|
|
83
|
+
- [SwiperDotsPaging](../swiperdotspaging/README.md)
|
|
84
|
+
- [SwiperSideArrowsPaging](../swipersidearrowspaging/README.md)
|
|
85
|
+
|
|
86
|
+
## Dependencies
|
|
87
|
+
|
|
88
|
+
- embla-carousel
|
|
89
|
+
- embla-carousel-react
|
|
90
|
+
- embla-carousel-autoplay
|
|
91
|
+
- embla-carousel-wheel-gestures
|
|
92
|
+
- @floating-ui/react
|
|
93
|
+
- lodash-es
|
|
94
|
+
|
|
95
|
+
## See Also
|
|
96
|
+
|
|
97
|
+
- [Component Source](./index.ts)
|
|
98
|
+
- [Storybook Stories](./swiper.stories.tsx)
|
|
99
|
+
- [Main README](../../README.md)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Switch
|
|
2
|
+
|
|
3
|
+
A toggle switch component built on a native checkbox input, styled as a sliding toggle. Use it for binary on/off settings where users expect immediate visual feedback without a form submission.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Switch } from '@janbox/storefront-ui';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Default | Description |
|
|
14
|
+
|------|------|---------|-------------|
|
|
15
|
+
| `color` | `ColorVariant` | `'primary'` | Color variant for the checked state. Accepts `'primary'` \| `'secondary'` \| `'green'` \| `'red'` \| `'orange'` \| `'blue'` \| `'neutral'`. |
|
|
16
|
+
| `checked` | `boolean` | - | Controlled checked state of the switch. |
|
|
17
|
+
| `defaultChecked` | `boolean` | `false` | Initial checked state for uncontrolled usage. |
|
|
18
|
+
| `disabled` | `boolean` | `false` | Disables the switch and prevents interaction. Renders the knob in a neutral gray and suppresses pointer events. |
|
|
19
|
+
| `onChange` | `React.ChangeEventHandler<HTMLInputElement>` | - | Change handler fired when the switch is toggled. |
|
|
20
|
+
| `children` | `React.ReactNode` | - | Optional label content rendered to the right of the toggle. |
|
|
21
|
+
| `className` | `string` | - | Additional CSS class applied to the underlying checkbox input. |
|
|
22
|
+
| `ref` | `React.Ref<HTMLInputElement>` | - | Forwarded ref to the underlying checkbox input element. |
|
|
23
|
+
|
|
24
|
+
Inherits all standard `<input type="checkbox">` HTML attributes.
|
|
25
|
+
|
|
26
|
+
## Examples
|
|
27
|
+
|
|
28
|
+
### Default (uncontrolled)
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
<Switch onChange={(e) => console.log(e.target.checked)} />
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Pre-checked with green color and label
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
<Switch color="green" defaultChecked>
|
|
38
|
+
Enable notifications
|
|
39
|
+
</Switch>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Disabled states
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
46
|
+
<Switch disabled>Disabled off</Switch>
|
|
47
|
+
<Switch disabled defaultChecked>Disabled on</Switch>
|
|
48
|
+
</div>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Controlled usage
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
const [enabled, setEnabled] = useState(false);
|
|
55
|
+
|
|
56
|
+
<Switch
|
|
57
|
+
checked={enabled}
|
|
58
|
+
onChange={(e) => setEnabled(e.target.checked)}
|
|
59
|
+
>
|
|
60
|
+
Dark mode
|
|
61
|
+
</Switch>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Dependencies
|
|
65
|
+
|
|
66
|
+
- `@floating-ui/react` (useMergeRefs)
|
|
67
|
+
|
|
68
|
+
## See Also
|
|
69
|
+
|
|
70
|
+
- [Component Source](./switch.tsx)
|
|
71
|
+
- [Types](./types.ts)
|
|
72
|
+
- [Storybook Stories](./switch.stories.ts)
|
|
73
|
+
- [Main README](../../README.md)
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { SwitchProps } from './types';
|
|
2
|
-
export declare const Switch: ({ children, className, ref, ...props }: SwitchProps) => import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare const Switch: ({ children, className, color, ref, ...props }: SwitchProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,15 +1,114 @@
|
|
|
1
1
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
import "lodash-es";
|
|
3
|
+
import { getColorVar } from "../../theme/palette.js";
|
|
4
|
+
import { Primitive } from "../primitive/primitive.js";
|
|
5
|
+
const Switch = ({ children, className, color = "primary", ref, ...props }) => {
|
|
6
|
+
return /* @__PURE__ */ jsxs(
|
|
7
|
+
Primitive,
|
|
8
|
+
{
|
|
9
|
+
as: "label",
|
|
10
|
+
sx: {
|
|
11
|
+
display: "flex",
|
|
12
|
+
alignItems: "center",
|
|
13
|
+
gap: "0.5rem",
|
|
14
|
+
cursor: "pointer",
|
|
15
|
+
width: "fit-content",
|
|
16
|
+
maxWidth: "100%",
|
|
17
|
+
paddingLeft: "0.125rem",
|
|
18
|
+
paddingRight: "0.125rem",
|
|
19
|
+
"&:has(input:disabled)": {
|
|
20
|
+
cursor: "default",
|
|
21
|
+
pointerEvents: "none"
|
|
22
|
+
},
|
|
23
|
+
'&:hover [data-switch-part="main"]': {
|
|
24
|
+
"--switch-track-background": getColorVar("neutral.300"),
|
|
25
|
+
"--switch-thumb-shadow": "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)"
|
|
26
|
+
},
|
|
27
|
+
'&:hover [data-switch-part="main"]:has(input:checked)': {
|
|
28
|
+
"--switch-track-background": getColorVar(`${color}.500`),
|
|
29
|
+
"--switch-thumb-shadow": `0 1px 3px 0 ${getColorVar(`${color}.600`)}, 0 1px 2px -1px ${getColorVar(
|
|
30
|
+
`${color}.600`
|
|
31
|
+
)}`
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
children: [
|
|
35
|
+
/* @__PURE__ */ jsx(
|
|
36
|
+
Primitive,
|
|
37
|
+
{
|
|
38
|
+
as: "span",
|
|
39
|
+
"data-switch-part": "main",
|
|
40
|
+
sx: {
|
|
41
|
+
"--switch-track-background": getColorVar("neutral.300"),
|
|
42
|
+
"--switch-thumb-background": "white",
|
|
43
|
+
"--switch-thumb-left": "0",
|
|
44
|
+
"--switch-thumb-transform": "translateY(-50%)",
|
|
45
|
+
"--switch-thumb-shadow": "none",
|
|
46
|
+
position: "relative",
|
|
47
|
+
width: "2rem",
|
|
48
|
+
height: "1.5rem",
|
|
49
|
+
"&::before": {
|
|
50
|
+
position: "absolute",
|
|
51
|
+
left: 0,
|
|
52
|
+
right: 0,
|
|
53
|
+
content: '""',
|
|
54
|
+
backgroundColor: "var(--switch-track-background)",
|
|
55
|
+
height: "1rem",
|
|
56
|
+
top: "50%",
|
|
57
|
+
transform: "translateY(-50%)",
|
|
58
|
+
borderRadius: 9999,
|
|
59
|
+
transition: "all 150ms cubic-bezier(0.4, 0, 0.2, 1)"
|
|
60
|
+
},
|
|
61
|
+
"&::after": {
|
|
62
|
+
position: "absolute",
|
|
63
|
+
left: "var(--switch-thumb-left)",
|
|
64
|
+
aspectRatio: 1,
|
|
65
|
+
content: '""',
|
|
66
|
+
backgroundColor: "var(--switch-thumb-background)",
|
|
67
|
+
height: "1.25rem",
|
|
68
|
+
top: "50%",
|
|
69
|
+
transform: "var(--switch-thumb-transform)",
|
|
70
|
+
borderRadius: 9999,
|
|
71
|
+
borderWidth: 1,
|
|
72
|
+
borderStyle: "solid",
|
|
73
|
+
borderColor: `var(--switch-thumb-border-color, ${getColorVar("border.default")})`,
|
|
74
|
+
boxShadow: "var(--switch-thumb-shadow)",
|
|
75
|
+
transition: "all 150ms cubic-bezier(0.4, 0, 0.2, 1)"
|
|
76
|
+
},
|
|
77
|
+
"&:has(input:checked)": {
|
|
78
|
+
"--switch-track-background": getColorVar(`${color}.400`),
|
|
79
|
+
"--switch-thumb-background": getColorVar(`${color}.600`),
|
|
80
|
+
"--switch-thumb-border-color": getColorVar(`${color}.600`),
|
|
81
|
+
"--switch-thumb-left": "100%",
|
|
82
|
+
"--switch-thumb-transform": "translateX(-100%) translateY(-50%)"
|
|
83
|
+
},
|
|
84
|
+
"&:has(input:disabled)": {
|
|
85
|
+
"--switch-track-background": getColorVar("neutral.300"),
|
|
86
|
+
"--switch-thumb-background": getColorVar("neutral.400"),
|
|
87
|
+
"--switch-thumb-border-color": getColorVar("neutral.400")
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
children: /* @__PURE__ */ jsx(
|
|
91
|
+
Primitive,
|
|
92
|
+
{
|
|
93
|
+
...props,
|
|
94
|
+
type: "checkbox",
|
|
95
|
+
className,
|
|
96
|
+
ref,
|
|
97
|
+
as: "input",
|
|
98
|
+
sx: {
|
|
99
|
+
width: 0,
|
|
100
|
+
height: 0,
|
|
101
|
+
opacity: 0,
|
|
102
|
+
display: "none"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
),
|
|
108
|
+
children
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
);
|
|
13
112
|
};
|
|
14
113
|
export {
|
|
15
114
|
Switch
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Table
|
|
2
|
+
|
|
3
|
+
A semantic HTML table component with composable sub-components for building data tables. Includes Table, TableHead, TableBody, TableFooter, TableRow, and TableCell components with built-in styling and sx prop support.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Table } from '@janbox/storefront-ui';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
|
|
13
|
+
| Prop | Type | Default | Description |
|
|
14
|
+
|------|------|---------|-------------|
|
|
15
|
+
| `children*` | `React.ReactNode` | - | Table content, typically TableHead, TableBody, and optionally TableFooter components |
|
|
16
|
+
| `sx` | `StyledCSS \| ToResponsiveProps<StyledCSS>` | - | Custom styles using Emotion CSS-in-JS with responsive breakpoint support (sm, md, lg) |
|
|
17
|
+
| `style` | `React.CSSProperties` | - | Inline styles for the table element |
|
|
18
|
+
| `className` | `string` | - | Additional CSS class names |
|
|
19
|
+
|
|
20
|
+
*Required props
|
|
21
|
+
|
|
22
|
+
## Examples
|
|
23
|
+
|
|
24
|
+
### Basic Table
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
<Table style={{ width: 600, borderCollapse: 'collapse' }}>
|
|
28
|
+
<TableHead>
|
|
29
|
+
<TableRow>
|
|
30
|
+
<TableCell style={{ fontWeight: 'bold' }}>Name</TableCell>
|
|
31
|
+
<TableCell style={{ fontWeight: 'bold' }}>Age</TableCell>
|
|
32
|
+
<TableCell style={{ fontWeight: 'bold' }}>Email</TableCell>
|
|
33
|
+
</TableRow>
|
|
34
|
+
</TableHead>
|
|
35
|
+
<TableBody>
|
|
36
|
+
<TableRow>
|
|
37
|
+
<TableCell>Alice</TableCell>
|
|
38
|
+
<TableCell>28</TableCell>
|
|
39
|
+
<TableCell>alice@example.com</TableCell>
|
|
40
|
+
</TableRow>
|
|
41
|
+
<TableRow>
|
|
42
|
+
<TableCell>Bob</TableCell>
|
|
43
|
+
<TableCell>34</TableCell>
|
|
44
|
+
<TableCell>bob@example.com</TableCell>
|
|
45
|
+
</TableRow>
|
|
46
|
+
</TableBody>
|
|
47
|
+
</Table>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Table with Footer
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
<Table style={{ width: 400, borderCollapse: 'collapse' }}>
|
|
54
|
+
<TableHead>
|
|
55
|
+
<TableRow>
|
|
56
|
+
<TableCell style={{ fontWeight: 'bold' }}>Item</TableCell>
|
|
57
|
+
<TableCell style={{ fontWeight: 'bold' }}>Price</TableCell>
|
|
58
|
+
</TableRow>
|
|
59
|
+
</TableHead>
|
|
60
|
+
<TableBody>
|
|
61
|
+
<TableRow>
|
|
62
|
+
<TableCell>Apple</TableCell>
|
|
63
|
+
<TableCell>$1.00</TableCell>
|
|
64
|
+
</TableRow>
|
|
65
|
+
<TableRow>
|
|
66
|
+
<TableCell>Banana</TableCell>
|
|
67
|
+
<TableCell>$0.50</TableCell>
|
|
68
|
+
</TableRow>
|
|
69
|
+
</TableBody>
|
|
70
|
+
<TableFooter>
|
|
71
|
+
<TableRow>
|
|
72
|
+
<TableCell style={{ fontWeight: 'bold' }}>Total</TableCell>
|
|
73
|
+
<TableCell style={{ fontWeight: 'bold' }}>$1.50</TableCell>
|
|
74
|
+
</TableRow>
|
|
75
|
+
</TableFooter>
|
|
76
|
+
</Table>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Table with Custom Styling
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
<Table sx={{ backgroundColor: 'var(--color-neutral-50)', borderRadius: '8px' }}>
|
|
83
|
+
<TableHead>
|
|
84
|
+
<TableRow>
|
|
85
|
+
<TableCell sx={{ color: 'var(--color-primary-600)' }}>Product</TableCell>
|
|
86
|
+
<TableCell sx={{ color: 'var(--color-primary-600)' }}>Status</TableCell>
|
|
87
|
+
</TableRow>
|
|
88
|
+
</TableHead>
|
|
89
|
+
<TableBody>
|
|
90
|
+
<TableRow>
|
|
91
|
+
<TableCell>Product A</TableCell>
|
|
92
|
+
<TableCell>In Stock</TableCell>
|
|
93
|
+
</TableRow>
|
|
94
|
+
</TableBody>
|
|
95
|
+
</Table>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Related Components
|
|
99
|
+
|
|
100
|
+
- [TableHead](../tablehead/README.md)
|
|
101
|
+
- [TableBody](../tablebody/README.md)
|
|
102
|
+
- [TableFooter](../tablefooter/README.md)
|
|
103
|
+
- [TableRow](../tablerow/README.md)
|
|
104
|
+
- [TableCell](../tablecell/README.md)
|
|
105
|
+
- [Primitive](../primitive/README.md)
|
|
106
|
+
|
|
107
|
+
## Dependencies
|
|
108
|
+
|
|
109
|
+
- @emotion/react
|
|
110
|
+
|
|
111
|
+
## See Also
|
|
112
|
+
|
|
113
|
+
- [Component Source](./index.ts)
|
|
114
|
+
- [Storybook Stories](./table.stories.tsx)
|
|
115
|
+
- [Main README](../../README.md)
|