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' ;
15+ import themeStyles from '@src/themes/theme-styles' ;
716
817import arrow from './arrow.svg' ;
918import testTube from './test-tube.svg' ;
1019
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+
1131/**
1232 * A template for demoing the use of a custom element.
1333 */
@@ -17,10 +37,21 @@ export class StoryTemplate extends LitElement {
1737
1838 @property ( { type : String } ) exampleUsage = '' ;
1939
40+ @property ( { type : Object } ) styleInputData ?: StyleInputData ;
41+
2042 @property ( { type : Boolean } ) labs = false ;
2143
2244 @state ( ) private visible = false ;
2345
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+
2455 render ( ) {
2556 return html `
2657 <h2>
@@ -48,28 +79,78 @@ export class StoryTemplate extends LitElement {
4879 return html `
4980 <div id= "container" >
5081 <h3> Demo </ h3>
51- <div class= "slot-container" >
82+ <div class= "slot-container" style = ${ ifDefined ( this . appliedStyles ) } >
5283 <slot name= "demo" > </ slot>
5384 </ div>
5485 <h3> Import </ h3>
5586 <syntax- highlighter .code = ${ this . importCode } > </ syntax- highlighter>
5687 <h3> Usage </ h3>
57- <syntax- highlighter .code = ${ this . exampleUsage } > </ syntax- highlighter>
58- <h3> Settings </ h3>
59- <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+ >
6098 <slot name= "settings" > </ slot>
6199 </ div>
62100 </ div>
63101 ` ;
64102 }
65103
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+
66137 private get importCode ( ) : string {
67138 return `
68139import '${ this . modulePath } ';
69140import { ${ this . elementClassName } } from '${ this . modulePath } ';
70141 ` ;
71142 }
72143
144+ private get cssCode ( ) : string {
145+ if ( ! this . appliedStyles ) return '' ;
146+ return `
147+
148+ ${ this . elementTag } {
149+ ${ this . appliedStyles }
150+ }
151+ ` ;
152+ }
153+
73154 private get elementClassName ( ) : string | undefined {
74155 return customElements . get ( this . elementTag ) ?. name ;
75156 }
@@ -80,47 +161,73 @@ import { ${this.elementClassName} } from '${this.modulePath}';
80161 : `@internetarchive/elements/${ this . elementTag } /${ this . elementTag } ` ;
81162 }
82163
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+
83187 static get styles ( ) : CSSResultGroup {
84- return css `
85- # container {
86- border : 1px solid # ccc ;
87- padding : 0 16px 16px 16px ;
88- }
89-
90- h2 {
91- cursor : pointer;
92- margin-top : 8px ;
93- margin-bottom : 8px ;
94- }
95-
96- h3 {
97- margin-bottom : 8px ;
98- }
99-
100- .slot-container {
101- background : # 282c34 ;
102- padding : 1em ;
103- }
104-
105- .disclosure-arrow {
106- width : 12px ;
107- height : 12px ;
108- transform : rotate (-90deg );
109- filter : invert (1 );
110- transition : transform 0.2s ease-in-out;
111- }
112-
113- .disclosure-arrow .open {
114- transform : rotate (0deg );
115- }
116-
117- .labs-icon {
118- width : 20px ;
119- height : 20px ;
120- margin-left : 4px ;
121- filter : invert (1 );
122- vertical-align : middle;
123- }
124- ` ;
188+ return [
189+ themeStyles ,
190+ css `
191+ # container {
192+ border : 1px solid # ccc ;
193+ padding : 0 16px 16px 16px ;
194+ }
195+
196+ h2 {
197+ cursor : pointer;
198+ margin-top : 8px ;
199+ margin-bottom : 8px ;
200+ }
201+
202+ h3 {
203+ margin-bottom : 8px ;
204+ }
205+
206+ .slot-container ,
207+ .style-options {
208+ background-color : var (--primary-background-color );
209+ padding : 1em ;
210+ }
211+
212+ .disclosure-arrow {
213+ width : 12px ;
214+ height : 12px ;
215+ transform : rotate (-90deg );
216+ transition : transform 0.2s ease-in-out;
217+ }
218+
219+ .disclosure-arrow .open {
220+ transform : rotate (0deg );
221+ }
222+
223+ .labs-icon {
224+ width : 20px ;
225+ height : 20px ;
226+ margin-left : 4px ;
227+ filter : invert (1 );
228+ vertical-align : middle;
229+ }
230+ ` ,
231+ ] ;
125232 }
126233}
0 commit comments