UNPKG

solid-tweakpane

Version:

Solid components for Tweakpane library

193 lines (187 loc) 5.63 kB
import { createContext, splitProps, onCleanup, createComponent, useContext, createEffect, onMount, on, For, Switch, Match } from 'solid-js'; import { Pane } from 'tweakpane'; const TWPRootContext = createContext(); const useTWPRoot = () => useContext(TWPRootContext); function TWPGroup(props) { return createComponent(TWPRootContext.Provider, { get value() { return props.root; }, get children() { return props.children; } }); } function Tweakpane(props) { const [compProps, paneProps] = splitProps(props, ["children"]); const pane = new Pane(paneProps); onCleanup(() => pane.dispose()); return createComponent(TWPGroup, { root: pane, get children() { return compProps.children; } }); } const TWPBindGroupContext = createContext(); const useTWPBingGroup = () => useContext(TWPBindGroupContext); function TWPBindGroup(props) { return createComponent(TWPBindGroupContext.Provider, { get value() { return props.target; }, get children() { return props.children; } }); } function createTWPUI(initFn) { return function (props) { const root = useTWPRoot(); if (!root) throw new Error(`Use tweakpane controls within <${"Tweakpane"}>`); const [comp, element] = initFn(root, props); onCleanup(() => { if (!comp) return; comp.dispose(); root.remove(comp); }); props.ref?.(comp); if (element) return element; }; } const TWPFolder = createTWPUI((root, props) => { const [compProps, folderProps] = splitProps(props, ["children"]); const folder = root.addFolder(folderProps); return [folder, createComponent(TWPGroup, { root: folder, get children() { return compProps.children; } })]; }); const TWPButton = createTWPUI((root, props) => { const button = root.addButton(props); props.onClick && button.on("click", props.onClick); createEffect(() => { button.title = props.title; }); return [button]; }); const TWPSeparator = createTWPUI((root, props) => { const separator = root.addSeparator(props); return [separator]; }); const TabContext = createContext(); const useTab = () => useContext(TabContext); const TWPTab = createTWPUI((root, props) => { const [compProps, tabProps] = splitProps(props, ["children"]); const tab = root.addTab({ ...tabProps, pages: [{ title: "mock" }] }); onMount(() => tab.removePage(0)); return [tab, createComponent(TabContext.Provider, { value: tab, get children() { return compProps.children; } })]; }); function TWPTabPage(props) { const root = useTab(); if (!root) throw new Error(`Parent for <${"TWPTabPage"}> should be <${"TWPTab"}>`); const [compProps, pageProps] = splitProps(props, ["children"]); const page = root.addPage(pageProps); createEffect(() => page.title = props.title); onCleanup(() => { root.removePage(root.pages.indexOf(page)); }); return createComponent(TWPGroup, { root: page, get children() { return compProps.children; } }); } function createTWPControl(initFn) { return function (props) { const root = useTWPRoot(); if (!root) throw new Error(`Use tweakpane controls within <${"Tweakpane"}>`); let binding = props.target ?? useTWPBingGroup(); if (!binding && props.key) { throw new Error(`In case of usage <TWPControl> with key, place it within <${"TWPBindGroup"}> and provide taget`); } if (binding && !Object.hasOwn(binding, props.key)) { throw new Error(`There is no key ${props.key} in binding ${binding}`); } if (!binding && !props.key) { if (!props.initialValue) { throw new Error(`In case of usage <TWPControl> without target and key, provide ${"initialValue"} prop`); } binding = { value: props.initialValue }; props.key = "value"; } const [comp, element] = initFn(root, binding, props); createEffect(on(() => binding[props.key], () => comp.refresh(), { defer: true })); onCleanup(() => { if (!comp) return; root.remove(comp); comp.dispose(); }); props.ref?.(comp); if (element) return element; }; } const TWPInput = createTWPControl((root, binding, props) => { const input = root.addInput(binding, props.key, props.params); props.onChange && input.on("change", props.onChange); return [input]; }); const TWPMonitor = createTWPControl((root, binding, props) => { const monitor = root.addMonitor(binding, props.key, props.params); props.onUpdate && monitor.on("update", props.onUpdate); return [monitor]; }); function TWPAutoMutable(props) { return createComponent(For, { get each() { return Object.keys(props.target); }, children: key => createComponent(Switch, { get fallback() { return createComponent(TWPInput, { key: key, get target() { return props.target; } }); }, get children() { return createComponent(Match, { get when() { return props.target[key].constructor === Object; }, get children() { return createComponent(TWPFolder, { title: key, get children() { return createComponent(TWPAutoMutable, { get target() { return props.target[key]; } }); } }); } }); } }) }); } export { TWPAutoMutable, TWPBindGroup, TWPButton, TWPFolder, TWPInput, TWPMonitor, TWPSeparator, TWPTab, TWPTabPage, Tweakpane };