@@ -38,9 +38,12 @@ import {
38
38
import {
39
39
parseCss ,
40
40
properties as propertiesData ,
41
+ keywordValues ,
41
42
propertyDescriptions ,
43
+ parseCssValue ,
42
44
} from "@webstudio-is/css-data" ;
43
45
import {
46
+ cssWideKeywords ,
44
47
hyphenateProperty ,
45
48
toValue ,
46
49
type StyleProperty ,
@@ -117,7 +120,43 @@ const AdvancedStyleSection = (props: {
117
120
) ;
118
121
} ;
119
122
120
- type SearchItem = { value : string ; label : string } ;
123
+ type SearchItem = { property : string ; label : string ; value ?: string } ;
124
+
125
+ const autoCompleteItems : Array < SearchItem > = [ ] ;
126
+
127
+ const getAutocompleteItems = ( ) => {
128
+ if ( autoCompleteItems . length > 0 ) {
129
+ return autoCompleteItems ;
130
+ }
131
+ for ( const property in propertiesData ) {
132
+ autoCompleteItems . push ( {
133
+ property,
134
+ label : hyphenateProperty ( property ) ,
135
+ } ) ;
136
+ }
137
+
138
+ const ignoreValues = new Set ( [ ...cssWideKeywords , ...keywordValues . color ] ) ;
139
+
140
+ for ( const property in keywordValues ) {
141
+ const values = keywordValues [ property as keyof typeof keywordValues ] ;
142
+ for ( const value of values ) {
143
+ if ( ignoreValues . has ( value ) ) {
144
+ continue ;
145
+ }
146
+ autoCompleteItems . push ( {
147
+ property,
148
+ value,
149
+ label : `${ hyphenateProperty ( property ) } : ${ value } ` ,
150
+ } ) ;
151
+ }
152
+ }
153
+
154
+ autoCompleteItems . sort ( ( a , b ) =>
155
+ Intl . Collator ( ) . compare ( a . property , b . property )
156
+ ) ;
157
+
158
+ return autoCompleteItems ;
159
+ } ;
121
160
122
161
const matchOrSuggestToCreate = (
123
162
search : string ,
@@ -127,36 +166,49 @@ const matchOrSuggestToCreate = (
127
166
const matched = matchSorter ( items , search , {
128
167
keys : [ itemToString ] ,
129
168
} ) ;
130
- const propertyName = search . trim ( ) ;
169
+
170
+ // Limit the array to 100 elements
171
+ matched . length = Math . min ( matched . length , 100 ) ;
172
+
173
+ const property = search . trim ( ) ;
131
174
if (
132
- propertyName . startsWith ( "--" ) &&
133
- lexer . match ( "<custom-ident>" , propertyName ) . matched
175
+ property . startsWith ( "--" ) &&
176
+ lexer . match ( "<custom-ident>" , property ) . matched
134
177
) {
135
178
matched . unshift ( {
136
- value : propertyName ,
137
- label : `Create "${ propertyName } "` ,
179
+ property ,
180
+ label : `Create "${ property } "` ,
138
181
} ) ;
139
182
}
140
183
// When there is no match we suggest to create a custom property.
141
- if ( matched . length === 0 ) {
184
+ if (
185
+ matched . length === 0 &&
186
+ lexer . match ( "<custom-ident>" , `--${ property } ` ) . matched
187
+ ) {
142
188
matched . unshift ( {
143
- value : `--${ propertyName } ` ,
144
- label : `--${ propertyName } ` ,
189
+ property : `--${ property } ` ,
190
+ label : `--${ property } : unset; ` ,
145
191
} ) ;
146
192
}
193
+
147
194
return matched ;
148
195
} ;
149
196
150
197
const getNewPropertyDescription = ( item : null | SearchItem ) => {
151
198
let description : string | undefined = `Create CSS variable.` ;
152
- if ( item && item . value in propertyDescriptions ) {
153
- description = propertyDescriptions [ item . value ] ;
199
+ if ( item && item . property in propertyDescriptions ) {
200
+ description = propertyDescriptions [ item . property ] ;
154
201
}
155
202
return < Box css = { { width : theme . spacing [ 28 ] } } > { description } </ Box > ;
156
203
} ;
157
204
158
205
const insertStyles = ( text : string ) => {
159
- const parsedStyles = parseCss ( `selector{${ text } }` ) ;
206
+ let parsedStyles = parseCss ( `selector{${ text } }` ) ;
207
+ if ( parsedStyles . length === 0 ) {
208
+ // Try a single property without a value.
209
+ parsedStyles = parseCss ( `selector{${ text } : unset}` ) ;
210
+ }
211
+
160
212
if ( parsedStyles . length === 0 ) {
161
213
return [ ] ;
162
214
}
@@ -168,13 +220,6 @@ const insertStyles = (text: string) => {
168
220
return parsedStyles ;
169
221
} ;
170
222
171
- const sortedProperties = Object . keys ( propertiesData )
172
- . sort ( Intl . Collator ( ) . compare )
173
- . map ( ( property ) => ( {
174
- value : property ,
175
- label : hyphenateProperty ( property ) ,
176
- } ) ) ;
177
-
178
223
/**
179
224
*
180
225
* Advanced search control supports following interactions
@@ -188,28 +233,46 @@ const sortedProperties = Object.keys(propertiesData)
188
233
const AddProperty = forwardRef <
189
234
HTMLInputElement ,
190
235
{
191
- onSelect : ( value : StyleProperty ) => void ;
192
236
onClose : ( ) => void ;
193
- onSubmit : ( value : string ) => void ;
237
+ onSubmit : ( css : string ) => void ;
194
238
onFocus : ( ) => void ;
195
239
}
196
- > ( ( { onSelect , onClose, onSubmit, onFocus } , forwardedRef ) => {
240
+ > ( ( { onClose, onSubmit, onFocus } , forwardedRef ) => {
197
241
const [ item , setItem ] = useState < SearchItem > ( {
198
- value : "" ,
242
+ property : "" ,
199
243
label : "" ,
200
244
} ) ;
245
+ const highlightedItemRef = useRef < SearchItem > ( ) ;
201
246
202
247
const combobox = useCombobox < SearchItem > ( {
203
- getItems : ( ) => sortedProperties ,
248
+ getItems : getAutocompleteItems ,
204
249
itemToString : ( item ) => item ?. label ?? "" ,
205
250
value : item ,
206
251
defaultHighlightedIndex : 0 ,
207
252
getItemProps : ( ) => ( { text : "sentence" } ) ,
208
253
match : matchOrSuggestToCreate ,
209
- onChange : ( value ) => setItem ( { value : value ?? "" , label : value ?? "" } ) ,
254
+ onChange : ( value ) => setItem ( { property : value ?? "" , label : value ?? "" } ) ,
210
255
onItemSelect : ( item ) => {
211
256
clear ( ) ;
212
- onSelect ( item . value as StyleProperty ) ;
257
+ onSubmit ( `${ item . property } : ${ item . value ?? "unset" } ` ) ;
258
+ } ,
259
+ onItemHighlight : ( item ) => {
260
+ const previousHighlightedItem = highlightedItemRef . current ;
261
+ if ( item ?. value === undefined && previousHighlightedItem ) {
262
+ deleteProperty ( previousHighlightedItem . property as StyleProperty , {
263
+ isEphemeral : true ,
264
+ } ) ;
265
+ highlightedItemRef . current = undefined ;
266
+ return ;
267
+ }
268
+
269
+ if ( item ?. value ) {
270
+ const value = parseCssValue ( item . property as StyleProperty , item . value ) ;
271
+ setProperty ( item . property as StyleProperty ) ( value , {
272
+ isEphemeral : true ,
273
+ } ) ;
274
+ highlightedItemRef . current = item ;
275
+ }
213
276
} ,
214
277
} ) ;
215
278
@@ -219,7 +282,7 @@ const AddProperty = forwardRef<
219
282
const inputProps = combobox . getInputProps ( ) ;
220
283
221
284
const clear = ( ) => {
222
- setItem ( { value : "" , label : "" } ) ;
285
+ setItem ( { property : "" , label : "" } ) ;
223
286
} ;
224
287
225
288
const handleKeys = ( event : KeyboardEvent ) => {
@@ -229,7 +292,7 @@ const AddProperty = forwardRef<
229
292
}
230
293
if ( event . key === "Enter" ) {
231
294
clear ( ) ;
232
- onSubmit ( item . value ) ;
295
+ onSubmit ( item . property ) ;
233
296
return ;
234
297
}
235
298
if ( event . key === "Escape" ) {
@@ -363,6 +426,7 @@ const AdvancedPropertyValue = ({
363
426
useEffect ( ( ) => {
364
427
if ( autoFocus ) {
365
428
inputRef . current ?. focus ( ) ;
429
+ inputRef . current ?. select ( ) ;
366
430
}
367
431
} , [ autoFocus ] ) ;
368
432
const isColor = colord ( toValue ( styleDecl . usedValue ) ) . isValid ( ) ;
@@ -560,7 +624,10 @@ const AdvancedProperty = memo(
560
624
< Text
561
625
variant = "mono"
562
626
// Improves the visual separation of value from the property.
563
- css = { { textIndent : "-0.5ch" , fontWeight : "bold" } }
627
+ css = { {
628
+ textIndent : "-0.5ch" ,
629
+ fontWeight : "bold" ,
630
+ } }
564
631
>
565
632
:
566
633
</ Text >
@@ -631,17 +698,6 @@ export const Section = () => {
631
698
}
632
699
>
633
700
< AddProperty
634
- onSelect = { ( property ) => {
635
- setIsAdding ( false ) ;
636
- const isNew = advancedProperties . includes ( property ) === false ;
637
- if ( isNew ) {
638
- setProperty ( property ) (
639
- { type : "guaranteedInvalid" } ,
640
- { listed : true }
641
- ) ;
642
- }
643
- addRecentProperties ( [ property ] ) ;
644
- } }
645
701
onSubmit = { ( value ) => {
646
702
setIsAdding ( false ) ;
647
703
const styles = insertStyles ( value ) ;
0 commit comments