@belocal/react 0.5.3 → 0.6.1

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/README.md CHANGED
@@ -84,7 +84,6 @@ The root provider component that initializes the Belocal engine.
84
84
  | Prop | Type | Required | Description |
85
85
  |------|------|----------|-------------|
86
86
  | `apiKey` | `string` | ✅ | Your Belocal API key |
87
- | `baseUrl` | `string` | ❌ | Custom API base URL |
88
87
  | `defaultLang` | `string` | ❌ | Default language for translations |
89
88
  | `children` | `React.ReactNode` | ✅ | Child components |
90
89
 
package/dist/index.cjs CHANGED
@@ -8,7 +8,6 @@ var jsxRuntime = require('react/jsx-runtime');
8
8
  var BelocalContext = react.createContext(null);
9
9
  var BelocalProvider = ({
10
10
  apiKey,
11
- baseUrl,
12
11
  defaultLang,
13
12
  batchWindowMs,
14
13
  timeoutMs,
@@ -17,7 +16,6 @@ var BelocalProvider = ({
17
16
  const contextValue = react.useMemo(() => {
18
17
  const engine = new jsSdk.BelocalEngine({
19
18
  apiKey,
20
- baseUrl,
21
19
  batchWindowMs,
22
20
  timeoutMs
23
21
  });
@@ -25,7 +23,7 @@ var BelocalProvider = ({
25
23
  engine,
26
24
  defaultLang
27
25
  };
28
- }, [apiKey, baseUrl, defaultLang, batchWindowMs, timeoutMs]);
26
+ }, [apiKey, batchWindowMs, timeoutMs]);
29
27
  return /* @__PURE__ */ jsxRuntime.jsx(BelocalContext.Provider, { value: contextValue, children });
30
28
  };
31
29
  var useBelocalContext = () => {
@@ -40,83 +38,76 @@ var useBelocalContext = () => {
40
38
  var useBelocal = () => {
41
39
  return useBelocalContext();
42
40
  };
43
- var translationCache = /* @__PURE__ */ new Map();
44
- var pendingRequests = /* @__PURE__ */ new Map();
45
- var Translate = ({
41
+ var useTranslationContext = (context, user_context) => {
42
+ const finalContext = react.useMemo(() => {
43
+ const ctx = { ...context };
44
+ if (user_context) {
45
+ ctx.user_context = user_context;
46
+ }
47
+ return ctx;
48
+ }, [context, user_context]);
49
+ const finalContextKey = react.useMemo(() => {
50
+ const keys = Object.keys(finalContext).sort();
51
+ return keys.map((k) => `${k}:${finalContext[k]}`).join("|");
52
+ }, [finalContext]);
53
+ return { finalContext, finalContextKey };
54
+ };
55
+ var useTranslation = ({
46
56
  text,
47
- lang,
57
+ targetLang,
48
58
  source_lang,
49
- context,
50
- user_context,
59
+ finalContext,
60
+ finalContextKey,
51
61
  fallback,
52
62
  onSuccess,
53
63
  onError,
54
- dots
64
+ engine
55
65
  }) => {
56
- const { engine, defaultLang } = useBelocal();
57
66
  const [translatedText, setTranslatedText] = react.useState(fallback || text);
58
67
  const [isLoading, setIsLoading] = react.useState(false);
59
- const [dotCount, setDotCount] = react.useState(1);
60
- const targetLang = lang ?? defaultLang;
61
- const finalContext = react.useMemo(() => {
62
- const ctx = { ...context };
63
- if (user_context) {
64
- ctx.user_context = user_context;
65
- }
66
- return ctx;
67
- }, [context, user_context]);
68
- const cacheKey = react.useMemo(() => {
69
- return JSON.stringify({
70
- text,
71
- targetLang,
72
- source_lang,
73
- finalContext
74
- });
75
- }, [text, targetLang, source_lang, finalContext]);
76
- const attachHandlers = (promise) => {
77
- setIsLoading(true);
78
- promise.then((translation) => {
79
- setTranslatedText(translation);
80
- onSuccess?.(translation);
81
- }).catch((error) => {
82
- console.error("Translation error:", error);
83
- setTranslatedText(text);
84
- onError?.(error instanceof Error ? error : new Error(String(error)));
85
- }).finally(() => {
86
- setIsLoading(false);
87
- });
88
- };
68
+ const requestIdRef = react.useRef(0);
69
+ const onSuccessRef = react.useRef(onSuccess);
70
+ const onErrorRef = react.useRef(onError);
71
+ react.useEffect(() => {
72
+ onSuccessRef.current = onSuccess;
73
+ }, [onSuccess]);
74
+ react.useEffect(() => {
75
+ onErrorRef.current = onError;
76
+ }, [onError]);
89
77
  react.useEffect(() => {
90
78
  if (!targetLang) {
91
79
  setTranslatedText(text);
92
80
  return;
93
81
  }
94
- const cachedTranslation = translationCache.get(cacheKey);
95
- if (cachedTranslation) {
96
- setTranslatedText(cachedTranslation);
97
- return;
98
- }
99
- const pendingRequest = pendingRequests.get(cacheKey);
100
- if (pendingRequest) {
101
- attachHandlers(pendingRequest);
102
- return;
103
- }
104
- const translationPromise = engine.translate(text, targetLang, source_lang, finalContext).then((translation) => {
105
- translationCache.set(cacheKey, translation);
106
- pendingRequests.delete(cacheKey);
107
- return translation;
82
+ const currentRequestId = ++requestIdRef.current;
83
+ setIsLoading(true);
84
+ engine.translate(text, targetLang, source_lang, finalContext).then((translation) => {
85
+ if (currentRequestId === requestIdRef.current) {
86
+ setTranslatedText(translation);
87
+ onSuccessRef.current?.(translation);
88
+ }
108
89
  }).catch((error) => {
109
- pendingRequests.delete(cacheKey);
110
- throw error;
90
+ if (currentRequestId === requestIdRef.current) {
91
+ console.error("Translation error:", error);
92
+ setTranslatedText(text);
93
+ onErrorRef.current?.(error instanceof Error ? error : new Error(String(error)));
94
+ }
95
+ }).finally(() => {
96
+ if (currentRequestId === requestIdRef.current) {
97
+ setIsLoading(false);
98
+ }
111
99
  });
112
- pendingRequests.set(cacheKey, translationPromise);
113
- attachHandlers(translationPromise);
114
- }, [text, targetLang, source_lang, finalContext, engine, cacheKey]);
100
+ return () => {
101
+ requestIdRef.current++;
102
+ };
103
+ }, [text, targetLang, source_lang, finalContextKey, engine]);
104
+ return { translatedText, isLoading };
105
+ };
106
+ var useLoadingDots = (isLoading, dots) => {
107
+ const [dotCount, setDotCount] = react.useState(1);
115
108
  react.useEffect(() => {
116
109
  if (!isLoading || !dots) {
117
- if (!isLoading) {
118
- setDotCount(1);
119
- }
110
+ setDotCount(1);
120
111
  return;
121
112
  }
122
113
  const interval = setInterval(() => {
@@ -125,7 +116,38 @@ var Translate = ({
125
116
  return () => clearInterval(interval);
126
117
  }, [isLoading, dots]);
127
118
  if (isLoading && dots) {
128
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: ".".repeat(dotCount) });
119
+ return ".".repeat(dotCount);
120
+ }
121
+ return null;
122
+ };
123
+ var Translate = ({
124
+ text,
125
+ lang,
126
+ source_lang,
127
+ context,
128
+ user_context,
129
+ fallback,
130
+ onSuccess,
131
+ onError,
132
+ dots
133
+ }) => {
134
+ const { engine, defaultLang } = useBelocal();
135
+ const targetLang = lang ?? defaultLang;
136
+ const { finalContext, finalContextKey } = useTranslationContext(context, user_context);
137
+ const { translatedText, isLoading } = useTranslation({
138
+ text,
139
+ targetLang,
140
+ source_lang,
141
+ finalContext,
142
+ finalContextKey,
143
+ fallback,
144
+ onSuccess,
145
+ onError,
146
+ engine
147
+ });
148
+ const loadingDots = useLoadingDots(isLoading, dots);
149
+ if (loadingDots) {
150
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: loadingDots });
129
151
  }
130
152
  if (isLoading) {
131
153
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback || text });
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/BelocalProvider.tsx","../src/useBelocal.ts","../src/Translate.tsx"],"names":["createContext","useMemo","BelocalEngine","useContext","useState","useEffect","jsx","Fragment"],"mappings":"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","file":"index.cjs","sourcesContent":["import React, { createContext, useContext, useMemo } from 'react';\nimport { BelocalEngine } from '@belocal/js-sdk';\n\nexport type BelocalProviderProps = {\n apiKey: string;\n baseUrl?: string;\n defaultLang?: string;\n batchWindowMs?: number;\n timeoutMs?: number;\n children: React.ReactNode;\n};\n\ntype BelocalContextType = {\n engine: BelocalEngine;\n defaultLang?: string;\n};\n\nconst BelocalContext = createContext<BelocalContextType | null>(null);\n\nexport const BelocalProvider: React.FC<BelocalProviderProps> = ({\n apiKey,\n baseUrl,\n defaultLang,\n batchWindowMs,\n timeoutMs,\n children,\n}) => {\n const contextValue = useMemo(() => {\n const engine = new BelocalEngine({\n apiKey,\n baseUrl,\n batchWindowMs,\n timeoutMs,\n });\n\n return {\n engine,\n defaultLang,\n };\n }, [apiKey, baseUrl, defaultLang, batchWindowMs, timeoutMs]);\n\n return (\n <BelocalContext.Provider value={contextValue}>\n {children}\n </BelocalContext.Provider>\n );\n};\n\nexport const useBelocalContext = () => {\n const context = useContext(BelocalContext);\n if (!context) {\n throw new Error('useBelocalContext must be used within a BelocalProvider');\n }\n return context;\n};\n\n","import { useBelocalContext } from './BelocalProvider';\n\nexport const useBelocal = () => {\n return useBelocalContext();\n};\n\n","import React, { useState, useEffect, useMemo } from 'react';\nimport { useBelocal } from './useBelocal';\n\nconst translationCache = new Map<string, string>();\nconst pendingRequests = new Map<string, Promise<string>>();\n\nexport type TranslateProps = {\n text: string;\n lang?: string;\n source_lang?: string;\n context?: Record<string, string>;\n user_context?: string;\n fallback?: string;\n onSuccess?: (translation: string) => void;\n onError?: (error: Error) => void;\n dots?: boolean;\n};\n\nexport const Translate: React.FC<TranslateProps> = ({\n text,\n lang,\n source_lang,\n context,\n user_context,\n fallback,\n onSuccess,\n onError,\n dots,\n}) => {\n const { engine, defaultLang } = useBelocal();\n const [translatedText, setTranslatedText] = useState<string>(fallback || text);\n const [isLoading, setIsLoading] = useState<boolean>(false);\n const [dotCount, setDotCount] = useState<number>(1);\n\n const targetLang = lang ?? defaultLang;\n\n const finalContext = useMemo(() => {\n const ctx: Record<string, string> = { ...context };\n if (user_context) {\n ctx.user_context = user_context;\n }\n return ctx;\n }, [context, user_context]);\n\n const cacheKey = useMemo(() => {\n return JSON.stringify({\n text,\n targetLang,\n source_lang,\n finalContext,\n });\n }, [text, targetLang, source_lang, finalContext]);\n\n const attachHandlers = (promise: Promise<string>) => {\n setIsLoading(true);\n promise\n .then((translation) => {\n setTranslatedText(translation);\n onSuccess?.(translation);\n })\n .catch((error) => {\n console.error('Translation error:', error);\n setTranslatedText(text);\n onError?.(error instanceof Error ? error : new Error(String(error)));\n })\n .finally(() => {\n setIsLoading(false);\n });\n };\n\n useEffect(() => {\n if (!targetLang) {\n setTranslatedText(text);\n return;\n }\n\n const cachedTranslation = translationCache.get(cacheKey);\n if (cachedTranslation) {\n setTranslatedText(cachedTranslation);\n return;\n }\n\n const pendingRequest = pendingRequests.get(cacheKey);\n if (pendingRequest) {\n attachHandlers(pendingRequest);\n return;\n }\n\n const translationPromise = engine\n .translate(text, targetLang, source_lang, finalContext)\n .then((translation) => {\n translationCache.set(cacheKey, translation);\n pendingRequests.delete(cacheKey);\n return translation;\n })\n .catch((error) => {\n pendingRequests.delete(cacheKey);\n throw error;\n });\n\n pendingRequests.set(cacheKey, translationPromise);\n \n attachHandlers(translationPromise);\n }, [text, targetLang, source_lang, finalContext, engine, cacheKey]);\n\n useEffect(() => {\n if (!isLoading || !dots) {\n if (!isLoading) {\n setDotCount(1);\n }\n return;\n }\n\n const interval = setInterval(() => {\n setDotCount((prev) => (prev >= 3 ? 1 : prev + 1));\n }, 500);\n\n return () => clearInterval(interval);\n }, [isLoading, dots]);\n\n if (isLoading && dots) {\n return <>{'.'.repeat(dotCount)}</>;\n }\n\n if (isLoading) {\n return <>{fallback || text}</>;\n }\n\n return <>{translatedText}</>;\n};\n"]}
1
+ {"version":3,"sources":["../src/BelocalProvider.tsx","../src/useBelocal.ts","../src/hooks/useTranslationContext.ts","../src/hooks/useTranslation.ts","../src/hooks/useLoadingDots.ts","../src/Translate.tsx"],"names":["createContext","useMemo","BelocalEngine","useContext","useState","useRef","useEffect","jsx","Fragment"],"mappings":"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","file":"index.cjs","sourcesContent":["import React, { createContext, useContext, useMemo } from 'react';\nimport { BelocalEngine } from '@belocal/js-sdk';\n\nexport type BelocalProviderProps = {\n apiKey: string;\n defaultLang?: string;\n batchWindowMs?: number;\n timeoutMs?: number;\n children: React.ReactNode;\n};\n\ntype BelocalContextType = {\n engine: BelocalEngine;\n defaultLang?: string;\n};\n\nconst BelocalContext = createContext<BelocalContextType | null>(null);\n\nexport const BelocalProvider: React.FC<BelocalProviderProps> = ({\n apiKey,\n defaultLang,\n batchWindowMs,\n timeoutMs,\n children,\n}) => {\n const contextValue = useMemo(() => {\n const engine = new BelocalEngine({\n apiKey,\n batchWindowMs,\n timeoutMs,\n });\n\n return {\n engine,\n defaultLang,\n };\n }, [apiKey, batchWindowMs, timeoutMs]);\n\n return (\n <BelocalContext.Provider value={contextValue}>\n {children}\n </BelocalContext.Provider>\n );\n};\n\nexport const useBelocalContext = () => {\n const context = useContext(BelocalContext);\n if (!context) {\n throw new Error('useBelocalContext must be used within a BelocalProvider');\n }\n return context;\n};\n\n","import { useBelocalContext } from './BelocalProvider';\n\nexport const useBelocal = () => {\n return useBelocalContext();\n};\n\n","import { useMemo } from 'react';\n\nexport const useTranslationContext = (\n context?: Record<string, string>,\n user_context?: string\n) => {\n const finalContext = useMemo(() => {\n const ctx: Record<string, string> = { ...context };\n if (user_context) {\n ctx.user_context = user_context;\n }\n return ctx;\n }, [context, user_context]);\n\n const finalContextKey = useMemo(() => {\n const keys = Object.keys(finalContext).sort();\n return keys.map((k) => `${k}:${finalContext[k]}`).join('|');\n }, [finalContext]);\n\n return { finalContext, finalContextKey };\n};\n\n","import { useState, useEffect, useRef } from 'react';\nimport { BelocalEngine } from '@belocal/js-sdk';\n\ntype UseTranslationParams = {\n text: string;\n targetLang?: string;\n source_lang?: string;\n finalContext: Record<string, string>;\n finalContextKey: string;\n fallback?: string;\n onSuccess?: (translation: string) => void;\n onError?: (error: Error) => void;\n engine: BelocalEngine;\n};\n\nexport const useTranslation = ({\n text,\n targetLang,\n source_lang,\n finalContext,\n finalContextKey,\n fallback,\n onSuccess,\n onError,\n engine,\n}: UseTranslationParams) => {\n const [translatedText, setTranslatedText] = useState<string>(fallback || text);\n const [isLoading, setIsLoading] = useState<boolean>(false);\n const requestIdRef = useRef(0);\n const onSuccessRef = useRef(onSuccess);\n const onErrorRef = useRef(onError);\n\n useEffect(() => {\n onSuccessRef.current = onSuccess;\n }, [onSuccess]);\n\n useEffect(() => {\n onErrorRef.current = onError;\n }, [onError]);\n\n useEffect(() => {\n if (!targetLang) {\n setTranslatedText(text);\n return;\n }\n\n const currentRequestId = ++requestIdRef.current;\n setIsLoading(true);\n\n engine\n .translate(text, targetLang, source_lang, finalContext)\n .then((translation) => {\n if (currentRequestId === requestIdRef.current) {\n setTranslatedText(translation);\n onSuccessRef.current?.(translation);\n }\n })\n .catch((error) => {\n if (currentRequestId === requestIdRef.current) {\n console.error('Translation error:', error);\n setTranslatedText(text);\n onErrorRef.current?.(error instanceof Error ? error : new Error(String(error)));\n }\n })\n .finally(() => {\n if (currentRequestId === requestIdRef.current) {\n setIsLoading(false);\n }\n });\n\n return () => {\n // При размонтировании или новом запросе увеличиваем ID, чтобы старые запросы игнорировались\n requestIdRef.current++;\n };\n }, [text, targetLang, source_lang, finalContextKey, engine]);\n\n return { translatedText, isLoading };\n};\n\n","import { useState, useEffect } from 'react';\n\nexport const useLoadingDots = (isLoading: boolean, dots?: boolean) => {\n const [dotCount, setDotCount] = useState<number>(1);\n\n useEffect(() => {\n if (!isLoading || !dots) {\n setDotCount(1);\n return;\n }\n\n const interval = setInterval(() => {\n setDotCount((prev) => (prev >= 3 ? 1 : prev + 1));\n }, 500);\n\n return () => clearInterval(interval);\n }, [isLoading, dots]);\n\n if (isLoading && dots) {\n return '.'.repeat(dotCount);\n }\n\n return null;\n};\n\n","import React from 'react';\nimport { useBelocal } from './useBelocal';\nimport { useTranslationContext } from './hooks/useTranslationContext';\nimport { useTranslation } from './hooks/useTranslation';\nimport { useLoadingDots } from './hooks/useLoadingDots';\n\nexport type TranslateProps = {\n text: string;\n lang?: string;\n source_lang?: string;\n context?: Record<string, string>;\n user_context?: string;\n fallback?: string;\n onSuccess?: (translation: string) => void;\n onError?: (error: Error) => void;\n dots?: boolean;\n};\n\nexport const Translate: React.FC<TranslateProps> = ({\n text,\n lang,\n source_lang,\n context,\n user_context,\n fallback,\n onSuccess,\n onError,\n dots,\n}) => {\n const { engine, defaultLang } = useBelocal();\n const targetLang = lang ?? defaultLang;\n const { finalContext, finalContextKey } = useTranslationContext(context, user_context);\n \n const { translatedText, isLoading } = useTranslation({\n text,\n targetLang,\n source_lang,\n finalContext,\n finalContextKey,\n fallback,\n onSuccess,\n onError,\n engine,\n });\n\n const loadingDots = useLoadingDots(isLoading, dots);\n\n if (loadingDots) {\n return <>{loadingDots}</>;\n }\n\n if (isLoading) {\n return <>{fallback || text}</>;\n }\n\n return <>{translatedText}</>;\n};\n"]}
package/dist/index.d.cts CHANGED
@@ -3,7 +3,6 @@ import * as _belocal_js_sdk from '@belocal/js-sdk';
3
3
 
4
4
  type BelocalProviderProps = {
5
5
  apiKey: string;
6
- baseUrl?: string;
7
6
  defaultLang?: string;
8
7
  batchWindowMs?: number;
9
8
  timeoutMs?: number;
package/dist/index.d.ts CHANGED
@@ -3,7 +3,6 @@ import * as _belocal_js_sdk from '@belocal/js-sdk';
3
3
 
4
4
  type BelocalProviderProps = {
5
5
  apiKey: string;
6
- baseUrl?: string;
7
6
  defaultLang?: string;
8
7
  batchWindowMs?: number;
9
8
  timeoutMs?: number;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createContext, useMemo, useState, useEffect, useContext } from 'react';
1
+ import { createContext, useMemo, useContext, useState, useRef, useEffect } from 'react';
2
2
  import { BelocalEngine } from '@belocal/js-sdk';
3
3
  import { jsx, Fragment } from 'react/jsx-runtime';
4
4
 
@@ -6,7 +6,6 @@ import { jsx, Fragment } from 'react/jsx-runtime';
6
6
  var BelocalContext = createContext(null);
7
7
  var BelocalProvider = ({
8
8
  apiKey,
9
- baseUrl,
10
9
  defaultLang,
11
10
  batchWindowMs,
12
11
  timeoutMs,
@@ -15,7 +14,6 @@ var BelocalProvider = ({
15
14
  const contextValue = useMemo(() => {
16
15
  const engine = new BelocalEngine({
17
16
  apiKey,
18
- baseUrl,
19
17
  batchWindowMs,
20
18
  timeoutMs
21
19
  });
@@ -23,7 +21,7 @@ var BelocalProvider = ({
23
21
  engine,
24
22
  defaultLang
25
23
  };
26
- }, [apiKey, baseUrl, defaultLang, batchWindowMs, timeoutMs]);
24
+ }, [apiKey, batchWindowMs, timeoutMs]);
27
25
  return /* @__PURE__ */ jsx(BelocalContext.Provider, { value: contextValue, children });
28
26
  };
29
27
  var useBelocalContext = () => {
@@ -38,83 +36,76 @@ var useBelocalContext = () => {
38
36
  var useBelocal = () => {
39
37
  return useBelocalContext();
40
38
  };
41
- var translationCache = /* @__PURE__ */ new Map();
42
- var pendingRequests = /* @__PURE__ */ new Map();
43
- var Translate = ({
39
+ var useTranslationContext = (context, user_context) => {
40
+ const finalContext = useMemo(() => {
41
+ const ctx = { ...context };
42
+ if (user_context) {
43
+ ctx.user_context = user_context;
44
+ }
45
+ return ctx;
46
+ }, [context, user_context]);
47
+ const finalContextKey = useMemo(() => {
48
+ const keys = Object.keys(finalContext).sort();
49
+ return keys.map((k) => `${k}:${finalContext[k]}`).join("|");
50
+ }, [finalContext]);
51
+ return { finalContext, finalContextKey };
52
+ };
53
+ var useTranslation = ({
44
54
  text,
45
- lang,
55
+ targetLang,
46
56
  source_lang,
47
- context,
48
- user_context,
57
+ finalContext,
58
+ finalContextKey,
49
59
  fallback,
50
60
  onSuccess,
51
61
  onError,
52
- dots
62
+ engine
53
63
  }) => {
54
- const { engine, defaultLang } = useBelocal();
55
64
  const [translatedText, setTranslatedText] = useState(fallback || text);
56
65
  const [isLoading, setIsLoading] = useState(false);
57
- const [dotCount, setDotCount] = useState(1);
58
- const targetLang = lang ?? defaultLang;
59
- const finalContext = useMemo(() => {
60
- const ctx = { ...context };
61
- if (user_context) {
62
- ctx.user_context = user_context;
63
- }
64
- return ctx;
65
- }, [context, user_context]);
66
- const cacheKey = useMemo(() => {
67
- return JSON.stringify({
68
- text,
69
- targetLang,
70
- source_lang,
71
- finalContext
72
- });
73
- }, [text, targetLang, source_lang, finalContext]);
74
- const attachHandlers = (promise) => {
75
- setIsLoading(true);
76
- promise.then((translation) => {
77
- setTranslatedText(translation);
78
- onSuccess?.(translation);
79
- }).catch((error) => {
80
- console.error("Translation error:", error);
81
- setTranslatedText(text);
82
- onError?.(error instanceof Error ? error : new Error(String(error)));
83
- }).finally(() => {
84
- setIsLoading(false);
85
- });
86
- };
66
+ const requestIdRef = useRef(0);
67
+ const onSuccessRef = useRef(onSuccess);
68
+ const onErrorRef = useRef(onError);
69
+ useEffect(() => {
70
+ onSuccessRef.current = onSuccess;
71
+ }, [onSuccess]);
72
+ useEffect(() => {
73
+ onErrorRef.current = onError;
74
+ }, [onError]);
87
75
  useEffect(() => {
88
76
  if (!targetLang) {
89
77
  setTranslatedText(text);
90
78
  return;
91
79
  }
92
- const cachedTranslation = translationCache.get(cacheKey);
93
- if (cachedTranslation) {
94
- setTranslatedText(cachedTranslation);
95
- return;
96
- }
97
- const pendingRequest = pendingRequests.get(cacheKey);
98
- if (pendingRequest) {
99
- attachHandlers(pendingRequest);
100
- return;
101
- }
102
- const translationPromise = engine.translate(text, targetLang, source_lang, finalContext).then((translation) => {
103
- translationCache.set(cacheKey, translation);
104
- pendingRequests.delete(cacheKey);
105
- return translation;
80
+ const currentRequestId = ++requestIdRef.current;
81
+ setIsLoading(true);
82
+ engine.translate(text, targetLang, source_lang, finalContext).then((translation) => {
83
+ if (currentRequestId === requestIdRef.current) {
84
+ setTranslatedText(translation);
85
+ onSuccessRef.current?.(translation);
86
+ }
106
87
  }).catch((error) => {
107
- pendingRequests.delete(cacheKey);
108
- throw error;
88
+ if (currentRequestId === requestIdRef.current) {
89
+ console.error("Translation error:", error);
90
+ setTranslatedText(text);
91
+ onErrorRef.current?.(error instanceof Error ? error : new Error(String(error)));
92
+ }
93
+ }).finally(() => {
94
+ if (currentRequestId === requestIdRef.current) {
95
+ setIsLoading(false);
96
+ }
109
97
  });
110
- pendingRequests.set(cacheKey, translationPromise);
111
- attachHandlers(translationPromise);
112
- }, [text, targetLang, source_lang, finalContext, engine, cacheKey]);
98
+ return () => {
99
+ requestIdRef.current++;
100
+ };
101
+ }, [text, targetLang, source_lang, finalContextKey, engine]);
102
+ return { translatedText, isLoading };
103
+ };
104
+ var useLoadingDots = (isLoading, dots) => {
105
+ const [dotCount, setDotCount] = useState(1);
113
106
  useEffect(() => {
114
107
  if (!isLoading || !dots) {
115
- if (!isLoading) {
116
- setDotCount(1);
117
- }
108
+ setDotCount(1);
118
109
  return;
119
110
  }
120
111
  const interval = setInterval(() => {
@@ -123,7 +114,38 @@ var Translate = ({
123
114
  return () => clearInterval(interval);
124
115
  }, [isLoading, dots]);
125
116
  if (isLoading && dots) {
126
- return /* @__PURE__ */ jsx(Fragment, { children: ".".repeat(dotCount) });
117
+ return ".".repeat(dotCount);
118
+ }
119
+ return null;
120
+ };
121
+ var Translate = ({
122
+ text,
123
+ lang,
124
+ source_lang,
125
+ context,
126
+ user_context,
127
+ fallback,
128
+ onSuccess,
129
+ onError,
130
+ dots
131
+ }) => {
132
+ const { engine, defaultLang } = useBelocal();
133
+ const targetLang = lang ?? defaultLang;
134
+ const { finalContext, finalContextKey } = useTranslationContext(context, user_context);
135
+ const { translatedText, isLoading } = useTranslation({
136
+ text,
137
+ targetLang,
138
+ source_lang,
139
+ finalContext,
140
+ finalContextKey,
141
+ fallback,
142
+ onSuccess,
143
+ onError,
144
+ engine
145
+ });
146
+ const loadingDots = useLoadingDots(isLoading, dots);
147
+ if (loadingDots) {
148
+ return /* @__PURE__ */ jsx(Fragment, { children: loadingDots });
127
149
  }
128
150
  if (isLoading) {
129
151
  return /* @__PURE__ */ jsx(Fragment, { children: fallback || text });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/BelocalProvider.tsx","../src/useBelocal.ts","../src/Translate.tsx"],"names":["useMemo","jsx"],"mappings":";;;;;AAiBA,IAAM,cAAA,GAAiB,cAAyC,IAAI,CAAA;AAE7D,IAAM,kBAAkD,CAAC;AAAA,EAC9D,MAAA;AAAA,EACA,OAAA;AAAA,EACA,WAAA;AAAA,EACA,aAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAAA,KAAM;AACJ,EAAA,MAAM,YAAA,GAAe,QAAQ,MAAM;AACjC,IAAA,MAAM,MAAA,GAAS,IAAI,aAAA,CAAc;AAAA,MAC/B,MAAA;AAAA,MACA,OAAA;AAAA,MACA,aAAA;AAAA,MACA;AAAA,KACD,CAAA;AAED,IAAA,OAAO;AAAA,MACL,MAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF,GAAG,CAAC,MAAA,EAAQ,SAAS,WAAA,EAAa,aAAA,EAAe,SAAS,CAAC,CAAA;AAE3D,EAAA,2BACG,cAAA,CAAe,QAAA,EAAf,EAAwB,KAAA,EAAO,cAC7B,QAAA,EACH,CAAA;AAEJ;AAEO,IAAM,oBAAoB,MAAM;AACrC,EAAA,MAAM,OAAA,GAAU,WAAW,cAAc,CAAA;AACzC,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,MAAM,IAAI,MAAM,yDAAyD,CAAA;AAAA,EAC3E;AACA,EAAA,OAAO,OAAA;AACT,CAAA;;;ACpDO,IAAM,aAAa,MAAM;AAC9B,EAAA,OAAO,iBAAA,EAAkB;AAC3B;ACDA,IAAM,gBAAA,uBAAuB,GAAA,EAAoB;AACjD,IAAM,eAAA,uBAAsB,GAAA,EAA6B;AAclD,IAAM,YAAsC,CAAC;AAAA,EAClD,IAAA;AAAA,EACA,IAAA;AAAA,EACA,WAAA;AAAA,EACA,OAAA;AAAA,EACA,YAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAA,KAAM;AACJ,EAAA,MAAM,EAAE,MAAA,EAAQ,WAAA,EAAY,GAAI,UAAA,EAAW;AAC3C,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAI,QAAA,CAAiB,YAAY,IAAI,CAAA;AAC7E,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAkB,KAAK,CAAA;AACzD,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAiB,CAAC,CAAA;AAElD,EAAA,MAAM,aAAa,IAAA,IAAQ,WAAA;AAE3B,EAAA,MAAM,YAAA,GAAeA,QAAQ,MAAM;AACjC,IAAA,MAAM,GAAA,GAA8B,EAAE,GAAG,OAAA,EAAQ;AACjD,IAAA,IAAI,YAAA,EAAc;AAChB,MAAA,GAAA,CAAI,YAAA,GAAe,YAAA;AAAA,IACrB;AACA,IAAA,OAAO,GAAA;AAAA,EACT,CAAA,EAAG,CAAC,OAAA,EAAS,YAAY,CAAC,CAAA;AAE1B,EAAA,MAAM,QAAA,GAAWA,QAAQ,MAAM;AAC7B,IAAA,OAAO,KAAK,SAAA,CAAU;AAAA,MACpB,IAAA;AAAA,MACA,UAAA;AAAA,MACA,WAAA;AAAA,MACA;AAAA,KACD,CAAA;AAAA,EACH,GAAG,CAAC,IAAA,EAAM,UAAA,EAAY,WAAA,EAAa,YAAY,CAAC,CAAA;AAEhD,EAAA,MAAM,cAAA,GAAiB,CAAC,OAAA,KAA6B;AACnD,IAAA,YAAA,CAAa,IAAI,CAAA;AACjB,IAAA,OAAA,CACG,IAAA,CAAK,CAAC,WAAA,KAAgB;AACrB,MAAA,iBAAA,CAAkB,WAAW,CAAA;AAC7B,MAAA,SAAA,GAAY,WAAW,CAAA;AAAA,IACzB,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,KAAA,KAAU;AAChB,MAAA,OAAA,CAAQ,KAAA,CAAM,sBAAsB,KAAK,CAAA;AACzC,MAAA,iBAAA,CAAkB,IAAI,CAAA;AACtB,MAAA,OAAA,GAAU,KAAA,YAAiB,QAAQ,KAAA,GAAQ,IAAI,MAAM,MAAA,CAAO,KAAK,CAAC,CAAC,CAAA;AAAA,IACrE,CAAC,CAAA,CACA,OAAA,CAAQ,MAAM;AACb,MAAA,YAAA,CAAa,KAAK,CAAA;AAAA,IACpB,CAAC,CAAA;AAAA,EACL,CAAA;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,UAAA,EAAY;AACf,MAAA,iBAAA,CAAkB,IAAI,CAAA;AACtB,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,iBAAA,GAAoB,gBAAA,CAAiB,GAAA,CAAI,QAAQ,CAAA;AACvD,IAAA,IAAI,iBAAA,EAAmB;AACrB,MAAA,iBAAA,CAAkB,iBAAiB,CAAA;AACnC,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,cAAA,GAAiB,eAAA,CAAgB,GAAA,CAAI,QAAQ,CAAA;AACnD,IAAA,IAAI,cAAA,EAAgB;AAClB,MAAA,cAAA,CAAe,cAAc,CAAA;AAC7B,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,kBAAA,GAAqB,MAAA,CACxB,SAAA,CAAU,IAAA,EAAM,UAAA,EAAY,aAAa,YAAY,CAAA,CACrD,IAAA,CAAK,CAAC,WAAA,KAAgB;AACrB,MAAA,gBAAA,CAAiB,GAAA,CAAI,UAAU,WAAW,CAAA;AAC1C,MAAA,eAAA,CAAgB,OAAO,QAAQ,CAAA;AAC/B,MAAA,OAAO,WAAA;AAAA,IACT,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,KAAA,KAAU;AAChB,MAAA,eAAA,CAAgB,OAAO,QAAQ,CAAA;AAC/B,MAAA,MAAM,KAAA;AAAA,IACR,CAAC,CAAA;AAEH,IAAA,eAAA,CAAgB,GAAA,CAAI,UAAU,kBAAkB,CAAA;AAEhD,IAAA,cAAA,CAAe,kBAAkB,CAAA;AAAA,EACnC,CAAA,EAAG,CAAC,IAAA,EAAM,UAAA,EAAY,aAAa,YAAA,EAAc,MAAA,EAAQ,QAAQ,CAAC,CAAA;AAElE,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,SAAA,IAAa,CAAC,IAAA,EAAM;AACvB,MAAA,IAAI,CAAC,SAAA,EAAW;AACd,QAAA,WAAA,CAAY,CAAC,CAAA;AAAA,MACf;AACA,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,QAAA,GAAW,YAAY,MAAM;AACjC,MAAA,WAAA,CAAY,CAAC,IAAA,KAAU,IAAA,IAAQ,CAAA,GAAI,CAAA,GAAI,OAAO,CAAE,CAAA;AAAA,IAClD,GAAG,GAAG,CAAA;AAEN,IAAA,OAAO,MAAM,cAAc,QAAQ,CAAA;AAAA,EACrC,CAAA,EAAG,CAAC,SAAA,EAAW,IAAI,CAAC,CAAA;AAEpB,EAAA,IAAI,aAAa,IAAA,EAAM;AACrB,IAAA,uBAAOC,GAAAA,CAAA,QAAA,EAAA,EAAG,QAAA,EAAA,GAAA,CAAI,MAAA,CAAO,QAAQ,CAAA,EAAE,CAAA;AAAA,EACjC;AAEA,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,uBAAOA,GAAAA,CAAA,QAAA,EAAA,EAAG,QAAA,EAAA,QAAA,IAAY,IAAA,EAAK,CAAA;AAAA,EAC7B;AAEA,EAAA,uBAAOA,GAAAA,CAAA,QAAA,EAAA,EAAG,QAAA,EAAA,cAAA,EAAe,CAAA;AAC3B","file":"index.js","sourcesContent":["import React, { createContext, useContext, useMemo } from 'react';\nimport { BelocalEngine } from '@belocal/js-sdk';\n\nexport type BelocalProviderProps = {\n apiKey: string;\n baseUrl?: string;\n defaultLang?: string;\n batchWindowMs?: number;\n timeoutMs?: number;\n children: React.ReactNode;\n};\n\ntype BelocalContextType = {\n engine: BelocalEngine;\n defaultLang?: string;\n};\n\nconst BelocalContext = createContext<BelocalContextType | null>(null);\n\nexport const BelocalProvider: React.FC<BelocalProviderProps> = ({\n apiKey,\n baseUrl,\n defaultLang,\n batchWindowMs,\n timeoutMs,\n children,\n}) => {\n const contextValue = useMemo(() => {\n const engine = new BelocalEngine({\n apiKey,\n baseUrl,\n batchWindowMs,\n timeoutMs,\n });\n\n return {\n engine,\n defaultLang,\n };\n }, [apiKey, baseUrl, defaultLang, batchWindowMs, timeoutMs]);\n\n return (\n <BelocalContext.Provider value={contextValue}>\n {children}\n </BelocalContext.Provider>\n );\n};\n\nexport const useBelocalContext = () => {\n const context = useContext(BelocalContext);\n if (!context) {\n throw new Error('useBelocalContext must be used within a BelocalProvider');\n }\n return context;\n};\n\n","import { useBelocalContext } from './BelocalProvider';\n\nexport const useBelocal = () => {\n return useBelocalContext();\n};\n\n","import React, { useState, useEffect, useMemo } from 'react';\nimport { useBelocal } from './useBelocal';\n\nconst translationCache = new Map<string, string>();\nconst pendingRequests = new Map<string, Promise<string>>();\n\nexport type TranslateProps = {\n text: string;\n lang?: string;\n source_lang?: string;\n context?: Record<string, string>;\n user_context?: string;\n fallback?: string;\n onSuccess?: (translation: string) => void;\n onError?: (error: Error) => void;\n dots?: boolean;\n};\n\nexport const Translate: React.FC<TranslateProps> = ({\n text,\n lang,\n source_lang,\n context,\n user_context,\n fallback,\n onSuccess,\n onError,\n dots,\n}) => {\n const { engine, defaultLang } = useBelocal();\n const [translatedText, setTranslatedText] = useState<string>(fallback || text);\n const [isLoading, setIsLoading] = useState<boolean>(false);\n const [dotCount, setDotCount] = useState<number>(1);\n\n const targetLang = lang ?? defaultLang;\n\n const finalContext = useMemo(() => {\n const ctx: Record<string, string> = { ...context };\n if (user_context) {\n ctx.user_context = user_context;\n }\n return ctx;\n }, [context, user_context]);\n\n const cacheKey = useMemo(() => {\n return JSON.stringify({\n text,\n targetLang,\n source_lang,\n finalContext,\n });\n }, [text, targetLang, source_lang, finalContext]);\n\n const attachHandlers = (promise: Promise<string>) => {\n setIsLoading(true);\n promise\n .then((translation) => {\n setTranslatedText(translation);\n onSuccess?.(translation);\n })\n .catch((error) => {\n console.error('Translation error:', error);\n setTranslatedText(text);\n onError?.(error instanceof Error ? error : new Error(String(error)));\n })\n .finally(() => {\n setIsLoading(false);\n });\n };\n\n useEffect(() => {\n if (!targetLang) {\n setTranslatedText(text);\n return;\n }\n\n const cachedTranslation = translationCache.get(cacheKey);\n if (cachedTranslation) {\n setTranslatedText(cachedTranslation);\n return;\n }\n\n const pendingRequest = pendingRequests.get(cacheKey);\n if (pendingRequest) {\n attachHandlers(pendingRequest);\n return;\n }\n\n const translationPromise = engine\n .translate(text, targetLang, source_lang, finalContext)\n .then((translation) => {\n translationCache.set(cacheKey, translation);\n pendingRequests.delete(cacheKey);\n return translation;\n })\n .catch((error) => {\n pendingRequests.delete(cacheKey);\n throw error;\n });\n\n pendingRequests.set(cacheKey, translationPromise);\n \n attachHandlers(translationPromise);\n }, [text, targetLang, source_lang, finalContext, engine, cacheKey]);\n\n useEffect(() => {\n if (!isLoading || !dots) {\n if (!isLoading) {\n setDotCount(1);\n }\n return;\n }\n\n const interval = setInterval(() => {\n setDotCount((prev) => (prev >= 3 ? 1 : prev + 1));\n }, 500);\n\n return () => clearInterval(interval);\n }, [isLoading, dots]);\n\n if (isLoading && dots) {\n return <>{'.'.repeat(dotCount)}</>;\n }\n\n if (isLoading) {\n return <>{fallback || text}</>;\n }\n\n return <>{translatedText}</>;\n};\n"]}
1
+ {"version":3,"sources":["../src/BelocalProvider.tsx","../src/useBelocal.ts","../src/hooks/useTranslationContext.ts","../src/hooks/useTranslation.ts","../src/hooks/useLoadingDots.ts","../src/Translate.tsx"],"names":["useMemo","useState","useEffect","jsx"],"mappings":"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","file":"index.js","sourcesContent":["import React, { createContext, useContext, useMemo } from 'react';\nimport { BelocalEngine } from '@belocal/js-sdk';\n\nexport type BelocalProviderProps = {\n apiKey: string;\n defaultLang?: string;\n batchWindowMs?: number;\n timeoutMs?: number;\n children: React.ReactNode;\n};\n\ntype BelocalContextType = {\n engine: BelocalEngine;\n defaultLang?: string;\n};\n\nconst BelocalContext = createContext<BelocalContextType | null>(null);\n\nexport const BelocalProvider: React.FC<BelocalProviderProps> = ({\n apiKey,\n defaultLang,\n batchWindowMs,\n timeoutMs,\n children,\n}) => {\n const contextValue = useMemo(() => {\n const engine = new BelocalEngine({\n apiKey,\n batchWindowMs,\n timeoutMs,\n });\n\n return {\n engine,\n defaultLang,\n };\n }, [apiKey, batchWindowMs, timeoutMs]);\n\n return (\n <BelocalContext.Provider value={contextValue}>\n {children}\n </BelocalContext.Provider>\n );\n};\n\nexport const useBelocalContext = () => {\n const context = useContext(BelocalContext);\n if (!context) {\n throw new Error('useBelocalContext must be used within a BelocalProvider');\n }\n return context;\n};\n\n","import { useBelocalContext } from './BelocalProvider';\n\nexport const useBelocal = () => {\n return useBelocalContext();\n};\n\n","import { useMemo } from 'react';\n\nexport const useTranslationContext = (\n context?: Record<string, string>,\n user_context?: string\n) => {\n const finalContext = useMemo(() => {\n const ctx: Record<string, string> = { ...context };\n if (user_context) {\n ctx.user_context = user_context;\n }\n return ctx;\n }, [context, user_context]);\n\n const finalContextKey = useMemo(() => {\n const keys = Object.keys(finalContext).sort();\n return keys.map((k) => `${k}:${finalContext[k]}`).join('|');\n }, [finalContext]);\n\n return { finalContext, finalContextKey };\n};\n\n","import { useState, useEffect, useRef } from 'react';\nimport { BelocalEngine } from '@belocal/js-sdk';\n\ntype UseTranslationParams = {\n text: string;\n targetLang?: string;\n source_lang?: string;\n finalContext: Record<string, string>;\n finalContextKey: string;\n fallback?: string;\n onSuccess?: (translation: string) => void;\n onError?: (error: Error) => void;\n engine: BelocalEngine;\n};\n\nexport const useTranslation = ({\n text,\n targetLang,\n source_lang,\n finalContext,\n finalContextKey,\n fallback,\n onSuccess,\n onError,\n engine,\n}: UseTranslationParams) => {\n const [translatedText, setTranslatedText] = useState<string>(fallback || text);\n const [isLoading, setIsLoading] = useState<boolean>(false);\n const requestIdRef = useRef(0);\n const onSuccessRef = useRef(onSuccess);\n const onErrorRef = useRef(onError);\n\n useEffect(() => {\n onSuccessRef.current = onSuccess;\n }, [onSuccess]);\n\n useEffect(() => {\n onErrorRef.current = onError;\n }, [onError]);\n\n useEffect(() => {\n if (!targetLang) {\n setTranslatedText(text);\n return;\n }\n\n const currentRequestId = ++requestIdRef.current;\n setIsLoading(true);\n\n engine\n .translate(text, targetLang, source_lang, finalContext)\n .then((translation) => {\n if (currentRequestId === requestIdRef.current) {\n setTranslatedText(translation);\n onSuccessRef.current?.(translation);\n }\n })\n .catch((error) => {\n if (currentRequestId === requestIdRef.current) {\n console.error('Translation error:', error);\n setTranslatedText(text);\n onErrorRef.current?.(error instanceof Error ? error : new Error(String(error)));\n }\n })\n .finally(() => {\n if (currentRequestId === requestIdRef.current) {\n setIsLoading(false);\n }\n });\n\n return () => {\n // При размонтировании или новом запросе увеличиваем ID, чтобы старые запросы игнорировались\n requestIdRef.current++;\n };\n }, [text, targetLang, source_lang, finalContextKey, engine]);\n\n return { translatedText, isLoading };\n};\n\n","import { useState, useEffect } from 'react';\n\nexport const useLoadingDots = (isLoading: boolean, dots?: boolean) => {\n const [dotCount, setDotCount] = useState<number>(1);\n\n useEffect(() => {\n if (!isLoading || !dots) {\n setDotCount(1);\n return;\n }\n\n const interval = setInterval(() => {\n setDotCount((prev) => (prev >= 3 ? 1 : prev + 1));\n }, 500);\n\n return () => clearInterval(interval);\n }, [isLoading, dots]);\n\n if (isLoading && dots) {\n return '.'.repeat(dotCount);\n }\n\n return null;\n};\n\n","import React from 'react';\nimport { useBelocal } from './useBelocal';\nimport { useTranslationContext } from './hooks/useTranslationContext';\nimport { useTranslation } from './hooks/useTranslation';\nimport { useLoadingDots } from './hooks/useLoadingDots';\n\nexport type TranslateProps = {\n text: string;\n lang?: string;\n source_lang?: string;\n context?: Record<string, string>;\n user_context?: string;\n fallback?: string;\n onSuccess?: (translation: string) => void;\n onError?: (error: Error) => void;\n dots?: boolean;\n};\n\nexport const Translate: React.FC<TranslateProps> = ({\n text,\n lang,\n source_lang,\n context,\n user_context,\n fallback,\n onSuccess,\n onError,\n dots,\n}) => {\n const { engine, defaultLang } = useBelocal();\n const targetLang = lang ?? defaultLang;\n const { finalContext, finalContextKey } = useTranslationContext(context, user_context);\n \n const { translatedText, isLoading } = useTranslation({\n text,\n targetLang,\n source_lang,\n finalContext,\n finalContextKey,\n fallback,\n onSuccess,\n onError,\n engine,\n });\n\n const loadingDots = useLoadingDots(isLoading, dots);\n\n if (loadingDots) {\n return <>{loadingDots}</>;\n }\n\n if (isLoading) {\n return <>{fallback || text}</>;\n }\n\n return <>{translatedText}</>;\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@belocal/react",
3
- "version": "0.5.3",
3
+ "version": "0.6.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -28,7 +28,7 @@
28
28
  "react-dom": ">=17.0.0"
29
29
  },
30
30
  "dependencies": {
31
- "@belocal/js-sdk": "^0.5.0"
31
+ "@belocal/js-sdk": "^0.6.1"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/react": "^18.0.0",