@developer_tribe/react-builder 1.0.9 → 1.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/dist/build-components/BIcon/BIconProps.generated.d.ts +2 -2
- package/dist/build-components/OnboardFooter/OnboardFooterProps.generated.d.ts +2 -2
- package/dist/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.d.ts +2 -2
- package/dist/build-components/OnboardTitle/OnboardTitleProps.generated.d.ts +2 -2
- package/dist/build-components/PaywallCloseButton/PaywallCloseButtonProps.generated.d.ts +2 -2
- package/dist/build-components/Text/TextProps.generated.d.ts +2 -2
- package/dist/build-components/patterns.generated.d.ts +78 -30
- package/dist/hooks/useProjectFonts.d.ts +13 -0
- package/dist/index.cjs.js +3 -3
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.esm.js +3 -3
- package/dist/index.esm.js.map +1 -1
- package/dist/index.native.cjs.js +3 -3
- package/dist/index.native.cjs.js.map +1 -1
- package/dist/index.native.esm.js +3 -3
- package/dist/index.native.esm.js.map +1 -1
- package/dist/pages/ProjectPage.d.ts +4 -1
- package/dist/store.d.ts +11 -0
- package/dist/types/Fonts.d.ts +12 -0
- package/dist/utils/fontWeight.d.ts +3 -0
- package/dist/utils/fontsDebug.d.ts +12 -0
- package/dist/utils/loadFontFamily.d.ts +30 -0
- package/package.json +1 -1
- package/scripts/prebuild/utils/createGeneratedProps.js +9 -1
- package/scripts/prebuild/utils/validateAllComponentsOrThrow.js +2 -0
- package/src/AttributesEditor.tsx +237 -1
- package/src/RenderPage.tsx +15 -6
- package/src/attributes-editor/Field.tsx +18 -0
- package/src/build-components/BIcon/BIconProps.generated.ts +2 -13
- package/src/build-components/OnboardFooter/OnboardFooterProps.generated.ts +2 -13
- package/src/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.ts +2 -13
- package/src/build-components/OnboardTitle/OnboardTitleProps.generated.ts +2 -13
- package/src/build-components/PaywallCloseButton/PaywallCloseButtonProps.generated.ts +2 -13
- package/src/build-components/Text/TextProps.generated.ts +2 -13
- package/src/build-components/Text/pattern.json +13 -17
- package/src/build-components/patterns.generated.ts +78 -102
- package/src/components/BuilderButton.tsx +13 -11
- package/src/hooks/useProjectFonts.ts +130 -0
- package/src/index.ts +1 -0
- package/src/pages/ProjectPage.tsx +8 -0
- package/src/store.ts +46 -1
- package/src/types/Fonts.ts +16 -0
- package/src/utils/analyseNodeByPatterns.ts +9 -0
- package/src/utils/extractTextStyle.ts +98 -1
- package/src/utils/fontWeight.ts +29 -0
- package/src/utils/fontsDebug.ts +16 -0
- package/src/utils/loadFontFamily.ts +318 -0
- package/src/utils/patterns.ts +2 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Project, ProjectColors } from '../types/Project';
|
|
2
2
|
import { AppConfig } from '../types/PreviewConfig';
|
|
3
3
|
import type { LogLevel } from '../types/Project';
|
|
4
|
+
import type { Fonts } from '../types/Fonts';
|
|
4
5
|
export type ProjectPageProps = {
|
|
5
6
|
project: Project;
|
|
6
7
|
onSaveProject: (project: Project) => void;
|
|
@@ -9,5 +10,7 @@ export type ProjectPageProps = {
|
|
|
9
10
|
projectColors?: ProjectColors;
|
|
10
11
|
onSaveProjectColors?: (colors: ProjectColors) => void;
|
|
11
12
|
name?: string;
|
|
13
|
+
fonts: Fonts;
|
|
14
|
+
appFont: string | undefined;
|
|
12
15
|
};
|
|
13
|
-
export declare function ProjectPage({ project, appConfig, onSaveProject, logLevel, projectColors, onSaveProjectColors, name, }: ProjectPageProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export declare function ProjectPage({ project, appConfig, onSaveProject, logLevel, projectColors, onSaveProjectColors, name, fonts, appFont, }: ProjectPageProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/store.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { Node } from './types/Node';
|
|
|
4
4
|
import type { LogEntry, LogLevel, ProjectColors } from './types/Project';
|
|
5
5
|
import type { Product } from './paywall/types/paywall-types';
|
|
6
6
|
import type { PaywallBenefits, PaywallBenefitValue } from './paywall/types/benefits';
|
|
7
|
+
import type { Fonts } from './types/Fonts';
|
|
7
8
|
type RenderStore = {
|
|
8
9
|
projectName: string;
|
|
9
10
|
setProjectName: (name: string) => void;
|
|
@@ -41,6 +42,16 @@ type RenderStore = {
|
|
|
41
42
|
timestamp?: number;
|
|
42
43
|
}) => void;
|
|
43
44
|
clearLogs: () => void;
|
|
45
|
+
fonts: Fonts;
|
|
46
|
+
setFonts: (fonts: Fonts) => void;
|
|
47
|
+
appFont: string | undefined;
|
|
48
|
+
setAppFont: (appFont: string | undefined) => void;
|
|
49
|
+
errors: string[];
|
|
50
|
+
setErrors: (errors: string[]) => void;
|
|
51
|
+
addError: (error: string) => void;
|
|
52
|
+
clearErrors: () => void;
|
|
53
|
+
loadedFonts: string[];
|
|
54
|
+
markFontLoaded: (fontFamily: string) => void;
|
|
44
55
|
};
|
|
45
56
|
export declare const useRenderStore: import("zustand/traditional").UseBoundStoreWithEqualityFn<Omit<import("zustand/vanilla").StoreApi<RenderStore>, "setState" | "persist"> & {
|
|
46
57
|
setState(partial: RenderStore | Partial<RenderStore> | ((state: RenderStore) => RenderStore | Partial<RenderStore>), replace?: false | undefined): unknown;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** CSS font-weight key (usually "100".."900"). */
|
|
2
|
+
export type FontWeightKey = string;
|
|
3
|
+
/** URL to a font file (ttf/otf/woff/woff2). */
|
|
4
|
+
export type FontFileUrl = string;
|
|
5
|
+
/** Map of font-weight -> font file URL. */
|
|
6
|
+
export type FontFamilySources = Record<FontWeightKey, FontFileUrl>;
|
|
7
|
+
export type FontDefinition = {
|
|
8
|
+
name: string;
|
|
9
|
+
family: FontFamilySources;
|
|
10
|
+
projects?: string[];
|
|
11
|
+
};
|
|
12
|
+
export type Fonts = FontDefinition[];
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const FONT_WEIGHT_OPTIONS: readonly ["normal", "bold", "100", "200", "300", "400", "500", "600", "700", "800", "900"];
|
|
2
|
+
export type FontWeightOption = (typeof FONT_WEIGHT_OPTIONS)[number];
|
|
3
|
+
export declare function normalizeFontWeight(value: unknown): FontWeightOption | undefined;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
type Payload = unknown;
|
|
2
|
+
/**
|
|
3
|
+
* Intentionally silent logger for font pipeline.
|
|
4
|
+
* (We keep the interface so call sites don't need to change.)
|
|
5
|
+
*/
|
|
6
|
+
export declare const fontsDebug: {
|
|
7
|
+
info(_message: string, _payload?: Payload): void;
|
|
8
|
+
warn(_message: string, _payload?: Payload): void;
|
|
9
|
+
error(_message: string, _payload?: Payload): void;
|
|
10
|
+
compactError(_message: string, _error: unknown, _payload?: Payload): void;
|
|
11
|
+
};
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Fonts, FontDefinition } from '../types/Fonts';
|
|
2
|
+
export declare function findFontDefinition(fonts: Fonts, familyName: string): FontDefinition | undefined;
|
|
3
|
+
/**
|
|
4
|
+
* Given a family sources map (weightKey -> url) and a requested weight,
|
|
5
|
+
* returns the closest available weight key (or a sensible fallback).
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveClosestFontWeightKey(family: Record<string, string>, requestedWeight?: string): string | undefined;
|
|
8
|
+
export declare function resolveFontSourceForWeight(family: Record<string, string>, requestedWeight?: string): {
|
|
9
|
+
weight: string;
|
|
10
|
+
url: string;
|
|
11
|
+
} | undefined;
|
|
12
|
+
type LoadFontFamilyOptions = {
|
|
13
|
+
preferWeight?: string;
|
|
14
|
+
/**
|
|
15
|
+
* If true (default), fetch the font file via `fetch()` first so:
|
|
16
|
+
* - the request is visible in DevTools Network (Fetch/XHR)
|
|
17
|
+
* - we can log response status and surface CORS/404 clearly
|
|
18
|
+
*
|
|
19
|
+
* If false, falls back to `new FontFace(name, 'url(...)')`.
|
|
20
|
+
*/
|
|
21
|
+
forceFetch?: boolean;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Web-only font loader.
|
|
25
|
+
* - Uses the `FontFace` API when available.
|
|
26
|
+
* - Loads a single preferred weight (default: 400) to keep it "lazy".
|
|
27
|
+
* - Caller is responsible for caching (e.g. via `loadedFonts` in the store).
|
|
28
|
+
*/
|
|
29
|
+
export declare function loadFontFamily(fonts: Fonts, familyName: string, options?: LoadFontFamilyOptions): Promise<void>;
|
|
30
|
+
export {};
|
package/package.json
CHANGED
|
@@ -10,9 +10,17 @@ const isPrimitive = t =>
|
|
|
10
10
|
t === 'boolean' ||
|
|
11
11
|
t === 'color' ||
|
|
12
12
|
t === 'size' ||
|
|
13
|
+
t === 'fontFamily' ||
|
|
14
|
+
t === 'fontWeight' ||
|
|
13
15
|
t === 'iconType';
|
|
14
16
|
const toTsPrimitive = t =>
|
|
15
|
-
t === 'color' ||
|
|
17
|
+
t === 'color' ||
|
|
18
|
+
t === 'size' ||
|
|
19
|
+
t === 'iconType' ||
|
|
20
|
+
t === 'fontFamily' ||
|
|
21
|
+
t === 'fontWeight'
|
|
22
|
+
? 'string'
|
|
23
|
+
: t;
|
|
16
24
|
const getArrayItem = t =>
|
|
17
25
|
typeof t === 'string' && t.endsWith('[]') ? t.slice(0, -2) : null;
|
|
18
26
|
// Convert a property name like "animation" or "on-press" to PascalCase
|
|
@@ -133,6 +133,8 @@ async function validatePatternJson(componentDir, componentName) {
|
|
|
133
133
|
t === 'boolean' ||
|
|
134
134
|
t === 'color' ||
|
|
135
135
|
t === 'size' ||
|
|
136
|
+
t === 'fontFamily' ||
|
|
137
|
+
t === 'fontWeight' ||
|
|
136
138
|
t === 'iconType';
|
|
137
139
|
const isNeverType = t => t === 'never';
|
|
138
140
|
const hasCustomTypes = typeof data.types === 'object' && data.types != null;
|
package/src/AttributesEditor.tsx
CHANGED
|
@@ -24,6 +24,9 @@ import { MockableFeatureModal } from './modals';
|
|
|
24
24
|
import { Icon } from './components/Icon.generated';
|
|
25
25
|
import { IconPickerModal } from './modals/IconPickerModal';
|
|
26
26
|
import type { IconsType } from './types/Icons';
|
|
27
|
+
import Modal from './modals/Modal';
|
|
28
|
+
import { loadFontFamily } from './utils/loadFontFamily';
|
|
29
|
+
import { fontsDebug } from './utils/fontsDebug';
|
|
27
30
|
|
|
28
31
|
type AttributesEditorProps = {
|
|
29
32
|
node: Node;
|
|
@@ -71,7 +74,22 @@ export function AttributesEditor({
|
|
|
71
74
|
|
|
72
75
|
const baseData = node as NodeData<NodeDefaultAttribute>;
|
|
73
76
|
const data = useNode(baseData);
|
|
74
|
-
const
|
|
77
|
+
const {
|
|
78
|
+
appConfig,
|
|
79
|
+
fonts: projectFonts,
|
|
80
|
+
loadedFonts,
|
|
81
|
+
markFontLoaded,
|
|
82
|
+
addError,
|
|
83
|
+
} = useRenderStore((state) => ({
|
|
84
|
+
appConfig: state.appConfig,
|
|
85
|
+
fonts: state.fonts,
|
|
86
|
+
loadedFonts: state.loadedFonts,
|
|
87
|
+
markFontLoaded: state.markFontLoaded,
|
|
88
|
+
addError: state.addError,
|
|
89
|
+
}));
|
|
90
|
+
const [activeFontField, setActiveFontField] = useState<string | null>(null);
|
|
91
|
+
const [isFontLoading, setIsFontLoading] = useState(false);
|
|
92
|
+
const [fontLoadError, setFontLoadError] = useState<string | null>(null);
|
|
75
93
|
const projectColorsForPicker = useMemo<ProjectColors | undefined>(() => {
|
|
76
94
|
if (projectColors) {
|
|
77
95
|
return projectColors;
|
|
@@ -401,6 +419,220 @@ export function AttributesEditor({
|
|
|
401
419
|
[activeIconField, handleAttributeChange],
|
|
402
420
|
);
|
|
403
421
|
|
|
422
|
+
const renderFontFamilyField = useCallback(
|
|
423
|
+
(name: string, currentValue: unknown) => {
|
|
424
|
+
const normalized =
|
|
425
|
+
typeof currentValue === 'string' && currentValue.trim().length > 0
|
|
426
|
+
? currentValue.trim()
|
|
427
|
+
: undefined;
|
|
428
|
+
const fontsList = Array.isArray(projectFonts) ? projectFonts : [];
|
|
429
|
+
const loaded = Array.isArray(loadedFonts) ? loadedFonts : [];
|
|
430
|
+
const isOpen = activeFontField === name;
|
|
431
|
+
return (
|
|
432
|
+
<>
|
|
433
|
+
<button
|
|
434
|
+
type="button"
|
|
435
|
+
onClick={() => {
|
|
436
|
+
setFontLoadError(null);
|
|
437
|
+
setActiveFontField(name);
|
|
438
|
+
}}
|
|
439
|
+
style={{
|
|
440
|
+
width: '100%',
|
|
441
|
+
display: 'flex',
|
|
442
|
+
alignItems: 'center',
|
|
443
|
+
justifyContent: 'space-between',
|
|
444
|
+
gap: 8,
|
|
445
|
+
borderRadius: 6,
|
|
446
|
+
border: '1px solid #ddd',
|
|
447
|
+
padding: '8px 10px',
|
|
448
|
+
background: 'hsl(var(--card, var(--rb-card, 0 0% 100%)))',
|
|
449
|
+
cursor: 'pointer',
|
|
450
|
+
}}
|
|
451
|
+
>
|
|
452
|
+
<span style={{ flex: 1, textAlign: 'left', fontWeight: 500 }}>
|
|
453
|
+
{normalized ?? 'Select font'}
|
|
454
|
+
</span>
|
|
455
|
+
<span style={{ fontSize: 12, color: '#666' }}>Open</span>
|
|
456
|
+
</button>
|
|
457
|
+
|
|
458
|
+
{isOpen ? (
|
|
459
|
+
<Modal
|
|
460
|
+
ariaLabelledBy="font-family-picker-title"
|
|
461
|
+
onClose={() => {
|
|
462
|
+
if (isFontLoading) return;
|
|
463
|
+
setActiveFontField(null);
|
|
464
|
+
}}
|
|
465
|
+
closeOnOverlayClick={!isFontLoading}
|
|
466
|
+
closeOnEsc={!isFontLoading}
|
|
467
|
+
>
|
|
468
|
+
<div style={{ display: 'grid', gap: 12 }}>
|
|
469
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
|
470
|
+
<p
|
|
471
|
+
id="font-family-picker-title"
|
|
472
|
+
style={{ margin: 0, fontWeight: 700 }}
|
|
473
|
+
>
|
|
474
|
+
Select Font
|
|
475
|
+
</p>
|
|
476
|
+
<div style={{ flex: 1 }} />
|
|
477
|
+
<button
|
|
478
|
+
type="button"
|
|
479
|
+
className="editor-button"
|
|
480
|
+
onClick={() => {
|
|
481
|
+
handleAttributeChange(name, undefined);
|
|
482
|
+
setFontLoadError(null);
|
|
483
|
+
setActiveFontField(null);
|
|
484
|
+
}}
|
|
485
|
+
disabled={isFontLoading}
|
|
486
|
+
>
|
|
487
|
+
Clear
|
|
488
|
+
</button>
|
|
489
|
+
<button
|
|
490
|
+
type="button"
|
|
491
|
+
className="editor-button"
|
|
492
|
+
onClick={() => setActiveFontField(null)}
|
|
493
|
+
disabled={isFontLoading}
|
|
494
|
+
>
|
|
495
|
+
Close
|
|
496
|
+
</button>
|
|
497
|
+
</div>
|
|
498
|
+
|
|
499
|
+
{isFontLoading ? (
|
|
500
|
+
<div style={{ fontSize: 12, color: '#666' }}>
|
|
501
|
+
Loading font…
|
|
502
|
+
</div>
|
|
503
|
+
) : null}
|
|
504
|
+
{fontLoadError ? (
|
|
505
|
+
<div style={{ fontSize: 12, color: '#b00020' }}>
|
|
506
|
+
{fontLoadError}
|
|
507
|
+
</div>
|
|
508
|
+
) : null}
|
|
509
|
+
|
|
510
|
+
<div style={{ fontSize: 12, color: '#666' }}>
|
|
511
|
+
{fontsList.length} fonts
|
|
512
|
+
</div>
|
|
513
|
+
|
|
514
|
+
<div
|
|
515
|
+
style={{
|
|
516
|
+
display: 'grid',
|
|
517
|
+
gridTemplateColumns:
|
|
518
|
+
'repeat(auto-fill, minmax(180px, 1fr))',
|
|
519
|
+
gap: 8,
|
|
520
|
+
maxHeight: '60vh',
|
|
521
|
+
overflow: 'auto',
|
|
522
|
+
paddingRight: 4,
|
|
523
|
+
}}
|
|
524
|
+
>
|
|
525
|
+
{fontsList.map((font) => {
|
|
526
|
+
const fontName = font?.name;
|
|
527
|
+
if (typeof fontName !== 'string' || !fontName.trim()) {
|
|
528
|
+
return null;
|
|
529
|
+
}
|
|
530
|
+
const familyName = fontName.trim();
|
|
531
|
+
const isActive = normalized === familyName;
|
|
532
|
+
const isLoaded = loaded.includes(familyName);
|
|
533
|
+
return (
|
|
534
|
+
<button
|
|
535
|
+
key={familyName}
|
|
536
|
+
type="button"
|
|
537
|
+
disabled={isFontLoading}
|
|
538
|
+
onClick={async () => {
|
|
539
|
+
fontsDebug.info(
|
|
540
|
+
'AttributesEditor: select fontFamily',
|
|
541
|
+
{
|
|
542
|
+
field: name,
|
|
543
|
+
familyName,
|
|
544
|
+
wasLoaded: isLoaded,
|
|
545
|
+
},
|
|
546
|
+
);
|
|
547
|
+
setFontLoadError(null);
|
|
548
|
+
handleAttributeChange(name, familyName);
|
|
549
|
+
if (isLoaded) return;
|
|
550
|
+
setIsFontLoading(true);
|
|
551
|
+
try {
|
|
552
|
+
fontsDebug.info(
|
|
553
|
+
'AttributesEditor: loadFontFamily start',
|
|
554
|
+
{ familyName },
|
|
555
|
+
);
|
|
556
|
+
await loadFontFamily(fontsList, familyName, {
|
|
557
|
+
forceFetch: true,
|
|
558
|
+
});
|
|
559
|
+
markFontLoaded(familyName);
|
|
560
|
+
fontsDebug.info(
|
|
561
|
+
'AttributesEditor: loadFontFamily success',
|
|
562
|
+
{ familyName },
|
|
563
|
+
);
|
|
564
|
+
} catch (e) {
|
|
565
|
+
const msg =
|
|
566
|
+
e instanceof Error ? e.message : String(e);
|
|
567
|
+
setFontLoadError(
|
|
568
|
+
`Failed to load "${familyName}": ${msg}`,
|
|
569
|
+
);
|
|
570
|
+
addError(
|
|
571
|
+
`Failed to load font "${familyName}": ${msg}`,
|
|
572
|
+
);
|
|
573
|
+
fontsDebug.compactError(
|
|
574
|
+
'AttributesEditor: loadFontFamily failed',
|
|
575
|
+
e,
|
|
576
|
+
{ familyName },
|
|
577
|
+
);
|
|
578
|
+
} finally {
|
|
579
|
+
setIsFontLoading(false);
|
|
580
|
+
}
|
|
581
|
+
}}
|
|
582
|
+
style={{
|
|
583
|
+
display: 'flex',
|
|
584
|
+
alignItems: 'center',
|
|
585
|
+
justifyContent: 'space-between',
|
|
586
|
+
gap: 8,
|
|
587
|
+
borderRadius: 8,
|
|
588
|
+
border: isActive
|
|
589
|
+
? '2px solid #222'
|
|
590
|
+
: '1px solid #ddd',
|
|
591
|
+
padding: '8px 10px',
|
|
592
|
+
background:
|
|
593
|
+
'hsl(var(--card, var(--rb-card, 0 0% 100%)))',
|
|
594
|
+
cursor: isFontLoading ? 'not-allowed' : 'pointer',
|
|
595
|
+
}}
|
|
596
|
+
aria-label={`Select font ${familyName}`}
|
|
597
|
+
>
|
|
598
|
+
<span
|
|
599
|
+
style={{
|
|
600
|
+
fontSize: 12,
|
|
601
|
+
fontWeight: isActive ? 700 : 500,
|
|
602
|
+
textAlign: 'left',
|
|
603
|
+
overflow: 'hidden',
|
|
604
|
+
textOverflow: 'ellipsis',
|
|
605
|
+
whiteSpace: 'nowrap',
|
|
606
|
+
}}
|
|
607
|
+
title={familyName}
|
|
608
|
+
>
|
|
609
|
+
{familyName}
|
|
610
|
+
</span>
|
|
611
|
+
<span style={{ fontSize: 11, color: '#666' }}>
|
|
612
|
+
{isLoaded ? 'Loaded' : 'Not loaded'}
|
|
613
|
+
</span>
|
|
614
|
+
</button>
|
|
615
|
+
);
|
|
616
|
+
})}
|
|
617
|
+
</div>
|
|
618
|
+
</div>
|
|
619
|
+
</Modal>
|
|
620
|
+
) : null}
|
|
621
|
+
</>
|
|
622
|
+
);
|
|
623
|
+
},
|
|
624
|
+
[
|
|
625
|
+
activeFontField,
|
|
626
|
+
addError,
|
|
627
|
+
handleAttributeChange,
|
|
628
|
+
isFontLoading,
|
|
629
|
+
loadedFonts,
|
|
630
|
+
markFontLoaded,
|
|
631
|
+
projectFonts,
|
|
632
|
+
fontLoadError,
|
|
633
|
+
],
|
|
634
|
+
);
|
|
635
|
+
|
|
404
636
|
const handleChildrenChange = useCallback(
|
|
405
637
|
(val: string) => {
|
|
406
638
|
const next: NodeData<NodeDefaultAttribute> = {
|
|
@@ -474,6 +706,8 @@ export function AttributesEditor({
|
|
|
474
706
|
) : null}
|
|
475
707
|
{type === 'iconType' ? (
|
|
476
708
|
renderIconTypeField(name, currentValue)
|
|
709
|
+
) : type === 'fontFamily' ? (
|
|
710
|
+
renderFontFamilyField(name, currentValue)
|
|
477
711
|
) : (
|
|
478
712
|
<Field
|
|
479
713
|
name={name}
|
|
@@ -682,6 +916,8 @@ export function AttributesEditor({
|
|
|
682
916
|
) : null}
|
|
683
917
|
{type === 'iconType' ? (
|
|
684
918
|
renderIconTypeField(name, getAttributeValue(name))
|
|
919
|
+
) : type === 'fontFamily' ? (
|
|
920
|
+
renderFontFamilyField(name, getAttributeValue(name))
|
|
685
921
|
) : (
|
|
686
922
|
<Field
|
|
687
923
|
name={name}
|
package/src/RenderPage.tsx
CHANGED
|
@@ -61,11 +61,14 @@ function isEmptyPreview(node: Node): boolean {
|
|
|
61
61
|
|
|
62
62
|
export function RenderPage({ data, name, onSelectNode }: RenderPageProps) {
|
|
63
63
|
useLogRender('RenderPage');
|
|
64
|
-
const { previewMode, forceRender, setCurrent } = useRenderStore(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
const { previewMode, forceRender, setCurrent, appFont } = useRenderStore(
|
|
65
|
+
(s) => ({
|
|
66
|
+
previewMode: s.previewMode,
|
|
67
|
+
forceRender: s.forceRender,
|
|
68
|
+
setCurrent: s.setCurrent,
|
|
69
|
+
appFont: s.appFont,
|
|
70
|
+
}),
|
|
71
|
+
);
|
|
69
72
|
const previewRootRef = useRef<HTMLDivElement | null>(null);
|
|
70
73
|
const showEmptyState = isEmptyPreview(data);
|
|
71
74
|
|
|
@@ -107,7 +110,13 @@ export function RenderPage({ data, name, onSelectNode }: RenderPageProps) {
|
|
|
107
110
|
|
|
108
111
|
return (
|
|
109
112
|
<DeviceMockFrame appName={name}>
|
|
110
|
-
<div
|
|
113
|
+
<div
|
|
114
|
+
className="screen-preview"
|
|
115
|
+
ref={previewRootRef}
|
|
116
|
+
style={{
|
|
117
|
+
fontFamily: appFont ? `"${appFont}"` : undefined,
|
|
118
|
+
}}
|
|
119
|
+
>
|
|
111
120
|
<RenderNode node={data} />
|
|
112
121
|
{showEmptyState && (
|
|
113
122
|
<div className="rb-empty-preview" aria-hidden="true">
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
getTypeSchema,
|
|
8
8
|
isPrimitiveType,
|
|
9
9
|
} from '../utils/patterns';
|
|
10
|
+
import { FONT_WEIGHT_OPTIONS, normalizeFontWeight } from '../utils/fontWeight';
|
|
10
11
|
import { Checkbox } from '../components/Checkbox';
|
|
11
12
|
import { LayoutPreviewPicker } from './LayoutPreviewPicker';
|
|
12
13
|
import { SizeField, type PreferredScale } from './SizeField';
|
|
@@ -439,6 +440,23 @@ export function Field({
|
|
|
439
440
|
fieldName={name}
|
|
440
441
|
/>
|
|
441
442
|
);
|
|
443
|
+
case 'fontWeight': {
|
|
444
|
+
const normalized = normalizeFontWeight(value);
|
|
445
|
+
return (
|
|
446
|
+
<select
|
|
447
|
+
value={normalized ?? ''}
|
|
448
|
+
onChange={(e) => onChange(e.target.value || undefined)}
|
|
449
|
+
className="input"
|
|
450
|
+
>
|
|
451
|
+
<option value="">(none)</option>
|
|
452
|
+
{FONT_WEIGHT_OPTIONS.map((opt) => (
|
|
453
|
+
<option key={opt} value={opt}>
|
|
454
|
+
{opt}
|
|
455
|
+
</option>
|
|
456
|
+
))}
|
|
457
|
+
</select>
|
|
458
|
+
);
|
|
459
|
+
}
|
|
442
460
|
case 'number':
|
|
443
461
|
return (
|
|
444
462
|
<input
|
|
@@ -2,18 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import type { NodeData } from '../../types/Node';
|
|
4
4
|
|
|
5
|
-
export type FontWeightOptionType =
|
|
6
|
-
| 'normal'
|
|
7
|
-
| 'bold'
|
|
8
|
-
| '100'
|
|
9
|
-
| '200'
|
|
10
|
-
| '300'
|
|
11
|
-
| '400'
|
|
12
|
-
| '500'
|
|
13
|
-
| '600'
|
|
14
|
-
| '700'
|
|
15
|
-
| '800'
|
|
16
|
-
| '900';
|
|
17
5
|
export type TextAlignOptionType = 'left' | 'center' | 'right' | 'justify';
|
|
18
6
|
export type FlexDirectionOptionType = 'row' | 'column';
|
|
19
7
|
export type AlignItemsOptionType =
|
|
@@ -37,7 +25,8 @@ export interface BIconPropsGenerated {
|
|
|
37
25
|
style?: Record<string, unknown>;
|
|
38
26
|
color?: string;
|
|
39
27
|
fontSize?: string;
|
|
40
|
-
|
|
28
|
+
fontFamily?: string;
|
|
29
|
+
fontWeight?: string;
|
|
41
30
|
textAlign?: TextAlignOptionType;
|
|
42
31
|
adjustsFontSizeToFit?: boolean;
|
|
43
32
|
showEllipsis?: boolean;
|
|
@@ -2,18 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import type { NodeData } from '../../types/Node';
|
|
4
4
|
|
|
5
|
-
export type FontWeightOptionType =
|
|
6
|
-
| 'normal'
|
|
7
|
-
| 'bold'
|
|
8
|
-
| '100'
|
|
9
|
-
| '200'
|
|
10
|
-
| '300'
|
|
11
|
-
| '400'
|
|
12
|
-
| '500'
|
|
13
|
-
| '600'
|
|
14
|
-
| '700'
|
|
15
|
-
| '800'
|
|
16
|
-
| '900';
|
|
17
5
|
export type TextAlignOptionType = 'left' | 'center' | 'right' | 'justify';
|
|
18
6
|
export type FlexDirectionOptionType = 'row' | 'column';
|
|
19
7
|
export type AlignItemsOptionType =
|
|
@@ -37,7 +25,8 @@ export interface OnboardFooterPropsGenerated {
|
|
|
37
25
|
style?: Record<string, unknown>;
|
|
38
26
|
color?: string;
|
|
39
27
|
fontSize?: string;
|
|
40
|
-
|
|
28
|
+
fontFamily?: string;
|
|
29
|
+
fontWeight?: string;
|
|
41
30
|
textAlign?: TextAlignOptionType;
|
|
42
31
|
adjustsFontSizeToFit?: boolean;
|
|
43
32
|
showEllipsis?: boolean;
|
|
@@ -2,18 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import type { NodeData } from '../../types/Node';
|
|
4
4
|
|
|
5
|
-
export type FontWeightOptionType =
|
|
6
|
-
| 'normal'
|
|
7
|
-
| 'bold'
|
|
8
|
-
| '100'
|
|
9
|
-
| '200'
|
|
10
|
-
| '300'
|
|
11
|
-
| '400'
|
|
12
|
-
| '500'
|
|
13
|
-
| '600'
|
|
14
|
-
| '700'
|
|
15
|
-
| '800'
|
|
16
|
-
| '900';
|
|
17
5
|
export type TextAlignOptionType = 'left' | 'center' | 'right' | 'justify';
|
|
18
6
|
export type FlexDirectionOptionType = 'row' | 'column';
|
|
19
7
|
export type AlignItemsOptionType =
|
|
@@ -37,7 +25,8 @@ export interface OnboardSubtitlePropsGenerated {
|
|
|
37
25
|
style?: Record<string, unknown>;
|
|
38
26
|
color?: string;
|
|
39
27
|
fontSize?: string;
|
|
40
|
-
|
|
28
|
+
fontFamily?: string;
|
|
29
|
+
fontWeight?: string;
|
|
41
30
|
textAlign?: TextAlignOptionType;
|
|
42
31
|
adjustsFontSizeToFit?: boolean;
|
|
43
32
|
showEllipsis?: boolean;
|
|
@@ -2,18 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import type { NodeData } from '../../types/Node';
|
|
4
4
|
|
|
5
|
-
export type FontWeightOptionType =
|
|
6
|
-
| 'normal'
|
|
7
|
-
| 'bold'
|
|
8
|
-
| '100'
|
|
9
|
-
| '200'
|
|
10
|
-
| '300'
|
|
11
|
-
| '400'
|
|
12
|
-
| '500'
|
|
13
|
-
| '600'
|
|
14
|
-
| '700'
|
|
15
|
-
| '800'
|
|
16
|
-
| '900';
|
|
17
5
|
export type TextAlignOptionType = 'left' | 'center' | 'right' | 'justify';
|
|
18
6
|
export type FlexDirectionOptionType = 'row' | 'column';
|
|
19
7
|
export type AlignItemsOptionType =
|
|
@@ -37,7 +25,8 @@ export interface OnboardTitlePropsGenerated {
|
|
|
37
25
|
style?: Record<string, unknown>;
|
|
38
26
|
color?: string;
|
|
39
27
|
fontSize?: string;
|
|
40
|
-
|
|
28
|
+
fontFamily?: string;
|
|
29
|
+
fontWeight?: string;
|
|
41
30
|
textAlign?: TextAlignOptionType;
|
|
42
31
|
adjustsFontSizeToFit?: boolean;
|
|
43
32
|
showEllipsis?: boolean;
|
|
@@ -2,18 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import type { NodeData } from '../../types/Node';
|
|
4
4
|
|
|
5
|
-
export type FontWeightOptionType =
|
|
6
|
-
| 'normal'
|
|
7
|
-
| 'bold'
|
|
8
|
-
| '100'
|
|
9
|
-
| '200'
|
|
10
|
-
| '300'
|
|
11
|
-
| '400'
|
|
12
|
-
| '500'
|
|
13
|
-
| '600'
|
|
14
|
-
| '700'
|
|
15
|
-
| '800'
|
|
16
|
-
| '900';
|
|
17
5
|
export type TextAlignOptionType = 'left' | 'center' | 'right' | 'justify';
|
|
18
6
|
export type FlexDirectionOptionType = 'row' | 'column';
|
|
19
7
|
export type AlignItemsOptionType =
|
|
@@ -40,7 +28,8 @@ export interface PaywallCloseButtonPropsGenerated {
|
|
|
40
28
|
strokeWidth?: number;
|
|
41
29
|
color?: string;
|
|
42
30
|
fontSize?: string;
|
|
43
|
-
|
|
31
|
+
fontFamily?: string;
|
|
32
|
+
fontWeight?: string;
|
|
44
33
|
textAlign?: TextAlignOptionType;
|
|
45
34
|
adjustsFontSizeToFit?: boolean;
|
|
46
35
|
showEllipsis?: boolean;
|
|
@@ -17,18 +17,6 @@ export type JustifyContentOptionType =
|
|
|
17
17
|
| 'space-around'
|
|
18
18
|
| 'space-evenly';
|
|
19
19
|
export type PositionOptionType = 'relative' | 'absolute';
|
|
20
|
-
export type FontWeightOptionType =
|
|
21
|
-
| 'normal'
|
|
22
|
-
| 'bold'
|
|
23
|
-
| '100'
|
|
24
|
-
| '200'
|
|
25
|
-
| '300'
|
|
26
|
-
| '400'
|
|
27
|
-
| '500'
|
|
28
|
-
| '600'
|
|
29
|
-
| '700'
|
|
30
|
-
| '800'
|
|
31
|
-
| '900';
|
|
32
20
|
export type TextAlignOptionType = 'left' | 'center' | 'right' | 'justify';
|
|
33
21
|
|
|
34
22
|
export interface TextPropsGenerated {
|
|
@@ -70,7 +58,8 @@ export interface TextPropsGenerated {
|
|
|
70
58
|
zIndex?: number;
|
|
71
59
|
color?: string;
|
|
72
60
|
fontSize?: string;
|
|
73
|
-
|
|
61
|
+
fontFamily?: string;
|
|
62
|
+
fontWeight?: string;
|
|
74
63
|
textAlign?: TextAlignOptionType;
|
|
75
64
|
adjustsFontSizeToFit?: boolean;
|
|
76
65
|
showEllipsis?: boolean;
|