@webqit/oohtml 1.10.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/.gitignore +3 -3
  2. package/LICENSE +21 -0
  3. package/README.md +399 -396
  4. package/dist/context-api.js +2 -0
  5. package/dist/context-api.js.map +7 -0
  6. package/dist/html-imports.js +1 -2
  7. package/dist/html-imports.js.map +3 -3
  8. package/dist/html-modules.js +1 -2
  9. package/dist/html-modules.js.map +3 -3
  10. package/dist/main.js +26 -14
  11. package/dist/main.js.map +3 -3
  12. package/dist/namespaced-html.js +1 -2
  13. package/dist/namespaced-html.js.map +3 -3
  14. package/dist/scoped-js.js +27 -0
  15. package/dist/scoped-js.js.map +7 -0
  16. package/dist/state-api.js +1 -2
  17. package/dist/state-api.js.map +3 -3
  18. package/package.json +76 -76
  19. package/src/context-api/HTMLContext.js +158 -0
  20. package/src/context-api/HTMLContextManager.js +77 -0
  21. package/src/context-api/_ContextRequestEvent.js +26 -0
  22. package/src/context-api/index.js +53 -0
  23. package/src/{namespaced-html/browser-entry.js → context-api/targets.browser.js} +9 -9
  24. package/src/html-imports/_HTMLImportElement.js +216 -0
  25. package/src/html-imports/index.js +92 -557
  26. package/src/{browser-entry.js → html-imports/targets.browser.js} +10 -10
  27. package/src/html-modules/HTMLExportsManager.js +191 -0
  28. package/src/html-modules/_HTMLImportsContext.js +114 -0
  29. package/src/html-modules/index.js +133 -384
  30. package/src/{html-imports/browser-entry.js → html-modules/targets.browser.js} +9 -9
  31. package/src/index.js +34 -39
  32. package/src/namespaced-html/index.js +130 -144
  33. package/src/namespaced-html/targets.browser.js +10 -0
  34. package/src/scoped-js/index.js +382 -0
  35. package/src/scoped-js/targets.browser.js +10 -0
  36. package/src/state-api/index.js +55 -142
  37. package/src/state-api/targets.browser.js +10 -0
  38. package/src/{html-modules/browser-entry.js → targets.browser.js} +10 -10
  39. package/src/util.js +20 -180
  40. package/test/imports.test.js +194 -0
  41. package/test/index.js +119 -0
  42. package/test/modules.test.js +198 -0
  43. package/test/namespaced-html.test.js +50 -0
  44. package/test/scoped-js.js +57 -0
  45. package/test/state-api.test.js +34 -0
  46. package/test/test.html +69 -0
  47. package/dist/subscript.js +0 -15
  48. package/dist/subscript.js.map +0 -7
  49. package/src/state-api/browser-entry.js +0 -10
  50. package/src/subscript/Element.js +0 -103
  51. package/src/subscript/browser-entry.js +0 -10
  52. package/src/subscript/index.js +0 -70
  53. package/test/all.test.js +0 -0
