@sproutsocial/seeds-react-modal 1.0.5 → 1.1.0

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.
@@ -1,8 +1,9 @@
1
- import { useState } from "react";
1
+ import React, { useState } from "react";
2
2
  import type { Meta, StoryObj } from "@storybook/react";
3
3
  import { Box } from "@sproutsocial/seeds-react-box";
4
4
  import { Button } from "@sproutsocial/seeds-react-button";
5
- import { Text } from "@sproutsocial/seeds-react-text";
5
+ import Text from "@sproutsocial/seeds-react-text";
6
+ import { FormField } from "@sproutsocial/seeds-react-form-field";
6
7
  import { Modal, ModalHeader, ModalFooter, ModalContent, ModalClose } from "./";
7
8
 
8
9
  const meta: Meta<typeof Modal> = {
@@ -280,3 +281,273 @@ export const ControlledState: Story = {
280
281
  );
281
282
  },
282
283
  };
284
+
285
+ export const DraggableWithFormInteraction: Story = {
286
+ render: () => {
287
+ const [formData, setFormData] = useState({
288
+ firstName: "",
289
+ lastName: "",
290
+ email: "",
291
+ company: "",
292
+ });
293
+
294
+ const [submittedData, setSubmittedData] = useState<typeof formData | null>(
295
+ null
296
+ );
297
+
298
+ const handleSubmit = (e: React.FormEvent) => {
299
+ e.preventDefault();
300
+ setSubmittedData(formData);
301
+ };
302
+
303
+ const handleInputChange = (field: keyof typeof formData, value: string) => {
304
+ setFormData((prev) => ({ ...prev, [field]: value }));
305
+ };
306
+
307
+ return (
308
+ <Box p={500}>
309
+ <Box mb={400}>
310
+ <Text fontSize={500} fontWeight="bold">
311
+ Test Draggable Modal with Form Interaction
312
+ </Text>
313
+ <Text fontSize={200} color="text.subtle" mt={200}>
314
+ Open the draggable modal, move it to the side, then try interacting
315
+ with the form below. You should be able to fill out the form while
316
+ the modal is open and moved aside.
317
+ </Text>
318
+ </Box>
319
+
320
+ <Box
321
+ p={500}
322
+ bg="neutral.100"
323
+ borderRadius="8px"
324
+ border="2px solid"
325
+ borderColor="neutral.400"
326
+ >
327
+ <Text fontSize={400} fontWeight="bold" mb={300}>
328
+ Contact Form
329
+ </Text>
330
+ <form onSubmit={handleSubmit}>
331
+ <Box display="flex" flexDirection="column" gap={300}>
332
+ <FormField
333
+ label="First Name"
334
+ error={null}
335
+ helperText={null}
336
+ id=""
337
+ qa={{}}
338
+ required={false}
339
+ >
340
+ {(props) => (
341
+ <input
342
+ {...props}
343
+ type="text"
344
+ value={formData.firstName}
345
+ onChange={(e) =>
346
+ handleInputChange("firstName", e.target.value)
347
+ }
348
+ style={{
349
+ padding: "8px 12px",
350
+ borderRadius: "4px",
351
+ border: "1px solid #ccc",
352
+ width: "100%",
353
+ fontSize: "14px",
354
+ }}
355
+ />
356
+ )}
357
+ </FormField>
358
+
359
+ <FormField
360
+ label="Last Name"
361
+ error={null}
362
+ helperText={null}
363
+ id=""
364
+ qa={{}}
365
+ required={false}
366
+ >
367
+ {(props) => (
368
+ <input
369
+ {...props}
370
+ type="text"
371
+ value={formData.lastName}
372
+ onChange={(e) =>
373
+ handleInputChange("lastName", e.target.value)
374
+ }
375
+ style={{
376
+ padding: "8px 12px",
377
+ borderRadius: "4px",
378
+ border: "1px solid #ccc",
379
+ width: "100%",
380
+ fontSize: "14px",
381
+ }}
382
+ />
383
+ )}
384
+ </FormField>
385
+
386
+ <FormField
387
+ label="Email"
388
+ error={null}
389
+ helperText={null}
390
+ id=""
391
+ qa={{}}
392
+ required={false}
393
+ >
394
+ {(props) => (
395
+ <input
396
+ {...props}
397
+ type="email"
398
+ value={formData.email}
399
+ onChange={(e) => handleInputChange("email", e.target.value)}
400
+ style={{
401
+ padding: "8px 12px",
402
+ borderRadius: "4px",
403
+ border: "1px solid #ccc",
404
+ width: "100%",
405
+ fontSize: "14px",
406
+ }}
407
+ />
408
+ )}
409
+ </FormField>
410
+
411
+ <FormField
412
+ label="Company"
413
+ error={null}
414
+ helperText={null}
415
+ id=""
416
+ qa={{}}
417
+ required={false}
418
+ >
419
+ {(props) => (
420
+ <input
421
+ {...props}
422
+ type="text"
423
+ value={formData.company}
424
+ onChange={(e) =>
425
+ handleInputChange("company", e.target.value)
426
+ }
427
+ style={{
428
+ padding: "8px 12px",
429
+ borderRadius: "4px",
430
+ border: "1px solid #ccc",
431
+ width: "100%",
432
+ fontSize: "14px",
433
+ }}
434
+ />
435
+ )}
436
+ </FormField>
437
+
438
+ <Box display="flex" gap={300} mt={200}>
439
+ <Button type="submit" appearance="primary">
440
+ Submit Form
441
+ </Button>
442
+
443
+ <Modal
444
+ aria-label="Reference Modal"
445
+ size="medium"
446
+ draggable={true}
447
+ showOverlay={true}
448
+ modalTrigger={
449
+ <Button appearance="secondary">Open Reference Modal</Button>
450
+ }
451
+ >
452
+ <ModalContent>
453
+ <ModalHeader
454
+ title="Reference Information"
455
+ subtitle="Drag this modal by the header to move it around"
456
+ />
457
+ <Box p={300}>
458
+ <Text fontSize={400} fontWeight="bold" mb={200}>
459
+ Instructions for filling out the form:
460
+ </Text>
461
+ <Box as="ul" pl={400}>
462
+ <li>
463
+ <Text mb={100}>
464
+ First Name: Enter your given name
465
+ </Text>
466
+ </li>
467
+ <li>
468
+ <Text mb={100}>
469
+ Last Name: Enter your family name
470
+ </Text>
471
+ </li>
472
+ <li>
473
+ <Text mb={100}>
474
+ Email: Use your work email address
475
+ </Text>
476
+ </li>
477
+ <li>
478
+ <Text mb={100}>
479
+ Company: Enter your organization name
480
+ </Text>
481
+ </li>
482
+ </Box>
483
+
484
+ <Box
485
+ mt={400}
486
+ p={300}
487
+ bg="blue.200"
488
+ borderRadius="6px"
489
+ border="1px solid"
490
+ borderColor="blue.400"
491
+ >
492
+ <Text fontWeight="bold" mb={100}>
493
+ Try this:
494
+ </Text>
495
+ <Text fontSize={100}>
496
+ 1. Grab this modal by the header (you'll see a grab
497
+ cursor)
498
+ <br />
499
+ 2. Drag it to the side of the screen
500
+ <br />
501
+ 3. Click on the form inputs in the background
502
+ <br />
503
+ 4. Notice you can interact with the form while the
504
+ modal is open!
505
+ </Text>
506
+ </Box>
507
+ </Box>
508
+ <ModalFooter>
509
+ <Box display="flex" justifyContent="flex-end">
510
+ <ModalClose asChild>
511
+ <Button appearance="primary">Got it!</Button>
512
+ </ModalClose>
513
+ </Box>
514
+ </ModalFooter>
515
+ </ModalContent>
516
+ </Modal>
517
+ </Box>
518
+ </Box>
519
+ </form>
520
+
521
+ {submittedData && (
522
+ <Box
523
+ mt={400}
524
+ p={400}
525
+ bg="green.200"
526
+ borderRadius="6px"
527
+ border="2px solid"
528
+ borderColor="green.500"
529
+ >
530
+ <Text fontSize={400} fontWeight="bold" mb={200}>
531
+ Form Submitted!
532
+ </Text>
533
+ <Box as="ul" pl={400}>
534
+ <li>
535
+ <Text>First Name: {submittedData.firstName}</Text>
536
+ </li>
537
+ <li>
538
+ <Text>Last Name: {submittedData.lastName}</Text>
539
+ </li>
540
+ <li>
541
+ <Text>Email: {submittedData.email}</Text>
542
+ </li>
543
+ <li>
544
+ <Text>Company: {submittedData.company}</Text>
545
+ </li>
546
+ </Box>
547
+ </Box>
548
+ )}
549
+ </Box>
550
+ </Box>
551
+ );
552
+ },
553
+ };
@@ -26,8 +26,25 @@ interface DraggableModalContentProps {
26
26
  dataAttributes: Record<string, string>;
27
27
  draggable?: boolean;
28
28
  rest: any;
29
+ railProps?: Partial<TypeModalV2Props["railProps"]>;
29
30
  }
