feat: scoped styles (#31)
This commit is contained in:
parent
9eae4e5b8a
commit
aee0ec74aa
6 changed files with 116 additions and 6 deletions
42
README.md
42
README.md
|
@ -25,6 +25,7 @@ The result is a reactive UI on property changes. [View on CodePen ↗](https://c
|
|||
1. [`template` vs `render()`](#template-vs-render)
|
||||
1. [Prop access](#prop-access)
|
||||
1. [Alternatives](#alternatives)
|
||||
1. [Styling](#styling)
|
||||
1. [Just the Templating](#just-the-templating)
|
||||
1. [Quick Start Example](#quick-start-example)
|
||||
1. [Life-Cycle Hooks](#life-cycle-hooks)
|
||||
|
@ -194,6 +195,47 @@ The current alternatives are using what `HTMLElement` provides out-of-the-box, w
|
|||
1. `HTMLElement.dataset` for attributes prefixed with `data-*`. Read more about this [on MDN](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset).
|
||||
1. Methods for reading/writing attribute values: `setAttribute(...)` and `getAttribute(...)`; note that managing the attribute names as strings can be difficult as the code grows.
|
||||
|
||||
## Styling
|
||||
|
||||
When using the built-in `html` function for tagged templates, a style object of type `Partial<CSSStyleDeclaration>` can be passed to any element's `style` attribute. This allows for calculated and conditional styles. Read more on style objects [on MDN](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration)
|
||||
|
||||
```js
|
||||
import { WebComponent } from "https://unpkg.com/web-component-base@latest/index.js";
|
||||
|
||||
class StyledElements extends WebComponent {
|
||||
static props = {
|
||||
condition: false,
|
||||
type: "warn",
|
||||
};
|
||||
|
||||
#typeStyles = {
|
||||
warn: {
|
||||
backgroundColor: "yellow",
|
||||
border: "1px solid orange",
|
||||
},
|
||||
error: {
|
||||
backgroundColor: "orange",
|
||||
border: "1px solid red",
|
||||
},
|
||||
};
|
||||
|
||||
get template() {
|
||||
return html`
|
||||
<div
|
||||
style=${{
|
||||
...this.#typeStyles[this.props.type],
|
||||
padding: "1em",
|
||||
}}
|
||||
>
|
||||
<p style=${{ fontStyle: this.props.condition && "italic" }}>Wow!</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("styled-elements", StyledElements);
|
||||
```
|
||||
|
||||
## Just the Templating
|
||||
|
||||
You don't have to extend the whole base class just to use some features. All internals are exposed and usable separately so you can practically build the behavior on your own classes.
|
||||
|
|
BIN
assets/styling.png
Normal file
BIN
assets/styling.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 319 KiB |
12
examples/scoped-styles/index.html
Normal file
12
examples/scoped-styles/index.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>WC demo</title>
|
||||
<script type="module" src="./index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<styled-elements type="warn" condition></styled-elements>
|
||||
</body>
|
||||
</html>
|
39
examples/scoped-styles/index.js
Normal file
39
examples/scoped-styles/index.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
// @ts-check
|
||||
import { WebComponent, html } from "../../src/index.js";
|
||||
|
||||
class StyledElements extends WebComponent {
|
||||
static props = {
|
||||
condition: false,
|
||||
type: "info",
|
||||
};
|
||||
|
||||
#typeStyles = {
|
||||
info: {
|
||||
backgroundColor: "blue",
|
||||
border: "1px solid green",
|
||||
},
|
||||
warn: {
|
||||
backgroundColor: "yellow",
|
||||
border: "1px solid orange",
|
||||
},
|
||||
error: {
|
||||
backgroundColor: "orange",
|
||||
border: "1px solid red",
|
||||
},
|
||||
};
|
||||
|
||||
get template() {
|
||||
return html`
|
||||
<div
|
||||
style=${{
|
||||
...this.#typeStyles[this.props.type],
|
||||
padding: "1em",
|
||||
}}
|
||||
>
|
||||
<p style=${{ fontStyle: this.props.condition && "italic" }}>Wow!</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("styled-elements", StyledElements);
|
|
@ -31,7 +31,7 @@ export class WebComponent extends HTMLElement {
|
|||
|
||||
/**
|
||||
* Read-only string property that represents how the component will be rendered
|
||||
* @returns {string | Node | (string | Node)[]}
|
||||
* @returns {string | any}
|
||||
* @see https://www.npmjs.com/package/web-component-base#template-vs-render
|
||||
*/
|
||||
get template() {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { serialize } from "./serialize.mjs";
|
||||
export function createElement(tree) {
|
||||
if (tree.type === undefined) {
|
||||
if (!tree.type) {
|
||||
if (Array.isArray(tree)) {
|
||||
const frag = document.createDocumentFragment();
|
||||
frag.replaceChildren(...tree.map((leaf) => createElement(leaf)));
|
||||
|
@ -8,16 +9,26 @@ export function createElement(tree) {
|
|||
return document.createTextNode(tree);
|
||||
} else {
|
||||
const el = document.createElement(tree.type);
|
||||
/**
|
||||
* handle props
|
||||
*/
|
||||
if (tree.props) {
|
||||
Object.keys(tree.props).forEach((prop) => {
|
||||
Object.entries(tree.props).forEach(([prop, value]) => {
|
||||
const domProp = prop.toLowerCase();
|
||||
if (domProp in el) {
|
||||
el[domProp] = tree.props[prop];
|
||||
if (domProp === "style" && typeof value === "object" && !!value) {
|
||||
applyStyles(el, value);
|
||||
} else if (prop in el) {
|
||||
el[prop] = value;
|
||||
} else if (domProp in el) {
|
||||
el[domProp] = value;
|
||||
} else {
|
||||
el.setAttribute(prop, tree.props[prop]);
|
||||
el.setAttribute(prop, serialize(value));
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* handle children
|
||||
*/
|
||||
tree.children?.forEach((child) => {
|
||||
const childEl = createElement(child);
|
||||
if (childEl instanceof Node) {
|
||||
|
@ -27,3 +38,9 @@ export function createElement(tree) {
|
|||
return el;
|
||||
}
|
||||
}
|
||||
|
||||
function applyStyles(el, styleObj) {
|
||||
Object.entries(styleObj).forEach(([rule, value]) => {
|
||||
if (rule in el.style && value) el.style[rule] = value;
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue