@@ -111,18 +111,17 @@ export default function dom(
111
111
` ) ;
112
112
}
113
113
114
- if ( generator . stylesheet . hasStyles && options . css !== false ) {
115
- const { css, cssMap } = generator . stylesheet . render ( options . filename ) ;
116
-
117
- const textContent = stringify ( options . dev ?
118
- `${ css } \n/*# sourceMappingURL=${ cssMap . toUrl ( ) } */` :
119
- css , { onlyEscapeAtSymbol : true } ) ;
114
+ const { css, cssMap } = generator . stylesheet . render ( options . filename , ! generator . customElement ) ;
115
+ const styles = generator . stylesheet . hasStyles && stringify ( options . dev ?
116
+ `${ css } \n/*# sourceMappingURL=${ cssMap . toUrl ( ) } */` :
117
+ css , { onlyEscapeAtSymbol : true } ) ;
120
118
119
+ if ( styles && generator . options . css !== false && ! generator . customElement ) {
121
120
builder . addBlock ( deindent `
122
121
function @add_css () {
123
122
var style = @createElement( 'style' );
124
123
style.id = '${ generator . stylesheet . id } -style';
125
- style.textContent = ${ textContent } ;
124
+ style.textContent = ${ styles } ;
126
125
@appendNode( style, document.head );
127
126
}
128
127
` ) ;
@@ -143,95 +142,156 @@ export default function dom(
143
142
? `@proto `
144
143
: deindent `
145
144
{
146
- ${ [ 'destroy' , 'get' , 'fire' , 'observe' , 'on' , 'set' , '_set' , 'teardown ' ]
145
+ ${ [ 'destroy' , 'get' , 'fire' , 'observe' , 'on' , 'set' , 'teardown' , ' _set', '_mount' , '_unmount ']
147
146
. map ( n => `${ n } : @${ n === 'teardown' ? 'destroy' : n } ` )
148
147
. join ( ',\n' ) }
149
148
}` ;
150
149
151
- // TODO deprecate component.teardown()
152
- builder . addBlock ( deindent `
153
- function ${ name } ( options ) {
154
- ${ options . dev &&
155
- `if ( !options || (!options.target && !options._root) ) throw new Error( "'target' is a required option" );` }
156
- this.options = options;
157
- ${ generator . usesRefs && `this.refs = {};` }
158
- this._state = ${ templateProperties . data
159
- ? `@assign( @template.data(), options.data )`
160
- : `options.data || {}` } ;
161
- ${ generator . metaBindings }
162
- ${ computations . length && `this._recompute( {}, this._state, {}, true );` }
163
- ${ options . dev &&
164
- Array . from ( generator . expectedProperties ) . map (
165
- prop =>
166
- `if ( !( '${ prop } ' in this._state ) ) console.warn( "Component was created without expected data property '${ prop } '" );`
167
- ) }
168
- ${ generator . bindingGroups . length &&
169
- `this._bindingGroups = [ ${ Array ( generator . bindingGroups . length )
170
- . fill ( '[]' )
171
- . join ( ', ' ) } ];`}
172
-
173
- this._observers = {
174
- pre: Object.create( null ),
175
- post: Object.create( null )
176
- };
150
+ const constructorBody = deindent `
151
+ ${ options . dev && ! generator . customElement &&
152
+ `if ( !options || (!options.target && !options._root) ) throw new Error( "'target' is a required option" );` }
153
+ this.options = options;
154
+ ${ generator . usesRefs && `this.refs = {};` }
155
+ this._state = ${ templateProperties . data
156
+ ? `@assign( @template.data(), options.data )`
157
+ : `options.data || {}` } ;
158
+ ${ generator . metaBindings }
159
+ ${ computations . length && `this._recompute( {}, this._state, {}, true );` }
160
+ ${ options . dev &&
161
+ Array . from ( generator . expectedProperties ) . map (
162
+ prop =>
163
+ `if ( !( '${ prop } ' in this._state ) ) console.warn( "Component was created without expected data property '${ prop } '" );`
164
+ ) }
165
+ ${ generator . bindingGroups . length &&
166
+ `this._bindingGroups = [ ${ Array ( generator . bindingGroups . length )
167
+ . fill ( '[]' )
168
+ . join ( ', ' ) } ];`}
169
+
170
+ this._observers = {
171
+ pre: Object.create( null ),
172
+ post: Object.create( null )
173
+ };
174
+
175
+ this._handlers = Object.create( null );
176
+ ${ templateProperties . ondestroy && `this._handlers.destroy = [@template.ondestroy]` }
177
+
178
+ this._root = options._root || this;
179
+ this._yield = options._yield;
180
+ this._bind = options._bind;
181
+ ${ generator . slots . size && `this._slotted = options.slots || {};` }
182
+
183
+ ${ generator . customElement ?
184
+ deindent `
185
+ this.attachShadow({ mode: 'open' });
186
+ ${ css && `this.shadowRoot.innerHTML = \`<style>${ options . dev ? `${ css } \n/*# sourceMappingURL=${ cssMap . toUrl ( ) } */` : css } </style>\`;` }
187
+ ` :
188
+ ( generator . stylesheet . hasStyles && options . css !== false &&
189
+ `if ( !document.getElementById( '${ generator . stylesheet . id } -style' ) ) @add_css();` )
190
+ }
177
191
178
- this._handlers = Object.create( null );
179
- ${ templateProperties . ondestroy && `this._handlers.destroy = [@template.ondestroy]` }
192
+ ${ templateProperties . oncreate && `var oncreate = @template.oncreate.bind( this );` }
180
193
181
- this._root = options._root || this;
182
- this._yield = options._yield;
183
- this._bind = options._bind;
184
- ${ generator . slots . size && `this._slotted = options.slots || {};` }
194
+ ${ ( templateProperties . oncreate || generator . hasComponents || generator . hasComplexBindings || generator . hasIntroTransitions ) && deindent `
195
+ if ( !options._root ) {
196
+ this._oncreate = [${ templateProperties . oncreate && `oncreate` } ];
197
+ ${ ( generator . hasComponents || generator . hasComplexBindings ) && `this._beforecreate = [];` }
198
+ ${ ( generator . hasComponents || generator . hasIntroTransitions ) && `this._aftercreate = [];` }
199
+ } ${ templateProperties . oncreate && deindent `
200
+ else {
201
+ this._root._oncreate.push(oncreate);
202
+ }
203
+ ` }
204
+ ` }
185
205
186
- ${ generator . stylesheet . hasStyles &&
187
- options . css !== false &&
188
- `if ( !document.getElementById( '${ generator . stylesheet . id } -style' ) ) @add_css();` }
206
+ ${ generator . slots . size && `this.slots = {};` }
189
207
190
- ${ templateProperties . oncreate && `var oncreate = @template.oncreate.bind ( this );` }
208
+ this._fragment = @create_main_fragment ( this._state, this );
191
209
192
- ${ ( templateProperties . oncreate || generator . hasComponents || generator . hasComplexBindings || generator . hasIntroTransitions ) && deindent `
193
- if ( !options._root ) {
194
- this._oncreate = [${ templateProperties . oncreate && `oncreate` } ];
195
- ${ ( generator . hasComponents || generator . hasComplexBindings ) && `this._beforecreate = [];` }
196
- ${ ( generator . hasComponents || generator . hasIntroTransitions ) && `this._aftercreate = [];` }
197
- } ${ templateProperties . oncreate && deindent `
198
- else {
199
- this._root._oncreate.push(oncreate);
200
- }
210
+ if ( options.target ) {
211
+ ${ generator . hydratable
212
+ ? deindent `
213
+ var nodes = @children( options.target );
214
+ options.hydrate ? this._fragment.claim( nodes ) : this._fragment.create();
215
+ nodes.forEach( @detachNode );
216
+ ` :
217
+ deindent `
218
+ ${ options . dev && `if ( options.hydrate ) throw new Error( 'options.hydrate only works if the component was compiled with the \`hydratable: true\` option' );` }
219
+ this._fragment.create();
201
220
` }
221
+ ${ generator . customElement ?
222
+ `this._mount( options.target, options.anchor || null );` :
223
+ `this._fragment.${ block . hasIntroMethod ? 'intro' : 'mount' } ( options.target, options.anchor || null );` }
224
+
225
+ ${ ( generator . hasComponents || generator . hasComplexBindings || templateProperties . oncreate || generator . hasIntroTransitions ) && deindent `
226
+ ${ generator . hasComponents && `this._lock = true;` }
227
+ ${ ( generator . hasComponents || generator . hasComplexBindings ) && `@callAll(this._beforecreate);` }
228
+ ${ ( generator . hasComponents || templateProperties . oncreate ) && `@callAll(this._oncreate);` }
229
+ ${ ( generator . hasComponents || generator . hasIntroTransitions ) && `@callAll(this._aftercreate);` }
230
+ ${ generator . hasComponents && `this._lock = false;` }
202
231
` }
232
+ }
233
+ ` ;
234
+
235
+ if ( generator . customElement ) {
236
+ const props = generator . props || Array . from ( generator . expectedProperties ) ;
237
+
238
+ builder . addBlock ( deindent `
239
+ class ${ name } extends HTMLElement {
240
+ constructor(options = {}) {
241
+ super();
242
+ ${ constructorBody }
243
+ }
244
+
245
+ static get observedAttributes() {
246
+ return ${ JSON . stringify ( props ) } ;
247
+ }
203
248
204
- ${ generator . slots . size && `this.slots = {};` }
205
-
206
- this._fragment = @create_main_fragment( this._state, this );
207
-
208
- if ( options.target ) {
209
- ${ generator . hydratable
210
- ? deindent `
211
- var nodes = @children( options.target );
212
- options.hydrate ? this._fragment.claim( nodes ) : this._fragment.create();
213
- nodes.forEach( @detachNode );
214
- ` :
215
- deindent `
216
- ${ options . dev && `if ( options.hydrate ) throw new Error( 'options.hydrate only works if the component was compiled with the \`hydratable: true\` option' );` }
217
- this._fragment.create();
218
- ` }
219
- this._fragment.${ block . hasIntroMethod ? 'intro' : 'mount' } ( options.target, options.anchor || null );
249
+ ${ props . map ( prop => deindent `
250
+ get ${ prop } () {
251
+ return this.get('${ prop } ');
252
+ }
253
+
254
+ set ${ prop } (value) {
255
+ this.set({ ${ prop } : value });
256
+ }
257
+ ` ) . join ( '\n\n' ) }
258
+
259
+ ${ generator . slots . size && deindent `
260
+ connectedCallback() {
261
+ Object.keys(this._slotted).forEach(key => {
262
+ this.appendChild(this._slotted[key]);
263
+ });
264
+ }` }
265
+
266
+ attributeChangedCallback ( attr, oldValue, newValue ) {
267
+ this.set({ [attr]: newValue });
268
+ }
220
269
}
221
270
222
- ${ ( generator . hasComponents || generator . hasComplexBindings || templateProperties . oncreate || generator . hasIntroTransitions ) && deindent `
223
- if ( !options._root ) {
224
- ${ generator . hasComponents && `this._lock = true;` }
225
- ${ ( generator . hasComponents || generator . hasComplexBindings ) && `@callAll(this._beforecreate);` }
226
- ${ ( generator . hasComponents || templateProperties . oncreate ) && `@callAll(this._oncreate);` }
227
- ${ ( generator . hasComponents || generator . hasIntroTransitions ) && `@callAll(this._aftercreate);` }
228
- ${ generator . hasComponents && `this._lock = false;` }
271
+ customElements.define('${ generator . tag } ', ${ name } );
272
+ @assign( ${ prototypeBase } , ${ proto } , {
273
+ _mount(target, anchor) {
274
+ this._fragment.${ block . hasIntroMethod ? 'intro' : 'mount' } (this.shadowRoot, null);
275
+ target.insertBefore(this, anchor);
276
+ },
277
+
278
+ _unmount() {
279
+ this.parentNode.removeChild(this);
229
280
}
230
- ` }
231
- }
281
+ });
282
+ ` ) ;
283
+ } else {
284
+ builder . addBlock ( deindent `
285
+ function ${ name } ( options ) {
286
+ ${ constructorBody }
287
+ }
232
288
233
- @assign( ${ prototypeBase } , ${ proto } );
289
+ @assign( ${ prototypeBase } , ${ proto } );
290
+ ` ) ;
291
+ }
234
292
293
+ // TODO deprecate component.teardown()
294
+ builder . addBlock ( deindent `
235
295
${ options . dev && deindent `
236
296
${ name } .prototype._checkReadOnly = function _checkReadOnly ( newState ) {
237
297
${ Array . from ( generator . readonly ) . map (
0 commit comments