Skip to content

Commit 669d954

Browse files
feat(iOS): add fontSize and fontFamily to customStyle
1 parent 61965a2 commit 669d954

16 files changed

Lines changed: 498 additions & 11 deletions
2.29 KB
Loading
2.38 KB
Loading
2.38 KB
Loading
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { type FC } from 'react';
2+
import { Pressable, ScrollView, StyleSheet, Text } from 'react-native';
3+
4+
export interface FontFamilyOption {
5+
label: string;
6+
value: string;
7+
}
8+
9+
interface Props {
10+
families: FontFamilyOption[];
11+
activeFamily: string;
12+
onSelectFamily: (family: string) => void;
13+
onClear: () => void;
14+
}
15+
16+
export const FontFamilyPickerRow: FC<Props> = ({
17+
families,
18+
activeFamily,
19+
onSelectFamily,
20+
onClear,
21+
}) => {
22+
return (
23+
<ScrollView
24+
horizontal
25+
showsHorizontalScrollIndicator={false}
26+
style={styles.container}
27+
contentContainerStyle={styles.content}
28+
>
29+
<Pressable
30+
testID="font-family-clear"
31+
style={styles.clearButton}
32+
onPress={onClear}
33+
>
34+
<Text style={styles.clearText}></Text>
35+
</Pressable>
36+
{families.map(({ label, value }) => {
37+
const isActive = value === activeFamily;
38+
return (
39+
<Pressable
40+
key={value}
41+
testID={`font-family-${value}`}
42+
onPress={() => onSelectFamily(value)}
43+
style={[styles.familyButton, isActive && styles.familyButtonActive]}
44+
>
45+
<Text style={[styles.familyText, { fontFamily: value }]}>
46+
{label}
47+
</Text>
48+
</Pressable>
49+
);
50+
})}
51+
</ScrollView>
52+
);
53+
};
54+
55+
const styles = StyleSheet.create({
56+
container: {
57+
width: '100%',
58+
backgroundColor: 'rgba(0, 26, 114, 0.9)',
59+
},
60+
content: {
61+
flexDirection: 'row',
62+
alignItems: 'center',
63+
paddingHorizontal: 8,
64+
paddingVertical: 8,
65+
gap: 8,
66+
},
67+
clearButton: {
68+
width: 28,
69+
height: 28,
70+
borderRadius: 14,
71+
backgroundColor: 'rgba(255,255,255,0.15)',
72+
justifyContent: 'center',
73+
alignItems: 'center',
74+
},
75+
clearText: {
76+
color: 'white',
77+
fontSize: 14,
78+
lineHeight: 16,
79+
},
80+
familyButton: {
81+
minWidth: 36,
82+
height: 28,
83+
paddingHorizontal: 8,
84+
borderRadius: 14,
85+
backgroundColor: 'rgba(255,255,255,0.15)',
86+
justifyContent: 'center',
87+
alignItems: 'center',
88+
},
89+
familyButtonActive: {
90+
backgroundColor: 'rgb(0, 26, 114)',
91+
borderWidth: 2,
92+
borderColor: 'white',
93+
},
94+
familyText: {
95+
color: 'white',
96+
fontSize: 14,
97+
fontWeight: '600',
98+
lineHeight: 16,
99+
},
100+
});
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { type FC } from 'react';
2+
import { Pressable, ScrollView, StyleSheet, Text } from 'react-native';
3+
4+
interface Props {
5+
sizes: number[];
6+
activeSize: number;
7+
onSelectSize: (size: number) => void;
8+
onClear: () => void;
9+
}
10+
11+
export const FontSizePickerRow: FC<Props> = ({
12+
sizes,
13+
activeSize,
14+
onSelectSize,
15+
onClear,
16+
}) => {
17+
return (
18+
<ScrollView
19+
horizontal
20+
showsHorizontalScrollIndicator={false}
21+
style={styles.container}
22+
contentContainerStyle={styles.content}
23+
>
24+
<Pressable
25+
testID="font-size-clear"
26+
style={styles.clearButton}
27+
onPress={onClear}
28+
>
29+
<Text style={styles.clearText}></Text>
30+
</Pressable>
31+
{sizes.map((size) => {
32+
const isActive = size === activeSize;
33+
return (
34+
<Pressable
35+
key={size}
36+
testID={`font-size-${size}`}
37+
onPress={() => onSelectSize(size)}
38+
style={[styles.sizeButton, isActive && styles.sizeButtonActive]}
39+
>
40+
<Text style={styles.sizeText}>{size}</Text>
41+
</Pressable>
42+
);
43+
})}
44+
</ScrollView>
45+
);
46+
};
47+
48+
const styles = StyleSheet.create({
49+
container: {
50+
width: '100%',
51+
backgroundColor: 'rgba(0, 26, 114, 0.9)',
52+
},
53+
content: {
54+
flexDirection: 'row',
55+
alignItems: 'center',
56+
paddingHorizontal: 8,
57+
paddingVertical: 8,
58+
gap: 8,
59+
},
60+
clearButton: {
61+
width: 28,
62+
height: 28,
63+
borderRadius: 14,
64+
backgroundColor: 'rgba(255,255,255,0.15)',
65+
justifyContent: 'center',
66+
alignItems: 'center',
67+
},
68+
clearText: {
69+
color: 'white',
70+
fontSize: 14,
71+
lineHeight: 16,
72+
},
73+
sizeButton: {
74+
minWidth: 36,
75+
height: 28,
76+
paddingHorizontal: 8,
77+
borderRadius: 14,
78+
backgroundColor: 'rgba(255,255,255,0.15)',
79+
justifyContent: 'center',
80+
alignItems: 'center',
81+
},
82+
sizeButtonActive: {
83+
backgroundColor: 'rgb(0, 26, 114)',
84+
borderWidth: 2,
85+
borderColor: 'white',
86+
},
87+
sizeText: {
88+
color: 'white',
89+
fontSize: 14,
90+
fontWeight: '600',
91+
lineHeight: 16,
92+
},
93+
});

apps/example/src/components/Toolbar.tsx

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import {
99
} from 'react-native';
1010
import { ToolbarButton } from './ToolbarButton';
1111
import { ColorPickerRow } from './ColorPickerRow';
12+
import { FontSizePickerRow } from './FontSizePickerRow';
13+
import { FontFamilyPickerRow } from './FontFamilyPickerRow';
1214
import type {
1315
OnChangeStateEvent,
1416
EnrichedTextInputInstance,
@@ -36,6 +38,17 @@ const COLORS = [
3638
'#ADD8E6',
3739
];
3840

41+
const FONT_SIZES = [12, 16, 20, 24, 28, 32, 36, 40];
42+
43+
const FONT_FAMILIES = [
44+
{ label: 'Regular', value: 'Nunito-Regular' },
45+
{ label: 'Bold', value: 'Nunito-Bold' },
46+
{ label: 'Italic', value: 'Nunito-Italic' },
47+
{ label: 'Code', value: 'CascadiaCode-Regular' },
48+
{ label: 'Code Bold', value: 'CascadiaCode-Bold' },
49+
{ label: 'Courier', value: 'Courier' },
50+
] as const;
51+
3952
const STYLE_ITEMS = [
4053
{
4154
name: 'bold',
@@ -133,11 +146,24 @@ const STYLE_ITEMS = [
133146
name: 'bg-color',
134147
text: 'BG',
135148
},
149+
{
150+
name: 'font-size',
151+
text: 'Aa',
152+
},
153+
{
154+
name: 'font-family',
155+
text: 'Ff',
156+
},
136157
] as const;
137158

138159
type Item = (typeof STYLE_ITEMS)[number];
139160
type StylesState = OnChangeStateEvent;
140-
type OpenPicker = 'text-color' | 'bg-color' | null;
161+
type OpenPicker =
162+
| 'text-color'
163+
| 'bg-color'
164+
| 'font-size'
165+
| 'font-family'
166+
| null;
141167

142168
export interface ToolbarProps {
143169
stylesState: StylesState;
@@ -158,6 +184,11 @@ export const Toolbar: FC<ToolbarProps> = ({
158184

159185
const activeFgColor = stylesState.customStyle?.foregroundColor ?? '';
160186
const activeBgColor = stylesState.customStyle?.backgroundColor ?? '';
187+
const activeFontSize = stylesState.customStyle?.fontSize ?? 0;
188+
const activeFontFamily = stylesState.customStyle?.fontFamily ?? '';
189+
const activeFontFamilyLabel =
190+
FONT_FAMILIES.find((family) => family.value === activeFontFamily)?.label ??
191+
'';
161192

162193
const fgIndicatorColor =
163194
activeFgColor.length > 0 ? activeFgColor : 'transparent';
@@ -395,6 +426,48 @@ export const Toolbar: FC<ToolbarProps> = ({
395426
);
396427
}
397428

429+
if (item.name === 'font-size') {
430+
const fontSizeLabel = activeFontSize > 0 ? String(activeFontSize) : 'Aa';
431+
return (
432+
<Pressable
433+
testID="toolbar-font-size"
434+
onPress={() =>
435+
setOpenPicker((prev) => (prev === 'font-size' ? null : 'font-size'))
436+
}
437+
style={[
438+
styles.colorButton,
439+
layout === 'grid' ? styles.gridItem : undefined,
440+
(openPicker === 'font-size' || activeFontSize > 0) &&
441+
styles.colorButtonActive,
442+
]}
443+
>
444+
<Text style={styles.colorButtonLabel}>{fontSizeLabel}</Text>
445+
</Pressable>
446+
);
447+
}
448+
449+
if (item.name === 'font-family') {
450+
const fontFamilyLabel = activeFontFamilyLabel || 'Ff';
451+
return (
452+
<Pressable
453+
testID="toolbar-font-family"
454+
onPress={() =>
455+
setOpenPicker((prev) =>
456+
prev === 'font-family' ? null : 'font-family'
457+
)
458+
}
459+
style={[
460+
styles.colorButton,
461+
layout === 'grid' ? styles.gridItem : undefined,
462+
(openPicker === 'font-family' || activeFontFamily.length > 0) &&
463+
styles.colorButtonActive,
464+
]}
465+
>
466+
<Text style={styles.colorButtonLabel}>{fontFamilyLabel}</Text>
467+
</Pressable>
468+
);
469+
}
470+
398471
return (
399472
<ToolbarButton
400473
{...item}
@@ -429,6 +502,26 @@ export const Toolbar: FC<ToolbarProps> = ({
429502
setOpenPicker(null);
430503
};
431504

505+
const handleSelectFontSize = (size: number) => {
506+
editorRef?.current?.setStyle({ fontSize: size });
507+
setOpenPicker(null);
508+
};
509+
510+
const handleClearFontSize = () => {
511+
editorRef?.current?.setStyle({ fontSize: null });
512+
setOpenPicker(null);
513+
};
514+
515+
const handleSelectFontFamily = (family: string) => {
516+
editorRef?.current?.setStyle({ fontFamily: family });
517+
setOpenPicker(null);
518+
};
519+
520+
const handleClearFontFamily = () => {
521+
editorRef?.current?.setStyle({ fontFamily: null });
522+
setOpenPicker(null);
523+
};
524+
432525
return (
433526
<View style={styles.wrapper}>
434527
<FlatList
@@ -458,6 +551,22 @@ export const Toolbar: FC<ToolbarProps> = ({
458551
onClear={handleClearBgColor}
459552
/>
460553
)}
554+
{openPicker === 'font-size' && (
555+
<FontSizePickerRow
556+
sizes={FONT_SIZES}
557+
activeSize={activeFontSize}
558+
onSelectSize={handleSelectFontSize}
559+
onClear={handleClearFontSize}
560+
/>
561+
)}
562+
{openPicker === 'font-family' && (
563+
<FontFamilyPickerRow
564+
families={[...FONT_FAMILIES]}
565+
activeFamily={activeFontFamily}
566+
onSelectFamily={handleSelectFontFamily}
567+
onClear={handleClearFontFamily}
568+
/>
569+
)}
461570
</View>
462571
);
463572
};

apps/example/src/constants/editorConfig.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ export const DEFAULT_STYLES: StylesState = {
3636
customStyle: {
3737
foregroundColor: '',
3838
backgroundColor: '',
39+
fontSize: 0,
40+
fontFamily: '',
3941
},
4042
};
4143

0 commit comments

Comments
 (0)