@webqit/oohtml 2.1.55 → 2.1.57

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 (2) hide show
  1. package/README.md +205 -153
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -161,18 +161,17 @@ foo.addEventListener('load', loadedCallback);
161
161
  └ *The HTMLImports API for programmatic module import*:
162
162
 
163
163
  ```js
164
- document.import('/foo#fragment1', divElement => {
165
- console.log(divElement); // module:/foo#fragment2, received synchronously
166
- });
164
+ const import1 = document.import('/foo#fragment1');
165
+ console.log(import1); // { value: div }
167
166
  ```
168
167
 
169
168
  ```js
170
- document.import('/foo/nested#fragment2', divElement => {
171
- console.log(divElement); // module:/foo/nested#fragment2;
172
- });
169
+ const import2 = document.import('/foo/nested#fragment2');
170
+ console.log(import2); // { value: div }
173
171
  ```
174
172
 
175
173
  └ *Scoped templates for object-scoped module system*:
174
+ > With an equivalent `Element.prototype.import()` API for accessing scoped modules
176
175
 
177
176
  ```html
178
177
  <section> <!-- object with own modules -->
@@ -191,16 +190,16 @@ document.import('/foo/nested#fragment2', divElement => {
191
190
 
192
191
  ```js
193
192
  // Using the HTMLImports API
194
- document.querySelector('div').import('foo#fragment1', divElement => {
195
- console.log(divElement); // the local module: foo#fragment1
196
- });
193
+ const moduleHost = document.querySelector('div');
194
+ const localImport1 = moduleHost.import('foo#fragment1'); // the local module: foo#fragment1
195
+ console.log(localImport1); // { value: div }
197
196
  ```
198
197
 
199
198
  ```js
200
199
  // Using the HTMLImports API
201
- document.querySelector('div').import('/foo#fragment1', divElement => {
202
- console.log(divElement); // the global module: foo#fragment1
203
- });
200
+ const moduleHost = document.querySelector('div');
201
+ const globalImport1 = moduleHost.import('/foo#fragment1'); // the global module: foo#fragment1
202
+ console.log(globalImport1); // { value: div }
204
203
  ```
205
204
 
206
205
  <details><summary>
@@ -270,6 +269,14 @@ document.import('/foo#fragment1', divElement => {
270
269
  });
271
270
  ```
272
271
 
272
+ ```js
273
+ const import1 = document.import('/foo#fragment1', true);
274
+ console.log(import1); // { value: undefined }
275
+ Observer.observe(import1, 'value', divElement => {
276
+ console.log(divElement); // To be received after remote module has been loaded
277
+ });
278
+ ```
279
+
273
280
  └ *"Imports Contexts" for context-based imports resolution*:
274
281
 
275
282
  ```html
@@ -290,9 +297,9 @@ document.import('/foo#fragment1', divElement => {
290
297
 
291
298
  ```js
292
299
  // Using the HTMLImports API
293
- document.querySelector('section').import('#fragment2', divElement => {
294
- console.log(divElement); // module:/foo/nested#fragment2
295
- });
300
+ const moduleHost = document.querySelector('section');
301
+ const globalImport2 = moduleHost.import('#fragment2'); // module:/foo/nested#fragment2
302
+ console.log(globalImport2); // { value: div }
296
303
  ```
297
304
 
298
305
  └ *"Imports Contexts" with named contexts*:
@@ -314,9 +321,9 @@ document.querySelector('section').import('#fragment2', divElement => {
314
321
 
315
322
  ```js
316
323
  // Using the HTMLImports API
317
- document.querySelector('div').import('@context1#fragment2', divElement => {
318
- console.log(divElement); // module:/foo/nested#fragment2
319
- });
324
+ const moduleHost = document.querySelector('div');
325
+ const globalImport2 = moduleHost.import('@context1#fragment2'); // module:/foo/nested#fragment2
326
+ console.log(globalImport2); // { value: div }
320
327
  ```
321
328
 
322
329
  └ *"Imports Contexts" with context inheritance*:
@@ -356,9 +363,9 @@ document.querySelector('div').import('@context1#fragment2', divElement => {
356
363
 
357
364
  ```js
