@synerise/ds-autocomplete 1.2.50 → 1.2.52
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 +190 -0
- package/package.json +11 -10
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.2.52](https://github.com/Synerise/synerise-design/compare/@synerise/ds-autocomplete@1.2.51...@synerise/ds-autocomplete@1.2.52) (2026-07-24)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @synerise/ds-autocomplete
|
|
9
|
+
|
|
10
|
+
## [1.2.51](https://github.com/Synerise/synerise-design/compare/@synerise/ds-autocomplete@1.2.50...@synerise/ds-autocomplete@1.2.51) (2026-07-23)
|
|
11
|
+
|
|
12
|
+
**Note:** Version bump only for package @synerise/ds-autocomplete
|
|
13
|
+
|
|
6
14
|
## [1.2.50](https://github.com/Synerise/synerise-design/compare/@synerise/ds-autocomplete@1.2.49...@synerise/ds-autocomplete@1.2.50) (2026-07-16)
|
|
7
15
|
|
|
8
16
|
**Note:** Version bump only for package @synerise/ds-autocomplete
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# Autocomplete (`@synerise/ds-autocomplete`)
|
|
2
|
+
|
|
3
|
+
> Fully native (no Ant Design) autocomplete: an autosizing text-input trigger plus a floating options dropdown built on `@synerise/ds-dropdown`. Adds a `FormField` label/error/description layer, optional icon slots, `readOnly` mode, and auto-resize behaviour.
|
|
4
|
+
|
|
5
|
+
## Package structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
src/
|
|
9
|
+
Autocomplete.tsx — main component (native input + ds-dropdown)
|
|
10
|
+
Autocomplete.types.ts — AutocompleteProps, AutocompleteOption, AutocompleteInputHandle
|
|
11
|
+
Autocomplete.styles.ts — AutocompleteWrapper, ComponentWrapper, InputContainer, NativeInput, IconWrapper, ClearButton
|
|
12
|
+
Autocomplete.const.ts — ICON_WIDTH (24), ICON_GAP (4), ICON_OFFSET (8)
|
|
13
|
+
Option.tsx — Autocomplete.Option marker component (renders null; props are read to build options)
|
|
14
|
+
index.ts — exports default + AutocompleteProps/AutocompleteOption/AutocompleteInputHandle/OptionProps types
|
|
15
|
+
AutocompleteDropdown/
|
|
16
|
+
AutocompleteDropdown.tsx — virtualised options overlay (SearchItems + ListItem in a Scrollbar)
|
|
17
|
+
AutocompleteDropdown.types.ts
|
|
18
|
+
AutocompleteDropdown.style.ts
|
|
19
|
+
utils/
|
|
20
|
+
getIconsWidth.ts — calculates right-padding offset for 1–2 icons
|
|
21
|
+
getOptionsFromChildren.ts — maps <Autocomplete.Option> children → AutocompleteOption[]
|
|
22
|
+
__specs__/
|
|
23
|
+
Autocomplete.spec.tsx — Vitest tests (native role/data-testid selectors)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
> There is **no `style/` LESS** — the package is fully de-antd'd. The dropdown overlay's
|
|
27
|
+
> box-shadow comes from `@synerise/ds-dropdown`'s `DropdownOverlay`; all other styling is
|
|
28
|
+
> styled-components in `*.styles.ts` / `AutocompleteDropdown/AutocompleteDropdown.style.ts`.
|
|
29
|
+
|
|
30
|
+
## Architecture (native rewrite)
|
|
31
|
+
|
|
32
|
+
- The trigger is a styled native `<input role="combobox">` (`S.NativeInput`), autosized via `useAutosizeWidth` from `@synerise/ds-input` (hidden `<span>` sizer; `stretchToFit` via `useStretchToFit`).
|
|
33
|
+
- The dropdown is `@synerise/ds-dropdown` (`asChild`, `size="match-trigger"`), controlled via `open`/`onOpenChange`. **`trigger={[]}`** — Autocomplete owns the open state through the input's focus/click/change handlers, so ds-dropdown is used only for positioning + outside-dismiss and does **not** toggle on trigger click (delegating the toggle to ds-dropdown as well double-fired against the focus-open and flashed the panel open→closed on re-click). The overlay (`AutocompleteDropdown`) renders the options with `SearchItems` (react-window) → `@synerise/ds-list-item` `ListItem`s inside `@synerise/ds-scrollbar`.
|
|
34
|
+
- The component does **not** filter options — it renders `options` (or the children-derived list) as given; the consumer filters via `onSearch`.
|
|
35
|
+
- Keyboard navigation is delegated to `@synerise/ds-dropdown`'s built-in floating-ui list navigation (`useListNavigation`): ArrowDown/Up move focus through the `ListItem`s and the focused item selects on Enter/click (ds-list-item's own keydown handler). Autocomplete does **not** keep a second manual highlight — that produced a duplicate active row offset from the real focused one. As antd parity for `defaultActiveFirstOption` (default `true`), an input-level Enter handler selects the first enabled option when Enter is pressed *before* arrow-navigating into the list (once arrowed, focus is on the item so that handler no longer fires).
|
|
36
|
+
- `Autocomplete.Option` is a marker that renders `null`; `getOptionsFromChildren` reads `value`/`children`(→`label`) off direct `Option` children when `options` is not provided.
|
|
37
|
+
|
|
38
|
+
## Public exports
|
|
39
|
+
|
|
40
|
+
### `Autocomplete` (default)
|
|
41
|
+
|
|
42
|
+
Fully native (no antd). Props:
|
|
43
|
+
|
|
44
|
+
**From `FormFieldCommonProps`** (rendered via `ds-form-field`):
|
|
45
|
+
|
|
46
|
+
| Prop | Type | Default | Description |
|
|
47
|
+
|------|------|---------|-------------|
|
|
48
|
+
| `label` | `ReactNode` | `undefined` | Field label rendered above the input |
|
|
49
|
+
| `tooltip` | `ReactNode` | `undefined` | Tooltip icon shown next to the label |
|
|
50
|
+
| `tooltipConfig` | `TooltipProps` | `undefined` | Extra config passed to `ds-tooltip` |
|
|
51
|
+
| `description` | `ReactNode` | `undefined` | Helper text rendered below the input |
|
|
52
|
+
| `errorText` | `ReactNode` | `undefined` | Error message; also triggers error styling |
|
|
53
|
+
|
|
54
|
+
**DS-specific overrides:**
|
|
55
|
+
|
|
56
|
+
| Prop | Type | Default | Description |
|
|
57
|
+
|------|------|---------|-------------|
|
|
58
|
+
| `className` | `string` | `undefined` | Additional CSS class on the outer wrapper |
|
|
59
|
+
| `error` | `boolean` | `undefined` | Explicit error state (without message); combined with `!!errorText` |
|
|
60
|
+
| `readOnly` | `boolean` | `undefined` | Renders as disabled + read-only styles (white bg, auto cursor, grey text) |
|
|
61
|
+
| `handleInputRef` | `(ref: MutableRefObject<AutocompleteInputHandle \| null>) => void` | `undefined` | Callback receiving a native handle `{ focus, blur, input }` (no longer the antd `RefSelectProps`) |
|
|
62
|
+
| `autoResize` | `AutoResizeProp` | `undefined` | Resizes input width to text content (see below) |
|
|
63
|
+
| `getPopupContainer` | `(node: HTMLElement) => HTMLElement` | parent node | Container for the dropdown; defaults to `triggerNode.parentNode` (not `document.body`) |
|
|
64
|
+
| `icon1` | `ReactNode` | `undefined` | First icon rendered at the right end of the input |
|
|
65
|
+
| `icon1Tooltip` | `ReactNode` | `undefined` | Tooltip for `icon1` |
|
|
66
|
+
| `icon2` | `ReactNode` | `undefined` | Second icon rendered at the right end of the input |
|
|
67
|
+
| `icon2Tooltip` | `ReactNode` | `undefined` | Tooltip for `icon2` |
|
|
68
|
+
|
|
69
|
+
**Native autocomplete props** (no longer derived from antd):
|
|
70
|
+
|
|
71
|
+
| Prop | Type | Default | Description |
|
|
72
|
+
|------|------|---------|-------------|
|
|
73
|
+
| `options` | `AutocompleteOption[]` | `undefined` | Options to show; `{ value: string; label?: ReactNode; disabled?: boolean }`. Rendered as given (no internal filtering) |
|
|
74
|
+
| `value` | `string` | `undefined` | Controlled input value |
|
|
75
|
+
| `onChange` | `(value: string) => void` | `undefined` | Fired on keystroke and on select |
|
|
76
|
+
| `onSearch` | `(value: string) => void` | `undefined` | Fired on every keystroke |
|
|
77
|
+
| `onSelect` | `(value: string) => void` | `undefined` | Fired when an option is chosen |
|
|
78
|
+
| `open` | `boolean` | `undefined` | Controlled dropdown visibility |
|
|
79
|
+
| `onDropdownVisibleChange` | `(open: boolean) => void` | `undefined` | Fired when visibility changes |
|
|
80
|
+
| `placeholder` | `ReactNode` | `undefined` | Input placeholder (string used for autosize measuring) |
|
|
81
|
+
| `disabled` | `boolean` | `undefined` | Disables the input + dropdown |
|
|
82
|
+
| `allowClear` | `boolean` | `undefined` | Shows a clear (×) button when the input has a value |
|
|
83
|
+
| `defaultActiveFirstOption` | `boolean` | `true` | antd parity: pressing Enter while open, before arrow-navigating, selects the first enabled option. `false` requires explicit arrow-nav before Enter selects |
|
|
84
|
+
| `notFoundContent` | `ReactNode` | `undefined` | Rendered when there are no options |
|
|
85
|
+
| `autoFocus` | `boolean` | `undefined` | Focuses the input on mount |
|
|
86
|
+
| `maxLength` | `number` | `undefined` | Native input max length |
|
|
87
|
+
| `id` | `string` | `undefined` | Input id (also used by `FormField`) |
|
|
88
|
+
| `placement` | `DropdownPlacement` | `'bottomLeft'` | Dropdown placement |
|
|
89
|
+
|
|
90
|
+
#### `AutoResizeProp`
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
type AutoResizeProp = boolean | {
|
|
94
|
+
minWidth: string;
|
|
95
|
+
maxWidth?: string;
|
|
96
|
+
stretchToFit?: boolean;
|
|
97
|
+
};
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- `true` — enables auto-resize with no constraints.
|
|
101
|
+
- Object form — sets min/max width constraints.
|
|
102
|
+
- `stretchToFit: true` — the input grows to fill the wrapper element (uses `ResizeObserver` on the outer `div`). If the input is inside a flex item, the flex item needs `min-width: 0; flex-grow: 1` to avoid layout issues.
|
|
103
|
+
|
|
104
|
+
### `Autocomplete.Option`
|
|
105
|
+
|
|
106
|
+
Native marker component (`Option.tsx`) that renders `null`. Use it to define dropdown items declaratively via children; `getOptionsFromChildren` reads each `Option`'s `value` and `children` (→ `label`) to build the internal options list. Ignored when the `options` prop is provided.
|
|
107
|
+
|
|
108
|
+
## Usage patterns
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
import Autocomplete from '@synerise/ds-autocomplete';
|
|
112
|
+
|
|
113
|
+
// Minimal
|
|
114
|
+
<Autocomplete options={[{ value: 'foo' }, { value: 'bar' }]} />
|
|
115
|
+
|
|
116
|
+
// With form-field decoration
|
|
117
|
+
<Autocomplete
|
|
118
|
+
label="Search"
|
|
119
|
+
description="Type to filter"
|
|
120
|
+
errorText={errors.search}
|
|
121
|
+
placeholder="Start typing..."
|
|
122
|
+
onSearch={handleSearch}
|
|
123
|
+
onChange={handleChange}
|
|
124
|
+
options={options}
|
|
125
|
+
/>
|
|
126
|
+
|
|
127
|
+
// With icons
|
|
128
|
+
<Autocomplete
|
|
129
|
+
icon1={<SearchIcon />}
|
|
130
|
+
icon1Tooltip="Search"
|
|
131
|
+
options={options}
|
|
132
|
+
/>
|
|
133
|
+
|
|
134
|
+
// Auto-resize with stretch
|
|
135
|
+
<Autocomplete
|
|
136
|
+
autoResize={{ minWidth: '80px', stretchToFit: true }}
|
|
137
|
+
options={options}
|
|
138
|
+
/>
|
|
139
|
+
|
|
140
|
+
// Accessing the input handle ({ focus, blur, input })
|
|
141
|
+
<Autocomplete
|
|
142
|
+
handleInputRef={(ref) => { inputHandleRef.current = ref.current; }}
|
|
143
|
+
options={options}
|
|
144
|
+
/>
|
|
145
|
+
|
|
146
|
+
// Declarative options via Option
|
|
147
|
+
<Autocomplete>
|
|
148
|
+
<Autocomplete.Option value="foo">Foo</Autocomplete.Option>
|
|
149
|
+
<Autocomplete.Option value="bar">Bar</Autocomplete.Option>
|
|
150
|
+
</Autocomplete>
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Styling
|
|
154
|
+
|
|
155
|
+
Styles live in `Autocomplete.styles.ts`. Uses `@synerise/ds-core` theme palette tokens (`blue-600`, `blue-050`, `red-600`, `red-050`, `grey-300`, `grey-400`, `grey-600`, `grey-700`, `white`). No hardcoded colour values. **All selectors are `.ds-*` / styled-components — no `.ant-*`.**
|
|
156
|
+
|
|
157
|
+
Styled components:
|
|
158
|
+
- `AutocompleteWrapper` — outermost `div`; stretches the `InputContainer` to `100%` when `autoResize` is set.
|
|
159
|
+
- `InputContainer` — relative flex container holding the input, sizer span, clear button, and icons.
|
|
160
|
+
- `NativeInput` — the styled native `<input role="combobox">`; carries border/focus/hover states and applies `autoresizeConfObjToCss` (min/max width) + `box-sizing: content-box` when `autoResize` is set; right-padding accounts for `iconCount`.
|
|
161
|
+
- `ComponentWrapper` — applies `error`/`readOnly` state styling onto `${NativeInput}`. Uses `&&& {}` specificity bump.
|
|
162
|
+
- `IconWrapper` — absolutely positioned container for `icon1`/`icon2`; `right: 8px`, vertically centred, `z-index: 5`.
|
|
163
|
+
- `ClearButton` — × button shown when `allowClear` + value present.
|
|
164
|
+
|
|
165
|
+
The dropdown overlay keeps class `ds-autocomplete-dropdown ps__child--consume` (PerfectScrollbar integration). The overlay box-shadow comes from `@synerise/ds-dropdown`'s `DropdownOverlay`; the active/focused row highlight is ds-list-item's own hover/focus styling (floating-ui moves DOM focus through the items). The overlay layout (wrapper/list/scrollbar/list-item) is in `AutocompleteDropdown/AutocompleteDropdown.style.ts`.
|
|
166
|
+
|
|
167
|
+
## Key dependencies
|
|
168
|
+
|
|
169
|
+
- `@synerise/ds-dropdown` — floating dropdown wrapper (`open`/`onOpenChange`/`placement`/`overlay`/`asChild`/`trigger`/`getPopupContainer`/`size="match-trigger"`)
|
|
170
|
+
- `@synerise/ds-search` — `SearchItems` (react-window virtualised list) renders the option rows
|
|
171
|
+
- `@synerise/ds-list-item` — `ListItem` rows + `ListItemProps`
|
|
172
|
+
- `@synerise/ds-scrollbar` — `Scrollbar` wrapping the option list
|
|
173
|
+
- `@synerise/ds-form-field` — provides label, description, error, tooltip layout
|
|
174
|
+
- `@synerise/ds-input` — provides `useAutosizeWidth`, `useStretchToFit`, `SIZER_STYLE`, `autoresizeConfObjToCss`
|
|
175
|
+
- `@synerise/ds-tooltip` — wraps `icon1`/`icon2` when tooltip props are provided
|
|
176
|
+
- `@synerise/ds-utils` — used transitively (no direct import in the rewrite)
|
|
177
|
+
|
|
178
|
+
> **No `antd` dependency** — antd was removed from `peerDependencies` and from all `src` imports/`.ant-*` selectors as part of the antd-removal effort.
|
|
179
|
+
|
|
180
|
+
## Implementation notes
|
|
181
|
+
|
|
182
|
+
- **`readOnly` disables the input** — `readOnly || disabled` is passed to the native input's `disabled` and to `Dropdown`'s `disabled` (so the popover never opens). `readOnly` additionally applies white-background/auto-cursor styling via `ComponentWrapper`.
|
|
183
|
+
- **Icon count adjusts input padding** — When icons are present, `NativeInput` adds right-padding `getIconsWidth(iconCount)` so the value never overlaps the icons. Clicking the icons focuses the input (`handleIconsClick`).
|
|
184
|
+
- **Controlled visibility** — `open` + `onDropdownVisibleChange` map directly to `ds-dropdown`'s `open`/`onOpenChange`. When `open` is omitted, internal state drives it; focus/typing/click **opens** (never toggles), select/Escape/outside-click closes. Opening is idempotent (each handler only calls `setOpen(true)` when closed), so no single interaction can open-then-close.
|
|
185
|
+
- **Keyboard nav** — delegated to `@synerise/ds-dropdown`'s floating-ui list navigation (not a local `activeIndex`). ArrowDown/Up move DOM focus through the `ListItem`s; the focused item selects on Enter/click via ds-list-item's own keydown. `defaultActiveFirstOption` (default `true`) adds an input-level Enter handler that selects the first enabled option when Enter is pressed before arrow-navigating (once focus is on an item, that handler no longer fires — no double-select).
|
|
186
|
+
- **Options precedence** — the `options` prop wins; only when it's empty/absent are `<Autocomplete.Option>` children parsed (`getOptionsFromChildren`).
|
|
187
|
+
- **`handleInputRef` is native now** — receives `MutableRefObject<AutocompleteInputHandle | null>` where `AutocompleteInputHandle = { focus, blur, input }`, replacing the antd `RefSelectProps`.
|
|
188
|
+
- **`autoResize`** — `useAutosizeWidth` writes the content-box width onto the `<input>`; `useStretchToFit` clamps `max-width` to the wrapper when `stretchToFit` is set (capturing/restoring `scrollLeft`). `AUTOSIZE_EXTRA_WIDTH = 27` (+ icon width) is the `extraWidth`.
|
|
189
|
+
- **`getPopupContainer` defaults to parent node** — `getParentNode` mounts the dropdown as a sibling of the trigger (not `document.body`), avoiding z-index stacking issues.
|
|
190
|
+
- **No `@floating-ui/react` import** — refs are merged with a small local callback (the package does not depend on `@floating-ui/react`).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synerise/ds-autocomplete",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.52",
|
|
4
4
|
"description": "Autocomplete 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"
|
|
@@ -41,14 +42,14 @@
|
|
|
41
42
|
],
|
|
42
43
|
"types": "dist/index.d.ts",
|
|
43
44
|
"dependencies": {
|
|
44
|
-
"@synerise/ds-dropdown": "^1.3.
|
|
45
|
-
"@synerise/ds-form-field": "^1.3.
|
|
46
|
-
"@synerise/ds-input": "^1.7.
|
|
47
|
-
"@synerise/ds-list-item": "^1.6.
|
|
48
|
-
"@synerise/ds-scrollbar": "^1.5.
|
|
49
|
-
"@synerise/ds-search": "^1.5.
|
|
50
|
-
"@synerise/ds-tooltip": "^1.5.
|
|
51
|
-
"@synerise/ds-utils": "^1.10.
|
|
45
|
+
"@synerise/ds-dropdown": "^1.3.21",
|
|
46
|
+
"@synerise/ds-form-field": "^1.3.24",
|
|
47
|
+
"@synerise/ds-input": "^1.7.14",
|
|
48
|
+
"@synerise/ds-list-item": "^1.6.2",
|
|
49
|
+
"@synerise/ds-scrollbar": "^1.5.2",
|
|
50
|
+
"@synerise/ds-search": "^1.5.34",
|
|
51
|
+
"@synerise/ds-tooltip": "^1.5.4",
|
|
52
|
+
"@synerise/ds-utils": "^1.10.2"
|
|
52
53
|
},
|
|
53
54
|
"peerDependencies": {
|
|
54
55
|
"@synerise/ds-core": "*",
|
|
@@ -56,5 +57,5 @@
|
|
|
56
57
|
"styled-components": "^5.3.3",
|
|
57
58
|
"vitest": "4"
|
|
58
59
|
},
|
|
59
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "018414e0d7ffd102a07f8aa35fa2e2d3d4ed19e2"
|
|
60
61
|
}
|