1+ import { useState } from "react" ;
2+ import { computed } from "nanostores" ;
13import { useStore } from "@nanostores/react" ;
2- import { Box , Select , theme } from "@webstudio-is/design-system" ;
4+ import { Box , Combobox , Select , theme } from "@webstudio-is/design-system" ;
35import { elementsByTag } from "@webstudio-is/html-data" ;
4- import { $selectedInstance } from "~/shared/awareness" ;
6+ import { tags } from "@webstudio-is/sdk" ;
7+ import { $selectedInstance , $selectedInstancePath } from "~/shared/awareness" ;
58import { updateWebstudioData } from "~/shared/instance-utils" ;
9+ import { isTreeSatisfyingContentModel } from "~/shared/content-model" ;
10+ import {
11+ $instances ,
12+ $props ,
13+ $registeredComponentMetas ,
14+ } from "~/shared/nano-states" ;
615import { type ControlProps , VerticalLayout } from "../shared" ;
716import { FieldLabel } from "../property-label" ;
817
18+ const $satisfyingTags = computed (
19+ [ $selectedInstancePath , $instances , $props , $registeredComponentMetas ] ,
20+ ( instancePath , instances , props , metas ) => {
21+ const satisfyingTags : string [ ] = [ ] ;
22+ if ( instancePath === undefined ) {
23+ return satisfyingTags ;
24+ }
25+ const [ { instance, instanceSelector } ] = instancePath ;
26+ const newInstances = new Map ( instances ) ;
27+ for ( const tag of tags ) {
28+ newInstances . set ( instance . id , { ...instance , tag } ) ;
29+ const isSatisfying = isTreeSatisfyingContentModel ( {
30+ instances : newInstances ,
31+ props,
32+ metas,
33+ instanceSelector,
34+ } ) ;
35+ if ( isSatisfying ) {
36+ satisfyingTags . push ( tag ) ;
37+ }
38+ }
39+ return satisfyingTags ;
40+ }
41+ ) ;
42+
943export const TagControl = ( { meta, prop } : ControlProps < "tag" > ) => {
1044 const instance = useStore ( $selectedInstance ) ;
1145 const propTag = prop ?. type === "string" ? prop . value : undefined ;
1246 const instanceTag = instance ?. tag ;
1347 const defaultTag = meta . options [ 0 ] ;
14- const value = propTag ?? instanceTag ?? defaultTag ;
48+ const computedTag = instanceTag ?? propTag ?? defaultTag ;
49+ const satisfyingTags = useStore ( $satisfyingTags ) ;
50+ const options = meta . options . filter ( ( tag ) => satisfyingTags . includes ( tag ) ) ;
51+ const [ value , setValue ] = useState < undefined | string > ( ) ;
52+ const updateTag = ( value : string ) => {
53+ if ( instance === undefined ) {
54+ return ;
55+ }
56+ const instanceId = instance . id ;
57+ updateWebstudioData ( ( data ) => {
58+ // clean legacy <Box tag> and <Text tag>
59+ if ( prop ) {
60+ data . props . delete ( prop . id ) ;
61+ }
62+ const instance = data . instances . get ( instanceId ) ;
63+ if ( instance ) {
64+ instance . tag = value ;
65+ }
66+ } ) ;
67+ } ;
1568 return (
1669 < VerticalLayout
1770 label = {
@@ -20,32 +73,36 @@ export const TagControl = ({ meta, prop }: ControlProps<"tag">) => {
2073 </ FieldLabel >
2174 }
2275 >
23- < Select
24- fullWidth
25- value = { value }
26- options = { meta . options }
27- onChange = { ( value ) => {
28- if ( instance === undefined ) {
29- return ;
30- }
31- const instanceId = instance . id ;
32- updateWebstudioData ( ( data ) => {
33- // clean legacy <Box tag> and <Text tag>
34- if ( prop ) {
35- data . props . delete ( prop . id ) ;
36- }
37- const instance = data . instances . get ( instanceId ) ;
38- if ( instance ) {
39- instance . tag = value ;
40- }
41- } ) ;
42- } }
43- getDescription = { ( item ) => (
44- < Box css = { { width : theme . spacing [ 28 ] } } >
45- { elementsByTag [ item ] ?. description }
46- </ Box >
47- ) }
48- />
76+ { options . length > 10 ? (
77+ < Combobox < string >
78+ defaultHighlightedIndex = { 0 }
79+ getItems = { ( ) => options }
80+ onItemSelect = { ( item ) => {
81+ updateTag ( item ) ;
82+ setValue ( undefined ) ;
83+ } }
84+ itemToString = { ( item ) => item ?? options [ 0 ] }
85+ value = { value ?? computedTag }
86+ onChange = { ( value ) => setValue ( value ?? undefined ) }
87+ getDescription = { ( item ) => (
88+ < Box css = { { width : theme . spacing [ 28 ] } } >
89+ { elementsByTag [ item ?? "" ] ?. description }
90+ </ Box >
91+ ) }
92+ />
93+ ) : (
94+ < Select
95+ fullWidth
96+ value = { computedTag }
97+ options = { options }
98+ onChange = { updateTag }
99+ getDescription = { ( item ) => (
100+ < Box css = { { width : theme . spacing [ 28 ] } } >
101+ { elementsByTag [ item ] ?. description }
102+ </ Box >
103+ ) }
104+ />
105+ ) }
49106 </ VerticalLayout >
50107 ) ;
51108} ;
0 commit comments