Skip to content

Instantly share code, notes, and snippets.

@doeixd
Last active January 22, 2024 20:36
Show Gist options
  • Save doeixd/bd70a6dac0b981d505e7f360532b0bdb to your computer and use it in GitHub Desktop.
Save doeixd/bd70a6dac0b981d505e7f360532b0bdb to your computer and use it in GitHub Desktop.
framework.js
import { createContext as unCreateContext } from 'unctx'
import { createFallback, isString } from './utilities.mjs'
// Setup
export function init() {
window.__registry = {
initial_component_contexts: {},
component_instances: {}
}
ensureRunAfterDOM(_ => {
const everyElement = [...document.querySelectorAll('*')]
for (let element of everyElement) {
addFrameworkListenersToElement(element)
}
createObserver((mutations) => {
const addedElements = getAddedElements(mutations)
for (let addedElement of addedElements) {
addFrameworkListenersToElement(addedElement)
}
}, {childList: true, subtree: true})(document)
})
}
export function getContext () {
return {
context: {},
args: {}
}
}
export function initialComponentContexts() {
return window?.__registry?.context?.initial_component_contexts
}
export function getInstance(name, key) {
let isNameAndKey = isString(name) && isString(key)
if (isNameAndKey) return window?.__registry?.component_instances?.[name]?.[key]
if (name) return window?.__registry?.component_instances?.[name]
return window?.__registry?.component_instances
}
export function createInitialContextObject({name, type = 'single', data}) {
return {
...data,
state: {},
props: {},
events: {},
roles: {},
mount: [],
name,
parent: undefined,
components: [],
type,
condition: (element) => {
return element.tagName.toLowerCase() == name.toLowerCase()
},
}
}
export function createComponent(name, constructor, data) {
const ctx = unCreateContext(name)
getContext = ctx.use
let componentContext = createInitialContextObject({name, data})
function call(args = {}, fn) {
let context = {
context: componentContext,
args,
ctx,
call
}
// ctx.set(context, true)
return ctx.call(context, fn)
}
call({}, constructor)
getContext().__registry
}
export function component(keyNameOrFn, key) {
let component_context = getContext()?.context
let createReturnValue = (_instance = instances) => createFallback(_instance, _instance?.element || {})
const isNameAndKey = isString(keyNameOrFn) && isString(key)
const isFn = keyNameOrFn instanceof Function
// const isKey = isString(keyNameOrFn) && !key
if (isNameAndKey) {
const instance = getInstance(keyNameOrFn, key)
return createReturnValue(instance)
}
if (isFn) {
let instances = Object.entries(window?.__registry?.component_instances)
let toReturn = []
for (let instance of instances) {
if(keyNameOrFn?.(instance)) {
toReturn.push(createReturnValue(instance))
}
}
}
let name = component_context?.name || (isString(keyNameOrFn) ? keyNameOrFn : undefined)
let type = component_context?.type || 'single'
let instances = getInstance(name)
let initial_component_contexts = initialComponentContexts()
let is_single = type.toLowerCase().includes('single')
if (is_single) return createReturnValue()
return instances.map(createReturnValue)
}
export function find() {
}
export function condition (cb) {
let context = getContext()?.context
context['condition'] = cb
}
export function key (cb) {
let context = getContext()?.context
context['key'] = cb
}
export function mount (cb) {
let context = getContext()?.context
context['mount'] ||= []
context['mount'].push(cb)
}
export function prop(key, value) {
let context = getContext()?.context
context[key] = value
}
export function on (condition, cb) {
let context = getContext()?.context
context['events'] ||= []
context['events'].push({condition, cb})
}
export function attr (name, cb, initial) {
let context = getContext()?.context
context['attributes'] ||= []
context['attributes'].push({name, cb, initial})
}
export function dispatch(name, cb) {
let context = getContext()?.context
context['dispatch'] ||= []
context['dispatch'].push({name, cb})
}
export function unsubscribe(condition, cb) {
let context = getContext()?.context
context['unsubscribe'] ||= []
context['unsubscribe'].push({condition, cb})
}
export function state () {
let context = getContext()?.context
let _state = {}
context['state'] ||= _state
return _state
}
export const role_symbol = Symbol('role')
export const role_name_symbol = Symbol('role_name')
export function role(name, cb) {
let context = getContext()?.context
let full_context = getContext()
let isRole = Object.hasOwn(role_symbol, full_context)
let isCondition = name && !(cb instanceof Function)
if (isCondition) {
}
if (isRole) {
let role_context = getContext()
return role_context
}
if (cb instanceof Function) {
let _role = {}
context['role'] ||= _role
context['role'][name] = {
[role_symbol]: true,
[role_name_symbol]: name,
}
let ctx = unCreateContext()
let og_getContext = getContext
getContext = ctx.use
ctx.call(createFallback(context['role'][name], context), cb)
getContext = og_getContext
}
}
export function el (condition) {
const context = getContext()
let element = context?.element || context?.context?.element
if (!condition) {
return context?.element || context?.context?.element
}
if (isString(condition) && element) {
return element.querySelector(string)
}
if (condition instanceof Function && element) {
return [...element.querySelectorAll('*')].find(condition)
}
}
@doeixd
Copy link
Author

doeixd commented Jan 22, 2024

export function createObserver(fn, options) {
  const observer = new MutationObserver(fn)
  return function (element) {
    if (isString(element)) element = document.querySelector(element)
    observer.observe(element, options)
  }
}

function getAddedElements (mutations) {
  let addedElements = []

  for (let mutation of mutations) {
    for (let element of mutation.addedNodes) {
      addedElements.push(element)
      const descendants = element?.querySelectorAll?.('*') || []
      addedElements.push(...descendants)
    }
  }

  return addedElements
}



export function ensureRunAfterDOM (fn) {
  let handleDOMLoaded = Fn(fn)
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', handleDOMLoaded);
  } else {
    handleDOMLoaded();
  }
}

export function Fn (fn) {
  return function (...args) {
    return fn(...args)
  }
}

export function isString (value) {
  return typeof value === 'string' || value instanceof String
}

export function isObject (o) { 
  return (typeof o === 'object' && o !== null)
}

export function makeEventName (name) {
  name = name.replace(/^on/, '')
  // name = name[0].toLowerCase() + name.substring(1)
  return name.toLowerCase()
}

export function createEvent (eventName, data) {
	return new CustomEvent(eventName, data)
}

export function el(e) {
  return e.currentTarget
}

export function createFallback (...objs) {

  const select = (field, ...moreObjs) => {
    for (let obj of [...objs, ...moreObjs]) {
      if (obj?.[field] || Object.hasOwn(obj, field)) return {
        obj,
        value: obj[field]
      }
    }
  }

  return new Proxy ({}, {
    get(target, prop, receiver) {
      return select(prop, target)?.value
    },
    set(target, prop, value) {
      let obj = select(prop, target)?.obj
      if (obj) {
        obj[prop] = value
      } else {
        target[prop] = value
      }
    }
  })
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment