@sproutsocial/seeds-react-drawer 1.2.11 → 2.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -30,74 +30,331 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ ActionRail: () => ActionRail,
34
+ ActionRailButton: () => ActionRailButton,
33
35
  Drawer: () => Drawer_default,
34
36
  DrawerContext: () => DrawerContext,
37
+ RAIL_BUTTON_HEIGHT: () => RAIL_BUTTON_HEIGHT,
38
+ RAIL_BUTTON_SIZE: () => RAIL_BUTTON_SIZE,
39
+ RAIL_GAP: () => RAIL_GAP,
40
+ RAIL_OFFSET: () => RAIL_OFFSET,
35
41
  default: () => index_default
36
42
  });
37
43
  module.exports = __toCommonJS(index_exports);
38
44
 
39
- // src/Drawer.tsx
40
- var React = __toESM(require("react"));
41
- var import_react = require("react");
42
- var import_react_focus_lock = __toESM(require("react-focus-lock"));
43
- var import_react2 = require("motion/react");
44
- var import_unitless = require("@sproutsocial/seeds-motion/unitless");
45
- var import_seeds_react_box2 = __toESM(require("@sproutsocial/seeds-react-box"));
46
- var import_seeds_react_button = __toESM(require("@sproutsocial/seeds-react-button"));
47
- var import_seeds_react_icon = __toESM(require("@sproutsocial/seeds-react-icon"));
48
- var import_seeds_react_text = __toESM(require("@sproutsocial/seeds-react-text"));
49
- var import_seeds_react_portal = __toESM(require("@sproutsocial/seeds-react-portal"));
45
+ // src/DrawerHybrid.tsx
46
+ var import_seeds_react_system_props2 = require("@sproutsocial/seeds-react-system-props");
47
+
48
+ // src/DrawerStyled.tsx
49
+ var import_drawer3 = require("@base-ui/react/drawer");
50
50
 
51
51
  // src/styles.ts
52
- var import_styled_components = __toESM(require("styled-components"));
52
+ var import_drawer = require("@base-ui/react/drawer");
53
+ var import_styled_components3 = __toESM(require("styled-components"));
53
54
  var import_seeds_react_system_props = require("@sproutsocial/seeds-react-system-props");
55
+ var import_unitless = require("@sproutsocial/seeds-motion/unitless");
54
56
  var import_seeds_react_box = __toESM(require("@sproutsocial/seeds-react-box"));
