Skip to content

Commit c12fdf3

Browse files
authored
refactor: split styles logic to controller (#96)
1 parent 097694d commit c12fdf3

File tree

4 files changed

+110
-72
lines changed

4 files changed

+110
-72
lines changed

src/api-demo-knobs-mixin.ts

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
import { LitElement, PropertyValues } from 'lit';
22
import { property } from 'lit/decorators/property.js';
33
import { Knob } from './lib/knobs.js';
4-
import {
5-
ComponentWithProps,
6-
CSSPropertyInfo,
7-
PropertyInfo
8-
} from './lib/types.js';
4+
import { ComponentWithProps, PropertyInfo } from './lib/types.js';
95
import {
106
getTemplates,
117
hasTemplate,
@@ -133,14 +129,11 @@ export interface ApiDemoKnobsInterface extends HasKnobs {
133129
tag: string;
134130
props: PropertyInfo[];
135131
propKnobs: Knob<PropertyInfo>[];
136-
cssProps: CSSPropertyInfo[];
137132
exclude: string;
138133
vid?: number;
139-
processedCss: CSSPropertyInfo[];
140134
customKnobs: Knob<PropertyInfo>[];
141135
knobs: Record<string, Knob>;
142136
setKnobs(target: HTMLInputElement): void;
143-
setCss(target: HTMLInputElement): void;
144137
initKnobs(component: HTMLElement): void;
145138
}
146139

@@ -153,16 +146,10 @@ export const ApiDemoKnobsMixin = <T extends Constructor<LitElement>>(
153146
@property({ attribute: false })
154147
props: PropertyInfo[] = [];
155148

156-
@property({ attribute: false })
157-
cssProps: CSSPropertyInfo[] = [];
158-
159149
@property() exclude = '';
160150

161151
@property({ type: Number }) vid?: number;
162152

163-
@property({ attribute: false })
164-
processedCss!: CSSPropertyInfo[];
165-
166153
@property({ attribute: false })
167154
customKnobs: Knob<PropertyInfo>[] = [];
168155

@@ -176,7 +163,6 @@ export const ApiDemoKnobsMixin = <T extends Constructor<LitElement>>(
176163
// Reset state if tag changed
177164
if (props.has('tag')) {
178165
this.knobs = {};
179-
this.processedCss = [];
180166
this.propKnobs = getKnobs(this.tag, this.props, this.exclude);
181167
this.customKnobs = getCustomKnobs(this.tag, this.vid);
182168
}
@@ -196,19 +182,6 @@ export const ApiDemoKnobsMixin = <T extends Constructor<LitElement>>(
196182
return { knob, custom };
197183
}
198184

199-
setCss(target: HTMLInputElement): void {
200-
const { value, dataset } = target;
201-
202-
this.processedCss = this.processedCss.map((prop) => {
203-
return prop.name === dataset.name
204-
? {
205-
...prop,
206-
value
207-
}
208-
: prop;
209-
});
210-
}
211-
212185
setKnobs(target: HTMLInputElement): void {
213186
const { name, type } = target.dataset;
214187
let value;
@@ -254,23 +227,6 @@ export const ApiDemoKnobsMixin = <T extends Constructor<LitElement>>(
254227
this.syncKnob(component, prop);
255228
});
256229
}
257-
258-
if (this.cssProps.length) {
259-
const style = getComputedStyle(component);
260-
261-
this.processedCss = this.cssProps.map((cssProp) => {
262-
let value = cssProp.default
263-
? unquote(cssProp.default)
264-
: style.getPropertyValue(cssProp.name);
265-
const result = cssProp;
266-
if (value) {
267-
value = value.trim();
268-
result.default = value;
269-
result.value = value;
270-
}
271-
return result;
272-
});
273-
}
274230
}
275231

276232
syncKnob(component: Element, changed: Knob<PropertyInfo>): void {

src/api-viewer-demo.ts

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { property } from 'lit/decorators/property.js';
33
import { cache } from 'lit/directives/cache.js';
44
import { EventsController } from './controllers/events-controller.js';
55
import { SlotsController } from './controllers/slots-controller.js';
6+
import { StylesController } from './controllers/styles-controller.js';
67
import { renderEvents } from './lib/demo-events.js';
78
import { renderSnippet } from './lib/demo-snippet.js';
89
import { renderer } from './lib/renderer.js';
@@ -12,7 +13,7 @@ import {
1213
renderKnobs,
1314
slotRenderer
1415
} from './lib/knobs.js';
15-
import { EventInfo, SlotInfo } from './lib/types.js';
16+
import { CSSPropertyInfo, EventInfo, SlotInfo } from './lib/types.js';
1617
import { hasTemplate, TemplateTypes } from './lib/utils.js';
1718
import { ApiDemoKnobsMixin } from './api-demo-knobs-mixin.js';
1819
import './api-viewer-panel.js';
@@ -22,6 +23,9 @@ import './api-viewer-tabs.js';
2223
class ApiViewerDemo extends ApiDemoKnobsMixin(LitElement) {
2324
@property() copyBtnText = 'copy';
2425

26+
@property({ attribute: false })
27+
cssProps: CSSPropertyInfo[] = [];
28+
2529
@property({ attribute: false })
2630
events: EventInfo[] = [];
2731

@@ -34,6 +38,8 @@ class ApiViewerDemo extends ApiDemoKnobsMixin(LitElement) {
3438

3539
private slotsController!: SlotsController;
3640

41+
private stylesController!: StylesController;
42+
3743
protected createRenderRoot(): this {
3844
return this;
3945
}
@@ -69,16 +75,12 @@ class ApiViewerDemo extends ApiDemoKnobsMixin(LitElement) {
6975
const id = this.vid as number;
7076
const log = this.eventsController?.log || [];
7177
const slots = this.slotsController?.slots || [];
78+
const cssProps = this.stylesController?.css || [];
7279
const hideSlots = noSlots || hasTemplate(id, tag, TemplateTypes.SLOT);
7380

7481
return html`
7582
<div part="demo-output" @rendered=${this.onRendered}>
76-
${renderer({
77-
id,
78-
tag,
79-
knobs: this.knobs,
80-
cssProps: this.processedCss
81-
})}
83+
${renderer({ id, tag, knobs: this.knobs })}
8284
</div>
8385
<api-viewer-tabs part="demo-tabs">
8486
<api-viewer-tab heading="Source" slot="tab" part="tab"></api-viewer-tab>
@@ -87,7 +89,7 @@ class ApiViewerDemo extends ApiDemoKnobsMixin(LitElement) {
8789
${this.copyBtnText}
8890
</button>
8991
<div part="demo-snippet">
90-
${renderSnippet(id, tag, this.knobs, slots, this.processedCss)}
92+
${renderSnippet(id, tag, this.knobs, slots, cssProps)}
9193
</div>
9294
</api-viewer-panel>
9395
<api-viewer-tab
@@ -126,7 +128,7 @@ class ApiViewerDemo extends ApiDemoKnobsMixin(LitElement) {
126128
<div part="knobs" ?hidden=${noCss}>
127129
<section part="knobs-column" @change=${this._onCssChanged}>
128130
${renderKnobs(
129-
this.cssProps,
131+
cssProps,
130132
'Custom CSS Properties',
131133
'css-prop',
132134
cssPropRenderer
@@ -207,6 +209,8 @@ class ApiViewerDemo extends ApiDemoKnobsMixin(LitElement) {
207209
this.initEvents(component);
208210

209211
this.initSlots(component);
212+
213+
this.initStyles(component);
210214
}
211215

212216
private initEvents(component: HTMLElement) {
@@ -233,8 +237,22 @@ class ApiViewerDemo extends ApiDemoKnobsMixin(LitElement) {
233237
);
234238
}
235239

240+
private initStyles(component: HTMLElement) {
241+
const controller = this.stylesController;
242+
if (controller) {
243+
this.removeController(controller);
244+
}
245+
246+
this.stylesController = new StylesController(
247+
this,
248+
component,
249+
this.cssProps
250+
);
251+
}
252+
236253
private _onCssChanged(e: CustomEvent): void {
237-
this.setCss(e.composedPath()[0] as HTMLInputElement);
254+
const target = e.composedPath()[0] as HTMLInputElement;
255+
this.stylesController.setValue(target.dataset.name as string, target.value);
238256
}
239257

240258
private _onPropChanged(e: Event): void {

src/controllers/styles-controller.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { ReactiveController, ReactiveControllerHost } from 'lit';
2+
import { CSSPropertyInfo } from '../lib/types.js';
3+
import { unquote } from '../lib/utils.js';
4+
5+
export class StylesController implements ReactiveController {
6+
host: HTMLElement & ReactiveControllerHost;
7+
8+
el: HTMLElement;
9+
10+
private _css: CSSPropertyInfo[] = [];
11+
12+
get css(): CSSPropertyInfo[] {
13+
return this._css;
14+
}
15+
16+
set css(cssProps: CSSPropertyInfo[]) {
17+
this._css = cssProps;
18+
19+
if (cssProps.length) {
20+
cssProps.forEach((prop) => {
21+
const { name, value } = prop;
22+
if (value) {
23+
if (value === prop.default) {
24+
this.el.style.removeProperty(name);
25+
} else {
26+
this.el.style.setProperty(name, value);
27+
}
28+
}
29+
});
30+
}
31+
32+
// Update the demo snippet
33+
if (this.host.isConnected) {
34+
this.host.requestUpdate();
35+
}
36+
}
37+
38+
constructor(
39+
host: HTMLElement & ReactiveControllerHost,
40+
component: HTMLElement,
41+
cssProps: CSSPropertyInfo[]
42+
) {
43+
(this.host = host).addController(this);
44+
this.el = component;
45+
46+
if (cssProps.length) {
47+
const style = getComputedStyle(component);
48+
49+
this.css = cssProps.map((cssProp) => {
50+
let value = cssProp.default
51+
? unquote(cssProp.default)
52+
: style.getPropertyValue(cssProp.name);
53+
54+
const result = cssProp;
55+
if (value) {
56+
value = value.trim();
57+
result.default = value;
58+
result.value = value;
59+
}
60+
61+
return result;
62+
});
63+
}
64+
}
65+
66+
hostDisconnected() {
67+
this.css = [];
68+
}
69+
70+
setValue(name: string, value: string) {
71+
this.css = this.css.map((prop) => {
72+
return prop.name === name
73+
? {
74+
...prop,
75+
value
76+
}
77+
: prop;
78+
});
79+
}
80+
}

src/lib/renderer.ts

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { directive, Directive, PartInfo, PartType } from 'lit/directive.js';
33
import { templateContent } from 'lit/directives/template-content.js';
44
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
55
import { Knob } from './knobs.js';
6-
import { CSSPropertyInfo } from './types.js';
76
import {
87
getTemplate,
98
getTemplateNode,
@@ -16,7 +15,6 @@ export type ComponentRendererOptions = {
1615
id: number;
1716
tag: string;
1817
knobs: Record<string, Knob>;
19-
cssProps: CSSPropertyInfo[];
2018
};
2119

2220
const { HOST, PREFIX, SLOT, SUFFIX, WRAPPER } = TemplateTypes;
@@ -25,7 +23,7 @@ const updateComponent = (
2523
component: HTMLElement,
2624
options: ComponentRendererOptions
2725
): void => {
28-
const { knobs, cssProps } = options;
26+
const { knobs } = options;
2927

3028
// Apply knobs using properties or attributes
3129
Object.keys(knobs).forEach((key: string) => {
@@ -42,20 +40,6 @@ const updateComponent = (
4240
(component as any)[key] = value;
4341
}
4442
});
45-
46-
// Apply CSS props
47-
if (cssProps.length) {
48-
cssProps.forEach((prop) => {
49-
const { name, value } = prop;
50-
if (value) {
51-
if (value === prop.default) {
52-
component.style.removeProperty(name);
53-
} else {
54-
component.style.setProperty(name, value);
55-
}
56-
}
57-
});
58-
}
5943
};
6044

6145
const isDefinedPromise = (action: unknown): action is Promise<unknown> =>

0 commit comments

Comments
 (0)