@synerise/ds-dropdown 1.3.19 → 1.3.21
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/CHANGELOG.md +8 -0
- package/CLAUDE.md +231 -0
- package/package.json +13 -12
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [1.3.21](https://github.com/synerise/synerise-design/compare/@synerise/ds-dropdown@1.3.20...@synerise/ds-dropdown@1.3.21) (2026-07-24)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @synerise/ds-dropdown
|
|
9
|
+
|
|
10
|
+
## [1.3.20](https://github.com/synerise/synerise-design/compare/@synerise/ds-dropdown@1.3.19...@synerise/ds-dropdown@1.3.20) (2026-07-23)
|
|
11
|
+
|
|
12
|
+
**Note:** Version bump only for package @synerise/ds-dropdown
|
|
13
|
+
|
|
6
14
|
## [1.3.19](https://github.com/synerise/synerise-design/compare/@synerise/ds-dropdown@1.3.18...@synerise/ds-dropdown@1.3.19) (2026-07-16)
|
|
7
15
|
|
|
8
16
|
**Note:** Version bump only for package @synerise/ds-dropdown
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# Dropdown (`@synerise/ds-dropdown`)
|
|
2
|
+
|
|
3
|
+
> Floating dropdown built on `@synerise/ds-popover` (floating-ui) — supports click/hover triggers, custom overlay content, built-in search+list via `DropdownMenu`, and optional footer.
|
|
4
|
+
|
|
5
|
+
## Package structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
src/
|
|
9
|
+
Dropdown.tsx — main component + sub-component registry
|
|
10
|
+
Dropdown.types.ts — DropdownProps, DropdownSharedProps, DropdownSize, DropdownPlacement
|
|
11
|
+
Dropdown.styles.ts — Wrapper styled-component
|
|
12
|
+
Dropdown.const.ts — PLACEMENT_MAP, size constants, popover configs
|
|
13
|
+
index.ts — public exports
|
|
14
|
+
hooks/
|
|
15
|
+
useDropdownVisibility.ts — controlled/uncontrolled open state
|
|
16
|
+
useDefaultTexts.tsx — i18n fallback texts for DropdownMenu
|
|
17
|
+
utils/
|
|
18
|
+
getDropdownWidth.ts — maps DropdownSize to pixel widths
|
|
19
|
+
getTransitionConfig.ts — scaleY transition config for floating-ui
|
|
20
|
+
isSplitFooter.ts — type guard for split footer shape
|
|
21
|
+
components/
|
|
22
|
+
DropdownPopover/ — floating-ui Popover wrapper + keyboard navigation context
|
|
23
|
+
DropdownMenu/ — searchable list dropdown (data-driven)
|
|
24
|
+
DropdownMenuList/ — virtualisable list renderer with empty state
|
|
25
|
+
DropdownOverlay/ — forwardRef overlay wrapper with size management
|
|
26
|
+
DropdownFooter/ — footer with optional left/right split
|
|
27
|
+
TextTrigger/ — text + rotating-arrow trigger element
|
|
28
|
+
BackAction/ — back-navigation row with left arrow
|
|
29
|
+
BottomAction/ — bottom call-to-action row
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Public exports
|
|
33
|
+
|
|
34
|
+
### `Dropdown` (default + named export)
|
|
35
|
+
|
|
36
|
+
Compound component. `children` is the trigger element.
|
|
37
|
+
|
|
38
|
+
| Prop | Type | Default | Description |
|
|
39
|
+
|------|------|---------|-------------|
|
|
40
|
+
| `children` | `ReactNode` | — | **Required.** Trigger element |
|
|
41
|
+
| `overlay` | `ReactNode` | `undefined` | Dropdown panel content |
|
|
42
|
+
| `open` | `boolean` | `undefined` | Controlled open state |
|
|
43
|
+
| `onOpenChange` | `(isOpen: boolean) => void` | `undefined` | Fires on open/close |
|
|
44
|
+
| `onDismiss` | `(event?: Event, reason?: OpenChangeReason) => void` | `undefined` | Fires when closed by clicking outside |
|
|
45
|
+
| `trigger` | `PopoverTriggerType \| PopoverTriggerType[]` | `'click'` | `'click'` \| `'hover'` \| `'focus'` |
|
|
46
|
+
| `placement` | `DropdownPlacement` | `undefined` | Floating-ui placement (legacy names supported) |
|
|
47
|
+
| `size` | `DropdownSize \| number` | `undefined` | Overlay width: `'small'`(216px) \| `'medium'`(282px) \| `'large'`(588px) \| `'auto'` \| `'match-trigger'` \| `'min-match-trigger'` \| px number |
|
|
48
|
+
| `disabled` | `boolean` | `false` | Renders children without popover wrapper |
|
|
49
|
+
| `asChild` | `boolean` | `undefined` | Passes trigger props to the first child (Radix-style) |
|
|
50
|
+
| `hideOnItemClick` | `string \| boolean` | `undefined` | CSS selector or `true` → use `[role="menuitem"]` to auto-close on item click |
|
|
51
|
+
| `footer` | `ReactNode \| { left?: ReactNode; right?: ReactNode }` | `undefined` | Footer below overlay; object shape renders split layout |
|
|
52
|
+
| `overlayStyle` | `CSSProperties` | `undefined` | Inline style on the overlay container |
|
|
53
|
+
| `overlayClassName` | `string` | `undefined` | Class on the overlay container |
|
|
54
|
+
| `overlayHTMLAttributes` | `HTMLAttributes<HTMLDivElement>` | `undefined` | HTML attributes forwarded to overlay div |
|
|
55
|
+
| `getPopupContainer` | `(trigger: HTMLElement) => HTMLElement` | `getPopupContainerDefault` | Popover mount container |
|
|
56
|
+
| `popoverProps` | `Partial<PopoverOptions>` | `undefined` | Pass-through to `@synerise/ds-popover` |
|
|
57
|
+
| `popoverTriggerProps` | `Omit<HTMLAttributes<HTMLSpanElement>, 'onClick'>` | `undefined` | HTML attributes on the trigger span |
|
|
58
|
+
| `dropdownRender` | `() => ReactNode` | — | **Deprecated** — use `overlay` |
|
|
59
|
+
| `destroyPopupOnHide` | `boolean` | — | **Deprecated** — all dropdowns are destroyed when hidden |
|
|
60
|
+
| `align` | `any` | — | **Deprecated** |
|
|
61
|
+
|
|
62
|
+
#### Sub-components attached to `Dropdown`
|
|
63
|
+
|
|
64
|
+
| Name | Description |
|
|
65
|
+
|------|-------------|
|
|
66
|
+
| `Dropdown.Wrapper` | Styled `div` — white background, clips `ListItemWrapper` width |
|
|
67
|
+
| `Dropdown.MenuWrapper` | Styled `div` — 8px padding, for manual menu item lists |
|
|
68
|
+
| `Dropdown.SearchInput` | Re-export of `@synerise/ds-search-bar` |
|
|
69
|
+
| `Dropdown.BottomAction` | See `BottomAction` below |
|
|
70
|
+
| `Dropdown.BackAction` | See `BackAction` below |
|
|
71
|
+
| `Dropdown.TextTrigger` | See `TextTrigger` below |
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
### `DropdownMenu<ItemType extends ListItemProps>`
|
|
76
|
+
|
|
77
|
+
Data-driven dropdown with optional search. Renders a filtered list from `dataSource` — no manual overlay needed.
|
|
78
|
+
|
|
79
|
+
| Prop | Type | Default | Description |
|
|
80
|
+
|------|------|---------|-------------|
|
|
81
|
+
| `dataSource` | `ItemType[]` | — | **Required.** Items to render (must satisfy `ListItemProps`) |
|
|
82
|
+
| `children` | `ReactNode` | — | **Required.** Trigger element |
|
|
83
|
+
| `withSearch` | `boolean` | `false` | Show search bar |
|
|
84
|
+
| `virtualised` | `boolean` | `false` | Use `react-window` virtualised list |
|
|
85
|
+
| `maxVisibleItems` | `number` | `7` | Max items before scroll |
|
|
86
|
+
| `texts` | `Partial<DropdownMenuTexts>` | built-in i18n | Translation overrides |
|
|
87
|
+
| `itemMatchesSearchQuery` | `(item: ItemType, query: string) => boolean` | case-insensitive text match | Custom search predicate |
|
|
88
|
+
| `onSearchQueryChange` | `(query: string) => void` | `undefined` | Fires on search input change |
|
|
89
|
+
| `hideOnItemClick` | `boolean` | `undefined` | Close on item click |
|
|
90
|
+
| + all `DropdownSharedProps` except `overlay`, `dropdownRender`, `align`, `hideOnItemClick` (string) | | | |
|
|
91
|
+
|
|
92
|
+
`DropdownMenuTexts` keys: `searchPlaceholder`, `noSearchResults`, `searchClearTooltip`.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
### `DropdownOverlay`
|
|
97
|
+
|
|
98
|
+
`forwardRef` wrapper for custom overlay content. Handles size calculation (`match-trigger`, `min-match-trigger`, static px) and `hideOnItemClick` event delegation.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
### `TextTrigger`
|
|
103
|
+
|
|
104
|
+
Text label with rotating chevron — intended as a dropdown trigger.
|
|
105
|
+
|
|
106
|
+
| Prop | Type | Default | Description |
|
|
107
|
+
|------|------|---------|-------------|
|
|
108
|
+
| `value` | `string \| ReactNode \| undefined` | — | Label text |
|
|
109
|
+
| `size` | `1 \| 2 \| 3 \| 4 \| 5 \| 6` | — | **Required.** Typography size level |
|
|
110
|
+
| `expanded` | `boolean` | `undefined` | Controls arrow rotation (open state) |
|
|
111
|
+
| `isDisabled` | `boolean` | `undefined` | Disables interactions |
|
|
112
|
+
| `inactiveColor` | `string` | `grey-800` | Color when not hovered/focused |
|
|
113
|
+
| `onClick` | `() => void` | `undefined` | Click handler |
|
|
114
|
+
| `onFocus` | `() => void` | `undefined` | Focus handler |
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
### `BackAction`
|
|
119
|
+
|
|
120
|
+
Back-navigation row rendered inside a dropdown overlay.
|
|
121
|
+
|
|
122
|
+
| Prop | Type | Default | Description |
|
|
123
|
+
|------|------|---------|-------------|
|
|
124
|
+
| `label` | `ReactNode` | — | **Required.** Button label |
|
|
125
|
+
| `onClick` | `() => void` | — | **Required.** Click handler |
|
|
126
|
+
| `tooltip` | `ReactNode` | `undefined` | Tooltip content |
|
|
127
|
+
| `tooltipProps` | `Partial<TooltipProps>` | `undefined` | Pass-through to `@synerise/ds-tooltip` |
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
### `BottomAction`
|
|
132
|
+
|
|
133
|
+
Bottom call-to-action row (grey-050 background, 52px height).
|
|
134
|
+
|
|
135
|
+
| Prop | Type | Default | Description |
|
|
136
|
+
|------|------|---------|-------------|
|
|
137
|
+
| `onClickAction` | `() => void` | — | **Required.** Click handler |
|
|
138
|
+
| `icon` | `ReactElement` | `undefined` | Icon before text |
|
|
139
|
+
| `children` | `ReactNode` | `undefined` | Label text |
|
|
140
|
+
| + all `HTMLDivElement` attributes | | | |
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
### Type exports
|
|
145
|
+
|
|
146
|
+
`DropdownPlacement`, `DropdownProps`, `DropdownSharedProps`, `DropdownSize`, `DropdownMenuProps`, `DropdownMenuListItemProps`.
|
|
147
|
+
|
|
148
|
+
## Usage patterns
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
import Dropdown, { DropdownMenu, TextTrigger, BackAction, BottomAction } from '@synerise/ds-dropdown';
|
|
152
|
+
|
|
153
|
+
// Basic overlay dropdown
|
|
154
|
+
<Dropdown overlay={<Menu>...</Menu>} trigger="click">
|
|
155
|
+
<Button>Open</Button>
|
|
156
|
+
</Dropdown>
|
|
157
|
+
|
|
158
|
+
// Data-driven with search
|
|
159
|
+
<DropdownMenu
|
|
160
|
+
dataSource={items}
|
|
161
|
+
withSearch
|
|
162
|
+
trigger="click"
|
|
163
|
+
texts={{ searchPlaceholder: 'Search...' }}
|
|
164
|
+
>
|
|
165
|
+
<Button>Open</Button>
|
|
166
|
+
</DropdownMenu>
|
|
167
|
+
|
|
168
|
+
// Custom overlay with sub-components
|
|
169
|
+
<Dropdown overlay={
|
|
170
|
+
<>
|
|
171
|
+
<Dropdown.BackAction label="Back" onClick={handleBack} />
|
|
172
|
+
<Dropdown.Wrapper>
|
|
173
|
+
<Menu>...</Menu>
|
|
174
|
+
</Dropdown.Wrapper>
|
|
175
|
+
<Dropdown.BottomAction onClickAction={handleAdd} icon={<PlusM />}>
|
|
176
|
+
Add item
|
|
177
|
+
</Dropdown.BottomAction>
|
|
178
|
+
</>
|
|
179
|
+
}>
|
|
180
|
+
<Dropdown.TextTrigger value="Select" size={2} />
|
|
181
|
+
</Dropdown>
|
|
182
|
+
|
|
183
|
+
// Controlled
|
|
184
|
+
const [open, setOpen] = useState(false);
|
|
185
|
+
<Dropdown open={open} onOpenChange={setOpen} overlay={...}>
|
|
186
|
+
<Button>Toggle</Button>
|
|
187
|
+
</Dropdown>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Controlled vs. uncontrolled
|
|
191
|
+
|
|
192
|
+
`useDropdownVisibility` handles both modes:
|
|
193
|
+
- **Uncontrolled:** omit `open` — internal state manages visibility.
|
|
194
|
+
- **Controlled:** pass `open` + `onOpenChange` — component stays in sync with external state.
|
|
195
|
+
|
|
196
|
+
A `skipDuplicates` ref prevents `onOpenChange` from firing twice when both the trigger's `onClick` and the popover's `onOpenChange` fire for the same event.
|
|
197
|
+
|
|
198
|
+
## Styling
|
|
199
|
+
|
|
200
|
+
- Overlay: `DropdownOverlay.styles.ts` — white background, `box-shadow-2`, `3px` border-radius, dynamic width.
|
|
201
|
+
- Footer: `grey-100` background, `48px` height; split layout via `DropdownFooterLeft`/`DropdownFooterRight`.
|
|
202
|
+
- `Dropdown.Wrapper` clips `ListItemWrapper` to prevent overflow in narrow overlays.
|
|
203
|
+
- All colours and shadows come from `@synerise/ds-core` tokens.
|
|
204
|
+
|
|
205
|
+
## Custom hooks
|
|
206
|
+
|
|
207
|
+
### `useDropdownVisibility`
|
|
208
|
+
|
|
209
|
+
Manages controlled/uncontrolled open state. Returns `{ open, toggleOpen }`. Deduplicates `onOpenChange` calls that would otherwise fire twice from concurrent click handlers.
|
|
210
|
+
|
|
211
|
+
### `useDefaultTexts`
|
|
212
|
+
|
|
213
|
+
Calls `useIntl()` to return default English strings for `DropdownMenu` (`searchPlaceholder`, `noSearchResults`, `searchClearTooltip`). Requires a `react-intl` `IntlProvider` ancestor.
|
|
214
|
+
|
|
215
|
+
## Key dependencies
|
|
216
|
+
|
|
217
|
+
- `@synerise/ds-popover` — floating-ui wrapper providing `Popover`, `PopoverContent`, `PopoverTrigger`
|
|
218
|
+
- `@synerise/ds-search-bar` — `Dropdown.SearchInput` is a direct re-export
|
|
219
|
+
- `@synerise/ds-list-item` — `ListItemProps` is the item type constraint for `DropdownMenu`
|
|
220
|
+
- `react-window` — `VariableSizeList` for virtualised `DropdownMenuList`
|
|
221
|
+
- `@floating-ui/react` — `FloatingList`, `useListNavigation` for keyboard arrow-key navigation inside `DropdownPopover`
|
|
222
|
+
|
|
223
|
+
## Implementation notes
|
|
224
|
+
|
|
225
|
+
- When `disabled=true`, `Dropdown` renders `children` directly (no span wrapper, no popover).
|
|
226
|
+
- `asChild` mode forwards all trigger props onto the first child element (Radix `Slot` pattern) — do not wrap children in an extra element when using this.
|
|
227
|
+
- `hideOnItemClick` on `DropdownMenu` accepts only `boolean`; on `Dropdown` it also accepts a CSS selector string.
|
|
228
|
+
- `DropdownMenu` sets `disabled` automatically when `dataSource` is empty.
|
|
229
|
+
- The overlay is always unmounted when hidden (`destroyPopupOnHide` is a no-op legacy prop).
|
|
230
|
+
- `DropdownPopover` provides `DropdownContext` (active index + close handler) to list items — do not skip it when building a custom overlay with keyboard navigation.
|
|
231
|
+
- `PLACEMENT_MAP` in `Dropdown.const.ts` maps legacy Ant Design placement strings (e.g. `bottomCenter`) to floating-ui equivalents — no consumer migration needed.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synerise/ds-dropdown",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.21",
|
|
4
4
|
"description": "Dropdown UI Component for the Synerise Design System",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"repository": "synerise/synerise-design",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"files": [
|
|
18
18
|
"/dist",
|
|
19
19
|
"CHANGELOG.md",
|
|
20
|
+
"CLAUDE.md",
|
|
20
21
|
"README.md",
|
|
21
22
|
"package.json",
|
|
22
23
|
"LICENSE.md"
|
|
@@ -42,16 +43,16 @@
|
|
|
42
43
|
"types": "dist/index.d.ts",
|
|
43
44
|
"dependencies": {
|
|
44
45
|
"@floating-ui/react": "^0.27.16",
|
|
45
|
-
"@synerise/ds-divider": "^1.3.
|
|
46
|
-
"@synerise/ds-empty-states": "^1.0.
|
|
47
|
-
"@synerise/ds-icon": "^1.18.
|
|
48
|
-
"@synerise/ds-list-item": "^1.6.
|
|
49
|
-
"@synerise/ds-popover": "^1.6.
|
|
50
|
-
"@synerise/ds-scrollbar": "^1.5.
|
|
51
|
-
"@synerise/ds-search-bar": "^1.4.
|
|
52
|
-
"@synerise/ds-tooltip": "^1.5.
|
|
53
|
-
"@synerise/ds-typography": "^1.1.
|
|
54
|
-
"@synerise/ds-utils": "^1.10.
|
|
46
|
+
"@synerise/ds-divider": "^1.3.16",
|
|
47
|
+
"@synerise/ds-empty-states": "^1.0.49",
|
|
48
|
+
"@synerise/ds-icon": "^1.18.5",
|
|
49
|
+
"@synerise/ds-list-item": "^1.6.2",
|
|
50
|
+
"@synerise/ds-popover": "^1.6.2",
|
|
51
|
+
"@synerise/ds-scrollbar": "^1.5.2",
|
|
52
|
+
"@synerise/ds-search-bar": "^1.4.36",
|
|
53
|
+
"@synerise/ds-tooltip": "^1.5.4",
|
|
54
|
+
"@synerise/ds-typography": "^1.1.27",
|
|
55
|
+
"@synerise/ds-utils": "^1.10.2",
|
|
55
56
|
"classnames": "^2.5.1",
|
|
56
57
|
"react-window": "^1.8.11"
|
|
57
58
|
},
|
|
@@ -64,5 +65,5 @@
|
|
|
64
65
|
"react-intl": "^6.8.7",
|
|
65
66
|
"styled-components": "^5.3.3"
|
|
66
67
|
},
|
|
67
|
-
"gitHead": "
|
|
68
|
+
"gitHead": "018414e0d7ffd102a07f8aa35fa2e2d3d4ed19e2"
|
|
68
69
|
}
|