55
- var Content = (0, import_styled_components.default)(import_seeds_react_box.default)`
57
+
58
+ // src/ActionRail/ActionRail.tsx
59
+ var React = require("react");
60
+ var import_styled_components = __toESM(require("styled-components"));
61
+ var import_jsx_runtime = require("react/jsx-runtime");
62
+ var RAIL_BUTTON_SIZE = 44;
63
+ var RAIL_BUTTON_HEIGHT = 32;
64
+ var RAIL_OFFSET = 12;
65
+ var RAIL_GAP = 12;
66
+ var mobileLayout = import_styled_components.css`
67
+ top: auto;
68
+ bottom: calc(100% + ${RAIL_OFFSET}px);
69
+ right: ${RAIL_OFFSET}px;
70
+ left: auto;
71
+ flex-direction: row-reverse;
72
+ `;
73
+ var desktopLayout = import_styled_components.css`
74
+ top: ${RAIL_OFFSET}px;
75
+ right: calc(-1 * (${RAIL_BUTTON_SIZE}px + ${RAIL_OFFSET}px));
76
+ flex-direction: column;
77
+ `;
78
+ var Rail = import_styled_components.default.div`
79
+ position: absolute;
80
+ display: flex;
81
+ gap: ${RAIL_GAP}px;
82
+ z-index: 1;
83
+ ${(props) => props.$isMobile ? mobileLayout : desktopLayout}
84
+ `;
85
+ var ActionRail = ({
86
+ children,
87
+ isMobile,
88
+ "aria-label": ariaLabel = "Quick actions"
89
+ }) => {
90
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
91
+ Rail,
92
+ {
93
+ $isMobile: isMobile,
94
+ "data-slot": "action-rail",
95
+ "data-qa-action-rail": true,
96
+ "aria-label": ariaLabel,
97
+ role: "group",
98
+ children
99
+ }
100
+ );
101
+ };
102
+ ActionRail.displayName = "ActionRail";
103
+
104
+ // src/ActionRail/ActionRailButton.tsx
105
+ var React2 = __toESM(require("react"));
106
+ var import_styled_components2 = __toESM(require("styled-components"));
107
+ var import_seeds_react_mixins = require("@sproutsocial/seeds-react-mixins");
108
+ var import_jsx_runtime2 = require("react/jsx-runtime");
109
+ var StyledButton = import_styled_components2.default.button`
110
+ width: ${RAIL_BUTTON_SIZE}px;
111
+ height: ${RAIL_BUTTON_HEIGHT}px;
112
+ display: inline-grid;
113
+ place-items: center;
114
+ border-radius: ${(props) => props.theme.radii.outer};
115
+ border: none;
116
+ background: ${(props) => props.theme.colors.button.overlay.background.base};
117
+ color: ${(props) => props.theme.colors.button.overlay.text.base};
118
+ cursor: pointer;
119
+ outline: none;
120
+ transition: all ${(props) => props.theme.duration.fast}
121
+ ${(props) => props.theme.easing.ease_inout};
122
+
123
+ &:hover {
124
+ background: ${(props) => props.theme.colors.button.overlay.background.hover};
125
+ }
126
+
127
+ &:hover,
128
+ &:active {
129
+ transform: none;
130
+ }
131
+
132
+ &:focus-visible {
133
+ ${import_seeds_react_mixins.focusRing}
134
+ }
135
+
136
+ &:disabled {
137
+ opacity: 0.5;
138
+ cursor: not-allowed;
139
+ }
140
+ `;
141
+ var ActionRailButton = React2.forwardRef(({ children, ...rest }, ref) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
142
+ StyledButton,
143
+ {
144
+ ref,
145
+ type: "button",
146
+ "data-slot": "action-rail-button",
147
+ ...rest,
148
+ children
149
+ }
150
+ ));
151
+ ActionRailButton.displayName = "ActionRailButton";
152
+
153
+ // src/styles.ts
154
+ var ACTION_RAIL_OUTSET = RAIL_BUTTON_HEIGHT + RAIL_OFFSET;
155
+ var Content = (0, import_styled_components3.default)(import_seeds_react_box.default)`
56
156
  overflow-y: auto;