@@ -0,0 +1,216 @@
1
+
2
+ /**
3
+ * @imports
4
+ */
5
+ import { HTMLContextManager } from '../context-api/index.js';
6
+ import _HTMLImportsContext from '../html-modules/_HTMLImportsContext.js';
7
+ import { _ } from '../util.js';
8
+
9
+ /**
10
+ * Creates the HTMLImportElement class.
11
+ *
12
+ * @param Object params
13
+ *
14
+ * @return HTMLImportElement
15
+ */
16
+ export default function( params ) {
17
+ const window = this, { dom } = window.wq;
18
+ const BaseExportElement = params.import.tagName.includes( '-' )
19
+ ? ( params.HTMLImportBaseElement || window.HTMLElement )
20
+ : class {};
21
+ return class HTMLImportElement extends BaseExportElement {
22
+
23
+ /**
24
+ * @instance
25
+ *
26
+ * @param HTMLElement node
27
+ *
28
+ * @returns
29
+ */
30
+ static instance( node ) {
31
+ if ( params.import.tagName.includes( '-' ) && ( node instanceof this ) ) return node;
32
+ return _( node ).get( 'import::instance' ) || new this( node );;
33
+ }
34
+
35
+ /**
36
+ * @constructor
37
+ */
38
+ constructor( ...args ) {
39
+ super();
40
+ // --------
41
+ const el = args[ 0 ] || this;
42
+ _( el ).set( 'import::instance', this );
43
+ Object.defineProperty( this, 'el', { get: () => el, configurable: false } );
44
+
45
+ const priv = {};
46
+ Object.defineProperty( this, '#', { get: () => priv, configurable: false } );
47
+ priv.slottedElements = new Set;
48
+
49
+ priv.setAnchorNode = anchorNode => {
50
+ priv.anchorNode = anchorNode;
51
+ _( anchorNode ).set( 'anchoredNode@imports', this.el );
52
+ };
53
+
54
+ priv.importRequest = ( callback, signal = null ) => {
55
+ const detail = priv.moduleRef && !priv.moduleRef.includes( '#' ) ? `${ priv.moduleRef }#default` : priv.moduleRef;
56
+ const request = _HTMLImportsContext.createRequest( { detail, live: signal && true, signal, selfScoped: true } );
57
+ HTMLContextManager.instance( this.el.isConnected ? this.el.parentNode : priv.anchorNode.parentNode ).ask( request, response => {
58
+ callback( ( response instanceof Set ? new Set( response ) : response ) || [] );
59
+ } );
60
+ };
61
+
62
+ priv.hydrate = ( anchorNode, slottedElements ) => {
63
+ // ----------------
64
+ priv.moduleRef = ( this.el.getAttribute( params.import.attr.moduleref ) || '' ).trim();
65
+ priv.setAnchorNode( anchorNode );
66
+ priv.autoRestore( () => {
67
+ slottedElements.forEach( slottedElement => {
68
+ priv.slottedElements.add( slottedElement );
69
+ _( slottedElement ).set( 'slot@imports', this.el );
70
+ } );
71
+ } );
72
+ // ----------------
73
+ priv.hydrationImportRequest = new AbortController;
74
+ priv.importRequest( modules => {
75
+ if ( priv.originalsRemapped ) { return this.fill( modules ); }
76
+ const identifiersMap = [ ...modules ].map( module => ( { el: module, exportId: module.getAttribute( params.export.attr.exportid ) || 'default', tagName: module.tagName, } ) );
77
+ slottedElements.forEach( slottedElement => {
78
+ const tagName = slottedElement.tagName, exportId = slottedElement.getAttribute( params.export.attr.exportid ) || 'default';
79
+ const originalsMatch = identifiersMap.filter( moduleIdentifiers => tagName === moduleIdentifiers.tagName && exportId === moduleIdentifiers.exportId );
80
+ if ( originalsMatch.length !== 1 ) return;
81
+ _( slottedElement ).set( 'original@imports', originalsMatch[ 0 ].el );
82
+ } );
83
+ priv.originalsRemapped = true;
84
+ }, priv.hydrationImportRequest.signal );
85
+ };
86
+
87
+ priv.autoRestore = ( callback = null ) => {
88
+ priv.autoRestoreRealtime?.disconnect();
89
+ if ( callback ) callback();
90
+ if ( !priv.slottedElements.size ) {
91
+ priv.anchorNode.replaceWith( this.el );
92
+ return;
93
+ }
94
+ const autoRestoreRealtime = dom.realtime( window.document ).observe( [ ...priv.slottedElements ], record => {
95
+ record.exits.forEach( outgoingNode => {
96
+ _( outgoingNode ).delete( 'slot@imports' );
97
+ priv.slottedElements.delete( outgoingNode );
98
+ } );
99
+ if ( !priv.slottedElements.size ) {
100
+ autoRestoreRealtime.disconnect();
101
+ // At this point, ignore if this is a removal involving the whole parent node
102
+ if ( !record.target.isConnected ) return;
103
+ priv.anchorNode.replaceWith( this.el );
104
+ }
105
+ }, { subtree: true, timing: 'sync', generation: 'exits' } );
106
+ priv.autoRestoreRealtime = autoRestoreRealtime;
107
+ };
108
+
109
+ priv.connectedCallback = () => {
110
+ // In case this is DOM node relocation or induced reinsertion into the DOM
111
+ if ( priv.slottedElements.size ) throw new Error( `Illegal reinsertion into the DOM; import slot is not empty!` );
112
+ // Totally initialize this instance?
113
+ if ( !priv.anchorNode ) { priv.setAnchorNode( this.createAnchorNode() ); }
114
+ if ( priv.moduleRefRealtime ) return;
115
+ priv.moduleRefRealtime = dom.realtime( this.el ).attr( params.import.attr.moduleref, ( record, { signal } ) => {
116
+ priv.moduleRef = record.value;
117
+ ;
118
+ // Below, we ignore first restore from hydration
119
+ priv.importRequest( modules => !priv.hydrationImportRequest && this.fill( modules ), signal );
120
+ }, { live: true, timing: 'sync', lifecycleSignals: true } );
121
+ // Must come after
122
+ priv.hydrationImportRequest?.abort();
123
+ priv.hydrationImportRequest = null;
124
+ };
125
+
126
+ priv.disconnectedCallback = () => {
127
+ priv.hydrationImportRequest?.abort();
128
+ priv.hydrationImportRequest = null;
129
+ if ( priv.anchorNode.isConnected ) return;
130
+ priv.moduleRefRealtime?.disconnect();
131
+ priv.moduleRefRealtime = null;
132
+ };
133
+ }
134
+
135
+ /**
136
+ * Creates the slot's anchor node.
137
+ *
138
+ * @return Element
139
+ */
140
+ createAnchorNode() {
141
+ if ( !params.isomorphic ) { return window.document.createTextNode( '' ) }
142
+ return window.document.createComment( this.el.outerHTML );
143
+ }
144
+
145
+ /**
146
+ * Fills the slot with slottableElements
147
+ *
148
+ * @param Iterable slottableElements
149
+ *
150
+ * @return void
151
+ */
152
+ fill( slottableElements ) {
153
+ if ( Array.isArray( slottableElements ) ) { slottableElements = new Set( slottableElements ) }
154
+ this[ '#' ].autoRestore( () => {
155
+ this[ '#' ].slottedElements.forEach( slottedElement => {
156
+ const slottedElementOriginal = _( slottedElement ).get( 'original@imports' );
157
+ // If still available in source, simply leave unchanged
158
+ // otherwise remove it from slot... to reflect this change
159
+ if ( slottableElements.has( slottedElementOriginal ) ) {
160
+ slottableElements.delete( slottedElementOriginal );
161
+ } else {
162
+ this[ '#' ].slottedElements.delete( slottedElement );
163
+ // This removal will not be caught
164
+ slottedElement.remove();
165
+ }
166
+ } );
167
+ // Make sure anchor node is what's in place...
168
+ // not the import element itslef - but all only when we have slottableElements.size
169
+ if ( this.el.isConnected && slottableElements.size ) {
170
+ this.el.replaceWith( this[ '#' ].anchorNode );
171
+ }
172
+ // Insert slottables now
173
+ slottableElements.forEach( slottableElement => {
174
+ // Clone each slottable element and give it a reference to its original
175
+ const slottableElementClone = slottableElement.cloneNode( true );
176
+ // The folllowing references must be set before adding to DODM
177
+ if ( !slottableElementClone.hasAttribute( params.export.attr.exportid ) ) {
178
+ slottableElementClone.toggleAttribute( params.export.attr.exportid, true );
179
+ }
180
+ _( slottableElementClone ).set( 'original@imports', slottableElement );
181
+ _( slottableElementClone ).set( 'slot@imports', this.el );
182
+ this[ '#' ].slottedElements.add( slottableElementClone );
183
+ this[ '#' ].anchorNode.before( slottableElementClone );
184
+ } );
185
+ } );
186
+ }
187
+
188
+ /**
189
+ * Empty slot.
190
+ *
191
+ * @return void
192
+ */
193
+ empty() { this[ '#' ].slottedElements.forEach( slottedElement => slottedElement.remove() ); }
194
+
195
+ /**
196
+ * Returns the slot's anchorNode.
197
+ *
198
+ * @return array
199
+ */
200
+ get anchorNode() { return this[ '#' ].anchorNode; }
201
+
202
+ /**
203
+ * Returns the slot's module reference, if any.
204
+ *
205
+ * @return string
206
+ */
207
+ get moduleRef() { return this[ '#' ].moduleRef; }
208
+
209
+ /**
210
+ * Returns the slot's slotted elements.
211
+ *
212
+ * @return array
213
+ */
214
+ get slottedElements() { return this[ '#' ].slottedElements; }
215
+ }
216
+ }