Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/stagas/mixter
A Web Components framework.
https://github.com/stagas/mixter
custom-element framework jsx ui vdom web-components
Last synced: 3 months ago
JSON representation
A Web Components framework.
- Host: GitHub
- URL: https://github.com/stagas/mixter
- Owner: stagas
- License: mit
- Created: 2022-03-29T07:33:39.000Z (almost 3 years ago)
- Default Branch: main
- Last Pushed: 2022-04-16T14:20:39.000Z (almost 3 years ago)
- Last Synced: 2024-10-12T17:58:15.875Z (4 months ago)
- Topics: custom-element, framework, jsx, ui, vdom, web-components
- Language: TypeScript
- Homepage:
- Size: 320 KB
- Stars: 4
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
mixterA Web Components framework.
npm i mixter
pnpm add mixter
yarn add mixter
## Examples
#
simple
- #
view source
example/simple.tsx
```tsx
/** @jsxImportSource mixter/jsx */
import { create } from 'mixter'
import { jsx } from 'mixter/jsx'
export const App = create(
class {
who = 'world'
},
({ $ }) => {
const { render } = jsx($)
render(({ who }) =>
}
)
```
todo-app
-
{todo.done
? {todo.name}
: todo.name}
Try it live
#view source
example/todo-app.tsx
```tsx
/** @jsxImportSource mixter/jsx */
import { attrs, css, event, events, mixter, props, shadow, state } from 'mixter'
import { jsx, refs } from 'mixter/jsx'
type Todo = {
name: string
done: boolean
}
export class TodoApp extends mixter(
// Extend basic HTMLElement
HTMLElement,
// Attach ShadowRoot
shadow(),
// Declare the events we will be emitting, this allows for
// `el.onsomeevent = fn` to be statically typed
events<{
done: CustomEvent<{ todo: Todo }>
}>(),
// Element attributes, can be String, Number, Boolean
attrs(
class {
name = 'My todos!'
background = '#446'
todoColor = '#fff'
doneColor = '#999'
}
),
// Declare properties, can be any type
props(
class {
form?: HTMLFormElement
textInput?: HTMLInputElement
todos: Todo[] = [
{ name: 'create todo list', done: true },
{ name: 'wow todo list', done: false },
{ name: 'so much todo', done: false },
]
onTodoCleanup?: () => void
onTodoAdd?: () => void
onTodoChange?: (todo: Todo) => (
e: Event & { currentTarget: HTMLInputElement },
) => void
}
),
// Reactive state handler
state(({ $, effect, reduce }) => {
// Use jsx, returns the render function which acts like a `reduce`
// that instead renders on the root element.
const { render } = jsx($)
// Use refs, `ref.someElement` can now be passed to `ref=` attributes in JSX.
// Refs are bidirectional, meaning if they already have a reference, passing them to
// a JSX element will "give" that element by reference, instead of filling "from" it.
const { ref } = refs($)
$.onTodoAdd = reduce(({ textInput, todos }) => (event().prevent.stop(() => {
$.todos = [mixter.todos, { name: textInput.value, done: false }]
textInput.value = ''
textInput.focus()
// initialized with a noop function otherwise the render() below will never "fire"
// because we are in its dependencies and our dependency `textInput` is
// assigned inside it.
})), () => {})
$.onTodoChange = reduce(({ host, todos }) => (todo => (e => {
todo.done = e.currentTarget.checked
$.todos = [mixter.todos]
if (todo.done) host.dispatch('done', { todo })
})))
$.onTodoCleanup = reduce(({ todos }) => (() => {
$.todos = [mixter.todos.filter(todo => !todo.done)]
}), () => {})
effect(({ todos }) => {
if (todos.length > 0 && todos.filter(todo => !todo.done).length === 0)
alert('All done! Congrats!')
})
render((
{
name,
background,
doneColor,
onTodoAdd,
onTodoChange,
onTodoCleanup,
todoColor,
todos,
},
) => (
<>
{css`
width: 250px;
padding: 10px;
display: inline-flex;
flex-flow: column nowrap;
align-items: center;
font-family: 'Ubuntu Mono', monospace;
background: ${background};
color: ${todoColor};
.done {
color: ${doneColor};
}
`()}
{name}
Add
{todos.map(todo => (
))}
{todos.some(todo => todo.done)
&& Cleanup}
>
))
})
) {}
customElements.define('todo-app', TodoApp)
const todoApp = new TodoApp()
document.body.appendChild(todoApp)
todoApp.ondone = ({ detail: { todo } }) => {
console.log('done', todo)
// change background to a random hue every time we finish a todo
todoApp.background = `hsl(${Math.random() * 360}, 30%, 20%)`
}
```
web
- #
view source
example/web.ts
```ts
import { App } from './simple'
customElements.define('x-app', App)
const app = new App()
document.body.appendChild(app)
```
## API
# EventHandler
src/mixins/events.ts#L3
# e
E & {
# currentTarget
src/mixins/events.ts#L4
target
src/mixins/events.ts#L4 Element
EventHandler(e) =>
- void
Class
src/types.ts#L1 # (args) src/types.ts#L1
Context
src/mixins/state.ts#L24 {
#
cleanup
() src/mixins/state.ts#L26
cleanup() =>
- void
effect
(fn, cb) src/mixins/state.ts#L27 # cb
(value) # value
any
cb(value) =>
- boolean | void
effect(fn, cb) =>
- void
reduce
(fn, initial) src/mixins/state.ts#L28 # initial
reduce<R>(fn, initial) =>
} #ContextFn
src/mixins/state.ts#L6 # Ctor
src/types.ts#L3 # (args) src/types.ts#L3
Deps
src/mixins/state.ts#L8 # EventMap
src/mixins/events.ts#L7 [K in keyof P ]: EventHandler<InstanceType<T>, P [K]>
Fx
src/mixins/state.ts#L12 {
# dispose
src/mixins/state.ts#L15
# ()
() =>
- void
null
# initial
src/mixins/state.ts#L17 any
keys
src/mixins/state.ts#L19 Set<keyof T>
pass
src/mixins/state.ts#L21 boolean
target
src/mixins/state.ts#L18 keyof T
type
src/mixins/state.ts#L16 any
values
src/mixins/state.ts#L20 # cb
(value) src/mixins/state.ts#L14 # fn
(deps) src/mixins/state.ts#L13 } # InlineEventMap
src/mixins/events.ts#L14 [K in keyof P ]: EventHandler<InstanceType<T>, P [K]>
Mixable
src/types.ts#L5 {} & Omit<T, "constructor"
>
Mixin
src/types.ts#L9 Mixable<CustomElementConstructor>
PropsType
src/mixins/attrs.ts#L6 [K in keyof T ]: NonNullable<T [K]> extends ValueConstructor ? ReturnType<NonNullable<T [K]>> : NonNullable<T [K]>
Root
src/types.ts#L72 {}
Slotted
src/slots.ts#L17 {
# elements
src/slots.ts#L17
Element []
firstChild
src/slots.ts#L17 Element
nodes
src/slots.ts#L17 Node []
ValueConstructor
src/mixins/attrs.ts#L4 typeof String | typeof Number | typeof Boolean
attrs
(attrs) src/mixins/attrs.ts#L34 # attrs
attrs<P>(attrs) =>
create
(props, fn) src/create.ts#L3 # events
() src/mixins/events.ts#L27
events<P extends Record<string, Event>>() =>
lifecycle
() src/mixins/lifecycle.ts#L3
lifecycle() =>
light
(html) src/mixins/light.ts#L3 # html
= ''
string
light(html) =>
mixter
(a, b, c, d, e, f, g, h, i, j, k) src/mixter.ts#L3 # c
(trait) # d
(trait) # e
(trait) # f
(trait) # g
(trait) # h
(trait) # i
(trait) # j
(trait) # k
(trait) mixter<A extends Ctor, B extends Ctor, C extends Ctor, D extends Ctor, E extends Ctor, F extends Ctor, G extends Ctor, H extends Ctor, I extends Ctor, J extends Ctor, K extends Ctor>(a, b, c, d, e, f, g, h, i, j, k) =>
#a
# b
(trait) # c
(trait) # d
(trait) # e
(trait) # f
(trait) # g
(trait) # h
(trait) # i
(trait) # j
(trait) mixter<A extends Ctor, B extends Ctor, C extends Ctor, D extends Ctor, E extends Ctor, F extends Ctor, G extends Ctor, H extends Ctor, I extends Ctor, J extends Ctor>(a, b, c, d, e, f, g, h, i, j) =>
#a
# b
(trait) # c
(trait) # d
(trait) # e
(trait) # f
(trait) # g
(trait) # h
(trait) # i
(trait) mixter<A extends Ctor, B extends Ctor, C extends Ctor, D extends Ctor, E extends Ctor, F extends Ctor, G extends Ctor, H extends Ctor, I extends Ctor>(a, b, c, d, e, f, g, h, i) =>
#a
# b
(trait) # c
(trait) # d
(trait) # e
(trait) # f
(trait) # g
(trait) # h
(trait) mixter<A extends Ctor, B extends Ctor, C extends Ctor, D extends Ctor, E extends Ctor, F extends Ctor, G extends Ctor, H extends Ctor>(a, b, c, d, e, f, g, h) =>
#a
# b
(trait) # c
(trait) # d
(trait) # e
(trait) # f
(trait) # g
(trait) mixter<A extends Ctor, B extends Ctor, C extends Ctor, D extends Ctor, E extends Ctor, F extends Ctor, G extends Ctor>(a, b, c, d, e, f, g) =>
#a
# b
(trait) # c
(trait) # d
(trait) # e
(trait) # f
(trait) mixter<A extends Ctor, B extends Ctor, C extends Ctor, D extends Ctor, E extends Ctor, F extends Ctor>(a, b, c, d, e, f) =>
#a
# b
(trait) # c
(trait) # d
(trait) # e
(trait) mixter<A extends Ctor, B extends Ctor, C extends Ctor, D extends Ctor, E extends Ctor>(a, b, c, d, e) =>
#a
# b
(trait) # c
(trait) # d
(trait) mixter<A extends Ctor, B extends Ctor, C extends Ctor, D extends Ctor>(a, b, c, d) =>
#a
# b
(trait) # c
(trait) mixter<A extends Ctor, B extends Ctor, C extends Ctor>(a, b, c) =>
#a
# b
(trait) mixter<A extends Ctor, B extends Ctor>(a, b) =>
#nodesToText
(nodes) src/slots.ts#L9 # nodes
null
| Node []
nodesToText(nodes) =>
- string
observeNodes
(nodes, observer, options) src/slots.ts#L3 # nodes
null
| Node []
observer
MutationObserver
options
MutationObserverInit
observeNodes(nodes, observer, options) =>
- undefined | void
onSlotChange
(el, cb, fn) src/slots.ts#L31 # fn
(el) onSlotChange<T>(el, cb, fn) =>
- any
onTextChange
(el, cb, fn) src/slots.ts#L37 # fn
(el) onTextChange<T>(el, cb, fn) =>
# ()
() =>
- void
props
(props) src/mixins/props.ts#L3 # props
props<P>(props) =>
shadow
(init, html) src/mixins/shadow.ts#L3 # slotted
(slots) src/slots.ts#L19 # state
(fn) src/mixins/state.ts#L151 # fn
state<P>(fn) =>
## Credits
- [argtor](https://npmjs.org/package/argtor) by [stagas](https://github.com/stagas) – Extracts destructured argument names from a function.
- [fluent-event](https://npmjs.org/package/fluent-event) by [stagas](https://github.com/stagas) – Fluent DOM event toolkit.
- [html-jsx](https://npmjs.org/package/html-jsx) by [stagas](https://github.com/stagas) – Extensible jsx type definitions for standard html interfaces.
- [html-vdom](https://npmjs.org/package/html-vdom) by [stagas](https://github.com/stagas) – JSX virtual DOM using standard HTML
- [nested-css](https://npmjs.org/package/nested-css) by [stagas](https://github.com/stagas) – compile nested css rules
## Contributing
[Fork](https://github.com/stagas/mixter/fork) or [edit](https://github.dev/stagas/mixter) and submit a PR.
All contributions are welcome!
## License
MIT © 2022 [stagas](https://github.com/stagas)