added runtime toolbox customization (show/hide)
parent
f2196f0c2a
commit
7d19311098
31
index.html
31
index.html
|
|
@ -40,6 +40,9 @@
|
|||
<button id="btn-addons" title="Manage addons">
|
||||
<span class="icon">🔌</span> Addons
|
||||
</button>
|
||||
<button id="btn-customize" title="Show/hide toolbox categories and blocks">
|
||||
<span class="icon">⚙</span> Customize
|
||||
</button>
|
||||
<span id="connection-status" class="status-disconnected">Disconnected</span>
|
||||
</div>
|
||||
</header>
|
||||
|
|
@ -51,6 +54,34 @@
|
|||
<div id="blockly-div"></div>
|
||||
</div>
|
||||
|
||||
<!-- Customize toolbox sidebar -->
|
||||
<div id="customizer-panel" class="ide-panel hidden">
|
||||
<div class="panel-header">
|
||||
<span class="panel-title">Customize Toolbox</span>
|
||||
<button id="customizer-done" class="cust-done-btn" title="Exit customize mode">Done</button>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="cust-presets">
|
||||
<div class="cust-preset-row">
|
||||
<select id="preset-select" title="Saved presets">
|
||||
<option value="">— select preset —</option>
|
||||
</select>
|
||||
<button id="preset-load" title="Load selected preset">Load</button>
|
||||
<button id="preset-delete" class="btn-danger" title="Delete selected preset">Del</button>
|
||||
</div>
|
||||
<div class="cust-preset-row">
|
||||
<input type="text" id="preset-name" placeholder="Preset name..." />
|
||||
<button id="preset-save" title="Save current settings as preset">Save</button>
|
||||
</div>
|
||||
<div class="cust-preset-row">
|
||||
<button id="preset-show-all" title="Show everything">Show All</button>
|
||||
<button id="preset-hide-all" title="Hide everything">Hide All</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="customizer-tree" class="customizer-tree"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Projects right sidebar -->
|
||||
<div id="projects-panel" class="ide-panel">
|
||||
<div class="panel-header">
|
||||
|
|
|
|||
|
|
@ -76,8 +76,95 @@ export function clearAddonCategories() {
|
|||
addonCategories.length = 0;
|
||||
}
|
||||
|
||||
export function buildToolbox(deviceId) {
|
||||
/**
|
||||
* Returns the full unfiltered category list for a device (for the customizer UI).
|
||||
*/
|
||||
export function getFullCategories(deviceId) {
|
||||
const profile = devices[deviceId || currentId];
|
||||
if (!profile) return [];
|
||||
const contents = [...profile.categories];
|
||||
if (addonCategories.length) {
|
||||
contents.push(...addonCategories);
|
||||
}
|
||||
contents.push(...builtinCategories);
|
||||
return contents.filter(c => c.kind === 'category');
|
||||
}
|
||||
|
||||
// --- toolbox filter (show/hide categories & blocks) ---
|
||||
|
||||
const FILTER_KEY = 'esp32block_toolbox_filter';
|
||||
const PRESETS_KEY = 'esp32block_toolbox_presets';
|
||||
|
||||
let _customizeMode = false;
|
||||
|
||||
export function setCustomizeMode(on) { _customizeMode = on; }
|
||||
export function isCustomizeMode() { return _customizeMode; }
|
||||
|
||||
export function getFilter(deviceId) {
|
||||
try {
|
||||
const data = JSON.parse(localStorage.getItem(FILTER_KEY)) || {};
|
||||
return data[deviceId || currentId] || {};
|
||||
} catch { return {}; }
|
||||
}
|
||||
|
||||
export function setFilter(deviceId, filter) {
|
||||
let data;
|
||||
try { data = JSON.parse(localStorage.getItem(FILTER_KEY)) || {}; }
|
||||
catch { data = {}; }
|
||||
data[deviceId || currentId] = filter;
|
||||
localStorage.setItem(FILTER_KEY, JSON.stringify(data));
|
||||
}
|
||||
|
||||
// --- presets ---
|
||||
|
||||
export function getSavedPresets() {
|
||||
try { return JSON.parse(localStorage.getItem(PRESETS_KEY)) || []; }
|
||||
catch { return []; }
|
||||
}
|
||||
|
||||
export function savePreset(name, filter) {
|
||||
const presets = getSavedPresets();
|
||||
const idx = presets.findIndex(p => p.name === name);
|
||||
if (idx >= 0) presets[idx] = { name, filter };
|
||||
else presets.push({ name, filter });
|
||||
localStorage.setItem(PRESETS_KEY, JSON.stringify(presets));
|
||||
}
|
||||
|
||||
export function deletePreset(name) {
|
||||
const presets = getSavedPresets().filter(p => p.name !== name);
|
||||
localStorage.setItem(PRESETS_KEY, JSON.stringify(presets));
|
||||
}
|
||||
|
||||
// --- filter application ---
|
||||
|
||||
function applyFilter(categories, deviceId) {
|
||||
const filter = getFilter(deviceId);
|
||||
const hidCats = new Set(filter.hiddenCategories || []);
|
||||
const hidBlocks = new Set(filter.hiddenBlocks || []);
|
||||
if (!hidCats.size && !hidBlocks.size) return categories;
|
||||
|
||||
const result = [];
|
||||
for (const item of categories) {
|
||||
if (item.kind === 'sep') { result.push(item); continue; }
|
||||
if (item.kind === 'category' && hidCats.has(item.name)) continue;
|
||||
if (item.kind === 'category' && item.contents) {
|
||||
const filtered = item.contents.filter(b => !hidBlocks.has(b.type));
|
||||
if (filtered.length === 0) continue;
|
||||
result.push({ ...item, contents: filtered });
|
||||
} else {
|
||||
result.push(item);
|
||||
}
|
||||
}
|
||||
return result.filter((item, i, arr) => {
|
||||
if (item.kind !== 'sep') return true;
|
||||
if (i === 0 || i === arr.length - 1) return false;
|
||||
return arr[i - 1]?.kind !== 'sep';
|
||||
});
|
||||
}
|
||||
|
||||
export function buildToolbox(deviceId) {
|
||||
const id = deviceId || currentId;
|
||||
const profile = devices[id];
|
||||
if (!profile) return { kind: 'categoryToolbox', contents: [] };
|
||||
|
||||
const contents = [...profile.categories];
|
||||
|
|
@ -88,5 +175,6 @@ export function buildToolbox(deviceId) {
|
|||
contents.push({ kind: 'sep' });
|
||||
contents.push(...builtinCategories);
|
||||
|
||||
return { kind: 'categoryToolbox', contents };
|
||||
if (_customizeMode) return { kind: 'categoryToolbox', contents };
|
||||
return { kind: 'categoryToolbox', contents: applyFilter(contents, id) };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import { executeCode, stopExecution, saveToDevice, writeFileToDevice } from './s
|
|||
import { flashFirmware } from './serial/flasher.js';
|
||||
import { appendToTerminal, clearTerminal } from './ui/terminal.js';
|
||||
import { initResizablePanels, initPanelToggles, initProjectTabs, setProjectsPanelCallbacks } from './ui/panels.js';
|
||||
import { initToolboxCustomizer, toggleCustomizeMode, refreshCustomizer } from './ui/toolboxCustomizer.js';
|
||||
import { initProjectsDialog, refreshAll as refreshProjects, refreshDeviceList } from './ui/projectsDialog.js';
|
||||
import './style.css';
|
||||
|
||||
|
|
@ -62,6 +63,10 @@ function rebuildDeviceSelect() {
|
|||
|
||||
setDeviceListRefreshCallback(rebuildDeviceSelect);
|
||||
|
||||
// Toolbox customizer (show/hide categories & blocks)
|
||||
initToolboxCustomizer(refreshToolbox);
|
||||
document.getElementById('btn-customize').addEventListener('click', toggleCustomizeMode);
|
||||
|
||||
// ─── Live Code Preview ───────────────────────────────────
|
||||
|
||||
const codeOutput = document.getElementById('code-output');
|
||||
|
|
@ -167,8 +172,8 @@ deviceSelect.value = getDeviceId();
|
|||
deviceSelect.addEventListener('change', () => {
|
||||
setDeviceId(deviceSelect.value);
|
||||
refreshToolbox();
|
||||
refreshCustomizer();
|
||||
updateCodePreview();
|
||||
// Update Flash button tooltip/label based on device
|
||||
btnFlash.title = canFlashInBrowser()
|
||||
? 'Flash MicroPython firmware'
|
||||
: 'Download firmware (drag to device)';
|
||||
|
|
|
|||
185
src/style.css
185
src/style.css
|
|
@ -832,3 +832,188 @@ html, body {
|
|||
color: var(--bg-toolbar);
|
||||
border-color: var(--red);
|
||||
}
|
||||
|
||||
/* --- Toolbox Customizer Sidebar Panel --- */
|
||||
#customizer-panel {
|
||||
width: 280px;
|
||||
min-width: 0;
|
||||
border-left: 1px solid var(--border);
|
||||
background: var(--bg-secondary);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
#customizer-panel.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
#customizer-panel .panel-body {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.cust-done-btn {
|
||||
background: var(--accent);
|
||||
color: var(--bg-toolbar);
|
||||
border: none;
|
||||
border-radius: var(--radius);
|
||||
padding: 2px 12px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.4px;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
.cust-done-btn:hover { opacity: 0.85; }
|
||||
|
||||
/* Preset controls */
|
||||
.cust-presets {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
padding: 8px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.cust-preset-row {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.cust-preset-row select,
|
||||
.cust-preset-row input {
|
||||
flex: 1;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 4px 6px;
|
||||
font-size: 11px;
|
||||
outline: none;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.cust-preset-row select:focus,
|
||||
.cust-preset-row input:focus {
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.cust-preset-row button {
|
||||
background: var(--bg-surface);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 4px 8px;
|
||||
font-size: 11px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
transition: background 0.15s, border-color 0.15s;
|
||||
}
|
||||
|
||||
.cust-preset-row button:hover {
|
||||
background: var(--accent);
|
||||
color: var(--bg-toolbar);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.cust-preset-row button.btn-danger:hover {
|
||||
background: var(--red);
|
||||
border-color: var(--red);
|
||||
}
|
||||
|
||||
/* Category/block tree */
|
||||
.customizer-tree {
|
||||
overflow-y: auto;
|
||||
padding: 4px 6px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.cust-cat {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 6px;
|
||||
border-radius: 4px;
|
||||
user-select: none;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
|
||||
.cust-cat:hover { background: var(--bg-surface); }
|
||||
|
||||
.cust-swatch {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 3px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.cust-cat-label {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.cust-expand {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
font-size: 11px;
|
||||
cursor: pointer;
|
||||
padding: 1px 4px;
|
||||
border-radius: 3px;
|
||||
line-height: 1;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.cust-expand:hover {
|
||||
color: var(--text-primary);
|
||||
background: var(--bg-primary);
|
||||
}
|
||||
|
||||
.cust-cb {
|
||||
accent-color: var(--accent);
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.cust-blocks {
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
.cust-block-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
|
||||
.cust-block-row:hover { background: var(--bg-surface); }
|
||||
|
||||
.cust-block-label {
|
||||
font-size: 11px;
|
||||
color: var(--text-secondary);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.cust-dimmed { opacity: 0.4; }
|
||||
|
||||
.cust-cb:disabled {
|
||||
opacity: 0.3;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Active customize button highlight */
|
||||
#btn-customize.active {
|
||||
background: var(--accent);
|
||||
color: var(--bg-toolbar);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,259 @@
|
|||
import {
|
||||
getDeviceId,
|
||||
getFullCategories,
|
||||
getFilter,
|
||||
setFilter,
|
||||
setCustomizeMode,
|
||||
isCustomizeMode,
|
||||
getSavedPresets,
|
||||
savePreset,
|
||||
deletePreset,
|
||||
} from '../devices/registry.js';
|
||||
|
||||
let refreshToolboxFn = () => {};
|
||||
let panelEl, treeEl, presetSelect, presetNameInput, btnDone;
|
||||
|
||||
export function initToolboxCustomizer(refreshCb) {
|
||||
refreshToolboxFn = refreshCb;
|
||||
panelEl = document.getElementById('customizer-panel');
|
||||
treeEl = document.getElementById('customizer-tree');
|
||||
presetSelect = document.getElementById('preset-select');
|
||||
presetNameInput = document.getElementById('preset-name');
|
||||
btnDone = document.getElementById('customizer-done');
|
||||
|
||||
btnDone.addEventListener('click', exitCustomizeMode);
|
||||
document.getElementById('preset-load').addEventListener('click', loadSelectedPreset);
|
||||
document.getElementById('preset-save').addEventListener('click', saveCurrentAsPreset);
|
||||
document.getElementById('preset-delete').addEventListener('click', deleteSelectedPreset);
|
||||
document.getElementById('preset-show-all').addEventListener('click', showAll);
|
||||
document.getElementById('preset-hide-all').addEventListener('click', hideAll);
|
||||
}
|
||||
|
||||
export function toggleCustomizeMode() {
|
||||
if (isCustomizeMode()) exitCustomizeMode();
|
||||
else enterCustomizeMode();
|
||||
}
|
||||
|
||||
export function refreshCustomizer() {
|
||||
if (isCustomizeMode()) {
|
||||
renderTree();
|
||||
renderPresetDropdown();
|
||||
}
|
||||
}
|
||||
|
||||
function enterCustomizeMode() {
|
||||
setCustomizeMode(true);
|
||||
refreshToolboxFn();
|
||||
renderTree();
|
||||
renderPresetDropdown();
|
||||
panelEl.classList.remove('hidden');
|
||||
document.getElementById('btn-customize').classList.add('active');
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
}
|
||||
|
||||
function exitCustomizeMode() {
|
||||
setCustomizeMode(false);
|
||||
panelEl.classList.add('hidden');
|
||||
document.getElementById('btn-customize').classList.remove('active');
|
||||
refreshToolboxFn();
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
}
|
||||
|
||||
// ─── Filter helpers ──────────────────────────────────────
|
||||
|
||||
function currentFilter() {
|
||||
const f = getFilter(getDeviceId());
|
||||
return {
|
||||
hiddenCategories: new Set(f.hiddenCategories || []),
|
||||
hiddenBlocks: new Set(f.hiddenBlocks || []),
|
||||
};
|
||||
}
|
||||
|
||||
function saveFilter(hiddenCategories, hiddenBlocks) {
|
||||
setFilter(getDeviceId(), {
|
||||
hiddenCategories: [...hiddenCategories],
|
||||
hiddenBlocks: [...hiddenBlocks],
|
||||
});
|
||||
}
|
||||
|
||||
// ─── Preset actions ──────────────────────────────────────
|
||||
|
||||
function renderPresetDropdown() {
|
||||
const presets = getSavedPresets();
|
||||
presetSelect.innerHTML = '<option value="">— select preset —</option>';
|
||||
for (const p of presets) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = p.name;
|
||||
opt.textContent = p.name;
|
||||
presetSelect.appendChild(opt);
|
||||
}
|
||||
}
|
||||
|
||||
function loadSelectedPreset() {
|
||||
const name = presetSelect.value;
|
||||
if (!name) return;
|
||||
const presets = getSavedPresets();
|
||||
const preset = presets.find(p => p.name === name);
|
||||
if (!preset) return;
|
||||
setFilter(getDeviceId(), preset.filter);
|
||||
renderTree();
|
||||
}
|
||||
|
||||
function saveCurrentAsPreset() {
|
||||
const name = presetNameInput.value.trim();
|
||||
if (!name) { presetNameInput.focus(); return; }
|
||||
const f = getFilter(getDeviceId());
|
||||
savePreset(name, f);
|
||||
presetNameInput.value = '';
|
||||
renderPresetDropdown();
|
||||
presetSelect.value = name;
|
||||
}
|
||||
|
||||
function deleteSelectedPreset() {
|
||||
const name = presetSelect.value;
|
||||
if (!name) return;
|
||||
deletePreset(name);
|
||||
renderPresetDropdown();
|
||||
}
|
||||
|
||||
function showAll() {
|
||||
saveFilter(new Set(), new Set());
|
||||
renderTree();
|
||||
}
|
||||
|
||||
function hideAll() {
|
||||
const cats = getFullCategories(getDeviceId());
|
||||
const hiddenCats = new Set();
|
||||
const hiddenBlocks = new Set();
|
||||
for (const cat of cats) {
|
||||
hiddenCats.add(cat.name);
|
||||
if (cat.contents) {
|
||||
for (const b of cat.contents) {
|
||||
if (b.type) hiddenBlocks.add(b.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
saveFilter(hiddenCats, hiddenBlocks);
|
||||
renderTree();
|
||||
}
|
||||
|
||||
// ─── Tree rendering ──────────────────────────────────────
|
||||
|
||||
function renderTree() {
|
||||
const cats = getFullCategories(getDeviceId());
|
||||
const { hiddenCategories, hiddenBlocks } = currentFilter();
|
||||
treeEl.innerHTML = '';
|
||||
|
||||
for (const cat of cats) {
|
||||
const catHidden = hiddenCategories.has(cat.name);
|
||||
|
||||
// Category row
|
||||
const row = document.createElement('div');
|
||||
row.className = 'cust-cat' + (catHidden ? ' cust-dimmed' : '');
|
||||
|
||||
const catCb = document.createElement('input');
|
||||
catCb.type = 'checkbox';
|
||||
catCb.checked = !catHidden;
|
||||
catCb.className = 'cust-cb';
|
||||
|
||||
const swatch = document.createElement('span');
|
||||
swatch.className = 'cust-swatch';
|
||||
if (cat.colour) {
|
||||
swatch.style.background = `hsl(${cat.colour}, 60%, 50%)`;
|
||||
} else {
|
||||
swatch.style.background = 'var(--text-muted)';
|
||||
}
|
||||
|
||||
const label = document.createElement('span');
|
||||
label.className = 'cust-cat-label';
|
||||
label.textContent = cat.name;
|
||||
|
||||
const hasBlocks = cat.contents && cat.contents.length > 0;
|
||||
|
||||
const toggle = document.createElement('button');
|
||||
toggle.className = 'cust-expand';
|
||||
toggle.innerHTML = '▸';
|
||||
toggle.title = 'Expand blocks';
|
||||
|
||||
row.appendChild(catCb);
|
||||
row.appendChild(swatch);
|
||||
row.appendChild(label);
|
||||
if (hasBlocks) row.appendChild(toggle);
|
||||
treeEl.appendChild(row);
|
||||
|
||||
// Block list (collapsed by default)
|
||||
const blockList = document.createElement('div');
|
||||
blockList.className = 'cust-blocks hidden';
|
||||
|
||||
if (hasBlocks) {
|
||||
for (const block of cat.contents) {
|
||||
if (!block.type) continue;
|
||||
|
||||
const bRow = document.createElement('div');
|
||||
const bHidden = catHidden || hiddenBlocks.has(block.type);
|
||||
bRow.className = 'cust-block-row' + (bHidden ? ' cust-dimmed' : '');
|
||||
|
||||
const bCb = document.createElement('input');
|
||||
bCb.type = 'checkbox';
|
||||
bCb.checked = !catHidden && !hiddenBlocks.has(block.type);
|
||||
bCb.disabled = catHidden;
|
||||
bCb.className = 'cust-cb';
|
||||
|
||||
const bLabel = document.createElement('span');
|
||||
bLabel.className = 'cust-block-label';
|
||||
bLabel.textContent = block.type.replace(/_/g, ' ');
|
||||
|
||||
bRow.appendChild(bCb);
|
||||
bRow.appendChild(bLabel);
|
||||
blockList.appendChild(bRow);
|
||||
|
||||
bCb.addEventListener('change', () => {
|
||||
const f = currentFilter();
|
||||
if (bCb.checked) {
|
||||
f.hiddenBlocks.delete(block.type);
|
||||
} else {
|
||||
f.hiddenBlocks.add(block.type);
|
||||
}
|
||||
saveFilter(f.hiddenCategories, f.hiddenBlocks);
|
||||
bRow.classList.toggle('cust-dimmed', !bCb.checked);
|
||||
});
|
||||
}
|
||||
}
|
||||
treeEl.appendChild(blockList);
|
||||
|
||||
// Expand toggle
|
||||
if (hasBlocks) {
|
||||
toggle.addEventListener('click', () => {
|
||||
blockList.classList.toggle('hidden');
|
||||
toggle.innerHTML = blockList.classList.contains('hidden') ? '▸' : '▾';
|
||||
});
|
||||
}
|
||||
|
||||
// Category checkbox
|
||||
catCb.addEventListener('change', () => {
|
||||
const f = currentFilter();
|
||||
if (catCb.checked) {
|
||||
f.hiddenCategories.delete(cat.name);
|
||||
} else {
|
||||
f.hiddenCategories.add(cat.name);
|
||||
}
|
||||
saveFilter(f.hiddenCategories, f.hiddenBlocks);
|
||||
row.classList.toggle('cust-dimmed', !catCb.checked);
|
||||
|
||||
const childCbs = blockList.querySelectorAll('.cust-cb');
|
||||
childCbs.forEach(cb => {
|
||||
cb.disabled = !catCb.checked;
|
||||
if (!catCb.checked) {
|
||||
cb.checked = false;
|
||||
cb.closest('.cust-block-row')?.classList.add('cust-dimmed');
|
||||
} else {
|
||||
const type = cb.closest('.cust-block-row')
|
||||
?.querySelector('.cust-block-label')?.textContent.replace(/ /g, '_');
|
||||
const isHidden = f.hiddenBlocks.has(type);
|
||||
cb.checked = !isHidden;
|
||||
cb.closest('.cust-block-row')?.classList.toggle('cust-dimmed', isHidden);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue