@beydesign/storybook 0.2.29 → 0.3.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.
Files changed (88) hide show
  1. package/dist/index.d.ts +0 -2
  2. package/dist/index.js +0 -2
  3. package/dist/stories/Form/Dropdown.js +38 -31
  4. package/dist/stories/Form/FileUpload/FileUpload.js +26 -16
  5. package/dist/stories/Form/FileUpload/FileUploading.js +35 -25
  6. package/dist/stories/Form/FileUpload/UploadedFile.js +15 -4
  7. package/dist/stories/Form/FileUpload/UploadedFile.stories.js +21 -26
  8. package/dist/stories/Form/types.d.ts +5 -9
  9. package/dist/stories/{Drawer → Navigation/Drawer}/Drawer.d.ts +4 -0
  10. package/dist/stories/Navigation/Drawer/Drawer.js +81 -0
  11. package/dist/stories/{Drawer → Navigation/Drawer}/Drawer.stories.js +1 -1
  12. package/dist/stories/Navigation/Drawer/DrawerBody.d.ts +9 -0
  13. package/dist/stories/Navigation/Drawer/DrawerBody.js +73 -0
  14. package/dist/stories/{Drawer → Navigation/Drawer}/DrawerCreditsCard.js +3 -3
  15. package/dist/stories/{Drawer → Navigation/Drawer}/DrawerCreditsCard.stories.js +1 -1
  16. package/dist/stories/{Drawer → Navigation/Drawer}/DrawerCreditsCardSkeleton.js +1 -1
  17. package/dist/stories/{Drawer → Navigation/Drawer}/DrawerCreditsCardSkeleton.stories.js +1 -1
  18. package/dist/stories/{Drawer → Navigation/Drawer}/DrawerNavItem.d.ts +1 -1
  19. package/dist/stories/Navigation/Drawer/DrawerNavItem.js +22 -0
  20. package/dist/stories/{Drawer → Navigation/Drawer}/DrawerNavItem.stories.js +1 -1
  21. package/dist/stories/{Drawer → Navigation/Drawer}/DrawerProfile.js +3 -3
  22. package/dist/stories/{Drawer → Navigation/Drawer}/DrawerProfile.stories.js +1 -1
  23. package/dist/stories/{Drawer → Navigation/Drawer}/DrawerProfileSkeleton.js +1 -1
  24. package/dist/stories/{Drawer → Navigation/Drawer}/DrawerProfileSkeleton.stories.js +1 -1
  25. package/dist/stories/{Drawer → Navigation/Drawer}/index.d.ts +1 -0
  26. package/dist/stories/{Drawer → Navigation/Drawer}/index.js +1 -0
  27. package/dist/stories/{Drawer → Navigation/Drawer}/types.d.ts +44 -34
  28. package/dist/stories/Navigation/Drawer/types.js +7 -0
  29. package/dist/stories/Navigation/MobileNav.d.ts +15 -0
  30. package/dist/stories/Navigation/MobileNav.js +97 -0
  31. package/dist/stories/Navigation/MobileNav.stories.d.ts +25 -0
  32. package/dist/stories/Navigation/MobileNav.stories.js +65 -0
  33. package/dist/stories/Navigation/MobileNavBar.js +3 -13
  34. package/dist/stories/Navigation/MobileNavBar.stories.js +5 -18
  35. package/dist/stories/Navigation/SettingsPage.stories.d.ts +19 -0
  36. package/dist/stories/Navigation/SettingsPage.stories.js +75 -0
  37. package/dist/stories/Navigation/SubNav/SubNav.d.ts +14 -0
  38. package/dist/stories/Navigation/SubNav/SubNav.js +16 -0
  39. package/dist/stories/{Settings/SettingsNav.stories.d.ts → Navigation/SubNav/SubNav.stories.d.ts} +3 -3
  40. package/dist/stories/{Settings/SettingsNav.stories.js → Navigation/SubNav/SubNav.stories.js} +3 -3
  41. package/dist/stories/Navigation/SubNav/SubNavBody.d.ts +10 -0
  42. package/dist/stories/Navigation/SubNav/SubNavBody.js +91 -0
  43. package/dist/stories/Navigation/SubNav/SubNavItem.d.ts +10 -0
  44. package/dist/stories/Navigation/SubNav/SubNavItem.js +12 -0
  45. package/dist/stories/Navigation/SubNav/index.d.ts +4 -0
  46. package/dist/stories/Navigation/SubNav/index.js +4 -0
  47. package/dist/stories/Navigation/SubNav/types.d.ts +54 -0
  48. package/dist/stories/Navigation/index.d.ts +3 -0
  49. package/dist/stories/Navigation/index.js +3 -0
  50. package/dist/stories/Navigation/internal/NavPanel.d.ts +21 -0
  51. package/dist/stories/Navigation/internal/NavPanel.js +45 -0
  52. package/dist/stories/Navigation/internal/NavRow.d.ts +45 -0
  53. package/dist/stories/Navigation/internal/NavRow.js +79 -0
  54. package/dist/stories/Navigation/internal/index.d.ts +11 -0
  55. package/dist/stories/Navigation/internal/index.js +11 -0
  56. package/dist/stories/Navigation/internal/types.d.ts +71 -0
  57. package/dist/stories/Navigation/internal/types.js +30 -0
  58. package/dist/stories/Navigation/navigation.fixtures.d.ts +22 -0
  59. package/dist/stories/Navigation/navigation.fixtures.js +64 -0
  60. package/dist/stories/Navigation/types.d.ts +24 -1
  61. package/dist/stories/UI/Menu.js +3 -1
  62. package/dist/stories/UI/types.d.ts +2 -0
  63. package/package.json +1 -1
  64. package/dist/stories/Drawer/Drawer.js +0 -163
  65. package/dist/stories/Drawer/DrawerNavItem.js +0 -87
  66. package/dist/stories/Drawer/types.js +0 -29
  67. package/dist/stories/Settings/SettingsNav.d.ts +0 -10
  68. package/dist/stories/Settings/SettingsNav.js +0 -97
  69. package/dist/stories/Settings/SettingsNavItem.d.ts +0 -9
  70. package/dist/stories/Settings/SettingsNavItem.js +0 -75
  71. package/dist/stories/Settings/SettingsPage.stories.d.ts +0 -11
  72. package/dist/stories/Settings/SettingsPage.stories.js +0 -93
  73. package/dist/stories/Settings/index.d.ts +0 -3
  74. package/dist/stories/Settings/index.js +0 -3
  75. package/dist/stories/Settings/types.d.ts +0 -68
  76. /package/dist/stories/{Drawer → Navigation/Drawer}/Drawer.stories.d.ts +0 -0
  77. /package/dist/stories/{Drawer → Navigation/Drawer}/DrawerCreditsCard.d.ts +0 -0
  78. /package/dist/stories/{Drawer → Navigation/Drawer}/DrawerCreditsCard.stories.d.ts +0 -0
  79. /package/dist/stories/{Drawer → Navigation/Drawer}/DrawerCreditsCardSkeleton.d.ts +0 -0
  80. /package/dist/stories/{Drawer → Navigation/Drawer}/DrawerCreditsCardSkeleton.stories.d.ts +0 -0
  81. /package/dist/stories/{Drawer → Navigation/Drawer}/DrawerIcons.d.ts +0 -0
  82. /package/dist/stories/{Drawer → Navigation/Drawer}/DrawerIcons.js +0 -0
  83. /package/dist/stories/{Drawer → Navigation/Drawer}/DrawerNavItem.stories.d.ts +0 -0
  84. /package/dist/stories/{Drawer → Navigation/Drawer}/DrawerProfile.d.ts +0 -0
  85. /package/dist/stories/{Drawer → Navigation/Drawer}/DrawerProfile.stories.d.ts +0 -0
  86. /package/dist/stories/{Drawer → Navigation/Drawer}/DrawerProfileSkeleton.d.ts +0 -0
  87. /package/dist/stories/{Drawer → Navigation/Drawer}/DrawerProfileSkeleton.stories.d.ts +0 -0
  88. /package/dist/stories/{Settings → Navigation/SubNav}/types.js +0 -0
