mirror of
https://github.com/jlengrand/open-wc.git
synced 2026-03-10 08:31:19 +00:00
chore(scoped-elements): add scoped and not scoped demo
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -11,6 +11,7 @@ coverage/
|
||||
## npm
|
||||
node_modules
|
||||
!packages/es-dev-server/test/**/node_modules
|
||||
!packages/scoped-elements/demo/**/node_modules
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
@@ -43,4 +44,3 @@ local.log
|
||||
|
||||
## generated codelabs
|
||||
docs/.vuepress/public/codelabs
|
||||
|
||||
|
||||
@@ -100,6 +100,11 @@ const sidebar = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Experiments',
|
||||
collapsable: true,
|
||||
children: [['/scoped-elements/', 'Scoped Elements']],
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
@@ -120,6 +125,7 @@ module.exports = {
|
||||
'/demoing/': sidebar,
|
||||
'/publishing/': sidebar,
|
||||
'/automating/': sidebar,
|
||||
'/scoped-elements/': sidebar,
|
||||
'/faq/': [
|
||||
['', 'Faq'],
|
||||
{
|
||||
|
||||
1
docs/scoped-elements/README.md
Symbolic link
1
docs/scoped-elements/README.md
Symbolic link
@@ -0,0 +1 @@
|
||||
../../packages/scoped-elements/README.md
|
||||
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 open-wc
|
||||
Copyright (c) 2020 open-wc
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
SOFTWARE.
|
||||
|
||||
@@ -2,21 +2,23 @@
|
||||
|
||||
[//]: # 'AUTO INSERT HEADER PREPUBLISH'
|
||||
|
||||
Complex Web Component applications often are developed by several teams across
|
||||
organizations. In that scenario is common that shared component libraries are
|
||||
used by teams to create an homogeneous look and feel or just to avoid creating
|
||||
the same components several times, but as those libraries evolve name collision
|
||||
problems between different versions of the same library appear because teams are
|
||||
not able to evolve their code at the same velocity. That causes bottlenecks in
|
||||
software delivery that should be managed by the teams and complex build systems
|
||||
trying to alleviate the problem.
|
||||
:::warning
|
||||
This is an experimental feature use it at your own risk and be sure to understand it's [limitations](#limitations).
|
||||
No bigger applications is using `scoped-elements` - so there is no prove yet if it works in production.
|
||||
This page focuses a lot on in depth explanations as it should help foster a discussion on scoping.
|
||||
:::
|
||||
|
||||
[Scoped Custom Element Registries](https://github.com/w3c/webcomponents/issues/716)
|
||||
is a proposal that will solve this problem, but until it is ready or we could
|
||||
use a polyfill, we have to scope the custom element tag names if we want to use
|
||||
different versions of them in our code. This package allows you to forget about
|
||||
how custom elements are defined, registering and scoping their tag names if it is
|
||||
necessary, avoiding the name collision problem.
|
||||
Complex Web Component applications often are developed by several teams across organizations.
|
||||
In that scenario is common that shared component libraries are used by teams to create an
|
||||
homogeneous look and feel or just to avoid creating the same components several times,
|
||||
but as those libraries evolve problems between different versions of the same library appear
|
||||
as teams are not able to evolve their code at the same velocity.
|
||||
That causes bottlenecks in software delivery that should be managed by the teams and complex
|
||||
build systems trying to alleviate the problem.
|
||||
|
||||
[Scoped Custom Element Registries](https://github.com/w3c/webcomponents/issues/716) is a proposal that will solve this problem,
|
||||
but until it is ready or we could use a polyfill, we have to scope the custom element tag names if we want to use different versions of them in our code.
|
||||
This package allows you to forget about how custom elements are defined, registering and scoping their tag names if it is necessary, avoiding the name collision problem.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -24,6 +26,61 @@ necessary, avoiding the name collision problem.
|
||||
npm i --save @open-wc/scoped-elements
|
||||
```
|
||||
|
||||
## Use case and demos
|
||||
|
||||
Consider the following setup
|
||||
|
||||
- Team Blue own Page A
|
||||
- Team Green own Page B
|
||||
- Team Black own Feature A & B
|
||||
|
||||
1. Everything is good and [your app is live](https://open-wc.org/scoped-elements/demo/before-nesting/) [[code](https://github.com/open-wc/open-wc/tree/master/packages/scoped-elements/demo/before-nesting)] with both pages.
|
||||
2. Team Black releases a new version (2.x) of Feature B which unfortunately needs to be breaking in order to support new use-cases.
|
||||
3. Team Blue (on Page A) does not use any of those new use cases and they have a tight deadline to meet so they can not update right now.
|
||||
4. Team Green (on Page B) has to deliver an important functionality to your end users but they need to upgrade to Feature B 2.x as it can only be solved with this new version.
|
||||
5. As Feature A 1x & 2x are both used in the same app it will get installed nested which will lead to [catastrophic failure]https://open-wc.org/scoped-elements./demo/no-scoping/) [[code](https://github.com/open-wc/open-wc/tree/master/packages/scoped-elements/demo/no-scope)]. Two possible solutions come to mind:
|
||||
1. Synchronizing updates of shared dependencies - e.g. make sure Team Blue & Green always us the same version when releasing. This can be a viable solution however it comes with a high organizational overhead and is hard to scale up (for 10+ teams)
|
||||
2. Temporarily (!) allow to ship similar source code (most breaking releases are not a total rewrite) and scope them via `@open-wc/scoped-elements` => see "fixed" [with-scope](https://open-wc.org/scoped-elements/demo/with-scoping/) [[code](https://github.com/open-wc/open-wc/tree/master/packages/scoped-elements/demo/with-scope)] running with nested dependencies.
|
||||
|
||||
#### Technical explanation of scenario
|
||||
|
||||
The simplified app has the following dependencies
|
||||
|
||||
- app
|
||||
- page-a
|
||||
- feature-a 1.x
|
||||
- feature-b 1.x
|
||||
- page-b
|
||||
- feature-a 2.x
|
||||
- feature-b 1.x
|
||||
|
||||
which leads to the following node_modules tree
|
||||
|
||||
```
|
||||
├── node_modules
|
||||
│ ├── feature-a
|
||||
│ ├── feature-b
|
||||
│ ├── page-a
|
||||
│ └── page-b
|
||||
│ └── node_modules
|
||||
│ └── feature-a
|
||||
├── demo-app.js
|
||||
└── index.html
|
||||
```
|
||||
|
||||
So there are 3 demos:
|
||||
|
||||
1. [before-nesting](https://open-wc.org/scoped-elements/demo/before-nesting/) [[code](https://github.com/open-wc/open-wc/tree/master/packages/scoped-elements/demo/before-nesting)] everything fine as Page A/B both are using the same version of Feature A
|
||||
|
||||
2. [no-scope](https://open-wc.org/scoped-elements/demo/no-scoping/) [[code](https://github.com/open-wc/open-wc/tree/master/packages/scoped-elements/demo/no-scope)] Feature A version 1.x and 2.x are imported via self registering entry points which leads to the following error message as "feature-a" tries to register multiple times
|
||||
|
||||
```
|
||||
Uncaught DOMException: Failed to execute 'define' on 'CustomElementRegistry': the name "feature-a" has already been used with this registry
|
||||
at [...]/node_modules/page-b/node_modules/feature-a/feature-a.js:3:16
|
||||
```
|
||||
|
||||
3. [with-scope](https://open-wc.org/scoped-elements/demo/with-scoping/) [[code](https://github.com/open-wc/open-wc/tree/master/packages/scoped-elements/demo/with-scope)] Successfully "fixing" the demo by using `createScopedHtml` on Page A/B.
|
||||
|
||||
## How it works
|
||||
|
||||
`createScopedHtml` is going to define the custom element classes and creates a
|
||||
@@ -67,7 +124,7 @@ template literal is going to be processed by `lit-html`.
|
||||
```js
|
||||
render() {
|
||||
return html`
|
||||
<my-panel class="my-panel">
|
||||
<my-panel class="panel">
|
||||
<my-button>${this.text}</my-button>
|
||||
</my-panel}>
|
||||
`;
|
||||
@@ -90,7 +147,7 @@ const html = createScopedHtml({
|
||||
export default class MyElement extends LitElement {
|
||||
static get styles() {
|
||||
return css`
|
||||
.my-panel {
|
||||
.panel {
|
||||
padding: 10px;
|
||||
background-color: grey;
|
||||
}
|
||||
@@ -105,7 +162,7 @@ export default class MyElement extends LitElement {
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<my-panel class="my-panel">
|
||||
<my-panel class="panel">
|
||||
<my-button>${this.text}</my-button>
|
||||
</my-panel}>
|
||||
`;
|
||||
@@ -113,47 +170,58 @@ export default class MyElement extends LitElement {
|
||||
}
|
||||
```
|
||||
|
||||
## When to use it
|
||||
## Limitations
|
||||
|
||||
If you are creating a custom element that depend on other custom elements then
|
||||
you should use `@open-wc/scoped-elements` to forget about how those custom
|
||||
elements are defined in the registry and which tags are using.
|
||||
1. Components imported via npm **MUST NOT** be self registering components
|
||||
If a shared component (on npm) does not offer an export to the class alone, without the registration side effect, then this component can not be used. e.g. every component
|
||||
that calls `customElement.define`
|
||||
|
||||
```js
|
||||
// MyElement.js
|
||||
import { LitElement } from 'lit-element';
|
||||
import { createScopedHtml } from '@open-wc/scoped-elements'; // <-- import the autoDefine and html functions
|
||||
import MyButton from './MyButton.js'; // your custom element depends on it
|
||||
```js
|
||||
export class MyEl { ... }
|
||||
customElement.define('my-el', MyEl);
|
||||
```
|
||||
|
||||
const html = createScopedHtml({ 'my-button': MyButton });
|
||||
or uses the typescript decorator `customElement`
|
||||
|
||||
export default class MyElement extends LitElement {
|
||||
render() {
|
||||
return html`
|
||||
<my-button>Click me!</my-button>
|
||||
`;
|
||||
}
|
||||
}
|
||||
```
|
||||
```ts
|
||||
@customElement('my-el')
|
||||
export class MyEl { ... }
|
||||
```
|
||||
|
||||
## When not to use it
|
||||
only side effects free class exports may be used
|
||||
|
||||
If you are developing a custom element that doesn't depend on other custom
|
||||
elements then you shouldn't use it. The reason for that is because there is a
|
||||
small performance penalty for using it, and it's better to avoid it if you can.
|
||||
```js
|
||||
export class MyEl { ... }
|
||||
```
|
||||
|
||||
```js
|
||||
// MyButton.js
|
||||
import { html, LitElement } from 'lit-element';
|
||||
Note: not share or components withing the main app shell are exempted from this rule.
|
||||
|
||||
export default class MyButton extends LitElement {
|
||||
render() {
|
||||
return html`
|
||||
<button><slot></slot></button>
|
||||
`;
|
||||
}
|
||||
}
|
||||
```
|
||||
2. Every (!!!) component with sub component needs to use `scoped-elements`
|
||||
Any import to a self registering component can result in a browser exception - completely breaking the full app.
|
||||
|
||||
3. For now only `lit-html` is supported (other rendering engines could be incorporated)
|
||||
|
||||
4. You can not use tag selectors in css
|
||||
|
||||
```css
|
||||
🚫 my-panel {
|
||||
width: 300px;
|
||||
}
|
||||
✅ .panel {
|
||||
width: 300px;
|
||||
}
|
||||
```
|
||||
|
||||
5. You can not use tag selectors in javascript querySelectors
|
||||
|
||||
```js
|
||||
🚫 this.shadowRoot.querySelector('my-panel');
|
||||
✅ this.shadowRoot.querySelector('.panel');
|
||||
```
|
||||
|
||||
6. Using `scoped-elements` may result in up to 8% of performance degradation
|
||||
7. Loading of duplicate/similar source code (most breaking releases are not a total rewrite) should always be a temporary solution
|
||||
8. A lot of times temporary solutions become more permanent 🙈 Be sure to focus on keeping the time of nested dependencies short.
|
||||
|
||||
## Performance
|
||||
|
||||
@@ -201,7 +269,7 @@ by their support and patience.
|
||||
const editLink = document.querySelector('.edit-link a');
|
||||
if (editLink) {
|
||||
const url = editLink.href;
|
||||
editLink.href = url.substr(0, url.indexOf('/master/')) + '/master/packages/classes-in-html/README.md';
|
||||
editLink.href = url.substr(0, url.indexOf('/master/')) + '/master/packages/scoped-elements/README.md';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
91
packages/scoped-elements/demo/before-nesting/demo-app.js
Normal file
91
packages/scoped-elements/demo/before-nesting/demo-app.js
Normal file
@@ -0,0 +1,91 @@
|
||||
import { render, html } from 'lit-html';
|
||||
|
||||
import 'page-a/page-a.js';
|
||||
import 'page-b/page-b.js';
|
||||
|
||||
class DemoApp extends HTMLElement {
|
||||
set page(newValue) {
|
||||
this.__page = newValue;
|
||||
this.update();
|
||||
}
|
||||
|
||||
get page() {
|
||||
return this.__page;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
this.page = 'A';
|
||||
}
|
||||
|
||||
update() {
|
||||
render(
|
||||
html`
|
||||
<style>
|
||||
:host {
|
||||
display: block;
|
||||
max-width: 680px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
nav {
|
||||
padding: 0 10px 10px 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
border: none;
|
||||
padding: 1rem 2rem;
|
||||
background: #0069ed;
|
||||
color: #fff;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
transition: background 250ms ease-in-out, transform 150ms ease;
|
||||
}
|
||||
|
||||
button:hover,
|
||||
button:focus {
|
||||
background: #0053ba;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: 1px solid #fff;
|
||||
outline-offset: -4px;
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: scale(0.99);
|
||||
}
|
||||
|
||||
button.active {
|
||||
background: #33a43f;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
<h1>Demo App</h1>
|
||||
<nav>
|
||||
<button class=${this.page === 'A' ? 'active' : ''} @click=${() => (this.page = 'A')}>
|
||||
Page A
|
||||
</button>
|
||||
<button class=${this.page === 'B' ? 'active' : ''} @click=${() => (this.page = 'B')}>
|
||||
Page B
|
||||
</button>
|
||||
</nav>
|
||||
${this.page === 'A'
|
||||
? html`
|
||||
<page-a></page-a>
|
||||
`
|
||||
: html`
|
||||
<page-b></page-b>
|
||||
`}
|
||||
`,
|
||||
this.shadowRoot,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('demo-app', DemoApp);
|
||||
12
packages/scoped-elements/demo/before-nesting/index.html
Normal file
12
packages/scoped-elements/demo/before-nesting/index.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<demo-app>Loading App...</demo-app>
|
||||
<script type="module" src="./demo-app.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
3
packages/scoped-elements/demo/before-nesting/node_modules/feature-a/feature-a.js
generated
vendored
Normal file
3
packages/scoped-elements/demo/before-nesting/node_modules/feature-a/feature-a.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import { FeatureA } from './index.js';
|
||||
|
||||
customElements.define('feature-a', FeatureA);
|
||||
13
packages/scoped-elements/demo/before-nesting/node_modules/feature-a/index.js
generated
vendored
Normal file
13
packages/scoped-elements/demo/before-nesting/node_modules/feature-a/index.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
export class FeatureA extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.shadowRoot.innerHTML = `
|
||||
<style> :host { display: block; padding: 5px; background: #b7e57d; }</style>
|
||||
<p>Feature A 1.x</p>
|
||||
`;
|
||||
}
|
||||
}
|
||||
3
packages/scoped-elements/demo/before-nesting/node_modules/feature-b/feature-b.js
generated
vendored
Normal file
3
packages/scoped-elements/demo/before-nesting/node_modules/feature-b/feature-b.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import { FeatureB } from './index.js';
|
||||
|
||||
customElements.define('feature-b', FeatureB);
|
||||
13
packages/scoped-elements/demo/before-nesting/node_modules/feature-b/index.js
generated
vendored
Normal file
13
packages/scoped-elements/demo/before-nesting/node_modules/feature-b/index.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
export class FeatureB extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.shadowRoot.innerHTML = `
|
||||
<style> :host { display: block; padding: 5px; background: #8478e9; }</style>
|
||||
<p>Feature B 1.x</p>
|
||||
`;
|
||||
}
|
||||
}
|
||||
19
packages/scoped-elements/demo/before-nesting/node_modules/page-a/index.js
generated
vendored
Normal file
19
packages/scoped-elements/demo/before-nesting/node_modules/page-a/index.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
import { render, html } from 'lit-html';
|
||||
import 'feature-a/feature-a.js';
|
||||
import 'feature-b/feature-b.js';
|
||||
|
||||
export class PageA extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
render(html`
|
||||
<style>:host { display: block; padding: 10px; border: 2px solid #ccc; }</style>
|
||||
<h3>I am page A</h3>
|
||||
<feature-a></feature-a>
|
||||
<feature-b></feature-b>
|
||||
`, this.shadowRoot);
|
||||
}
|
||||
}
|
||||
3
packages/scoped-elements/demo/before-nesting/node_modules/page-a/page-a.js
generated
vendored
Normal file
3
packages/scoped-elements/demo/before-nesting/node_modules/page-a/page-a.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import { PageA } from './index.js';
|
||||
|
||||
customElements.define('page-a', PageA);
|
||||
19
packages/scoped-elements/demo/before-nesting/node_modules/page-b/index.js
generated
vendored
Normal file
19
packages/scoped-elements/demo/before-nesting/node_modules/page-b/index.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
import { render, html } from 'lit-html';
|
||||
import 'feature-a/feature-a.js';
|
||||
import 'feature-b/feature-b.js';
|
||||
|
||||
export class PageB extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
render(html`
|
||||
<style>:host { display: block; padding: 10px; border: 2px solid #ccc; }</style>
|
||||
<h3>I am page B</h3>
|
||||
<feature-a></feature-a>
|
||||
<feature-b></feature-b>
|
||||
`, this.shadowRoot);
|
||||
}
|
||||
}
|
||||
3
packages/scoped-elements/demo/before-nesting/node_modules/page-b/page-b.js
generated
vendored
Normal file
3
packages/scoped-elements/demo/before-nesting/node_modules/page-b/page-b.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import { PageB } from './index.js';
|
||||
|
||||
customElements.define('page-b', PageB);
|
||||
@@ -0,0 +1,13 @@
|
||||
const { createCompatibilityConfig } = require('@open-wc/building-rollup');
|
||||
|
||||
const configs = createCompatibilityConfig({
|
||||
input: './demo/before-nesting/index.html',
|
||||
});
|
||||
|
||||
module.exports = configs.map(config => ({
|
||||
...config,
|
||||
output: {
|
||||
...config.output,
|
||||
dir: '../../_site/scoped-elements/demo/before-nesting',
|
||||
},
|
||||
}));
|
||||
6
packages/scoped-elements/demo/before-nesting/server.js
Normal file
6
packages/scoped-elements/demo/before-nesting/server.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
rootDir: '../../',
|
||||
appIndex: 'packages/scoped-elements/demo/before-nesting/index.html',
|
||||
nodeResolve: true,
|
||||
open: true,
|
||||
};
|
||||
91
packages/scoped-elements/demo/no-scope/demo-app.js
Normal file
91
packages/scoped-elements/demo/no-scope/demo-app.js
Normal file
@@ -0,0 +1,91 @@
|
||||
import { render, html } from 'lit-html';
|
||||
|
||||
import 'page-a/page-a.js';
|
||||
import 'page-b/page-b.js';
|
||||
|
||||
class DemoApp extends HTMLElement {
|
||||
set page(newValue) {
|
||||
this.__page = newValue;
|
||||
this.update();
|
||||
}
|
||||
|
||||
get page() {
|
||||
return this.__page;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
this.page = 'A';
|
||||
}
|
||||
|
||||
update() {
|
||||
render(
|
||||
html`
|
||||
<style>
|
||||
:host {
|
||||
display: block;
|
||||
max-width: 680px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
nav {
|
||||
padding: 0 10px 10px 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
border: none;
|
||||
padding: 1rem 2rem;
|
||||
background: #0069ed;
|
||||
color: #fff;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
transition: background 250ms ease-in-out, transform 150ms ease;
|
||||
}
|
||||
|
||||
button:hover,
|
||||
button:focus {
|
||||
background: #0053ba;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: 1px solid #fff;
|
||||
outline-offset: -4px;
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: scale(0.99);
|
||||
}
|
||||
|
||||
button.active {
|
||||
background: #33a43f;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
<h1>Demo App</h1>
|
||||
<nav>
|
||||
<button class=${this.page === 'A' ? 'active' : ''} @click=${() => (this.page = 'A')}>
|
||||
Page A
|
||||
</button>
|
||||
<button class=${this.page === 'B' ? 'active' : ''} @click=${() => (this.page = 'B')}>
|
||||
Page B
|
||||
</button>
|
||||
</nav>
|
||||
${this.page === 'A'
|
||||
? html`
|
||||
<page-a></page-a>
|
||||
`
|
||||
: html`
|
||||
<page-b></page-b>
|
||||
`}
|
||||
`,
|
||||
this.shadowRoot,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('demo-app', DemoApp);
|
||||
11
packages/scoped-elements/demo/no-scope/index.html
Normal file
11
packages/scoped-elements/demo/no-scope/index.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<demo-app>Loading App...</demo-app>
|
||||
<script type="module" src="./demo-app.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
3
packages/scoped-elements/demo/no-scope/node_modules/feature-a/feature-a.js
generated
vendored
Normal file
3
packages/scoped-elements/demo/no-scope/node_modules/feature-a/feature-a.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import { FeatureA } from './index.js';
|
||||
|
||||
customElements.define('feature-a', FeatureA);
|
||||
13
packages/scoped-elements/demo/no-scope/node_modules/feature-a/index.js
generated
vendored
Normal file
13
packages/scoped-elements/demo/no-scope/node_modules/feature-a/index.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
export class FeatureA extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.shadowRoot.innerHTML = `
|
||||
<style> :host { display: block; padding: 5px; background: #b7e57d; }</style>
|
||||
<p>Feature A 1.x</p>
|
||||
`;
|
||||
}
|
||||
}
|
||||
3
packages/scoped-elements/demo/no-scope/node_modules/feature-b/feature-b.js
generated
vendored
Normal file
3
packages/scoped-elements/demo/no-scope/node_modules/feature-b/feature-b.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import { FeatureB } from './index.js';
|
||||
|
||||
customElements.define('feature-b', FeatureB);
|
||||
13
packages/scoped-elements/demo/no-scope/node_modules/feature-b/index.js
generated
vendored
Normal file
13
packages/scoped-elements/demo/no-scope/node_modules/feature-b/index.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
export class FeatureB extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.shadowRoot.innerHTML = `
|
||||
<style> :host { display: block; padding: 5px; background: #8478e9; }</style>
|
||||
<p>Feature B 1.x</p>
|
||||
`;
|
||||
}
|
||||
}
|
||||
20
packages/scoped-elements/demo/no-scope/node_modules/page-a/index.js
generated
vendored
Normal file
20
packages/scoped-elements/demo/no-scope/node_modules/page-a/index.js
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
import { render } from 'lit-html';
|
||||
import { createScopedHtml } from '../../../../index.js';
|
||||
import 'feature-a/feature-a.js';
|
||||
import 'feature-b/feature-b.js';
|
||||
|
||||
export class PageA extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
render(html`
|
||||
<style>:host { display: block; padding: 10px; border: 2px solid #ccc; }</style>
|
||||
<h3>I am page A</h3>
|
||||
<feature-a></feature-a>
|
||||
<feature-b></feature-b>
|
||||
`, this.shadowRoot);
|
||||
}
|
||||
}
|
||||
3
packages/scoped-elements/demo/no-scope/node_modules/page-a/page-a.js
generated
vendored
Normal file
3
packages/scoped-elements/demo/no-scope/node_modules/page-a/page-a.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import { PageA } from './index.js';
|
||||
|
||||
customElements.define('page-a', PageA);
|
||||
20
packages/scoped-elements/demo/no-scope/node_modules/page-b/index.js
generated
vendored
Normal file
20
packages/scoped-elements/demo/no-scope/node_modules/page-b/index.js
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
import { render } from 'lit-html';
|
||||
import { createScopedHtml } from '../../../../index.js';
|
||||
import 'feature-a/feature-a.js';
|
||||
import 'feature-b/feature-b.js';
|
||||
|
||||
export class PageB extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
render(html`
|
||||
<style>:host { display: block; padding: 10px; border: 2px solid #ccc; }</style>
|
||||
<h3>I am page B</h3>
|
||||
<feature-a></feature-a>
|
||||
<feature-b></feature-b>
|
||||
`, this.shadowRoot);
|
||||
}
|
||||
}
|
||||
3
packages/scoped-elements/demo/no-scope/node_modules/page-b/node_modules/feature-a/feature-a.js
generated
vendored
Normal file
3
packages/scoped-elements/demo/no-scope/node_modules/page-b/node_modules/feature-a/feature-a.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import { FeatureA } from './index.js';
|
||||
|
||||
customElements.define('feature-a', FeatureA);
|
||||
13
packages/scoped-elements/demo/no-scope/node_modules/page-b/node_modules/feature-a/index.js
generated
vendored
Normal file
13
packages/scoped-elements/demo/no-scope/node_modules/page-b/node_modules/feature-a/index.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
export class FeatureA extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.shadowRoot.innerHTML = `
|
||||
<style> :host { display: block; padding: 5px; background: #e57d7d; }</style>
|
||||
<p>!! Feature A 2.x</p>
|
||||
`;
|
||||
}
|
||||
}
|
||||
3
packages/scoped-elements/demo/no-scope/node_modules/page-b/page-b.js
generated
vendored
Normal file
3
packages/scoped-elements/demo/no-scope/node_modules/page-b/page-b.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import { PageB } from './index.js';
|
||||
|
||||
customElements.define('page-b', PageB);
|
||||
13
packages/scoped-elements/demo/no-scope/rollup.config.js
Normal file
13
packages/scoped-elements/demo/no-scope/rollup.config.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const { createCompatibilityConfig } = require('@open-wc/building-rollup');
|
||||
|
||||
const configs = createCompatibilityConfig({
|
||||
input: './demo/no-scope/index.html',
|
||||
});
|
||||
|
||||
module.exports = configs.map(config => ({
|
||||
...config,
|
||||
output: {
|
||||
...config.output,
|
||||
dir: '../../_site/scoped-elements/demo/no-scope',
|
||||
},
|
||||
}));
|
||||
6
packages/scoped-elements/demo/no-scope/server.js
Normal file
6
packages/scoped-elements/demo/no-scope/server.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
rootDir: '../../',
|
||||
appIndex: 'packages/scoped-elements/demo/no-scope/index.html',
|
||||
nodeResolve: true,
|
||||
open: true,
|
||||
};
|
||||
91
packages/scoped-elements/demo/with-scope/demo-app.js
Normal file
91
packages/scoped-elements/demo/with-scope/demo-app.js
Normal file
@@ -0,0 +1,91 @@
|
||||
import { render, html } from 'lit-html';
|
||||
|
||||
import 'page-a/page-a.js';
|
||||
import 'page-b/page-b.js';
|
||||
|
||||
class DemoApp extends HTMLElement {
|
||||
set page(newValue) {
|
||||
this.__page = newValue;
|
||||
this.update();
|
||||
}
|
||||
|
||||
get page() {
|
||||
return this.__page;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
this.page = 'A';
|
||||
}
|
||||
|
||||
update() {
|
||||
render(
|
||||
html`
|
||||
<style>
|
||||
:host {
|
||||
display: block;
|
||||
max-width: 680px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
nav {
|
||||
padding: 0 10px 10px 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
border: none;
|
||||
padding: 1rem 2rem;
|
||||
background: #0069ed;
|
||||
color: #fff;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
transition: background 250ms ease-in-out, transform 150ms ease;
|
||||
}
|
||||
|
||||
button:hover,
|
||||
button:focus {
|
||||
background: #0053ba;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: 1px solid #fff;
|
||||
outline-offset: -4px;
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: scale(0.99);
|
||||
}
|
||||
|
||||
button.active {
|
||||
background: #33a43f;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
<h1>Demo App</h1>
|
||||
<nav>
|
||||
<button class=${this.page === 'A' ? 'active' : ''} @click=${() => (this.page = 'A')}>
|
||||
Page A
|
||||
</button>
|
||||
<button class=${this.page === 'B' ? 'active' : ''} @click=${() => (this.page = 'B')}>
|
||||
Page B
|
||||
</button>
|
||||
</nav>
|
||||
${this.page === 'A'
|
||||
? html`
|
||||
<page-a></page-a>
|
||||
`
|
||||
: html`
|
||||
<page-b></page-b>
|
||||
`}
|
||||
`,
|
||||
this.shadowRoot,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('demo-app', DemoApp);
|
||||
11
packages/scoped-elements/demo/with-scope/index.html
Normal file
11
packages/scoped-elements/demo/with-scope/index.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<demo-app>Loading App...</demo-app>
|
||||
<script type="module" src="./demo-app.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
3
packages/scoped-elements/demo/with-scope/node_modules/feature-a/feature-a.js
generated
vendored
Normal file
3
packages/scoped-elements/demo/with-scope/node_modules/feature-a/feature-a.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import { FeatureA } from './index.js';
|
||||
|
||||
customElements.define('feature-a', FeatureA);
|
||||
13
packages/scoped-elements/demo/with-scope/node_modules/feature-a/index.js
generated
vendored
Normal file
13
packages/scoped-elements/demo/with-scope/node_modules/feature-a/index.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
export class FeatureA extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.shadowRoot.innerHTML = `
|
||||
<style> :host { display: block; padding: 5px; background: #b7e57d; }</style>
|
||||
<p>Feature A 1.x</p>
|
||||
`;
|
||||
}
|
||||
}
|
||||
3
packages/scoped-elements/demo/with-scope/node_modules/feature-b/feature-b.js
generated
vendored
Normal file
3
packages/scoped-elements/demo/with-scope/node_modules/feature-b/feature-b.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import { FeatureB } from './index.js';
|
||||
|
||||
customElements.define('feature-b', FeatureB);
|
||||
13
packages/scoped-elements/demo/with-scope/node_modules/feature-b/index.js
generated
vendored
Normal file
13
packages/scoped-elements/demo/with-scope/node_modules/feature-b/index.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
export class FeatureB extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.shadowRoot.innerHTML = `
|
||||
<style> :host { display: block; padding: 5px; background: #8478e9; }</style>
|
||||
<p>Feature B 1.x</p>
|
||||
`;
|
||||
}
|
||||
}
|
||||
25
packages/scoped-elements/demo/with-scope/node_modules/page-a/index.js
generated
vendored
Normal file
25
packages/scoped-elements/demo/with-scope/node_modules/page-a/index.js
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
import { render } from 'lit-html';
|
||||
import { createScopedHtml } from '../../../../index.js';
|
||||
import { FeatureA } from 'feature-a/index.js';
|
||||
import { FeatureB } from 'feature-b/index.js';
|
||||
|
||||
const html = createScopedHtml({
|
||||
'feature-a': FeatureA,
|
||||
'feature-b': FeatureB,
|
||||
});
|
||||
|
||||
export class PageA extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
render(html`
|
||||
<style>:host { display: block; padding: 10px; border: 2px solid #ccc; }</style>
|
||||
<h3>I am page A</h3>
|
||||
<feature-a></feature-a>
|
||||
<feature-b></feature-b>
|
||||
`, this.shadowRoot);
|
||||
}
|
||||
}
|
||||
3
packages/scoped-elements/demo/with-scope/node_modules/page-a/page-a.js
generated
vendored
Normal file
3
packages/scoped-elements/demo/with-scope/node_modules/page-a/page-a.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import { PageA } from './index.js';
|
||||
|
||||
customElements.define('page-a', PageA);
|
||||
25
packages/scoped-elements/demo/with-scope/node_modules/page-b/index.js
generated
vendored
Normal file
25
packages/scoped-elements/demo/with-scope/node_modules/page-b/index.js
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
import { render } from 'lit-html';
|
||||
import { createScopedHtml } from '../../../../index.js';
|
||||
import { FeatureA } from 'feature-a/index.js';
|
||||
import { FeatureB } from 'feature-b/index.js';
|
||||
|
||||
const html = createScopedHtml({
|
||||
'feature-a': FeatureA,
|
||||
'feature-b': FeatureB,
|
||||
});
|
||||
|
||||
export class PageB extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
render(html`
|
||||
<style>:host { display: block; padding: 10px; border: 2px solid #ccc; }</style>
|
||||
<h3>I am page B</h3>
|
||||
<feature-a></feature-a>
|
||||
<feature-b></feature-b>
|
||||
`, this.shadowRoot);
|
||||
}
|
||||
}
|
||||
3
packages/scoped-elements/demo/with-scope/node_modules/page-b/node_modules/feature-a/feature-a.js
generated
vendored
Normal file
3
packages/scoped-elements/demo/with-scope/node_modules/page-b/node_modules/feature-a/feature-a.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import { FeatureA } from './index.js';
|
||||
|
||||
customElements.define('feature-a', FeatureA);
|
||||
13
packages/scoped-elements/demo/with-scope/node_modules/page-b/node_modules/feature-a/index.js
generated
vendored
Normal file
13
packages/scoped-elements/demo/with-scope/node_modules/page-b/node_modules/feature-a/index.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
export class FeatureA extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.shadowRoot.innerHTML = `
|
||||
<style> :host { display: block; padding: 5px; background: #e57d7d; }</style>
|
||||
<p>!! Feature A 2.x</p>
|
||||
`;
|
||||
}
|
||||
}
|
||||
3
packages/scoped-elements/demo/with-scope/node_modules/page-b/page-b.js
generated
vendored
Normal file
3
packages/scoped-elements/demo/with-scope/node_modules/page-b/page-b.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import { PageB } from './index.js';
|
||||
|
||||
customElements.define('page-b', PageB);
|
||||
13
packages/scoped-elements/demo/with-scope/rollup.config.js
Normal file
13
packages/scoped-elements/demo/with-scope/rollup.config.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const { createCompatibilityConfig } = require('@open-wc/building-rollup');
|
||||
|
||||
const configs = createCompatibilityConfig({
|
||||
input: './demo/with-scope/index.html',
|
||||
});
|
||||
|
||||
module.exports = configs.map(config => ({
|
||||
...config,
|
||||
output: {
|
||||
...config.output,
|
||||
dir: '../../_site/scoped-elements/demo/with-scope',
|
||||
},
|
||||
}));
|
||||
6
packages/scoped-elements/demo/with-scope/server.js
Normal file
6
packages/scoped-elements/demo/with-scope/server.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
rootDir: '../../',
|
||||
appIndex: 'packages/scoped-elements/demo/with-scope/index.html',
|
||||
nodeResolve: true,
|
||||
open: true,
|
||||
};
|
||||
@@ -14,7 +14,14 @@
|
||||
"author": "open-wc",
|
||||
"homepage": "https://github.com/open-wc/open-wc/",
|
||||
"scripts": {
|
||||
"prepublishOnly": "../../scripts/insert-header.js"
|
||||
"demo-build:before-nesting": "rollup -c demo/before-nesting/rollup.config.js",
|
||||
"demo-build:no-scope": "rollup -c demo/no-scope/rollup.config.js",
|
||||
"demo-build:with-scope": "rollup -c demo/with-scope/rollup.config.js",
|
||||
"prepublishOnly": "../../scripts/insert-header.js",
|
||||
"site:build": "run-p demo-build:*",
|
||||
"start:before-nesting": "es-dev-server -c demo/before-nesting/server.js",
|
||||
"start:no-scope": "es-dev-server -c demo/no-scope/server.js",
|
||||
"start:with-scope": "es-dev-server -c demo/with-scope/server.js"
|
||||
},
|
||||
"files": [
|
||||
"*.d.ts",
|
||||
@@ -32,7 +39,12 @@
|
||||
"lit-html": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@open-wc/testing": "^2.5.0"
|
||||
"@open-wc/testing": "^2.5.0",
|
||||
"@open-wc/building-rollup": "^0.19.1",
|
||||
"es-dev-server": "^1.36.2",
|
||||
"lit-html": "^1.0.0",
|
||||
"npm-run-all": "4.1.3",
|
||||
"rollup": "^1.15.6"
|
||||
},
|
||||
"module": "index.js"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user