Skip to content

Commit cd41264

Browse files
authored
refactor: update knobs types, simplify logic (#89)
1 parent f01f016 commit cd41264

File tree

7 files changed

+145
-141
lines changed

7 files changed

+145
-141
lines changed

src/api-demo-layout-mixin.ts renamed to src/api-demo-knobs-mixin.ts

Lines changed: 106 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import { LitElement, PropertyValues } from 'lit';
22
import { property } from 'lit/decorators/property.js';
3-
import { getSlotDefault } from './lib/knobs.js';
3+
import { getSlotDefault, Knob } from './lib/knobs.js';
44
import {
55
ComponentWithProps,
66
CSSPropertyInfo,
77
PropertyInfo,
88
SlotInfo,
99
SlotValue,
10-
EventInfo,
11-
KnobValues,
12-
KnobValue
10+
EventInfo
1311
} from './lib/types.js';
1412
import {
1513
getTemplates,
@@ -24,10 +22,10 @@ import {
2422
const { HOST, KNOB } = TemplateTypes;
2523

2624
const getDefault = (
27-
prop: PropertyInfo
25+
prop: Knob<PropertyInfo>
2826
): string | number | boolean | null | undefined => {
29-
const { type, default: value } = prop;
30-
switch (normalizeType(type)) {
27+
const { knobType, default: value } = prop;
28+
switch (knobType) {
3129
case 'boolean':
3230
return value !== 'false';
3331
case 'number':
@@ -61,13 +59,78 @@ const isGetter = (
6159
return result;
6260
};
6361

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+
64127
/* eslint-disable @typescript-eslint/no-explicit-any */
65128
export type Constructor<T = unknown> = new (...args: any[]) => T;
66129

67-
export interface ApiDemoLayoutInterface {
130+
export interface ApiDemoKnobsInterface {
68131
tag: string;
69132
props: PropertyInfo[];
70-
finalProps: PropertyInfo[];
133+
propKnobs: Knob<PropertyInfo>[];
71134
slots: SlotInfo[];
72135
events: EventInfo[];
73136
cssProps: CSSPropertyInfo[];
@@ -76,17 +139,17 @@ export interface ApiDemoLayoutInterface {
76139
processedSlots: SlotValue[];
77140
processedCss: CSSPropertyInfo[];
78141
eventLog: CustomEvent[];
79-
customKnobs: PropertyInfo[];
80-
knobs: KnobValues;
142+
customKnobs: Knob<PropertyInfo>[];
143+
knobs: Record<string, Knob>;
81144
setKnobs(target: HTMLInputElement): void;
82145
setSlots(target: HTMLInputElement): void;
83146
setCss(target: HTMLInputElement): void;
84147
onRendered(event: CustomEvent): void;
85148
}
86149

87-
export const ApiDemoLayoutMixin = <T extends Constructor<LitElement>>(
150+
export const ApiDemoKnobsMixin = <T extends Constructor<LitElement>>(
88151
base: T
89-
): T & Constructor<ApiDemoLayoutInterface> => {
152+
): T & Constructor<ApiDemoKnobsInterface> => {
90153
class DemoLayout extends base {
91154
@property() tag = '';
92155

@@ -116,13 +179,13 @@ export const ApiDemoLayoutMixin = <T extends Constructor<LitElement>>(
116179
eventLog!: CustomEvent[];
117180

118181
@property({ attribute: false })
119-
customKnobs: PropertyInfo[] = [];
182+
customKnobs: Knob<PropertyInfo>[] = [];
120183

121184
@property({ attribute: false })
122-
knobs: KnobValues = {};
185+
knobs: Record<string, Knob> = {};
123186

124187
@property({ attribute: false })
125-
finalProps!: PropertyInfo[];
188+
propKnobs!: Knob<PropertyInfo>[];
126189

127190
willUpdate(props: PropertyValues) {
128191
// Reset state if tag changed
@@ -131,30 +194,10 @@ export const ApiDemoLayoutMixin = <T extends Constructor<LitElement>>(
131194
this.eventLog = [];
132195
this.processedCss = [];
133196
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);
141199
}
142200

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 {
158201
if (props.has('slots') && this.slots) {
159202
this.processedSlots = this.slots
160203
.sort((a: SlotInfo, b: SlotInfo) => {
@@ -175,53 +218,18 @@ export const ApiDemoLayoutMixin = <T extends Constructor<LitElement>>(
175218
}
176219
}
177220

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+
} {
217225
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 };
225233
}
226234

227235
setCss(target: HTMLInputElement): void {
@@ -240,7 +248,7 @@ export const ApiDemoLayoutMixin = <T extends Constructor<LitElement>>(
240248
setKnobs(target: HTMLInputElement): void {
241249
const { name, type } = target.dataset;
242250
let value;
243-
switch (normalizeType(type)) {
251+
switch (type) {
244252
case 'boolean':
245253
value = target.checked;
246254
break;
@@ -256,7 +264,12 @@ export const ApiDemoLayoutMixin = <T extends Constructor<LitElement>>(
256264
const { attribute } = prop;
257265
this.knobs = {
258266
...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>
260273
};
261274
}
262275
}
@@ -280,13 +293,13 @@ export const ApiDemoLayoutMixin = <T extends Constructor<LitElement>>(
280293

281294
if (hasTemplate(this.vid as number, this.tag, HOST)) {
282295
// Apply property values from template
283-
this.finalProps
296+
this.propKnobs
284297
.filter((prop) => {
285-
const { name, type } = prop;
298+
const { name, knobType } = prop;
286299
const defaultValue = getDefault(prop);
287300
return (
288301
component[name] !== defaultValue ||
289-
(normalizeType(type) === 'boolean' && defaultValue)
302+
(knobType === 'boolean' && defaultValue)
290303
);
291304
})
292305
.forEach((prop) => {
@@ -326,17 +339,17 @@ export const ApiDemoLayoutMixin = <T extends Constructor<LitElement>>(
326339
}
327340
}
328341

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;
331344
const value = (component as unknown as ComponentWithProps)[name];
332345

333346
// update knobs to avoid duplicate event
334347
this.knobs = {
335348
...this.knobs,
336-
[name]: { type, value, attribute }
349+
[name]: { knobType, value, attribute }
337350
};
338351

339-
this.finalProps = this.finalProps.map((prop) => {
352+
this.propKnobs = this.propKnobs.map((prop) => {
340353
return prop.name === name
341354
? {
342355
...prop,

src/api-viewer-demo.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ import {
1111
slotRenderer
1212
} from './lib/knobs.js';
1313
import { hasTemplate, TemplateTypes } from './lib/utils.js';
14-
import { ApiDemoLayoutMixin } from './api-demo-layout-mixin.js';
14+
import { ApiDemoKnobsMixin } from './api-demo-knobs-mixin.js';
1515
import './api-viewer-panel.js';
1616
import './api-viewer-tab.js';
1717
import './api-viewer-tabs.js';
1818

19-
class ApiViewerDemo extends ApiDemoLayoutMixin(LitElement) {
19+
class ApiViewerDemo extends ApiDemoKnobsMixin(LitElement) {
2020
@property() copyBtnText = 'copy';
2121

2222
private _whenDefined: Record<string, Promise<unknown>> = {};
@@ -49,7 +49,7 @@ class ApiViewerDemo extends ApiDemoLayoutMixin(LitElement) {
4949
this.events,
5050
this.slots,
5151
this.customKnobs,
52-
this.finalProps
52+
this.propKnobs
5353
].map((arr) => arr.length === 0);
5454

5555
const id = this.vid as number;
@@ -92,12 +92,7 @@ class ApiViewerDemo extends ApiDemoLayoutMixin(LitElement) {
9292
<api-viewer-panel slot="panel" part="tab-panel">
9393
<div part="knobs">
9494
<section part="knobs-column" @change=${this._onPropChanged}>
95-
${renderKnobs(
96-
this.finalProps,
97-
'Properties',
98-
'prop',
99-
propRenderer
100-
)}
95+
${renderKnobs(this.propKnobs, 'Properties', 'prop', propRenderer)}
10196
${renderKnobs(
10297
this.customKnobs,
10398
'Attributes',

src/lib/demo-snippet.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import { htmlRender } from 'highlight-ts/es/render/html';
44
import { registerLanguages } from 'highlight-ts/es/languages';
55
import { XML } from 'highlight-ts/es/languages/xml';
66
import { init, process } from 'highlight-ts/es/process';
7-
import { CSSPropertyInfo, KnobValues, SlotValue } from './types.js';
7+
import { CSSPropertyInfo, SlotValue } from './types.js';
8+
import { Knob } from './knobs.js';
89
import { CSS } from './highlight-css.js';
910
import {
1011
getTemplate,
@@ -52,7 +53,7 @@ const getTplContent = (
5253
export const renderSnippet = (
5354
id: number,
5455
tag: string,
55-
values: KnobValues,
56+
values: Record<string, Knob>,
5657
slots: SlotValue[],
5758
cssProps: CSSPropertyInfo[]
5859
): TemplateResult => {
@@ -80,9 +81,9 @@ export const renderSnippet = (
8081
Object.keys(values)
8182
.sort((a, b) => (a > b ? 1 : -1))
8283
.forEach((key: string) => {
83-
const { value, type, attribute } = values[key];
84+
const { value, knobType, attribute } = values[key];
8485
const attr = attribute || key;
85-
switch (normalizeType(type)) {
86+
switch (normalizeType(knobType)) {
8687
case 'boolean':
8788
markup += value ? ` ${attr}` : '';
8889
break;

0 commit comments

Comments
 (0)