@intlayer/editor 8.10.1 → 8.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -129,8 +129,39 @@ var IntlayerContentSelectorWrapperElement = class extends _HTMLElement {
129
129
  return;
130
130
  }
131
131
  const keyPath = this._getFilteredKeyPath();
132
+ const wasSelected = this._isSelected;
132
133
  this._isSelected = focusedContent.dictionaryKey === this._dictionaryKey && (focusedContent.keyPath?.length ?? 0) > 0 && (0, _intlayer_core_utils.isSameKeyPath)(focusedContent.keyPath ?? [], keyPath);
133
134
  this._updateSelectorAttr();
135
+ if (this._isSelected && !wasSelected) this._scrollIntoViewIfNeeded();
136
+ }
137
+ _scrollIntoViewIfNeeded() {
138
+ try {
139
+ let rect;
140
+ if (this._selector) {
141
+ const innerWrapper = this._selector.shadowRoot?.querySelector(".wrapper");
142
+ if (innerWrapper) {
143
+ const r = innerWrapper.getBoundingClientRect();
144
+ if (r.width > 0 || r.height > 0) rect = r;
145
+ }
146
+ }
147
+ if (!rect && this.childNodes.length > 0) {
148
+ const range = document.createRange();
149
+ range.selectNodeContents(this);
150
+ const r = range.getBoundingClientRect();
151
+ if (r.width > 0 || r.height > 0) rect = r;
152
+ }
153
+ if (!rect) rect = this.getBoundingClientRect();
154
+ const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
155
+ const viewportWidth = window.innerWidth || document.documentElement.clientWidth;
156
+ if (!(rect.width > 0 && rect.height > 0 && rect.bottom > 0 && rect.right > 0 && rect.top < viewportHeight && rect.left < viewportWidth)) {
157
+ const scrollY = window.scrollY ?? document.documentElement.scrollTop;
158
+ const targetScrollY = rect.top + scrollY - viewportHeight * .25;
159
+ window.scrollTo({
160
+ top: Math.max(0, targetScrollY),
161
+ behavior: "smooth"
162
+ });
163
+ }
164
+ } catch {}
134
165
  }