358
365
  // Using the HTMLImports API
359
- document.querySelector('div').import('#fragment2', divElement => {
360
- console.log(divElement); // the local module: foo#fragment2, and if not found, resolves from context to the module: /bar/nested#fragment2
361
- });
366
+ const moduleHost = document.querySelector('div');
367
+ const localOrGlobalImport2 = moduleHost.import('#fragment2'); // the local module: foo#fragment2, and if not found, resolves from context to the module: /bar/nested#fragment2
368
+ console.log(localOrGlobalImport2); // { value: div }
362
369
  ```
363
370
 
364
371
  </details>
@@ -491,37 +498,7 @@ OOHTML is being developed as something to be used today - via a polyfill.
491
498
 
492
499
  └ This is to be placed early on in the document and should be a classic script without any `defer` or `async` directives!
493
500
 
494
- <details><summary>Want Async Loading?</summary>
495
-
496
- If you must load the script "async", one little trade-off has to be made for `<script scoped>` and `<script stateful>` elements to have them ignored by the browser until the polyfill comes picking them up: *employing a custom MIME type in place of the standard `text/javascript` and `module` types*, in which case, a `<meta name="scoped-js">` element is used to configure the polyfill to honor the custom MIME type:
497
-
498
- ```html
499
- <head>
500
- <meta name="scoped-js" content="script.mimeType=some-mime">
501
- <script async src="https://unpkg.com/@webqit/oohtml/dist/main.js"></script>
502
- </head>
503
- <body>
504
- <script type="some-mime" scoped>
505
- console.log(this); // body
506
- </script>
507
- </body>
508
- ```
509
-
510
- The custom MIME type strategy also comes in as a "fix" for when in a browser or other runtime where the polyfill is not able to intercept `<script scoped>` and `<script stateful>` elements ahead of the runtime - e.g. where...
511
-
512
- ```html
513
- <body>
514
- <script scoped>
515
- console.log(this); // body
516
- </script>
517
- </body>
518
- ```
519
-
520
- ...still gives the `window` object in the console.
521
-
522
- </details>
523
-
524
- For the Scoped Styles feature, you'd also need something like the [samthor/scoped](https://github.com/samthor/scoped) polyfill (more details below):
501
+ └ For the Scoped Styles feature, you'd also need something like the [samthor/scoped](https://github.com/samthor/scoped) polyfill (more details below):
525
502
 
526
503
  ```html
527
504
  <head>
@@ -559,6 +536,34 @@ Also, if you'll be going ahead to build a real app to see OOHTML in action, you
559
536
 
560
537
  <details><summary>Implementation Notes</summary>
561
538
 
