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

feature: element-based API #25

Open
trusktr opened this issue Aug 3, 2024 · 2 comments
Open

feature: element-based API #25

trusktr opened this issue Aug 3, 2024 · 2 comments

Comments

@trusktr
Copy link

trusktr commented Aug 3, 2024

For example, I think it would be easier to use context with any custom element system regardless of the authoring experience if users could use elements, because all authoring experiences standardize on HTML.

As an example, solid-element allows writing custom elements with function instead of class, so it is impossible to apply a class mixin to a custom element written with solid-element.

However, providing an element-based API would make context fully agnostic of element authoring patterns.

Here's a code sample. This next piece of code is higher up in the tree in some ancestor custom element:

// Let's assume we're using JSX for templating, but the elements being used as custom elements,
// and we're using a function-based custom element authoring library (f.e. solid-element, enhance, etc):
customElement('higher-up-element', function () {
  return <context-provider name="foo-context" value={anyValue}>
    <any-other-element />
  </context-provider>
})

Now here's the tree inside of any-other-element:

// Let's assume we're using JSX for templating, but the elements being used as custom elements.
// Also for sake of example, this is function-based element f.e. solid-element, enhance, etc
customElement('any-other-element', function () {
  const [fooContext, setFooContext] = createSignal(null)

  createEffect(() => {
    // re-runs any time the context changes because fooContext is reactive
    console.log('foo context', fooContext())
  })

  return <context-consumer name="foo-context" receive={value => setFooContext(value)}>
    <div>context value: {fooContext()}</div>
  </context-provider>
})

As we can see by the example,

  • the end user's ability to use context was not limited to class-based custom elements.
  • the pattern can even be used with custom elements in any other framework (React, Vue, Svelte, Solid, Angular, etc) whereas the class mixin format can't

React example:

export function MyComponent() {
  // ... use hooks here ...
  const [fooContext, setFooContext] = useState(null)

  return <context-consumer name="foo-context" receive={value => setFooContext(value)}>
    <div>context value: {fooContext}</div>
  </context-provider>
}

etc.

Some custom element libraries may ship with their own set of context to make composition and mixing and matching of their features flexible, and regardless if we're in React/Vue/Svelte/etc we'd want to be able to configure and use that custom element library.

For example, foo-context and any-other-element might both come from a library. A user would be able to configure the elements how they see fit:

// f.e. Preact code:
return <context-provider name="foo-context" value={fooValue}>
  <any-other-element />
  <any-other-element />
  <any-other-element />
</context-provider>

vs

// f.e. Preact code:
return (
  <context-provider name="foo-context" value={fooValue1}>
    <any-other-element />
  </context-provider>
  <context-provider name="foo-context" value={fooValue2}>
    <any-other-element />
  </context-provider>
  <context-provider name="foo-context" value={fooValue3}>
    <any-other-element />
  </context-provider>
)

A custom element library could also abstract it more, as needed, regardless of end user framework:

<!-- f.e. Vue code: -->
<template>
  <foo-context :value="fooValue1">
    <any-other-element />
  </foo-context>
  <foo-context :value="fooValue2">
    <any-other-element />
    <any-other-element />
  </foo-context>
</template>

<script setup>...</script>
@trusktr
Copy link
Author

trusktr commented Aug 3, 2024

Maybe this is worthy of discussion for the actual context protocol itself, so I opened an issue and linked it to here for more details:

@trusktr
Copy link
Author

trusktr commented Aug 3, 2024

I just realized too that an element-based API could provide nide type safety in TypeScript-powered templating. All frameworks have the machinery for type definitions of custom elements and their props (typically via JSX types even if the framework's syntax is not JSX). This is nice because a CE lib author can add a JSX type definition for an element such as <foo-context> along with the type for its value prop/attribute, and framework users will have type checking in their type-capable template systems. It requires multiple JSX definitions though, as frameworks have varying shapes for their JSX interfaces.

As an example, I defined the Solid JSX types for Lume's <lume-camera-rig> element here, while the React JSX types are here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant