@tolgee/web 4.9.3-rc.128edeb.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.
Files changed (206) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +31 -0
  3. package/dist/tolgee-backend-fetch.cjs.min.js +2 -0
  4. package/dist/tolgee-backend-fetch.cjs.min.js.map +1 -0
  5. package/dist/tolgee-backend-fetch.esm.min.mjs +2 -0
  6. package/dist/tolgee-backend-fetch.esm.min.mjs.map +1 -0
  7. package/dist/tolgee-backend-fetch.umd.min.js +2 -0
  8. package/dist/tolgee-backend-fetch.umd.min.js.map +1 -0
  9. package/dist/tolgee-context-ui.cjs.min.js +143 -0
  10. package/dist/tolgee-context-ui.cjs.min.js.map +1 -0
  11. package/dist/tolgee-context-ui.esm.min.mjs +143 -0
  12. package/dist/tolgee-context-ui.esm.min.mjs.map +1 -0
  13. package/dist/tolgee-context-ui.umd.min.js +143 -0
  14. package/dist/tolgee-context-ui.umd.min.js.map +1 -0
  15. package/dist/tolgee-in-context-tools.cjs.min.js +143 -0
  16. package/dist/tolgee-in-context-tools.cjs.min.js.map +1 -0
  17. package/dist/tolgee-in-context-tools.esm.min.mjs +143 -0
  18. package/dist/tolgee-in-context-tools.esm.min.mjs.map +1 -0
  19. package/dist/tolgee-in-context-tools.umd.min.js +143 -0
  20. package/dist/tolgee-in-context-tools.umd.min.js.map +1 -0
  21. package/dist/tolgee-invisible-observer.cjs.min.js +2 -0
  22. package/dist/tolgee-invisible-observer.cjs.min.js.map +1 -0
  23. package/dist/tolgee-invisible-observer.esm.min.mjs +2 -0
  24. package/dist/tolgee-invisible-observer.esm.min.mjs.map +1 -0
  25. package/dist/tolgee-invisible-observer.umd.min.js +2 -0
  26. package/dist/tolgee-invisible-observer.umd.min.js.map +1 -0
  27. package/dist/tolgee-language-detector.cjs.min.js +2 -0
  28. package/dist/tolgee-language-detector.cjs.min.js.map +1 -0
  29. package/dist/tolgee-language-detector.esm.min.mjs +2 -0
  30. package/dist/tolgee-language-detector.esm.min.mjs.map +1 -0
  31. package/dist/tolgee-language-detector.umd.min.js +2 -0
  32. package/dist/tolgee-language-detector.umd.min.js.map +1 -0
  33. package/dist/tolgee-language-storage.cjs.min.js +2 -0
  34. package/dist/tolgee-language-storage.cjs.min.js.map +1 -0
  35. package/dist/tolgee-language-storage.esm.min.mjs +2 -0
  36. package/dist/tolgee-language-storage.esm.min.mjs.map +1 -0
  37. package/dist/tolgee-language-storage.umd.min.js +2 -0
  38. package/dist/tolgee-language-storage.umd.min.js.map +1 -0
  39. package/dist/tolgee-text-observer.cjs.min.js +2 -0
  40. package/dist/tolgee-text-observer.cjs.min.js.map +1 -0
  41. package/dist/tolgee-text-observer.esm.min.mjs +2 -0
  42. package/dist/tolgee-text-observer.esm.min.mjs.map +1 -0
  43. package/dist/tolgee-text-observer.umd.min.js +2 -0
  44. package/dist/tolgee-text-observer.umd.min.js.map +1 -0
  45. package/dist/tolgee-web-tolgee.cjs.min.js +2 -0
  46. package/dist/tolgee-web-tolgee.cjs.min.js.map +1 -0
  47. package/dist/tolgee-web-tolgee.esm.min.mjs +2 -0
  48. package/dist/tolgee-web-tolgee.esm.min.mjs.map +1 -0
  49. package/dist/tolgee-web-tolgee.umd.min.js +2 -0
  50. package/dist/tolgee-web-tolgee.umd.min.js.map +1 -0
  51. package/dist/tolgee-web.cjs.js +27076 -0
  52. package/dist/tolgee-web.cjs.js.map +1 -0
  53. package/dist/tolgee-web.cjs.min.js +143 -0
  54. package/dist/tolgee-web.cjs.min.js.map +1 -0
  55. package/dist/tolgee-web.esm.js +27052 -0
  56. package/dist/tolgee-web.esm.js.map +1 -0
  57. package/dist/tolgee-web.esm.min.mjs +143 -0
  58. package/dist/tolgee-web.esm.min.mjs.map +1 -0
  59. package/dist/tolgee-web.umd.js +27082 -0
  60. package/dist/tolgee-web.umd.js.map +1 -0
  61. package/dist/tolgee-web.umd.min.js +143 -0
  62. package/dist/tolgee-web.umd.min.js.map +1 -0
  63. package/lib/BackendFetch.d.ts +4 -0
  64. package/lib/BrowserExtensionPlugin/BrowserExtensionPlugin.d.ts +8 -0
  65. package/lib/BrowserExtensionPlugin/constants.d.ts +3 -0
  66. package/lib/BrowserExtensionPlugin/loadInContextLib.d.ts +1 -0
  67. package/lib/ContextUi.d.ts +2 -0
  68. package/lib/DevBackend.d.ts +2 -0
  69. package/lib/DevTools.d.ts +3 -0
  70. package/lib/InContextTools.d.ts +2 -0
  71. package/lib/InvisibleObserver.d.ts +2 -0
  72. package/lib/LanguageDetector.d.ts +3 -0
  73. package/lib/LanguageStorage.d.ts +3 -0
  74. package/lib/TextObserver.d.ts +2 -0
  75. package/lib/WebTolgee.d.ts +3 -0
  76. package/lib/constants.d.ts +5 -0
  77. package/lib/index.d.ts +5 -0
  78. package/lib/observers/general/DomHelper.d.ts +4 -0
  79. package/lib/observers/general/ElementHighlighter.d.ts +10 -0
  80. package/lib/observers/general/ElementMeta.d.ts +3 -0
  81. package/lib/observers/general/ElementRegistry.d.ts +11 -0
  82. package/lib/observers/general/ElementStore.d.ts +10 -0
  83. package/lib/observers/general/GeneralObserver.d.ts +12 -0
  84. package/lib/observers/general/MouseEventHandler.d.ts +13 -0
  85. package/lib/observers/general/NodeHandler.d.ts +6 -0
  86. package/lib/observers/general/helpers.d.ts +7 -0
  87. package/lib/observers/invisible/InvisibleWrapper.d.ts +2 -0
  88. package/lib/observers/invisible/ValueMemory.d.ts +5 -0
  89. package/lib/observers/invisible/encoderPolyfill.d.ts +8 -0
  90. package/lib/observers/invisible/secret.d.ts +6 -0
  91. package/lib/observers/text/TextWrapper.d.ts +8 -0
  92. package/lib/observers/text/helpers.d.ts +3 -0
  93. package/lib/tools/decodeApiKey.d.ts +1 -0
  94. package/lib/tools/extension.d.ts +28 -0
  95. package/lib/typedIndex.d.ts +11 -0
  96. package/lib/types.d.ts +28 -0
  97. package/lib/ui/KeyContextMenu/KeyContextMenu.d.ts +19 -0
  98. package/lib/ui/KeyDialog/KeyDialog.d.ts +23 -0
  99. package/lib/ui/KeyDialog/KeyForm.d.ts +2 -0
  100. package/lib/ui/KeyDialog/LanguageSelect.d.ts +2 -0
  101. package/lib/ui/KeyDialog/NewWindow.d.ts +2 -0
  102. package/lib/ui/KeyDialog/NsSelect.d.ts +9 -0
  103. package/lib/ui/KeyDialog/ScreenshotGallery/ExtensionPrompt.d.ts +6 -0
  104. package/lib/ui/KeyDialog/ScreenshotGallery/ScreenshotDetail.d.ts +8 -0
  105. package/lib/ui/KeyDialog/ScreenshotGallery/ScreenshotDropzone.d.ts +6 -0
  106. package/lib/ui/KeyDialog/ScreenshotGallery/ScreenshotGallery.d.ts +2 -0
  107. package/lib/ui/KeyDialog/ScreenshotGallery/ScreenshotThumbnail.d.ts +8 -0
  108. package/lib/ui/KeyDialog/ScreenshotGallery/utils.d.ts +3 -0
  109. package/lib/ui/KeyDialog/TranslationDialog.d.ts +2 -0
  110. package/lib/ui/KeyDialog/TranslationDialogContextProvider.d.ts +128 -0
  111. package/lib/ui/KeyDialog/TranslationDialogWrapper.d.ts +2 -0
  112. package/lib/ui/KeyDialog/TranslationFields.d.ts +2 -0
  113. package/lib/ui/KeyDialog/languageHelpers.d.ts +12 -0
  114. package/lib/ui/KeyDialog/tools.d.ts +3 -0
  115. package/lib/ui/ThemeProvider.d.ts +2 -0
  116. package/lib/ui/client/QueryProvider.d.ts +12 -0
  117. package/lib/ui/client/apiSchema.generated.d.ts +3283 -0
  118. package/lib/ui/client/client.d.ts +5 -0
  119. package/lib/ui/client/types.d.ts +58 -0
  120. package/lib/ui/client/useQueryApi.d.ts +84 -0
  121. package/lib/ui/common/BodyEnd.d.ts +13 -0
  122. package/lib/ui/common/FieldTitle.d.ts +2 -0
  123. package/lib/ui/common/LoadingButton.d.ts +7 -0
  124. package/lib/ui/index.d.ts +13 -0
  125. package/lib/ui/tools/createProvider.d.ts +5 -0
  126. package/lib/ui/tools/isLanguagePermitted.d.ts +1 -0
  127. package/lib/ui/tools/sleep.d.ts +1 -0
  128. package/package.json +91 -0
  129. package/src/BackendFetch.ts +64 -0
  130. package/src/BrowserExtensionPlugin/BrowserExtensionPlugin.ts +98 -0
  131. package/src/BrowserExtensionPlugin/constants.ts +3 -0
  132. package/src/BrowserExtensionPlugin/loadInContextLib.ts +32 -0
  133. package/src/ContextUi.ts +7 -0
  134. package/src/DevBackend.ts +30 -0
  135. package/src/DevTools.ts +9 -0
  136. package/src/InContextTools.ts +20 -0
  137. package/src/InvisibleObserver.ts +16 -0
  138. package/src/LanguageDetector.test.ts +19 -0
  139. package/src/LanguageDetector.ts +32 -0
  140. package/src/LanguageStorage.ts +23 -0
  141. package/src/TextObserver.ts +45 -0
  142. package/src/WebTolgee.ts +8 -0
  143. package/src/__test__/browser.extension.test.ts +70 -0
  144. package/src/__test__/observer.test.ts +13 -0
  145. package/src/__test__/testObserver.ts +106 -0
  146. package/src/__test__/testRetranslate.ts +47 -0
  147. package/src/constants.ts +12 -0
  148. package/src/index.ts +8 -0
  149. package/src/observers/general/DomHelper.ts +46 -0
  150. package/src/observers/general/ElementHighlighter.ts +72 -0
  151. package/src/observers/general/ElementMeta.ts +17 -0
  152. package/src/observers/general/ElementRegistry.ts +159 -0
  153. package/src/observers/general/ElementStore.ts +34 -0
  154. package/src/observers/general/GeneralObserver.ts +133 -0
  155. package/src/observers/general/MouseEventHandler.ts +199 -0
  156. package/src/observers/general/NodeHandler.ts +39 -0
  157. package/src/observers/general/helpers.ts +65 -0
  158. package/src/observers/invisible/InvisibleWrapper.test.ts +17 -0
  159. package/src/observers/invisible/InvisibleWrapper.ts +96 -0
  160. package/src/observers/invisible/ValueMemory.test.ts +25 -0
  161. package/src/observers/invisible/ValueMemory.ts +20 -0
  162. package/src/observers/invisible/encoderPolyfill.ts +96 -0
  163. package/src/observers/invisible/secret.test.ts +61 -0
  164. package/src/observers/invisible/secret.ts +68 -0
  165. package/src/observers/text/TextWrapper.ts +258 -0
  166. package/src/observers/text/helpers.ts +56 -0
  167. package/src/tools/decodeApiKey.test.ts +14 -0
  168. package/src/tools/decodeApiKey.ts +74 -0
  169. package/src/tools/extension.test.ts +159 -0
  170. package/src/tools/extension.ts +119 -0
  171. package/src/typedIndex.ts +13 -0
  172. package/src/types.ts +33 -0
  173. package/src/ui/KeyContextMenu/KeyContextMenu.test.ts +51 -0
  174. package/src/ui/KeyContextMenu/KeyContextMenu.tsx +108 -0
  175. package/src/ui/KeyDialog/KeyDialog.tsx +67 -0
  176. package/src/ui/KeyDialog/KeyForm.tsx +208 -0
  177. package/src/ui/KeyDialog/LanguageSelect.tsx +78 -0
  178. package/src/ui/KeyDialog/NewWindow.tsx +106 -0
  179. package/src/ui/KeyDialog/NsSelect.tsx +67 -0
  180. package/src/ui/KeyDialog/ScreenshotGallery/ExtensionPrompt.tsx +97 -0
  181. package/src/ui/KeyDialog/ScreenshotGallery/ScreenshotDetail.tsx +33 -0
  182. package/src/ui/KeyDialog/ScreenshotGallery/ScreenshotDropzone.tsx +138 -0
  183. package/src/ui/KeyDialog/ScreenshotGallery/ScreenshotGallery.tsx +240 -0
  184. package/src/ui/KeyDialog/ScreenshotGallery/ScreenshotThumbnail.tsx +113 -0
  185. package/src/ui/KeyDialog/ScreenshotGallery/utils.ts +17 -0
  186. package/src/ui/KeyDialog/TranslationDialog.tsx +14 -0
  187. package/src/ui/KeyDialog/TranslationDialogContextProvider.tsx +464 -0
  188. package/src/ui/KeyDialog/TranslationDialogWrapper.tsx +44 -0
  189. package/src/ui/KeyDialog/TranslationFields.tsx +113 -0
  190. package/src/ui/KeyDialog/languageHelpers.ts +18 -0
  191. package/src/ui/KeyDialog/tools.ts +30 -0
  192. package/src/ui/ThemeProvider.tsx +71 -0
  193. package/src/ui/client/QueryProvider.tsx +38 -0
  194. package/src/ui/client/apiSchema.generated.ts +3281 -0
  195. package/src/ui/client/client.ts +155 -0
  196. package/src/ui/client/types.ts +113 -0
  197. package/src/ui/client/useQueryApi.ts +121 -0
  198. package/src/ui/common/BodyEnd.tsx +44 -0
  199. package/src/ui/common/FieldTitle.tsx +9 -0
  200. package/src/ui/common/LoadingButton.tsx +45 -0
  201. package/src/ui/index.ts +88 -0
  202. package/src/ui/screenshots/ScreenshotPreview.tsx +18 -0
  203. package/src/ui/tools/createProvider.tsx +54 -0
  204. package/src/ui/tools/isLanguagePermitted.ts +14 -0
  205. package/src/ui/tools/sleep.ts +2 -0
  206. package/types/index.d.ts +9 -0