57
157
  `;
58
- var Container = import_styled_components.default.div`
158
+ var StyledViewport = (0, import_styled_components3.default)(import_drawer.Drawer.Viewport)`
159
+ ${(props) => props.$hasSnapPoints ? `
160
+ position: fixed;
161
+ inset: 0;
162
+ display: flex;
163
+ align-items: flex-end;
164
+ justify-content: center;
165
+ touch-action: none;
166
+ /* The viewport spans the whole screen but the popup only fills part of
167
+ it. Letting clicks fall through the empty area means a nested
168
+ snap-point drawer doesn't block selection/interaction with the
169
+ parent drawer visible underneath. The popup re-enables events. */
170
+ pointer-events: none;
171
+ ` : ""}
172
+ `;
173
+ var StyledPopup = (0, import_styled_components3.default)(import_drawer.Drawer.Popup)`
59
174
  display: flex;
60
175
  flex-direction: column;
61
176
  position: fixed;
62
- top: 0;
63
- height: 100%;
64
- width: ${(props) => props.width}px;
65
177
  background-color: ${(props) => props.theme.colors.container.background.base};
66
- box-shadow: ${(props) => props.theme.shadows.high};
178
+ z-index: ${(props) => props.$zIndex};
67
179
  filter: blur(0);
180
+ transition: transform ${import_unitless.MOTION_DURATION_MEDIUM}s ease,
181
+ border-radius ${import_unitless.MOTION_DURATION_MEDIUM}s ease,
182
+ height ${import_unitless.MOTION_DURATION_MEDIUM}s ease;
183
+
184
+ /* Always-on opacity transition so content fades smoothly both into and out of stacked state */
185
+ > * {
186
+ transition: opacity ${import_unitless.MOTION_DURATION_MEDIUM}s ease;
187
+ }
188
+
189
+ ${(props) => {
190
+ if (props.$direction === "bottom") {
191
+ if (props.$hasSnapPoints) {
192
+ return `
193
+ box-sizing: border-box;
194
+ position: relative;
195
+ width: 100%;
196
+ height: calc(100dvh - 1rem);
197
+ padding-bottom: max(0px, calc(var(--drawer-snap-point-offset, 0px) + var(--drawer-swipe-movement-y, 0px)));
198
+ overflow: visible;
199
+ touch-action: none;
200
+ /* Counterpart to the viewport's pointer-events: none \u2014 the popup
201
+ itself stays interactive. */
202
+ pointer-events: auto;
203
+ border-radius: ${props.theme.radii[800]} ${props.theme.radii[800]} 0 0;
204
+ box-shadow: 0px -8px 16px rgba(39,51,51,.25);
205
+ transform: translateY(calc(var(--drawer-snap-point-offset, 0px) + var(--drawer-swipe-movement-y, 0px)));
206
+ will-change: transform;
207
+ transition:
208
+ transform ${import_unitless.MOTION_DURATION_MEDIUM}s cubic-bezier(0.32, 0.72, 0, 1),
209
+ box-shadow ${import_unitless.MOTION_DURATION_MEDIUM}s cubic-bezier(0.32, 0.72, 0, 1);
210
+ `;
211
+ }
212
+ return `
213
+ --bleed: 3rem;
214
+ --stack-scale: 1;
215
+ --translate-y: 0px;
216
+ bottom: 0;
217
+ left: 0;
218
+ right: 0;
219
+ width: 100%;
220
+ height: calc(90vh + var(--bleed));
221
+ margin-bottom: calc(-1 * var(--bleed));
222
+ border-radius: ${props.theme.radii[800]} ${props.theme.radii[800]} 0 0;
223
+ transform: translateY(var(--translate-y)) scale(var(--stack-scale));
224
+ transform-origin: 50% calc(100% - var(--bleed));
225
+ box-shadow: 0px -8px 16px rgba(39,51,51,.25);
226
+ `;
227
+ }
228
+ return `
229
+ top: 0;
230
+ height: 100%;
231
+ width: ${props.$width}px;
232
+ ${props.$direction === "right" ? `right: ${props.$offset}px;` : `left: ${props.$offset}px;`}
233
+ box-shadow: ${props.theme.shadows.medium};
234
+ `;
235
+ }}
236
+
237
+ &[data-starting-style],
238
+ &[data-ending-style] {
239
+ ${(props) => {
240
+ if (props.$direction !== "bottom") {
241
+ return `transform: translateX(${props.$direction === "right" ? "100%" : "-100%"});`;
242
+ }
243
+ const railOffset = props.$hasActionRail ? ` + ${ACTION_RAIL_OUTSET}px` : "";
244
+ if (props.$hasSnapPoints) {
245
+ return `
246
+ transform: translateY(calc(100% + 2px${railOffset}));
247
+ padding-bottom: 0;
248
+ `;
249
+ }
250
+ return `transform: translateY(calc(100% - var(--bleed) + 2px${railOffset}));`;
251
+ }}
252
+ }
253
+
254
+ &[data-swiping],
255
+ &[data-nested-drawer-swiping] {
256
+ transition-duration: 0ms;
257
+ }
258
+
259
+ &[data-nested-drawer-open] {
260
+ border-radius: ${(props) => props.theme.radii[800]};
261
+
262
+ ${(props) => {
263
+ if (props.$direction === "bottom") {
264
+ if (props.$hasSnapPoints) return "";
265
+ return `
266
+ --stack-step: 0.05;
267
+ --stack-progress: clamp(0, var(--drawer-swipe-progress), 1);
268
+ /* Override default vars; base transform rule picks them up automatically */
269
+ --stack-scale: max(0, calc(
270
+ 1 - (var(--nested-drawers) * var(--stack-step))
271
+ + (var(--stack-step) * var(--stack-progress))
272
+ ));
273
+ --stack-shrink: calc(1 - var(--stack-scale));
274
+ --stack-height: max(0px, calc(
275
+ var(--drawer-frontmost-height, var(--drawer-height)) - var(--bleed)
276
+ ));
277
+ --stack-peek-offset: max(0px, calc(
278
+ (var(--nested-drawers) - var(--stack-progress)) * 1rem
279
+ ));
280
+ --translate-y: calc(
281
+ var(--drawer-swipe-movement-y)
282
+ - var(--stack-peek-offset)
283
+ - (var(--stack-shrink) * var(--stack-height))
284
+ );
285
+ height: calc(var(--stack-height) + var(--bleed));
286
+ overflow: hidden;
287
+ `;
288
+ }
289
+ return `
290
+ transform: translateY(calc(var(--nested-drawers) * -20px))
291
+ scale(calc(1 - var(--nested-drawers) * 0.08));
292
+ transform-origin: top center;
293
+ `;
294
+ }}
295
+
296
+ ${(props) => props.$hasSnapPoints ? "" : `
297
+ > * {
298
+ opacity: 0;
299
+ }
300
+ `}
301
+ }
68
302
 
69
- ${(props) => import_styled_components.css`
70
- ${props.direction}: ${props.offset}px;
71
- `}
303
+ &[data-nested-drawer-open][data-nested-drawer-swiping] {
304
+ > * {
305
+ opacity: 1;
306
+ }
307
+ }
72
308
 
73
309
  ${import_seeds_react_system_props.COMMON}
74
310
  `;