package/dist/index.d.ts CHANGED
@@ -8,8 +8,6 @@ export * from './stories/Buttons';
8
8
  export * from './stories/Typography';
9
9
  export * from './stories/Credits';
10
10
  export * from './stories/Navigation';
11
- export * from './stories/Drawer';
12
- export * from './stories/Settings';
13
11
  export * from './stories/Typography';
14
12
  export * from './stories/UI';
15
13
  export * from './stories/StripeProduct';
package/dist/index.js CHANGED
@@ -8,8 +8,6 @@ export * from './stories/Buttons';
8
8
  export * from './stories/Typography';
9
9
  export * from './stories/Credits';
10
10
  export * from './stories/Navigation';
11
- export * from './stories/Drawer';
12
- export * from './stories/Settings';
13
11
  export * from './stories/Typography';
14
12
  export * from './stories/UI';
15
13
  export * from './stories/StripeProduct';
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
3
3
  import { DropdownType } from './types';
4
- import { Box, CircularProgress, ClickAwayListener, Popper, } from '@mui/material';
4
+ import { Box, CircularProgress, ClickAwayListener, Grow, Popper, } from '@mui/material';
5
5
  import { Typography, TypographySize, TypographyWeight } from '../Typography';
6
6
  import { getIconComponent } from '../../utils';
7
7
  import { Checkbox } from './Checkbox';
