format project

This commit is contained in:
Kurt Hutten
2021-08-06 20:17:55 +10:00
parent 725e877f1b
commit 7cc989014a
5 changed files with 242 additions and 203 deletions

View File

@@ -1,4 +1,4 @@
import { useRender, } from 'src/components/IdeWrapper/useRender' import { useRender } from 'src/components/IdeWrapper/useRender'
import { useIdeContext } from 'src/helpers/hooks/useIdeContext' import { useIdeContext } from 'src/helpers/hooks/useIdeContext'
import { genParams, getParams } from 'src/helpers/cadPackages/jscadParams' import { genParams, getParams } from 'src/helpers/cadPackages/jscadParams'
@@ -11,17 +11,23 @@ const Customizer = () => {
const customizerParams = state?.objectData?.customizerParams const customizerParams = state?.objectData?.customizerParams
const lastParameters = state?.objectData?.lastParameters const lastParameters = state?.objectData?.lastParameters
const handleRender = useRender() const handleRender = useRender()
const handleRender2 = ()=>handleRender(getParams(ref.current)) const handleRender2 = () => handleRender(getParams(ref.current))
React.useEffect(() => { React.useEffect(() => {
if (jsCadCustomizerElement && customizerParams) { if (jsCadCustomizerElement && customizerParams) {
genParams(customizerParams, jsCadCustomizerElement, lastParameters || {}, (values, source)=>{ genParams(
if(source === 'group'){ customizerParams,
jsCadCustomizerElement,
lastParameters || {},
(values, source) => {
if (source === 'group') {
// save to local storage but do not render // save to local storage but do not render
return return
} }
if(checked) handleRender(values) if (checked) handleRender(values)
},[]) },
[]
)
} }
}, [jsCadCustomizerElement, customizerParams, lastParameters, checked]) }, [jsCadCustomizerElement, customizerParams, lastParameters, checked])
return ( return (
@@ -37,11 +43,17 @@ const Customizer = () => {
</button> </button>
<div>Parameters</div> <div>Parameters</div>
</div> </div>
<div className='flex items-center'> <div className="flex items-center">
<input className='mr-6' type='checkbox' checked={checked} onChange={({target}) => { <input
className="mr-6"
type="checkbox"
checked={checked}
onChange={({ target }) => {
const newValue = !checked const newValue = !checked
if (newValue) handleRender2() if (newValue) handleRender2()
setChecked(newValue)}}/> setChecked(newValue)
}}
/>
<button <button
className="px-4 py-1 rounded bg-ch-gray-300 text-ch-gray-800" className="px-4 py-1 rounded bg-ch-gray-300 text-ch-gray-800"
onClick={handleRender2} onClick={handleRender2}
@@ -49,7 +61,6 @@ const Customizer = () => {
Update Update
</button> </button>
</div> </div>
</div> </div>
<div className={`${open ? 'h-full' : 'h-0'} overflow-y-auto py-3 px-12`}> <div className={`${open ? 'h-full' : 'h-0'} overflow-y-auto py-3 px-12`}>
<div id="jscad-customizer-block" ref={ref}> <div id="jscad-customizer-block" ref={ref}>

View File

@@ -12,7 +12,7 @@ export const stlToGeometry = (url) =>
export interface RenderArgs { export interface RenderArgs {
code: State['code'] code: State['code']
parameters: any, parameters: any
settings: { settings: {
camera: State['camera'] camera: State['camera']
viewerSize: State['viewerSize'] viewerSize: State['viewerSize']
@@ -42,7 +42,7 @@ export function createHealthyResponse({
type, type,
customizerParams, customizerParams,
lastParameters, lastParameters,
}: { }: {
date: Date date: Date
data: any data: any
consoleMessage: string consoleMessage: string

View File

@@ -86,7 +86,9 @@ export const render: DefaultKernelExport['render'] = async ({
settings, settings,
}: RenderArgs) => { }: RenderArgs) => {
if (!scriptWorker) { if (!scriptWorker) {
console.trace('************************** creating new worker ************************') console.trace(
'************************** creating new worker ************************'
)
const baseURI = document.baseURI.toString() const baseURI = document.baseURI.toString()
const script = `let baseURI = '${baseURI}' const script = `let baseURI = '${baseURI}'
importScripts(new URL('${scriptUrl}',baseURI)) importScripts(new URL('${scriptUrl}',baseURI))
@@ -127,14 +129,14 @@ export const render: DefaultKernelExport['render'] = async ({
scriptWorker.postMessage({ action: 'init', baseURI, alias: [] }) scriptWorker.postMessage({ action: 'init', baseURI, alias: [] })
} }
if(parameters){ if (parameters) {
// we are not evaluating code, but reacting to parameters change // we are not evaluating code, but reacting to parameters change
scriptWorker.postMessage({ scriptWorker.postMessage({
action: 'updateParams', action: 'updateParams',
worker: 'script', worker: 'script',
params: parameters, params: parameters,
}) })
}else{ } else {
scriptWorker.postMessage({ scriptWorker.postMessage({
action: 'runScript', action: 'runScript',
worker: 'script', worker: 'script',
@@ -154,7 +156,7 @@ export const render: DefaultKernelExport['render'] = async ({
await waitResult await waitResult
resolveReference = null resolveReference = null
if(parameters) delete response.customizerParams if (parameters) delete response.customizerParams
return response return response
} }

View File

@@ -1,32 +1,37 @@
const GROUP_SELECTOR = 'DIV[type="group"]' const GROUP_SELECTOR = 'DIV[type="group"]'
const INPUT_SELECTOR = 'INPUT, SELECT' const INPUT_SELECTOR = 'INPUT, SELECT'
function forEachInput(target, callback){ function forEachInput(target, callback) {
target.querySelectorAll(INPUT_SELECTOR).forEach(callback) target.querySelectorAll(INPUT_SELECTOR).forEach(callback)
} }
function forEachGroup(target, callback){ function forEachGroup(target, callback) {
target.querySelectorAll(GROUP_SELECTOR).forEach(callback) target.querySelectorAll(GROUP_SELECTOR).forEach(callback)
} }
const numeric = {number:1, float:1, int:1, range:1, slider:1} const numeric = { number: 1, float: 1, int: 1, range: 1, slider: 1 }
function applyRange(inp){ function applyRange(inp) {
let label = inp.previousElementSibling let label = inp.previousElementSibling
if(label && label.tagName == 'LABEL'){ if (label && label.tagName == 'LABEL') {
let info = label.querySelector('I') let info = label.querySelector('I')
if(info) info.innerHTML = inp.value if (info) info.innerHTML = inp.value
} }
} }
export function genParams(defs, target, storedParams={}, callback=undefined, buttons=['reset','save','load','edit','link']){ export function genParams(
defs,
target,
storedParams = {},
callback = undefined,
buttons = ['reset', 'save', 'load', 'edit', 'link']
) {
let funcs = { let funcs = {
group:function({name,type, caption, captions, value, min,max}){ group: function ({ name, type, caption, captions, value, min, max }) {
return '' return ''
}, },
choice:inputChoice, choice: inputChoice,
radio:inputRadio, radio: inputRadio,
float: inputNumber, float: inputNumber,
range: inputNumber, range: inputNumber,
slider: inputNumber, slider: inputNumber,
@@ -38,155 +43,173 @@ export function genParams(defs, target, storedParams={}, callback=undefined, but
password: inputNumber, password: inputNumber,
color: inputNumber, color: inputNumber,
// TODO radio similar options as choice // TODO radio similar options as choice
checkbox :function({name,type, caption, captions, value, checked, min,max}){ checkbox: function ({
let checkedStr = (value === 'checked' || value === true) ? 'checked':'' name,
type,
caption,
captions,
value,
checked,
min,
max,
}) {
let checkedStr = value === 'checked' || value === true ? 'checked' : ''
return `<input type="checkbox" name="${name}" ${checkedStr}/>` return `<input type="checkbox" name="${name}" ${checkedStr}/>`
}, },
number: inputNumber, number: inputNumber,
} }
function inputRadio({name, type, captions, value, values}){ function inputRadio({ name, type, captions, value, values }) {
if(!captions) captions = values if (!captions) captions = values
let ret = '<div type="radio">' let ret = '<div type="radio">'
for(let i =0; i<values.length; i++){ for (let i = 0; i < values.length; i++) {
let checked = (value == values[i] || value == captions[i] ) ? 'checked':'' let checked = value == values[i] || value == captions[i] ? 'checked' : ''
ret += `<label><input type="radio" _type="${type}" name="${name}" numeric="${typeof values[0] == 'number' ? '1':'0'}" value="${values[i]}" ${checked}/>${captions[i]}</label>` ret += `<label><input type="radio" _type="${type}" name="${name}" numeric="${
typeof values[0] == 'number' ? '1' : '0'
}" value="${values[i]}" ${checked}/>${captions[i]}</label>`
} }
return ret +'</div>' return ret + '</div>'
} }
function inputChoice({name, type, captions, value, values}){ function inputChoice({ name, type, captions, value, values }) {
if(!captions) captions = values if (!captions) captions = values
let ret = `<select _type="${type}" name="${name}" numeric="${typeof values[0] == 'number' ? '1':'0'}">` let ret = `<select _type="${type}" name="${name}" numeric="${
typeof values[0] == 'number' ? '1' : '0'
}">`
for(let i =0; i<values.length; i++){ for (let i = 0; i < values.length; i++) {
let checked = (value == values[i] || value == captions[i] ) ? 'selected':'' let checked = value == values[i] || value == captions[i] ? 'selected' : ''
ret += `<option value="${values[i]}" ${checked}>${captions[i]}</option>` ret += `<option value="${values[i]}" ${checked}>${captions[i]}</option>`
} }
return ret + '</select>' return ret + '</select>'
} }
function inputNumber(def){ function inputNumber(def) {
let {name,type, value, min,max, step, placeholder, live} = def let { name, type, value, min, max, step, placeholder, live } = def
if(value === null || value === undefined) value = numeric[type] ? 0:''; if (value === null || value === undefined) value = numeric[type] ? 0 : ''
let inputType = type let inputType = type
if(type == 'int' || type=='float') inputType = 'number' if (type == 'int' || type == 'float') inputType = 'number'
if(type == 'range' || type=='slider') inputType = 'range' if (type == 'range' || type == 'slider') inputType = 'range'
var str = `<input _type="${type}" type="${inputType}" name="${name}"` var str = `<input _type="${type}" type="${inputType}" name="${name}"`
if(step !== undefined) str += ` step="${step||''}"` if (step !== undefined) str += ` step="${step || ''}"`
if(min !== undefined) str += ` min="${min||''}"` if (min !== undefined) str += ` min="${min || ''}"`
if(max !== undefined) str += ` max="${max||''}"` if (max !== undefined) str += ` max="${max || ''}"`
if(value !== undefined) str += ` value="${value}"` if (value !== undefined) str += ` value="${value}"`
if(live !== undefined) str += ` live="${live ? 1:0}"` if (live !== undefined) str += ` live="${live ? 1 : 0}"`
if(placeholder !== undefined) str += ` placeholder="${placeholder}"` if (placeholder !== undefined) str += ` placeholder="${placeholder}"`
return str+'/>'; return str + '/>'
} }
let html = ''; let html = ''
let closed = false let closed = false
let missing = {} let missing = {}
defs.forEach(def=>{ defs.forEach((def) => {
let {type, caption, name} = def let { type, caption, name } = def
if(storedParams[name] !== undefined){ if (storedParams[name] !== undefined) {
def.value = storedParams[name]; def.value = storedParams[name]
}else { } else {
def.value = def.initial || def['default'] || def.checked def.value = def.initial || def['default'] || def.checked
} }
if(type == 'group'){ if (type == 'group') {
closed = def.value == 'closed' closed = def.value == 'closed'
} }
def.closed = closed def.closed = closed
html +=`<div class="form-line" type="${def.type}" closed="${closed ? 1:0}" ` html += `<div class="form-line" type="${def.type}" closed="${
if(type == 'group') html += ` name="${name}"` closed ? 1 : 0
html +=`">` }" `
if (type == 'group') html += ` name="${name}"`
html += `">`
html += `<label` html += `<label`
if(type == 'group') html += ` name="${name}"` if (type == 'group') html += ` name="${name}"`
html += `>` html += `>`
if(type == 'checkbox') html += funcs[type](def) if (type == 'checkbox') html += funcs[type](def)
html += `${caption}<i>${def.value}</i></label>` html += `${caption}<i>${def.value}</i></label>`
if(funcs[type] && type != 'checkbox') html += funcs[type](def) if (funcs[type] && type != 'checkbox') html += funcs[type](def)
if(!funcs[type]) missing[type] = 1 if (!funcs[type]) missing[type] = 1
html +='</div>\n' html += '</div>\n'
}) })
let missingKeys = Object.keys(missing) let missingKeys = Object.keys(missing)
if(missingKeys.length) console.log('missing param impl',missingKeys); if (missingKeys.length) console.log('missing param impl', missingKeys)
function _callback(source='change'){ function _callback(source = 'change') {
if(callback) callback(getParams(target), source) if (callback) callback(getParams(target), source)
} }
html +='<div class="jscad-param-buttons"><div>' html += '<div class="jscad-param-buttons"><div>'
buttons.forEach(b=>{ buttons.forEach((b) => {
if(typeof b === 'string') b = {id:b, name:b} if (typeof b === 'string') b = { id: b, name: b }
let {id,name} = b let { id, name } = b
html += `<button action="${id}"><b>${name}</b></button>` html += `<button action="${id}"><b>${name}</b></button>`
}) })
html += '</div></div>' html += '</div></div>'
target.innerHTML = html target.innerHTML = html
forEachInput(target, inp=>{ forEachInput(target, (inp) => {
let type = inp.type let type = inp.type
inp.addEventListener('input', function(evt){ inp.addEventListener('input', function (evt) {
applyRange(inp) applyRange(inp)
if(inp.getAttribute('live') === '1') _callback('live'); if (inp.getAttribute('live') === '1') _callback('live')
}) })
if(inp.getAttribute('live') !== '1') inp.addEventListener('change', _callback) if (inp.getAttribute('live') !== '1')
inp.addEventListener('change', _callback)
}) })
function groupClick(evt){ function groupClick(evt) {
var groupDiv = evt.target var groupDiv = evt.target
if(groupDiv.tagName === 'LABEL') groupDiv = groupDiv.parentNode if (groupDiv.tagName === 'LABEL') groupDiv = groupDiv.parentNode
var closed = (groupDiv.getAttribute('closed') == '1') ? '0':'1' var closed = groupDiv.getAttribute('closed') == '1' ? '0' : '1'
var name = evt.target.getAttribute('name') var name = evt.target.getAttribute('name')
do{ do {
groupDiv.setAttribute('closed', closed) groupDiv.setAttribute('closed', closed)
groupDiv = groupDiv.nextElementSibling groupDiv = groupDiv.nextElementSibling
}while(groupDiv && groupDiv.getAttribute('type') != 'group') } while (groupDiv && groupDiv.getAttribute('type') != 'group')
_callback('group') _callback('group')
} }
forEachGroup(target, div=>{ forEachGroup(target, (div) => {
div.onclick=groupClick div.onclick = groupClick
}) })
} }
export function getParams(target){ export function getParams(target) {
let params = {} let params = {}
if(!target) return params if (!target) return params
forEachGroup(target,elem=>{ forEachGroup(target, (elem) => {
let name = elem.getAttribute('name') let name = elem.getAttribute('name')
params[name] = (elem.getAttribute('closed') == '1') ? 'closed':'' params[name] = elem.getAttribute('closed') == '1' ? 'closed' : ''
}) })
forEachInput(target,elem=>{ forEachInput(target, (elem) => {
let name = elem.name let name = elem.name
let value = elem.value let value = elem.value
if(elem.tagName == 'INPUT'){ if (elem.tagName == 'INPUT') {
if(elem.type == 'checkbox') value = elem.checked if (elem.type == 'checkbox') value = elem.checked
if(elem.type == 'range' || elem.type == 'color') applyRange(elem) if (elem.type == 'range' || elem.type == 'color') applyRange(elem)
} }
if(numeric[elem.getAttribute('type')] || elem.getAttribute('numeric') == '1') value = parseFloat(value || 0) if (
numeric[elem.getAttribute('type')] ||
elem.getAttribute('numeric') == '1'
)
value = parseFloat(value || 0)
if(elem.type == 'radio' && !elem.checked) return // skip if not checked radio button if (elem.type == 'radio' && !elem.checked) return // skip if not checked radio button
params[name] = value params[name] = value
}) })
return params; return params
} }

View File

@@ -180,7 +180,8 @@ export const useIdeState = (): [State, (actionOrThunk: any) => any] => {
...state.objectData, ...state.objectData,
type: payload.objectData?.type, type: payload.objectData?.type,
data: payload.objectData?.data, data: payload.objectData?.data,
customizerParams: payload.customizerParams || state.objectData.customizerParams, customizerParams:
payload.customizerParams || state.objectData.customizerParams,
lastParameters: payload.lastParameters, lastParameters: payload.lastParameters,
}, },
consoleMessages: payload.message consoleMessages: payload.message
@@ -246,7 +247,7 @@ export const useIdeState = (): [State, (actionOrThunk: any) => any] => {
interface RequestRenderArgs { interface RequestRenderArgs {
state: State state: State
dispatch: any dispatch: any
parameters: any, parameters: any
code: State['code'] code: State['code']
camera: State['camera'] camera: State['camera']
viewerSize: State['viewerSize'] viewerSize: State['viewerSize']
@@ -280,7 +281,8 @@ export const requestRender = ({
quality, quality,
}, },
}) })
.then(({ objectData, message, status, customizerParams, lastParameters }) => { .then(
({ objectData, message, status, customizerParams, lastParameters }) => {
if (status === 'error') { if (status === 'error') {
dispatch({ dispatch({
type: 'errorRender', type: 'errorRender',
@@ -299,7 +301,8 @@ export const requestRender = ({
}) })
return objectData return objectData
} }
}) }
)
.catch(() => dispatch({ type: 'resetLoading' })) // TODO should probably display something to the user here .catch(() => dispatch({ type: 'resetLoading' })) // TODO should probably display something to the user here
} }
} }