75
- var styles_default = Container;
311
+ var DragHandle = import_styled_components3.default.div`
312
+ position: absolute;
313
+ top: 8px;
314
+ left: 50%;
315
+ transform: translateX(-50%);
316
+ width: 36px;
317
+ height: 4px;
318
+ border-radius: 2px;
319
+ background-color: ${(props) => props.theme.colors.container.border.base};
320
+ `;
321
+ var StyledBackdrop = (0, import_styled_components3.default)(import_drawer.Drawer.Backdrop)`
322
+ position: fixed;
323
+ inset: 0;
324
+ background: transparent;
325
+ pointer-events: none;
326
+ `;
76
327
 
77
- // src/Drawer.tsx
78
- var import_jsx_runtime = require("react/jsx-runtime");
79
- var MotionContainer = (0, import_react2.motion)(styles_default);
80
- var doesRefContainEventTarget = (ref, event) => {
81
- return ref.current && event.target instanceof Node && ref.current.contains(event.target);
82
- };
83
- var DrawerContext = React.createContext({
328
+ // src/DrawerShared.tsx
329
+ var React3 = __toESM(require("react"));
330
+ var import_react = require("react");
331
+ var import_drawer2 = require("@base-ui/react/drawer");
332
+ var import_seeds_react_box2 = __toESM(require("@sproutsocial/seeds-react-box"));
333
+ var import_seeds_react_button = __toESM(require("@sproutsocial/seeds-react-button"));
334
+ var import_seeds_react_icon = __toESM(require("@sproutsocial/seeds-react-icon"));
335
+ var import_seeds_react_text = __toESM(require("@sproutsocial/seeds-react-text"));
336
+ var import_seeds_react_hooks = require("@sproutsocial/seeds-react-hooks");
337
+ var import_seeds_react_portal = require("@sproutsocial/seeds-react-portal");
338
+ var import_jsx_runtime3 = require("react/jsx-runtime");
339
+ var DrawerContext = React3.createContext({
84
340
  isLocked: false,
85
341
  setIsLocked: () => {
86
- }
342
+ },
343
+ hasActionRail: false
87
344
  });
88
345
  var DrawerCloseButton = (props) => {
89
346
  const drawerContext = (0, import_react.useContext)(DrawerContext);
90
347
  if (props.render) {
91
348
  return props.render(drawerContext) ?? null;
92
349
  }
93
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
350
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
94
351
  import_seeds_react_button.default,
95
352
  {
96
353
  appearance: "pill",
97
354
  "aria-label": drawerContext.closeButtonLabel,
98
355
  onClick: drawerContext.onClose,
99
356
  ...props,
100
- children: props.children || /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_seeds_react_icon.default, { "aria-hidden": true, name: "x-outline" })
357
+ children: props.children || /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_seeds_react_icon.default, { "aria-hidden": true, name: "x-outline" })
101
358
  }
102
359
  );
103
360
  };
