import type { RawCustomizerParams } from '../common'
const GROUP_SELECTOR = 'DIV[type="group"]'
const INPUT_SELECTOR = 'INPUT, SELECT'
function forEachInput(
target: HTMLElement,
callback: (e: HTMLInputElement) => void
) {
target.querySelectorAll(INPUT_SELECTOR).forEach(callback)
}
function forEachGroup(target: HTMLElement, callback: (e: HTMLElement) => void) {
target.querySelectorAll(GROUP_SELECTOR).forEach(callback)
}
const numeric = { number: 1, float: 1, int: 1, range: 1, slider: 1 }
function applyRange(inp) {
const label = inp.previousElementSibling
if (label && label.tagName == 'LABEL') {
const info = label.querySelector('I')
if (info) info.innerHTML = inp.value
}
}
export function genParams(
defs,
target,
storedParams = {},
callback: (values: RawCustomizerParams, source: any) => void = undefined,
buttons = ['reset', 'save', 'load', 'edit', 'link']
) {
const funcs = {
group: () => '',
choice: inputChoice,
radio: inputRadio,
float: inputNumber,
range: inputNumber,
slider: inputNumber,
int: inputNumber,
text: inputNumber,
url: inputNumber,
email: inputNumber,
date: inputNumber,
password: inputNumber,
color: inputNumber,
// TODO radio similar options as choice
checkbox: function ({ name, value }) {
const checkedStr = value === 'checked' || value === true ? 'checked' : ''
return ``
},
number: inputNumber,
}
function inputRadio({ name, type, captions, value, values }) {
if (!captions) captions = values
let ret = '
'
for (let i = 0; i < values.length; i++) {
const checked =
value == values[i] || value == captions[i] ? 'checked' : ''
ret += ``
}
return ret + '
'
}
function inputChoice({ name, type, captions, value, values }) {
if (!captions) captions = values
let ret = `'
}
function inputNumber(def) {
let { name, type, value, min, max, step, placeholder, live } = def
if (value === null || value === undefined) value = numeric[type] ? 0 : ''
let inputType = type
if (type == 'int' || type == 'float') inputType = 'number'
if (type == 'range' || type == 'slider') inputType = 'range'
let str = `'
}
let html = ''
let closed = false
const missing = {}
defs.forEach((def) => {
const { type, caption, name } = def
if (storedParams[name] !== undefined) {
def.value = storedParams[name]
} else {
def.value = def.initial || def['default'] || def.checked
}
if (type == 'group') {
closed = def.value == 'closed'
}
def.closed = closed
html += ``
html += ``
if (funcs[type] && type != 'checkbox') html += funcs[type](def)
if (!funcs[type]) missing[type] = 1
html += '
\n'
})
const missingKeys = Object.keys(missing)
if (missingKeys.length) console.log('missing param impl', missingKeys)
function _callback(source = 'change') {
if (callback && source !== 'group') callback(getParams(target), source)
}
html += ''
target.innerHTML = html
forEachInput(target, (inp) => {
const type = inp.type
inp.addEventListener('input', function (evt) {
applyRange(inp)
if (inp.getAttribute('live') === '1') _callback('live')
})
if (inp.getAttribute('live') !== '1')
inp.addEventListener('change', () => _callback('change'))
})
function groupClick(evt) {
let groupDiv = evt.target
if (groupDiv.tagName === 'LABEL') groupDiv = groupDiv.parentNode
const closed = groupDiv.getAttribute('closed') == '1' ? '0' : '1'
do {
groupDiv.setAttribute('closed', closed)
groupDiv = groupDiv.nextElementSibling
} while (groupDiv && groupDiv.getAttribute('type') != 'group')
_callback('group')
}
forEachGroup(target, (div) => {
div.onclick = groupClick
})
}
function getParams(target: HTMLElement): RawCustomizerParams {
const params = {}
if (!target) return params
forEachGroup(target, (elem) => {
const name = elem.getAttribute('name')
params[name] = elem.getAttribute('closed') == '1' ? 'closed' : ''
})
forEachInput(target, (elem) => {
const name = elem.name
let value: RawCustomizerParams[string] = elem.value
if (elem.tagName == 'INPUT') {
if (elem.type == 'checkbox') value = elem?.checked
if (elem.type == 'range' || elem.type == 'color') applyRange(elem)
}
if (
numeric[elem.getAttribute('type')] ||
elem.getAttribute('numeric') == '1'
) {
value = parseFloat(String(value || 0))
} else if (
value &&
typeof value === 'string' &&
/^(\d+|\d+\.\d+)$/.test(value.trim())
) {
value = parseFloat(String(value || 0))
}
if (elem.type == 'radio' && !elem.checked) return // skip if not checked radio button
params[name] = value
})
return params
}