fbt-inline-translations 0.1.5 → 0.2.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
@@ -23,6 +23,54 @@ You can make translations available to your translators, users, or the entire co
23
23
  npm install fbt-inline-translations
24
24
  ```
25
25
 
26
+ ### How to add polyfills in Webpack 5
27
+
28
+ Errors in the terminal will give hints on how to add the required polyfill.
29
+
30
+ **Example for path module:**
31
+
32
+ In your Webpack config:
33
+
34
+ ```javascript
35
+ module.exports = {
36
+ resolve: {
37
+ // ...
38
+ fallback: {
39
+ "buffer": require.resolve("buffer/"),
40
+ "url": require.resolve("url/"),
41
+ "path": require.resolve("path-browserify"),
42
+ "http": require.resolve("stream-http"),
43
+ "https": require.resolve("https-browserify"),
44
+ },
45
+
46
+ alias: {
47
+ process: "process/browser",
48
+ },
49
+
50
+ plugins: [
51
+ // Work around for Buffer is undefined:
52
+ // https://github.com/webpack/changelog-v5/issues/10
53
+ new webpack.ProvidePlugin({
54
+ Buffer: ['buffer', 'Buffer'],
55
+ }),
56
+ new webpack.ProvidePlugin({
57
+ process: 'process/browser',
58
+ }),
59
+ ],
60
+ },
61
+ };
62
+ ```
63
+
64
+ and
65
+
66
+ ```cmd
67
+ npm install buffer url path-browserify stream-http https-browserify --save-dev
68
+ # you may need to clear your cache after this change
69
+ rm -fr node_modules/.cache
70
+ ```
71
+
72
+ Restart the Dev Server and the error will be gone.
73
+
26
74
  ## 🔧 Configuration
27
75
 
28
76
  Register your FBT project on [Swiftyper Translations](https://translations.swiftyper.sk)
@@ -44,4 +92,27 @@ Add this code to your page:
44
92
  darkMode: false, // dark mode also depends on html class 'tw-dark'
45
93
  })
46
94
  </script>
95
+ ```
96
+
97
+ ### React usage
98
+
99
+ In your application, wrap your `App` component. We recommend adding it around your root component in the `index.js` file.
100
+
101
+ ```js
102
+ // src/index.js
103
+
104
+ import ReactDOM from 'react-dom';
105
+ import { FbtInlineTranslationsWrapper } from 'fbt-inline-translations';
106
+
107
+ ReactDOM.render(
108
+ <FbtInlineTranslationsWrapper
109
+ token={'YOUR_API_KEY_HERE'}
110
+ locale={"sk_SK"} // translation locale
111
+ contributor={"contributor@email.com"} // contributor must be invited
112
+ darkMode={false} // dark mode also depends on html class 'tw-dark'
113
+ >
114
+ <App />
115
+ </FbtInlineTranslationsWrapper>,
116
+ document.getElementById('root')
117
+ );
47
118
  ```
@@ -1,16 +1,16 @@
1
1
  import { Translation } from './Translation';
2
- export declare type TableJSFBTTreeLeaf = {
2
+ export type TableJSFBTTreeLeaf = {
3
3
  desc: string;
4
4
  hash?: string;
5
5
  text: string;
6
6
  tokenAliases?: string;
7
7
  outerTokenName?: string;
8
8
  };
9
- export declare type TableJSFBTTreeBranch = {
9
+ export type TableJSFBTTreeBranch = {
10
10
  [key: number]: TableJSFBTTree;
11
11
  };
12
- export declare type TableJSFBTTree = TableJSFBTTreeLeaf | TableJSFBTTreeBranch;
13
- export declare type JSFBTMetaEntry = {
12
+ export type TableJSFBTTree = TableJSFBTTreeLeaf | TableJSFBTTreeBranch;
13
+ export type JSFBTMetaEntry = {
14
14
  type: string;
15
15
  singular?: boolean;
16
16
  token?: string;
@@ -22,11 +22,11 @@ export declare type JSFBTMetaEntry = {
22
22
  } | {
23
23
  range: string[];
24
24
  };
25
- export declare type TableJSFBT = {
25
+ export type TableJSFBT = {
26
26
  t: TableJSFBTTree;
27
27
  m: JSFBTMetaEntry[];
28
28
  };
29
- export declare type Phrase = {
29
+ export type Phrase = {
30
30
  id: number;
31
31
  parent_id: number | null;
32
32
  object: string;
@@ -42,7 +42,20 @@ export declare type Phrase = {
42
42
  project: string;
43
43
  created_at: string;
44
44
  translations: Translation[];
45
+ glossary: Term[];
45
46
  locale: {
46
47
  name: string;
47
48
  };
48
49
  };
50
+ export type Term = {
51
+ id: number;
52
+ object: string;
53
+ hash: string;
54
+ translation: Translation;
55
+ text: string;
56
+ description: string;
57
+ translatable: boolean;
58
+ case_insensitive: boolean;
59
+ part_of_speech: string;
60
+ created_at: string;
61
+ };
@@ -1,4 +1,4 @@
1
- export declare type SwiftyperContextType = {
1
+ export type SwiftyperContextType = {
2
2
  token: string;
3
3
  locale: string;
4
4
  contributor: string;
@@ -1,4 +1,4 @@
1
- export declare type Translation = {
1
+ export type Translation = {
2
2
  id: number;
3
3
  voted: boolean;
4
4
  phrase_id: number;
package/lib/App.css ADDED
@@ -0,0 +1,444 @@
1
+ /* @tailwind base; */
2
+
3
+ .tw-relative {
4
+ position: relative;
5
+ }
6
+
7
+ .tw-m-0 {
8
+ margin: 0px;
9
+ }
10
+
11
+ .tw-mx-auto {
12
+ margin-left: auto;
13
+ margin-right: auto;
14
+ }
15
+
16
+ .tw-mt-3 {
17
+ margin-top: 0.75rem;
18
+ }
19
+
20
+ .tw-mb-1 {
21
+ margin-bottom: 0.25rem;
22
+ }
23
+
24
+ .tw-mt-4 {
25
+ margin-top: 1rem;
26
+ }
27
+
28
+ .tw-mb-2 {
29
+ margin-bottom: 0.5rem;
30
+ }
31
+
32
+ .tw-mt-2 {
33
+ margin-top: 0.5rem;
34
+ }
35
+
36
+ .tw-ml-3 {
37
+ margin-left: 0.75rem;
38
+ }
39
+
40
+ .tw-mt-1 {
41
+ margin-top: 0.25rem;
42
+ }
43
+
44
+ .tw-mb-3 {
45
+ margin-bottom: 0.75rem;
46
+ }
47
+
48
+ .tw-mb-0 {
49
+ margin-bottom: 0px;
50
+ }
51
+
52
+ .tw-box-border {
53
+ box-sizing: border-box;
54
+ }
55
+
56
+ .tw-block {
57
+ display: block;
58
+ }
59
+
60
+ .tw-flex {
61
+ display: flex;
62
+ }
63
+
64
+ .tw-inline-flex {
65
+ display: inline-flex;
66
+ }
67
+
68
+ .tw-h-12 {
69
+ height: 3rem;
70
+ }
71
+
72
+ .tw-h-6 {
73
+ height: 1.5rem;
74
+ }
75
+
76
+ .tw-w-full {
77
+ width: 100%;
78
+ }
79
+
80
+ .tw-w-12 {
81
+ width: 3rem;
82
+ }
83
+
84
+ .tw-w-6 {
85
+ width: 1.5rem;
86
+ }
87
+
88
+ .tw-min-w-\[20px\] {
89
+ min-width: 20px;
90
+ }
91
+
92
+ .tw-max-w-lg {
93
+ max-width: 32rem;
94
+ }
95
+
96
+ .tw-resize-y {
97
+ resize: vertical;
98
+ }
99
+
100
+ .tw-list-inside {
101
+ list-style-position: inside;
102
+ }
103
+
104
+ .tw-list-none {
105
+ list-style-type: none;
106
+ }
107
+
108
+ .tw-items-center {
109
+ align-items: center;
110
+ }
111
+
112
+ .tw-justify-end {
113
+ justify-content: flex-end;
114
+ }
115
+
116
+ .tw-justify-center {
117
+ justify-content: center;
118
+ }
119
+
120
+ .tw-gap-2 {
121
+ gap: 0.5rem;
122
+ }
123
+
124
+ .tw-gap-1\.5 {
125
+ gap: 0.375rem;
126
+ }
127
+
128
+ .tw-gap-1 {
129
+ gap: 0.25rem;
130
+ }
131
+
132
+ .tw-divide-y > :not([hidden]) ~ :not([hidden]) {
133
+ --tw-divide-y-reverse: 0;
134
+ border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
135
+ border-bottom-width: calc(1px * var(--tw-divide-y-reverse));
136
+ }
137
+
138
+ .tw-divide-solid > :not([hidden]) ~ :not([hidden]) {
139
+ border-style: solid;
140
+ }
141
+
142
+ .tw-divide-gray-300 > :not([hidden]) ~ :not([hidden]) {
143
+ --tw-divide-opacity: 1;
144
+ border-color: rgb(209 213 219 / var(--tw-divide-opacity));
145
+ }
146
+
147
+ .tw-overflow-auto {
148
+ overflow: auto;
149
+ }
150
+
151
+ .tw-break-words {
152
+ overflow-wrap: break-word;
153
+ }
154
+
155
+ .tw-rounded-md {
156
+ border-radius: 0.375rem;
157
+ }
158
+
159
+ .tw-rounded-full {
160
+ border-radius: 9999px;
161
+ }
162
+
163
+ .tw-border-0 {
164
+ border-width: 0px;
165
+ }
166
+
167
+ .tw-border {
168
+ border-width: 1px;
169
+ }
170
+
171
+ .tw-border-gray-300 {
172
+ --tw-border-opacity: 1;
173
+ border-color: rgb(209 213 219 / var(--tw-border-opacity));
174
+ }
175
+
176
+ .\!tw-border-red-500 {
177
+ --tw-border-opacity: 1 !important;
178
+ border-color: rgb(239 68 68 / var(--tw-border-opacity)) !important;
179
+ }
180
+
181
+ .tw-border-transparent {
182
+ border-color: transparent;
183
+ }
184
+
185
+ .tw-bg-transparent {
186
+ background-color: transparent;
187
+ }
188
+
189
+ .tw-bg-blue-600 {
190
+ --tw-bg-opacity: 1;
191
+ background-color: rgb(37 99 235 / var(--tw-bg-opacity));
192
+ }
193
+
194
+ .tw-bg-red-100 {
195
+ --tw-bg-opacity: 1;
196
+ background-color: rgb(254 226 226 / var(--tw-bg-opacity));
197
+ }
198
+
199
+ .tw-p-3 {
200
+ padding: 0.75rem;
201
+ }
202
+
203
+ .tw-p-0 {
204
+ padding: 0px;
205
+ }
206
+
207
+ .tw-py-2 {
208
+ padding-top: 0.5rem;
209
+ padding-bottom: 0.5rem;
210
+ }
211
+
212
+ .tw-px-4 {
213
+ padding-left: 1rem;
214
+ padding-right: 1rem;
215
+ }
216
+
217
+ .tw-text-center {
218
+ text-align: center;
219
+ }
220
+
221
+ .tw-text-xl {
222
+ font-size: 1.25rem;
223
+ line-height: 1.75rem;
224
+ }
225
+
226
+ .tw-text-xs {
227
+ font-size: 0.75rem;
228
+ line-height: 1rem;
229
+ }
230
+
231
+ .tw-text-sm {
232
+ font-size: 0.875rem;
233
+ line-height: 1.25rem;
234
+ }
235
+
236
+ .tw-text-lg {
237
+ font-size: 1.125rem;
238
+ line-height: 1.75rem;
239
+ }
240
+
241
+ .tw-font-medium {
242
+ font-weight: 500;
243
+ }
244
+
245
+ .tw-leading-6 {
246
+ line-height: 1.5rem;
247
+ }
248
+
249
+ .tw-text-blue-600 {
250
+ --tw-text-opacity: 1;
251
+ color: rgb(37 99 235 / var(--tw-text-opacity));
252
+ }
253
+
254
+ .tw-text-gray-500 {
255
+ --tw-text-opacity: 1;
256
+ color: rgb(107 114 128 / var(--tw-text-opacity));
257
+ }
258
+
259
+ .tw-text-red-600 {
260
+ --tw-text-opacity: 1;
261
+ color: rgb(220 38 38 / var(--tw-text-opacity));
262
+ }
263
+
264
+ .tw-text-gray-800 {
265
+ --tw-text-opacity: 1;
266
+ color: rgb(31 41 55 / var(--tw-text-opacity));
267
+ }
268
+
269
+ .tw-text-gray-900 {
270
+ --tw-text-opacity: 1;
271
+ color: rgb(17 24 39 / var(--tw-text-opacity));
272
+ }
273
+
274
+ .tw-text-blue-500 {
275
+ --tw-text-opacity: 1;
276
+ color: rgb(59 130 246 / var(--tw-text-opacity));
277
+ }
278
+
279
+ .tw-text-white {
280
+ --tw-text-opacity: 1;
281
+ color: rgb(255 255 255 / var(--tw-text-opacity));
282
+ }
283
+
284
+ .tw-shadow-sm {
285
+ --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
286
+ --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);
287
+ box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
288
+ }
289
+
290
+ .tw-outline-none {
291
+ outline: 2px solid transparent;
292
+ outline-offset: 2px;
293
+ }
294
+
295
+ #inline-translator * {
296
+ font-family: 'Roboto', sans-serif;
297
+ }
298
+
299
+ .tw-dark #inline-translator .rodal-close:hover:before,
300
+ .tw-dark #inline-translator .rodal-close:hover:after {
301
+ background: #fff;
302
+ }
303
+
304
+ #inline-translator .loader {
305
+ border: 5px solid #bebebe;
306
+ border-bottom-color: transparent;
307
+ border-radius: 50%;
308
+ box-sizing: border-box;
309
+ animation: rotation 1s linear infinite;
310
+ }
311
+
312
+ @keyframes rotation {
313
+ 0% {
314
+ transform: rotate(0deg);
315
+ }
316
+
317
+ 100% {
318
+ transform: rotate(360deg);
319
+ }
320
+ }
321
+
322
+ .intlInlineMode_normal {
323
+ font-style: normal;
324
+ }
325
+
326
+ .intlInlineMode_translatable,
327
+ .intlInlineMode_approvable,
328
+ .intlInlineMode_reportable {
329
+ background: none;
330
+ font-style: normal;
331
+ padding: 0px;
332
+ width: auto;
333
+ }
334
+
335
+ .intlInlineMode_reportable {
336
+ border-bottom: 1px solid #f99;
337
+ }
338
+
339
+ .intlInlineMode_translatable,
340
+ .intlInlineMode_approvable {
341
+ border-bottom: 1px solid #999;
342
+ }
343
+
344
+ .intlInlineMode_translatable:hover,
345
+ .intlInlineMode_approvable:hover,
346
+ .intlInlineMode_reportable:hover {
347
+ background-color: #fcc;
348
+ border-top: 1px solid #ccc;
349
+ cursor: help;
350
+ }
351
+
352
+ .hover\:tw-cursor-pointer:hover {
353
+ cursor: pointer;
354
+ }
355
+
356
+ .hover\:tw-cursor-help:hover {
357
+ cursor: help;
358
+ }
359
+
360
+ .hover\:tw-bg-blue-700:hover {
361
+ --tw-bg-opacity: 1;
362
+ background-color: rgb(29 78 216 / var(--tw-bg-opacity));
363
+ }
364
+
365
+ .focus\:\!tw-border-blue-500:focus {
366
+ --tw-border-opacity: 1 !important;
367
+ border-color: rgb(59 130 246 / var(--tw-border-opacity)) !important;
368
+ }
369
+
370
+ .focus\:tw-outline-none:focus {
371
+ outline: 2px solid transparent;
372
+ outline-offset: 2px;
373
+ }
374
+
375
+ .focus\:tw-ring-2:focus {
376
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
377
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
378
+ box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
379
+ }
380
+
381
+ .focus\:\!tw-ring-blue-500:focus {
382
+ --tw-ring-opacity: 1 !important;
383
+ --tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity)) !important;
384
+ }
385
+
386
+ .focus\:tw-ring-blue-500:focus {
387
+ --tw-ring-opacity: 1;
388
+ --tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity));
389
+ }
390
+
391
+ .focus\:tw-ring-offset-2:focus {
392
+ --tw-ring-offset-width: 2px;
393
+ }
394
+
395
+ .tw-dark .dark\:tw-divide-gray-700 > :not([hidden]) ~ :not([hidden]) {
396
+ --tw-divide-opacity: 1;
397
+ border-color: rgb(55 65 81 / var(--tw-divide-opacity));
398
+ }
399
+
400
+ .tw-dark .dark\:tw-border-gray-900 {
401
+ --tw-border-opacity: 1;
402
+ border-color: rgb(17 24 39 / var(--tw-border-opacity));
403
+ }
404
+
405
+ .tw-dark .dark\:tw-bg-gray-800 {
406
+ --tw-bg-opacity: 1;
407
+ background-color: rgb(31 41 55 / var(--tw-bg-opacity));
408
+ }
409
+
410
+ .tw-dark .dark\:tw-bg-gray-900 {
411
+ --tw-bg-opacity: 1;
412
+ background-color: rgb(17 24 39 / var(--tw-bg-opacity));
413
+ }
414
+
415
+ .tw-dark .dark\:tw-text-gray-300 {
416
+ --tw-text-opacity: 1;
417
+ color: rgb(209 213 219 / var(--tw-text-opacity));
418
+ }
419
+
420
+ .tw-dark .dark\:tw-text-gray-500 {
421
+ --tw-text-opacity: 1;
422
+ color: rgb(107 114 128 / var(--tw-text-opacity));
423
+ }
424
+
425
+ .tw-dark .dark\:tw-text-gray-400 {
426
+ --tw-text-opacity: 1;
427
+ color: rgb(156 163 175 / var(--tw-text-opacity));
428
+ }
429
+
430
+ .tw-dark .dark\:tw-text-white {
431
+ --tw-text-opacity: 1;
432
+ color: rgb(255 255 255 / var(--tw-text-opacity));
433
+ }
434
+
435
+ @media (min-width: 640px) {
436
+ .sm\:tw-mt-5 {
437
+ margin-top: 1.25rem;
438
+ }
439
+
440
+ .sm\:tw-text-sm {
441
+ font-size: 0.875rem;
442
+ line-height: 1.25rem;
443
+ }
444
+ }
package/lib/App.d.ts CHANGED
@@ -3,8 +3,8 @@ import { Translation } from './@types/Translation';
3
3
  import 'rodal/lib/rodal.css';
4
4
  import 'typeface-roboto';
5
5
  import './App.css';
6
- declare type Props = {};
7
- declare type State = {
6
+ type Props = {};
7
+ type State = {
8
8
  visible: boolean;
9
9
  translation: Translation | null;
10
10
  hash: string | null;
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import { Translation } from './@types/Translation';
3
+ import './rodal.css';
4
+ import 'typeface-roboto';
5
+ import './App.css';
6
+ type Props = {};
7
+ type State = {
8
+ visible: boolean;
9
+ translation: Translation | null;
10
+ hash: string | null;
11
+ locale: string | null;
12
+ };
13
+ declare class TranslationDialog extends React.Component<Props, State> {
14
+ constructor(props: Props);
15
+ componentDidMount(): void;
16
+ openTranslationDialog(event: MouseEvent): void;
17
+ hide(): void;
18
+ render(): JSX.Element;
19
+ }
20
+ export default TranslationDialog;
@@ -0,0 +1,57 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import TranslationCard from './components/TranslationCard';
4
+ import './rodal.css';
5
+ import 'typeface-roboto';
6
+ import './App.css';
7
+ import Rodal from './components/Rodal';
8
+ class TranslationDialog extends React.Component {
9
+ constructor(props) {
10
+ super(props);
11
+ this.state = {
12
+ visible: false,
13
+ translation: null,
14
+ hash: null,
15
+ locale: null,
16
+ };
17
+ this.hide = this.hide.bind(this);
18
+ }
19
+ componentDidMount() {
20
+ document.addEventListener('contextmenu', this.openTranslationDialog.bind(this));
21
+ }
22
+ openTranslationDialog(event) {
23
+ let parent = event.target;
24
+ while (parent) {
25
+ if (parent.classList.contains('intlInlineMode_normal') ||
26
+ parent.classList.contains('intlInlineMode_translatable') ||
27
+ parent.classList.contains('intlInlineMode_approvable') ||
28
+ parent.classList.contains('intlInlineMode_reportable')) {
29
+ const hash = parent.getAttribute('data-intl-hash');
30
+ const locale = parent.getAttribute('data-intl-locale');
31
+ this.setState({
32
+ visible: true,
33
+ hash,
34
+ locale,
35
+ });
36
+ event.preventDefault();
37
+ return;
38
+ }
39
+ parent = parent.parentElement;
40
+ }
41
+ }
42
+ hide() {
43
+ this.setState({ visible: false });
44
+ }
45
+ render() {
46
+ const { hash } = this.state;
47
+ const { children } = this.props;
48
+ return (_jsxs(_Fragment, { children: [children, _jsx(Rodal, { customStyles: {
49
+ height: 'auto',
50
+ bottom: 'auto',
51
+ top: 'auto',
52
+ }, closeMaskOnClick: false, className: "tw-flex tw-items-center tw-overflow-auto", enterAnimation: " rodal-zoom-enter dark:tw-bg-gray-800 " // ugly hack
53
+ , leaveAnimation: " rodal-fade-leave dark:tw-bg-gray-800 " // ugly hack
54
+ , visible: this.state.visible, onClose: this.hide, children: _jsx(TranslationCard, { hash: hash, visible: this.state.visible, hide: this.hide }, hash) })] }));
55
+ }
56
+ }
57
+ export default TranslationDialog;
@@ -1,6 +1,6 @@
1
1
  /// <reference types="react" />
2
2
  import { Translation } from '../@types/Translation';
3
- declare type Props = {
3
+ type Props = {
4
4
  candidate: Translation;
5
5
  onClick: (translation: string) => void;
6
6
  onVote: (id: number) => void;
@@ -1,6 +1,8 @@
1
1
  /// <reference types="react" />
2
- declare type Props = {
2
+ import { Term } from '../@types/Phrase';
3
+ type Props = {
3
4
  hash: string;
5
+ glossary: Term[];
4
6
  translation: string | undefined;
5
7
  language: string;
6
8
  error: string | null;
@@ -9,5 +11,5 @@ declare type Props = {
9
11
  setTranslation: (translation: string) => void;
10
12
  onSubmit: (translation: string, hash: string) => void;
11
13
  };
12
- export default function Composer({ hash, translation, language, error, loading, hide, setTranslation, onSubmit, }: Props): JSX.Element;
14
+ export default function Composer({ hash, glossary, translation, language, error, loading, hide, setTranslation, onSubmit, }: Props): JSX.Element;
13
15
  export {};