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.
@@ -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: LocalesValues) => {
20
- readonly locale: LocalesValues;
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 { createCommunicator } from '../editor/communicator';
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
- // 2. Create Reactive State (Svelte 5)
24
+ // Create Reactive State (Svelte 5)
33
25
  // We make the locale a "rune" so updates propagate
34
26
  let locale = $state(initialLocale);
35
- // 3. Define the Context Object
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
- // 4. Set the Context
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 { onDestroy, onMount } from 'svelte';
2
+ import type { NodeProps } from '@intlayer/core/interpreter';
3
3
 
4
- // Props
5
- // biome-ignore lint/style/useConst: Svelte props need let
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
- <span
98
- bind:this={containerRef}
99
- role="button"
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
- </span>
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
- onClickOutside?: (() => void) | undefined;
21
- pressDuration?: number;
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;
@@ -1,46 +1,34 @@
1
- import { MessageKey } from '@intlayer/editor';
2
- import { onDestroy } from 'svelte';
3
- import { get } from 'svelte/store';
4
- import { useDictionariesRecord } from './dictionariesRecord';
5
- import { useEditorEnabled } from './editorEnabled';
6
- import { useCrossFrameMessageListener } from './useCrossFrameMessageListener';
7
- import { useIframeClickMerger } from './useIframeClickInterceptor';
8
- let initialized = false;
9
- let unsubscribeIsInIframe = null;
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 (typeof window === 'undefined')
14
+ if (!isEnabled)
12
15
  return;
13
- if (initialized)
14
- return;
15
- initialized = true;
16
- const editorEnabled = useEditorEnabled();
17
- useDictionariesRecord();
18
- useIframeClickMerger();
19
- // Listen for editor enabled
20
- useCrossFrameMessageListener(`${MessageKey.INTLAYER_EDITOR_ENABLED}/post`, (data) => {
21
- editorEnabled.wrapperEnabled.set(data);
22
- }, false);
23
- const getEditorEnabled = useCrossFrameMessageListener(`${MessageKey.INTLAYER_EDITOR_ENABLED}/get`, (data) => {
24
- editorEnabled.wrapperEnabled.set(data);
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
- initialized = false;
41
- if (unsubscribeIsInIframe) {
42
- unsubscribeIsInIframe();
43
- unsubscribeIsInIframe = null;
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 { useEditor as useIntlayerEditor, setMarkdownContext as setIntlayerMarkdown, setHTMLContext as setIntlayerHTML, };
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 { useEditor as useIntlayerEditor, setMarkdownContext as setIntlayerMarkdown, setHTMLContext as setIntlayerHTML, };
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 ContentSelectorWrapper from '../editor/ContentSelectorWrapper.svelte';
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
- <ContentSelectorWrapper {dictionaryKey} {keyPath}>
12
+ <ContentSelector {dictionaryKey} {keyPath}>
13
13
  <MarkdownMetadataRenderer
14
14
  {dictionaryKey}
15
15
  {keyPath}
16
16
  {value}
17
17
  {metadataKeyPath}
18
18
  />
19
- </ContentSelectorWrapper>
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 ContentSelectorWrapper from '../editor/ContentSelectorWrapper.svelte';
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
- <ContentSelectorWrapper {dictionaryKey} {keyPath}>
13
+ <ContentSelector {dictionaryKey} {keyPath}>
14
14
  <MarkdownRenderer {value} {overrides} />
15
- </ContentSelectorWrapper>
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 { ContentSelectorWrapper } from './editor';
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
- ? ContentSelectorWrapper
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
- ContentSelectorWrapper: () => null,
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.3",
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.3.3",
76
- "@intlayer/config": "8.3.3",
77
- "@intlayer/core": "8.3.3",
78
- "@intlayer/editor": "8.3.3",
79
- "@intlayer/types": "8.3.3",
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.2",
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
- };
@@ -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,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,2 +0,0 @@
1
- import { type MessageKey } from '@intlayer/editor';
2
- export declare const useCrossFrameMessageListener: <S>(key: `${MessageKey}` | `${MessageKey}/post` | `${MessageKey}/get`, onEventTriggered?: (data: S) => void, autoCleanup?: boolean) => (data?: S) => void;
@@ -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,2 +0,0 @@
1
- export declare const useIframeClickInterceptor: () => void;
2
- export declare const useIframeClickMerger: () => void;
@@ -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
- };