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