@developer_tribe/react-builder 1.2.40 → 1.2.42
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/dist/attributes-editor/FallbackLocalizationField.d.ts +6 -0
- package/dist/build-components/PaywallFooter/PaywallFooter.d.ts +5 -0
- package/dist/build-components/PaywallFooter/PaywallFooterProps.generated.d.ts +68 -0
- package/dist/build-components/index.d.ts +2 -1
- package/dist/build-components/patterns.generated.d.ts +495 -3
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.web.cjs.js +4 -4
- package/dist/index.web.cjs.js.map +1 -1
- package/dist/index.web.d.ts +8 -0
- package/dist/index.web.esm.js +4 -4
- package/dist/index.web.esm.js.map +1 -1
- package/dist/modals/IconPickerModal.d.ts +1 -1
- package/dist/product-base/types.d.ts +3 -0
- package/dist/store.d.ts +25 -0
- package/dist/styles.css +1 -1
- package/dist/types/PreviewConfig.d.ts +1 -1
- package/package.json +2 -2
- package/scripts/public/bin.js +0 -0
- package/src/RenderPage.tsx +1 -1
- package/src/assets/meta.json +1 -1
- package/src/assets/prompt-scheme-onboard.generated.ts +1 -1
- package/src/assets/prompt-scheme-paywall.generated.ts +1 -1
- package/src/assets/samples/paywall-1.json +18 -1
- package/src/assets/samples/paywall-2.json +2 -2
- package/src/assets/samples/paywall-app-delete-offer.json +2 -2
- package/src/assets/samples/paywall-app-open-offer.json +2 -2
- package/src/assets/samples/paywall-back-offer.json +1 -1
- package/src/assets/samples/paywall-notification-offer.json +1 -1
- package/src/assets/samples/vpn-onboard-1.json +3 -3
- package/src/assets/samples/vpn-onboard-2.json +3 -3
- package/src/assets/samples/vpn-onboard-3.json +3 -3
- package/src/assets/samples/vpn-onboard-4.json +3 -3
- package/src/assets/samples/vpn-onboard-5.json +3 -3
- package/src/assets/samples/vpn-onboard-6.json +3 -3
- package/src/assets/samples/vpn-onboard-7.json +3 -3
- package/src/attributes-editor/AttributesEditorFields.tsx +1 -1
- package/src/attributes-editor/AttributesEditorView.tsx +17 -6
- package/src/attributes-editor/FallbackLocalizationField.tsx +384 -0
- package/src/build-components/BIcon/BIcon.tsx +1 -1
- package/src/build-components/OnboardButton/OnboardButton.tsx +1 -1
- package/src/build-components/OnboardButton/pattern.json +1 -1
- package/src/build-components/OnboardFooter/OnboardFooter.tsx +29 -20
- package/src/build-components/OnboardFooter/pattern.json +2 -1
- package/src/build-components/OnboardProvider/pattern.json +1 -1
- package/src/build-components/PaywallCloseButton/PaywallCloseButton.tsx +1 -1
- package/src/build-components/PaywallFooter/PaywallFooter.tsx +242 -0
- package/src/build-components/PaywallFooter/PaywallFooterProps.generated.ts +85 -0
- package/src/build-components/PaywallFooter/pattern.json +86 -0
- package/src/build-components/RenderNode.generated.tsx +5 -0
- package/src/build-components/index.ts +5 -0
- package/src/build-components/patterns.generated.ts +511 -3
- package/src/components/BottomBar.tsx +136 -32
- package/src/components/DeviceNavigationBar.tsx +2 -2
- package/src/hooks/useLocalize.ts +13 -1
- package/src/index.web.ts +19 -0
- package/src/mockOS/managers/mockPermissionManager.ts +5 -3
- package/src/mockOS/managers/navigationManager.ts +6 -4
- package/src/modals/IconPickerModal.tsx +1 -1
- package/src/modals/InspectModal.tsx +106 -0
- package/src/product-base/buildPaywallLocalizationParams.ts +3 -0
- package/src/product-base/types.ts +3 -0
- package/src/store.ts +39 -0
- package/src/styles/base/_global.scss +1 -1
- package/src/types/PreviewConfig.ts +30 -6
- package/dist/build-components/index.generated.d.ts +0 -38
- package/dist/types/Icons.generated.d.ts +0 -2
- package/src/build-components/index.generated.ts +0 -184
- package/src/types/Icons.generated.ts +0 -244
|
@@ -389,7 +389,7 @@
|
|
|
389
389
|
},
|
|
390
390
|
{
|
|
391
391
|
"type": "PaywallSubscribeButton",
|
|
392
|
-
"children": "
|
|
392
|
+
"children": "@subscribe",
|
|
393
393
|
"attributes": {
|
|
394
394
|
"description": "Abonelik onay butonu. (#1)",
|
|
395
395
|
"title": "Subscribe",
|
|
@@ -404,6 +404,23 @@
|
|
|
404
404
|
"testID": "Promo-test-id-1"
|
|
405
405
|
},
|
|
406
406
|
"children": "@baseLocalizedPromoText"
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
"type": "PaywallFooter",
|
|
410
|
+
"attributes": {
|
|
411
|
+
"textLocalizationKey": "base.builder.paywall.footer.description",
|
|
412
|
+
"linkedWordFirstLocalizationKey": "base.builder.paywall.btnPrivacy",
|
|
413
|
+
"linkedWordFirstColor": "STATIC_COLORS.ONBOARD_LINK_COLOR",
|
|
414
|
+
"linkedWordFirstPage": "privacy",
|
|
415
|
+
"linkedWordSecondLocalizationKey": "base.builder.paywall.btnTerms",
|
|
416
|
+
"linkedWordSecondColor": "STATIC_COLORS.ONBOARD_LINK_COLOR",
|
|
417
|
+
"linkedWordSecondPage": "terms",
|
|
418
|
+
"styles": {
|
|
419
|
+
"gap": 8,
|
|
420
|
+
"color": "THEME_COLORS.ONBOARD_FOOTER_TEXT"
|
|
421
|
+
},
|
|
422
|
+
"testID": "PaywallFooter-test-id-1"
|
|
423
|
+
}
|
|
407
424
|
}
|
|
408
425
|
]
|
|
409
426
|
}
|
|
@@ -349,9 +349,9 @@
|
|
|
349
349
|
"children": [
|
|
350
350
|
{
|
|
351
351
|
"type": "PaywallSubscribeButton",
|
|
352
|
-
"children": "
|
|
352
|
+
"children": "@subscribe",
|
|
353
353
|
"attributes": {
|
|
354
|
-
"description": "
|
|
354
|
+
"description": "Abonelik onay butonu. (#1)",
|
|
355
355
|
"title": "Subscribe",
|
|
356
356
|
"testID": "PaywallSubscribeButton-test-id-1"
|
|
357
357
|
}
|
|
@@ -363,9 +363,9 @@
|
|
|
363
363
|
"children": [
|
|
364
364
|
{
|
|
365
365
|
"type": "PaywallSubscribeButton",
|
|
366
|
-
"children": "
|
|
366
|
+
"children": "@subscribe",
|
|
367
367
|
"attributes": {
|
|
368
|
-
"description": "
|
|
368
|
+
"description": "Abonelik onay butonu. (#1)",
|
|
369
369
|
"title": "Subscribe",
|
|
370
370
|
"testID": "PaywallSubscribeButton-test-id-1"
|
|
371
371
|
}
|
|
@@ -363,9 +363,9 @@
|
|
|
363
363
|
"children": [
|
|
364
364
|
{
|
|
365
365
|
"type": "PaywallSubscribeButton",
|
|
366
|
-
"children": "
|
|
366
|
+
"children": "@subscribe",
|
|
367
367
|
"attributes": {
|
|
368
|
-
"description": "
|
|
368
|
+
"description": "Abonelik onay butonu. (#1)",
|
|
369
369
|
"title": "Subscribe",
|
|
370
370
|
"testID": "PaywallSubscribeButton-test-id-1"
|
|
371
371
|
}
|
|
@@ -522,11 +522,11 @@
|
|
|
522
522
|
{
|
|
523
523
|
"type": "OnboardFooter",
|
|
524
524
|
"attributes": {
|
|
525
|
-
"textLocalizationKey": "
|
|
526
|
-
"linkedWordFirstLocalizationKey": "
|
|
525
|
+
"textLocalizationKey": "base.onboard.footer.description",
|
|
526
|
+
"linkedWordFirstLocalizationKey": "base.onboard.btnPrivacy",
|
|
527
527
|
"linkedWordFirstColor": "STATIC_COLORS.ONBOARD_LINK_COLOR",
|
|
528
528
|
"linkedWordFirstPage": "privacy",
|
|
529
|
-
"linkedWordSecondLocalizationKey": "
|
|
529
|
+
"linkedWordSecondLocalizationKey": "base.onboard.btnTerms",
|
|
530
530
|
"linkedWordSecondColor": "STATIC_COLORS.ONBOARD_LINK_COLOR",
|
|
531
531
|
"linkedWordSecondPage": "terms",
|
|
532
532
|
"styles": {
|
|
@@ -525,11 +525,11 @@
|
|
|
525
525
|
{
|
|
526
526
|
"type": "OnboardFooter",
|
|
527
527
|
"attributes": {
|
|
528
|
-
"textLocalizationKey": "
|
|
529
|
-
"linkedWordFirstLocalizationKey": "
|
|
528
|
+
"textLocalizationKey": "base.onboard.footer.description",
|
|
529
|
+
"linkedWordFirstLocalizationKey": "base.onboard.btnPrivacy",
|
|
530
530
|
"linkedWordFirstColor": "STATIC_COLORS.ONBOARD_LINK_COLOR",
|
|
531
531
|
"linkedWordFirstPage": "privacy",
|
|
532
|
-
"linkedWordSecondLocalizationKey": "
|
|
532
|
+
"linkedWordSecondLocalizationKey": "base.onboard.btnTerms",
|
|
533
533
|
"linkedWordSecondColor": "STATIC_COLORS.ONBOARD_LINK_COLOR",
|
|
534
534
|
"linkedWordSecondPage": "terms",
|
|
535
535
|
"styles": {
|
|
@@ -486,11 +486,11 @@
|
|
|
486
486
|
{
|
|
487
487
|
"type": "OnboardFooter",
|
|
488
488
|
"attributes": {
|
|
489
|
-
"textLocalizationKey": "
|
|
490
|
-
"linkedWordFirstLocalizationKey": "
|
|
489
|
+
"textLocalizationKey": "base.onboard.footer.description",
|
|
490
|
+
"linkedWordFirstLocalizationKey": "base.onboard.btnPrivacy",
|
|
491
491
|
"linkedWordFirstColor": "STATIC_COLORS.ONBOARD_LINK_COLOR",
|
|
492
492
|
"linkedWordFirstPage": "privacy",
|
|
493
|
-
"linkedWordSecondLocalizationKey": "
|
|
493
|
+
"linkedWordSecondLocalizationKey": "base.onboard.btnTerms",
|
|
494
494
|
"linkedWordSecondColor": "STATIC_COLORS.ONBOARD_LINK_COLOR",
|
|
495
495
|
"linkedWordSecondPage": "terms",
|
|
496
496
|
"styles": {
|
|
@@ -486,11 +486,11 @@
|
|
|
486
486
|
{
|
|
487
487
|
"type": "OnboardFooter",
|
|
488
488
|
"attributes": {
|
|
489
|
-
"textLocalizationKey": "
|
|
490
|
-
"linkedWordFirstLocalizationKey": "
|
|
489
|
+
"textLocalizationKey": "base.onboard.footer.description",
|
|
490
|
+
"linkedWordFirstLocalizationKey": "base.onboard.btnPrivacy",
|
|
491
491
|
"linkedWordFirstColor": "STATIC_COLORS.ONBOARD_LINK_COLOR",
|
|
492
492
|
"linkedWordFirstPage": "privacy",
|
|
493
|
-
"linkedWordSecondLocalizationKey": "
|
|
493
|
+
"linkedWordSecondLocalizationKey": "base.onboard.btnTerms",
|
|
494
494
|
"linkedWordSecondColor": "STATIC_COLORS.ONBOARD_LINK_COLOR",
|
|
495
495
|
"linkedWordSecondPage": "terms",
|
|
496
496
|
"styles": {
|
|
@@ -710,11 +710,11 @@
|
|
|
710
710
|
{
|
|
711
711
|
"type": "OnboardFooter",
|
|
712
712
|
"attributes": {
|
|
713
|
-
"textLocalizationKey": "
|
|
714
|
-
"linkedWordFirstLocalizationKey": "
|
|
713
|
+
"textLocalizationKey": "base.onboard.footer.description",
|
|
714
|
+
"linkedWordFirstLocalizationKey": "base.onboard.btnPrivacy",
|
|
715
715
|
"linkedWordFirstColor": "STATIC_COLORS.ONBOARD_LINK_COLOR",
|
|
716
716
|
"linkedWordFirstPage": "privacy",
|
|
717
|
-
"linkedWordSecondLocalizationKey": "
|
|
717
|
+
"linkedWordSecondLocalizationKey": "base.onboard.btnTerms",
|
|
718
718
|
"linkedWordSecondColor": "STATIC_COLORS.ONBOARD_LINK_COLOR",
|
|
719
719
|
"linkedWordSecondPage": "terms",
|
|
720
720
|
"styles": {
|
|
@@ -498,11 +498,11 @@
|
|
|
498
498
|
{
|
|
499
499
|
"type": "OnboardFooter",
|
|
500
500
|
"attributes": {
|
|
501
|
-
"textLocalizationKey": "
|
|
502
|
-
"linkedWordFirstLocalizationKey": "
|
|
501
|
+
"textLocalizationKey": "base.onboard.footer.description",
|
|
502
|
+
"linkedWordFirstLocalizationKey": "base.onboard.btnPrivacy",
|
|
503
503
|
"linkedWordFirstColor": "STATIC_COLORS.ONBOARD_LINK_COLOR",
|
|
504
504
|
"linkedWordFirstPage": "privacy",
|
|
505
|
-
"linkedWordSecondLocalizationKey": "
|
|
505
|
+
"linkedWordSecondLocalizationKey": "base.onboard.btnTerms",
|
|
506
506
|
"linkedWordSecondColor": "STATIC_COLORS.ONBOARD_LINK_COLOR",
|
|
507
507
|
"linkedWordSecondPage": "terms",
|
|
508
508
|
"styles": {
|
|
@@ -536,11 +536,11 @@
|
|
|
536
536
|
{
|
|
537
537
|
"type": "OnboardFooter",
|
|
538
538
|
"attributes": {
|
|
539
|
-
"textLocalizationKey": "
|
|
540
|
-
"linkedWordFirstLocalizationKey": "
|
|
539
|
+
"textLocalizationKey": "base.onboard.footer.description",
|
|
540
|
+
"linkedWordFirstLocalizationKey": "base.onboard.btnPrivacy",
|
|
541
541
|
"linkedWordFirstColor": "STATIC_COLORS.ONBOARD_LINK_COLOR",
|
|
542
542
|
"linkedWordFirstPage": "privacy",
|
|
543
|
-
"linkedWordSecondLocalizationKey": "
|
|
543
|
+
"linkedWordSecondLocalizationKey": "base.onboard.btnTerms",
|
|
544
544
|
"linkedWordSecondColor": "STATIC_COLORS.ONBOARD_LINK_COLOR",
|
|
545
545
|
"linkedWordSecondPage": "terms",
|
|
546
546
|
"styles": {
|
|
@@ -5,7 +5,7 @@ import { IconPickerModal } from '../modals/IconPickerModal';
|
|
|
5
5
|
import Modal from '../modals/Modal';
|
|
6
6
|
import { loadFontFamily } from '../utils/loadFontFamily';
|
|
7
7
|
import { fontsDebug } from '../utils/fontsDebug';
|
|
8
|
-
import type { IconsType } from '../types/Icons
|
|
8
|
+
import type { IconsType } from '../types/Icons';
|
|
9
9
|
|
|
10
10
|
type IconTypePickerFieldProps = {
|
|
11
11
|
name: string;
|
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
FontFamilyPickerField,
|
|
10
10
|
IconTypePickerField,
|
|
11
11
|
} from './AttributesEditorFields';
|
|
12
|
+
import { useRenderStore } from '../store';
|
|
13
|
+
import { FallbackLocalizationField } from './FallbackLocalizationField';
|
|
12
14
|
import type {
|
|
13
15
|
AttributesEditorModel,
|
|
14
16
|
AttributesEditorSpecialSection,
|
|
@@ -192,15 +194,24 @@ export function AttributesEditorView(props: AttributesEditorViewProps) {
|
|
|
192
194
|
</div>
|
|
193
195
|
);
|
|
194
196
|
|
|
197
|
+
const CustomChildrenField = useRenderStore(
|
|
198
|
+
(s) => s.renderStringChildrenField,
|
|
199
|
+
);
|
|
200
|
+
|
|
195
201
|
const childrenSection = hasStringChildren ? (
|
|
196
202
|
<div className="attributes-editor__field-wrapper attributes-editor__field-wrapper--children">
|
|
197
203
|
<p className="attributes-editor__field-label">Text</p>
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
+
{CustomChildrenField ? (
|
|
205
|
+
<CustomChildrenField
|
|
206
|
+
value={childrenValue}
|
|
207
|
+
onChange={handleChildrenChange}
|
|
208
|
+
/>
|
|
209
|
+
) : (
|
|
210
|
+
<FallbackLocalizationField
|
|
211
|
+
value={childrenValue}
|
|
212
|
+
onChange={handleChildrenChange}
|
|
213
|
+
/>
|
|
214
|
+
)}
|
|
204
215
|
</div>
|
|
205
216
|
) : null;
|
|
206
217
|
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
import React, { useState, useCallback, useRef, useEffect } from 'react';
|
|
2
|
+
import { useRenderStore } from '../store';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Self-contained fallback localization field for react-builder.
|
|
6
|
+
*
|
|
7
|
+
* Uses the localizationApiConfig from the store to:
|
|
8
|
+
* 1. Pre-load ALL keys on first open (per_page=100, auto-paginate)
|
|
9
|
+
* 2. Filter locally — no subsequent API calls for search
|
|
10
|
+
* 3. Works independently of the host app
|
|
11
|
+
*
|
|
12
|
+
* API: GET {forgeUrl}/api/manage/localization/{platform}?per_page=100&page=N&app_id=X
|
|
13
|
+
* Header: X-Authorization: {forgeToken}
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
type LocalizationRecord = {
|
|
17
|
+
key: string;
|
|
18
|
+
translations: Record<string, { value: string | null }>;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type FallbackLocalizationFieldProps = {
|
|
22
|
+
value: string;
|
|
23
|
+
onChange: (v: string) => void;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export function FallbackLocalizationField({
|
|
27
|
+
value,
|
|
28
|
+
onChange,
|
|
29
|
+
}: FallbackLocalizationFieldProps) {
|
|
30
|
+
const config = useRenderStore((s) => s.localizationApiConfig);
|
|
31
|
+
const [open, setOpen] = useState(false);
|
|
32
|
+
const [query, setQuery] = useState('');
|
|
33
|
+
const [allRecords, setAllRecords] = useState<LocalizationRecord[]>([]);
|
|
34
|
+
const [filteredRecords, setFilteredRecords] = useState<LocalizationRecord[]>(
|
|
35
|
+
[],
|
|
36
|
+
);
|
|
37
|
+
const [preloadDone, setPreloadDone] = useState(false);
|
|
38
|
+
const [loading, setLoading] = useState(false);
|
|
39
|
+
const preloadVersionRef = useRef(0);
|
|
40
|
+
|
|
41
|
+
const canSearch = !!config?.forgeUrl && !!config?.forgeToken;
|
|
42
|
+
const platform = 'ios'; // default platform for fallback
|
|
43
|
+
|
|
44
|
+
// --- Forge API helpers ---
|
|
45
|
+
const buildUrl = useCallback(
|
|
46
|
+
(page: number) => {
|
|
47
|
+
if (!config) return '';
|
|
48
|
+
const base = `${config.forgeUrl.replace(/\/$/, '')}/api/manage/localization/${platform}`;
|
|
49
|
+
const params = new URLSearchParams({
|
|
50
|
+
per_page: '1000',
|
|
51
|
+
page: String(page),
|
|
52
|
+
});
|
|
53
|
+
if (config.forgeAppId) {
|
|
54
|
+
params.append('app_id', String(config.forgeAppId));
|
|
55
|
+
}
|
|
56
|
+
return `${base}?${params.toString()}`;
|
|
57
|
+
},
|
|
58
|
+
[config],
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const fetchPage = useCallback(
|
|
62
|
+
async (page: number) => {
|
|
63
|
+
const url = buildUrl(page);
|
|
64
|
+
const res = await fetch(url, {
|
|
65
|
+
headers: {
|
|
66
|
+
'Content-Type': 'application/json',
|
|
67
|
+
Accept: 'application/json',
|
|
68
|
+
'X-Authorization': config!.forgeToken,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
if (!res.ok) return { data: [] as LocalizationRecord[], hasMore: false };
|
|
72
|
+
const json = await res.json();
|
|
73
|
+
const records: LocalizationRecord[] = json.data ?? [];
|
|
74
|
+
const pagination = json.pagination;
|
|
75
|
+
const hasMore =
|
|
76
|
+
pagination && pagination.current_page < pagination.last_page;
|
|
77
|
+
return { data: records, hasMore };
|
|
78
|
+
},
|
|
79
|
+
[buildUrl, config],
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// --- Pre-load all records on first dialog open ---
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
if (!open || !canSearch || preloadDone) return;
|
|
85
|
+
|
|
86
|
+
const version = ++preloadVersionRef.current;
|
|
87
|
+
setLoading(true);
|
|
88
|
+
|
|
89
|
+
const loadAll = async () => {
|
|
90
|
+
const all: LocalizationRecord[] = [];
|
|
91
|
+
let page = 1;
|
|
92
|
+
let hasMore = true;
|
|
93
|
+
while (hasMore) {
|
|
94
|
+
const result = await fetchPage(page);
|
|
95
|
+
if (version !== preloadVersionRef.current) return;
|
|
96
|
+
all.push(...result.data);
|
|
97
|
+
hasMore = result.hasMore;
|
|
98
|
+
page++;
|
|
99
|
+
}
|
|
100
|
+
if (version === preloadVersionRef.current) {
|
|
101
|
+
setAllRecords(all);
|
|
102
|
+
setFilteredRecords(all);
|
|
103
|
+
setPreloadDone(true);
|
|
104
|
+
setLoading(false);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
void loadAll();
|
|
109
|
+
}, [open, canSearch, preloadDone, fetchPage]);
|
|
110
|
+
|
|
111
|
+
// --- Local filter ---
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
if (!preloadDone) return;
|
|
114
|
+
const q = query.trim().toLowerCase();
|
|
115
|
+
if (!q) {
|
|
116
|
+
setFilteredRecords(allRecords);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
setFilteredRecords(
|
|
120
|
+
allRecords.filter((r) => {
|
|
121
|
+
if (r.key.toLowerCase().includes(q)) return true;
|
|
122
|
+
return Object.values(r.translations).some(
|
|
123
|
+
(t) => t?.value && t.value.toLowerCase().includes(q),
|
|
124
|
+
);
|
|
125
|
+
}),
|
|
126
|
+
);
|
|
127
|
+
}, [query, allRecords, preloadDone]);
|
|
128
|
+
|
|
129
|
+
const invalidate = () => {
|
|
130
|
+
setPreloadDone(false);
|
|
131
|
+
setAllRecords([]);
|
|
132
|
+
setFilteredRecords([]);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<>
|
|
137
|
+
<div style={styles.row}>
|
|
138
|
+
<input
|
|
139
|
+
type="text"
|
|
140
|
+
className="attributes-editor__text-input"
|
|
141
|
+
value={value ?? ''}
|
|
142
|
+
onChange={(e) => onChange(e.target.value)}
|
|
143
|
+
style={{ flex: 1 }}
|
|
144
|
+
/>
|
|
145
|
+
{canSearch ? (
|
|
146
|
+
<button
|
|
147
|
+
type="button"
|
|
148
|
+
onClick={() => setOpen(true)}
|
|
149
|
+
title="Browse localization keys"
|
|
150
|
+
style={styles.browseBtn}
|
|
151
|
+
>
|
|
152
|
+
🔍
|
|
153
|
+
</button>
|
|
154
|
+
) : (
|
|
155
|
+
<span title="Localization API yapılandırılmadı" style={styles.dot} />
|
|
156
|
+
)}
|
|
157
|
+
</div>
|
|
158
|
+
{open && (
|
|
159
|
+
<div onClick={() => setOpen(false)} style={styles.overlay}>
|
|
160
|
+
<div onClick={(e) => e.stopPropagation()} style={styles.modal}>
|
|
161
|
+
{/* Header */}
|
|
162
|
+
<div style={styles.header}>
|
|
163
|
+
<span style={{ fontWeight: 600, fontSize: 14 }}>
|
|
164
|
+
Localization Anahtarı Seç
|
|
165
|
+
</span>
|
|
166
|
+
<button
|
|
167
|
+
type="button"
|
|
168
|
+
onClick={() => setOpen(false)}
|
|
169
|
+
style={styles.closeBtn}
|
|
170
|
+
>
|
|
171
|
+
✕
|
|
172
|
+
</button>
|
|
173
|
+
</div>
|
|
174
|
+
{/* Search */}
|
|
175
|
+
<div style={{ padding: '10px 16px 6px' }}>
|
|
176
|
+
<input
|
|
177
|
+
autoFocus
|
|
178
|
+
type="text"
|
|
179
|
+
value={query}
|
|
180
|
+
onChange={(e) => setQuery(e.target.value)}
|
|
181
|
+
placeholder="Anahtar veya çeviri ara…"
|
|
182
|
+
style={styles.searchInput}
|
|
183
|
+
/>
|
|
184
|
+
</div>
|
|
185
|
+
{/* Info */}
|
|
186
|
+
{preloadDone && (
|
|
187
|
+
<div style={styles.infoBar}>
|
|
188
|
+
{filteredRecords.length} / {allRecords.length} anahtar
|
|
189
|
+
</div>
|
|
190
|
+
)}
|
|
191
|
+
{/* Results */}
|
|
192
|
+
<div style={styles.resultsList}>
|
|
193
|
+
{loading && <div style={styles.emptyMsg}>Yükleniyor…</div>}
|
|
194
|
+
{!loading && preloadDone && filteredRecords.length === 0 && (
|
|
195
|
+
<div style={styles.emptyMsg}>Sonuç bulunamadı</div>
|
|
196
|
+
)}
|
|
197
|
+
{filteredRecords.map((entry) => {
|
|
198
|
+
const isSelected = entry.key === value;
|
|
199
|
+
const translations = Object.entries(entry.translations);
|
|
200
|
+
return (
|
|
201
|
+
<button
|
|
202
|
+
key={entry.key}
|
|
203
|
+
type="button"
|
|
204
|
+
onClick={() => {
|
|
205
|
+
onChange(entry.key);
|
|
206
|
+
setOpen(false);
|
|
207
|
+
}}
|
|
208
|
+
style={{
|
|
209
|
+
...styles.resultItem,
|
|
210
|
+
background: isSelected ? '#333' : 'transparent',
|
|
211
|
+
border: isSelected
|
|
212
|
+
? '1px solid #6366f1'
|
|
213
|
+
: '1px solid #333',
|
|
214
|
+
}}
|
|
215
|
+
onMouseEnter={(e) => {
|
|
216
|
+
if (!isSelected)
|
|
217
|
+
e.currentTarget.style.background = '#2a2a2a';
|
|
218
|
+
}}
|
|
219
|
+
onMouseLeave={(e) => {
|
|
220
|
+
if (!isSelected)
|
|
221
|
+
e.currentTarget.style.background = 'transparent';
|
|
222
|
+
}}
|
|
223
|
+
>
|
|
224
|
+
<div
|
|
225
|
+
style={{
|
|
226
|
+
fontWeight: 600,
|
|
227
|
+
fontSize: 12,
|
|
228
|
+
color: '#e0e0e0',
|
|
229
|
+
}}
|
|
230
|
+
>
|
|
231
|
+
{entry.key}
|
|
232
|
+
</div>
|
|
233
|
+
{translations.slice(0, 3).map(([lang, cell]) => (
|
|
234
|
+
<div key={lang} style={styles.translationRow}>
|
|
235
|
+
<span style={styles.langBadge}>{lang}</span>
|
|
236
|
+
<span style={styles.translationText}>
|
|
237
|
+
{cell?.value && cell.value.length > 80
|
|
238
|
+
? cell.value.slice(0, 80) + '…'
|
|
239
|
+
: cell?.value || (
|
|
240
|
+
<span style={{ color: '#ef4444' }}>Eksik</span>
|
|
241
|
+
)}
|
|
242
|
+
</span>
|
|
243
|
+
</div>
|
|
244
|
+
))}
|
|
245
|
+
</button>
|
|
246
|
+
);
|
|
247
|
+
})}
|
|
248
|
+
</div>
|
|
249
|
+
</div>
|
|
250
|
+
</div>
|
|
251
|
+
)}
|
|
252
|
+
</>
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const styles: Record<string, React.CSSProperties> = {
|
|
257
|
+
row: {
|
|
258
|
+
display: 'flex',
|
|
259
|
+
alignItems: 'center',
|
|
260
|
+
gap: 4,
|
|
261
|
+
},
|
|
262
|
+
browseBtn: {
|
|
263
|
+
width: 28,
|
|
264
|
+
height: 28,
|
|
265
|
+
display: 'flex',
|
|
266
|
+
alignItems: 'center',
|
|
267
|
+
justifyContent: 'center',
|
|
268
|
+
border: '1px solid hsl(0 0% 40%)',
|
|
269
|
+
borderRadius: 4,
|
|
270
|
+
background: '#2a2a2a',
|
|
271
|
+
color: '#fff',
|
|
272
|
+
cursor: 'pointer',
|
|
273
|
+
fontSize: 13,
|
|
274
|
+
flexShrink: 0,
|
|
275
|
+
},
|
|
276
|
+
dot: {
|
|
277
|
+
display: 'inline-block',
|
|
278
|
+
width: 8,
|
|
279
|
+
height: 8,
|
|
280
|
+
borderRadius: '50%',
|
|
281
|
+
backgroundColor: '#ef4444',
|
|
282
|
+
flexShrink: 0,
|
|
283
|
+
cursor: 'help',
|
|
284
|
+
},
|
|
285
|
+
overlay: {
|
|
286
|
+
position: 'fixed',
|
|
287
|
+
inset: 0,
|
|
288
|
+
background: 'rgba(0,0,0,0.6)',
|
|
289
|
+
zIndex: 9999,
|
|
290
|
+
display: 'flex',
|
|
291
|
+
alignItems: 'center',
|
|
292
|
+
justifyContent: 'center',
|
|
293
|
+
},
|
|
294
|
+
modal: {
|
|
295
|
+
background: '#1e1e1e',
|
|
296
|
+
borderRadius: 10,
|
|
297
|
+
width: 460,
|
|
298
|
+
maxHeight: '75vh',
|
|
299
|
+
display: 'flex',
|
|
300
|
+
flexDirection: 'column',
|
|
301
|
+
boxShadow: '0 12px 40px rgba(0,0,0,0.5)',
|
|
302
|
+
color: '#e0e0e0',
|
|
303
|
+
border: '1px solid #333',
|
|
304
|
+
overflow: 'hidden',
|
|
305
|
+
},
|
|
306
|
+
header: {
|
|
307
|
+
display: 'flex',
|
|
308
|
+
justifyContent: 'space-between',
|
|
309
|
+
alignItems: 'center',
|
|
310
|
+
padding: '14px 18px',
|
|
311
|
+
borderBottom: '1px solid #333',
|
|
312
|
+
},
|
|
313
|
+
closeBtn: {
|
|
314
|
+
background: 'none',
|
|
315
|
+
border: 'none',
|
|
316
|
+
cursor: 'pointer',
|
|
317
|
+
fontSize: 18,
|
|
318
|
+
color: '#999',
|
|
319
|
+
padding: 4,
|
|
320
|
+
},
|
|
321
|
+
searchInput: {
|
|
322
|
+
width: '100%',
|
|
323
|
+
padding: '8px 10px',
|
|
324
|
+
border: '1px solid #444',
|
|
325
|
+
borderRadius: 6,
|
|
326
|
+
background: '#2a2a2a',
|
|
327
|
+
color: '#e0e0e0',
|
|
328
|
+
fontSize: 13,
|
|
329
|
+
outline: 'none',
|
|
330
|
+
boxSizing: 'border-box' as const,
|
|
331
|
+
},
|
|
332
|
+
infoBar: {
|
|
333
|
+
padding: '2px 18px 4px',
|
|
334
|
+
fontSize: 11,
|
|
335
|
+
color: '#666',
|
|
336
|
+
textAlign: 'right' as const,
|
|
337
|
+
},
|
|
338
|
+
resultsList: {
|
|
339
|
+
overflowY: 'auto' as const,
|
|
340
|
+
flex: 1,
|
|
341
|
+
padding: '6px 10px 10px',
|
|
342
|
+
},
|
|
343
|
+
emptyMsg: {
|
|
344
|
+
padding: 20,
|
|
345
|
+
textAlign: 'center' as const,
|
|
346
|
+
color: '#888',
|
|
347
|
+
fontSize: 13,
|
|
348
|
+
},
|
|
349
|
+
resultItem: {
|
|
350
|
+
display: 'block',
|
|
351
|
+
width: '100%',
|
|
352
|
+
textAlign: 'left' as const,
|
|
353
|
+
borderRadius: 6,
|
|
354
|
+
padding: '8px 10px',
|
|
355
|
+
cursor: 'pointer',
|
|
356
|
+
marginBottom: 3,
|
|
357
|
+
transition: 'background 0.15s',
|
|
358
|
+
},
|
|
359
|
+
translationRow: {
|
|
360
|
+
display: 'flex',
|
|
361
|
+
alignItems: 'center',
|
|
362
|
+
gap: 6,
|
|
363
|
+
marginTop: 3,
|
|
364
|
+
fontSize: 11,
|
|
365
|
+
},
|
|
366
|
+
langBadge: {
|
|
367
|
+
display: 'inline-block',
|
|
368
|
+
background: '#333',
|
|
369
|
+
border: '1px solid #444',
|
|
370
|
+
borderRadius: 3,
|
|
371
|
+
padding: '1px 5px',
|
|
372
|
+
fontSize: 10,
|
|
373
|
+
fontWeight: 600,
|
|
374
|
+
color: '#aaa',
|
|
375
|
+
flexShrink: 0,
|
|
376
|
+
},
|
|
377
|
+
translationText: {
|
|
378
|
+
color: '#999',
|
|
379
|
+
overflow: 'hidden',
|
|
380
|
+
whiteSpace: 'nowrap' as const,
|
|
381
|
+
textOverflow: 'ellipsis',
|
|
382
|
+
flex: 1,
|
|
383
|
+
},
|
|
384
|
+
};
|
|
@@ -6,7 +6,7 @@ import { useLogRender } from '../../utils/useLogRender';
|
|
|
6
6
|
import { isNodeSelected, SELECTED_OUTLINE_STYLE } from '../../utils/selection';
|
|
7
7
|
import { useMergedStyle } from '../../utils/useMergedStyle';
|
|
8
8
|
import { Icon } from '../../components/Icon.generated';
|
|
9
|
-
import { IconsType } from '../../types/Icons
|
|
9
|
+
import { IconsType } from '../../types/Icons';
|
|
10
10
|
import { useExtractTextStyle } from '../../attribute-analyser/style/web/useExtractTextStyle';
|
|
11
11
|
|
|
12
12
|
export function BIcon({ node }: BIconComponentProps) {
|
|
@@ -56,7 +56,7 @@ export function OnboardButton({ node }: OnboardButtonComponentProps) {
|
|
|
56
56
|
if (context) {
|
|
57
57
|
context.navigation(eventTarget as any);
|
|
58
58
|
} else {
|
|
59
|
-
|
|
59
|
+
console.warn('Mock OS context not available for navigation.');
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
}
|