Skip to content

Commit 97f8843

Browse files
committedJun 13, 2024·
feat: remove content property & add shadow mode detection to render property (#258)
BREAKING CHANGE: The `content` property is no longer supported. The `render` property must be used. In some cases, usage of the `shadow` option might be required.
1 parent 36d6e39 commit 97f8843

28 files changed

+616
-434
lines changed
 

‎README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ import Details from "./details.js";
104104
const Home = define({
105105
[router.connect]: { stack: [Details, ...] },
106106
tag: "app-home",
107-
content: () => html`
107+
render: () => html`
108108
<template layout="column">
109109
<h1>Home</h1>
110110
<nav layout="row gap">
@@ -118,7 +118,7 @@ const Home = define({
118118
export define({
119119
tag: "app-router",
120120
stack: router(Home),
121-
content: ({ stack }) => html`
121+
render: ({ stack }) => html`
122122
<template layout="column">
123123
${stack}
124124
</template>
@@ -139,7 +139,7 @@ Create CSS layouts in-place in templates, even without using Shadow DOM, but sti
139139
```javascript
140140
define({
141141
tag: "app-home-view",
142-
content: () => html`
142+
render: () => html`
143143
<template layout="column center gap:2">
144144
<div layout="grow grid:1|max">
145145
<h1>Home</h1>

‎docs/README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ import Details from "./details.js";
110110
const Home = define({
111111
[router.connect]: { stack: [Details, ...] },
112112
tag: "app-home",
113-
content: () => html`
113+
render: () => html`
114114
<template layout="column">
115115
<h1>Home</h1>
116116
<nav layout="row gap">
@@ -124,7 +124,7 @@ const Home = define({
124124
export define({
125125
tag: "app-router",
126126
stack: router(Home),
127-
content: ({ stack }) => html`
127+
render: ({ stack }) => html`
128128
<template layout="column">
129129
${stack}
130130
</template>
@@ -145,7 +145,7 @@ Create CSS layouts in-place in templates, even without using Shadow DOM, but sti
145145
```javascript
146146
define({
147147
tag: "app-home-view",
148-
content: () => html`
148+
render: () => html`
149149
<template layout="column center gap:2">
150150
<div layout="grow grid:1|max">
151151
<h1>Home</h1>

‎docs/component-model/definition.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ import Home from './views/home.js';
145145

146146
const App = {
147147
stack: router(Home),
148-
content: () => html`
148+
render: () => html`
149149
<template layout="column">
150150
...
151151
${stack}

‎docs/component-model/layout-engine.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ The feature supports both `content` and `render` properties of the component's d
2828
```js
2929
define({
3030
tag: "my-app-view",
31-
content: () => html`
31+
render: () => html`
3232
<template layout="column">
3333
...
3434
</template>

‎docs/component-model/structure.md

+92-67
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ The cache mechanism uses equality check to compare values (`nextValue` !== `last
1212

1313
## Reserved Keys
1414

15-
There are three reserved property names in the definition:
15+
There are two reserved property names in the definition:
1616

1717
* `tag` - a string which sets the custom element tag name
18-
* `render` and `content`, which expect the value as a function, and have additional options available
18+
* `render` - expects its value as a function for rendering the internal structure of the custom element
1919

2020
## Translation
2121

@@ -53,7 +53,7 @@ define({
5353
});
5454
```
5555

56-
Usually, the shorthand definition is more readable and less verbose, but the second one gives more control over the property behavior, as it provides additional options.
56+
Usually, the shorthand syntax is more readable and less verbose, but the second one gives more control over the property behavior, as it provides additional options.
5757

5858
## Attributes
5959

@@ -286,21 +286,39 @@ define({
286286
});
287287
```
288288
289-
## `render` & `content`
289+
## Render
290290
291-
The `render` and `content` properties are reserved for the rendering structure of the custom element. The `value` option must be a function, which returns a result of the call to the built-in template engine or a custom update function.
291+
The `render` property is reserved for the creating structure of the custom element.
292292
293-
The library uses internally the `observe` pattern to called function automatically when dependencies change. As the property returns an update function, it can also be called manually, by `el.render()` or `el.content()`.
293+
The `value` option must be a function, which returns a result of the call to the built-in template engine.
294294
295-
> You can use built-in [template engine](/component/templates.md) with those properties without additional code
295+
The library uses the `observe` pattern to call the function automatically when dependencies change. As the property resolves to the update function, it can also be called manually, by `el.render()`.
296296
297-
### Shadow DOM
297+
### Element's Content
298298
299-
Use the `render` key for the internal structure of the custom element, where you can add isolated styles, slot elements, etc.
299+
By default `render` property creates and updates the contents of the custom element:
300300
301301
```javascript
302-
import { define, html } from "hybrids";
302+
define({
303+
tag: "my-element",
304+
name: "",
305+
render: ({ name }) => html`<h1>Hello ${name}!</h1>`,
306+
});
307+
```
303308
309+
```html
310+
<my-element>
311+
<h1>Hello John!</h1>
312+
</my-element>
313+
```
314+
315+
### Shadow DOM
316+
317+
If the root template of the element includes styles or `<slot>` elements, the library renders the content to the shadow DOM:
318+
319+
The template with inline styles:
320+
321+
```javascript
304322
define({
305323
tag: "my-element",
306324
name: "",
@@ -313,88 +331,95 @@ define({
313331
});
314332
```
315333
316-
The `render` property provides unique `options` key for passing additional arguments to `host.attachShadow()` method:
317-
318-
```ts
319-
render: {
320-
value: (host) => { ... },
321-
options: {
322-
mode: "open" | "closed",
323-
delegatesFocus: boolean,
324-
},
325-
...
326-
}
334+
```html
335+
<my-element>
336+
#adopted-stylesheets
337+
#shadow-root
338+
<h1>Hello John!</h1>
339+
</my-element>
327340
```
328341
329-
```javascript
330-
import { define, html } from "hybrids";
342+
The template with `<slot>` element:
331343
344+
```javascript
332345
define({
333346
tag: "my-element",
334-
render: {
335-
value: html`<div>...</div>`,
336-
options: { delegatesFocus: true },
337-
},
347+
render: () => html`
348+
<div id="container">
349+
<slot></slot>
350+
</div>
351+
`,
338352
});
339353
```
340354
341-
### Element's Content
355+
```html
356+
<my-element>
357+
#shadow-root
358+
<div id="container">
359+
<slot></slot>
360+
</div>
361+
</my-element>
362+
```
342363
343-
Use the `content` property for rendering templates in the content of the custom element. By the design, it does not support isolated styles, slot elements, etc.
364+
!> Only the root template can be used to determine the rendering mode implicitly. The nested template with styles does not force rendering in the Shadow DOM.
344365
345-
However, it is the way to build an app-like views structure, which can be rendered as a document content in light DOM. It is easily accessible in developer tools and search engines. For example, form elements (like `<input>`) have to be in the same subtree with the `<form>` element.
366+
### Explicit Mode
346367
347-
```javascript
348-
import { define, html } from "hybrids";
368+
Use the `shadow` option to force one of the rendering modes:
349369
350-
define({
351-
tag: "my-element",
352-
name: "",
353-
content: ({ name }) => html`<h1>Hello ${name}!</h1>`
354-
});
370+
```ts
371+
// Disable Shadow DOM (even if the template includes styles or slot elements)
372+
render: {
373+
value: (host) => html`...`.css`...`,
374+
shadow: false,
375+
...
376+
}
377+
378+
// Force Shadow DOM
379+
render: {
380+
value: (host) => html`...`,
381+
shadow: true,
382+
}
355383
```
356384
357-
### Custom Function
385+
#### Nested Templates
358386
359-
The preferred way is to use a built-in [template engine](/component/templates.md), but you can use any function to update the DOM of the custom element, which accepts the following structure:
387+
If only your nested template includes styles, you must use the `shadow` option to force rendering in the Shadow DOM explicitly:
360388
361389
```javascript
362-
import React from "react";
363-
import ReactDOM from "react-dom";
364-
365-
export default function reactify(fn) {
366-
return (host) => {
367-
// get the component using the fn and host element
368-
const Component = fn(host);
369-
370-
// return the update function
371-
return (host, target) => {
372-
ReactDOM.render(Component, target);
373-
}
374-
}
375-
}
390+
define({
391+
tag: "my-element",
392+
show: false,
393+
render: {
394+
value: ({ show }) => html`
395+
<div id="container">
396+
${show && html`<slot></slot>`}
397+
</div>
398+
`,
399+
shadow: true,
400+
},
401+
});
376402
```
377403
378-
```javascript
379-
import reactify from "./reactify.js";
404+
#### Shadow DOM Options
380405
381-
function MyComponent({ name }) {
382-
return <div>{name}</div>;
383-
}
406+
You can use `shadow` option for passing custom arguments to the `host.attachShadow()` method:
407+
408+
```javascript
409+
import { define, html } from "hybrids";
384410

385411
define({
386412
tag: "my-element",
387-
render: reactify(({ name }) => <MyComponent name={name} />),
388-
})
413+
render: {
414+
value: html`<div>...</div>`,
415+
shadow: { mode: "close", delegatesFocus: true },
416+
},
417+
});
389418
```
390419
391-
The above example uses the [`factory` pattern](#factories), to produce a function, which accepts the host element and returns the update function, which has `host` and `target` arguments. The `target` argument in the update function can be a `host` or `host.shadowRoot` depending on the property name.
392-
393-
!> The other properties from the `host` must be called in the main function body (not in the update function), as only then they will be correctly observed
394-
395420
### Reference Internals
396421
397-
Both `render` and `content` properties can be used to reference internals of the custom element. The DOM update process is asynchronous, so to avoid rendering timing issues, always use a property as a reference to the target element. If the property depending on `render` or `content` is called before the first update, the update will be triggered manually by calling the function.
422+
The `render` property can be used to reference internals of the custom element. The DOM update process is asynchronous, so to avoid rendering timing issues, always use the property as a reference to the target element. If the property depending on `render` is called before the first update, the update will be triggered manually by calling the function.
398423
399424
```javascript
400425
import { define, html } from "hybrids";
@@ -419,8 +444,8 @@ define({
419444
console.log("connected");
420445
return () => console.log("disconnected");
421446
},
422-
observe(host, value, lastValue) {
423-
console.log(`${value} -> ${lastValue}`);
447+
observe(host) {
448+
console.log("rendered");
424449
},
425450
},
426451
});

‎docs/component-model/templates.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,7 @@ The built-in `html.transition` plugin utilizes the [Transition API](https://deve
545545
define({
546546
tag: 'my-app',
547547
stack: router([Home]),
548-
content: ({ stack }) => html`
548+
render: ({ stack }) => html`
549549
<header>...</header>
550550
<main>${stack}</main>
551551
...
@@ -560,7 +560,7 @@ The transition API can be customized by the CSS properties. The DOM elements mig
560560
```javascript
561561
export default define({
562562
tag: 'my-app-home',
563-
content: () => html`
563+
render: () => html`
564564
<template layout="column">
565565
...
566566
<div layout="view:card"></div>

‎docs/getting-started.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Otherwise, you can use it directly from a number of CDNs, which provide a hybrid
2121
define({
2222
tag: "hello-world",
2323
name: '',
24-
content: ({ name }) => html`<p>Hello ${name}!</p>`,
24+
render: ({ name }) => html`<p>Hello ${name}!</p>`,
2525
});
2626
</script>
2727
```

0 commit comments

Comments
 (0)
Please sign in to comment.