Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

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.

Awesome Lists containing this project

README

        


mixter

A 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 }) =>


    Hello, {who}!
    )
    }
    )
    ```

# todo-app

    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 => (



    1. {todo.done
      ? {todo.name}
      : todo.name}


    2. ))}

    {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

EventHandler(e)  =>

    void
# Class src/types.ts#L1 # Context src/mixins/state.ts#L24 # effect(fn, cb) src/mixins/state.ts#L27

    # fn(ctx)

      # ctx


      fn(ctx)  =>


        any

# cb(value)

    # value


      any


    cb(value)  =>


      boolean | void

effect(fn, cb)  =>

    void
# reduce(fn, initial) src/mixins/state.ts#L28

    # fn(ctx)

# initial

reduce<R>(fn, initial)  =>

} # ContextFn src/mixins/state.ts#L6

    # (this, ctx)

# Ctor src/types.ts#L3 # Deps src/mixins/state.ts#L8

    [K in keyof T ]-?: NonNullable<T [K]>

# EventMap src/mixins/events.ts#L7 # Fx src/mixins/state.ts#L12 | 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

    # value


    cb(value)  =>


      boolean | void
# fn(deps) src/mixins/state.ts#L13 } # InlineEventMap src/mixins/events.ts#L14 # 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 # ValueConstructor src/mixins/attrs.ts#L4

    typeof String | typeof Number | typeof Boolean

# attrs(attrs) src/mixins/attrs.ts#L34 # create(props, fn) src/create.ts#L3 # events() src/mixins/events.ts#L27


    events<P extends  Record<string, Event>>()  =>



      # (superclass)

        # superclass


        <T extends  Mixin>(superclass)  =>



          T & Class<{

          # addEventListener(type, listener, options)

            # type

            # listener

              any

            # options

              any


            addEventListener<K>(type, listener, options)  =>


              void



      # dispatch(name, detail, init)

        # name


          string

        # detail

          any

        # init

          CustomEventInit<any>


        dispatch(name, detail, init)  =>


          any
      } & InlineEventMap<T, P>>
# lifecycle() src/mixins/lifecycle.ts#L3 # 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

    # a

    # b(trait)

      # trait


      b(trait)  =>



# c(trait)

    # trait


    c(trait)  =>


# d(trait)

    # trait


    d(trait)  =>


# e(trait)

    # trait


    e(trait)  =>


# f(trait)

    # trait


    f(trait)  =>


# g(trait)

    # trait


    g(trait)  =>


# h(trait)

    # trait


    h(trait)  =>


# i(trait)

    # trait


    i(trait)  =>


# j(trait)

    # trait


    j(trait)  =>


# k(trait)

    # 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)

    # trait


    b(trait)  =>


# c(trait)

    # trait


    c(trait)  =>


# d(trait)

    # trait


    d(trait)  =>


# e(trait)

    # trait


    e(trait)  =>


# f(trait)

    # trait


    f(trait)  =>


# g(trait)

    # trait


    g(trait)  =>


# h(trait)

    # trait


    h(trait)  =>


# i(trait)

    # trait


    i(trait)  =>


# j(trait)

    # 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)

    # trait


    b(trait)  =>


# c(trait)

    # trait


    c(trait)  =>


# d(trait)

    # trait


    d(trait)  =>


# e(trait)

    # trait


    e(trait)  =>


# f(trait)

    # trait


    f(trait)  =>


# g(trait)

    # trait


    g(trait)  =>


# h(trait)

    # trait


    h(trait)  =>


# i(trait)

    # 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)

    # trait


    b(trait)  =>


# c(trait)

    # trait


    c(trait)  =>


# d(trait)

    # trait


    d(trait)  =>


# e(trait)

    # trait


    e(trait)  =>


# f(trait)

    # trait


    f(trait)  =>


# g(trait)

    # trait


    g(trait)  =>


# h(trait)

    # 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)

    # trait


    b(trait)  =>


# c(trait)

    # trait


    c(trait)  =>


# d(trait)

    # trait


    d(trait)  =>


# e(trait)

    # trait


    e(trait)  =>


# f(trait)

    # trait


    f(trait)  =>


# g(trait)

    # 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)

    # trait


    b(trait)  =>


# c(trait)

    # trait


    c(trait)  =>


# d(trait)

    # trait


    d(trait)  =>


# e(trait)

    # trait


    e(trait)  =>


# f(trait)

    # 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)

    # trait


    b(trait)  =>


# c(trait)

    # trait


    c(trait)  =>


# d(trait)

    # trait


    d(trait)  =>


# e(trait)

    # 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)

    # trait


    b(trait)  =>


# c(trait)

    # trait


    c(trait)  =>


# d(trait)

    # trait


    d(trait)  =>


mixter<A extends  Ctor, B extends  Ctor, C extends  Ctor, D extends  Ctor>(a, b, c, d)  =>

# a # b(trait)

    # trait


    b(trait)  =>


# c(trait)

    # trait


    c(trait)  =>


mixter<A extends  Ctor, B extends  Ctor, C extends  Ctor>(a, b, c)  =>

# a # b(trait)

    # trait


    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

    # el

    # cb(slotted)

      # slotted


      cb(slotted)  =>


        void

# fn(el)

    # el


    fn(el)  =>



      HTMLSlotElement []

onSlotChange<T>(el, cb, fn)  =>

    any
# onTextChange(el, cb, fn) src/slots.ts#L37

    # el

    # cb(text)

      # text


        string


      cb(text)  =>


        void

# fn(el)

    # el


    fn(el)  =>



      HTMLSlotElement []

onTextChange<T>(el, cb, fn)  =>


    # ()


      ()  =>


        void

# props(props) src/mixins/props.ts#L3

    # props


    props<P>(props)  =>



      # (superclass)
# shadow(init, html) src/mixins/shadow.ts#L3

    # init  =  ''


      string | ShadowRootInit

    # html  =  ''

      string


    shadow(init, html)  =>


# slotted(slots) src/slots.ts#L19

    # slots


      HTMLSlotElement []


    slotted(slots)  =>


# state(fn) src/mixins/state.ts#L151

## 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)