@sproutsocial/seeds-react-drawer 1.2.11 → 2.2.8

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;
68
183
 
69
- ${(props) => import_styled_components.css`
70
- ${props.direction}: ${props.offset}px;
71
- `}
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
+ }
302
+
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,348 @@ 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 = ({
400
+ children,
401
+ ...rest
402
+ }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
403
+ Content,
404
+ {
405
+ height: "100%",
406
+ p: 450,
407
+ color: "text.body",
408
+ "data-drawer-content": "",
409
+ ...rest,
410
+ children
411
+ }
412
+ );
413
+ DrawerHeader.displayName = "Drawer.Header";
414
+ DrawerContent.displayName = "Drawer.Content";
415
+ DrawerCloseButton.displayName = "Drawer.CloseButton";
416
+ var useDrawerShell = (props) => {
417
+ const {
418
+ children,
419
+ closeButtonLabel,
420
+ direction = "right",
421
+ disableCloseOnClickOutside = false,
422
+ id,
423
+ modal = false,
424
+ open,
425
+ offset = 0,
426
+ onClose,
427
+ zIndex = 7,
428
+ width = 600,
429
+ snapPoints,
430
+ defaultSnapPoint,
431
+ snapPoint,
432
+ onSnapPointChange,
433
+ snapToSequentialPoints,
434
+ actions,
435
+ actionsInHeader = false,
436
+ ...rest
437
+ } = props;
438
+ const isMobile = (0, import_seeds_react_hooks.useIsMobile)();
439
+ const effectiveDirection = isMobile ? "bottom" : direction;
440
+ const effectiveSnapPoints = effectiveDirection === "bottom" ? snapPoints : void 0;
441
+ const hasSnapPoints = !!effectiveSnapPoints && effectiveSnapPoints.length > 0;
442
+ const hasActionRail = isMobile && !actionsInHeader && !!actions && actions.length > 0;
443
+ const [isLocked, setIsLocked] = React3.useState(false);
444
+ const ariaLabelledBy = rest["aria-labelledby"];
445
+ const ariaLabel = rest["aria-label"];
446
+ React3.useEffect(() => {
447
+ if (process.env.NODE_ENV !== "production") {
448
+ if (!ariaLabelledBy && !ariaLabel) {
449
+ console.warn(
450
+ "[Drawer] Missing accessible name. Add aria-labelledby pointing to your Drawer.Header id, or aria-label, so screen readers can identify the dialog."
451
+ );
167
452
  }
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
453
  }
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
- );
454
+ }, [ariaLabelledBy, ariaLabel]);
455
+ const isLockedRef = React3.useRef(isLocked);
456
+ isLockedRef.current = isLocked;
457
+ const prevChildrenRef = React3.useRef(null);
458
+ if (open) {
459
+ prevChildrenRef.current = children;
460
+ }
461
+ const renderedChildren = open ? children : prevChildrenRef.current;
462
+ const rootProps = {
463
+ open,
464
+ onOpenChange: (isOpen, eventDetails) => {
465
+ if (!isOpen) {
466
+ if (isLockedRef.current && eventDetails.reason === "escape-key") {
467
+ return;
468
+ }
469
+ onClose();
193
470
  }
194
- };
195
- }, [
196
- onClose,
197
- disableCloseOnClickOutside,
198
- closeTargets,
199
- ref,
471
+ },
472
+ disablePointerDismissal: disableCloseOnClickOutside,
473
+ swipeDirection: effectiveDirection === "bottom" ? "down" : effectiveDirection,
474
+ modal,
475
+ snapPoints: effectiveSnapPoints,
476
+ defaultSnapPoint,
477
+ snapPoint,
478
+ onSnapPointChange: onSnapPointChange ? (sp) => onSnapPointChange(sp) : void 0,
479
+ snapToSequentialPoints
480
+ };
481
+ return {
482
+ effectiveDirection,
483
+ hasSnapPoints,
484
+ hasActionRail,
200
485
  isLocked,
201
- isOpen
202
- ]);
486
+ setIsLocked,
487
+ renderedChildren,
488
+ rootProps,
489
+ rest,
490
+ width,
491
+ offset,
492
+ zIndex,
493
+ id,
494
+ open,
495
+ closeButtonLabel,
496
+ onClose,
497
+ actions
498
+ };
203
499
  };
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,
500
+ var DrawerPopupContents = ({ shell }) => {
501
+ const {
502
+ hasActionRail,
503
+ closeButtonLabel,
222
504
  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,
505
+ actions,
506
+ isLocked,
507
+ setIsLocked,
508
+ renderedChildren
509
+ } = shell;
510
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
511
+ hasActionRail && actions && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(ActionRail, { isMobile: true, "aria-label": "Drawer quick actions", children: [
512
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
513
+ ActionRailButton,
236
514
  {
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
515
+ "aria-label": closeButtonLabel,
516
+ onClick: onClose,
517
+ "data-qa-drawer-rail-close": true,
518
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_seeds_react_icon.default, { "aria-hidden": true, name: "x-outline" })
250
519
  }
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,
520
+ ),
521
+ actions.map((action, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
522
+ ActionRailButton,
282
523
  {
283
- isOpen,
284
- id,
285
- offset,
286
- direction,
287
- disableCloseOnClickOutside,
524
+ "aria-label": action["aria-label"],
525
+ onClick: action.onClick,
526
+ disabled: action.disabled,
527
+ children: action.iconName && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_seeds_react_icon.default, { "aria-hidden": true, name: action.iconName })
528
+ },
529
+ i
530
+ ))
531
+ ] }),
532
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_seeds_react_portal.DisablePortalToBodyContext.Provider, { value: true, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
533
+ DrawerContext.Provider,
534
+ {
535
+ value: {
288
536
  onClose,
289
- zIndex,
290
- closeTargets,
291
- width,
292
- "data-qa-drawer": id || "",
293
- "data-qa-drawer-isopen": isOpen,
294
- ...rest,
295
- children
537
+ closeButtonLabel,
538
+ isLocked,
539
+ setIsLocked,
540
+ hasActionRail
541
+ },
542
+ children: renderedChildren
543
+ }
544
+ ) })
545
+ ] });
546
+ };
547
+
548
+ // src/DrawerStyled.tsx
549
+ var import_jsx_runtime4 = require("react/jsx-runtime");
550
+ var DrawerStyled = (props) => {
551
+ const shell = useDrawerShell(props);
552
+ const {
553
+ effectiveDirection,
554
+ hasSnapPoints,
555
+ hasActionRail,
556
+ rootProps,
557
+ rest,
558
+ width,
559
+ offset,
560
+ zIndex,
561
+ id,
562
+ open
563
+ } = shell;
564
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_drawer3.Drawer.Root, { ...rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_drawer3.Drawer.Portal, { children: [
565
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StyledBackdrop, { "data-testid": "drawer-backdrop" }),
566
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StyledViewport, { $hasSnapPoints: hasSnapPoints, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
567
+ StyledPopup,
568
+ {
569
+ initialFocus: true,
570
+ $width: width,
571
+ $offset: offset,
572
+ $direction: effectiveDirection,
573
+ $zIndex: zIndex,
574
+ $hasSnapPoints: hasSnapPoints,
575
+ $hasActionRail: hasActionRail,
576
+ "data-qa-drawer": id,
577
+ "data-qa-drawer-isopen": open,
578
+ ...rest,
579
+ children: [
580
+ effectiveDirection === "bottom" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DragHandle, { "aria-hidden": true }),
581
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DrawerPopupContents, { shell })
582
+ ]
583
+ }
584
+ ) })
585
+ ] }) });
586
+ };
587
+ var DrawerStyled_default = DrawerStyled;
588
+
589
+ // src/DrawerTailwind.tsx
590
+ var import_drawer4 = require("@base-ui/react/drawer");
591
+ var import_jsx_runtime5 = require("react/jsx-runtime");
592
+ function cn(...inputs) {
593
+ const classes = [];
594
+ for (const input of inputs) {
595
+ if (!input) continue;
596
+ if (typeof input === "string") {
597
+ classes.push(input);
598
+ } else if (typeof input === "object") {
599
+ for (const [key, value] of Object.entries(input)) {
600
+ if (value) {
601
+ classes.push(key);
296
602
  }
297
- )
603
+ }
298
604
  }
299
- ) });
605
+ }
606
+ return classes.join(" ");
607
+ }
608
+ var DrawerTailwind = (props) => {
609
+ const shell = useDrawerShell(props);
610
+ const {
611
+ effectiveDirection,
612
+ hasSnapPoints,
613
+ hasActionRail,
614
+ rootProps,
615
+ rest,
616
+ width,
617
+ offset,
618
+ zIndex,
619
+ id,
620
+ open
621
+ } = shell;
622
+ const { className, style, ...domRest } = rest;
623
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_drawer4.Drawer.Root, { ...rootProps, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_drawer4.Drawer.Portal, { children: [
624
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
625
+ import_drawer4.Drawer.Backdrop,
626
+ {
627
+ "data-testid": "drawer-backdrop",
628
+ className: "seeds-drawer-backdrop"
629
+ }
630
+ ),
631
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
632
+ import_drawer4.Drawer.Viewport,
633
+ {
634
+ className: cn("seeds-drawer-viewport", {
635
+ "seeds-drawer-viewport--snap": hasSnapPoints
636
+ }),
637
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
638
+ import_drawer4.Drawer.Popup,
639
+ {
640
+ initialFocus: true,
641
+ "data-qa-drawer": id,
642
+ "data-qa-drawer-isopen": open,
643
+ ...domRest,
644
+ className: cn(
645
+ "seeds-drawer-popup",
646
+ effectiveDirection === "bottom" ? "seeds-drawer-popup--bottom" : "seeds-drawer-popup--side",
647
+ effectiveDirection === "right" && "seeds-drawer-popup--right",
648
+ effectiveDirection === "left" && "seeds-drawer-popup--left",
649
+ {
650
+ "seeds-drawer-popup--snap": hasSnapPoints,
651
+ "seeds-drawer-popup--action-rail": hasActionRail
652
+ },
653
+ // Consumer className folded in last so it appends to (not
654
+ // replaces) the component classes.
655
+ className
656
+ ),
657
+ style: {
658
+ // z-index is always inline (runtime numeric, mirrors styles.ts).
659
+ zIndex,
660
+ // Side drawers carry their width/offset as direct inline styles —
661
+ // runtime numerics, and required for the jsdom `toHaveStyle`
662
+ // assertions. Bottom drawers derive width/position from the class.
663
+ ...effectiveDirection === "right" ? { width: `${width}px`, right: `${offset}px` } : effectiveDirection === "left" ? { width: `${width}px`, left: `${offset}px` } : {},
664
+ // Consumer style folded in last so it wins over the computed
665
+ // geometry, matching the styled-components inline-style semantics.
666
+ ...style
667
+ },
668
+ children: [
669
+ effectiveDirection === "bottom" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { "aria-hidden": true, className: "seeds-drawer-drag-handle" }),
670
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(DrawerPopupContents, { shell })
671
+ ]
672
+ }
673
+ )
674
+ }
675
+ )
676
+ ] }) });
300
677
  };
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;
678
+ var DrawerTailwind_default = DrawerTailwind;
679
+
680
+ // src/DrawerHybrid.tsx
681
+ var import_jsx_runtime6 = require("react/jsx-runtime");
682
+ var DRAWER_OWN_PROPS = [
683
+ "children",
684
+ "closeButtonLabel",
685
+ "direction",
686
+ "disableCloseOnClickOutside",
687
+ "id",
688
+ "modal",
689
+ "open",
690
+ "offset",
691
+ "onClose",
692
+ "zIndex",
693
+ "width",
694
+ "snapPoints",
695
+ "defaultSnapPoint",
696
+ "snapPoint",
697
+ "onSnapPointChange",
698
+ "snapToSequentialPoints",
699
+ "actions",
700
+ "actionsInHeader"
701
+ ];
702
+ var DrawerHybrid = (props) => {
703
+ const rest = {};
704
+ for (const key of Object.keys(props)) {
705
+ if (!DRAWER_OWN_PROPS.includes(key)) {
706
+ rest[key] = props[key];
707
+ }
708
+ }
709
+ if ((0, import_seeds_react_system_props2.hasStyledProps)(rest)) {
710
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DrawerStyled_default, { ...props });
711
+ }
712
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DrawerTailwind_default, { ...props });
713
+ };
714
+ DrawerHybrid.Header = DrawerHeader;
715
+ DrawerHybrid.Content = DrawerContent;
716
+ DrawerHybrid.CloseButton = DrawerCloseButton;
717
+ var DrawerHybrid_default = DrawerHybrid;
718
+
719
+ // src/Drawer.tsx
720
+ var Drawer_default = DrawerHybrid_default;
308
721
 
309
722
  // src/DrawerTypes.ts
310
- var React2 = require("react");
723
+ var React4 = require("react");
311
724
 
312
725
  // src/index.ts
313
726
  var index_default = Drawer_default;
314
727
  // Annotate the CommonJS export names for ESM import in node:
315
728
  0 && (module.exports = {
729
+ ActionRail,
730
+ ActionRailButton,
316
731
  Drawer,
317
- DrawerContext
732
+ DrawerContext,
733
+ RAIL_BUTTON_HEIGHT,
734
+ RAIL_BUTTON_SIZE,
735
+ RAIL_GAP,
736
+ RAIL_OFFSET
318
737
  });
319
738
  //# sourceMappingURL=index.js.map