1.5.0 • Published 1 year ago

minimal-view v1.5.0

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

Minimal reactive component view library.

Examples

/* eslint-disable @typescript-eslint/ban-types */
/** @jsxImportSource minimal-view */

// import { Class } from 'everyday-types'
import { web, view, render, element, on, queue, enableDebug } from 'minimal-view'

// enableDebug(5000)

const drawFns = new Set<any>()
function drawSchedule(fn: any) {
  drawFns.add(fn)
  anim()
}
function drawRemoveSchedule(fn: any) {
  drawFns.delete(fn)
}

function animCall(fn: any) { fn() }
const anim = queue.raf(function animRaf() {
  if (drawFns.size) {
    anim()
    const fns = [minimal-view.drawFns]
    drawFns.clear()
    fns.forEach(animCall)
  }
})

const Button = web('btn', view(
  class props {
    state!: 'active' | 'inactive'
    isActive!: boolean
    onToggle!: () => void
    children?: JSX.Element
  }, class local {
  more?: string
}, ({ $, fx }) => {
  $.css = /*css*/`
  &([state=active]) {
    button {
      background: teal;
    }
  }
  &([state=inactive]) {
    button {
      background: grey;
    }
  }`

  fx(({ onToggle, isActive, children }) => {
    // console.log('fire', onToggle, isActive)
    $.view = <>
      <button onclick={onToggle}>
        {children}
        &nbsp;
        {isActive ? 'off' : 'on'}
      </button>
    </>
  })
}))

const Wave = web('wave', view(
  class props {
    width = 200
    height = 100
  }, class local {
  canvas?: HTMLCanvasElement
  ctx?: CanvasRenderingContext2D | null
  running = true
}, (({ $, fx, fn, refs }) => {
  $.css = /*css*/`
  canvas {
    background: #000;
    display: flex;
    image-rendering: pixelated;
  }`

  const pr = window.devicePixelRatio

  let t = 0
  let stop = () => { }
  const draw = fn(({ canvas, ctx, width, height }) => {
    stop = () => drawRemoveSchedule(drawTick)
    function drawTick() {
      drawSchedule(drawTick)
      // draw()
      ctx.imageSmoothingEnabled = false
      ctx.fillStyle = '#333'
      ctx.drawImage(canvas, -1, 0, width, height)
      ctx.fillRect(canvas.width - pr, 0, pr, height)
      ctx.fillStyle = '#aaa'
      t += 0.025
      ctx.fillRect(width - pr, (height - pr) * (Math.sin(t) * 0.5 + 0.5), pr, pr)
    }
    return drawTick
  })

  fx(({ canvas }) => {
    $.ctx = canvas.getContext('2d')
  })

  fx.raf(({ ctx, width, height }) => {
    ctx.fillStyle = '#333'
    ctx.fillRect(0, 0, width, height)
    draw()
  })

  fx(({ width, height }) => {
    $.view = <canvas ref={refs.canvas} width={width} height={height}
      onclick={() => {
        if ($.running) {
          stop()
          $.running = false
        } else {
          draw()
          $.running = true
        }
      }}
    />
  })
})))

const App = web('app', view(
  class props {
    numberOfItems = 1
  }, class local {
  host = element
  isActive = true
  items: any[] = []
  itemsView: JSX.Element = false
  scale = 1
}, ({ $, fx }) => {
  $.css = /*css*/`
  & {
    display: flex;
    flex-flow: row wrap;
    background: brown;
    padding: 10px;
    gap: 10px;
    transition: transform 100ms ease-out;
  }
  canvas {
    background: #000;
    display: block;
  }`

  fx.raf(({ host, scale }) => {
    host.style.transform = `scale(${scale})`
  })

  fx(() =>
    on(window, 'wheel').not.passive.prevent.stop.raf((ev) => {
      $.scale = Math.max(0.01, $.scale + ev.deltaY * 0.001)
    })
  )

  fx(({ numberOfItems }) => {
    $.itemsView = Array.from({ length: numberOfItems }, () =>
      <Wave width={200} height={100} />
    )
  })

  fx(({ itemsView }) => {
    $.view = <>
      {itemsView}
    </>
  })
}))

// let isActive = true

render(<>
  <style>{/*css*/`
    html, body {
      margin: 0;
      padding: 0;
      width: 100%;
      height: 100%;
      overflow: hidden;
    }
  `}</style>
  <App numberOfItems={1} />
</>, document.body)
/** @jsxImportSource minimal-view */

import { view } from 'minimal-view'

