From f601a40776843f6196221e74ba502c571a5b1bcd Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Sun, 12 Jul 2026 21:16:58 -0400 Subject: fix(admin): standardize most search dropdowns and radio buttons --- internal/admin/static/admin.css | 29 ++++++++++++++++----- internal/admin/static/cardeditor.js | 48 +++++++++++++++++++++++++++++++++-- internal/admin/static/hazardeditor.js | 2 +- internal/admin/static/itemeditor.js | 23 ++++++++++++++--- internal/admin/static/mobeditor.js | 38 +++++++++++++++++++++------ internal/admin/static/moduleeditor.js | 8 +++--- internal/admin/static/talktree.js | 8 +++--- internal/admin/static/techeditor.js | 4 +-- 8 files changed, 131 insertions(+), 29 deletions(-) (limited to 'internal/admin/static') diff --git a/internal/admin/static/admin.css b/internal/admin/static/admin.css index c500aad..a1a364e 100644 --- a/internal/admin/static/admin.css +++ b/internal/admin/static/admin.css @@ -38,7 +38,8 @@ body{font-family:monospace;background:var(--bg);color:var(--text);display:flex;f .table a{color:var(--text);text-decoration:none} .table a:hover{color:#fff} .search-bar{margin-bottom:12px} -.search-bar input{width:100%;padding:6px 8px;font-size:13px;background:var(--input-bg);color:var(--text);border:1px solid var(--input-border);border-radius:3px} +.search-bar input{width:100%;padding:6px 28px 6px 8px;font-size:13px;background:var(--input-bg);color:var(--text);border:1px solid var(--input-border);border-radius:3px;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cpath d='m21 21-4.3-4.3'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 8px center} +input.search-input{padding-right:24px;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cpath d='m21 21-4.3-4.3'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 6px center} .login{max-width:360px;margin:80px auto;background:var(--panel);padding:30px;border:1px solid var(--border);border-radius:6px} .login h1{font-size:18px;margin-bottom:20px;text-align:center} .z-controls-wrapper{position:absolute;top:12px;left:12px;z-index:10} @@ -278,7 +279,7 @@ g.ud-hover:hover text{font-weight:bold} .panel-tab-content .entry-fields textarea{resize:vertical;min-height:28px} .panel-tab-content .entry-fields .entry-id{font-size:11px;color:#6cf;display:block;padding:3px 0} .panel-tab-content .section-search{position:relative;margin-top:4px} -.panel-tab-content .search-input{width:100%;padding:4px 6px;font-size:11px;background:var(--input-bg);color:var(--text);border:1px solid var(--input-border);border-radius:3px;font-family:monospace} +.panel-tab-content .search-input{width:100%;padding:4px 24px 4px 6px;font-size:11px;background-color:var(--input-bg);color:var(--text);border:1px solid var(--input-border);border-radius:3px;font-family:monospace} .panel-tab-content .search-results{position:absolute;top:100%;left:0;right:0;max-height:160px;overflow-y:auto;background:var(--panel);border:1px solid var(--border);border-radius:3px;z-index:50;box-shadow:0 4px 12px rgba(0,0,0,.4)} .panel-tab-content .search-results .sr-item{padding:4px 8px;font-size:11px;cursor:pointer;color:var(--text);border-bottom:1px solid rgba(255,255,255,.03)} .panel-tab-content .search-results .sr-item:hover,.panel-tab-content .search-results .sr-item.active{background:var(--accent)} @@ -300,7 +301,7 @@ g.ud-hover:hover text{font-weight:bold} .obj-card-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:6px 8px} .obj-field{display:flex;flex-direction:column;gap:2px;font-size:11px} .obj-field span{color:#888;font-size:9px;text-transform:uppercase;letter-spacing:.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} -.obj-field input,.obj-field textarea,.obj-field select{padding:4px 6px;font-size:11px;background:var(--input-bg);color:var(--text);border:1px solid var(--input-border);border-radius:3px;font-family:monospace;width:100%} +.obj-field input,.obj-field textarea,.obj-field select{padding:4px 6px;font-size:11px;background-color:var(--input-bg);color:var(--text);border:1px solid var(--input-border);border-radius:3px;font-family:monospace;width:100%} .obj-field textarea{resize:vertical;min-height:28px} .obj-field.obj-wide{grid-column:1/-1} .obj-field.obj-check{flex-direction:row;align-items:center;gap:4px;padding-bottom:3px} @@ -313,6 +314,15 @@ g.ud-hover:hover text{font-weight:bold} .seg-toggle input:checked+span{color:#3a8fd4} .seg-toggle label:has(input:checked){color:var(--text);background:var(--accent)} .seg-toggle input{position:absolute;opacity:0;pointer-events:none;width:0;height:0} +.seg-pill{position:relative;border-radius:6px;background:var(--input-bg);display:inline-flex;border:1px solid var(--border)} +.seg-pill .seg-opt{flex:1;text-align:center;position:relative;z-index:1;padding:5px 14px;font-size:11px;cursor:pointer;color:#888;background:transparent;border:none;font-family:monospace;transition:color .15s} +.seg-pill .seg-opt:hover{color:var(--text)} +.seg-pill .seg-opt.seg-active{color:var(--text)} +.seg-pill::before{content:'';position:absolute;top:2px;bottom:2px;left:0;width:calc(100% / 3);background:var(--accent);border-radius:4px;transition:transform .2s ease;z-index:0} +.seg-pill.seg-pill-3.seg-idx-0::before{transform:translateX(0)} +.seg-pill.seg-pill-3.seg-idx-1::before{transform:translateX(100%)} +.seg-pill.seg-pill-3.seg-idx-2::before{transform:translateX(200%)} +.seg-pill .seg-opt:focus-visible{outline:2px solid var(--accent);outline-offset:-1px;border-radius:3px;z-index:2} .obj-color-row{display:flex;align-items:center;gap:4px} .obj-color-row .color-swatch{flex-shrink:0} .obj-color-row input{width:38px!important;text-align:center;padding:4px 2px} @@ -335,16 +345,21 @@ g.ud-hover:hover text{font-weight:bold} .add-section-bar button{padding:4px 12px;font-size:11px} .obj-kv-list{margin-top:4px} .obj-kv-row{display:flex;gap:4px;align-items:center;margin-bottom:3px} -.obj-kv-row input{padding:3px 5px;font-size:10px;background:var(--input-bg);color:var(--text);border:1px solid var(--input-border);border-radius:2px;font-family:monospace;flex:1} +.obj-kv-row input{padding:3px 5px;font-size:10px;background-color:var(--input-bg);color:var(--text);border:1px solid var(--input-border);border-radius:2px;font-family:monospace;flex:1} .obj-kv-row .obj-kv-key{flex:1} +.obj-kv-key-wrap.obj-search{position:relative!important} +.obj-kv-key-wrap.obj-search .search-input.obj-kv-key{width:100%;flex:none;padding-right:24px} +.obj-kv-key-wrap.obj-search .search-results{position:absolute;top:100%;left:0;right:0;max-height:160px;overflow-y:auto;background:var(--panel);border:1px solid var(--border);border-radius:3px;z-index:50;box-shadow:0 4px 12px rgba(0,0,0,.4);margin-top:2px} +.obj-kv-key-wrap.obj-search .search-results .sr-item{padding:4px 8px;font-size:11px;cursor:pointer;color:var(--text);border-bottom:1px solid rgba(255,255,255,.03)} +.obj-kv-key-wrap.obj-search .search-results .sr-item:hover,.obj-kv-key-wrap.obj-search .search-results .sr-item.active{background:var(--accent)} .obj-kv-row .obj-kv-val{width:70px;flex:none} .obj-core-row{display:flex;gap:6px;align-items:flex-end;margin-bottom:8px} -.obj-field-error input,.obj-field-error textarea,.obj-field-error select{border-color:var(--danger)!important;background:rgba(192,57,43,.08)} +.obj-field-error input,.obj-field-error textarea,.obj-field-error select{border-color:var(--danger)!important;background-color:rgba(192,57,43,.08)} .obj-search{position:relative!important} .obj-search .search-results{position:absolute;top:100%;left:0;right:0;max-height:160px;overflow-y:auto;background:var(--panel);border:1px solid var(--border);border-radius:3px;z-index:50;box-shadow:0 4px 12px rgba(0,0,0,.4);margin-top:2px} .obj-search .search-results .sr-item{padding:4px 8px;font-size:11px;cursor:pointer;color:var(--text);border-bottom:1px solid rgba(255,255,255,.03)} .obj-search .search-results .sr-item:hover,.obj-search .search-results .sr-item.active{background:var(--accent)} -.obj-search input.search-input{padding-right:24px;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cpath d='m21 21-4.3-4.3'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 6px center} +.obj-search input.search-input{padding-right:24px} .obj-search input.search-input::placeholder{color:#666} .obj-narrow.obj-search input{width:80px} .obj-sub-row .obj-search input.search-input{min-width:80px} @@ -475,3 +490,5 @@ g.ud-hover:hover text{font-weight:bold} .obj-card[data-card="trigger_action"]>.obj-card-body>.obj-sub-add{display:none} .obj-card[data-card="trigger_action"] .ie-inter-header .ie-trig-up, .obj-card[data-card="trigger_action"] .ie-inter-header .ie-trig-dn{display:none} +input:focus-visible,select:focus-visible,textarea:focus-visible,button:focus-visible{outline:2px solid rgba(100,160,255,.5);outline-offset:1px} +.sr-no-results{padding:6px 8px;font-size:11px;color:#666;text-align:center;cursor:default;pointer-events:none} diff --git a/internal/admin/static/cardeditor.js b/internal/admin/static/cardeditor.js index 1e8d259..1b2b76b 100644 --- a/internal/admin/static/cardeditor.js +++ b/internal/admin/static/cardeditor.js @@ -90,6 +90,46 @@ function ced_addKVRow(cardID, subKey) { el.appendChild(row); } +function ced_buildDynamicSelect(fid, dpAttr, val, dynKey) { + var items = (window._dynOpts && window._dynOpts[dynKey]) ? window._dynOpts[dynKey].slice().sort() : []; + var s = ''; + return s; +} + +function ced_renderKVSearch(sec, il, data, getter) { + var sectionRoot = data[ced_cardPath(sec)] || data; + var map = getter ? getter(sectionRoot, il) : (sectionRoot[il.key] || {}); + var entity = il.searchEntity || 'items'; + var h = '
'; + h += '' + esc(il.label) + ''; + h += '
'; + Object.keys(map).forEach(function(k, idx) { + h += '
'; + h += ''; + h += ''; + h += '
'; + }); + h += '
'; + h += ''; + h += '
'; + return h; +} + +function ced_addKVSearchRow(cardID, subKey, entity) { + var el = document.querySelector('.obj-kv-list[data-sec="' + cardID + '"][data-key="' + subKey + '"]'); + if (!el) return; + var row = document.createElement('div'); + row.className = 'obj-kv-row'; + row.innerHTML = ''; + el.appendChild(row); + ced_setupSearchFields(row); +} + // ---- Search autocomplete ---- function ced_setupSearchFields(root) { @@ -114,7 +154,7 @@ function ced_setupSearchFields(root) { if (entityType === 'drops' && data.drops) items = data.drops; if (items.length === 0) { results.style.display = 'block'; - results.innerHTML = '
No results
'; + results.innerHTML = '
No results
'; return; } results.style.display = 'block'; @@ -315,6 +355,10 @@ function ced_renderField(sec, f, data, idx, subPath, optStyle, cardPathFn, field return wrap('obj-field', '' + esc(label) + ''); } if (type === 'select') { + if (hint && hint.indexOf('dyn:') === 0) { + var inner = '' + esc(label) + '' + ced_buildDynamicSelect(fid, dpAttr, val, hint.substring(4)); + return wrap('obj-field', inner); + } var opts = hint ? hint.split('|') : []; var inner = '' + esc(label) + ' ' + esc(f[1]) + ''; } if (f[2] === 'select') { - var opts = f[3] ? f[3].split('|') : []; + var hint = f[3] || ''; + if (hint.indexOf('dyn:') === 0) { + return ''; + } + var opts = hint.split('|'); var sel = ''; + h += '
'; + meleeOpts.forEach(function(t, i) { + h += ''; }); h += '
'; h += '
'; @@ -337,12 +340,31 @@ function renderMobAttackTypeWidget(data) { return h; } +function mobAtkPillSelect(val, btn) { + var pill = document.getElementById('mob_atktype_pill'); + if (!pill) return; + var opts = pill.querySelectorAll('.seg-opt'); + var idx = -1; + opts.forEach(function(o, i) { + if (o.getAttribute('data-v') === val) { + o.classList.add('seg-active'); + idx = i; + } else { + o.classList.remove('seg-active'); + } + }); + pill.className = pill.className.replace(/seg-idx-\d/g, 'seg-idx-' + idx); +} + // collectMobAttackType reads the Atk Type widget into an array: the selected // melee type first, then any optional ranged/science types. function collectMobAttackType() { var melee = 'crush'; - var r = document.querySelector('input[name="mob_atktype_melee"]:checked'); - if (r) melee = r.value; + var pill = document.getElementById('mob_atktype_pill'); + if (pill) { + var active = pill.querySelector('.seg-opt.seg-active'); + if (active) melee = active.getAttribute('data-v'); + } var out = [melee]; var rng = document.getElementById('mob_atktype_ranged'); var sci = document.getElementById('mob_atktype_science'); @@ -852,7 +874,7 @@ function saveMob() { hasValues = ced_saveSectionFields(sec, sectionObj, cardPath, fieldID) || hasValues; } - if (sec.id === 'combat' && document.querySelector('input[name="mob_atktype_melee"]')) { + if (sec.id === 'combat' && document.getElementById('mob_atktype_pill')) { sectionObj['attack_types'] = collectMobAttackType(); hasValues = true; } diff --git a/internal/admin/static/moduleeditor.js b/internal/admin/static/moduleeditor.js index 739fc2e..fe71f88 100644 --- a/internal/admin/static/moduleeditor.js +++ b/internal/admin/static/moduleeditor.js @@ -13,11 +13,11 @@ var MODULE_SECTIONS = [ ['max_hit', 'Max Hit', 'number', '10', 'narrow'], ['base_xp', 'Base XP', 'number', '50', 'narrow'], ['category', 'Category', 'select', 'combat|utility|enchant|processing|transport'], - ['element', 'Element', 'text', 'e.g. fire'], + ['element', 'Element', 'select', '|bio|eco|hydro|solar|chaos'], ['destination', 'Destination', 'number', '5', 'narrow'], ], inline: [ - {label:'Junk Cost', key:'junk_cost', type:'kv', valType:'number'}, + {label:'Junk Cost', key:'junk_cost', type:'kv_search', searchEntity:'items', valType:'number'}, ] }, { @@ -144,6 +144,8 @@ function renderCard(sec, data) { sec.inline.forEach(function(il) { if (il.type === 'kv') { h += ced_renderKV(sec, il, data); + } else if (il.type === 'kv_search') { + h += ced_renderKVSearch(sec, il, data); } }); } @@ -274,7 +276,7 @@ function saveModule() { if (sec.inline) { sec.inline.forEach(function(il) { - if (il.type === 'kv') { + if (il.type === 'kv' || il.type === 'kv_search') { var kvEl = document.querySelector('.obj-kv-list[data-sec="' + sec.id + '"][data-key="' + il.key + '"]'); if (kvEl) { var map = {}; diff --git a/internal/admin/static/talktree.js b/internal/admin/static/talktree.js index b8c7106..e55653e 100644 --- a/internal/admin/static/talktree.js +++ b/internal/admin/static/talktree.js @@ -207,7 +207,7 @@ else if (type === 'number') { var n = parseFloat(el.value); val = isNaN(n) ? 0 : n; } else { val = el.value; } talkSyncPath(path, val); - if (path.endsWith('.goto')) renderTalkTree(window._talkData); + if (path.endsWith('.goto') && e.type === 'change') renderTalkTree(window._talkData); } else if (tag === 'SELECT') { talkSyncPath(path, el.value); } @@ -492,7 +492,7 @@ label = label || 'Auto-advance to'; return '
' + '' + esc(label) + '' + - '' + + '' + '
'; } @@ -530,7 +530,7 @@ html += '
'; html += '' + (idx + 1) + ''; html += ''; - html += ''; + html += ''; html += '
'; if (opt.condition && typeof opt.condition === 'object' && !Array.isArray(opt.condition)) { @@ -582,7 +582,7 @@ html += '
' + '' + (options.length + 1) + '' + '' + - '' + + '' + '' + '' + '
'; diff --git a/internal/admin/static/techeditor.js b/internal/admin/static/techeditor.js index c9d7c24..812cded 100644 --- a/internal/admin/static/techeditor.js +++ b/internal/admin/static/techeditor.js @@ -10,8 +10,8 @@ var TECH_SECTIONS = [ ['name', 'Name', 'text', 'Protect from Melee'], ['level', 'Level', 'number', '37', 'narrow'], ['drain_rate', 'Drain Rate', 'number', '0.5', 'narrow'], - ['category', 'Category', 'text', 'protection'], - ['group', 'Group', 'text', 'melee'], + ['category', 'Category', 'select', '|accuracy|combo|defense|protection|ranged|science|strength|utility'], + ['group', 'Group', 'select', '|accuracy_tier|defense_tier|protection|ranged_tier|regen_tier|science_tier|strength_tier'], ] }, { -- cgit v1.2.3