539
+ + **Loading Requirements**. As specified above, the OOHTML script tag is to be placed early on in the document and should be a classic script without any `defer` or `async` directives!
540
+
541
+ If you must load the script "async", one little trade-off has to be made for `<script scoped>` and `<script stateful>` elements to have them ignored by the browser until the polyfill comes picking them up: *employing a custom MIME type in place of the standard `text/javascript` and `module` types*, in which case, a `<meta name="scoped-js">` element is used to configure the polyfill to honor the custom MIME type:
542
+
543
+ ```html
544
+ <head>
545
+ <meta name="scoped-js" content="script.mimeType=some-mime">
546
+ <script async src="https://unpkg.com/@webqit/oohtml/dist/main.js"></script>
547
+ </head>
548
+ <body>
549
+ <script type="some-mime" scoped>
550
+ console.log(this); // body
551
+ </script>
552
+ </body>
553
+ ```
554
+
555
+ The custom MIME type strategy also comes in as a "fix" for when in a browser or other runtime where the polyfill is not able to intercept `<script scoped>` and `<script stateful>` elements ahead of the runtime - e.g. where...
556
+
557
+ ```html
558
+ <body>
559
+ <script scoped>
560
+ console.log(this); // body
561
+ </script>
562
+ </body>
563
+ ```
564
+
565
+ ...still gives the `window` object in the console.
566
+
562
567
  + **Scoped/Stateful Scripts**. This feature is an extension of [Stateful JS](https://github.com/webqit/stateful-js). The default OOHTML build is based on the [Stateful JS Lite APIs](https://github.com/webqit/stateful-js#stateful-js-lite) and this means that `<script stateful></script>` and `<script scoped></script>` elements are parsed "asynchronously", in the same timing as `<script type="module"></script>`!
563
568
 
564
569
  This timing works perfectly generally, but if you have a requirment to have classic scripts follow their [native synchronous timing](https://html.spec.whatwg.org/multipage/parsing.html#scripts-that-modify-the-page-as-it-is-being-parsed), then you need to the *realtime* OOHTML build:
@@ -623,70 +628,71 @@ Here are a few examples in the wide range of use cases these features cover.
623
628
  + [Example 2: *Multi-Level Namespacing*](#example-2-multi-level-namespacing)
624
629
  + [Example 3: *Dynamic Shadow DOM*](#example-3-dynamic-shadow-dom)
625
630
  + [Example 4: *List Items*](#example-4-list-items)
631
+ + [Example 5: *Live List*](#example-4-live-list)
626
632
 
627
633
  ### Example 1: *Single Page Application*
628
634
 
629
635
  The following is how something you could call a Single Page Application ([SPA](https://en.wikipedia.org/wiki/Single-page_application)) could be made - with zero tooling:
630
636
 
631
- *First, two components that are themselves analogous to a Single File Component ([SFC](https://vuejs.org/guide/scaling-up/sfc.html))*:
632
-
633
- <details><summary>Code</summary>
634
-
635
- ```html
636
- <template def="pages">
637
+ + *First, two components that are themselves analogous to a Single File Component ([SFC](https://vuejs.org/guide/scaling-up/sfc.html))*:
637
638
 
638
- <template def="layout">
639
- <header def="header"></header>
640
- <footer def="footer"></footer>
641
- </template>
639
+ <details><summary>Code</summary>
642
640
 
643
- <!-- Home Page -->
644
- <template def="home" extends="layout">
645
- <main def="main" namespace>
646
- <h1 id="banner">Home Page</h1>
647
- <a id="cta" href="#/products">Go to Products</a>
648
- <template scoped></template>
649
- <style scoped></style>
650
- <script scoped></script>
651
- </main>
652
- </template>
653
-
654
- <!-- Products Page -->
655
- <template def="products" extends="layout">
656
- <main def="main" namespace>
657
- <h1 id="banner">Products Page</h1>
658
- <a id="cta" href="#/home">Go to Home</a>
659
- <template scoped></template>
660
- <style scoped></style>
661
- <script scoped></script>
662
- </main>
663
- </template>
641
+ ```html
642
+ <template def="pages">
643
+
644
+ <template def="layout">
645
+ <header def="header"></header>
646
+ <footer def="footer"></footer>
647
+ </template>
648
+
649
+ <!-- Home Page -->
650
+ <template def="home" extends="layout">
651
+ <main def="main" namespace>
652
+ <h1 id="banner">Home Page</h1>
653
+ <a id="cta" href="#/products">Go to Products</a>
654
+ <template scoped></template>
655
+ <style scoped></style>
656
+ <script scoped></script>
657
+ </main>
658
+ </template>
659
+
660
+ <!-- Products Page -->
661
+ <template def="products" extends="layout">
662
+ <main def="main" namespace>
663
+ <h1 id="banner">Products Page</h1>
664
+ <a id="cta" href="#/home">Go to Home</a>
665
+ <template scoped></template>
666
+ <style scoped></style>
667
+ <script scoped></script>
668
+ </main>
669
+ </template>
664
670
 
665
- </template>
666
- ```
671
+ </template>
672
+ ```
667
673
 
668
- </details>
674
+ </details>
669
675
 
670
- *Then a 2-line router that alternates the view based on the URL hash*:
676
+ + *Then a 2-line router that alternates the view based on the URL hash*:
671
677
 
672
- <details><summary>Code</summary>
678
+ <details><summary>Code</summary>
673
679
 
674
- ```html
675
- <body importscontext="/pages/home">
676
-
677
- <import ref="#header"></import>
678
- <import ref="#main"></import>
679
- <import ref="#footer"></import>
680
-
681
- <script>
682
- const route = () => { document.body.setAttribute('importscontext', '/pages' + location.hash.substring(1)); };
683
- window.addEventListener('hashchange', route);
684
- </script>
685
-
686
- </body>
687
- ```
680
+ ```html
681
+ <body importscontext="/pages/home">
682
+
683
+ <import ref="#header"></import>
684
+ <import ref="#main"></import>
685
+ <import ref="#footer"></import>
686
+
687
+ <script>
688
+ const route = () => { document.body.setAttribute('importscontext', '/pages' + location.hash.substring(1)); };
689
+ window.addEventListener('hashchange', route);
690
+ </script>
691
+
692
+ </body>
693
+ ```
688
694
 
689
- </details>
695
+ </details>
690
696
 
691
697
  ### Example 2: *Multi-Level Namespacing*
692
698
 
@@ -763,93 +769,139 @@ The following is a Listbox component lifted directly from the [ARIA Authoring Pr
763
769
 
764
770
  The following is a custom element that derives its Shadow DOM from an imported `<tenplate>` element. The idea is to have different Shadow DOM layouts defined and let the "usage" context decide which variant is imported!
765
771
 
766
- *First, two layout options defined for the Shadow DOM*:
772
+ + *First, two layout options defined for the Shadow DOM*:
767
773
 
768
- <details><summary>Code</summary>
774
+ <details><summary>Code</summary>
769
775
 
770
- ```html
771
- <template def="vendor1">
776
+ ```html
777
+ <template def="vendor1">
772
778
 
773
- <template def="components-layout1">
774
- <template def="magic-button">
775
- <span id="icon"></span> <span id="text"></span>
776
- </template>
777
- </template>
779
+ <template def="components-layout1">
780
+ <template def="magic-button">
781
+ <span id="icon"></span> <span id="text"></span>
782
+ </template>
783
+ </template>
778
784
 
779
- <template def="components-layout2">
780
- <template def="magic-button">
781
- <span id="text"></span> <span id="icon"></span>
782
- </template>
783
- </template>
785
+ <template def="components-layout2">
786
+ <template def="magic-button">
787
+ <span id="text"></span> <span id="icon"></span>
788
+ </template>
789
+ </template>
784
790
 
785
- </template>
786
- ```
791
+ </template>
792
+ ```
787
793
 
788
- </details>
794
+ </details>
789
795
 
790
- *Next, the Shadow DOM creation that imports its layout from context*:
796
+ + *Next, the Shadow DOM creation that imports its layout from context*:
791
797
 
792
- <details><summary>Code</summary>
798
+ <details><summary>Code</summary>
793
799
 
794
- ```js
795
- customElements.define('magic-button', class extends HTMLElement {
796
- connectedCallback() {
797
- const shadowRoot = this.attachShadow({ mode: 'open' });
798
- this.import('@vendor1/magic-button', template => {
799
- shadowRoot.appendChild( template.content.cloneNode(true) );
800
+ ```js
801
+ customElements.define('magic-button', class extends HTMLElement {
802
+ connectedCallback() {
803
+ const shadowRoot = this.attachShadow({ mode: 'open' });
804
+ this.import('@vendor1/magic-button', template => {
805
+ shadowRoot.appendChild( template.content.cloneNode(true) );
806
+ });
807
+ }
800
808
  });
801
- }
802
- });
803
- ```
809
+ ```
804
810
 
805
- </details>
811
+ </details>
806
812
 
807
- *Then, the part where we just drop the component in "layout" contexts*:
813
+ + *Then, the part where we just drop the component in "layout" contexts*:
814
+
815
+ <details><summary>Code</summary>
816
+
817
+ ```html
818
+ <div contextname="vendor1" importscontext="/vendor1/components-layout1">
819
+
820
+ <magic-button></magic-button>
821
+
822
+ <aside contextname="vendor1" importscontext="/vendor1/components-layout2">
823
+ <magic-button></magic-button>
824
+ </aside>
825
+
826
+ </div>
827
+ ```
828
+
829
+ </details>
830
+
831
+ ### Example 4: *List Items*
832
+
833
+ The following is a "component" that derives its list items and other reusable snippets from "scoped" `<tenplate>` elements. The idea is to have a "self-contained" component that's all markup-based, not class-based!
808
834
 
809
835
  <details><summary>Code</summary>
810
836
 
811
837
  ```html
812
- <div contextname="vendor1" importscontext="/vendor1/components-layout1">
838
+ <div namespace>
813
839
 
814
- <magic-button></magic-button>
840
+ <ul id="list"></ul>
815
841
 
816
- <aside contextname="vendor1" importscontext="/vendor1/components-layout2">
817
- <magic-button></magic-button>
818
- </aside>
842
+ <template def="item" scoped>
843
+ <li><a>Item</a></li>
844
+ </template>
845
+
846
+ <script scoped>
847
+ // Import item template
848
+ let itemImport = this.import('item');
849
+ let itemTemplate = itemImport.value;
850
+
851
+ // Iterate
852
+ [ 'Item 1', 'Item 2', 'Item 3' ].forEach(entry => {
853
+ const currentItem = itemTemplate.content.cloneNode(true);
854
+ // Add to DOM
855
+ this.namespace.list.appendChild(currentItem);
856
+ // Render
857
+ currentItem.innerHTML = entry;
858
+ });
859
+ </script>
819
860
 
820
861
  </div>
821
862
  ```
822
863
 
823
864
  </details>
824
865
 
825
- ### Example 4: *List Items*
866
+ ### Example 4: *Live List*
826
867
 
827
- The following is a "component" that derives its list items and other reusable snippets from "scoped" `<tenplate>` elements. The idea is to have a "self-contained" component that's all markup-based, not class-based!
868
+ The following is the same list as above but implemented as a live list! Here, we make a few changes: the script element is Stateful; the loop itself now uses the literal `for ... of` construct, [which is capable of rendering live lists](https://github.com/webqit/stateful-js/wiki#with-control-structures), so that any additions and removals on the original list is statically reflected!
828
869
 
829
870
  <details><summary>Code</summary>
830
871
 
831
872
  ```html
832
873
  <div namespace>
833
874
 
834
- <import ref="other"></import>
835
875
  <ul id="list"></ul>
836
- <import ref="other"></import>
837
876
 
838
877
  <template def="item" scoped>
839
- <li>
840
- <a></a>
841
- </li>
842
- </template>
843
-
844
- <template def="other" scoped>
845
- <button>Call to Action >><button>
878
+ <li><a>Item</a></li>
846
879
  </template>
847
880
 
848
881
  <script scoped>
849
- this.import('item', template => {
850
- const clone = template.content.cloneNode(true);
851
- this.namespace.list.appendChild(clone);
852
- });
882
+ // Import item template
883
+ let itemImport = this.import('item');
884
+ let itemTemplate = itemImport.value;
885
+
886
+ // Iterate
887
+ let items = [ 'Item 1', 'Item 2', 'Item 3' ];
888
+ for (let entry of items) {
889
+ const currentItem = itemTemplate.content.cloneNode(true);
890
+ // Add to DOM
891
+ this.namespace.list.appendChild(currentItem);
892
+ // Remove from DOM whenever corresponding entry is removed
893
+ if (typeof entry === 'undefined') {
894
+ currentItem.remove();
895
+ continue;
896
+ }
897
+ // Render
898
+ currentItem.innerHTML = entry;
899
+ }
900
+
901
+ // Add a new entry
902
+ setTimeout(() => items.push('Item 4'), 1000);
903
+ // Remove an new entry
904
+ setTimeout(() => items.pop(), 2000);
853
905
  </script>
854
906
 
855
907
  </div>
package/package.json CHANGED
@@ -14,7 +14,7 @@
14
14
  "wicg-proposal"
15
15
  ],
16
16
  "homepage": "https://webqit.io/tooling/oohtml",
17
- "version": "2.1.55",
17
+ "version": "2.1.57",
18
18
  "license": "MIT",
19
19
  "repository": {
20
20
  "type": "git",