@@ -0,0 +1,155 @@
1
+ import { getProjectIdFromApiKey } from '../../tools/decodeApiKey';
2
+ import { paths } from './apiSchema.generated';
3
+ import { GlobalOptions } from './QueryProvider';
4
+ import { RequestParamsType, ResponseContent } from './types';
5
+
6
+ type Params = {
7
+ [k: string]: string | string[] | null | undefined | Params;
8
+ };
9
+
10
+ async function getResObject(r: Response) {
11
+ const textBody = await r.text();
12
+ try {
13
+ return JSON.parse(textBody);
14
+ } catch (e) {
15
+ return textBody;
16
+ }
17
+ }
18
+
19
+ const flattenParams = (
20
+ params: Params | null | undefined
21
+ ): Record<string, string | string[]> => {
22
+ if (params) {
23
+ return Object.entries(params).reduce(
24
+ (acc, [key, value]) =>
25
+ Array.isArray(value) || typeof value !== 'object'
26
+ ? { ...acc, [key]: value }
27
+ : { ...acc, ...flattenParams(value) },
28
+ {}
29
+ );
30
+ } else {
31
+ return {};
32
+ }
33
+ };
34
+
35
+ function buildQuery(object: { [key: string]: any }): string {
36
+ return Object.keys(object)
37
+ .filter((k) => !!object[k])
38
+ .map((k) => {
39
+ if (Array.isArray(object[k])) {
40
+ return object[k]
41
+ .map((v: any) => encodeURIComponent(k) + '=' + encodeURIComponent(v))
42
+ .join('&');
43
+ } else {
44
+ return encodeURIComponent(k) + '=' + encodeURIComponent(object[k]);
45
+ }
46
+ })
47
+ .join('&');
48
+ }
49
+
50
+ async function customFetch(
51
+ input: RequestInfo,
52
+ options: GlobalOptions,
53
+ init?: RequestInit
54
+ ): Promise<Response> {
55
+ if (options.apiUrl === undefined) {
56
+ throw 'Api url not specified';
57
+ }
58
+ if (options.apiKey === undefined) {
59
+ throw 'Api key not specified';
60
+ }
61
+
62
+ init = init || {};
63
+ init.headers = init.headers || {};
64
+ init.headers = {
65
+ ...init.headers,
66
+ 'X-API-Key': options.apiKey,
67
+ };
68
+
69
+ return fetch(options.apiUrl + input, init)
70
+ .then(async (r) => {
71
+ if (!r.ok) {
72
+ const data = await getResObject(r);
73
+ const message = `${r.status}: ${
74
+ data?.message || 'Error status code from server'
75
+ }`;
76
+ throw new Error(message);
77
+ }
78
+ return await getResObject(r);
79
+ })
80
+ .catch((e) => {
81
+ throw e.message || 'Failed to fetch';
82
+ });
83
+ }
84
+
85
+ export const addProjectIdToUrl = (url: string) => {
86
+ return url.replace('/projects/', '/projects/{projectId}/');
87
+ };
88
+
89
+ export async function client<
90
+ Url extends keyof Paths,
91
+ Method extends keyof Paths[Url],
92
+ Paths = paths
93
+ >(
94
+ url: Url,
95
+ method: Method,
96
+ request: RequestParamsType<Url, Method, Paths>,
97
+ options: GlobalOptions
98
+ ) {
99
+ const pathParams = (request as any)?.path || {};
100
+ let urlResult = url as string;
101
+
102
+ const projectId = getProjectIdFromApiKey(options.apiKey);
103
+ if (projectId !== undefined) {
104
+ pathParams.projectId = projectId;
105
+ urlResult = addProjectIdToUrl(urlResult);
106
+ }
107
+
108
+ if (pathParams) {
109
+ Object.entries(pathParams).forEach(([key, value]) => {
110
+ urlResult = urlResult.replace(`{${key}}`, value as any);
111
+ });
112
+ }
113
+
114
+ const formData = request?.content?.['multipart/form-data'] as Record<
115
+ string,
116
+ unknown
117
+ >;
118
+ let body: FormData | undefined = undefined;
119
+ if (formData) {
120
+ body = new FormData();
121
+ Object.entries(formData).forEach(([key, value]) => {
122
+ if (Array.isArray(value)) {
123
+ let fileName: undefined | string = undefined;
124
+ if (Object.prototype.toString.call(value) === '[object File]') {
125
+ fileName = (value as any as File).name;
126
+ }
127
+
128
+ value.forEach((item) => body?.append(key, item as any, fileName));
129
+ return;
130
+ }
131
+ body?.append(key, value as any);
132
+ });
133
+ }
134
+
135
+ const jsonBody = JSON.stringify(request?.content?.['application/json']);
136
+
137
+ const queryParams = request?.query as any;
138
+ let queryString = '';
139
+
140
+ const params = flattenParams(queryParams);
141
+ const query = buildQuery(params);
142
+ if (query) {
143
+ queryString = '?' + query;
144
+ }
145
+
146
+ return customFetch(urlResult + queryString, options, {
147
+ method: method as string,
148
+ body: body || jsonBody,
149
+ headers: jsonBody
150
+ ? {
151
+ 'Content-Type': 'application/json',
152
+ }
153
+ : undefined,
154
+ }) as Promise<ResponseContent<Url, Method, Paths>>;
155
+ }
@@ -0,0 +1,113 @@
1
+ import { paths } from './apiSchema.generated';
2
+
3
+ type ExcludeAk<T extends Record<string, Record<string, any>>> = Omit<
4
+ T,
5
+ 'query'
6
+ > & {
7
+ query?: Omit<T['query'], 'ak'>;
8
+ };
9
+
10
+ export type RequestParamsType<
11
+ Url extends keyof Paths,
12
+ Method extends keyof Paths[Url],
13
+ Paths = paths
14
+ > = ExcludeAk<
15
+ // @ts-ignore
16
+ Omit<OperationSchema<Url, Method, Paths>['parameters'], 'header'>
17
+ > &
18
+ OperationSchema<Url, Method, Paths>['requestBody'];
19
+
20
+ export type ResponseContent<
21
+ Url extends keyof Paths,
22
+ Method extends keyof Paths[Url],
23
+ Paths
24
+ > = OperationSchema<
25
+ Url,
26
+ Method,
27
+ Paths
28
+ >['responses'][200] extends NotNullAnyContent
29
+ ? OperationSchema<Url, Method, Paths>['responses'][200]['content']['*/*']
30
+ : OperationSchema<
31
+ Url,
32
+ Method,
33
+ Paths
34
+ >['responses'][200] extends NotNullJsonHalContent
35
+ ? OperationSchema<
36
+ Url,
37
+ Method,
38
+ Paths
39
+ >['responses'][200]['content']['application/hal+json']
40
+ : OperationSchema<
41
+ Url,
42
+ Method,
43
+ Paths
44
+ >['responses'][200] extends NotNullJsonContent
45
+ ? OperationSchema<
46
+ Url,
47
+ Method,
48
+ Paths
49
+ >['responses'][200]['content']['application/json']
50
+ : OperationSchema<
51
+ Url,
52
+ Method,
53
+ Paths
54
+ >['responses'][201] extends NotNullAnyContent
55
+ ? OperationSchema<Url, Method, Paths>['responses'][201]['content']['*/*']
56
+ : void;
57
+
58
+ type NotNullAnyContent = {
59
+ content: {
60
+ '*/*': any;
61
+ };
62
+ };
63
+
64
+ type NotNullJsonHalContent = {
65
+ content: {
66
+ 'application/hal+json': any;
67
+ };
68
+ };
69
+
70
+ type NotNullJsonContent = {
71
+ content: {
72
+ 'application/json': any;
73
+ };
74
+ };
75
+
76
+ type ResponseType = {
77
+ 200?:
78
+ | {
79
+ content?: {
80
+ '*/*'?: any;
81
+ 'application/json'?: any;
82
+ 'application/hal+json'?: any;
83
+ };
84
+ }
85
+ | unknown;
86
+ 201?:
87
+ | {
88
+ content?: {
89
+ '*/*'?: any;
90
+ };
91
+ }
92
+ | unknown;
93
+ };
94
+
95
+ type OperationSchemaType = {
96
+ requestBody?: {
97
+ content?: {
98
+ 'multipart/form-data'?: { [key: string]: any };
99
+ 'application/json'?: any;
100
+ };
101
+ };
102
+ parameters?: {
103
+ path?: { [key: string]: any };
104
+ query?: { [key: string]: { [key: string]: any } | any };
105
+ };
106
+ responses: ResponseType;
107
+ };
108
+
109
+ type OperationSchema<
110
+ Url extends keyof Paths,
111
+ Method extends keyof Paths[Url],
112
+ Paths = paths
113
+ > = Paths[Url][Method] extends OperationSchemaType ? Paths[Url][Method] : never;
@@ -0,0 +1,121 @@
1
+ import { RequestOptions } from 'https';
2
+ import { useCallback, useContext } from 'react';
3
+ import {
4
+ UseQueryOptions,
5
+ useQuery,
6
+ useQueryClient,
7
+ useMutation,
8
+ Query,
9
+ QueryClient,
10
+ UseMutationOptions,
11
+ MutationOptions,
12
+ } from 'react-query';
13
+ import { paths } from './apiSchema.generated';
14
+ import { client } from './client';
15
+ import { QueryContext } from './QueryProvider';
16
+ import { RequestParamsType, ResponseContent } from './types';
17
+
18
+ export type QueryProps<
19
+ Url extends keyof Paths,
20
+ Method extends keyof Paths[Url],
21
+ Paths = paths
22
+ > = {
23
+ url: Url;
24
+ method: Method;
25
+ fetchOptions?: RequestOptions;
26
+ options?: UseQueryOptions<ResponseContent<Url, Method, Paths>>;
27
+ } & RequestParamsType<Url, Method, Paths>;
28
+
29
+ export const useApiQuery = <
30
+ Url extends keyof Paths,
31
+ Method extends keyof Paths[Url],
32
+ Paths = paths
33
+ >(
34
+ props: QueryProps<Url, Method, Paths>
35
+ ) => {
36
+ const { url, method, options, ...request } = props;
37
+
38
+ const globalOptions = useContext(QueryContext);
39
+
40
+ return useQuery<ResponseContent<Url, Method, Paths>, any>(
41
+ [url, (request as any)?.path, (request as any)?.query],
42
+ () => client(url, method, request as any, globalOptions),
43
+ options
44
+ );
45
+ };
46
+
47
+ export type MutationProps<
48
+ Url extends keyof Paths,
49
+ Method extends keyof Paths[Url],
50
+ Paths = paths
51
+ > = {
52
+ url: Url;
53
+ method: Method;
54
+ fetchOptions?: RequestOptions;
55
+ options?: UseMutationOptions<
56
+ ResponseContent<Url, Method, Paths>,
57
+ any,
58
+ RequestParamsType<Url, Method, Paths>
59
+ >;
60
+ invalidatePrefix?: string;
61
+ };
62
+
63
+ export const useApiMutation = <
64
+ Url extends keyof Paths,
65
+ Method extends keyof Paths[Url],
66
+ Paths = paths
67
+ >(
68
+ props: MutationProps<Url, Method, Paths>
69
+ ) => {
70
+ const queryClient = useQueryClient();
71
+ const { url, method, options, invalidatePrefix } = props;
72
+ const globalOptions = useContext(QueryContext);
73
+ const mutation = useMutation<
74
+ ResponseContent<Url, Method, Paths>,
75
+ any,
76
+ RequestParamsType<Url, Method, Paths>
77
+ >(
78
+ (request) =>
79
+ client<Url, Method, Paths>(url, method, request, globalOptions),
80
+ options
81
+ );
82
+
83
+ // inject custom onSuccess
84
+ const customOptions = (options: MutationOptions<any, any, any, any>) => ({
85
+ ...options,
86
+ onSuccess: (...args: any) => {
87
+ if (invalidatePrefix !== undefined) {
88
+ invalidateUrlPrefix(queryClient, invalidatePrefix);
89
+ }
90
+ // @ts-ignore
91
+ options?.onSuccess?.(...args);
92
+ },
93
+ });
94
+
95
+ const mutate = useCallback<typeof mutation.mutate>(
96
+ (variables, options) => {
97
+ return mutation.mutate(variables, customOptions(options as any));
98
+ },
99
+ [mutation.mutate]
100
+ );
101
+
102
+ const mutateAsync = useCallback<typeof mutation.mutateAsync>(
103
+ (variables, options) => {
104
+ return mutation.mutateAsync(variables, customOptions(options as any));
105
+ },
106
+ [mutation.mutateAsync]
107
+ );
108
+
109
+ return { ...mutation, mutate, mutateAsync };
110
+ };
111
+
112
+ export const matchUrlPrefix = (prefix: string) => {
113
+ return {
114
+ predicate: (query: Query) => {
115
+ return (query.queryKey[0] as string)?.startsWith(prefix);
116
+ },
117
+ };
118
+ };
119
+
120
+ export const invalidateUrlPrefix = (queryClient: QueryClient, prefix: string) =>
121
+ queryClient.invalidateQueries(matchUrlPrefix(prefix));
@@ -0,0 +1,44 @@
1
+ import { DEVTOOLS_ID } from '../../constants';
2
+ import * as React from 'react';
3
+ import * as ReactDOM from 'react-dom';
4
+
5
+ export class BodyEnd extends React.PureComponent<{ document?: Document }> {
6
+ _popup = null as HTMLElement | null;
7
+
8
+ get document() {
9
+ return this.props.document || document;
10
+ }
11
+
12
+ get devTools() {
13
+ return (
14
+ this.document.getElementById(DEVTOOLS_ID)?.shadowRoot ||
15
+ this.document.body
16
+ );
17
+ }
18
+
19
+ componentDidMount() {
20
+ this._popup = this.document.createElement('div')!;
21
+
22
+ this.devTools.appendChild(this._popup);
23
+ this._render();
24
+ }
25
+
26
+ componentDidUpdate() {
27
+ this._render();
28
+ }
29
+
30
+ componentWillUnmount() {
31
+ if (this._popup) {
32
+ ReactDOM.unmountComponentAtNode(this._popup);
33
+ this.devTools.removeChild(this._popup);
34
+ }
35
+ }
36
+
37
+ _render() {
38
+ ReactDOM.render(<>{this.props.children}</>, this._popup);
39
+ }
40
+
41
+ render() {
42
+ return null;
43
+ }
44
+ }
@@ -0,0 +1,9 @@
1
+ import { styled } from '@mui/material/styles';
2
+
3
+ export const ScFieldTitle = styled('div')`
4
+ display: flex;
5
+ margin-top: 16px;
6
+ margin-bottom: 4px;
7
+ font-size: 14px;
8
+ color: ${({ theme }) => theme.palette.text.secondary};
9
+ `;
@@ -0,0 +1,45 @@
1
+ import React from 'react';
2
+
3
+ import Button, { ButtonProps } from '@mui/material/Button';
4
+ import CircularProgress from '@mui/material/CircularProgress';
5
+
6
+ import { styled } from '@mui/material/styles';
7
+
8
+ const StyledButton = styled(Button)`
9
+ position: 'relative';
10
+ `;
11
+
12
+ const LoadingWrapper = styled('div')`
13
+ position: absolute;
14
+ top: 0;
15
+ bottom: 0;
16
+ left: 0;
17
+ right: 0;
18
+ display: flex;
19
+ align-items: center;
20
+ justify-content: center;
21
+ `;
22
+
23
+ type Props = ButtonProps & {
24
+ loading?: boolean;
25
+ };
26
+
27
+ export const LoadingButton: React.FC<Props> = ({
28
+ children,
29
+ loading,
30
+ disabled,
31
+ ...props
32
+ }) => {
33
+ const isDisabled = loading || disabled;
34
+
35
+ return (
36
+ <StyledButton {...props} disabled={isDisabled}>
37
+ {loading && (
38
+ <LoadingWrapper>
39
+ <CircularProgress size={25} />
40
+ </LoadingWrapper>
41
+ )}
42
+ {children}
43
+ </StyledButton>
44
+ );
45
+ };
@@ -0,0 +1,88 @@
1
+ import { createElement } from 'react';
2
+ import * as ReactDOM from 'react-dom';
3
+ import type {
4
+ FallbackNSTranslation,
5
+ UiProps,
6
+ UiInterface,
7
+ UiKeyOption,
8
+ } from '@tolgee/core';
9
+
10
+ import { KeyDialog } from './KeyDialog/KeyDialog';
11
+ import { KeyContextMenu } from './KeyContextMenu/KeyContextMenu';
12
+ import { DEVTOOLS_ID } from '../constants';
13
+
14
+ export class UI implements UiInterface {
15
+ private viewerComponent: KeyDialog;
16
+ private keyContextMenu: KeyContextMenu;
17
+
18
+ constructor(private props: UiProps) {
19
+ let rootElement = document.getElementById(DEVTOOLS_ID);
20
+ if (!rootElement) {
21
+ rootElement = document.createElement('div');
22
+ rootElement.id = DEVTOOLS_ID;
23
+ rootElement.attachShadow({ mode: 'open' });
24
+ document.body.appendChild(rootElement);
25
+ }
26
+ const devTools = rootElement.shadowRoot!;
27
+
28
+ const tolgeeModalContainer = document.createElement('div');
29
+ devTools.appendChild(tolgeeModalContainer);
30
+
31
+ const contextMenuContainer = document.createElement('div');
32
+ devTools.appendChild(contextMenuContainer);
33
+
34
+ const viewerElement = createElement(KeyDialog, {
35
+ ...this.props,
36
+ });
37
+
38
+ this.viewerComponent = ReactDOM.render(viewerElement, tolgeeModalContainer);
39
+
40
+ this.keyContextMenu = ReactDOM.render(
41
+ createElement(KeyContextMenu),
42
+ contextMenuContainer
43
+ );
44
+ }
45
+
46
+ public renderViewer(
47
+ key: string,
48
+ defaultValue: string | undefined,
49
+ ns: FallbackNSTranslation
50
+ ) {
51
+ this.viewerComponent.translationEdit(key, defaultValue, ns);
52
+ }
53
+
54
+ public async getKey(props: {
55
+ openEvent: MouseEvent;
56
+ keys: Map<string, string | undefined>;
57
+ }): Promise<string | undefined> {
58
+ return await new Promise<string | undefined>((resolve) => {
59
+ this.keyContextMenu.show({
60
+ ...props,
61
+ onSelect: (key) => resolve(key),
62
+ });
63
+ });
64
+ }
65
+
66
+ public async handleElementClick(
67
+ event: MouseEvent,
68
+ keysAndDefaults: UiKeyOption[]
69
+ ) {
70
+ let key = keysAndDefaults[0].key as string | undefined;
71
+ if (keysAndDefaults.length > 1) {
72
+ const keys = new Map(
73
+ keysAndDefaults.map(({ key, translation, defaultValue }) => [
74
+ key,
75
+ translation || defaultValue,
76
+ ])
77
+ );
78
+ key = await this.getKey({
79
+ openEvent: event,
80
+ keys,
81
+ });
82
+ }
83
+ if (key) {
84
+ const value = keysAndDefaults.find((val) => val.key === key)!;
85
+ this?.renderViewer(key, value.defaultValue, value.ns);
86
+ }
87
+ }
88
+ }
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import styled from '@emotion/styled';
3
+
4
+ const Img = styled.img`
5
+ width: 100px;
6
+ height: 100px;
7
+ object-fit: contain;
8
+ padding: 2px;
9
+ box-sizing: 'border-box';
10
+ `;
11
+
12
+ type Props = {
13
+ url: string;
14
+ };
15
+
16
+ export const ScreenshotPreview: React.FC<Props> = ({ url }) => {
17
+ return <Img src={url} />;
18
+ };
@@ -0,0 +1,54 @@
1
+ import React, { useCallback, useRef } from 'react';
2
+ import { createContext, useContextSelector } from 'use-context-selector';
3
+
4
+ type DispatchType<ActionType, DispatchReturn> = (
5
+ action: ActionType
6
+ ) => DispatchReturn;
7
+
8
+ type SelectorType<StateType, ReturnType> = (state: StateType) => ReturnType;
9
+
10
+ export const createProvider = <
11
+ StateType,
12
+ ActionType,
13
+ DispatchReturn,
14
+ ProviderProps
15
+ >(
16
+ body: (
17
+ props: ProviderProps
18
+ ) => [state: StateType, dispatch: DispatchType<ActionType, DispatchReturn>]
19
+ ) => {
20
+ const StateContext = createContext<StateType>(null as any);
21
+ const DispatchContext = React.createContext<
22
+ DispatchType<ActionType, DispatchReturn>
23
+ >(null as any);
24
+
25
+ const Provider: React.FC<ProviderProps> = ({ children, ...props }) => {
26
+ const [state, _dispatch] = body(props as any);
27
+ const dispatchRef = useRef(_dispatch);
28
+
29
+ dispatchRef.current = _dispatch;
30
+
31
+ // stable dispatch function
32
+ const dispatch = useCallback(
33
+ (action: ActionType) => dispatchRef.current?.(action),
34
+ [dispatchRef]
35
+ );
36
+
37
+ return (
38
+ <StateContext.Provider value={state}>
39
+ <DispatchContext.Provider value={dispatch}>
40
+ {children}
41
+ </DispatchContext.Provider>
42
+ </StateContext.Provider>
43
+ );
44
+ };
45
+
46
+ const useDispatch = () => React.useContext(DispatchContext);
47
+ const useStateContext = function <SelectorReturn>(
48
+ selector: SelectorType<StateType, SelectorReturn>
49
+ ) {
50
+ return useContextSelector(StateContext, selector);
51
+ };
52
+
53
+ return [Provider, useDispatch, useStateContext] as const;
54
+ };
@@ -0,0 +1,14 @@
1
+ export const isLanguagePermitted = (
2
+ language: string,
3
+ permittedLanguages: number[] | undefined,
4
+ allLanguages?: any[]
5
+ ) => {
6
+ if (!permittedLanguages?.length) {
7
+ return true;
8
+ }
9
+ const languageId = allLanguages?.find((l) => l.tag === language)?.id;
10
+ if (languageId === undefined) {
11
+ return true;
12
+ }
13
+ return permittedLanguages.includes(languageId as number);
14
+ };
@@ -0,0 +1,2 @@
1
+ export const sleep = (ms: number) =>
2
+ new Promise((resolve) => setTimeout(resolve, ms));
@@ -0,0 +1,9 @@
1
+ import type { TolgeePlugin, DevCredentials } from '@tolgee/core';
2
+
3
+ export declare const ContextUi: () => TolgeePlugin;
4
+ export declare const InContextTools: (
5
+ overrideCredentials?: DevCredentials
6
+ ) => TolgeePlugin;
7
+ export declare const DevTools: typeof InContextTools;
8
+
9
+ export * from '../lib/typedIndex';