@intlayer/editor 8.4.3 → 8.4.5
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.
- package/dist/cjs/_virtual/_@oxc-project_runtime@0.115.0/helpers/decorate.cjs +11 -0
- package/dist/cjs/_virtual/_rolldown/runtime.cjs +29 -0
- package/dist/cjs/compareUrls.cjs +39 -1
- package/dist/cjs/compareUrls.cjs.map +1 -1
- package/dist/cjs/components/ContentSelector.cjs +168 -1
- package/dist/cjs/components/ContentSelector.cjs.map +1 -0
- package/dist/cjs/components/ContentSelectorWrapper.cjs +193 -4
- package/dist/cjs/components/ContentSelectorWrapper.cjs.map +1 -1
- package/dist/cjs/components/EditedContent.cjs +128 -4
- package/dist/cjs/components/EditedContent.cjs.map +1 -1
- package/dist/cjs/components/IntlayerEditor.cjs +87 -1
- package/dist/cjs/components/IntlayerEditor.cjs.map +1 -0
- package/dist/cjs/components/index.cjs +14 -1
- package/dist/cjs/core/CrossFrameMessenger.cjs +77 -1
- package/dist/cjs/core/CrossFrameMessenger.cjs.map +1 -1
- package/dist/cjs/core/CrossFrameStateManager.cjs +71 -1
- package/dist/cjs/core/CrossFrameStateManager.cjs.map +1 -1
- package/dist/cjs/core/EditorStateManager.cjs +258 -1
- package/dist/cjs/core/EditorStateManager.cjs.map +1 -1
- package/dist/cjs/core/IframeClickInterceptor.cjs +45 -1
- package/dist/cjs/core/IframeClickInterceptor.cjs.map +1 -1
- package/dist/cjs/core/UrlStateManager.cjs +59 -1
- package/dist/cjs/core/UrlStateManager.cjs.map +1 -1
- package/dist/cjs/core/globalManager.cjs +82 -1
- package/dist/cjs/core/globalManager.cjs.map +1 -1
- package/dist/cjs/core/index.cjs +20 -1
- package/dist/cjs/core/initEditorClient.cjs +58 -1
- package/dist/cjs/core/initEditorClient.cjs.map +1 -0
- package/dist/cjs/index.cjs +38 -1
- package/dist/cjs/isEnabled.cjs +10 -1
- package/dist/cjs/isEnabled.cjs.map +1 -1
- package/dist/cjs/mergeIframeClick.cjs +22 -1
- package/dist/cjs/mergeIframeClick.cjs.map +1 -1
- package/dist/cjs/messageKey.cjs +30 -1
- package/dist/cjs/messageKey.cjs.map +1 -1
- package/dist/esm/_virtual/_@oxc-project_runtime@0.115.0/helpers/decorate.mjs +10 -0
- package/dist/esm/compareUrls.mjs +37 -1
- package/dist/esm/compareUrls.mjs.map +1 -1
- package/dist/esm/components/ContentSelector.mjs +165 -1
- package/dist/esm/components/ContentSelector.mjs.map +1 -0
- package/dist/esm/components/ContentSelectorWrapper.mjs +189 -4
- package/dist/esm/components/ContentSelectorWrapper.mjs.map +1 -1
- package/dist/esm/components/EditedContent.mjs +125 -4
- package/dist/esm/components/EditedContent.mjs.map +1 -1
- package/dist/esm/components/IntlayerEditor.mjs +84 -1
- package/dist/esm/components/IntlayerEditor.mjs.map +1 -0
- package/dist/esm/components/index.mjs +6 -1
- package/dist/esm/core/CrossFrameMessenger.mjs +76 -1
- package/dist/esm/core/CrossFrameMessenger.mjs.map +1 -1
- package/dist/esm/core/CrossFrameStateManager.mjs +69 -1
- package/dist/esm/core/CrossFrameStateManager.mjs.map +1 -1
- package/dist/esm/core/EditorStateManager.mjs +255 -1
- package/dist/esm/core/EditorStateManager.mjs.map +1 -1
- package/dist/esm/core/IframeClickInterceptor.mjs +44 -1
- package/dist/esm/core/IframeClickInterceptor.mjs.map +1 -1
- package/dist/esm/core/UrlStateManager.mjs +58 -1
- package/dist/esm/core/UrlStateManager.mjs.map +1 -1
- package/dist/esm/core/globalManager.mjs +78 -1
- package/dist/esm/core/globalManager.mjs.map +1 -1
- package/dist/esm/core/index.mjs +9 -1
- package/dist/esm/core/initEditorClient.mjs +53 -1
- package/dist/esm/core/initEditorClient.mjs.map +1 -0
- package/dist/esm/index.mjs +16 -1
- package/dist/esm/isEnabled.mjs +7 -1
- package/dist/esm/isEnabled.mjs.map +1 -1
- package/dist/esm/mergeIframeClick.mjs +20 -1
- package/dist/esm/mergeIframeClick.mjs.map +1 -1
- package/dist/esm/messageKey.mjs +28 -1
- package/dist/esm/messageKey.mjs.map +1 -1
- package/dist/types/components/ContentSelector.d.ts +49 -2
- package/dist/types/components/ContentSelector.d.ts.map +1 -0
- package/dist/types/components/ContentSelectorWrapper.d.ts +49 -2
- package/dist/types/components/ContentSelectorWrapper.d.ts.map +1 -0
- package/dist/types/components/EditedContent.d.ts +39 -2
- package/dist/types/components/EditedContent.d.ts.map +1 -0
- package/dist/types/components/IntlayerEditor.d.ts +40 -2
- package/dist/types/components/IntlayerEditor.d.ts.map +1 -0
- package/dist/types/components/index.d.ts +4 -4
- package/dist/types/core/CrossFrameMessenger.d.ts +51 -2
- package/dist/types/core/CrossFrameMessenger.d.ts.map +1 -0
- package/dist/types/core/CrossFrameStateManager.d.ts +44 -2
- package/dist/types/core/CrossFrameStateManager.d.ts.map +1 -0
- package/dist/types/core/EditorStateManager.d.ts +73 -2
- package/dist/types/core/EditorStateManager.d.ts.map +1 -0
- package/dist/types/core/IframeClickInterceptor.d.ts +26 -2
- package/dist/types/core/IframeClickInterceptor.d.ts.map +1 -0
- package/dist/types/core/UrlStateManager.d.ts +21 -2
- package/dist/types/core/UrlStateManager.d.ts.map +1 -0
- package/dist/types/core/globalManager.d.ts +39 -2
- package/dist/types/core/globalManager.d.ts.map +1 -0
- package/dist/types/core/index.d.ts +7 -7
- package/dist/types/core/initEditorClient.d.ts +20 -2
- package/dist/types/core/initEditorClient.d.ts.map +1 -0
- package/dist/types/index.d.ts +11 -11
- package/package.json +5 -5
- package/dist/cjs/chunk-Bmb41Sf3.cjs +0 -1
- package/dist/cjs/components-DWu35JEb.cjs +0 -41
- package/dist/cjs/components-DWu35JEb.cjs.map +0 -1
- package/dist/cjs/decorate-Bg73f0d3.cjs +0 -1
- package/dist/esm/components-RtOXxg9h.mjs +0 -41
- package/dist/esm/components-RtOXxg9h.mjs.map +0 -1
- package/dist/esm/decorate-BWURH4oJ.mjs +0 -1
- package/dist/types/ContentSelector-sIfZu4Dd.d.ts +0 -49
- package/dist/types/ContentSelector-sIfZu4Dd.d.ts.map +0 -1
- package/dist/types/ContentSelectorWrapper-CID6anMf.d.ts +0 -49
- package/dist/types/ContentSelectorWrapper-CID6anMf.d.ts.map +0 -1
- package/dist/types/CrossFrameMessenger-CPt3Bu8S.d.ts +0 -51
- package/dist/types/CrossFrameMessenger-CPt3Bu8S.d.ts.map +0 -1
- package/dist/types/CrossFrameStateManager-CW1DPY_Z.d.ts +0 -44
- package/dist/types/CrossFrameStateManager-CW1DPY_Z.d.ts.map +0 -1
- package/dist/types/EditedContent-2kq4wk4R.d.ts +0 -39
- package/dist/types/EditedContent-2kq4wk4R.d.ts.map +0 -1
- package/dist/types/EditorStateManager-Y9j0SYCd.d.ts +0 -73
- package/dist/types/EditorStateManager-Y9j0SYCd.d.ts.map +0 -1
- package/dist/types/IframeClickInterceptor-Cm89LRcI.d.ts +0 -26
- package/dist/types/IframeClickInterceptor-Cm89LRcI.d.ts.map +0 -1
- package/dist/types/IntlayerEditor-ePRSIuBI.d.ts +0 -40
- package/dist/types/IntlayerEditor-ePRSIuBI.d.ts.map +0 -1
- package/dist/types/UrlStateManager-CIOVEeTq.d.ts +0 -21
- package/dist/types/UrlStateManager-CIOVEeTq.d.ts.map +0 -1
- package/dist/types/globalManager-BD6UaK_1.d.ts +0 -39
- package/dist/types/globalManager-BD6UaK_1.d.ts.map +0 -1
- package/dist/types/initEditorClient-ovRVUf_n.d.ts +0 -20
- package/dist/types/initEditorClient-ovRVUf_n.d.ts.map +0 -1
|
@@ -1,2 +1,259 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
|
|
3
|
+
const require_messageKey = require('../messageKey.cjs');
|
|
4
|
+
const require_core_CrossFrameMessenger = require('./CrossFrameMessenger.cjs');
|
|
5
|
+
const require_core_CrossFrameStateManager = require('./CrossFrameStateManager.cjs');
|
|
6
|
+
const require_core_IframeClickInterceptor = require('./IframeClickInterceptor.cjs');
|
|
7
|
+
const require_core_UrlStateManager = require('./UrlStateManager.cjs');
|
|
8
|
+
let _intlayer_types_nodeType = require("@intlayer/types/nodeType");
|
|
9
|
+
_intlayer_types_nodeType = require_runtime.__toESM(_intlayer_types_nodeType);
|
|
10
|
+
let _intlayer_core_dictionaryManipulator = require("@intlayer/core/dictionaryManipulator");
|
|
11
|
+
|
|
12
|
+
//#region src/core/EditorStateManager.ts
|
|
13
|
+
/**
|
|
14
|
+
* EditorStateManager is the single entry point for all Intlayer editor state.
|
|
15
|
+
* It is framework-agnostic: instantiate one instance at the root of the application
|
|
16
|
+
* and subscribe to its EventTarget-based events from any framework adapter.
|
|
17
|
+
*
|
|
18
|
+
* Replaces all context providers, hooks and store files across React, Preact,
|
|
19
|
+
* Solid, Svelte, and Vue integrations.
|
|
20
|
+
*/
|
|
21
|
+
var EditorStateManager = class {
|
|
22
|
+
constructor(config) {
|
|
23
|
+
this._unsubAreYouThere = null;
|
|
24
|
+
this._unsubActivate = null;
|
|
25
|
+
this._unsubClientReady = null;
|
|
26
|
+
this._mode = config.mode;
|
|
27
|
+
this._configuration = config.configuration;
|
|
28
|
+
this.messenger = new require_core_CrossFrameMessenger.CrossFrameMessenger(config.messenger);
|
|
29
|
+
this.editorEnabled = new require_core_CrossFrameStateManager.CrossFrameStateManager(require_messageKey.MessageKey.INTLAYER_EDITOR_ENABLED, this.messenger, {
|
|
30
|
+
emit: false,
|
|
31
|
+
receive: true,
|
|
32
|
+
initialValue: false
|
|
33
|
+
});
|
|
34
|
+
this.focusedContent = new require_core_CrossFrameStateManager.CrossFrameStateManager(require_messageKey.MessageKey.INTLAYER_FOCUSED_CONTENT_CHANGED, this.messenger, {
|
|
35
|
+
emit: true,
|
|
36
|
+
receive: true,
|
|
37
|
+
initialValue: null
|
|
38
|
+
});
|
|
39
|
+
this.localeDictionaries = new require_core_CrossFrameStateManager.CrossFrameStateManager(require_messageKey.MessageKey.INTLAYER_LOCALE_DICTIONARIES_CHANGED, this.messenger);
|
|
40
|
+
this.editedContent = new require_core_CrossFrameStateManager.CrossFrameStateManager(require_messageKey.MessageKey.INTLAYER_EDITED_CONTENT_CHANGED, this.messenger);
|
|
41
|
+
this.configuration = new require_core_CrossFrameStateManager.CrossFrameStateManager(require_messageKey.MessageKey.INTLAYER_CONFIGURATION, this.messenger, {
|
|
42
|
+
emit: true,
|
|
43
|
+
receive: false,
|
|
44
|
+
...config.configuration ? { initialValue: config.configuration } : {}
|
|
45
|
+
});
|
|
46
|
+
this.currentLocale = new require_core_CrossFrameStateManager.CrossFrameStateManager(require_messageKey.MessageKey.INTLAYER_CURRENT_LOCALE, this.messenger, {
|
|
47
|
+
emit: config.mode === "client",
|
|
48
|
+
receive: config.mode === "editor"
|
|
49
|
+
});
|
|
50
|
+
this._urlManager = new require_core_UrlStateManager.UrlStateManager(this.messenger);
|
|
51
|
+
this._iframeInterceptor = new require_core_IframeClickInterceptor.IframeClickInterceptor(this.messenger);
|
|
52
|
+
}
|
|
53
|
+
start() {
|
|
54
|
+
this.messenger.start();
|
|
55
|
+
this.editorEnabled.start();
|
|
56
|
+
this.focusedContent.start();
|
|
57
|
+
this.localeDictionaries.start();
|
|
58
|
+
this.editedContent.start();
|
|
59
|
+
this.configuration.start();
|
|
60
|
+
this.currentLocale.start();
|
|
61
|
+
if (this._mode === "client") {
|
|
62
|
+
this._urlManager.start();
|
|
63
|
+
this._iframeInterceptor.startInterceptor();
|
|
64
|
+
this._loadDictionaries();
|
|
65
|
+
this.messenger.send(`${require_messageKey.MessageKey.INTLAYER_EDITED_CONTENT_CHANGED}/get`);
|
|
66
|
+
if (this._configuration?.editor?.enabled !== false) this._setupActivationHandshake();
|
|
67
|
+
} else {
|
|
68
|
+
this._iframeInterceptor.startMerger();
|
|
69
|
+
this._setupEditorHandshake();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
stop() {
|
|
73
|
+
this._unsubAreYouThere?.();
|
|
74
|
+
this._unsubActivate?.();
|
|
75
|
+
this._unsubClientReady?.();
|
|
76
|
+
this._unsubAreYouThere = null;
|
|
77
|
+
this._unsubActivate = null;
|
|
78
|
+
this._unsubClientReady = null;
|
|
79
|
+
this.messenger.stop();
|
|
80
|
+
this.editorEnabled.stop();
|
|
81
|
+
this.focusedContent.stop();
|
|
82
|
+
this.localeDictionaries.stop();
|
|
83
|
+
this.editedContent.stop();
|
|
84
|
+
this.configuration.stop();
|
|
85
|
+
this.currentLocale.stop();
|
|
86
|
+
this._urlManager.stop();
|
|
87
|
+
this._iframeInterceptor.stopInterceptor();
|
|
88
|
+
this._iframeInterceptor.stopMerger();
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* EDITOR mode: re-send ARE_YOU_THERE to attempt re-connection with the client.
|
|
92
|
+
* Call this when the user clicks "Enable Editor" or when the iframe reloads.
|
|
93
|
+
*/
|
|
94
|
+
pingClient() {
|
|
95
|
+
if (this._mode !== "editor") return;
|
|
96
|
+
this.messenger.send(require_messageKey.MessageKey.INTLAYER_ARE_YOU_THERE);
|
|
97
|
+
}
|
|
98
|
+
setFocusedContentKeyPath(keyPath) {
|
|
99
|
+
const filtered = keyPath.filter((key) => key.type !== _intlayer_types_nodeType.TRANSLATION);
|
|
100
|
+
const prev = this.focusedContent.value;
|
|
101
|
+
if (!prev) return;
|
|
102
|
+
this.focusedContent.set({
|
|
103
|
+
...prev,
|
|
104
|
+
keyPath: filtered
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
setLocaleDictionary(dictionary) {
|
|
108
|
+
if (!dictionary.localId) return;
|
|
109
|
+
const current = this.localeDictionaries.value ?? {};
|
|
110
|
+
this.localeDictionaries.set({
|
|
111
|
+
...current,
|
|
112
|
+
[dictionary.localId]: dictionary
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
setEditedDictionary(newDict) {
|
|
116
|
+
if (!newDict.localId) {
|
|
117
|
+
console.error("setEditedDictionary: missing localId", newDict);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const current = this.editedContent.value ?? {};
|
|
121
|
+
this.editedContent.set({
|
|
122
|
+
...current,
|
|
123
|
+
[newDict.localId]: newDict
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
setEditedContent(localDictionaryId, newValue) {
|
|
127
|
+
const current = this.editedContent.value ?? {};
|
|
128
|
+
this.editedContent.set({
|
|
129
|
+
...current,
|
|
130
|
+
[localDictionaryId]: {
|
|
131
|
+
...current[localDictionaryId],
|
|
132
|
+
content: newValue
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
addContent(localDictionaryId, newValue, keyPath = [], overwrite = true) {
|
|
137
|
+
const current = this.editedContent.value ?? {};
|
|
138
|
+
const originalContent = (this.localeDictionaries.value ?? {})[localDictionaryId]?.content;
|
|
139
|
+
const currentContent = structuredClone(current[localDictionaryId]?.content ?? originalContent);
|
|
140
|
+
let newKeyPath = keyPath;
|
|
141
|
+
if (!overwrite) {
|
|
142
|
+
let index = 0;
|
|
143
|
+
const otherKeyPath = keyPath.slice(0, -1);
|
|
144
|
+
const lastKeyPath = keyPath[keyPath.length - 1];
|
|
145
|
+
let finalKey = lastKeyPath.key;
|
|
146
|
+
while (typeof (0, _intlayer_core_dictionaryManipulator.getContentNodeByKeyPath)(currentContent, newKeyPath) !== "undefined") {
|
|
147
|
+
index++;
|
|
148
|
+
finalKey = index === 0 ? lastKeyPath.key : `${lastKeyPath.key} (${index})`;
|
|
149
|
+
newKeyPath = [...otherKeyPath, {
|
|
150
|
+
...lastKeyPath,
|
|
151
|
+
key: finalKey
|
|
152
|
+
}];
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const updatedContent = (0, _intlayer_core_dictionaryManipulator.editDictionaryByKeyPath)(currentContent, newKeyPath, newValue);
|
|
156
|
+
this.editedContent.set({
|
|
157
|
+
...current,
|
|
158
|
+
[localDictionaryId]: {
|
|
159
|
+
...current[localDictionaryId],
|
|
160
|
+
content: updatedContent
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
renameContent(localDictionaryId, newKey, keyPath = []) {
|
|
165
|
+
const current = this.editedContent.value ?? {};
|
|
166
|
+
const originalContent = (this.localeDictionaries.value ?? {})[localDictionaryId]?.content;
|
|
167
|
+
const updated = (0, _intlayer_core_dictionaryManipulator.renameContentNodeByKeyPath)(structuredClone(current[localDictionaryId]?.content ?? originalContent), newKey, keyPath);
|
|
168
|
+
this.editedContent.set({
|
|
169
|
+
...current,
|
|
170
|
+
[localDictionaryId]: {
|
|
171
|
+
...current[localDictionaryId],
|
|
172
|
+
content: updated
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
removeContent(localDictionaryId, keyPath) {
|
|
177
|
+
const current = this.editedContent.value ?? {};
|
|
178
|
+
const originalContent = (this.localeDictionaries.value ?? {})[localDictionaryId]?.content;
|
|
179
|
+
const restored = (0, _intlayer_core_dictionaryManipulator.editDictionaryByKeyPath)(structuredClone(current[localDictionaryId]?.content ?? originalContent), keyPath, (0, _intlayer_core_dictionaryManipulator.getContentNodeByKeyPath)(originalContent, keyPath));
|
|
180
|
+
this.editedContent.set({
|
|
181
|
+
...current,
|
|
182
|
+
[localDictionaryId]: {
|
|
183
|
+
...current[localDictionaryId],
|
|
184
|
+
content: restored
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
restoreContent(localDictionaryId) {
|
|
189
|
+
const updated = { ...this.editedContent.value ?? {} };
|
|
190
|
+
delete updated[localDictionaryId];
|
|
191
|
+
this.editedContent.set(updated);
|
|
192
|
+
}
|
|
193
|
+
clearContent(localDictionaryId) {
|
|
194
|
+
const filtered = { ...this.editedContent.value ?? {} };
|
|
195
|
+
delete filtered[localDictionaryId];
|
|
196
|
+
this.editedContent.set(filtered);
|
|
197
|
+
}
|
|
198
|
+
clearAllContent() {
|
|
199
|
+
this.editedContent.set({});
|
|
200
|
+
}
|
|
201
|
+
getContentValue(localDictionaryIdOrKey, keyPath) {
|
|
202
|
+
const edited = this.editedContent.value;
|
|
203
|
+
if (!edited) return void 0;
|
|
204
|
+
const filteredKeyPath = keyPath.filter((key) => key.type !== _intlayer_types_nodeType.TRANSLATION);
|
|
205
|
+
const localeDicts = this.localeDictionaries.value;
|
|
206
|
+
if (localDictionaryIdOrKey.includes(":local:") || localDictionaryIdOrKey.includes(":remote:")) {
|
|
207
|
+
if (localeDicts && !(localDictionaryIdOrKey in localeDicts)) return;
|
|
208
|
+
return (0, _intlayer_core_dictionaryManipulator.getContentNodeByKeyPath)(edited[localDictionaryIdOrKey]?.content ?? {}, filteredKeyPath, this.currentLocale.value);
|
|
209
|
+
}
|
|
210
|
+
const matchingIds = Object.keys(edited).filter((key) => key.startsWith(`${localDictionaryIdOrKey}:`) && (!localeDicts || key in localeDicts));
|
|
211
|
+
for (const localId of matchingIds) {
|
|
212
|
+
const node = (0, _intlayer_core_dictionaryManipulator.getContentNodeByKeyPath)(edited[localId]?.content ?? {}, filteredKeyPath, this.currentLocale.value);
|
|
213
|
+
if (node) return node;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* EDITOR mode: listen for CLIENT_READY and respond with EDITOR_ACTIVATE.
|
|
218
|
+
* Also pings the client immediately in case it loaded before the editor.
|
|
219
|
+
*/
|
|
220
|
+
_setupEditorHandshake() {
|
|
221
|
+
this._unsubClientReady = this.messenger.subscribe(require_messageKey.MessageKey.INTLAYER_CLIENT_READY, () => {
|
|
222
|
+
this.editorEnabled.set(true);
|
|
223
|
+
this.messenger.send(require_messageKey.MessageKey.INTLAYER_EDITOR_ACTIVATE);
|
|
224
|
+
});
|
|
225
|
+
this.messenger.send(require_messageKey.MessageKey.INTLAYER_ARE_YOU_THERE);
|
|
226
|
+
}
|
|
227
|
+
_setupActivationHandshake() {
|
|
228
|
+
this.messenger.send(require_messageKey.MessageKey.INTLAYER_CLIENT_READY);
|
|
229
|
+
this._unsubAreYouThere = this.messenger.subscribe(require_messageKey.MessageKey.INTLAYER_ARE_YOU_THERE, () => {
|
|
230
|
+
this.messenger.send(require_messageKey.MessageKey.INTLAYER_CLIENT_READY);
|
|
231
|
+
});
|
|
232
|
+
this._unsubActivate = this.messenger.subscribe(require_messageKey.MessageKey.INTLAYER_EDITOR_ACTIVATE, () => {
|
|
233
|
+
this.editorEnabled.set(true);
|
|
234
|
+
this._broadcastData();
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
_broadcastData() {
|
|
238
|
+
const configVal = this.configuration.value;
|
|
239
|
+
if (configVal) this.messenger.send(`${require_messageKey.MessageKey.INTLAYER_CONFIGURATION}/post`, configVal);
|
|
240
|
+
const localeVal = this.currentLocale.value;
|
|
241
|
+
if (localeVal) this.messenger.send(`${require_messageKey.MessageKey.INTLAYER_CURRENT_LOCALE}/post`, localeVal);
|
|
242
|
+
const dicts = this.localeDictionaries.value;
|
|
243
|
+
if (dicts) this.messenger.send(`${require_messageKey.MessageKey.INTLAYER_LOCALE_DICTIONARIES_CHANGED}/post`, dicts);
|
|
244
|
+
}
|
|
245
|
+
async _loadDictionaries() {
|
|
246
|
+
try {
|
|
247
|
+
const unmergedDictionaries = (await import("@intlayer/unmerged-dictionaries-entry")).getUnmergedDictionaries();
|
|
248
|
+
const dictionariesList = Object.fromEntries(Object.values(unmergedDictionaries).flat().map((dictionary) => [dictionary.localId, dictionary]));
|
|
249
|
+
this.localeDictionaries.set(dictionariesList);
|
|
250
|
+
if (this.editorEnabled.value) this._broadcastData();
|
|
251
|
+
} catch (e) {
|
|
252
|
+
console.warn("[intlayer] Failed to load unmerged dictionaries:", e);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
//#endregion
|
|
258
|
+
exports.EditorStateManager = EditorStateManager;
|
|
2
259
|
//# sourceMappingURL=EditorStateManager.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditorStateManager.cjs","names":["CrossFrameMessenger","CrossFrameStateManager","MessageKey","UrlStateManager","IframeClickInterceptor","NodeType"],"sources":["../../../src/core/EditorStateManager.ts"],"sourcesContent":["import {\n editDictionaryByKeyPath,\n getContentNodeByKeyPath,\n renameContentNodeByKeyPath,\n} from '@intlayer/core/dictionaryManipulator';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type {\n ContentNode,\n Dictionary,\n LocalDictionaryId,\n} from '@intlayer/types/dictionary';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport { NodeType } from '@intlayer/types/nodeType';\nimport { MessageKey } from '../messageKey';\nimport {\n CrossFrameMessenger,\n type MessengerConfig,\n} from './CrossFrameMessenger';\nimport { CrossFrameStateManager } from './CrossFrameStateManager';\nimport { IframeClickInterceptor } from './IframeClickInterceptor';\nimport { UrlStateManager } from './UrlStateManager';\n\nexport type DictionaryContent = Record<LocalDictionaryId, Dictionary>;\n\nexport type FileContent = {\n dictionaryKey: string;\n dictionaryLocalId?: LocalDictionaryId;\n keyPath?: KeyPath[];\n};\n\nexport type EditorStateManagerConfig = {\n /** 'client' = the app running inside the iframe; 'editor' = the editor wrapping the app */\n mode: 'editor' | 'client';\n /** Cross-frame messaging configuration */\n messenger: MessengerConfig;\n /** Optional initial Intlayer configuration to broadcast */\n configuration?: IntlayerConfig;\n};\n\n/**\n * EditorStateManager is the single entry point for all Intlayer editor state.\n * It is framework-agnostic: instantiate one instance at the root of the application\n * and subscribe to its EventTarget-based events from any framework adapter.\n *\n * Replaces all context providers, hooks and store files across React, Preact,\n * Solid, Svelte, and Vue integrations.\n */\nexport class EditorStateManager {\n readonly messenger: CrossFrameMessenger;\n readonly editorEnabled: CrossFrameStateManager<boolean>;\n readonly focusedContent: CrossFrameStateManager<FileContent | null>;\n readonly localeDictionaries: CrossFrameStateManager<DictionaryContent>;\n readonly editedContent: CrossFrameStateManager<DictionaryContent>;\n readonly configuration: CrossFrameStateManager<IntlayerConfig>;\n readonly currentLocale: CrossFrameStateManager<Locale | undefined>;\n\n private readonly _urlManager: UrlStateManager;\n private readonly _iframeInterceptor: IframeClickInterceptor;\n private readonly _mode: 'editor' | 'client';\n private readonly _configuration: IntlayerConfig | undefined;\n\n // Client-mode handshake subscribers\n private _unsubAreYouThere: (() => void) | null = null;\n private _unsubActivate: (() => void) | null = null;\n // Editor-mode handshake subscriber\n private _unsubClientReady: (() => void) | null = null;\n\n constructor(config: EditorStateManagerConfig) {\n this._mode = config.mode;\n this._configuration = config.configuration;\n\n this.messenger = new CrossFrameMessenger(config.messenger);\n\n this.editorEnabled = new CrossFrameStateManager<boolean>(\n MessageKey.INTLAYER_EDITOR_ENABLED,\n this.messenger,\n { emit: false, receive: true, initialValue: false }\n );\n\n this.focusedContent = new CrossFrameStateManager<FileContent | null>(\n MessageKey.INTLAYER_FOCUSED_CONTENT_CHANGED,\n this.messenger,\n { emit: true, receive: true, initialValue: null }\n );\n\n this.localeDictionaries = new CrossFrameStateManager<DictionaryContent>(\n MessageKey.INTLAYER_LOCALE_DICTIONARIES_CHANGED,\n this.messenger\n );\n\n this.editedContent = new CrossFrameStateManager<DictionaryContent>(\n MessageKey.INTLAYER_EDITED_CONTENT_CHANGED,\n this.messenger\n );\n\n this.configuration = new CrossFrameStateManager<IntlayerConfig>(\n MessageKey.INTLAYER_CONFIGURATION,\n this.messenger,\n {\n emit: true,\n receive: false,\n ...(config.configuration ? { initialValue: config.configuration } : {}),\n }\n );\n\n // Client emits its locale to the editor; editor receives it.\n this.currentLocale = new CrossFrameStateManager<Locale>(\n MessageKey.INTLAYER_CURRENT_LOCALE,\n this.messenger,\n {\n emit: config.mode === 'client',\n receive: config.mode === 'editor',\n }\n );\n\n this._urlManager = new UrlStateManager(this.messenger);\n this._iframeInterceptor = new IframeClickInterceptor(this.messenger);\n }\n\n start(): void {\n this.messenger.start();\n this.editorEnabled.start();\n this.focusedContent.start();\n this.localeDictionaries.start();\n this.editedContent.start();\n this.configuration.start();\n this.currentLocale.start();\n\n if (this._mode === 'client') {\n this._urlManager.start();\n this._iframeInterceptor.startInterceptor();\n this._loadDictionaries();\n // Request current edited content from the editor\n this.messenger.send(`${MessageKey.INTLAYER_EDITED_CONTENT_CHANGED}/get`);\n // Activation handshake: only participate if editor.enabled !== false\n if (this._configuration?.editor?.enabled !== false) {\n this._setupActivationHandshake();\n }\n } else {\n this._iframeInterceptor.startMerger();\n this._setupEditorHandshake();\n }\n }\n\n stop(): void {\n this._unsubAreYouThere?.();\n this._unsubActivate?.();\n this._unsubClientReady?.();\n this._unsubAreYouThere = null;\n this._unsubActivate = null;\n this._unsubClientReady = null;\n this.messenger.stop();\n this.editorEnabled.stop();\n this.focusedContent.stop();\n this.localeDictionaries.stop();\n this.editedContent.stop();\n this.configuration.stop();\n this.currentLocale.stop();\n this._urlManager.stop();\n this._iframeInterceptor.stopInterceptor();\n this._iframeInterceptor.stopMerger();\n }\n\n // ─── Handshake helpers ───────────────────────────────────────────────────────\n\n /**\n * EDITOR mode: re-send ARE_YOU_THERE to attempt re-connection with the client.\n * Call this when the user clicks \"Enable Editor\" or when the iframe reloads.\n */\n pingClient(): void {\n if (this._mode !== 'editor') return;\n\n this.messenger.send(MessageKey.INTLAYER_ARE_YOU_THERE);\n }\n\n // ─── Focus helpers ──────────────────────────────────────────────────────────\n\n setFocusedContentKeyPath(keyPath: KeyPath[]): void {\n const filtered = keyPath.filter((key) => key.type !== NodeType.Translation);\n const prev = this.focusedContent.value;\n\n if (!prev) return;\n\n this.focusedContent.set({ ...prev, keyPath: filtered });\n }\n\n // ─── Dictionary record helpers ───────────────────────────────────────────\n\n setLocaleDictionary(dictionary: Dictionary): void {\n if (!dictionary.localId) return;\n const current = this.localeDictionaries.value ?? {};\n\n this.localeDictionaries.set({\n ...current,\n [dictionary.localId as LocalDictionaryId]: dictionary,\n });\n }\n\n // ─── Edited content helpers ───────────────────────────────────────────────\n\n setEditedDictionary(newDict: Dictionary): void {\n if (!newDict.localId) {\n console.error('setEditedDictionary: missing localId', newDict);\n return;\n }\n const current = this.editedContent.value ?? {};\n\n this.editedContent.set({\n ...current,\n [newDict.localId as LocalDictionaryId]: newDict,\n });\n }\n\n setEditedContent(\n localDictionaryId: LocalDictionaryId,\n newValue: Dictionary['content']\n ): void {\n const current = this.editedContent.value ?? {};\n\n this.editedContent.set({\n ...current,\n [localDictionaryId]: {\n ...current[localDictionaryId],\n content: newValue,\n },\n });\n }\n\n addContent(\n localDictionaryId: LocalDictionaryId,\n newValue: ContentNode,\n keyPath: KeyPath[] = [],\n overwrite = true\n ): void {\n const current = this.editedContent.value ?? {};\n const localeDicts = this.localeDictionaries.value ?? {};\n\n const originalContent = localeDicts[localDictionaryId]?.content;\n const currentContent = structuredClone(\n current[localDictionaryId]?.content ?? originalContent\n );\n\n let newKeyPath = keyPath;\n if (!overwrite) {\n let index = 0;\n\n const otherKeyPath = keyPath.slice(0, -1);\n const lastKeyPath = keyPath[keyPath.length - 1];\n\n let finalKey = lastKeyPath.key;\n\n while (\n typeof getContentNodeByKeyPath(currentContent, newKeyPath) !==\n 'undefined'\n ) {\n index++;\n finalKey =\n index === 0 ? lastKeyPath.key : `${lastKeyPath.key} (${index})`;\n newKeyPath = [\n ...otherKeyPath,\n { ...lastKeyPath, key: finalKey } as KeyPath,\n ];\n }\n }\n\n const updatedContent = editDictionaryByKeyPath(\n currentContent,\n newKeyPath,\n newValue\n );\n\n this.editedContent.set({\n ...current,\n [localDictionaryId]: {\n ...current[localDictionaryId],\n content: updatedContent as Dictionary['content'],\n },\n });\n }\n\n renameContent(\n localDictionaryId: LocalDictionaryId,\n newKey: KeyPath['key'],\n keyPath: KeyPath[] = []\n ): void {\n const current = this.editedContent.value ?? {};\n const localeDicts = this.localeDictionaries.value ?? {};\n const originalContent = localeDicts[localDictionaryId]?.content;\n const currentContent = structuredClone(\n current[localDictionaryId]?.content ?? originalContent\n );\n const updated = renameContentNodeByKeyPath(currentContent, newKey, keyPath);\n\n this.editedContent.set({\n ...current,\n [localDictionaryId]: {\n ...current[localDictionaryId],\n content: updated as Dictionary['content'],\n },\n });\n }\n\n removeContent(\n localDictionaryId: LocalDictionaryId,\n keyPath: KeyPath[]\n ): void {\n const current = this.editedContent.value ?? {};\n const localeDicts = this.localeDictionaries.value ?? {};\n const originalContent = localeDicts[localDictionaryId]?.content;\n const currentContent = structuredClone(\n current[localDictionaryId]?.content ?? originalContent\n );\n const initialContent = getContentNodeByKeyPath(originalContent, keyPath);\n const restored = editDictionaryByKeyPath(\n currentContent,\n keyPath,\n initialContent\n );\n\n this.editedContent.set({\n ...current,\n [localDictionaryId]: {\n ...current[localDictionaryId],\n content: restored as Dictionary['content'],\n },\n });\n }\n\n restoreContent(localDictionaryId: LocalDictionaryId): void {\n const current = this.editedContent.value ?? {};\n const updated = { ...current };\n\n delete updated[localDictionaryId];\n\n this.editedContent.set(updated);\n }\n\n clearContent(localDictionaryId: LocalDictionaryId): void {\n const current = this.editedContent.value ?? {};\n const filtered = { ...current };\n\n delete filtered[localDictionaryId];\n\n this.editedContent.set(filtered);\n }\n\n clearAllContent(): void {\n this.editedContent.set({});\n }\n\n getContentValue(\n localDictionaryIdOrKey: LocalDictionaryId | string,\n keyPath: KeyPath[]\n ): ContentNode | undefined {\n const edited = this.editedContent.value;\n if (!edited) return undefined;\n\n const filteredKeyPath = keyPath.filter(\n (key) => key.type !== NodeType.Translation\n );\n\n // Only use edited content entries whose localId is known to this client.\n // This prevents stale edits from other apps (different framework demos) from\n // being applied when the editor sends back its stored editedContent.\n const localeDicts = this.localeDictionaries.value;\n\n const isDictionaryId =\n localDictionaryIdOrKey.includes(':local:') ||\n localDictionaryIdOrKey.includes(':remote:');\n\n if (isDictionaryId) {\n // If localeDictionaries is loaded, verify this localId belongs to us\n if (localeDicts && !(localDictionaryIdOrKey in localeDicts)) {\n return undefined;\n }\n const content =\n edited[localDictionaryIdOrKey as LocalDictionaryId]?.content ?? {};\n\n return getContentNodeByKeyPath(\n content,\n filteredKeyPath,\n this.currentLocale.value\n );\n }\n\n const matchingIds = Object.keys(edited).filter(\n (key) =>\n key.startsWith(`${localDictionaryIdOrKey}:`) &&\n // If localeDictionaries is loaded, only include known localIds\n (!localeDicts || key in localeDicts)\n );\n\n for (const localId of matchingIds) {\n const content = edited[localId as LocalDictionaryId]?.content ?? {};\n const node = getContentNodeByKeyPath(\n content,\n filteredKeyPath,\n this.currentLocale.value\n );\n if (node) return node;\n }\n\n return undefined;\n }\n\n /**\n * EDITOR mode: listen for CLIENT_READY and respond with EDITOR_ACTIVATE.\n * Also pings the client immediately in case it loaded before the editor.\n */\n private _setupEditorHandshake(): void {\n // When the client announces it is ready, activate it\n this._unsubClientReady = this.messenger.subscribe(\n MessageKey.INTLAYER_CLIENT_READY,\n () => {\n this.editorEnabled.set(true);\n this.messenger.send(MessageKey.INTLAYER_EDITOR_ACTIVATE);\n }\n );\n\n // Ping any already-running client (covers editor-opens-after-client scenario)\n this.messenger.send(MessageKey.INTLAYER_ARE_YOU_THERE);\n }\n\n private _setupActivationHandshake(): void {\n // Announce to the editor that the client is ready\n this.messenger.send(MessageKey.INTLAYER_CLIENT_READY);\n\n // Respond to \"are you there?\" pings from the editor\n this._unsubAreYouThere = this.messenger.subscribe(\n MessageKey.INTLAYER_ARE_YOU_THERE,\n () => {\n this.messenger.send(MessageKey.INTLAYER_CLIENT_READY);\n }\n );\n\n // When the editor activates us, enable the selector and broadcast state\n this._unsubActivate = this.messenger.subscribe(\n MessageKey.INTLAYER_EDITOR_ACTIVATE,\n () => {\n this.editorEnabled.set(true);\n this._broadcastData();\n }\n );\n }\n\n private _broadcastData(): void {\n const configVal = this.configuration.value;\n\n if (configVal) {\n this.messenger.send(\n `${MessageKey.INTLAYER_CONFIGURATION}/post`,\n configVal\n );\n }\n const localeVal = this.currentLocale.value;\n\n if (localeVal) {\n this.messenger.send(\n `${MessageKey.INTLAYER_CURRENT_LOCALE}/post`,\n localeVal\n );\n }\n const dicts = this.localeDictionaries.value;\n\n if (dicts) {\n this.messenger.send(\n `${MessageKey.INTLAYER_LOCALE_DICTIONARIES_CHANGED}/post`,\n dicts\n );\n }\n }\n\n private async _loadDictionaries(): Promise<void> {\n try {\n const mod = await import('@intlayer/unmerged-dictionaries-entry');\n const unmergedDictionaries = mod.getUnmergedDictionaries();\n const dictionariesList = Object.fromEntries(\n Object.values(unmergedDictionaries)\n .flat()\n .map((dictionary) => [dictionary.localId, dictionary])\n ) as DictionaryContent;\n\n this.localeDictionaries.set(dictionariesList);\n\n // If the editor already activated us before dictionaries finished loading,\n // re-broadcast now so the editor receives the dictionaries.\n if (this.editorEnabled.value) {\n this._broadcastData();\n }\n } catch (e) {\n // Dynamic entry not available (expected in editor mode or when not configured)\n console.warn('[intlayer] Failed to load unmerged dictionaries:', e);\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EditorStateManager.cjs","names":["CrossFrameMessenger","CrossFrameStateManager","MessageKey","UrlStateManager","IframeClickInterceptor","NodeTypes"],"sources":["../../../src/core/EditorStateManager.ts"],"sourcesContent":["import {\n editDictionaryByKeyPath,\n getContentNodeByKeyPath,\n renameContentNodeByKeyPath,\n} from '@intlayer/core/dictionaryManipulator';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type {\n ContentNode,\n Dictionary,\n LocalDictionaryId,\n} from '@intlayer/types/dictionary';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport { MessageKey } from '../messageKey';\nimport {\n CrossFrameMessenger,\n type MessengerConfig,\n} from './CrossFrameMessenger';\nimport { CrossFrameStateManager } from './CrossFrameStateManager';\nimport { IframeClickInterceptor } from './IframeClickInterceptor';\nimport { UrlStateManager } from './UrlStateManager';\n\nexport type DictionaryContent = Record<LocalDictionaryId, Dictionary>;\n\nexport type FileContent = {\n dictionaryKey: string;\n dictionaryLocalId?: LocalDictionaryId;\n keyPath?: KeyPath[];\n};\n\nexport type EditorStateManagerConfig = {\n /** 'client' = the app running inside the iframe; 'editor' = the editor wrapping the app */\n mode: 'editor' | 'client';\n /** Cross-frame messaging configuration */\n messenger: MessengerConfig;\n /** Optional initial Intlayer configuration to broadcast */\n configuration?: IntlayerConfig;\n};\n\n/**\n * EditorStateManager is the single entry point for all Intlayer editor state.\n * It is framework-agnostic: instantiate one instance at the root of the application\n * and subscribe to its EventTarget-based events from any framework adapter.\n *\n * Replaces all context providers, hooks and store files across React, Preact,\n * Solid, Svelte, and Vue integrations.\n */\nexport class EditorStateManager {\n readonly messenger: CrossFrameMessenger;\n readonly editorEnabled: CrossFrameStateManager<boolean>;\n readonly focusedContent: CrossFrameStateManager<FileContent | null>;\n readonly localeDictionaries: CrossFrameStateManager<DictionaryContent>;\n readonly editedContent: CrossFrameStateManager<DictionaryContent>;\n readonly configuration: CrossFrameStateManager<IntlayerConfig>;\n readonly currentLocale: CrossFrameStateManager<Locale | undefined>;\n\n private readonly _urlManager: UrlStateManager;\n private readonly _iframeInterceptor: IframeClickInterceptor;\n private readonly _mode: 'editor' | 'client';\n private readonly _configuration: IntlayerConfig | undefined;\n\n // Client-mode handshake subscribers\n private _unsubAreYouThere: (() => void) | null = null;\n private _unsubActivate: (() => void) | null = null;\n // Editor-mode handshake subscriber\n private _unsubClientReady: (() => void) | null = null;\n\n constructor(config: EditorStateManagerConfig) {\n this._mode = config.mode;\n this._configuration = config.configuration;\n\n this.messenger = new CrossFrameMessenger(config.messenger);\n\n this.editorEnabled = new CrossFrameStateManager<boolean>(\n MessageKey.INTLAYER_EDITOR_ENABLED,\n this.messenger,\n { emit: false, receive: true, initialValue: false }\n );\n\n this.focusedContent = new CrossFrameStateManager<FileContent | null>(\n MessageKey.INTLAYER_FOCUSED_CONTENT_CHANGED,\n this.messenger,\n { emit: true, receive: true, initialValue: null }\n );\n\n this.localeDictionaries = new CrossFrameStateManager<DictionaryContent>(\n MessageKey.INTLAYER_LOCALE_DICTIONARIES_CHANGED,\n this.messenger\n );\n\n this.editedContent = new CrossFrameStateManager<DictionaryContent>(\n MessageKey.INTLAYER_EDITED_CONTENT_CHANGED,\n this.messenger\n );\n\n this.configuration = new CrossFrameStateManager<IntlayerConfig>(\n MessageKey.INTLAYER_CONFIGURATION,\n this.messenger,\n {\n emit: true,\n receive: false,\n ...(config.configuration ? { initialValue: config.configuration } : {}),\n }\n );\n\n // Client emits its locale to the editor; editor receives it.\n this.currentLocale = new CrossFrameStateManager<Locale>(\n MessageKey.INTLAYER_CURRENT_LOCALE,\n this.messenger,\n {\n emit: config.mode === 'client',\n receive: config.mode === 'editor',\n }\n );\n\n this._urlManager = new UrlStateManager(this.messenger);\n this._iframeInterceptor = new IframeClickInterceptor(this.messenger);\n }\n\n start(): void {\n this.messenger.start();\n this.editorEnabled.start();\n this.focusedContent.start();\n this.localeDictionaries.start();\n this.editedContent.start();\n this.configuration.start();\n this.currentLocale.start();\n\n if (this._mode === 'client') {\n this._urlManager.start();\n this._iframeInterceptor.startInterceptor();\n this._loadDictionaries();\n // Request current edited content from the editor\n this.messenger.send(`${MessageKey.INTLAYER_EDITED_CONTENT_CHANGED}/get`);\n // Activation handshake: only participate if editor.enabled !== false\n if (this._configuration?.editor?.enabled !== false) {\n this._setupActivationHandshake();\n }\n } else {\n this._iframeInterceptor.startMerger();\n this._setupEditorHandshake();\n }\n }\n\n stop(): void {\n this._unsubAreYouThere?.();\n this._unsubActivate?.();\n this._unsubClientReady?.();\n this._unsubAreYouThere = null;\n this._unsubActivate = null;\n this._unsubClientReady = null;\n this.messenger.stop();\n this.editorEnabled.stop();\n this.focusedContent.stop();\n this.localeDictionaries.stop();\n this.editedContent.stop();\n this.configuration.stop();\n this.currentLocale.stop();\n this._urlManager.stop();\n this._iframeInterceptor.stopInterceptor();\n this._iframeInterceptor.stopMerger();\n }\n\n // ─── Handshake helpers ───────────────────────────────────────────────────────\n\n /**\n * EDITOR mode: re-send ARE_YOU_THERE to attempt re-connection with the client.\n * Call this when the user clicks \"Enable Editor\" or when the iframe reloads.\n */\n pingClient(): void {\n if (this._mode !== 'editor') return;\n\n this.messenger.send(MessageKey.INTLAYER_ARE_YOU_THERE);\n }\n\n // ─── Focus helpers ──────────────────────────────────────────────────────────\n\n setFocusedContentKeyPath(keyPath: KeyPath[]): void {\n const filtered = keyPath.filter(\n (key) => key.type !== NodeTypes.TRANSLATION\n );\n const prev = this.focusedContent.value;\n\n if (!prev) return;\n\n this.focusedContent.set({ ...prev, keyPath: filtered });\n }\n\n // ─── Dictionary record helpers ───────────────────────────────────────────\n\n setLocaleDictionary(dictionary: Dictionary): void {\n if (!dictionary.localId) return;\n const current = this.localeDictionaries.value ?? {};\n\n this.localeDictionaries.set({\n ...current,\n [dictionary.localId as LocalDictionaryId]: dictionary,\n });\n }\n\n // ─── Edited content helpers ───────────────────────────────────────────────\n\n setEditedDictionary(newDict: Dictionary): void {\n if (!newDict.localId) {\n console.error('setEditedDictionary: missing localId', newDict);\n return;\n }\n const current = this.editedContent.value ?? {};\n\n this.editedContent.set({\n ...current,\n [newDict.localId as LocalDictionaryId]: newDict,\n });\n }\n\n setEditedContent(\n localDictionaryId: LocalDictionaryId,\n newValue: Dictionary['content']\n ): void {\n const current = this.editedContent.value ?? {};\n\n this.editedContent.set({\n ...current,\n [localDictionaryId]: {\n ...current[localDictionaryId],\n content: newValue,\n },\n });\n }\n\n addContent(\n localDictionaryId: LocalDictionaryId,\n newValue: ContentNode,\n keyPath: KeyPath[] = [],\n overwrite = true\n ): void {\n const current = this.editedContent.value ?? {};\n const localeDicts = this.localeDictionaries.value ?? {};\n\n const originalContent = localeDicts[localDictionaryId]?.content;\n const currentContent = structuredClone(\n current[localDictionaryId]?.content ?? originalContent\n );\n\n let newKeyPath = keyPath;\n if (!overwrite) {\n let index = 0;\n\n const otherKeyPath = keyPath.slice(0, -1);\n const lastKeyPath = keyPath[keyPath.length - 1];\n\n let finalKey = lastKeyPath.key;\n\n while (\n typeof getContentNodeByKeyPath(currentContent, newKeyPath) !==\n 'undefined'\n ) {\n index++;\n finalKey =\n index === 0 ? lastKeyPath.key : `${lastKeyPath.key} (${index})`;\n newKeyPath = [\n ...otherKeyPath,\n { ...lastKeyPath, key: finalKey } as KeyPath,\n ];\n }\n }\n\n const updatedContent = editDictionaryByKeyPath(\n currentContent,\n newKeyPath,\n newValue\n );\n\n this.editedContent.set({\n ...current,\n [localDictionaryId]: {\n ...current[localDictionaryId],\n content: updatedContent as Dictionary['content'],\n },\n });\n }\n\n renameContent(\n localDictionaryId: LocalDictionaryId,\n newKey: KeyPath['key'],\n keyPath: KeyPath[] = []\n ): void {\n const current = this.editedContent.value ?? {};\n const localeDicts = this.localeDictionaries.value ?? {};\n const originalContent = localeDicts[localDictionaryId]?.content;\n const currentContent = structuredClone(\n current[localDictionaryId]?.content ?? originalContent\n );\n const updated = renameContentNodeByKeyPath(currentContent, newKey, keyPath);\n\n this.editedContent.set({\n ...current,\n [localDictionaryId]: {\n ...current[localDictionaryId],\n content: updated as Dictionary['content'],\n },\n });\n }\n\n removeContent(\n localDictionaryId: LocalDictionaryId,\n keyPath: KeyPath[]\n ): void {\n const current = this.editedContent.value ?? {};\n const localeDicts = this.localeDictionaries.value ?? {};\n const originalContent = localeDicts[localDictionaryId]?.content;\n const currentContent = structuredClone(\n current[localDictionaryId]?.content ?? originalContent\n );\n const initialContent = getContentNodeByKeyPath(originalContent, keyPath);\n const restored = editDictionaryByKeyPath(\n currentContent,\n keyPath,\n initialContent\n );\n\n this.editedContent.set({\n ...current,\n [localDictionaryId]: {\n ...current[localDictionaryId],\n content: restored as Dictionary['content'],\n },\n });\n }\n\n restoreContent(localDictionaryId: LocalDictionaryId): void {\n const current = this.editedContent.value ?? {};\n const updated = { ...current };\n\n delete updated[localDictionaryId];\n\n this.editedContent.set(updated);\n }\n\n clearContent(localDictionaryId: LocalDictionaryId): void {\n const current = this.editedContent.value ?? {};\n const filtered = { ...current };\n\n delete filtered[localDictionaryId];\n\n this.editedContent.set(filtered);\n }\n\n clearAllContent(): void {\n this.editedContent.set({});\n }\n\n getContentValue(\n localDictionaryIdOrKey: LocalDictionaryId | string,\n keyPath: KeyPath[]\n ): ContentNode | undefined {\n const edited = this.editedContent.value;\n if (!edited) return undefined;\n\n const filteredKeyPath = keyPath.filter(\n (key) => key.type !== NodeTypes.TRANSLATION\n );\n\n // Only use edited content entries whose localId is known to this client.\n // This prevents stale edits from other apps (different framework demos) from\n // being applied when the editor sends back its stored editedContent.\n const localeDicts = this.localeDictionaries.value;\n\n const isDictionaryId =\n localDictionaryIdOrKey.includes(':local:') ||\n localDictionaryIdOrKey.includes(':remote:');\n\n if (isDictionaryId) {\n // If localeDictionaries is loaded, verify this localId belongs to us\n if (localeDicts && !(localDictionaryIdOrKey in localeDicts)) {\n return undefined;\n }\n const content =\n edited[localDictionaryIdOrKey as LocalDictionaryId]?.content ?? {};\n\n return getContentNodeByKeyPath(\n content,\n filteredKeyPath,\n this.currentLocale.value\n );\n }\n\n const matchingIds = Object.keys(edited).filter(\n (key) =>\n key.startsWith(`${localDictionaryIdOrKey}:`) &&\n // If localeDictionaries is loaded, only include known localIds\n (!localeDicts || key in localeDicts)\n );\n\n for (const localId of matchingIds) {\n const content = edited[localId as LocalDictionaryId]?.content ?? {};\n const node = getContentNodeByKeyPath(\n content,\n filteredKeyPath,\n this.currentLocale.value\n );\n if (node) return node;\n }\n\n return undefined;\n }\n\n /**\n * EDITOR mode: listen for CLIENT_READY and respond with EDITOR_ACTIVATE.\n * Also pings the client immediately in case it loaded before the editor.\n */\n private _setupEditorHandshake(): void {\n // When the client announces it is ready, activate it\n this._unsubClientReady = this.messenger.subscribe(\n MessageKey.INTLAYER_CLIENT_READY,\n () => {\n this.editorEnabled.set(true);\n this.messenger.send(MessageKey.INTLAYER_EDITOR_ACTIVATE);\n }\n );\n\n // Ping any already-running client (covers editor-opens-after-client scenario)\n this.messenger.send(MessageKey.INTLAYER_ARE_YOU_THERE);\n }\n\n private _setupActivationHandshake(): void {\n // Announce to the editor that the client is ready\n this.messenger.send(MessageKey.INTLAYER_CLIENT_READY);\n\n // Respond to \"are you there?\" pings from the editor\n this._unsubAreYouThere = this.messenger.subscribe(\n MessageKey.INTLAYER_ARE_YOU_THERE,\n () => {\n this.messenger.send(MessageKey.INTLAYER_CLIENT_READY);\n }\n );\n\n // When the editor activates us, enable the selector and broadcast state\n this._unsubActivate = this.messenger.subscribe(\n MessageKey.INTLAYER_EDITOR_ACTIVATE,\n () => {\n this.editorEnabled.set(true);\n this._broadcastData();\n }\n );\n }\n\n private _broadcastData(): void {\n const configVal = this.configuration.value;\n\n if (configVal) {\n this.messenger.send(\n `${MessageKey.INTLAYER_CONFIGURATION}/post`,\n configVal\n );\n }\n const localeVal = this.currentLocale.value;\n\n if (localeVal) {\n this.messenger.send(\n `${MessageKey.INTLAYER_CURRENT_LOCALE}/post`,\n localeVal\n );\n }\n const dicts = this.localeDictionaries.value;\n\n if (dicts) {\n this.messenger.send(\n `${MessageKey.INTLAYER_LOCALE_DICTIONARIES_CHANGED}/post`,\n dicts\n );\n }\n }\n\n private async _loadDictionaries(): Promise<void> {\n try {\n const mod = await import('@intlayer/unmerged-dictionaries-entry');\n const unmergedDictionaries = mod.getUnmergedDictionaries();\n const dictionariesList = Object.fromEntries(\n Object.values(unmergedDictionaries)\n .flat()\n .map((dictionary) => [dictionary.localId, dictionary])\n ) as DictionaryContent;\n\n this.localeDictionaries.set(dictionariesList);\n\n // If the editor already activated us before dictionaries finished loading,\n // re-broadcast now so the editor receives the dictionaries.\n if (this.editorEnabled.value) {\n this._broadcastData();\n }\n } catch (e) {\n // Dynamic entry not available (expected in editor mode or when not configured)\n console.warn('[intlayer] Failed to load unmerged dictionaries:', e);\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,46 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_messageKey = require('../messageKey.cjs');
|
|
3
|
+
const require_mergeIframeClick = require('../mergeIframeClick.cjs');
|
|
4
|
+
|
|
5
|
+
//#region src/core/IframeClickInterceptor.ts
|
|
6
|
+
/**
|
|
7
|
+
* IframeClickInterceptor handles click events across iframe boundaries.
|
|
8
|
+
*
|
|
9
|
+
* - startInterceptor(): called in the client (iframe) — broadcasts mousedown to parent
|
|
10
|
+
* - startMerger(): called in the editor (parent) — merges received clicks into DOM events
|
|
11
|
+
*
|
|
12
|
+
* Replaces useIframeClickInterceptor / useIframeClickMerger across all frameworks.
|
|
13
|
+
*/
|
|
14
|
+
var IframeClickInterceptor = class {
|
|
15
|
+
constructor(messenger) {
|
|
16
|
+
this._mousedownHandler = null;
|
|
17
|
+
this._unsubscribeMerge = null;
|
|
18
|
+
this._messenger = messenger;
|
|
19
|
+
}
|
|
20
|
+
/** Called on the client side (inside iframe). Broadcasts click events to parent. */
|
|
21
|
+
startInterceptor() {
|
|
22
|
+
if (typeof window === "undefined") return;
|
|
23
|
+
this._mousedownHandler = () => {
|
|
24
|
+
this._messenger.send(require_messageKey.MessageKey.INTLAYER_IFRAME_CLICKED);
|
|
25
|
+
};
|
|
26
|
+
window.addEventListener("mousedown", this._mousedownHandler);
|
|
27
|
+
}
|
|
28
|
+
/** Called on the editor side (parent frame). Merges incoming iframe clicks into DOM. */
|
|
29
|
+
startMerger() {
|
|
30
|
+
this._unsubscribeMerge = this._messenger.subscribe(require_messageKey.MessageKey.INTLAYER_IFRAME_CLICKED, require_mergeIframeClick.mergeIframeClick);
|
|
31
|
+
}
|
|
32
|
+
stopInterceptor() {
|
|
33
|
+
if (this._mousedownHandler) {
|
|
34
|
+
window.removeEventListener("mousedown", this._mousedownHandler);
|
|
35
|
+
this._mousedownHandler = null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
stopMerger() {
|
|
39
|
+
this._unsubscribeMerge?.();
|
|
40
|
+
this._unsubscribeMerge = null;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
//#endregion
|
|
45
|
+
exports.IframeClickInterceptor = IframeClickInterceptor;
|
|
2
46
|
//# sourceMappingURL=IframeClickInterceptor.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IframeClickInterceptor.cjs","names":["MessageKey","mergeIframeClick"],"sources":["../../../src/core/IframeClickInterceptor.ts"],"sourcesContent":["import { mergeIframeClick } from '../mergeIframeClick';\nimport { MessageKey } from '../messageKey';\nimport type { CrossFrameMessenger } from './CrossFrameMessenger';\n\n/**\n * IframeClickInterceptor handles click events across iframe boundaries.\n *\n * - startInterceptor(): called in the client (iframe) — broadcasts mousedown to parent\n * - startMerger(): called in the editor (parent) — merges received clicks into DOM events\n *\n * Replaces useIframeClickInterceptor / useIframeClickMerger across all frameworks.\n */\nexport class IframeClickInterceptor {\n private readonly _messenger: CrossFrameMessenger;\n private _mousedownHandler: EventListener | null = null;\n private _unsubscribeMerge: (() => void) | null = null;\n\n constructor(messenger: CrossFrameMessenger) {\n this._messenger = messenger;\n }\n\n /** Called on the client side (inside iframe). Broadcasts click events to parent. */\n startInterceptor(): void {\n if (typeof window === 'undefined') return;\n this._mousedownHandler = () => {\n this._messenger.send(MessageKey.INTLAYER_IFRAME_CLICKED);\n };\n window.addEventListener('mousedown', this._mousedownHandler);\n }\n\n /** Called on the editor side (parent frame). Merges incoming iframe clicks into DOM. */\n startMerger(): void {\n this._unsubscribeMerge = this._messenger.subscribe(\n MessageKey.INTLAYER_IFRAME_CLICKED,\n mergeIframeClick as (data: unknown) => void\n );\n }\n\n stopInterceptor(): void {\n if (this._mousedownHandler) {\n window.removeEventListener('mousedown', this._mousedownHandler);\n this._mousedownHandler = null;\n }\n }\n\n stopMerger(): void {\n this._unsubscribeMerge?.();\n this._unsubscribeMerge = null;\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"IframeClickInterceptor.cjs","names":["MessageKey","mergeIframeClick"],"sources":["../../../src/core/IframeClickInterceptor.ts"],"sourcesContent":["import { mergeIframeClick } from '../mergeIframeClick';\nimport { MessageKey } from '../messageKey';\nimport type { CrossFrameMessenger } from './CrossFrameMessenger';\n\n/**\n * IframeClickInterceptor handles click events across iframe boundaries.\n *\n * - startInterceptor(): called in the client (iframe) — broadcasts mousedown to parent\n * - startMerger(): called in the editor (parent) — merges received clicks into DOM events\n *\n * Replaces useIframeClickInterceptor / useIframeClickMerger across all frameworks.\n */\nexport class IframeClickInterceptor {\n private readonly _messenger: CrossFrameMessenger;\n private _mousedownHandler: EventListener | null = null;\n private _unsubscribeMerge: (() => void) | null = null;\n\n constructor(messenger: CrossFrameMessenger) {\n this._messenger = messenger;\n }\n\n /** Called on the client side (inside iframe). Broadcasts click events to parent. */\n startInterceptor(): void {\n if (typeof window === 'undefined') return;\n this._mousedownHandler = () => {\n this._messenger.send(MessageKey.INTLAYER_IFRAME_CLICKED);\n };\n window.addEventListener('mousedown', this._mousedownHandler);\n }\n\n /** Called on the editor side (parent frame). Merges incoming iframe clicks into DOM. */\n startMerger(): void {\n this._unsubscribeMerge = this._messenger.subscribe(\n MessageKey.INTLAYER_IFRAME_CLICKED,\n mergeIframeClick as (data: unknown) => void\n );\n }\n\n stopInterceptor(): void {\n if (this._mousedownHandler) {\n window.removeEventListener('mousedown', this._mousedownHandler);\n this._mousedownHandler = null;\n }\n }\n\n stopMerger(): void {\n this._unsubscribeMerge?.();\n this._unsubscribeMerge = null;\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAYA,IAAa,yBAAb,MAAoC;CAKlC,YAAY,WAAgC;2BAHM;2BACD;AAG/C,OAAK,aAAa;;;CAIpB,mBAAyB;AACvB,MAAI,OAAO,WAAW,YAAa;AACnC,OAAK,0BAA0B;AAC7B,QAAK,WAAW,KAAKA,8BAAW,wBAAwB;;AAE1D,SAAO,iBAAiB,aAAa,KAAK,kBAAkB;;;CAI9D,cAAoB;AAClB,OAAK,oBAAoB,KAAK,WAAW,UACvCA,8BAAW,yBACXC,0CACD;;CAGH,kBAAwB;AACtB,MAAI,KAAK,mBAAmB;AAC1B,UAAO,oBAAoB,aAAa,KAAK,kBAAkB;AAC/D,QAAK,oBAAoB;;;CAI7B,aAAmB;AACjB,OAAK,qBAAqB;AAC1B,OAAK,oBAAoB"}
|
|
@@ -1,2 +1,60 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_messageKey = require('../messageKey.cjs');
|
|
3
|
+
|
|
4
|
+
//#region src/core/UrlStateManager.ts
|
|
5
|
+
/**
|
|
6
|
+
* UrlStateManager patches window.history and broadcasts URL path changes
|
|
7
|
+
* across frames via postMessage.
|
|
8
|
+
*
|
|
9
|
+
* Replaces useCrossURLPathSetter / useCrossURLPathState across all frameworks.
|
|
10
|
+
*/
|
|
11
|
+
var UrlStateManager = class {
|
|
12
|
+
constructor(messenger) {
|
|
13
|
+
this._originalPushState = null;
|
|
14
|
+
this._originalReplaceState = null;
|
|
15
|
+
this._listeners = [];
|
|
16
|
+
this._messenger = messenger;
|
|
17
|
+
}
|
|
18
|
+
start() {
|
|
19
|
+
if (typeof window === "undefined") return;
|
|
20
|
+
const updateURLState = () => {
|
|
21
|
+
this._messenger.send(`${require_messageKey.MessageKey.INTLAYER_URL_CHANGE}/post`, window.location.pathname);
|
|
22
|
+
};
|
|
23
|
+
this._originalPushState = history.pushState;
|
|
24
|
+
this._originalReplaceState = history.replaceState;
|
|
25
|
+
const injectLocationChange = (method) => function(...args) {
|
|
26
|
+
method.apply(this, args);
|
|
27
|
+
window.dispatchEvent(new Event("locationchange"));
|
|
28
|
+
};
|
|
29
|
+
history.pushState = injectLocationChange(this._originalPushState);
|
|
30
|
+
history.replaceState = injectLocationChange(this._originalReplaceState);
|
|
31
|
+
for (const eventName of [
|
|
32
|
+
"locationchange",
|
|
33
|
+
"popstate",
|
|
34
|
+
"hashchange",
|
|
35
|
+
"load"
|
|
36
|
+
]) {
|
|
37
|
+
const listener = updateURLState;
|
|
38
|
+
window.addEventListener(eventName, listener);
|
|
39
|
+
this._listeners.push([eventName, listener]);
|
|
40
|
+
}
|
|
41
|
+
updateURLState();
|
|
42
|
+
}
|
|
43
|
+
stop() {
|
|
44
|
+
if (typeof window === "undefined") return;
|
|
45
|
+
for (const [eventName, listener] of this._listeners) window.removeEventListener(eventName, listener);
|
|
46
|
+
this._listeners = [];
|
|
47
|
+
if (this._originalPushState) {
|
|
48
|
+
history.pushState = this._originalPushState;
|
|
49
|
+
this._originalPushState = null;
|
|
50
|
+
}
|
|
51
|
+
if (this._originalReplaceState) {
|
|
52
|
+
history.replaceState = this._originalReplaceState;
|
|
53
|
+
this._originalReplaceState = null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
//#endregion
|
|
59
|
+
exports.UrlStateManager = UrlStateManager;
|
|
2
60
|
//# sourceMappingURL=UrlStateManager.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UrlStateManager.cjs","names":["MessageKey"],"sources":["../../../src/core/UrlStateManager.ts"],"sourcesContent":["import { MessageKey } from '../messageKey';\nimport type { CrossFrameMessenger } from './CrossFrameMessenger';\n\n/**\n * UrlStateManager patches window.history and broadcasts URL path changes\n * across frames via postMessage.\n *\n * Replaces useCrossURLPathSetter / useCrossURLPathState across all frameworks.\n */\nexport class UrlStateManager {\n private readonly _messenger: CrossFrameMessenger;\n private _originalPushState: typeof history.pushState | null = null;\n private _originalReplaceState: typeof history.replaceState | null = null;\n private _listeners: Array<[string, EventListener]> = [];\n\n constructor(messenger: CrossFrameMessenger) {\n this._messenger = messenger;\n }\n\n start(): void {\n if (typeof window === 'undefined') return;\n\n const updateURLState = () => {\n this._messenger.send(\n `${MessageKey.INTLAYER_URL_CHANGE}/post`,\n window.location.pathname\n );\n };\n\n this._originalPushState = history.pushState;\n this._originalReplaceState = history.replaceState;\n\n const injectLocationChange = (method: typeof history.pushState) =>\n function (\n this: typeof history,\n ...args: Parameters<typeof history.pushState>\n ) {\n method.apply(this, args);\n window.dispatchEvent(new Event('locationchange'));\n };\n\n history.pushState = injectLocationChange(\n this._originalPushState\n ) as typeof history.pushState;\n history.replaceState = injectLocationChange(\n this._originalReplaceState\n ) as typeof history.replaceState;\n\n for (const eventName of [\n 'locationchange',\n 'popstate',\n 'hashchange',\n 'load',\n ] as const) {\n const listener = updateURLState as EventListener;\n window.addEventListener(eventName, listener);\n this._listeners.push([eventName, listener]);\n }\n\n updateURLState();\n }\n\n stop(): void {\n if (typeof window === 'undefined') return;\n\n for (const [eventName, listener] of this._listeners) {\n window.removeEventListener(eventName, listener);\n }\n this._listeners = [];\n\n if (this._originalPushState) {\n history.pushState = this._originalPushState;\n this._originalPushState = null;\n }\n if (this._originalReplaceState) {\n history.replaceState = this._originalReplaceState;\n this._originalReplaceState = null;\n }\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"UrlStateManager.cjs","names":["MessageKey"],"sources":["../../../src/core/UrlStateManager.ts"],"sourcesContent":["import { MessageKey } from '../messageKey';\nimport type { CrossFrameMessenger } from './CrossFrameMessenger';\n\n/**\n * UrlStateManager patches window.history and broadcasts URL path changes\n * across frames via postMessage.\n *\n * Replaces useCrossURLPathSetter / useCrossURLPathState across all frameworks.\n */\nexport class UrlStateManager {\n private readonly _messenger: CrossFrameMessenger;\n private _originalPushState: typeof history.pushState | null = null;\n private _originalReplaceState: typeof history.replaceState | null = null;\n private _listeners: Array<[string, EventListener]> = [];\n\n constructor(messenger: CrossFrameMessenger) {\n this._messenger = messenger;\n }\n\n start(): void {\n if (typeof window === 'undefined') return;\n\n const updateURLState = () => {\n this._messenger.send(\n `${MessageKey.INTLAYER_URL_CHANGE}/post`,\n window.location.pathname\n );\n };\n\n this._originalPushState = history.pushState;\n this._originalReplaceState = history.replaceState;\n\n const injectLocationChange = (method: typeof history.pushState) =>\n function (\n this: typeof history,\n ...args: Parameters<typeof history.pushState>\n ) {\n method.apply(this, args);\n window.dispatchEvent(new Event('locationchange'));\n };\n\n history.pushState = injectLocationChange(\n this._originalPushState\n ) as typeof history.pushState;\n history.replaceState = injectLocationChange(\n this._originalReplaceState\n ) as typeof history.replaceState;\n\n for (const eventName of [\n 'locationchange',\n 'popstate',\n 'hashchange',\n 'load',\n ] as const) {\n const listener = updateURLState as EventListener;\n window.addEventListener(eventName, listener);\n this._listeners.push([eventName, listener]);\n }\n\n updateURLState();\n }\n\n stop(): void {\n if (typeof window === 'undefined') return;\n\n for (const [eventName, listener] of this._listeners) {\n window.removeEventListener(eventName, listener);\n }\n this._listeners = [];\n\n if (this._originalPushState) {\n history.pushState = this._originalPushState;\n this._originalPushState = null;\n }\n if (this._originalReplaceState) {\n history.replaceState = this._originalReplaceState;\n this._originalReplaceState = null;\n }\n }\n}\n"],"mappings":";;;;;;;;;;AASA,IAAa,kBAAb,MAA6B;CAM3B,YAAY,WAAgC;4BAJkB;+BACM;oBACf,EAAE;AAGrD,OAAK,aAAa;;CAGpB,QAAc;AACZ,MAAI,OAAO,WAAW,YAAa;EAEnC,MAAM,uBAAuB;AAC3B,QAAK,WAAW,KACd,GAAGA,8BAAW,oBAAoB,QAClC,OAAO,SAAS,SACjB;;AAGH,OAAK,qBAAqB,QAAQ;AAClC,OAAK,wBAAwB,QAAQ;EAErC,MAAM,wBAAwB,WAC5B,SAEE,GAAG,MACH;AACA,UAAO,MAAM,MAAM,KAAK;AACxB,UAAO,cAAc,IAAI,MAAM,iBAAiB,CAAC;;AAGrD,UAAQ,YAAY,qBAClB,KAAK,mBACN;AACD,UAAQ,eAAe,qBACrB,KAAK,sBACN;AAED,OAAK,MAAM,aAAa;GACtB;GACA;GACA;GACA;GACD,EAAW;GACV,MAAM,WAAW;AACjB,UAAO,iBAAiB,WAAW,SAAS;AAC5C,QAAK,WAAW,KAAK,CAAC,WAAW,SAAS,CAAC;;AAG7C,kBAAgB;;CAGlB,OAAa;AACX,MAAI,OAAO,WAAW,YAAa;AAEnC,OAAK,MAAM,CAAC,WAAW,aAAa,KAAK,WACvC,QAAO,oBAAoB,WAAW,SAAS;AAEjD,OAAK,aAAa,EAAE;AAEpB,MAAI,KAAK,oBAAoB;AAC3B,WAAQ,YAAY,KAAK;AACzB,QAAK,qBAAqB;;AAE5B,MAAI,KAAK,uBAAuB;AAC9B,WAAQ,eAAe,KAAK;AAC5B,QAAK,wBAAwB"}
|
|
@@ -1,2 +1,83 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
//#region src/core/globalManager.ts
|
|
4
|
+
/**
|
|
5
|
+
* Keys on window used to share the singleton across multiple module instances.
|
|
6
|
+
* When bundlers include @intlayer/editor more than once (e.g. framework package +
|
|
7
|
+
* app), each copy has its own module-level variables. Using window ensures they
|
|
8
|
+
* all read/write the same manager and event-target.
|
|
9
|
+
*/
|
|
10
|
+
const MANAGER_KEY = "__intlayer_editor_manager__";
|
|
11
|
+
const EVENTS_KEY = "__intlayer_editor_manager_events__";
|
|
12
|
+
/**
|
|
13
|
+
* Retrieves or creates a shared EventTarget for global editor manager events.
|
|
14
|
+
* In browser environments, stores the EventTarget on window to ensure sharing
|
|
15
|
+
* across multiple module instances. In SSR environments, creates a fresh
|
|
16
|
+
* EventTarget (no sharing needed on server-side).
|
|
17
|
+
*
|
|
18
|
+
* @returns The shared EventTarget instance for dispatching manager change events
|
|
19
|
+
* @private
|
|
20
|
+
*/
|
|
21
|
+
const getEventTarget = () => {
|
|
22
|
+
if (typeof window === "undefined") return new EventTarget();
|
|
23
|
+
const windowGlobals = window;
|
|
24
|
+
if (!windowGlobals[EVENTS_KEY]) windowGlobals[EVENTS_KEY] = new EventTarget();
|
|
25
|
+
return windowGlobals[EVENTS_KEY];
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Retrieves the global editor state manager instance.
|
|
29
|
+
* Returns null if no manager has been set or in SSR environments where
|
|
30
|
+
* window is undefined.
|
|
31
|
+
*
|
|
32
|
+
* @returns The global EditorStateManager instance or null if not set
|
|
33
|
+
*/
|
|
34
|
+
const getGlobalEditorManager = () => {
|
|
35
|
+
if (typeof window === "undefined") return null;
|
|
36
|
+
return window[MANAGER_KEY] ?? null;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Sets the global editor state manager instance and notifies all listeners
|
|
40
|
+
* of the change through a CustomEvent.
|
|
41
|
+
*
|
|
42
|
+
* @param manager - The EditorStateManager instance to set globally, or null to clear it
|
|
43
|
+
*/
|
|
44
|
+
const setGlobalEditorManager = (manager) => {
|
|
45
|
+
if (typeof window !== "undefined") {
|
|
46
|
+
const windowGlobals = window;
|
|
47
|
+
windowGlobals[MANAGER_KEY] = manager;
|
|
48
|
+
}
|
|
49
|
+
getEventTarget().dispatchEvent(new CustomEvent("change", { detail: manager }));
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Registers a callback function to be invoked whenever the global editor
|
|
53
|
+
* manager changes. Useful for reactive updates across the application.
|
|
54
|
+
*
|
|
55
|
+
* @param changeCallback - Function to invoke with the new manager state whenever it changes
|
|
56
|
+
* @returns An unsubscribe function that removes the listener when called
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const unsubscribe = onGlobalEditorManagerChange((manager) => {
|
|
61
|
+
* console.log('Manager updated:', manager);
|
|
62
|
+
* });
|
|
63
|
+
*
|
|
64
|
+
* // Later, clean up the listener
|
|
65
|
+
* unsubscribe();
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
const onGlobalEditorManagerChange = (changeCallback) => {
|
|
69
|
+
const eventTarget = getEventTarget();
|
|
70
|
+
const eventHandler = (event) => {
|
|
71
|
+
changeCallback(event.detail);
|
|
72
|
+
};
|
|
73
|
+
eventTarget.addEventListener("change", eventHandler);
|
|
74
|
+
return () => {
|
|
75
|
+
eventTarget.removeEventListener("change", eventHandler);
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
//#endregion
|
|
80
|
+
exports.getGlobalEditorManager = getGlobalEditorManager;
|
|
81
|
+
exports.onGlobalEditorManagerChange = onGlobalEditorManagerChange;
|
|
82
|
+
exports.setGlobalEditorManager = setGlobalEditorManager;
|
|
2
83
|
//# sourceMappingURL=globalManager.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"globalManager.cjs","names":[],"sources":["../../../src/core/globalManager.ts"],"sourcesContent":["import type { EditorStateManager } from './EditorStateManager';\n\n/**\n * Type for storing custom properties on the global window object.\n * Used to safely attach singleton instances across multiple module instances.\n */\ntype WindowWithEditorGlobals = typeof window & {\n [MANAGER_KEY]?: EditorStateManager | null;\n [EVENTS_KEY]?: EventTarget;\n [key: string]: unknown;\n};\n\n/**\n * Keys on window used to share the singleton across multiple module instances.\n * When bundlers include @intlayer/editor more than once (e.g. framework package +\n * app), each copy has its own module-level variables. Using window ensures they\n * all read/write the same manager and event-target.\n */\nconst MANAGER_KEY = '__intlayer_editor_manager__';\nconst EVENTS_KEY = '__intlayer_editor_manager_events__';\n\n/**\n * Retrieves or creates a shared EventTarget for global editor manager events.\n * In browser environments, stores the EventTarget on window to ensure sharing\n * across multiple module instances. In SSR environments, creates a fresh\n * EventTarget (no sharing needed on server-side).\n *\n * @returns The shared EventTarget instance for dispatching manager change events\n * @private\n */\nconst getEventTarget = (): EventTarget => {\n if (typeof window === 'undefined') {\n // SSR fallback — create a fresh one (no sharing needed server-side)\n return new EventTarget();\n }\n\n const windowGlobals = window as WindowWithEditorGlobals;\n if (!windowGlobals[EVENTS_KEY]) {\n windowGlobals[EVENTS_KEY] = new EventTarget();\n }\n\n return windowGlobals[EVENTS_KEY] as EventTarget;\n};\n\n/**\n * Retrieves the global editor state manager instance.\n * Returns null if no manager has been set or in SSR environments where\n * window is undefined.\n *\n * @returns The global EditorStateManager instance or null if not set\n */\nexport const getGlobalEditorManager = (): EditorStateManager | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n const windowGlobals = window as WindowWithEditorGlobals;\n const manager = windowGlobals[MANAGER_KEY];\n\n return manager ?? null;\n};\n\n/**\n * Sets the global editor state manager instance and notifies all listeners\n * of the change through a CustomEvent.\n *\n * @param manager - The EditorStateManager instance to set globally, or null to clear it\n */\nexport const setGlobalEditorManager = (\n manager: EditorStateManager | null\n): void => {\n if (typeof window !== 'undefined') {\n const windowGlobals = window as WindowWithEditorGlobals;\n windowGlobals[MANAGER_KEY] = manager;\n }\n\n const eventTarget = getEventTarget();\n\n eventTarget.dispatchEvent(\n new CustomEvent<EditorStateManager | null>('change', { detail: manager })\n );\n};\n\n/**\n * Registers a callback function to be invoked whenever the global editor\n * manager changes. Useful for reactive updates across the application.\n *\n * @param changeCallback - Function to invoke with the new manager state whenever it changes\n * @returns An unsubscribe function that removes the listener when called\n *\n * @example\n * ```typescript\n * const unsubscribe = onGlobalEditorManagerChange((manager) => {\n * console.log('Manager updated:', manager);\n * });\n *\n * // Later, clean up the listener\n * unsubscribe();\n * ```\n */\nexport const onGlobalEditorManagerChange = (\n changeCallback: (manager: EditorStateManager | null) => void\n): (() => void) => {\n const eventTarget = getEventTarget();\n\n const eventHandler = (event: Event) => {\n const customEvent = event as CustomEvent<EditorStateManager | null>;\n changeCallback(customEvent.detail);\n };\n\n eventTarget.addEventListener('change', eventHandler);\n\n return () => {\n eventTarget.removeEventListener('change', eventHandler);\n };\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"globalManager.cjs","names":[],"sources":["../../../src/core/globalManager.ts"],"sourcesContent":["import type { EditorStateManager } from './EditorStateManager';\n\n/**\n * Type for storing custom properties on the global window object.\n * Used to safely attach singleton instances across multiple module instances.\n */\ntype WindowWithEditorGlobals = typeof window & {\n [MANAGER_KEY]?: EditorStateManager | null;\n [EVENTS_KEY]?: EventTarget;\n [key: string]: unknown;\n};\n\n/**\n * Keys on window used to share the singleton across multiple module instances.\n * When bundlers include @intlayer/editor more than once (e.g. framework package +\n * app), each copy has its own module-level variables. Using window ensures they\n * all read/write the same manager and event-target.\n */\nconst MANAGER_KEY = '__intlayer_editor_manager__';\nconst EVENTS_KEY = '__intlayer_editor_manager_events__';\n\n/**\n * Retrieves or creates a shared EventTarget for global editor manager events.\n * In browser environments, stores the EventTarget on window to ensure sharing\n * across multiple module instances. In SSR environments, creates a fresh\n * EventTarget (no sharing needed on server-side).\n *\n * @returns The shared EventTarget instance for dispatching manager change events\n * @private\n */\nconst getEventTarget = (): EventTarget => {\n if (typeof window === 'undefined') {\n // SSR fallback — create a fresh one (no sharing needed server-side)\n return new EventTarget();\n }\n\n const windowGlobals = window as WindowWithEditorGlobals;\n if (!windowGlobals[EVENTS_KEY]) {\n windowGlobals[EVENTS_KEY] = new EventTarget();\n }\n\n return windowGlobals[EVENTS_KEY] as EventTarget;\n};\n\n/**\n * Retrieves the global editor state manager instance.\n * Returns null if no manager has been set or in SSR environments where\n * window is undefined.\n *\n * @returns The global EditorStateManager instance or null if not set\n */\nexport const getGlobalEditorManager = (): EditorStateManager | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n const windowGlobals = window as WindowWithEditorGlobals;\n const manager = windowGlobals[MANAGER_KEY];\n\n return manager ?? null;\n};\n\n/**\n * Sets the global editor state manager instance and notifies all listeners\n * of the change through a CustomEvent.\n *\n * @param manager - The EditorStateManager instance to set globally, or null to clear it\n */\nexport const setGlobalEditorManager = (\n manager: EditorStateManager | null\n): void => {\n if (typeof window !== 'undefined') {\n const windowGlobals = window as WindowWithEditorGlobals;\n windowGlobals[MANAGER_KEY] = manager;\n }\n\n const eventTarget = getEventTarget();\n\n eventTarget.dispatchEvent(\n new CustomEvent<EditorStateManager | null>('change', { detail: manager })\n );\n};\n\n/**\n * Registers a callback function to be invoked whenever the global editor\n * manager changes. Useful for reactive updates across the application.\n *\n * @param changeCallback - Function to invoke with the new manager state whenever it changes\n * @returns An unsubscribe function that removes the listener when called\n *\n * @example\n * ```typescript\n * const unsubscribe = onGlobalEditorManagerChange((manager) => {\n * console.log('Manager updated:', manager);\n * });\n *\n * // Later, clean up the listener\n * unsubscribe();\n * ```\n */\nexport const onGlobalEditorManagerChange = (\n changeCallback: (manager: EditorStateManager | null) => void\n): (() => void) => {\n const eventTarget = getEventTarget();\n\n const eventHandler = (event: Event) => {\n const customEvent = event as CustomEvent<EditorStateManager | null>;\n changeCallback(customEvent.detail);\n };\n\n eventTarget.addEventListener('change', eventHandler);\n\n return () => {\n eventTarget.removeEventListener('change', eventHandler);\n };\n};\n"],"mappings":";;;;;;;;;AAkBA,MAAM,cAAc;AACpB,MAAM,aAAa;;;;;;;;;;AAWnB,MAAM,uBAAoC;AACxC,KAAI,OAAO,WAAW,YAEpB,QAAO,IAAI,aAAa;CAG1B,MAAM,gBAAgB;AACtB,KAAI,CAAC,cAAc,YACjB,eAAc,cAAc,IAAI,aAAa;AAG/C,QAAO,cAAc;;;;;;;;;AAUvB,MAAa,+BAA0D;AACrE,KAAI,OAAO,WAAW,YACpB,QAAO;AAMT,QAHsB,OACQ,gBAEZ;;;;;;;;AASpB,MAAa,0BACX,YACS;AACT,KAAI,OAAO,WAAW,aAAa;EACjC,MAAM,gBAAgB;AACtB,gBAAc,eAAe;;AAK/B,CAFoB,gBAAgB,CAExB,cACV,IAAI,YAAuC,UAAU,EAAE,QAAQ,SAAS,CAAC,CAC1E;;;;;;;;;;;;;;;;;;;AAoBH,MAAa,+BACX,mBACiB;CACjB,MAAM,cAAc,gBAAgB;CAEpC,MAAM,gBAAgB,UAAiB;AAErC,iBADoB,MACO,OAAO;;AAGpC,aAAY,iBAAiB,UAAU,aAAa;AAEpD,cAAa;AACX,cAAY,oBAAoB,UAAU,aAAa"}
|