1- import { css , html , LitElement , type CSSResultGroup } from 'lit' ;
2- import { property , state } from 'lit/decorators.js' ;
1+ import {
2+ css ,
3+ html ,
4+ LitElement ,
5+ nothing ,
6+ TemplateResult ,
7+ type CSSResultGroup ,
8+ } from 'lit' ;
9+ import { property , queryAll , state } from 'lit/decorators.js' ;
310import { customElement } from 'lit/decorators/custom-element.js' ;
411import { when } from 'lit/directives/when.js' ;
12+ import { ifDefined } from 'lit/directives/if-defined.js' ;
513
614import './syntax-highlighter' ;
715import themeStyles from '@src/themes/theme-styles' ;
816
917import arrow from './arrow.svg' ;
1018import testTube from './test-tube.svg' ;
1119
20+ export type StyleInputSettings = {
21+ label : string ;
22+ cssVariable : string ;
23+ defaultValue ?: string ;
24+ inputType ?: 'color' | 'text' ;
25+ } ;
26+
27+ export type StyleInputData = {
28+ settings : StyleInputSettings [ ] ;
29+ } ;
30+
1231/**
1332 * A template for demoing the use of a custom element.
1433 */
@@ -18,10 +37,21 @@ export class StoryTemplate extends LitElement {
1837
1938 @property ( { type : String } ) exampleUsage = '' ;
2039
40+ @property ( { type : Object } ) styleInputData ?: StyleInputData ;
41+
2142 @property ( { type : Boolean } ) labs = false ;
2243
2344 @state ( ) private visible = false ;
2445
46+ /* Stringified styles applied for the demo component */
47+ @state ( ) private appliedStyles ?: string ;
48+
49+ /* Whether settings inputs have been slotted in and should be displayed */
50+ @state ( ) private shouldShowPropertySettings : boolean = false ;
51+
52+ @queryAll ( '.style-input' )
53+ private styleInputs ?: NodeListOf < HTMLInputElement > ;
54+
2555 render ( ) {
2656 return html `
2757 <h2>
@@ -49,28 +79,78 @@ export class StoryTemplate extends LitElement {
4979 return html `
5080 <div id= "container" >
5181 <h3> Demo </ h3>
52- <div class= "slot-container" >
82+ <div class= "slot-container" style = ${ ifDefined ( this . appliedStyles ) } >
5383 <slot name= "demo" > </ slot>
5484 </ div>
5585 <h3> Import </ h3>
5686 <syntax- highlighter .code = ${ this . importCode } > </ syntax- highlighter>
5787 <h3> Usage </ h3>
58- <syntax- highlighter .code = ${ this . exampleUsage } > </ syntax- highlighter>
59- <h3> Settings </ h3>
60- <div class= "slot-container" >
88+ <syntax- highlighter
89+ .code = ${ this . exampleUsage + this . cssCode }
90+ > </ syntax- highlighter>
91+ ${ this . styleSettingsTemplate }
92+ ${ this . shouldShowPropertySettings ? html ` <h3> Settings </ h3> ` : nothing }
93+ <div
94+ class= "slot-container"
95+ style = "${ ! this . shouldShowPropertySettings ? 'display: none' : '' } "
96+ @slotchange = ${ this . handleSettingsSlotChange }
97+ >
6198 <slot name= "settings" > </ slot>
6299 </ div>
63100 </ div>
64101 ` ;
65102 }
66103
104+ private get styleSettingsTemplate ( ) : TemplateResult | typeof nothing {
105+ if ( ! this . styleInputData ) return nothing ;
106+
107+ return html `
108+ <h3> Styles </ h3>
109+ <div class= "style-options" >
110+ <table>
111+ ${ this . styleInputData . settings . map (
112+ ( input ) => html `
113+ <tr>
114+ <td>
115+ <label for = ${ this . labelToId ( input . label ) }
116+ > ${ input . label } </ label
117+ >
118+ </ td>
119+ <td>
120+ <input
121+ id= ${ this . labelToId ( input . label ) }
122+ class= "style-input"
123+ type = ${ input . inputType ?? 'text' }
124+ value= ${ input . defaultValue ?? '' }
125+ data- variable= ${ input . cssVariable }
126+ / >
127+ </ td>
128+ </ tr>
129+ ` ,
130+ ) }
131+ </ table>
132+ <butto n @click = ${ this . applyStyles } > Apply </ butto n>
133+ </ div>
134+ ` ;
135+ }
136+
67137 private get importCode ( ) : string {
68138 return `
69139import '${ this . modulePath } ';
70140import { ${ this . elementClassName } } from '${ this . modulePath } ';
71141 ` ;
72142 }
73143
144+ private get cssCode ( ) : string {
145+ if ( ! this . appliedStyles ) return '' ;
146+ return `
147+
148+ ${ this . elementTag } {
149+ ${ this . appliedStyles }
150+ }
151+ ` ;
152+ }
153+
74154 private get elementClassName ( ) : string | undefined {
75155 return customElements . get ( this . elementTag ) ?. name ;
76156 }
@@ -81,6 +161,29 @@ import { ${this.elementClassName} } from '${this.modulePath}';
81161 : `@internetarchive/elements/${ this . elementTag } /${ this . elementTag } ` ;
82162 }
83163
164+ /* Toggles visibility of section depending on whether inputs have been slotted into it */
165+ private handleSettingsSlotChange ( e : Event ) : void {
166+ const slottedChildren = ( e . target as HTMLSlotElement ) . assignedElements ( ) ;
167+ this . shouldShowPropertySettings = slottedChildren . length > 0 ;
168+ }
169+
170+ /* Applies styles to demo component. */
171+ private applyStyles ( ) : void {
172+ const appliedStyles : string [ ] = [ ] ;
173+
174+ this . styleInputs ?. forEach ( ( input ) => {
175+ if ( ! input . dataset . variable || ! input . value ) return ;
176+ appliedStyles . push ( `${ input . dataset . variable } : ${ input . value } ;` ) ;
177+ } ) ;
178+
179+ this . appliedStyles = appliedStyles . join ( '\n ' ) ;
180+ }
181+
182+ /* Converts a label to a usable input id, i.e. My setting -> my-setting */
183+ private labelToId ( label : string ) : string {
184+ return label . toLowerCase ( ) . split ( ' ' ) . join ( '-' ) ;
185+ }
186+
84187 static get styles ( ) : CSSResultGroup {
85188 return [
86189 themeStyles ,
@@ -100,7 +203,8 @@ import { ${this.elementClassName} } from '${this.modulePath}';
100203 margin-bottom : 8px ;
101204 }
102205
103- .slot-container {
206+ .slot-container ,
207+ .style-options {
104208 background-color : var (--primary-background-color );
105209 padding : 1em ;
106210 }
0 commit comments