@magicx-eng/ai-autocomplete-react 0.43.1 → 0.45.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/README.md CHANGED
@@ -367,6 +367,7 @@ function App() {
367
367
  | `productImageFallback?` | `"image" \| "initial"` | `"image"` | Fallback for a missing or blank product image URL, or an image load error. `"image"` shows the placeholder pictogram; `"initial"` shows the title initial. A working image still takes priority. See [Product strip](#product-strip). |
368
368
  | `products?` | `ProductsConfig` | — | Custom source for the product strip — see [Your own product search](#your-own-product-search-products). Omit it and the strip shows the items the `/suggest` response reports. |
369
369
  | `onProductSelect?` | `(product: Product) => void` | — | Called when a product card is activated. The SDK never navigates. |
370
+ | `onProductAddToCart?` | `(product: Product) => void \| PromiseLike<unknown>` | — | When set, every card that isn't sold out shows an add-to-cart button; pressing it calls this instead of `onProductSelect`. Return a promise to show progress. See [Product strip](#product-strip). |
370
371
  | `resetOnSubmit?` | `boolean` | `true` | Clear the input and start a new session after `onSubmit` runs. Set to `false` to keep the query in place — e.g. when `onSubmit` navigates away — and call the handle's `reset()` yourself. |
371
372
  | `submitButton?` | `ReactNode` | — | Custom submit button. Pass any `ReactNode` to replace the default arrow button. Pass `null` to render no button. Clicks bubble up and trigger submit, so consumer-supplied buttons work without re-wiring `onClick`. |
372
373
  | `ref?` | `Ref<AIAutocompleteHandle>` | — | Imperative handle with `focus()`, `blur()`, `reset()`, and `setMode()`. |
@@ -614,6 +615,33 @@ type Product = {
614
615
  itself change the query. Removing the config restores the server's items at
615
616
  once.
616
617
 
618
+ **Add to cart.** Pass `onProductAddToCart` and every card that isn't sold out
619
+ (`available: false`) shows a round cart button over the bottom-right corner of
620
+ its image (in the list layout, at the right end of the row). Leave it unset and
621
+ no card has one. The SDK never touches a cart: pressing the button calls your
622
+ handler with the card's `Product`, and does not fire `onProductSelect`,
623
+ navigate or close the panel.
624
+
625
+ ```tsx
626
+ <AIAutocomplete
627
+ onSubmit={handleSubmit}
628
+ onProductAddToCart={(product) => cart.add(product.id)}
629
+ />
630
+ ```
631
+
632
+ Return a promise to have the button show progress. While it is pending the
633
+ button carries `data-aia-cart-state="adding"` (and `aria-busy="true"`), dims
634
+ and ignores further presses; once it resolves the cart turns into a check
635
+ (`data-aia-cart-state="added"`) and stays one until the card's product changes
636
+ (a price-only change keeps it). Pressing it again adds another unit, back
637
+ through "adding" to "added". A rejection puts the button back at rest — show your own error. A handler that returns nothing goes
638
+ straight to "added". The button is labelled "Add to cart: {title}" in the
639
+ widget language, reachable by Tab and pressed with Enter or Space. Theme it
640
+ with `--aia-product-cart-size` (28px), `--aia-product-cart-bg` and
641
+ `--aia-product-cart-color`; its focus ring uses `--aia-product-focus-ring`.
642
+ The button is a `[data-aia-product-cart]` element, and a card that has one
643
+ carries `data-aia-product-has-cart`.
644
+
617
645
  **Selection and links.** Cards are real `<a href>` elements, so cmd/ctrl-click,
618
646
  middle-click and "copy link address" behave natively. A plain left click is
619
647
  intercepted (`preventDefault`) and emits `onProductSelect` instead.
@@ -631,10 +659,10 @@ scrolls horizontally by trackpad, wheel, touch and keyboard; the page never
631
659
  scrolls sideways and the scrollbar chrome is hidden in all engines.
632
660
 
633
661
  Tier 2 gets the same thing: `useAIAutocomplete()` takes `showProducts`,
634
- `products`, `productImageFallback` and `onProductSelect`, and the strip renders inside
635
- `<AIAutocompleteDropdown />` via `dropdownProps`. Tier 3 consumers read
636
- `products` (and `customFields`) from the hook and call
637
- `selectProduct(product)` from their own cards.
662
+ `products`, `productImageFallback`, `onProductSelect` and `onProductAddToCart`,
663
+ and the strip renders inside `<AIAutocompleteDropdown />` via `dropdownProps`.
664
+ Tier 3 consumers read `products` (and `customFields`) from the hook and call
665
+ `selectProduct(product)` and `addProductToCart(product)` from their own cards.
638
666
 
639
667
  #### Custom submit button
640
668
 
@@ -732,6 +760,7 @@ The headless hook for Tier 2 and Tier 3. Accepts the same options as `<AIAutocom
732
760
  | `reset` | `() => void` | Clear all state, re-fetch, and start a new session (rotates `session_id`). Call this after handling submit. |
733
761
  | `recordSubmit` | `(result: AutocompleteResult) => void` | Record a submit from a button you render. Enter is recorded for you; call this once per button submit. |
734
762
  | `selectProduct` | `(product: Product) => void` | Announce a product selection (fires `onProductSelect`). The built-in dropdown calls this for you; hand-rolled strips call it themselves. Never navigates. |
763
+ | `addProductToCart` | `(product: Product) => Promise<void>` | Add a product through `onProductAddToCart`. The built-in dropdown's cart buttons call this for you; hand-rolled strips call it themselves. Resolves when the handler's promise settles and rejects with its error; does nothing without a handler. |
735
764
 
736
765
  **Input forwarding (custom inputs)** — call these instead of spreading `inputProps` when your input isn't a `<textarea>` (contentEditable / rich-text editors):
737
766
 
@@ -747,7 +776,7 @@ The headless hook for Tier 2 and Tier 3. Accepts the same options as `<AIAutocom
747
776
  | Field | Type | Description |
748
777
  |---|---|---|
749
778
  | `inputProps` | `object` | Spread onto a `<textarea>`. Includes `value`, `placeholder`, `onChange`, `onKeyDown`, and ARIA attributes. |
750
- | `dropdownProps` | `AIAutocompleteDropdownProps` | Spread onto `<AIAutocompleteDropdown />`. Carries the options, `activeIndex`, `onSelect` / `onHighlight`, pills, open state, `optionsPosition`, and the product strip's `products` / `productImageFallback` / `onProductSelect` / `onProductFocusChange` — read these directly to render your own dropdown in Tier 3. |
779
+ | `dropdownProps` | `AIAutocompleteDropdownProps` | Spread onto `<AIAutocompleteDropdown />`. Carries the options, `activeIndex`, `onSelect` / `onHighlight`, pills, open state, `optionsPosition`, and the product strip's `products` / `productImageFallback` / `onProductSelect` / `onProductAddToCart` (only when the hook has one) / `onProductFocusChange` — read these directly to render your own dropdown in Tier 3. |
751
780
 
752
781
  ### `<AIAutocompleteDropdown />`
753
782
 
@@ -770,6 +799,7 @@ The dropdown component for Tier 2. Spread `dropdownProps` from the hook (and add
770
799
  | `products?` | `Product[]` | Product cards to render below the options grid. Empty renders no strip. |
771
800
  | `productImageFallback?` | `"image" \| "initial"` | Default: `"image"`. Fallback for a missing or blank product image URL, or an image load error. `"image"` shows the placeholder pictogram; `"initial"` shows the title initial. A working image still takes priority. See [Product strip](#product-strip). Forwarded by the hook in `dropdownProps`. |
772
801
  | `onProductSelect?` | `(product: Product) => void` | Called when a card is activated. |
802
+ | `onProductAddToCart?` | `(product: Product) => void \| PromiseLike<unknown>` | When set, every card that isn't sold out draws an add-to-cart button whose press calls this. See [Product strip](#product-strip). |
773
803
  | `onProductFocusChange?` | `(focused: boolean) => void` | Called when focus enters/leaves the strip. Wired to `setFocused` by `dropdownProps` so a tabbed-to card doesn't close the panel. |
774
804
  | `isLoading?` | `boolean` | When `true`, the dropdown renders its pills + options as a skeleton: text masked, layout (count and widths) preserved, shimmer pulse animating. Falls back to a generic 3-bar placeholder when no pills/options are cached. |
775
805
 
package/dist/index.d.mts CHANGED
@@ -177,6 +177,20 @@ interface AIAutocompleteProps {
177
177
  * browser (so cmd-click still opens a tab) and don't fire this.
178
178
  */
179
179
  onProductSelect?: (product: Product) => void;
180
+ /**
181
+ * Adds a product to the cart from its card. When set, every card that isn't
182
+ * sold out (`available: false`) shows an add-to-cart button in the bottom
183
+ * right corner of its image; when unset, no card has one. Pressing the button
184
+ * calls this, not `onProductSelect`, and the panel stays open. The SDK never
185
+ * touches a cart itself.
186
+ *
187
+ * Return a promise to have the button show progress: it carries
188
+ * `data-aia-cart-state="adding"` until the promise settles, then `"added"`
189
+ * if it resolved. `"added"` stays until the card's product changes; pressing
190
+ * again adds another unit (adding → added again). A rejection puts the
191
+ * button back at rest.
192
+ */
193
+ onProductAddToCart?: (product: Product) => void | PromiseLike<unknown>;
180
194
  /**
181
195
  * Custom submit button. Pass any ReactNode to replace the default button.
182
196
  * Pass `null` to render no button. Default (`undefined`) renders the built-in
@@ -337,6 +351,20 @@ interface UseAIAutocompleteOptions {
337
351
  * browser (so cmd-click still opens a tab) and don't fire this.
338
352
  */
339
353
  onProductSelect?: (product: Product) => void;
354
+ /**
355
+ * Adds a product to the cart from its card. When set, every card that isn't
356
+ * sold out (`available: false`) shows an add-to-cart button in the bottom
357
+ * right corner of its image; when unset, no card has one. Pressing the button
358
+ * calls this, not `onProductSelect`, and the panel stays open. The SDK never
359
+ * touches a cart itself.
360
+ *
361
+ * Return a promise to have the button show progress: it carries
362
+ * `data-aia-cart-state="adding"` until the promise settles, then `"added"`
363
+ * if it resolved. `"added"` stays until the card's product changes; pressing
364
+ * again adds another unit (adding → added again). A rejection puts the
365
+ * button back at rest.
366
+ */
367
+ onProductAddToCart?: (product: Product) => void | PromiseLike<unknown>;
340
368
  value?: string;
341
369
  completedParams?: CompletedParamState[];
342
370
  onChange?: (value: string) => void;
@@ -431,6 +459,13 @@ interface UseAIAutocompleteReturn {
431
459
  * never navigates.
432
460
  */
433
461
  selectProduct: (product: Product) => void;
462
+ /**
463
+ * Add a product to the cart through `onProductAddToCart`. The built-in
464
+ * dropdown's cart buttons call this for you; hand-rolled strips call it
465
+ * themselves. Resolves once the handler's promise settles and rejects with
466
+ * its error. Without a handler it does nothing.
467
+ */
468
+ addProductToCart: (product: Product) => Promise<void>;
434
469
  /** Tier 1 helper: forward plain-text input to the core (autocapitalize handled). */
435
470
  handleTextChange: (value: string) => void;
436
471
  /** Tier 1 helper: forward keyboard events to the core. */
@@ -557,6 +592,14 @@ interface AIAutocompleteDropdownProps {
557
592
  products?: Product[];
558
593
  /** Called when a product card is activated. Provided by `dropdownProps` from the hook. */
559
594
  onProductSelect?: (product: Product) => void;
595
+ /**
596
+ * Called when a card's add-to-cart button is pressed. When set, every card
597
+ * that isn't sold out draws the button; when unset, none do. Provided by
598
+ * `dropdownProps` from the hook when `onProductAddToCart` is set. Return a
599
+ * promise to have the button show `data-aia-cart-state="adding"`, then
600
+ * `"added"` once it resolves, until the card's product changes.
601
+ */
602
+ onProductAddToCart?: (product: Product) => void | PromiseLike<unknown>;
560
603
  /** How the product strip lays its cards out — see the Tier 1 prop of the same name. Default: `"row"`. */
561
604
  productsLayout?: ProductsLayout;
562
605
  /**
@@ -617,8 +660,8 @@ interface AIAutocompleteDropdownProps {
617
660
 
618
661
  declare const AIAutocomplete: react.ForwardRefExoticComponent<AIAutocompleteProps & react.RefAttributes<AIAutocompleteHandle>>;
619
662
 
620
- declare function AIAutocompleteDropdown({ suggestions, activeIndex, onSelect, onHighlight, onUnhighlight, isOpen, id, className, pills, onPillClick, showPills, onSkip, showSkipButton, showOptionIcons, showOptionImages, showOptionCounts, skipDisabled, activeSelected, isLoading, skeletonExiting, isInputEmpty, language, products, onProductSelect, onProductFocusChange, productsLayout, productImageFallback, formatType, dateView, selectedDateIso, selectedDateRange, dateRangeStart, onPreviousMonth, onNextMonth, optionsPosition, mode, }: AIAutocompleteDropdownProps): react_jsx_runtime.JSX.Element;
663
+ declare function AIAutocompleteDropdown({ suggestions, activeIndex, onSelect, onHighlight, onUnhighlight, isOpen, id, className, pills, onPillClick, showPills, onSkip, showSkipButton, showOptionIcons, showOptionImages, showOptionCounts, skipDisabled, activeSelected, isLoading, skeletonExiting, isInputEmpty, language, products, onProductSelect, onProductAddToCart, onProductFocusChange, productsLayout, productImageFallback, formatType, dateView, selectedDateIso, selectedDateRange, dateRangeStart, onPreviousMonth, onNextMonth, optionsPosition, mode, }: AIAutocompleteDropdownProps): react_jsx_runtime.JSX.Element;
621
664
 
622
- declare function useAIAutocomplete({ onSubmit, onResult, onError, optionOverrides, maskCompletedText, apiConfig, additionalContext, language, currency, productId, visitorId, columns, dropdownTrigger, optionsPosition, closeDropdownOnBlur, showNonTappableOptions, showOptionIcons, showOptionImages, showOptionCounts, showSkipButton, showProducts, productImageFallback, onFocus, onBlur, value: controlledValue, completedParams: controlledParams, onChange: onChangeProp, onParamsChange, products, mapProducts, onProductSelect, source, setCursor, }: UseAIAutocompleteOptions): UseAIAutocompleteReturn;
665
+ declare function useAIAutocomplete({ onSubmit, onResult, onError, optionOverrides, maskCompletedText, apiConfig, additionalContext, language, currency, productId, visitorId, columns, dropdownTrigger, optionsPosition, closeDropdownOnBlur, showNonTappableOptions, showOptionIcons, showOptionImages, showOptionCounts, showSkipButton, showProducts, productImageFallback, onFocus, onBlur, value: controlledValue, completedParams: controlledParams, onChange: onChangeProp, onParamsChange, products, mapProducts, onProductSelect, onProductAddToCart, source, setCursor, }: UseAIAutocompleteOptions): UseAIAutocompleteReturn;
623
666
 
624
667
  export { AIAutocomplete, AIAutocompleteDropdown, type AIAutocompleteDropdownProps, type AIAutocompleteHandle, type AIAutocompleteProps, type UseAIAutocompleteOptions, type UseAIAutocompleteReturn, useAIAutocomplete };
package/dist/index.d.ts CHANGED
@@ -177,6 +177,20 @@ interface AIAutocompleteProps {
177
177
  * browser (so cmd-click still opens a tab) and don't fire this.
178
178
  */
179
179
  onProductSelect?: (product: Product) => void;
180
+ /**
181
+ * Adds a product to the cart from its card. When set, every card that isn't
182
+ * sold out (`available: false`) shows an add-to-cart button in the bottom
183
+ * right corner of its image; when unset, no card has one. Pressing the button
184
+ * calls this, not `onProductSelect`, and the panel stays open. The SDK never
185
+ * touches a cart itself.
186
+ *
187
+ * Return a promise to have the button show progress: it carries
188
+ * `data-aia-cart-state="adding"` until the promise settles, then `"added"`
189
+ * if it resolved. `"added"` stays until the card's product changes; pressing
190
+ * again adds another unit (adding → added again). A rejection puts the
191
+ * button back at rest.
192
+ */
193
+ onProductAddToCart?: (product: Product) => void | PromiseLike<unknown>;
180
194
  /**
181
195
  * Custom submit button. Pass any ReactNode to replace the default button.
182
196
  * Pass `null` to render no button. Default (`undefined`) renders the built-in
@@ -337,6 +351,20 @@ interface UseAIAutocompleteOptions {
337
351
  * browser (so cmd-click still opens a tab) and don't fire this.
338
352
  */
339
353
  onProductSelect?: (product: Product) => void;
354
+ /**
355
+ * Adds a product to the cart from its card. When set, every card that isn't
356
+ * sold out (`available: false`) shows an add-to-cart button in the bottom
357
+ * right corner of its image; when unset, no card has one. Pressing the button
358
+ * calls this, not `onProductSelect`, and the panel stays open. The SDK never
359
+ * touches a cart itself.
360
+ *
361
+ * Return a promise to have the button show progress: it carries
362
+ * `data-aia-cart-state="adding"` until the promise settles, then `"added"`
363
+ * if it resolved. `"added"` stays until the card's product changes; pressing
364
+ * again adds another unit (adding → added again). A rejection puts the
365
+ * button back at rest.
366
+ */
367
+ onProductAddToCart?: (product: Product) => void | PromiseLike<unknown>;
340
368
  value?: string;
341
369
  completedParams?: CompletedParamState[];
342
370
  onChange?: (value: string) => void;
@@ -431,6 +459,13 @@ interface UseAIAutocompleteReturn {
431
459
  * never navigates.
432
460
  */
433
461
  selectProduct: (product: Product) => void;
462
+ /**
463
+ * Add a product to the cart through `onProductAddToCart`. The built-in
464
+ * dropdown's cart buttons call this for you; hand-rolled strips call it
465
+ * themselves. Resolves once the handler's promise settles and rejects with
466
+ * its error. Without a handler it does nothing.
467
+ */
468
+ addProductToCart: (product: Product) => Promise<void>;
434
469
  /** Tier 1 helper: forward plain-text input to the core (autocapitalize handled). */
435
470
  handleTextChange: (value: string) => void;
436
471
  /** Tier 1 helper: forward keyboard events to the core. */
@@ -557,6 +592,14 @@ interface AIAutocompleteDropdownProps {
557
592
  products?: Product[];
558
593
  /** Called when a product card is activated. Provided by `dropdownProps` from the hook. */
559
594
  onProductSelect?: (product: Product) => void;
595
+ /**
596
+ * Called when a card's add-to-cart button is pressed. When set, every card
597
+ * that isn't sold out draws the button; when unset, none do. Provided by
598
+ * `dropdownProps` from the hook when `onProductAddToCart` is set. Return a
599
+ * promise to have the button show `data-aia-cart-state="adding"`, then
600
+ * `"added"` once it resolves, until the card's product changes.
601
+ */
602
+ onProductAddToCart?: (product: Product) => void | PromiseLike<unknown>;
560
603
  /** How the product strip lays its cards out — see the Tier 1 prop of the same name. Default: `"row"`. */
561
604
  productsLayout?: ProductsLayout;
562
605
  /**
@@ -617,8 +660,8 @@ interface AIAutocompleteDropdownProps {
617
660
 
618
661
  declare const AIAutocomplete: react.ForwardRefExoticComponent<AIAutocompleteProps & react.RefAttributes<AIAutocompleteHandle>>;
619
662
 
620
- declare function AIAutocompleteDropdown({ suggestions, activeIndex, onSelect, onHighlight, onUnhighlight, isOpen, id, className, pills, onPillClick, showPills, onSkip, showSkipButton, showOptionIcons, showOptionImages, showOptionCounts, skipDisabled, activeSelected, isLoading, skeletonExiting, isInputEmpty, language, products, onProductSelect, onProductFocusChange, productsLayout, productImageFallback, formatType, dateView, selectedDateIso, selectedDateRange, dateRangeStart, onPreviousMonth, onNextMonth, optionsPosition, mode, }: AIAutocompleteDropdownProps): react_jsx_runtime.JSX.Element;
663
+ declare function AIAutocompleteDropdown({ suggestions, activeIndex, onSelect, onHighlight, onUnhighlight, isOpen, id, className, pills, onPillClick, showPills, onSkip, showSkipButton, showOptionIcons, showOptionImages, showOptionCounts, skipDisabled, activeSelected, isLoading, skeletonExiting, isInputEmpty, language, products, onProductSelect, onProductAddToCart, onProductFocusChange, productsLayout, productImageFallback, formatType, dateView, selectedDateIso, selectedDateRange, dateRangeStart, onPreviousMonth, onNextMonth, optionsPosition, mode, }: AIAutocompleteDropdownProps): react_jsx_runtime.JSX.Element;
621
664
 
622
- declare function useAIAutocomplete({ onSubmit, onResult, onError, optionOverrides, maskCompletedText, apiConfig, additionalContext, language, currency, productId, visitorId, columns, dropdownTrigger, optionsPosition, closeDropdownOnBlur, showNonTappableOptions, showOptionIcons, showOptionImages, showOptionCounts, showSkipButton, showProducts, productImageFallback, onFocus, onBlur, value: controlledValue, completedParams: controlledParams, onChange: onChangeProp, onParamsChange, products, mapProducts, onProductSelect, source, setCursor, }: UseAIAutocompleteOptions): UseAIAutocompleteReturn;
665
+ declare function useAIAutocomplete({ onSubmit, onResult, onError, optionOverrides, maskCompletedText, apiConfig, additionalContext, language, currency, productId, visitorId, columns, dropdownTrigger, optionsPosition, closeDropdownOnBlur, showNonTappableOptions, showOptionIcons, showOptionImages, showOptionCounts, showSkipButton, showProducts, productImageFallback, onFocus, onBlur, value: controlledValue, completedParams: controlledParams, onChange: onChangeProp, onParamsChange, products, mapProducts, onProductSelect, onProductAddToCart, source, setCursor, }: UseAIAutocompleteOptions): UseAIAutocompleteReturn;
623
666
 
624
667
  export { AIAutocomplete, AIAutocompleteDropdown, type AIAutocompleteDropdownProps, type AIAutocompleteHandle, type AIAutocompleteProps, type UseAIAutocompleteOptions, type UseAIAutocompleteReturn, useAIAutocomplete };