import { h } from 'vue'; import { ObjectShort } from './ObjectShort.js'; import { getKindGroups } from '../subobject-kinds.js'; function _barGroupKey(id) { const m = id.match(/(\d+|\D+)$/); return m ? id.slice(0, m.index) : id; } function _incrementId(id) { const m = id.match(/(\d+)$/); if (!m) return id + '1'; const num = parseInt(m[1], 10) + 1; const padded = String(num).padStart(m[1].length, '0'); return id.slice(0, m.index) + padded; } export const PaneSubObjects = { props: ['store', 'kind', 'onFocusFO'], setup(props) { function focused() { const fp = props.store.focusPath; return fp.length ? fp[fp.length - 1] : props.store.scoreModel; } function deleteItem(kind, node) { const store = props.store; const model = store.scoreModel; if (!model) return; if (kind === 'instrument') { const idx = model.instruments.indexOf(node); if (idx !== -1) { model.instruments.splice(idx, 1); store.markDirty(); } } else if (kind === 'articles') { const idx = (model.articles ?? []).indexOf(node); if (idx !== -1) { model.articles.splice(idx, 1); model.articlesModified = true; store.markDirty(); } } else if (kind === 'bar') { node.deleted = true; node.isDirty = true; store.markDirty(); } else if (kind === 'variation') { for (const instr of model.instruments) { const idx = instr.variations.indexOf(node); if (idx !== -1) { instr.variations.splice(idx, 1); instr.isDirty = true; store.markDirty(); return; } for (const v of instr.variations) { const sidx = v.subvariations.indexOf(node); if (sidx !== -1) { v.subvariations.splice(sidx, 1); instr.isDirty = true; store.markDirty(); return; } } } } else if (kind === 'label_spec') { for (const instr of model.instruments) { for (const v of instr.variations) { const idx = v.labelSpecs.indexOf(node); if (idx !== -1) { v.labelSpecs.splice(idx, 1); instr.isDirty = true; store.markDirty(); return; } for (const sv of v.subvariations) { const idx2 = sv.labelSpecs.indexOf(node); if (idx2 !== -1) { sv.labelSpecs.splice(idx2, 1); instr.isDirty = true; store.markDirty(); return; } } } } } } function addBar(afterId) { const store = props.store; const model = store.scoreModel; if (!model) return; let newId; if (afterId) { newId = _incrementId(afterId); } else { const liveBars = (model.bars ?? []).filter(b => !b.deleted); newId = liveBars.length ? _incrementId(liveBars[liveBars.length - 1].id) : 'bar001'; } const newBar = { type: 'bar', id: newId, isDirty: true, isNew: true, stressor: null, tempoLevels: null, upperStressBound: null, lowerStressBound: null, tempoShape: null, voices: {}, }; model.bars.push(newBar); store.markDirty(); store.pushFocus(newBar); props.onFocusFO?.(); } return () => { const node = focused(); const groups = getKindGroups(node); const group = props.kind ? groups.find(g => g.kind === props.kind) : groups[0]; const items = group?.items ?? []; if (!items.length && props.kind !== 'bar') return h('div', null, h('em', null, 'No sub-objects')); if (props.kind === 'bar') { const visibleItems = items.filter(item => !item.node.deleted); const barGroups = []; const seen = new Map(); for (const item of visibleItems) { const key = _barGroupKey(item.label); if (!seen.has(key)) { const g = { key, items: [] }; barGroups.push(g); seen.set(key, g); } seen.get(key).items.push(item); } const groupEls = barGroups.map(g => { const lastId = g.items[g.items.length - 1].label; return h('div', { key: g.key, class: 'se-bar-group' }, [ ...g.items.map((item, idx) => h('span', { key: idx, class: ['se-bar-chip', props.store.focusPath.includes(item.node) ? 'focused' : null], }, [ h('span', { class: 'se-bar-chip-label', onClick: () => { props.store.pushFocus(item.node); props.onFocusFO?.(); }, }, item.label), h('button', { class: 'se-bar-chip-delete', title: 'Delete bar', onClick: e => { e.stopPropagation(); deleteItem('bar', item.node); }, }, '×'), ]) ), h('button', { class: 'se-bar-add', title: `Add bar after ${lastId}`, onClick: () => addBar(lastId), }, '+ add'), ]); }); return h('div', { class: 'se-bar-groups' }, [ ...groupEls, h('div', { class: 'se-bar-group' }, h('button', { class: 'se-bar-add', onClick: () => addBar(null) }, '+ add bar') ), ]); } return h('div', null, h('ul', { class: 'se-object-list' }, items.map((item, idx) => h(ObjectShort, { key: idx, label: item.label, typeTag: item.kind, focused: props.store.focusPath.includes(item.node), hasChildren: item.hasChildren, readOnly: item.readOnly ?? false, deletable: item.deletable ?? false, onFocus: () => { props.store.pushFocus(item.node); props.onFocusFO?.(); }, onDrillDown: () => { props.store.pushFocus(item.node); props.onFocusFO?.(); }, onDelete: item.deletable ? () => deleteItem(item.kind, item.node) : undefined, }) )) ); }; }, };