1
1
import { LitElement , PropertyValues } from 'lit' ;
2
2
import { property } from 'lit/decorators/property.js' ;
3
- import { getSlotDefault } from './lib/knobs.js' ;
3
+ import { getSlotDefault , Knob } from './lib/knobs.js' ;
4
4
import {
5
5
ComponentWithProps ,
6
6
CSSPropertyInfo ,
7
7
PropertyInfo ,
8
8
SlotInfo ,
9
9
SlotValue ,
10
- EventInfo ,
11
- KnobValues ,
12
- KnobValue
10
+ EventInfo
13
11
} from './lib/types.js' ;
14
12
import {
15
13
getTemplates ,
@@ -24,10 +22,10 @@ import {
24
22
const { HOST , KNOB } = TemplateTypes ;
25
23
26
24
const getDefault = (
27
- prop : PropertyInfo
25
+ prop : Knob < PropertyInfo >
28
26
) : string | number | boolean | null | undefined => {
29
- const { type , default : value } = prop ;
30
- switch ( normalizeType ( type ) ) {
27
+ const { knobType , default : value } = prop ;
28
+ switch ( knobType ) {
31
29
case 'boolean' :
32
30
return value !== 'false' ;
33
31
case 'number' :
@@ -61,13 +59,78 @@ const isGetter = (
61
59
return result ;
62
60
} ;
63
61
62
+ const getKnobs = (
63
+ tag : string ,
64
+ props : PropertyInfo [ ] ,
65
+ exclude = ''
66
+ ) : Knob < PropertyInfo > [ ] => {
67
+ // Exclude getters and specific properties
68
+ let propKnobs = props . filter (
69
+ ( { name } ) =>
70
+ ! exclude . includes ( name ) && ! isGetter ( customElements . get ( tag ) , name )
71
+ ) as Knob < PropertyInfo > [ ] ;
72
+
73
+ // Set knob types and default knobs values
74
+ propKnobs = propKnobs . map ( ( prop ) => {
75
+ const knob = {
76
+ ...prop ,
77
+ knobType : normalizeType ( prop . type )
78
+ } ;
79
+
80
+ if ( typeof knob . default === 'string' ) {
81
+ knob . value = getDefault ( knob ) ;
82
+ }
83
+
84
+ return knob ;
85
+ } ) ;
86
+
87
+ return propKnobs ;
88
+ } ;
89
+
90
+ const getCustomKnobs = ( tag : string , vid ?: number ) : Knob < PropertyInfo > [ ] => {
91
+ return getTemplates ( vid as number , tag , KNOB )
92
+ . map ( ( template ) => {
93
+ const { attr, type } = template . dataset ;
94
+ let result = null ;
95
+ if ( attr ) {
96
+ if ( type === 'select' ) {
97
+ const node = getTemplateNode ( template ) ;
98
+ const options = node
99
+ ? Array . from ( node . children )
100
+ . filter (
101
+ ( c ) : c is HTMLOptionElement => c instanceof HTMLOptionElement
102
+ )
103
+ . map ( ( option ) => option . value )
104
+ : [ ] ;
105
+ if ( node instanceof HTMLSelectElement && options . length > 1 ) {
106
+ result = {
107
+ name : attr ,
108
+ attribute : attr ,
109
+ knobType : type ,
110
+ options
111
+ } ;
112
+ }
113
+ }
114
+ if ( type === 'string' || type === 'boolean' ) {
115
+ result = {
116
+ name : attr ,
117
+ attribute : attr ,
118
+ knobType : type
119
+ } ;
120
+ }
121
+ }
122
+ return result as Knob < PropertyInfo > ;
123
+ } )
124
+ . filter ( Boolean ) ;
125
+ } ;
126
+
64
127
/* eslint-disable @typescript-eslint/no-explicit-any */
65
128
export type Constructor < T = unknown > = new ( ...args : any [ ] ) => T ;
66
129
67
- export interface ApiDemoLayoutInterface {
130
+ export interface ApiDemoKnobsInterface {
68
131
tag : string ;
69
132
props : PropertyInfo [ ] ;
70
- finalProps : PropertyInfo [ ] ;
133
+ propKnobs : Knob < PropertyInfo > [ ] ;
71
134
slots : SlotInfo [ ] ;
72
135
events : EventInfo [ ] ;
73
136
cssProps : CSSPropertyInfo [ ] ;
@@ -76,17 +139,17 @@ export interface ApiDemoLayoutInterface {
76
139
processedSlots : SlotValue [ ] ;
77
140
processedCss : CSSPropertyInfo [ ] ;
78
141
eventLog : CustomEvent [ ] ;
79
- customKnobs : PropertyInfo [ ] ;
80
- knobs : KnobValues ;
142
+ customKnobs : Knob < PropertyInfo > [ ] ;
143
+ knobs : Record < string , Knob > ;
81
144
setKnobs ( target : HTMLInputElement ) : void ;
82
145
setSlots ( target : HTMLInputElement ) : void ;
83
146
setCss ( target : HTMLInputElement ) : void ;
84
147
onRendered ( event : CustomEvent ) : void ;
85
148
}
86
149
87
- export const ApiDemoLayoutMixin = < T extends Constructor < LitElement > > (
150
+ export const ApiDemoKnobsMixin = < T extends Constructor < LitElement > > (
88
151
base : T
89
- ) : T & Constructor < ApiDemoLayoutInterface > => {
152
+ ) : T & Constructor < ApiDemoKnobsInterface > => {
90
153
class DemoLayout extends base {
91
154
@property ( ) tag = '' ;
92
155
@@ -116,13 +179,13 @@ export const ApiDemoLayoutMixin = <T extends Constructor<LitElement>>(
116
179
eventLog ! : CustomEvent [ ] ;
117
180
118
181
@property ( { attribute : false } )
119
- customKnobs : PropertyInfo [ ] = [ ] ;
182
+ customKnobs : Knob < PropertyInfo > [ ] = [ ] ;
120
183
121
184
@property ( { attribute : false } )
122
- knobs : KnobValues = { } ;
185
+ knobs : Record < string , Knob > = { } ;
123
186
124
187
@property ( { attribute : false } )
125
- finalProps ! : PropertyInfo [ ] ;
188
+ propKnobs ! : Knob < PropertyInfo > [ ] ;
126
189
127
190
willUpdate ( props : PropertyValues ) {
128
191
// Reset state if tag changed
@@ -131,30 +194,10 @@ export const ApiDemoLayoutMixin = <T extends Constructor<LitElement>>(
131
194
this . eventLog = [ ] ;
132
195
this . processedCss = [ ] ;
133
196
this . processedSlots = [ ] ;
134
-
135
- // Store properties without getters
136
- this . finalProps = this . props . filter (
137
- ( { name } ) => ! isGetter ( customElements . get ( this . tag ) , name )
138
- ) ;
139
-
140
- this . customKnobs = this . _getCustomKnobs ( ) ;
197
+ this . propKnobs = getKnobs ( this . tag , this . props , this . exclude ) ;
198
+ this . customKnobs = getCustomKnobs ( this . tag , this . vid ) ;
141
199
}
142
200
143
- if ( props . has ( 'exclude' ) ) {
144
- this . finalProps = this . finalProps
145
- . filter ( ( { name } ) => ! this . exclude . includes ( name ) )
146
- . map ( ( prop : PropertyInfo ) => {
147
- return typeof prop . default === 'string'
148
- ? {
149
- ...prop ,
150
- value : getDefault ( prop )
151
- }
152
- : prop ;
153
- } ) ;
154
- }
155
- }
156
-
157
- protected updated ( props : PropertyValues ) : void {
158
201
if ( props . has ( 'slots' ) && this . slots ) {
159
202
this . processedSlots = this . slots
160
203
. sort ( ( a : SlotInfo , b : SlotInfo ) => {
@@ -175,53 +218,18 @@ export const ApiDemoLayoutMixin = <T extends Constructor<LitElement>>(
175
218
}
176
219
}
177
220
178
- private _getCustomKnobs ( ) : PropertyInfo [ ] {
179
- return getTemplates ( this . vid as number , this . tag , KNOB )
180
- . map ( ( template ) => {
181
- const { attr, type } = template . dataset ;
182
- let result = null ;
183
- if ( attr ) {
184
- if ( type === 'select' ) {
185
- const node = getTemplateNode ( template ) ;
186
- const options = node
187
- ? Array . from ( node . children )
188
- . filter (
189
- ( c ) : c is HTMLOptionElement =>
190
- c instanceof HTMLOptionElement
191
- )
192
- . map ( ( option ) => option . value )
193
- : [ ] ;
194
- if ( node instanceof HTMLSelectElement && options . length > 1 ) {
195
- result = {
196
- name : attr ,
197
- attribute : attr ,
198
- type,
199
- options
200
- } ;
201
- }
202
- }
203
- if ( type === 'string' || type === 'boolean' ) {
204
- result = {
205
- name : attr ,
206
- attribute : attr ,
207
- type
208
- } ;
209
- }
210
- }
211
- return result ;
212
- } )
213
- . filter ( Boolean ) as PropertyInfo [ ] ;
214
- }
215
-
216
- private _getProp ( name : string ) : { prop ?: PropertyInfo ; custom ?: boolean } {
221
+ private _getProp ( name : string ) : {
222
+ prop : Knob < PropertyInfo > ;
223
+ custom ?: boolean ;
224
+ } {
217
225
const isMatch = isPropMatch ( name ) ;
218
- const prop = this . finalProps . find ( isMatch ) ;
219
- return prop
220
- ? { prop }
221
- : {
222
- prop : this . customKnobs . find ( isMatch ) ,
223
- custom : true
224
- } ;
226
+ let prop = this . propKnobs . find ( isMatch ) ;
227
+ let custom = false ;
228
+ if ( ! prop ) {
229
+ prop = this . customKnobs . find ( isMatch ) as Knob < PropertyInfo > ;
230
+ custom = true ;
231
+ }
232
+ return { prop , custom } ;
225
233
}
226
234
227
235
setCss ( target : HTMLInputElement ) : void {
@@ -240,7 +248,7 @@ export const ApiDemoLayoutMixin = <T extends Constructor<LitElement>>(
240
248
setKnobs ( target : HTMLInputElement ) : void {
241
249
const { name, type } = target . dataset ;
242
250
let value ;
243
- switch ( normalizeType ( type ) ) {
251
+ switch ( type ) {
244
252
case 'boolean' :
245
253
value = target . checked ;
246
254
break ;
@@ -256,7 +264,12 @@ export const ApiDemoLayoutMixin = <T extends Constructor<LitElement>>(
256
264
const { attribute } = prop ;
257
265
this . knobs = {
258
266
...this . knobs ,
259
- [ name as string ] : { type, value, attribute, custom } as KnobValue
267
+ [ name as string ] : {
268
+ knobType : type ,
269
+ value,
270
+ attribute,
271
+ custom
272
+ } as Knob < PropertyInfo >
260
273
} ;
261
274
}
262
275
}
@@ -280,13 +293,13 @@ export const ApiDemoLayoutMixin = <T extends Constructor<LitElement>>(
280
293
281
294
if ( hasTemplate ( this . vid as number , this . tag , HOST ) ) {
282
295
// Apply property values from template
283
- this . finalProps
296
+ this . propKnobs
284
297
. filter ( ( prop ) => {
285
- const { name, type } = prop ;
298
+ const { name, knobType } = prop ;
286
299
const defaultValue = getDefault ( prop ) ;
287
300
return (
288
301
component [ name ] !== defaultValue ||
289
- ( normalizeType ( type ) === 'boolean' && defaultValue )
302
+ ( knobType === 'boolean' && defaultValue )
290
303
) ;
291
304
} )
292
305
. forEach ( ( prop ) => {
@@ -326,17 +339,17 @@ export const ApiDemoLayoutMixin = <T extends Constructor<LitElement>>(
326
339
}
327
340
}
328
341
329
- private _syncKnob ( component : Element , changed : PropertyInfo ) : void {
330
- const { name, type , attribute } = changed ;
342
+ private _syncKnob ( component : Element , changed : Knob < PropertyInfo > ) : void {
343
+ const { name, knobType , attribute } = changed ;
331
344
const value = ( component as unknown as ComponentWithProps ) [ name ] ;
332
345
333
346
// update knobs to avoid duplicate event
334
347
this . knobs = {
335
348
...this . knobs ,
336
- [ name ] : { type , value, attribute }
349
+ [ name ] : { knobType , value, attribute }
337
350
} ;
338
351
339
- this . finalProps = this . finalProps . map ( ( prop ) => {
352
+ this . propKnobs = this . propKnobs . map ( ( prop ) => {
340
353
return prop . name === name
341
354
? {
342
355
...prop ,
0 commit comments