nested props

This commit is contained in:
jrkb 2023-10-07 18:01:00 +02:00
parent 7eb3ab7615
commit fad1bbb875
4 changed files with 117 additions and 32 deletions

View file

@ -1,6 +1,10 @@
import { import {
mapValue, mapValue,
mix, mix,
toCssClass,
flattenObject,
deFlattenObject,
clone,
} from './utils.js'; } from './utils.js';
window.mapValue = mapValue; window.mapValue = mapValue;
@ -100,28 +104,28 @@ const Audio = function(tp, record) {
console.log('Audio::addAudioOptions::error',`cannot find panelPropTitle "${propTitle}"`); console.log('Audio::addAudioOptions::error',`cannot find panelPropTitle "${propTitle}"`);
return; return;
} }
if (tp.getPanel().querySelector(`.audioOptions${propTitle}`) !== null) { if (tp.getPanel().querySelector(toCssClass(`audioOptions${propTitle}`, '.')) !== null) {
//console.log('Audio::addAudioOptions::error',`audioOptions already exist for "${propTitle}"`); //console.log('Audio::addAudioOptions::error',`audioOptions already exist for "${propTitle}"`);
return; return;
} }
const container = tp.getPanelPropContainer(panelPropTitle); const container = panelPropTitle.parentNode.parentNode;
const mappingOptions = mapping[layer.id()][propTitle]; const mappingOptions = mapping[layer.id()][propTitle];
const panel = tp.getPanel(); const panel = tp.getPanel();
const audioOptions = document.createElement('div'); const audioOptions = document.createElement('div');
audioOptions.classList.add('audioOptions'); audioOptions.classList.add('audioOptions');
audioOptions.classList.add('audioOptionsTypeDefault'); audioOptions.classList.add('audioOptionsTypeDefault');
audioOptions.classList.add(`audioOptions${propTitle}`); audioOptions.classList.add(toCssClass(`audioOptions${propTitle}`));
audioOptions.style.position = 'relative'; audioOptions.style.position = 'relative';
audioOptions.style.width = '100%'; audioOptions.style.width = '100%';
audioOptions.style.background = 'rgba(0,255,255,0.2)'; audioOptions.style.background = 'rgba(0,255,255,0.2)';
audioOptions.style.order = parseInt(container.style.order) + 1; audioOptions.style.order = parseInt(container.style.order) + 1;
const updateMappingOptions = () => { const updateMappingOptions = () => {
mappingOptions.min_out = parseFloat(panel.querySelector(`#audio_min${propTitle}`).value); mappingOptions.min_out = parseFloat(panel.querySelector(toCssClass(`audio_min${propTitle}`,'#')).value);
mappingOptions.max_out = parseFloat(panel.querySelector(`#audio_max${propTitle}`).value); mappingOptions.max_out = parseFloat(panel.querySelector(toCssClass(`audio_max${propTitle}`,'#')).value);
mappingOptions.sync = mappingOptions.sync =
panel.querySelector(`input[name="audio_sync${propTitle}"]:checked`).value; panel.querySelector(`input[name="${toCssClass('audio_sync' + propTitle)}"]:checked`).value;
const s = panel.querySelector(`#audio_smoothing${propTitle}`).value; const s = panel.querySelector(toCssClass(`audio_smoothing${propTitle}`,'#')).value;
mappingOptions.smoothing = parseFloat(s); mappingOptions.smoothing = parseFloat(s);
}; };
@ -132,24 +136,24 @@ const Audio = function(tp, record) {
min_inputDom_label.innerHTML = 'audio_min'; min_inputDom_label.innerHTML = 'audio_min';
const min_inputDom = document.createElement('input'); const min_inputDom = document.createElement('input');
min_inputDom.type = 'number'; min_inputDom.type = 'number';
min_inputDom.name = `audio_min${propTitle}`; min_inputDom.name = toCssClass(`audio_min${propTitle}`);
min_inputDom.id = `audio_min${propTitle}`; min_inputDom.id = toCssClass(`audio_min${propTitle}`);
min_inputDom.value = `${mappingOptions.min_out}`; min_inputDom.value = `${mappingOptions.min_out}`;
const max_inputDom_label = document.createElement('label'); const max_inputDom_label = document.createElement('label');
max_inputDom_label.for = 'audio_max'; max_inputDom_label.for = 'audio_max';
max_inputDom_label.innerHTML = 'audio_max'; max_inputDom_label.innerHTML = 'audio_max';
const max_inputDom = document.createElement('input'); const max_inputDom = document.createElement('input');
max_inputDom.type = 'number'; max_inputDom.type = 'number';
max_inputDom.name = `audio_max${propTitle}`; max_inputDom.name = toCssClass(`audio_max${propTitle}`);
max_inputDom.id = `audio_max${propTitle}`; max_inputDom.id = toCssClass(`audio_max${propTitle}`);
max_inputDom.value = `${mappingOptions.max_out}`; max_inputDom.value = `${mappingOptions.max_out}`;
const smoothing_inputDom_label = document.createElement('label'); const smoothing_inputDom_label = document.createElement('label');
smoothing_inputDom_label.for = 'audio_smoothing'; smoothing_inputDom_label.for = 'audio_smoothing';
smoothing_inputDom_label.innerHTML = 'audio_smoothing'; smoothing_inputDom_label.innerHTML = 'audio_smoothing';
const smoothing_inputDom = document.createElement('input'); const smoothing_inputDom = document.createElement('input');
smoothing_inputDom.type = 'number'; smoothing_inputDom.type = 'number';
smoothing_inputDom.name = `audio_smoothing${propTitle}`; smoothing_inputDom.name = toCssClass(`audio_smoothing${propTitle}`);
smoothing_inputDom.id = `audio_smoothing${propTitle}`; smoothing_inputDom.id = toCssClass(`audio_smoothing${propTitle}`);
smoothing_inputDom.value = mappingOptions.smoothing; smoothing_inputDom.value = mappingOptions.smoothing;
smoothing_inputDom.min = 0; smoothing_inputDom.min = 0;
smoothing_inputDom.max = 1; smoothing_inputDom.max = 1;
@ -173,8 +177,8 @@ const Audio = function(tp, record) {
sync_inputDom_label.innerHTML = o; sync_inputDom_label.innerHTML = o;
const sync_inputDom = document.createElement('input'); const sync_inputDom = document.createElement('input');
sync_inputDom.type = 'radio'; sync_inputDom.type = 'radio';
sync_inputDom.name = `audio_sync${propTitle}`; sync_inputDom.name = toCssClass(`audio_sync${propTitle}`);
sync_inputDom.id = `audio_sync${propTitle}${o}`; sync_inputDom.id = toCssClass(`audio_sync${propTitle}${o}`);
sync_inputDom.value = o; sync_inputDom.value = o;
// default select first option // default select first option
if (o === mappingOptions.sync) { if (o === mappingOptions.sync) {
@ -257,11 +261,11 @@ const Audio = function(tp, record) {
// only selected layers have options // only selected layers have options
// otherwise the ui is not there // otherwise the ui is not there
if (layer.isSelected()) { if (layer.isSelected()) {
const audioOptions = panel.querySelector(`.audioOptions${propTitle}`); const audioOptions = panel.querySelector(toCssClass(`audioOptions${propTitle}`,'.'));
if (audioOptions !== null) { if (audioOptions !== null) {
audioOptions.remove(); audioOptions.remove();
} }
const audioButton = panel.querySelector(`.audioButton${propTitle}`); const audioButton = panel.querySelector(toCssClass(`audioButton${propTitle}`,'.'));
if (audioButton !== null) { if (audioButton !== null) {
audioButton.classList.remove('active'); audioButton.classList.remove('active');
} }
@ -273,7 +277,8 @@ const Audio = function(tp, record) {
const panel = tp.getPanel(); const panel = tp.getPanel();
const panelPropTitle = tp.getPanelPropTitle(propTitle); const panelPropTitle = tp.getPanelPropTitle(propTitle);
if (panelPropTitle !== null) { if (panelPropTitle !== null) {
const container = tp.getPanelPropContainer(panelPropTitle); //const container = tp.getPanelPropContainer(panelPropTitle);
const container = panelPropTitle.parentNode.parentNode;
if (container === null) { if (container === null) {
console.log("Audio::addAudioButton", console.log("Audio::addAudioButton",
@ -285,7 +290,7 @@ const Audio = function(tp, record) {
} else { } else {
const button = document.createElement('div'); const button = document.createElement('div');
button.classList.add('audioButton'); button.classList.add('audioButton');
button.classList.add(`audioButton${propTitle}`); button.classList.add(toCssClass(`audioButton${propTitle}`));
button.innerHTML = `<img src="/web/assets/sound.svg" alt="audio" />`; button.innerHTML = `<img src="/web/assets/sound.svg" alt="audio" />`;
container.append(button); container.append(button);
button.addEventListener('click', () => { button.addEventListener('click', () => {
@ -312,7 +317,9 @@ const Audio = function(tp, record) {
}; };
const injectPanel = (layer) => { const injectPanel = (layer) => {
const props = Object.keys(layer.theatreObject.value); const flatValues = clone(layer.theatreObject.value);
flattenObject(flatValues, ['color']);
const props = Object.keys(flatValues);
props.forEach((propTitle) => { props.forEach((propTitle) => {
if (config.audio.ignoreProps.indexOf(propTitle) < 0) { if (config.audio.ignoreProps.indexOf(propTitle) < 0) {
let isActive = false; let isActive = false;
@ -604,7 +611,6 @@ const Audio = function(tp, record) {
default: default:
break; break;
} }
if (m.sync === 'volume') {}
}); });
} }
}); });
@ -626,13 +632,15 @@ const Audio = function(tp, record) {
}; };
}); });
Object.keys(values).forEach((layerID) => { Object.keys(values).forEach((layerID) => {
deFlattenObject(values[layerID]);
record.liveUpdater.immediateUpdate(getLayer(layerID), values[layerID]); record.liveUpdater.immediateUpdate(getLayer(layerID), values[layerID]);
}); });
} }
} else { } else {
propsToSet.forEach((p) => { propsToSet.forEach((p) => {
const title = tp const title = tp
.getPanelPropContainer(p.title); .getPanelPropTitle(p.title)
.parentNode.parentNode;
if (title !== null) { if (title !== null) {
const inputElement = title const inputElement = title

View file

@ -1,6 +1,9 @@
import { import {
clone, clone,
sequencialPromises, sequencialPromises,
toCssClass,
flattenObject,
deFlattenObject,
} from './utils.js'; } from './utils.js';
const LiveBuffer = function() { const LiveBuffer = function() {
@ -150,7 +153,8 @@ const Record = function(tp) {
// handle UI only if layer is selected // handle UI only if layer is selected
if (getLayer(layerID).isSelected()) { if (getLayer(layerID).isSelected()) {
const button = tp const button = tp
.getPanelPropContainer(propTitle) .getPanelPropTitle(propTitle)
.parentNode.parentNode
.querySelector('.recordButton'); .querySelector('.recordButton');
if (button !== null) { if (button !== null) {
button.classList.add('active'); button.classList.add('active');
@ -171,7 +175,8 @@ const Record = function(tp) {
// handle UI only if layer is selected // handle UI only if layer is selected
if (getLayer(layerID).isSelected()) { if (getLayer(layerID).isSelected()) {
const button = tp const button = tp
.getPanelPropContainer(propTitle) .getPanelPropTitle(propTitle)
.parentNode.parentNode
.querySelector('.recordButton'); .querySelector('.recordButton');
if (button !== null) { if (button !== null) {
button.classList.remove('active'); button.classList.remove('active');
@ -188,7 +193,7 @@ const Record = function(tp) {
const panel = tp.getPanel(); const panel = tp.getPanel();
const panelPropTitle = tp.getPanelPropTitle(propTitle); const panelPropTitle = tp.getPanelPropTitle(propTitle);
if (panelPropTitle !== null) { if (panelPropTitle !== null) {
const container = tp.getPanelPropContainer(panelPropTitle); const container = panelPropTitle.parentNode.parentNode;
if (container === null) { if (container === null) {
console.log("Record::addRecordButton", console.log("Record::addRecordButton",
`impossible! cannot find panelPropContainer for ${propTitle}`); `impossible! cannot find panelPropContainer for ${propTitle}`);
@ -199,7 +204,7 @@ const Record = function(tp) {
} else { } else {
const button = document.createElement('div'); const button = document.createElement('div');
button.classList.add('recordButton'); button.classList.add('recordButton');
button.classList.add(`recordButton${propTitle}`); button.classList.add(toCssClass(`recordButton${propTitle}`));
button.innerHTML = `<img src="/web/assets/record.svg" alt="record" />`; button.innerHTML = `<img src="/web/assets/record.svg" alt="record" />`;
container.append(button); container.append(button);
button.addEventListener('click', () => { button.addEventListener('click', () => {
@ -238,7 +243,7 @@ const Record = function(tp) {
const panel = tp.getPanel(); const panel = tp.getPanel();
const panelPropTitle = tp.getPanelPropTitle(propTitle); const panelPropTitle = tp.getPanelPropTitle(propTitle);
if (panelPropTitle !== null) { if (panelPropTitle !== null) {
const container = tp.getPanelPropContainer(panelPropTitle); const container = panelPropTitle.parentNode.parentNode;
if (container === null) { if (container === null) {
console.log("Record::cloneInput", console.log("Record::cloneInput",
`impossible! cannot find panelPropContainer for ${propTitle}`); `impossible! cannot find panelPropContainer for ${propTitle}`);
@ -268,7 +273,7 @@ const Record = function(tp) {
const panel = tp.getPanel(); const panel = tp.getPanel();
const panelPropTitle = tp.getPanelPropTitle(propTitle); const panelPropTitle = tp.getPanelPropTitle(propTitle);
if (panelPropTitle !== null) { if (panelPropTitle !== null) {
const container = tp.getPanelPropContainer(panelPropTitle); const container = panelPropTitle.parentNode.parentNode;
if (container === null) { if (container === null) {
console.log("Record::uncloneInput", console.log("Record::uncloneInput",
`impossible! cannot find panelPropContainer for ${propTitle}`); `impossible! cannot find panelPropContainer for ${propTitle}`);
@ -295,7 +300,9 @@ const Record = function(tp) {
}; };
const injectPanel = (layer) => { const injectPanel = (layer) => {
const props = Object.keys(layer.theatreObject.value); const flatValues = clone(layer.theatreObject.value);
flattenObject(flatValues, ['color']);
const props = Object.keys(flatValues);
props.forEach((propTitle) => { props.forEach((propTitle) => {
if (config.record.ignoreProps.indexOf(propTitle) < 0) { if (config.record.ignoreProps.indexOf(propTitle) < 0) {
addRecordButton(layer, propTitle); addRecordButton(layer, propTitle);
@ -328,7 +335,8 @@ const Record = function(tp) {
[propTitle]: value, [propTitle]: value,
}; };
buffy.addValues(layerID, recording, position, lastPosition); buffy.addValues(layerID, recording, position, lastPosition);
const merged = buffy.getValues(layerID, position); const merged = clone(buffy.getValues(layerID, position));
deFlattenObject(merged);
liveUpdater.immediateUpdate(layer, merged); liveUpdater.immediateUpdate(layer, merged);
lastPosition = position; lastPosition = position;
}); });
@ -366,7 +374,7 @@ const Record = function(tp) {
// and should be the layer anyways // and should be the layer anyways
uncloneInput(layerID, propTitle); uncloneInput(layerID, propTitle);
keyframes.push({ keyframes.push({
path: [propTitle], path: propTitle.split('.'),
keyframes: hot[layerID][propTitle].recording, keyframes: hot[layerID][propTitle].recording,
}); });
}); });

View file

@ -167,7 +167,8 @@ const TheatrePlay = function(autoInit = false) {
window.removeEventListener('sequenceEvent', finishedSequencedEvent); window.removeEventListener('sequenceEvent', finishedSequencedEvent);
resolve(true); resolve(true);
} else { } else {
console.log('TheatrePlayu::setSequenced', 'ignored event', e, e.detail); // pretty verbose
//console.log('TheatrePlayu::setSequenced', 'ignored event', e, e.detail);
} }
} }
}; };

View file

@ -414,6 +414,70 @@ const sequencialPromises = async (iterable, callback = false) => {
} }
}; };
// NOTE: this is not perfect,
// but good enough for our use case
// theoretically we would have to get
// rid of all special characters
const toCssClass = (text, prefix = '') => {
return prefix + 'vt_' + text
.replaceAll('.','-dot-')
.replaceAll('#','-hash-')
;
};
const renameProperty = (o, old_key, new_key) => {
Object.defineProperty(o, new_key,
Object.getOwnPropertyDescriptor(o, old_key));
delete o[old_key];
};
const flattenObject = (o, ignoreKeys = [], pathSymbol = '.') => {
if (typeof o !== 'object') {
return o;
}
let doItAgain = false;
Object.keys(o).forEach((k) => {
if (typeof o[k] === 'object' &&
ignoreKeys.indexOf(k) < 0) {
doItAgain = true;
Object.keys(o[k]).forEach((sk) => {
const nk = `${k}${pathSymbol}${sk}`;
o[nk] = o[k][sk];
});
delete o[k];
}
});
if (doItAgain) {
flattenObject(o, ignoreKeys, pathSymbol);
}
};
const deFlattenObject = (o, ignoreKeys = [], pathSymbol = '.') => {
Object.keys(o).forEach((k) => {
if (ignoreKeys.indexOf(k) < 0) {
const ks = k.split(pathSymbol);
if (ks.length > 1) {
let sos = o[k];
for (let i = ks.length - 1; i > 0; i--) {
sos = {[ks[i]]: sos};
}
o[ks[0]] = sos;
delete o[k];
}
}
});
};
/////////////////////////////////////
// you can test these functions in
// the browser like so:
/*
import("./web/js/utils.js").then((module) => {
const toast = module.toCssClass('lol.lol');
console.log(toast);
});
*/
//
///////////////////////////////////// /////////////////////////////////////
export { export {
@ -435,4 +499,8 @@ export {
mapValue, mapValue,
isMobile, isMobile,
sequencialPromises, sequencialPromises,
toCssClass,
renameProperty,
flattenObject,
deFlattenObject,
} }