@@ -15,17 +15,27 @@ import { isFeatureEnabled } from "@webstudio-is/feature-flags";
1515import { PlusIcon } from "@webstudio-is/icons" ;
1616import {
1717 Box ,
18- Combobox ,
18+ ComboboxAnchor ,
19+ ComboboxContent ,
20+ ComboboxItemDescription ,
21+ ComboboxListbox ,
22+ ComboboxListboxItem ,
23+ ComboboxRoot ,
24+ ComboboxScrollArea ,
1925 Flex ,
26+ InputField ,
2027 Label ,
28+ NestedInputButton ,
2129 SectionTitle ,
2230 SectionTitleButton ,
2331 SectionTitleLabel ,
2432 Text ,
2533 theme ,
2634 Tooltip ,
35+ useCombobox ,
2736} from "@webstudio-is/design-system" ;
2837import {
38+ parseCss ,
2939 properties as propertiesData ,
3040 propertyDescriptions ,
3141} from "@webstudio-is/css-data" ;
@@ -41,7 +51,11 @@ import {
4151} from "~/builder/shared/collapsible-section" ;
4252import { CssValueInputContainer } from "../../shared/css-value-input" ;
4353import { styleConfigByName } from "../../shared/configs" ;
44- import { deleteProperty , setProperty } from "../../shared/use-style-data" ;
54+ import {
55+ createBatchUpdate ,
56+ deleteProperty ,
57+ setProperty ,
58+ } from "../../shared/use-style-data" ;
4559import {
4660 $availableVariables ,
4761 $matchingBreakpoints ,
@@ -125,12 +139,48 @@ const matchOrSuggestToCreate = (
125139 return matched ;
126140} ;
127141
142+ const getNewPropertyDescription = ( item : null | SearchItem ) => {
143+ let description = `Create CSS variable.` ;
144+ if ( item && item . value in propertyDescriptions ) {
145+ description =
146+ propertyDescriptions [ item . value as keyof typeof propertyDescriptions ] ;
147+ }
148+ return < Box css = { { width : theme . spacing [ 28 ] } } > { description } </ Box > ;
149+ } ;
150+
151+ const insertStyles = ( text : string ) => {
152+ const parsedStyles = parseCss ( `selector{${ text } }` , {
153+ customProperties : true ,
154+ } ) ;
155+ if ( parsedStyles . length === 0 ) {
156+ return false ;
157+ }
158+ const batch = createBatchUpdate ( ) ;
159+ for ( const { property, value } of parsedStyles ) {
160+ batch . setProperty ( property ) ( value ) ;
161+ }
162+ batch . publish ( { listed : true } ) ;
163+ return true ;
164+ } ;
165+
166+ /**
167+ *
168+ * Advanced search control supports following interactions
169+ *
170+ * find property
171+ * create custom property
172+ * submit css declarations
173+ * paste css declarations
174+ *
175+ */
128176const AdvancedSearch = ( {
129177 usedProperties,
130178 onSelect,
179+ onClose,
131180} : {
132181 usedProperties : string [ ] ;
133182 onSelect : ( value : StyleProperty ) => void ;
183+ onClose : ( ) => void ;
134184} ) => {
135185 const availableProperties = useMemo ( ( ) => {
136186 const properties = Object . keys ( propertiesData ) . sort (
@@ -152,31 +202,63 @@ const AdvancedSearch = ({
152202 label : "" ,
153203 } ) ;
154204
205+ const combobox = useCombobox < SearchItem > ( {
206+ getItems : ( ) => availableProperties ,
207+ itemToString : ( item ) => item ?. label ?? "" ,
208+ value : item ,
209+ defaultHighlightedIndex : 0 ,
210+ getItemProps : ( ) => ( { text : "sentence" } ) ,
211+ match : matchOrSuggestToCreate ,
212+ onChange : ( value ) => setItem ( { value : value ?? "" , label : value ?? "" } ) ,
213+ onItemSelect : ( item ) => onSelect ( item . value as StyleProperty ) ,
214+ } ) ;
215+
216+ const descriptionItem = combobox . items [ combobox . highlightedIndex ] ;
217+ const description = getNewPropertyDescription ( descriptionItem ) ;
218+ const descriptions = combobox . items . map ( getNewPropertyDescription ) ;
219+
155220 return (
156- < Combobox
157- autoFocus
158- placeholder = "Find or create a property"
159- getItems = { ( ) => availableProperties }
160- defaultHighlightedIndex = { 0 }
161- value = { item }
162- itemToString = { ( item ) => item ?. label ?? "" }
163- getItemProps = { ( ) => ( { text : "sentence" } ) }
164- getDescription = { ( item ) => {
165- let description = `Create CSS variable.` ;
166- if ( item && item . value in propertyDescriptions ) {
167- description =
168- propertyDescriptions [
169- item . value as keyof typeof propertyDescriptions
170- ] ;
171- }
172- return < Box css = { { width : theme . spacing [ 28 ] } } > { description } </ Box > ;
173- } }
174- match = { matchOrSuggestToCreate }
175- onChange = { ( value ) => {
176- setItem ( { value : value ?? "" , label : value ?? "" } ) ;
177- } }
178- onItemSelect = { ( item ) => onSelect ( item . value as StyleProperty ) }
179- />
221+ < ComboboxRoot open = { combobox . isOpen } >
222+ < form
223+ { ...combobox . getComboboxProps ( ) }
224+ onSubmit = { ( event ) => {
225+ event . preventDefault ( ) ;
226+ const isInserted = insertStyles ( item . value ) ;
227+ if ( isInserted ) {
228+ onClose ( ) ;
229+ }
230+ } }
231+ >
232+ < input type = "submit" hidden />
233+ < ComboboxAnchor >
234+ < InputField
235+ { ...combobox . getInputProps ( ) }
236+ autoFocus = { true }
237+ placeholder = "Add styles"
238+ suffix = { < NestedInputButton { ...combobox . getToggleButtonProps ( ) } /> }
239+ />
240+ </ ComboboxAnchor >
241+ < ComboboxContent >
242+ < ComboboxListbox { ...combobox . getMenuProps ( ) } >
243+ < ComboboxScrollArea >
244+ { combobox . items . map ( ( item , index ) => (
245+ < ComboboxListboxItem
246+ { ...combobox . getItemProps ( { item, index } ) }
247+ key = { index }
248+ >
249+ { item . label }
250+ </ ComboboxListboxItem >
251+ ) ) }
252+ </ ComboboxScrollArea >
253+ { description && (
254+ < ComboboxItemDescription descriptions = { descriptions } >
255+ { description }
256+ </ ComboboxItemDescription >
257+ ) }
258+ </ ComboboxListbox >
259+ </ ComboboxContent >
260+ </ form >
261+ </ ComboboxRoot >
180262 ) ;
181263} ;
182264
@@ -438,6 +520,7 @@ export const Section = () => {
438520 { listed : true }
439521 ) ;
440522 } }
523+ onClose = { ( ) => setIsAdding ( false ) }
441524 />
442525 ) }
443526 < Box >
0 commit comments