@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,108 @@
1
+ # MultipleSelect
2
+
3
+ A generic multi-select dropdown component that renders selected values as tags inside an input trigger. It supports search filtering, virtualized option lists, controlled/uncontrolled state, and responsive sizing.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { MultipleSelect } 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` | `Array<Partial<O> \| MultipleSelectOptionValue>` | - | Controlled selected values. Can be full option objects or primitive values (string \| number). |
17
+ | `defaultValue` | `Array<Partial<O> \| MultipleSelectOptionValue>` | - | Initial selected values for uncontrolled usage. |
18
+ | `onChange` | `(value: O[]) => void` | - | Callback fired when the selection changes, receiving the full array of selected option objects. |
19
+ | `optionValue` | `keyof O \| ((option: O) => MultipleSelectOptionValue)` | `'value'` | Key or function to derive the unique value from an option object. |
20
+ | `optionLabel` | `keyof O \| ((option: O) => React.ReactNode)` | `'label'` | Key or function to derive the display label from an option object. |
21
+ | `tagLabel` | `keyof O \| ((option: O) => React.ReactNode)` | - | Key or function to derive the label shown inside the selected tag chip. Falls back to `optionLabel` if not set. |
22
+ | `optionSearchLabel` | `keyof O \| Array<keyof O> \| ((option: O) => string \| string[])` | - | Field(s) or function used when filtering options by the search input. Defaults to `optionLabel`. |
23
+ | `searchable` | `boolean` | `true` | Whether to show a search input inside the dropdown for filtering options. |
24
+ | `displayCount` | `number` | `1` | Maximum number of tag chips shown in the trigger before collapsing into a `+N` badge. Use `Infinity` to show all. |
25
+ | `placeholder` | `string` | - | Placeholder text shown in the trigger when no values are selected. |
26
+ | `open` | `boolean` | - | Controlled open state of the dropdown. |
27
+ | `onOpenChange` | `UseFloatingOptions['onOpenChange']` | - | Callback fired when the open state changes. |
28
+ | `placement` | `Placement` | `'bottom-start'` | Floating UI placement for the dropdown panel. |
29
+ | `error` | `boolean` | - | When true, renders the trigger in an error/invalid visual state. |
30
+ | `helperText` | `string` | - | Helper or validation message rendered below the trigger via `FormHelperText`. |
31
+ | `containerProps` | `PrimitiveProps<JSX.IntrinsicElements['div']>` | - | Props forwarded to the outer container `Primitive` wrapper. |
32
+ | `inputProps` | `InputProps` | - | Props forwarded to the search `Input` rendered inside the dropdown panel. |
33
+ | `listProps` | `PropsWithoutRef<HTMLProps<HTMLDivElement>>` | - | Props forwarded to the `VirtualizedList` container element. |
34
+ | `size` | `'xs' \| 'sm' \| 'md' \| 'lg'` | `'md'` | Size variant controlling height, padding, and font size of the trigger and option rows. |
35
+
36
+ *Required props
37
+
38
+ ## Examples
39
+
40
+ ### Basic usage
41
+
42
+ ```tsx
43
+ type Option = { label: string; value: string };
44
+
45
+ const fruits: Option[] = [
46
+ { label: 'Apple', value: 'apple' },
47
+ { label: 'Banana', value: 'banana' },
48
+ { label: 'Cherry', value: 'cherry' },
49
+ ];
50
+
51
+ <MultipleSelect<Option>
52
+ options={fruits}
53
+ placeholder="Select fruits..."
54
+ onChange={(selected) => console.log(selected)}
55
+ />
56
+ ```
57
+
58
+ ### Controlled with error state
59
+
60
+ ```tsx
61
+ const [selected, setSelected] = useState<string[]>([]);
62
+
63
+ <MultipleSelect
64
+ options={fruits}
65
+ value={selected}
66
+ onChange={(opts) => setSelected(opts.map((o) => o.value))}
67
+ error={selected.length === 0}
68
+ helperText={selected.length === 0 ? 'Please select at least one option' : undefined}
69
+ placeholder="Select fruits..."
70
+ />
71
+ ```
72
+
73
+ ### Display multiple tags with overflow badge
74
+
75
+ ```tsx
76
+ // Shows up to 2 tag chips, remaining selections collapse into '+N' badge
77
+ <MultipleSelect<Option>
78
+ options={fruits}
79
+ value={['apple', 'banana', 'cherry']}
80
+ displayCount={2}
81
+ placeholder="Select fruits..."
82
+ onChange={handleChange}
83
+ />
84
+ ```
85
+
86
+ ## Related Components
87
+
88
+ - [Input](../input/README.md)
89
+ - [InputMask](../inputmask/README.md)
90
+ - [Checkbox](../checkbox/README.md)
91
+ - [Floating](../floating/README.md)
92
+ - [FloatingTrigger](../floatingtrigger/README.md)
93
+ - [FloatingContent](../floatingcontent/README.md)
94
+ - [FormHelperText](../formhelpertext/README.md)
95
+ - [VirtualizedList](../virtualizedlist/README.md)
96
+ - [Primitive](../primitive/README.md)
97
+
98
+ ## Dependencies
99
+
100
+ - @floating-ui/react
101
+ - @tanstack/react-virtual
102
+ - lodash-es
103
+
104
+ ## See Also
105
+
106
+ - [Component Source](./index.ts)
107
+ - [Storybook Stories](./multipleselect.stories.tsx)
108
+ - [Main README](../../README.md)
@@ -0,0 +1,61 @@
1
+ # NavLink
2
+
3
+ A wrapper around React Router's NavLink component that adds safe handling for undefined `to` prop. When `to` is nil, the link prevents navigation and treats itself as inactive, making it safe to render navigation items that may not yet have a destination.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { NavLink } from '@janbox/storefront-ui';
9
+ ```
10
+
11
+ ## Props
12
+
13
+ | Prop | Type | Default | Description |
14
+ |------|------|---------|-------------|
15
+ | `to` | `ReactRouterNavLinkProps['to'] \| undefined` | - | Đường dẫn hoặc location object. Khi `undefined` hoặc `null`, link sẽ ngăn navigation và được coi là inactive (`isActive = false`). |
16
+ | `children` | `React.ReactNode \| ((props: NavLinkRenderProps) => React.ReactNode)` | - | Nội dung bên trong link. Có thể là render function nhận NavLink render props gồm `isActive`, `isPending`, `isTransitioning`. Khi `to` là nil, `isActive` luôn là `false`. |
17
+ | `className` | `string \| ((props: NavLinkRenderProps) => string \| undefined)` | - | Class name hoặc function nhận render props để tính class động. `isActive` trong render props tuân theo nil-to override. |
18
+ | `style` | `React.CSSProperties \| ((props: NavLinkRenderProps) => React.CSSProperties \| undefined)` | - | Inline styles hoặc function nhận render props để tính styles động. `isActive` trong render props tuân theo nil-to override. |
19
+ | `end` | `boolean` | - | Khi `true`, active class/style chỉ được áp dụng khi URL hiện tại khớp chính xác với `to` (không partial matching). |
20
+ | `ref` | `Ref<HTMLAnchorElement>` | - | Forwarded ref đến anchor element bên dưới. |
21
+ | `onClick` | `(event: React.MouseEvent<HTMLAnchorElement>) => void` | - | Click handler. Navigation tự động bị ngăn khi `to` là nil hoặc khi `aria-disabled` là `true`. |
22
+
23
+ ## Examples
24
+
25
+ ### Basic navigation link
26
+
27
+ ```tsx
28
+ <NavLink to="/home">Home</NavLink>
29
+ ```
30
+
31
+ ### Active state with exact match and dynamic class
32
+
33
+ ```tsx
34
+ <NavLink
35
+ to="/dashboard"
36
+ end
37
+ className={({ isActive }) => isActive ? 'nav-item nav-item--active' : 'nav-item'}
38
+ >
39
+ Dashboard
40
+ </NavLink>
41
+ ```
42
+
43
+ ### Placeholder link with no destination (disabled navigation)
44
+
45
+ ```tsx
46
+ <NavLink to={undefined} aria-disabled="true">
47
+ {({ isActive }) => (
48
+ <span style={{ opacity: isActive ? 1 : 0.5 }}>Coming Soon</span>
49
+ )}
50
+ </NavLink>
51
+ ```
52
+
53
+ ## Dependencies
54
+
55
+ - react-router
56
+
57
+ ## See Also
58
+
59
+ - [Component Source](./index.ts)
60
+ - [Storybook Stories](./navlink.stories.tsx)
61
+ - [Main README](../../README.md)
@@ -0,0 +1,103 @@
1
+ # Notifications
2
+
3
+ A toast notification system that renders dismissible notification cards in a fixed portal overlay at the top-right of the screen. It uses an external store (`notificationStore`) and the `useNotifications` hook to push notifications imperatively from anywhere in the app.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { Notifications } from '@janbox/storefront-ui';
9
+ ```
10
+
11
+ ## Props
12
+
13
+ | Prop | Type | Default | Description |
14
+ |------|------|---------|-------------|
15
+
16
+ ## Examples
17
+
18
+ ### Basic usage — mount Notifications once and push via hook
19
+
20
+ ```tsx
21
+ import { Notifications, useNotifications } from '@janbox/storefront-ui';
22
+
23
+ const App = () => (
24
+ <>
25
+ {/* Mount once at the root level */}
26
+ <Notifications />
27
+ <MyPage />
28
+ </>
29
+ );
30
+
31
+ const MyPage = () => {
32
+ const { pushNotifications } = useNotifications();
33
+
34
+ return (
35
+ <button
36
+ onClick={() =>
37
+ pushNotifications({
38
+ title: 'Success',
39
+ message: 'Your changes have been saved.',
40
+ color: 'green',
41
+ })
42
+ }
43
+ >
44
+ Save
45
+ </button>
46
+ );
47
+ };
48
+ ```
49
+
50
+ ### Push multiple notifications with custom duration
51
+
52
+ ```tsx
53
+ const { pushNotifications } = useNotifications();
54
+
55
+ pushNotifications([
56
+ {
57
+ title: 'Item added',
58
+ message: 'Product has been added to your cart.',
59
+ color: 'primary',
60
+ duration: 4000,
61
+ },
62
+ {
63
+ title: 'Stock low',
64
+ message: 'Only 2 items left in stock.',
65
+ color: 'orange',
66
+ duration: 6000,
67
+ },
68
+ ]);
69
+ ```
70
+
71
+ ### Error notification on API failure
72
+
73
+ ```tsx
74
+ const { pushNotifications } = useNotifications();
75
+
76
+ try {
77
+ await submitOrder();
78
+ } catch (err) {
79
+ pushNotifications({
80
+ title: 'Order failed',
81
+ message: 'Something went wrong. Please try again.',
82
+ color: 'red',
83
+ duration: 5000,
84
+ });
85
+ }
86
+ ```
87
+
88
+ ## Related Components
89
+
90
+ - [IconButton](../icon-button/README.md)
91
+ - [Primitive](../primitive/README.md)
92
+
93
+ ## Dependencies
94
+
95
+ - @floating-ui/react
96
+ - lodash-es
97
+ - motion/react
98
+
99
+ ## See Also
100
+
101
+ - [Component Source](./index.ts)
102
+ - [Storybook Stories](./notifications.stories.tsx)
103
+ - [Main README](../../README.md)
@@ -0,0 +1,71 @@
1
+ # OtpInput
2
+
3
+ A one-time password input component that renders multiple individual input fields for entering verification codes. Supports auto-focus, paste handling, keyboard navigation, and automatic progression between fields.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { OtpInput } from '@janbox/storefront-ui';
9
+ ```
10
+
11
+ ## Props
12
+
13
+ | Prop | Type | Default | Description |
14
+ |------|------|---------|-------------|
15
+ | `length` | `number` | 6 | Number of OTP input fields to render |
16
+ | `onChange` | `(otp: string) => void` | - | Callback fired when any digit changes, receives the complete OTP string |
17
+ | `onFullfilled` | `(otp: string) => void` | - | Callback fired when all digits are filled, receives the complete OTP string |
18
+ | `disabled` | `boolean` | - | Disables all input fields |
19
+ | `autoFocus` | `boolean` | - | Auto-focuses the first input field on mount |
20
+ | `isLoading` | `boolean` | - | Makes all input fields read-only during loading state |
21
+ | `defaultValue` | `string` | - | Default OTP value (uncontrolled) |
22
+ | `value` | `string` | - | Controlled OTP value |
23
+ | `error` | `boolean` | - | Shows error state styling on all input fields |
24
+ | `size` | `InputSizeVariant` | - | Size variant for all input fields |
25
+ | `sm` | `InputResponsiveProps` | - | Responsive props for small breakpoint (768px+) |
26
+ | `md` | `InputResponsiveProps` | - | Responsive props for medium breakpoint (1280px+) |
27
+ | `lg` | `InputResponsiveProps` | - | Responsive props for large breakpoint (1680px+) |
28
+
29
+ ## Examples
30
+
31
+ ### Basic 6-digit OTP
32
+
33
+ ```tsx
34
+ <OtpInput length={6} onChange={(otp) => console.log(otp)} />
35
+ ```
36
+
37
+ ### 4-digit PIN with auto-submit
38
+
39
+ ```tsx
40
+ <OtpInput
41
+ length={4}
42
+ autoFocus
43
+ onFullfilled={(otp) => handleVerify(otp)}
44
+ />
45
+ ```
46
+
47
+ ### Controlled with error state
48
+
49
+ ```tsx
50
+ <OtpInput
51
+ length={6}
52
+ value={otpValue}
53
+ onChange={setOtpValue}
54
+ error={hasError}
55
+ disabled={isSubmitting}
56
+ />
57
+ ```
58
+
59
+ ## Related Components
60
+
61
+ - [Input](../input/README.md)
62
+
63
+ ## Dependencies
64
+
65
+ - lodash-es
66
+
67
+ ## See Also
68
+
69
+ - [Component Source](./index.ts)
70
+ - [Storybook Stories](./otpinput.stories.tsx)
71
+ - [Main README](../../README.md)
@@ -0,0 +1,84 @@
1
+ # Pagination
2
+
3
+ A pagination component for navigating through multiple pages of content. Displays page numbers with ellipsis for large page counts, supports customizable navigation buttons, and provides responsive sizing.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { Pagination } from '@janbox/storefront-ui';
9
+ ```
10
+
11
+ ## Props
12
+
13
+ | Prop | Type | Default | Description |
14
+ |------|------|---------|-------------|
15
+ | `page` | `number` | 1 | Current active page number |
16
+ | `totalPages` | `number` | 1 | Total number of pages available |
17
+ | `onChange` | `(page: number) => void` | - | Callback function triggered when page changes |
18
+ | `disabled` | `boolean` | - | Disables all pagination interactions |
19
+ | `size` | `'xs' \| 'sm' \| 'md' \| 'lg'` | `sm` | Size variant for pagination buttons and text |
20
+ | `showPrevPageButton` | `boolean` | `true` | Show/hide the previous page navigation button |
21
+ | `showNextPageButton` | `boolean` | `true` | Show/hide the next page navigation button |
22
+ | `showFirstPageButton` | `boolean` | `false` | Show/hide the first page navigation button |
23
+ | `showLastPageButton` | `boolean` | `false` | Show/hide the last page navigation button |
24
+ | `siblingCount` | `number` | `2` | Number of pages to show on each side of the current page |
25
+ | `states` | `Partial<Record<'normal' \| 'active', { color?: IconButtonProps['color']; variant?: IconButtonProps['variant']; }>>` | `{ normal: { color: 'neutral', variant: 'outlined' }, active: { color: 'primary', variant: 'outlined' } }` | Customize color and variant for normal and active pagination button states |
26
+ | `sm` | `{ size?: SizeVariant }` | - | Responsive props for small breakpoint (768px) |
27
+ | `md` | `{ size?: SizeVariant }` | - | Responsive props for medium breakpoint (1280px) |
28
+ | `lg` | `{ size?: SizeVariant }` | - | Responsive props for large breakpoint (1680px) |
29
+
30
+ ## Examples
31
+
32
+ ### Basic pagination with 10 pages
33
+
34
+ ```tsx
35
+ <Pagination
36
+ page={5}
37
+ totalPages={10}
38
+ onChange={(page) => console.log('Navigate to page:', page)}
39
+ />
40
+ ```
41
+
42
+ ### Pagination with first/last buttons and custom sibling count
43
+
44
+ ```tsx
45
+ <Pagination
46
+ page={7}
47
+ totalPages={50}
48
+ siblingCount={2}
49
+ showFirstPageButton
50
+ showLastPageButton
51
+ onChange={(page) => setCurrentPage(page)}
52
+ />
53
+ ```
54
+
55
+ ### Disabled pagination with custom button states
56
+
57
+ ```tsx
58
+ <Pagination
59
+ page={3}
60
+ totalPages={10}
61
+ disabled
62
+ states={{
63
+ normal: { color: 'neutral', variant: 'outlined' },
64
+ active: { color: 'blue', variant: 'contained' }
65
+ }}
66
+ onChange={(page) => handlePageChange(page)}
67
+ />
68
+ ```
69
+
70
+ ## Related Components
71
+
72
+ - [IconButton](../iconbutton/README.md)
73
+ - [Icon](../icon/README.md)
74
+ - [Text](../text/README.md)
75
+
76
+ ## Dependencies
77
+
78
+ - @emotion/react
79
+
80
+ ## See Also
81
+
82
+ - [Component Source](./index.ts)
83
+ - [Storybook Stories](./pagination.stories.tsx)
84
+ - [Main README](../../README.md)
@@ -0,0 +1,80 @@
1
+ # PhoneInput
2
+
3
+ A phone number input component that combines a country dial code selector with a national number text field. It supports controlled and uncontrolled usage, auto-parses country codes from dial codes, and displays formatted phone numbers when not focused.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { PhoneInput } from '@janbox/storefront-ui';
9
+ ```
10
+
11
+ ## Props
12
+
13
+ | Prop | Type | Default | Description |
14
+ |------|------|---------|-------------|
15
+ | `value` | `PhoneInputValue` | - | Controlled value object containing countryCode, dialCode, and nationalNumber. |
16
+ | `defaultValue` | `PhoneInputValue` | - | Uncontrolled default value. Defaults to the first country in the countries list if not provided. |
17
+ | `onChange` | `(value: PhoneInputValue) => void` | - | Callback fired when either the country dial code or the national number changes. Receives a PhoneInputValue object. |
18
+ | `error` | `boolean` | `false` | Puts the input into an error state, rendering the border in red. |
19
+ | `helperText` | `string` | - | Helper or error message displayed below the input via FormHelperText. |
20
+ | `disabled` | `boolean` | `false` | Disables both the country select and the number input, applying a disabled surface color. |
21
+ | `containerProps` | `PrimitiveProps<JSX.IntrinsicElements['div']>` | - | Props forwarded to the outer container Primitive div, useful for layout overrides via sx. |
22
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size variant for the input. xs is excluded. Supports responsive values via sm/md/lg breakpoint keys. |
23
+ | `css` | `Interpolation` | - | Emotion CSS interpolation applied directly to the InputMask wrapper element. |
24
+
25
+ ## Examples
26
+
27
+ ### Default (uncontrolled)
28
+
29
+ ```tsx
30
+ <div style={{ width: 300 }}>
31
+ <PhoneInput onChange={(value) => console.log(value)} />
32
+ </div>
33
+ ```
34
+
35
+ ### Controlled with initial value
36
+
37
+ ```tsx
38
+ const [phone, setPhone] = useState<PhoneInputValue>({
39
+ countryCode: 'JP',
40
+ dialCode: '+81',
41
+ nationalNumber: '9012345678',
42
+ });
43
+
44
+ <PhoneInput
45
+ value={phone}
46
+ onChange={setPhone}
47
+ />
48
+ ```
49
+
50
+ ### Error state with helper text
51
+
52
+ ```tsx
53
+ <PhoneInput
54
+ error
55
+ helperText="Please enter a valid phone number."
56
+ defaultValue={{ countryCode: 'VN' }}
57
+ onChange={(value) => console.log(value)}
58
+ />
59
+ ```
60
+
61
+ ## Related Components
62
+
63
+ - [Input](../input/README.md)
64
+ - [InputMask](../inputmask/README.md)
65
+ - [Select](../select/README.md)
66
+ - [Flag](../flag/README.md)
67
+ - [FormHelperText](../formhelpertext/README.md)
68
+ - [Primitive](../primitive/README.md)
69
+
70
+ ## Dependencies
71
+
72
+ - phone
73
+ - @floating-ui/react
74
+ - lodash-es
75
+
76
+ ## See Also
77
+
78
+ - [Component Source](./index.ts)
79
+ - [Storybook Stories](./phoneinput.stories.tsx)
80
+ - [Main README](../../README.md)
@@ -0,0 +1,93 @@
1
+ # Popover
2
+
3
+ A floating overlay component for displaying contextual content anchored to a trigger element. Built on top of @floating-ui/react, it supports click-to-open, dismiss-on-outside-click, and controlled/uncontrolled open state — composed via Popover, PopoverTrigger, PopoverContent, and PopoverClose sub-components.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { Popover } from '@janbox/storefront-ui';
9
+ ```
10
+
11
+ ## Props
12
+
13
+ | Prop | Type | Default | Description |
14
+ |------|------|---------|-------------|
15
+ | `open` | `boolean` | - | Controlled open state of the popover. |
16
+ | `onOpenChange` | `UseFloatingOptions['onOpenChange']` | - | Callback fired when the open state changes. Receives (open: boolean, event: Event | undefined, reason: OpenChangeReason | undefined). |
17
+ | `placement` | `Placement` | `bottom` | Preferred placement of the floating content relative to the trigger. Accepts all @floating-ui/react Placement values (e.g. `'top'`, `'bottom'`, `'left'`, `'right'` and their `'-start'`/`'-end'` variants). |
18
+ | `clickProps` | `UseClickProps` | `{ enabled: true }` | Options forwarded to @floating-ui/react useClick interaction hook. Enabled by default in Popover. |
19
+ | `dismissProps` | `UseDismissProps` | `{ enabled: true }` | Options forwarded to @floating-ui/react useDismiss interaction hook (close on outside click / Escape key). Enabled by default in Popover. |
20
+ | `hoverProps` | `UseHoverProps` | - | Options forwarded to @floating-ui/react useHover interaction hook. |
21
+ | `focusProps` | `UseFocusProps` | - | Options forwarded to @floating-ui/react useFocus interaction hook. |
22
+ | `offsetOptions` | `OffsetOptions` | - | Distance (in px) between the trigger and the floating content. |
23
+ | `sizeOptions` | `SizeOptions` | - | Options for the @floating-ui/react size middleware — constrain the floating element dimensions. |
24
+ | `autoUpdateOptions` | `AutoUpdateOptions` | - | Options passed to @floating-ui/react autoUpdate to control when the position recalculates. |
25
+ | `elements` | `UseFloatingOptions['elements']` | - | Override the reference or floating elements used by floating-ui. |
26
+ | `ref` | `React.Ref<FloatingRef>` | - | Ref forwarded to the underlying UseFloatingReturn object, exposing the full floating-ui context. |
27
+ | `children` | `React.ReactNode` | - | Should contain PopoverTrigger and PopoverContent (and optionally PopoverClose inside content). |
28
+
29
+ ## Examples
30
+
31
+ ### Default (click to open, dismiss on outside click)
32
+
33
+ ```tsx
34
+ <Popover>
35
+ <PopoverTrigger>
36
+ <button>Open Popover</button>
37
+ </PopoverTrigger>
38
+ <PopoverContent>
39
+ <p>Popover content here</p>
40
+ <PopoverClose>
41
+ <button>Close</button>
42
+ </PopoverClose>
43
+ </PopoverContent>
44
+ </Popover>
45
+ ```
46
+
47
+ ### Top placement
48
+
49
+ ```tsx
50
+ <Popover placement="top">
51
+ <PopoverTrigger>
52
+ <button>Open Top</button>
53
+ </PopoverTrigger>
54
+ <PopoverContent>
55
+ <p>Popover anchored above the trigger</p>
56
+ </PopoverContent>
57
+ </Popover>
58
+ ```
59
+
60
+ ### Controlled open state
61
+
62
+ ```tsx
63
+ const [open, setOpen] = useState(false);
64
+
65
+ <Popover open={open} onOpenChange={(next) => setOpen(next)}>
66
+ <PopoverTrigger>
67
+ <button>Toggle</button>
68
+ </PopoverTrigger>
69
+ <PopoverContent>
70
+ <p>Controlled popover</p>
71
+ <PopoverClose>
72
+ <button onClick={() => setOpen(false)}>Close</button>
73
+ </PopoverClose>
74
+ </PopoverContent>
75
+ </Popover>
76
+ ```
77
+
78
+ ## Related Components
79
+
80
+ - [Floating](../floating/README.md)
81
+ - [FloatingTrigger](../floating-trigger/README.md)
82
+ - [FloatingContent](../floating-content/README.md)
83
+ - [FloatingClose](../floating-close/README.md)
84
+
85
+ ## Dependencies
86
+
87
+ - @floating-ui/react
88
+
89
+ ## See Also
90
+
91
+ - [Component Source](./index.ts)
92
+ - [Storybook Stories](./popover.stories.tsx)
93
+ - [Main README](../../README.md)
@@ -0,0 +1,78 @@
1
+ # PriceLabel
2
+
3
+ A component for displaying a primary price alongside an optional exchange/secondary price. Supports currency formatting, pending states, skeleton placeholders, responsive typography sizing, and both vertical and horizontal layout orientations.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { PriceLabel } from '@janbox/storefront-ui';
9
+ ```
10
+
11
+ ## Props
12
+
13
+ | Prop | Type | Default | Description |
14
+ |------|------|---------|-------------|
15
+ | `price*` | `PriceLabelEntry` | - | Primary price data. Contains currency, value, isPending, placeholder, prefix, and suffix fields. |
16
+ | `price.currency` | `string` | - | ISO currency code (e.g. 'USD', 'VND'). When combined with value, renders a formatted price string. |
17
+ | `price.value` | `number` | - | Numeric price value to format and display. |
18
+ | `price.isPending` | `boolean` | - | When true and no value is set, renders an 'updating' label. |
19
+ | `price.placeholder` | `boolean` | - | When true and no value or pending state, renders a skeleton placeholder. Defaults to true on the primary price. |
20
+ | `price.prefix` | `string` | - | String prepended to the formatted price. |
21
+ | `price.suffix` | `string` | - | String appended to the formatted price. |
22
+ | `exchangePrice` | `PriceLabelEntry` | - | Optional secondary/exchange price displayed below (vertical) or beside (horizontal) the primary price. |
23
+ | `priceProps` | `PrimitiveProps<JSX.IntrinsicElements['span']>` | - | Additional props forwarded to the primary price span element, including sx for style overrides. |
24
+ | `exchangePriceProps` | `PrimitiveProps<JSX.IntrinsicElements['span']>` | - | Additional props forwarded to the exchange price span element, including sx for style overrides. |
25
+ | `color` | `ColorVariant` | `neutral` | Color variant used for the price text. Primary price uses the 600 shade, exchange price uses the 500 shade. |
26
+ | `orientation` | `'horizontal' \| 'vertical'` | `vertical` | Layout direction of the primary and exchange prices. Vertical stacks them with a line break; horizontal places them side by side. |
27
+ | `showEqual` | `boolean` | `false` | When true, shows the exchange price even if it is equal to the primary price (same currency and value). |
28
+ | `size` | `TypographySizeVariant` | `sm` | Typography size for the primary price. Accepts responsive values via sm/md/lg breakpoint props. Exchange price automatically uses one size smaller. |
29
+
30
+ *Required props
31
+
32
+ ## Examples
33
+
34
+ ### Basic price with exchange rate
35
+
36
+ ```tsx
37
+ <PriceLabel
38
+ price={{ value: 100, currency: 'USD' }}
39
+ exchangePrice={{ value: 2000000, currency: 'VND' }}
40
+ />
41
+ ```
42
+
43
+ ### Horizontal layout with color variant and larger size
44
+
45
+ ```tsx
46
+ <PriceLabel
47
+ price={{ value: 49.99, currency: 'USD' }}
48
+ exchangePrice={{ value: 999000, currency: 'VND' }}
49
+ orientation="horizontal"
50
+ color="primary"
51
+ size="lg"
52
+ />
53
+ ```
54
+
55
+ ### Pending state while price is loading
56
+
57
+ ```tsx
58
+ <PriceLabel
59
+ price={{ isPending: true }}
60
+ exchangePrice={{ isPending: true }}
61
+ color="neutral"
62
+ />
63
+ ```
64
+
65
+ ## Related Components
66
+
67
+ - [TextSkeleton](../loading/README.md)
68
+ - [Primitive](../primitive/README.md)
69
+
70
+ ## Dependencies
71
+
72
+ - lodash-es
73
+
74
+ ## See Also
75
+
76
+ - [Component Source](./index.ts)
77
+ - [Storybook Stories](./pricelabel.stories.tsx)
78
+ - [Main README](../../README.md)