Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

State instanceid context #182

Merged
merged 18 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions app/docs/md/conventions/browser.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Write a pure function for returning the HTML markup for the `my-message.mjs` cus

```javascript
export default function MyMessage({ html, state }) {
const { attrs=[] } = state
const { attrs={} } = state
const { message='' } = attrs

return html`<h1>${ message }</h1>`
Expand Down Expand Up @@ -117,12 +117,7 @@ class MyMessageElement extends HTMLElement {
}

html(strings, ...values) {
const collect = []
for (let i = 0; i < strings.length - 1; i++) {
collect.push(strings[i], values[i])
}
collect.push(strings[strings.length - 1])
return collect.join('')
return String.raw({ raw: strings }, ...values)
}

static get observedAttributes() {
Expand Down
77 changes: 77 additions & 0 deletions app/docs/md/elements/state/context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
title: Context
---

The context object enables passing state to child elements without needing to resort to passing attributes down through multiple elements.

## Set parent context

The context object is passed as a key on the state object. Add data to the context object and it will be available to child elements.

[Follow along by checking out the context demo from GitHub →](https://github.com/enhance-dev/context-demo)

Given this markup you can use the context object to pass state directly to a deeply nested child element. Consider the page structure in the example below:

<doc-code filename="app/pages/index.html">

```html
<context-parent>
<my-container>
<my-container>
<my-container>
<context-heading></context-heading>
</my-container>
</my-container>
</my-container>
</context-parent>
```

</doc-code>


Add a heading key to the context object in the parent element:

<doc-code filename="app/pages/index.html">

```javascript
export default function ContextParent({ html, state }) {
const { context } = state
context.heading = 'Heading set via context'
return html`
<style>
:host {
display: block;
height: 100dvh;
padding-top: 3rem;
text-align: center;
font-family: sans-serif;
}
</style>
<slot></slot>
`
}
```

</doc-code>

Render the heading passed via context in the deeply nested child element:
<doc-code filenam="app/elements/context/heading.mjs">

```javascript
export default function ContextHeading({ html, state }) {
const { context } = state
const { heading='Default heading' } = context
return html`
<style>
:host > h1 {
font-size: 1.5rem;
}
</style>
<h1>${heading}</h1>
`
}
```

</doc-code>


6 changes: 4 additions & 2 deletions app/docs/md/elements/state/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,14 @@ export default function MyElement ({ html, state }) {

</doc-code>

The state object contains two top level entries:
The state object contains four top level keys:

- `attrs`, which contains all the key value pairs of attributes passed into your custom element’s instance
- `store`, which contains the global state of your Enhance application
- `instanceID`, which is a unique ID per instance of Custom Element
- `context`, which is an Object that can be used to pass state to child elements to avoid prop drilling

These two different entries allow you to work with both basic and complex state in powerful ways without the need for complex abstractions or third party libraries.
These keys allow you to work with both basic and complex state in powerful ways without the need for complex abstractions or third party libraries.

<doc-callout level="none" mark="✏️">

Expand Down
23 changes: 23 additions & 0 deletions app/docs/md/elements/state/instance-id.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
title: Instance ID
---

`instanceID` is a unique identifier that is generated per Custom Element instance. This enables you to differentiate between multiple instances of the same element on a page. It can also be useful when using a string based diffing library like [Morphdom](https://github.com/patrick-steele-idem/morphdom) which keys on unique identifiers to know when to update versus replace an element.
kristoferjoseph marked this conversation as resolved.
Show resolved Hide resolved

```javascript
export default function MyCard({ html, state }) {
const { attrs={}, instanceID='' } = state
const { content='', heading='' } = attrs

return html`
<figure id="figure-${instanceID}">
<h2>${heading}</h2>
<p>${content}</p>
</figure>
`
}
```

<doc-callout mark="ℹ️">
Pro tip: As in the example above, prefix the id with the element name in order to use the id with multiple child elements.
</doc-callout>
10 changes: 9 additions & 1 deletion app/docs/nav-data.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,15 @@ export const data = [
path: '/docs/elements/state/',
label: 'State',
hasChildren: true,
items: ['attributes', 'store'],
items: [
'attributes',
'store',
{
slug: 'instance-id',
label: 'Instance ID',
},
'context',
],
},
],
},
Expand Down
10 changes: 4 additions & 6 deletions app/elements/docs/theme-toggle.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ export default function DocsThemeToggle({ html }) {
<div class="flex gap-2 align-items-center pb-4 pi-3">
<label title="Light theme" class="cursor-pointer">
<input
id="use-light"
class="hidden"
class="hidden js-use-light"
type="checkbox"
name="light-toggle"
value="light"
Expand All @@ -55,8 +54,7 @@ export default function DocsThemeToggle({ html }) {
</label>
<label title="Dark theme" class="cursor-pointer">
<input
id="use-dark"
class="hidden"
class="hidden js-use-dark"
type="checkbox"
name="dark-toggle"
value="dark"
Expand All @@ -74,8 +72,8 @@ export default function DocsThemeToggle({ html }) {
constructor() {
super()

this.inputs.dark = this.querySelector('input#use-dark')
this.inputs.light = this.querySelector('input#use-light')
this.inputs.dark = this.querySelector('input.js-use-dark')
this.inputs.light = this.querySelector('input.js-use-light')

const modeOverride = window.localStorage.getItem('theme-mode')
if (typeof modeOverride === 'string') this.changeMode(modeOverride)
Expand Down
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "enhance.dev",
"version": "1.16.5",
"version": "1.17.0",
"description": "Enhance landing page, docs, and tutorial",
"type": "module",
"scripts": {
Expand All @@ -13,7 +13,7 @@
"dependencies": {
"@architect/functions": "^7.0.0",
"@begin/data": "^4.0.2",
"@begin/masthead": "github:beginner-corp/masthead",
"@begin/masthead": "github:beginner-corp/masthead#v1.1.0",
"@docsearch/js": "^3.3.0",
"@enhance/arc-plugin-enhance": "^9.1.2",
"@enhance/arc-plugin-styles": "^5.0.0",
Expand Down
1 change: 1 addition & 0 deletions scripts/dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ lowercas(e|ed)
MDN
MDN's
mixi(n|ns)
Morphdom
natively
Netlify
polyfills
Expand Down