@belocal/react 0.6.0 → 0.7.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/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,48 +38,76 @@ var useBelocalContext = () => {
40
38
  var useBelocal = () => {
41
39
  return useBelocalContext();
42
40
  };
43
- 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 = ({
44
56
  text,
45
- lang,
57
+ targetLang,
46
58
  source_lang,
47
- context,
48
- user_context,
59
+ finalContext,
60
+ finalContextKey,
49
61
  fallback,
50
62
  onSuccess,
51
63
  onError,
52
- dots
64
+ engine
53
65
  }) => {
54
- const { engine, defaultLang } = useBelocal();
55
66
  const [translatedText, setTranslatedText] = react.useState(fallback || text);
56
67
  const [isLoading, setIsLoading] = react.useState(false);
57
- const [dotCount, setDotCount] = react.useState(1);
58
- const targetLang = lang ?? defaultLang;
59
- const finalContext = { ...context };
60
- if (user_context) {
61
- finalContext.user_context = user_context;
62
- }
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]);
63
77
  react.useEffect(() => {
64
78
  if (!targetLang) {
65
79
  setTranslatedText(text);
66
80
  return;
67
81
  }
82
+ const currentRequestId = ++requestIdRef.current;
68
83
  setIsLoading(true);
69
84
  engine.translate(text, targetLang, source_lang, finalContext).then((translation) => {
70
- setTranslatedText(translation);
71
- onSuccess?.(translation);
85
+ if (currentRequestId === requestIdRef.current) {
86
+ setTranslatedText(translation);
87
+ onSuccessRef.current?.(translation);
88
+ }
72
89
  }).catch((error) => {
73
- console.error("Translation error:", error);
74
- setTranslatedText(text);
75
- onError?.(error instanceof Error ? error : new Error(String(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
+ }
76
95
  }).finally(() => {
77
- setIsLoading(false);
96
+ if (currentRequestId === requestIdRef.current) {
97
+ setIsLoading(false);
98
+ }
78
99
  });
79
- }, [text, targetLang, source_lang, finalContext, engine]);
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);
80
108
  react.useEffect(() => {
81
109
  if (!isLoading || !dots) {
82
- if (!isLoading) {
83
- setDotCount(1);
84
- }
110
+ setDotCount(1);
85
111
  return;
86
112
  }
87
113
  const interval = setInterval(() => {
@@ -90,7 +116,38 @@ var Translate = ({
90
116
  return () => clearInterval(interval);
91
117
  }, [isLoading, dots]);
92
118
  if (isLoading && dots) {
93
- 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 });
94
151
  }
95
152
  if (isLoading) {
96
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 } from 'react';\nimport { useBelocal } from './useBelocal';\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: Record<string, string> = { ...context };\n if (user_context) {\n finalContext.user_context = user_context;\n }\n\n useEffect(() => {\n if (!targetLang) {\n setTranslatedText(text);\n return;\n }\n\n setIsLoading(true);\n engine\n .translate(text, targetLang, source_lang, finalContext)\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 }, [text, targetLang, source_lang, finalContext, engine]);\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,48 +36,76 @@ var useBelocalContext = () => {
38
36
  var useBelocal = () => {
39
37
  return useBelocalContext();
40
38
  };
41
- 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 = ({
42
54
  text,
43
- lang,
55
+ targetLang,
44
56
  source_lang,
45
- context,
46
- user_context,
57
+ finalContext,
58
+ finalContextKey,
47
59
  fallback,
48
60
  onSuccess,
49
61
  onError,
50
- dots
62
+ engine
51
63
  }) => {
52
- const { engine, defaultLang } = useBelocal();
53
64
  const [translatedText, setTranslatedText] = useState(fallback || text);
54
65
  const [isLoading, setIsLoading] = useState(false);
55
- const [dotCount, setDotCount] = useState(1);
56
- const targetLang = lang ?? defaultLang;
57
- const finalContext = { ...context };
58
- if (user_context) {
59
- finalContext.user_context = user_context;
60
- }
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]);
61
75
  useEffect(() => {
62
76
  if (!targetLang) {
63
77
  setTranslatedText(text);
64
78
  return;
65
79
  }
80
+ const currentRequestId = ++requestIdRef.current;
66
81
  setIsLoading(true);
67
82
  engine.translate(text, targetLang, source_lang, finalContext).then((translation) => {
68
- setTranslatedText(translation);
69
- onSuccess?.(translation);
83
+ if (currentRequestId === requestIdRef.current) {
84
+ setTranslatedText(translation);
85
+ onSuccessRef.current?.(translation);
86
+ }
70
87
  }).catch((error) => {
71
- console.error("Translation error:", error);
72
- setTranslatedText(text);
73
- onError?.(error instanceof Error ? error : new Error(String(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
+ }
74
93
  }).finally(() => {
75
- setIsLoading(false);
94
+ if (currentRequestId === requestIdRef.current) {
95
+ setIsLoading(false);
96
+ }
76
97
  });
77
- }, [text, targetLang, source_lang, finalContext, engine]);
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);
78
106
  useEffect(() => {
79
107
  if (!isLoading || !dots) {
80
- if (!isLoading) {
81
- setDotCount(1);
82
- }
108
+ setDotCount(1);
83
109
  return;
84
110
  }
85
111
  const interval = setInterval(() => {
@@ -88,7 +114,38 @@ var Translate = ({
88
114
  return () => clearInterval(interval);
89
115
  }, [isLoading, dots]);
90
116
  if (isLoading && dots) {
91
- 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 });
92
149
  }
93
150
  if (isLoading) {
94
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":["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 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 } from 'react';\nimport { useBelocal } from './useBelocal';\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: Record<string, string> = { ...context };\n if (user_context) {\n finalContext.user_context = user_context;\n }\n\n useEffect(() => {\n if (!targetLang) {\n setTranslatedText(text);\n return;\n }\n\n setIsLoading(true);\n engine\n .translate(text, targetLang, source_lang, finalContext)\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 }, [text, targetLang, source_lang, finalContext, engine]);\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.6.0",
3
+ "version": "0.7.0",
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.6.0"
31
+ "@belocal/js-sdk": "^0.7.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/react": "^18.0.0",