svelte-intlayer 8.3.3 → 8.4.0-canary.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.
- package/dist/client/setupIntlayer.svelte.d.ts +2 -2
- package/dist/client/setupIntlayer.svelte.js +11 -15
- package/dist/editor/ContentSelector.svelte +7 -119
- package/dist/editor/ContentSelector.svelte.d.ts +3 -7
- package/dist/editor/useEditor.d.ts +9 -0
- package/dist/editor/useEditor.js +29 -41
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/markdown/MarkdownMetadataWithSelector.svelte +3 -3
- package/dist/markdown/MarkdownWithSelector.svelte +3 -3
- package/dist/plugins.js +2 -2
- package/dist/renderIntlayerNode.test.js +1 -1
- package/package.json +7 -8
- package/dist/editor/ContentSelectorWrapper.svelte +0 -70
- package/dist/editor/ContentSelectorWrapper.svelte.d.ts +0 -31
- package/dist/editor/communicator.d.ts +0 -9
- package/dist/editor/communicator.js +0 -54
- package/dist/editor/dictionariesRecord.d.ts +0 -5
- package/dist/editor/dictionariesRecord.js +0 -18
- package/dist/editor/editorEnabled.d.ts +0 -14
- package/dist/editor/editorEnabled.js +0 -41
- package/dist/editor/focusDictionary.d.ts +0 -16
- package/dist/editor/focusDictionary.js +0 -22
- package/dist/editor/index.d.ts +0 -9
- package/dist/editor/index.js +0 -9
- package/dist/editor/useCrossFrameMessageListener.d.ts +0 -2
- package/dist/editor/useCrossFrameMessageListener.js +0 -65
- package/dist/editor/useCrossFrameState.d.ts +0 -7
- package/dist/editor/useCrossFrameState.js +0 -82
- package/dist/editor/useIframeClickInterceptor.d.ts +0 -2
- package/dist/editor/useIframeClickInterceptor.js +0 -29
|
@@ -16,7 +16,7 @@ import type { LocalesValues } from '@intlayer/types/module_augmentation';
|
|
|
16
16
|
* </script>
|
|
17
17
|
* ```
|
|
18
18
|
*/
|
|
19
|
-
export declare const setupIntlayer: (initialLocale
|
|
20
|
-
readonly locale:
|
|
19
|
+
export declare const setupIntlayer: (initialLocale?: LocalesValues) => {
|
|
20
|
+
readonly locale: "id" | "cy" | "hr" | "th" | "tr" | "tt" | "mi" | "mn" | "ms" | "af" | "af-ZA" | "ar" | "ar-AE" | "ar-BH" | "ar-DZ" | "ar-EG" | "ar-IQ" | "ar-JO" | "ar-KW" | "ar-LB" | "ar-LY" | "ar-MA" | "ar-OM" | "ar-QA" | "ar-SA" | "ar-SY" | "ar-TN" | "ar-YE" | "az" | "az-AZ" | "be" | "be-BY" | "bg" | "bg-BG" | "bs" | "bs-BA" | "ca" | "ca-ES" | "cs" | "cs-CZ" | "cy-GB" | "da" | "da-DK" | "de" | "de-AT" | "de-CH" | "de-DE" | "de-LI" | "de-LU" | "dv" | "dv-MV" | "el" | "el-GR" | "en" | "en-AU" | "en-BZ" | "en-CA" | "en-CB" | "en-GB" | "en-IE" | "en-JM" | "en-NZ" | "en-PH" | "en-TT" | "en-US" | "en-ZA" | "en-ZW" | "eo" | "es" | "es-AR" | "es-BO" | "es-CL" | "es-CO" | "es-CR" | "es-DO" | "es-EC" | "es-ES" | "es-GT" | "es-HN" | "es-MX" | "es-NI" | "es-PA" | "es-PE" | "es-PR" | "es-PY" | "es-SV" | "es-UY" | "es-VE" | "et" | "et-EE" | "eu" | "eu-ES" | "fa" | "fa-IR" | "fi" | "fi-FI" | "fo" | "fo-FO" | "fr" | "fr-BE" | "fr-CA" | "fr-CH" | "fr-FR" | "fr-LU" | "fr-MC" | "ga" | "ga-IE" | "gd" | "gd-GB" | "gl" | "gl-ES" | "gu" | "gu-IN" | "he" | "he-IL" | "hi" | "hi-IN" | "hr-BA" | "hr-HR" | "hu" | "hu-HU" | "hy" | "hy-AM" | "id-ID" | "is" | "is-IS" | "it" | "it-CH" | "it-IT" | "ja" | "ja-JP" | "ka" | "ka-GE" | "kk" | "kk-KZ" | "kn" | "kn-IN" | "ko" | "ko-KR" | "kok" | "kok-IN" | "ku" | "ku-TR" | "ky" | "ky-KG" | "lt" | "lt-LT" | "lv" | "lv-LV" | "mi-NZ" | "mk" | "mk-MK" | "mn-MN" | "mr" | "mr-IN" | "ms-BN" | "ms-MY" | "mt" | "mt-MT" | "ml" | "ml-IN" | "no" | "nb" | "nb-NO" | "nl" | "nl-BE" | "nl-NL" | "nn" | "nn-NO" | "ns" | "ns-ZA" | "pa" | "pa-IN" | "pl" | "pl-PL" | "ps" | "ps-AR" | "pt" | "pt-BR" | "pt-PT" | "qu" | "qu-BO" | "qu-EC" | "qu-PE" | "ro" | "ro-RO" | "ro-MD" | "rm" | "rm-CH" | "ru" | "ru-RU" | "ru-MD" | "sa" | "sa-IN" | "se" | "se-FI" | "se-NO" | "se-SE" | "hsb" | "hsb-DE" | "dsb" | "dsb-DE" | "sk" | "sk-SK" | "sl" | "sl-SI" | "sq" | "sq-AL" | "sr" | "sr-BA" | "sr-SP" | "sv" | "sv-FI" | "sv-SE" | "sw" | "sw-KE" | "syr" | "syr-SY" | "ta" | "ta-IN" | "te" | "te-IN" | "th-TH" | "tl" | "tl-PH" | "tn" | "tn-ZA" | "tr-TR" | "tt-RU" | "ts" | "uk" | "uk-UA" | "ur" | "ur-PK" | "uz" | "uz-UZ" | "vi" | "vi-VN" | "ve" | "ve-ZA" | "xh" | "xh-ZA" | "zh" | "zh-Hans" | "zh-CN" | "zh-HK" | "zh-MO" | "zh-SG" | "zh-Hant" | "zu" | "zu-ZA" | "bn" | "bn-BD" | "bn-IN" | "bn-MM" | "my" | "my-MM" | "km" | "km-KH" | "lo" | "lo-LA" | "yo" | "yo-NG" | "yi" | "yi-001" | "am" | "am-ET" | "ne" | "ne-NP" | "si" | "si-LK" | "sr-Cyrl" | "sr-RS" | "en-IN" | "en-SG" | "en-HK" | "en-NG" | "en-PK" | "en-MY" | "en-BW" | "en-KE" | "en-TZ" | "en-GH" | "en-UG" | "es-CU" | "es-US" | "pt-GW" | "pt-MZ" | "pt-ST" | "pt-CV" | "pt-TL" | "pt-MO" | "zh-TW" | "ar-MR" | "ar-PS" | "ar-SD" | "ar-DJ" | "ar-SO" | "ar-TD" | "ar-KM" | "mg-MG" | (string & {});
|
|
21
21
|
setLocale: (newLocale: LocalesValues) => void;
|
|
22
22
|
};
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { createEditorEnabledClient } from '../editor/editorEnabled';
|
|
3
|
-
import { createFocusDictionaryClient } from '../editor/focusDictionary';
|
|
1
|
+
import configuration from '@intlayer/config/built';
|
|
4
2
|
import { useEditor } from '../editor/useEditor';
|
|
5
|
-
// Assuming setIntlayerContext exports a specific key or symbol
|
|
6
3
|
import { setIntlayerContext } from './intlayerContext';
|
|
4
|
+
import { intlayerStore } from './intlayerStore';
|
|
7
5
|
/**
|
|
8
6
|
* Setups Intlayer in your Svelte application.
|
|
9
7
|
*
|
|
@@ -22,27 +20,25 @@ import { setIntlayerContext } from './intlayerContext';
|
|
|
22
20
|
* ```
|
|
23
21
|
*/
|
|
24
22
|
export const setupIntlayer = (initialLocale) => {
|
|
25
|
-
// 1. Initialize your side effects
|
|
26
|
-
// Note: If these need to run only in the browser, wrap them in $effect or onMount
|
|
27
|
-
// inside your side-effect logic if they aren't already safe.
|
|
28
|
-
createCommunicator();
|
|
29
|
-
createEditorEnabledClient();
|
|
30
|
-
createFocusDictionaryClient();
|
|
31
23
|
useEditor();
|
|
32
|
-
//
|
|
24
|
+
// Create Reactive State (Svelte 5)
|
|
33
25
|
// We make the locale a "rune" so updates propagate
|
|
34
26
|
let locale = $state(initialLocale);
|
|
35
|
-
//
|
|
27
|
+
// Keep intlayerStore in sync so useEditor can subscribe to it
|
|
28
|
+
if (initialLocale) {
|
|
29
|
+
intlayerStore.setLocale(initialLocale);
|
|
30
|
+
}
|
|
31
|
+
// Define the Context Object
|
|
36
32
|
const contextValue = {
|
|
37
33
|
get locale() {
|
|
38
|
-
return locale;
|
|
34
|
+
return locale ?? configuration.internationalization.defaultLocale;
|
|
39
35
|
},
|
|
40
36
|
setLocale: (newLocale) => {
|
|
41
37
|
locale = newLocale;
|
|
38
|
+
intlayerStore.setLocale(newLocale);
|
|
42
39
|
},
|
|
43
40
|
};
|
|
44
|
-
//
|
|
45
|
-
// setIntlayerContext internal logic usually wraps setContext(KEY, value)
|
|
41
|
+
// Set the Context
|
|
46
42
|
setIntlayerContext(contextValue);
|
|
47
43
|
// Return the state if you need it immediately in the layout
|
|
48
44
|
return contextValue;
|
|
@@ -1,125 +1,13 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import type { NodeProps } from '@intlayer/core/interpreter';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export let onClickOutside: (() => void) | undefined = undefined;
|
|
7
|
-
// biome-ignore lint/style/useConst: Svelte props need let
|
|
8
|
-
export let pressDuration = 250;
|
|
9
|
-
// biome-ignore lint/style/useConst: Svelte props need let
|
|
10
|
-
export let isSelecting = false;
|
|
11
|
-
|
|
12
|
-
// Event props
|
|
13
|
-
// biome-ignore lint/style/useConst: Svelte props need let
|
|
14
|
-
export let onPress: (() => void) | undefined = undefined;
|
|
15
|
-
// biome-ignore lint/style/useConst: Svelte props need let
|
|
16
|
-
export let onUnhover: (() => void) | undefined = undefined;
|
|
17
|
-
// biome-ignore lint/style/useConst: Svelte props need let
|
|
18
|
-
export let onHover: (() => void) | undefined = undefined;
|
|
19
|
-
// biome-ignore lint/style/useConst: Svelte props need let
|
|
20
|
-
export let onClick: ((event: MouseEvent) => void) | undefined = undefined;
|
|
21
|
-
|
|
22
|
-
// biome-ignore lint/style/useConst: don't transform to const
|
|
23
|
-
let containerRef: HTMLElement | null = null;
|
|
24
|
-
let isHovered = false;
|
|
25
|
-
let isSelectingState = isSelecting;
|
|
26
|
-
let pressTimerRef: ReturnType<typeof setTimeout> | null = null;
|
|
27
|
-
|
|
28
|
-
$: currentIsSelecting = isSelecting || isSelectingState;
|
|
29
|
-
|
|
30
|
-
const handleOnLongPress = () => {
|
|
31
|
-
isSelectingState = true;
|
|
32
|
-
onPress?.();
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const startPressTimer = () => {
|
|
36
|
-
pressTimerRef = setTimeout(() => {
|
|
37
|
-
handleOnLongPress();
|
|
38
|
-
}, pressDuration);
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const clearPressTimer = () => {
|
|
42
|
-
if (pressTimerRef) {
|
|
43
|
-
clearTimeout(pressTimerRef);
|
|
44
|
-
pressTimerRef = null;
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const handleMouseDown = () => {
|
|
49
|
-
clearPressTimer();
|
|
50
|
-
startPressTimer();
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const handleMouseUp = () => {
|
|
54
|
-
if (isHovered) {
|
|
55
|
-
isHovered = false;
|
|
56
|
-
onUnhover?.();
|
|
57
|
-
}
|
|
58
|
-
clearPressTimer();
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
const handleMouseLeave = () => {
|
|
62
|
-
if (isHovered) {
|
|
63
|
-
isHovered = false;
|
|
64
|
-
onUnhover?.();
|
|
65
|
-
}
|
|
66
|
-
clearPressTimer();
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const handleClickOutside = (event: MouseEvent) => {
|
|
70
|
-
if (containerRef && !containerRef.contains(event.target as Node)) {
|
|
71
|
-
isSelectingState = false;
|
|
72
|
-
if (onClickOutside) onClickOutside();
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const handleClick = (e: MouseEvent) => {
|
|
77
|
-
if (currentIsSelecting) {
|
|
78
|
-
e.preventDefault();
|
|
79
|
-
e.stopPropagation();
|
|
80
|
-
} else {
|
|
81
|
-
onClick?.(e);
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
onMount(() => {
|
|
86
|
-
document.addEventListener('mousedown', handleClickOutside);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
onDestroy(() => {
|
|
90
|
-
if (typeof document !== 'undefined') {
|
|
91
|
-
document.removeEventListener('mousedown', handleClickOutside);
|
|
92
|
-
}
|
|
93
|
-
clearPressTimer();
|
|
94
|
-
});
|
|
4
|
+
export let dictionaryKey: NodeProps['dictionaryKey'];
|
|
5
|
+
export let keyPath: NodeProps['keyPath'];
|
|
95
6
|
</script>
|
|
96
7
|
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
tabindex="0"
|
|
101
|
-
onclick={handleClick}
|
|
102
|
-
onkeydown={() => {}}
|
|
103
|
-
onmousedown={handleMouseDown}
|
|
104
|
-
onmouseup={handleMouseUp}
|
|
105
|
-
onmouseleave={handleMouseLeave}
|
|
106
|
-
ontouchstart={handleMouseDown}
|
|
107
|
-
ontouchend={handleMouseUp}
|
|
108
|
-
ontouchcancel={handleMouseLeave}
|
|
109
|
-
onmouseenter={() => {
|
|
110
|
-
isHovered = true;
|
|
111
|
-
onHover?.();
|
|
112
|
-
}}
|
|
113
|
-
style={`display: inline-block;
|
|
114
|
-
cursor: pointer;
|
|
115
|
-
user-select: none;
|
|
116
|
-
border-radius: 0.375rem;
|
|
117
|
-
outline-width: 2px;
|
|
118
|
-
outline-offset: 4px;
|
|
119
|
-
outline-style: solid;
|
|
120
|
-
outline-color: ${currentIsSelecting || isHovered ? 'inherit' : 'transparent'};
|
|
121
|
-
transition: all 100ms 50ms ease-in-out;
|
|
122
|
-
`}
|
|
8
|
+
<intlayer-content-selector-wrapper
|
|
9
|
+
key-path={JSON.stringify(keyPath)}
|
|
10
|
+
dictionary-key={dictionaryKey}
|
|
123
11
|
>
|
|
124
12
|
<slot />
|
|
125
|
-
</
|
|
13
|
+
</intlayer-content-selector-wrapper>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { NodeProps } from '@intlayer/core/interpreter';
|
|
1
2
|
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
2
3
|
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
3
4
|
$$bindings?: Bindings;
|
|
@@ -17,13 +18,8 @@ type $$__sveltets_2_PropsWithChildren<Props, Slots> = Props & (Slots extends {
|
|
|
17
18
|
children?: any;
|
|
18
19
|
} : {});
|
|
19
20
|
declare const ContentSelector: $$__sveltets_2_IsomorphicComponent<$$__sveltets_2_PropsWithChildren<{
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
isSelecting?: boolean;
|
|
23
|
-
onPress?: (() => void) | undefined;
|
|
24
|
-
onUnhover?: (() => void) | undefined;
|
|
25
|
-
onHover?: (() => void) | undefined;
|
|
26
|
-
onClick?: ((event: MouseEvent) => void) | undefined;
|
|
21
|
+
dictionaryKey: NodeProps["dictionaryKey"];
|
|
22
|
+
keyPath: NodeProps["keyPath"];
|
|
27
23
|
}, {
|
|
28
24
|
default: {};
|
|
29
25
|
}>, {
|
|
@@ -1 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Initialises the Intlayer editor client singleton when the editor is enabled.
|
|
3
|
+
* Syncs the current locale from intlayerStore into the editor manager so the
|
|
4
|
+
* editor always knows which locale the app is displaying.
|
|
5
|
+
*
|
|
6
|
+
* setupIntlayer keeps intlayerStore in sync whenever setLocale is called, so
|
|
7
|
+
* subscribing to the store gives us reactive locale updates without needing
|
|
8
|
+
* direct access to the Svelte 5 rune state.
|
|
9
|
+
*/
|
|
1
10
|
export declare const useEditor: () => void;
|
package/dist/editor/useEditor.js
CHANGED
|
@@ -1,46 +1,34 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { onDestroy } from 'svelte';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import { isEnabled } from '@intlayer/editor/isEnabled';
|
|
2
|
+
import { onDestroy, onMount } from 'svelte';
|
|
3
|
+
import { intlayerStore } from '../client/intlayerStore';
|
|
4
|
+
/**
|
|
5
|
+
* Initialises the Intlayer editor client singleton when the editor is enabled.
|
|
6
|
+
* Syncs the current locale from intlayerStore into the editor manager so the
|
|
7
|
+
* editor always knows which locale the app is displaying.
|
|
8
|
+
*
|
|
9
|
+
* setupIntlayer keeps intlayerStore in sync whenever setLocale is called, so
|
|
10
|
+
* subscribing to the store gives us reactive locale updates without needing
|
|
11
|
+
* direct access to the Svelte 5 rune state.
|
|
12
|
+
*/
|
|
10
13
|
export const useEditor = () => {
|
|
11
|
-
if (
|
|
14
|
+
if (!isEnabled)
|
|
12
15
|
return;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}, false);
|
|
26
|
-
// Initial check
|
|
27
|
-
const checkEnabled = () => {
|
|
28
|
-
const inIframe = get(editorEnabled.isInIframe);
|
|
29
|
-
const setting = get(editorEnabled.settingEnabled);
|
|
30
|
-
if (inIframe && setting) {
|
|
31
|
-
getEditorEnabled();
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
checkEnabled();
|
|
35
|
-
// Subscribe to changes
|
|
36
|
-
editorEnabled.isInIframe.subscribe(() => checkEnabled());
|
|
37
|
-
// Keep track of unsubscribe so we *could* clean up if we ever wanted
|
|
38
|
-
unsubscribeIsInIframe = editorEnabled.isInIframe.subscribe(() => checkEnabled());
|
|
16
|
+
let unsubscribeLocale = null;
|
|
17
|
+
onMount(() => {
|
|
18
|
+
import('@intlayer/editor').then(({ initEditorClient }) => {
|
|
19
|
+
const manager = initEditorClient();
|
|
20
|
+
// Subscribe immediately — Svelte stores call the subscriber with the
|
|
21
|
+
// current value on subscription, so the initial locale is set right away.
|
|
22
|
+
unsubscribeLocale = intlayerStore.subscribe(({ locale }) => {
|
|
23
|
+
if (locale)
|
|
24
|
+
manager.currentLocale.set(locale);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
});
|
|
39
28
|
onDestroy(() => {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
29
|
+
unsubscribeLocale?.();
|
|
30
|
+
import('@intlayer/editor').then(({ stopEditorClient }) => {
|
|
31
|
+
stopEditorClient();
|
|
32
|
+
});
|
|
45
33
|
});
|
|
46
34
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -10,7 +10,6 @@ export * from './getIntlayer';
|
|
|
10
10
|
export * from './html';
|
|
11
11
|
export * from './markdown';
|
|
12
12
|
export * from './plugins';
|
|
13
|
-
import { useEditor } from './editor';
|
|
14
13
|
import { setHTMLContext } from './html';
|
|
15
14
|
import { setMarkdownContext } from './markdown';
|
|
16
|
-
export {
|
|
15
|
+
export { setHTMLContext as setIntlayerHTML, setMarkdownContext as setIntlayerMarkdown, };
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,6 @@ export * from './getIntlayer';
|
|
|
4
4
|
export * from './html';
|
|
5
5
|
export * from './markdown';
|
|
6
6
|
export * from './plugins';
|
|
7
|
-
import { useEditor } from './editor';
|
|
8
7
|
import { setHTMLContext } from './html';
|
|
9
8
|
import { setMarkdownContext } from './markdown';
|
|
10
|
-
export {
|
|
9
|
+
export { setHTMLContext as setIntlayerHTML, setMarkdownContext as setIntlayerMarkdown, };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { KeyPath } from '@intlayer/types/keyPath';
|
|
3
|
-
import
|
|
3
|
+
import ContentSelector from '../editor/ContentSelector.svelte';
|
|
4
4
|
import MarkdownMetadataRenderer from './MarkdownMetadataRenderer.svelte';
|
|
5
5
|
|
|
6
6
|
export let dictionaryKey: string;
|
|
@@ -9,13 +9,13 @@ export let value: string;
|
|
|
9
9
|
export let metadataKeyPath: KeyPath[];
|
|
10
10
|
</script>
|
|
11
11
|
|
|
12
|
-
<
|
|
12
|
+
<ContentSelector {dictionaryKey} {keyPath}>
|
|
13
13
|
<MarkdownMetadataRenderer
|
|
14
14
|
{dictionaryKey}
|
|
15
15
|
{keyPath}
|
|
16
16
|
{value}
|
|
17
17
|
{metadataKeyPath}
|
|
18
18
|
/>
|
|
19
|
-
</
|
|
19
|
+
</ContentSelector>
|
|
20
20
|
|
|
21
21
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { KeyPath } from '@intlayer/types/keyPath';
|
|
3
|
-
import
|
|
3
|
+
import ContentSelector from '../editor/ContentSelector.svelte';
|
|
4
4
|
import MarkdownRenderer from './MarkdownRenderer.svelte';
|
|
5
5
|
|
|
6
6
|
export let dictionaryKey: string;
|
|
@@ -10,8 +10,8 @@ export let value: string;
|
|
|
10
10
|
$: overrides = { ...$$restProps };
|
|
11
11
|
</script>
|
|
12
12
|
|
|
13
|
-
<
|
|
13
|
+
<ContentSelector {dictionaryKey} {keyPath}>
|
|
14
14
|
<MarkdownRenderer {value} {overrides} />
|
|
15
|
-
</
|
|
15
|
+
</ContentSelector>
|
|
16
16
|
|
|
17
17
|
|
package/dist/plugins.js
CHANGED
|
@@ -3,7 +3,7 @@ import { conditionPlugin, enumerationPlugin, filePlugin, genderPlugin, getHTML,
|
|
|
3
3
|
import { compile, getMarkdownMetadata } from '@intlayer/core/markdown';
|
|
4
4
|
import { HTML_TAGS, } from '@intlayer/core/transpiler';
|
|
5
5
|
import { NodeType } from '@intlayer/types/nodeType';
|
|
6
|
-
import {
|
|
6
|
+
import { default as ContentSelector } from './editor/ContentSelector.svelte';
|
|
7
7
|
import MarkdownMetadataWithSelector from './markdown/MarkdownMetadataWithSelector.svelte';
|
|
8
8
|
import MarkdownWithSelector from './markdown/MarkdownWithSelector.svelte';
|
|
9
9
|
import { svelteHtmlRuntime } from './markdown/runtime';
|
|
@@ -20,7 +20,7 @@ export const intlayerNodePlugins = {
|
|
|
20
20
|
transform: (node, { children, ...rest }) => renderIntlayerNode({
|
|
21
21
|
value: children ?? node,
|
|
22
22
|
component: configuration?.editor.enabled
|
|
23
|
-
?
|
|
23
|
+
? ContentSelector
|
|
24
24
|
: (children ?? node),
|
|
25
25
|
props: rest,
|
|
26
26
|
}),
|
|
@@ -10,7 +10,7 @@ vi.mock('./IntlayerNodeWrapper.svelte', () => ({
|
|
|
10
10
|
}));
|
|
11
11
|
// Mock the editor module (exports Svelte components).
|
|
12
12
|
vi.mock('./editor', () => ({
|
|
13
|
-
|
|
13
|
+
ContentSelector: () => null,
|
|
14
14
|
}));
|
|
15
15
|
// Mock Svelte markdown components.
|
|
16
16
|
vi.mock('./markdown/MarkdownMetadataWithSelector.svelte', () => ({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelte-intlayer",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.4.0-canary.0",
|
|
4
4
|
"description": "Easily internationalize i18n your Svelte applications with type-safe multilingual content management.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"intlayer",
|
|
@@ -72,12 +72,11 @@
|
|
|
72
72
|
"typecheck": "tsc --noEmit --project tsconfig.types.json"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@intlayer/api": "8.
|
|
76
|
-
"@intlayer/config": "8.
|
|
77
|
-
"@intlayer/core": "8.
|
|
78
|
-
"@intlayer/editor": "8.
|
|
79
|
-
"@intlayer/types": "8.
|
|
80
|
-
"@intlayer/unmerged-dictionaries-entry": "8.3.3"
|
|
75
|
+
"@intlayer/api": "8.4.0-canary.0",
|
|
76
|
+
"@intlayer/config": "8.4.0-canary.0",
|
|
77
|
+
"@intlayer/core": "8.4.0-canary.0",
|
|
78
|
+
"@intlayer/editor": "8.4.0-canary.0",
|
|
79
|
+
"@intlayer/types": "8.4.0-canary.0"
|
|
81
80
|
},
|
|
82
81
|
"devDependencies": {
|
|
83
82
|
"@sveltejs/adapter-auto": "7.0.1",
|
|
@@ -90,7 +89,7 @@
|
|
|
90
89
|
"rimraf": "6.1.3",
|
|
91
90
|
"svelte": "5.53.11",
|
|
92
91
|
"svelte-check": "4.4.5",
|
|
93
|
-
"tsdown": "0.21.
|
|
92
|
+
"tsdown": "0.21.4",
|
|
94
93
|
"typescript": "5.9.3",
|
|
95
94
|
"vite": "8.0.0",
|
|
96
95
|
"vitest": "4.1.0"
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { NodeProps } from '@intlayer/core/interpreter';
|
|
3
|
-
import { isSameKeyPath } from '@intlayer/core/utils';
|
|
4
|
-
import { MessageKey } from '@intlayer/editor';
|
|
5
|
-
import { NodeType } from '@intlayer/types/nodeType';
|
|
6
|
-
import { get } from 'svelte/store';
|
|
7
|
-
import ContentSelector from './ContentSelector.svelte';
|
|
8
|
-
import { useCommunicator } from './communicator';
|
|
9
|
-
import { useEditorEnabled } from './editorEnabled';
|
|
10
|
-
import { useFocusDictionary } from './focusDictionary';
|
|
11
|
-
import { useEditor } from './useEditor';
|
|
12
|
-
|
|
13
|
-
export let dictionaryKey: NodeProps['dictionaryKey'];
|
|
14
|
-
export let keyPath: NodeProps['keyPath'];
|
|
15
|
-
|
|
16
|
-
const { focusedContent, setFocusedContent } = useFocusDictionary();
|
|
17
|
-
const editorEnabled = useEditorEnabled();
|
|
18
|
-
const communicatorStore = useCommunicator();
|
|
19
|
-
const { enabled } = editorEnabled;
|
|
20
|
-
|
|
21
|
-
useEditor();
|
|
22
|
-
|
|
23
|
-
$: filteredKeyPath = keyPath.filter((key) => key.type !== NodeType.Translation);
|
|
24
|
-
|
|
25
|
-
$: isSelected =
|
|
26
|
-
$focusedContent?.dictionaryKey === dictionaryKey &&
|
|
27
|
-
($focusedContent?.keyPath?.length ?? 0) > 0 &&
|
|
28
|
-
isSameKeyPath($focusedContent?.keyPath ?? [], filteredKeyPath);
|
|
29
|
-
|
|
30
|
-
const handleSelect = () => {
|
|
31
|
-
setFocusedContent({
|
|
32
|
-
dictionaryKey,
|
|
33
|
-
keyPath: filteredKeyPath,
|
|
34
|
-
});
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const handleHover = () => {
|
|
38
|
-
const { postMessage, senderId } = get(communicatorStore);
|
|
39
|
-
postMessage({
|
|
40
|
-
type: `${MessageKey.INTLAYER_HOVERED_CONTENT_CHANGED}/post`,
|
|
41
|
-
data: {
|
|
42
|
-
dictionaryKey,
|
|
43
|
-
keyPath: filteredKeyPath,
|
|
44
|
-
},
|
|
45
|
-
senderId,
|
|
46
|
-
});
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const handleUnhover = () => {
|
|
50
|
-
const { postMessage, senderId } = get(communicatorStore);
|
|
51
|
-
postMessage({
|
|
52
|
-
type: `${MessageKey.INTLAYER_HOVERED_CONTENT_CHANGED}/post`,
|
|
53
|
-
data: null,
|
|
54
|
-
senderId,
|
|
55
|
-
});
|
|
56
|
-
};
|
|
57
|
-
</script>
|
|
58
|
-
|
|
59
|
-
{#if $enabled}
|
|
60
|
-
<ContentSelector
|
|
61
|
-
onPress={handleSelect}
|
|
62
|
-
isSelecting={isSelected}
|
|
63
|
-
onHover={handleHover}
|
|
64
|
-
onUnhover={handleUnhover}
|
|
65
|
-
>
|
|
66
|
-
<slot />
|
|
67
|
-
</ContentSelector>
|
|
68
|
-
{:else}
|
|
69
|
-
<slot />
|
|
70
|
-
{/if}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import type { NodeProps } from '@intlayer/core/interpreter';
|
|
2
|
-
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
3
|
-
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
4
|
-
$$bindings?: Bindings;
|
|
5
|
-
} & Exports;
|
|
6
|
-
(internal: unknown, props: Props & {
|
|
7
|
-
$$events?: Events;
|
|
8
|
-
$$slots?: Slots;
|
|
9
|
-
}): Exports & {
|
|
10
|
-
$set?: any;
|
|
11
|
-
$on?: any;
|
|
12
|
-
};
|
|
13
|
-
z_$$bindings?: Bindings;
|
|
14
|
-
}
|
|
15
|
-
type $$__sveltets_2_PropsWithChildren<Props, Slots> = Props & (Slots extends {
|
|
16
|
-
default: any;
|
|
17
|
-
} ? Props extends Record<string, never> ? any : {
|
|
18
|
-
children?: any;
|
|
19
|
-
} : {});
|
|
20
|
-
declare const ContentSelectorWrapper: $$__sveltets_2_IsomorphicComponent<$$__sveltets_2_PropsWithChildren<{
|
|
21
|
-
dictionaryKey: NodeProps["dictionaryKey"];
|
|
22
|
-
keyPath: NodeProps["keyPath"];
|
|
23
|
-
}, {
|
|
24
|
-
default: {};
|
|
25
|
-
}>, {
|
|
26
|
-
[evt: string]: CustomEvent<any>;
|
|
27
|
-
}, {
|
|
28
|
-
default: {};
|
|
29
|
-
}, {}, string>;
|
|
30
|
-
type ContentSelectorWrapper = InstanceType<typeof ContentSelectorWrapper>;
|
|
31
|
-
export default ContentSelectorWrapper;
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { type Writable } from 'svelte/store';
|
|
2
|
-
export type Communicator = {
|
|
3
|
-
postMessage: typeof window.postMessage;
|
|
4
|
-
allowedOrigins?: string[];
|
|
5
|
-
senderId: string;
|
|
6
|
-
};
|
|
7
|
-
export type CommunicatorOptions = Omit<Communicator, 'senderId'>;
|
|
8
|
-
export declare const createCommunicator: (options?: Partial<CommunicatorOptions>) => Writable<Communicator>;
|
|
9
|
-
export declare const useCommunicator: () => Writable<Communicator>;
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import configuration from '@intlayer/config/built';
|
|
2
|
-
import { getContext, setContext } from 'svelte';
|
|
3
|
-
import { writable } from 'svelte/store';
|
|
4
|
-
const randomUUID = () => Math.random().toString(36).slice(2);
|
|
5
|
-
const { editor } = configuration;
|
|
6
|
-
const postMessage = (data) => {
|
|
7
|
-
if (typeof window === 'undefined')
|
|
8
|
-
return;
|
|
9
|
-
const isInIframe = window.self !== window.top;
|
|
10
|
-
if (!isInIframe)
|
|
11
|
-
return;
|
|
12
|
-
if (editor.applicationURL.length > 0) {
|
|
13
|
-
window.postMessage(data, editor.applicationURL);
|
|
14
|
-
}
|
|
15
|
-
if (editor.editorURL.length > 0) {
|
|
16
|
-
window.parent.postMessage(data, editor.editorURL);
|
|
17
|
-
}
|
|
18
|
-
if (editor.cmsURL.length > 0) {
|
|
19
|
-
window.parent.postMessage(data, editor.cmsURL);
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
const defaultValue = {
|
|
23
|
-
postMessage,
|
|
24
|
-
allowedOrigins: [
|
|
25
|
-
editor?.applicationURL,
|
|
26
|
-
editor?.editorURL,
|
|
27
|
-
editor?.cmsURL,
|
|
28
|
-
],
|
|
29
|
-
senderId: randomUUID(),
|
|
30
|
-
};
|
|
31
|
-
const COMMUNICATOR_KEY = Symbol('INTLAYER_COMMUNICATOR');
|
|
32
|
-
export const createCommunicator = (options = {}) => {
|
|
33
|
-
const store = writable({
|
|
34
|
-
...defaultValue,
|
|
35
|
-
...options,
|
|
36
|
-
});
|
|
37
|
-
setContext(COMMUNICATOR_KEY, store);
|
|
38
|
-
return store;
|
|
39
|
-
};
|
|
40
|
-
export const useCommunicator = () => {
|
|
41
|
-
let context;
|
|
42
|
-
// `getContext` must only run inside a component.
|
|
43
|
-
// If this is called in plain JS, we catch the error and fallback.
|
|
44
|
-
try {
|
|
45
|
-
context = getContext(COMMUNICATOR_KEY);
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
// called outside component -> ignore, we’ll use global store
|
|
49
|
-
}
|
|
50
|
-
if (!context) {
|
|
51
|
-
return createCommunicator();
|
|
52
|
-
}
|
|
53
|
-
return context;
|
|
54
|
-
};
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { Dictionary, LocalDictionaryId } from '@intlayer/types/dictionary';
|
|
2
|
-
export type DictionaryContent = Record<LocalDictionaryId, Dictionary>;
|
|
3
|
-
export declare const useDictionariesRecord: () => {
|
|
4
|
-
dictionariesRecord: import("svelte/store").Writable<DictionaryContent | undefined>;
|
|
5
|
-
};
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { MessageKey } from '@intlayer/editor';
|
|
2
|
-
import { useCrossFrameState } from './useCrossFrameState';
|
|
3
|
-
let loaded = false;
|
|
4
|
-
export const useDictionariesRecord = () => {
|
|
5
|
-
const [dictionariesRecord, setDictionariesRecord] = useCrossFrameState(MessageKey.INTLAYER_LOCALE_DICTIONARIES_CHANGED, {});
|
|
6
|
-
if (!loaded && typeof window !== 'undefined') {
|
|
7
|
-
// Load dictionaries dynamically to do not impact the bundle, and send them to the editor
|
|
8
|
-
import('@intlayer/unmerged-dictionaries-entry').then((mod) => {
|
|
9
|
-
const unmergedDictionaries = mod.getUnmergedDictionaries();
|
|
10
|
-
const dictionariesList = Object.fromEntries(Object.values(unmergedDictionaries)
|
|
11
|
-
.flat()
|
|
12
|
-
.map((dictionary) => [dictionary.localId, dictionary]));
|
|
13
|
-
setDictionariesRecord?.(dictionariesList);
|
|
14
|
-
});
|
|
15
|
-
loaded = true;
|
|
16
|
-
}
|
|
17
|
-
return { dictionariesRecord };
|
|
18
|
-
};
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { type Readable, type Writable } from 'svelte/store';
|
|
2
|
-
export type EditorEnabledStateProps = {
|
|
3
|
-
settingEnabled: Writable<boolean>;
|
|
4
|
-
wrapperEnabled: Writable<boolean>;
|
|
5
|
-
isInIframe: Writable<boolean>;
|
|
6
|
-
enabled: Readable<boolean>;
|
|
7
|
-
};
|
|
8
|
-
export declare const createEditorEnabledClient: () => {
|
|
9
|
-
settingEnabled: Writable<boolean>;
|
|
10
|
-
wrapperEnabled: Writable<boolean>;
|
|
11
|
-
isInIframe: Writable<boolean>;
|
|
12
|
-
enabled: Readable<boolean>;
|
|
13
|
-
};
|
|
14
|
-
export declare const useEditorEnabled: () => EditorEnabledStateProps;
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import configuration from '@intlayer/config/built';
|
|
2
|
-
import { getContext, setContext } from 'svelte';
|
|
3
|
-
import { derived, writable } from 'svelte/store';
|
|
4
|
-
const EDITOR_ENABLED_KEY = Symbol('EDITOR_ENABLED');
|
|
5
|
-
const detectIframe = () => {
|
|
6
|
-
if (typeof window === 'undefined')
|
|
7
|
-
return false;
|
|
8
|
-
return window.self !== window.top;
|
|
9
|
-
};
|
|
10
|
-
export const createEditorEnabledClient = () => {
|
|
11
|
-
const settingEnabled = writable(configuration.editor.enabled);
|
|
12
|
-
const wrapperEnabled = writable(false);
|
|
13
|
-
const isInIframe = writable(false);
|
|
14
|
-
const enabled = derived([settingEnabled, wrapperEnabled, isInIframe], ([$setting, $wrapper, $iframe]) => $setting && $wrapper && $iframe);
|
|
15
|
-
if (typeof window !== 'undefined') {
|
|
16
|
-
isInIframe.set(detectIframe());
|
|
17
|
-
}
|
|
18
|
-
const state = {
|
|
19
|
-
settingEnabled,
|
|
20
|
-
wrapperEnabled,
|
|
21
|
-
isInIframe,
|
|
22
|
-
enabled,
|
|
23
|
-
};
|
|
24
|
-
setContext(EDITOR_ENABLED_KEY, state);
|
|
25
|
-
return state;
|
|
26
|
-
};
|
|
27
|
-
export const useEditorEnabled = () => {
|
|
28
|
-
let context;
|
|
29
|
-
// `getContext` must only run inside a component.
|
|
30
|
-
// If this is called in plain JS, we catch the error and fallback.
|
|
31
|
-
try {
|
|
32
|
-
context = getContext(EDITOR_ENABLED_KEY);
|
|
33
|
-
}
|
|
34
|
-
catch {
|
|
35
|
-
// called outside component -> ignore, we’ll use global store
|
|
36
|
-
}
|
|
37
|
-
if (!context) {
|
|
38
|
-
return createEditorEnabledClient();
|
|
39
|
-
}
|
|
40
|
-
return context;
|
|
41
|
-
};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { KeyPath } from '@intlayer/types/keyPath';
|
|
2
|
-
import type { DictionaryKeys } from '@intlayer/types/module_augmentation';
|
|
3
|
-
import type { Writable } from 'svelte/store';
|
|
4
|
-
export type FocusedContent = {
|
|
5
|
-
dictionaryKey: DictionaryKeys;
|
|
6
|
-
keyPath: KeyPath[];
|
|
7
|
-
} | null;
|
|
8
|
-
export type FocusDictionaryStateProps = {
|
|
9
|
-
focusedContent?: Writable<FocusedContent | undefined>;
|
|
10
|
-
setFocusedContent: (content: FocusedContent) => void;
|
|
11
|
-
};
|
|
12
|
-
export declare const createFocusDictionaryClient: () => {
|
|
13
|
-
focusedContent: Writable<FocusedContent | undefined>;
|
|
14
|
-
setFocusedContent: (v: FocusedContent | ((prev: FocusedContent | undefined) => FocusedContent)) => void;
|
|
15
|
-
};
|
|
16
|
-
export declare const useFocusDictionary: () => FocusDictionaryStateProps;
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { MessageKey } from '@intlayer/editor';
|
|
2
|
-
import { getContext, setContext } from 'svelte';
|
|
3
|
-
import { useCrossFrameState } from './useCrossFrameState';
|
|
4
|
-
const FOCUS_DICTIONARY_KEY = Symbol('FOCUS_DICTIONARY');
|
|
5
|
-
export const createFocusDictionaryClient = () => {
|
|
6
|
-
const [focusedContent, setFocusedContent] = useCrossFrameState(MessageKey.INTLAYER_FOCUSED_CONTENT_CHANGED, null);
|
|
7
|
-
setContext(FOCUS_DICTIONARY_KEY, { focusedContent, setFocusedContent });
|
|
8
|
-
return { focusedContent, setFocusedContent };
|
|
9
|
-
};
|
|
10
|
-
export const useFocusDictionary = () => {
|
|
11
|
-
let context;
|
|
12
|
-
try {
|
|
13
|
-
context = getContext(FOCUS_DICTIONARY_KEY);
|
|
14
|
-
}
|
|
15
|
-
catch {
|
|
16
|
-
// called outside component -> ignore, we’ll use global store
|
|
17
|
-
}
|
|
18
|
-
if (!context) {
|
|
19
|
-
return createFocusDictionaryClient();
|
|
20
|
-
}
|
|
21
|
-
return context;
|
|
22
|
-
};
|
package/dist/editor/index.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export { default as ContentSelector } from './ContentSelector.svelte';
|
|
2
|
-
export { default as ContentSelectorWrapper } from './ContentSelectorWrapper.svelte';
|
|
3
|
-
export * from './communicator';
|
|
4
|
-
export * from './editorEnabled';
|
|
5
|
-
export * from './focusDictionary';
|
|
6
|
-
export * from './useCrossFrameMessageListener';
|
|
7
|
-
export * from './useCrossFrameState';
|
|
8
|
-
export * from './useEditor';
|
|
9
|
-
export * from './useIframeClickInterceptor';
|
package/dist/editor/index.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export { default as ContentSelector } from './ContentSelector.svelte';
|
|
2
|
-
export { default as ContentSelectorWrapper } from './ContentSelectorWrapper.svelte';
|
|
3
|
-
export * from './communicator';
|
|
4
|
-
export * from './editorEnabled';
|
|
5
|
-
export * from './focusDictionary';
|
|
6
|
-
export * from './useCrossFrameMessageListener';
|
|
7
|
-
export * from './useCrossFrameState';
|
|
8
|
-
export * from './useEditor';
|
|
9
|
-
export * from './useIframeClickInterceptor';
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { compareUrls } from '@intlayer/editor';
|
|
2
|
-
import { onDestroy } from 'svelte';
|
|
3
|
-
import { get } from 'svelte/store';
|
|
4
|
-
import { useCommunicator } from './communicator';
|
|
5
|
-
const subscribers = new Map();
|
|
6
|
-
let windowListenerAttached = false;
|
|
7
|
-
const addSubscriber = (key, cb) => {
|
|
8
|
-
let set = subscribers.get(key);
|
|
9
|
-
if (!set) {
|
|
10
|
-
set = new Set();
|
|
11
|
-
subscribers.set(key, set);
|
|
12
|
-
}
|
|
13
|
-
set.add(cb);
|
|
14
|
-
};
|
|
15
|
-
const removeSubscriber = (key, cb) => {
|
|
16
|
-
const set = subscribers.get(key);
|
|
17
|
-
if (!set)
|
|
18
|
-
return;
|
|
19
|
-
set.delete(cb);
|
|
20
|
-
if (set.size === 0)
|
|
21
|
-
subscribers.delete(key);
|
|
22
|
-
};
|
|
23
|
-
const ensureGlobalListener = (allowedOrigins, selfId) => {
|
|
24
|
-
if (windowListenerAttached)
|
|
25
|
-
return;
|
|
26
|
-
if (typeof window === 'undefined')
|
|
27
|
-
return;
|
|
28
|
-
window.addEventListener('message', (event) => {
|
|
29
|
-
const { type, data, senderId } = event.data ?? {};
|
|
30
|
-
if (!type)
|
|
31
|
-
return;
|
|
32
|
-
if (senderId === selfId)
|
|
33
|
-
return;
|
|
34
|
-
if (!allowedOrigins ||
|
|
35
|
-
allowedOrigins.includes('*') ||
|
|
36
|
-
allowedOrigins.some((o) => compareUrls(o, event.origin))) {
|
|
37
|
-
subscribers.get(type)?.forEach((cb) => {
|
|
38
|
-
cb(data);
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
windowListenerAttached = true;
|
|
43
|
-
};
|
|
44
|
-
export const useCrossFrameMessageListener = (key, onEventTriggered, autoCleanup = true) => {
|
|
45
|
-
const communicatorStore = useCommunicator();
|
|
46
|
-
const { allowedOrigins, senderId } = get(communicatorStore);
|
|
47
|
-
ensureGlobalListener(allowedOrigins, senderId);
|
|
48
|
-
if (onEventTriggered) {
|
|
49
|
-
const cb = onEventTriggered;
|
|
50
|
-
addSubscriber(key, cb);
|
|
51
|
-
if (autoCleanup) {
|
|
52
|
-
try {
|
|
53
|
-
onDestroy(() => removeSubscriber(key, cb));
|
|
54
|
-
}
|
|
55
|
-
catch {
|
|
56
|
-
// Might be called outside component, manual cleanup needed if needed
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
const postMessageWrapper = (data) => {
|
|
61
|
-
const { postMessage, senderId: currentSenderId } = get(communicatorStore);
|
|
62
|
-
postMessage({ type: key, data, senderId: currentSenderId }, '*');
|
|
63
|
-
};
|
|
64
|
-
return postMessageWrapper;
|
|
65
|
-
};
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { MessageKey } from '@intlayer/editor';
|
|
2
|
-
import { type Writable } from 'svelte/store';
|
|
3
|
-
export type CrossFrameStateOptions = {
|
|
4
|
-
emit?: boolean;
|
|
5
|
-
receive?: boolean;
|
|
6
|
-
};
|
|
7
|
-
export declare const useCrossFrameState: <S>(key: `${MessageKey}`, initialState?: S | (() => S), options?: CrossFrameStateOptions) => [Writable<S | undefined>, (v: S | ((prev: S | undefined) => S)) => void];
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { get, writable } from 'svelte/store';
|
|
2
|
-
import { useCommunicator } from './communicator';
|
|
3
|
-
import { useCrossFrameMessageListener } from './useCrossFrameMessageListener';
|
|
4
|
-
const crossFrameStateCache = new Map();
|
|
5
|
-
const resolveState = (state, prevState) => {
|
|
6
|
-
if (typeof state === 'function') {
|
|
7
|
-
return state(prevState);
|
|
8
|
-
}
|
|
9
|
-
return state;
|
|
10
|
-
};
|
|
11
|
-
const toSerializable = (obj) => {
|
|
12
|
-
if (obj === null || obj === undefined)
|
|
13
|
-
return obj;
|
|
14
|
-
return JSON.parse(JSON.stringify(obj));
|
|
15
|
-
};
|
|
16
|
-
export const useCrossFrameState = (key, initialState, options = { emit: true, receive: true }) => {
|
|
17
|
-
if (crossFrameStateCache.has(key)) {
|
|
18
|
-
const { state, setState } = crossFrameStateCache.get(key);
|
|
19
|
-
return [state, setState];
|
|
20
|
-
}
|
|
21
|
-
const { emit = true, receive = true } = options;
|
|
22
|
-
// Initialize state
|
|
23
|
-
const initialValue = resolveState(initialState);
|
|
24
|
-
const state = writable(initialValue);
|
|
25
|
-
const communicatorStore = useCommunicator();
|
|
26
|
-
const broadcastState = (value) => {
|
|
27
|
-
const { postMessage, senderId } = get(communicatorStore) ?? {};
|
|
28
|
-
if (!emit ||
|
|
29
|
-
typeof postMessage !== 'function' ||
|
|
30
|
-
typeof value === 'undefined') {
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
postMessage({
|
|
34
|
-
type: `${key}/post`,
|
|
35
|
-
data: value,
|
|
36
|
-
senderId,
|
|
37
|
-
}, '*');
|
|
38
|
-
};
|
|
39
|
-
const setState = (valueOrUpdater) => {
|
|
40
|
-
state.update((prev) => {
|
|
41
|
-
const next = resolveState(valueOrUpdater, prev);
|
|
42
|
-
const serialised = toSerializable(next);
|
|
43
|
-
broadcastState(serialised);
|
|
44
|
-
return serialised;
|
|
45
|
-
});
|
|
46
|
-
};
|
|
47
|
-
const postState = () => {
|
|
48
|
-
const { postMessage, senderId } = get(communicatorStore) ?? {};
|
|
49
|
-
if (typeof postMessage !== 'function')
|
|
50
|
-
return;
|
|
51
|
-
postMessage({
|
|
52
|
-
type: `${key}/post`,
|
|
53
|
-
data: get(state),
|
|
54
|
-
senderId,
|
|
55
|
-
}, '*');
|
|
56
|
-
};
|
|
57
|
-
// Emit initial state
|
|
58
|
-
broadcastState(initialValue);
|
|
59
|
-
// If receiving, ask for state
|
|
60
|
-
if (receive && typeof get(state) === 'undefined') {
|
|
61
|
-
const { postMessage, senderId } = get(communicatorStore) ?? {};
|
|
62
|
-
if (typeof postMessage === 'function') {
|
|
63
|
-
postMessage({ type: `${key}/get`, senderId }, '*');
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
// Listen for updates
|
|
67
|
-
const listenerKey = receive ? `${key}/post` : `${key}/ignore`;
|
|
68
|
-
useCrossFrameMessageListener(listenerKey, (data) => {
|
|
69
|
-
if (receive) {
|
|
70
|
-
state.set(data);
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
// Listen for requests
|
|
74
|
-
const getListenerKey = emit ? `${key}/get` : `${key}/ignore`;
|
|
75
|
-
useCrossFrameMessageListener(getListenerKey, (_) => {
|
|
76
|
-
if (emit) {
|
|
77
|
-
broadcastState(get(state));
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
crossFrameStateCache.set(key, { state, setState, postState });
|
|
81
|
-
return [state, setState];
|
|
82
|
-
};
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { MessageKey, mergeIframeClick } from '@intlayer/editor';
|
|
2
|
-
import { onDestroy, onMount } from 'svelte';
|
|
3
|
-
import { useCrossFrameMessageListener } from './useCrossFrameMessageListener';
|
|
4
|
-
export const useIframeClickInterceptor = () => {
|
|
5
|
-
const postMessage = useCrossFrameMessageListener(MessageKey.INTLAYER_IFRAME_CLICKED);
|
|
6
|
-
const handler = () => {
|
|
7
|
-
postMessage();
|
|
8
|
-
};
|
|
9
|
-
onMount(() => {
|
|
10
|
-
if (typeof window !== 'undefined') {
|
|
11
|
-
window.addEventListener('mousedown', handler);
|
|
12
|
-
}
|
|
13
|
-
});
|
|
14
|
-
onDestroy(() => {
|
|
15
|
-
if (typeof window !== 'undefined') {
|
|
16
|
-
window.removeEventListener('mousedown', handler);
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
};
|
|
20
|
-
export const useIframeClickMerger = () => {
|
|
21
|
-
useIframeClickInterceptor();
|
|
22
|
-
useCrossFrameMessageListener(MessageKey.INTLAYER_IFRAME_CLICKED, (data) => {
|
|
23
|
-
// mergeIframeClick(data); // mergeIframeClick expects an event, but data might be stripped?
|
|
24
|
-
// Actually mergeIframeClick logic in editor package probably dispatches a custom event or similar.
|
|
25
|
-
// The Vue implementation passes `mergeIframeClick` directly as the callback.
|
|
26
|
-
// Let's assume mergeIframeClick handles whatever data is passed, or we wrap it if needed.
|
|
27
|
-
mergeIframeClick(data);
|
|
28
|
-
});
|
|
29
|
-
};
|