@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
@@ -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 clsx from "clsx";
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, contrastText } = getColorShadesByVariant(color);
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
- "--checkbox-contrast-text": contrastText,
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("span", { className: classes.checkbox__main, children: [
42
- /* @__PURE__ */ jsx("input", { ...props, type: "checkbox", className: clsx(classes.checkbox__input, className), ref: mergedRefs }),
43
- /* @__PURE__ */ jsxs("span", { className: classes.checkbox__checkmark, children: [
44
- /* @__PURE__ */ jsx(SvgLoader, { className: classes.checkbox__icon_checked, width: 18, height: 18, source: CheckIcon }),
45
- /* @__PURE__ */ jsx(SvgLoader, { className: classes.checkbox__icon_indeterminate, width: 18, height: 18, source: MinusIcon })
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)
@@ -1,5 +1,6 @@
1
1
  import { jsxs, jsx } from "@emotion/react/jsx-runtime";
2
- /* empty css */
2
+ import "react-date-range/dist/styles.css";
3
+ import "react-date-range/dist/theme/default.css";
3
4
  import { forwardRef, useRef } from "react";
4
5
  import dayjs from "dayjs";
5
6
  import { Calendar } from "react-date-range";