2026-03-13 22:21:37 +01:00
|
|
|
/**
|
|
|
|
|
* Initializes MultiSelect.js instances from select[multiple] elements.
|
|
|
|
|
*/
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
import { warnOnce, resolveHost } from '../core/app-dom.js';
|
2026-02-11 19:28:12 +01:00
|
|
|
|
2026-02-04 23:31:53 +01:00
|
|
|
let multiSelectLoader = null;
|
|
|
|
|
|
|
|
|
|
const resolveScriptUrl = (script) => {
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
if (script) {
|
|
|
|
|
return script;
|
|
|
|
|
}
|
|
|
|
|
const root = document.documentElement;
|
|
|
|
|
const assetBase = String(root?.dataset?.assetBase || '').trim() || document.baseURI;
|
2026-02-04 23:31:53 +01:00
|
|
|
return new URL('vendor/multi-select/MultiSelect.js', assetBase).toString();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const ensureMultiSelect = async (script) => {
|
|
|
|
|
if (typeof window.MultiSelect !== 'undefined') {
|
|
|
|
|
return window.MultiSelect;
|
|
|
|
|
}
|
|
|
|
|
if (!multiSelectLoader) {
|
|
|
|
|
multiSelectLoader = new Promise((resolve, reject) => {
|
|
|
|
|
const tag = document.createElement('script');
|
|
|
|
|
tag.src = resolveScriptUrl(script);
|
|
|
|
|
tag.async = true;
|
|
|
|
|
tag.onload = () => resolve(window.MultiSelect);
|
|
|
|
|
tag.onerror = (event) => reject(event);
|
|
|
|
|
document.head.appendChild(tag);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
return multiSelectLoader;
|
|
|
|
|
};
|
|
|
|
|
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
const resolveElements = (target, host) => {
|
|
|
|
|
if (typeof target === 'string') {
|
|
|
|
|
return Array.from(host.querySelectorAll(target));
|
|
|
|
|
}
|
|
|
|
|
if (target instanceof Element) {
|
|
|
|
|
return [target];
|
|
|
|
|
}
|
|
|
|
|
if (target && typeof target.length === 'number') {
|
|
|
|
|
return Array.from(target);
|
|
|
|
|
}
|
|
|
|
|
return [];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const destroyElementInstance = (element, instance = null) => {
|
|
|
|
|
if (!(element instanceof HTMLElement)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const cleanup = element._multiSelectCleanup;
|
|
|
|
|
if (typeof cleanup === 'function') {
|
|
|
|
|
cleanup();
|
|
|
|
|
}
|
|
|
|
|
delete element._multiSelectCleanup;
|
|
|
|
|
delete element.dataset.multiSelectInit;
|
|
|
|
|
|
|
|
|
|
const resolvedInstance = instance || element._multiSelectInstance || null;
|
|
|
|
|
if (resolvedInstance && typeof resolvedInstance.destroy === 'function') {
|
|
|
|
|
resolvedInstance.destroy();
|
|
|
|
|
}
|
|
|
|
|
delete element._multiSelectInstance;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const initMultiSelect = async (target = '[data-multi-select]', root = document) => {
|
|
|
|
|
const host = resolveHost(root);
|
|
|
|
|
const elements = resolveElements(target, host);
|
|
|
|
|
if (!elements.length) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
2026-02-04 23:31:53 +01:00
|
|
|
|
|
|
|
|
const MultiSelect = await ensureMultiSelect();
|
|
|
|
|
const instances = [];
|
|
|
|
|
elements.forEach((element) => {
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
if (!(element instanceof HTMLElement) || element.dataset.multiSelectInit === '1') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const cleanupFns = [];
|
2026-02-04 23:31:53 +01:00
|
|
|
const syncTarget = element.dataset.syncTarget;
|
|
|
|
|
const syncInput = syncTarget ? document.querySelector(syncTarget) : null;
|
|
|
|
|
const options = {};
|
|
|
|
|
const placeholder = element.dataset.placeholder;
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
if (placeholder) {
|
|
|
|
|
options.placeholder = placeholder;
|
|
|
|
|
}
|
2026-02-04 23:31:53 +01:00
|
|
|
if (element.dataset.search !== undefined) {
|
|
|
|
|
options.search = element.dataset.search === 'true';
|
|
|
|
|
}
|
|
|
|
|
if (element.dataset.selectAll !== undefined) {
|
|
|
|
|
options.selectAll = element.dataset.selectAll === 'true';
|
|
|
|
|
}
|
|
|
|
|
if (element.dataset.disabled !== undefined) {
|
|
|
|
|
options.disabled = element.dataset.disabled === 'true';
|
|
|
|
|
}
|
|
|
|
|
if (element.disabled) {
|
|
|
|
|
options.disabled = true;
|
|
|
|
|
}
|
|
|
|
|
if (element.dataset.listAll !== undefined) {
|
|
|
|
|
options.listAll = element.dataset.listAll === 'true';
|
|
|
|
|
}
|
|
|
|
|
if (element.dataset.selectedLabel) {
|
|
|
|
|
options.selectedLabel = element.dataset.selectedLabel;
|
|
|
|
|
}
|
|
|
|
|
if (element.dataset.searchPlaceholder) {
|
|
|
|
|
options.searchPlaceholder = element.dataset.searchPlaceholder;
|
|
|
|
|
}
|
|
|
|
|
if (element.dataset.selectAllLabel) {
|
|
|
|
|
options.selectAllLabel = element.dataset.selectAllLabel;
|
|
|
|
|
}
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
|
2026-02-04 23:31:53 +01:00
|
|
|
let isSyncing = false;
|
|
|
|
|
let instance;
|
|
|
|
|
try {
|
|
|
|
|
instance = new MultiSelect(element, options);
|
|
|
|
|
} catch (error) {
|
2026-02-11 19:28:12 +01:00
|
|
|
warnOnce('UI_INIT_FAIL', 'multi-select init failed', { module: 'multi-select', element, error });
|
2026-02-04 23:31:53 +01:00
|
|
|
return;
|
|
|
|
|
}
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
|
2026-02-04 23:31:53 +01:00
|
|
|
if (element.dataset.disabled === 'true' || element.disabled) {
|
|
|
|
|
instance.disable();
|
|
|
|
|
}
|
|
|
|
|
element.dataset.multiSelectInit = '1';
|
2026-02-11 19:28:12 +01:00
|
|
|
element._multiSelectInstance = instance;
|
2026-02-04 23:31:53 +01:00
|
|
|
instances.push(instance);
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
|
|
|
|
|
if (syncInput instanceof HTMLInputElement) {
|
2026-02-04 23:31:53 +01:00
|
|
|
syncInput._multiSelectInstance = instance;
|
|
|
|
|
const syncValues = () => {
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
if (isSyncing) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2026-02-04 23:31:53 +01:00
|
|
|
isSyncing = true;
|
|
|
|
|
const values = instance.selectedValues || [];
|
|
|
|
|
syncInput.value = Array.isArray(values) ? values.join(',') : '';
|
|
|
|
|
syncInput.dispatchEvent(new Event('change', { bubbles: true }));
|
|
|
|
|
isSyncing = false;
|
|
|
|
|
};
|
2026-03-04 15:56:58 +01:00
|
|
|
let boundElement = null;
|
|
|
|
|
const bindSyncListener = () => {
|
|
|
|
|
if (!instance?.element || instance.element === boundElement) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
instance.element.addEventListener('multi-select:change', syncValues);
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
cleanupFns.push(() => instance.element?.removeEventListener('multi-select:change', syncValues));
|
2026-03-04 15:56:58 +01:00
|
|
|
boundElement = instance.element;
|
|
|
|
|
};
|
|
|
|
|
bindSyncListener();
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
|
2026-03-04 15:56:58 +01:00
|
|
|
if (typeof instance.refresh === 'function' && instance._syncRefreshWrapped !== true) {
|
|
|
|
|
const originalRefresh = instance.refresh.bind(instance);
|
|
|
|
|
instance.refresh = (...args) => {
|
|
|
|
|
const result = originalRefresh(...args);
|
|
|
|
|
bindSyncListener();
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
instance._syncRefreshWrapped = true;
|
|
|
|
|
}
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
|
2026-02-04 23:31:53 +01:00
|
|
|
const values = instance.selectedValues || [];
|
|
|
|
|
syncInput.value = Array.isArray(values) ? values.join(',') : '';
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
const onSyncInputChange = () => {
|
|
|
|
|
if (isSyncing) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2026-02-04 23:31:53 +01:00
|
|
|
const nextValues = syncInput.value
|
|
|
|
|
? syncInput.value.split(',').map((item) => item.trim()).filter(Boolean)
|
|
|
|
|
: [];
|
|
|
|
|
instance.setValues(nextValues);
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
};
|
|
|
|
|
syncInput.addEventListener('change', onSyncInputChange);
|
|
|
|
|
cleanupFns.push(() => syncInput.removeEventListener('change', onSyncInputChange));
|
2026-02-04 23:31:53 +01:00
|
|
|
}
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
|
|
|
|
|
element._multiSelectCleanup = () => {
|
|
|
|
|
cleanupFns.forEach((cleanup) => cleanup());
|
|
|
|
|
cleanupFns.length = 0;
|
|
|
|
|
if (syncInput instanceof HTMLInputElement && syncInput._multiSelectInstance === instance) {
|
|
|
|
|
delete syncInput._multiSelectInstance;
|
|
|
|
|
}
|
|
|
|
|
};
|
2026-02-04 23:31:53 +01:00
|
|
|
});
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
|
2026-02-04 23:31:53 +01:00
|
|
|
return instances;
|
|
|
|
|
};
|
|
|
|
|
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
export const getMultiSelectInstance = (target, root = document) => {
|
|
|
|
|
const host = resolveHost(root);
|
|
|
|
|
const element = typeof target === 'string' ? host.querySelector(target) : target;
|
|
|
|
|
if (!element) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
if (element._multiSelectInstance) {
|
|
|
|
|
return element._multiSelectInstance;
|
|
|
|
|
}
|
2026-02-11 19:28:12 +01:00
|
|
|
const syncTarget = element.dataset?.syncTarget;
|
|
|
|
|
if (syncTarget) {
|
|
|
|
|
const syncElement = document.querySelector(syncTarget);
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
if (syncElement && syncElement._multiSelectInstance) {
|
|
|
|
|
return syncElement._multiSelectInstance;
|
|
|
|
|
}
|
2026-02-11 19:28:12 +01:00
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
};
|
|
|
|
|
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
export function initMultiSelectComponent(root = document, options = {}) {
|
|
|
|
|
const selector = String(options.selector || '[data-multi-select]').trim() || '[data-multi-select]';
|
|
|
|
|
const host = resolveHost(root);
|
|
|
|
|
let destroyed = false;
|
|
|
|
|
let instances = [];
|
2026-02-04 23:31:53 +01:00
|
|
|
|
feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
|
|
|
initMultiSelect(selector, host)
|
|
|
|
|
.then((initialized) => {
|
|
|
|
|
if (destroyed) {
|
|
|
|
|
initialized.forEach((instance) => {
|
|
|
|
|
const element = instance?.element;
|
|
|
|
|
if (element instanceof HTMLElement) {
|
|
|
|
|
destroyElementInstance(element, instance);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
instances = initialized;
|
|
|
|
|
})
|
|
|
|
|
.catch((error) => {
|
|
|
|
|
warnOnce('UI_INIT_FAIL', 'multi-select runtime init failed', { module: 'multi-select', error });
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
destroy: () => {
|
|
|
|
|
destroyed = true;
|
|
|
|
|
instances.forEach((instance) => {
|
|
|
|
|
const element = instance?.element;
|
|
|
|
|
if (element instanceof HTMLElement) {
|
|
|
|
|
destroyElementInstance(element, instance);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
instances = [];
|
|
|
|
|
},
|
|
|
|
|
};
|
2026-02-04 23:31:53 +01:00
|
|
|
}
|