UNPKG

@underpostnet/underpost

Version:

Underpost Platform — end-to-end CI/CD and application-delivery toolchain CLI. Covers bare metal, Kubernetes, K3s, kubeadm, LXD, container/image orchestration, secrets, databases, cron jobs, monitoring, SSH, runners, PWA + Workbox delivery, and release orc

322 lines (321 loc) 14.6 kB
import { Badge } from './Badge.js'; import { getId, newInstance } from './CommonJs.js'; import { darkTheme, ThemeEvents } from './Css.js'; import { Input } from './Input.js'; import { ToggleSwitch } from './ToggleSwitch.js'; import { Translate } from './Translate.js'; import { s, htmls } from './VanillaJs.js'; class DropDown { static Tokens = {}; static async instance(options) { const id = options.id ? options.id : getId(DropDown.Tokens, 'dropdown-'); DropDown.Tokens[id] = { onClickEvents: {}, lastSelectValue: undefined, oncheckvalues: {}, originData: options.data ? newInstance(options.data) : [], }; const _renderSelectedBadges = async () => { if (options.type !== 'checkbox') return; const container = s(`.dropdown-current-${id}`); if (!container) return; const selected = Object.entries(DropDown.Tokens[id].oncheckvalues); if (selected.length === 0) { htmls(`.dropdown-current-${id}`, ''); return; } let badgesHtml = ''; for (const [key, val] of selected) { badgesHtml += html`<span class="inl" style="display:inline-flex;align-items:center;margin:2px;"> ${await Badge.instance({ text: html`<i class="fa-solid fa-tag" style="margin-right:3px;font-size:9px;"></i>${val.display}`, style: { background: darkTheme ? '#335' : '#cde', color: darkTheme ? '#adf' : '#246', 'border-radius': '4px', 'font-size': '11px', height: 'auto', 'min-width': 'auto', }, })} <span class="dd-badge-del-${id}" data-key="${key}" style="cursor:pointer;padding:0 4px;font-size:14px;color:${darkTheme ? '#f88' : '#a00'};line-height:1;" >&times;</span > </span>`; } htmls(`.dropdown-current-${id}`, badgesHtml); container.querySelectorAll(`.dd-badge-del-${id}`).forEach((btn) => { btn.onclick = async (e) => { e.stopPropagation(); const key = btn.dataset.key; delete DropDown.Tokens[id].oncheckvalues[key]; const dataItem = options.data.find((d) => d.value.trim().replaceAll(' ', '-') === key); if (dataItem) dataItem.checked = false; if (ToggleSwitch.Tokens[`checkbox-role-${key}`]) { const checkbox = s(`.checkbox-role-${key}-checkbox`); if (checkbox && checkbox.checked) ToggleSwitch.Tokens[`checkbox-role-${key}`].click(); } DropDown.Tokens[id].value = Object.values(DropDown.Tokens[id].oncheckvalues).map((v) => v.data); s(`.${id}`).value = DropDown.Tokens[id].value; await _renderSelectedBadges(); }; }); }; DropDown.Tokens[id]._renderSelectedBadges = _renderSelectedBadges; options.data.push({ value: 'reset', display: html`<i class="fa-solid fa-broom"></i> ${Translate.instance('clear')}`, onClick: () => { console.log('DropDown onClick', DropDown.value); if (options && options.resetOnClick) options.resetOnClick(); if (options && options.type === 'checkbox') { DropDown.Tokens[id].oncheckvalues = {}; DropDown.Tokens[id].value = []; s(`.${id}`).value = []; htmls(`.dropdown-current-${id}`, ''); if (options.serviceProvider) { htmls(`.${id}-render-container`, ''); } else { for (const optionData of options.data) { if (optionData.value !== 'reset' && optionData.value !== 'close' && optionData.checked) { optionData.checked = false; const vd = optionData.value.trim().replaceAll(' ', '-'); if (ToggleSwitch.Tokens[`checkbox-role-${vd}`]) { const checkbox = s(`.checkbox-role-${vd}-checkbox`); if (checkbox && checkbox.checked) ToggleSwitch.Tokens[`checkbox-role-${vd}`].click(); } } } } } else DropDown.Tokens[id].value = undefined; }, }); if (!(options && options.disableClose)) options.data.push({ value: 'close', display: html`<i class="fa-solid fa-xmark"></i> ${Translate.instance('close')}`, onClick: function () { console.log('DropDown onClick', DropDown.value); }, }); const switchOptionsPanel = () => { if (Array.from(s(`.dropdown-option-${id}`).classList).includes('hide')) s(`.dropdown-option-${id}`).classList.remove('hide'); else s(`.dropdown-option-${id}`).classList.add('hide'); }; const _render = async (data) => { let render = ''; let index = -1; for (const optionData of data) { index++; const i = index; const optionValue = typeof optionData.value === 'string' ? optionData.value : `option-${i}`; const valueDisplay = optionValue.trim().replaceAll(' ', '-'); const isGroup = optionData.kind === 'group' || optionData.nonSelectable === true; if (!isGroup) { setTimeout(() => { const onclick = async (e) => { if (options && options.lastSelectClass && s(`.dropdown-option-${DropDown.Tokens[id].lastSelectValue}`)) { s(`.dropdown-option-${DropDown.Tokens[id].lastSelectValue}`).classList.remove(options.lastSelectClass); } DropDown.Tokens[id].lastSelectValue = valueDisplay; if (options && options.lastSelectClass && s(`.dropdown-option-${DropDown.Tokens[id].lastSelectValue}`)) { s(`.dropdown-option-${DropDown.Tokens[id].lastSelectValue}`).classList.add(options.lastSelectClass); } if ( !(options && options.disableClose) && (options.type !== 'checkbox' || optionData.value === 'close' || optionData.value === 'reset') ) s(`.dropdown-option-${id}`).classList.add('hide'); if (options.type === 'checkbox' && ToggleSwitch.Tokens[`checkbox-role-${valueDisplay}`]) ToggleSwitch.Tokens[`checkbox-role-${valueDisplay}`].click(); if (optionData.value !== 'close') { if (optionData.value !== 'reset') { if (options.type === 'checkbox') { _renderSelectedBadges(); } else { htmls(`.dropdown-current-${id}`, optionData.display); } } else htmls(`.dropdown-current-${id}`, ''); DropDown.Tokens[id].value = options.type === 'checkbox' ? options.serviceProvider ? Object.values(DropDown.Tokens[id].oncheckvalues).map((v) => v.data) : data.filter((d) => d.checked).map((d) => d.data) : optionData.data; console.warn('current value dropdown id:' + id, DropDown.Tokens[id].value); s(`.${id}`).value = DropDown.Tokens[id].value; optionData.onClick(e); } }; DropDown.Tokens[id].onClickEvents[`dropdown-option-${id}-${i}`] = onclick; DropDown.Tokens[id].onClickEvents[`dropdown-option-${id}-${valueDisplay}`] = onclick; DropDown.Tokens[id].onClickEvents[`dropdown-option-${valueDisplay}`] = onclick; s(`.dropdown-option-${id}-${i}`).onclick = onclick; }); } render += html` <div class="in dropdown-option ${isGroup ? `dropdown-option-group dropdown-option-group-${id}` : `dropdown-option-${id}-${i} dropdown-option-${id}-${valueDisplay} dropdown-option-${valueDisplay} ${valueDisplay === 'reset' && options && !(options.resetOption === true) ? 'hide' : ''}`}" style="${isGroup ? 'cursor:default;opacity:.8;font-size:11px;letter-spacing:.08em;text-transform:uppercase;padding-top:8px;padding-bottom:6px;' : ''}" > ${!isGroup && options.type === 'checkbox' && optionData.value !== 'close' && optionData.value !== 'reset' ? html` ${await ToggleSwitch.instance({ id: `checkbox-role-${valueDisplay}`, type: 'checkbox', disabledOnClick: true, checked: optionData.checked, on: { unchecked: () => { optionData.checked = false; delete DropDown.Tokens[id].oncheckvalues[valueDisplay]; }, checked: () => { optionData.checked = true; DropDown.Tokens[id].oncheckvalues[valueDisplay] = { data: optionData.data, display: optionData.display, value: optionData.value, }; }, }, })} ` : ''}${optionData.display} </div> `; } return { render, index }; }; setTimeout(() => { if (options.type === 'checkbox') options.data.map((optionData) => { const indexValue = options.data.findIndex((t) => optionData.checked && t.value === optionData.value); if (indexValue > -1) setTimeout(() => s(`.dropdown-option-${id}-${indexValue}`).click()); }); else { const indexValue = options.data.findIndex((t) => t.value === options.value); if (indexValue > -1) setTimeout(() => s(`.dropdown-option-${id}-${indexValue}`).click()); } s(`.dropdown-label-${id}`).onclick = switchOptionsPanel; s(`.dropdown-current-${id}`).onclick = switchOptionsPanel; if (options && options.open) switchOptionsPanel(); if (options.type === 'checkbox') { ThemeEvents[`dropdown-badge-${id}`] = () => _renderSelectedBadges(); } const dropDownSearchHandle = async () => { const _data = []; if (!s(`.search-box-${id}`)) return; let _value = s(`.search-box-${id}`).value.toLowerCase(); for (const objData of options.data) { if ( options.excludeSelected && options.type === 'checkbox' && DropDown.Tokens[id].oncheckvalues[objData.value.trim().replaceAll(' ', '-')] ) continue; const objValue = objData.value.toLowerCase(); if ( objValue.match(_value) || (Translate.Data[objData.value] && Object.keys(Translate.Data[objData.value]).find((t) => Translate.Data[objData.value][t].toLowerCase().match(_value), )) ) { _data.push(objData); } } if (_data.length > 0) { const { render, index } = await _render(_data); htmls(`.${id}-render-container`, render); } else { // const { render, index } = await _render(options.data); htmls( `.${id}-render-container`, html` <div class="inl" style="padding: 10px; color: red"> <i class="fas fa-exclamation-circle"></i> ${Translate.instance('no-result-found')} </div>`, ); } }; if (options.serviceProvider) { let serviceSearchTimeout = null; s(`.search-box-${id}`).oninput = () => { clearTimeout(serviceSearchTimeout); const q = s(`.search-box-${id}`).value.trim(); if (!q) { htmls(`.${id}-render-container`, ''); return; } serviceSearchTimeout = setTimeout(async () => { try { let results = await options.serviceProvider(q); if (options.type === 'checkbox') { if (options.excludeSelected) { const selectedKeys = Object.keys(DropDown.Tokens[id].oncheckvalues); results = results.filter((item) => !selectedKeys.includes(item.value.trim().replaceAll(' ', '-'))); } results = results.map((item) => { const vd = item.value.trim().replaceAll(' ', '-'); return { ...item, checked: !!DropDown.Tokens[id].oncheckvalues[vd] }; }); } const controlItems = options.data.filter((d) => d.value === 'reset' || d.value === 'close'); const allData = [...results, ...controlItems]; if (allData.length > controlItems.length) { const { render } = await _render(allData); htmls(`.${id}-render-container`, render); } else { htmls( `.${id}-render-container`, html` <div class="inl" style="padding: 10px; color: red"> <i class="fas fa-exclamation-circle"></i> ${Translate.instance('no-result-found')} </div>`, ); } } catch (e) { console.error('DropDown serviceProvider error:', e); } }, 200); }; } else { s(`.search-box-${id}`).oninput = dropDownSearchHandle; } // Not use onblur generate bug on input toggle // s(`.search-box-${id}`).onblur = dropDownSearchHandle; }); const { render, index } = await _render(options.data); return html` <div class="inl dropdown-container ${id} ${options?.containerClass ? options.containerClass : ''}"> <div class="in dropdown-option dropdown-label-${id} ${options && options.disableSelectLabel ? 'hide' : ''}"> <i class="fa-solid fa-caret-down"> </i> ${options.label} </div> <div class="in dropdown-option dropdown-current-${id} ${options && options.disableSelectOptionsLabel ? 'hide' : ''}" ></div> <div class="in dropdown-option-${id} hide"> <div class="in dropdown-option ${options && options.disableSearchBox ? 'hide' : ''}"> ${await Input.instance({ id: `search-box-${id}`, label: html`<i class="fa-solid fa-magnifying-glass"></i> ${Translate.instance('search')}`, containerClass: 'in', placeholder: true, })} </div> <div class="${id}-render-container">${render}</div> </div> </div> `; } } export { DropDown };