vanjs-org / van

🍦 VanJS: World's smallest reactive UI framework. Incredibly Powerful, Insanely Small - Everyone can build a useful UI app in an hour.
https://vanjs.org
MIT License
3.77k stars 87 forks source link

A new version of the TodoList in Javascript with editable input fields #315

Closed ArcaneEngineer closed 4 months ago

ArcaneEngineer commented 4 months ago

Seems kinda pointless having a bunch of examples that don't even show reactive / persistent text per line... so I added editability / persistence to the to-do item text fields (assuming I've done it right...?)

Perhaps you would like to include this on your examples page since for a beginner like me, it takes a while how to figure out how to do this, and really it should just be on your examples page by default for newcomers to use.

P.S. Also wise to have the ability to show your examples in both TS and JS... though this was trivial to convert to the latter.

class TodoItemState {
  constructor(text, done, deleted) {this.text = text; this.done = done; this.deleted = deleted;}
  serialize() { return {text: this.text.val, done: this.done.val} }
}

const TodoItem = ({text, done, deleted}) => () => deleted.val ? null : div(
  input({type: "checkbox", checked: done, onclick: e => done.val = e.target.checked}),
  (done.val ? del(text) : input({type: "text", value: text.val,  onchange: e => text.val = e.target.value})),
  a({onclick: () => deleted.val = true}, "❌"),
)

class TodoListState {
  constructor(todos) {this.todos = todos;}

  save() {
    localStorage.setItem("appState", JSON.stringify(
      (this.todos = this.todos.filter(t => !t.deleted.val)).map(t => t.serialize())))
  }

  static load = () => new TodoListState(
    JSON.parse(localStorage.getItem("appState") ?? "[]")
      .map((t) => new TodoItemState(van.state(t.text), van.state(t.done), van.state(false)))
  )

  add(text) {
    this.todos.push(new TodoItemState(van.state(text), van.state(false), van.state(false)))
    return new TodoListState(this.todos)
  }
}

const TodoList = () => {
  const appState = van.state(TodoListState.load())
  van.derive(() => appState.val.save())
  const inputDom = input({type: "text"})
  return div(
    inputDom, button({onclick: () => appState.val = appState.val.add(inputDom.value)}, "Add"),
    (dom) => dom ?
      van.add(dom, TodoItem(appState.val.todos.at(-1))) :
      div(appState.val.todos.map(TodoItem)),
  )
}

van.add(document.body, TodoList());