diff --git a/CHANGELOG.md b/CHANGELOG.md index 5320cff..16634b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Changed `ViewModel#setDataProgress()` to no longer change the context, despite accepting `context` argument - Updated `ViewModel` to trigger a render when the `context` event is fired - Added `Model#legacyPrepare` readonly property to indicate whether the legacy `prepare` method is used or the new `setup()` method +- Updated the `struct` view to define the `setStructViewAnnotations` action when using the new `setup()` method, allowing custom annotations to be specified across all `struct` views - Added the `EmbedApp#publicApi.setLocationSync()` method to simplify sync between the embed app and the host location, preventing potential pitfalls - Added `ViewModel#enforceScheduledRenders()` to immediately execute scheduled renders - Changed `ViewModel#scheduleRender()` to use `setTimeout()` instead of `Promise.resolve()` to ensure proper processing of event loop tasks, eliminating unnecessary renders diff --git a/src/views/struct/index.js b/src/views/struct/index.js index 244a385..039efd0 100644 --- a/src/views/struct/index.js +++ b/src/views/struct/index.js @@ -6,7 +6,7 @@ import { hasOwn, objectToString } from '../../core/utils/object-utils.js'; import { createClickHandler } from './click-handler.js'; import { createValueActionsPopup } from './popup-value-actions.js'; import value2html, { stringifyIfNeeded } from './value-to-html.js'; -import { getDefaultAnnotations, prepareAnnotations, renderAnnotations } from './render-annotations.js'; +import { concatAnnotations, getDefaultAnnotations, prepareAnnotations, preprocessAnnotations, renderAnnotations } from './render-annotations.js'; import usage from './struct.usage.js'; import { stringValueProto, @@ -343,8 +343,9 @@ export default function(host) { const elementContext = new WeakMap(); const elementOptions = new WeakMap(); const structViewRoots = new WeakSet(); - const defaultAnnotations = getDefaultAnnotations(host); const annotationsToRender = []; + const defaultAnnotations = getDefaultAnnotations(host); + let customAnnotations = defaultAnnotations; let annotationsTimer = null; const valueActionsPopup = createValueActionsPopup(host, elementData, elementContext, buildPathForElement); @@ -362,6 +363,16 @@ export default function(host) { // single event handler for all `struct` view instances host.addHostElEventListener('click', clickHandler, false); + // define an action to set custom annotations + if (!host.legacyPrepare) { + host.action.define('setStructViewAnnotations', (newAnnotations) => { + customAnnotations = concatAnnotations( + defaultAnnotations, + preprocessAnnotations(newAnnotations) + ); + }); + } + host.view.define('struct', function(el, config, data, context) { const { annotations, @@ -378,10 +389,11 @@ export default function(host) { } = config; const normalizedAnnotations = prepareAnnotations( annotations, - defaultAnnotations || - // FIXME: that's a fallback to work with legacy prepare, - // remove when discard model-legacy-extension-api - host.annotations + !host.legacyPrepare + ? customAnnotations + // FIXME: that's a fallback to work with legacy prepare, + // remove when discard model-legacy-extension-api + : host.annotations ); const options = { diff --git a/src/views/struct/render-annotations.ts b/src/views/struct/render-annotations.ts index bf63d6e..d84204f 100644 --- a/src/views/struct/render-annotations.ts +++ b/src/views/struct/render-annotations.ts @@ -36,39 +36,50 @@ type RenderAnnotationConfig = { const styles = ['none', 'default', 'badge']; const annotationsElByEl = new WeakMap(); -export function getDefaultAnnotations(host: ViewModel) { - const annotations: ValueAnnotation[] = []; - - for (const { name, lookup } of host.objectMarkers.values) { - annotations.push({ - query(value: unknown, context: ValueAnnotationContext) { - const marker = lookup(value, true); - - if (marker !== null && marker.object !== context.host) { - return { - place: 'before', - style: 'badge', - text: name, - href: marker.href - }; - } - } - }); +export function concatAnnotations(a: ValueAnnotation[] | false, b: ValueAnnotation[] | false) { + if (Array.isArray(a)) { + return Array.isArray(b) + ? a.concat(b) + : a; } - return annotations.length > 0 ? annotations : false; + return b; } -export function prepareAnnotations(annotations: unknown[], hostAnnotations: ValueAnnotation[]) { +export function preprocessAnnotations(annotations: unknown[]) { if (Array.isArray(annotations) && annotations.length > 0) { - return hostAnnotations.concat(annotations.map(annotation => + return annotations.map((annotation) => typeof annotation === 'string' || typeof annotation === 'function' ? { query: annotation as Query } : annotation as ValueAnnotation - )); + ); } - return hostAnnotations; + return false; +} + +export function getDefaultAnnotations(host: ViewModel) { + return preprocessAnnotations([...host.objectMarkers.values].map(({ name, lookup }) => + (value: unknown, context: ValueAnnotationContext) => { + const marker = lookup(value, true); + + if (marker !== null && marker.object !== context.host) { + return { + place: 'before', + style: 'badge', + text: name, + href: marker.href + }; + } + } + )); +} + +export function prepareAnnotations( + viewAnnotations: unknown[], + customAnnotations: ValueAnnotation[] +) { + return concatAnnotations(customAnnotations, preprocessAnnotations(viewAnnotations)); } function isValidStyle(value: unknown): value is RenderAnnotationConfig['style'] {