@magicx-eng/ai-autocomplete-react 0.1.3 → 0.1.5
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/README.md +84 -72
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +32 -37
- package/dist/index.d.ts +32 -37
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,6 +9,8 @@ A React/TypeScript SDK that provides a guided AI-powered autocomplete experience
|
|
|
9
9
|
- **Keyboard navigation** — arrow keys, enter to submit, tab to autocomplete
|
|
10
10
|
- **Client-side filtering** — instant substring filtering on every keystroke
|
|
11
11
|
- **Option overrides** — inject or dynamically generate client-side options per suggestion type
|
|
12
|
+
- **Controlled & uncontrolled** — works out of the box or integrates with external state
|
|
13
|
+
- **Ref forwarding** — imperative `focus()` and `reset()` via ref
|
|
12
14
|
- **Accessible** — ARIA combobox pattern with `role="listbox"`, `aria-activedescendant`
|
|
13
15
|
- **Lightweight** — zero dependencies beyond React
|
|
14
16
|
- **TypeScript first** — full type definitions shipped with the package
|
|
@@ -39,6 +41,7 @@ import { AIAutocomplete } from "@magicx-eng/ai-autocomplete-react";
|
|
|
39
41
|
function App() {
|
|
40
42
|
return (
|
|
41
43
|
<AIAutocomplete
|
|
44
|
+
apiConfig={{ apiKey: "your_api_key" }}
|
|
42
45
|
onSubmit={(result) => {
|
|
43
46
|
console.log(result.query); // "Create a email"
|
|
44
47
|
console.log(result.raw_query); // "Create a {{TASK_1}}"
|
|
@@ -69,7 +72,8 @@ function App() {
|
|
|
69
72
|
error,
|
|
70
73
|
reset,
|
|
71
74
|
} = useAIAutocomplete({
|
|
72
|
-
onSubmit: () => handleMySubmit(),
|
|
75
|
+
onSubmit: (result) => handleMySubmit(result),
|
|
76
|
+
apiConfig: { apiKey: "your_api_key" },
|
|
73
77
|
});
|
|
74
78
|
|
|
75
79
|
return (
|
|
@@ -81,6 +85,38 @@ function App() {
|
|
|
81
85
|
}
|
|
82
86
|
```
|
|
83
87
|
|
|
88
|
+
### Controlled Mode
|
|
89
|
+
|
|
90
|
+
Pass `value` and `onChange` to control the text externally:
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
const [text, setText] = useState("");
|
|
94
|
+
const [params, setParams] = useState([]);
|
|
95
|
+
|
|
96
|
+
<AIAutocomplete
|
|
97
|
+
value={text}
|
|
98
|
+
onChange={setText}
|
|
99
|
+
completedParams={params}
|
|
100
|
+
onParamsChange={setParams}
|
|
101
|
+
onSubmit={(result) => console.log(result)}
|
|
102
|
+
/>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Imperative Handle
|
|
106
|
+
|
|
107
|
+
Use a ref to focus or reset programmatically:
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
import { useRef } from "react";
|
|
111
|
+
import { AIAutocomplete, type AIAutocompleteHandle } from "@magicx-eng/ai-autocomplete-react";
|
|
112
|
+
|
|
113
|
+
const ref = useRef<AIAutocompleteHandle>(null);
|
|
114
|
+
// ref.current?.focus()
|
|
115
|
+
// ref.current?.reset()
|
|
116
|
+
|
|
117
|
+
<AIAutocomplete ref={ref} onSubmit={handleSubmit} />
|
|
118
|
+
```
|
|
119
|
+
|
|
84
120
|
## API Reference
|
|
85
121
|
|
|
86
122
|
### `<AIAutocomplete />`
|
|
@@ -89,10 +125,26 @@ The full component. Owns the input, pills, dropdown, and keyboard navigation.
|
|
|
89
125
|
|
|
90
126
|
| Prop | Type | Default | Description |
|
|
91
127
|
|---|---|---|---|
|
|
92
|
-
| `onSubmit` | `(result: AutocompleteResult) => void` | **required** | Called
|
|
93
|
-
| `
|
|
94
|
-
| `
|
|
95
|
-
| `
|
|
128
|
+
| `onSubmit` | `(result: AutocompleteResult) => void` | **required** | Called on Enter or submit button. |
|
|
129
|
+
| `onError?` | `(error: Error) => void` | — | Called when a fetch fails. |
|
|
130
|
+
| `apiConfig?` | `APIConfig` | — | Runtime API configuration (see below). |
|
|
131
|
+
| `optionOverrides?` | `Record<string, (query: string) => SuggestionOption[]>` | — | Override options per suggestion type. |
|
|
132
|
+
| `placeholder?` | `string` | — | Fallback placeholder when the server doesn't return one. |
|
|
133
|
+
| `className?` | `string` | — | CSS class applied to the container. |
|
|
134
|
+
| `columns?` | `number` | `2` | Number of columns in the dropdown grid. |
|
|
135
|
+
| `value?` | `string` | — | Controlled text value. |
|
|
136
|
+
| `completedParams?` | `CompletedParamState[]` | — | Controlled completed params. |
|
|
137
|
+
| `onChange?` | `(value: string) => void` | — | Called when text changes (controlled mode). |
|
|
138
|
+
| `onParamsChange?` | `(params: CompletedParamState[]) => void` | — | Called when params change (controlled mode). |
|
|
139
|
+
| `ref?` | `Ref<AIAutocompleteHandle>` | — | Imperative handle with `focus()` and `reset()`. |
|
|
140
|
+
|
|
141
|
+
### `APIConfig`
|
|
142
|
+
|
|
143
|
+
| Field | Type | Description |
|
|
144
|
+
|---|---|---|
|
|
145
|
+
| `apiKey?` | `string` | API key for Authorization header. Falls back to `MAGICX_AI_AUTOCOMPLETE_API_KEY` env var. |
|
|
146
|
+
| `authScheme?` | `"Bearer" \| "Basic"` | Auth header scheme. Default: `"Bearer"`. |
|
|
147
|
+
| `headers?` | `Record<string, string>` | Additional headers merged into every request. |
|
|
96
148
|
|
|
97
149
|
### `AutocompleteResult`
|
|
98
150
|
|
|
@@ -104,39 +156,11 @@ The object passed to `onSubmit`:
|
|
|
104
156
|
| `raw_query` | `string` | Text with placeholder tokens (e.g. `"Create a {{TASK_1}}"`). |
|
|
105
157
|
| `completed_params` | `CompletedParam[]` | Array of filled parameter values. |
|
|
106
158
|
|
|
107
|
-
### `CompletedParam`
|
|
108
|
-
|
|
109
|
-
| Field | Type | Description |
|
|
110
|
-
|---|---|---|
|
|
111
|
-
| `placeholder` | `string` | The placeholder token (e.g. `"{{TASK_1}}"`). |
|
|
112
|
-
| `type` | `string` | Suggestion type (e.g. `"task"`, `"goal"`). |
|
|
113
|
-
| `text?` | `string` | The selected option text. |
|
|
114
|
-
| `kind` | `string \| null` | Category of the option, or `null`. |
|
|
115
|
-
|
|
116
|
-
### `<AIAutocompleteDropdown />`
|
|
117
|
-
|
|
118
|
-
The dropdown component. Used in Tier 2 for headless integration.
|
|
119
|
-
|
|
120
|
-
| Prop | Type | Description |
|
|
121
|
-
|---|---|---|
|
|
122
|
-
| `suggestions` | `Suggestion[]` | Suggestions to display (from `dropdownProps`). |
|
|
123
|
-
| `activeIndex` | `number` | Currently highlighted option index. |
|
|
124
|
-
| `onSelect` | `(option: SuggestionOption) => void` | Called when an option is selected. |
|
|
125
|
-
| `isOpen` | `boolean` | Whether the dropdown is visible. |
|
|
126
|
-
| `id` | `string` | Listbox ID for ARIA. |
|
|
127
|
-
| `className?` | `string` | CSS class applied to the dropdown. |
|
|
128
|
-
|
|
129
159
|
### `useAIAutocomplete(options)`
|
|
130
160
|
|
|
131
161
|
The core hook. Manages state, API calls, filtering, pills, and keyboard navigation.
|
|
132
162
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
| Field | Type | Default | Description |
|
|
136
|
-
|---|---|---|---|
|
|
137
|
-
| `onSubmit?` | `() => void` | — | Called when Enter is pressed with no option highlighted. |
|
|
138
|
-
| `optionOverrides?` | `OptionOverrides` | — | Override options per suggestion type (same as component prop). |
|
|
139
|
-
| `placeholder?` | `string` | — | Fallback placeholder. |
|
|
163
|
+
Accepts the same props as `<AIAutocomplete />` (except `className` and `ref`), with `onSubmit` receiving an `AutocompleteResult`.
|
|
140
164
|
|
|
141
165
|
#### Return Value
|
|
142
166
|
|
|
@@ -144,38 +168,37 @@ The core hook. Manages state, API calls, filtering, pills, and keyboard navigati
|
|
|
144
168
|
|---|---|---|
|
|
145
169
|
| `completedParams` | `CompletedParamState[]` | All filled parameters with client-side state. |
|
|
146
170
|
| `suggestionPills` | `Suggestion[]` | Unfilled suggestion pills (excludes placeholders). |
|
|
147
|
-
| `activePillIndex` | `number` | Index of the active pill (always `0`). |
|
|
148
171
|
| `setActivePill` | `(index: number) => void` | Reorder pills by moving one to the front. |
|
|
149
|
-
| `removeLastParam` | `() => void` | Remove the last completed parameter and restore its
|
|
150
|
-
| `reEditParam` | `(param: CompletedParamState) => void` | Remove a
|
|
172
|
+
| `removeLastParam` | `() => void` | Remove the last completed parameter and restore its pill. |
|
|
173
|
+
| `reEditParam` | `(param: CompletedParamState) => void` | Remove a completed parameter and restore its pill. |
|
|
151
174
|
| `reset` | `() => void` | Clear all state and re-fetch initial suggestions. |
|
|
152
|
-
| `segments` | `Segment[]` | Text segments for overlay rendering
|
|
175
|
+
| `segments` | `Segment[]` | Text segments for overlay rendering. |
|
|
153
176
|
| `suggestions` | `Suggestion[]` | All suggestions including placeholders. |
|
|
154
|
-
| `activeIndex` | `number` | Currently highlighted
|
|
177
|
+
| `activeIndex` | `number` | Currently highlighted option index (`-1` when none). |
|
|
155
178
|
| `isLoading` | `boolean` | Whether a fetch is in progress. |
|
|
179
|
+
| `isReady` | `boolean` | Whether the server considers the query complete. |
|
|
156
180
|
| `error` | `Error \| null` | Last fetch error, or `null`. |
|
|
157
|
-
| `inputProps` | `object` | Spread onto a `<textarea>` —
|
|
181
|
+
| `inputProps` | `object` | Spread onto a `<textarea>` — value, placeholder, onChange, onKeyDown, ARIA. |
|
|
158
182
|
| `dropdownProps` | `AIAutocompleteDropdownProps` | Spread onto `<AIAutocompleteDropdown />`. |
|
|
159
183
|
|
|
160
|
-
###
|
|
184
|
+
### `<AIAutocompleteDropdown />`
|
|
161
185
|
|
|
162
|
-
|
|
163
|
-
|---|---|---|
|
|
164
|
-
| `text` | `string` | Display text for the option. |
|
|
165
|
-
| `icon?` | `string` | Icon shown before the text (e.g. emoji). |
|
|
166
|
-
| `tag?` | `string` | Tag shown after the text in smaller, muted style. |
|
|
167
|
-
| `is_tappable` | `boolean` | Whether the option can be selected. Non-tappable options are always-visible hints. |
|
|
168
|
-
| `kind` | `string \| null` | Category of the option. |
|
|
169
|
-
| `metadata?` | `Record<string, unknown>` | Arbitrary metadata passed through with the selection. |
|
|
186
|
+
The dropdown component for Tier 2 headless integration. Spread `dropdownProps` from the hook.
|
|
170
187
|
|
|
171
|
-
|
|
188
|
+
## CSS Customization
|
|
172
189
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
190
|
+
The component uses CSS Modules with customizable CSS variables. Override them on a parent element:
|
|
191
|
+
|
|
192
|
+
```css
|
|
193
|
+
.my-autocomplete {
|
|
194
|
+
--ac-color-border-default: #ccc;
|
|
195
|
+
--ac-color-text-default: #333;
|
|
196
|
+
--ac-color-text-muted: #999;
|
|
197
|
+
--ac-color-bg-default: #fff;
|
|
198
|
+
--ac-color-background-default: #fff;
|
|
199
|
+
--ac-color-background-supportive: #f0f0f0;
|
|
200
|
+
}
|
|
201
|
+
```
|
|
179
202
|
|
|
180
203
|
## Keyboard Behavior
|
|
181
204
|
|
|
@@ -189,35 +212,24 @@ The core hook. Manages state, API calls, filtering, pills, and keyboard navigati
|
|
|
189
212
|
|
|
190
213
|
## Option Overrides
|
|
191
214
|
|
|
192
|
-
Use `optionOverrides` to inject client-side options per suggestion type
|
|
215
|
+
Use `optionOverrides` to inject client-side options per suggestion type:
|
|
193
216
|
|
|
194
217
|
```tsx
|
|
195
|
-
// Static options (ignore query)
|
|
196
218
|
const overrides = {
|
|
219
|
+
// Static options
|
|
197
220
|
account: () => [
|
|
198
221
|
{ text: "Savings", is_tappable: true, kind: null },
|
|
199
222
|
{ text: "Checking", is_tappable: true, kind: null },
|
|
200
223
|
],
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
// Dynamic options (use query)
|
|
204
|
-
const overrides = {
|
|
224
|
+
// Dynamic options based on filter query
|
|
205
225
|
value: (query) => {
|
|
206
226
|
const digits = query.replace(/\D/g, "");
|
|
207
227
|
if (!digits) return [{ text: "$100", is_tappable: true, kind: null }];
|
|
208
|
-
return [
|
|
209
|
-
{ text: `$${digits}`, is_tappable: true, kind: null },
|
|
210
|
-
{ text: `$${digits}0`, is_tappable: true, kind: null },
|
|
211
|
-
];
|
|
228
|
+
return [{ text: `$${digits}`, is_tappable: true, kind: null }];
|
|
212
229
|
},
|
|
213
230
|
};
|
|
214
231
|
|
|
215
|
-
|
|
216
|
-
const overrides = {
|
|
217
|
-
value: () => [
|
|
218
|
-
{ text: "Enter an amount", is_tappable: false, kind: null },
|
|
219
|
-
],
|
|
220
|
-
};
|
|
232
|
+
<AIAutocomplete optionOverrides={overrides} onSubmit={handleSubmit} />
|
|
221
233
|
```
|
|
222
234
|
|
|
223
235
|
Override options are prepended to server options (deduplicated by `text`). When a filter query is active and an override function exists, it replaces the default client-side filtering entirely.
|
package/dist/index.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
.container{position:relative;font-family:IBM Plex Sans,sans-serif}.checkmark{position:absolute;bottom:-130px;left:50%;transform:translate(-50%) translateY(8px) scale(.8);opacity:0;pointer-events:none;z-index:10;animation:none}.checkmarkVisible{animation:checkmarkFadeInOut 3s ease forwards}@keyframes checkmarkFadeInOut{0%{opacity:0;transform:translate(-50%) translateY(8px) scale(.8)}10%{opacity:1;transform:translate(-50%) translateY(0) scale(1)}80%{opacity:1;transform:translate(-50%) translateY(0) scale(1)}to{opacity:0;transform:translate(-50%) translateY(-8px) scale(.8)}}.checkmarkPath{stroke-dasharray:30;stroke-dashoffset:30}.checkmarkVisible .checkmarkPath{animation:drawCheck .4s ease forwards .1s}@keyframes drawCheck{to{stroke-dashoffset:0}}.inputWrapper{min-height:60px;padding:24px;border:1px solid var(--color-border-default, #9ea5b2);border-radius:23px;background:transparent;overflow:hidden;display:flex;align-items:center;gap:12px}.editorArea{position:relative;flex:1;min-width:0}.sizerContent{position:relative;z-index:1;pointer-events:none;min-height:60px;white-space:pre-wrap;word-break:break-word;font-family:inherit;font-size:21px;line-height:38px}.sizerText{color:transparent}.placeholderText{color:var(--color-text-muted, #c1c4cb);opacity:.7}.textarea{position:absolute;top:0;left:0;width:100%;height:100%;padding:0;border:none;background:transparent;color:var(--color-text-default, #fff);caret-color:var(--color-text-default, #fff);font-family:inherit;font-size:21px;line-height:38px;white-space:pre-wrap;word-break:break-word;outline:none;resize:none;overflow:hidden}.textarea::placeholder{color:var(--color-text-muted, #c1c4cb)}.submitButton{flex-shrink:0;width:36px;height:36px;border-radius:50%;border:none;background:var(--color-text-default, #fff);color:var(--color-bg-default, #000);cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0;transition:opacity .2s ease}.submitButton:hover{opacity:.85}.dropdown{position:absolute;left:0;right:0;top:100%;margin-top:6px;background:var(--color-background-default, #00002d);border-radius:23px;overflow:hidden;z-index:10;
|
|
1
|
+
.container{position:relative;font-family:IBM Plex Sans,sans-serif}.checkmark{position:absolute;bottom:-130px;left:50%;transform:translate(-50%) translateY(8px) scale(.8);opacity:0;pointer-events:none;z-index:10;animation:none}.checkmarkVisible{animation:checkmarkFadeInOut 3s ease forwards}@keyframes checkmarkFadeInOut{0%{opacity:0;transform:translate(-50%) translateY(8px) scale(.8)}10%{opacity:1;transform:translate(-50%) translateY(0) scale(1)}80%{opacity:1;transform:translate(-50%) translateY(0) scale(1)}to{opacity:0;transform:translate(-50%) translateY(-8px) scale(.8)}}.checkmarkPath{stroke-dasharray:30;stroke-dashoffset:30}.checkmarkVisible .checkmarkPath{animation:drawCheck .4s ease forwards .1s}@keyframes drawCheck{to{stroke-dashoffset:0}}.inputWrapper{min-height:60px;padding:24px;border:1px solid var(--ac-color-border-default, #9ea5b2);border-radius:23px;background:transparent;overflow:hidden;display:flex;align-items:center;gap:12px}.editorArea{position:relative;flex:1;min-width:0}.sizerContent{position:relative;z-index:1;pointer-events:none;min-height:60px;white-space:pre-wrap;word-break:break-word;font-family:inherit;font-size:21px;line-height:38px}.sizerText{color:transparent}.placeholderText{color:var(--ac-color-text-muted, #c1c4cb);opacity:.7}.textarea{position:absolute;top:0;left:0;width:100%;height:100%;padding:0;border:none;background:transparent;color:var(--ac-color-text-default, #fff);caret-color:var(--ac-color-text-default, #fff);font-family:inherit;font-size:21px;line-height:38px;white-space:pre-wrap;word-break:break-word;outline:none;resize:none;overflow:hidden}.textarea::placeholder{color:var(--ac-color-text-muted, #c1c4cb)}.submitButton{flex-shrink:0;width:36px;height:36px;border-radius:50%;border:none;background:var(--ac-color-text-default, #fff);color:var(--ac-color-bg-default, #000);cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0;transition:opacity .2s ease}.submitButton:hover{opacity:.85}.dropdown{position:absolute;left:0;right:0;top:100%;margin-top:6px;background:var(--ac-color-background-default, #00002d);border-radius:23px;overflow:hidden;z-index:10;opacity:0;pointer-events:none;transition:opacity .4s cubic-bezier(.4,0,.2,1)}.visible{opacity:1;pointer-events:auto}.grid{display:grid;grid-template-columns:1fr 1fr;gap:12px 18px;padding:18px 24px;max-height:192px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.3) transparent}.grid::-webkit-scrollbar{width:6px}.grid::-webkit-scrollbar-track{background:transparent}.grid::-webkit-scrollbar-thumb{background:#ffffff4d;border-radius:3px}.item{display:flex;align-items:center;font-family:IBM Plex Sans,sans-serif;font-size:21px;line-height:30px;color:var(--ac-color-text-muted, #c1c4cb);white-space:nowrap;opacity:.35;animation:fadeIn .4s cubic-bezier(.4,0,.2,1) forwards}.tappable{cursor:pointer}.tappable:hover{color:var(--ac-color-text-default, #fff)}.nonTappable{cursor:default;opacity:.3}.highlighted{color:var(--ac-color-text-default, #fff);opacity:.5}.tag{font-size:13px;margin-left:6px;opacity:.5}.list{position:relative;z-index:1;pointer-events:auto;display:inline-flex;gap:5px;align-items:center;vertical-align:middle;transform:translateY(-3px)}.pill{display:inline-flex;align-items:center;justify-content:center;height:36px;padding:7px 9px;border:none;border-radius:6px;background:var(--ac-color-background-supportive, #313255);color:var(--ac-color-text-muted, #c1c4cb);font-family:IBM Plex Sans,sans-serif;font-size:21px;line-height:30px;cursor:pointer;white-space:nowrap;animation:fadeIn .4s cubic-bezier(.4,0,.2,1) forwards}.pill:hover{filter:brightness(1.2)}.active{outline:1px solid #5a5b8a}@keyframes fadeIn{0%{opacity:0}}
|
|
2
2
|
/*# sourceMappingURL=index.css.map */
|
package/dist/index.css.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/AIAutocomplete.module.css","../src/AIAutocompleteDropdown.module.css","../src/components/SuggestionGrid.module.css","../src/components/SuggestionItem.module.css","../src/components/PillList.module.css"],"sourcesContent":[".container {\n position: relative;\n font-family: \"IBM Plex Sans\", sans-serif;\n}\n\n.checkmark {\n position: absolute;\n bottom: -130px;\n left: 50%;\n transform: translateX(-50%) translateY(8px) scale(0.8);\n opacity: 0;\n pointer-events: none;\n z-index: 10;\n animation: none;\n}\n\n.checkmarkVisible {\n animation: checkmarkFadeInOut 3s ease forwards;\n}\n\n@keyframes checkmarkFadeInOut {\n 0% {\n opacity: 0;\n transform: translateX(-50%) translateY(8px) scale(0.8);\n }\n 10% {\n opacity: 1;\n transform: translateX(-50%) translateY(0) scale(1);\n }\n 80% {\n opacity: 1;\n transform: translateX(-50%) translateY(0) scale(1);\n }\n 100% {\n opacity: 0;\n transform: translateX(-50%) translateY(-8px) scale(0.8);\n }\n}\n\n.checkmarkPath {\n stroke-dasharray: 30;\n stroke-dashoffset: 30;\n}\n\n.checkmarkVisible .checkmarkPath {\n animation: drawCheck 0.4s ease forwards 0.1s;\n}\n\n@keyframes drawCheck {\n to {\n stroke-dashoffset: 0;\n }\n}\n\n.inputWrapper {\n min-height: 60px;\n padding: 24px;\n border: 1px solid var(--color-border-default, #9ea5b2);\n border-radius: 23px;\n background: transparent;\n overflow: hidden;\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.editorArea {\n position: relative;\n flex: 1;\n min-width: 0;\n}\n\n.sizerContent {\n position: relative;\n z-index: 1;\n pointer-events: none;\n min-height: 60px;\n white-space: pre-wrap;\n word-break: break-word;\n font-family: inherit;\n font-size: 21px;\n line-height: 38px;\n}\n\n.sizerText {\n color: transparent;\n}\n\n.placeholderText {\n color: var(--color-text-muted, #c1c4cb);\n opacity: 0.7;\n}\n\n.textarea {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n padding: 0;\n border: none;\n background: transparent;\n color: var(--color-text-default, #fff);\n caret-color: var(--color-text-default, #fff);\n font-family: inherit;\n font-size: 21px;\n line-height: 38px;\n white-space: pre-wrap;\n word-break: break-word;\n outline: none;\n resize: none;\n overflow: hidden;\n}\n\n.textarea::placeholder {\n color: var(--color-text-muted, #c1c4cb);\n}\n\n.submitButton {\n flex-shrink: 0;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n border: none;\n background: var(--color-text-default, #fff);\n color: var(--color-bg-default, #000);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0;\n transition: opacity 0.2s ease;\n}\n\n.submitButton:hover {\n opacity: 0.85;\n}\n",".dropdown {\n position: absolute;\n left: 0;\n right: 0;\n top: 100%;\n margin-top: 6px;\n background: var(--color-background-default, #00002d);\n border-radius: 23px;\n overflow: hidden;\n z-index: 10;\n
|
|
1
|
+
{"version":3,"sources":["../src/AIAutocomplete.module.css","../src/AIAutocompleteDropdown.module.css","../src/components/SuggestionGrid.module.css","../src/components/SuggestionItem.module.css","../src/components/PillList.module.css"],"sourcesContent":[".container {\n position: relative;\n font-family: \"IBM Plex Sans\", sans-serif;\n}\n\n.checkmark {\n position: absolute;\n bottom: -130px;\n left: 50%;\n transform: translateX(-50%) translateY(8px) scale(0.8);\n opacity: 0;\n pointer-events: none;\n z-index: 10;\n animation: none;\n}\n\n.checkmarkVisible {\n animation: checkmarkFadeInOut 3s ease forwards;\n}\n\n@keyframes checkmarkFadeInOut {\n 0% {\n opacity: 0;\n transform: translateX(-50%) translateY(8px) scale(0.8);\n }\n 10% {\n opacity: 1;\n transform: translateX(-50%) translateY(0) scale(1);\n }\n 80% {\n opacity: 1;\n transform: translateX(-50%) translateY(0) scale(1);\n }\n 100% {\n opacity: 0;\n transform: translateX(-50%) translateY(-8px) scale(0.8);\n }\n}\n\n.checkmarkPath {\n stroke-dasharray: 30;\n stroke-dashoffset: 30;\n}\n\n.checkmarkVisible .checkmarkPath {\n animation: drawCheck 0.4s ease forwards 0.1s;\n}\n\n@keyframes drawCheck {\n to {\n stroke-dashoffset: 0;\n }\n}\n\n.inputWrapper {\n min-height: 60px;\n padding: 24px;\n border: 1px solid var(--ac-color-border-default, #9ea5b2);\n border-radius: 23px;\n background: transparent;\n overflow: hidden;\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.editorArea {\n position: relative;\n flex: 1;\n min-width: 0;\n}\n\n.sizerContent {\n position: relative;\n z-index: 1;\n pointer-events: none;\n min-height: 60px;\n white-space: pre-wrap;\n word-break: break-word;\n font-family: inherit;\n font-size: 21px;\n line-height: 38px;\n}\n\n.sizerText {\n color: transparent;\n}\n\n.placeholderText {\n color: var(--ac-color-text-muted, #c1c4cb);\n opacity: 0.7;\n}\n\n.textarea {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n padding: 0;\n border: none;\n background: transparent;\n color: var(--ac-color-text-default, #fff);\n caret-color: var(--ac-color-text-default, #fff);\n font-family: inherit;\n font-size: 21px;\n line-height: 38px;\n white-space: pre-wrap;\n word-break: break-word;\n outline: none;\n resize: none;\n overflow: hidden;\n}\n\n.textarea::placeholder {\n color: var(--ac-color-text-muted, #c1c4cb);\n}\n\n.submitButton {\n flex-shrink: 0;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n border: none;\n background: var(--ac-color-text-default, #fff);\n color: var(--ac-color-bg-default, #000);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0;\n transition: opacity 0.2s ease;\n}\n\n.submitButton:hover {\n opacity: 0.85;\n}\n",".dropdown {\n position: absolute;\n left: 0;\n right: 0;\n top: 100%;\n margin-top: 6px;\n background: var(--ac-color-background-default, #00002d);\n border-radius: 23px;\n overflow: hidden;\n z-index: 10;\n opacity: 0;\n pointer-events: none;\n transition: opacity 400ms cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.visible {\n opacity: 1;\n pointer-events: auto;\n}\n",".grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px 18px;\n padding: 18px 24px;\n max-height: 192px;\n overflow-y: auto;\n scrollbar-width: thin;\n scrollbar-color: rgba(255, 255, 255, 0.3) transparent;\n}\n\n.grid::-webkit-scrollbar {\n width: 6px;\n}\n\n.grid::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.grid::-webkit-scrollbar-thumb {\n background: rgba(255, 255, 255, 0.3);\n border-radius: 3px;\n}\n",".item {\n display: flex;\n align-items: center;\n font-family: \"IBM Plex Sans\", sans-serif;\n font-size: 21px;\n line-height: 30px;\n color: var(--ac-color-text-muted, #c1c4cb);\n white-space: nowrap;\n opacity: 0.35;\n animation: fadeIn 400ms cubic-bezier(0.4, 0, 0.2, 1) forwards;\n}\n\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n}\n\n.tappable {\n cursor: pointer;\n}\n\n.tappable:hover {\n color: var(--ac-color-text-default, #fff);\n}\n\n.nonTappable {\n cursor: default;\n opacity: 0.3;\n}\n\n.highlighted {\n color: var(--ac-color-text-default, #fff);\n opacity: 0.5;\n}\n\n.tag {\n font-size: 13px;\n margin-left: 6px;\n opacity: 0.5;\n}\n",".list {\n position: relative;\n z-index: 1;\n pointer-events: auto;\n display: inline-flex;\n gap: 5px;\n align-items: center;\n vertical-align: middle;\n transform: translateY(-3px);\n}\n\n.pill {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n height: 36px;\n padding: 7px 9px;\n border: none;\n border-radius: 6px;\n background: var(--ac-color-background-supportive, #313255);\n color: var(--ac-color-text-muted, #c1c4cb);\n font-family: \"IBM Plex Sans\", sans-serif;\n font-size: 21px;\n line-height: 30px;\n cursor: pointer;\n white-space: nowrap;\n animation: fadeIn 400ms cubic-bezier(0.4, 0, 0.2, 1) forwards;\n}\n\n.pill:hover {\n filter: brightness(1.2);\n}\n\n.active {\n outline: 1px solid #5a5b8a;\n}\n\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react from 'react';
|
|
2
2
|
import { ChangeEvent, KeyboardEvent } from 'react';
|
|
3
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
4
|
|
|
4
5
|
type TaskKind = "automation" | "email" | "insight";
|
|
5
6
|
interface CompletedParam {
|
|
@@ -22,37 +23,8 @@ interface Suggestion {
|
|
|
22
23
|
required: boolean;
|
|
23
24
|
options: SuggestionOption[];
|
|
24
25
|
}
|
|
25
|
-
interface InputItem {
|
|
26
|
-
type: string;
|
|
27
|
-
text: string;
|
|
28
|
-
state: "completed" | "in_progress";
|
|
29
|
-
}
|
|
30
|
-
interface AutocompleteRequest {
|
|
31
|
-
data: {
|
|
32
|
-
raw_query: string;
|
|
33
|
-
completed_params: CompletedParam[];
|
|
34
|
-
contact_account_count?: number;
|
|
35
|
-
};
|
|
36
|
-
meta: {
|
|
37
|
-
request_id: string;
|
|
38
|
-
request_at: string;
|
|
39
|
-
language: string;
|
|
40
|
-
client_version: string;
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
interface AutocompleteResponse {
|
|
44
|
-
data: {
|
|
45
|
-
raw_query: string;
|
|
46
|
-
input: InputItem[];
|
|
47
|
-
suggestions: Suggestion[];
|
|
48
|
-
is_ready?: boolean;
|
|
49
|
-
};
|
|
50
|
-
meta: {
|
|
51
|
-
request_id: string;
|
|
52
|
-
request_at: string;
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
26
|
interface CompletedParamState extends CompletedParam {
|
|
27
|
+
id: string;
|
|
56
28
|
text: string;
|
|
57
29
|
suggestionType: string;
|
|
58
30
|
suggestionPlaceholder: string;
|
|
@@ -67,13 +39,29 @@ type Segment = {
|
|
|
67
39
|
value: string;
|
|
68
40
|
param: CompletedParamState;
|
|
69
41
|
};
|
|
42
|
+
interface AIAutocompleteHandle {
|
|
43
|
+
focus: () => void;
|
|
44
|
+
reset: () => void;
|
|
45
|
+
}
|
|
46
|
+
interface APIConfig {
|
|
47
|
+
apiKey?: string;
|
|
48
|
+
authScheme?: "Bearer" | "Basic";
|
|
49
|
+
headers?: Record<string, string>;
|
|
50
|
+
}
|
|
70
51
|
type OptionOverrides = Record<string, (query: string) => SuggestionOption[]>;
|
|
71
52
|
interface AIAutocompleteProps {
|
|
72
53
|
onSubmit: (result: AutocompleteResult) => void;
|
|
54
|
+
onError?: (error: Error) => void;
|
|
73
55
|
optionOverrides?: OptionOverrides;
|
|
74
56
|
maskCompletedText?: boolean;
|
|
75
57
|
placeholder?: string;
|
|
76
58
|
className?: string;
|
|
59
|
+
apiConfig?: APIConfig;
|
|
60
|
+
columns?: number;
|
|
61
|
+
value?: string;
|
|
62
|
+
completedParams?: CompletedParamState[];
|
|
63
|
+
onChange?: (value: string) => void;
|
|
64
|
+
onParamsChange?: (params: CompletedParamState[]) => void;
|
|
77
65
|
}
|
|
78
66
|
interface AutocompleteResult {
|
|
79
67
|
query: string;
|
|
@@ -81,15 +69,21 @@ interface AutocompleteResult {
|
|
|
81
69
|
completed_params: CompletedParam[];
|
|
82
70
|
}
|
|
83
71
|
interface UseAIAutocompleteOptions {
|
|
84
|
-
onSubmit?: () => void;
|
|
72
|
+
onSubmit?: (result: AutocompleteResult) => void;
|
|
73
|
+
onError?: (error: Error) => void;
|
|
85
74
|
optionOverrides?: OptionOverrides;
|
|
86
75
|
maskCompletedText?: boolean;
|
|
87
76
|
placeholder?: string;
|
|
77
|
+
apiConfig?: APIConfig;
|
|
78
|
+
columns?: number;
|
|
79
|
+
value?: string;
|
|
80
|
+
completedParams?: CompletedParamState[];
|
|
81
|
+
onChange?: (value: string) => void;
|
|
82
|
+
onParamsChange?: (params: CompletedParamState[]) => void;
|
|
88
83
|
}
|
|
89
84
|
interface UseAIAutocompleteReturn {
|
|
90
85
|
completedParams: CompletedParamState[];
|
|
91
86
|
suggestionPills: Suggestion[];
|
|
92
|
-
activePillIndex: number;
|
|
93
87
|
setActivePill: (index: number) => void;
|
|
94
88
|
removeLastParam: () => void;
|
|
95
89
|
reEditParam: (param: CompletedParamState) => void;
|
|
@@ -117,15 +111,16 @@ interface AIAutocompleteDropdownProps {
|
|
|
117
111
|
suggestions: Suggestion[];
|
|
118
112
|
activeIndex: number;
|
|
119
113
|
onSelect: (option: SuggestionOption) => void;
|
|
114
|
+
onHighlight: (index: number) => void;
|
|
120
115
|
isOpen: boolean;
|
|
121
116
|
id: string;
|
|
122
117
|
className?: string;
|
|
123
118
|
}
|
|
124
119
|
|
|
125
|
-
declare
|
|
120
|
+
declare const AIAutocomplete: react.ForwardRefExoticComponent<AIAutocompleteProps & react.RefAttributes<AIAutocompleteHandle>>;
|
|
126
121
|
|
|
127
|
-
declare function AIAutocompleteDropdown({ suggestions, activeIndex, onSelect, isOpen, id, className, }: AIAutocompleteDropdownProps): react_jsx_runtime.JSX.Element
|
|
122
|
+
declare function AIAutocompleteDropdown({ suggestions, activeIndex, onSelect, onHighlight, isOpen, id, className, }: AIAutocompleteDropdownProps): react_jsx_runtime.JSX.Element;
|
|
128
123
|
|
|
129
|
-
declare function useAIAutocomplete({ onSubmit, optionOverrides, maskCompletedText, placeholder: customPlaceholder, }: UseAIAutocompleteOptions): UseAIAutocompleteReturn;
|
|
124
|
+
declare function useAIAutocomplete({ onSubmit, onError, optionOverrides, maskCompletedText, placeholder: customPlaceholder, apiConfig, columns, value: controlledValue, completedParams: controlledParams, onChange: onChangeProp, onParamsChange, }: UseAIAutocompleteOptions): UseAIAutocompleteReturn;
|
|
130
125
|
|
|
131
|
-
export { AIAutocomplete, AIAutocompleteDropdown, type AIAutocompleteDropdownProps, type
|
|
126
|
+
export { AIAutocomplete, AIAutocompleteDropdown, type AIAutocompleteDropdownProps, type AIAutocompleteHandle, type AIAutocompleteProps, type APIConfig, type AutocompleteResult, type CompletedParam, type CompletedParamState, type OptionOverrides, type Segment, type Suggestion, type SuggestionOption, type TaskKind, type UseAIAutocompleteOptions, type UseAIAutocompleteReturn, useAIAutocomplete };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react from 'react';
|
|
2
2
|
import { ChangeEvent, KeyboardEvent } from 'react';
|
|
3
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
4
|
|
|
4
5
|
type TaskKind = "automation" | "email" | "insight";
|
|
5
6
|
interface CompletedParam {
|
|
@@ -22,37 +23,8 @@ interface Suggestion {
|
|
|
22
23
|
required: boolean;
|
|
23
24
|
options: SuggestionOption[];
|
|
24
25
|
}
|
|
25
|
-
interface InputItem {
|
|
26
|
-
type: string;
|
|
27
|
-
text: string;
|
|
28
|
-
state: "completed" | "in_progress";
|
|
29
|
-
}
|
|
30
|
-
interface AutocompleteRequest {
|
|
31
|
-
data: {
|
|
32
|
-
raw_query: string;
|
|
33
|
-
completed_params: CompletedParam[];
|
|
34
|
-
contact_account_count?: number;
|
|
35
|
-
};
|
|
36
|
-
meta: {
|
|
37
|
-
request_id: string;
|
|
38
|
-
request_at: string;
|
|
39
|
-
language: string;
|
|
40
|
-
client_version: string;
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
interface AutocompleteResponse {
|
|
44
|
-
data: {
|
|
45
|
-
raw_query: string;
|
|
46
|
-
input: InputItem[];
|
|
47
|
-
suggestions: Suggestion[];
|
|
48
|
-
is_ready?: boolean;
|
|
49
|
-
};
|
|
50
|
-
meta: {
|
|
51
|
-
request_id: string;
|
|
52
|
-
request_at: string;
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
26
|
interface CompletedParamState extends CompletedParam {
|
|
27
|
+
id: string;
|
|
56
28
|
text: string;
|
|
57
29
|
suggestionType: string;
|
|
58
30
|
suggestionPlaceholder: string;
|
|
@@ -67,13 +39,29 @@ type Segment = {
|
|
|
67
39
|
value: string;
|
|
68
40
|
param: CompletedParamState;
|
|
69
41
|
};
|
|
42
|
+
interface AIAutocompleteHandle {
|
|
43
|
+
focus: () => void;
|
|
44
|
+
reset: () => void;
|
|
45
|
+
}
|
|
46
|
+
interface APIConfig {
|
|
47
|
+
apiKey?: string;
|
|
48
|
+
authScheme?: "Bearer" | "Basic";
|
|
49
|
+
headers?: Record<string, string>;
|
|
50
|
+
}
|
|
70
51
|
type OptionOverrides = Record<string, (query: string) => SuggestionOption[]>;
|
|
71
52
|
interface AIAutocompleteProps {
|
|
72
53
|
onSubmit: (result: AutocompleteResult) => void;
|
|
54
|
+
onError?: (error: Error) => void;
|
|
73
55
|
optionOverrides?: OptionOverrides;
|
|
74
56
|
maskCompletedText?: boolean;
|
|
75
57
|
placeholder?: string;
|
|
76
58
|
className?: string;
|
|
59
|
+
apiConfig?: APIConfig;
|
|
60
|
+
columns?: number;
|
|
61
|
+
value?: string;
|
|
62
|
+
completedParams?: CompletedParamState[];
|
|
63
|
+
onChange?: (value: string) => void;
|
|
64
|
+
onParamsChange?: (params: CompletedParamState[]) => void;
|
|
77
65
|
}
|
|
78
66
|
interface AutocompleteResult {
|
|
79
67
|
query: string;
|
|
@@ -81,15 +69,21 @@ interface AutocompleteResult {
|
|
|
81
69
|
completed_params: CompletedParam[];
|
|
82
70
|
}
|
|
83
71
|
interface UseAIAutocompleteOptions {
|
|
84
|
-
onSubmit?: () => void;
|
|
72
|
+
onSubmit?: (result: AutocompleteResult) => void;
|
|
73
|
+
onError?: (error: Error) => void;
|
|
85
74
|
optionOverrides?: OptionOverrides;
|
|
86
75
|
maskCompletedText?: boolean;
|
|
87
76
|
placeholder?: string;
|
|
77
|
+
apiConfig?: APIConfig;
|
|
78
|
+
columns?: number;
|
|
79
|
+
value?: string;
|
|
80
|
+
completedParams?: CompletedParamState[];
|
|
81
|
+
onChange?: (value: string) => void;
|
|
82
|
+
onParamsChange?: (params: CompletedParamState[]) => void;
|
|
88
83
|
}
|
|
89
84
|
interface UseAIAutocompleteReturn {
|
|
90
85
|
completedParams: CompletedParamState[];
|
|
91
86
|
suggestionPills: Suggestion[];
|
|
92
|
-
activePillIndex: number;
|
|
93
87
|
setActivePill: (index: number) => void;
|
|
94
88
|
removeLastParam: () => void;
|
|
95
89
|
reEditParam: (param: CompletedParamState) => void;
|
|
@@ -117,15 +111,16 @@ interface AIAutocompleteDropdownProps {
|
|
|
117
111
|
suggestions: Suggestion[];
|
|
118
112
|
activeIndex: number;
|
|
119
113
|
onSelect: (option: SuggestionOption) => void;
|
|
114
|
+
onHighlight: (index: number) => void;
|
|
120
115
|
isOpen: boolean;
|
|
121
116
|
id: string;
|
|
122
117
|
className?: string;
|
|
123
118
|
}
|
|
124
119
|
|
|
125
|
-
declare
|
|
120
|
+
declare const AIAutocomplete: react.ForwardRefExoticComponent<AIAutocompleteProps & react.RefAttributes<AIAutocompleteHandle>>;
|
|
126
121
|
|
|
127
|
-
declare function AIAutocompleteDropdown({ suggestions, activeIndex, onSelect, isOpen, id, className, }: AIAutocompleteDropdownProps): react_jsx_runtime.JSX.Element
|
|
122
|
+
declare function AIAutocompleteDropdown({ suggestions, activeIndex, onSelect, onHighlight, isOpen, id, className, }: AIAutocompleteDropdownProps): react_jsx_runtime.JSX.Element;
|
|
128
123
|
|
|
129
|
-
declare function useAIAutocomplete({ onSubmit, optionOverrides, maskCompletedText, placeholder: customPlaceholder, }: UseAIAutocompleteOptions): UseAIAutocompleteReturn;
|
|
124
|
+
declare function useAIAutocomplete({ onSubmit, onError, optionOverrides, maskCompletedText, placeholder: customPlaceholder, apiConfig, columns, value: controlledValue, completedParams: controlledParams, onChange: onChangeProp, onParamsChange, }: UseAIAutocompleteOptions): UseAIAutocompleteReturn;
|
|
130
125
|
|
|
131
|
-
export { AIAutocomplete, AIAutocompleteDropdown, type AIAutocompleteDropdownProps, type
|
|
126
|
+
export { AIAutocomplete, AIAutocompleteDropdown, type AIAutocompleteDropdownProps, type AIAutocompleteHandle, type AIAutocompleteProps, type APIConfig, type AutocompleteResult, type CompletedParam, type CompletedParamState, type OptionOverrides, type Segment, type Suggestion, type SuggestionOption, type TaskKind, type UseAIAutocompleteOptions, type UseAIAutocompleteReturn, useAIAutocomplete };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var de=Object.defineProperty;var $e=Object.getOwnPropertyDescriptor;var Be=Object.getOwnPropertyNames;var Ue=Object.prototype.hasOwnProperty;var qe=(e,i)=>{for(var o in i)de(e,o,{get:i[o],enumerable:!0})},ze=(e,i,o,p)=>{if(i&&typeof i=="object"||typeof i=="function")for(let t of Be(i))!Ue.call(e,t)&&t!==o&&de(e,t,{get:()=>i[t],enumerable:!(p=$e(i,t))||p.enumerable});return e};var Fe=e=>ze(de({},"__esModule",{value:!0}),e);var it={};qe(it,{AIAutocomplete:()=>Te,AIAutocompleteDropdown:()=>se,useAIAutocomplete:()=>le});module.exports=Fe(it);var T=require("react");var A={};var ve={};var Se={};var Q={};var re=require("react/jsx-runtime");function Pe({option:e,isHighlighted:i,onSelect:o,onHighlight:p,id:t}){let l=[Q.item,i?Q.highlighted:"",e.is_tappable?Q.tappable:Q.nonTappable].filter(Boolean).join(" ");return(0,re.jsxs)("div",{id:t,role:"option","aria-selected":i,className:l,tabIndex:e.is_tappable?0:-1,onClick:()=>e.is_tappable&&o(e),onKeyDown:r=>{e.is_tappable&&(r.key==="Enter"||r.key===" ")&&(r.preventDefault(),o(e))},onMouseEnter:p,children:[e.icon?`${e.icon} ${e.text}`:e.text,e.tag&&(0,re.jsx)("span",{className:Q.tag,children:e.tag})]})}var fe=require("react/jsx-runtime");function ke({options:e,activeIndex:i,onSelect:o,onHighlight:p,listboxId:t}){return(0,fe.jsx)("div",{className:Se.grid,children:e.map((l,r)=>(0,fe.jsx)(Pe,{option:l,isHighlighted:r===i,onSelect:o,onHighlight:()=>p(r),id:`${t}-option-${r}`},l.text))})}var me=require("react/jsx-runtime");function se({suggestions:e,activeIndex:i,onSelect:o,isOpen:p,id:t,className:l}){if(!p||e.length===0)return null;let r=e[0];return r.options.length===0?null:(0,me.jsx)("div",{id:t,role:"listbox",className:`${ve.dropdown} ${l??""}`,onMouseDown:b=>b.preventDefault(),children:(0,me.jsx)(ke,{options:r.options,activeIndex:i,onSelect:o,onHighlight:()=>{},listboxId:t})})}var ae={};var ge=require("react/jsx-runtime");function Ge(e){return e===0?.4:e===1?.3:.15}function we({pills:e,activePillIndex:i,onSelectPill:o}){return(0,ge.jsx)("span",{className:ae.list,children:e.map((p,t)=>(0,ge.jsx)("button",{type:"button",className:`${ae.pill} ${t===i?ae.active:""}`,style:{opacity:Ge(t)},onClick:()=>o(t),children:p.text},`${p.type}-${p.text}`))})}var c=require("react");var Ye="0.1.0",Ve=process?.env.MAGICX_API_ENDPOINT||"/api/suggest",Ae=!1;function je(){let e=process?.env.MAGICX_AI_AUTOCOMPLETE_API_KEY||"";return!e&&!Ae&&(Ae=!0,console.warn("[AIAutocomplete] No API key set (MAGICX_AI_AUTOCOMPLETE_API_KEY). Requests will be sent without an Authorization header.")),e}function Je(){return process?.env.MAGICX_AUTH_SCHEME==="Basic"?"Basic":"Bearer"}function Ze(){return crypto.randomUUID()}function et(e,i){return{placeholder:e.placeholder,type:e.type,...i&&{text:e.text},kind:e.kind}}async function Ie(e,i,o){let p=je(),t=Je(),l=!o?.maskCompletedText,r=i.find(x=>x.type==="contact"&&x.metadata?.contact_account_count)?.metadata?.contact_account_count,b=typeof r=="number"?r:void 0,v={data:{raw_query:e,completed_params:i.map(x=>et(x,l)),...b!=null&&{contact_account_count:b}},meta:{request_id:Ze(),request_at:new Date().toISOString(),language:typeof navigator<"u"?navigator.language:"en-US",client_version:Ye}},h={"Content-Type":"application/json","X-App-Identifier":process?.env.MAGICX_APP_IDENTIFIER||"active-campaign-demo"};p&&(h.Authorization=t==="Basic"?`Basic ${btoa(p)}`:`Bearer ${p}`);let f=await fetch(Ve,{method:"POST",headers:h,body:JSON.stringify(v),signal:o?.signal});if(!f.ok)throw new Error(`API error: ${f.status} ${f.statusText}`);return f.json()}function ie(e,i){let o=e,p={},t=[];for(let l of i){let r=(p[l.type]??0)+1;p[l.type]=r;let v=`{{${l.type.toUpperCase().replace(/\s+/g,"_")}_${r}}}`,h=o.indexOf(l.text);h!==-1&&(o=o.slice(0,h)+v+o.slice(h+l.text.length)),t.push({...l,placeholder:v})}return{rawQuery:o,completedParams:t}}var tt=100,ot=300,nt=2;function Ce(e,i){let o=i.trimStart();if(!o)return e;let p=o.toLowerCase();return e.filter(t=>!t.is_tappable||t.text.toLowerCase().includes(p))}function he(e,i){let o=i.trim();if(!o)return null;let p=o.toLowerCase();return e.find(t=>t.is_tappable&&t.text.toLowerCase()===p)??null}function rt(e,i){let o=[],p=0;for(let l of i){let r=e.indexOf(l.text,p);r!==-1&&(r>p&&o.push({type:"text",value:e.slice(p,r)}),o.push({type:"completed",value:l.text,param:l}),p=r+l.text.length)}let t=e.slice(p);return t&&o.push({type:"text",value:t}),o}function st(e,i){let o=[],p=[],t=0;for(let l of i){let r=e.indexOf(l.text,t);r===-1?p.push(l):(o.push(l),t=r+l.text.length)}return{valid:o,invalid:p}}function at(e,i){return i?e.map(o=>{let p=i[o.type];if(!p)return o;let t=p("");if(t.length===0)return o;let l=new Set(t.map(b=>b.text)),r=(o.options??[]).filter(b=>!l.has(b.text));return{...o,options:[...t,...r]}}):e}function le({onSubmit:e,optionOverrides:i,maskCompletedText:o,placeholder:p}){let[t,l]=(0,c.useState)([]),[r,b]=(0,c.useState)(""),[v,h]=(0,c.useState)([]),[f,x]=(0,c.useState)(-1),[G,$]=(0,c.useState)(!1),[_,Y]=(0,c.useState)(null),[V,j]=(0,c.useState)(!1),E=(0,c.useRef)(0),B=(0,c.useRef)(null),U=(0,c.useRef)(""),q=(0,c.useRef)(e);q.current=e;let J=(0,c.useRef)(i);J.current=i;let Z=(0,c.useRef)(o);Z.current=o;let I=(0,c.useRef)(r);I.current=r;let D=(0,c.useRef)(v);D.current=v;let P=(0,c.useRef)(0),H=(0,c.useRef)(!1),ce=(0,c.useRef)(!1),pe=(0,c.useId)(),N=(0,c.useCallback)(async(s,d)=>{B.current?.abort();let n=new AbortController;B.current=n;let a=++E.current;D.current.some(u=>u.type!=="placeholder")||$(!0),Y(null);try{let u=await Ie(s,d,{maskCompletedText:Z.current,signal:n.signal});if(a!==E.current)return;let S=at(u.data.suggestions??[],J.current);j(u.data.is_ready??!1),U.current=s;let k=u.data.input??[],O=k[k.length-1],F=I.current;if(O?.state==="in_progress"){let R=F.lastIndexOf(O.text);R!==-1?P.current=R:P.current=F.length}else P.current=F.length;let C=S.filter(R=>R.type!=="placeholder")[0];if(C){let R=F.slice(P.current),K=he(C.options,R);K&&(l(L=>[...L,{placeholder:"",type:C.type,text:K.text,kind:K.kind,suggestionType:C.type,suggestionPlaceholder:C.text,options:C.options,metadata:K.metadata}]),S=S.filter(L=>L!==C))}h(S),$(!1),x(-1)}catch(u){a===E.current&&(Y(u instanceof Error?u:new Error(String(u))),$(!1))}},[]);(0,c.useEffect)(()=>(N("",[]),()=>{B.current?.abort()}),[N]);let _e=(0,c.useMemo)(()=>rt(r,t),[r,t]);P.current=Math.min(P.current,r.length);let ee=r.slice(P.current);console.log(`[filter] base=${P.current} query="${ee}" text="${r}"`);let z=v.filter(s=>s.type==="placeholder").map(s=>s.text).join(" ")||p||"",te=v.filter(s=>s.type!=="placeholder"),m=te[0],xe=m?i?.[m.type]:void 0,w=m?xe&&ee.trim()?xe(ee.trim()):Ce(m.options??[],ee):[],ue=z.length>0,oe=!G&&w.length>0&&(!!r||H.current||!ue),X=(0,c.useCallback)(s=>{if(!m)return;let d={placeholder:"",type:m.type,text:s.text,kind:s.kind,suggestionType:m.type,suggestionPlaceholder:m.text,options:m.options,metadata:s.metadata},n=P.current,a=r.slice(0,n);if(a.length>0&&!a.endsWith(" ")){let k=a.split(/\s+/).pop()??"";k&&s.text.toLowerCase().startsWith(k.toLowerCase())&&(a=a.slice(0,a.length-k.length))}let g=a.length>0&&a[a.length-1]!==" ",u=a+(g?" ":"")+s.text+" ";b(u),P.current=u.length,l(k=>[...k,d]),h(k=>k.filter(O=>O!==m)),H.current=!1,x(-1),te.length-1>0&&(ce.current=!0)},[m,te,r]),Oe=(0,c.useCallback)(s=>{let d=s.target.value,n=d.length>0?d[0].toUpperCase()+d.slice(1):d;b(n),H.current=!1,x(-1);let{valid:a,invalid:g}=st(n,t);if(g.length>0){l(a);for(let u of g)h(S=>[{type:u.suggestionType,text:u.suggestionPlaceholder,required:!0,options:u.options},...S])}if(m&&g.length===0){let u=n.slice(P.current),S=he(m.options,u);S&&(l(k=>[...k,{placeholder:"",type:m.type,text:S.text,kind:S.kind,suggestionType:m.type,suggestionPlaceholder:m.text,options:m.options,metadata:S.metadata}]),h(k=>k.filter(O=>O!==m)))}},[t,m]),W=(0,c.useRef)(null),M=(0,c.useRef)(null),ye=(0,c.useRef)(!0);(0,c.useEffect)(()=>{W.current&&clearTimeout(W.current),M.current&&clearTimeout(M.current);let s=d=>{if(ce.current)return ce.current=!1,!1;if(!r&&t.length===0)return ye.current?(N("",[]),!0):(ye.current=!0,!1);let n=r.slice(P.current),u=D.current.filter(L=>L.type!=="placeholder")[0],k=(u?Ce(u.options,n):[]).filter(L=>L.is_tappable),O=u?he(u.options,n)!==null:!1,F=n.trim().length>0;if(k.length>0&&!O&&F)return!1;let{rawQuery:ne,completedParams:C}=ie(r,t),R=ne.length<U.current.length,K=Math.abs(ne.length-U.current.length);return R||K>=d?(N(ne,C),!0):!1};return W.current=setTimeout(()=>{s(nt)&&M.current&&clearTimeout(M.current)},tt),M.current=setTimeout(()=>s(1),ot),()=>{W.current&&clearTimeout(W.current),M.current&&clearTimeout(M.current)}},[r,t,N]);let be=(0,c.useCallback)(()=>{let d=w.map((g,u)=>g.is_tappable?u:-1).filter(g=>g!==-1),n=d.filter(g=>g%2===0),a=d.filter(g=>g%2===1);return[...n,...a]},[w]),Re=(0,c.useCallback)(s=>{let d=be();switch(s.key){case"ArrowDown":{if(s.preventDefault(),d.length===0)return;let n=d.indexOf(f),a=n<d.length-1?n+1:0;x(d[a]);break}case"ArrowUp":{if(s.preventDefault(),d.length===0)return;let n=d.indexOf(f),a=n>0?n-1:d.length-1;x(d[a]);break}case"ArrowRight":{if(f<0)break;if(f%2===0){let a=f+1;a<w.length&&w[a]?.is_tappable&&(s.preventDefault(),x(a))}break}case"ArrowLeft":{if(f<0)break;if(f%2===1){let a=f-1;a>=0&&w[a]?.is_tappable&&(s.preventDefault(),x(a))}break}case"Enter":{s.preventDefault(),f>=0&&w[f]?.is_tappable?X(w[f]):q.current&&q.current();break}case"Tab":{if(f>=0&&w[f]?.is_tappable)s.preventDefault(),X(w[f]);else if(oe){let n=w.find(a=>a.is_tappable);n&&(s.preventDefault(),X(n))}else if(!r&&ue){s.preventDefault();let n=v.find(a=>a.type==="placeholder");b(z),P.current=z.length,n&&(l(a=>[...a,{placeholder:"",type:n.type,text:z,kind:null,suggestionType:n.type,suggestionPlaceholder:n.text,options:n.options}]),h(a=>a.filter(g=>g!==n)))}break}case"Escape":x(-1);break}},[f,w,ue,oe,z,X,v,be,r]),Ee=(0,c.useCallback)(s=>{let d=v.filter(u=>u.type!=="placeholder");if(s<0||s>=d.length)return;let n=d[s],a=d.filter((u,S)=>S!==s),g=v.filter(u=>u.type==="placeholder");h([...g,n,...a]),H.current=!0,x(-1)},[v]),De=(0,c.useCallback)(()=>{if(t.length===0)return;let s=t[t.length-1],d={type:s.suggestionType,text:s.suggestionPlaceholder,required:!0,options:s.options};l(n=>n.slice(0,-1)),h(n=>[d,...n]),x(-1)},[t]),Ne=(0,c.useCallback)(s=>{let d={type:s.suggestionType,text:s.suggestionPlaceholder,required:!0,options:s.options};b(n=>{let a=n.indexOf(s.text);if(a===-1)return n;let g=n.slice(0,a),u=n.slice(a+s.text.length),S=(g+u).replace(/ {2,}/g," ");return P.current=Math.min(P.current,S.length),S}),l(n=>n.filter(a=>a!==s)),h(n=>[d,...n]),x(-1),H.current=!0},[]),Me=(0,c.useCallback)(()=>{b(""),l([]),h([]),x(-1),j(!1),P.current=0,U.current="",N("",[])},[N]),Le=f>=0?`${pe}-option-${f}`:void 0;return{completedParams:t,suggestionPills:te,activePillIndex:0,setActivePill:Ee,removeLastParam:De,reEditParam:Ne,segments:_e,suggestions:v,activeIndex:f,isReady:V,isLoading:G,error:_,inputProps:{value:r,placeholder:z||void 0,onChange:Oe,onKeyDown:Re,role:"combobox","aria-expanded":oe,"aria-activedescendant":Le,"aria-autocomplete":"list","aria-controls":pe},reset:Me,dropdownProps:{suggestions:m?[{...m,options:w}]:[],activeIndex:f,onSelect:X,isOpen:oe,id:pe}}}var y=require("react/jsx-runtime");function Te({onSubmit:e,optionOverrides:i,maskCompletedText:o,placeholder:p,className:t}){let l=(0,T.useRef)(null),[r,b]=(0,T.useState)(!1),v=(0,T.useRef)(()=>{});(0,T.useEffect)(()=>{l.current?.focus()},[]);let{completedParams:h,suggestionPills:f,activePillIndex:x,setActivePill:G,segments:$,inputProps:_,dropdownProps:Y,reset:V}=le({onSubmit:()=>v.current(),optionOverrides:i,maskCompletedText:o,placeholder:p}),j=()=>{l.current?.focus()},E=!!_.value||h.length>0,B=(0,T.useCallback)(()=>{if(!E)return;let{rawQuery:I,completedParams:D}=ie(_.value,h);e({query:_.value.trim(),raw_query:I,completed_params:D}),V(),b(!0),setTimeout(()=>b(!1),3e3)},[E,_.value,h,e,V]);v.current=B;let{onChange:U,placeholder:q,...J}=_,Z=!_.value;return(0,y.jsxs)("div",{className:`${A.container} ${t??""}`,children:[(0,y.jsx)("div",{className:`${A.checkmark} ${r?A.checkmarkVisible:""}`,children:(0,y.jsxs)("svg",{width:"72",height:"72",viewBox:"0 0 24 24",fill:"none",role:"img","aria-label":"Success",children:[(0,y.jsx)("circle",{cx:"12",cy:"12",r:"12",fill:"#34C759"}),(0,y.jsx)("path",{d:"M7 12.5l3.5 3.5L17 9",stroke:"#000",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round",className:A.checkmarkPath})]})}),(0,y.jsx)(se,{...Y}),(0,y.jsxs)("div",{className:A.inputWrapper,onClick:j,children:[(0,y.jsxs)("div",{className:A.editorArea,children:[(0,y.jsxs)("div",{className:A.sizerContent,"aria-hidden":"true",children:[Z&&q?(0,y.jsxs)("span",{className:A.placeholderText,children:[q," "]}):(0,y.jsxs)("span",{className:A.sizerText,children:[$.map((I,D)=>(0,y.jsx)("span",{children:I.value},`${D}-${I.type}`)),$.length===0&&"\xA0"]})," ",(0,y.jsx)(we,{pills:f,activePillIndex:x,onSelectPill:G})]}),(0,y.jsx)("textarea",{ref:l,className:A.textarea,rows:1,onChange:U,...J})]}),(0,y.jsx)("button",{type:"button",className:A.submitButton,disabled:!E,onClick:I=>{I.stopPropagation(),B()},"aria-label":"Submit",children:(0,y.jsx)("svg",{width:"18",height:"18",viewBox:"0 0 18 18",fill:"none",role:"img","aria-label":"Submit",children:(0,y.jsx)("path",{d:"M9 14V4M9 4L4 9M9 4L14 9",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})})]})]})}0&&(module.exports={AIAutocomplete,AIAutocompleteDropdown,useAIAutocomplete});
|
|
1
|
+
"use strict";var be=Object.defineProperty;var Je=Object.getOwnPropertyDescriptor;var Ze=Object.getOwnPropertyNames;var et=Object.prototype.hasOwnProperty;var tt=(e,o)=>{for(var t in o)be(e,t,{get:o[t],enumerable:!0})},ot=(e,o,t,r)=>{if(o&&typeof o=="object"||typeof o=="function")for(let n of Ze(o))!et.call(e,n)&&n!==t&&be(e,n,{get:()=>o[n],enumerable:!(r=Je(o,n))||r.enumerable});return e};var nt=e=>ot(be({},"__esModule",{value:!0}),e);var St={};tt(St,{AIAutocomplete:()=>ze,AIAutocompleteDropdown:()=>re,useAIAutocomplete:()=>ce});module.exports=nt(St);var D=require("react");var j={};var ye={};var Re={};var W={};var ne=require("react/jsx-runtime");function _e({option:e,isHighlighted:o,onSelect:t,onHighlight:r,id:n}){let a=[W.item,o?W.highlighted:"",e.is_tappable?W.tappable:W.nonTappable].filter(Boolean).join(" ");return(0,ne.jsxs)("div",{id:n,role:"option","aria-selected":o,className:a,tabIndex:e.is_tappable?0:-1,onClick:()=>e.is_tappable&&t(e),onKeyDown:i=>{e.is_tappable&&(i.key==="Enter"||i.key===" ")&&(i.preventDefault(),t(e))},onMouseEnter:r,children:[e.icon?`${e.icon} ${e.text}`:e.text,e.tag&&(0,ne.jsx)("span",{className:W.tag,children:e.tag})]})}var Se=require("react/jsx-runtime");function Te({options:e,activeIndex:o,onSelect:t,onHighlight:r,listboxId:n}){return(0,Se.jsx)("div",{className:Re.grid,children:e.map((a,i)=>(0,Se.jsx)(_e,{option:a,isHighlighted:i===o,onSelect:t,onHighlight:()=>r(i),id:`${n}-option-${i}`},a.text))})}var xe=require("react/jsx-runtime");function re({suggestions:e,activeIndex:o,onSelect:t,onHighlight:r,isOpen:n,id:a,className:i}){let l=e[0],d=n&&l&&l.options.length>0;return(0,xe.jsx)("div",{id:a,role:"listbox",className:`${ye.dropdown} ${d?ye.visible:""} ${i??""}`,onMouseDown:g=>g.preventDefault(),children:l&&l.options.length>0&&(0,xe.jsx)(Te,{options:l.options,activeIndex:o,onSelect:t,onHighlight:r,listboxId:a})})}var ae={};var Pe=require("react/jsx-runtime");function lt(e){return e===0?.4:e===1?.3:.15}function Ee({pills:e,activePillIndex:o,onSelectPill:t}){return(0,Pe.jsx)("span",{className:ae.list,children:e.map((r,n)=>(0,Pe.jsx)("button",{type:"button",className:`${ae.pill} ${n===o?ae.active:""}`,style:{opacity:lt(n)},onClick:()=>t(n),children:r.text},`${r.type}-${r.text}`))})}var u=require("react");function se(e,o){let t=o.trimStart();if(!t)return e;let r=t.toLowerCase();return e.filter(n=>!n.is_tappable||n.text.toLowerCase().includes(r))}function Z(e,o){let t=o.trim();if(!t)return null;let r=t.toLowerCase();return e.find(n=>n.is_tappable&&n.text.toLowerCase()===r)??null}function Me(e,o){let t=[],r=0;for(let a of o){let i=e.indexOf(a.text,r);i!==-1&&(i>r&&t.push({type:"text",value:e.slice(r,i)}),t.push({type:"completed",value:a.text,param:a}),r=i+a.text.length)}let n=e.slice(r);return n&&t.push({type:"text",value:n}),t}function Ue(e,o){let t=[],r=[],n=0;for(let a of o){let i=e.indexOf(a.text,n);i===-1?r.push(a):(t.push(a),n=i+a.text.length)}return{valid:t,invalid:r}}var T=require("react");var ut="0.1.5",pt=process?.env.MAGICX_API_ENDPOINT||"/api/suggest",Le=!1;function mt(e){let o=e?.apiKey||process?.env.MAGICX_AI_AUTOCOMPLETE_API_KEY||"";return!o&&!Le&&(Le=!0,console.warn("[AIAutocomplete] No API key set (MAGICX_AI_AUTOCOMPLETE_API_KEY). Requests will be sent without an Authorization header.")),o}function dt(e){return e?.authScheme?e.authScheme:process?.env.MAGICX_AUTH_SCHEME==="Basic"?"Basic":"Bearer"}function ft(){return crypto.randomUUID()}function gt(e,o){return{placeholder:e.placeholder,type:e.type,...o&&{text:e.text},kind:e.kind}}async function Ne(e,o,t){let r=t?.apiConfig,n=mt(r),a=dt(r),i=!t?.maskCompletedText,l=t?.contactAccountCount,d={data:{raw_query:e,completed_params:o.map(b=>gt(b,i)),...l!=null&&{contact_account_count:l}},meta:{request_id:ft(),request_at:new Date().toISOString(),language:typeof navigator<"u"?navigator.language:"en-US",client_version:ut}},g={"Content-Type":"application/json","X-App-Identifier":process?.env.MAGICX_APP_IDENTIFIER||"active-campaign-demo",...r?.headers};n&&(g.Authorization=a==="Basic"?`Basic ${btoa(n)}`:`Bearer ${n}`);let p=await fetch(pt,{method:"POST",headers:g,body:JSON.stringify(d),signal:t?.signal});if(!p.ok)throw new Error(`API error: ${p.status} ${p.statusText}`);return p.json()}function G(e,o){let t=e,r={},n=[];for(let a of o){let i=(r[a.type]??0)+1;r[a.type]=i;let d=`{{${a.type.toUpperCase().replace(/\s+/g,"_")}_${i}}}`,g=t.indexOf(a.text);g!==-1&&(t=t.slice(0,g)+d+t.slice(g+a.text.length)),n.push({...a,placeholder:d})}return{rawQuery:t,completedParams:n}}function De(e,o){return o?e.map(t=>{let r=o[t.type];if(!r)return t;let n=r("");if(n.length===0)return t;let a=new Set(n.map(l=>l.text)),i=(t.options??[]).filter(l=>!a.has(l.text));return{...t,options:[...n,...i]}}):e}var ht=100,bt=300,yt=2;function Fe({textRef:e,suggestionsRef:o,filterBaseRef:t,maskCompletedTextRef:r,apiConfigRef:n,optionOverridesRef:a,onErrorRef:i,setCompletedParams:l,setSuggestions:d,setActiveDropdownIndex:g}){let[p,b]=(0,T.useState)(!1),[h,S]=(0,T.useState)(null),[k,x]=(0,T.useState)(!1),v=(0,T.useRef)(0),A=(0,T.useRef)(null),m=(0,T.useRef)(""),f=(0,T.useCallback)(async(s,c)=>{A.current?.abort();let P=new AbortController;A.current=P;let U=++v.current,F=e.current.length;o.current.some($=>$.type!=="placeholder")||b(!0),S(null);try{let $=c.find(_=>_.type==="contact"&&_.metadata?.contact_account_count)?.metadata?.contact_account_count,K=await Ne(s,c,{maskCompletedText:r.current,signal:P.signal,contactAccountCount:typeof $=="number"?$:void 0,apiConfig:n.current});if(U!==v.current)return;let R=De(K.data.suggestions??[],a.current);x(K.data.is_ready??!1),m.current=s;let E=K.data.input??[],q=E[E.length-1],H=e.current;if(q?.state==="in_progress"){let _=H.toLowerCase().lastIndexOf(q.text.toLowerCase());_!==-1?t.current=_:t.current=F}else t.current=F;let O=R.filter(_=>_.type!=="placeholder")[0];if(O){let _=H.slice(t.current),B=Z(O.options,_);B&&(l(M=>[...M,{id:crypto.randomUUID(),placeholder:"",type:O.type,text:B.text,kind:B.kind,suggestionType:O.type,suggestionPlaceholder:O.text,options:O.options,metadata:B.metadata}]),R=R.filter(M=>M!==O))}d(R),b(!1),g(-1)}catch($){if(U===v.current){let K=$ instanceof Error?$:new Error(String($));S(K),b(!1),i.current?.(K)}}},[e,o,t,r,n,a,i,l,d,g]);return(0,T.useEffect)(()=>(f("",[]),()=>{A.current?.abort()}),[f]),{doFetch:f,isLoading:p,error:h,isReady:k,lastRawQueryRef:m}}function $e({text:e,completedParams:o,doFetch:t,filterBaseRef:r,skipNextFetchRef:n,suggestionsRef:a,lastRawQueryRef:i}){let l=(0,T.useRef)(null),d=(0,T.useRef)(null),g=(0,T.useRef)(!0);(0,T.useEffect)(()=>{l.current&&clearTimeout(l.current),d.current&&clearTimeout(d.current);let p=b=>{if(n.current)return n.current=!1,!1;if(!e&&o.length===0)return g.current?(t("",[]),!0):(g.current=!0,!1);let h=e.slice(r.current),x=a.current.filter(F=>F.type!=="placeholder")[0],A=(x?se(x.options,h):[]).filter(F=>F.is_tappable),m=x?Z(x.options,h)!==null:!1,f=h.trim().length>0;if(A.length>0&&!m&&f)return!1;let{rawQuery:s,completedParams:c}=G(e,o),P=s.length<i.current.length,U=Math.abs(s.length-i.current.length);return P||U>=b?(t(s,c),!0):!1};return l.current=setTimeout(()=>{p(yt)&&d.current&&clearTimeout(d.current)},ht),d.current=setTimeout(()=>p(1),bt),()=>{l.current&&clearTimeout(l.current),d.current&&clearTimeout(d.current)}},[e,o,t,r,n,a,i])}var ve=require("react");function Ke({activeDropdownIndex:e,setActiveDropdownIndex:o,filteredOptions:t,selectOption:r,onSubmitRef:n,text:a,completedParams:i,isDropdownOpen:l,hasPlaceholder:d,placeholderText:g,suggestions:p,filterBaseRef:b,columns:h,setText:S,setCompletedParams:k,setSuggestions:x}){let v=(0,ve.useCallback)(()=>{let m=t.map((c,P)=>c.is_tappable?P:-1).filter(c=>c!==-1),f=m.filter(c=>c%h===0),s=m.filter(c=>c%h===1);return[...f,...s]},[t,h]);return{handleKeyDown:(0,ve.useCallback)(m=>{let f=v();switch(m.key){case"ArrowDown":{if(m.preventDefault(),f.length===0)return;let s=f.indexOf(e),c=s<f.length-1?s+1:0;o(f[c]);break}case"ArrowUp":{if(m.preventDefault(),f.length===0)return;let s=f.indexOf(e),c=s>0?s-1:f.length-1;o(f[c]);break}case"ArrowRight":{if(e<0)break;if(e%h===0){let s=e+1;s<t.length&&t[s]?.is_tappable&&(m.preventDefault(),o(s))}break}case"ArrowLeft":{if(e<0)break;if(e%h===1){let s=e-1;s>=0&&t[s]?.is_tappable&&(m.preventDefault(),o(s))}break}case"Enter":{if(m.preventDefault(),e>=0&&t[e]?.is_tappable)r(t[e]);else if(n.current){let{rawQuery:s,completedParams:c}=G(a,i),P={query:a.trim(),raw_query:s,completed_params:c};n.current(P)}break}case"Tab":{if(e>=0&&t[e]?.is_tappable)m.preventDefault(),r(t[e]);else if(l){let s=t.find(c=>c.is_tappable);s&&(m.preventDefault(),r(s))}else if(!a&&d){m.preventDefault();let s=p.find(c=>c.type==="placeholder");s?(S(g),b.current=g.length,k(c=>[...c,{id:crypto.randomUUID(),placeholder:"",type:s.type,text:g,kind:null,suggestionType:s.type,suggestionPlaceholder:s.text,options:s.options}]),x(c=>c.filter(P=>P!==s))):(S(g),b.current=g.length)}break}case"Escape":o(-1);break}},[e,h,i,t,b,v,d,l,n,g,r,o,k,x,S,p,a]),getTappableIndices:v}}var ie=require("react");function je({completedParams:e,suggestions:o,setCompletedParams:t,setSuggestions:r,setActiveDropdownIndex:n,filterBaseRef:a,pillTappedRef:i}){let l=(0,ie.useCallback)(p=>{let b=o.filter(x=>x.type!=="placeholder");if(p<0||p>=b.length)return;let h=b[p],S=b.filter((x,v)=>v!==p),k=o.filter(x=>x.type==="placeholder");r([...k,h,...S]),i.current=!0,n(-1)},[o,r,n,i]),d=(0,ie.useCallback)(()=>{if(e.length===0)return;let p=e[e.length-1],b={type:p.suggestionType,text:p.suggestionPlaceholder,required:!0,options:p.options};t(h=>h.slice(0,-1)),r(h=>[b,...h]),n(-1)},[e,t,r,n]),g=(0,ie.useCallback)(p=>{let b={type:p.suggestionType,text:p.suggestionPlaceholder,required:!0,options:p.options};return{apply:h=>{h(S=>{let k=0;for(let x of e){let v=S.indexOf(x.text,k);if(v!==-1){if(x.id===p.id){let A=S.slice(0,v),m=S.slice(v+p.text.length),f=(A+m).replace(/ {2,}/g," ");return a.current=Math.min(a.current,f.length),f}k=v+x.text.length}}return S}),t(S=>S.filter(k=>k.id!==p.id)),r(S=>[b,...S]),n(-1),i.current=!0}}},[e,t,r,n,a,i]);return{setActivePill:l,removeLastParam:d,reEditParam:g}}function ce({onSubmit:e,onError:o,optionOverrides:t,maskCompletedText:r,placeholder:n,apiConfig:a,columns:i=2,value:l,completedParams:d,onChange:g,onParamsChange:p}){let b=l!==void 0,h=d!==void 0,[S,k]=(0,u.useState)(""),[x,v]=(0,u.useState)([]),[A,m]=(0,u.useState)([]),[f,s]=(0,u.useState)(-1),c=b?l:S,P=h?d:x,U=(0,u.useRef)(e);U.current=e;let F=(0,u.useRef)(g);F.current=g;let z=(0,u.useRef)(p);z.current=p;let $=(0,u.useRef)(l);$.current=l;let K=(0,u.useRef)(d);K.current=d;let R=(0,u.useCallback)(y=>{if(typeof y=="function")if(b){let w=y($.current??"");F.current?.(w)}else k(w=>{let Q=y(w);return F.current?.(Q),Q});else b||k(y),F.current?.(y)},[b]),E=(0,u.useCallback)(y=>{if(typeof y=="function")if(h){let w=y(K.current??[]);z.current?.(w)}else v(w=>{let Q=y(w);return z.current?.(Q),Q});else h||v(y),z.current?.(y)},[h]),q=(0,u.useRef)(o);q.current=o;let H=(0,u.useRef)(t);H.current=t;let Y=(0,u.useRef)(r);Y.current=r;let O=(0,u.useRef)(a);O.current=a;let _=(0,u.useRef)(c);_.current=c;let B=(0,u.useRef)(A);B.current=A;let M=(0,u.useRef)(0),ee=(0,u.useRef)(!1),Ae=(0,u.useRef)(!1),le=(0,u.useId)(),{doFetch:ue,isLoading:Ce,error:qe,isReady:Be,lastRawQueryRef:pe}=Fe({textRef:_,suggestionsRef:B,filterBaseRef:M,maskCompletedTextRef:Y,apiConfigRef:O,optionOverridesRef:H,onErrorRef:q,setCompletedParams:E,setSuggestions:m,setActiveDropdownIndex:s});$e({text:c,completedParams:P,doFetch:ue,filterBaseRef:M,skipNextFetchRef:Ae,suggestionsRef:B,lastRawQueryRef:pe});let Qe=(0,u.useMemo)(()=>Me(c,P),[c,P]);M.current=Math.min(M.current,c.length);let me=c.slice(M.current),de=(0,u.useMemo)(()=>A.filter(w=>w.type==="placeholder").map(w=>w.text).join(" ")||n||"",[A,n]),te=(0,u.useMemo)(()=>A.filter(y=>y.type!=="placeholder"),[A]),C=te[0],ke=C?t?.[C.type]:void 0,we=C?ke?ke(me.trim()):C.options??[]:[],fe=(0,u.useMemo)(()=>se(we,me),[we,me]),Oe=de.length>0,ge=!Ce&&fe.length>0&&(!!c||ee.current||!Oe),Ie=(0,u.useCallback)(y=>{if(!C)return;let w={id:crypto.randomUUID(),placeholder:"",type:C.type,text:y.text,kind:y.kind,suggestionType:C.type,suggestionPlaceholder:C.text,options:C.options,metadata:y.metadata},Q=M.current,L=_.current.slice(0,Q);if(L.length>0&&!L.endsWith(" ")){let N=L.split(/\s+/).pop()??"";N&&y.text.toLowerCase().startsWith(N.toLowerCase())&&(L=L.slice(0,L.length-N.length))}let he=L.length>0&&L[L.length-1]!==" ",V=L+(he?" ":"")+y.text+" ";R(V),M.current=V.length,E(N=>[...N,w]),m(N=>N.filter(J=>J!==C)),ee.current=!1,s(-1),te.length-1>0&&(Ae.current=!0)},[C,te,R,E]),He=(0,u.useCallback)(y=>{let w=y.target.value,L=w.length>0&&!y.nativeEvent?.isComposing&&w[0]!==w[0].toUpperCase()?w[0].toUpperCase()+w.slice(1):w;R(L),ee.current=!1,s(-1);let{valid:he,invalid:V}=Ue(L,P);if(V.length>0){E(()=>he);for(let X of V)m(N=>[{type:X.suggestionType,text:X.suggestionPlaceholder,required:!0,options:X.options},...N])}if(C&&V.length===0){let X=L.slice(M.current),N=Z(C.options,X);N&&(E(J=>[...J,{id:crypto.randomUUID(),placeholder:"",type:C.type,text:N.text,kind:N.kind,suggestionType:C.type,suggestionPlaceholder:C.text,options:C.options,metadata:N.metadata}]),m(J=>J.filter(Ye=>Ye!==C)))}},[P,C,R,E]),{handleKeyDown:Ve}=Ke({activeDropdownIndex:f,setActiveDropdownIndex:s,filteredOptions:fe,selectOption:Ie,onSubmitRef:U,text:c,completedParams:P,isDropdownOpen:ge,hasPlaceholder:Oe,placeholderText:de,suggestions:A,filterBaseRef:M,columns:i,setText:R,setCompletedParams:E,setSuggestions:m}),oe=je({completedParams:P,suggestions:A,setCompletedParams:E,setSuggestions:m,setActiveDropdownIndex:s,filterBaseRef:M,pillTappedRef:ee}),Xe=(0,u.useCallback)(y=>{oe.reEditParam(y).apply(R)},[oe,R]),We=(0,u.useCallback)(()=>{R(""),E(()=>[]),m([]),s(-1),M.current=0,pe.current="",ue("",[])},[ue,R,E,pe]),Ge=f>=0?`${le}-option-${f}`:void 0;return{completedParams:P,suggestionPills:te,setActivePill:oe.setActivePill,removeLastParam:oe.removeLastParam,reEditParam:Xe,segments:Qe,suggestions:A,activeIndex:f,isReady:Be,isLoading:Ce,error:qe,inputProps:{value:c,placeholder:de||void 0,onChange:He,onKeyDown:Ve,role:"combobox","aria-expanded":ge,"aria-activedescendant":Ge,"aria-autocomplete":"list","aria-controls":le},reset:We,dropdownProps:{suggestions:C?[{...C,options:fe}]:[],activeIndex:f,onSelect:Ie,onHighlight:s,isOpen:ge,id:le}}}var I=require("react/jsx-runtime"),ze=(0,D.forwardRef)(function({onSubmit:o,onError:t,optionOverrides:r,maskCompletedText:n,placeholder:a,className:i,apiConfig:l,columns:d,value:g,completedParams:p,onChange:b,onParamsChange:h},S){let k=(0,D.useRef)(null),[x,v]=(0,D.useState)(!1),A=(0,D.useRef)(()=>{}),m=(0,D.useRef)(void 0);(0,D.useEffect)(()=>(k.current?.focus(),()=>clearTimeout(m.current)),[]);let{completedParams:f,suggestionPills:s,setActivePill:c,segments:P,inputProps:U,dropdownProps:F,reset:z}=ce({onSubmit:O=>A.current(O),onError:t,optionOverrides:r,maskCompletedText:n,placeholder:a,apiConfig:l,columns:d,value:g,completedParams:p,onChange:b,onParamsChange:h});(0,D.useImperativeHandle)(S,()=>({focus:()=>k.current?.focus(),reset:z}),[z]);let $=()=>{k.current?.focus()},K=!!U.value||f.length>0,R=(0,D.useCallback)(()=>{if(!K)return;let{rawQuery:O,completedParams:_}=G(U.value,f);o({query:U.value.trim(),raw_query:O,completed_params:_}),z(),v(!0),clearTimeout(m.current),m.current=setTimeout(()=>v(!1),3e3)},[K,U.value,f,o,z]);A.current=R;let{onChange:E,placeholder:q,...H}=U,Y=!U.value;return(0,I.jsxs)("div",{className:`${j.container} ${i??""}`,children:[(0,I.jsx)("div",{className:`${j.checkmark} ${x?j.checkmarkVisible:""}`,children:(0,I.jsxs)("svg",{width:"72",height:"72",viewBox:"0 0 24 24",fill:"none",role:"img","aria-label":"Success",children:[(0,I.jsx)("circle",{cx:"12",cy:"12",r:"12",fill:"#34C759"}),(0,I.jsx)("path",{d:"M7 12.5l3.5 3.5L17 9",stroke:"#000",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round",className:j.checkmarkPath})]})}),(0,I.jsx)(re,{...F}),(0,I.jsxs)("div",{className:j.inputWrapper,onClick:$,children:[(0,I.jsxs)("div",{className:j.editorArea,children:[(0,I.jsxs)("div",{className:j.sizerContent,"aria-hidden":"true",children:[Y&&q?(0,I.jsxs)("span",{className:j.placeholderText,children:[q," "]}):(0,I.jsxs)("span",{className:j.sizerText,children:[P.map((O,_)=>(0,I.jsx)("span",{children:O.value},`${_}-${O.type}`)),P.length===0&&"\xA0"]})," ",(0,I.jsx)(Ee,{pills:s,activePillIndex:0,onSelectPill:c})]}),(0,I.jsx)("textarea",{ref:k,className:j.textarea,rows:1,onChange:E,...H})]}),(0,I.jsx)("button",{type:"button",className:j.submitButton,disabled:!K,onClick:O=>{O.stopPropagation(),R()},"aria-label":"Submit",children:(0,I.jsx)("svg",{width:"18",height:"18",viewBox:"0 0 18 18",fill:"none",role:"img","aria-label":"Submit",children:(0,I.jsx)("path",{d:"M9 14V4M9 4L4 9M9 4L14 9",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})})]})]})});0&&(module.exports={AIAutocomplete,AIAutocompleteDropdown,useAIAutocomplete});
|
|
2
2
|
//# sourceMappingURL=index.js.map
|