@@ -112,7 +369,7 @@ var DrawerHeader = ({
112
369
  if (render) {
113
370
  return render(drawerContext);
114
371
  }
115
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
372
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
116
373
  import_seeds_react_box2.default,
117
374
  {
118
375
  display: "flex",
@@ -122,8 +379,8 @@ var DrawerHeader = ({
122
379
  pt: 400,
123
380
  px: 450,
124
381
  ...rest,
125
- children: children || /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(React.Fragment, { children: [
126
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
382
+ children: children || /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
383
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
127
384
  import_seeds_react_text.default,
128
385
  {
129
386
  as: "h2",
@@ -134,186 +391,345 @@ var DrawerHeader = ({
134
391
  children: title
135
392
  }
136
393
  ),
137
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DrawerCloseButton, {})
394
+ !drawerContext.hasActionRail && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DrawerCloseButton, {})
138
395
  ] })
139
396
  }
140
397
  );
141
398
  };
142
- var DrawerContent = ({ children, ...rest }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Content, { height: "100%", p: 450, color: "text.body", ...rest, children });
143
- var useCloseOnBodyClick = ({
144
- isOpen,
145
- ref,
146
- disableCloseOnClickOutside,
147
- onClose,
148
- closeTargets
149
- }) => {
150
- const { isLocked } = (0, import_react.useContext)(DrawerContext);
151
- (0, import_react.useEffect)(() => {
152
- const documentBody = document.body;
153
- if (!documentBody || !isOpen) {
154
- return;
155
- }
156
- const onEsc = (event) => {
157
- if (!isLocked && event.key === "Escape") {
158
- onClose();
159
- }
160
- };
161
- const bodyClick = (event) => {
162
- if (
163
- // @ts-ignore I'm not sure how to type this ref properly
164
- !doesRefContainEventTarget(ref, event) && !disableCloseOnClickOutside
165
- ) {
166
- onClose();
399
+ var DrawerContent = ({ children, ...rest }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
400
+ Content,
401
+ {
402
+ height: "100%",
403
+ p: 450,
404
+ color: "text.body",
405
+ "data-drawer-content": "",
406
+ ...rest,
407
+ children
408
+ }
409
+ );
410
+ DrawerHeader.displayName = "Drawer.Header";
411
+ DrawerContent.displayName = "Drawer.Content";
412
+ DrawerCloseButton.displayName = "Drawer.CloseButton";
413
+ var useDrawerShell = (props) => {
414
+ const {
415
+ children,
416
+ closeButtonLabel,
417
+ direction = "right",
418
+ disableCloseOnClickOutside = false,
419
+ id,
420
+ modal = false,
421
+ open,
422
+ offset = 0,
423
+ onClose,
424
+ zIndex = 7,
425
+ width = 600,
426
+ snapPoints,
427
+ defaultSnapPoint,
428
+ snapPoint,
429
+ onSnapPointChange,
430
+ snapToSequentialPoints,
431
+ actions,
432
+ actionsInHeader = false,
433
+ ...rest
434
+ } = props;
435
+ const isMobile = (0, import_seeds_react_hooks.useIsMobile)();
436
+ const effectiveDirection = isMobile ? "bottom" : direction;
437
+ const effectiveSnapPoints = effectiveDirection === "bottom" ? snapPoints : void 0;
438
+ const hasSnapPoints = !!effectiveSnapPoints && effectiveSnapPoints.length > 0;
439
+ const hasActionRail = isMobile && !actionsInHeader && !!actions && actions.length > 0;
440
+ const [isLocked, setIsLocked] = React3.useState(false);
441
+ const ariaLabelledBy = rest["aria-labelledby"];
442
+ const ariaLabel = rest["aria-label"];
443
+ React3.useEffect(() => {
444
+ if (process.env.NODE_ENV !== "production") {
445
+ if (!ariaLabelledBy && !ariaLabel) {
446
+ console.warn(
447
+ "[Drawer] Missing accessible name. Add aria-labelledby pointing to your Drawer.Header id, or aria-label, so screen readers can identify the dialog."
448
+ );
167
449
  }
168
- };
169
- documentBody?.addEventListener("keydown", onEsc, { capture: true });
170
- if (closeTargets) {
171
- closeTargets.forEach(
172
- (targetElement) => targetElement?.addEventListener("click", bodyClick, { capture: true })
173
- );
174
- } else {
175
- documentBody.firstElementChild?.addEventListener("click", bodyClick, {
176
- capture: true
177
- });
178
450
  }
179
- return () => {
180
- documentBody?.removeEventListener("keydown", onEsc, { capture: true });
181
- if (closeTargets) {
182
- closeTargets.forEach(
183
- (targetElement) => targetElement?.removeEventListener("click", bodyClick, {
184
- capture: true
185
- })
186
- );
187
- } else {
188
- documentBody.firstElementChild?.removeEventListener(
189
- "click",
190
- bodyClick,
191
- { capture: true }
192
- );
451
+ }, [ariaLabelledBy, ariaLabel]);
452
+ const isLockedRef = React3.useRef(isLocked);
453
+ isLockedRef.current = isLocked;
454
+ const prevChildrenRef = React3.useRef(null);
455
+ if (open) {
456
+ prevChildrenRef.current = children;
457
+ }
458
+ const renderedChildren = open ? children : prevChildrenRef.current;
459
+ const rootProps = {
460
+ open,
461
+ onOpenChange: (isOpen, eventDetails) => {
462
+ if (!isOpen) {
463
+ if (isLockedRef.current && eventDetails.reason === "escape-key") {
464
+ return;
465
+ }
466
+ onClose();
193
467
  }
194
- };
195
- }, [
196
- onClose,
197
- disableCloseOnClickOutside,
198
- closeTargets,
199
- ref,
468
+ },
469
+ disablePointerDismissal: disableCloseOnClickOutside,
470
+ swipeDirection: effectiveDirection === "bottom" ? "down" : effectiveDirection,
471
+ modal,
472
+ snapPoints: effectiveSnapPoints,
473
+ defaultSnapPoint,
474
+ snapPoint,
475
+ onSnapPointChange: onSnapPointChange ? (sp) => onSnapPointChange(sp) : void 0,
476
+ snapToSequentialPoints
477
+ };
478
+ return {
479
+ effectiveDirection,
480
+ hasSnapPoints,
481
+ hasActionRail,
200
482
  isLocked,
201
- isOpen
202
- ]);
483
+ setIsLocked,
484
+ renderedChildren,
485
+ rootProps,
486
+ rest,
487
+ width,
488
+ offset,
489
+ zIndex,
490
+ id,
491
+ open,
492
+ closeButtonLabel,
493
+ onClose,
494
+ actions
495
+ };
203
496
  };
204
- var Drawer = ({
205
- id,
206
- offset,
207
- direction,
208
- children,
209
- disableCloseOnClickOutside,
210
- onClose,
211
- zIndex,
212
- closeTargets,
213
- width,
214
- focusLockExemptCheck,
215
- isOpen,
216
- ...rest
217
- }) => {
218
- const ref = (0, import_react.useRef)(null);
219
- useCloseOnBodyClick({
220
- ref,
221
- disableCloseOnClickOutside,
497
+ var DrawerPopupContents = ({ shell }) => {
498
+ const {
499
+ hasActionRail,
500
+ closeButtonLabel,
222
501
  onClose,
223
- closeTargets,
224
- isOpen
225
- });
226
- const offset_x = width * (direction === "left" ? -1 : 1);
227
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
228
- import_react_focus_lock.default,
229
- {
230
- autoFocus: true,
231
- disabled: !isOpen,
232
- returnFocus: true,
233
- whiteList: focusLockExemptCheck ? (e) => !focusLockExemptCheck(e) : void 0,
234
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.AnimatePresence, { children: isOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
235
- MotionContainer,
502
+ actions,
503
+ isLocked,
504
+ setIsLocked,
505
+ renderedChildren
506
+ } = shell;
507
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
508
+ hasActionRail && actions && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(ActionRail, { isMobile: true, "aria-label": "Drawer quick actions", children: [
509
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
510
+ ActionRailButton,
236
511
  {
237
- ref,
238
- style: { zIndex },
239
- width,
240
- offset,
241
- direction,
242
- "data-qa-drawer": id,
243
- role: "dialog",
244
- initial: { opacity: 0, x: offset_x },
245
- animate: { opacity: 1, x: 0 },
246
- exit: { opacity: 0, x: offset_x },
247
- transition: { duration: import_unitless.MOTION_DURATION_MEDIUM },
248
- ...rest,
249
- children
512
+ "aria-label": closeButtonLabel,
513
+ onClick: onClose,
514
+ "data-qa-drawer-rail-close": true,
515
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_seeds_react_icon.default, { "aria-hidden": true, name: "x-outline" })
250
516
  }
251
- ) })
252
- },
253
- id
254
- );
255
- };
256
- var DrawerContainer = ({
257
- children,
258
- closeButtonLabel,
259
- direction = "right",
260
- disableCloseOnClickOutside = false,
261
- id,
262
- isOpen,
263
- offset = 0,
264
- onClose,
265
- zIndex = 7,
266
- closeTargets = [],
267
- width = 600,
268
- ...rest
269
- }) => {
270
- const [isLocked, setIsLocked] = React.useState(false);
271
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_seeds_react_portal.default, { id, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
272
- DrawerContext.Provider,
273
- {
274
- value: {
275
- onClose,
276
- closeButtonLabel,
277
- isLocked,
278
- setIsLocked
279
- },
280
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
281
- Drawer,
517
+ ),
518
+ actions.map((action, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
519
+ ActionRailButton,
282
520
  {
283
- isOpen,
284
- id,
285
- offset,
286
- direction,
287
- disableCloseOnClickOutside,
521
+ "aria-label": action["aria-label"],
522
+ onClick: action.onClick,
523
+ disabled: action.disabled,
524
+ children: action.iconName && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_seeds_react_icon.default, { "aria-hidden": true, name: action.iconName })
525
+ },
526
+ i
527
+ ))
528
+ ] }),
529
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_seeds_react_portal.DisablePortalToBodyContext.Provider, { value: true, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
530
+ DrawerContext.Provider,
531
+ {
532
+ value: {
288
533
  onClose,
289
- zIndex,
290
- closeTargets,
291
- width,
292
- "data-qa-drawer": id || "",
293
- "data-qa-drawer-isopen": isOpen,
294
- ...rest,
295
- children
534
+ closeButtonLabel,
535
+ isLocked,
536
+ setIsLocked,
537
+ hasActionRail
538
+ },
539
+ children: renderedChildren
540
+ }
541
+ ) })
542
+ ] });
543
+ };
544
+
545
+ // src/DrawerStyled.tsx
546
+ var import_jsx_runtime4 = require("react/jsx-runtime");
547
+ var DrawerStyled = (props) => {
548
+ const shell = useDrawerShell(props);
549
+ const {
550
+ effectiveDirection,
551
+ hasSnapPoints,
552
+ hasActionRail,
553
+ rootProps,
554
+ rest,
555
+ width,
556
+ offset,
557
+ zIndex,
558
+ id,
559
+ open
560
+ } = shell;
561
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_drawer3.Drawer.Root, { ...rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_drawer3.Drawer.Portal, { children: [
562
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StyledBackdrop, { "data-testid": "drawer-backdrop" }),
563
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StyledViewport, { $hasSnapPoints: hasSnapPoints, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
564
+ StyledPopup,
565
+ {
566
+ initialFocus: true,
567
+ $width: width,
568
+ $offset: offset,
569
+ $direction: effectiveDirection,
570
+ $zIndex: zIndex,
571
+ $hasSnapPoints: hasSnapPoints,
572
+ $hasActionRail: hasActionRail,
573
+ "data-qa-drawer": id,
574
+ "data-qa-drawer-isopen": open,
575
+ ...rest,
576
+ children: [
577
+ effectiveDirection === "bottom" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DragHandle, { "aria-hidden": true }),
578
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DrawerPopupContents, { shell })
579
+ ]
580
+ }
581
+ ) })
582
+ ] }) });
583
+ };
584
+ var DrawerStyled_default = DrawerStyled;
585
+
586
+ // src/DrawerTailwind.tsx
587
+ var import_drawer4 = require("@base-ui/react/drawer");
588
+ var import_jsx_runtime5 = require("react/jsx-runtime");
589
+ function cn(...inputs) {
590
+ const classes = [];
591
+ for (const input of inputs) {
592
+ if (!input) continue;
593
+ if (typeof input === "string") {
594
+ classes.push(input);
595
+ } else if (typeof input === "object") {
596
+ for (const [key, value] of Object.entries(input)) {
597
+ if (value) {
598
+ classes.push(key);
296
599
  }
297
- )
600
+ }
298
601
  }
299
- ) });
602
+ }
603
+ return classes.join(" ");
604
+ }
605
+ var DrawerTailwind = (props) => {
606
+ const shell = useDrawerShell(props);
607
+ const {
608
+ effectiveDirection,
609
+ hasSnapPoints,
610
+ hasActionRail,
611
+ rootProps,
612
+ rest,
613
+ width,
614
+ offset,
615
+ zIndex,
616
+ id,
617
+ open
618
+ } = shell;
619
+ const { className, style, ...domRest } = rest;
620
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_drawer4.Drawer.Root, { ...rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_drawer4.Drawer.Portal, { children: [
621
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
622
+ import_drawer4.Drawer.Backdrop,
623
+ {
624
+ "data-testid": "drawer-backdrop",
625
+ className: "seeds-drawer-backdrop"
626
+ }
627
+ ),
628
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
629
+ import_drawer4.Drawer.Viewport,
630
+ {
631
+ className: cn("seeds-drawer-viewport", {
632
+ "seeds-drawer-viewport--snap": hasSnapPoints
633
+ }),
634
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
635
+ import_drawer4.Drawer.Popup,
636
+ {
637
+ initialFocus: true,
638
+ "data-qa-drawer": id,
639
+ "data-qa-drawer-isopen": open,
640
+ ...domRest,
641
+ className: cn(
642
+ "seeds-drawer-popup",
643
+ effectiveDirection === "bottom" ? "seeds-drawer-popup--bottom" : "seeds-drawer-popup--side",
644
+ effectiveDirection === "right" && "seeds-drawer-popup--right",
645
+ effectiveDirection === "left" && "seeds-drawer-popup--left",
646
+ {
647
+ "seeds-drawer-popup--snap": hasSnapPoints,
648
+ "seeds-drawer-popup--action-rail": hasActionRail
649
+ },
650
+ // Consumer className folded in last so it appends to (not
651
+ // replaces) the component classes.
652
+ className
653
+ ),
654
+ style: {
655
+ // z-index is always inline (runtime numeric, mirrors styles.ts).
656
+ zIndex,
657
+ // Side drawers carry their width/offset as direct inline styles —
658
+ // runtime numerics, and required for the jsdom `toHaveStyle`
659
+ // assertions. Bottom drawers derive width/position from the class.
660
+ ...effectiveDirection === "right" ? { width: `${width}px`, right: `${offset}px` } : effectiveDirection === "left" ? { width: `${width}px`, left: `${offset}px` } : {},
661
+ // Consumer style folded in last so it wins over the computed
662
+ // geometry, matching the styled-components inline-style semantics.
663
+ ...style
664
+ },
665
+ children: [
666
+ effectiveDirection === "bottom" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { "aria-hidden": true, className: "seeds-drawer-drag-handle" }),
667
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(DrawerPopupContents, { shell })
668
+ ]
669
+ }
670
+ )
671
+ }
672
+ )
673
+ ] }) });
300
674
  };
