solid-tweakpane
Version:
Solid components for Tweakpane library
193 lines (187 loc) • 5.63 kB
JavaScript
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 };