@magicx-eng/ai-autocomplete-react 0.21.2 → 0.22.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 +7 -5
- package/dist/index.js +57 -17
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +57 -17
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -383,11 +383,12 @@ narrowing.
|
|
|
383
383
|
|
|
384
384
|
Each server item becomes a `Product` with `id` (the item's stable id), `title`,
|
|
385
385
|
`price` (the item's lowest price, verbatim as the catalog spells it — the
|
|
386
|
-
currency is yours to add), `vendor`, `imageUrl`, and a
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
386
|
+
currency is yours to add), `vendor`, `imageUrl`, and a `url`: the item's own
|
|
387
|
+
`item_url` when the uploaded catalog named one (absolute or root-relative;
|
|
388
|
+
anything else is ignored), else a **relative** `/products/{handle}` — the right
|
|
389
|
+
link for a Shopify widget running on the storefront itself. An item with
|
|
390
|
+
neither gets no `url`: its card is still activatable and still emits
|
|
391
|
+
`onProductSelect`, it just has nothing for cmd-click to open. `productFromMatchedItem` / `productsFromCustomFields` are
|
|
391
392
|
exported for a hand-rolled strip that wants the same mapping.
|
|
392
393
|
|
|
393
394
|
Pass `showProducts={false}` to render no strip at all. The catalog report
|
|
@@ -749,6 +750,7 @@ Override on the container (via `className`). The variables are declared with `:w
|
|
|
749
750
|
| `--aia-border` | `rgba(17, 24, 39, 0.14)` | `#505050` | Input container border color |
|
|
750
751
|
| `--aia-shadow` | `inset 0 1px 0 rgba(255,255,255,0.9), 0 1px 2px rgba(16,24,40,0.1), 0 8px 24px rgba(16,24,40,0.16)` | `inset 0 1px 0 rgba(255,255,255,0.06), 0 1px 2px rgba(0,0,0,0.4), 0 8px 24px rgba(0,0,0,0.5)` | Input container elevation (box-shadow) |
|
|
751
752
|
| `--aia-dropdown-offset` | `13px` | `13px` | Gap between the input box and the dropdown (the dropdown's margin toward the input). |
|
|
753
|
+
| `--aia-content-inset-left` | `16px` | `16px` | Left inset of the SDK's content: the input wrapper's left padding, and the line the dropdown's content (parameter label, option text, product strip) starts on. Set it when you pad the field to clear a leading icon of your own, so the dropdown's content stays aligned under the typed text. Practical minimum is `10px` (the option text's own inset); both surfaces floor it there. |
|
|
752
754
|
| `--aia-footer-gap` | `8px` | `8px` | Breathing room above the dropdown footer (badge / keyboard hints), additive to the dropdown's 8px section gap. |
|
|
753
755
|
| `--aia-footer-chip-bg` | `--aia-surface` at 65% | `--aia-surface` at 65% | Fill behind the footer's keyboard hint and AI-Autocomplete badge. The option list scrolls under the footer, so this keeps both legible over the row passing behind them. `transparent` on the glass surface. |
|
|
754
756
|
| `--aia-dropdown-bg` | — | — | Optional bg color the dropdown's "glass" rim shadow tints toward. Set this to the page background behind the dropdown so the bottom-corner glow blends seamlessly. |
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,14 @@
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
.aia-inputWrapper-FLq1b {
|
|
11
|
-
|
|
11
|
+
/* The left inset is a token shared with the dropdown (which insets its
|
|
12
|
+
content by the same amount), so the typed text, the parameter label,
|
|
13
|
+
the option text and the product strip share one left edge. A host that
|
|
14
|
+
pads the field to clear its own leading icon sets --aia-content-inset-left
|
|
15
|
+
and the dropdown follows. Floored at 10px \u2014 the dropdown's sections inset
|
|
16
|
+
their text by 10px and its own padding can't go below zero, so below
|
|
17
|
+
10px the two sides would diverge; flooring both keeps them together. */
|
|
18
|
+
padding: 12px 16px 12px max(10px, var(--aia-content-inset-left, 16px));
|
|
12
19
|
border: 1px solid var(--aia-border, #b0b0b0);
|
|
13
20
|
border-radius: 20px;
|
|
14
21
|
background: var(--aia-surface, #ffffff);
|
|
@@ -701,7 +708,15 @@
|
|
|
701
708
|
display: flex;
|
|
702
709
|
flex-direction: column;
|
|
703
710
|
box-sizing: border-box;
|
|
704
|
-
padding
|
|
711
|
+
/* The left padding derives from --aia-content-inset-left, the token the
|
|
712
|
+
input wrapper reads for its own left padding. The option text and the
|
|
713
|
+
parameter label sit 10px inside this padding edge (the option's padding;
|
|
714
|
+
the pill bar's list wrapper carries the same 10px \u2014 see appearance.css),
|
|
715
|
+
and the product strip is inset to the same line, so the whole panel's
|
|
716
|
+
content starts exactly under the typed text. max() keeps the padding at
|
|
717
|
+
zero (never negative, which CSS would clamp anyway) for a token under
|
|
718
|
+
10px \u2014 the input wrapper floors the token at 10px to match. */
|
|
719
|
+
padding: 10px 8px 10px max(0px, calc(var(--aia-content-inset-left, 16px) - 10px));
|
|
705
720
|
overflow: hidden;
|
|
706
721
|
container-type: inline-size;
|
|
707
722
|
z-index: 10;
|
|
@@ -770,9 +785,10 @@
|
|
|
770
785
|
/* "More below" scroll arrow \u2014 see the core's render/scrollArrow.ts.
|
|
771
786
|
A bare down chevron at the bottom-centre of the option list, shown while
|
|
772
787
|
there is more to scroll to \u2014 no disc, border or shadow around it. It rests
|
|
773
|
-
just above the grid's reserved band
|
|
774
|
-
|
|
775
|
-
|
|
788
|
+
just above the grid's reserved band, or \u2014 when a product strip follows the
|
|
789
|
+
grid \u2014 centred on the strip's "Products" label row, off the last option row
|
|
790
|
+
(--aia-scroll-arrow-bottom is measured and set inline by the controller so
|
|
791
|
+
it tracks the layout). It never travels with the scroll: the controller toggles
|
|
776
792
|
data-aia-visible and the only thing that transitions is opacity \u2014 a slow,
|
|
777
793
|
even second in and the same second out. Its one motion is an idle bob, a few
|
|
778
794
|
pixels up and back over a couple of seconds, running all the time so a fade
|
|
@@ -1384,9 +1400,11 @@
|
|
|
1384
1400
|
display: flex;
|
|
1385
1401
|
flex-direction: column;
|
|
1386
1402
|
gap: 6px;
|
|
1387
|
-
/*
|
|
1388
|
-
|
|
1389
|
-
|
|
1403
|
+
/* The option text sits 10px inside the dropdown's padding edge (the
|
|
1404
|
+
option's own padding), so the section carries the same 10px and its
|
|
1405
|
+
label lands on the option text's left edge \u2014 which the dropdown in turn
|
|
1406
|
+
places under the typed text (--aia-content-inset-left). */
|
|
1407
|
+
padding: 0 10px;
|
|
1390
1408
|
min-width: 0;
|
|
1391
1409
|
}
|
|
1392
1410
|
|
|
@@ -1414,6 +1432,10 @@
|
|
|
1414
1432
|
overflow-y: hidden;
|
|
1415
1433
|
overscroll-behavior-x: contain;
|
|
1416
1434
|
padding-bottom: 2px;
|
|
1435
|
+
/* Pull the row out by the card's own 6px padding so the first card's
|
|
1436
|
+
image tile \u2014 not its hover fill \u2014 starts on the label's left edge, the
|
|
1437
|
+
same way the option's highlight fill hangs outside its text. */
|
|
1438
|
+
margin-inline: -6px;
|
|
1417
1439
|
/* Hide the scrollbar chrome across all three engines while leaving the box
|
|
1418
1440
|
scrollable by trackpad, wheel, touch and keyboard. */
|
|
1419
1441
|
scrollbar-width: none;
|
|
@@ -1542,7 +1564,7 @@
|
|
|
1542
1564
|
var(--aia-option-color-selected, var(--aia-color-text-default, #fff))
|
|
1543
1565
|
);
|
|
1544
1566
|
}
|
|
1545
|
-
`,document.head.appendChild(e)}var Y={section:"aia-section-Hugfg",label:"aia-label-nuc93",row:"aia-row-WDVBX",card:"aia-card-JBGYT",media:"aia-media-RrbGe",image:"aia-image-5pNL7",body:"aia-body-ly032",vendor:"aia-vendor-Gvu7G",title:"aia-title-gCNmq",price:"aia-price-gULcE"};var q=require("react/jsx-runtime");function wt({products:e,listboxId:i,onSelect:a,onFocusChange:r,focusable:n=!0}){if(e.length===0)return null;let o=`${i}-products-label`;return(0,q.jsxs)("section",{className:Y.section,"data-aia-products":"",role:"group","aria-labelledby":o,children:[(0,q.jsx)("div",{className:Y.label,id:o,children:"Products"}),(0,q.jsx)("div",{className:Y.row,"data-aia-products-row":"",children:e.map((s,g)=>(0,q.jsx)(Jt,{product:s,id:`${i}-product-${g}`,onSelect:a,onFocusChange:r,focusable:n},s.id))})]})}function Jt({product:e,id:i,onSelect:a,onFocusChange:r,focusable:n}){let o=s=>{s.metaKey||s.ctrlKey||s.shiftKey||s.altKey||s.button!==0||(s.preventDefault(),a(e))};return(0,q.jsxs)("a",{id:i,className:Y.card,"data-aia-product":"",role:"option","aria-selected":!1,href:e.url,tabIndex:n?0:-1,onClick:o,onKeyDown:s=>{s.key!=="Enter"&&s.key!==" "||(s.preventDefault(),a(e))},onFocus:()=>r?.(!0),onBlur:s=>{s.relatedTarget?.closest("[data-aia-dropdown]")||r?.(!1)},children:[(0,q.jsx)("span",{className:Y.media,"data-aia-product-placeholder":e.imageUrl?void 0:"",children:e.imageUrl?(0,q.jsx)("img",{className:Y.image,src:e.imageUrl,alt:"",loading:"lazy",decoding:"async"}):null}),(0,q.jsxs)("span",{className:Y.body,children:[e.vendor?(0,q.jsx)("span",{className:Y.vendor,children:e.vendor}):null,(0,q.jsx)("span",{className:Y.title,children:e.title}),e.price?(0,q.jsx)("span",{className:Y.price,children:e.price}):null]})]})}var U=require("@magicx-eng/ai-autocomplete-vanilla"),fe=require("react");var xt=require("@magicx-eng/ai-autocomplete-vanilla"),je=require("react");if(typeof document<"u"&&!document.getElementById("ac-style-948e58da")){let e=document.createElement("style");e.id="ac-style-948e58da",e.textContent=`.aia-grid {
|
|
1567
|
+
`,document.head.appendChild(e)}var Y={section:"aia-section-Hugfg",label:"aia-label-nuc93",row:"aia-row-WDVBX",card:"aia-card-JBGYT",media:"aia-media-RrbGe",image:"aia-image-5pNL7",body:"aia-body-ly032",vendor:"aia-vendor-Gvu7G",title:"aia-title-gCNmq",price:"aia-price-gULcE"};var q=require("react/jsx-runtime");function wt({products:e,listboxId:i,onSelect:a,onFocusChange:r,focusable:n=!0}){if(e.length===0)return null;let o=`${i}-products-label`;return(0,q.jsxs)("section",{className:Y.section,"data-aia-products":"",role:"group","aria-labelledby":o,children:[(0,q.jsx)("div",{className:Y.label,id:o,"data-aia-products-label":"",children:"Products"}),(0,q.jsx)("div",{className:Y.row,"data-aia-products-row":"",children:e.map((s,g)=>(0,q.jsx)(Jt,{product:s,id:`${i}-product-${g}`,onSelect:a,onFocusChange:r,focusable:n},s.id))})]})}function Jt({product:e,id:i,onSelect:a,onFocusChange:r,focusable:n}){let o=s=>{s.metaKey||s.ctrlKey||s.shiftKey||s.altKey||s.button!==0||(s.preventDefault(),a(e))};return(0,q.jsxs)("a",{id:i,className:Y.card,"data-aia-product":"",role:"option","aria-selected":!1,href:e.url,tabIndex:n?0:-1,onClick:o,onKeyDown:s=>{s.key!=="Enter"&&s.key!==" "||(s.preventDefault(),a(e))},onFocus:()=>r?.(!0),onBlur:s=>{s.relatedTarget?.closest("[data-aia-dropdown]")||r?.(!1)},children:[(0,q.jsx)("span",{className:Y.media,"data-aia-product-placeholder":e.imageUrl?void 0:"",children:e.imageUrl?(0,q.jsx)("img",{className:Y.image,src:e.imageUrl,alt:"",loading:"lazy",decoding:"async"}):null}),(0,q.jsxs)("span",{className:Y.body,children:[e.vendor?(0,q.jsx)("span",{className:Y.vendor,children:e.vendor}):null,(0,q.jsx)("span",{className:Y.title,children:e.title}),e.price?(0,q.jsx)("span",{className:Y.price,children:e.price}):null]})]})}var U=require("@magicx-eng/ai-autocomplete-vanilla"),fe=require("react");var xt=require("@magicx-eng/ai-autocomplete-vanilla"),je=require("react");if(typeof document<"u"&&!document.getElementById("ac-style-948e58da")){let e=document.createElement("style");e.id="ac-style-948e58da",e.textContent=`.aia-grid {
|
|
1546
1568
|
display: grid;
|
|
1547
1569
|
grid-template-columns: repeat(
|
|
1548
1570
|
auto-fit,
|
|
@@ -1577,7 +1599,17 @@
|
|
|
1577
1599
|
}
|
|
1578
1600
|
`,document.head.appendChild(e)}var kt=require("react/jsx-runtime");function yt({min:e="16rem",max:i,gap:a,scroll:r=!1,maxHeight:n,scrollResetKey:o,cols:s,template:g,innerRef:w,className:b,children:x,...m}){let y=(0,je.useRef)(null);(0,je.useLayoutEffect)(()=>{if(o===void 0)return;let v=y.current;v&&(v.scrollTop=0)},[o]);let P={"--aia-grid-min":e};return i&&(P["--aia-grid-max"]=i),a&&(P["--aia-grid-gap"]=a),n&&(P["--aia-grid-max-height"]=n),s&&(P.gridTemplateColumns=(0,xt.optionsGridTemplateColumns)(s)),g&&(P.gridTemplateColumns=g),(0,kt.jsx)("div",{ref:v=>{y.current=v,w?.(v)},className:b?`aia-grid ${b}`:"aia-grid","data-scroll":r||void 0,style:P,...m,children:x})}var De=require("@magicx-eng/ai-autocomplete-vanilla"),me=require("react");if(typeof document<"u"&&!document.getElementById("ac-style-82820da7")){let e=document.createElement("style");e.id="ac-style-82820da7",e.textContent=`.aia-item-d4vpD {
|
|
1579
1601
|
position: relative;
|
|
1580
|
-
|
|
1602
|
+
/* Clip, not visible: the entrance rise below runs on the inner content,
|
|
1603
|
+
and a transformed box extends its scroll container's scrollable area.
|
|
1604
|
+
Unclipped, the rows rising 16px from below made the option grid overflow
|
|
1605
|
+
for the length of the entrance whenever nothing sat under the grid to
|
|
1606
|
+
absorb it (product strip, optionsPosition="above"), so a scrollbar
|
|
1607
|
+
flashed in and out and the columns shifted sideways by its width. The
|
|
1608
|
+
cell's own children never paint outside it (the streak layer clips
|
|
1609
|
+
itself; press and highlight stay inside the box), so nothing is lost.
|
|
1610
|
+
\`hidden\` first for engines without \`clip\`. */
|
|
1611
|
+
overflow: hidden;
|
|
1612
|
+
overflow: clip;
|
|
1581
1613
|
display: flex;
|
|
1582
1614
|
/* Top-align so single-line and multi-line options in the same row share
|
|
1583
1615
|
the same baseline at the top edge of the cell. */
|
|
@@ -1595,11 +1627,10 @@
|
|
|
1595
1627
|
padding: 8px 10px;
|
|
1596
1628
|
/* Entrance: fade in while rising into place, each row a beat after the one
|
|
1597
1629
|
before it (--aia-option-enter-delay is set inline per row from the core's
|
|
1598
|
-
optionEnterDelayMs). The
|
|
1599
|
-
the
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
aia-riseIn-etXZT 280ms cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
|
|
1630
|
+
optionEnterDelayMs). The fade is here; the rise is on the inner content
|
|
1631
|
+
(see the overflow note above). The durations and distance mirror
|
|
1632
|
+
OPTION_ENTER_* in the core's derive/optionsEntrance.ts \u2014 change both together. */
|
|
1633
|
+
animation: aia-fadeIn-I8u35 150ms ease-out both;
|
|
1603
1634
|
animation-delay: var(--aia-option-enter-delay, 0ms);
|
|
1604
1635
|
}
|
|
1605
1636
|
|
|
@@ -1616,7 +1647,8 @@
|
|
|
1616
1647
|
}
|
|
1617
1648
|
|
|
1618
1649
|
@media (prefers-reduced-motion: reduce) {
|
|
1619
|
-
.aia-item-d4vpD
|
|
1650
|
+
.aia-item-d4vpD,
|
|
1651
|
+
.aia-content-T-Qba {
|
|
1620
1652
|
animation-duration: 0s;
|
|
1621
1653
|
animation-delay: 0s;
|
|
1622
1654
|
}
|
|
@@ -1625,6 +1657,10 @@
|
|
|
1625
1657
|
.aia-content-T-Qba {
|
|
1626
1658
|
position: relative;
|
|
1627
1659
|
z-index: 2;
|
|
1660
|
+
/* The entrance rise (the cell above keeps the fade). Inherits the cell's
|
|
1661
|
+
per-row delay through the custom property. */
|
|
1662
|
+
animation: aia-riseIn-etXZT 280ms cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
|
|
1663
|
+
animation-delay: var(--aia-option-enter-delay, 0ms);
|
|
1628
1664
|
}
|
|
1629
1665
|
|
|
1630
1666
|
.aia-tappable-70KcX {
|
|
@@ -1766,7 +1802,7 @@
|
|
|
1766
1802
|
filter: brightness(0.55);
|
|
1767
1803
|
}
|
|
1768
1804
|
}
|
|
1769
|
-
`,document.head.appendChild(e)}var X={item:"aia-item-d4vpD",fadeIn:"aia-fadeIn-I8u35",
|
|
1805
|
+
`,document.head.appendChild(e)}var X={item:"aia-item-d4vpD",fadeIn:"aia-fadeIn-I8u35",content:"aia-content-T-Qba",riseIn:"aia-riseIn-etXZT",tappable:"aia-tappable-70KcX",nonTappable:"aia-nonTappable-xSZM-",highlighted:"aia-highlighted-Hb0SU",tag:"aia-tag-e3Fwe",pressed:"aia-pressed-98o-r",pressCompress:"aia-pressCompress-ICV3q",scrollHint:"aia-scrollHint-xKXPt",skeletonPulse:"aia-skeletonPulse-plvdD",text:"aia-text-yqoh9",icon:"aia-icon-Qw938"};var Ie=require("react/jsx-runtime");function Ct({option:e,isHighlighted:i,onSelect:a,onHighlight:r,id:n,loading:o,showIcon:s=!0,scrollHint:g,enterDelayMs:w=0}){let[b]=(0,me.useState)(w),[x,m]=(0,me.useState)(!1),y=(0,me.useMemo)(()=>s&&e.icon_svg?(0,De.sanitizeOptionIconSvg)(e.icon_svg):null,[s,e.icon_svg]),P=(0,me.useRef)(void 0);(0,me.useEffect)(()=>()=>clearTimeout(P.current),[]);let v=()=>{o||!e.is_tappable||x||(m(!0),a(e),clearTimeout(P.current),P.current=setTimeout(()=>m(!1),170))},A=[X.item,i&&!o?X.highlighted:"",e.is_tappable?X.tappable:X.nonTappable,x?X.pressed:"",g?X.scrollHint:""].filter(Boolean).join(" ");return(0,Ie.jsx)("div",{id:n,role:"option","data-aia-option":"","data-aia-loading":o?"":void 0,"data-aia-icon":y&&e.icon?e.icon:void 0,"aria-selected":i,className:A,style:{[De.OPTION_ENTER_DELAY_VAR]:`${b}ms`},tabIndex:o||!e.is_tappable?-1:0,onClick:v,onKeyDown:M=>{!o&&e.is_tappable&&(M.key==="Enter"||M.key===" ")&&(M.preventDefault(),v())},onMouseEnter:!o&&e.is_tappable?r:void 0,children:(0,Ie.jsxs)("span",{className:X.content,children:[y&&(0,Ie.jsx)("span",{className:`${X.icon} magicx-aia-option-icon`,"aria-hidden":"true",dangerouslySetInnerHTML:{__html:y}}),(0,Ie.jsx)("span",{className:X.text,children:(0,De.optionLabel)(e)}),e.tag&&(0,Ie.jsx)("span",{className:X.tag,children:e.tag})]})})}var nt=require("react/jsx-runtime");function ea(){let[e,i]=(0,fe.useState)(U.isOptionsGridMobileViewport);return(0,fe.useEffect)(()=>{if(typeof window>"u"||!window.matchMedia)return;let a=window.matchMedia(U.OPTIONS_GRID_MOBILE_QUERY),r=()=>i(a.matches);return r(),a.addEventListener("change",r),()=>a.removeEventListener("change",r)},[]),e}function Pt({options:e,activeIndex:i,onSelect:a,onHighlight:r,listboxId:n,loading:o,groupKey:s="",optionsPosition:g="below",showIcons:w=!0}){let b=ea(),x=(0,fe.useRef)(null),[m,y]=(0,fe.useState)(null);(0,fe.useLayoutEffect)(()=>{let v=x.current,M=v&&!o&&(0,U.needsOptionsGridMeasurement)(e.length,b)?(0,U.measureOptionsGrid)(v):null;y(M?(0,U.planOptionsGrid)(e.length,b,M.rowWidths,M.gridWidth):null)},[e,b,o]);let P=m??(0,U.planOptionsGrid)(e.length,b,null,null);return(0,nt.jsx)(yt,{min:"250px",max:"1fr",gap:"0",scroll:!0,scrollResetKey:s,template:P.template,maxHeight:P.maxHeight,innerRef:v=>{x.current=v},"data-aia-group":s,children:e.map((v,A)=>(0,nt.jsx)(Ct,{option:v,isHighlighted:A===i,onSelect:a,onHighlight:()=>r(A),id:`${n}-option-${A}`,loading:o,showIcon:w,scrollHint:!o&&P.scrollHintIndices.includes(A),enterDelayMs:o?0:(0,U.optionEnterDelayMs)(A,P.cols,e.length,g)},`${s}\0${v.text}`))})}if(typeof document<"u"&&!document.getElementById("ac-style-8414cd5f")){let e=document.createElement("style");e.id="ac-style-8414cd5f",e.textContent=`.aia-stack {
|
|
1770
1806
|
display: flex;
|
|
1771
1807
|
flex-direction: column;
|
|
1772
1808
|
gap: var(--aia-stack-space, 0.5rem);
|
|
@@ -1805,6 +1841,10 @@
|
|
|
1805
1841
|
width: 18px;
|
|
1806
1842
|
height: 18px;
|
|
1807
1843
|
fill: none;
|
|
1844
|
+
/* The root draws nothing and the path pins its own stroke, but a page's
|
|
1845
|
+
\`svg { stroke: \u2026 }\` would otherwise still land here; say none, as the
|
|
1846
|
+
markup does. */
|
|
1847
|
+
stroke: none;
|
|
1808
1848
|
}
|
|
1809
1849
|
.aia-submitButton-otz7H svg path {
|
|
1810
1850
|
fill: none;
|