301
- DrawerHeader.displayName = "Drawer.Header";
302
- DrawerContent.displayName = "Drawer.Content";
303
- DrawerCloseButton.displayName = "Drawer.CloseButton";
304
- DrawerContainer.Header = DrawerHeader;
305
- DrawerContainer.Content = DrawerContent;
306
- DrawerContainer.CloseButton = DrawerCloseButton;
307
- var Drawer_default = DrawerContainer;
675
+ var DrawerTailwind_default = DrawerTailwind;
676
+
677
+ // src/DrawerHybrid.tsx
678
+ var import_jsx_runtime6 = require("react/jsx-runtime");
679
+ var DRAWER_OWN_PROPS = [
680
+ "children",
681
+ "closeButtonLabel",
682
+ "direction",
683
+ "disableCloseOnClickOutside",
684
+ "id",
685
+ "modal",
686
+ "open",
687
+ "offset",
688
+ "onClose",
689
+ "zIndex",
690
+ "width",
691
+ "snapPoints",
692
+ "defaultSnapPoint",
693
+ "snapPoint",
694
+ "onSnapPointChange",
695
+ "snapToSequentialPoints",
696
+ "actions",
697
+ "actionsInHeader"
698
+ ];
699
+ var DrawerHybrid = (props) => {
700
+ const rest = {};
701
+ for (const key of Object.keys(props)) {
702
+ if (!DRAWER_OWN_PROPS.includes(key)) {
703
+ rest[key] = props[key];
704
+ }
705
+ }
706
+ if ((0, import_seeds_react_system_props2.hasStyledProps)(rest)) {
707
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DrawerStyled_default, { ...props });
708
+ }
709
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DrawerTailwind_default, { ...props });
710
+ };
711
+ DrawerHybrid.Header = DrawerHeader;
712
+ DrawerHybrid.Content = DrawerContent;
713
+ DrawerHybrid.CloseButton = DrawerCloseButton;
714
+ var DrawerHybrid_default = DrawerHybrid;
715
+
716
+ // src/Drawer.tsx
717
+ var Drawer_default = DrawerHybrid_default;
308
718
 
309
719
  // src/DrawerTypes.ts
310
- var React2 = require("react");
720
+ var React4 = require("react");
311
721
 
312
722
  // src/index.ts
313
723
  var index_default = Drawer_default;
314
724
  // Annotate the CommonJS export names for ESM import in node:
315
725
  0 && (module.exports = {
726
+ ActionRail,
727
+ ActionRailButton,
316
728
  Drawer,
317
- DrawerContext
729
+ DrawerContext,
730
+ RAIL_BUTTON_HEIGHT,
731
+ RAIL_BUTTON_SIZE,
732
+ RAIL_GAP,
733
+ RAIL_OFFSET
318
734
  });
319
735
  //# sourceMappingURL=index.js.map