Add type picker for entity creation, icon grid filter, and serial port placeholder

- Replace inline type selectors with pre-modal type picker grid for devices,
  color strip sources, and value sources
- Add filterable search to icon grid when items > 9 (no auto-focus on touch)
- Show disabled (grayed-out) filtered items instead of hiding them
- Responsive grid columns (2-5 cols based on viewport width)
- Add "Select a port..." placeholder to serial port dropdown
- Update en/ru/zh locales with new keys

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-14 21:44:26 +03:00
parent 6395709bb8
commit a922c6e052
9 changed files with 233 additions and 16 deletions

View File

@@ -23,7 +23,7 @@ import {
} from '../core/icons.js';
import { wrapCard } from '../core/card-colors.js';
import { TagInput, renderTagChips } from '../core/tag-input.js';
import { IconSelect } from '../core/icon-select.js';
import { IconSelect, showTypePicker } from '../core/icon-select.js';
import { EntitySelect } from '../core/entity-palette.js';
import { loadPictureSources } from './streams.js';
@@ -165,13 +165,27 @@ function _ensureVSTypeIconSelect() {
// ── Modal ─────────────────────────────────────────────────────
export async function showValueSourceModal(editData) {
export async function showValueSourceModal(editData, presetType = null) {
// When creating new: show type picker first, then re-enter with presetType
if (!editData && !presetType) {
showTypePicker({
title: t('value_source.select_type'),
items: _buildVSTypeItems(),
onPick: (type) => showValueSourceModal(null, type),
});
return;
}
const hasId = editData?.id;
const isEdit = !!hasId;
const titleKey = isEdit ? 'value_source.edit' : 'value_source.add';
const titleIcon = editData ? getValueSourceIcon(editData.source_type) : getValueSourceIcon('static');
document.getElementById('value-source-modal-title').innerHTML = `${titleIcon} ${t(titleKey)}`;
const sourceType = editData?.source_type || presetType || 'static';
const titleIcon = getValueSourceIcon(sourceType);
const titleKey = isEdit ? 'value_source.edit' : 'value_source.add';
const typeName = t(`value_source.type.${sourceType}`);
document.getElementById('value-source-modal-title').innerHTML = isEdit
? `${titleIcon} ${t(titleKey)}`
: `${titleIcon} ${t(titleKey)}: ${typeName}`;
document.getElementById('value-source-id').value = isEdit ? editData.id : '';
document.getElementById('value-source-error').style.display = 'none';
@@ -180,7 +194,8 @@ export async function showValueSourceModal(editData) {
_ensureVSTypeIconSelect();
const typeSelect = document.getElementById('value-source-type');
document.getElementById('value-source-type-group').style.display = isEdit ? 'none' : '';
// Type is chosen before the modal opens — always hide selector
document.getElementById('value-source-type-group').style.display = 'none';
if (editData) {
document.getElementById('value-source-name').value = editData.name || '';
@@ -227,7 +242,7 @@ export async function showValueSourceModal(editData) {
} else {
document.getElementById('value-source-name').value = '';
document.getElementById('value-source-description').value = '';
typeSelect.value = 'static';
typeSelect.value = presetType || 'static';
onValueSourceTypeChange();
_setSlider('value-source-value', 1.0);
_setSlider('value-source-speed', 10);