const V = view('x',
  class props {
    yo = 123
  },
  class local { },
  function actions({ $, fns, fn }) {
    return fns(new class actions {
      foo = () => {

      }
    })
  },
  function effects({ $, fx, deps, refs }) {
    fx(() => {
      $.foo()

      $.view = 'hey'
    })
  }
)
import { reactive } from 'minimal-view/reactive'

const X = reactive('x',
  class props {
    foo!: string
  },
  class local {
    a = 1
    b = 2
  },

  function actions({ $, fns, fn }) {
    return fns(new class actions {
      add = fn(({ a, b }) => () => {
        return a + b
      })
    })
  },

  function effects({ $, fx }) {
  }
)

const x = X({ foo: 'yes' })
/** @jsxImportSource minimal-view */

// import { Class } from 'everyday-types'
import { effect } from 'minimal-reactive'
import { view, render } from 'minimal-view'

// let i = 0
effect.debug = (changed, fn, minimal-view.stackErr) => {
  console.groupCollapsed(minimal-view.changed)
  console.log((fn as any).source)
  stackErr.forEach(err => console.warn(err))
  console.groupEnd()
  // if (i++ > 100) effect.stop()
}

// let inc = 0


const Button = view(class props {
  isActive!: boolean
  onToggle!: () => void
}, class local {
  more?: string
}, ({ $, fx }) => {
  fx(({ onToggle, isActive }) => {
    // console.log('fire', onToggle, isActive)
    $.view =
      <button onclick={onToggle}>{
        isActive ? 'off' : 'on'
      }</button>
  })
  // return ({ isActive, onToggle }) => {
  //   if (isActive) onToggle()
  // }
})

// setTimeout(() => {
//   effect.stop()
// }, 50)

// Button(props: { color: string }) {
//   hook.$ ??= use(props, class local {
//     btn?: HTMLButtonElement
//     view: JSX.Element
//   })

//   const $ = hook.$

//   let someScoped = 0

//   // const id = inc++
//   $.effect(({ color }) => {
//     // console.log('COLOR IS', color)
//     // console.log('I AM', id)
//     $.view = `${color} scoped: ${someScoped++}`
//     // queueMicrotask(() => {
//     // })
//     return () => {
//       console.warn('color disposed')
//     }
//   })

//   return <button ref={$.refs.btn}>{$.view}</button>
// }

// function App() {
//   const $ = use(class local {
//     color = 'pink'
//     text = 'hello'
//     counter = 0
//     late?: boolean
//     some?: Stateful<{
//       prop?: boolean,
//       deep?: Stateful<{ other?: number }>
//     }>
//     status?: JSX.Element
//   })
//     .reduce(
//       ({ counter }) => ({
//         total: counter
//       }))
//     .reduce(
//       ({ color, text }) => ({
//         greeting: color + text
//       }))
//     .pick(
//       ({ some }) => ({
//         prop: some.prop,
//         deepOther: some.deep.other
//       }))

//   $.effect(({ counter, deepOther }) => {
//     if (counter % 2 === 1 + deepOther) {
//       $.late = true
//       $.color = ['red', 'green', 'blue'][Math.random() * 3 | 0]
//     } else {
//       $.late = false
//     }
//     return () => {
//       console.log('disposed?', counter)
//     }
//   })

//   $.effect(({ greeting, late }) => {
//     $.status = greeting + `${late ? <h1>LATE</h1> : 'early'}`
//   })

//   $.effect(({ text, color, counter }) => {
//     $.view =
//       <div onclick={() => {
//         $.text = 'yo'
//         $.counter++
//       }}>
//         hello world {color} {text} {counter}
//         <Button color={color} />
//       </div>
//   })

//   return <>
//     {$.view}
//     {$.status}
//   </>
// }

const App = view(
  class props {
    skata = 1
  }, class local {
  isActive = true
  items: any[] = []
  itemsView: JSX.Element = false
}, ({ $, fn, fx }) => {
  const onToggle = fn(
    ({ isActive }) =>
      () => {
        $.isActive = !isActive
      }
  )

  const addItem = fn(
    ({ isActive }) =>
      isActive
        ? (item: any) => { $.items = [minimal-view.$.items, item] }
        : () => { console.log('not active') }
  )

  fx(({ items }) => {
    $.itemsView = items.map((item) => <i>{item}<br /></i>)
  })

  fx(({ $, itemsView, isActive }) => {
    // $.view = 'hi'
    $.view = <>
      <Button
        isActive={isActive}
        onToggle={onToggle}
      />

      <button onclick={() => addItem(Math.random())}>add item</button>

      {itemsView}
    </>
  })

  return ($) => {
    $.skata
  }
})

// let isActive = true

render(<App skata={2} />, document.body)

API