@reactzero/combo 0.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.
- package/CHANGELOG.md +31 -0
- package/LICENSE +21 -0
- package/README.md +188 -0
- package/dist/Combo-Cx3kSkop.cjs +2 -0
- package/dist/Combo-Cx3kSkop.cjs.map +1 -0
- package/dist/Combo-qs6_L512.js +439 -0
- package/dist/Combo-qs6_L512.js.map +1 -0
- package/dist/components/Combo.d.ts +71 -0
- package/dist/components/Combo.d.ts.map +1 -0
- package/dist/components/LiveRegion.d.ts +7 -0
- package/dist/components/LiveRegion.d.ts.map +1 -0
- package/dist/components/Portal.d.ts +8 -0
- package/dist/components/Portal.d.ts.map +1 -0
- package/dist/components/slots/CheckboxItem.d.ts +38 -0
- package/dist/components/slots/CheckboxItem.d.ts.map +1 -0
- package/dist/components/slots/CustomItem.d.ts +35 -0
- package/dist/components/slots/CustomItem.d.ts.map +1 -0
- package/dist/components/slots/FooterActions.d.ts +42 -0
- package/dist/components/slots/FooterActions.d.ts.map +1 -0
- package/dist/components/slots/GroupSeparator.d.ts +30 -0
- package/dist/components/slots/GroupSeparator.d.ts.map +1 -0
- package/dist/components/slots/index.d.ts +9 -0
- package/dist/components/slots/index.d.ts.map +1 -0
- package/dist/components/tabs/TabbedCombo.d.ts +45 -0
- package/dist/components/tabs/TabbedCombo.d.ts.map +1 -0
- package/dist/components/tabs/index.d.ts +3 -0
- package/dist/components/tabs/index.d.ts.map +1 -0
- package/dist/core/announce.d.ts +10 -0
- package/dist/core/announce.d.ts.map +1 -0
- package/dist/core/ids.d.ts +13 -0
- package/dist/core/ids.d.ts.map +1 -0
- package/dist/core/keyboard.d.ts +13 -0
- package/dist/core/keyboard.d.ts.map +1 -0
- package/dist/core/scroll.d.ts +5 -0
- package/dist/core/scroll.d.ts.map +1 -0
- package/dist/core/stateMachine.d.ts +32 -0
- package/dist/core/stateMachine.d.ts.map +1 -0
- package/dist/core/utils.d.ts +26 -0
- package/dist/core/utils.d.ts.map +1 -0
- package/dist/defaults-iFGq2Q-7.cjs +2 -0
- package/dist/defaults-iFGq2Q-7.cjs.map +1 -0
- package/dist/defaults-rhC5DFTg.js +53 -0
- package/dist/defaults-rhC5DFTg.js.map +1 -0
- package/dist/entries/hook-bare.d.ts +4 -0
- package/dist/entries/hook-bare.d.ts.map +1 -0
- package/dist/entries/hook.d.ts +4 -0
- package/dist/entries/hook.d.ts.map +1 -0
- package/dist/entries/icons.d.ts +4 -0
- package/dist/entries/icons.d.ts.map +1 -0
- package/dist/entries/index.d.ts +9 -0
- package/dist/entries/index.d.ts.map +1 -0
- package/dist/entries/position.d.ts +3 -0
- package/dist/entries/position.d.ts.map +1 -0
- package/dist/entries/slots.d.ts +9 -0
- package/dist/entries/slots.d.ts.map +1 -0
- package/dist/entries/tabs.d.ts +4 -0
- package/dist/entries/tabs.d.ts.map +1 -0
- package/dist/hook-bare.cjs +2 -0
- package/dist/hook-bare.cjs.map +1 -0
- package/dist/hook-bare.js +9 -0
- package/dist/hook-bare.js.map +1 -0
- package/dist/hook.cjs +2 -0
- package/dist/hook.cjs.map +1 -0
- package/dist/hook.js +11 -0
- package/dist/hook.js.map +1 -0
- package/dist/hooks/useCombo.d.ts +3 -0
- package/dist/hooks/useCombo.d.ts.map +1 -0
- package/dist/hooks/usePosition.d.ts +16 -0
- package/dist/hooks/usePosition.d.ts.map +1 -0
- package/dist/icons/defaults.d.ts +16 -0
- package/dist/icons/defaults.d.ts.map +1 -0
- package/dist/icons/icons.d.ts +30 -0
- package/dist/icons/icons.d.ts.map +1 -0
- package/dist/icons-Ch1Q5AhF.js +40 -0
- package/dist/icons-Ch1Q5AhF.js.map +1 -0
- package/dist/icons-vzkEacAb.cjs +2 -0
- package/dist/icons-vzkEacAb.cjs.map +1 -0
- package/dist/icons.cjs +2 -0
- package/dist/icons.cjs.map +1 -0
- package/dist/icons.js +20 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/position.cjs +2 -0
- package/dist/position.cjs.map +1 -0
- package/dist/position.js +5 -0
- package/dist/position.js.map +1 -0
- package/dist/slots.cjs +2 -0
- package/dist/slots.cjs.map +1 -0
- package/dist/slots.js +92 -0
- package/dist/slots.js.map +1 -0
- package/dist/style.css +1 -0
- package/dist/styles/base.css +205 -0
- package/dist/styles/checkbox.css +36 -0
- package/dist/styles/chips.css +71 -0
- package/dist/styles/custom-item.css +64 -0
- package/dist/styles/footer.css +73 -0
- package/dist/styles/groups.css +23 -0
- package/dist/styles/meta.css +30 -0
- package/dist/styles/radio.css +36 -0
- package/dist/styles/select.css +35 -0
- package/dist/styles/states.css +22 -0
- package/dist/tabs.cjs +2 -0
- package/dist/tabs.cjs.map +1 -0
- package/dist/tabs.js +132 -0
- package/dist/tabs.js.map +1 -0
- package/dist/themes/dark.css +96 -0
- package/dist/themes/default.css +126 -0
- package/dist/themes/high-contrast.css +98 -0
- package/dist/types.d.ts +168 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/useCombo-D_vriwVz.cjs +2 -0
- package/dist/useCombo-D_vriwVz.cjs.map +1 -0
- package/dist/useCombo-gPeBdkRf.js +887 -0
- package/dist/useCombo-gPeBdkRf.js.map +1 -0
- package/dist/usePosition-6GfutqGX.cjs +2 -0
- package/dist/usePosition-6GfutqGX.cjs.map +1 -0
- package/dist/usePosition-DVw8IlwA.js +36 -0
- package/dist/usePosition-DVw8IlwA.js.map +1 -0
- package/package.json +219 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2025-03-25
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **`useCombo` hook** — Headless hook with 9 prop getters, 8 imperative actions, full state exposure
|
|
13
|
+
- `getInputProps`, `getToggleButtonProps`, `getLabelProps`, `getClearButtonProps`, `getMenuProps`, `getItemProps`, `getGroupProps`, `getChevronProps`, `getTriggerProps`
|
|
14
|
+
- Actions: `openMenu`, `closeMenu`, `toggleMenu`, `selectItem`, `removeItem`, `clearSelection`, `setInputValue`, `reset`
|
|
15
|
+
- **`<Combo>` component** — Pre-built component with render props
|
|
16
|
+
- `renderItem`, `renderEmpty`, `renderLoading`, `renderError`, `renderFooter`, `renderTrigger`, `renderGroupHeader`
|
|
17
|
+
- `classNames` slots for all sub-elements
|
|
18
|
+
- `label` prop with automatic `<label>` association
|
|
19
|
+
- `variant="select"` for non-editable dropdown
|
|
20
|
+
- `groups` prop for grouped item rendering
|
|
21
|
+
- `theme` prop for built-in theme switching
|
|
22
|
+
- **Portal component** — Renders listbox in a portal to escape overflow containers
|
|
23
|
+
- **LiveRegion component** — ARIA live region for screen reader announcements
|
|
24
|
+
- **Pure state machine** — 7 status states, 16+ actions, pure reducer
|
|
25
|
+
- **Keyboard navigation** — ArrowUp/Down, Enter, Escape, Home, End, Tab
|
|
26
|
+
- **CSS custom properties** — 40+ design tokens for full visual customization
|
|
27
|
+
- **Built-in themes** — Default, dark, and high-contrast
|
|
28
|
+
- **Icon system** — 5 swappable icon slots + 6 chevron styles (caret, arrow, angle, plusminus, dots, none)
|
|
29
|
+
- **Dual entry points** — `@reactzero/combo/hook` (4.2 kB) and `@reactzero/combo` (5.5 kB)
|
|
30
|
+
- **332 tests** — Full coverage with jest-axe accessibility validation
|
|
31
|
+
- **TypeScript** — Full generic type inference
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 motiondesignlv
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# @reactzero/combo
|
|
2
|
+
|
|
3
|
+
Headless, accessible React combo & select. **Zero dependencies.** ARIA 1.2 compliant, < 7 kB gzipped.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@reactzero/combo)
|
|
6
|
+
[](https://bundlephobia.com/package/@reactzero/combo)
|
|
7
|
+
[](./LICENSE)
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Zero dependencies** — Only peer depends on React 18+. No runtime surprises.
|
|
12
|
+
- **Headless architecture** — Full control over markup and styling via the `useCombo` hook
|
|
13
|
+
- **Pre-built component** — Drop-in `<Combo>` with render props for quick setup
|
|
14
|
+
- **ARIA 1.2 compliant** — Keyboard navigation, screen reader support, live region announcements
|
|
15
|
+
- **Tiny bundle** — Hook: 4.8 kB, Full: 6.7 kB, CSS: 2.5 kB (all brotli)
|
|
16
|
+
- **CSS custom properties** — 40+ design tokens, built-in dark and high-contrast themes
|
|
17
|
+
- **TypeScript first** — Full generic type inference on all APIs
|
|
18
|
+
- **Multiple variants** — Combobox, select dropdown, grouped items, custom triggers
|
|
19
|
+
|
|
20
|
+
## AI Reference
|
|
21
|
+
|
|
22
|
+
Download the [AI reference file](./ai-reference.md) and feed it to your AI coding assistant for complete API docs, examples, and patterns.
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install @reactzero/combo
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick Start — Headless Hook
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
import { useCombo } from '@reactzero/combo/hook';
|
|
34
|
+
|
|
35
|
+
function MyCombo() {
|
|
36
|
+
const items = ['Apple', 'Banana', 'Cherry', 'Date'];
|
|
37
|
+
|
|
38
|
+
const {
|
|
39
|
+
isOpen,
|
|
40
|
+
filteredItems,
|
|
41
|
+
highlightedIndex,
|
|
42
|
+
selectedItem,
|
|
43
|
+
getLabelProps,
|
|
44
|
+
getInputProps,
|
|
45
|
+
getToggleButtonProps,
|
|
46
|
+
getClearButtonProps,
|
|
47
|
+
getMenuProps,
|
|
48
|
+
getItemProps,
|
|
49
|
+
getChevronProps,
|
|
50
|
+
hasSelection,
|
|
51
|
+
icons,
|
|
52
|
+
chevronIcon,
|
|
53
|
+
} = useCombo({ items });
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<div>
|
|
57
|
+
<label {...getLabelProps()}>Fruit</label>
|
|
58
|
+
<div>
|
|
59
|
+
<input {...getInputProps({ placeholder: 'Search...' })} />
|
|
60
|
+
{hasSelection && <button {...getClearButtonProps()}>{icons.clear}</button>}
|
|
61
|
+
<button {...getToggleButtonProps()}>
|
|
62
|
+
<span {...getChevronProps()}>{chevronIcon}</span>
|
|
63
|
+
</button>
|
|
64
|
+
</div>
|
|
65
|
+
<ul {...getMenuProps()} style={{ display: isOpen ? 'block' : 'none' }}>
|
|
66
|
+
{isOpen &&
|
|
67
|
+
filteredItems.map((item, index) => (
|
|
68
|
+
<li
|
|
69
|
+
key={index}
|
|
70
|
+
{...getItemProps({ item, index })}
|
|
71
|
+
style={{
|
|
72
|
+
background: highlightedIndex === index ? '#dbeafe' : 'transparent',
|
|
73
|
+
fontWeight: selectedItem === item ? 600 : 400,
|
|
74
|
+
}}
|
|
75
|
+
>
|
|
76
|
+
{String(item)}
|
|
77
|
+
</li>
|
|
78
|
+
))}
|
|
79
|
+
</ul>
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Quick Start — Pre-built Component
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
import { Combo } from '@reactzero/combo';
|
|
89
|
+
import '@reactzero/combo/styles';
|
|
90
|
+
|
|
91
|
+
function App() {
|
|
92
|
+
return (
|
|
93
|
+
<Combo
|
|
94
|
+
items={['Apple', 'Banana', 'Cherry', 'Date']}
|
|
95
|
+
label="Favorite Fruit"
|
|
96
|
+
placeholder="Search fruits..."
|
|
97
|
+
onSelectedItemChange={(item) => console.log('Selected:', item)}
|
|
98
|
+
renderItem={({ item, isHighlighted, isSelected }) => (
|
|
99
|
+
<div style={{ fontWeight: isSelected ? 600 : 400 }}>
|
|
100
|
+
{String(item)}
|
|
101
|
+
</div>
|
|
102
|
+
)}
|
|
103
|
+
/>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Import Paths
|
|
109
|
+
|
|
110
|
+
| Path | Size | Description |
|
|
111
|
+
|------|------|-------------|
|
|
112
|
+
| `@reactzero/combo/hook` | 4.8 kB | Hook + types only |
|
|
113
|
+
| `@reactzero/combo` | 6.7 kB | Hook + Combo + Portal + LiveRegion |
|
|
114
|
+
| `@reactzero/combo/styles` | 2.5 kB | Base structural CSS |
|
|
115
|
+
| `@reactzero/combo/themes/dark.css` | — | Dark theme tokens |
|
|
116
|
+
| `@reactzero/combo/themes/high-contrast.css` | — | High-contrast theme tokens |
|
|
117
|
+
|
|
118
|
+
## Variants
|
|
119
|
+
|
|
120
|
+
### Select Dropdown
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
<Combo
|
|
124
|
+
items={['United States', 'Canada', 'Mexico']}
|
|
125
|
+
variant="select"
|
|
126
|
+
placeholder="Choose a country..."
|
|
127
|
+
/>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Grouped Items
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
<Combo
|
|
134
|
+
groups={[
|
|
135
|
+
{ label: 'Fruits', items: ['Apple', 'Banana'] },
|
|
136
|
+
{ label: 'Vegetables', items: ['Carrot', 'Broccoli'] },
|
|
137
|
+
]}
|
|
138
|
+
items={[]}
|
|
139
|
+
placeholder="Search..."
|
|
140
|
+
/>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Custom Trigger
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
<Combo
|
|
147
|
+
items={items}
|
|
148
|
+
renderTrigger={({ getInputProps, getToggleButtonProps, isOpen, chevronIcon }) => (
|
|
149
|
+
<div className="my-trigger">
|
|
150
|
+
<input {...getInputProps()} />
|
|
151
|
+
<button {...getToggleButtonProps()}>{chevronIcon}</button>
|
|
152
|
+
</div>
|
|
153
|
+
)}
|
|
154
|
+
/>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Theming
|
|
158
|
+
|
|
159
|
+
Override CSS custom properties to customize the appearance:
|
|
160
|
+
|
|
161
|
+
```css
|
|
162
|
+
.my-combobox {
|
|
163
|
+
--rzero-combo-input-border: 2px solid #6366f1;
|
|
164
|
+
--rzero-combo-input-focus-ring: 0 0 0 3px rgba(99, 102, 241, 0.3);
|
|
165
|
+
--rzero-combo-item-highlighted-bg: #e0e7ff;
|
|
166
|
+
--rzero-combo-item-selected-color: #4f46e5;
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Or use built-in themes:
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
<Combo items={items} theme="dark" />
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Documentation
|
|
177
|
+
|
|
178
|
+
- [Getting Started](https://reactzero-combo.dev/guide/getting-started)
|
|
179
|
+
- [Headless Usage](https://reactzero-combo.dev/guide/headless-usage)
|
|
180
|
+
- [Pre-built Component](https://reactzero-combo.dev/guide/pre-built)
|
|
181
|
+
- [Theming](https://reactzero-combo.dev/guide/theming)
|
|
182
|
+
- [Accessibility](https://reactzero-combo.dev/guide/accessibility)
|
|
183
|
+
- [API Reference — useCombo](https://reactzero-combo.dev/api/use-combo)
|
|
184
|
+
- [API Reference — Combo](https://reactzero-combo.dev/api/combo)
|
|
185
|
+
|
|
186
|
+
## License
|
|
187
|
+
|
|
188
|
+
[MIT](./LICENSE)
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";const e=require("react/jsx-runtime"),f=require("react"),Te=require("./useCombo-D_vriwVz.cjs"),Ie=require("./usePosition-6GfutqGX.cjs"),Pe=require("react-dom");function re({children:c,target:g,disabled:h}){if(h)return e.jsx(e.Fragment,{children:c});const d=g??(typeof document<"u"?document.body:null);return d?Pe.createPortal(c,d):null}const we={position:"absolute",width:"1px",height:"1px",padding:0,margin:"-1px",overflow:"hidden",clip:"rect(0,0,0,0)",whiteSpace:"nowrap",borderWidth:0};function oe({message:c,id:g,clearAfterMs:h=3e3}){const d=f.useRef(null);return f.useEffect(()=>{if(!d.current||!c)return;const p=d.current;p.textContent="";const I=requestAnimationFrame(()=>{p.textContent=c}),P=setTimeout(()=>{p.textContent=""},h);return()=>{cancelAnimationFrame(I),clearTimeout(P)}},[c,h]),e.jsx("div",{ref:d,id:g,role:"status","aria-live":"polite","aria-atomic":"true",style:we})}function l(...c){return c.filter(Boolean).join(" ")}function De(c){const{placeholder:g,label:h,theme:d,itemVariant:p,renderItem:I,renderEmpty:P,renderLoading:V,renderError:K,renderFooter:U,renderTrigger:A,renderGroupHeader:G,renderListHeader:W,hintText:O,errorText:w,minSelected:b,onInputKeyDown:J,classNames:n={},...r}=c,ne=f.useMemo(()=>r.groups&&!r.items?.length?r.groups.flatMap(t=>t.items):r.items,[r.groups,r.items]),se=Te.useCombo({...r,items:ne}),{isOpen:a,selectedItem:D,highlightedIndex:ae,filteredItems:j,hasSelection:v,triggerLabel:M,isLoading:x,isError:z,error:le,getLabelProps:ie,getInputProps:B,getToggleButtonProps:H,getClearButtonProps:k,getMenuProps:ce,getItemProps:Q,getGroupProps:de,getChevronProps:C,getTriggerProps:X,icons:R,chevronIcon:L,inputRef:Y,listboxRef:ue,triggerRef:me,inputValue:ge,openMenu:he,closeMenu:pe,clearSelection:be,removeItem:xe,selectedItems:m}=se,q=r.itemToString??(t=>t!=null?String(t):""),u=r.itemToValue??(t=>typeof t=="object"&&t!=null&&"value"in t?t.value:String(t)),fe=r.isItemDisabled??(()=>!1),Z=r.variant==="select",S=r.mode==="multi",E=z||!!w,_=f.useRef(null),je=Z?me:S?_:Y,y=Ie.usePosition({triggerRef:je,listboxRef:ue,isOpen:a}),ve=f.useMemo(()=>{if(!a)return"";if(x)return"Loading options...";const t=j.length;return t===0?"No options available":`${t} option${t!==1?"s":""} available`},[a,x,j.length]),Ne=d?{"data-rzero-theme":d}:{},N=r.disabled===!0,ee=f.useMemo(()=>{const t=new Map;return j.forEach((o,s)=>{t.set(u(o),s)}),t},[j,u]),$=f.useMemo(()=>r.groups?r.groups.map((t,o)=>{const s=t.items.map(i=>({item:i,globalIndex:ee.get(u(i))})).filter(i=>i.globalIndex!==void 0);return{group:t,groupIdx:o,items:s}}).filter(t=>t.items.length>0):null,[r.groups,ee,u]);function te(t,o){const s=ae===o,i=S?m.some(ye=>u(ye)===u(t)):D!=null&&u(D)===u(t),T=fe(t),F=p&&p!=="default"?p:void 0;return I?e.jsx("li",{...Q({item:t,index:o,variant:F}),className:l("rzero-combo-item",n.item,s&&n.itemHighlighted,i&&n.itemSelected,T&&n.itemDisabled),children:I({item:t,index:o,isHighlighted:s,isSelected:i,isDisabled:T})},o):e.jsxs("li",{...Q({item:t,index:o,variant:F}),className:l("rzero-combo-item",n.item,s&&n.itemHighlighted,i&&n.itemSelected,T&&n.itemDisabled),children:[e.jsx("span",{children:q(t)}),i&&!F&&e.jsx("span",{className:"rzero-combo-check","aria-hidden":"true",children:R.check})]},o)}function ze(){if(S)return e.jsxs("div",{ref:_,className:l("rzero-combo-trigger","rzero-combo-trigger--multi",n.trigger),"data-rzero-trigger":"","data-disabled":N||void 0,"data-readonly":r.readOnly||void 0,"data-loading":r.disabled==="loading"||void 0,"data-error":E||void 0,onClick:o=>{if(N||r.readOnly)return;const s=o.target;s.tagName==="INPUT"||s.tagName==="BUTTON"||s.closest("button")||(Y.current?.focus(),a||he())},onMouseDown:o=>{o.target.tagName!=="INPUT"&&o.preventDefault()},children:[e.jsxs("div",{className:"rzero-combo-trigger-content",children:[m.map(o=>e.jsxs("span",{className:"rzero-combo-chip",children:[e.jsx("span",{className:"rzero-combo-chip-label",children:q(o)}),!N&&!r.readOnly&&e.jsx("button",{type:"button",className:"rzero-combo-chip-remove","aria-label":`Remove ${q(o)}`,onMouseDown:s=>s.preventDefault(),onClick:s=>{s.stopPropagation(),xe(o)},children:e.jsx("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.5",strokeLinecap:"round",children:e.jsx("path",{d:"M18 6 6 18M6 6l12 12"})})})]},String(u(o)))),e.jsx("input",{...B({placeholder:m.length===0?g:"",className:n.input,onKeyDown:J})})]}),e.jsxs("div",{className:"rzero-combo-trigger-actions",children:[v&&!N&&!r.readOnly&&e.jsx("button",{...k({className:n.clearButton}),children:R.clear}),e.jsx("button",{...H({className:n.toggleButton}),children:e.jsx("span",{...C(),children:L})})]})]});const t=v&&a&&ge!==M;return e.jsxs("div",{className:l("rzero-combo-trigger",n.trigger),"data-rzero-trigger":"","data-disabled":N||void 0,"data-readonly":r.readOnly||void 0,"data-loading":r.disabled==="loading"||void 0,"data-error":E||void 0,"data-has-hidden-selection":t||void 0,children:[e.jsx("input",{...B({placeholder:v?M:g,className:n.input,onKeyDown:J})}),v&&!N&&!r.readOnly&&e.jsx("button",{...k({className:n.clearButton}),children:R.clear}),e.jsx("button",{...H({className:n.toggleButton}),children:e.jsx("span",{...C(),children:L})})]})}function Se(){return e.jsxs("button",{...X({className:l("rzero-combo-select-trigger",n.trigger),"data-error":E||void 0}),children:[e.jsx("span",{className:"rzero-combo-select-value",children:v?M:g||"Select..."}),e.jsx("span",{...C(),children:L})]})}return e.jsxs("div",{className:l("rzero-combo",n.root),...Ne,"data-rzero-root":"",children:[h&&e.jsx("label",{...ie({className:l("rzero-combo-label",n.label)}),children:h}),A?A({getInputProps:B,getToggleButtonProps:H,getTriggerProps:X,getClearButtonProps:k,getChevronProps:C,selectedItem:D,hasSelection:v,triggerLabel:M,isOpen:a,icons:R,chevronIcon:L}):Z?Se():ze(),(w||O||S&&(b!=null||r.maxSelected!=null))&&e.jsxs("div",{className:"rzero-combo-meta",children:[w?e.jsx("span",{className:"rzero-combo-error-text",children:w}):O?e.jsx("span",{className:"rzero-combo-hint",children:O}):e.jsx("span",{}),S&&(b!=null||r.maxSelected!=null)&&e.jsxs("span",{className:"rzero-combo-selection-count","data-warning":b!=null&&m.length<b||r.maxSelected!=null&&m.length>=r.maxSelected?"":void 0,children:[m.length,r.maxSelected!=null?` / ${r.maxSelected}`:"",b!=null&&m.length<b?` · min ${b}`:""]})]}),e.jsx(re,{disabled:r.disablePortal,target:r.portalTarget,children:e.jsxs("ul",{...ce({className:l("rzero-combo-list",n.list),style:a?{position:"absolute",top:y.top,left:y.left,width:y.width,maxHeight:y.maxHeight,overflow:"auto",zIndex:"var(--rzero-combo-z-index, 9999)",margin:0}:{display:"none"}}),"data-state":a?"open":"closed","data-placement":y.placement,hidden:!a,children:[a&&W&&e.jsx("li",{role:"presentation",children:W()}),a&&x&&e.jsx("li",{role:"presentation",className:l("rzero-combo-loading",n.loadingState),children:V?V():"Loading..."}),a&&!x&&z&&K&&e.jsx("li",{role:"presentation",className:l("rzero-combo-error",n.errorState),children:K({error:le})}),a&&!x&&!z&&j.length===0&&e.jsx("li",{role:"presentation",className:l("rzero-combo-empty",n.emptyState),children:P?P():"No results found"}),a&&!x&&!z&&$&&$.map(({group:t,groupIdx:o,items:s})=>e.jsxs("li",{role:"presentation",children:[e.jsx("div",{...de({group:t,index:o}),className:l("rzero-combo-group-header",n.groupHeader),children:G?G({group:t,index:o}):t.label}),e.jsx("ul",{role:"group","aria-label":t.label,style:{listStyle:"none",margin:0,padding:0},children:s.map(({item:i,globalIndex:T})=>te(i,T))})]},`group-${o}`)),a&&!x&&!z&&!$&&j.map((t,o)=>te(t,o)),a&&U&&e.jsx("li",{role:"presentation",className:l("rzero-combo-footer",n.footer),children:U({selectedItem:D,selectedItems:m,closeMenu:pe,clearSelection:be})})]})}),e.jsx(oe,{message:ve})]})}exports.Combo=De;exports.LiveRegion=oe;exports.Portal=re;
|
|
2
|
+
//# sourceMappingURL=Combo-Cx3kSkop.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Combo-Cx3kSkop.cjs","sources":["../src/components/Portal.tsx","../src/components/LiveRegion.tsx","../src/components/Combo.tsx"],"sourcesContent":["import { createPortal } from 'react-dom';\nimport type { ReactNode } from 'react';\n\nexport interface PortalProps {\n children: ReactNode;\n target?: Element | null;\n disabled?: boolean;\n}\n\nexport function Portal({ children, target, disabled }: PortalProps) {\n if (disabled) return <>{children}</>;\n\n const mountNode =\n target ?? (typeof document !== 'undefined' ? document.body : null);\n if (!mountNode) return null;\n\n return createPortal(children, mountNode);\n}\n","import { useEffect, useRef } from 'react';\n\nexport interface LiveRegionProps {\n message: string;\n id?: string;\n clearAfterMs?: number;\n}\n\nconst srOnlyStyle: React.CSSProperties = {\n position: 'absolute',\n width: '1px',\n height: '1px',\n padding: 0,\n margin: '-1px',\n overflow: 'hidden',\n clip: 'rect(0,0,0,0)',\n whiteSpace: 'nowrap',\n borderWidth: 0,\n};\n\nexport function LiveRegion({\n message,\n id,\n clearAfterMs = 3000,\n}: LiveRegionProps) {\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!ref.current || !message) return;\n const el = ref.current;\n // Clear then set — forces screen readers to re-announce\n el.textContent = '';\n const raf = requestAnimationFrame(() => {\n el.textContent = message;\n });\n const timer = setTimeout(() => {\n el.textContent = '';\n }, clearAfterMs);\n return () => {\n cancelAnimationFrame(raf);\n clearTimeout(timer);\n };\n }, [message, clearAfterMs]);\n\n return (\n <div\n ref={ref}\n id={id}\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n style={srOnlyStyle}\n />\n );\n}\n","import React, { useMemo, useRef } from 'react';\nimport { useCombo } from '../hooks/useCombo';\nimport { usePosition } from '../hooks/usePosition';\nimport { Portal } from './Portal';\nimport { LiveRegion } from './LiveRegion';\nimport type {\n UseComboOptions,\n UseComboReturn,\n ComboGroup,\n IconSlots,\n} from '../types';\n\nexport interface ComboClassNames {\n root?: string;\n label?: string;\n input?: string;\n trigger?: string;\n popover?: string;\n list?: string;\n item?: string;\n itemSelected?: string;\n itemHighlighted?: string;\n itemDisabled?: string;\n clearButton?: string;\n toggleButton?: string;\n groupHeader?: string;\n loadingState?: string;\n emptyState?: string;\n errorState?: string;\n footer?: string;\n}\n\nexport type ItemVariant = 'default' | 'checkbox' | 'radio';\n\nexport interface ComboProps<T> extends UseComboOptions<T> {\n placeholder?: string;\n label?: string;\n theme?: 'default' | 'dark' | 'high-contrast' | 'system';\n itemVariant?: ItemVariant;\n renderItem?: (props: {\n item: T;\n index: number;\n isHighlighted: boolean;\n isSelected: boolean;\n isDisabled: boolean;\n }) => React.ReactNode;\n renderEmpty?: () => React.ReactNode;\n renderLoading?: () => React.ReactNode;\n renderError?: (props: { error: Error | null }) => React.ReactNode;\n renderFooter?: (props: {\n selectedItem: T | null;\n selectedItems: T[];\n closeMenu: () => void;\n clearSelection: () => void;\n }) => React.ReactNode;\n renderTrigger?: (props: {\n getInputProps: UseComboReturn<T>['getInputProps'];\n getToggleButtonProps: UseComboReturn<T>['getToggleButtonProps'];\n getTriggerProps: UseComboReturn<T>['getTriggerProps'];\n getClearButtonProps: UseComboReturn<T>['getClearButtonProps'];\n getChevronProps: UseComboReturn<T>['getChevronProps'];\n selectedItem: T | null;\n hasSelection: boolean;\n triggerLabel: string;\n isOpen: boolean;\n icons: IconSlots;\n chevronIcon: React.ReactNode;\n }) => React.ReactNode;\n renderGroupHeader?: (props: {\n group: ComboGroup<T>;\n index: number;\n }) => React.ReactNode;\n renderListHeader?: () => React.ReactNode;\n hintText?: string;\n errorText?: string;\n minSelected?: number;\n onInputKeyDown?: (e: React.KeyboardEvent) => void;\n classNames?: ComboClassNames;\n}\n\nfunction cx(...args: (string | false | null | undefined)[]): string {\n return args.filter(Boolean).join(' ');\n}\n\nexport function Combo<T>(props: ComboProps<T>) {\n const {\n placeholder,\n label,\n theme,\n itemVariant,\n renderItem,\n renderEmpty,\n renderLoading,\n renderError,\n renderFooter,\n renderTrigger,\n renderGroupHeader,\n renderListHeader,\n hintText,\n errorText,\n minSelected,\n onInputKeyDown,\n classNames = {},\n ...hookOptions\n } = props;\n\n // When groups are provided, flatten into items for the hook\n const flatItems = useMemo(() => {\n if (hookOptions.groups && !hookOptions.items?.length) {\n return hookOptions.groups.flatMap((g) => g.items);\n }\n return hookOptions.items;\n }, [hookOptions.groups, hookOptions.items]);\n\n const combo = useCombo({ ...hookOptions, items: flatItems });\n const {\n isOpen,\n selectedItem,\n highlightedIndex,\n filteredItems,\n hasSelection,\n triggerLabel,\n isLoading,\n isError,\n error,\n getLabelProps,\n getInputProps,\n getToggleButtonProps,\n getClearButtonProps,\n getMenuProps,\n getItemProps,\n getGroupProps,\n getChevronProps,\n getTriggerProps,\n icons,\n chevronIcon,\n inputRef,\n listboxRef,\n triggerRef,\n inputValue,\n openMenu,\n closeMenu,\n clearSelection,\n removeItem,\n selectedItems,\n } = combo;\n\n const itemToString =\n hookOptions.itemToString ??\n ((item: T | null) => (item != null ? String(item) : ''));\n const itemToValue =\n hookOptions.itemToValue ??\n ((item: T) => {\n if (\n typeof item === 'object' &&\n item != null &&\n 'value' in (item as object)\n )\n return (item as Record<string, unknown>).value as string | number;\n return String(item);\n });\n const isItemDisabled = hookOptions.isItemDisabled ?? (() => false);\n const isSelectVariant = hookOptions.variant === 'select';\n const isMultiMode = hookOptions.mode === 'multi';\n const hasError = isError || !!errorText;\n\n // Stable ref for the multi-select trigger container so the popover\n // anchors to the outer div rather than the input (which shifts as chips wrap).\n const multiTriggerRef = useRef<HTMLDivElement>(null);\n\n const positionTriggerRef = isSelectVariant\n ? triggerRef\n : isMultiMode\n ? multiTriggerRef\n : inputRef;\n\n const position = usePosition({\n triggerRef: positionTriggerRef,\n listboxRef,\n isOpen,\n });\n\n const liveMessage = useMemo(() => {\n if (!isOpen) return '';\n if (isLoading) return 'Loading options...';\n const count = filteredItems.length;\n return count === 0\n ? 'No options available'\n : `${count} option${count !== 1 ? 's' : ''} available`;\n }, [isOpen, isLoading, filteredItems.length]);\n\n const themeAttr = theme ? { 'data-rzero-theme': theme } : {};\n const isDisabled = hookOptions.disabled === true;\n\n // Build a value → filteredItems index map for grouped rendering\n const valueToIndex = useMemo(() => {\n const map = new Map<string | number, number>();\n filteredItems.forEach((item, index) => {\n map.set(itemToValue(item), index);\n });\n return map;\n }, [filteredItems, itemToValue]);\n\n // Compute grouped items with global indices\n const groupedItems = useMemo(() => {\n if (!hookOptions.groups) return null;\n return hookOptions.groups\n .map((group, groupIdx) => {\n const items = group.items\n .map((item) => ({\n item,\n globalIndex: valueToIndex.get(itemToValue(item)),\n }))\n .filter(\n (entry): entry is { item: T; globalIndex: number } =>\n entry.globalIndex !== undefined,\n );\n return { group, groupIdx, items };\n })\n .filter((g) => g.items.length > 0);\n }, [hookOptions.groups, valueToIndex, itemToValue]);\n\n // Shared item renderer\n\n function renderItemLi(item: T, index: number) {\n const isHighlighted = highlightedIndex === index;\n const isSelected = isMultiMode\n ? selectedItems.some((si: T) => itemToValue(si) === itemToValue(item))\n : selectedItem != null &&\n itemToValue(selectedItem) === itemToValue(item);\n const itemDisabled = isItemDisabled(item);\n\n const variantForItem =\n itemVariant && itemVariant !== 'default' ? itemVariant : undefined;\n\n if (renderItem) {\n return (\n <li\n key={index}\n {...getItemProps({ item, index, variant: variantForItem })}\n className={cx(\n 'rzero-combo-item',\n classNames.item,\n isHighlighted && classNames.itemHighlighted,\n isSelected && classNames.itemSelected,\n itemDisabled && classNames.itemDisabled,\n )}\n >\n {renderItem({\n item,\n index,\n isHighlighted,\n isSelected,\n isDisabled: itemDisabled,\n })}\n </li>\n );\n }\n\n return (\n <li\n key={index}\n {...getItemProps({ item, index, variant: variantForItem })}\n className={cx(\n 'rzero-combo-item',\n classNames.item,\n isHighlighted && classNames.itemHighlighted,\n isSelected && classNames.itemSelected,\n itemDisabled && classNames.itemDisabled,\n )}\n >\n <span>{itemToString(item)}</span>\n {isSelected && !variantForItem && (\n <span className=\"rzero-combo-check\" aria-hidden=\"true\">\n {icons.check}\n </span>\n )}\n </li>\n );\n }\n\n // Default trigger (input variant)\n function renderDefaultTrigger() {\n if (isMultiMode) {\n return (\n <div\n ref={multiTriggerRef}\n className={cx('rzero-combo-trigger', 'rzero-combo-trigger--multi', classNames.trigger)}\n data-rzero-trigger=\"\"\n data-disabled={isDisabled || undefined}\n data-readonly={hookOptions.readOnly || undefined}\n data-loading={hookOptions.disabled === 'loading' || undefined}\n data-error={hasError || undefined}\n onClick={(e) => {\n if (isDisabled || hookOptions.readOnly) return;\n // Only handle clicks on the container itself, not on input/buttons\n const target = e.target as HTMLElement;\n if (target.tagName === 'INPUT' || target.tagName === 'BUTTON' || target.closest('button')) return;\n inputRef.current?.focus();\n if (!isOpen) openMenu();\n }}\n onMouseDown={(e) => {\n // Prevent blur when clicking the container (not input)\n const target = e.target as HTMLElement;\n if (target.tagName !== 'INPUT') {\n e.preventDefault();\n }\n }}\n >\n <div className=\"rzero-combo-trigger-content\">\n {selectedItems.map((item: T) => (\n <span className=\"rzero-combo-chip\" key={String(itemToValue(item))}>\n <span className=\"rzero-combo-chip-label\">{itemToString(item)}</span>\n {!isDisabled && !hookOptions.readOnly && (\n <button\n type=\"button\"\n className=\"rzero-combo-chip-remove\"\n aria-label={`Remove ${itemToString(item)}`}\n onMouseDown={(e) => e.preventDefault()}\n onClick={(e) => {\n e.stopPropagation();\n removeItem(item);\n }}\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\">\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n </button>\n )}\n </span>\n ))}\n <input\n {...getInputProps({\n placeholder: selectedItems.length === 0 ? placeholder : '',\n className: classNames.input,\n onKeyDown: onInputKeyDown,\n } as Record<string, unknown>)}\n />\n </div>\n\n <div className=\"rzero-combo-trigger-actions\">\n {hasSelection && !isDisabled && !hookOptions.readOnly && (\n <button\n {...getClearButtonProps({\n className: classNames.clearButton,\n } as Record<string, unknown>)}\n >\n {icons.clear}\n </button>\n )}\n\n <button\n {...getToggleButtonProps({\n className: classNames.toggleButton,\n } as Record<string, unknown>)}\n >\n <span {...getChevronProps()}>{chevronIcon}</span>\n </button>\n </div>\n </div>\n );\n }\n\n const hasHiddenSelection = hasSelection && isOpen && inputValue !== triggerLabel;\n\n return (\n <div\n className={cx('rzero-combo-trigger', classNames.trigger)}\n data-rzero-trigger=\"\"\n data-disabled={isDisabled || undefined}\n data-readonly={hookOptions.readOnly || undefined}\n data-loading={hookOptions.disabled === 'loading' || undefined}\n data-error={hasError || undefined}\n data-has-hidden-selection={hasHiddenSelection || undefined}\n >\n <input\n {...getInputProps({\n placeholder: hasSelection ? triggerLabel : placeholder,\n className: classNames.input,\n onKeyDown: onInputKeyDown,\n } as Record<string, unknown>)}\n />\n\n {hasSelection && !isDisabled && !hookOptions.readOnly && (\n <button\n {...getClearButtonProps({\n className: classNames.clearButton,\n } as Record<string, unknown>)}\n >\n {icons.clear}\n </button>\n )}\n\n <button\n {...getToggleButtonProps({\n className: classNames.toggleButton,\n } as Record<string, unknown>)}\n >\n <span {...getChevronProps()}>{chevronIcon}</span>\n </button>\n </div>\n );\n }\n\n // Select variant trigger\n function renderSelectTrigger() {\n return (\n <button\n {...getTriggerProps({\n className: cx('rzero-combo-select-trigger', classNames.trigger),\n 'data-error': hasError || undefined,\n } as Record<string, unknown>)}\n >\n <span className=\"rzero-combo-select-value\">\n {hasSelection ? triggerLabel : placeholder || 'Select...'}\n </span>\n <span {...getChevronProps()}>{chevronIcon}</span>\n </button>\n );\n }\n\n return (\n <div\n className={cx('rzero-combo', classNames.root)}\n {...themeAttr}\n data-rzero-root=\"\"\n >\n {/* Label */}\n {label && (\n <label\n {...getLabelProps({\n className: cx('rzero-combo-label', classNames.label),\n } as Record<string, unknown>)}\n >\n {label}\n </label>\n )}\n\n {/* Trigger */}\n {renderTrigger\n ? renderTrigger({\n getInputProps,\n getToggleButtonProps,\n getTriggerProps,\n getClearButtonProps,\n getChevronProps,\n selectedItem,\n hasSelection,\n triggerLabel,\n isOpen,\n icons,\n chevronIcon,\n })\n : isSelectVariant\n ? renderSelectTrigger()\n : renderDefaultTrigger()}\n\n {/* Meta row: hint/error text + selection count */}\n {(errorText || hintText || (isMultiMode && (minSelected != null || hookOptions.maxSelected != null))) && (\n <div className=\"rzero-combo-meta\">\n {errorText ? (\n <span className=\"rzero-combo-error-text\">{errorText}</span>\n ) : hintText ? (\n <span className=\"rzero-combo-hint\">{hintText}</span>\n ) : (\n <span />\n )}\n {isMultiMode && (minSelected != null || hookOptions.maxSelected != null) && (\n <span\n className=\"rzero-combo-selection-count\"\n data-warning={\n (minSelected != null && selectedItems.length < minSelected) ||\n (hookOptions.maxSelected != null && selectedItems.length >= hookOptions.maxSelected)\n ? ''\n : undefined\n }\n >\n {selectedItems.length}\n {hookOptions.maxSelected != null ? ` / ${hookOptions.maxSelected}` : ''}\n {minSelected != null && selectedItems.length < minSelected\n ? ` \\u00B7 min ${minSelected}`\n : ''}\n </span>\n )}\n </div>\n )}\n\n {/* Popover */}\n <Portal\n disabled={hookOptions.disablePortal}\n target={hookOptions.portalTarget}\n >\n <ul\n {...getMenuProps({\n className: cx('rzero-combo-list', classNames.list),\n style: isOpen\n ? ({\n position: 'absolute',\n top: position.top,\n left: position.left,\n width: position.width,\n maxHeight: position.maxHeight,\n overflow: 'auto',\n zIndex: 'var(--rzero-combo-z-index, 9999)',\n margin: 0,\n } as React.CSSProperties)\n : ({ display: 'none' } as React.CSSProperties),\n } as Record<string, unknown>)}\n data-state={isOpen ? 'open' : 'closed'}\n data-placement={position.placement}\n hidden={!isOpen}\n >\n {/* List header (e.g. tab strip) */}\n {isOpen && renderListHeader && (\n <li role=\"presentation\">\n {renderListHeader()}\n </li>\n )}\n\n {/* Loading */}\n {isOpen && isLoading && (\n <li\n role=\"presentation\"\n className={cx('rzero-combo-loading', classNames.loadingState)}\n >\n {renderLoading ? renderLoading() : 'Loading...'}\n </li>\n )}\n\n {/* Error */}\n {isOpen && !isLoading && isError && renderError && (\n <li\n role=\"presentation\"\n className={cx('rzero-combo-error', classNames.errorState)}\n >\n {renderError({ error })}\n </li>\n )}\n\n {/* Empty */}\n {isOpen &&\n !isLoading &&\n !isError &&\n filteredItems.length === 0 && (\n <li\n role=\"presentation\"\n className={cx('rzero-combo-empty', classNames.emptyState)}\n >\n {renderEmpty ? renderEmpty() : 'No results found'}\n </li>\n )}\n\n {/* Items — grouped rendering */}\n {isOpen &&\n !isLoading &&\n !isError &&\n groupedItems &&\n groupedItems.map(({ group, groupIdx, items }) => (\n <li key={`group-${groupIdx}`} role=\"presentation\">\n <div\n {...getGroupProps({ group, index: groupIdx })}\n className={cx(\n 'rzero-combo-group-header',\n classNames.groupHeader,\n )}\n >\n {renderGroupHeader\n ? renderGroupHeader({ group, index: groupIdx })\n : group.label}\n </div>\n <ul role=\"group\" aria-label={group.label} style={{ listStyle: 'none', margin: 0, padding: 0 }}>\n {items.map(({ item, globalIndex }) =>\n renderItemLi(item, globalIndex),\n )}\n </ul>\n </li>\n ))}\n\n {/* Items — flat rendering (no groups) */}\n {isOpen &&\n !isLoading &&\n !isError &&\n !groupedItems &&\n filteredItems.map((item, index) => renderItemLi(item, index))}\n\n {/* Footer */}\n {isOpen && renderFooter && (\n <li\n role=\"presentation\"\n className={cx('rzero-combo-footer', classNames.footer)}\n >\n {renderFooter({\n selectedItem,\n selectedItems,\n closeMenu,\n clearSelection,\n })}\n </li>\n )}\n </ul>\n </Portal>\n\n {/* Live region */}\n <LiveRegion message={liveMessage} />\n </div>\n );\n}\n"],"names":["Portal","children","target","disabled","jsx","Fragment","mountNode","createPortal","srOnlyStyle","LiveRegion","message","id","clearAfterMs","ref","useRef","useEffect","el","raf","timer","cx","args","Combo","props","placeholder","label","theme","itemVariant","renderItem","renderEmpty","renderLoading","renderError","renderFooter","renderTrigger","renderGroupHeader","renderListHeader","hintText","errorText","minSelected","onInputKeyDown","classNames","hookOptions","flatItems","useMemo","g","combo","useCombo","isOpen","selectedItem","highlightedIndex","filteredItems","hasSelection","triggerLabel","isLoading","isError","error","getLabelProps","getInputProps","getToggleButtonProps","getClearButtonProps","getMenuProps","getItemProps","getGroupProps","getChevronProps","getTriggerProps","icons","chevronIcon","inputRef","listboxRef","triggerRef","inputValue","openMenu","closeMenu","clearSelection","removeItem","selectedItems","itemToString","item","itemToValue","isItemDisabled","isSelectVariant","isMultiMode","hasError","multiTriggerRef","positionTriggerRef","position","usePosition","liveMessage","count","themeAttr","isDisabled","valueToIndex","map","index","groupedItems","group","groupIdx","items","entry","renderItemLi","isHighlighted","isSelected","si","itemDisabled","variantForItem","jsxs","renderDefaultTrigger","e","hasHiddenSelection","renderSelectTrigger","globalIndex"],"mappings":"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"}
|