135
166
  _updateSelectorAttr() {
136
167
  if (!this._selector) return;
@@ -1 +1 @@
1
- {"version":3,"file":"ContentSelectorWrapper.cjs","names":["getGlobalEditorManager","NodeTypes","node","onGlobalEditorManagerChange"],"sources":["../../../src/components/ContentSelectorWrapper.ts"],"sourcesContent":["import { isSameKeyPath } from '@intlayer/core/utils';\n\nimport type { ContentNode } from '@intlayer/types/dictionary';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport type { TypedNodeModel } from '@intlayer/types/nodeType';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport type {\n EditorStateManager,\n FileContent,\n} from '../core/EditorStateManager';\nimport {\n getGlobalEditorManager,\n onGlobalEditorManagerChange,\n} from '../core/globalManager';\nimport { MessageKey } from '../messageKey';\n\ntype RenderState = 'simple' | 'wrapped-slot' | 'wrapped-text';\n\nconst _HTMLElement =\n typeof HTMLElement !== 'undefined'\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\n/**\n * <intlayer-content-selector-wrapper>\n *\n * Framework-agnostic web component that wraps content with the Intlayer editor\n * selection UI. It replaces the per-framework ContentSelectorWrapper components\n * (Vue, Svelte, Solid, Preact).\n *\n * It reads from the global EditorStateManager singleton (set by initEditorClient)\n * and conditionally renders <intlayer-content-selector> around its slot content\n * when the editor is active and the app is running inside an iframe.\n *\n * @attr {string} key-path - JSON-serialized KeyPath[] for this content node\n * @attr {string} dictionary-key - The dictionary key owning this content node\n */\nexport class IntlayerContentSelectorWrapperElement extends _HTMLElement {\n private _keyPathJson = '[]';\n private _dictionaryKey = '';\n private _editorEnabled = false;\n private _isInIframe = false;\n private _isSelected = false;\n private _editedValue: ContentNode | undefined = undefined;\n\n private _renderState: RenderState | null = null;\n private _selector: HTMLElement | null = null;\n\n private _unsubManager: (() => void) | null = null;\n private _unsubEnabled: (() => void) | null = null;\n private _unsubFocused: (() => void) | null = null;\n private _unsubEditedContent: (() => void) | null = null;\n\n static get observedAttributes(): string[] {\n return ['key-path', 'dictionary-key'];\n }\n\n get keyPathJson(): string {\n return this._keyPathJson;\n }\n set keyPathJson(v: string) {\n this._keyPathJson = v;\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n\n get dictionaryKey(): string {\n return this._dictionaryKey;\n }\n set dictionaryKey(v: string) {\n this._dictionaryKey = v;\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n\n constructor() {\n super();\n const shadow = this.attachShadow({ mode: 'open' });\n const style = document.createElement('style');\n style.textContent = ':host { display: contents; }';\n shadow.appendChild(style);\n }\n\n attributeChangedCallback(\n name: string,\n _oldVal: string | null,\n newVal: string | null\n ): void {\n if (name === 'key-path') {\n this._keyPathJson = newVal ?? '[]';\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n } else if (name === 'dictionary-key') {\n this._dictionaryKey = newVal ?? '';\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n }\n\n connectedCallback(): void {\n if (typeof window !== 'undefined') {\n this._isInIframe = window.self !== window.top;\n }\n this._subscribeToManager();\n this._render();\n }\n\n disconnectedCallback(): void {\n this._teardown();\n }\n\n private _teardown(): void {\n this._unsubManager?.();\n this._unsubEnabled?.();\n this._unsubFocused?.();\n this._unsubEditedContent?.();\n this._unsubManager = null;\n this._unsubEnabled = null;\n this._unsubFocused = null;\n this._unsubEditedContent = null;\n }\n\n private _getRawKeyPath(): KeyPath[] {\n try {\n return JSON.parse(this._keyPathJson) as KeyPath[];\n } catch {\n return [];\n }\n }\n\n private _getFilteredKeyPath(): KeyPath[] {\n return this._getRawKeyPath().filter(\n (keyPath) => keyPath.type !== NodeTypes.TRANSLATION\n );\n }\n\n private _updateEditedValue(manager: EditorStateManager): void {\n const filteredKeyPath = this._getFilteredKeyPath();\n if (!this._dictionaryKey || filteredKeyPath.length === 0) {\n this._editedValue = undefined;\n this._render();\n return;\n }\n\n // Node types whose display requires framework-level rendering (markdown,\n // HTML, insertion, file): do not override the slot — the framework handles\n // those. Only plain / translated strings can be substituted here.\n const rawKeyPath = this._getRawKeyPath();\n const lastStepType = rawKeyPath[rawKeyPath.length - 1]?.type;\n if (\n lastStepType === NodeTypes.MARKDOWN ||\n lastStepType === NodeTypes.HTML ||\n lastStepType === NodeTypes.INSERTION ||\n lastStepType === NodeTypes.FILE\n ) {\n this._editedValue = undefined;\n this._render();\n return;\n }\n\n let value = manager.getContentValue(this._dictionaryKey, filteredKeyPath);\n\n // getContentNodeByKeyPath resolves translation nodes only at intermediate\n // steps, not the final leaf. Resolve manually when the returned value is\n // still a translation object (happens when Translation steps are filtered\n // out and the leaf IS the translation object).\n if (\n value !== null &&\n value !== undefined &&\n typeof value === 'object' &&\n (value as { nodeType?: unknown }).nodeType === NodeTypes.TRANSLATION\n ) {\n const locale = manager.currentLocale.value as string | undefined;\n // TypedNodeModel<Translation, …> structurally satisfies TypedNode<BaseNode>\n // (both have nodeType), so this narrowing cast is sound.\n const node = value as TypedNodeModel<\n typeof NodeTypes.TRANSLATION,\n Record<string, ContentNode>\n >;\n value = locale ? node[NodeTypes.TRANSLATION][locale] : undefined;\n }\n\n this._editedValue = value;\n this._render();\n }\n\n private _updateIsSelected(\n focusedContent: FileContent | null | undefined\n ): void {\n if (!focusedContent) {\n this._isSelected = false;\n this._updateSelectorAttr();\n return;\n }\n const keyPath = this._getFilteredKeyPath();\n this._isSelected =\n focusedContent.dictionaryKey === this._dictionaryKey &&\n (focusedContent.keyPath?.length ?? 0) > 0 &&\n isSameKeyPath(focusedContent.keyPath ?? [], keyPath);\n this._updateSelectorAttr();\n }\n\n private _updateSelectorAttr(): void {\n if (!this._selector) return;\n if (this._isSelected) {\n this._selector.setAttribute('is-selecting', '');\n } else {\n this._selector.removeAttribute('is-selecting');\n }\n }\n\n private _subscribeToManager(): void {\n const manager = getGlobalEditorManager();\n if (manager) {\n this._setupManagerSubscriptions(manager);\n }\n // Keep listening for manager changes (handles stop + re-init cycles)\n this._unsubManager = onGlobalEditorManagerChange((m) => {\n this._unsubEnabled?.();\n this._unsubFocused?.();\n this._unsubEditedContent?.();\n this._unsubEnabled = null;\n this._unsubFocused = null;\n this._unsubEditedContent = null;\n if (m) {\n this._setupManagerSubscriptions(m);\n } else {\n this._editorEnabled = false;\n this._isSelected = false;\n this._editedValue = undefined;\n this._render();\n }\n });\n }\n\n private _setupManagerSubscriptions(manager: EditorStateManager): void {\n this._editorEnabled = manager.editorEnabled.value ?? false;\n this._updateIsSelected(manager.focusedContent.value);\n this._updateEditedValue(manager);\n\n const handleEnabledChange = (e: Event) => {\n this._editorEnabled = (e as CustomEvent<boolean>).detail;\n this._render();\n };\n const handleFocusedChange = (e: Event) => {\n this._updateIsSelected((e as CustomEvent<FileContent | null>).detail);\n };\n const handleEditedContentChange = () => {\n this._updateEditedValue(manager);\n };\n\n manager.editorEnabled.addEventListener('change', handleEnabledChange);\n manager.focusedContent.addEventListener('change', handleFocusedChange);\n manager.editedContent.addEventListener('change', handleEditedContentChange);\n\n this._unsubEnabled = () =>\n manager.editorEnabled.removeEventListener('change', handleEnabledChange);\n this._unsubFocused = () =>\n manager.focusedContent.removeEventListener('change', handleFocusedChange);\n this._unsubEditedContent = () =>\n manager.editedContent.removeEventListener(\n 'change',\n handleEditedContentChange\n );\n }\n\n private _handlePress(e: Event): void {\n // Stop propagation so nested wrappers don't also fire (composed + bubbles)\n e.stopPropagation();\n const manager = getGlobalEditorManager();\n if (!manager) return;\n manager.focusedContent.set({\n dictionaryKey: this._dictionaryKey,\n keyPath: this._getFilteredKeyPath(),\n });\n }\n\n private _handleHover(e: Event): void {\n e.stopPropagation();\n getGlobalEditorManager()?.messenger.send(\n `${MessageKey.INTLAYER_HOVERED_CONTENT_CHANGED}/post`,\n {\n dictionaryKey: this._dictionaryKey,\n keyPath: this._getFilteredKeyPath(),\n }\n );\n }\n\n private _handleUnhover(e: Event): void {\n e.stopPropagation();\n getGlobalEditorManager()?.messenger.send(\n `${MessageKey.INTLAYER_HOVERED_CONTENT_CHANGED}/post`,\n null\n );\n }\n\n private _render(): void {\n const useWrapper = this._isInIframe && this._editorEnabled;\n const editedValue = this._editedValue;\n const isSimpleValue =\n typeof editedValue === 'string' ||\n typeof editedValue === 'number' ||\n typeof editedValue === 'boolean';\n\n const newState: RenderState = !useWrapper\n ? 'simple'\n : isSimpleValue\n ? 'wrapped-text'\n : 'wrapped-slot';\n\n if (this._renderState !== newState) {\n this._rebuildContent(newState);\n return;\n }\n\n // Structure unchanged — update only dynamic parts\n if (newState !== 'simple' && this._selector) {\n this._updateSelectorAttr();\n if (\n newState === 'wrapped-text' &&\n this._selector.firstChild?.nodeType === Node.TEXT_NODE\n ) {\n (this._selector.firstChild as Text).data = String(editedValue);\n }\n }\n }\n\n private _rebuildContent(state: RenderState): void {\n const shadow = this.shadowRoot!;\n // Remove all nodes except the style element (first child)\n while (shadow.childNodes.length > 1) {\n shadow.removeChild(shadow.lastChild!);\n }\n this._selector = null;\n\n if (state === 'simple') {\n shadow.appendChild(document.createElement('slot'));\n } else {\n const selector = document.createElement('intlayer-content-selector');\n this._selector = selector;\n if (this._isSelected) selector.setAttribute('is-selecting', '');\n selector.addEventListener('intlayer:press', (e) => this._handlePress(e));\n selector.addEventListener('intlayer:hover', (e) => this._handleHover(e));\n selector.addEventListener('intlayer:unhover', (e) =>\n this._handleUnhover(e)\n );\n\n if (state === 'wrapped-text') {\n selector.appendChild(\n document.createTextNode(String(this._editedValue))\n );\n } else {\n selector.appendChild(document.createElement('slot'));\n }\n shadow.appendChild(selector);\n }\n\n this._renderState = state;\n }\n}\n\nexport const defineIntlayerContentSelectorWrapper = (): void => {\n if (typeof customElements === 'undefined') return;\n\n if (!customElements.get('intlayer-content-selector-wrapper')) {\n customElements.define(\n 'intlayer-content-selector-wrapper',\n IntlayerContentSelectorWrapperElement\n );\n }\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"ContentSelectorWrapper.cjs","names":["getGlobalEditorManager","NodeTypes","node","onGlobalEditorManagerChange"],"sources":["../../../src/components/ContentSelectorWrapper.ts"],"sourcesContent":["import { isSameKeyPath } from '@intlayer/core/utils';\n\nimport type { ContentNode } from '@intlayer/types/dictionary';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport type { TypedNodeModel } from '@intlayer/types/nodeType';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport type {\n EditorStateManager,\n FileContent,\n} from '../core/EditorStateManager';\nimport {\n getGlobalEditorManager,\n onGlobalEditorManagerChange,\n} from '../core/globalManager';\nimport { MessageKey } from '../messageKey';\n\ntype RenderState = 'simple' | 'wrapped-slot' | 'wrapped-text';\n\nconst _HTMLElement =\n typeof HTMLElement !== 'undefined'\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\n/**\n * <intlayer-content-selector-wrapper>\n *\n * Framework-agnostic web component that wraps content with the Intlayer editor\n * selection UI. It replaces the per-framework ContentSelectorWrapper components\n * (Vue, Svelte, Solid, Preact).\n *\n * It reads from the global EditorStateManager singleton (set by initEditorClient)\n * and conditionally renders <intlayer-content-selector> around its slot content\n * when the editor is active and the app is running inside an iframe.\n *\n * @attr {string} key-path - JSON-serialized KeyPath[] for this content node\n * @attr {string} dictionary-key - The dictionary key owning this content node\n */\nexport class IntlayerContentSelectorWrapperElement extends _HTMLElement {\n private _keyPathJson = '[]';\n private _dictionaryKey = '';\n private _editorEnabled = false;\n private _isInIframe = false;\n private _isSelected = false;\n private _editedValue: ContentNode | undefined = undefined;\n\n private _renderState: RenderState | null = null;\n private _selector: HTMLElement | null = null;\n\n private _unsubManager: (() => void) | null = null;\n private _unsubEnabled: (() => void) | null = null;\n private _unsubFocused: (() => void) | null = null;\n private _unsubEditedContent: (() => void) | null = null;\n\n static get observedAttributes(): string[] {\n return ['key-path', 'dictionary-key'];\n }\n\n get keyPathJson(): string {\n return this._keyPathJson;\n }\n set keyPathJson(v: string) {\n this._keyPathJson = v;\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n\n get dictionaryKey(): string {\n return this._dictionaryKey;\n }\n set dictionaryKey(v: string) {\n this._dictionaryKey = v;\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n\n constructor() {\n super();\n const shadow = this.attachShadow({ mode: 'open' });\n const style = document.createElement('style');\n style.textContent = ':host { display: contents; }';\n shadow.appendChild(style);\n }\n\n attributeChangedCallback(\n name: string,\n _oldVal: string | null,\n newVal: string | null\n ): void {\n if (name === 'key-path') {\n this._keyPathJson = newVal ?? '[]';\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n } else if (name === 'dictionary-key') {\n this._dictionaryKey = newVal ?? '';\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n }\n\n connectedCallback(): void {\n if (typeof window !== 'undefined') {\n this._isInIframe = window.self !== window.top;\n }\n this._subscribeToManager();\n this._render();\n }\n\n disconnectedCallback(): void {\n this._teardown();\n }\n\n private _teardown(): void {\n this._unsubManager?.();\n this._unsubEnabled?.();\n this._unsubFocused?.();\n this._unsubEditedContent?.();\n this._unsubManager = null;\n this._unsubEnabled = null;\n this._unsubFocused = null;\n this._unsubEditedContent = null;\n }\n\n private _getRawKeyPath(): KeyPath[] {\n try {\n return JSON.parse(this._keyPathJson) as KeyPath[];\n } catch {\n return [];\n }\n }\n\n private _getFilteredKeyPath(): KeyPath[] {\n return this._getRawKeyPath().filter(\n (keyPath) => keyPath.type !== NodeTypes.TRANSLATION\n );\n }\n\n private _updateEditedValue(manager: EditorStateManager): void {\n const filteredKeyPath = this._getFilteredKeyPath();\n if (!this._dictionaryKey || filteredKeyPath.length === 0) {\n this._editedValue = undefined;\n this._render();\n return;\n }\n\n // Node types whose display requires framework-level rendering (markdown,\n // HTML, insertion, file): do not override the slot — the framework handles\n // those. Only plain / translated strings can be substituted here.\n const rawKeyPath = this._getRawKeyPath();\n const lastStepType = rawKeyPath[rawKeyPath.length - 1]?.type;\n if (\n lastStepType === NodeTypes.MARKDOWN ||\n lastStepType === NodeTypes.HTML ||\n lastStepType === NodeTypes.INSERTION ||\n lastStepType === NodeTypes.FILE\n ) {\n this._editedValue = undefined;\n this._render();\n return;\n }\n\n let value = manager.getContentValue(this._dictionaryKey, filteredKeyPath);\n\n // getContentNodeByKeyPath resolves translation nodes only at intermediate\n // steps, not the final leaf. Resolve manually when the returned value is\n // still a translation object (happens when Translation steps are filtered\n // out and the leaf IS the translation object).\n if (\n value !== null &&\n value !== undefined &&\n typeof value === 'object' &&\n (value as { nodeType?: unknown }).nodeType === NodeTypes.TRANSLATION\n ) {\n const locale = manager.currentLocale.value as string | undefined;\n // TypedNodeModel<Translation, …> structurally satisfies TypedNode<BaseNode>\n // (both have nodeType), so this narrowing cast is sound.\n const node = value as TypedNodeModel<\n typeof NodeTypes.TRANSLATION,\n Record<string, ContentNode>\n >;\n value = locale ? node[NodeTypes.TRANSLATION][locale] : undefined;\n }\n\n this._editedValue = value;\n this._render();\n }\n\n private _updateIsSelected(\n focusedContent: FileContent | null | undefined\n ): void {\n if (!focusedContent) {\n this._isSelected = false;\n this._updateSelectorAttr();\n return;\n }\n const keyPath = this._getFilteredKeyPath();\n const wasSelected = this._isSelected;\n this._isSelected =\n focusedContent.dictionaryKey === this._dictionaryKey &&\n (focusedContent.keyPath?.length ?? 0) > 0 &&\n isSameKeyPath(focusedContent.keyPath ?? [], keyPath);\n this._updateSelectorAttr();\n\n // Scroll into view when this element becomes selected and is not already\n // visible in the iframe viewport. This covers the case where the editor\n // focuses a content block that is off-screen in the client app.\n if (this._isSelected && !wasSelected) {\n this._scrollIntoViewIfNeeded();\n }\n }\n\n private _scrollIntoViewIfNeeded(): void {\n try {\n let rect: DOMRect | undefined;\n\n // Primary: the .wrapper span inside the selector's open shadow DOM has a\n // real CSS box (display:inline-block) for both wrapped-text and wrapped-slot.\n // This is the only reliable source for simple-string nodes where this host's\n // light DOM is empty and this.getBoundingClientRect() returns zeros.\n if (this._selector) {\n const innerWrapper = this._selector.shadowRoot?.querySelector(\n '.wrapper'\n ) as HTMLElement | null;\n if (innerWrapper) {\n const r = innerWrapper.getBoundingClientRect();\n if (r.width > 0 || r.height > 0) rect = r;\n }\n }\n\n // Fallback: range over light-DOM children for wrapped-slot nodes rendered\n // by a framework (React/markdown) before _selector is available.\n if (!rect && this.childNodes.length > 0) {\n const range = document.createRange();\n\n range.selectNodeContents(this);\n\n const r = range.getBoundingClientRect();\n\n if (r.width > 0 || r.height > 0) rect = r;\n }\n\n if (!rect) {\n rect = this.getBoundingClientRect();\n }\n\n const viewportHeight =\n window.innerHeight || document.documentElement.clientHeight;\n const viewportWidth =\n window.innerWidth || document.documentElement.clientWidth;\n\n const isVisible =\n rect.width > 0 &&\n rect.height > 0 &&\n rect.bottom > 0 &&\n rect.right > 0 &&\n rect.top < viewportHeight &&\n rect.left < viewportWidth;\n\n if (!isVisible) {\n // Scroll so the element lands at 25 % from the top of the viewport.\n const scrollY = window.scrollY ?? document.documentElement.scrollTop;\n const targetScrollY = rect.top + scrollY - viewportHeight * 0.25;\n window.scrollTo({\n top: Math.max(0, targetScrollY),\n behavior: 'smooth',\n });\n }\n } catch {\n // scroll APIs may not be available in all environments\n }\n }\n\n private _updateSelectorAttr(): void {\n if (!this._selector) return;\n if (this._isSelected) {\n this._selector.setAttribute('is-selecting', '');\n } else {\n this._selector.removeAttribute('is-selecting');\n }\n }\n\n private _subscribeToManager(): void {\n const manager = getGlobalEditorManager();\n if (manager) {\n this._setupManagerSubscriptions(manager);\n }\n // Keep listening for manager changes (handles stop + re-init cycles)\n this._unsubManager = onGlobalEditorManagerChange((m) => {\n this._unsubEnabled?.();\n this._unsubFocused?.();\n this._unsubEditedContent?.();\n this._unsubEnabled = null;\n this._unsubFocused = null;\n this._unsubEditedContent = null;\n if (m) {\n this._setupManagerSubscriptions(m);\n } else {\n this._editorEnabled = false;\n this._isSelected = false;\n this._editedValue = undefined;\n this._render();\n }\n });\n }\n\n private _setupManagerSubscriptions(manager: EditorStateManager): void {\n this._editorEnabled = manager.editorEnabled.value ?? false;\n this._updateIsSelected(manager.focusedContent.value);\n this._updateEditedValue(manager);\n\n const handleEnabledChange = (e: Event) => {\n this._editorEnabled = (e as CustomEvent<boolean>).detail;\n this._render();\n };\n const handleFocusedChange = (e: Event) => {\n this._updateIsSelected((e as CustomEvent<FileContent | null>).detail);\n };\n const handleEditedContentChange = () => {\n this._updateEditedValue(manager);\n };\n\n manager.editorEnabled.addEventListener('change', handleEnabledChange);\n manager.focusedContent.addEventListener('change', handleFocusedChange);\n manager.editedContent.addEventListener('change', handleEditedContentChange);\n\n this._unsubEnabled = () =>\n manager.editorEnabled.removeEventListener('change', handleEnabledChange);\n this._unsubFocused = () =>\n manager.focusedContent.removeEventListener('change', handleFocusedChange);\n this._unsubEditedContent = () =>\n manager.editedContent.removeEventListener(\n 'change',\n handleEditedContentChange\n );\n }\n\n private _handlePress(e: Event): void {\n // Stop propagation so nested wrappers don't also fire (composed + bubbles)\n e.stopPropagation();\n const manager = getGlobalEditorManager();\n if (!manager) return;\n manager.focusedContent.set({\n dictionaryKey: this._dictionaryKey,\n keyPath: this._getFilteredKeyPath(),\n });\n }\n\n private _handleHover(e: Event): void {\n e.stopPropagation();\n getGlobalEditorManager()?.messenger.send(\n `${MessageKey.INTLAYER_HOVERED_CONTENT_CHANGED}/post`,\n {\n dictionaryKey: this._dictionaryKey,\n keyPath: this._getFilteredKeyPath(),\n }\n );\n }\n\n private _handleUnhover(e: Event): void {\n e.stopPropagation();\n getGlobalEditorManager()?.messenger.send(\n `${MessageKey.INTLAYER_HOVERED_CONTENT_CHANGED}/post`,\n null\n );\n }\n\n private _render(): void {\n const useWrapper = this._isInIframe && this._editorEnabled;\n const editedValue = this._editedValue;\n const isSimpleValue =\n typeof editedValue === 'string' ||\n typeof editedValue === 'number' ||\n typeof editedValue === 'boolean';\n\n const newState: RenderState = !useWrapper\n ? 'simple'\n : isSimpleValue\n ? 'wrapped-text'\n : 'wrapped-slot';\n\n if (this._renderState !== newState) {\n this._rebuildContent(newState);\n return;\n }\n\n // Structure unchanged — update only dynamic parts\n if (newState !== 'simple' && this._selector) {\n this._updateSelectorAttr();\n if (\n newState === 'wrapped-text' &&\n this._selector.firstChild?.nodeType === Node.TEXT_NODE\n ) {\n (this._selector.firstChild as Text).data = String(editedValue);\n }\n }\n }\n\n private _rebuildContent(state: RenderState): void {\n const shadow = this.shadowRoot!;\n // Remove all nodes except the style element (first child)\n while (shadow.childNodes.length > 1) {\n shadow.removeChild(shadow.lastChild!);\n }\n this._selector = null;\n\n if (state === 'simple') {\n shadow.appendChild(document.createElement('slot'));\n } else {\n const selector = document.createElement('intlayer-content-selector');\n this._selector = selector;\n if (this._isSelected) selector.setAttribute('is-selecting', '');\n selector.addEventListener('intlayer:press', (e) => this._handlePress(e));\n selector.addEventListener('intlayer:hover', (e) => this._handleHover(e));\n selector.addEventListener('intlayer:unhover', (e) =>\n this._handleUnhover(e)\n );\n\n if (state === 'wrapped-text') {\n selector.appendChild(\n document.createTextNode(String(this._editedValue))\n );\n } else {\n selector.appendChild(document.createElement('slot'));\n }\n shadow.appendChild(selector);\n }\n\n this._renderState = state;\n }\n}\n\nexport const defineIntlayerContentSelectorWrapper = (): void => {\n if (typeof customElements === 'undefined') return;\n\n if (!customElements.get('intlayer-content-selector-wrapper')) {\n customElements.define(\n 'intlayer-content-selector-wrapper',\n IntlayerContentSelectorWrapperElement\n );\n }\n};\n"],"mappings":"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"}
@@ -126,8 +126,39 @@ var IntlayerContentSelectorWrapperElement = class extends _HTMLElement {
126
126
  return;
127
127
  }
128
128
  const keyPath = this._getFilteredKeyPath();
129
+ const wasSelected = this._isSelected;
129
130
  this._isSelected = focusedContent.dictionaryKey === this._dictionaryKey && (focusedContent.keyPath?.length ?? 0) > 0 && isSameKeyPath(focusedContent.keyPath ?? [], keyPath);
130
131
  this._updateSelectorAttr();
132
+ if (this._isSelected && !wasSelected) this._scrollIntoViewIfNeeded();
133
+ }
134
+ _scrollIntoViewIfNeeded() {
135
+ try {
136
+ let rect;
137
+ if (this._selector) {
138
+ const innerWrapper = this._selector.shadowRoot?.querySelector(".wrapper");
139
+ if (innerWrapper) {
140
+ const r = innerWrapper.getBoundingClientRect();
141
+ if (r.width > 0 || r.height > 0) rect = r;
142
+ }
143
+ }
144
+ if (!rect && this.childNodes.length > 0) {
145
+ const range = document.createRange();
146
+ range.selectNodeContents(this);
147
+ const r = range.getBoundingClientRect();
148
+ if (r.width > 0 || r.height > 0) rect = r;
149
+ }
150
+ if (!rect) rect = this.getBoundingClientRect();
151
+ const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
152
+ const viewportWidth = window.innerWidth || document.documentElement.clientWidth;
153
+ if (!(rect.width > 0 && rect.height > 0 && rect.bottom > 0 && rect.right > 0 && rect.top < viewportHeight && rect.left < viewportWidth)) {
154
+ const scrollY = window.scrollY ?? document.documentElement.scrollTop;
155
+ const targetScrollY = rect.top + scrollY - viewportHeight * .25;
156
+ window.scrollTo({
157
+ top: Math.max(0, targetScrollY),
158
+ behavior: "smooth"
159
+ });
160
+ }
161
+ } catch {}
131
162
  }
132
163
  _updateSelectorAttr() {
133
164
  if (!this._selector) return;
@@ -1 +1 @@
1
- {"version":3,"file":"ContentSelectorWrapper.mjs","names":["node"],"sources":["../../../src/components/ContentSelectorWrapper.ts"],"sourcesContent":["import { isSameKeyPath } from '@intlayer/core/utils';\n\nimport type { ContentNode } from '@intlayer/types/dictionary';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport type { TypedNodeModel } from '@intlayer/types/nodeType';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport type {\n EditorStateManager,\n FileContent,\n} from '../core/EditorStateManager';\nimport {\n getGlobalEditorManager,\n onGlobalEditorManagerChange,\n} from '../core/globalManager';\nimport { MessageKey } from '../messageKey';\n\ntype RenderState = 'simple' | 'wrapped-slot' | 'wrapped-text';\n\nconst _HTMLElement =\n typeof HTMLElement !== 'undefined'\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\n/**\n * <intlayer-content-selector-wrapper>\n *\n * Framework-agnostic web component that wraps content with the Intlayer editor\n * selection UI. It replaces the per-framework ContentSelectorWrapper components\n * (Vue, Svelte, Solid, Preact).\n *\n * It reads from the global EditorStateManager singleton (set by initEditorClient)\n * and conditionally renders <intlayer-content-selector> around its slot content\n * when the editor is active and the app is running inside an iframe.\n *\n * @attr {string} key-path - JSON-serialized KeyPath[] for this content node\n * @attr {string} dictionary-key - The dictionary key owning this content node\n */\nexport class IntlayerContentSelectorWrapperElement extends _HTMLElement {\n private _keyPathJson = '[]';\n private _dictionaryKey = '';\n private _editorEnabled = false;\n private _isInIframe = false;\n private _isSelected = false;\n private _editedValue: ContentNode | undefined = undefined;\n\n private _renderState: RenderState | null = null;\n private _selector: HTMLElement | null = null;\n\n private _unsubManager: (() => void) | null = null;\n private _unsubEnabled: (() => void) | null = null;\n private _unsubFocused: (() => void) | null = null;\n private _unsubEditedContent: (() => void) | null = null;\n\n static get observedAttributes(): string[] {\n return ['key-path', 'dictionary-key'];\n }\n\n get keyPathJson(): string {\n return this._keyPathJson;\n }\n set keyPathJson(v: string) {\n this._keyPathJson = v;\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n\n get dictionaryKey(): string {\n return this._dictionaryKey;\n }\n set dictionaryKey(v: string) {\n this._dictionaryKey = v;\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n\n constructor() {\n super();\n const shadow = this.attachShadow({ mode: 'open' });\n const style = document.createElement('style');\n style.textContent = ':host { display: contents; }';\n shadow.appendChild(style);\n }\n\n attributeChangedCallback(\n name: string,\n _oldVal: string | null,\n newVal: string | null\n ): void {\n if (name === 'key-path') {\n this._keyPathJson = newVal ?? '[]';\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n } else if (name === 'dictionary-key') {\n this._dictionaryKey = newVal ?? '';\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n }\n\n connectedCallback(): void {\n if (typeof window !== 'undefined') {\n this._isInIframe = window.self !== window.top;\n }\n this._subscribeToManager();\n this._render();\n }\n\n disconnectedCallback(): void {\n this._teardown();\n }\n\n private _teardown(): void {\n this._unsubManager?.();\n this._unsubEnabled?.();\n this._unsubFocused?.();\n this._unsubEditedContent?.();\n this._unsubManager = null;\n this._unsubEnabled = null;\n this._unsubFocused = null;\n this._unsubEditedContent = null;\n }\n\n private _getRawKeyPath(): KeyPath[] {\n try {\n return JSON.parse(this._keyPathJson) as KeyPath[];\n } catch {\n return [];\n }\n }\n\n private _getFilteredKeyPath(): KeyPath[] {\n return this._getRawKeyPath().filter(\n (keyPath) => keyPath.type !== NodeTypes.TRANSLATION\n );\n }\n\n private _updateEditedValue(manager: EditorStateManager): void {\n const filteredKeyPath = this._getFilteredKeyPath();\n if (!this._dictionaryKey || filteredKeyPath.length === 0) {\n this._editedValue = undefined;\n this._render();\n return;\n }\n\n // Node types whose display requires framework-level rendering (markdown,\n // HTML, insertion, file): do not override the slot — the framework handles\n // those. Only plain / translated strings can be substituted here.\n const rawKeyPath = this._getRawKeyPath();\n const lastStepType = rawKeyPath[rawKeyPath.length - 1]?.type;\n if (\n lastStepType === NodeTypes.MARKDOWN ||\n lastStepType === NodeTypes.HTML ||\n lastStepType === NodeTypes.INSERTION ||\n lastStepType === NodeTypes.FILE\n ) {\n this._editedValue = undefined;\n this._render();\n return;\n }\n\n let value = manager.getContentValue(this._dictionaryKey, filteredKeyPath);\n\n // getContentNodeByKeyPath resolves translation nodes only at intermediate\n // steps, not the final leaf. Resolve manually when the returned value is\n // still a translation object (happens when Translation steps are filtered\n // out and the leaf IS the translation object).\n if (\n value !== null &&\n value !== undefined &&\n typeof value === 'object' &&\n (value as { nodeType?: unknown }).nodeType === NodeTypes.TRANSLATION\n ) {\n const locale = manager.currentLocale.value as string | undefined;\n // TypedNodeModel<Translation, …> structurally satisfies TypedNode<BaseNode>\n // (both have nodeType), so this narrowing cast is sound.\n const node = value as TypedNodeModel<\n typeof NodeTypes.TRANSLATION,\n Record<string, ContentNode>\n >;\n value = locale ? node[NodeTypes.TRANSLATION][locale] : undefined;\n }\n\n this._editedValue = value;\n this._render();\n }\n\n private _updateIsSelected(\n focusedContent: FileContent | null | undefined\n ): void {\n if (!focusedContent) {\n this._isSelected = false;\n this._updateSelectorAttr();\n return;\n }\n const keyPath = this._getFilteredKeyPath();\n this._isSelected =\n focusedContent.dictionaryKey === this._dictionaryKey &&\n (focusedContent.keyPath?.length ?? 0) > 0 &&\n isSameKeyPath(focusedContent.keyPath ?? [], keyPath);\n this._updateSelectorAttr();\n }\n\n private _updateSelectorAttr(): void {\n if (!this._selector) return;\n if (this._isSelected) {\n this._selector.setAttribute('is-selecting', '');\n } else {\n this._selector.removeAttribute('is-selecting');\n }\n }\n\n private _subscribeToManager(): void {\n const manager = getGlobalEditorManager();\n if (manager) {\n this._setupManagerSubscriptions(manager);\n }\n // Keep listening for manager changes (handles stop + re-init cycles)\n this._unsubManager = onGlobalEditorManagerChange((m) => {\n this._unsubEnabled?.();\n this._unsubFocused?.();\n this._unsubEditedContent?.();\n this._unsubEnabled = null;\n this._unsubFocused = null;\n this._unsubEditedContent = null;\n if (m) {\n this._setupManagerSubscriptions(m);\n } else {\n this._editorEnabled = false;\n this._isSelected = false;\n this._editedValue = undefined;\n this._render();\n }\n });\n }\n\n private _setupManagerSubscriptions(manager: EditorStateManager): void {\n this._editorEnabled = manager.editorEnabled.value ?? false;\n this._updateIsSelected(manager.focusedContent.value);\n this._updateEditedValue(manager);\n\n const handleEnabledChange = (e: Event) => {\n this._editorEnabled = (e as CustomEvent<boolean>).detail;\n this._render();\n };\n const handleFocusedChange = (e: Event) => {\n this._updateIsSelected((e as CustomEvent<FileContent | null>).detail);\n };\n const handleEditedContentChange = () => {\n this._updateEditedValue(manager);\n };\n\n manager.editorEnabled.addEventListener('change', handleEnabledChange);\n manager.focusedContent.addEventListener('change', handleFocusedChange);\n manager.editedContent.addEventListener('change', handleEditedContentChange);\n\n this._unsubEnabled = () =>\n manager.editorEnabled.removeEventListener('change', handleEnabledChange);\n this._unsubFocused = () =>\n manager.focusedContent.removeEventListener('change', handleFocusedChange);\n this._unsubEditedContent = () =>\n manager.editedContent.removeEventListener(\n 'change',\n handleEditedContentChange\n );\n }\n\n private _handlePress(e: Event): void {\n // Stop propagation so nested wrappers don't also fire (composed + bubbles)\n e.stopPropagation();\n const manager = getGlobalEditorManager();\n if (!manager) return;\n manager.focusedContent.set({\n dictionaryKey: this._dictionaryKey,\n keyPath: this._getFilteredKeyPath(),\n });\n }\n\n private _handleHover(e: Event): void {\n e.stopPropagation();\n getGlobalEditorManager()?.messenger.send(\n `${MessageKey.INTLAYER_HOVERED_CONTENT_CHANGED}/post`,\n {\n dictionaryKey: this._dictionaryKey,\n keyPath: this._getFilteredKeyPath(),\n }\n );\n }\n\n private _handleUnhover(e: Event): void {\n e.stopPropagation();\n getGlobalEditorManager()?.messenger.send(\n `${MessageKey.INTLAYER_HOVERED_CONTENT_CHANGED}/post`,\n null\n );\n }\n\n private _render(): void {\n const useWrapper = this._isInIframe && this._editorEnabled;\n const editedValue = this._editedValue;\n const isSimpleValue =\n typeof editedValue === 'string' ||\n typeof editedValue === 'number' ||\n typeof editedValue === 'boolean';\n\n const newState: RenderState = !useWrapper\n ? 'simple'\n : isSimpleValue\n ? 'wrapped-text'\n : 'wrapped-slot';\n\n if (this._renderState !== newState) {\n this._rebuildContent(newState);\n return;\n }\n\n // Structure unchanged — update only dynamic parts\n if (newState !== 'simple' && this._selector) {\n this._updateSelectorAttr();\n if (\n newState === 'wrapped-text' &&\n this._selector.firstChild?.nodeType === Node.TEXT_NODE\n ) {\n (this._selector.firstChild as Text).data = String(editedValue);\n }\n }\n }\n\n private _rebuildContent(state: RenderState): void {\n const shadow = this.shadowRoot!;\n // Remove all nodes except the style element (first child)\n while (shadow.childNodes.length > 1) {\n shadow.removeChild(shadow.lastChild!);\n }\n this._selector = null;\n\n if (state === 'simple') {\n shadow.appendChild(document.createElement('slot'));\n } else {\n const selector = document.createElement('intlayer-content-selector');\n this._selector = selector;\n if (this._isSelected) selector.setAttribute('is-selecting', '');\n selector.addEventListener('intlayer:press', (e) => this._handlePress(e));\n selector.addEventListener('intlayer:hover', (e) => this._handleHover(e));\n selector.addEventListener('intlayer:unhover', (e) =>\n this._handleUnhover(e)\n );\n\n if (state === 'wrapped-text') {\n selector.appendChild(\n document.createTextNode(String(this._editedValue))\n );\n } else {\n selector.appendChild(document.createElement('slot'));\n }\n shadow.appendChild(selector);\n }\n\n this._renderState = state;\n }\n}\n\nexport const defineIntlayerContentSelectorWrapper = (): void => {\n if (typeof customElements === 'undefined') return;\n\n if (!customElements.get('intlayer-content-selector-wrapper')) {\n customElements.define(\n 'intlayer-content-selector-wrapper',\n IntlayerContentSelectorWrapperElement\n );\n }\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"ContentSelectorWrapper.mjs","names":["node"],"sources":["../../../src/components/ContentSelectorWrapper.ts"],"sourcesContent":["import { isSameKeyPath } from '@intlayer/core/utils';\n\nimport type { ContentNode } from '@intlayer/types/dictionary';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport type { TypedNodeModel } from '@intlayer/types/nodeType';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport type {\n EditorStateManager,\n FileContent,\n} from '../core/EditorStateManager';\nimport {\n getGlobalEditorManager,\n onGlobalEditorManagerChange,\n} from '../core/globalManager';\nimport { MessageKey } from '../messageKey';\n\ntype RenderState = 'simple' | 'wrapped-slot' | 'wrapped-text';\n\nconst _HTMLElement =\n typeof HTMLElement !== 'undefined'\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\n/**\n * <intlayer-content-selector-wrapper>\n *\n * Framework-agnostic web component that wraps content with the Intlayer editor\n * selection UI. It replaces the per-framework ContentSelectorWrapper components\n * (Vue, Svelte, Solid, Preact).\n *\n * It reads from the global EditorStateManager singleton (set by initEditorClient)\n * and conditionally renders <intlayer-content-selector> around its slot content\n * when the editor is active and the app is running inside an iframe.\n *\n * @attr {string} key-path - JSON-serialized KeyPath[] for this content node\n * @attr {string} dictionary-key - The dictionary key owning this content node\n */\nexport class IntlayerContentSelectorWrapperElement extends _HTMLElement {\n private _keyPathJson = '[]';\n private _dictionaryKey = '';\n private _editorEnabled = false;\n private _isInIframe = false;\n private _isSelected = false;\n private _editedValue: ContentNode | undefined = undefined;\n\n private _renderState: RenderState | null = null;\n private _selector: HTMLElement | null = null;\n\n private _unsubManager: (() => void) | null = null;\n private _unsubEnabled: (() => void) | null = null;\n private _unsubFocused: (() => void) | null = null;\n private _unsubEditedContent: (() => void) | null = null;\n\n static get observedAttributes(): string[] {\n return ['key-path', 'dictionary-key'];\n }\n\n get keyPathJson(): string {\n return this._keyPathJson;\n }\n set keyPathJson(v: string) {\n this._keyPathJson = v;\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n\n get dictionaryKey(): string {\n return this._dictionaryKey;\n }\n set dictionaryKey(v: string) {\n this._dictionaryKey = v;\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n\n constructor() {\n super();\n const shadow = this.attachShadow({ mode: 'open' });\n const style = document.createElement('style');\n style.textContent = ':host { display: contents; }';\n shadow.appendChild(style);\n }\n\n attributeChangedCallback(\n name: string,\n _oldVal: string | null,\n newVal: string | null\n ): void {\n if (name === 'key-path') {\n this._keyPathJson = newVal ?? '[]';\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n } else if (name === 'dictionary-key') {\n this._dictionaryKey = newVal ?? '';\n const manager = getGlobalEditorManager();\n if (manager) this._updateEditedValue(manager);\n }\n }\n\n connectedCallback(): void {\n if (typeof window !== 'undefined') {\n this._isInIframe = window.self !== window.top;\n }\n this._subscribeToManager();\n this._render();\n }\n\n disconnectedCallback(): void {\n this._teardown();\n }\n\n private _teardown(): void {\n this._unsubManager?.();\n this._unsubEnabled?.();\n this._unsubFocused?.();\n this._unsubEditedContent?.();\n this._unsubManager = null;\n this._unsubEnabled = null;\n this._unsubFocused = null;\n this._unsubEditedContent = null;\n }\n\n private _getRawKeyPath(): KeyPath[] {\n try {\n return JSON.parse(this._keyPathJson) as KeyPath[];\n } catch {\n return [];\n }\n }\n\n private _getFilteredKeyPath(): KeyPath[] {\n return this._getRawKeyPath().filter(\n (keyPath) => keyPath.type !== NodeTypes.TRANSLATION\n );\n }\n\n private _updateEditedValue(manager: EditorStateManager): void {\n const filteredKeyPath = this._getFilteredKeyPath();\n if (!this._dictionaryKey || filteredKeyPath.length === 0) {\n this._editedValue = undefined;\n this._render();\n return;\n }\n\n // Node types whose display requires framework-level rendering (markdown,\n // HTML, insertion, file): do not override the slot — the framework handles\n // those. Only plain / translated strings can be substituted here.\n const rawKeyPath = this._getRawKeyPath();\n const lastStepType = rawKeyPath[rawKeyPath.length - 1]?.type;\n if (\n lastStepType === NodeTypes.MARKDOWN ||\n lastStepType === NodeTypes.HTML ||\n lastStepType === NodeTypes.INSERTION ||\n lastStepType === NodeTypes.FILE\n ) {\n this._editedValue = undefined;\n this._render();\n return;\n }\n\n let value = manager.getContentValue(this._dictionaryKey, filteredKeyPath);\n\n // getContentNodeByKeyPath resolves translation nodes only at intermediate\n // steps, not the final leaf. Resolve manually when the returned value is\n // still a translation object (happens when Translation steps are filtered\n // out and the leaf IS the translation object).\n if (\n value !== null &&\n value !== undefined &&\n typeof value === 'object' &&\n (value as { nodeType?: unknown }).nodeType === NodeTypes.TRANSLATION\n ) {\n const locale = manager.currentLocale.value as string | undefined;\n // TypedNodeModel<Translation, …> structurally satisfies TypedNode<BaseNode>\n // (both have nodeType), so this narrowing cast is sound.\n const node = value as TypedNodeModel<\n typeof NodeTypes.TRANSLATION,\n Record<string, ContentNode>\n >;\n value = locale ? node[NodeTypes.TRANSLATION][locale] : undefined;\n }\n\n this._editedValue = value;\n this._render();\n }\n\n private _updateIsSelected(\n focusedContent: FileContent | null | undefined\n ): void {\n if (!focusedContent) {\n this._isSelected = false;\n this._updateSelectorAttr();\n return;\n }\n const keyPath = this._getFilteredKeyPath();\n const wasSelected = this._isSelected;\n this._isSelected =\n focusedContent.dictionaryKey === this._dictionaryKey &&\n (focusedContent.keyPath?.length ?? 0) > 0 &&\n isSameKeyPath(focusedContent.keyPath ?? [], keyPath);\n this._updateSelectorAttr();\n\n // Scroll into view when this element becomes selected and is not already\n // visible in the iframe viewport. This covers the case where the editor\n // focuses a content block that is off-screen in the client app.\n if (this._isSelected && !wasSelected) {\n this._scrollIntoViewIfNeeded();\n }\n }\n\n private _scrollIntoViewIfNeeded(): void {\n try {\n let rect: DOMRect | undefined;\n\n // Primary: the .wrapper span inside the selector's open shadow DOM has a\n // real CSS box (display:inline-block) for both wrapped-text and wrapped-slot.\n // This is the only reliable source for simple-string nodes where this host's\n // light DOM is empty and this.getBoundingClientRect() returns zeros.\n if (this._selector) {\n const innerWrapper = this._selector.shadowRoot?.querySelector(\n '.wrapper'\n ) as HTMLElement | null;\n if (innerWrapper) {\n const r = innerWrapper.getBoundingClientRect();\n if (r.width > 0 || r.height > 0) rect = r;\n }\n }\n\n // Fallback: range over light-DOM children for wrapped-slot nodes rendered\n // by a framework (React/markdown) before _selector is available.\n if (!rect && this.childNodes.length > 0) {\n const range = document.createRange();\n\n range.selectNodeContents(this);\n\n const r = range.getBoundingClientRect();\n\n if (r.width > 0 || r.height > 0) rect = r;\n }\n\n if (!rect) {\n rect = this.getBoundingClientRect();\n }\n\n const viewportHeight =\n window.innerHeight || document.documentElement.clientHeight;\n const viewportWidth =\n window.innerWidth || document.documentElement.clientWidth;\n\n const isVisible =\n rect.width > 0 &&\n rect.height > 0 &&\n rect.bottom > 0 &&\n rect.right > 0 &&\n rect.top < viewportHeight &&\n rect.left < viewportWidth;\n\n if (!isVisible) {\n // Scroll so the element lands at 25 % from the top of the viewport.\n const scrollY = window.scrollY ?? document.documentElement.scrollTop;\n const targetScrollY = rect.top + scrollY - viewportHeight * 0.25;\n window.scrollTo({\n top: Math.max(0, targetScrollY),\n behavior: 'smooth',\n });\n }\n } catch {\n // scroll APIs may not be available in all environments\n }\n }\n\n private _updateSelectorAttr(): void {\n if (!this._selector) return;\n if (this._isSelected) {\n this._selector.setAttribute('is-selecting', '');\n } else {\n this._selector.removeAttribute('is-selecting');\n }\n }\n\n private _subscribeToManager(): void {\n const manager = getGlobalEditorManager();\n if (manager) {\n this._setupManagerSubscriptions(manager);\n }\n // Keep listening for manager changes (handles stop + re-init cycles)\n this._unsubManager = onGlobalEditorManagerChange((m) => {\n this._unsubEnabled?.();\n this._unsubFocused?.();\n this._unsubEditedContent?.();\n this._unsubEnabled = null;\n this._unsubFocused = null;\n this._unsubEditedContent = null;\n if (m) {\n this._setupManagerSubscriptions(m);\n } else {\n this._editorEnabled = false;\n this._isSelected = false;\n this._editedValue = undefined;\n this._render();\n }\n });\n }\n\n private _setupManagerSubscriptions(manager: EditorStateManager): void {\n this._editorEnabled = manager.editorEnabled.value ?? false;\n this._updateIsSelected(manager.focusedContent.value);\n this._updateEditedValue(manager);\n\n const handleEnabledChange = (e: Event) => {\n this._editorEnabled = (e as CustomEvent<boolean>).detail;\n this._render();\n };\n const handleFocusedChange = (e: Event) => {\n this._updateIsSelected((e as CustomEvent<FileContent | null>).detail);\n };\n const handleEditedContentChange = () => {\n this._updateEditedValue(manager);\n };\n\n manager.editorEnabled.addEventListener('change', handleEnabledChange);\n manager.focusedContent.addEventListener('change', handleFocusedChange);\n manager.editedContent.addEventListener('change', handleEditedContentChange);\n\n this._unsubEnabled = () =>\n manager.editorEnabled.removeEventListener('change', handleEnabledChange);\n this._unsubFocused = () =>\n manager.focusedContent.removeEventListener('change', handleFocusedChange);\n this._unsubEditedContent = () =>\n manager.editedContent.removeEventListener(\n 'change',\n handleEditedContentChange\n );\n }\n\n private _handlePress(e: Event): void {\n // Stop propagation so nested wrappers don't also fire (composed + bubbles)\n e.stopPropagation();\n const manager = getGlobalEditorManager();\n if (!manager) return;\n manager.focusedContent.set({\n dictionaryKey: this._dictionaryKey,\n keyPath: this._getFilteredKeyPath(),\n });\n }\n\n private _handleHover(e: Event): void {\n e.stopPropagation();\n getGlobalEditorManager()?.messenger.send(\n `${MessageKey.INTLAYER_HOVERED_CONTENT_CHANGED}/post`,\n {\n dictionaryKey: this._dictionaryKey,\n keyPath: this._getFilteredKeyPath(),\n }\n );\n }\n\n private _handleUnhover(e: Event): void {\n e.stopPropagation();\n getGlobalEditorManager()?.messenger.send(\n `${MessageKey.INTLAYER_HOVERED_CONTENT_CHANGED}/post`,\n null\n );\n }\n\n private _render(): void {\n const useWrapper = this._isInIframe && this._editorEnabled;\n const editedValue = this._editedValue;\n const isSimpleValue =\n typeof editedValue === 'string' ||\n typeof editedValue === 'number' ||\n typeof editedValue === 'boolean';\n\n const newState: RenderState = !useWrapper\n ? 'simple'\n : isSimpleValue\n ? 'wrapped-text'\n : 'wrapped-slot';\n\n if (this._renderState !== newState) {\n this._rebuildContent(newState);\n return;\n }\n\n // Structure unchanged — update only dynamic parts\n if (newState !== 'simple' && this._selector) {\n this._updateSelectorAttr();\n if (\n newState === 'wrapped-text' &&\n this._selector.firstChild?.nodeType === Node.TEXT_NODE\n ) {\n (this._selector.firstChild as Text).data = String(editedValue);\n }\n }\n }\n\n private _rebuildContent(state: RenderState): void {\n const shadow = this.shadowRoot!;\n // Remove all nodes except the style element (first child)\n while (shadow.childNodes.length > 1) {\n shadow.removeChild(shadow.lastChild!);\n }\n this._selector = null;\n\n if (state === 'simple') {\n shadow.appendChild(document.createElement('slot'));\n } else {\n const selector = document.createElement('intlayer-content-selector');\n this._selector = selector;\n if (this._isSelected) selector.setAttribute('is-selecting', '');\n selector.addEventListener('intlayer:press', (e) => this._handlePress(e));\n selector.addEventListener('intlayer:hover', (e) => this._handleHover(e));\n selector.addEventListener('intlayer:unhover', (e) =>\n this._handleUnhover(e)\n );\n\n if (state === 'wrapped-text') {\n selector.appendChild(\n document.createTextNode(String(this._editedValue))\n );\n } else {\n selector.appendChild(document.createElement('slot'));\n }\n shadow.appendChild(selector);\n }\n\n this._renderState = state;\n }\n}\n\nexport const defineIntlayerContentSelectorWrapper = (): void => {\n if (typeof customElements === 'undefined') return;\n\n if (!customElements.get('intlayer-content-selector-wrapper')) {\n customElements.define(\n 'intlayer-content-selector-wrapper',\n IntlayerContentSelectorWrapperElement\n );\n }\n};\n"],"mappings":"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"}
@@ -44,6 +44,7 @@ declare class IntlayerContentSelectorWrapperElement extends _HTMLElement {
44
44
  private _getFilteredKeyPath;
45
45
  private _updateEditedValue;
46
46
  private _updateIsSelected;
47
+ private _scrollIntoViewIfNeeded;
47
48
  private _updateSelectorAttr;
48
49
  private _subscribeToManager;
49
50
  private _setupManagerSubscriptions;
@@ -1 +1 @@
1
- {"version":3,"file":"ContentSelectorWrapper.d.ts","names":[],"sources":["../../../src/components/ContentSelectorWrapper.ts"],"mappings":";cAkBM,YAAA;EAAA,QAG2C,WAAA;aAAA,WAAA;AAAA;;;;;;;AAgBjD;;;;;;;;cAAa,qCAAA,SAA8C,YAAY;EAAA,QAC7D,YAAA;EAAA,QACA,cAAA;EAAA,QACA,cAAA;EAAA,QACA,WAAA;EAAA,QACA,WAAA;EAAA,QACA,YAAA;EAAA,QAEA,YAAA;EAAA,QACA,SAAA;EAAA,QAEA,aAAA;EAAA,QACA,aAAA;EAAA,QACA,aAAA;EAAA,QACA,mBAAA;EAAA,WAEG,kBAAA,CAAA;EAAA,IAIP,WAAA,CAAA;EAAA,IAGA,WAAA,CAAY,CAAA;EAAA,IAMZ,aAAA,CAAA;EAAA,IAGA,aAAA,CAAc,CAAA;;EAclB,wBAAA,CACE,IAAA,UACA,OAAA,iBACA,MAAA;EAaF,iBAAA,CAAA;EAQA,oBAAA,CAAA;EAAA,QAIQ,SAAA;EAAA,QAWA,cAAA;EAAA,QAQA,mBAAA;EAAA,QAMA,kBAAA;EAAA,QAkDA,iBAAA;EAAA,QAgBA,mBAAA;EAAA,QASA,mBAAA;EAAA,QAwBA,0BAAA;EAAA,QA+BA,YAAA;EAAA,QAWA,YAAA;EAAA,QAWA,cAAA;EAAA,QAQA,OAAA;EAAA,QA+BA,eAAA;AAAA;AAAA,cAkCG,oCAAA"}
1
+ {"version":3,"file":"ContentSelectorWrapper.d.ts","names":[],"sources":["../../../src/components/ContentSelectorWrapper.ts"],"mappings":";cAkBM,YAAA;EAAA,QAG2C,WAAA;aAAA,WAAA;AAAA;;;;;;;AAgBjD;;;;;;;;cAAa,qCAAA,SAA8C,YAAY;EAAA,QAC7D,YAAA;EAAA,QACA,cAAA;EAAA,QACA,cAAA;EAAA,QACA,WAAA;EAAA,QACA,WAAA;EAAA,QACA,YAAA;EAAA,QAEA,YAAA;EAAA,QACA,SAAA;EAAA,QAEA,aAAA;EAAA,QACA,aAAA;EAAA,QACA,aAAA;EAAA,QACA,mBAAA;EAAA,WAEG,kBAAA,CAAA;EAAA,IAIP,WAAA,CAAA;EAAA,IAGA,WAAA,CAAY,CAAA;EAAA,IAMZ,aAAA,CAAA;EAAA,IAGA,aAAA,CAAc,CAAA;;EAclB,wBAAA,CACE,IAAA,UACA,OAAA,iBACA,MAAA;EAaF,iBAAA,CAAA;EAQA,oBAAA,CAAA;EAAA,QAIQ,SAAA;EAAA,QAWA,cAAA;EAAA,QAQA,mBAAA;EAAA,QAMA,kBAAA;EAAA,QAkDA,iBAAA;EAAA,QAwBA,uBAAA;EAAA,QA6DA,mBAAA;EAAA,QASA,mBAAA;EAAA,QAwBA,0BAAA;EAAA,QA+BA,YAAA;EAAA,QAWA,YAAA;EAAA,QAWA,cAAA;EAAA,QAQA,OAAA;EAAA,QA+BA,eAAA;AAAA;AAAA,cAkCG,oCAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intlayer/editor",
3
- "version": "8.10.1",
3
+ "version": "8.11.0",
4
4
  "private": false,
5
5
  "description": "Provides the utilities to interface the application with the Intlayer editor and manipulate dictionaries",
6
6
  "keywords": [
@@ -75,10 +75,10 @@
75
75
  "typecheck": "tsc --noEmit --project tsconfig.types.json"
76
76
  },
77
77
  "dependencies": {
78
- "@intlayer/config": "8.10.1",
79
- "@intlayer/core": "8.10.1",
80
- "@intlayer/types": "8.10.1",
81
- "@intlayer/unmerged-dictionaries-entry": "8.10.1"
78
+ "@intlayer/config": "8.11.0",
79
+ "@intlayer/core": "8.11.0",
80
+ "@intlayer/types": "8.11.0",
81
+ "@intlayer/unmerged-dictionaries-entry": "8.11.0"
82
82
  },
83
83
  "devDependencies": {
84
84
  "@types/node": "25.9.1",
@@ -88,7 +88,7 @@
88
88
  "rimraf": "6.1.3",
89
89
  "tsdown": "0.22.00",
90
90
  "typescript": "6.0.3",
91
- "vitest": "4.1.6"
91
+ "vitest": "4.1.7"
92
92
  },
93
93
  "engines": {
94
94
  "node": ">=14.18"