@webqit/oohtml 3.0.1-0 → 3.0.1-10
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.
- package/README.md +292 -132
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,13 +38,13 @@ This project pursues an object-oriented approach to HTML and implicitly revisits
|
|
|
38
38
|
|
|
39
39
|
## Modular HTML
|
|
40
40
|
|
|
41
|
-
Modular HTML is a markup pattern that lets us write arbitrary markup as self-contained objects - with each *encapsulating*
|
|
41
|
+
Modular HTML is a markup pattern that lets us write arbitrary markup as self-contained objects - with each *encapsulating* their own structure, styling and logic!
|
|
42
42
|
|
|
43
43
|
OOHTML makes this possible in just simple conventions - via two new attributes: `namespace` and `scoped`!
|
|
44
44
|
|
|
45
45
|
### Namespacing
|
|
46
46
|
|
|
47
|
-
Naming things is hard! That's especially so where
|
|
47
|
+
Naming things is hard! That's especially so where you have one global namespace and a miriad of potentially conflicting names to coordinate!
|
|
48
48
|
|
|
49
49
|
Here, we get the `namespace` attribute for designating an element as own naming context for identifiers instead of the global namespace:
|
|
50
50
|
|
|
@@ -148,9 +148,18 @@ Here, we get the `scoped` attribute for *scoping* said element-specific styleshe
|
|
|
148
148
|
let { styleSheets, scripts } = user; // APIs that are analogous to the document.styleSheets, document.scripts properties
|
|
149
149
|
```
|
|
150
150
|
|
|
151
|
+
<details><summary>Learn more</summary>
|
|
152
|
+
|
|
153
|
+
The `scoped` has two effects on the `<script>` element:
|
|
154
|
+
|
|
155
|
+
+ The `this` keyword is a reference to the script's host element
|
|
156
|
+
+ The `<script>` element is executed again on each re-insertion to the DOM
|
|
157
|
+
|
|
158
|
+
</details>
|
|
159
|
+
|
|
151
160
|
## HTML Imports
|
|
152
161
|
|
|
153
|
-
HTML Imports is a realtime module system for HTML
|
|
162
|
+
HTML Imports is a realtime module system for HTML that speaks HTML! Something like it is the [`<defs>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs) and [`<use>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/use) system in SVG.
|
|
154
163
|
|
|
155
164
|
OOHTML makes this possible in just simple conventions - via a new `def` attribute and a complementary new `<import>` element!
|
|
156
165
|
|
|
@@ -366,7 +375,7 @@ setTimeout(() => abortController.abort(), 1000);
|
|
|
366
375
|
|
|
367
376
|
We can defer module loading until we really need them.
|
|
368
377
|
|
|
369
|
-
Here, we get the `loading="lazy"` directive for that; and loading is only then triggered on the first attempt to import
|
|
378
|
+
Here, we get the `loading="lazy"` directive for that; and loading is only then triggered on the first attempt to import their contents:
|
|
370
379
|
|
|
371
380
|
```html
|
|
372
381
|
<!-- Loading doesn't happen until the first time this is being accessed -->
|
|
@@ -570,7 +579,7 @@ console.log(localOrGlobalImport2); // { value: div }
|
|
|
570
579
|
|
|
571
580
|
## Data Binding
|
|
572
581
|
|
|
573
|
-
Data binding is
|
|
582
|
+
Data binding is about declaratively binding the UI to application data, ensuring that the relevant parts of the UI are *automatically* updated as application state changes.
|
|
574
583
|
|
|
575
584
|
OOHTML makes this possible in just simple conventions - via a new comment-based data-binding syntax `<?{ }?>` and a complementary new `binding` attribute! And there's one more: Quantum Scripts which brings the most advanced form of reactivity to HTML!
|
|
576
585
|
|
|
@@ -584,12 +593,42 @@ Here, we get a comment-based data-binding tag `<?{ }?>` which works like regular
|
|
|
584
593
|
<title><?{ app.title }?></title>
|
|
585
594
|
</head>
|
|
586
595
|
<body>
|
|
587
|
-
Hi, I'm <?{
|
|
588
|
-
and here's another way to write the same comment: <!--?{
|
|
596
|
+
Hi, I'm <?{ name ?? 'Default name' }?>!
|
|
597
|
+
and here's another way to write the same comment: <!--?{ cool }?-->
|
|
589
598
|
</body>
|
|
590
599
|
</html>
|
|
591
600
|
```
|
|
592
601
|
|
|
602
|
+
<details><summary>Details</summary>
|
|
603
|
+
|
|
604
|
+
Here, JavaScript references are resolved from the closest node up the document hierarchy that exposes a corresponding *binding* on its Bindings API ([discussed below](#bindings-api)). Thus, the above markup could have an underlying data structure like the below:
|
|
605
|
+
|
|
606
|
+
```js
|
|
607
|
+
document.bind({ name: 'James Boye', cool: '100%', app: { title: 'Demo App' } });
|
|
608
|
+
document.body.bind({ name: 'John Doe' });
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
```js
|
|
612
|
+
document: { name: 'James Boye', cool: '100%', app: { title: 'Demo App' } }
|
|
613
|
+
└── html
|
|
614
|
+
├── head
|
|
615
|
+
└── body: { name: 'John Doe' }
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
Now, the `name` reference remains bound to the `name` *binding* on the `<body>` element until the meaning of "closest node" changes again:
|
|
619
|
+
|
|
620
|
+
```js
|
|
621
|
+
delete document.body.bindings.name;
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
While the `cool` reference remains bound to the `cool` *binding* on the `document` node until the meaning of "closest node" changes again:
|
|
625
|
+
|
|
626
|
+
```js
|
|
627
|
+
document.body.bindings.cool = '200%';
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
</details>
|
|
631
|
+
|
|
593
632
|
<details><summary>With SSR Support</summary>
|
|
594
633
|
|
|
595
634
|
On the server, these data-binding tags would retain their place in the DOM while having their output rendered to their right in a text node.
|
|
@@ -638,11 +677,11 @@ Here, we get the `binding` attribute for a declarative and neat, key/value data-
|
|
|
638
677
|
|
|
639
678
|
| Directive | Type | Usage |
|
|
640
679
|
| :---- | :---- | :---- |
|
|
641
|
-
| `&` | CSS Property | `<div binding="& color:someColor; & backgroundColor:
|
|
680
|
+
| `&` | CSS Property | `<div binding="& color:someColor; & backgroundColor:someBgColor;"></div>` |
|
|
642
681
|
| `%` | Class Name | `<div binding="% active:app.isActive; % expanded:app.isExpanded;"></div>` |
|
|
643
682
|
| `~` | Attribute Name | `<a binding="~ href:person.profileUrl+'#bio'; ~ title:'Click me';"></a>` |
|
|
644
683
|
| | Boolean Attribute | `<a binding="~ ?required:formField.required; ~ ?aria-checked: formField.checked"></a>` |
|
|
645
|
-
| `@` | Structural Directive: | *See
|
|
684
|
+
| `@` | Structural Directive: | *See below* |
|
|
646
685
|
| `@text` | Plain text content | `<span binding="@text:firstName+' '+lastName;"></span>` |
|
|
647
686
|
| `@html` | Markup content | `<span binding="@html: '<i>'+firstName+'</i>';"></span>` |
|
|
648
687
|
| `@items` | A list, with argument in the following format:<br>`<declaration> <of\|in> <iterable> / <importRef>` | *See next two tables* |
|
|
@@ -667,21 +706,160 @@ Here, we get the `binding` attribute for a declarative and neat, key/value data-
|
|
|
667
706
|
|
|
668
707
|
</details>
|
|
669
708
|
|
|
709
|
+
<details><summary>Details</summary>
|
|
710
|
+
|
|
711
|
+
Here, JavaScript references are resolved from the closest node up the document hierarchy that exposes a corresponding *binding* on its Bindings API ([discussed below](#bindings-api)). Thus, the above CSS example code could have an underlying data structure like the below:
|
|
712
|
+
|
|
713
|
+
```js
|
|
714
|
+
document.bind({ someColor: 'green', someBgColor: 'yellow' });
|
|
715
|
+
document.body.bind({ someBgColor: 'silver' });
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
```js
|
|
719
|
+
document: { someColor: 'green', someBgColor: 'yellow' }
|
|
720
|
+
└── html
|
|
721
|
+
├── head
|
|
722
|
+
└── body: { someBgColor: 'silver' }
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
Now, the `someBgColor` reference remains bound to the `someBgColor` *binding* on the `<body>` element until the meaning of "closest node" changes again:
|
|
726
|
+
|
|
727
|
+
```js
|
|
728
|
+
delete document.body.bindings.someBgColor;
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
While the `someColor` reference remains bound to the `someColor` *binding* on the `document` node until the meaning of "closest node" changes again:
|
|
732
|
+
|
|
733
|
+
```js
|
|
734
|
+
document.body.bindings.someColor = 'brown';
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
</details>
|
|
738
|
+
|
|
670
739
|
<details><summary>All in realtime</summary>
|
|
671
740
|
|
|
672
|
-
|
|
741
|
+
Bindings are resolved in realtime! And in fact, for lists, in-place mutations - additions and removals - on the *iteratee* are automatically reflected on the UI!
|
|
673
742
|
|
|
674
743
|
</details>
|
|
675
744
|
|
|
676
745
|
<details><summary>With SSR Support</summary>
|
|
677
746
|
|
|
678
|
-
|
|
747
|
+
For lists, generated item elements are automatically assigned a corresponding index with a `data-index` attribute! This helps in remapping generated item nodes to their respective entry in *iteratee* - universally.
|
|
679
748
|
|
|
680
749
|
</details>
|
|
681
750
|
|
|
682
751
|
### Quantum Scripts
|
|
683
752
|
|
|
684
|
-
|
|
753
|
+
We often still need to write more serious reactive logic on the UI than a declarative data-binding language can provide for. But we shouldn't need to reach for special tooling or some "serious" programming paradigm on top of JavaScript.
|
|
754
|
+
|
|
755
|
+
Here, from the same `<script>` element we already write, we get a direct upgrade path to reactive programming in just an attribute: `quantum`:
|
|
756
|
+
|
|
757
|
+
```html
|
|
758
|
+
<script quantum>
|
|
759
|
+
// Code here
|
|
760
|
+
console.log(this); // window
|
|
761
|
+
</script>
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
```html
|
|
765
|
+
<script type="module" quantum>
|
|
766
|
+
// Code here
|
|
767
|
+
console.log(this); // undefined
|
|
768
|
+
</script>
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
**-->** *which adds up really well with the idea of scoping*:
|
|
772
|
+
|
|
773
|
+
```html
|
|
774
|
+
<main>
|
|
775
|
+
|
|
776
|
+
<script scoped quantum>
|
|
777
|
+
// Code here
|
|
778
|
+
console.log(this); // main
|
|
779
|
+
</script>
|
|
780
|
+
|
|
781
|
+
</main>
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
```html
|
|
785
|
+
<main>
|
|
786
|
+
|
|
787
|
+
<script type="module" scoped quantum>
|
|
788
|
+
// Code here
|
|
789
|
+
console.log(this); // main
|
|
790
|
+
</script>
|
|
791
|
+
|
|
792
|
+
</main>
|
|
793
|
+
```
|
|
794
|
+
|
|
795
|
+
**-->** *with content being whatever you normally would write in a `<script>` element, but without the "manual" work for reactivity*:
|
|
796
|
+
|
|
797
|
+
```html
|
|
798
|
+
<main>
|
|
799
|
+
|
|
800
|
+
<script type="module" scoped quantum>
|
|
801
|
+
import { someAPI } from 'some-module';
|
|
802
|
+
|
|
803
|
+
let clickCount = 0;
|
|
804
|
+
console.log(clickCount);
|
|
805
|
+
someAPI(clickCount);
|
|
806
|
+
|
|
807
|
+
this.addEventListener('click', e => clickCount++);
|
|
808
|
+
</script>
|
|
809
|
+
|
|
810
|
+
</main>
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
**-->** *within which other "live" APIs, like the Namespace API above, fit seamlessly*:
|
|
814
|
+
|
|
815
|
+
```html
|
|
816
|
+
<main namespace>
|
|
817
|
+
|
|
818
|
+
<script scoped quantum>
|
|
819
|
+
if (this.namespace.specialButton) {
|
|
820
|
+
console.log('specialButton present!');
|
|
821
|
+
} else {
|
|
822
|
+
console.log('specialButton not present!');
|
|
823
|
+
}
|
|
824
|
+
let specialButton = this.namespace.specialButton;
|
|
825
|
+
console.log(specialButton);
|
|
826
|
+
</script>
|
|
827
|
+
|
|
828
|
+
</main>
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
```js
|
|
832
|
+
const main = document.querySelector('main');
|
|
833
|
+
const button = document.createElement('button');
|
|
834
|
+
button.id = 'specialButton';
|
|
835
|
+
|
|
836
|
+
const addButton = () => {
|
|
837
|
+
main.appendChild(button);
|
|
838
|
+
setTimeout(removeButton, 5000);
|
|
839
|
+
};
|
|
840
|
+
const removeButton = () => {
|
|
841
|
+
button.remove();
|
|
842
|
+
setTimeout(addButton, 5000);
|
|
843
|
+
};
|
|
844
|
+
```
|
|
845
|
+
|
|
846
|
+
<details><summary>Details</summary>
|
|
847
|
+
|
|
848
|
+
It's Imperative Reactive Programming ([IRP](https://en.wikipedia.org/wiki/Reactive_programming#Imperative)) right there and it's the [Quantum](https://github.com/webqit/quantum-js) runtime extension to JavaScript!
|
|
849
|
+
|
|
850
|
+
Here, the runtime executes your code in a special execution mode that gets literal JavaScript expressions to statically reflect changes. This makes a lot of things possible on the UI! The [Quantum JS](https://github.com/webqit/quantum-js) documentation has a detailed run down.
|
|
851
|
+
|
|
852
|
+
Now, in each case above, reactivity terminates on script's removal from the DOM. And a programmatic termination is sill possible:
|
|
853
|
+
|
|
854
|
+
```js
|
|
855
|
+
const script = document.querySelector('script[quantum]');
|
|
856
|
+
// const script = document.querySelector('main').scripts[0];
|
|
857
|
+
script.abort();
|
|
858
|
+
```
|
|
859
|
+
|
|
860
|
+
But while that is automatic, DOM event handlers bound via `addEventListener()` would still need to be terminated in their own way.
|
|
861
|
+
|
|
862
|
+
</details>
|
|
685
863
|
|
|
686
864
|
## Data Plumbing
|
|
687
865
|
|
|
@@ -782,7 +960,7 @@ Observer.set(element, 'liveProperty'); // Live expressions rerun
|
|
|
782
960
|
|
|
783
961
|
## Polyfill
|
|
784
962
|
|
|
785
|
-
OOHTML is being developed as something to be used today
|
|
963
|
+
OOHTML is being developed as something to be used today—via a polyfill. This is an active and intentional effort that continues to ensure that the project evolves through a practice-driven process.
|
|
786
964
|
|
|
787
965
|
<details><summary>Load from a CDN<br>
|
|
788
966
|
└───────── <a href="https://bundlephobia.com/result?p=@webqit/oohtml"><img align="right" src="https://img.shields.io/bundlephobia/minzip/@webqit/oohtml?label=&style=flat&colorB=black"></a></summary>
|
|
@@ -803,9 +981,8 @@ OOHTML is being developed as something to be used today - via a polyfill.
|
|
|
803
981
|
|
|
804
982
|
</details>
|
|
805
983
|
|
|
806
|
-
<details><summary>
|
|
807
|
-
|
|
808
|
-
To use the polyfill on server-side DOM instances as made possible by libraries like [jsdom](https://github.com/jsdom/jsdom), simply install and initialize the library `@webqit/oohtml` with the DOM instance:
|
|
984
|
+
<details><summary>Install from NPM<br>
|
|
985
|
+
└───────── <a href="https://npmjs.com/package/@webqit/oohtml"><img align="right" src="https://img.shields.io/npm/v/@webqit/oohtml?style=flat&label=&colorB=black"></a></summary>
|
|
809
986
|
|
|
810
987
|
```bash
|
|
811
988
|
npm i @webqit/oohtml
|
|
@@ -819,9 +996,15 @@ import init from '@webqit/oohtml';
|
|
|
819
996
|
init.call( window[, options = {} ]);
|
|
820
997
|
```
|
|
821
998
|
|
|
822
|
-
|
|
999
|
+
└ To use the polyfill on server-side DOM instances as made possible by libraries like [jsdom](https://github.com/jsdom/jsdom), simply install and initialize the library `@webqit/oohtml` with the DOM instance as above.
|
|
1000
|
+
|
|
1001
|
+
└ But all things "SSR" for OOHTML are best left to the [`@webqit/oohtml-ssr`](https://github.com/webqit/oohtml-ssr) package!
|
|
1002
|
+
|
|
1003
|
+
</details>
|
|
1004
|
+
|
|
1005
|
+
<details><summary>Extended usage concepts</summary>
|
|
823
1006
|
|
|
824
|
-
|
|
1007
|
+
If you'll be going ahead to build a real app to see OOHTML in action, you may want to consider also using:
|
|
825
1008
|
|
|
826
1009
|
+ the [`@webqit/oohtml-cli`](https://github.com/webqit/oohtml-cli) package for operating a file-based templating system.
|
|
827
1010
|
|
|
@@ -859,7 +1042,7 @@ Also, if you'll be going ahead to build a real app to see OOHTML in action, you
|
|
|
859
1042
|
|
|
860
1043
|
...still gives the `window` object in the console.
|
|
861
1044
|
|
|
862
|
-
+ **Scoped/Quantum Scripts**. This feature is an extension of [Quantum JS](https://github.com/webqit/quantum-js)
|
|
1045
|
+
+ **Scoped/Quantum Scripts**. This feature is an extension of [Quantum JS](https://github.com/webqit/quantum-js) and the default OOHTML build is based on the [Quantum JS Lite APIs](https://github.com/webqit/quantum-js#quantum-js-lite). Now, while Quantum JS Lite yields faster load times, it also means that `<script quantum></script>` and `<script scoped></script>` elements are parsed "asynchronously", in the same timing as `<script type="module"></script>`!
|
|
863
1046
|
|
|
864
1047
|
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'd need to use the *realtime* OOHTML build:
|
|
865
1048
|
|
|
@@ -917,84 +1100,72 @@ Also, if you'll be going ahead to build a real app to see OOHTML in action, you
|
|
|
917
1100
|
|
|
918
1101
|
## Examples
|
|
919
1102
|
|
|
920
|
-
Here are a few examples in the wide range of use cases these features cover.
|
|
921
|
-
|
|
922
|
-
+ [Example 1: *Single Page Application*](#example-1-single-page-application)
|
|
923
|
-
+ [Example 2: *Multi-Level Namespacing*](#example-2-multi-level-namespacing)
|
|
924
|
-
+ [Example 3: *Dynamic Shadow DOM*](#example-3-dynamic-shadow-dom)
|
|
925
|
-
+ [Example 4: *Declarative Lists*](#example-4-declarative-lists)
|
|
926
|
-
+ [Example 5: *Imperative Lists*](#example-4-imperative-lists)
|
|
1103
|
+
Here are a few examples in the wide range of use cases these features cover. While we'll demonstrate the most basic forms of these scenarios, it takes roughly the same principles to build an intricate form and a highly interactive UI.
|
|
927
1104
|
|
|
928
|
-
|
|
1105
|
+
<details><summary>Example 1: <i>Single Page Application</i><br>
|
|
1106
|
+
└───────── </summary>
|
|
929
1107
|
|
|
930
1108
|
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:
|
|
931
1109
|
|
|
932
|
-
|
|
1110
|
+
**-->** *First, two components that are themselves analogous to a Single File Component ([SFC](https://vuejs.org/guide/scaling-up/sfc.html))*:
|
|
933
1111
|
|
|
934
|
-
|
|
1112
|
+
```html
|
|
1113
|
+
<template def="pages">
|
|
935
1114
|
|
|
936
|
-
|
|
937
|
-
<
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
<header def="header"></header>
|
|
941
|
-
<footer def="footer"></footer>
|
|
942
|
-
</template>
|
|
943
|
-
|
|
944
|
-
<!-- Home Page -->
|
|
945
|
-
<template def="home" extends="layout">
|
|
946
|
-
<main def="main" namespace>
|
|
947
|
-
<h1 id="banner">Home Page</h1>
|
|
948
|
-
<a id="cta" href="#/products">Go to Products</a>
|
|
949
|
-
<template scoped></template>
|
|
950
|
-
<style scoped></style>
|
|
951
|
-
<script scoped></script>
|
|
952
|
-
</main>
|
|
953
|
-
</template>
|
|
954
|
-
|
|
955
|
-
<!-- Products Page -->
|
|
956
|
-
<template def="products" extends="layout">
|
|
957
|
-
<main def="main" namespace>
|
|
958
|
-
<h1 id="banner">Products Page</h1>
|
|
959
|
-
<a id="cta" href="#/home">Go to Home</a>
|
|
960
|
-
<template scoped></template>
|
|
961
|
-
<style scoped></style>
|
|
962
|
-
<script scoped></script>
|
|
963
|
-
</main>
|
|
964
|
-
</template>
|
|
1115
|
+
<template def="layout">
|
|
1116
|
+
<header def="header"></header>
|
|
1117
|
+
<footer def="footer"></footer>
|
|
1118
|
+
</template>
|
|
965
1119
|
|
|
966
|
-
|
|
967
|
-
|
|
1120
|
+
<!-- Home Page -->
|
|
1121
|
+
<template def="home" extends="layout">
|
|
1122
|
+
<main def="main" namespace>
|
|
1123
|
+
<h1 id="banner">Home Page</h1>
|
|
1124
|
+
<a id="cta" href="#/products">Go to Products</a>
|
|
1125
|
+
<template scoped></template>
|
|
1126
|
+
<style scoped></style>
|
|
1127
|
+
<script scoped></script>
|
|
1128
|
+
</main>
|
|
1129
|
+
</template>
|
|
968
1130
|
|
|
969
|
-
|
|
1131
|
+
<!-- Products Page -->
|
|
1132
|
+
<template def="products" extends="layout">
|
|
1133
|
+
<main def="main" namespace>
|
|
1134
|
+
<h1 id="banner">Products Page</h1>
|
|
1135
|
+
<a id="cta" href="#/home">Go to Home</a>
|
|
1136
|
+
<template scoped></template>
|
|
1137
|
+
<style scoped></style>
|
|
1138
|
+
<script scoped></script>
|
|
1139
|
+
</main>
|
|
1140
|
+
</template>
|
|
970
1141
|
|
|
971
|
-
|
|
1142
|
+
</template>
|
|
1143
|
+
```
|
|
972
1144
|
|
|
973
|
-
|
|
1145
|
+
**-->** *Then a 2-line router that alternates the view based on the URL hash*:
|
|
974
1146
|
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
1147
|
+
```html
|
|
1148
|
+
<body importscontext="/pages/home">
|
|
1149
|
+
|
|
1150
|
+
<import ref="#header"></import>
|
|
1151
|
+
<import ref="#main"></import>
|
|
1152
|
+
<import ref="#footer"></import>
|
|
1153
|
+
|
|
1154
|
+
<script>
|
|
1155
|
+
const route = () => { document.body.setAttribute('importscontext', '/pages' + location.hash.substring(1)); };
|
|
1156
|
+
window.addEventListener('hashchange', route);
|
|
1157
|
+
</script>
|
|
1158
|
+
|
|
1159
|
+
</body>
|
|
1160
|
+
```
|
|
989
1161
|
|
|
990
|
-
|
|
1162
|
+
</details>
|
|
991
1163
|
|
|
992
|
-
|
|
1164
|
+
<details><summary>Example 2: <i>Multi-Level Namespacing</i><br>
|
|
1165
|
+
└───────── </summary>
|
|
993
1166
|
|
|
994
1167
|
The following is a Listbox component lifted directly from the [ARIA Authoring Practices Guide (APG)](https://www.w3.org/WAI/ARIA/apg/patterns/listbox/examples/listbox-grouped/#sc_label) but with IDs effectively "contained" at different levels within the component using the `namespace` attribute.
|
|
995
1168
|
|
|
996
|
-
<details><summary>Code</summary>
|
|
997
|
-
|
|
998
1169
|
```html
|
|
999
1170
|
<div namespace class="listbox-area">
|
|
1000
1171
|
<div>
|
|
@@ -1060,75 +1231,65 @@ The following is a Listbox component lifted directly from the [ARIA Authoring Pr
|
|
|
1060
1231
|
|
|
1061
1232
|
</details>
|
|
1062
1233
|
|
|
1063
|
-
|
|
1234
|
+
<details><summary>Example 3: <i>Dynamic Shadow DOM</i><br>
|
|
1235
|
+
└───────── </summary>
|
|
1064
1236
|
|
|
1065
1237
|
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!
|
|
1066
1238
|
|
|
1067
|
-
|
|
1239
|
+
**-->** *First, two layout options defined for the Shadow DOM*:
|
|
1068
1240
|
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
```html
|
|
1072
|
-
<template def="vendor1">
|
|
1073
|
-
|
|
1074
|
-
<template def="components-layout1">
|
|
1075
|
-
<template def="magic-button">
|
|
1076
|
-
<span id="icon"></span> <span id="text"></span>
|
|
1077
|
-
</template>
|
|
1078
|
-
</template>
|
|
1079
|
-
|
|
1080
|
-
<template def="components-layout2">
|
|
1081
|
-
<template def="magic-button">
|
|
1082
|
-
<span id="text"></span> <span id="icon"></span>
|
|
1083
|
-
</template>
|
|
1084
|
-
</template>
|
|
1241
|
+
```html
|
|
1242
|
+
<template def="vendor1">
|
|
1085
1243
|
|
|
1244
|
+
<template def="components-layout1">
|
|
1245
|
+
<template def="magic-button">
|
|
1246
|
+
<span id="icon"></span> <span id="text"></span>
|
|
1086
1247
|
</template>
|
|
1087
|
-
|
|
1248
|
+
</template>
|
|
1088
1249
|
|
|
1089
|
-
|
|
1250
|
+
<template def="components-layout2">
|
|
1251
|
+
<template def="magic-button">
|
|
1252
|
+
<span id="text"></span> <span id="icon"></span>
|
|
1253
|
+
</template>
|
|
1254
|
+
</template>
|
|
1090
1255
|
|
|
1091
|
-
|
|
1256
|
+
</template>
|
|
1257
|
+
```
|
|
1092
1258
|
|
|
1093
|
-
|
|
1259
|
+
**-->** *Next, the Shadow DOM creation that imports its layout from context*:
|
|
1094
1260
|
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
});
|
|
1102
|
-
}
|
|
1261
|
+
```js
|
|
1262
|
+
customElements.define('magic-button', class extends HTMLElement {
|
|
1263
|
+
connectedCallback() {
|
|
1264
|
+
const shadowRoot = this.attachShadow({ mode: 'open' });
|
|
1265
|
+
this.import('@vendor1/magic-button', template => {
|
|
1266
|
+
shadowRoot.appendChild( template.content.cloneNode(true) );
|
|
1103
1267
|
});
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
+ *Then, the part where we just drop the component in "layout" contexts*:
|
|
1268
|
+
}
|
|
1269
|
+
});
|
|
1270
|
+
```
|
|
1109
1271
|
|
|
1110
|
-
|
|
1272
|
+
**-->** *Then, the part where we just drop the component in "layout" contexts*:
|
|
1111
1273
|
|
|
1112
|
-
|
|
1113
|
-
|
|
1274
|
+
```html
|
|
1275
|
+
<div contextname="vendor1" importscontext="/vendor1/components-layout1">
|
|
1114
1276
|
|
|
1115
|
-
|
|
1277
|
+
<magic-button></magic-button>
|
|
1116
1278
|
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1279
|
+
<aside contextname="vendor1" importscontext="/vendor1/components-layout2">
|
|
1280
|
+
<magic-button></magic-button>
|
|
1281
|
+
</aside>
|
|
1120
1282
|
|
|
1121
|
-
|
|
1122
|
-
|
|
1283
|
+
</div>
|
|
1284
|
+
```
|
|
1123
1285
|
|
|
1124
|
-
|
|
1286
|
+
</details>
|
|
1125
1287
|
|
|
1126
|
-
|
|
1288
|
+
<details><summary>Example 4: <i>Declarative Lists</i><br>
|
|
1289
|
+
└───────── </summary>
|
|
1127
1290
|
|
|
1128
1291
|
The following is a hypothetical list page!
|
|
1129
1292
|
|
|
1130
|
-
<details><summary>Code</summary>
|
|
1131
|
-
|
|
1132
1293
|
```html
|
|
1133
1294
|
<section>
|
|
1134
1295
|
|
|
@@ -1145,12 +1306,11 @@ The following is a hypothetical list page!
|
|
|
1145
1306
|
|
|
1146
1307
|
</details>
|
|
1147
1308
|
|
|
1148
|
-
|
|
1309
|
+
<details><summary>Example 5: <i>Imperative Lists</i><br>
|
|
1310
|
+
└───────── </summary>
|
|
1149
1311
|
|
|
1150
1312
|
The following is much like the above, but imperative. Additions and removals on the data items are also statically reflected!
|
|
1151
1313
|
|
|
1152
|
-
<details><summary>Code</summary>
|
|
1153
|
-
|
|
1154
1314
|
```html
|
|
1155
1315
|
<section namespace>
|
|
1156
1316
|
|