30
31
 
32
+ // Context to share drag state between modal content and header
33
+ interface DragContextValue {
34
+ position: { x: number; y: number };
35
+ isDragging: boolean;
36
+ onHeaderMouseDown: (e: React.MouseEvent) => void;
37
+ contentRef: React.RefObject<HTMLDivElement>;
38
+ draggable: boolean;
39
+ }
40
+
41
+ const DragContext = React.createContext<DragContextValue | null>(null);
42
+
43
+ export const useDragContext = () => {
44
+ const context = React.useContext(DragContext);
45
+ return context;
46
+ };
47
+
31
48
  const DraggableModalContent: React.FC<DraggableModalContentProps> = ({
32
49
  children,
33
50
  computedWidth,
@@ -37,16 +54,26 @@ const DraggableModalContent: React.FC<DraggableModalContentProps> = ({
37
54
  dataAttributes,
38
55
  draggable,
39
56
  rest,
57
+ railProps,
40
58
  }) => {
41
59
  const [position, setPosition] = React.useState({ x: 0, y: 0 });
42
60
  const [isDragging, setIsDragging] = React.useState(false);
43
61
  const contentRef = React.useRef<HTMLDivElement>(null);
44
62
 
45
- const handleMouseDown = React.useCallback(
63
+ // Calculate rail dimensions for boundary constraints
64
+ const railSize = railProps?.size ?? 44;
65
+ const railOffset = railProps?.offset ?? 12;
66
+ const railSide = railProps?.side ?? "right";
67
+
68
+ // Total extra space needed on the side with the rail
69
+ // (only when viewport is wider than 400px breakpoint)
70
+ const railExtraSpace = railSize + railOffset;
71
+
72
+ const handleHeaderMouseDown = React.useCallback(
46
73
  (e: React.MouseEvent) => {
47
74
  if (!draggable) return;
48
75
 
49
- // Only allow dragging from certain areas (not interactive elements)
76
+ // Only allow dragging from header (not interactive elements)
50
77
  const target = e.target as HTMLElement;
51
78
  if (
52
79
  target.tagName === "BUTTON" ||
@@ -73,13 +100,33 @@ const DraggableModalContent: React.FC<DraggableModalContentProps> = ({
73
100
  const newX = e.clientX - offsetX;
74
101
  const newY = e.clientY - offsetY;
75
102
 
76
- // Constrain to viewport bounds (keeping modal fully visible)
103
+ // Determine if rail is on the side (viewport > 400px) or above (viewport <= 400px)
104
+ const isRailOnSide = window.innerWidth > 400;
105
+
106
+ // Constrain to viewport bounds (keeping modal AND rail fully visible)
77
107
  const modalWidth = rect.width;
78
108
  const modalHeight = rect.height;
79
- const maxX = window.innerWidth - modalWidth;
80
- const minX = 0;
81
- const maxY = window.innerHeight - modalHeight;
82
- const minY = 0;
109
+
110
+ // Adjust boundaries to account for rail position
111
+ let maxX = window.innerWidth - modalWidth;
112
+ let minX = 0;
113
+ let maxY = window.innerHeight - modalHeight;
114
+ let minY = 0;
115
+
116
+ if (isRailOnSide) {
117
+ // Rail is positioned on the side of the modal
118
+ if (railSide === "right") {
119
+ // Rail is on the right, so reduce maxX to prevent rail from going off-screen
120
+ maxX = window.innerWidth - modalWidth - railExtraSpace;
121
+ } else {
122
+ // Rail is on the left, so increase minX to prevent rail from going off-screen
123
+ minX = railExtraSpace;
124
+ }
125
+ } else {
126
+ // Rail is positioned above the modal (viewport <= 400px)
127
+ // Account for rail height + offset at the top
128
+ minY = railSize + railOffset;
129
+ }
83
130
 
84
131
  const constrainedX = Math.max(minX, Math.min(maxX, newX));
85
132
  const constrainedY = Math.max(minY, Math.min(maxY, newY));
@@ -103,32 +150,58 @@ const DraggableModalContent: React.FC<DraggableModalContentProps> = ({
103
150
  document.addEventListener("mousemove", handleMouseMove);
104
151
  document.addEventListener("mouseup", handleMouseUp);
105
152
  },
153
+ [draggable, railSide, railExtraSpace]
154
+ );
155
+
156
+ const dragContextValue = React.useMemo<DragContextValue>(
157
+ () => ({
158
+ position,
159
+ isDragging,
160
+ onHeaderMouseDown: handleHeaderMouseDown,
161
+ contentRef,
162
+ draggable: draggable ?? false,
163
+ }),
164
+ [position, isDragging, handleHeaderMouseDown, draggable]
165
+ );
166
+
167
+ // Prevent modal from closing on outside interaction when draggable
168
+ const handleInteractOutside = React.useCallback(
169
+ (e: Event) => {
170
+ if (draggable) {
171
+ e.preventDefault();
172
+ }
173
+ },
106
174
  [draggable]
107
175
  );
108
176
 
109
177
  return (
110
- <StyledContent
111
- ref={contentRef}
112
- width={computedWidth}
113
- bg={bg}
114
- zIndex={computedZIndex}
115
- aria-label={label}
116
- draggable={draggable}
117
- isDragging={isDragging}
118
- onMouseDown={handleMouseDown}
119
- style={
120
- draggable
121
- ? {
122
- transform: `translate(calc(-50% + ${position.x}px), calc(-50% + ${position.y}px))`,
123
- transition: isDragging ? "none" : undefined,
124
- }
125
- : undefined
126
- }
127
- {...dataAttributes}
128
- {...rest}
129
- >
130
- {children}
131
- </StyledContent>
178
+ <DragContext.Provider value={draggable ? dragContextValue : null}>
179
+ <StyledContent
180
+ ref={contentRef}
181
+ width={computedWidth}
182
+ bg={bg}
183
+ zIndex={computedZIndex}
184
+ aria-label={label}
185
+ draggable={draggable}
186
+ isDragging={isDragging}
187
+ railSize={railSize}
188
+ railOffset={railOffset}
189
+ railSide={railSide}
190
+ onInteractOutside={handleInteractOutside}
191
+ style={
192
+ draggable
193
+ ? {
194
+ transform: `translate(calc(-50% + ${position.x}px), calc(-50% + ${position.y}px))`,
195
+ transition: isDragging ? "none" : undefined,
196
+ }
197
+ : undefined
198
+ }
199
+ {...dataAttributes}
200
+ {...rest}
201
+ >
202
+ {children}
203
+ </StyledContent>
204
+ </DragContext.Provider>
132
205
  );
133
206
  };
134
207
 
@@ -233,8 +306,14 @@ const Modal = (props: TypeModalV2Props) => {
233
306
  props.onOpenChange = handleOpenChange;
234
307
  }
235
308
 
309
+ // When draggable, prevent modal from closing on outside interaction
310
+ // This allows users to interact with background content
311
+ if (draggable) {
312
+ props.modal = false;
313
+ }
314
+
236
315
  return props;
237
- }, [open, defaultOpen, handleOpenChange, onOpenChange]);
316
+ }, [open, defaultOpen, handleOpenChange, onOpenChange, draggable]);
238
317
 
239
318
  // Handle trigger - use modalTrigger prop if provided, otherwise look for ModalTrigger children (backward compatibility)
240
319
  const triggers: React.ReactNode[] = [];
@@ -266,7 +345,9 @@ const Modal = (props: TypeModalV2Props) => {
266
345
  {triggers}
267
346
 
268
347
  <Dialog.Portal>
269
- {showOverlay && <StyledOverlay zIndex={computedZIndex} />}
348
+ {showOverlay && (
349
+ <StyledOverlay zIndex={computedZIndex} allowInteraction={draggable} />
350
+ )}
270
351
  <DraggableModalContent
271
352
  computedWidth={computedWidth}
272
353
  bg={bg}
@@ -275,6 +356,7 @@ const Modal = (props: TypeModalV2Props) => {
275
356
  dataAttributes={dataAttributes}
276
357
  draggable={draggable}
277
358
  rest={rest}
359
+ railProps={railProps}
278
360
  >
279
361
  {/* Floating actions rail - always show a close by default */}
280
362
  <ModalRail {...railProps}>
@@ -11,6 +11,7 @@ import {
11
11
 
12
12
  interface StyledOverlayProps extends TypeContainerProps {
13
13
  zIndex?: number;
14
+ allowInteraction?: boolean;
14
15
  }
15
16
 
16
17
  export const StyledOverlay = styled(Dialog.Overlay)<StyledOverlayProps>`
@@ -27,6 +28,13 @@ export const StyledOverlay = styled(Dialog.Overlay)<StyledOverlayProps>`
27
28
  z-index: ${(props) =>
28
29
  props.zIndex ? props.zIndex + DEFAULT_OVERLAY_Z_INDEX_OFFSET : 999};
29
30
 
31
+ /* Allow clicking through overlay when modal is draggable */
32
+ ${(props) =>
33
+ props.allowInteraction &&
34
+ `
35
+ pointer-events: none;
36
+ `}
37
+
30
38
  ${zIndex}
31
39
 
32
40
  &[data-state="open"] {
@@ -41,6 +49,9 @@ interface StyledContentProps extends TypeContainerProps {
41
49
  zIndex?: number;
42
50
  isDragging?: boolean;
43
51
  draggable?: boolean;
52
+ railSize?: number;
53
+ railOffset?: number;
54
+ railSide?: "right" | "left";
44
55
  }
45
56
 
46
57
  export const StyledContent = styled(Dialog.Content)<StyledContentProps>`
@@ -64,17 +75,23 @@ export const StyledContent = styled(Dialog.Content)<StyledContentProps>`
64
75
  filter: blur(0);
65
76
  color: ${(props) => props.theme.colors.text.body};
66
77
  outline: none;
67
- max-width: calc(100vw - ${BODY_PADDING});
78
+ max-width: ${(props) => {
79
+ // Calculate extra space needed for the rail when it's on the side (viewport > 400px)
80
+ const railSize = props.railSize ?? 44;
81
+ const railOffset = props.railOffset ?? 12;
82
+ const railExtraSpace = railSize + railOffset;
83
+
84
+ // Account for rail space when positioned on the side
85
+ // At viewport <= 400px, rail is above modal, so no horizontal space needed
86
+ return `calc(100vw - ${BODY_PADDING} - ${railExtraSpace}px)`;
87
+ }};
68
88
  max-height: calc(100vh - ${BODY_PADDING});
69
89
  z-index: ${(props) => props.zIndex || 1000};
70
90
 
71
- /* Draggable styling */
72
- ${(props) =>
73
- props.draggable &&
74
- `
75
- cursor: ${props.isDragging ? "grabbing" : "grab"};
76
- user-select: none;
77
- `}
91
+ /* When viewport is <= 400px, rail is above modal, so restore full width */
92
+ @media (max-width: 400px) {
93
+ max-width: calc(100vw - ${BODY_PADDING});
94
+ }
78
95
 
79
96
  @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
80
97
  height: calc(100vh - ${BODY_PADDING});
@@ -120,7 +137,12 @@ export const Content = styled(Box)`
120
137
  }
121
138
  `;
122
139
 
123
- export const Header = styled(Box)`
140
+ interface HeaderProps {
141
+ draggable?: boolean;
142
+ isDragging?: boolean;
143
+ }
144
+
145
+ export const Header = styled(Box)<HeaderProps>`
124
146
  font-family: ${(props) => props.theme.fontFamily};
125
147
  padding: ${(props) => props.theme.space[400]}
126
148
  ${(props) => props.theme.space[300]};
@@ -128,6 +150,14 @@ export const Header = styled(Box)`
128
150
  align-items: center;
129
151
  justify-content: space-between;
130
152
  flex: 0 0 auto;
153
+
154
+ /* Draggable cursor styling */
155
+ ${(props) =>
156
+ props.draggable &&
157
+ `
158
+ cursor: ${props.isDragging ? "grabbing" : "grab"};
159
+ user-select: none;
160
+ `}
131
161
  `;
132
162
 
133
163
  export const Footer = styled(Box)`
@@ -42,9 +42,6 @@ export interface TypeModalV2HeaderProps extends TypeBoxProps {
42
42
  /** Passing children will override the default modal header */
43
43
  children?: React.ReactNode;
44
44
 
45
- /** If you're rendering a custom header, you can use this prop to add a bottom border */
46
- bordered?: boolean;
47
-
48
45
  /** Additional props for the Dialog.Title when title is provided */
49
46
  titleProps?: Omit<
50
47
  React.ComponentPropsWithoutRef<typeof Dialog.Title>,
@@ -137,7 +134,6 @@ export type TypeModalRailProps = {
137
134
  offset?: number; // px from card edge; default: 12
138
135
  gap?: number; // space between buttons; default: 12
139
136
  size?: number; // button square size; default: 44
140
- collapseAt?: number; // px viewport width to pull rail inside; default: 640
141
137
  children?: React.ReactNode;
142
138
  };
143
139
 
@@ -5,21 +5,29 @@ import Text from "@sproutsocial/seeds-react-text";
5
5
  import { Header } from "../ModalV2Styles";
6
6
  import { ModalCloseButton } from "./ModalCloseButton";
7
7
  import type { TypeModalV2HeaderProps } from "../ModalV2Types";
8
+ import { useDragContext } from "../ModalV2";
8
9
 
9
10
  export const ModalHeader = (props: TypeModalV2HeaderProps) => {
10
11
  const {
11
12
  title,
12
13
  subtitle,
13
14
  children,
14
- bordered,
15
15
  titleProps = {},
16
16
  subtitleProps = {},
17
17
  showInlineClose,
18
18
  ...rest
19
19
  } = props;
20
20
 
21
+ const dragContext = useDragContext();
22
+ const isDraggable = dragContext?.draggable ?? false;
23
+
21
24
  return (
22
- <Header {...rest}>
25
+ <Header
26
+ {...rest}
27
+ onMouseDown={isDraggable ? dragContext?.onHeaderMouseDown : undefined}
28
+ draggable={isDraggable}
29
+ isDragging={dragContext?.isDragging}
30
+ >
23
31
  {children ? (
24
32
  children
25
33
  ) : (
@@ -2,7 +2,7 @@
2
2
  import * as React from "react";
3
3
  import * as Dialog from "@radix-ui/react-dialog";
4
4
  import styled from "styled-components";
5
- import Icon from "@sproutsocial/seeds-react-icon"; // adjust import if path differs
5
+ import Icon from "@sproutsocial/seeds-react-icon";
6
6
  import type { TypeModalRailProps, TypeModalActionProps } from "../ModalV2Types";
7
7
 
8
8
  // --- styled wrappers ---
@@ -11,7 +11,6 @@ const Rail = styled.div<{
11
11
  offset: number;
12
12
  gap: number;
13
13
  size: number;
14
- collapseAt: number;
15
14
  }>`
16
15
  position: absolute;
17
16
  top: ${(p) => p.offset}px;
@@ -19,14 +18,19 @@ const Rail = styled.div<{
19
18
  p.side === "right"
20
19
  ? `right: calc(-1 * (${p.size}px + ${p.offset}px));`
21
20
  : `left: calc(-1 * (${p.size}px + ${p.offset}px));`}
22
- display: grid;
23
- grid-auto-flow: row;
21
+ display: flex;
22
+ flex-direction: column;
24
23
  gap: ${(p) => p.gap}px;
25
24
  z-index: 1;
26
25
 
27
- @media (max-width: ${(p) => p.collapseAt}px) {
28
- ${(p) =>
29
- p.side === "right" ? `right: ${p.offset}px;` : `left: ${p.offset}px;`}
26
+ @media (max-width: 400px) {
27
+ /* Move rail above the modal on the right side */
28
+ top: auto;
29
+ bottom: calc(100% + ${(p) => p.offset}px);
30
+ right: ${(p) => p.offset}px;
31
+ left: auto;
32
+ /* Change to horizontal layout with reversed order */
33
+ flex-direction: row-reverse;
30
34
  }
31
35
  `;
32
36
 
@@ -68,7 +72,6 @@ export const ModalRail: React.FC<TypeModalRailProps> = ({
68
72
  offset = 12,
69
73
  gap = 12,
70
74
  size = 44,
71
- collapseAt = 640,
72
75
  children,
73
76
  }) => {
74
77
  return (
@@ -78,7 +81,6 @@ export const ModalRail: React.FC<TypeModalRailProps> = ({
78
81
  offset={offset}
79
82
  gap={gap}
80
83
  size={size}
81
- collapseAt={collapseAt}
82
84
  aria-label="Modal quick actions"
83
85
  >
84
86
  {React.Children.map(children, (child) =>