@contentful/f36-autocomplete 5.0.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.mdx +2 -0
- package/dist/esm/index.js +6 -5
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.mts +161 -0
- package/dist/index.d.ts +45 -3
- package/dist/index.js +7 -8
- package/dist/index.js.map +1 -1
- package/package.json +14 -12
package/README.mdx
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: 'Autocomplete'
|
|
3
|
+
type: 'component'
|
|
4
|
+
status: 'stable'
|
|
3
5
|
slug: /components/autocomplete/
|
|
4
6
|
github: 'https://github.com/contentful/forma-36/tree/main/packages/components/autocomplete'
|
|
5
7
|
storybook: 'https://f36-storybook.contentful.com/?path=/story/components-autocomplete--basic'
|
package/dist/esm/index.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import i, { useState, useCallback } from 'react';
|
|
2
2
|
import { cx, css } from 'emotion';
|
|
3
3
|
import { useCombobox } from 'downshift';
|
|
4
|
-
import { mergeRefs } from '@contentful/f36-core';
|
|
4
|
+
import { mergeRefs, getMenuItemStyles } from '@contentful/f36-core';
|
|
5
5
|
import { IconButton } from '@contentful/f36-button';
|
|
6
6
|
import { TextInput } from '@contentful/f36-forms';
|
|
7
|
-
import {
|
|
7
|
+
import { CaretDownIcon, XIcon } from '@contentful/f36-icons';
|
|
8
8
|
import { Skeleton } from '@contentful/f36-skeleton';
|
|
9
9
|
import { Popover } from '@contentful/f36-popover';
|
|
10
10
|
import { Subheading, SectionHeading, Text } from '@contentful/f36-typography';
|
|
11
11
|
import { getStringMatch } from '@contentful/f36-utils';
|
|
12
|
-
import
|
|
12
|
+
import l from '@contentful/f36-tokens';
|
|
13
13
|
|
|
14
|
-
var
|
|
14
|
+
var Ve=Object.defineProperty,Ge=Object.defineProperties;var ke=Object.getOwnPropertyDescriptors;var A=Object.getOwnPropertySymbols;var Q=Object.prototype.hasOwnProperty,Y=Object.prototype.propertyIsEnumerable;var J=(t,o,n)=>o in t?Ve(t,o,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[o]=n,m=(t,o)=>{for(var n in o||(o={}))Q.call(o,n)&&J(t,n,o[n]);if(A)for(var n of A(o))Y.call(o,n)&&J(t,n,o[n]);return t},u=(t,o)=>Ge(t,ke(o));var Z=(t,o)=>{var n={};for(var s in t)Q.call(t,s)&&o.indexOf(s)<0&&(n[s]=t[s]);if(t!=null&&A)for(var s of A(t))o.indexOf(s)<0&&Y.call(t,s)&&(n[s]=t[s]);return n};var E=t=>({autocomplete:css({position:"relative",width:"100%"}),combobox:css({position:"relative"}),inputField:css({paddingRight:l.spacingXl,textOverflow:"ellipsis",whiteSpace:"nowrap"}),toggleButton:css({position:"absolute",top:"1px",right:"1px",zIndex:1,padding:l.spacing2Xs,minHeight:"calc(100% - 2px)"}),content:css({overflow:"auto",maxHeight:`${t}px`}),list:css({listStyle:"none",padding:`${l.spacingXs} ${l.spacing2Xs}`,margin:0}),groupTitle:css({padding:`${l.spacingXs} ${l.spacingM}`,lineHeight:l.lineHeightM}),noMatchesTitle:css({color:l.gray500,margin:`${l.spacingM} 0 ${l.spacingM} 0`}),item:({isActive:o=!1,isDisabled:n=!1})=>cx(getMenuItemStyles({isActive:o,isDisabled:n})),highlighted:css({backgroundColor:l.gray100}),hidden:css({display:"none"})});var N=t=>{let{items:o,elementStartIndex:n,highlightedIndex:s,getItemProps:g,renderItem:x,inputValue:v,listMaxHeight:P=180}=t,y=E(P);return i.createElement("ul",{className:y.list,"data-test-id":"cf-autocomplete-list"},o.map((I,V)=>{let r=n+V,C=g({item:I,index:r});return i.createElement(Text,u(m({},C),{as:"li",key:r,className:cx([y.item({}),s===r&&y.highlighted]),"data-test-id":`cf-autocomplete-list-item-${r}`}),x?x(I,v):typeof I=="string"?i.createElement(ee,{item:I,inputValue:v}):I)}))};N.displayName="AutocompleteItems";function ee({item:t,inputValue:o}){let{before:n,match:s,after:g}=getStringMatch(t,o);return i.createElement(i.Fragment,null,n,i.createElement("b",null,s),g)}ee.displayName="HighlightedItem";function je(t,o){let {isOpen:n,onClose:s,onOpen:g,id:x,className:v,textOnAfterSelect:P="replace",closeAfterSelect:y=!0,defaultValue:I="",selectedItem:V,items:r,inputValue:C,onInputValueChange:S,onSelectItem:ie,onFocus:G,onBlur:k,renderItem:_,icon:se=i.createElement(CaretDownIcon,{color:l.gray600}),itemToString:re=e=>e,isInvalid:pe,isDisabled:U,isRequired:le,isReadOnly:ae,showEmptyList:me,noMatchesMessage:ue="No matches found",placeholder:ce="Search",inputRef:de,toggleRef:ge,listRef:Ie,listWidth:he="auto",listMaxHeight:fe=180,isGrouped:M=!1,isLoading:L=!1,usePortal:ye=!1,testId:Te="cf-autocomplete",popoverTestId:be="cf-autocomplete-container",showClearButton:R,aria:W={clearSelectionIconLabel:"Clear",showListIconLabel:"Show list"}}=t,a=E(fe),[xe,ve]=useState(I),T=typeof C=="undefined"?xe:C,b=useCallback(e=>{ve(e),S==null||S(e);},[S]),Pe=useCallback(e=>{let p=e.target.value;b(p);},[b]),Ce=O(M)?r.reduce((e,p)=>[...e,...p.options],[]):r,Se=O(M)?r.every(e=>e.options.length===0):r.length===0,{getComboboxProps:Me,getInputProps:Le,getItemProps:q,getMenuProps:we,getToggleButtonProps:He,highlightedIndex:K,isOpen:Ae,openMenu:Ee,toggleMenu:Ne}=useCombobox({isOpen:n,onIsOpenChange:({isOpen:e})=>{e?g==null||g():s==null||s();},stateReducer:(e,{type:p,changes:h})=>{switch(p){case useCombobox.stateChangeTypes.InputBlur:return u(m({},h),{inputValue:e.inputValue});case useCombobox.stateChangeTypes.InputKeyDownEnter:case useCombobox.stateChangeTypes.ItemClick:return y?h:u(m({},h),{isOpen:e.isOpen});default:return h}},items:Ce,selectedItem:V,inputValue:T,itemToString:re,onInputValueChange:({type:e,inputValue:p})=>{switch(e){case useCombobox.stateChangeTypes.InputChange:return;case useCombobox.stateChangeTypes.ItemClick:case useCombobox.stateChangeTypes.InputKeyDownEnter:{if(P==="clear"){b("");return}if(P==="preserve")return}}b(p);},onStateChange:({type:e,selectedItem:p})=>{switch(e){case useCombobox.stateChangeTypes.InputKeyDownEnter:case useCombobox.stateChangeTypes.ItemClick:p&&ie(p);break;}}}),j=Le(),w=Z(j,["aria-labelledby","id"]),Oe=Me(),z=He(),H=we(),B=0,D=R!=null?R:T;return i.createElement("div",{"data-test-id":Te,className:cx(a.autocomplete,v),ref:o},i.createElement(Popover,{usePortal:ye,isOpen:Ae,isFullWidth:he==="full",renderOnlyWhenOpen:!1,autoFocus:!1,id:H.id},i.createElement(Popover.Trigger,null,i.createElement("div",u(m({},Oe),{className:a.combobox}),i.createElement(TextInput,u(m({className:a.inputField},w),{onFocus:e=>{G==null||G(e),Ee();},onBlur:e=>{k==null||k(e),w.onBlur(e);},id:x,isInvalid:pe,isDisabled:U,isRequired:le,isReadOnly:ae,ref:mergeRefs(w.ref,de),testId:"cf-autocomplete-input",placeholder:ce,onChange:e=>{w.onChange(e),Pe(e);}})),i.createElement(IconButton,u(m({},z),{ref:mergeRefs(z.ref,ge),"aria-label":D?W.clearSelectionIconLabel:W.showListIconLabel,className:a.toggleButton,variant:"transparent",icon:D?i.createElement(XIcon,{color:l.gray600}):se,onClick:()=>{D?b(""):Ne();},isDisabled:U,size:"small"})))),r.length>0||T.length>0||me?i.createElement(Popover.Content,u(m({},H),{ref:mergeRefs(H.ref,Ie),className:a.content,testId:be}),L&&[...Array(3)].map((e,p)=>i.createElement("div",{key:p,className:cx(a.item({isDisabled:!0}))},i.createElement(Je,null))),!L&&Se&&i.createElement("div",{className:a.item({})},i.createElement(Subheading,{className:a.noMatchesTitle},ue)),!L&&O(M)&&r.map((e,p)=>{if(e.options.length<1)return;let h=i.createElement("div",{key:p},i.createElement(SectionHeading,{key:p,"data-test-id":"cf-autocomplete-grouptitle",marginBottom:"none",className:a.groupTitle},e.groupTitle),i.createElement(N,{items:e.options,highlightedIndex:K,getItemProps:q,renderItem:_,inputValue:T,elementStartIndex:B}));return B+=e.options.length,h}),!L&&!O(M)&&r.length>0&&i.createElement(N,{items:r,elementStartIndex:B,highlightedIndex:K,getItemProps:q,renderItem:_,inputValue:T})):i.createElement("div",u(m({},H),{className:cx(a.hidden)}))))}var Je=()=>i.createElement(Skeleton.Container,{svgHeight:16},i.createElement(Skeleton.BodyText,{numberOfLines:1}));function O(t,o){return t}var Qe=i.forwardRef(je);
|
|
15
15
|
|
|
16
|
-
export {
|
|
16
|
+
export { Qe as Autocomplete };
|
|
17
|
+
//# sourceMappingURL=out.js.map
|
|
17
18
|
//# sourceMappingURL=index.js.map
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/Autocomplete.tsx","../../src/AutocompleteItems.tsx","../../src/Autocomplete.styles.ts"],"names":["React","useCallback","useState","cx","useCombobox","mergeRefs","IconButton","TextInput","CloseIcon","ChevronDownIcon","Skeleton","Popover","Subheading","SectionHeading","getStringMatch","Text","css","tokens","getAutocompleteStyles","listMaxHeight","AutocompleteItems","props","items","elementStartIndex","highlightedIndex","getItemProps","renderItem","inputValue","styles","item","index","itemIndex","itemProps","__spreadProps","__spreadValues","HighlightedItem","before","match","after","_Autocomplete","ref","id","className","clearAfterSelect","closeAfterSelect","defaultValue","selectedItem","onInputValueChange","onSelectItem","icon","itemToString","isInvalid","isDisabled","isRequired","isReadOnly","showEmptyList","noMatchesMessage","placeholder","inputRef","toggleRef","listRef","listWidth","isGrouped","isLoading","usePortal","testId","setInputValue","handleInputValueChange","value","handleNativeChangeEvent","event","flattenItems","isUsingGroups","acc","group","isShowingNoMatches","getComboboxProps","getInputProps","getMenuProps","getToggleButtonProps","isOpen","toggleMenu","type","_a","_labelledby","_inputId","inputProps","__objRest","comboboxProps","toggleProps","menuProps","_","ListItemLoadingState","render","Autocomplete"],"mappings":"qlBAAA,OAAOA,GAAS,eAAAC,EAAa,YAAAC,OAAgB,QAC7C,OAAS,MAAAC,MAAU,UACnB,OAAS,eAAAC,MAAmB,YAE5B,OACE,aAAAC,MAGK,uBACP,OAAS,cAAAC,OAAkB,yBAC3B,OAAS,aAAAC,OAAsC,wBAC/C,OAAS,aAAAC,GAAW,mBAAAC,OAAuB,wBAC3C,OAAS,YAAAC,MAAgB,2BACzB,OAAS,WAAAC,MAAe,0BACxB,OAAS,cAAAC,GAAY,kBAAAC,OAAsB,6BCd3C,OAAOb,MAA+B,QACtC,OAAS,MAAAG,OAAU,UACnB,OAAS,kBAAAW,OAAsB,wBAE/B,OAAS,QAAAC,OAAY,6BCJrB,OAAS,OAAAC,MAAW,UACpB,OAAOC,MAAY,yBAEZ,IAAMC,EAAyBC,IAA2B,CAC/D,aAAcH,EAAI,CAChB,SAAU,WACV,MAAO,MACT,CAAC,EACD,SAAUA,EAAI,CACZ,SAAU,UACZ,CAAC,EACD,WAAYA,EAAI,CACd,aAAcC,EAAO,UACrB,aAAc,WACd,WAAY,QACd,CAAC,EACD,aAAcD,EAAI,CAChB,SAAU,WACV,IAAK,MACL,MAAO,MACP,OAAQ,EACR,QAASC,EAAO,WAChB,OAAQ,MACV,CAAC,EACD,QAASD,EAAI,CACX,SAAU,OACV,UAAW,GAAGG,KAChB,CAAC,EACD,KAAMH,EAAI,CACR,UAAW,OACX,QAAS,GAAGC,EAAO,cACnB,OAAQ,CACV,CAAC,EACD,WAAYD,EAAI,CACd,QAAS,GAAGC,EAAO,aAAaA,EAAO,WACvC,WAAYA,EAAO,WACrB,CAAC,EACD,eAAgBD,EAAI,CAClB,MAAOC,EAAO,QACd,OAAQ,GAAGA,EAAO,cAAcA,EAAO,YACzC,CAAC,EACD,KAAMD,EAAI,CACR,QAAS,QACT,QAAS,GAAGC,EAAO,aAAaA,EAAO,WACvC,UAAW,aACX,WAAY,eACZ,OAAQ,UACR,QAAS,OAET,mBAAoB,CAClB,gBAAiBA,EAAO,OAC1B,EACA,WAAY,CACV,gBAAiBA,EAAO,OAC1B,CACF,CAAC,EACD,SAAUD,EAAI,CACZ,QAAS,GACT,cAAe,MACjB,CAAC,EACD,YAAaA,EAAI,CACf,gBAAiBC,EAAO,OAC1B,CAAC,EACD,OAAQD,EAAI,CACV,QAAS,MACX,CAAC,CACH,GD9CO,IAAMI,EACXC,GACG,CACH,GAAM,CACJ,MAAAC,EACA,kBAAAC,EACA,iBAAAC,EACA,aAAAC,EACA,WAAAC,EACA,WAAAC,EACA,cAAAR,EAAgB,GAClB,EAAIE,EAEEO,EAASV,EAAsBC,CAAa,EAElD,OACEnB,EAAA,cAAC,MAAG,UAAW4B,EAAO,KAAM,eAAa,wBACtCN,EAAM,IAAI,CAACO,EAAgBC,IAAkB,CAC5C,IAAMC,EAAYR,EAAoBO,EAChCE,EAAYP,EAAa,CAAE,KAAAI,EAAM,MAAOE,CAAU,CAAC,EACzD,OACE/B,EAAA,cAACe,GAAAkB,EAAAC,EAAA,GACKF,GADL,CAEC,GAAG,KACH,IAAKD,EACL,UAAW5B,GAAG,CACZyB,EAAO,KACPJ,IAAqBO,GAAaH,EAAO,WAC3C,CAAC,EACD,eAAc,6BAA6BG,MAE1CL,EACCA,EAAWG,EAAMF,CAAU,EACzB,OAAOE,GAAS,SAClB7B,EAAA,cAACmC,EAAA,CAAgB,KAAMN,EAAM,WAAYF,EAAY,EAErDE,CAEJ,CAEJ,CAAC,CACH,CAEJ,EAEAT,EAAkB,YAAc,oBAEhC,SAASe,EAAgB,CACvB,KAAAN,EACA,WAAAF,CACF,EAGG,CACD,GAAM,CAAE,OAAAS,EAAQ,MAAAC,EAAO,MAAAC,CAAM,EAAIxB,GAAee,EAAMF,CAAU,EAEhE,OACE3B,EAAA,cAAAA,EAAA,cACGoC,EACDpC,EAAA,cAAC,SAAGqC,CAAM,EACTC,CACH,CAEJ,CAEAH,EAAgB,YAAc,kBDwD9B,SAASI,GACPlB,EACAmB,EACA,CACA,GAAM,CACJ,GAAAC,EACA,UAAAC,EACA,iBAAAC,EAAmB,GACnB,iBAAAC,EAAmB,GACnB,aAAAC,EAAe,GACf,aAAAC,EACA,MAAAxB,EACA,mBAAAyB,EACA,aAAAC,EACA,WAAAtB,EACA,KAAAuB,EAAOjD,EAAA,cAACS,GAAA,CAAgB,QAAQ,QAAQ,EACxC,aAAAyC,EAAgBrB,GAAmBA,EACnC,UAAAsB,EACA,WAAAC,EACA,WAAAC,EACA,WAAAC,GACA,cAAAC,GACA,iBAAAC,GAAmB,mBACnB,YAAAC,GAAc,SACd,SAAAC,GACA,UAAAC,GACA,QAAAC,GACA,UAAAC,GAAY,OACZ,cAAA1C,GAAgB,IAChB,UAAA2C,EAAY,GACZ,UAAAC,EAAY,GACZ,UAAAC,GAAY,GACZ,OAAAC,GAAS,iBACX,EAAI5C,EAIEO,EAASV,EAAsBC,EAAa,EAE5C,CAACQ,EAAYuC,EAAa,EAAIhE,GAAS2C,CAAY,EAEnDsB,EAAyBlE,EAC5BmE,GAAkB,CACjBF,GAAcE,CAAK,EAEnBrB,GAAA,MAAAA,EAAqBqB,EACvB,EACA,CAACrB,CAAkB,CACrB,EAGMsB,GAA0BpE,EAC7BqE,GAAqE,CACpE,IAAMF,EAAQE,EAAM,OAAO,MAC3BH,EAAuBC,CAAK,CAC9B,EACA,CAACD,CAAsB,CACzB,EAEMI,GAAeC,EAAcV,EAAWxC,CAAK,EAC/CA,EAAM,OACJ,CAACmD,EAAiBC,IAAqB,CAAC,GAAGD,EAAK,GAAGC,EAAM,OAAO,EAChE,CAAC,CACH,EACApD,EAEEqD,GAAqBH,EAAcV,EAAWxC,CAAK,EACrDA,EAAM,MAAOoD,GAAqBA,EAAM,QAAQ,SAAW,CAAC,EAC5DpD,EAAM,SAAW,EAEf,CACJ,iBAAAsD,GACA,cAAAC,GACA,aAAApD,EACA,aAAAqD,GACA,qBAAAC,GACA,iBAAAvD,EACA,OAAAwD,EACA,WAAAC,CACF,EAAI7E,EAAY,CACd,MAAOmE,GACP,aAAAzB,EACA,WAAAnB,EACA,aAAAuB,EACA,mBAAoB,CAAC,CAAE,KAAAgC,EAAM,WAAAvD,CAAW,IAAM,CACxCuD,IAAS,oBACXf,EAAuBxC,CAAU,CAErC,EACA,cAAe,CAAC,CAAE,KAAAuD,EAAM,aAAApC,CAAa,IAAM,CACzC,OAAQoC,QACD9E,EAAY,iBAAiB,uBAC7BA,EAAY,iBAAiB,UAC5B0C,GACFE,EAAaF,CAAY,EAEvBH,GACFwB,EAAuB,EAAE,EAEtBvB,GACHqC,EAAW,EAEb,cAEA,MAEN,CACF,CAAC,EAMGE,EAAAN,GAAc,EAHhB,mBAAmBO,GACnB,GAAIC,EA5PR,EA8PMF,EADCG,EAAAC,EACDJ,EADC,CAFH,kBACA,OAGIK,GAAgBZ,GAAiB,EACjCa,EAAcV,GAAqB,EACnCW,EAAYZ,GAAa,EAC3BvD,EAAoB,EAExB,OACEvB,EAAA,cAAC,OACC,eAAciE,GACd,UAAW9D,EAAGyB,EAAO,aAAcc,CAAS,EAC5C,IAAKF,GAELxC,EAAA,cAACW,EAAA,CACC,UAAWqD,GACX,OAAQgB,EACR,YAAanB,KAAc,OAC3B,mBAAoB,GAIpB,UAAW,GACX,GAAI6B,EAAU,IAEd1F,EAAA,cAACW,EAAQ,QAAR,KACCX,EAAA,cAAC,MAAAiC,EAAAC,EAAA,GAAQsD,IAAR,CAAuB,UAAW5D,EAAO,WACxC5B,EAAA,cAACO,GAAA0B,EAAAC,EAAA,CACC,UAAWN,EAAO,YACd0D,GAFL,CAGC,QAAS,IAAM,CACRN,GACHC,EAAW,CAEf,EACA,GAAIxC,EACJ,UAAWU,EACX,WAAYC,EACZ,WAAYC,EACZ,WAAYC,GACZ,IAAKjD,EAAUiF,EAAW,IAAK5B,EAAQ,EACvC,OAAO,wBACP,YAAaD,GACb,SAAWa,GAAU,CACnBgB,EAAW,SAAShB,CAAK,EACzBD,GAAwBC,CAAK,CAC/B,GACF,EACAtE,EAAA,cAACM,GAAA2B,EAAAC,EAAA,GACKuD,GADL,CAEC,IAAKpF,EAAUoF,EAAY,IAAK9B,EAAS,EACzC,aAAYhC,EAAa,QAAU,YACnC,UAAWC,EAAO,aAClB,QAAQ,cACR,KAAMD,EAAa3B,EAAA,cAACQ,GAAA,CAAU,QAAQ,QAAQ,EAAKyC,EACnD,QAAS,IAAM,CACTtB,EACFwC,EAAuB,EAAE,EAEzBc,EAAW,CAEf,EACA,WAAY7B,EACZ,KAAK,SACP,CACF,CACF,EAEC9B,EAAM,OAAS,GAAKK,EAAW,OAAS,GAAK4B,GAC5CvD,EAAA,cAACW,EAAQ,QAARsB,EAAAC,EAAA,GACKwD,GADL,CAEC,IAAKrF,EAAUqF,EAAU,IAAK9B,EAAO,EACrC,UAAWhC,EAAO,QAClB,OAAO,8BAENmC,GACC,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC4B,EAAG7D,IACpB9B,EAAA,cAAC,OAAI,IAAK8B,EAAO,UAAW3B,EAAGyB,EAAO,KAAMA,EAAO,QAAQ,GACzD5B,EAAA,cAAC4F,GAAA,IAAqB,CACxB,CACD,EAEF,CAAC7B,GAAaY,IACb3E,EAAA,cAAC,OAAI,UAAW4B,EAAO,MACrB5B,EAAA,cAACY,GAAA,CAAW,UAAWgB,EAAO,gBAC3B4B,EACH,CACF,EAGD,CAACO,GACAS,EAAcV,EAAWxC,CAAK,GAC9BA,EAAM,IAAI,CAACoD,EAAkB5C,IAAkB,CAC7C,GAAI4C,EAAM,QAAQ,OAAS,EACzB,OAEF,IAAMmB,GACJ7F,EAAA,cAAC,OAAI,IAAK8B,GACR9B,EAAA,cAACa,GAAA,CACC,IAAKiB,EACL,eAAa,6BACb,aAAa,OACb,UAAWF,EAAO,YAEjB8C,EAAM,UACT,EACA1E,EAAA,cAACoB,EAAA,CACC,MAAOsD,EAAM,QACb,iBAAkBlD,EAClB,aAAcC,EACd,WAAYC,EACZ,WAAYC,EACZ,kBAAmBJ,EACrB,CACF,EAEF,OAAAA,GAAqBmD,EAAM,QAAQ,OAC5BmB,EACT,CAAC,EAEF,CAAC9B,GACA,CAACS,EAAcV,EAAWxC,CAAK,GAC/BA,EAAM,OAAS,GACbtB,EAAA,cAACoB,EAAA,CACC,MAAOE,EACP,kBAAmBC,EACnB,iBAAkBC,EAClB,aAAcC,EACd,WAAYC,EACZ,WAAYC,EACd,CAEN,EAIA3B,EAAA,cAAC,MAAAiC,EAAAC,EAAA,GAAQwD,GAAR,CAAmB,UAAWvF,EAAGyB,EAAO,MAAM,GAAG,CAEtD,CACF,CAEJ,CAEA,IAAMgE,GAAuB,IAEzB5F,EAAA,cAACU,EAAS,UAAT,CAAmB,UAAW,IAC7BV,EAAA,cAACU,EAAS,SAAT,CAAkB,cAAe,EAAG,CACvC,EAKJ,SAAS8D,EACPV,EACAxC,EACuC,CACvC,OAAOwC,CACT,CAOO,IAAMgC,GAAe9F,EAAM,WAAWuC,EAAa","sourcesContent":["import React, { useCallback, useState } from 'react';\nimport { cx } from 'emotion';\nimport { useCombobox } from 'downshift';\n\nimport {\n mergeRefs,\n type CommonProps,\n type ExpandProps,\n} from '@contentful/f36-core';\nimport { IconButton } from '@contentful/f36-button';\nimport { TextInput, type TextInputProps } from '@contentful/f36-forms';\nimport { CloseIcon, ChevronDownIcon } from '@contentful/f36-icons';\nimport { Skeleton } from '@contentful/f36-skeleton';\nimport { Popover } from '@contentful/f36-popover';\nimport { Subheading, SectionHeading } from '@contentful/f36-typography';\n\nimport { AutocompleteItems } from './AutocompleteItems';\nimport { getAutocompleteStyles } from './Autocomplete.styles';\n\nexport interface GenericGroupType<ItemType> {\n groupTitle: string;\n options: ItemType[];\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport interface AutocompleteProps<ItemType>\n extends CommonProps,\n Pick<\n TextInputProps,\n | 'isDisabled'\n | 'isInvalid'\n | 'isReadOnly'\n | 'isRequired'\n | 'id'\n | 'defaultValue'\n > {\n /**\n * It’s an array of data to be used as \"options\" by the autocomplete component.\n * This can either be a plain list of items or a list of groups of items.\n */\n items: ItemType[] | GenericGroupType<ItemType>[];\n\n /**\n * Set a custom icon for the text input\n */\n icon?: React.ReactElement;\n\n /**\n * Tells if the item is a object with groups\n */\n isGrouped?: boolean;\n\n /**\n * Function called whenever the input value changes\n */\n onInputValueChange?: (value: string) => void;\n /**\n * This is the function that will be called when the user selects one of the \"options\" in the list.\n * The component will pass the selected \"item\" as an argument to the function..\n */\n onSelectItem: (item: ItemType) => void;\n\n /**\n * Applying the selectedItem property turns autocomplete into a controlled component.\n * Can be used to display e.g. previously selected element. If it is an object the itemToString function will apply to it.\n */\n selectedItem?: ItemType;\n\n /**\n * This is the function that will be called for each \"item\" passed in the `items` prop.\n * It receives the \"item\" and \"inputValue\" as arguments and returns a ReactNode.\n * The inputValue is passed in case you want to highlight the match on the render.\n */\n renderItem?: (item: ItemType, inputValue: string) => React.ReactNode;\n /**\n * When using objects as `items`, we recommend passing a function that tells Downshift how to extract a string\n * from those objetcs to be used as inputValue\n */\n itemToString?: (item: ItemType) => string;\n /**\n * If this is set to `true` the text input will be cleared after an item is selected\n * @default false\n */\n clearAfterSelect?: boolean;\n /**\n * If this is set to `false` the dropdown menu will stay open after selecting an item\n * @default true\n */\n closeAfterSelect?: boolean;\n /**\n * This is the value will be passed to the `placeholder` prop of the input.\n * @default \"Search\"\n */\n placeholder?: string;\n /**\n * Defines if the list should be shown even if empty, when input is focused\n * @default false\n */\n showEmptyList?: boolean;\n /**\n * A message that will be shown when it is not possible to find any option that matches the input value\n * @default \"No matches\"\n */\n noMatchesMessage?: string;\n /**\n * Use this prop to get a ref to the input element of the component\n */\n inputRef?: React.Ref<HTMLInputElement>;\n /**\n * Use this prop to get a ref to the toggle button of the component\n */\n toggleRef?: React.Ref<HTMLButtonElement>;\n /**\n * Use this prop to get a ref to the list of items of the component\n */\n listRef?: React.Ref<HTMLUListElement>;\n /**\n * It sets the width of the list\n * @default \"auto\"\n */\n listWidth?: 'auto' | 'full';\n /**\n * It sets the max-height, in pixels, of the list\n * The default value is the height of 5 single line items\n * @default 180\n */\n listMaxHeight?: number;\n /**\n * Sets the list to show its loading state\n * @default false\n */\n isLoading?: boolean;\n /**\n * Boolean to control whether or not to render the suggestions box in a React Portal.\n * Rendering content inside a Portal allows the suggestions box to escape the bounds\n * of its parent while still being positioned correctly.\n * Defaults to `false`\n */\n usePortal?: boolean;\n}\n\nfunction _Autocomplete<ItemType>(\n props: AutocompleteProps<ItemType>,\n ref: React.Ref<HTMLDivElement>,\n) {\n const {\n id,\n className,\n clearAfterSelect = false,\n closeAfterSelect = true,\n defaultValue = '',\n selectedItem,\n items,\n onInputValueChange,\n onSelectItem,\n renderItem,\n icon = <ChevronDownIcon variant=\"muted\" />,\n itemToString = (item: ItemType) => item as unknown as string,\n isInvalid,\n isDisabled,\n isRequired,\n isReadOnly,\n showEmptyList,\n noMatchesMessage = 'No matches found',\n placeholder = 'Search',\n inputRef,\n toggleRef,\n listRef,\n listWidth = 'auto',\n listMaxHeight = 180,\n isGrouped = false,\n isLoading = false,\n usePortal = false,\n testId = 'cf-autocomplete',\n } = props;\n\n type GroupType = GenericGroupType<ItemType>;\n\n const styles = getAutocompleteStyles(listMaxHeight);\n\n const [inputValue, setInputValue] = useState(defaultValue);\n\n const handleInputValueChange = useCallback(\n (value: string) => {\n setInputValue(value);\n\n onInputValueChange?.(value);\n },\n [onInputValueChange],\n );\n\n // Handle manually to avoid a jumping cursor, see https://github.com/downshift-js/downshift/issues/1108#issuecomment-842407759\n const handleNativeChangeEvent = useCallback(\n (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {\n const value = event.target.value;\n handleInputValueChange(value);\n },\n [handleInputValueChange],\n );\n\n const flattenItems = isUsingGroups(isGrouped, items)\n ? items.reduce(\n (acc: ItemType[], group: GroupType) => [...acc, ...group.options],\n [],\n )\n : items;\n\n const isShowingNoMatches = isUsingGroups(isGrouped, items)\n ? items.every((group: GroupType) => group.options.length === 0)\n : items.length === 0;\n\n const {\n getComboboxProps,\n getInputProps,\n getItemProps,\n getMenuProps,\n getToggleButtonProps,\n highlightedIndex,\n isOpen,\n toggleMenu,\n } = useCombobox({\n items: flattenItems,\n selectedItem,\n inputValue,\n itemToString,\n onInputValueChange: ({ type, inputValue }) => {\n if (type !== '__input_change__') {\n handleInputValueChange(inputValue);\n }\n },\n onStateChange: ({ type, selectedItem }) => {\n switch (type) {\n case useCombobox.stateChangeTypes.InputKeyDownEnter:\n case useCombobox.stateChangeTypes.ItemClick:\n if (selectedItem) {\n onSelectItem(selectedItem);\n }\n if (clearAfterSelect) {\n handleInputValueChange('');\n }\n if (!closeAfterSelect) {\n toggleMenu();\n }\n break;\n default:\n break;\n }\n },\n });\n\n const {\n 'aria-labelledby': _labelledby,\n id: _inputId,\n ...inputProps\n } = getInputProps();\n const comboboxProps = getComboboxProps();\n const toggleProps = getToggleButtonProps();\n const menuProps = getMenuProps();\n let elementStartIndex = 0;\n\n return (\n <div\n data-test-id={testId}\n className={cx(styles.autocomplete, className)}\n ref={ref}\n >\n <Popover\n usePortal={usePortal}\n isOpen={isOpen}\n isFullWidth={listWidth === 'full'}\n renderOnlyWhenOpen={false}\n // This is necessary, otherwise the focus will change from the input to the Popover\n // and the user won't be able to type in the input\n // eslint-disable-next-line jsx-a11y/no-autofocus\n autoFocus={false}\n id={menuProps.id}\n >\n <Popover.Trigger>\n <div {...comboboxProps} className={styles.combobox}>\n <TextInput\n className={styles.inputField}\n {...inputProps}\n onFocus={() => {\n if (!isOpen) {\n toggleMenu();\n }\n }}\n id={id}\n isInvalid={isInvalid}\n isDisabled={isDisabled}\n isRequired={isRequired}\n isReadOnly={isReadOnly}\n ref={mergeRefs(inputProps.ref, inputRef)}\n testId=\"cf-autocomplete-input\"\n placeholder={placeholder}\n onChange={(event) => {\n inputProps.onChange(event);\n handleNativeChangeEvent(event);\n }}\n />\n <IconButton\n {...toggleProps}\n ref={mergeRefs(toggleProps.ref, toggleRef)}\n aria-label={inputValue ? 'Clear' : 'Show list'}\n className={styles.toggleButton}\n variant=\"transparent\"\n icon={inputValue ? <CloseIcon variant=\"muted\" /> : icon}\n onClick={() => {\n if (inputValue) {\n handleInputValueChange('');\n } else {\n toggleMenu();\n }\n }}\n isDisabled={isDisabled}\n size=\"small\"\n />\n </div>\n </Popover.Trigger>\n\n {items.length > 0 || inputValue.length > 0 || showEmptyList ? (\n <Popover.Content\n {...menuProps}\n ref={mergeRefs(menuProps.ref, listRef)}\n className={styles.content}\n testId=\"cf-autocomplete-container\"\n >\n {isLoading &&\n [...Array(3)].map((_, index) => (\n <div key={index} className={cx(styles.item, styles.disabled)}>\n <ListItemLoadingState />\n </div>\n ))}\n\n {!isLoading && isShowingNoMatches && (\n <div className={styles.item}>\n <Subheading className={styles.noMatchesTitle}>\n {noMatchesMessage}\n </Subheading>\n </div>\n )}\n\n {!isLoading &&\n isUsingGroups(isGrouped, items) &&\n items.map((group: GroupType, index: number) => {\n if (group.options.length < 1) {\n return;\n }\n const render = (\n <div key={index}>\n <SectionHeading\n key={index}\n data-test-id=\"cf-autocomplete-grouptitle\"\n marginBottom=\"none\"\n className={styles.groupTitle}\n >\n {group.groupTitle}\n </SectionHeading>\n <AutocompleteItems<ItemType>\n items={group.options}\n highlightedIndex={highlightedIndex}\n getItemProps={getItemProps}\n renderItem={renderItem}\n inputValue={inputValue}\n elementStartIndex={elementStartIndex}\n />\n </div>\n );\n elementStartIndex += group.options.length;\n return render;\n })}\n\n {!isLoading &&\n !isUsingGroups(isGrouped, items) &&\n items.length > 0 && (\n <AutocompleteItems<ItemType>\n items={items}\n elementStartIndex={elementStartIndex}\n highlightedIndex={highlightedIndex}\n getItemProps={getItemProps}\n renderItem={renderItem}\n inputValue={inputValue}\n />\n )}\n </Popover.Content>\n ) : (\n // We need to render an empty hidden div, so we can pass the menuProps or downshift will show a warning about it\n // https://github.com/downshift-js/downshift/issues/1167#issuecomment-1088022842\n <div {...menuProps} className={cx(styles.hidden)} />\n )}\n </Popover>\n </div>\n );\n}\n\nconst ListItemLoadingState = () => {\n return (\n <Skeleton.Container svgHeight={16}>\n <Skeleton.BodyText numberOfLines={1} />\n </Skeleton.Container>\n );\n};\n\n// This is required to infer correct typings when differentiating groups and items\nfunction isUsingGroups<ItemType>(\n isGrouped: boolean,\n items: ItemType[] | GenericGroupType<ItemType>[],\n): items is GenericGroupType<ItemType>[] {\n return isGrouped;\n}\n\n/**\n * The Autocomplete is a component that will show a `TextInput` where a user can type any word which will be used\n * to filter a list of items. That list of filtered items will be shown to the user as possible options for the input.\n * Once one of the options is selected, that option becomes the value of the `TextInput`.\n */\nexport const Autocomplete = React.forwardRef(_Autocomplete) as <T>(\n props: ExpandProps<AutocompleteProps<T>> & {\n ref?: React.Ref<HTMLDivElement>;\n },\n) => ReturnType<typeof _Autocomplete>;\n","import React, { HTMLAttributes } from 'react';\nimport { cx } from 'emotion';\nimport { getStringMatch } from '@contentful/f36-utils';\nimport type { UseComboboxGetItemPropsOptions } from 'downshift';\nimport { Text } from '@contentful/f36-typography';\n\nimport { getAutocompleteStyles } from './Autocomplete.styles';\n\ninterface AutocompleteItemsProps<ItemType> {\n items: ItemType[];\n elementStartIndex: number;\n highlightedIndex: number;\n getItemProps: (\n options: UseComboboxGetItemPropsOptions<ItemType>,\n ) => HTMLAttributes<HTMLLIElement>;\n renderItem: (item: ItemType, inputValue: string) => React.ReactNode;\n inputValue: string;\n listMaxHeight?: number;\n}\n\nexport const AutocompleteItems = <ItemType,>(\n props: AutocompleteItemsProps<ItemType>,\n) => {\n const {\n items,\n elementStartIndex,\n highlightedIndex,\n getItemProps,\n renderItem,\n inputValue,\n listMaxHeight = 180,\n } = props;\n\n const styles = getAutocompleteStyles(listMaxHeight);\n\n return (\n <ul className={styles.list} data-test-id=\"cf-autocomplete-list\">\n {items.map((item: ItemType, index: number) => {\n const itemIndex = elementStartIndex + index;\n const itemProps = getItemProps({ item, index: itemIndex });\n return (\n <Text\n {...itemProps}\n as=\"li\"\n key={itemIndex}\n className={cx([\n styles.item,\n highlightedIndex === itemIndex && styles.highlighted,\n ])}\n data-test-id={`cf-autocomplete-list-item-${itemIndex}`}\n >\n {renderItem ? (\n renderItem(item, inputValue)\n ) : typeof item === 'string' ? (\n <HighlightedItem item={item} inputValue={inputValue} />\n ) : (\n item\n )}\n </Text>\n );\n })}\n </ul>\n );\n};\n\nAutocompleteItems.displayName = 'AutocompleteItems';\n\nfunction HighlightedItem({\n item,\n inputValue,\n}: {\n item: string;\n inputValue: string;\n}) {\n const { before, match, after } = getStringMatch(item, inputValue);\n\n return (\n <>\n {before}\n <b>{match}</b>\n {after}\n </>\n );\n}\n\nHighlightedItem.displayName = 'HighlightedItem';\n","import { css } from 'emotion';\nimport tokens from '@contentful/f36-tokens';\n\nexport const getAutocompleteStyles = (listMaxHeight: number) => ({\n autocomplete: css({\n position: 'relative',\n width: '100%',\n }),\n combobox: css({\n position: 'relative',\n }),\n inputField: css({\n paddingRight: tokens.spacingXl,\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }),\n toggleButton: css({\n position: 'absolute',\n top: '1px',\n right: '1px',\n zIndex: 1,\n padding: tokens.spacing2Xs,\n height: '38px',\n }),\n content: css({\n overflow: 'auto',\n maxHeight: `${listMaxHeight}px`,\n }),\n list: css({\n listStyle: 'none',\n padding: `${tokens.spacingXs} 0`,\n margin: 0,\n }),\n groupTitle: css({\n padding: `${tokens.spacingXs} ${tokens.spacingM}`,\n lineHeight: tokens.lineHeightM,\n }),\n noMatchesTitle: css({\n color: tokens.gray500,\n margin: `${tokens.spacingM} 0 ${tokens.spacingM} 0`,\n }),\n item: css({\n display: 'block',\n padding: `${tokens.spacingXs} ${tokens.spacingM}`,\n wordBreak: 'break-word',\n whiteSpace: 'break-spaces',\n cursor: 'pointer',\n hyphens: 'auto',\n\n '&:focus, &:hover': {\n backgroundColor: tokens.gray100,\n },\n '&:active': {\n backgroundColor: tokens.gray200,\n },\n }),\n disabled: css({\n opacity: 0.5,\n pointerEvents: 'none',\n }),\n highlighted: css({\n backgroundColor: tokens.gray100,\n }),\n hidden: css({\n display: 'none',\n }),\n});\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/Autocomplete.tsx","../../src/AutocompleteItems.tsx","../../src/Autocomplete.styles.ts"],"names":["React","useCallback","useState","cx","useCombobox","mergeRefs","IconButton","TextInput","XIcon","CaretDownIcon","Skeleton","Popover","Subheading","SectionHeading","getStringMatch","Text","css","tokens","getMenuItemStyles","getAutocompleteStyles","listMaxHeight","isActive","isDisabled","AutocompleteItems","props","items","elementStartIndex","highlightedIndex","getItemProps","renderItem","inputValue","styles","item","index","itemIndex","itemProps","__spreadProps","__spreadValues","HighlightedItem","before","match","after","_Autocomplete","ref","isOpenProp","onClose","onOpen","id","className","textOnAfterSelect","closeAfterSelect","defaultValue","selectedItem","inputValueProp","onInputValueChange","onSelectItem","onFocus","onBlur","icon","itemToString","isInvalid","isRequired","isReadOnly","showEmptyList","noMatchesMessage","placeholder","inputRef","toggleRef","listRef","listWidth","isGrouped","isLoading","usePortal","testId","popoverTestId","showClearButtonProp","aria","_inputValue","setInputValue","handleInputValueChange","value","handleNativeChangeEvent","event","flattenItems","isUsingGroups","acc","group","isShowingNoMatches","getComboboxProps","getInputProps","getMenuProps","getToggleButtonProps","isOpen","openMenu","toggleMenu","state","type","changes","_a","_labelledby","_inputId","inputProps","__objRest","comboboxProps","toggleProps","menuProps","showClearButton","_","ListItemLoadingState","render","Autocomplete"],"mappings":"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","sourcesContent":["import React, { useCallback, useState } from 'react';\nimport { cx } from 'emotion';\nimport { useCombobox } from 'downshift';\n\nimport {\n mergeRefs,\n type CommonProps,\n type ExpandProps,\n} from '@contentful/f36-core';\nimport { IconButton } from '@contentful/f36-button';\nimport { TextInput, type TextInputProps } from '@contentful/f36-forms';\nimport { XIcon, CaretDownIcon } from '@contentful/f36-icons';\nimport { Skeleton } from '@contentful/f36-skeleton';\nimport { Popover } from '@contentful/f36-popover';\nimport { Subheading, SectionHeading } from '@contentful/f36-typography';\n\nimport { AutocompleteItems } from './AutocompleteItems';\nimport { getAutocompleteStyles } from './Autocomplete.styles';\nimport tokens from '@contentful/f36-tokens';\n\nexport interface GenericGroupType<ItemType> {\n groupTitle: string;\n options: ItemType[];\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport interface AutocompleteProps<ItemType>\n extends CommonProps,\n Pick<\n TextInputProps,\n | 'isDisabled'\n | 'isInvalid'\n | 'isReadOnly'\n | 'isRequired'\n | 'id'\n | 'defaultValue'\n > {\n /**\n * It’s an array of data to be used as \"options\" by the autocomplete component.\n * This can either be a plain list of items or a list of groups of items.\n */\n items: ItemType[] | GenericGroupType<ItemType>[];\n\n /**\n * Boolean to control whether the Autocomplete menu is open\n */\n isOpen?: boolean;\n\n /**\n * Callback fired when the Autocomplete menu opens\n */\n onOpen?: () => void;\n\n /**\n * Callback fired when the Autocomplete menu closes\n */\n onClose?: () => void;\n\n /**\n * Set a custom icon for the text input\n */\n icon?: React.ReactElement;\n\n /**\n * Tells if the item is a object with groups\n */\n isGrouped?: boolean;\n\n /**\n * Set the value of the text input\n */\n inputValue?: string;\n /**\n * Function called whenever the input value changes\n */\n onInputValueChange?: (value: string) => void;\n /**\n * This is the function that will be called when the user selects one of the \"options\" in the list.\n * The component will pass the selected \"item\" as an argument to the function..\n */\n onSelectItem: (item: ItemType) => void;\n\n /**\n * Applying the selectedItem property turns autocomplete into a controlled component.\n * Can be used to display e.g. previously selected element. If it is an object the itemToString function will apply to it.\n */\n selectedItem?: ItemType;\n\n /**\n * This is the function that will be called for each \"item\" passed in the `items` prop.\n * It receives the \"item\" and \"inputValue\" as arguments and returns a ReactNode.\n * The inputValue is passed in case you want to highlight the match on the render.\n */\n renderItem?: (item: ItemType, inputValue: string) => React.ReactNode;\n /**\n * When using objects as `items`, we recommend passing a function that tells Downshift how to extract a string\n * from those objetcs to be used as inputValue\n */\n itemToString?: (item: ItemType) => string;\n /**\n * Text input behaviour after an item is selected\n * @default \"replace\"\n */\n textOnAfterSelect?: 'clear' | 'preserve' | 'replace';\n /**\n * If this is set to `false` the dropdown menu will stay open after selecting an item\n * @default true\n */\n closeAfterSelect?: boolean;\n /**\n * This is the value will be passed to the `placeholder` prop of the input.\n * @default \"Search\"\n */\n placeholder?: string;\n /**\n * Defines if the list should be shown even if empty, when input is focused\n * @default false\n */\n showEmptyList?: boolean;\n /**\n * A message that will be shown when it is not possible to find any option that matches the input value\n * @default \"No matches\"\n */\n noMatchesMessage?: string;\n /**\n * Use this prop to get a ref to the input element of the component\n */\n inputRef?: React.Ref<HTMLInputElement>;\n /**\n * Use this prop to get a ref to the toggle button of the component\n */\n toggleRef?: React.Ref<HTMLButtonElement>;\n /**\n * Use this prop to get a ref to the list of items of the component\n */\n listRef?: React.Ref<HTMLUListElement>;\n /**\n * It sets the width of the list\n * @default \"auto\"\n */\n listWidth?: 'auto' | 'full';\n /**\n * It sets the max-height, in pixels, of the list\n * The default value is the height of 5 single line items\n * @default 180\n */\n listMaxHeight?: number;\n /**\n * Sets the list to show its loading state\n * @default false\n */\n isLoading?: boolean;\n /**\n * Boolean to control whether or not to render the suggestions box in a React Portal.\n * Rendering content inside a Portal allows the suggestions box to escape the bounds\n * of its parent while still being positioned correctly.\n * Defaults to `false`\n */\n usePortal?: boolean;\n\n /**\n * A [data-test-id] attribute for the suggestions box used for testing purposes\n */\n popoverTestId?: string;\n\n /**\n * Function called when the input is focused\n *\n * @param event\n */\n onFocus?: (event: React.FocusEvent<HTMLInputElement>) => void;\n /**\n * Function called when the input is blurred\n * @param event\n */\n onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void;\n /**\n * Manually control when the button to clear the input value is shown\n */\n showClearButton?: boolean;\n /**\n * Additional aria attributes\n */\n aria?: {\n showListIconLabel?: string;\n clearSelectionIconLabel?: string;\n };\n}\n\nfunction _Autocomplete<ItemType>(\n props: AutocompleteProps<ItemType>,\n ref: React.Ref<HTMLDivElement>,\n) {\n const {\n isOpen: isOpenProp,\n onClose,\n onOpen,\n id,\n className,\n textOnAfterSelect = 'replace',\n closeAfterSelect = true,\n defaultValue = '',\n selectedItem,\n items,\n inputValue: inputValueProp,\n onInputValueChange,\n onSelectItem,\n onFocus,\n onBlur,\n renderItem,\n icon = <CaretDownIcon color={tokens.gray600} />,\n itemToString = (item: ItemType) => item as unknown as string,\n isInvalid,\n isDisabled,\n isRequired,\n isReadOnly,\n showEmptyList,\n noMatchesMessage = 'No matches found',\n placeholder = 'Search',\n inputRef,\n toggleRef,\n listRef,\n listWidth = 'auto',\n listMaxHeight = 180,\n isGrouped = false,\n isLoading = false,\n usePortal = false,\n testId = 'cf-autocomplete',\n popoverTestId = 'cf-autocomplete-container',\n showClearButton: showClearButtonProp,\n aria = {\n clearSelectionIconLabel: 'Clear',\n showListIconLabel: 'Show list',\n },\n } = props;\n\n type GroupType = GenericGroupType<ItemType>;\n\n const styles = getAutocompleteStyles(listMaxHeight);\n\n const [_inputValue, setInputValue] = useState(defaultValue);\n const inputValue =\n typeof inputValueProp === 'undefined' ? _inputValue : inputValueProp;\n\n const handleInputValueChange = useCallback(\n (value: string) => {\n setInputValue(value);\n\n onInputValueChange?.(value);\n },\n [onInputValueChange],\n );\n\n // Handle manually to avoid a jumping cursor, see https://github.com/downshift-js/downshift/issues/1108#issuecomment-842407759\n const handleNativeChangeEvent = useCallback(\n (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {\n const value = event.target.value;\n handleInputValueChange(value);\n },\n [handleInputValueChange],\n );\n\n const flattenItems = isUsingGroups(isGrouped, items)\n ? items.reduce(\n (acc: ItemType[], group: GroupType) => [...acc, ...group.options],\n [],\n )\n : items;\n\n const isShowingNoMatches = isUsingGroups(isGrouped, items)\n ? items.every((group: GroupType) => group.options.length === 0)\n : items.length === 0;\n\n const {\n getComboboxProps,\n getInputProps,\n getItemProps,\n getMenuProps,\n getToggleButtonProps,\n highlightedIndex,\n isOpen,\n openMenu,\n toggleMenu,\n } = useCombobox({\n isOpen: isOpenProp,\n onIsOpenChange: ({ isOpen }) => {\n if (isOpen) {\n onOpen?.();\n } else {\n onClose?.();\n }\n },\n stateReducer: (state, { type, changes }) => {\n switch (type) {\n case useCombobox.stateChangeTypes.InputBlur: {\n // don't change input value on blur\n return { ...changes, inputValue: state.inputValue };\n }\n\n // item is selected by click or keydown\n case useCombobox.stateChangeTypes.InputKeyDownEnter:\n case useCombobox.stateChangeTypes.ItemClick: {\n // prevent the menu from being closed when the user selects an item with a keyboard or mouse\n if (!closeAfterSelect) {\n return {\n ...changes,\n isOpen: state.isOpen,\n };\n }\n\n return changes;\n }\n default:\n return changes;\n }\n },\n items: flattenItems,\n selectedItem,\n inputValue,\n itemToString,\n onInputValueChange: ({ type, inputValue }) => {\n switch (type) {\n // value is set directly from the TextInput onChange handler\n case useCombobox.stateChangeTypes.InputChange: {\n return;\n }\n\n // item is selected by click or keydown\n case useCombobox.stateChangeTypes.ItemClick:\n case useCombobox.stateChangeTypes.InputKeyDownEnter: {\n // clear the TextInput value\n if (textOnAfterSelect === 'clear') {\n handleInputValueChange('');\n return;\n }\n\n // keep the current TextInput value\n if (textOnAfterSelect === 'preserve') {\n return;\n }\n }\n }\n\n handleInputValueChange(inputValue);\n },\n onStateChange: ({ type, selectedItem }) => {\n switch (type) {\n case useCombobox.stateChangeTypes.InputKeyDownEnter:\n case useCombobox.stateChangeTypes.ItemClick:\n if (selectedItem) {\n onSelectItem(selectedItem);\n }\n break;\n default:\n break;\n }\n },\n });\n\n const {\n 'aria-labelledby': _labelledby,\n id: _inputId,\n ...inputProps\n } = getInputProps();\n const comboboxProps = getComboboxProps();\n const toggleProps = getToggleButtonProps();\n const menuProps = getMenuProps();\n let elementStartIndex = 0;\n\n const showClearButton = showClearButtonProp ?? inputValue;\n\n return (\n <div\n data-test-id={testId}\n className={cx(styles.autocomplete, className)}\n ref={ref}\n >\n <Popover\n usePortal={usePortal}\n isOpen={isOpen}\n isFullWidth={listWidth === 'full'}\n renderOnlyWhenOpen={false}\n // This is necessary, otherwise the focus will change from the input to the Popover\n // and the user won't be able to type in the input\n // eslint-disable-next-line jsx-a11y/no-autofocus\n autoFocus={false}\n id={menuProps.id}\n >\n <Popover.Trigger>\n <div {...comboboxProps} className={styles.combobox}>\n <TextInput\n className={styles.inputField}\n {...inputProps}\n onFocus={(e) => {\n onFocus?.(e as React.FocusEvent<HTMLInputElement>);\n openMenu();\n }}\n onBlur={(e) => {\n onBlur?.(e as React.FocusEvent<HTMLInputElement>);\n inputProps.onBlur(e);\n }}\n id={id}\n isInvalid={isInvalid}\n isDisabled={isDisabled}\n isRequired={isRequired}\n isReadOnly={isReadOnly}\n ref={mergeRefs(inputProps.ref, inputRef)}\n testId=\"cf-autocomplete-input\"\n placeholder={placeholder}\n onChange={(event) => {\n inputProps.onChange(event);\n handleNativeChangeEvent(event);\n }}\n />\n <IconButton\n {...toggleProps}\n ref={mergeRefs(toggleProps.ref, toggleRef)}\n aria-label={\n showClearButton\n ? aria.clearSelectionIconLabel\n : aria.showListIconLabel\n }\n className={styles.toggleButton}\n variant=\"transparent\"\n icon={showClearButton ? <XIcon color={tokens.gray600} /> : icon}\n onClick={() => {\n if (showClearButton) {\n handleInputValueChange('');\n } else {\n toggleMenu();\n }\n }}\n isDisabled={isDisabled}\n size=\"small\"\n />\n </div>\n </Popover.Trigger>\n\n {items.length > 0 || inputValue.length > 0 || showEmptyList ? (\n <Popover.Content\n {...menuProps}\n ref={mergeRefs(menuProps.ref, listRef)}\n className={styles.content}\n testId={popoverTestId}\n >\n {isLoading &&\n [...Array(3)].map((_, index) => (\n <div\n key={index}\n className={cx(styles.item({ isDisabled: true }))}\n >\n <ListItemLoadingState />\n </div>\n ))}\n\n {!isLoading && isShowingNoMatches && (\n <div className={styles.item({})}>\n <Subheading className={styles.noMatchesTitle}>\n {noMatchesMessage}\n </Subheading>\n </div>\n )}\n\n {!isLoading &&\n isUsingGroups(isGrouped, items) &&\n items.map((group: GroupType, index: number) => {\n if (group.options.length < 1) {\n return;\n }\n const render = (\n <div key={index}>\n <SectionHeading\n key={index}\n data-test-id=\"cf-autocomplete-grouptitle\"\n marginBottom=\"none\"\n className={styles.groupTitle}\n >\n {group.groupTitle}\n </SectionHeading>\n <AutocompleteItems<ItemType>\n items={group.options}\n highlightedIndex={highlightedIndex}\n getItemProps={getItemProps}\n renderItem={renderItem}\n inputValue={inputValue}\n elementStartIndex={elementStartIndex}\n />\n </div>\n );\n elementStartIndex += group.options.length;\n return render;\n })}\n\n {!isLoading &&\n !isUsingGroups(isGrouped, items) &&\n items.length > 0 && (\n <AutocompleteItems<ItemType>\n items={items}\n elementStartIndex={elementStartIndex}\n highlightedIndex={highlightedIndex}\n getItemProps={getItemProps}\n renderItem={renderItem}\n inputValue={inputValue}\n />\n )}\n </Popover.Content>\n ) : (\n // We need to render an empty hidden div, so we can pass the menuProps or downshift will show a warning about it\n // https://github.com/downshift-js/downshift/issues/1167#issuecomment-1088022842\n <div {...menuProps} className={cx(styles.hidden)} />\n )}\n </Popover>\n </div>\n );\n}\n\nconst ListItemLoadingState = () => {\n return (\n <Skeleton.Container svgHeight={16}>\n <Skeleton.BodyText numberOfLines={1} />\n </Skeleton.Container>\n );\n};\n\n// This is required to infer correct typings when differentiating groups and items\nfunction isUsingGroups<ItemType>(\n isGrouped: boolean,\n items: ItemType[] | GenericGroupType<ItemType>[],\n): items is GenericGroupType<ItemType>[] {\n return isGrouped;\n}\n\n/**\n * The Autocomplete is a component that will show a `TextInput` where a user can type any word which will be used\n * to filter a list of items. That list of filtered items will be shown to the user as possible options for the input.\n * Once one of the options is selected, that option becomes the value of the `TextInput`.\n */\nexport const Autocomplete = React.forwardRef(_Autocomplete) as <T>(\n props: ExpandProps<AutocompleteProps<T>> & {\n ref?: React.Ref<HTMLDivElement>;\n },\n) => ReturnType<typeof _Autocomplete>;\n","import React, { HTMLAttributes } from 'react';\nimport { cx } from 'emotion';\nimport { getStringMatch } from '@contentful/f36-utils';\nimport type { UseComboboxGetItemPropsOptions } from 'downshift';\nimport { Text } from '@contentful/f36-typography';\n\nimport { getAutocompleteStyles } from './Autocomplete.styles';\n\ninterface AutocompleteItemsProps<ItemType> {\n items: ItemType[];\n elementStartIndex: number;\n highlightedIndex: number;\n getItemProps: (\n options: UseComboboxGetItemPropsOptions<ItemType>,\n ) => HTMLAttributes<HTMLLIElement>;\n renderItem: (item: ItemType, inputValue: string) => React.ReactNode;\n inputValue: string;\n listMaxHeight?: number;\n}\n\nexport const AutocompleteItems = <ItemType,>(\n props: AutocompleteItemsProps<ItemType>,\n) => {\n const {\n items,\n elementStartIndex,\n highlightedIndex,\n getItemProps,\n renderItem,\n inputValue,\n listMaxHeight = 180,\n } = props;\n\n const styles = getAutocompleteStyles(listMaxHeight);\n\n return (\n <ul className={styles.list} data-test-id=\"cf-autocomplete-list\">\n {items.map((item: ItemType, index: number) => {\n const itemIndex = elementStartIndex + index;\n const itemProps = getItemProps({ item, index: itemIndex });\n return (\n <Text\n {...itemProps}\n as=\"li\"\n key={itemIndex}\n className={cx([\n styles.item({}),\n highlightedIndex === itemIndex && styles.highlighted,\n ])}\n data-test-id={`cf-autocomplete-list-item-${itemIndex}`}\n >\n {renderItem ? (\n renderItem(item, inputValue)\n ) : typeof item === 'string' ? (\n <HighlightedItem item={item} inputValue={inputValue} />\n ) : (\n item\n )}\n </Text>\n );\n })}\n </ul>\n );\n};\n\nAutocompleteItems.displayName = 'AutocompleteItems';\n\nfunction HighlightedItem({\n item,\n inputValue,\n}: {\n item: string;\n inputValue: string;\n}) {\n const { before, match, after } = getStringMatch(item, inputValue);\n\n return (\n <>\n {before}\n <b>{match}</b>\n {after}\n </>\n );\n}\n\nHighlightedItem.displayName = 'HighlightedItem';\n","import { css, cx } from 'emotion';\nimport tokens from '@contentful/f36-tokens';\nimport { getMenuItemStyles } from '@contentful/f36-core';\n\nexport const getAutocompleteStyles = (listMaxHeight: number) => ({\n autocomplete: css({\n position: 'relative',\n width: '100%',\n }),\n combobox: css({\n position: 'relative',\n }),\n inputField: css({\n paddingRight: tokens.spacingXl,\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }),\n toggleButton: css({\n position: 'absolute',\n top: '1px',\n right: '1px',\n zIndex: 1,\n padding: tokens.spacing2Xs,\n minHeight: 'calc(100% - 2px)',\n }),\n content: css({\n overflow: 'auto',\n maxHeight: `${listMaxHeight}px`,\n }),\n list: css({\n listStyle: 'none',\n padding: `${tokens.spacingXs} ${tokens.spacing2Xs}`,\n margin: 0,\n }),\n groupTitle: css({\n padding: `${tokens.spacingXs} ${tokens.spacingM}`,\n lineHeight: tokens.lineHeightM,\n }),\n noMatchesTitle: css({\n color: tokens.gray500,\n margin: `${tokens.spacingM} 0 ${tokens.spacingM} 0`,\n }),\n item: ({\n isActive = false,\n isDisabled = false,\n }: {\n isActive?: boolean;\n isDisabled?: boolean;\n }) => cx(getMenuItemStyles({ isActive, isDisabled })),\n highlighted: css({\n backgroundColor: tokens.gray100,\n }),\n hidden: css({\n display: 'none',\n }),\n});\n"]}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { CommonProps } from '@contentful/f36-core';
|
|
3
|
+
import { TextInputProps } from '@contentful/f36-forms';
|
|
4
|
+
|
|
5
|
+
interface GenericGroupType<ItemType> {
|
|
6
|
+
groupTitle: string;
|
|
7
|
+
options: ItemType[];
|
|
8
|
+
}
|
|
9
|
+
interface AutocompleteProps<ItemType> extends CommonProps, Pick<TextInputProps, 'isDisabled' | 'isInvalid' | 'isReadOnly' | 'isRequired' | 'id' | 'defaultValue'> {
|
|
10
|
+
/**
|
|
11
|
+
* It’s an array of data to be used as "options" by the autocomplete component.
|
|
12
|
+
* This can either be a plain list of items or a list of groups of items.
|
|
13
|
+
*/
|
|
14
|
+
items: ItemType[] | GenericGroupType<ItemType>[];
|
|
15
|
+
/**
|
|
16
|
+
* Boolean to control whether the Autocomplete menu is open
|
|
17
|
+
*/
|
|
18
|
+
isOpen?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Callback fired when the Autocomplete menu opens
|
|
21
|
+
*/
|
|
22
|
+
onOpen?: () => void;
|
|
23
|
+
/**
|
|
24
|
+
* Callback fired when the Autocomplete menu closes
|
|
25
|
+
*/
|
|
26
|
+
onClose?: () => void;
|
|
27
|
+
/**
|
|
28
|
+
* Set a custom icon for the text input
|
|
29
|
+
*/
|
|
30
|
+
icon?: React.ReactElement;
|
|
31
|
+
/**
|
|
32
|
+
* Tells if the item is a object with groups
|
|
33
|
+
*/
|
|
34
|
+
isGrouped?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Set the value of the text input
|
|
37
|
+
*/
|
|
38
|
+
inputValue?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Function called whenever the input value changes
|
|
41
|
+
*/
|
|
42
|
+
onInputValueChange?: (value: string) => void;
|
|
43
|
+
/**
|
|
44
|
+
* This is the function that will be called when the user selects one of the "options" in the list.
|
|
45
|
+
* The component will pass the selected "item" as an argument to the function..
|
|
46
|
+
*/
|
|
47
|
+
onSelectItem: (item: ItemType) => void;
|
|
48
|
+
/**
|
|
49
|
+
* Applying the selectedItem property turns autocomplete into a controlled component.
|
|
50
|
+
* Can be used to display e.g. previously selected element. If it is an object the itemToString function will apply to it.
|
|
51
|
+
*/
|
|
52
|
+
selectedItem?: ItemType;
|
|
53
|
+
/**
|
|
54
|
+
* This is the function that will be called for each "item" passed in the `items` prop.
|
|
55
|
+
* It receives the "item" and "inputValue" as arguments and returns a ReactNode.
|
|
56
|
+
* The inputValue is passed in case you want to highlight the match on the render.
|
|
57
|
+
*/
|
|
58
|
+
renderItem?: (item: ItemType, inputValue: string) => React.ReactNode;
|
|
59
|
+
/**
|
|
60
|
+
* When using objects as `items`, we recommend passing a function that tells Downshift how to extract a string
|
|
61
|
+
* from those objetcs to be used as inputValue
|
|
62
|
+
*/
|
|
63
|
+
itemToString?: (item: ItemType) => string;
|
|
64
|
+
/**
|
|
65
|
+
* Text input behaviour after an item is selected
|
|
66
|
+
* @default "replace"
|
|
67
|
+
*/
|
|
68
|
+
textOnAfterSelect?: 'clear' | 'preserve' | 'replace';
|
|
69
|
+
/**
|
|
70
|
+
* If this is set to `false` the dropdown menu will stay open after selecting an item
|
|
71
|
+
* @default true
|
|
72
|
+
*/
|
|
73
|
+
closeAfterSelect?: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* This is the value will be passed to the `placeholder` prop of the input.
|
|
76
|
+
* @default "Search"
|
|
77
|
+
*/
|
|
78
|
+
placeholder?: string;
|
|
79
|
+
/**
|
|
80
|
+
* Defines if the list should be shown even if empty, when input is focused
|
|
81
|
+
* @default false
|
|
82
|
+
*/
|
|
83
|
+
showEmptyList?: boolean;
|
|
84
|
+
/**
|
|
85
|
+
* A message that will be shown when it is not possible to find any option that matches the input value
|
|
86
|
+
* @default "No matches"
|
|
87
|
+
*/
|
|
88
|
+
noMatchesMessage?: string;
|
|
89
|
+
/**
|
|
90
|
+
* Use this prop to get a ref to the input element of the component
|
|
91
|
+
*/
|
|
92
|
+
inputRef?: React.Ref<HTMLInputElement>;
|
|
93
|
+
/**
|
|
94
|
+
* Use this prop to get a ref to the toggle button of the component
|
|
95
|
+
*/
|
|
96
|
+
toggleRef?: React.Ref<HTMLButtonElement>;
|
|
97
|
+
/**
|
|
98
|
+
* Use this prop to get a ref to the list of items of the component
|
|
99
|
+
*/
|
|
100
|
+
listRef?: React.Ref<HTMLUListElement>;
|
|
101
|
+
/**
|
|
102
|
+
* It sets the width of the list
|
|
103
|
+
* @default "auto"
|
|
104
|
+
*/
|
|
105
|
+
listWidth?: 'auto' | 'full';
|
|
106
|
+
/**
|
|
107
|
+
* It sets the max-height, in pixels, of the list
|
|
108
|
+
* The default value is the height of 5 single line items
|
|
109
|
+
* @default 180
|
|
110
|
+
*/
|
|
111
|
+
listMaxHeight?: number;
|
|
112
|
+
/**
|
|
113
|
+
* Sets the list to show its loading state
|
|
114
|
+
* @default false
|
|
115
|
+
*/
|
|
116
|
+
isLoading?: boolean;
|
|
117
|
+
/**
|
|
118
|
+
* Boolean to control whether or not to render the suggestions box in a React Portal.
|
|
119
|
+
* Rendering content inside a Portal allows the suggestions box to escape the bounds
|
|
120
|
+
* of its parent while still being positioned correctly.
|
|
121
|
+
* Defaults to `false`
|
|
122
|
+
*/
|
|
123
|
+
usePortal?: boolean;
|
|
124
|
+
/**
|
|
125
|
+
* A [data-test-id] attribute for the suggestions box used for testing purposes
|
|
126
|
+
*/
|
|
127
|
+
popoverTestId?: string;
|
|
128
|
+
/**
|
|
129
|
+
* Function called when the input is focused
|
|
130
|
+
*
|
|
131
|
+
* @param event
|
|
132
|
+
*/
|
|
133
|
+
onFocus?: (event: React.FocusEvent<HTMLInputElement>) => void;
|
|
134
|
+
/**
|
|
135
|
+
* Function called when the input is blurred
|
|
136
|
+
* @param event
|
|
137
|
+
*/
|
|
138
|
+
onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void;
|
|
139
|
+
/**
|
|
140
|
+
* Manually control when the button to clear the input value is shown
|
|
141
|
+
*/
|
|
142
|
+
showClearButton?: boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Additional aria attributes
|
|
145
|
+
*/
|
|
146
|
+
aria?: {
|
|
147
|
+
showListIconLabel?: string;
|
|
148
|
+
clearSelectionIconLabel?: string;
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
declare function _Autocomplete<ItemType>(props: AutocompleteProps<ItemType>, ref: React.Ref<HTMLDivElement>): JSX.Element;
|
|
152
|
+
/**
|
|
153
|
+
* The Autocomplete is a component that will show a `TextInput` where a user can type any word which will be used
|
|
154
|
+
* to filter a list of items. That list of filtered items will be shown to the user as possible options for the input.
|
|
155
|
+
* Once one of the options is selected, that option becomes the value of the `TextInput`.
|
|
156
|
+
*/
|
|
157
|
+
declare const Autocomplete: <T>(props: AutocompleteProps<T> & {
|
|
158
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
159
|
+
}) => ReturnType<typeof _Autocomplete>;
|
|
160
|
+
|
|
161
|
+
export { Autocomplete, AutocompleteProps };
|
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,18 @@ interface AutocompleteProps<ItemType> extends CommonProps, Pick<TextInputProps,
|
|
|
12
12
|
* This can either be a plain list of items or a list of groups of items.
|
|
13
13
|
*/
|
|
14
14
|
items: ItemType[] | GenericGroupType<ItemType>[];
|
|
15
|
+
/**
|
|
16
|
+
* Boolean to control whether the Autocomplete menu is open
|
|
17
|
+
*/
|
|
18
|
+
isOpen?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Callback fired when the Autocomplete menu opens
|
|
21
|
+
*/
|
|
22
|
+
onOpen?: () => void;
|
|
23
|
+
/**
|
|
24
|
+
* Callback fired when the Autocomplete menu closes
|
|
25
|
+
*/
|
|
26
|
+
onClose?: () => void;
|
|
15
27
|
/**
|
|
16
28
|
* Set a custom icon for the text input
|
|
17
29
|
*/
|
|
@@ -20,6 +32,10 @@ interface AutocompleteProps<ItemType> extends CommonProps, Pick<TextInputProps,
|
|
|
20
32
|
* Tells if the item is a object with groups
|
|
21
33
|
*/
|
|
22
34
|
isGrouped?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Set the value of the text input
|
|
37
|
+
*/
|
|
38
|
+
inputValue?: string;
|
|
23
39
|
/**
|
|
24
40
|
* Function called whenever the input value changes
|
|
25
41
|
*/
|
|
@@ -46,10 +62,10 @@ interface AutocompleteProps<ItemType> extends CommonProps, Pick<TextInputProps,
|
|
|
46
62
|
*/
|
|
47
63
|
itemToString?: (item: ItemType) => string;
|
|
48
64
|
/**
|
|
49
|
-
*
|
|
50
|
-
* @default
|
|
65
|
+
* Text input behaviour after an item is selected
|
|
66
|
+
* @default "replace"
|
|
51
67
|
*/
|
|
52
|
-
|
|
68
|
+
textOnAfterSelect?: 'clear' | 'preserve' | 'replace';
|
|
53
69
|
/**
|
|
54
70
|
* If this is set to `false` the dropdown menu will stay open after selecting an item
|
|
55
71
|
* @default true
|
|
@@ -105,6 +121,32 @@ interface AutocompleteProps<ItemType> extends CommonProps, Pick<TextInputProps,
|
|
|
105
121
|
* Defaults to `false`
|
|
106
122
|
*/
|
|
107
123
|
usePortal?: boolean;
|
|
124
|
+
/**
|
|
125
|
+
* A [data-test-id] attribute for the suggestions box used for testing purposes
|
|
126
|
+
*/
|
|
127
|
+
popoverTestId?: string;
|
|
128
|
+
/**
|
|
129
|
+
* Function called when the input is focused
|
|
130
|
+
*
|
|
131
|
+
* @param event
|
|
132
|
+
*/
|
|
133
|
+
onFocus?: (event: React.FocusEvent<HTMLInputElement>) => void;
|
|
134
|
+
/**
|
|
135
|
+
* Function called when the input is blurred
|
|
136
|
+
* @param event
|
|
137
|
+
*/
|
|
138
|
+
onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void;
|
|
139
|
+
/**
|
|
140
|
+
* Manually control when the button to clear the input value is shown
|
|
141
|
+
*/
|
|
142
|
+
showClearButton?: boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Additional aria attributes
|
|
145
|
+
*/
|
|
146
|
+
aria?: {
|
|
147
|
+
showListIconLabel?: string;
|
|
148
|
+
clearSelectionIconLabel?: string;
|
|
149
|
+
};
|
|
108
150
|
}
|
|
109
151
|
declare function _Autocomplete<ItemType>(props: AutocompleteProps<ItemType>, ref: React.Ref<HTMLDivElement>): JSX.Element;
|
|
110
152
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
3
|
var i = require('react');
|
|
6
4
|
var emotion = require('emotion');
|
|
7
5
|
var downshift = require('downshift');
|
|
@@ -13,14 +11,15 @@ var f36Skeleton = require('@contentful/f36-skeleton');
|
|
|
13
11
|
var f36Popover = require('@contentful/f36-popover');
|
|
14
12
|
var f36Typography = require('@contentful/f36-typography');
|
|
15
13
|
var f36Utils = require('@contentful/f36-utils');
|
|
16
|
-
var
|
|
14
|
+
var l = require('@contentful/f36-tokens');
|
|
17
15
|
|
|
18
|
-
function
|
|
16
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
19
17
|
|
|
20
|
-
var i__default = /*#__PURE__*/
|
|
21
|
-
var
|
|
18
|
+
var i__default = /*#__PURE__*/_interopDefault(i);
|
|
19
|
+
var l__default = /*#__PURE__*/_interopDefault(l);
|
|
22
20
|
|
|
23
|
-
var
|
|
21
|
+
var Ve=Object.defineProperty,Ge=Object.defineProperties;var ke=Object.getOwnPropertyDescriptors;var A=Object.getOwnPropertySymbols;var Q=Object.prototype.hasOwnProperty,Y=Object.prototype.propertyIsEnumerable;var J=(t,o,n)=>o in t?Ve(t,o,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[o]=n,m=(t,o)=>{for(var n in o||(o={}))Q.call(o,n)&&J(t,n,o[n]);if(A)for(var n of A(o))Y.call(o,n)&&J(t,n,o[n]);return t},u=(t,o)=>Ge(t,ke(o));var Z=(t,o)=>{var n={};for(var s in t)Q.call(t,s)&&o.indexOf(s)<0&&(n[s]=t[s]);if(t!=null&&A)for(var s of A(t))o.indexOf(s)<0&&Y.call(t,s)&&(n[s]=t[s]);return n};var E=t=>({autocomplete:emotion.css({position:"relative",width:"100%"}),combobox:emotion.css({position:"relative"}),inputField:emotion.css({paddingRight:l__default.default.spacingXl,textOverflow:"ellipsis",whiteSpace:"nowrap"}),toggleButton:emotion.css({position:"absolute",top:"1px",right:"1px",zIndex:1,padding:l__default.default.spacing2Xs,minHeight:"calc(100% - 2px)"}),content:emotion.css({overflow:"auto",maxHeight:`${t}px`}),list:emotion.css({listStyle:"none",padding:`${l__default.default.spacingXs} ${l__default.default.spacing2Xs}`,margin:0}),groupTitle:emotion.css({padding:`${l__default.default.spacingXs} ${l__default.default.spacingM}`,lineHeight:l__default.default.lineHeightM}),noMatchesTitle:emotion.css({color:l__default.default.gray500,margin:`${l__default.default.spacingM} 0 ${l__default.default.spacingM} 0`}),item:({isActive:o=!1,isDisabled:n=!1})=>emotion.cx(f36Core.getMenuItemStyles({isActive:o,isDisabled:n})),highlighted:emotion.css({backgroundColor:l__default.default.gray100}),hidden:emotion.css({display:"none"})});var N=t=>{let{items:o,elementStartIndex:n,highlightedIndex:s,getItemProps:g,renderItem:x,inputValue:v,listMaxHeight:P=180}=t,y=E(P);return i__default.default.createElement("ul",{className:y.list,"data-test-id":"cf-autocomplete-list"},o.map((I,V)=>{let r=n+V,C=g({item:I,index:r});return i__default.default.createElement(f36Typography.Text,u(m({},C),{as:"li",key:r,className:emotion.cx([y.item({}),s===r&&y.highlighted]),"data-test-id":`cf-autocomplete-list-item-${r}`}),x?x(I,v):typeof I=="string"?i__default.default.createElement(ee,{item:I,inputValue:v}):I)}))};N.displayName="AutocompleteItems";function ee({item:t,inputValue:o}){let{before:n,match:s,after:g}=f36Utils.getStringMatch(t,o);return i__default.default.createElement(i__default.default.Fragment,null,n,i__default.default.createElement("b",null,s),g)}ee.displayName="HighlightedItem";function je(t,o){let {isOpen:n,onClose:s,onOpen:g,id:x,className:v,textOnAfterSelect:P="replace",closeAfterSelect:y=!0,defaultValue:I="",selectedItem:V,items:r,inputValue:C,onInputValueChange:S,onSelectItem:ie,onFocus:G,onBlur:k,renderItem:_,icon:se=i__default.default.createElement(f36Icons.CaretDownIcon,{color:l__default.default.gray600}),itemToString:re=e=>e,isInvalid:pe,isDisabled:U,isRequired:le,isReadOnly:ae,showEmptyList:me,noMatchesMessage:ue="No matches found",placeholder:ce="Search",inputRef:de,toggleRef:ge,listRef:Ie,listWidth:he="auto",listMaxHeight:fe=180,isGrouped:M=!1,isLoading:L=!1,usePortal:ye=!1,testId:Te="cf-autocomplete",popoverTestId:be="cf-autocomplete-container",showClearButton:R,aria:W={clearSelectionIconLabel:"Clear",showListIconLabel:"Show list"}}=t,a=E(fe),[xe,ve]=i.useState(I),T=typeof C=="undefined"?xe:C,b=i.useCallback(e=>{ve(e),S==null||S(e);},[S]),Pe=i.useCallback(e=>{let p=e.target.value;b(p);},[b]),Ce=O(M)?r.reduce((e,p)=>[...e,...p.options],[]):r,Se=O(M)?r.every(e=>e.options.length===0):r.length===0,{getComboboxProps:Me,getInputProps:Le,getItemProps:q,getMenuProps:we,getToggleButtonProps:He,highlightedIndex:K,isOpen:Ae,openMenu:Ee,toggleMenu:Ne}=downshift.useCombobox({isOpen:n,onIsOpenChange:({isOpen:e})=>{e?g==null||g():s==null||s();},stateReducer:(e,{type:p,changes:h})=>{switch(p){case downshift.useCombobox.stateChangeTypes.InputBlur:return u(m({},h),{inputValue:e.inputValue});case downshift.useCombobox.stateChangeTypes.InputKeyDownEnter:case downshift.useCombobox.stateChangeTypes.ItemClick:return y?h:u(m({},h),{isOpen:e.isOpen});default:return h}},items:Ce,selectedItem:V,inputValue:T,itemToString:re,onInputValueChange:({type:e,inputValue:p})=>{switch(e){case downshift.useCombobox.stateChangeTypes.InputChange:return;case downshift.useCombobox.stateChangeTypes.ItemClick:case downshift.useCombobox.stateChangeTypes.InputKeyDownEnter:{if(P==="clear"){b("");return}if(P==="preserve")return}}b(p);},onStateChange:({type:e,selectedItem:p})=>{switch(e){case downshift.useCombobox.stateChangeTypes.InputKeyDownEnter:case downshift.useCombobox.stateChangeTypes.ItemClick:p&&ie(p);break;}}}),j=Le(),w=Z(j,["aria-labelledby","id"]),Oe=Me(),z=He(),H=we(),B=0,D=R!=null?R:T;return i__default.default.createElement("div",{"data-test-id":Te,className:emotion.cx(a.autocomplete,v),ref:o},i__default.default.createElement(f36Popover.Popover,{usePortal:ye,isOpen:Ae,isFullWidth:he==="full",renderOnlyWhenOpen:!1,autoFocus:!1,id:H.id},i__default.default.createElement(f36Popover.Popover.Trigger,null,i__default.default.createElement("div",u(m({},Oe),{className:a.combobox}),i__default.default.createElement(f36Forms.TextInput,u(m({className:a.inputField},w),{onFocus:e=>{G==null||G(e),Ee();},onBlur:e=>{k==null||k(e),w.onBlur(e);},id:x,isInvalid:pe,isDisabled:U,isRequired:le,isReadOnly:ae,ref:f36Core.mergeRefs(w.ref,de),testId:"cf-autocomplete-input",placeholder:ce,onChange:e=>{w.onChange(e),Pe(e);}})),i__default.default.createElement(f36Button.IconButton,u(m({},z),{ref:f36Core.mergeRefs(z.ref,ge),"aria-label":D?W.clearSelectionIconLabel:W.showListIconLabel,className:a.toggleButton,variant:"transparent",icon:D?i__default.default.createElement(f36Icons.XIcon,{color:l__default.default.gray600}):se,onClick:()=>{D?b(""):Ne();},isDisabled:U,size:"small"})))),r.length>0||T.length>0||me?i__default.default.createElement(f36Popover.Popover.Content,u(m({},H),{ref:f36Core.mergeRefs(H.ref,Ie),className:a.content,testId:be}),L&&[...Array(3)].map((e,p)=>i__default.default.createElement("div",{key:p,className:emotion.cx(a.item({isDisabled:!0}))},i__default.default.createElement(Je,null))),!L&&Se&&i__default.default.createElement("div",{className:a.item({})},i__default.default.createElement(f36Typography.Subheading,{className:a.noMatchesTitle},ue)),!L&&O(M)&&r.map((e,p)=>{if(e.options.length<1)return;let h=i__default.default.createElement("div",{key:p},i__default.default.createElement(f36Typography.SectionHeading,{key:p,"data-test-id":"cf-autocomplete-grouptitle",marginBottom:"none",className:a.groupTitle},e.groupTitle),i__default.default.createElement(N,{items:e.options,highlightedIndex:K,getItemProps:q,renderItem:_,inputValue:T,elementStartIndex:B}));return B+=e.options.length,h}),!L&&!O(M)&&r.length>0&&i__default.default.createElement(N,{items:r,elementStartIndex:B,highlightedIndex:K,getItemProps:q,renderItem:_,inputValue:T})):i__default.default.createElement("div",u(m({},H),{className:emotion.cx(a.hidden)}))))}var Je=()=>i__default.default.createElement(f36Skeleton.Skeleton.Container,{svgHeight:16},i__default.default.createElement(f36Skeleton.Skeleton.BodyText,{numberOfLines:1}));function O(t,o){return t}var Qe=i__default.default.forwardRef(je);
|
|
24
22
|
|
|
25
|
-
exports.Autocomplete =
|
|
23
|
+
exports.Autocomplete = Qe;
|
|
24
|
+
//# sourceMappingURL=out.js.map
|
|
26
25
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/Autocomplete.tsx","../src/AutocompleteItems.tsx","../src/Autocomplete.styles.ts"],"names":["React","useCallback","useState","cx","useCombobox","mergeRefs","IconButton","TextInput","CloseIcon","ChevronDownIcon","Skeleton","Popover","Subheading","SectionHeading","getStringMatch","Text","css","tokens","getAutocompleteStyles","listMaxHeight","AutocompleteItems","props","items","elementStartIndex","highlightedIndex","getItemProps","renderItem","inputValue","styles","item","index","itemIndex","itemProps","__spreadProps","__spreadValues","HighlightedItem","before","match","after","_Autocomplete","ref","id","className","clearAfterSelect","closeAfterSelect","defaultValue","selectedItem","onInputValueChange","onSelectItem","icon","itemToString","isInvalid","isDisabled","isRequired","isReadOnly","showEmptyList","noMatchesMessage","placeholder","inputRef","toggleRef","listRef","listWidth","isGrouped","isLoading","usePortal","testId","setInputValue","handleInputValueChange","value","handleNativeChangeEvent","event","flattenItems","isUsingGroups","acc","group","isShowingNoMatches","getComboboxProps","getInputProps","getMenuProps","getToggleButtonProps","isOpen","toggleMenu","type","_a","_labelledby","_inputId","inputProps","__objRest","comboboxProps","toggleProps","menuProps","_","ListItemLoadingState","render","Autocomplete"],"mappings":"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","sourcesContent":["import React, { useCallback, useState } from 'react';\nimport { cx } from 'emotion';\nimport { useCombobox } from 'downshift';\n\nimport {\n mergeRefs,\n type CommonProps,\n type ExpandProps,\n} from '@contentful/f36-core';\nimport { IconButton } from '@contentful/f36-button';\nimport { TextInput, type TextInputProps } from '@contentful/f36-forms';\nimport { CloseIcon, ChevronDownIcon } from '@contentful/f36-icons';\nimport { Skeleton } from '@contentful/f36-skeleton';\nimport { Popover } from '@contentful/f36-popover';\nimport { Subheading, SectionHeading } from '@contentful/f36-typography';\n\nimport { AutocompleteItems } from './AutocompleteItems';\nimport { getAutocompleteStyles } from './Autocomplete.styles';\n\nexport interface GenericGroupType<ItemType> {\n groupTitle: string;\n options: ItemType[];\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport interface AutocompleteProps<ItemType>\n extends CommonProps,\n Pick<\n TextInputProps,\n | 'isDisabled'\n | 'isInvalid'\n | 'isReadOnly'\n | 'isRequired'\n | 'id'\n | 'defaultValue'\n > {\n /**\n * It’s an array of data to be used as \"options\" by the autocomplete component.\n * This can either be a plain list of items or a list of groups of items.\n */\n items: ItemType[] | GenericGroupType<ItemType>[];\n\n /**\n * Set a custom icon for the text input\n */\n icon?: React.ReactElement;\n\n /**\n * Tells if the item is a object with groups\n */\n isGrouped?: boolean;\n\n /**\n * Function called whenever the input value changes\n */\n onInputValueChange?: (value: string) => void;\n /**\n * This is the function that will be called when the user selects one of the \"options\" in the list.\n * The component will pass the selected \"item\" as an argument to the function..\n */\n onSelectItem: (item: ItemType) => void;\n\n /**\n * Applying the selectedItem property turns autocomplete into a controlled component.\n * Can be used to display e.g. previously selected element. If it is an object the itemToString function will apply to it.\n */\n selectedItem?: ItemType;\n\n /**\n * This is the function that will be called for each \"item\" passed in the `items` prop.\n * It receives the \"item\" and \"inputValue\" as arguments and returns a ReactNode.\n * The inputValue is passed in case you want to highlight the match on the render.\n */\n renderItem?: (item: ItemType, inputValue: string) => React.ReactNode;\n /**\n * When using objects as `items`, we recommend passing a function that tells Downshift how to extract a string\n * from those objetcs to be used as inputValue\n */\n itemToString?: (item: ItemType) => string;\n /**\n * If this is set to `true` the text input will be cleared after an item is selected\n * @default false\n */\n clearAfterSelect?: boolean;\n /**\n * If this is set to `false` the dropdown menu will stay open after selecting an item\n * @default true\n */\n closeAfterSelect?: boolean;\n /**\n * This is the value will be passed to the `placeholder` prop of the input.\n * @default \"Search\"\n */\n placeholder?: string;\n /**\n * Defines if the list should be shown even if empty, when input is focused\n * @default false\n */\n showEmptyList?: boolean;\n /**\n * A message that will be shown when it is not possible to find any option that matches the input value\n * @default \"No matches\"\n */\n noMatchesMessage?: string;\n /**\n * Use this prop to get a ref to the input element of the component\n */\n inputRef?: React.Ref<HTMLInputElement>;\n /**\n * Use this prop to get a ref to the toggle button of the component\n */\n toggleRef?: React.Ref<HTMLButtonElement>;\n /**\n * Use this prop to get a ref to the list of items of the component\n */\n listRef?: React.Ref<HTMLUListElement>;\n /**\n * It sets the width of the list\n * @default \"auto\"\n */\n listWidth?: 'auto' | 'full';\n /**\n * It sets the max-height, in pixels, of the list\n * The default value is the height of 5 single line items\n * @default 180\n */\n listMaxHeight?: number;\n /**\n * Sets the list to show its loading state\n * @default false\n */\n isLoading?: boolean;\n /**\n * Boolean to control whether or not to render the suggestions box in a React Portal.\n * Rendering content inside a Portal allows the suggestions box to escape the bounds\n * of its parent while still being positioned correctly.\n * Defaults to `false`\n */\n usePortal?: boolean;\n}\n\nfunction _Autocomplete<ItemType>(\n props: AutocompleteProps<ItemType>,\n ref: React.Ref<HTMLDivElement>,\n) {\n const {\n id,\n className,\n clearAfterSelect = false,\n closeAfterSelect = true,\n defaultValue = '',\n selectedItem,\n items,\n onInputValueChange,\n onSelectItem,\n renderItem,\n icon = <ChevronDownIcon variant=\"muted\" />,\n itemToString = (item: ItemType) => item as unknown as string,\n isInvalid,\n isDisabled,\n isRequired,\n isReadOnly,\n showEmptyList,\n noMatchesMessage = 'No matches found',\n placeholder = 'Search',\n inputRef,\n toggleRef,\n listRef,\n listWidth = 'auto',\n listMaxHeight = 180,\n isGrouped = false,\n isLoading = false,\n usePortal = false,\n testId = 'cf-autocomplete',\n } = props;\n\n type GroupType = GenericGroupType<ItemType>;\n\n const styles = getAutocompleteStyles(listMaxHeight);\n\n const [inputValue, setInputValue] = useState(defaultValue);\n\n const handleInputValueChange = useCallback(\n (value: string) => {\n setInputValue(value);\n\n onInputValueChange?.(value);\n },\n [onInputValueChange],\n );\n\n // Handle manually to avoid a jumping cursor, see https://github.com/downshift-js/downshift/issues/1108#issuecomment-842407759\n const handleNativeChangeEvent = useCallback(\n (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {\n const value = event.target.value;\n handleInputValueChange(value);\n },\n [handleInputValueChange],\n );\n\n const flattenItems = isUsingGroups(isGrouped, items)\n ? items.reduce(\n (acc: ItemType[], group: GroupType) => [...acc, ...group.options],\n [],\n )\n : items;\n\n const isShowingNoMatches = isUsingGroups(isGrouped, items)\n ? items.every((group: GroupType) => group.options.length === 0)\n : items.length === 0;\n\n const {\n getComboboxProps,\n getInputProps,\n getItemProps,\n getMenuProps,\n getToggleButtonProps,\n highlightedIndex,\n isOpen,\n toggleMenu,\n } = useCombobox({\n items: flattenItems,\n selectedItem,\n inputValue,\n itemToString,\n onInputValueChange: ({ type, inputValue }) => {\n if (type !== '__input_change__') {\n handleInputValueChange(inputValue);\n }\n },\n onStateChange: ({ type, selectedItem }) => {\n switch (type) {\n case useCombobox.stateChangeTypes.InputKeyDownEnter:\n case useCombobox.stateChangeTypes.ItemClick:\n if (selectedItem) {\n onSelectItem(selectedItem);\n }\n if (clearAfterSelect) {\n handleInputValueChange('');\n }\n if (!closeAfterSelect) {\n toggleMenu();\n }\n break;\n default:\n break;\n }\n },\n });\n\n const {\n 'aria-labelledby': _labelledby,\n id: _inputId,\n ...inputProps\n } = getInputProps();\n const comboboxProps = getComboboxProps();\n const toggleProps = getToggleButtonProps();\n const menuProps = getMenuProps();\n let elementStartIndex = 0;\n\n return (\n <div\n data-test-id={testId}\n className={cx(styles.autocomplete, className)}\n ref={ref}\n >\n <Popover\n usePortal={usePortal}\n isOpen={isOpen}\n isFullWidth={listWidth === 'full'}\n renderOnlyWhenOpen={false}\n // This is necessary, otherwise the focus will change from the input to the Popover\n // and the user won't be able to type in the input\n // eslint-disable-next-line jsx-a11y/no-autofocus\n autoFocus={false}\n id={menuProps.id}\n >\n <Popover.Trigger>\n <div {...comboboxProps} className={styles.combobox}>\n <TextInput\n className={styles.inputField}\n {...inputProps}\n onFocus={() => {\n if (!isOpen) {\n toggleMenu();\n }\n }}\n id={id}\n isInvalid={isInvalid}\n isDisabled={isDisabled}\n isRequired={isRequired}\n isReadOnly={isReadOnly}\n ref={mergeRefs(inputProps.ref, inputRef)}\n testId=\"cf-autocomplete-input\"\n placeholder={placeholder}\n onChange={(event) => {\n inputProps.onChange(event);\n handleNativeChangeEvent(event);\n }}\n />\n <IconButton\n {...toggleProps}\n ref={mergeRefs(toggleProps.ref, toggleRef)}\n aria-label={inputValue ? 'Clear' : 'Show list'}\n className={styles.toggleButton}\n variant=\"transparent\"\n icon={inputValue ? <CloseIcon variant=\"muted\" /> : icon}\n onClick={() => {\n if (inputValue) {\n handleInputValueChange('');\n } else {\n toggleMenu();\n }\n }}\n isDisabled={isDisabled}\n size=\"small\"\n />\n </div>\n </Popover.Trigger>\n\n {items.length > 0 || inputValue.length > 0 || showEmptyList ? (\n <Popover.Content\n {...menuProps}\n ref={mergeRefs(menuProps.ref, listRef)}\n className={styles.content}\n testId=\"cf-autocomplete-container\"\n >\n {isLoading &&\n [...Array(3)].map((_, index) => (\n <div key={index} className={cx(styles.item, styles.disabled)}>\n <ListItemLoadingState />\n </div>\n ))}\n\n {!isLoading && isShowingNoMatches && (\n <div className={styles.item}>\n <Subheading className={styles.noMatchesTitle}>\n {noMatchesMessage}\n </Subheading>\n </div>\n )}\n\n {!isLoading &&\n isUsingGroups(isGrouped, items) &&\n items.map((group: GroupType, index: number) => {\n if (group.options.length < 1) {\n return;\n }\n const render = (\n <div key={index}>\n <SectionHeading\n key={index}\n data-test-id=\"cf-autocomplete-grouptitle\"\n marginBottom=\"none\"\n className={styles.groupTitle}\n >\n {group.groupTitle}\n </SectionHeading>\n <AutocompleteItems<ItemType>\n items={group.options}\n highlightedIndex={highlightedIndex}\n getItemProps={getItemProps}\n renderItem={renderItem}\n inputValue={inputValue}\n elementStartIndex={elementStartIndex}\n />\n </div>\n );\n elementStartIndex += group.options.length;\n return render;\n })}\n\n {!isLoading &&\n !isUsingGroups(isGrouped, items) &&\n items.length > 0 && (\n <AutocompleteItems<ItemType>\n items={items}\n elementStartIndex={elementStartIndex}\n highlightedIndex={highlightedIndex}\n getItemProps={getItemProps}\n renderItem={renderItem}\n inputValue={inputValue}\n />\n )}\n </Popover.Content>\n ) : (\n // We need to render an empty hidden div, so we can pass the menuProps or downshift will show a warning about it\n // https://github.com/downshift-js/downshift/issues/1167#issuecomment-1088022842\n <div {...menuProps} className={cx(styles.hidden)} />\n )}\n </Popover>\n </div>\n );\n}\n\nconst ListItemLoadingState = () => {\n return (\n <Skeleton.Container svgHeight={16}>\n <Skeleton.BodyText numberOfLines={1} />\n </Skeleton.Container>\n );\n};\n\n// This is required to infer correct typings when differentiating groups and items\nfunction isUsingGroups<ItemType>(\n isGrouped: boolean,\n items: ItemType[] | GenericGroupType<ItemType>[],\n): items is GenericGroupType<ItemType>[] {\n return isGrouped;\n}\n\n/**\n * The Autocomplete is a component that will show a `TextInput` where a user can type any word which will be used\n * to filter a list of items. That list of filtered items will be shown to the user as possible options for the input.\n * Once one of the options is selected, that option becomes the value of the `TextInput`.\n */\nexport const Autocomplete = React.forwardRef(_Autocomplete) as <T>(\n props: ExpandProps<AutocompleteProps<T>> & {\n ref?: React.Ref<HTMLDivElement>;\n },\n) => ReturnType<typeof _Autocomplete>;\n","import React, { HTMLAttributes } from 'react';\nimport { cx } from 'emotion';\nimport { getStringMatch } from '@contentful/f36-utils';\nimport type { UseComboboxGetItemPropsOptions } from 'downshift';\nimport { Text } from '@contentful/f36-typography';\n\nimport { getAutocompleteStyles } from './Autocomplete.styles';\n\ninterface AutocompleteItemsProps<ItemType> {\n items: ItemType[];\n elementStartIndex: number;\n highlightedIndex: number;\n getItemProps: (\n options: UseComboboxGetItemPropsOptions<ItemType>,\n ) => HTMLAttributes<HTMLLIElement>;\n renderItem: (item: ItemType, inputValue: string) => React.ReactNode;\n inputValue: string;\n listMaxHeight?: number;\n}\n\nexport const AutocompleteItems = <ItemType,>(\n props: AutocompleteItemsProps<ItemType>,\n) => {\n const {\n items,\n elementStartIndex,\n highlightedIndex,\n getItemProps,\n renderItem,\n inputValue,\n listMaxHeight = 180,\n } = props;\n\n const styles = getAutocompleteStyles(listMaxHeight);\n\n return (\n <ul className={styles.list} data-test-id=\"cf-autocomplete-list\">\n {items.map((item: ItemType, index: number) => {\n const itemIndex = elementStartIndex + index;\n const itemProps = getItemProps({ item, index: itemIndex });\n return (\n <Text\n {...itemProps}\n as=\"li\"\n key={itemIndex}\n className={cx([\n styles.item,\n highlightedIndex === itemIndex && styles.highlighted,\n ])}\n data-test-id={`cf-autocomplete-list-item-${itemIndex}`}\n >\n {renderItem ? (\n renderItem(item, inputValue)\n ) : typeof item === 'string' ? (\n <HighlightedItem item={item} inputValue={inputValue} />\n ) : (\n item\n )}\n </Text>\n );\n })}\n </ul>\n );\n};\n\nAutocompleteItems.displayName = 'AutocompleteItems';\n\nfunction HighlightedItem({\n item,\n inputValue,\n}: {\n item: string;\n inputValue: string;\n}) {\n const { before, match, after } = getStringMatch(item, inputValue);\n\n return (\n <>\n {before}\n <b>{match}</b>\n {after}\n </>\n );\n}\n\nHighlightedItem.displayName = 'HighlightedItem';\n","import { css } from 'emotion';\nimport tokens from '@contentful/f36-tokens';\n\nexport const getAutocompleteStyles = (listMaxHeight: number) => ({\n autocomplete: css({\n position: 'relative',\n width: '100%',\n }),\n combobox: css({\n position: 'relative',\n }),\n inputField: css({\n paddingRight: tokens.spacingXl,\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }),\n toggleButton: css({\n position: 'absolute',\n top: '1px',\n right: '1px',\n zIndex: 1,\n padding: tokens.spacing2Xs,\n height: '38px',\n }),\n content: css({\n overflow: 'auto',\n maxHeight: `${listMaxHeight}px`,\n }),\n list: css({\n listStyle: 'none',\n padding: `${tokens.spacingXs} 0`,\n margin: 0,\n }),\n groupTitle: css({\n padding: `${tokens.spacingXs} ${tokens.spacingM}`,\n lineHeight: tokens.lineHeightM,\n }),\n noMatchesTitle: css({\n color: tokens.gray500,\n margin: `${tokens.spacingM} 0 ${tokens.spacingM} 0`,\n }),\n item: css({\n display: 'block',\n padding: `${tokens.spacingXs} ${tokens.spacingM}`,\n wordBreak: 'break-word',\n whiteSpace: 'break-spaces',\n cursor: 'pointer',\n hyphens: 'auto',\n\n '&:focus, &:hover': {\n backgroundColor: tokens.gray100,\n },\n '&:active': {\n backgroundColor: tokens.gray200,\n },\n }),\n disabled: css({\n opacity: 0.5,\n pointerEvents: 'none',\n }),\n highlighted: css({\n backgroundColor: tokens.gray100,\n }),\n hidden: css({\n display: 'none',\n }),\n});\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/Autocomplete.tsx","../src/AutocompleteItems.tsx","../src/Autocomplete.styles.ts"],"names":["React","useCallback","useState","cx","useCombobox","mergeRefs","IconButton","TextInput","XIcon","CaretDownIcon","Skeleton","Popover","Subheading","SectionHeading","getStringMatch","Text","css","tokens","getMenuItemStyles","getAutocompleteStyles","listMaxHeight","isActive","isDisabled","AutocompleteItems","props","items","elementStartIndex","highlightedIndex","getItemProps","renderItem","inputValue","styles","item","index","itemIndex","itemProps","__spreadProps","__spreadValues","HighlightedItem","before","match","after","_Autocomplete","ref","isOpenProp","onClose","onOpen","id","className","textOnAfterSelect","closeAfterSelect","defaultValue","selectedItem","inputValueProp","onInputValueChange","onSelectItem","onFocus","onBlur","icon","itemToString","isInvalid","isRequired","isReadOnly","showEmptyList","noMatchesMessage","placeholder","inputRef","toggleRef","listRef","listWidth","isGrouped","isLoading","usePortal","testId","popoverTestId","showClearButtonProp","aria","_inputValue","setInputValue","handleInputValueChange","value","handleNativeChangeEvent","event","flattenItems","isUsingGroups","acc","group","isShowingNoMatches","getComboboxProps","getInputProps","getMenuProps","getToggleButtonProps","isOpen","openMenu","toggleMenu","state","type","changes","_a","_labelledby","_inputId","inputProps","__objRest","comboboxProps","toggleProps","menuProps","showClearButton","_","ListItemLoadingState","render","Autocomplete"],"mappings":"qlBAAA,OAAOA,GAAS,eAAAC,GAAa,YAAAC,OAAgB,QAC7C,OAAS,MAAAC,MAAU,UACnB,OAAS,eAAAC,MAAmB,YAE5B,OACE,aAAAC,MAGK,uBACP,OAAS,cAAAC,OAAkB,yBAC3B,OAAS,aAAAC,OAAsC,wBAC/C,OAAS,SAAAC,GAAO,iBAAAC,OAAqB,wBACrC,OAAS,YAAAC,OAAgB,2BACzB,OAAS,WAAAC,MAAe,0BACxB,OAAS,cAAAC,GAAY,kBAAAC,OAAsB,6BCd3C,OAAOb,MAA+B,QACtC,OAAS,MAAAG,OAAU,UACnB,OAAS,kBAAAW,OAAsB,wBAE/B,OAAS,QAAAC,OAAY,6BCJrB,OAAS,OAAAC,EAAK,MAAAb,OAAU,UACxB,OAAOc,MAAY,yBACnB,OAAS,qBAAAC,OAAyB,uBAE3B,IAAMC,EAAyBC,IAA2B,CAC/D,aAAcJ,EAAI,CAChB,SAAU,WACV,MAAO,MACT,CAAC,EACD,SAAUA,EAAI,CACZ,SAAU,UACZ,CAAC,EACD,WAAYA,EAAI,CACd,aAAcC,EAAO,UACrB,aAAc,WACd,WAAY,QACd,CAAC,EACD,aAAcD,EAAI,CAChB,SAAU,WACV,IAAK,MACL,MAAO,MACP,OAAQ,EACR,QAASC,EAAO,WAChB,UAAW,kBACb,CAAC,EACD,QAASD,EAAI,CACX,SAAU,OACV,UAAW,GAAGI,CAAa,IAC7B,CAAC,EACD,KAAMJ,EAAI,CACR,UAAW,OACX,QAAS,GAAGC,EAAO,SAAS,IAAIA,EAAO,UAAU,GACjD,OAAQ,CACV,CAAC,EACD,WAAYD,EAAI,CACd,QAAS,GAAGC,EAAO,SAAS,IAAIA,EAAO,QAAQ,GAC/C,WAAYA,EAAO,WACrB,CAAC,EACD,eAAgBD,EAAI,CAClB,MAAOC,EAAO,QACd,OAAQ,GAAGA,EAAO,QAAQ,MAAMA,EAAO,QAAQ,IACjD,CAAC,EACD,KAAM,CAAC,CACL,SAAAI,EAAW,GACX,WAAAC,EAAa,EACf,IAGMnB,GAAGe,GAAkB,CAAE,SAAAG,EAAU,WAAAC,CAAW,CAAC,CAAC,EACpD,YAAaN,EAAI,CACf,gBAAiBC,EAAO,OAC1B,CAAC,EACD,OAAQD,EAAI,CACV,QAAS,MACX,CAAC,CACH,GDnCO,IAAMO,EACXC,GACG,CACH,GAAM,CACJ,MAAAC,EACA,kBAAAC,EACA,iBAAAC,EACA,aAAAC,EACA,WAAAC,EACA,WAAAC,EACA,cAAAV,EAAgB,GAClB,EAAII,EAEEO,EAASZ,EAAsBC,CAAa,EAElD,OACEpB,EAAA,cAAC,MAAG,UAAW+B,EAAO,KAAM,eAAa,wBACtCN,EAAM,IAAI,CAACO,EAAgBC,IAAkB,CAC5C,IAAMC,EAAYR,EAAoBO,EAChCE,EAAYP,EAAa,CAAE,KAAAI,EAAM,MAAOE,CAAU,CAAC,EACzD,OACElC,EAAA,cAACe,GAAAqB,EAAAC,EAAA,GACKF,GADL,CAEC,GAAG,KACH,IAAKD,EACL,UAAW/B,GAAG,CACZ4B,EAAO,KAAK,CAAC,CAAC,EACdJ,IAAqBO,GAAaH,EAAO,WAC3C,CAAC,EACD,eAAc,6BAA6BG,CAAS,KAEnDL,EACCA,EAAWG,EAAMF,CAAU,EACzB,OAAOE,GAAS,SAClBhC,EAAA,cAACsC,GAAA,CAAgB,KAAMN,EAAM,WAAYF,EAAY,EAErDE,CAEJ,CAEJ,CAAC,CACH,CAEJ,EAEAT,EAAkB,YAAc,oBAEhC,SAASe,GAAgB,CACvB,KAAAN,EACA,WAAAF,CACF,EAGG,CACD,GAAM,CAAE,OAAAS,EAAQ,MAAAC,EAAO,MAAAC,CAAM,EAAI3B,GAAekB,EAAMF,CAAU,EAEhE,OACE9B,EAAA,cAAAA,EAAA,cACGuC,EACDvC,EAAA,cAAC,SAAGwC,CAAM,EACTC,CACH,CAEJ,CAEAH,GAAgB,YAAc,kBDnE9B,OAAOrB,OAAY,yBA2KnB,SAASyB,GACPlB,EACAmB,EACA,CACA,GAAM,CACJ,OAAQC,EACR,QAAAC,EACA,OAAAC,EACA,GAAAC,EACA,UAAAC,EACA,kBAAAC,EAAoB,UACpB,iBAAAC,EAAmB,GACnB,aAAAC,EAAe,GACf,aAAAC,EACA,MAAA3B,EACA,WAAY4B,EACZ,mBAAAC,EACA,aAAAC,GACA,QAAAC,EACA,OAAAC,EACA,WAAA5B,EACA,KAAA6B,GAAO1D,EAAA,cAACS,GAAA,CAAc,MAAOQ,GAAO,QAAS,EAC7C,aAAA0C,GAAgB3B,GAAmBA,EACnC,UAAA4B,GACA,WAAAtC,EACA,WAAAuC,GACA,WAAAC,GACA,cAAAC,GACA,iBAAAC,GAAmB,mBACnB,YAAAC,GAAc,SACd,SAAAC,GACA,UAAAC,GACA,QAAAC,GACA,UAAAC,GAAY,OACZ,cAAAjD,GAAgB,IAChB,UAAAkD,EAAY,GACZ,UAAAC,EAAY,GACZ,UAAAC,GAAY,GACZ,OAAAC,GAAS,kBACT,cAAAC,GAAgB,4BAChB,gBAAiBC,EACjB,KAAAC,EAAO,CACL,wBAAyB,QACzB,kBAAmB,WACrB,CACF,EAAIpD,EAIEO,EAASZ,EAAsBC,EAAa,EAE5C,CAACyD,GAAaC,EAAa,EAAI5E,GAASiD,CAAY,EACpDrB,EACJ,OAAOuB,GAAmB,YAAcwB,GAAcxB,EAElD0B,EAAyB9E,GAC5B+E,GAAkB,CACjBF,GAAcE,CAAK,EAEnB1B,GAAA,MAAAA,EAAqB0B,EACvB,EACA,CAAC1B,CAAkB,CACrB,EAGM2B,GAA0BhF,GAC7BiF,GAAqE,CACpE,IAAMF,EAAQE,EAAM,OAAO,MAC3BH,EAAuBC,CAAK,CAC9B,EACA,CAACD,CAAsB,CACzB,EAEMI,GAAeC,EAAcd,EAAW7C,CAAK,EAC/CA,EAAM,OACJ,CAAC4D,EAAiBC,IAAqB,CAAC,GAAGD,EAAK,GAAGC,EAAM,OAAO,EAChE,CAAC,CACH,EACA7D,EAEE8D,GAAqBH,EAAcd,EAAW7C,CAAK,EACrDA,EAAM,MAAO6D,GAAqBA,EAAM,QAAQ,SAAW,CAAC,EAC5D7D,EAAM,SAAW,EAEf,CACJ,iBAAA+D,GACA,cAAAC,GACA,aAAA7D,EACA,aAAA8D,GACA,qBAAAC,GACA,iBAAAhE,EACA,OAAAiE,GACA,SAAAC,GACA,WAAAC,EACF,EAAI1F,EAAY,CACd,OAAQwC,EACR,eAAgB,CAAC,CAAE,OAAAgD,CAAO,IAAM,CAC1BA,EACF9C,GAAA,MAAAA,IAEAD,GAAA,MAAAA,GAEJ,EACA,aAAc,CAACkD,EAAO,CAAE,KAAAC,EAAM,QAAAC,CAAQ,IAAM,CAC1C,OAAQD,EAAM,CACZ,KAAK5F,EAAY,iBAAiB,UAEhC,OAAOgC,EAAAC,EAAA,GAAK4D,GAAL,CAAc,WAAYF,EAAM,UAAW,GAIpD,KAAK3F,EAAY,iBAAiB,kBAClC,KAAKA,EAAY,iBAAiB,UAEhC,OAAK8C,EAOE+C,EANE7D,EAAAC,EAAA,GACF4D,GADE,CAEL,OAAQF,EAAM,MAChB,GAKJ,QACE,OAAOE,CACX,CACF,EACA,MAAOd,GACP,aAAA/B,EACA,WAAAtB,EACA,aAAA6B,GACA,mBAAoB,CAAC,CAAE,KAAAqC,EAAM,WAAAlE,CAAW,IAAM,CAC5C,OAAQkE,EAAM,CAEZ,KAAK5F,EAAY,iBAAiB,YAChC,OAIF,KAAKA,EAAY,iBAAiB,UAClC,KAAKA,EAAY,iBAAiB,kBAAmB,CAEnD,GAAI6C,IAAsB,QAAS,CACjC8B,EAAuB,EAAE,EACzB,MACF,CAGA,GAAI9B,IAAsB,WACxB,MAEJ,CACF,CAEA8B,EAAuBjD,CAAU,CACnC,EACA,cAAe,CAAC,CAAE,KAAAkE,EAAM,aAAA5C,CAAa,IAAM,CACzC,OAAQ4C,EAAM,CACZ,KAAK5F,EAAY,iBAAiB,kBAClC,KAAKA,EAAY,iBAAiB,UAC5BgD,GACFG,GAAaH,CAAY,EAE3B,MACF,QACE,KACJ,CACF,CACF,CAAC,EAMG8C,EAAAT,GAAc,EAHhB,mBAAmBU,GACnB,GAAIC,EAzWR,EA2WMF,EADCG,EAAAC,EACDJ,EADC,CAFH,kBACA,OAGIK,GAAgBf,GAAiB,EACjCgB,EAAcb,GAAqB,EACnCc,EAAYf,GAAa,EAC3BhE,EAAoB,EAElBgF,EAAkB/B,GAAA,KAAAA,EAAuB7C,EAE/C,OACE9B,EAAA,cAAC,OACC,eAAcyE,GACd,UAAWtE,EAAG4B,EAAO,aAAciB,CAAS,EAC5C,IAAKL,GAEL3C,EAAA,cAACW,EAAA,CACC,UAAW6D,GACX,OAAQoB,GACR,YAAavB,KAAc,OAC3B,mBAAoB,GAIpB,UAAW,GACX,GAAIoC,EAAU,IAEdzG,EAAA,cAACW,EAAQ,QAAR,KACCX,EAAA,cAAC,MAAAoC,EAAAC,EAAA,GAAQkE,IAAR,CAAuB,UAAWxE,EAAO,WACxC/B,EAAA,cAACO,GAAA6B,EAAAC,EAAA,CACC,UAAWN,EAAO,YACdsE,GAFL,CAGC,QAAU,GAAM,CACd7C,GAAA,MAAAA,EAAU,GACVqC,GAAS,CACX,EACA,OAAS,GAAM,CACbpC,GAAA,MAAAA,EAAS,GACT4C,EAAW,OAAO,CAAC,CACrB,EACA,GAAItD,EACJ,UAAWa,GACX,WAAYtC,EACZ,WAAYuC,GACZ,WAAYC,GACZ,IAAKzD,EAAUgG,EAAW,IAAKnC,EAAQ,EACvC,OAAO,wBACP,YAAaD,GACb,SAAWiB,GAAU,CACnBmB,EAAW,SAASnB,CAAK,EACzBD,GAAwBC,CAAK,CAC/B,GACF,EACAlF,EAAA,cAACM,GAAA8B,EAAAC,EAAA,GACKmE,GADL,CAEC,IAAKnG,EAAUmG,EAAY,IAAKrC,EAAS,EACzC,aACEuC,EACI9B,EAAK,wBACLA,EAAK,kBAEX,UAAW7C,EAAO,aAClB,QAAQ,cACR,KAAM2E,EAAkB1G,EAAA,cAACQ,GAAA,CAAM,MAAOS,GAAO,QAAS,EAAKyC,GAC3D,QAAS,IAAM,CACTgD,EACF3B,EAAuB,EAAE,EAEzBe,GAAW,CAEf,EACA,WAAYxE,EACZ,KAAK,SACP,CACF,CACF,EAECG,EAAM,OAAS,GAAKK,EAAW,OAAS,GAAKiC,GAC5C/D,EAAA,cAACW,EAAQ,QAARyB,EAAAC,EAAA,GACKoE,GADL,CAEC,IAAKpG,EAAUoG,EAAU,IAAKrC,EAAO,EACrC,UAAWrC,EAAO,QAClB,OAAQ2C,KAEPH,GACC,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,IAAI,CAACoC,EAAG1E,IACpBjC,EAAA,cAAC,OACC,IAAKiC,EACL,UAAW9B,EAAG4B,EAAO,KAAK,CAAE,WAAY,EAAK,CAAC,CAAC,GAE/C/B,EAAA,cAAC4G,GAAA,IAAqB,CACxB,CACD,EAEF,CAACrC,GAAagB,IACbvF,EAAA,cAAC,OAAI,UAAW+B,EAAO,KAAK,CAAC,CAAC,GAC5B/B,EAAA,cAACY,GAAA,CAAW,UAAWmB,EAAO,gBAC3BiC,EACH,CACF,EAGD,CAACO,GACAa,EAAcd,EAAW7C,CAAK,GAC9BA,EAAM,IAAI,CAAC6D,EAAkBrD,IAAkB,CAC7C,GAAIqD,EAAM,QAAQ,OAAS,EACzB,OAEF,IAAMuB,EACJ7G,EAAA,cAAC,OAAI,IAAKiC,GACRjC,EAAA,cAACa,GAAA,CACC,IAAKoB,EACL,eAAa,6BACb,aAAa,OACb,UAAWF,EAAO,YAEjBuD,EAAM,UACT,EACAtF,EAAA,cAACuB,EAAA,CACC,MAAO+D,EAAM,QACb,iBAAkB3D,EAClB,aAAcC,EACd,WAAYC,EACZ,WAAYC,EACZ,kBAAmBJ,EACrB,CACF,EAEF,OAAAA,GAAqB4D,EAAM,QAAQ,OAC5BuB,CACT,CAAC,EAEF,CAACtC,GACA,CAACa,EAAcd,EAAW7C,CAAK,GAC/BA,EAAM,OAAS,GACbzB,EAAA,cAACuB,EAAA,CACC,MAAOE,EACP,kBAAmBC,EACnB,iBAAkBC,EAClB,aAAcC,EACd,WAAYC,EACZ,WAAYC,EACd,CAEN,EAIA9B,EAAA,cAAC,MAAAoC,EAAAC,EAAA,GAAQoE,GAAR,CAAmB,UAAWtG,EAAG4B,EAAO,MAAM,GAAG,CAEtD,CACF,CAEJ,CAEA,IAAM6E,GAAuB,IAEzB5G,EAAA,cAACU,GAAS,UAAT,CAAmB,UAAW,IAC7BV,EAAA,cAACU,GAAS,SAAT,CAAkB,cAAe,EAAG,CACvC,EAKJ,SAAS0E,EACPd,EACA7C,EACuC,CACvC,OAAO6C,CACT,CAOO,IAAMwC,GAAe9G,EAAM,WAAW0C,EAAa","sourcesContent":["import React, { useCallback, useState } from 'react';\nimport { cx } from 'emotion';\nimport { useCombobox } from 'downshift';\n\nimport {\n mergeRefs,\n type CommonProps,\n type ExpandProps,\n} from '@contentful/f36-core';\nimport { IconButton } from '@contentful/f36-button';\nimport { TextInput, type TextInputProps } from '@contentful/f36-forms';\nimport { XIcon, CaretDownIcon } from '@contentful/f36-icons';\nimport { Skeleton } from '@contentful/f36-skeleton';\nimport { Popover } from '@contentful/f36-popover';\nimport { Subheading, SectionHeading } from '@contentful/f36-typography';\n\nimport { AutocompleteItems } from './AutocompleteItems';\nimport { getAutocompleteStyles } from './Autocomplete.styles';\nimport tokens from '@contentful/f36-tokens';\n\nexport interface GenericGroupType<ItemType> {\n groupTitle: string;\n options: ItemType[];\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport interface AutocompleteProps<ItemType>\n extends CommonProps,\n Pick<\n TextInputProps,\n | 'isDisabled'\n | 'isInvalid'\n | 'isReadOnly'\n | 'isRequired'\n | 'id'\n | 'defaultValue'\n > {\n /**\n * It’s an array of data to be used as \"options\" by the autocomplete component.\n * This can either be a plain list of items or a list of groups of items.\n */\n items: ItemType[] | GenericGroupType<ItemType>[];\n\n /**\n * Boolean to control whether the Autocomplete menu is open\n */\n isOpen?: boolean;\n\n /**\n * Callback fired when the Autocomplete menu opens\n */\n onOpen?: () => void;\n\n /**\n * Callback fired when the Autocomplete menu closes\n */\n onClose?: () => void;\n\n /**\n * Set a custom icon for the text input\n */\n icon?: React.ReactElement;\n\n /**\n * Tells if the item is a object with groups\n */\n isGrouped?: boolean;\n\n /**\n * Set the value of the text input\n */\n inputValue?: string;\n /**\n * Function called whenever the input value changes\n */\n onInputValueChange?: (value: string) => void;\n /**\n * This is the function that will be called when the user selects one of the \"options\" in the list.\n * The component will pass the selected \"item\" as an argument to the function..\n */\n onSelectItem: (item: ItemType) => void;\n\n /**\n * Applying the selectedItem property turns autocomplete into a controlled component.\n * Can be used to display e.g. previously selected element. If it is an object the itemToString function will apply to it.\n */\n selectedItem?: ItemType;\n\n /**\n * This is the function that will be called for each \"item\" passed in the `items` prop.\n * It receives the \"item\" and \"inputValue\" as arguments and returns a ReactNode.\n * The inputValue is passed in case you want to highlight the match on the render.\n */\n renderItem?: (item: ItemType, inputValue: string) => React.ReactNode;\n /**\n * When using objects as `items`, we recommend passing a function that tells Downshift how to extract a string\n * from those objetcs to be used as inputValue\n */\n itemToString?: (item: ItemType) => string;\n /**\n * Text input behaviour after an item is selected\n * @default \"replace\"\n */\n textOnAfterSelect?: 'clear' | 'preserve' | 'replace';\n /**\n * If this is set to `false` the dropdown menu will stay open after selecting an item\n * @default true\n */\n closeAfterSelect?: boolean;\n /**\n * This is the value will be passed to the `placeholder` prop of the input.\n * @default \"Search\"\n */\n placeholder?: string;\n /**\n * Defines if the list should be shown even if empty, when input is focused\n * @default false\n */\n showEmptyList?: boolean;\n /**\n * A message that will be shown when it is not possible to find any option that matches the input value\n * @default \"No matches\"\n */\n noMatchesMessage?: string;\n /**\n * Use this prop to get a ref to the input element of the component\n */\n inputRef?: React.Ref<HTMLInputElement>;\n /**\n * Use this prop to get a ref to the toggle button of the component\n */\n toggleRef?: React.Ref<HTMLButtonElement>;\n /**\n * Use this prop to get a ref to the list of items of the component\n */\n listRef?: React.Ref<HTMLUListElement>;\n /**\n * It sets the width of the list\n * @default \"auto\"\n */\n listWidth?: 'auto' | 'full';\n /**\n * It sets the max-height, in pixels, of the list\n * The default value is the height of 5 single line items\n * @default 180\n */\n listMaxHeight?: number;\n /**\n * Sets the list to show its loading state\n * @default false\n */\n isLoading?: boolean;\n /**\n * Boolean to control whether or not to render the suggestions box in a React Portal.\n * Rendering content inside a Portal allows the suggestions box to escape the bounds\n * of its parent while still being positioned correctly.\n * Defaults to `false`\n */\n usePortal?: boolean;\n\n /**\n * A [data-test-id] attribute for the suggestions box used for testing purposes\n */\n popoverTestId?: string;\n\n /**\n * Function called when the input is focused\n *\n * @param event\n */\n onFocus?: (event: React.FocusEvent<HTMLInputElement>) => void;\n /**\n * Function called when the input is blurred\n * @param event\n */\n onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void;\n /**\n * Manually control when the button to clear the input value is shown\n */\n showClearButton?: boolean;\n /**\n * Additional aria attributes\n */\n aria?: {\n showListIconLabel?: string;\n clearSelectionIconLabel?: string;\n };\n}\n\nfunction _Autocomplete<ItemType>(\n props: AutocompleteProps<ItemType>,\n ref: React.Ref<HTMLDivElement>,\n) {\n const {\n isOpen: isOpenProp,\n onClose,\n onOpen,\n id,\n className,\n textOnAfterSelect = 'replace',\n closeAfterSelect = true,\n defaultValue = '',\n selectedItem,\n items,\n inputValue: inputValueProp,\n onInputValueChange,\n onSelectItem,\n onFocus,\n onBlur,\n renderItem,\n icon = <CaretDownIcon color={tokens.gray600} />,\n itemToString = (item: ItemType) => item as unknown as string,\n isInvalid,\n isDisabled,\n isRequired,\n isReadOnly,\n showEmptyList,\n noMatchesMessage = 'No matches found',\n placeholder = 'Search',\n inputRef,\n toggleRef,\n listRef,\n listWidth = 'auto',\n listMaxHeight = 180,\n isGrouped = false,\n isLoading = false,\n usePortal = false,\n testId = 'cf-autocomplete',\n popoverTestId = 'cf-autocomplete-container',\n showClearButton: showClearButtonProp,\n aria = {\n clearSelectionIconLabel: 'Clear',\n showListIconLabel: 'Show list',\n },\n } = props;\n\n type GroupType = GenericGroupType<ItemType>;\n\n const styles = getAutocompleteStyles(listMaxHeight);\n\n const [_inputValue, setInputValue] = useState(defaultValue);\n const inputValue =\n typeof inputValueProp === 'undefined' ? _inputValue : inputValueProp;\n\n const handleInputValueChange = useCallback(\n (value: string) => {\n setInputValue(value);\n\n onInputValueChange?.(value);\n },\n [onInputValueChange],\n );\n\n // Handle manually to avoid a jumping cursor, see https://github.com/downshift-js/downshift/issues/1108#issuecomment-842407759\n const handleNativeChangeEvent = useCallback(\n (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {\n const value = event.target.value;\n handleInputValueChange(value);\n },\n [handleInputValueChange],\n );\n\n const flattenItems = isUsingGroups(isGrouped, items)\n ? items.reduce(\n (acc: ItemType[], group: GroupType) => [...acc, ...group.options],\n [],\n )\n : items;\n\n const isShowingNoMatches = isUsingGroups(isGrouped, items)\n ? items.every((group: GroupType) => group.options.length === 0)\n : items.length === 0;\n\n const {\n getComboboxProps,\n getInputProps,\n getItemProps,\n getMenuProps,\n getToggleButtonProps,\n highlightedIndex,\n isOpen,\n openMenu,\n toggleMenu,\n } = useCombobox({\n isOpen: isOpenProp,\n onIsOpenChange: ({ isOpen }) => {\n if (isOpen) {\n onOpen?.();\n } else {\n onClose?.();\n }\n },\n stateReducer: (state, { type, changes }) => {\n switch (type) {\n case useCombobox.stateChangeTypes.InputBlur: {\n // don't change input value on blur\n return { ...changes, inputValue: state.inputValue };\n }\n\n // item is selected by click or keydown\n case useCombobox.stateChangeTypes.InputKeyDownEnter:\n case useCombobox.stateChangeTypes.ItemClick: {\n // prevent the menu from being closed when the user selects an item with a keyboard or mouse\n if (!closeAfterSelect) {\n return {\n ...changes,\n isOpen: state.isOpen,\n };\n }\n\n return changes;\n }\n default:\n return changes;\n }\n },\n items: flattenItems,\n selectedItem,\n inputValue,\n itemToString,\n onInputValueChange: ({ type, inputValue }) => {\n switch (type) {\n // value is set directly from the TextInput onChange handler\n case useCombobox.stateChangeTypes.InputChange: {\n return;\n }\n\n // item is selected by click or keydown\n case useCombobox.stateChangeTypes.ItemClick:\n case useCombobox.stateChangeTypes.InputKeyDownEnter: {\n // clear the TextInput value\n if (textOnAfterSelect === 'clear') {\n handleInputValueChange('');\n return;\n }\n\n // keep the current TextInput value\n if (textOnAfterSelect === 'preserve') {\n return;\n }\n }\n }\n\n handleInputValueChange(inputValue);\n },\n onStateChange: ({ type, selectedItem }) => {\n switch (type) {\n case useCombobox.stateChangeTypes.InputKeyDownEnter:\n case useCombobox.stateChangeTypes.ItemClick:\n if (selectedItem) {\n onSelectItem(selectedItem);\n }\n break;\n default:\n break;\n }\n },\n });\n\n const {\n 'aria-labelledby': _labelledby,\n id: _inputId,\n ...inputProps\n } = getInputProps();\n const comboboxProps = getComboboxProps();\n const toggleProps = getToggleButtonProps();\n const menuProps = getMenuProps();\n let elementStartIndex = 0;\n\n const showClearButton = showClearButtonProp ?? inputValue;\n\n return (\n <div\n data-test-id={testId}\n className={cx(styles.autocomplete, className)}\n ref={ref}\n >\n <Popover\n usePortal={usePortal}\n isOpen={isOpen}\n isFullWidth={listWidth === 'full'}\n renderOnlyWhenOpen={false}\n // This is necessary, otherwise the focus will change from the input to the Popover\n // and the user won't be able to type in the input\n // eslint-disable-next-line jsx-a11y/no-autofocus\n autoFocus={false}\n id={menuProps.id}\n >\n <Popover.Trigger>\n <div {...comboboxProps} className={styles.combobox}>\n <TextInput\n className={styles.inputField}\n {...inputProps}\n onFocus={(e) => {\n onFocus?.(e as React.FocusEvent<HTMLInputElement>);\n openMenu();\n }}\n onBlur={(e) => {\n onBlur?.(e as React.FocusEvent<HTMLInputElement>);\n inputProps.onBlur(e);\n }}\n id={id}\n isInvalid={isInvalid}\n isDisabled={isDisabled}\n isRequired={isRequired}\n isReadOnly={isReadOnly}\n ref={mergeRefs(inputProps.ref, inputRef)}\n testId=\"cf-autocomplete-input\"\n placeholder={placeholder}\n onChange={(event) => {\n inputProps.onChange(event);\n handleNativeChangeEvent(event);\n }}\n />\n <IconButton\n {...toggleProps}\n ref={mergeRefs(toggleProps.ref, toggleRef)}\n aria-label={\n showClearButton\n ? aria.clearSelectionIconLabel\n : aria.showListIconLabel\n }\n className={styles.toggleButton}\n variant=\"transparent\"\n icon={showClearButton ? <XIcon color={tokens.gray600} /> : icon}\n onClick={() => {\n if (showClearButton) {\n handleInputValueChange('');\n } else {\n toggleMenu();\n }\n }}\n isDisabled={isDisabled}\n size=\"small\"\n />\n </div>\n </Popover.Trigger>\n\n {items.length > 0 || inputValue.length > 0 || showEmptyList ? (\n <Popover.Content\n {...menuProps}\n ref={mergeRefs(menuProps.ref, listRef)}\n className={styles.content}\n testId={popoverTestId}\n >\n {isLoading &&\n [...Array(3)].map((_, index) => (\n <div\n key={index}\n className={cx(styles.item({ isDisabled: true }))}\n >\n <ListItemLoadingState />\n </div>\n ))}\n\n {!isLoading && isShowingNoMatches && (\n <div className={styles.item({})}>\n <Subheading className={styles.noMatchesTitle}>\n {noMatchesMessage}\n </Subheading>\n </div>\n )}\n\n {!isLoading &&\n isUsingGroups(isGrouped, items) &&\n items.map((group: GroupType, index: number) => {\n if (group.options.length < 1) {\n return;\n }\n const render = (\n <div key={index}>\n <SectionHeading\n key={index}\n data-test-id=\"cf-autocomplete-grouptitle\"\n marginBottom=\"none\"\n className={styles.groupTitle}\n >\n {group.groupTitle}\n </SectionHeading>\n <AutocompleteItems<ItemType>\n items={group.options}\n highlightedIndex={highlightedIndex}\n getItemProps={getItemProps}\n renderItem={renderItem}\n inputValue={inputValue}\n elementStartIndex={elementStartIndex}\n />\n </div>\n );\n elementStartIndex += group.options.length;\n return render;\n })}\n\n {!isLoading &&\n !isUsingGroups(isGrouped, items) &&\n items.length > 0 && (\n <AutocompleteItems<ItemType>\n items={items}\n elementStartIndex={elementStartIndex}\n highlightedIndex={highlightedIndex}\n getItemProps={getItemProps}\n renderItem={renderItem}\n inputValue={inputValue}\n />\n )}\n </Popover.Content>\n ) : (\n // We need to render an empty hidden div, so we can pass the menuProps or downshift will show a warning about it\n // https://github.com/downshift-js/downshift/issues/1167#issuecomment-1088022842\n <div {...menuProps} className={cx(styles.hidden)} />\n )}\n </Popover>\n </div>\n );\n}\n\nconst ListItemLoadingState = () => {\n return (\n <Skeleton.Container svgHeight={16}>\n <Skeleton.BodyText numberOfLines={1} />\n </Skeleton.Container>\n );\n};\n\n// This is required to infer correct typings when differentiating groups and items\nfunction isUsingGroups<ItemType>(\n isGrouped: boolean,\n items: ItemType[] | GenericGroupType<ItemType>[],\n): items is GenericGroupType<ItemType>[] {\n return isGrouped;\n}\n\n/**\n * The Autocomplete is a component that will show a `TextInput` where a user can type any word which will be used\n * to filter a list of items. That list of filtered items will be shown to the user as possible options for the input.\n * Once one of the options is selected, that option becomes the value of the `TextInput`.\n */\nexport const Autocomplete = React.forwardRef(_Autocomplete) as <T>(\n props: ExpandProps<AutocompleteProps<T>> & {\n ref?: React.Ref<HTMLDivElement>;\n },\n) => ReturnType<typeof _Autocomplete>;\n","import React, { HTMLAttributes } from 'react';\nimport { cx } from 'emotion';\nimport { getStringMatch } from '@contentful/f36-utils';\nimport type { UseComboboxGetItemPropsOptions } from 'downshift';\nimport { Text } from '@contentful/f36-typography';\n\nimport { getAutocompleteStyles } from './Autocomplete.styles';\n\ninterface AutocompleteItemsProps<ItemType> {\n items: ItemType[];\n elementStartIndex: number;\n highlightedIndex: number;\n getItemProps: (\n options: UseComboboxGetItemPropsOptions<ItemType>,\n ) => HTMLAttributes<HTMLLIElement>;\n renderItem: (item: ItemType, inputValue: string) => React.ReactNode;\n inputValue: string;\n listMaxHeight?: number;\n}\n\nexport const AutocompleteItems = <ItemType,>(\n props: AutocompleteItemsProps<ItemType>,\n) => {\n const {\n items,\n elementStartIndex,\n highlightedIndex,\n getItemProps,\n renderItem,\n inputValue,\n listMaxHeight = 180,\n } = props;\n\n const styles = getAutocompleteStyles(listMaxHeight);\n\n return (\n <ul className={styles.list} data-test-id=\"cf-autocomplete-list\">\n {items.map((item: ItemType, index: number) => {\n const itemIndex = elementStartIndex + index;\n const itemProps = getItemProps({ item, index: itemIndex });\n return (\n <Text\n {...itemProps}\n as=\"li\"\n key={itemIndex}\n className={cx([\n styles.item({}),\n highlightedIndex === itemIndex && styles.highlighted,\n ])}\n data-test-id={`cf-autocomplete-list-item-${itemIndex}`}\n >\n {renderItem ? (\n renderItem(item, inputValue)\n ) : typeof item === 'string' ? (\n <HighlightedItem item={item} inputValue={inputValue} />\n ) : (\n item\n )}\n </Text>\n );\n })}\n </ul>\n );\n};\n\nAutocompleteItems.displayName = 'AutocompleteItems';\n\nfunction HighlightedItem({\n item,\n inputValue,\n}: {\n item: string;\n inputValue: string;\n}) {\n const { before, match, after } = getStringMatch(item, inputValue);\n\n return (\n <>\n {before}\n <b>{match}</b>\n {after}\n </>\n );\n}\n\nHighlightedItem.displayName = 'HighlightedItem';\n","import { css, cx } from 'emotion';\nimport tokens from '@contentful/f36-tokens';\nimport { getMenuItemStyles } from '@contentful/f36-core';\n\nexport const getAutocompleteStyles = (listMaxHeight: number) => ({\n autocomplete: css({\n position: 'relative',\n width: '100%',\n }),\n combobox: css({\n position: 'relative',\n }),\n inputField: css({\n paddingRight: tokens.spacingXl,\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }),\n toggleButton: css({\n position: 'absolute',\n top: '1px',\n right: '1px',\n zIndex: 1,\n padding: tokens.spacing2Xs,\n minHeight: 'calc(100% - 2px)',\n }),\n content: css({\n overflow: 'auto',\n maxHeight: `${listMaxHeight}px`,\n }),\n list: css({\n listStyle: 'none',\n padding: `${tokens.spacingXs} ${tokens.spacing2Xs}`,\n margin: 0,\n }),\n groupTitle: css({\n padding: `${tokens.spacingXs} ${tokens.spacingM}`,\n lineHeight: tokens.lineHeightM,\n }),\n noMatchesTitle: css({\n color: tokens.gray500,\n margin: `${tokens.spacingM} 0 ${tokens.spacingM} 0`,\n }),\n item: ({\n isActive = false,\n isDisabled = false,\n }: {\n isActive?: boolean;\n isDisabled?: boolean;\n }) => cx(getMenuItemStyles({ isActive, isDisabled })),\n highlighted: css({\n backgroundColor: tokens.gray100,\n }),\n hidden: css({\n display: 'none',\n }),\n});\n"]}
|
package/package.json
CHANGED
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/f36-autocomplete",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"description": "Forma 36: Autocomplete component",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "tsup"
|
|
7
7
|
},
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@contentful/f36-button": "^5.
|
|
10
|
-
"@contentful/f36-core": "^5.
|
|
11
|
-
"@contentful/f36-forms": "^5.
|
|
12
|
-
"@contentful/f36-
|
|
13
|
-
"@contentful/f36-
|
|
14
|
-
"@contentful/f36-
|
|
15
|
-
"@contentful/f36-tokens": "^
|
|
16
|
-
"@contentful/f36-typography": "^5.
|
|
17
|
-
"@contentful/f36-utils": "^5.
|
|
9
|
+
"@contentful/f36-button": "^5.1.0",
|
|
10
|
+
"@contentful/f36-core": "^5.1.0",
|
|
11
|
+
"@contentful/f36-forms": "^5.1.0",
|
|
12
|
+
"@contentful/f36-popover": "^5.1.0",
|
|
13
|
+
"@contentful/f36-skeleton": "^5.1.0",
|
|
14
|
+
"@contentful/f36-icons": "^5.1.0",
|
|
15
|
+
"@contentful/f36-tokens": "^5.1.0",
|
|
16
|
+
"@contentful/f36-typography": "^5.1.0",
|
|
17
|
+
"@contentful/f36-utils": "^5.1.0",
|
|
18
18
|
"downshift": "^6.1.12",
|
|
19
19
|
"emotion": "^10.0.17"
|
|
20
20
|
},
|
|
21
21
|
"peerDependencies": {
|
|
22
|
-
"react": ">=16.8"
|
|
22
|
+
"react": ">=16.8",
|
|
23
|
+
"react-dom": ">=16.8"
|
|
23
24
|
},
|
|
24
25
|
"license": "MIT",
|
|
25
26
|
"files": [
|
|
@@ -33,9 +34,10 @@
|
|
|
33
34
|
"browserslist": "extends @contentful/browserslist-config",
|
|
34
35
|
"repository": {
|
|
35
36
|
"type": "git",
|
|
36
|
-
"url": "https://github.com/contentful/forma-36"
|
|
37
|
+
"url": "git+https://github.com/contentful/forma-36.git"
|
|
37
38
|
},
|
|
38
39
|
"publishConfig": {
|
|
40
|
+
"registry": "https://npm.pkg.github.com/",
|
|
39
41
|
"access": "public"
|
|
40
42
|
}
|
|
41
43
|
}
|