@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.
Files changed (76) hide show
  1. package/README.md +681 -0
  2. package/dist/lib/accordion/README.md +81 -0
  3. package/dist/lib/avatar/README.md +74 -0
  4. package/dist/lib/badge/README.md +58 -0
  5. package/dist/lib/box/README.md +69 -0
  6. package/dist/lib/breadcrumbs/README.md +70 -0
  7. package/dist/lib/button/README.md +115 -0
  8. package/dist/lib/cascader/README.md +128 -0
  9. package/dist/lib/checkbox/README.md +74 -0
  10. package/dist/lib/checkbox/checkbox.js +107 -12
  11. package/dist/lib/checkbox/types.d.ts +2 -2
  12. package/dist/lib/chip/README.md +72 -0
  13. package/dist/lib/collapse/README.md +78 -0
  14. package/dist/lib/container/README.md +59 -0
  15. package/dist/lib/count-up/README.md +52 -0
  16. package/dist/lib/countdown-timer/README.md +77 -0
  17. package/dist/lib/date-picker/README.md +94 -0
  18. package/dist/lib/date-picker/date-picker.js +2 -1
  19. package/dist/lib/dialog/README.md +109 -0
  20. package/dist/lib/drawer/README.md +97 -0
  21. package/dist/lib/filter-panel/README.md +146 -0
  22. package/dist/lib/flag/README.md +58 -0
  23. package/dist/lib/flexbox/README.md +59 -0
  24. package/dist/lib/floating/README.md +109 -0
  25. package/dist/lib/form-helper-text/README.md +54 -0
  26. package/dist/lib/form-label/README.md +50 -0
  27. package/dist/lib/grid/README.md +72 -0
  28. package/dist/lib/highlight-words/README.md +64 -0
  29. package/dist/lib/highlight-words/highlight-words.js +1 -2
  30. package/dist/lib/icon/README.md +69 -0
  31. package/dist/lib/icon-button/README.md +92 -0
  32. package/dist/lib/image/README.md +80 -0
  33. package/dist/lib/input/README.md +118 -0
  34. package/dist/lib/input-mask/README.md +88 -0
  35. package/dist/lib/input-number/README.md +92 -0
  36. package/dist/lib/input-range/README.md +85 -0
  37. package/dist/lib/lightbox/README.md +107 -0
  38. package/dist/lib/linear-progress/README.md +54 -0
  39. package/dist/lib/link/README.md +67 -0
  40. package/dist/lib/loading/README.md +65 -0
  41. package/dist/lib/marquee/README.md +83 -0
  42. package/dist/lib/menu/README.md +92 -0
  43. package/dist/lib/multiple-select/README.md +108 -0
  44. package/dist/lib/nav-link/README.md +61 -0
  45. package/dist/lib/notifications/README.md +103 -0
  46. package/dist/lib/otp-input/README.md +71 -0
  47. package/dist/lib/pagination/README.md +84 -0
  48. package/dist/lib/phone-input/README.md +80 -0
  49. package/dist/lib/popover/README.md +93 -0
  50. package/dist/lib/price-label/README.md +78 -0
  51. package/dist/lib/primitive/README.md +80 -0
  52. package/dist/lib/progress/README.md +50 -0
  53. package/dist/lib/radio-button/README.md +89 -0
  54. package/dist/lib/radio-button/radio-button.js +98 -7
  55. package/dist/lib/ripple-effect/README.md +66 -0
  56. package/dist/lib/select/README.md +124 -0
  57. package/dist/lib/star-rating/README.md +67 -0
  58. package/dist/lib/stepper/README.md +99 -0
  59. package/dist/lib/suspense-query/README.md +78 -0
  60. package/dist/lib/swiper/README.md +99 -0
  61. package/dist/lib/switch/README.md +73 -0
  62. package/dist/lib/switch/switch.d.ts +1 -1
  63. package/dist/lib/switch/switch.js +110 -11
  64. package/dist/lib/table/README.md +115 -0
  65. package/dist/lib/tabs/README.md +103 -0
  66. package/dist/lib/text/README.md +59 -0
  67. package/dist/lib/textarea/README.md +76 -0
  68. package/dist/lib/time-picker/README.md +100 -0
  69. package/dist/lib/tooltip/README.md +106 -0
  70. package/dist/lib/unordered-list/README.md +85 -0
  71. package/dist/lib/video/README.md +88 -0
  72. package/package.json +5 -5
  73. package/dist/lib/checkbox/checkbox.module.scss.js +0 -23
  74. package/dist/lib/radio-button/radio-button.module.scss.js +0 -17
  75. package/dist/lib/switch/switch.module.scss.js +0 -14
  76. 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 { useRef } from "react";
3
- import { useMergeRefs } from "@floating-ui/react";
4
- import classes from "./switch.module.scss.js";
5
- import { clsx } from "clsx";
6
- const Switch = ({ children, className, ref, ...props }) => {
7
- const inputRef = useRef(null);
8
- const mergedRefs = useMergeRefs([ref, inputRef]);
9
- return /* @__PURE__ */ jsxs("label", { className: classes.switch__container, children: [
10
- /* @__PURE__ */ jsx("span", { className: classes.switch__main, children: /* @__PURE__ */ jsx("input", { ...props, type: "checkbox", className: clsx(classes.switch__input, className), ref: mergedRefs }) }),
11
- children
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)