Skip to content

How did we get to fastn?

Mark Wylde edited this page Nov 7, 2019 · 8 revisions

Element Creation

How do we put something on the screen

const container = document.createElement('div')
const el1 = document.createElement('span')
el1.textContent = 'This is a test'

const el2 = document.createElement('span')
el2.textContent = 'This is another test'

container.appendChild(el1)
container.appendChild(el2)

But, this get's annoying and verbose, Let's make a helper

const container = buildElement('div')
const el1 = buildElement('span', { textContent: 'This is a test'})
const el2 = buildElement('span', { textContent: 'This is another test'})

container.appendChild(el1)
container.appendChild(el2)

But what if we could nest our elements

const ui = buildElement('div',
  buildElement('span', { textContent: 'This is a test' }),
  buildElement('span', { textContent: 'This is another test' })
)

Since textContent is actually a child, let's put in text

const ui = buildElement('div',
  buildElement('span', 'This is a test'),
  buildElement('span', 'This is another test')
)

But I want properties

const colouredEl = buildElement('div', 'I am pretty')
colouredEl.style.backgroundColor = 'yellow'
const ui = buildElement('div',
  buildElement('span', 'This is a test'),
  buildElement('span', 'This is another test'),
  colouredEl
)

Why can't I do this inline?

const ui = buildElement('div',
  buildElement('span', 'This is a test'),
  buildElement('span', 'This is another test'),
  buildElement('div', {
    style: {
      backgroundColor: 'yellow'
    }
  }, 'I am pretty')
)

Events

And update the UI when events happen

const ui = buildElement('div',
  buildElement('span', 'This is a test'),
  buildElement('span', 'This is another test'),
  buildElement('div', {
    style: {
      backgroundColor: 'yellow'
    }
  }, 'I am pretty')
  .on('click', function() {
    this.element.style.backgroundColor = 'blue'
  })
)

State

But Rather than updating directly, we should bind to state

const state = {
  backgroundColor: 'yellow'
}

const syncBackgroundColor = new EventEmitter()

const ui = buildElement('div',
  buildElement('span', 'This is a test'),
  buildElement('span', 'This is another test'),
  buildElement('div', {
    style: {
      backgroundColor: syncBackgroundColor
    }
  }, 'I am pretty')
  .on('click', () => {
    state.backgroundColor = 'yellow'
    syncBackgroundColor.emit('change', state.backgroundColor)
  })
)

But I don't want to manually emit every time I change state

const state = {
  backgroundColor: 'yellow'
}

const stateEvents = new EventEmitter()

function changeState(state, property, newValue) {
  state[property] = newValue || state[property]
  stateEvents.emit('change', state[property])
}

function observe (emitter, eventName) {
  const localEventEmitter = new EventEmitter()
  emitter.addEventListener(eventName, newValue => {
    localEventEmitter.emit('change', newValue)
  })
  return localEventEmitter
}

const ui = buildElement('div',
  buildElement('span', 'This is a test'),
  buildElement('span', 'This is another test'),
  buildElement('div', {
    style: {
      backgroundColor: observe(stateEvents, 'backgroundColor')
    }
  }, 'I am pretty')
  .on('click', () => {
    changeState(state, 'backgroundColor', 'yellow')
  })
)

But, I don't have my data yet

const state = {
  title: 'yellow'
}

const stateEvents = new EventEmitter()

function changeState(state, property, newValue) {
  state[property] = newValue || state[property]
  stateEvents.emit(property, state[property])
}

function observe (eventName) {
  const localEventEmitter = new EventEmitter()
  let currentEmitter
  const onChange = newValue => {
    localEventEmitter.emit('change', newValue)
  }
  localEventEmitter.attach = emitter => {
    if(currentEmitter){
      currentEmitter.removeListener('change', onChange)
    }
    emitter.on(eventName, onChange)
    localEventEmitter.emit('change', state[eventName])
    return localEventEmitter
  }
  return localEventEmitter
}

const ui = fastn('div',
  fastn('span', 'This is a test'),
  fastn('span', 'This is another test'),
  fastn('button', {
    title: observe('title').attach(stateEvents)
  }, 'I am pretty')
    .on('click', () => {
      changeState(state, 'title', 'blue')
    })
)

But wait! State has been hard coded into the observe function

TODO: Do without concept of inheriting state (ie firmness)
      Because we use fastn (maybe too soon) which has firmness built in

But, I want to attach at multiple levels of my ui

const state = {
  title: 'yellow'
}

const stateEvents = new EventEmitter()
stateEvents.get = path => {
  if(path === '.'){
    return state
  }

  return state[path]
}

function changeState(state, property, newValue) {
  state[property] = newValue || state[property]
  stateEvents.emit(property, state[property])
}

function observe (eventName) {
  const localEventEmitter = new EventEmitter()
  let currentEmitter
  let currentFirmness = 0
  const onChange = newValue => {
    localEventEmitter.emit('change', newValue)
  }
  localEventEmitter.attach = (emitter, firmness = Infinity) => {
    if(firmness >= currentFirmness){
      currentFirmness = firmness

      if(currentEmitter){
        currentEmitter.removeListener('change', onChange)
      }
      currentEmitter = emitter
      currentEmitter.on(eventName, onChange)
    }

    localEventEmitter.emit('change', currentEmitter.get(eventName))
    return localEventEmitter
  }
  return localEventEmitter
}

const ui = buildElement('div',
  buildElement('span', 'This is a test'),
  buildElement('span', 'This is another test'),
  buildElement('button', {
    title: observe('title').attach(stateEvents)
  }, 'I am pretty')
    .on('click', () => {
      changeState(state, 'title', 'blue')
    })
)