Skip to content

Commit 46956cc

Browse files
committed
feat(typescript): update API
1 parent 4e23c55 commit 46956cc

File tree

11 files changed

+86
-24
lines changed

11 files changed

+86
-24
lines changed

src/SwiftUI.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,21 +34,25 @@ export type SwiftUIProps = {
3434
id?: string;
3535
onEvent?: (event: { nativeEvent: NativeSwiftUIEvent }) => void;
3636
style?: StyleProp<ViewStyle>;
37+
debug?: boolean;
3738
};
3839

3940
export const SwiftUIRoot = ({
4041
id: rootId,
4142
children,
4243
style,
4344
onEvent: rootOnEvent,
45+
debug = false,
4446
}: PropsWithChildren<SwiftUIProps>): ReactNode => {
4547
const { nativeRef, getEventHandler, nodesKey, getNodes, renderSequence } = useSwiftUIContext();
4648

4749
const log = useCallback(
4850
(message: string, ...args: unknown[]) => {
49-
console.log(`SwiftUIRoot(${rootId}) ${message}`, ...args);
51+
if (debug) {
52+
console.log(`SwiftUIRoot(${rootId}) ${message}`, ...args);
53+
}
5054
},
51-
[rootId],
55+
[rootId, debug],
5256
);
5357

5458
const nodes = getNodes();
@@ -78,13 +82,13 @@ export const SwiftUIRoot = ({
7882
);
7983
};
8084

81-
export const SwiftUI = ({ children, ...props }: PropsWithChildren<SwiftUIProps>): ReactElement => {
85+
export const SwiftUI = ({ children, debug, ...props }: PropsWithChildren<SwiftUIProps>): ReactElement => {
8286
// eslint-disable-next-line react-hooks/rules-of-hooks, @typescript-eslint/prefer-nullish-coalescing
8387
const id = props.id || `root:${useId()}`;
8488
return (
85-
<SwiftUIProvider id={id}>
89+
<SwiftUIProvider id={id} debug={debug}>
8690
<SwiftUIParentIdProvider id="__root">
87-
<SwiftUIRoot id={id} {...props}>
91+
<SwiftUIRoot id={id} debug={debug} {...props}>
8892
{children}
8993
</SwiftUIRoot>
9094
</SwiftUIParentIdProvider>

src/components/Button.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { type PropsWithChildren } from "react";
2+
import { type StyleProp } from "react-native";
23
import { SwiftUIParentIdProvider } from "../contexts";
3-
import { useSwiftUINode } from "../hooks";
4+
import { useNormalizedStyles, useSwiftUINode } from "../hooks";
45
import type { FunctionComponentWithId, NativeTextStyle } from "../types";
56

67
export type CustomNativeButtonStyle = "subtle" | "picker";
@@ -16,16 +17,26 @@ export type NativeButtonProps = {
1617
title?: string;
1718
disabled?: boolean;
1819
buttonStyle?: NativeButtonStyle;
19-
style?: NativeTextStyle;
20+
style?: StyleProp<NativeTextStyle>;
2021
onPress?: () => void;
2122
};
2223

2324
export const Button: FunctionComponentWithId<PropsWithChildren<NativeButtonProps>> = ({
25+
title,
2426
children,
2527
onPress,
28+
style,
2629
...otherProps
2730
}) => {
28-
const { id } = useSwiftUINode("Button", otherProps, { press: onPress });
31+
const normalizedTitle = title ? String(title) : undefined;
32+
const normalizedStyle = useNormalizedStyles<NativeTextStyle>(style);
33+
34+
const { id } = useSwiftUINode(
35+
"Button",
36+
{ title: normalizedTitle, style: normalizedStyle, ...otherProps },
37+
{ press: onPress },
38+
);
39+
2940
return <SwiftUIParentIdProvider id={id}>{children}</SwiftUIParentIdProvider>;
3041
};
3142
Button.displayName = "Button";

src/components/DatePicker.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@ export const DatePicker: FunctionComponentWithId<NativeDatePickerProps> = ({
2929
() =>
3030
onChangeProp
3131
? (date: string) => {
32-
onChangeProp(new Date(date)); // Convert string to Date
32+
let parsedDate = new Date(date);
33+
if (isNaN(parsedDate.getTime())) {
34+
parsedDate = new Date();
35+
}
36+
onChangeProp(parsedDate);
3337
}
3438
: undefined,
3539
[onChangeProp],

src/components/Form.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
import { type PropsWithChildren } from "react";
2+
import { StyleProp } from "react-native";
23
import { SwiftUIParentIdProvider } from "../contexts";
3-
import { useSwiftUINode } from "../hooks";
4+
import { useNormalizedStyles, useSwiftUINode } from "../hooks";
45
import type { FunctionComponentWithId, NativeViewStyle } from "../types";
56

67
// https://developer.apple.com/documentation/swiftui/form
78

89
export type NativeFormProps = {
9-
style?: NativeViewStyle;
10+
style?: StyleProp<NativeViewStyle>;
1011
};
1112

1213
export const Form: FunctionComponentWithId<PropsWithChildren<NativeFormProps>> = ({
1314
children,
15+
style,
1416
...otherProps
1517
}) => {
16-
const { id } = useSwiftUINode("Form", otherProps);
18+
const normalizedStyle = useNormalizedStyles<NativeViewStyle>(style);
19+
20+
const { id } = useSwiftUINode("Form", { style: normalizedStyle, ...otherProps });
1721

1822
return <SwiftUIParentIdProvider id={id}>{children}</SwiftUIParentIdProvider>;
1923
};

src/components/NumberField.tsx

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useMemo } from "react";
2-
import { useSwiftUINode } from "../hooks";
2+
import { StyleProp } from "react-native";
3+
import { useNormalizedStyles, useSwiftUINode } from "../hooks";
34
import type { FunctionComponentWithId, NativeTextStyle } from "../types";
45
import { NativeKeyboardType, NativeReturnKeyType } from "./TextField";
56

@@ -15,17 +16,17 @@ export type NumberFormatter =
1516
| "currencyAccounting";
1617

1718
export type NativeNumberFieldProps<T = number> = {
18-
value?: T;
19+
value?: T | null;
1920
label?: string;
2021
placeholder?: string;
2122
keyboardType?: NativeKeyboardType;
2223
returnKeyType?: NativeReturnKeyType;
2324
min?: number | null;
2425
max?: number | null;
2526
disabled?: boolean;
26-
style?: NativeTextStyle;
27+
style?: StyleProp<NativeTextStyle>;
2728
formatter?: NumberFormatter;
28-
onChange?: (value: T) => void;
29+
onChange?: (value: T | null) => void;
2930
onFocus?: () => void;
3031
onBlur?: () => void;
3132
};
@@ -34,19 +35,27 @@ export const NumberField: FunctionComponentWithId<NativeNumberFieldProps> = ({
3435
onChange: onChangeProp,
3536
onFocus,
3637
onBlur,
38+
style,
3739
...otherProps
3840
}) => {
3941
const onChange = useMemo(
4042
() =>
4143
onChangeProp
4244
? (date: string) => {
43-
onChangeProp(parseFloat(date)); // Convert string to number
45+
const parsedFloat = parseFloat(date);
46+
onChangeProp(!isNaN(parsedFloat) ? parsedFloat : null); // Convert string to number
4447
}
4548
: undefined,
4649
[onChangeProp],
4750
);
4851

49-
useSwiftUINode("NumberField", otherProps, { change: onChange, focus: onFocus, blur: onBlur });
52+
const normalizedStyle = useNormalizedStyles<NativeTextStyle>(style);
53+
54+
useSwiftUINode(
55+
"NumberField",
56+
{ style: normalizedStyle, ...otherProps },
57+
{ change: onChange, focus: onFocus, blur: onBlur },
58+
);
5059

5160
return null;
5261
};

src/components/Picker.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ReactNode, useMemo } from "react";
2-
import { StyleProp, StyleSheet } from "react-native";
3-
import { useSwiftUINode } from "../hooks";
2+
import { StyleProp } from "react-native";
3+
import { useNormalizedStyles, useSwiftUINode } from "../hooks";
44
import type { NativeTextStyle } from "../types";
55

66
// https://developer.apple.com/documentation/swiftui/picker
@@ -20,6 +20,7 @@ export type NativePickerProps<T extends string> = {
2020
};
2121

2222
export const Picker = <T extends string>({
23+
selection,
2324
options,
2425
onChange: onChangeProp,
2526
onFocus,
@@ -37,6 +38,7 @@ export const Picker = <T extends string>({
3738
[onChangeProp],
3839
);
3940

41+
const normalizedSelection = selection ? String(selection) : undefined;
4042
const normalizedOptions = useMemo(
4143
() =>
4244
options.length > 0 && typeof options[0] === "string"
@@ -45,11 +47,11 @@ export const Picker = <T extends string>({
4547
[options],
4648
);
4749

48-
const normalizedStyles = useMemo(() => (Array.isArray(style) ? StyleSheet.flatten(style) : style), [style]);
50+
const normalizedStyles = useNormalizedStyles<NativeTextStyle>(style);
4951

5052
useSwiftUINode(
5153
"Picker",
52-
{ options: normalizedOptions, style: normalizedStyles, ...otherProps },
54+
{ options: normalizedOptions, selection: normalizedSelection, style: normalizedStyles, ...otherProps },
5355
{
5456
change: onChange,
5557
focus: onFocus,

src/components/TextField.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ export type NativeKeyboardType = "default" | "numberPad" | "emailAddress" | "dec
55
export type NativeTextContentType = "username" | "password" | "emailAddress";
66
export type NativeReturnKeyType = "default" | "done" | "next" | "search";
77
export type NativeAutocapitalizationType = "none" | "words" | "sentences" | "allCharacters";
8+
export type NativeSubmitLabel =
9+
| "continue"
10+
| "done"
11+
| "go"
12+
| "join"
13+
| "next"
14+
| "return"
15+
| "route"
16+
| "search"
17+
| "send";
818

919
export type NativeTextFieldProps<T = string> = {
1020
text?: T;
@@ -15,6 +25,7 @@ export type NativeTextFieldProps<T = string> = {
1525
returnKeyType?: NativeReturnKeyType;
1626
secure?: boolean;
1727
autocapitalizationType?: NativeAutocapitalizationType;
28+
submitLabel?: NativeSubmitLabel;
1829
maxLength?: number | null;
1930
multiline?: boolean;
2031
disabled?: boolean;

src/contexts/SwiftUIContext.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,12 @@ const SwiftUIContext = createContext<SwiftUIContextValue | undefined>(undefined)
3838

3939
export type SwiftUIProviderProps = {
4040
id: string;
41+
debug?: boolean;
4142
};
4243

4344
export const SwiftUIProvider: FunctionComponent<PropsWithChildren<SwiftUIProviderProps>> = ({
4445
id: rootId,
46+
debug,
4547
children,
4648
}) => {
4749
const eventRegistry = useRef<EventRegistry>(new Map());
@@ -52,9 +54,11 @@ export const SwiftUIProvider: FunctionComponent<PropsWithChildren<SwiftUIProvide
5254

5355
const log = useCallback(
5456
(message: string, ...args: unknown[]) => {
55-
console.log(`SwiftUIContext(${rootId}) ${message}`, ...args);
57+
if (debug) {
58+
console.log(`SwiftUIContext(${rootId}) ${message}`, ...args);
59+
}
5660
},
57-
[rootId],
61+
[debug, rootId],
5862
);
5963

6064
const nodesKey = useMemo(() => {

src/hooks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export * from "./useDebounce";
22
export * from "./useDebugEffect";
33
export * from "./useJsonMemo";
4+
export * from "./useNormalizedStyles";
45
export * from "./useSwiftUINode";
56
export * from "./useThrottle";

src/hooks/useNormalizedStyles.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { useMemo } from "react";
2+
import { StyleProp, StyleSheet } from "react-native";
3+
import { NativeTextStyle, NativeViewStyle } from "src/types";
4+
5+
export const useNormalizedStyles = <T extends NativeTextStyle | NativeViewStyle>(
6+
style?: StyleProp<T>,
7+
): T | undefined => {
8+
return useMemo(() => (Array.isArray(style) ? StyleSheet.flatten<T>(style) : style), [style]) as
9+
| T
10+
| undefined;
11+
};

0 commit comments

Comments
 (0)