@pk-design/react-tailwind 1.12.0 → 1.13.1

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.
@@ -21,8 +21,10 @@ const panelAlignmentMap = {
21
21
  start: 'data-[closed]:-translate-x-1/2 rounded-r-2xl',
22
22
  end: 'data-[closed]:translate-x-1/2 rounded-l-2xl',
23
23
  top: 'data-[closed]:-translate-y-1/2 rounded-b-2xl',
24
- // Bottom sheets clear the home indicator on notched phones (0 elsewhere).
25
- bottom: 'data-[closed]:translate-y-full rounded-t-2xl md:rounded-t-3xl max-h-[88vh] max-h-[88dvh] pb-[env(safe-area-inset-bottom,0px)]',
24
+ // Bottom sheets clear the home indicator on notched phones (0 elsewhere). Never
25
+ // taller than the container: with the keyboard up it's shorter than 88dvh, and a
26
+ // bottom-anchored panel past its top is off screen and can't be scrolled to.
27
+ bottom: 'data-[closed]:translate-y-full rounded-t-2xl md:rounded-t-3xl max-h-[min(88dvh,100%)] pb-[env(safe-area-inset-bottom,0px)]',
26
28
  };
27
29
  const panelSizeMap = {
28
30
  xs: 'max-w-xs',
@@ -33,27 +35,58 @@ const panelSizeMap = {
33
35
  '2xl': 'max-w-2xl',
34
36
  full: 'w-full',
35
37
  };
36
- const Drawer = ({ isOpen, onClose = () => { }, closeOnOutsideClick = true, align: alignProp = 'end', size: sizeProp = 'md', backdrop = true, title, description, children, footer, footerSticky = false, panelClass = '', contentClass = '', titleClass = '', descriptionClass = '', titleSticky = false, showBackButton = false, showCloseButton = true, dragHandle, }) => {
38
+ // How much shorter than the window the visual viewport must be before it's
39
+ // taken to be the on-screen keyboard rather than the URL bar (≈ 56px).
40
+ const KEYBOARD_THRESHOLD = 150;
41
+ const Drawer = ({ isOpen, onClose = () => { }, closeOnOutsideClick = true, align: alignProp = 'end', size: sizeProp = 'md', backdrop = true, title, description, children, footer, footerSticky = false, panelClass = '', contentClass = '', titleClass = '', descriptionClass = '', titleSticky = false, showBackButton = false, showCloseButton = true, dragHandle, phoneLayout = 'default', headerAction, }) => {
37
42
  const align = useBreakpoint.useResponsiveValue(alignProp);
38
43
  const size = useBreakpoint.useResponsiveValue(sizeProp);
44
+ const breakpoint = useBreakpoint.useBreakpoint();
45
+ const fullscreen = phoneLayout === 'fullscreen' && breakpoint === 'compact';
39
46
  const showTitleSection = Boolean(title || description || showBackButton || showCloseButton);
40
- const shouldShowDragHandle = dragHandle !== null && dragHandle !== void 0 ? dragHandle : align === 'bottom';
41
- const [viewportHeight, setViewportHeight] = React.useState(null);
47
+ const shouldShowDragHandle = !fullscreen && (dragHandle !== null && dragHandle !== void 0 ? dragHandle : align === 'bottom');
48
+ useCloseOnBack(fullscreen && isOpen, onClose);
49
+ // The visible area while the on-screen keyboard is open; null otherwise.
50
+ const [keyboardViewport, setKeyboardViewport] = React.useState(null);
42
51
  React.useEffect(() => {
43
52
  if (!isOpen) {
44
- setViewportHeight(null);
53
+ setKeyboardViewport(null);
45
54
  return;
46
55
  }
56
+ // Follow the visual viewport only while the on-screen keyboard takes space
57
+ // (it's then much shorter than the window), so the drawer and its footer
58
+ // stay above the keyboard. Otherwise it's pinned to the edges: tracking every resize and
59
+ // scroll in state re-rendered the drawer on each frame as the browser's
60
+ // URL bar moved, and its sticky header and footer jumped while scrolling.
61
+ let frame = 0;
62
+ const measure = () => {
63
+ frame = 0;
64
+ const vv = window.visualViewport;
65
+ if (!vv)
66
+ return;
67
+ const keyboardOpen = window.innerHeight - vv.height > KEYBOARD_THRESHOLD;
68
+ const top = Math.round(vv.offsetTop);
69
+ const height = Math.round(vv.height);
70
+ // The browser scrolls the visible area to the focused field, so the
71
+ // drawer follows its offset as well as its height. An unchanged value
72
+ // doesn't re-render.
73
+ setKeyboardViewport((current) => !keyboardOpen
74
+ ? null
75
+ : current && current.top === top && current.height === height
76
+ ? current
77
+ : { top, height });
78
+ };
47
79
  const updateHeight = () => {
48
- if (typeof window !== 'undefined' && window.visualViewport) {
49
- setViewportHeight(window.visualViewport.height);
50
- }
80
+ if (!frame)
81
+ frame = window.requestAnimationFrame(measure);
51
82
  };
52
- updateHeight();
83
+ measure();
53
84
  const vv = typeof window !== 'undefined' ? window.visualViewport : null;
54
85
  vv === null || vv === void 0 ? void 0 : vv.addEventListener('resize', updateHeight);
55
86
  vv === null || vv === void 0 ? void 0 : vv.addEventListener('scroll', updateHeight);
56
87
  return () => {
88
+ if (frame)
89
+ window.cancelAnimationFrame(frame);
57
90
  vv === null || vv === void 0 ? void 0 : vv.removeEventListener('resize', updateHeight);
58
91
  vv === null || vv === void 0 ? void 0 : vv.removeEventListener('scroll', updateHeight);
59
92
  };
@@ -63,9 +96,96 @@ const Drawer = ({ isOpen, onClose = () => { }, closeOnOutsideClick = true, align
63
96
  onClose === null || onClose === void 0 ? void 0 : onClose();
64
97
  }
65
98
  };
66
- return (jsxRuntime.jsxs(react.Dialog, { open: isOpen, as: "div", className: "relative z-50 focus:outline-none", onClose: handleDialogClose, transition: true, children: [backdrop && (jsxRuntime.jsx(react.DialogBackdrop, { className: "fixed inset-0 bg-black/40 backdrop-blur-xs transition-opacity duration-200" })), jsxRuntime.jsx("div", { className: "fixed left-0 top-0 w-full", style: { height: viewportHeight ? `${viewportHeight}px` : '100dvh' }, children: jsxRuntime.jsx("div", { className: buildClassname.buildClassName('flex', alignmentMap[align]), children: jsxRuntime.jsxs(react.DialogPanel, { transition: true, className: buildClassname.buildClassName('w-full max-w-full bg-white dark:bg-gray-800 shadow-xl flex flex-col min-w-0', 'overflow-y-auto overflow-x-hidden overscroll-contain touch-pan-y [-webkit-overflow-scrolling:touch] duration-200 ease-out data-[closed]:opacity-0', panelAlignmentMap[align], panelSizeMap[size], panelClass), children: [shouldShowDragHandle && (jsxRuntime.jsx("div", { className: "w-12 h-1.5 rounded-full bg-gray-300 dark:bg-gray-600 mx-auto my-2.5 shrink-0 cursor-grab", "data-testid": "drawer-drag-handle" })), showTitleSection && (jsxRuntime.jsxs("div", { className: buildClassname.buildClassName('space-y-1 py-3 px-4 md:px-6 flex flex-col border-b border-[var(--ui-border)] shrink-0', titleSticky && 'sticky top-0 bg-white dark:bg-gray-800 z-10'), children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 min-w-0", children: [jsxRuntime.jsxs("div", { className: "flex items-center gap-2 min-w-0 flex-1", children: [showBackButton && (jsxRuntime.jsx(Button.Button, { iconOnly: true, "aria-label": "Back", variant: "plain", theme: "secondary", size: "xs", onClick: onClose, children: jsxRuntime.jsx(ChevronLeft.default, { className: "size-4" }) })), title && (jsxRuntime.jsx(react.DialogTitle, { as: "h3", className: buildClassname.buildClassName('text-lg font-semibold text-gray-900 dark:text-white truncate tracking-tight min-w-0', titleClass), children: title }))] }), showCloseButton && (jsxRuntime.jsx(Button.Button, { iconOnly: true, "aria-label": "Close drawer", variant: "plain", theme: "secondary", size: "xs", onClick: onClose, className: "rounded-full text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 shrink-0", children: jsxRuntime.jsx(X.default, { className: "size-4" }) }))] }), description && (jsxRuntime.jsx("p", { className: buildClassname.buildClassName('text-sm text-gray-500 dark:text-gray-400 font-normal truncate', descriptionClass), children: description }))] })), jsxRuntime.jsx("div", { className: buildClassname.buildClassName('flex-1 p-4 md:p-6 space-y-4 min-w-0', contentClass), children: children }), footer && (jsxRuntime.jsx("div", { className: buildClassname.buildClassName('p-4 border-t border-[var(--ui-border-muted)] bg-white dark:bg-gray-800 shrink-0 min-w-0', footerSticky && 'sticky bottom-0 z-10'), children: footer }))] }) }) })] }));
99
+ return (jsxRuntime.jsxs(react.Dialog, { open: isOpen, as: "div", className: "relative z-50 focus:outline-none", onClose: handleDialogClose, transition: true, children: [backdrop && (jsxRuntime.jsx(react.DialogBackdrop, { className: "fixed inset-0 bg-black/40 backdrop-blur-xs transition-opacity duration-200" })), jsxRuntime.jsx("div", { className: "fixed inset-0", style: keyboardViewport
100
+ ? {
101
+ top: `${keyboardViewport.top}px`,
102
+ height: `${keyboardViewport.height}px`,
103
+ bottom: 'auto',
104
+ }
105
+ : undefined, "data-testid": "drawer-viewport", children: jsxRuntime.jsx("div", { className: buildClassname.buildClassName('flex', fullscreen ? 'h-full' : alignmentMap[align]), children: jsxRuntime.jsxs(react.DialogPanel, { transition: true, className: buildClassname.buildClassName('w-full max-w-full bg-white dark:bg-gray-800 shadow-xl flex flex-col min-w-0', 'overflow-y-auto overflow-x-hidden overscroll-contain touch-pan-y [-webkit-overflow-scrolling:touch] duration-200 ease-out data-[closed]:opacity-0', ...(fullscreen
106
+ ? ['h-full data-[closed]:translate-y-4']
107
+ : [panelAlignmentMap[align], panelSizeMap[size], panelClass])), "data-layout": fullscreen ? 'fullscreen' : undefined, children: [shouldShowDragHandle && (jsxRuntime.jsx("div", { className: "w-12 h-1.5 rounded-full bg-gray-300 dark:bg-gray-600 mx-auto my-2.5 shrink-0 cursor-grab", "data-testid": "drawer-drag-handle" })), fullscreen && (jsxRuntime.jsxs("div", { className: "sticky top-0 z-10 flex h-14 shrink-0 items-center gap-1 border-b border-[var(--ui-border)] bg-white pl-1 pr-3 pt-[env(safe-area-inset-top,0px)] box-content dark:bg-gray-800", children: [jsxRuntime.jsx(Button.Button, { iconOnly: true, "aria-label": "Close", variant: "plain", theme: "secondary", onClick: onClose, className: "shrink-0", children: jsxRuntime.jsx(X.default, { className: "size-5" }) }), title && (jsxRuntime.jsx(react.DialogTitle, { as: "h3", className: "min-w-0 flex-1 truncate text-base font-semibold text-gray-900 dark:text-white", children: title })), headerAction && jsxRuntime.jsx("div", { className: "ml-auto shrink-0", children: headerAction })] })), !fullscreen && showTitleSection && (jsxRuntime.jsxs("div", { className: buildClassname.buildClassName('space-y-1 py-3 px-4 md:px-6 flex flex-col border-b border-[var(--ui-border)] shrink-0', titleSticky && 'sticky top-0 bg-white dark:bg-gray-800 z-10'), children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 min-w-0", children: [jsxRuntime.jsxs("div", { className: "flex items-center gap-2 min-w-0 flex-1", children: [showBackButton && (jsxRuntime.jsx(Button.Button, { iconOnly: true, "aria-label": "Back", variant: "plain", theme: "secondary", size: "xs", onClick: onClose, children: jsxRuntime.jsx(ChevronLeft.default, { className: "size-4" }) })), title && (jsxRuntime.jsx(react.DialogTitle, { as: "h3", className: buildClassname.buildClassName('text-lg font-semibold text-gray-900 dark:text-white truncate tracking-tight min-w-0', titleClass), children: title }))] }), showCloseButton && (jsxRuntime.jsx(Button.Button, { iconOnly: true, "aria-label": "Close drawer", variant: "plain", theme: "secondary", size: "xs", onClick: onClose, className: "rounded-full text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 shrink-0", children: jsxRuntime.jsx(X.default, { className: "size-4" }) }))] }), description && (jsxRuntime.jsx("p", { className: buildClassname.buildClassName('text-sm text-gray-500 dark:text-gray-400 font-normal truncate', descriptionClass), children: description }))] })), jsxRuntime.jsxs("div", { className: buildClassname.buildClassName('flex-1 p-4 md:p-6 space-y-4 min-w-0', contentClass, fullscreen && 'pb-[calc(1rem+env(safe-area-inset-bottom,0px))]'), children: [fullscreen && description && (jsxRuntime.jsx("p", { className: buildClassname.buildClassName('text-sm text-gray-500 dark:text-gray-400', descriptionClass), children: description })), children] }), !fullscreen && footer && (jsxRuntime.jsx("div", { className: buildClassname.buildClassName('p-4 border-t border-[var(--ui-border-muted)] bg-white dark:bg-gray-800 shrink-0 min-w-0', footerSticky && 'sticky bottom-0 z-10'), children: footer }))] }) }) })] }));
67
108
  };
68
109
  Drawer.displayName = 'Drawer';
110
+ // history.back() lands later, so a drawer opening meanwhile (one replacing another,
111
+ // or React re-running effects under StrictMode) would have its new entry removed
112
+ // instead, and close at once. Removals are counted, and an opening drawer waits for them.
113
+ let pendingBacks = 0;
114
+ const afterBacks = [];
115
+ function settleBack() {
116
+ pendingBacks = Math.max(0, pendingBacks - 1);
117
+ if (pendingBacks === 0)
118
+ afterBacks.splice(0).forEach((run) => run());
119
+ }
120
+ function removeEntry() {
121
+ pendingBacks += 1;
122
+ let settled = false;
123
+ const settle = () => {
124
+ if (settled)
125
+ return;
126
+ settled = true;
127
+ window.removeEventListener('popstate', settle);
128
+ settleBack();
129
+ };
130
+ window.addEventListener('popstate', settle);
131
+ // A back that never reports (nothing to go back to) mustn't hold others up.
132
+ window.setTimeout(settle, 500);
133
+ window.history.back();
134
+ }
135
+ /**
136
+ * While `active`, the browser's back button (and Android's back gesture)
137
+ * closes the drawer instead of leaving the page: opening adds a history entry
138
+ * (keeping the current state, so routers see the same location), and back
139
+ * removes it. Closed any other way, the entry is removed too, but only if it's
140
+ * still the latest one, so a form that saves and then navigates isn't undone.
141
+ */
142
+ function useCloseOnBack(active, onClose) {
143
+ const onCloseRef = React.useRef(onClose);
144
+ React.useEffect(() => {
145
+ onCloseRef.current = onClose;
146
+ });
147
+ const token = React.useId();
148
+ React.useEffect(() => {
149
+ if (!active || typeof window === 'undefined')
150
+ return;
151
+ const pushEntry = () => { var _a; return window.history.pushState({ ...((_a = window.history.state) !== null && _a !== void 0 ? _a : {}), drawer: token }, ''); };
152
+ let entryPresent = false;
153
+ let disposed = false;
154
+ const handlePop = () => {
155
+ var _a;
156
+ if (((_a = window.history.state) === null || _a === void 0 ? void 0 : _a.drawer) === token)
157
+ return;
158
+ entryPresent = false;
159
+ onCloseRef.current();
160
+ // onClose may step back within the drawer instead of closing it; if it's
161
+ // still open afterwards, take back its entry so the next back works too.
162
+ window.setTimeout(() => {
163
+ if (disposed)
164
+ return;
165
+ pushEntry();
166
+ entryPresent = true;
167
+ }, 0);
168
+ };
169
+ const start = () => {
170
+ if (disposed)
171
+ return;
172
+ pushEntry();
173
+ entryPresent = true;
174
+ window.addEventListener('popstate', handlePop);
175
+ };
176
+ if (pendingBacks > 0)
177
+ afterBacks.push(start);
178
+ else
179
+ start();
180
+ return () => {
181
+ var _a;
182
+ disposed = true;
183
+ window.removeEventListener('popstate', handlePop);
184
+ if (entryPresent && ((_a = window.history.state) === null || _a === void 0 ? void 0 : _a.drawer) === token)
185
+ removeEntry();
186
+ };
187
+ }, [active, token]);
188
+ }
69
189
 
70
190
  exports.Drawer = Drawer;
71
191
  //# sourceMappingURL=Drawer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Drawer.js","sources":["../../../../../src/components/Drawer/Drawer.tsx"],"sourcesContent":["import React from 'react'\nimport { Dialog, DialogBackdrop, DialogPanel, DialogTitle } from '@headlessui/react'\nimport { buildClassName } from '../../utils/build-classname'\nimport { Button } from '../Button'\nimport ChevronLeftIcon from '../Icons/ChevronLeft.svg'\nimport XIcon from '../Icons/X.svg'\nimport { useResponsiveValue } from '../../hooks/use-breakpoint'\nimport type { AlignmentMap, DrawerProps, SizeMap } from './Drawer.types'\n\nconst alignmentMap: AlignmentMap = {\n center: 'items-center justify-center h-full',\n start: 'justify-start h-full',\n end: 'justify-end h-full',\n top: 'items-start justify-center w-full',\n bottom: 'items-end justify-center h-full',\n}\n\nconst panelAlignmentMap: AlignmentMap = {\n center: 'data-[closed]:scale-95 rounded-2xl',\n start: 'data-[closed]:-translate-x-1/2 rounded-r-2xl',\n end: 'data-[closed]:translate-x-1/2 rounded-l-2xl',\n top: 'data-[closed]:-translate-y-1/2 rounded-b-2xl',\n // Bottom sheets clear the home indicator on notched phones (0 elsewhere).\n bottom:\n 'data-[closed]:translate-y-full rounded-t-2xl md:rounded-t-3xl max-h-[88vh] max-h-[88dvh] pb-[env(safe-area-inset-bottom,0px)]',\n}\n\nconst panelSizeMap: SizeMap = {\n xs: 'max-w-xs',\n sm: 'max-w-sm',\n md: 'max-w-md',\n lg: 'max-w-lg',\n xl: 'max-w-xl',\n '2xl': 'max-w-2xl',\n full: 'w-full',\n}\n\nexport const Drawer: React.FC<DrawerProps> = ({\n isOpen,\n onClose = () => {},\n closeOnOutsideClick = true,\n align: alignProp = 'end',\n size: sizeProp = 'md',\n backdrop = true,\n title,\n description,\n children,\n footer,\n footerSticky = false,\n panelClass = '',\n contentClass = '',\n titleClass = '',\n descriptionClass = '',\n titleSticky = false,\n showBackButton = false,\n showCloseButton = true,\n dragHandle,\n}) => {\n const align = useResponsiveValue(alignProp)\n const size = useResponsiveValue(sizeProp)\n const showTitleSection = Boolean(title || description || showBackButton || showCloseButton)\n const shouldShowDragHandle = dragHandle ?? align === 'bottom'\n const [viewportHeight, setViewportHeight] = React.useState<number | null>(null)\n\n React.useEffect(() => {\n if (!isOpen) {\n setViewportHeight(null)\n return\n }\n\n const updateHeight = () => {\n if (typeof window !== 'undefined' && window.visualViewport) {\n setViewportHeight(window.visualViewport.height)\n }\n }\n\n updateHeight()\n const vv = typeof window !== 'undefined' ? window.visualViewport : null\n vv?.addEventListener('resize', updateHeight)\n vv?.addEventListener('scroll', updateHeight)\n\n return () => {\n vv?.removeEventListener('resize', updateHeight)\n vv?.removeEventListener('scroll', updateHeight)\n }\n }, [isOpen])\n\n const handleDialogClose = () => {\n if (closeOnOutsideClick) {\n onClose?.()\n }\n }\n\n return (\n <Dialog\n open={isOpen}\n as=\"div\"\n className=\"relative z-50 focus:outline-none\"\n onClose={handleDialogClose}\n transition\n >\n {backdrop && (\n <DialogBackdrop className=\"fixed inset-0 bg-black/40 backdrop-blur-xs transition-opacity duration-200\" />\n )}\n\n <div\n className=\"fixed left-0 top-0 w-full\"\n style={{ height: viewportHeight ? `${viewportHeight}px` : '100dvh' }}\n >\n <div className={buildClassName('flex', alignmentMap[align])}>\n <DialogPanel\n transition\n className={buildClassName(\n 'w-full max-w-full bg-white dark:bg-gray-800 shadow-xl flex flex-col min-w-0',\n 'overflow-y-auto overflow-x-hidden overscroll-contain touch-pan-y [-webkit-overflow-scrolling:touch] duration-200 ease-out data-[closed]:opacity-0',\n panelAlignmentMap[align],\n panelSizeMap[size],\n panelClass,\n )}\n >\n {/* Top drag handle indicator for mobile bottom sheet */}\n {shouldShowDragHandle && (\n <div\n className=\"w-12 h-1.5 rounded-full bg-gray-300 dark:bg-gray-600 mx-auto my-2.5 shrink-0 cursor-grab\"\n data-testid=\"drawer-drag-handle\"\n />\n )}\n\n {/* Header Section */}\n {showTitleSection && (\n <div\n className={buildClassName(\n 'space-y-1 py-3 px-4 md:px-6 flex flex-col border-b border-[var(--ui-border)] shrink-0',\n titleSticky && 'sticky top-0 bg-white dark:bg-gray-800 z-10',\n )}\n >\n <div className=\"flex items-center justify-between gap-3 min-w-0\">\n <div className=\"flex items-center gap-2 min-w-0 flex-1\">\n {showBackButton && (\n <Button\n iconOnly\n aria-label=\"Back\"\n variant=\"plain\"\n theme=\"secondary\"\n size=\"xs\"\n onClick={onClose}\n >\n <ChevronLeftIcon className=\"size-4\" />\n </Button>\n )}\n {title && (\n <DialogTitle\n as=\"h3\"\n className={buildClassName(\n 'text-lg font-semibold text-gray-900 dark:text-white truncate tracking-tight min-w-0',\n titleClass,\n )}\n >\n {title}\n </DialogTitle>\n )}\n </div>\n\n {showCloseButton && (\n <Button\n iconOnly\n aria-label=\"Close drawer\"\n variant=\"plain\"\n theme=\"secondary\"\n size=\"xs\"\n onClick={onClose}\n className=\"rounded-full text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 shrink-0\"\n >\n <XIcon className=\"size-4\" />\n </Button>\n )}\n </div>\n\n {description && (\n <p\n className={buildClassName(\n 'text-sm text-gray-500 dark:text-gray-400 font-normal truncate',\n descriptionClass,\n )}\n >\n {description}\n </p>\n )}\n </div>\n )}\n\n {/* Content Body */}\n <div className={buildClassName('flex-1 p-4 md:p-6 space-y-4 min-w-0', contentClass)}>\n {children}\n </div>\n\n {/* Footer Section */}\n {footer && (\n <div\n className={buildClassName(\n 'p-4 border-t border-[var(--ui-border-muted)] bg-white dark:bg-gray-800 shrink-0 min-w-0',\n footerSticky && 'sticky bottom-0 z-10',\n )}\n >\n {footer}\n </div>\n )}\n </DialogPanel>\n </div>\n </div>\n </Dialog>\n )\n}\n\nDrawer.displayName = 'Drawer'\n"],"names":["useResponsiveValue","_jsxs","Dialog","_jsx","DialogBackdrop","buildClassName","DialogPanel","Button","ChevronLeftIcon","DialogTitle","XIcon"],"mappings":"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"}
1
+ {"version":3,"file":"Drawer.js","sources":["../../../../../src/components/Drawer/Drawer.tsx"],"sourcesContent":["import React from 'react'\nimport { Dialog, DialogBackdrop, DialogPanel, DialogTitle } from '@headlessui/react'\nimport { buildClassName } from '../../utils/build-classname'\nimport { Button } from '../Button'\nimport ChevronLeftIcon from '../Icons/ChevronLeft.svg'\nimport XIcon from '../Icons/X.svg'\nimport { useBreakpoint, useResponsiveValue } from '../../hooks/use-breakpoint'\nimport type { AlignmentMap, DrawerProps, SizeMap } from './Drawer.types'\n\nconst alignmentMap: AlignmentMap = {\n center: 'items-center justify-center h-full',\n start: 'justify-start h-full',\n end: 'justify-end h-full',\n top: 'items-start justify-center w-full',\n bottom: 'items-end justify-center h-full',\n}\n\nconst panelAlignmentMap: AlignmentMap = {\n center: 'data-[closed]:scale-95 rounded-2xl',\n start: 'data-[closed]:-translate-x-1/2 rounded-r-2xl',\n end: 'data-[closed]:translate-x-1/2 rounded-l-2xl',\n top: 'data-[closed]:-translate-y-1/2 rounded-b-2xl',\n // Bottom sheets clear the home indicator on notched phones (0 elsewhere). Never\n // taller than the container: with the keyboard up it's shorter than 88dvh, and a\n // bottom-anchored panel past its top is off screen and can't be scrolled to.\n bottom:\n 'data-[closed]:translate-y-full rounded-t-2xl md:rounded-t-3xl max-h-[min(88dvh,100%)] pb-[env(safe-area-inset-bottom,0px)]',\n}\n\nconst panelSizeMap: SizeMap = {\n xs: 'max-w-xs',\n sm: 'max-w-sm',\n md: 'max-w-md',\n lg: 'max-w-lg',\n xl: 'max-w-xl',\n '2xl': 'max-w-2xl',\n full: 'w-full',\n}\n\n// How much shorter than the window the visual viewport must be before it's\n// taken to be the on-screen keyboard rather than the URL bar (≈ 56px).\nconst KEYBOARD_THRESHOLD = 150\nexport const Drawer: React.FC<DrawerProps> = ({\n isOpen,\n onClose = () => {},\n closeOnOutsideClick = true,\n align: alignProp = 'end',\n size: sizeProp = 'md',\n backdrop = true,\n title,\n description,\n children,\n footer,\n footerSticky = false,\n panelClass = '',\n contentClass = '',\n titleClass = '',\n descriptionClass = '',\n titleSticky = false,\n showBackButton = false,\n showCloseButton = true,\n dragHandle,\n phoneLayout = 'default',\n headerAction,\n}) => {\n const align = useResponsiveValue(alignProp)\n const size = useResponsiveValue(sizeProp)\n const breakpoint = useBreakpoint()\n const fullscreen = phoneLayout === 'fullscreen' && breakpoint === 'compact'\n const showTitleSection = Boolean(title || description || showBackButton || showCloseButton)\n const shouldShowDragHandle = !fullscreen && (dragHandle ?? align === 'bottom')\n useCloseOnBack(fullscreen && isOpen, onClose)\n // The visible area while the on-screen keyboard is open; null otherwise.\n const [keyboardViewport, setKeyboardViewport] = React.useState<{\n top: number\n height: number\n } | null>(null)\n\n React.useEffect(() => {\n if (!isOpen) {\n setKeyboardViewport(null)\n return\n }\n\n // Follow the visual viewport only while the on-screen keyboard takes space\n // (it's then much shorter than the window), so the drawer and its footer\n // stay above the keyboard. Otherwise it's pinned to the edges: tracking every resize and\n // scroll in state re-rendered the drawer on each frame as the browser's\n // URL bar moved, and its sticky header and footer jumped while scrolling.\n let frame = 0\n const measure = () => {\n frame = 0\n const vv = window.visualViewport\n if (!vv) return\n const keyboardOpen = window.innerHeight - vv.height > KEYBOARD_THRESHOLD\n const top = Math.round(vv.offsetTop)\n const height = Math.round(vv.height)\n // The browser scrolls the visible area to the focused field, so the\n // drawer follows its offset as well as its height. An unchanged value\n // doesn't re-render.\n setKeyboardViewport((current) =>\n !keyboardOpen\n ? null\n : current && current.top === top && current.height === height\n ? current\n : { top, height },\n )\n }\n const updateHeight = () => {\n if (!frame) frame = window.requestAnimationFrame(measure)\n }\n\n measure()\n const vv = typeof window !== 'undefined' ? window.visualViewport : null\n vv?.addEventListener('resize', updateHeight)\n vv?.addEventListener('scroll', updateHeight)\n\n return () => {\n if (frame) window.cancelAnimationFrame(frame)\n vv?.removeEventListener('resize', updateHeight)\n vv?.removeEventListener('scroll', updateHeight)\n }\n }, [isOpen])\n\n const handleDialogClose = () => {\n if (closeOnOutsideClick) {\n onClose?.()\n }\n }\n\n return (\n <Dialog\n open={isOpen}\n as=\"div\"\n className=\"relative z-50 focus:outline-none\"\n onClose={handleDialogClose}\n transition\n >\n {backdrop && (\n <DialogBackdrop className=\"fixed inset-0 bg-black/40 backdrop-blur-xs transition-opacity duration-200\" />\n )}\n\n {/* Pinned to all four edges rather than given a height: mobile browsers\n move fixed edges with the URL bar on every frame, while 100dvh only\n updates once its animation ends (the drawer briefly came up short).\n With the keyboard open it takes the visible area's offset and height. */}\n <div\n className=\"fixed inset-0\"\n style={\n keyboardViewport\n ? {\n top: `${keyboardViewport.top}px`,\n height: `${keyboardViewport.height}px`,\n bottom: 'auto',\n }\n : undefined\n }\n data-testid=\"drawer-viewport\"\n >\n <div className={buildClassName('flex', fullscreen ? 'h-full' : alignmentMap[align])}>\n <DialogPanel\n transition\n className={buildClassName(\n 'w-full max-w-full bg-white dark:bg-gray-800 shadow-xl flex flex-col min-w-0',\n 'overflow-y-auto overflow-x-hidden overscroll-contain touch-pan-y [-webkit-overflow-scrolling:touch] duration-200 ease-out data-[closed]:opacity-0',\n ...(fullscreen\n ? ['h-full data-[closed]:translate-y-4']\n : [panelAlignmentMap[align], panelSizeMap[size], panelClass]),\n )}\n data-layout={fullscreen ? 'fullscreen' : undefined}\n >\n {/* Top drag handle indicator for mobile bottom sheet */}\n {shouldShowDragHandle && (\n <div\n className=\"w-12 h-1.5 rounded-full bg-gray-300 dark:bg-gray-600 mx-auto my-2.5 shrink-0 cursor-grab\"\n data-testid=\"drawer-drag-handle\"\n />\n )}\n\n {/* Phone full-screen bar: the only fixed part, so the fields keep the room. */}\n {fullscreen && (\n <div className=\"sticky top-0 z-10 flex h-14 shrink-0 items-center gap-1 border-b border-[var(--ui-border)] bg-white pl-1 pr-3 pt-[env(safe-area-inset-top,0px)] box-content dark:bg-gray-800\">\n <Button\n iconOnly\n aria-label=\"Close\"\n variant=\"plain\"\n theme=\"secondary\"\n onClick={onClose}\n className=\"shrink-0\"\n >\n <XIcon className=\"size-5\" />\n </Button>\n {title && (\n <DialogTitle\n as=\"h3\"\n className=\"min-w-0 flex-1 truncate text-base font-semibold text-gray-900 dark:text-white\"\n >\n {title}\n </DialogTitle>\n )}\n {headerAction && <div className=\"ml-auto shrink-0\">{headerAction}</div>}\n </div>\n )}\n\n {/* Header Section */}\n {!fullscreen && showTitleSection && (\n <div\n className={buildClassName(\n 'space-y-1 py-3 px-4 md:px-6 flex flex-col border-b border-[var(--ui-border)] shrink-0',\n titleSticky && 'sticky top-0 bg-white dark:bg-gray-800 z-10',\n )}\n >\n <div className=\"flex items-center justify-between gap-3 min-w-0\">\n <div className=\"flex items-center gap-2 min-w-0 flex-1\">\n {showBackButton && (\n <Button\n iconOnly\n aria-label=\"Back\"\n variant=\"plain\"\n theme=\"secondary\"\n size=\"xs\"\n onClick={onClose}\n >\n <ChevronLeftIcon className=\"size-4\" />\n </Button>\n )}\n {title && (\n <DialogTitle\n as=\"h3\"\n className={buildClassName(\n 'text-lg font-semibold text-gray-900 dark:text-white truncate tracking-tight min-w-0',\n titleClass,\n )}\n >\n {title}\n </DialogTitle>\n )}\n </div>\n\n {showCloseButton && (\n <Button\n iconOnly\n aria-label=\"Close drawer\"\n variant=\"plain\"\n theme=\"secondary\"\n size=\"xs\"\n onClick={onClose}\n className=\"rounded-full text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 shrink-0\"\n >\n <XIcon className=\"size-4\" />\n </Button>\n )}\n </div>\n\n {description && (\n <p\n className={buildClassName(\n 'text-sm text-gray-500 dark:text-gray-400 font-normal truncate',\n descriptionClass,\n )}\n >\n {description}\n </p>\n )}\n </div>\n )}\n\n {/* Content Body */}\n <div\n className={buildClassName(\n 'flex-1 p-4 md:p-6 space-y-4 min-w-0',\n contentClass,\n fullscreen && 'pb-[calc(1rem+env(safe-area-inset-bottom,0px))]',\n )}\n >\n {fullscreen && description && (\n <p\n className={buildClassName(\n 'text-sm text-gray-500 dark:text-gray-400',\n descriptionClass,\n )}\n >\n {description}\n </p>\n )}\n {children}\n </div>\n\n {/* Footer Section (the phone full-screen bar carries the action instead) */}\n {!fullscreen && footer && (\n <div\n className={buildClassName(\n 'p-4 border-t border-[var(--ui-border-muted)] bg-white dark:bg-gray-800 shrink-0 min-w-0',\n footerSticky && 'sticky bottom-0 z-10',\n )}\n >\n {footer}\n </div>\n )}\n </DialogPanel>\n </div>\n </div>\n </Dialog>\n )\n}\n\nDrawer.displayName = 'Drawer'\n\n// history.back() lands later, so a drawer opening meanwhile (one replacing another,\n// or React re-running effects under StrictMode) would have its new entry removed\n// instead, and close at once. Removals are counted, and an opening drawer waits for them.\nlet pendingBacks = 0\nconst afterBacks: Array<() => void> = []\n\nfunction settleBack() {\n pendingBacks = Math.max(0, pendingBacks - 1)\n if (pendingBacks === 0) afterBacks.splice(0).forEach((run) => run())\n}\n\nfunction removeEntry() {\n pendingBacks += 1\n let settled = false\n const settle = () => {\n if (settled) return\n settled = true\n window.removeEventListener('popstate', settle)\n settleBack()\n }\n window.addEventListener('popstate', settle)\n // A back that never reports (nothing to go back to) mustn't hold others up.\n window.setTimeout(settle, 500)\n window.history.back()\n}\n\n/**\n * While `active`, the browser's back button (and Android's back gesture)\n * closes the drawer instead of leaving the page: opening adds a history entry\n * (keeping the current state, so routers see the same location), and back\n * removes it. Closed any other way, the entry is removed too, but only if it's\n * still the latest one, so a form that saves and then navigates isn't undone.\n */\nfunction useCloseOnBack(active: boolean, onClose: () => void) {\n const onCloseRef = React.useRef(onClose)\n React.useEffect(() => {\n onCloseRef.current = onClose\n })\n const token = React.useId()\n\n React.useEffect(() => {\n if (!active || typeof window === 'undefined') return\n const pushEntry = () =>\n window.history.pushState({ ...(window.history.state ?? {}), drawer: token }, '')\n let entryPresent = false\n let disposed = false\n const handlePop = () => {\n if (window.history.state?.drawer === token) return\n entryPresent = false\n onCloseRef.current()\n // onClose may step back within the drawer instead of closing it; if it's\n // still open afterwards, take back its entry so the next back works too.\n window.setTimeout(() => {\n if (disposed) return\n pushEntry()\n entryPresent = true\n }, 0)\n }\n const start = () => {\n if (disposed) return\n pushEntry()\n entryPresent = true\n window.addEventListener('popstate', handlePop)\n }\n if (pendingBacks > 0) afterBacks.push(start)\n else start()\n return () => {\n disposed = true\n window.removeEventListener('popstate', handlePop)\n if (entryPresent && window.history.state?.drawer === token) removeEntry()\n }\n }, [active, token])\n}\n"],"names":["useResponsiveValue","useBreakpoint","_jsxs","Dialog","_jsx","DialogBackdrop","buildClassName","DialogPanel","Button","XIcon","DialogTitle","ChevronLeftIcon"],"mappings":"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"}
@@ -58,7 +58,7 @@ const QuantityStepper = React.forwardRef((props, ref) => {
58
58
  };
59
59
  const isAtMin = disabled || value <= min;
60
60
  const isAtMax = disabled || value >= max;
61
- return (jsxRuntime.jsxs("div", { className: buildClassname.buildClassName('flex flex-col gap-1 inline-flex', className), children: [label && (jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2", children: [jsxRuntime.jsx(Label.Label, { htmlFor: stepperId, className: disabled ? 'opacity-70 cursor-not-allowed' : '', children: label }), labelHint && jsxRuntime.jsx(TextContent.TextContent, { xsmall: true, children: labelHint })] })), jsxRuntime.jsxs("div", { className: buildClassname.buildClassName('inline-flex items-center rounded-lg shadow-2xs transition-all duration-150', 'outline outline-1 -outline-offset-1', CONTAINER_SIZE_CLASS[size], hasError
61
+ return (jsxRuntime.jsxs("div", { className: buildClassname.buildClassName('flex flex-col gap-1.5 inline-flex', className), children: [label && (jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2", children: [jsxRuntime.jsx(Label.Label, { htmlFor: stepperId, className: disabled ? 'opacity-70 cursor-not-allowed' : '', children: label }), labelHint && jsxRuntime.jsx(TextContent.TextContent, { xsmall: true, children: labelHint })] })), jsxRuntime.jsxs("div", { className: buildClassname.buildClassName('inline-flex items-center rounded-lg shadow-2xs transition-all duration-150', 'outline outline-1 -outline-offset-1', CONTAINER_SIZE_CLASS[size], hasError
62
62
  ? '!outline-red-500 !ring-1 !ring-red-500/50 dark:!outline-red-500'
63
63
  : 'outline-[var(--ui-border)]', disabled
64
64
  ? 'pointer-events-none bg-gray-100 dark:bg-gray-800 outline-[var(--ui-border-muted)]'
@@ -1 +1 @@
1
- {"version":3,"file":"QuantityStepper.js","sources":["../../../../../src/components/QuantityStepper/QuantityStepper.tsx"],"sourcesContent":["import React, { forwardRef, useId } from 'react'\nimport { buildClassName } from '../../utils/build-classname'\nimport { Label } from '../Label'\nimport { TextContent } from '../TextContent'\nimport type { QuantityStepperProps, QuantityStepperSize } from './QuantityStepper.types'\n\nconst MinusIcon = ({ className }: { className?: string }) => (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n aria-hidden=\"true\"\n >\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" />\n </svg>\n)\n\nconst PlusIcon = ({ className }: { className?: string }) => (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n aria-hidden=\"true\"\n >\n <line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\" />\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" />\n </svg>\n)\n\nconst CONTAINER_SIZE_CLASS: Record<QuantityStepperSize, string> = {\n sm: 'h-8 text-xs',\n md: 'h-10 text-sm',\n lg: 'h-12 text-base',\n}\n\nconst BUTTON_SIZE_CLASS: Record<QuantityStepperSize, string> = {\n sm: 'w-7.5 h-full',\n md: 'w-9.5 h-full',\n lg: 'w-11 h-full',\n}\n\nconst ICON_SIZE_CLASS: Record<QuantityStepperSize, string> = {\n sm: 'size-3.5',\n md: 'size-4',\n lg: 'size-4.5',\n}\n\nconst INPUT_TEXT_CLASS: Record<QuantityStepperSize, string> = {\n sm: 'w-12 text-xs',\n md: 'w-16 text-sm',\n lg: 'w-20 text-base',\n}\n\nexport const QuantityStepper = forwardRef<HTMLInputElement, QuantityStepperProps>((props, ref) => {\n const {\n value,\n onChange,\n min = 0,\n max = 99999,\n step = 1,\n label,\n labelHint,\n helperText,\n error,\n showErrorMessage = true,\n size = 'md',\n prefix,\n suffix,\n disabled = false,\n className,\n onFocus,\n id: customId,\n ...restProps\n } = props\n\n const autoId = useId()\n const stepperId = customId || `stepper-${autoId}`\n const hasError = Boolean(error)\n\n const handleDecrement = () => {\n if (disabled) return\n const next = Math.max(min, Number((value - step).toFixed(4)))\n onChange(next)\n }\n\n const handleIncrement = () => {\n if (disabled) return\n const next = Math.min(max, Number((value + step).toFixed(4)))\n onChange(next)\n }\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const raw = e.target.value\n if (raw === '') {\n onChange(0)\n return\n }\n const parsed = parseFloat(raw)\n if (!isNaN(parsed)) {\n onChange(Math.max(min, Math.min(max, parsed)))\n }\n }\n\n const isAtMin = disabled || value <= min\n const isAtMax = disabled || value >= max\n\n return (\n <div className={buildClassName('flex flex-col gap-1 inline-flex', className)}>\n {/* Label Header Row matching Input */}\n {label && (\n <div className=\"flex items-center justify-between gap-2\">\n <Label htmlFor={stepperId} className={disabled ? 'opacity-70 cursor-not-allowed' : ''}>\n {label}\n </Label>\n {labelHint && <TextContent xsmall>{labelHint}</TextContent>}\n </div>\n )}\n\n {/* Input Container Frame matching Input outline & focus ring */}\n <div\n className={buildClassName(\n 'inline-flex items-center rounded-lg shadow-2xs transition-all duration-150',\n 'outline outline-1 -outline-offset-1',\n CONTAINER_SIZE_CLASS[size],\n hasError\n ? '!outline-red-500 !ring-1 !ring-red-500/50 dark:!outline-red-500'\n : 'outline-[var(--ui-border)]',\n disabled\n ? 'pointer-events-none bg-gray-100 dark:bg-gray-800 outline-[var(--ui-border-muted)]'\n : 'bg-white dark:bg-gray-700',\n 'focus-within:outline-[var(--ui-focus-ring)] focus-within:outline-2 focus-within:-outline-offset-2',\n )}\n data-testid=\"quantity-stepper\"\n >\n {/* Decrement Button */}\n <button\n type=\"button\"\n disabled={isAtMin}\n onClick={handleDecrement}\n tabIndex={-1}\n aria-label=\"Decrease quantity\"\n className={buildClassName(\n 'flex items-center justify-center rounded-l-lg text-gray-500 hover:text-gray-800 dark:text-gray-400 dark:hover:text-white transition-colors shrink-0',\n BUTTON_SIZE_CLASS[size],\n isAtMin\n ? 'opacity-30 hover:bg-transparent cursor-not-allowed'\n : 'cursor-pointer hover:bg-gray-100/80 dark:hover:bg-gray-600/60',\n )}\n data-testid=\"stepper-decrement\"\n >\n <MinusIcon className={ICON_SIZE_CLASS[size]} />\n </button>\n\n {prefix && (\n <span className=\"pl-1 text-gray-400 dark:text-gray-400 select-none text-xs shrink-0\">\n {prefix}\n </span>\n )}\n\n {/* Number Input */}\n <input\n ref={ref}\n id={stepperId}\n type=\"number\"\n min={min}\n max={max}\n step={step}\n value={value === 0 ? '' : value}\n placeholder=\"0\"\n disabled={disabled}\n onFocus={(e) => {\n e.target.select()\n onFocus?.(e)\n }}\n onChange={handleInputChange}\n className={buildClassName(\n 'text-center font-semibold text-gray-900 dark:text-white bg-transparent focus:outline-none [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none',\n INPUT_TEXT_CLASS[size],\n )}\n data-testid=\"stepper-input\"\n {...restProps}\n />\n\n {suffix && (\n <span className=\"pr-1 text-gray-500 dark:text-gray-400 select-none text-xs shrink-0\">\n {suffix}\n </span>\n )}\n\n {/* Increment Button */}\n <button\n type=\"button\"\n disabled={isAtMax}\n onClick={handleIncrement}\n tabIndex={-1}\n aria-label=\"Increase quantity\"\n className={buildClassName(\n 'flex items-center justify-center rounded-r-lg text-gray-500 hover:text-gray-800 dark:text-gray-400 dark:hover:text-white transition-colors shrink-0',\n BUTTON_SIZE_CLASS[size],\n isAtMax\n ? 'opacity-30 hover:bg-transparent cursor-not-allowed'\n : 'cursor-pointer hover:bg-gray-100/80 dark:hover:bg-gray-600/60',\n )}\n data-testid=\"stepper-increment\"\n >\n <PlusIcon className={ICON_SIZE_CLASS[size]} />\n </button>\n </div>\n\n {/* Footer Row matching Input error / helper text */}\n {(showErrorMessage && error) || helperText ? (\n <div className=\"flex items-center justify-between gap-2 mt-0.5\">\n {showErrorMessage && error ? (\n <TextContent xsmall className=\"text-red-500 dark:text-red-400 font-medium\">\n {error}\n </TextContent>\n ) : helperText ? (\n <TextContent xsmall muted>\n {helperText}\n </TextContent>\n ) : null}\n </div>\n ) : null}\n </div>\n )\n})\n\nQuantityStepper.displayName = 'QuantityStepper'\n"],"names":["_jsx","_jsxs","forwardRef","useId","buildClassName","Label","TextContent"],"mappings":"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"}
1
+ {"version":3,"file":"QuantityStepper.js","sources":["../../../../../src/components/QuantityStepper/QuantityStepper.tsx"],"sourcesContent":["import React, { forwardRef, useId } from 'react'\nimport { buildClassName } from '../../utils/build-classname'\nimport { Label } from '../Label'\nimport { TextContent } from '../TextContent'\nimport type { QuantityStepperProps, QuantityStepperSize } from './QuantityStepper.types'\n\nconst MinusIcon = ({ className }: { className?: string }) => (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n aria-hidden=\"true\"\n >\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" />\n </svg>\n)\n\nconst PlusIcon = ({ className }: { className?: string }) => (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n aria-hidden=\"true\"\n >\n <line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\" />\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" />\n </svg>\n)\n\nconst CONTAINER_SIZE_CLASS: Record<QuantityStepperSize, string> = {\n sm: 'h-8 text-xs',\n md: 'h-10 text-sm',\n lg: 'h-12 text-base',\n}\n\nconst BUTTON_SIZE_CLASS: Record<QuantityStepperSize, string> = {\n sm: 'w-7.5 h-full',\n md: 'w-9.5 h-full',\n lg: 'w-11 h-full',\n}\n\nconst ICON_SIZE_CLASS: Record<QuantityStepperSize, string> = {\n sm: 'size-3.5',\n md: 'size-4',\n lg: 'size-4.5',\n}\n\nconst INPUT_TEXT_CLASS: Record<QuantityStepperSize, string> = {\n sm: 'w-12 text-xs',\n md: 'w-16 text-sm',\n lg: 'w-20 text-base',\n}\n\nexport const QuantityStepper = forwardRef<HTMLInputElement, QuantityStepperProps>((props, ref) => {\n const {\n value,\n onChange,\n min = 0,\n max = 99999,\n step = 1,\n label,\n labelHint,\n helperText,\n error,\n showErrorMessage = true,\n size = 'md',\n prefix,\n suffix,\n disabled = false,\n className,\n onFocus,\n id: customId,\n ...restProps\n } = props\n\n const autoId = useId()\n const stepperId = customId || `stepper-${autoId}`\n const hasError = Boolean(error)\n\n const handleDecrement = () => {\n if (disabled) return\n const next = Math.max(min, Number((value - step).toFixed(4)))\n onChange(next)\n }\n\n const handleIncrement = () => {\n if (disabled) return\n const next = Math.min(max, Number((value + step).toFixed(4)))\n onChange(next)\n }\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const raw = e.target.value\n if (raw === '') {\n onChange(0)\n return\n }\n const parsed = parseFloat(raw)\n if (!isNaN(parsed)) {\n onChange(Math.max(min, Math.min(max, parsed)))\n }\n }\n\n const isAtMin = disabled || value <= min\n const isAtMax = disabled || value >= max\n\n return (\n <div className={buildClassName('flex flex-col gap-1.5 inline-flex', className)}>\n {/* Label Header Row matching Input */}\n {label && (\n <div className=\"flex items-center justify-between gap-2\">\n <Label htmlFor={stepperId} className={disabled ? 'opacity-70 cursor-not-allowed' : ''}>\n {label}\n </Label>\n {labelHint && <TextContent xsmall>{labelHint}</TextContent>}\n </div>\n )}\n\n {/* Input Container Frame matching Input outline & focus ring */}\n <div\n className={buildClassName(\n 'inline-flex items-center rounded-lg shadow-2xs transition-all duration-150',\n 'outline outline-1 -outline-offset-1',\n CONTAINER_SIZE_CLASS[size],\n hasError\n ? '!outline-red-500 !ring-1 !ring-red-500/50 dark:!outline-red-500'\n : 'outline-[var(--ui-border)]',\n disabled\n ? 'pointer-events-none bg-gray-100 dark:bg-gray-800 outline-[var(--ui-border-muted)]'\n : 'bg-white dark:bg-gray-700',\n 'focus-within:outline-[var(--ui-focus-ring)] focus-within:outline-2 focus-within:-outline-offset-2',\n )}\n data-testid=\"quantity-stepper\"\n >\n {/* Decrement Button */}\n <button\n type=\"button\"\n disabled={isAtMin}\n onClick={handleDecrement}\n tabIndex={-1}\n aria-label=\"Decrease quantity\"\n className={buildClassName(\n 'flex items-center justify-center rounded-l-lg text-gray-500 hover:text-gray-800 dark:text-gray-400 dark:hover:text-white transition-colors shrink-0',\n BUTTON_SIZE_CLASS[size],\n isAtMin\n ? 'opacity-30 hover:bg-transparent cursor-not-allowed'\n : 'cursor-pointer hover:bg-gray-100/80 dark:hover:bg-gray-600/60',\n )}\n data-testid=\"stepper-decrement\"\n >\n <MinusIcon className={ICON_SIZE_CLASS[size]} />\n </button>\n\n {prefix && (\n <span className=\"pl-1 text-gray-400 dark:text-gray-400 select-none text-xs shrink-0\">\n {prefix}\n </span>\n )}\n\n {/* Number Input */}\n <input\n ref={ref}\n id={stepperId}\n type=\"number\"\n min={min}\n max={max}\n step={step}\n value={value === 0 ? '' : value}\n placeholder=\"0\"\n disabled={disabled}\n onFocus={(e) => {\n e.target.select()\n onFocus?.(e)\n }}\n onChange={handleInputChange}\n className={buildClassName(\n 'text-center font-semibold text-gray-900 dark:text-white bg-transparent focus:outline-none [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none',\n INPUT_TEXT_CLASS[size],\n )}\n data-testid=\"stepper-input\"\n {...restProps}\n />\n\n {suffix && (\n <span className=\"pr-1 text-gray-500 dark:text-gray-400 select-none text-xs shrink-0\">\n {suffix}\n </span>\n )}\n\n {/* Increment Button */}\n <button\n type=\"button\"\n disabled={isAtMax}\n onClick={handleIncrement}\n tabIndex={-1}\n aria-label=\"Increase quantity\"\n className={buildClassName(\n 'flex items-center justify-center rounded-r-lg text-gray-500 hover:text-gray-800 dark:text-gray-400 dark:hover:text-white transition-colors shrink-0',\n BUTTON_SIZE_CLASS[size],\n isAtMax\n ? 'opacity-30 hover:bg-transparent cursor-not-allowed'\n : 'cursor-pointer hover:bg-gray-100/80 dark:hover:bg-gray-600/60',\n )}\n data-testid=\"stepper-increment\"\n >\n <PlusIcon className={ICON_SIZE_CLASS[size]} />\n </button>\n </div>\n\n {/* Footer Row matching Input error / helper text */}\n {(showErrorMessage && error) || helperText ? (\n <div className=\"flex items-center justify-between gap-2 mt-0.5\">\n {showErrorMessage && error ? (\n <TextContent xsmall className=\"text-red-500 dark:text-red-400 font-medium\">\n {error}\n </TextContent>\n ) : helperText ? (\n <TextContent xsmall muted>\n {helperText}\n </TextContent>\n ) : null}\n </div>\n ) : null}\n </div>\n )\n})\n\nQuantityStepper.displayName = 'QuantityStepper'\n"],"names":["_jsx","_jsxs","forwardRef","useId","buildClassName","Label","TextContent"],"mappings":"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"}
@@ -274,7 +274,7 @@ const SelectBox = React.forwardRef((props, forwardedRef) => {
274
274
  const isOptSelected = selectedValueSet.has(optVal);
275
275
  return (jsxRuntime.jsx(react.ComboboxOption, { disabled: option.disabled, value: option, className: "group rounded-lg py-1.5 px-3 select-none data-[focus]:bg-gray-100 dark:data-[focus]:bg-white/10 data-disabled:opacity-50 cursor-pointer data-disabled:pointer-events-none", children: renderOption ? (renderOption(option, Boolean(isOptSelected))) : (jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 text-gray-900 dark:text-white group-data-[selected]:text-blue-600", children: [jsxRuntime.jsx("span", { className: "text-sm/6", children: getDisplayValue(option) }), jsxRuntime.jsx(Check.default, { className: "invisible size-5 dark:stroke-gray-300 group-data-[selected]:visible" })] })) }, optVal));
276
276
  };
277
- return (jsxRuntime.jsxs("div", { className: buildClassname.buildClassName('group space-y-2', hasError && 'has-error', containerClass), ref: containerRef, children: [label && (jsxRuntime.jsxs("div", { className: buildClassname.buildClassName('flex items-center justify-between', labelWrapperClass), children: [jsxRuntime.jsx(Label.Label, { className: labelClass, htmlFor: id, "aria-required": required, children: label }), labelHint && jsxRuntime.jsx(TextContent.TextContent, { xsmall: true, children: labelHint })] })), jsxRuntime.jsxs(react.Combobox, { multiple: multiple, immediate: immediate, name: name, value: normalizedSelected, onChange: comboboxOnChangeWrapper, onClose: () => setQuery(''), disabled: disabled, by: (a, b) => (a && b ? getValue(a) === getValue(b) : a === b), children: [multiple ? (jsxRuntime.jsxs("div", { className: buildClassname.buildClassName(baseInputClass, 'flex flex-wrap gap-2 peer relative', rightPaddingClass, hasLeftGroup ? (size === 'lg' ? 'ps-12' : size === 'sm' ? 'ps-8' : 'ps-10') : ''), ref: forwardedRef, children: [hasLeftGroup && (jsxRuntime.jsx("span", { className: buildClassname.buildClassName('absolute left-0 top-0 h-full flex items-center justify-center text-gray-400 dark:text-gray-500', currentSize.group), children: leftGroup })), isArray.isArray(normalizedSelected) &&
277
+ return (jsxRuntime.jsxs("div", { className: buildClassname.buildClassName('group', hasError && 'has-error', containerClass), ref: containerRef, children: [label && (jsxRuntime.jsxs("div", { className: buildClassname.buildClassName('flex items-center justify-between mb-1.5', labelWrapperClass), children: [jsxRuntime.jsx(Label.Label, { className: labelClass, htmlFor: id, "aria-required": required, children: label }), labelHint && jsxRuntime.jsx(TextContent.TextContent, { xsmall: true, children: labelHint })] })), jsxRuntime.jsxs(react.Combobox, { multiple: multiple, immediate: immediate, name: name, value: normalizedSelected, onChange: comboboxOnChangeWrapper, onClose: () => setQuery(''), disabled: disabled, by: (a, b) => (a && b ? getValue(a) === getValue(b) : a === b), children: [multiple ? (jsxRuntime.jsxs("div", { className: buildClassname.buildClassName(baseInputClass, 'flex flex-wrap gap-2 peer relative', rightPaddingClass, hasLeftGroup ? (size === 'lg' ? 'ps-12' : size === 'sm' ? 'ps-8' : 'ps-10') : ''), ref: forwardedRef, children: [hasLeftGroup && (jsxRuntime.jsx("span", { className: buildClassname.buildClassName('absolute left-0 top-0 h-full flex items-center justify-center text-gray-400 dark:text-gray-500', currentSize.group), children: leftGroup })), isArray.isArray(normalizedSelected) &&
278
278
  (normalizedSelected === null || normalizedSelected === void 0 ? void 0 : normalizedSelected.length) > 0 &&
279
279
  normalizedSelected.map((option, idx) => (jsxRuntime.jsx(Badge.Badge, { theme: "secondary", size: currentSize.badgeSize, removable: !disabled, onRemove: () => removeSelection(idx), children: getDisplayValue(option) }, `${getValue(option)}-${idx}`))), jsxRuntime.jsx(react.ComboboxInput, { className: "outline-none flex-1 dark:placeholder-gray-400 placeholder-gray-400 bg-transparent", "aria-label": name, value: query, autoComplete: "off", placeholder: (normalizedSelected === null || normalizedSelected === void 0 ? void 0 : normalizedSelected.length) ? '' : placeholder, onChange: (e) => {
280
280
  const val = e.target.value;