-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
improved structure a bit, added useHostElement and useShadowRoot hook…
…s and used slots in todo example
- Loading branch information
1 parent
f35d19a
commit 5e3c3a8
Showing
7 changed files
with
302 additions
and
284 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { createHook, useRenderer } from "../index.mjs"; | ||
import { html, render } from "https://unpkg.com/htm/preact/standalone.mjs"; | ||
export const usePreactHtm = createHook(() => { | ||
useRenderer((view, shadowRoot) => { | ||
render(view, shadowRoot); | ||
}); | ||
return [html]; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,65 +1,14 @@ | ||
import { queueRender, defaultRenderer, getPassableProps } from "./renderer.mjs"; | ||
export { prps } from "./renderer.mjs"; | ||
export { defineComponent } from "./lib/component.mjs"; | ||
export { prps } from "./lib/renderer.mjs"; | ||
export { | ||
useReducer, | ||
useState, | ||
usePreactHtm, | ||
useEffect, | ||
useAttribute, | ||
useCSS, | ||
useExposeMethod | ||
} from "./hooks.mjs"; | ||
const componentMap = new Map(); | ||
function addComponent(name, options) { | ||
class Component extends HTMLElement { | ||
constructor() { | ||
super(); | ||
this.props = {}; | ||
this.renderer = defaultRenderer; | ||
} | ||
connectedCallback() { | ||
if (!this._shadowRoot) { | ||
this._shadowRoot = this.attachShadow({ mode: "open" }); | ||
queueRender(this); | ||
} | ||
} | ||
render() { | ||
const propsId = this.getAttribute("data-props"); | ||
if (propsId) { | ||
this.props = getPassableProps(propsId); | ||
this.skipQueue = true; | ||
this.removeAttribute("data-props"); | ||
} | ||
const view = componentMap.get(name)(this.props); | ||
this.renderer(view, this._shadowRoot); | ||
this.init = false; | ||
} | ||
attributeChangedCallback(attrName, oldVal, newVal) { | ||
if (this.init) { | ||
return; | ||
} | ||
if (!this.skipQueue && oldVal !== newVal) { | ||
queueRender(this); | ||
} | ||
this.skipQueue = false; | ||
} | ||
static get observedAttributes() { | ||
let observedAttributes = ["data-props"]; | ||
if (options.observedAttributes) { | ||
observedAttributes = observedAttributes.concat( | ||
options.observedAttributes | ||
); | ||
} | ||
return observedAttributes; | ||
} | ||
} | ||
customElements.define(name, Component); | ||
} | ||
export const defineComponent = (name, component, options = {}) => { | ||
if (!componentMap.has(name)) { | ||
componentMap.set(name, component); | ||
addComponent(name, options); | ||
} else { | ||
console.warn(`Component ${name} was already defined.`); | ||
} | ||
}; | ||
useExposeMethod, | ||
useRenderer, | ||
useHostElement, | ||
useShadowRoot, | ||
createHook | ||
} from "./lib/base_hooks.mjs"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,122 +1,121 @@ | ||
import { | ||
getCurrentElement, | ||
getCurrentHookState, | ||
queueRender, | ||
queueAfterRender, | ||
nextHook | ||
} from "./renderer.mjs"; | ||
export const createHook = hook => (...args) => { | ||
nextHook(); | ||
return hook(...args); | ||
}; | ||
export const useReducer = createHook((reducer, initialState) => { | ||
const hookState = getCurrentHookState({ | ||
reducer, | ||
state: initialState | ||
}); | ||
const element = getCurrentElement(); | ||
return [ | ||
hookState.state, | ||
action => { | ||
hookState.state = hookState.reducer(hookState.state, action); | ||
queueRender(element); | ||
} | ||
]; | ||
}); | ||
export const useState = createHook(initialState => { | ||
const [state, dispatch] = useReducer((_, action) => { | ||
return action.value; | ||
}, initialState); | ||
|
||
return [ | ||
state, | ||
newState => | ||
dispatch({ | ||
type: "set_state", | ||
value: newState | ||
}) | ||
]; | ||
}); | ||
export const useRenderer = createHook(rendererIn => { | ||
const renderer = getCurrentHookState(rendererIn); | ||
const element = getCurrentElement(); | ||
element.renderer = renderer; | ||
}); | ||
|
||
import { html, render } from "https://unpkg.com/htm/preact/standalone.mjs"; | ||
export const usePreactHtm = createHook(() => { | ||
useRenderer((view, shadowRoot) => { | ||
render(view, shadowRoot); | ||
}); | ||
return [html]; | ||
}); | ||
export const useEffect = createHook((effect, values) => { | ||
const state = getCurrentHookState({ | ||
effect, | ||
values, | ||
cleanUp: () => {} | ||
}); | ||
let nothingChanged = false; | ||
if (state.values !== values && state.values && state.values.length > 0) { | ||
nothingChanged = true; | ||
let index = state.values.length; | ||
|
||
while (index--) { | ||
if (values[index] !== state.values[index]) { | ||
nothingChanged = false; | ||
break; | ||
} | ||
} | ||
state.values = values; | ||
} | ||
if (!nothingChanged) { | ||
state.cleanUp(); | ||
queueAfterRender(() => { | ||
const cleanUp = state.effect(); | ||
if (cleanUp) { | ||
state.cleanUp = cleanUp; | ||
} | ||
}); | ||
} | ||
}); | ||
export const useAttribute = createHook(attributeName => { | ||
const element = getCurrentElement(); | ||
const attributeValue = element.getAttribute(attributeName); | ||
return [ | ||
attributeValue, | ||
value => { | ||
element.skipQueue = true; | ||
element.setAttribute(attributeName, value); | ||
} | ||
]; | ||
}); | ||
export const useCSS = createHook((parts, ...slots) => { | ||
let styles; | ||
if (parts instanceof Array) { | ||
styles = parts | ||
.map((part, index) => { | ||
if (slots[index]) { | ||
return part + slots[index]; | ||
} else { | ||
return part; | ||
} | ||
}) | ||
.join(""); | ||
} else { | ||
styles = parts; | ||
} | ||
styles = styles.replace(/ +(?= )/g, "").replace(/\n/g, ""); | ||
const element = getCurrentElement(); | ||
const style = document.createElement("style"); | ||
style.innerHTML = styles; | ||
useEffect(() => { | ||
element._shadowRoot.appendChild(style); | ||
return () => { | ||
element._shadowRoot.removeChild(style); | ||
}; | ||
}); | ||
}); | ||
export const useExposeMethod = createHook((name, method) => { | ||
const element = getCurrentElement(); | ||
element[name] = (...args) => method(...args); | ||
}); | ||
import { | ||
getCurrentElement, | ||
getCurrentHookState, | ||
queueRender, | ||
queueAfterRender, | ||
nextHook | ||
} from "./renderer.mjs"; | ||
export const createHook = hook => (...args) => { | ||
nextHook(); | ||
return hook(...args); | ||
}; | ||
|
||
export const useHostElement = createHook(() => { | ||
return getCurrentElement(); | ||
}); | ||
export const useShadowRoot = createHook(() => { | ||
return useHostElement()._shadowRoot; | ||
}); | ||
export const useReducer = createHook((reducer, initialState) => { | ||
const hookState = getCurrentHookState({ | ||
reducer, | ||
state: initialState | ||
}); | ||
const element = useHostElement(); | ||
return [ | ||
hookState.state, | ||
action => { | ||
hookState.state = hookState.reducer(hookState.state, action); | ||
queueRender(element); | ||
} | ||
]; | ||
}); | ||
export const useState = createHook(initialState => { | ||
const [state, dispatch] = useReducer((_, action) => { | ||
return action.value; | ||
}, initialState); | ||
|
||
return [ | ||
state, | ||
newState => | ||
dispatch({ | ||
type: "set_state", | ||
value: newState | ||
}) | ||
]; | ||
}); | ||
export const useRenderer = createHook(rendererIn => { | ||
const renderer = getCurrentHookState(rendererIn); | ||
const element = useHostElement(); | ||
element.renderer = renderer; | ||
}); | ||
export const useEffect = createHook((effect, values) => { | ||
const state = getCurrentHookState({ | ||
effect, | ||
values, | ||
cleanUp: () => {} | ||
}); | ||
let nothingChanged = false; | ||
if (state.values !== values && state.values && state.values.length > 0) { | ||
nothingChanged = true; | ||
let index = state.values.length; | ||
|
||
while (index--) { | ||
if (values[index] !== state.values[index]) { | ||
nothingChanged = false; | ||
break; | ||
} | ||
} | ||
state.values = values; | ||
} | ||
if (!nothingChanged) { | ||
state.cleanUp(); | ||
queueAfterRender(() => { | ||
const cleanUp = state.effect(); | ||
if (cleanUp) { | ||
state.cleanUp = cleanUp; | ||
} | ||
}); | ||
} | ||
}); | ||
export const useAttribute = createHook(attributeName => { | ||
const element = useHostElement(); | ||
const attributeValue = element.getAttribute(attributeName); | ||
return [ | ||
attributeValue, | ||
value => { | ||
element.skipQueue = true; | ||
element.setAttribute(attributeName, value); | ||
} | ||
]; | ||
}); | ||
export const useCSS = createHook((parts, ...slots) => { | ||
let styles; | ||
if (parts instanceof Array) { | ||
styles = parts | ||
.map((part, index) => { | ||
if (slots[index]) { | ||
return part + slots[index]; | ||
} else { | ||
return part; | ||
} | ||
}) | ||
.join(""); | ||
} else { | ||
styles = parts; | ||
} | ||
styles = styles.replace(/ +(?= )/g, "").replace(/\n/g, ""); | ||
const shadowRoot = useShadowRoot(); | ||
const style = document.createElement("style"); | ||
style.innerHTML = styles; | ||
useEffect(() => { | ||
shadowRoot.appendChild(style); | ||
return () => { | ||
shadowRoot.removeChild(style); | ||
}; | ||
}); | ||
}); | ||
export const useExposeMethod = createHook((name, method) => { | ||
const element = useHostElement(); | ||
element[name] = (...args) => method(...args); | ||
}); |
Oops, something went wrong.