@tolgee/web 4.9.3-rc.04b22e6.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/LICENSE +21 -0
- package/README.md +31 -0
- package/dist/tolgee-backend-fetch.cjs.min.js +2 -0
- package/dist/tolgee-backend-fetch.cjs.min.js.map +1 -0
- package/dist/tolgee-backend-fetch.esm.min.mjs +2 -0
- package/dist/tolgee-backend-fetch.esm.min.mjs.map +1 -0
- package/dist/tolgee-backend-fetch.umd.min.js +2 -0
- package/dist/tolgee-backend-fetch.umd.min.js.map +1 -0
- package/dist/tolgee-context-ui.cjs.min.js +143 -0
- package/dist/tolgee-context-ui.cjs.min.js.map +1 -0
- package/dist/tolgee-context-ui.esm.min.mjs +143 -0
- package/dist/tolgee-context-ui.esm.min.mjs.map +1 -0
- package/dist/tolgee-context-ui.umd.min.js +143 -0
- package/dist/tolgee-context-ui.umd.min.js.map +1 -0
- package/dist/tolgee-in-context-tools.cjs.min.js +143 -0
- package/dist/tolgee-in-context-tools.cjs.min.js.map +1 -0
- package/dist/tolgee-in-context-tools.esm.min.mjs +143 -0
- package/dist/tolgee-in-context-tools.esm.min.mjs.map +1 -0
- package/dist/tolgee-in-context-tools.umd.min.js +143 -0
- package/dist/tolgee-in-context-tools.umd.min.js.map +1 -0
- package/dist/tolgee-invisible-observer.cjs.min.js +2 -0
- package/dist/tolgee-invisible-observer.cjs.min.js.map +1 -0
- package/dist/tolgee-invisible-observer.esm.min.mjs +2 -0
- package/dist/tolgee-invisible-observer.esm.min.mjs.map +1 -0
- package/dist/tolgee-invisible-observer.umd.min.js +2 -0
- package/dist/tolgee-invisible-observer.umd.min.js.map +1 -0
- package/dist/tolgee-language-detector.cjs.min.js +2 -0
- package/dist/tolgee-language-detector.cjs.min.js.map +1 -0
- package/dist/tolgee-language-detector.esm.min.mjs +2 -0
- package/dist/tolgee-language-detector.esm.min.mjs.map +1 -0
- package/dist/tolgee-language-detector.umd.min.js +2 -0
- package/dist/tolgee-language-detector.umd.min.js.map +1 -0
- package/dist/tolgee-language-storage.cjs.min.js +2 -0
- package/dist/tolgee-language-storage.cjs.min.js.map +1 -0
- package/dist/tolgee-language-storage.esm.min.mjs +2 -0
- package/dist/tolgee-language-storage.esm.min.mjs.map +1 -0
- package/dist/tolgee-language-storage.umd.min.js +2 -0
- package/dist/tolgee-language-storage.umd.min.js.map +1 -0
- package/dist/tolgee-text-observer.cjs.min.js +2 -0
- package/dist/tolgee-text-observer.cjs.min.js.map +1 -0
- package/dist/tolgee-text-observer.esm.min.mjs +2 -0
- package/dist/tolgee-text-observer.esm.min.mjs.map +1 -0
- package/dist/tolgee-text-observer.umd.min.js +2 -0
- package/dist/tolgee-text-observer.umd.min.js.map +1 -0
- package/dist/tolgee-web-tolgee.cjs.min.js +2 -0
- package/dist/tolgee-web-tolgee.cjs.min.js.map +1 -0
- package/dist/tolgee-web-tolgee.esm.min.mjs +2 -0
- package/dist/tolgee-web-tolgee.esm.min.mjs.map +1 -0
- package/dist/tolgee-web-tolgee.umd.min.js +2 -0
- package/dist/tolgee-web-tolgee.umd.min.js.map +1 -0
- package/dist/tolgee-web.cjs.js +27079 -0
- package/dist/tolgee-web.cjs.js.map +1 -0
- package/dist/tolgee-web.cjs.min.js +143 -0
- package/dist/tolgee-web.cjs.min.js.map +1 -0
- package/dist/tolgee-web.esm.js +27055 -0
- package/dist/tolgee-web.esm.js.map +1 -0
- package/dist/tolgee-web.esm.min.mjs +143 -0
- package/dist/tolgee-web.esm.min.mjs.map +1 -0
- package/dist/tolgee-web.umd.js +27085 -0
- package/dist/tolgee-web.umd.js.map +1 -0
- package/dist/tolgee-web.umd.min.js +143 -0
- package/dist/tolgee-web.umd.min.js.map +1 -0
- package/lib/BackendFetch.d.ts +4 -0
- package/lib/BrowserExtensionPlugin/BrowserExtensionPlugin.d.ts +8 -0
- package/lib/BrowserExtensionPlugin/constants.d.ts +3 -0
- package/lib/BrowserExtensionPlugin/loadInContextLib.d.ts +1 -0
- package/lib/ContextUi.d.ts +2 -0
- package/lib/DevBackend.d.ts +2 -0
- package/lib/DevTools.d.ts +3 -0
- package/lib/InContextTools.d.ts +2 -0
- package/lib/InvisibleObserver.d.ts +2 -0
- package/lib/LanguageDetector.d.ts +3 -0
- package/lib/LanguageStorage.d.ts +3 -0
- package/lib/TextObserver.d.ts +2 -0
- package/lib/WebTolgee.d.ts +3 -0
- package/lib/constants.d.ts +5 -0
- package/lib/index.d.ts +5 -0
- package/lib/observers/general/DomHelper.d.ts +4 -0
- package/lib/observers/general/ElementHighlighter.d.ts +10 -0
- package/lib/observers/general/ElementMeta.d.ts +3 -0
- package/lib/observers/general/ElementRegistry.d.ts +11 -0
- package/lib/observers/general/ElementStore.d.ts +10 -0
- package/lib/observers/general/GeneralObserver.d.ts +12 -0
- package/lib/observers/general/MouseEventHandler.d.ts +13 -0
- package/lib/observers/general/NodeHandler.d.ts +6 -0
- package/lib/observers/general/helpers.d.ts +7 -0
- package/lib/observers/invisible/InvisibleWrapper.d.ts +2 -0
- package/lib/observers/invisible/ValueMemory.d.ts +5 -0
- package/lib/observers/invisible/encoderPolyfill.d.ts +8 -0
- package/lib/observers/invisible/secret.d.ts +6 -0
- package/lib/observers/text/TextWrapper.d.ts +8 -0
- package/lib/observers/text/helpers.d.ts +3 -0
- package/lib/tools/decodeApiKey.d.ts +1 -0
- package/lib/tools/extension.d.ts +28 -0
- package/lib/typedIndex.d.ts +11 -0
- package/lib/types.d.ts +28 -0
- package/lib/ui/KeyContextMenu/KeyContextMenu.d.ts +19 -0
- package/lib/ui/KeyDialog/KeyDialog.d.ts +23 -0
- package/lib/ui/KeyDialog/KeyForm.d.ts +2 -0
- package/lib/ui/KeyDialog/LanguageSelect.d.ts +2 -0
- package/lib/ui/KeyDialog/NewWindow.d.ts +2 -0
- package/lib/ui/KeyDialog/NsSelect.d.ts +9 -0
- package/lib/ui/KeyDialog/ScreenshotGallery/ExtensionPrompt.d.ts +6 -0
- package/lib/ui/KeyDialog/ScreenshotGallery/ScreenshotDetail.d.ts +8 -0
- package/lib/ui/KeyDialog/ScreenshotGallery/ScreenshotDropzone.d.ts +6 -0
- package/lib/ui/KeyDialog/ScreenshotGallery/ScreenshotGallery.d.ts +2 -0
- package/lib/ui/KeyDialog/ScreenshotGallery/ScreenshotThumbnail.d.ts +8 -0
- package/lib/ui/KeyDialog/ScreenshotGallery/utils.d.ts +3 -0
- package/lib/ui/KeyDialog/TranslationDialog.d.ts +2 -0
- package/lib/ui/KeyDialog/TranslationDialogContextProvider.d.ts +128 -0
- package/lib/ui/KeyDialog/TranslationDialogWrapper.d.ts +2 -0
- package/lib/ui/KeyDialog/TranslationFields.d.ts +2 -0
- package/lib/ui/KeyDialog/languageHelpers.d.ts +12 -0
- package/lib/ui/KeyDialog/tools.d.ts +3 -0
- package/lib/ui/ThemeProvider.d.ts +2 -0
- package/lib/ui/client/QueryProvider.d.ts +12 -0
- package/lib/ui/client/apiSchema.generated.d.ts +3283 -0
- package/lib/ui/client/client.d.ts +5 -0
- package/lib/ui/client/types.d.ts +58 -0
- package/lib/ui/client/useQueryApi.d.ts +84 -0
- package/lib/ui/common/BodyEnd.d.ts +13 -0
- package/lib/ui/common/FieldTitle.d.ts +2 -0
- package/lib/ui/common/LoadingButton.d.ts +7 -0
- package/lib/ui/index.d.ts +13 -0
- package/lib/ui/tools/createProvider.d.ts +5 -0
- package/lib/ui/tools/isLanguagePermitted.d.ts +1 -0
- package/lib/ui/tools/sleep.d.ts +1 -0
- package/package.json +91 -0
- package/src/BackendFetch.ts +64 -0
- package/src/BrowserExtensionPlugin/BrowserExtensionPlugin.ts +98 -0
- package/src/BrowserExtensionPlugin/constants.ts +3 -0
- package/src/BrowserExtensionPlugin/loadInContextLib.ts +32 -0
- package/src/ContextUi.ts +7 -0
- package/src/DevBackend.ts +30 -0
- package/src/DevTools.ts +9 -0
- package/src/InContextTools.ts +20 -0
- package/src/InvisibleObserver.ts +16 -0
- package/src/LanguageDetector.test.ts +19 -0
- package/src/LanguageDetector.ts +32 -0
- package/src/LanguageStorage.ts +23 -0
- package/src/TextObserver.ts +45 -0
- package/src/WebTolgee.ts +8 -0
- package/src/__test__/browser.extension.test.ts +70 -0
- package/src/__test__/observer.test.ts +13 -0
- package/src/__test__/testObserver.ts +106 -0
- package/src/__test__/testRetranslate.ts +47 -0
- package/src/constants.ts +12 -0
- package/src/index.ts +8 -0
- package/src/observers/general/DomHelper.ts +46 -0
- package/src/observers/general/ElementHighlighter.ts +72 -0
- package/src/observers/general/ElementMeta.ts +17 -0
- package/src/observers/general/ElementRegistry.ts +159 -0
- package/src/observers/general/ElementStore.ts +34 -0
- package/src/observers/general/GeneralObserver.ts +133 -0
- package/src/observers/general/MouseEventHandler.ts +199 -0
- package/src/observers/general/NodeHandler.ts +39 -0
- package/src/observers/general/helpers.ts +65 -0
- package/src/observers/invisible/InvisibleWrapper.test.ts +17 -0
- package/src/observers/invisible/InvisibleWrapper.ts +96 -0
- package/src/observers/invisible/ValueMemory.test.ts +25 -0
- package/src/observers/invisible/ValueMemory.ts +20 -0
- package/src/observers/invisible/encoderPolyfill.ts +96 -0
- package/src/observers/invisible/secret.test.ts +61 -0
- package/src/observers/invisible/secret.ts +68 -0
- package/src/observers/text/TextWrapper.ts +258 -0
- package/src/observers/text/helpers.ts +56 -0
- package/src/tools/decodeApiKey.test.ts +14 -0
- package/src/tools/decodeApiKey.ts +74 -0
- package/src/tools/extension.test.ts +159 -0
- package/src/tools/extension.ts +119 -0
- package/src/typedIndex.ts +13 -0
- package/src/types.ts +33 -0
- package/src/ui/KeyContextMenu/KeyContextMenu.test.ts +51 -0
- package/src/ui/KeyContextMenu/KeyContextMenu.tsx +108 -0
- package/src/ui/KeyDialog/KeyDialog.tsx +67 -0
- package/src/ui/KeyDialog/KeyForm.tsx +208 -0
- package/src/ui/KeyDialog/LanguageSelect.tsx +78 -0
- package/src/ui/KeyDialog/NewWindow.tsx +106 -0
- package/src/ui/KeyDialog/NsSelect.tsx +67 -0
- package/src/ui/KeyDialog/ScreenshotGallery/ExtensionPrompt.tsx +97 -0
- package/src/ui/KeyDialog/ScreenshotGallery/ScreenshotDetail.tsx +33 -0
- package/src/ui/KeyDialog/ScreenshotGallery/ScreenshotDropzone.tsx +138 -0
- package/src/ui/KeyDialog/ScreenshotGallery/ScreenshotGallery.tsx +240 -0
- package/src/ui/KeyDialog/ScreenshotGallery/ScreenshotThumbnail.tsx +113 -0
- package/src/ui/KeyDialog/ScreenshotGallery/utils.ts +17 -0
- package/src/ui/KeyDialog/TranslationDialog.tsx +14 -0
- package/src/ui/KeyDialog/TranslationDialogContextProvider.tsx +464 -0
- package/src/ui/KeyDialog/TranslationDialogWrapper.tsx +44 -0
- package/src/ui/KeyDialog/TranslationFields.tsx +113 -0
- package/src/ui/KeyDialog/languageHelpers.ts +18 -0
- package/src/ui/KeyDialog/tools.ts +30 -0
- package/src/ui/ThemeProvider.tsx +71 -0
- package/src/ui/client/QueryProvider.tsx +38 -0
- package/src/ui/client/apiSchema.generated.ts +3281 -0
- package/src/ui/client/client.ts +155 -0
- package/src/ui/client/types.ts +113 -0
- package/src/ui/client/useQueryApi.ts +121 -0
- package/src/ui/common/BodyEnd.tsx +44 -0
- package/src/ui/common/FieldTitle.tsx +9 -0
- package/src/ui/common/LoadingButton.tsx +45 -0
- package/src/ui/index.ts +88 -0
- package/src/ui/screenshots/ScreenshotPreview.tsx +18 -0
- package/src/ui/tools/createProvider.tsx +54 -0
- package/src/ui/tools/isLanguagePermitted.ts +14 -0
- package/src/ui/tools/sleep.ts +2 -0
- 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,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
|
+
};
|
package/src/ui/index.ts
ADDED
|
@@ -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
|
+
};
|
package/types/index.d.ts
ADDED
|
@@ -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';
|