@@ -152,7 +152,10 @@ export const Dropdown = ({ disabled = false, placeholder = 'Select an option', l
152
152
  ? 'var(--color-border-border-brand)'
153
153
  : 'var(--color-border-border-component)'}`, borderRadius: '4px', bgcolor: disabled
154
154
  ? 'var(--color-background-bg-component-disabled)'
155
- : 'var(--color-background-bg-component)', position: 'relative', onClick: () => !disabled && setOpen((prev) => !prev), onKeyDown: handleKeyDown, tabIndex: disabled ? -1 : 0, "aria-haspopup": "listbox", "aria-expanded": open, "aria-disabled": disabled, role: "combobox", sx: { boxSizing: 'border-box' }, children: [_jsx(Box, { display: "flex", flexDirection: "row", alignItems: "center", gap: "var(--spacing-tokens-size-in-px-spacing-spacing-sm)", flexGrow: 1, minWidth: 0, overflow: "hidden", children: hasSelection && (hasRichSelection || hasAvatarOption) ? (_jsxs(_Fragment, { children: [hasAvatarOption && (_jsx(Box, { flexShrink: 0, display: "flex", children: _jsx(Avatar, { image: selectedOptions[0].avatar, text: selectedOptions[0].label, size: AvatarSize.xs, border: true }) })), _jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Regular, color: "var(--color-text-text-title)", textStyle: {
155
+ : 'var(--color-background-bg-component)', position: 'relative', onClick: () => !disabled && setOpen((prev) => !prev), onKeyDown: handleKeyDown, tabIndex: disabled ? -1 : 0, "aria-haspopup": "listbox", "aria-expanded": open, "aria-disabled": disabled, role: "combobox", sx: {
156
+ boxSizing: 'border-box',
157
+ cursor: disabled ? 'not-allowed' : 'pointer',
158
+ }, children: [_jsx(Box, { display: "flex", flexDirection: "row", alignItems: "center", gap: "var(--spacing-tokens-size-in-px-spacing-spacing-sm)", flexGrow: 1, minWidth: 0, overflow: "hidden", children: hasSelection && (hasRichSelection || hasAvatarOption) ? (_jsxs(_Fragment, { children: [hasAvatarOption && (_jsx(Box, { flexShrink: 0, display: "flex", children: _jsx(Avatar, { image: selectedOptions[0].avatar, text: selectedOptions[0].label, size: AvatarSize.xs, border: true }) })), _jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Regular, color: "var(--color-text-text-title)", textStyle: {
156
159
  minWidth: 0,
157
160
  textAlign: 'left',
158
161
  overflow: 'hidden',
@@ -177,8 +180,8 @@ export const Dropdown = ({ disabled = false, placeholder = 'Select an option', l
177
180
  showClear &&
178
181
  hasSelection &&
179
182
  getIconComponent('X', {
180
- width: '24px',
181
- height: '24px',
183
+ width: '20px',
184
+ height: '20px',
182
185
  color: 'var(--color-foreground-fg-secondary)',
183
186
  onClick: handleClear,
184
187
  'aria-label': 'Clear selection',
@@ -191,33 +194,37 @@ export const Dropdown = ({ disabled = false, placeholder = 'Select an option', l
191
194
  }
192
195
  },
193
196
  }), !disabled &&
194
- getIconComponent(open ? 'CaretUp' : 'CaretDown', {
195
- width: '24px',
196
- height: '24px',
197
+ getIconComponent('CaretDown', {
198
+ width: '20px',
199
+ height: '20px',
197
200
  color: 'var(--color-foreground-fg-secondary)',
198
201
  'aria-hidden': 'true',
199
- })] })] }), _jsx(Popper, { open: open, anchorEl: anchorRef.current, placement: "bottom-start", style: { width: anchorRef.current?.offsetWidth, zIndex: 1300 }, children: _jsxs(Box, { ref: optionsContainerRef, onScroll: handleOptionsScroll, bgcolor: 'var(--color-background-bg-component)', borderRadius: '4px', width: '100%', maxHeight: '200px', overflow: 'auto', boxShadow: '0px 4px 8px rgba(0, 0, 0, 0.1)', mt: '4px', role: "listbox", sx: {
200
- border: '1px solid var(--color-border-border-component)',
201
- }, children: [normalizedOptions.length === 0 && !isLoadingMore ? (_jsx(Box, { display: 'flex', flexDirection: 'row', paddingX: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', paddingY: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', children: _jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Regular, children: "No options found" }) })) : (normalizedOptions.map((option, index) => (_jsxs(Box, { display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', height: '36px', padding: '0 var(--spacing-tokens-size-in-px-spacing-spacing-md)', onClick: () => handleOptionSelect(option.value), onMouseEnter: () => setHighlightedIndex(index), sx: {
202
- cursor: 'pointer',
203
- backgroundColor: highlightedIndex === index
204
- ? 'var(--color-background-bg-secondary-action-hover)'
205
- : 'transparent',
206
- '&:hover': {
207
- backgroundColor: 'var(--color-background-bg-secondary-action-hover)',
208
- },
209
- width: '100%',
210
- boxSizing: 'border-box',
211
- }, role: "option", "aria-selected": isOptionSelected(option.value), children: [_jsxs(Box, { display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', flexGrow: 1, minWidth: 0, overflow: "hidden", children: [showCheckboxes && (_jsx(Box, { flexShrink: 0, children: _jsx(Checkbox, { checked: isOptionSelected(option.value), onChange: () => handleOptionSelect(option.value), style: { margin: 0, padding: 0 } }) })), hasAvatarOption && (_jsx(Box, { flexShrink: 0, display: "flex", children: _jsx(Avatar, { image: option.avatar, text: option.label, size: AvatarSize.xs, border: true }) })), _jsx(Box, { flexGrow: 1, minWidth: 0, overflow: "hidden", children: _jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Regular, color: "var(--color-text-text-title)", textStyle: {
212
- overflow: 'hidden',
213
- textOverflow: 'ellipsis',
214
- whiteSpace: 'nowrap',
215
- width: '100%',
216
- }, children: option.label }) }), option.badge && (_jsx(Box, { flexShrink: 0, children: _jsx(Badge, { size: BadgeSize.xs, color: BadgeColor.Default, text: option.badge }) }))] }), _jsx(Box, { flexShrink: 0, display: "flex", alignItems: "center", children: showTicks &&
217
- isOptionSelected(option.value) &&
218
- getIconComponent('Check', {
219
- width: '20px',
220
- height: '20px',
221
- color: 'var(--color-foreground-fg-brand-primary)',
222
- }) })] }, option.value)))), isLoadingMore && (_jsx(Box, { display: 'flex', flexDirection: 'row', justifyContent: 'center', alignItems: 'center', height: '36px', width: '100%', role: "status", "aria-label": "Loading more options", children: _jsx(CircularProgress, { size: 20, sx: { color: 'var(--color-foreground-fg-brand-primary)' } }) }))] }) })] }) }));
202
+ style: {
203
+ transition: 'transform 200ms ease',
204
+ transform: open ? 'rotate(180deg)' : 'rotate(0deg)',
205
+ },
206
+ })] })] }), _jsx(Popper, { open: open, anchorEl: anchorRef.current, placement: "bottom-start", transition: true, style: { width: anchorRef.current?.offsetWidth, zIndex: 1300 }, children: ({ TransitionProps }) => (_jsx(Grow, { ...TransitionProps, timeout: 200, style: { transformOrigin: 'top' }, children: _jsxs(Box, { ref: optionsContainerRef, onScroll: handleOptionsScroll, bgcolor: 'var(--color-background-bg-component)', borderRadius: '4px', width: '100%', maxHeight: '200px', overflow: 'auto', boxShadow: '0px 4px 8px rgba(0, 0, 0, 0.1)', mt: '4px', role: "listbox", sx: {
207
+ border: '1px solid var(--color-border-border-component)',
208
+ }, children: [normalizedOptions.length === 0 && !isLoadingMore ? (_jsx(Box, { display: 'flex', flexDirection: 'row', paddingX: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', paddingY: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', children: _jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Regular, children: "No options found" }) })) : (normalizedOptions.map((option, index) => (_jsxs(Box, { display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', height: '36px', padding: '0 var(--spacing-tokens-size-in-px-spacing-spacing-md)', onClick: () => handleOptionSelect(option.value), onMouseEnter: () => setHighlightedIndex(index), sx: {
209
+ cursor: 'pointer',
210
+ backgroundColor: highlightedIndex === index
211
+ ? 'var(--color-background-bg-secondary-action-hover)'
212
+ : 'transparent',
213
+ '&:hover': {
214
+ backgroundColor: 'var(--color-background-bg-secondary-action-hover)',
215
+ },
216
+ width: '100%',
217
+ boxSizing: 'border-box',
218
+ }, role: "option", "aria-selected": isOptionSelected(option.value), children: [_jsxs(Box, { display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', flexGrow: 1, minWidth: 0, overflow: "hidden", children: [showCheckboxes && (_jsx(Box, { flexShrink: 0, children: _jsx(Checkbox, { checked: isOptionSelected(option.value), onChange: () => handleOptionSelect(option.value), style: { margin: 0, padding: 0 } }) })), hasAvatarOption && (_jsx(Box, { flexShrink: 0, display: "flex", children: _jsx(Avatar, { image: option.avatar, text: option.label, size: AvatarSize.xs, border: true }) })), _jsx(Box, { flexGrow: 1, minWidth: 0, overflow: "hidden", children: _jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Regular, color: "var(--color-text-text-title)", textStyle: {
219
+ overflow: 'hidden',
220
+ textOverflow: 'ellipsis',
221
+ whiteSpace: 'nowrap',
222
+ width: '100%',
223
+ }, children: option.label }) }), option.badge && (_jsx(Box, { flexShrink: 0, children: _jsx(Badge, { size: BadgeSize.xs, color: BadgeColor.Default, text: option.badge }) }))] }), _jsx(Box, { flexShrink: 0, display: "flex", alignItems: "center", children: showTicks &&
224
+ isOptionSelected(option.value) &&
225
+ getIconComponent('Check', {
226
+ width: '20px',
227
+ height: '20px',
228
+ color: 'var(--color-foreground-fg-brand-primary)',
229
+ }) })] }, option.value)))), isLoadingMore && (_jsx(Box, { display: 'flex', flexDirection: 'row', justifyContent: 'center', alignItems: 'center', height: '36px', width: '100%', role: "status", "aria-label": "Loading more options", children: _jsx(CircularProgress, { size: 20, sx: { color: 'var(--color-foreground-fg-brand-primary)' } }) }))] }) })) })] }) }));
223
230
  };
@@ -55,14 +55,20 @@ export const FileUpload = ({ disabled, sx, acceptedFileTypes = '.pdf,.doc,.docx,
55
55
  .split(',')
56
56
  .map((ext) => ext.trim())
57
57
  .filter((ext) => ext.startsWith('.'));
58
- // Get unique icons for the accepted file types
59
- const uniqueIcons = new Map();
58
+ // Collect one icon per accepted file type, de-duplicated by component
59
+ // identity (e.g. `.doc` and `.docx` share FileDoc) while preserving order.
60
+ // NB: we can't key on `IconComponent.name` — Phosphor icons are forwardRef
61
+ // objects with no `name`, so every entry would collapse to a single key.
62
+ const seen = new Set();
63
+ const icons = [];
60
64
  extensions.forEach((ext) => {
61
65
  const IconComponent = fileIconMap[ext] || defaultIcon;
62
- // Use the component name as key to avoid duplicates
63
- uniqueIcons.set(IconComponent.name, IconComponent);
66
+ if (!seen.has(IconComponent)) {
67
+ seen.add(IconComponent);
68
+ icons.push(IconComponent);
69
+ }
64
70
  });
65
- return Array.from(uniqueIcons.values());
71
+ return icons;
66
72
  };
67
73
  const fileIcons = getFileTypeIcons();
68
74
  const handleFileSelect = (files) => {
@@ -104,25 +110,29 @@ export const FileUpload = ({ disabled, sx, acceptedFileTypes = '.pdf,.doc,.docx,
104
110
  handleFileSelect(e.target.files);
105
111
  };
106
112
  const iconProps = {
107
- height: '20px',
108
- width: '20px',
113
+ height: '24px',
114
+ width: '24px',
109
115
  color: disabled
110
116
  ? 'var(--color-text-text-subtle)'
111
117
  : 'var(--color-text-text-paragraph)',
112
118
  };
113
- return (_jsxs(Box, { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)', padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-lg)', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', border: `1.5px dashed ${isDragging ? 'var(--color-background-bg-brand-primary-hover)' : 'var(--color-background-bg-brand-primary)'}`, bgcolor: disabled
114
- ? 'var(--color-background-bg-component-contrast-subtle)'
119
+ return (_jsxs(Box, { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)', padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-lg)', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', border: `1px dashed ${disabled
120
+ ? 'var(--color-border-border-disabled)'
121
+ : isDragging
122
+ ? 'var(--color-border-border-brand-hover)'
123
+ : 'var(--color-border-border-component)'}`, bgcolor: disabled
124
+ ? 'var(--color-background-bg-component-disabled)'
115
125
  : isDragging
116
126
  ? 'var(--color-background-bg-component-hover)'
117
- : 'var(--color-background-bg-element)', sx: { cursor: disabled ? 'not-allowed' : 'pointer', ...sx }, onClick: handleClick, onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleDrop, children: [_jsx("input", { type: "file", ref: fileInputRef, style: { display: 'none' }, accept: acceptedFileTypes, onChange: handleInputChange, disabled: disabled }), _jsx(Box, { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)', border: `2px solid ${disabled
118
- ? 'var(--color-border-border-component)'
119
- : isDragging
120
- ? 'var(--color-background-bg-brand-primary-hover)'
121
- : 'var(--color-background-bg-brand-primary)'}`, borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', bgcolor: 'var(--color-background-bg-component)', children: _jsx(UploadSimple, { height: '20px', width: '20px', weight: "bold", color: disabled
127
+ : 'var(--color-background-bg-element)', sx: { cursor: disabled ? 'not-allowed' : 'pointer', ...sx }, onClick: handleClick, onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleDrop, children: [_jsx("input", { type: "file", ref: fileInputRef, style: { display: 'none' }, accept: acceptedFileTypes, onChange: handleInputChange, disabled: disabled }), _jsx(Box, { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)', border: `1px solid ${disabled
128
+ ? 'var(--color-border-border-disabled)'
129
+ : 'var(--color-border-border-component)'}`, borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', bgcolor: disabled
130
+ ? 'var(--color-background-bg-button-disabled)'
131
+ : 'var(--color-background-bg-element)', children: _jsx(UploadSimple, { height: '20px', width: '20px', weight: "bold", color: disabled
122
132
  ? 'var(--color-foreground-fg-disabled)'
123
133
  : 'var(--color-foreground-fg-brand-primary)' }) }), _jsxs(Box, { display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)', children: [_jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.SemiBold, color: disabled
134
+ ? 'var(--color-text-text-inactive)'
135
+ : 'var(--color-text-text-clicable)', textStyle: { cursor: disabled ? 'not-allowed' : 'pointer' }, children: "Click to upload" }), _jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Regular, color: disabled
124
136
  ? 'var(--color-text-text-disabled)'
125
- : 'var(--color-text-text-accent)', textStyle: { cursor: disabled ? 'not-allowed' : 'pointer' }, children: "Click to upload" }), _jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Regular, color: disabled
126
- ? 'var(--color-text-text-subtle)'
127
137
  : 'var(--color-text-text-paragraph)', children: "or drag and drop" }), _jsxs(Box, { display: 'flex', flexDirection: 'row', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)', children: [fileIcons.map((Icon, index) => (_jsx(Icon, { ...iconProps }, index))), fileIcons.length === 0 && _jsx(FileCode, { ...iconProps })] })] })] }));
128
138
  };
@@ -1,5 +1,4 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Fragment } from 'react';
3
2
  import { FileUploadingStatus, FileUploadingProgressMode, } from '../types';
4
3
  import { Box } from '@mui/material';
5
4
  import { Typography, TypographySize, TypographyWeight } from '../../Typography';
@@ -7,7 +6,18 @@ import { getIconComponent } from '../../../utils';
7
6
  import { Player } from '@lottiefiles/react-lottie-player';
8
7
  import loadingAnimation from '../../../assets/animations/loading-animation.json';
9
8
  import { ProgressIndicator, ProgressIndicatorMode } from '../../UI';
10
- export const FileUploading = ({ uploadProgress, uploadingStatus, progressMode = FileUploadingProgressMode.Animation, sx, onCancel, onRetry, fileName, showCancel, showRetry, }) => {
9
+ // Transparent, subtly shadowed frame that wraps a trailing action icon,
10
+ // matching the "Button" node in the Figma design.
11
+ const actionButtonSx = {
12
+ display: 'flex',
13
+ alignItems: 'center',
14
+ justifyContent: 'center',
15
+ padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)',
16
+ borderRadius: 'var(--spacing-tokens-size-in-px-radius-radius-xs)',
17
+ boxShadow: '0px 1px 1px rgba(25, 28, 31, 0.05)',
18
+ backgroundColor: 'transparent',
19
+ };
20
+ export const FileUploading = ({ uploadProgress, uploadingStatus, progressMode = FileUploadingProgressMode.Percentage, sx, onCancel, onRetry, fileName, showCancel, showRetry, }) => {
11
21
  const isUploading = uploadingStatus === FileUploadingStatus.Uploading;
12
22
  const isSuccess = uploadingStatus === FileUploadingStatus.Success;
13
23
  const isError = uploadingStatus === FileUploadingStatus.Error;
@@ -23,27 +33,27 @@ export const FileUploading = ({ uploadProgress, uploadingStatus, progressMode =
23
33
  return 'FileTxt';
24
34
  return 'File';
25
35
  };
26
- return (_jsxs(Box, { display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)', paddingX: 'var(--spacing-tokens-size-in-px-spacing-spacing-xl)', paddingY: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', border: `1px solid ${isError ? 'var(--color-border-border-error)' : 'var(--color-border-border-component)'}`, bgcolor: 'var(--color-background-bg-component-contrast-subtle)', minWidth: '300px', sx: { ...sx }, children: [_jsxs(Box, { display: 'flex', flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', children: [_jsxs(Box, { display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', children: [getIconComponent(getFileIcon(), {
27
- width: '24px',
28
- height: '24px',
29
- color: isError
30
- ? 'var(--color-text-text-error)'
31
- : 'var(--color-foreground-fg-primary)',
32
- }), _jsx(Typography, { size: TypographySize.TextM, weight: TypographyWeight.Regular, color: isError
33
- ? 'var(--color-text-text-error)'
34
- : 'var(--color-text-text-title)', children: fileName })] }), _jsxs(Box, { display: 'flex', flexDirection: 'row', alignItems: 'center', paddingY: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', children: [showCancel &&
35
- onCancel &&
36
- isUploading &&
37
- getIconComponent('X', {
38
- width: '24px',
39
- height: '24px',
40
- color: 'var(--color-text-text-title)',
41
- onClick: onCancel,
42
- style: { cursor: 'pointer' },
43
- }), isSuccess &&
44
- getIconComponent('Check', {
45
- width: '24px',
46
- height: '24px',
47
- color: 'var(--color-foreground-fg-success)',
48
- })] })] }), _jsxs(Box, { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)', width: '100%', children: [isUploading && isAnimationProgress && (_jsx(Player, { autoplay: true, loop: true, src: loadingAnimation, style: { height: '100%', width: '100%' } })), isUploading && isPercentageProgress && (_jsx(ProgressIndicator, { currentValue: uploadProgress || 0, maxValue: 100, mode: ProgressIndicatorMode.Percentage, style: { width: '100%', height: '100%' } })), isError && (_jsxs(Fragment, { children: [_jsx(Typography, { size: TypographySize.TextXS, weight: TypographyWeight.Regular, color: 'var(--color-text-text-error)', children: "Upload failed, please try again." }), showRetry && onRetry && (_jsx(Box, { onClick: onRetry, style: { cursor: 'pointer' }, children: _jsx(Typography, { size: TypographySize.TextXS, weight: TypographyWeight.SemiBold, color: 'var(--color-text-text-error)', children: "Try again" }) }))] }))] })] }));
36
+ return (_jsxs(Box, { display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)', paddingX: 'var(--spacing-tokens-size-in-px-spacing-spacing-xl)', paddingY: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', border: `1px solid ${isError ? 'var(--color-border-border-error)' : 'var(--color-border-border-divider)'}`, bgcolor: isError
37
+ ? 'var(--color-background-bg-badge-accent-red)'
38
+ : 'var(--color-background-bg-component-contrast-subtle)', minWidth: '300px', sx: { ...sx }, children: [_jsxs(Box, { display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', width: '100%', children: [getIconComponent(getFileIcon(), {
39
+ width: '24px',
40
+ height: '24px',
41
+ color: isError
42
+ ? 'var(--color-text-text-error)'
43
+ : 'var(--color-foreground-fg-primary)',
44
+ }), _jsx(Box, { flex: 1, minWidth: 0, children: _jsx(Typography, { size: TypographySize.TextS, weight: isError ? TypographyWeight.SemiBold : TypographyWeight.Regular, color: isError
45
+ ? 'var(--color-text-text-error)'
46
+ : 'var(--color-text-text-title)', children: fileName }) }), isUploading && showCancel && onCancel && (_jsx(Box, { onClick: onCancel, sx: { ...actionButtonSx, cursor: 'pointer' }, children: getIconComponent('X', {
47
+ width: '20px',
48
+ height: '20px',
49
+ color: 'var(--color-text-text-title)',
50
+ }) })), isSuccess && (_jsx(Box, { sx: actionButtonSx, children: getIconComponent('CheckCircle', {
51
+ width: '20px',
52
+ height: '20px',
53
+ weight: 'fill',
54
+ color: 'var(--color-foreground-fg-success)',
55
+ }) }))] }), _jsxs(Box, { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)', width: '100%', children: [isUploading && isAnimationProgress && (_jsx(Player, { autoplay: true, loop: true, src: loadingAnimation, style: { height: '100%', width: '100%' } })), ((isUploading && isPercentageProgress) || isSuccess) && (_jsx(ProgressIndicator, { currentValue: isSuccess ? 100 : uploadProgress || 0, maxValue: 100, mode: ProgressIndicatorMode.Percentage, fillColor: 'var(--color-foreground-fg-violet)', backgroundColor: 'var(--color-background-bg-element)', textColor: 'var(--color-text-text-paragraph)', minWidth: 0, style: {
56
+ width: '100%',
57
+ gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-lg)',
58
+ } })), isError && (_jsxs(Box, { display: 'flex', flexDirection: 'row', alignItems: 'baseline', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', width: '100%', children: [_jsx(Typography, { size: TypographySize.TextXS, weight: TypographyWeight.Regular, color: 'var(--color-text-text-error)', children: "Upload failed, please try again" }), showRetry && onRetry && (_jsx(Box, { onClick: onRetry, style: { cursor: 'pointer' }, children: _jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.SemiBold, color: 'var(--color-text-text-error)', children: "Try again" }) }))] }))] })] }));
49
59
  };
@@ -1,12 +1,23 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
2
3
  import { FileType } from '../types';
3
4
  import { Box } from '@mui/material';
4
5
  import { File } from '@phosphor-icons/react';
5
6
  import { Typography, TypographySize, TypographyWeight } from '../../Typography';
6
- import { ButtonDisplayMode, ButtonHierarchy, ButtonSize, MainButton, } from '../../Buttons';
7
- export const UploadedFile = ({ type = FileType.Default, showDelete = true, onDelete, showDownload = true, onDownload, fileName, sx, }) => {
7
+ import { Menu } from '../../UI';
8
+ import { ButtonHierarchy, ButtonSize } from '../../Buttons';
9
+ export const UploadedFile = ({ type = FileType.Default, showMenu = true, menuItems, fileName, sx, }) => {
8
10
  const isSampleFile = type === FileType.Sample;
9
- return (_jsxs(Box, { display: 'flex', flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)', padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', paddingLeft: 'var(--spacing-tokens-size-in-px-spacing-spacing-lg)', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', bgcolor: isSampleFile
11
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
12
+ // While its overflow menu is open, the row takes on the active highlight so
13
+ // it stands out from the other files in the list.
14
+ const backgroundColor = isMenuOpen
15
+ ? 'var(--color-background-bg-component-hover)'
16
+ : isSampleFile
10
17
  ? 'var(--color-background-bg-card-highlight)'
11
- : 'var(--color-background-bg-component-contrast-subtle)', minWidth: '240px', minHeight: '36px', sx: { ...sx }, children: [_jsxs(Box, { display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', children: [_jsx(File, { width: '24px', height: '24px', color: 'var(--color-foreground-fg-primary)' }), _jsx(Typography, { size: TypographySize.TextM, weight: TypographyWeight.Medium, color: 'var(--color-foreground-fg-primary)', children: fileName })] }), _jsxs(Box, { display: 'flex', flexDirection: 'row', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', children: [showDownload && onDownload && (_jsx(MainButton, { hierarchy: ButtonHierarchy.Secondary, size: ButtonSize.xs, displayIconMode: ButtonDisplayMode.Only, displayIcon: "DownloadSimple", onClick: onDownload })), showDelete && onDelete && (_jsx(MainButton, { hierarchy: ButtonHierarchy.Secondary, size: ButtonSize.xs, displayIconMode: ButtonDisplayMode.Only, displayIcon: "X", onClick: onDelete }))] })] }));
18
+ : 'var(--color-background-bg-element)';
19
+ return (_jsx(Box, { display: 'flex', flexDirection: 'column', alignItems: 'flex-start', paddingLeft: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', paddingRight: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', paddingY: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)', bgcolor: backgroundColor, minWidth: '240px', sx: { ...sx }, children: _jsxs(Box, { display: 'flex', flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', width: '100%', children: [_jsxs(Box, { display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)', flex: 1, minWidth: 0, children: [_jsx(File, { width: '20px', height: '20px', color: 'var(--color-foreground-fg-secondary)' }), _jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Regular, color: 'var(--color-text-text-title)', children: fileName })] }), showMenu && (_jsx(Menu, { icon: "DotsThree", iconWeight: "bold", items: menuItems, size: ButtonSize.xs, hierarchy: ButtonHierarchy.Tertiary, onOpenChange: setIsMenuOpen, style: {
20
+ background: 'transparent',
21
+ boxShadow: '0px 1px 1px rgba(25, 28, 31, 0.05)',
22
+ } }))] }) }));
12
23
  };
@@ -23,25 +23,15 @@ const meta = {
23
23
  description: 'The type of the file',
24
24
  table: { type: { summary: 'string' } },
25
25
  },
26
- showDelete: {
26
+ showMenu: {
27
27
  control: 'boolean',
28
- description: 'Whether to show the delete button',
28
+ description: 'Whether to show the trailing overflow-menu (kebab) button',
29
29
  table: { type: { summary: 'boolean' } },
30
30
  },
31
- showDownload: {
32
- control: 'boolean',
33
- description: 'Whether to show the download button',
34
- table: { type: { summary: 'boolean' } },
35
- },
36
- onDelete: {
37
- action: 'onDelete',
38
- description: 'Callback function when the delete button is clicked',
39
- table: { type: { summary: 'function' } },
40
- },
41
- onDownload: {
42
- action: 'onDownload',
43
- description: 'Callback function when the download button is clicked',
44
- table: { type: { summary: 'function' } },
31
+ menuItems: {
32
+ control: 'object',
33
+ description: 'Items rendered in the overflow (kebab) menu',
34
+ table: { type: { summary: 'MenuItemProps[]' } },
45
35
  },
46
36
  sx: {
47
37
  control: 'object',
@@ -51,23 +41,28 @@ const meta = {
51
41
  },
52
42
  };
53
43
  export default meta;
44
+ const sampleMenuItems = [
45
+ { label: 'Download', icon: 'DownloadSimple', onClick: () => { } },
46
+ {
47
+ label: 'Delete',
48
+ icon: 'Trash',
49
+ onClick: () => { },
50
+ destructive: true,
51
+ },
52
+ ];
54
53
  export const Default = {
55
54
  args: {
56
- fileName: 'example.pdf',
55
+ fileName: 'File name sample.csv',
57
56
  type: FileType.Default,
58
- showDelete: true,
59
- showDownload: true,
60
- onDelete: () => { },
61
- onDownload: () => { },
57
+ showMenu: true,
58
+ menuItems: sampleMenuItems,
62
59
  },
63
60
  };
64
61
  export const Sample = {
65
62
  args: {
66
- fileName: 'sample.txt',
63
+ fileName: 'File name sample.csv',
67
64
  type: FileType.Sample,
68
- showDelete: false,
69
- showDownload: false,
70
- onDelete: () => { },
71
- onDownload: () => { },
65
+ showMenu: true,
66
+ menuItems: sampleMenuItems,
72
67
  },
73
68
  };
@@ -1,5 +1,5 @@
1
1
  import { SxProps, Theme } from '@mui/material';
2
- import { HintBubbleProps } from '../UI';
2
+ import { HintBubbleProps, MenuItemProps } from '../UI';
3
3
  import { IconName } from '../../utils';
4
4
  import React from 'react';
5
5
  /**
@@ -165,14 +165,10 @@ export interface UploadedFileProps {
165
165
  type?: FileType;
166
166
  /** File name */
167
167
  fileName: string;
168
- /** Whether to show the delete button */
169
- showDelete?: boolean;
170
- /** Callback function when the delete button is clicked */
171
- onDelete?: () => void;
172
- /** Whether to show the download button */
173
- showDownload?: boolean;
174
- /** Callback function when the download button is clicked */
175
- onDownload?: () => void;
168
+ /** Whether to show the trailing overflow-menu (kebab) button */
169
+ showMenu?: boolean;
170
+ /** Items rendered in the overflow (kebab) menu, e.g. Download / Delete */
171
+ menuItems?: MenuItemProps[];
176
172
  /** Style of the uploaded file */
177
173
  sx?: SxProps<Theme>;
178
174
  }
@@ -6,5 +6,9 @@ import { DrawerProps } from './types';
6
6
  * toggle. Collapse state can be controlled (`collapsed` + `onToggle`) or left
7
7
  * uncontrolled (`defaultCollapsed`). Every colour, space and radius resolves to
8
8
  * `@beydesign/tokens` CSS variables, so it renders correctly in light and dark.
9
+ *
10
+ * Below the mobile breakpoint there is no room for this beside a settings
11
+ * sub-nav — use `MobileNav`, which renders the same {@link DrawerBody} as one
12
+ * pane of a single overlay.
9
13
  */
10
14
  export declare const Drawer: React.FC<DrawerProps>;
@@ -0,0 +1,81 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import styled from '@emotion/styled';
4
+ import { DrawerSize } from './types';
5
+ import { DrawerBody } from './DrawerBody';
6
+ import { NavPanel } from '../internal';
7
+ import { getIconComponent } from '../../../utils';
8
+ const EXPANDED_WIDTH = '238px';
9
+ const COLLAPSED_WIDTH = '68px';
10
+ /** Composed from the semantic `shadow-sm` design token. */
11
+ const SHADOW_SM = 'var(--global-shadows-shadow-sm-offset-x) var(--global-shadows-shadow-sm-offset-y) var(--global-shadows-shadow-sm-blur) var(--global-shadows-shadow-sm-spread) var(--global-shadows-shadow-sm-color)';
12
+ /**
13
+ * Non-clipping outer shell. The toggle lives here (a sibling of the panel) so
14
+ * it can overhang the right edge without being clipped by the panel's
15
+ * `overflow`.
16
+ */
17
+ const Shell = styled.aside `
18
+ position: relative;
19
+ flex-shrink: 0;
20
+ height: 100vh;
21
+ width: ${({ $collapsed }) => ($collapsed ? COLLAPSED_WIDTH : EXPANDED_WIDTH)};
22
+ transition: width 0.3s ease-in-out;
23
+ `;
24
+ const ToggleButton = styled.button `
25
+ appearance: none;
26
+ position: absolute;
27
+ top: 52px;
28
+ right: -16px;
29
+ z-index: 2;
30
+ display: inline-flex;
31
+ align-items: center;
32
+ justify-content: center;
33
+ width: 32px;
34
+ height: 32px;
35
+ padding: 0;
36
+ border: 1px solid var(--color-border-border-subtle);
37
+ border-radius: var(--spacing-tokens-size-in-px-radius-radius-full);
38
+ background-color: var(--color-background-bg-element);
39
+ color: var(--color-text-text-paragraph);
40
+ cursor: pointer;
41
+ box-shadow: ${SHADOW_SM};
42
+ transition: color 0.2s ease-in-out;
43
+
44
+ &:hover {
45
+ color: var(--color-text-text-clicable);
46
+ }
47
+ `;
48
+ /** Rotates the chevron smoothly between expanded (◀) and collapsed (▶). */
49
+ const ToggleIcon = styled.span `
50
+ display: inline-flex;
51
+ transform: rotate(${({ $collapsed }) => ($collapsed ? '180deg' : '0deg')});
52
+ transition: transform 0.3s ease-in-out;
53
+
54
+ @media (prefers-reduced-motion: reduce) {
55
+ transition: none;
56
+ }
57
+ `;
58
+ /**
59
+ * Composed application sidebar ("Drawer"): logo header, navigation list,
60
+ * credits card and user-profile footer, with a floating collapse/expand
61
+ * toggle. Collapse state can be controlled (`collapsed` + `onToggle`) or left
62
+ * uncontrolled (`defaultCollapsed`). Every colour, space and radius resolves to
63
+ * `@beydesign/tokens` CSS variables, so it renders correctly in light and dark.
64
+ *
65
+ * Below the mobile breakpoint there is no room for this beside a settings
66
+ * sub-nav — use `MobileNav`, which renders the same {@link DrawerBody} as one
67
+ * pane of a single overlay.
68
+ */
69
+ export const Drawer = ({ navItems, activeKey, collapsed, defaultCollapsed = false, onToggle, showToggle = true, credits, creditsLoading = false, profile, profileLoading = false, logo, className, style, testId, }) => {
70
+ const isControlled = collapsed !== undefined;
71
+ const [internalCollapsed, setInternalCollapsed] = useState(defaultCollapsed);
72
+ const isCollapsed = isControlled ? collapsed : internalCollapsed;
73
+ const drawerSize = isCollapsed ? DrawerSize.Collapsed : DrawerSize.Expanded;
74
+ const handleToggle = () => {
75
+ const next = !isCollapsed;
76
+ if (!isControlled)
77
+ setInternalCollapsed(next);
78
+ onToggle?.(next);
79
+ };
80
+ return (_jsxs(Shell, { "$collapsed": isCollapsed, className: className, style: style, "data-testid": testId, children: [_jsx(NavPanel, { children: _jsx(DrawerBody, { navItems: navItems, activeKey: activeKey, size: drawerSize, credits: credits, creditsLoading: creditsLoading, profile: profile, profileLoading: profileLoading, logo: logo }) }), showToggle && (_jsx(ToggleButton, { type: "button", onClick: handleToggle, "aria-label": isCollapsed ? 'Expand sidebar' : 'Collapse sidebar', children: _jsx(ToggleIcon, { "$collapsed": isCollapsed, children: getIconComponent('CaretLeft', { size: 16, weight: 'bold' }) }) }))] }));
81
+ };
@@ -17,7 +17,7 @@ const navItems = [
17
17
  { key: 'developers', text: 'Developers', icon: 'BracketsCurly' },
18
18
  ];
19
19
  const meta = {
20
- title: 'Design System/Components/Drawer/Drawer',
20
+ title: 'Design System/Components/Navigation/Drawer',
21
21
  component: Drawer,
22
22
  parameters: {
23
23
  layout: 'fullscreen',
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import { DrawerBodyProps } from './types';
3
+ /**
4
+ * The Drawer's content — logo header, navigation list, credits card and profile
5
+ * footer — without any panel chrome. `Drawer` wraps it in a `NavPanel` beside a
6
+ * collapse toggle; `MobileNav` renders the very same body as the root pane of
7
+ * its overlay, which is why the two never drift apart.
8
+ */
9
+ export declare const DrawerBody: React.FC<DrawerBodyProps>;
@@ -0,0 +1,73 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import styled from '@emotion/styled';
3
+ import { DrawerNavItemHierarchy, DrawerNavItemState, DrawerSize, } from './types';
4
+ import { DrawerNavItem } from './DrawerNavItem';
5
+ import { DrawerCreditsCard } from './DrawerCreditsCard';
6
+ import { DrawerCreditsCardSkeleton } from './DrawerCreditsCardSkeleton';
7
+ import { DrawerProfile } from './DrawerProfile';
8
+ import { DrawerProfileSkeleton } from './DrawerProfileSkeleton';
9
+ import { Logo, LogoVariant } from '../../UI';
10
+ const LogoHeader = styled.div `
11
+ display: flex;
12
+ align-items: center;
13
+ justify-content: ${({ $collapsed }) => $collapsed ? 'center' : 'flex-start'};
14
+ height: 38px;
15
+ min-height: 38px;
16
+ margin-bottom: 40px;
17
+ `;
18
+ const Nav = styled.nav `
19
+ display: flex;
20
+ flex-direction: column;
21
+ flex: 1;
22
+ justify-content: space-between;
23
+ gap: var(--spacing-tokens-size-in-px-spacing-spacing-2xl, 24px);
24
+ `;
25
+ const NavTop = styled.div `
26
+ display: flex;
27
+ flex-direction: column;
28
+ gap: var(--spacing-tokens-size-in-px-spacing-spacing-lg, 12px);
29
+ `;
30
+ const Footer = styled.div `
31
+ display: flex;
32
+ flex-direction: column;
33
+ gap: var(--spacing-tokens-size-in-px-spacing-spacing-2xl, 24px);
34
+ `;
35
+ const FooterSlot = styled.div `
36
+ display: flex;
37
+ justify-content: center;
38
+ `;
39
+ const resolveState = (item, activeKey) => {
40
+ if (item.state)
41
+ return item.state;
42
+ if (item.disabled)
43
+ return DrawerNavItemState.Disabled;
44
+ const key = item.key ?? item.text;
45
+ if (activeKey !== undefined && key === activeKey) {
46
+ return DrawerNavItemState.Active;
47
+ }
48
+ return DrawerNavItemState.Inactive;
49
+ };
50
+ /**
51
+ * The Drawer's content — logo header, navigation list, credits card and profile
52
+ * footer — without any panel chrome. `Drawer` wraps it in a `NavPanel` beside a
53
+ * collapse toggle; `MobileNav` renders the very same body as the root pane of
54
+ * its overlay, which is why the two never drift apart.
55
+ */
56
+ export const DrawerBody = ({ navItems, activeKey, size = DrawerSize.Expanded, credits, creditsLoading = false, profile, profileLoading = false, logo, onSubNavOpen, }) => {
57
+ const isCollapsed = size === DrawerSize.Collapsed;
58
+ const handleItemClick = (item) => {
59
+ if (item.disabled)
60
+ return;
61
+ if (item.subNav && onSubNavOpen) {
62
+ onSubNavOpen(item);
63
+ return;
64
+ }
65
+ if (item.link) {
66
+ window.location.href = item.link;
67
+ }
68
+ else {
69
+ item.onClick?.();
70
+ }
71
+ };
72
+ return (_jsxs(_Fragment, { children: [_jsx(LogoHeader, { "$collapsed": isCollapsed, children: _jsx(Logo, { variant: isCollapsed ? LogoVariant.Icon : LogoVariant.Horizontal, color: logo?.color ?? 'var(--color-foreground-fg-brand-logo)', gradient: logo?.gradient, svgUrl: logo?.svgUrl }) }), _jsxs(Nav, { children: [_jsx(NavTop, { children: navItems.map((item) => (_jsx(DrawerNavItem, { text: item.text, icon: item.icon, state: resolveState(item, activeKey), size: size, hierarchy: item.hierarchy ?? DrawerNavItemHierarchy.First, showBadge: item.showBadge, badgeText: item.badgeText, showSubNavCaret: Boolean(item.subNav && onSubNavOpen), onClick: () => handleItemClick(item) }, item.key ?? item.text))) }), (credits || creditsLoading || profile || profileLoading) && (_jsxs(Footer, { children: [(credits || creditsLoading) && (_jsx(FooterSlot, { children: creditsLoading ? (_jsx(DrawerCreditsCardSkeleton, { size: size })) : (_jsx(DrawerCreditsCard, { ...credits, size: size })) })), (profile || profileLoading) && (_jsx(FooterSlot, { children: profileLoading ? (_jsx(DrawerProfileSkeleton, { size: size })) : (profile && _jsx(DrawerProfile, { ...profile, size: size })) }))] }))] })] }));
73
+ };