@korsolutions/ui 0.0.41 → 0.0.43
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/module/components/button/variants/default.js +0 -1
- package/dist/module/components/button/variants/default.js.map +1 -1
- package/dist/module/components/button/variants/secondary.js +2 -3
- package/dist/module/components/button/variants/secondary.js.map +1 -1
- package/dist/module/components/toast/toast-manager.js +7 -4
- package/dist/module/components/toast/toast-manager.js.map +1 -1
- package/dist/module/hooks/use-relative-position.js.map +1 -1
- package/dist/module/index.js +14 -5
- package/dist/module/index.js.map +1 -1
- package/dist/module/primitives/autocomplete/autocomplete-content.js +9 -5
- package/dist/module/primitives/autocomplete/autocomplete-content.js.map +1 -1
- package/dist/module/primitives/autocomplete/autocomplete-input.js +1 -7
- package/dist/module/primitives/autocomplete/autocomplete-input.js.map +1 -1
- package/dist/module/primitives/autocomplete/autocomplete-option.js +15 -7
- package/dist/module/primitives/autocomplete/autocomplete-option.js.map +1 -1
- package/dist/module/primitives/autocomplete/autocomplete-overlay.js +10 -6
- package/dist/module/primitives/autocomplete/autocomplete-overlay.js.map +1 -1
- package/dist/module/primitives/autocomplete/autocomplete-portal.js +2 -10
- package/dist/module/primitives/autocomplete/autocomplete-portal.js.map +1 -1
- package/dist/module/primitives/autocomplete/autocomplete-root.js +10 -10
- package/dist/module/primitives/autocomplete/autocomplete-root.js.map +1 -1
- package/dist/module/primitives/autocomplete/context.js.map +1 -1
- package/dist/module/primitives/avatar/avatar-fallback.js +2 -3
- package/dist/module/primitives/avatar/avatar-fallback.js.map +1 -1
- package/dist/module/primitives/calendar/calendar-day.js +0 -1
- package/dist/module/primitives/calendar/calendar-day.js.map +1 -1
- package/dist/module/primitives/popover/popover-content.js +4 -1
- package/dist/module/primitives/popover/popover-content.js.map +1 -1
- package/dist/module/safe-area/context.js +13 -0
- package/dist/module/safe-area/context.js.map +1 -0
- package/dist/module/safe-area/index.js +5 -0
- package/dist/module/safe-area/index.js.map +1 -0
- package/dist/module/safe-area/provider.js +26 -0
- package/dist/module/safe-area/provider.js.map +1 -0
- package/dist/module/safe-area/types.js +2 -0
- package/dist/module/safe-area/types.js.map +1 -0
- package/dist/module/themes/adapters/react-navigation.js +3 -2
- package/dist/module/themes/adapters/react-navigation.js.map +1 -1
- package/dist/module/themes/default/colors.js +28 -28
- package/dist/module/themes/default/colors.js.map +1 -1
- package/dist/module/themes/provider.js +13 -11
- package/dist/module/themes/provider.js.map +1 -1
- package/dist/module/themes/utils.js +21 -0
- package/dist/module/themes/utils.js.map +1 -0
- package/dist/typescript/src/components/button/variants/default.d.ts.map +1 -1
- package/dist/typescript/src/components/button/variants/secondary.d.ts.map +1 -1
- package/dist/typescript/src/components/toast/toast-manager.d.ts.map +1 -1
- package/dist/typescript/src/hooks/use-relative-position.d.ts +2 -2
- package/dist/typescript/src/hooks/use-relative-position.d.ts.map +1 -1
- package/dist/typescript/src/index.d.ts +7 -1
- package/dist/typescript/src/index.d.ts.map +1 -1
- package/dist/typescript/src/primitives/autocomplete/autocomplete-content.d.ts.map +1 -1
- package/dist/typescript/src/primitives/autocomplete/autocomplete-input.d.ts.map +1 -1
- package/dist/typescript/src/primitives/autocomplete/autocomplete-option.d.ts.map +1 -1
- package/dist/typescript/src/primitives/autocomplete/autocomplete-overlay.d.ts +0 -1
- package/dist/typescript/src/primitives/autocomplete/autocomplete-overlay.d.ts.map +1 -1
- package/dist/typescript/src/primitives/autocomplete/autocomplete-portal.d.ts +1 -1
- package/dist/typescript/src/primitives/autocomplete/autocomplete-portal.d.ts.map +1 -1
- package/dist/typescript/src/primitives/autocomplete/autocomplete-root.d.ts +0 -5
- package/dist/typescript/src/primitives/autocomplete/autocomplete-root.d.ts.map +1 -1
- package/dist/typescript/src/primitives/autocomplete/context.d.ts +5 -6
- package/dist/typescript/src/primitives/autocomplete/context.d.ts.map +1 -1
- package/dist/typescript/src/primitives/avatar/avatar-fallback.d.ts +0 -1
- package/dist/typescript/src/primitives/avatar/avatar-fallback.d.ts.map +1 -1
- package/dist/typescript/src/primitives/calendar/calendar-day.d.ts.map +1 -1
- package/dist/typescript/src/primitives/popover/popover-content.d.ts.map +1 -1
- package/dist/typescript/src/safe-area/context.d.ts +5 -0
- package/dist/typescript/src/safe-area/context.d.ts.map +1 -0
- package/dist/typescript/src/safe-area/index.d.ts +4 -0
- package/dist/typescript/src/safe-area/index.d.ts.map +1 -0
- package/dist/typescript/src/safe-area/provider.d.ts +7 -0
- package/dist/typescript/src/safe-area/provider.d.ts.map +1 -0
- package/dist/typescript/src/safe-area/types.d.ts +10 -0
- package/dist/typescript/src/safe-area/types.d.ts.map +1 -0
- package/dist/typescript/src/themes/adapters/react-navigation.d.ts.map +1 -1
- package/dist/typescript/src/themes/provider.d.ts +5 -4
- package/dist/typescript/src/themes/provider.d.ts.map +1 -1
- package/dist/typescript/src/themes/types.d.ts +0 -1
- package/dist/typescript/src/themes/types.d.ts.map +1 -1
- package/dist/typescript/src/themes/utils.d.ts +3 -0
- package/dist/typescript/src/themes/utils.d.ts.map +1 -0
- package/nodemon.json +14 -0
- package/package.json +3 -1
- package/src/components/button/variants/default.tsx +1 -2
- package/src/components/button/variants/secondary.tsx +3 -4
- package/src/components/toast/toast-manager.tsx +5 -4
- package/src/hooks/use-relative-position.ts +4 -4
- package/src/index.tsx +16 -9
- package/src/primitives/autocomplete/autocomplete-content.tsx +12 -3
- package/src/primitives/autocomplete/autocomplete-input.tsx +1 -7
- package/src/primitives/autocomplete/autocomplete-option.tsx +16 -8
- package/src/primitives/autocomplete/autocomplete-overlay.tsx +16 -8
- package/src/primitives/autocomplete/autocomplete-portal.tsx +2 -12
- package/src/primitives/autocomplete/autocomplete-root.tsx +10 -16
- package/src/primitives/autocomplete/context.ts +4 -5
- package/src/primitives/avatar/avatar-fallback.tsx +2 -4
- package/src/primitives/calendar/calendar-day.tsx +1 -2
- package/src/primitives/popover/popover-content.tsx +3 -0
- package/src/safe-area/context.ts +14 -0
- package/src/safe-area/index.ts +3 -0
- package/src/safe-area/provider.tsx +28 -0
- package/src/safe-area/types.ts +10 -0
- package/src/themes/adapters/react-navigation.ts +4 -3
- package/src/themes/default/colors.ts +28 -28
- package/src/themes/provider.tsx +17 -15
- package/src/themes/types.ts +0 -1
- package/src/themes/utils.ts +14 -0
- package/dist/module/themes/themes.js +0 -7
- package/dist/module/themes/themes.js.map +0 -1
- package/dist/typescript/src/themes/themes.d.ts +0 -3
- package/dist/typescript/src/themes/themes.d.ts.map +0 -1
- package/src/themes/themes.ts +0 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@korsolutions/ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.43",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
|
+
"dev": "nodemon",
|
|
39
40
|
"prepare": "./scripts/build.sh",
|
|
40
41
|
"ts-check": "tsc --noEmit"
|
|
41
42
|
},
|
|
@@ -44,6 +45,7 @@
|
|
|
44
45
|
"@types/react": "^19.2.3",
|
|
45
46
|
"@types/react-dom": "^19.2.3",
|
|
46
47
|
"babel-plugin-module-resolver": "^5.0.2",
|
|
48
|
+
"nodemon": "^3.1.11",
|
|
47
49
|
"react-native-builder-bob": "^0.40.17",
|
|
48
50
|
"tsc-alias": "^1.8.16",
|
|
49
51
|
"typescript": "^5.9.3"
|
|
@@ -32,7 +32,6 @@ export const useButtonVariantDefault = (): ButtonStyles => {
|
|
|
32
32
|
default: {
|
|
33
33
|
color: colors.primaryForeground,
|
|
34
34
|
fontSize,
|
|
35
|
-
fontWeight: "bold",
|
|
36
35
|
fontFamily,
|
|
37
36
|
},
|
|
38
37
|
disabled: {
|
|
@@ -53,6 +52,6 @@ export const useButtonVariantDefault = (): ButtonStyles => {
|
|
|
53
52
|
color: colors.mutedForeground,
|
|
54
53
|
},
|
|
55
54
|
},
|
|
56
|
-
})
|
|
55
|
+
}),
|
|
57
56
|
);
|
|
58
57
|
};
|
|
@@ -30,9 +30,8 @@ export const useButtonVariantSecondary = (): ButtonStyles => {
|
|
|
30
30
|
},
|
|
31
31
|
label: {
|
|
32
32
|
default: {
|
|
33
|
-
color: colors.
|
|
33
|
+
color: colors.secondaryForeground,
|
|
34
34
|
fontSize,
|
|
35
|
-
fontWeight: "bold",
|
|
36
35
|
fontFamily,
|
|
37
36
|
},
|
|
38
37
|
disabled: {
|
|
@@ -44,7 +43,7 @@ export const useButtonVariantSecondary = (): ButtonStyles => {
|
|
|
44
43
|
},
|
|
45
44
|
spinner: {
|
|
46
45
|
default: {
|
|
47
|
-
color: colors.
|
|
46
|
+
color: colors.secondaryForeground,
|
|
48
47
|
},
|
|
49
48
|
disabled: {
|
|
50
49
|
color: colors.mutedForeground,
|
|
@@ -53,6 +52,6 @@ export const useButtonVariantSecondary = (): ButtonStyles => {
|
|
|
53
52
|
color: colors.mutedForeground,
|
|
54
53
|
},
|
|
55
54
|
},
|
|
56
|
-
})
|
|
55
|
+
}),
|
|
57
56
|
);
|
|
58
57
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { Portal } from "@/primitives/portal";
|
|
2
|
+
import { useSafeAreaInsets } from "@/safe-area";
|
|
1
3
|
import React, { useSyncExternalStore } from "react";
|
|
2
|
-
import {
|
|
4
|
+
import { StyleSheet, View } from "react-native";
|
|
3
5
|
import { ToastComponent } from "./toast";
|
|
4
6
|
import { ToastVariants } from "./variants";
|
|
5
|
-
import { Portal } from "@/primitives/portal";
|
|
6
7
|
|
|
7
8
|
export const TOAST_PORTAL_NAME = "toast-portal";
|
|
8
9
|
|
|
@@ -76,12 +77,13 @@ export const ToastAPI = {
|
|
|
76
77
|
|
|
77
78
|
export function ToastContainer() {
|
|
78
79
|
const toasts = useSyncExternalStore(subscribe, getSnapshot);
|
|
80
|
+
const insets = useSafeAreaInsets();
|
|
79
81
|
|
|
80
82
|
if (!toasts.length) return null;
|
|
81
83
|
|
|
82
84
|
return (
|
|
83
85
|
<Portal name={TOAST_PORTAL_NAME}>
|
|
84
|
-
<View style={s.wrapper}>
|
|
86
|
+
<View style={[s.wrapper, { top: insets.top + 24 }]}>
|
|
85
87
|
{toasts.map((toast) => (
|
|
86
88
|
<ToastComponent key={toast.id} title={toast.title} description={toast.description} variant={toast.variant} />
|
|
87
89
|
))}
|
|
@@ -95,7 +97,6 @@ const s = StyleSheet.create({
|
|
|
95
97
|
width: "100%",
|
|
96
98
|
alignItems: "center",
|
|
97
99
|
position: "absolute",
|
|
98
|
-
top: 24,
|
|
99
100
|
gap: 8,
|
|
100
101
|
},
|
|
101
102
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { useWindowDimensions, type
|
|
2
|
+
import { useWindowDimensions, type DisplayMetrics, type LayoutRectangle } from "react-native";
|
|
3
3
|
|
|
4
4
|
interface Insets {
|
|
5
5
|
top?: number;
|
|
@@ -56,7 +56,7 @@ export interface LayoutPosition {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
interface GetPositionArgs {
|
|
59
|
-
dimensions:
|
|
59
|
+
dimensions: DisplayMetrics;
|
|
60
60
|
avoidCollisions: boolean;
|
|
61
61
|
triggerPosition: LayoutPosition;
|
|
62
62
|
contentLayout: LayoutRectangle;
|
|
@@ -136,7 +136,7 @@ function getLeftPosition(
|
|
|
136
136
|
alignOffset: number,
|
|
137
137
|
insetLeft: number,
|
|
138
138
|
insetRight: number,
|
|
139
|
-
dimensions:
|
|
139
|
+
dimensions: DisplayMetrics,
|
|
140
140
|
) {
|
|
141
141
|
let left = 0;
|
|
142
142
|
if (align === "start") {
|
|
@@ -183,6 +183,6 @@ function getContentStyle({
|
|
|
183
183
|
alignOffset,
|
|
184
184
|
insets,
|
|
185
185
|
dimensions,
|
|
186
|
-
})
|
|
186
|
+
}),
|
|
187
187
|
);
|
|
188
188
|
}
|
package/src/index.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SafeAreaProvider, type SafeAreaInsets } from "@/safe-area";
|
|
2
|
+
import { ThemeProvider, type ThemeProviderProps } from "@/themes";
|
|
2
3
|
import { AsyncAlertDialogManager } from "./components/alert-dialog/async-alert-dialog";
|
|
3
4
|
import { ToastContainer } from "./components/toast/toast-manager";
|
|
4
5
|
import { PortalHost } from "./primitives/portal";
|
|
@@ -6,21 +7,27 @@ import { type PortalHostProps } from "./primitives/portal/portal.constants";
|
|
|
6
7
|
|
|
7
8
|
export interface ProviderProps {
|
|
8
9
|
children: React.ReactNode;
|
|
9
|
-
|
|
10
10
|
portalContainer?: PortalHostProps["container"];
|
|
11
|
+
theme?: ThemeProviderProps["theme"];
|
|
12
|
+
safeAreaInsets?: SafeAreaInsets;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
|
-
export const UniversalUIProvider = ({ children, portalContainer }: ProviderProps) => {
|
|
15
|
+
export const UniversalUIProvider = ({ children, portalContainer, theme, safeAreaInsets }: ProviderProps) => {
|
|
14
16
|
return (
|
|
15
|
-
<
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
<SafeAreaProvider insets={safeAreaInsets}>
|
|
18
|
+
<ThemeProvider theme={theme}>
|
|
19
|
+
<ToastContainer />
|
|
20
|
+
{children}
|
|
21
|
+
<PortalHost container={portalContainer} />
|
|
22
|
+
<AsyncAlertDialogManager />
|
|
23
|
+
</ThemeProvider>
|
|
24
|
+
</SafeAreaProvider>
|
|
21
25
|
);
|
|
22
26
|
};
|
|
23
27
|
|
|
24
28
|
export * from "./themes/adapters";
|
|
29
|
+
export * from "./themes/default";
|
|
25
30
|
export { useTheme } from "./themes/provider";
|
|
26
31
|
export * from "./themes/types";
|
|
32
|
+
|
|
33
|
+
export * from "./safe-area";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useRelativePosition } from "@/hooks/use-relative-position";
|
|
2
|
+
import { useSafeAreaInsets } from "@/safe-area";
|
|
2
3
|
import { calculateComposedStyles } from "@/utils/calculate-styles";
|
|
3
4
|
import React from "react";
|
|
4
5
|
import { ScrollView, type StyleProp, type ViewStyle } from "react-native";
|
|
@@ -12,6 +13,7 @@ export interface AutocompleteContentProps {
|
|
|
12
13
|
export function AutocompleteContent(props: AutocompleteContentProps) {
|
|
13
14
|
const autocomplete = useAutocomplete();
|
|
14
15
|
const composedStyles = calculateComposedStyles(autocomplete.styles, autocomplete.state, "content", props.style);
|
|
16
|
+
const safeAreaInsets = useSafeAreaInsets();
|
|
15
17
|
|
|
16
18
|
const positionStyle = useRelativePosition({
|
|
17
19
|
align: "start",
|
|
@@ -21,16 +23,23 @@ export function AutocompleteContent(props: AutocompleteContentProps) {
|
|
|
21
23
|
alignOffset: 0,
|
|
22
24
|
side: "bottom",
|
|
23
25
|
sideOffset: 0,
|
|
26
|
+
insets: safeAreaInsets,
|
|
24
27
|
});
|
|
25
|
-
|
|
26
28
|
return (
|
|
27
29
|
<ScrollView
|
|
28
|
-
style={[positionStyle, composedStyles, { width: autocomplete.inputPosition.width }]}
|
|
29
30
|
onLayout={(e) => {
|
|
30
31
|
autocomplete.setContentLayout(e.nativeEvent.layout);
|
|
31
32
|
}}
|
|
32
|
-
pointerEvents="box-none"
|
|
33
33
|
keyboardShouldPersistTaps="handled"
|
|
34
|
+
style={[
|
|
35
|
+
positionStyle,
|
|
36
|
+
composedStyles,
|
|
37
|
+
{
|
|
38
|
+
display: autocomplete.isOpen ? "flex" : "none",
|
|
39
|
+
width: autocomplete.inputPosition.width,
|
|
40
|
+
pointerEvents: "box-none",
|
|
41
|
+
},
|
|
42
|
+
]}
|
|
34
43
|
>
|
|
35
44
|
{props.children}
|
|
36
45
|
</ScrollView>
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { TextInputRef } from "@/types/element.types";
|
|
2
|
-
import { setInnerInputValue } from "@/utils/input-utils";
|
|
3
2
|
import { measureLayoutPosition } from "@/utils/normalize-layout";
|
|
4
3
|
import { forwardRef, useEffect, useImperativeHandle, useRef } from "react";
|
|
5
4
|
import { StyleSheet, TextInput, type StyleProp, type TextInputProps, type TextStyle } from "react-native";
|
|
@@ -61,12 +60,7 @@ export const AutocompleteInput = forwardRef<TextInputRef, AutocompleteInputProps
|
|
|
61
60
|
|
|
62
61
|
useImperativeHandle(ref, () => inputRef.current!);
|
|
63
62
|
useEffect(() => {
|
|
64
|
-
autocomplete.
|
|
65
|
-
inputRef.current?.blur();
|
|
66
|
-
});
|
|
67
|
-
autocomplete.setInputDisplayValueSetter(() => (v: string) => {
|
|
68
|
-
setInnerInputValue(inputRef.current!, v);
|
|
69
|
-
});
|
|
63
|
+
autocomplete.setInputRef(inputRef.current);
|
|
70
64
|
}, []);
|
|
71
65
|
|
|
72
66
|
return (
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { calculateComposedStyles } from "@/utils/calculate-styles";
|
|
2
|
+
import { setInnerInputValue } from "@/utils/input-utils";
|
|
2
3
|
import { useEffect, useState } from "react";
|
|
3
4
|
import { type StyleProp, Text, type TextStyle } from "react-native";
|
|
4
5
|
import { useAutocomplete } from "./context";
|
|
@@ -34,6 +35,18 @@ export function AutocompleteOption(props: AutocompleteOptionProps) {
|
|
|
34
35
|
const optionState = calculateState(autocomplete.state, isHovered, isSelected);
|
|
35
36
|
const composedStyles = calculateComposedStyles(autocomplete.styles, optionState, "option", props.style);
|
|
36
37
|
|
|
38
|
+
const handlePress = () => {
|
|
39
|
+
autocomplete.onChange?.(props.value);
|
|
40
|
+
autocomplete.setInputValue?.("");
|
|
41
|
+
autocomplete.setIsOpen(false);
|
|
42
|
+
if (autocomplete.inputRef) {
|
|
43
|
+
setInnerInputValue(autocomplete.inputRef, props.children);
|
|
44
|
+
autocomplete.inputRef.blur();
|
|
45
|
+
} else {
|
|
46
|
+
console.warn("Input reference is not available");
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
37
50
|
useEffect(() => {
|
|
38
51
|
autocomplete.setOptions((prev) => {
|
|
39
52
|
if (prev.find((option) => option.value === props.value)) {
|
|
@@ -41,16 +54,11 @@ export function AutocompleteOption(props: AutocompleteOptionProps) {
|
|
|
41
54
|
}
|
|
42
55
|
return [...prev, { value: props.value, label: props.children }];
|
|
43
56
|
});
|
|
57
|
+
return () => {
|
|
58
|
+
autocomplete.setOptions((prev) => prev.filter((option) => option.value !== props.value));
|
|
59
|
+
};
|
|
44
60
|
}, [props.value, props.children]);
|
|
45
61
|
|
|
46
|
-
const handlePress = () => {
|
|
47
|
-
autocomplete.onChange?.(props.value);
|
|
48
|
-
autocomplete.setInputValue?.("");
|
|
49
|
-
autocomplete.setInputDisplayValue(props.children);
|
|
50
|
-
autocomplete.setIsOpen(false);
|
|
51
|
-
autocomplete.blurInput();
|
|
52
|
-
};
|
|
53
|
-
|
|
54
62
|
const Component = props.render ?? Text;
|
|
55
63
|
return (
|
|
56
64
|
<Component
|
|
@@ -7,7 +7,6 @@ export interface AutocompleteOverlayProps {
|
|
|
7
7
|
children?: React.ReactNode;
|
|
8
8
|
onPress?: () => void;
|
|
9
9
|
style?: StyleProp<ViewStyle>;
|
|
10
|
-
render?: (props: AutocompleteOverlayProps) => React.ReactElement;
|
|
11
10
|
}
|
|
12
11
|
|
|
13
12
|
export function AutocompleteOverlay(props: AutocompleteOverlayProps) {
|
|
@@ -15,15 +14,24 @@ export function AutocompleteOverlay(props: AutocompleteOverlayProps) {
|
|
|
15
14
|
|
|
16
15
|
const composedStyles = calculateComposedStyles(autocomplete.styles, autocomplete.state, "overlay", props.style);
|
|
17
16
|
|
|
18
|
-
const
|
|
17
|
+
const handlePress = () => {
|
|
18
|
+
props.onPress?.();
|
|
19
|
+
autocomplete.setIsOpen(false);
|
|
20
|
+
autocomplete.inputRef?.blur();
|
|
21
|
+
};
|
|
22
|
+
|
|
19
23
|
return (
|
|
20
|
-
<
|
|
21
|
-
onPress={
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
<Pressable
|
|
25
|
+
onPress={handlePress}
|
|
26
|
+
style={[
|
|
27
|
+
StyleSheet.absoluteFill,
|
|
28
|
+
composedStyles,
|
|
29
|
+
{
|
|
30
|
+
display: autocomplete.isOpen ? "flex" : "none",
|
|
31
|
+
},
|
|
32
|
+
]}
|
|
25
33
|
>
|
|
26
34
|
{props.children}
|
|
27
|
-
</
|
|
35
|
+
</Pressable>
|
|
28
36
|
);
|
|
29
37
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from "react";
|
|
2
2
|
import { Portal } from "../portal";
|
|
3
|
-
import {
|
|
3
|
+
import { AutocompleteContext, useAutocomplete } from "./context";
|
|
4
4
|
|
|
5
5
|
export interface AutocompletePortalProps {
|
|
6
6
|
children?: React.ReactNode;
|
|
@@ -9,16 +9,6 @@ export interface AutocompletePortalProps {
|
|
|
9
9
|
export function AutocompletePortal(props: AutocompletePortalProps) {
|
|
10
10
|
const autocomplete = useAutocomplete();
|
|
11
11
|
|
|
12
|
-
useEffect(() => {
|
|
13
|
-
return () => {
|
|
14
|
-
autocomplete.setOptions([]);
|
|
15
|
-
};
|
|
16
|
-
}, []);
|
|
17
|
-
|
|
18
|
-
if (!autocomplete.isOpen) {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
12
|
return (
|
|
23
13
|
<Portal name="autocomplete-portal">
|
|
24
14
|
<AutocompleteContext.Provider value={autocomplete}>{props.children}</AutocompleteContext.Provider>
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { DEFAULT_LAYOUT, DEFAULT_POSITION, type LayoutPosition } from "@/hooks";
|
|
2
|
+
import type { TextInputRef } from "@/types/element.types";
|
|
2
3
|
import { calculateComposedStyles } from "@/utils/calculate-styles";
|
|
4
|
+
import { setInnerInputValue } from "@/utils/input-utils";
|
|
3
5
|
import React, { useEffect, useMemo, useState } from "react";
|
|
4
6
|
import { type LayoutRectangle, type StyleProp, View, type ViewStyle } from "react-native";
|
|
5
7
|
import { AutocompleteContext } from "./context";
|
|
6
8
|
import type { AutocompleteOption, AutocompleteState, AutocompleteStyles } from "./types";
|
|
7
9
|
|
|
8
|
-
interface AutocompleteRootInjectedProps {
|
|
9
|
-
style?: StyleProp<ViewStyle>;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
10
|
export interface AutocompleteRootBaseProps {
|
|
13
11
|
value?: string;
|
|
14
12
|
onChange?: (value: string) => void;
|
|
@@ -21,7 +19,6 @@ export interface AutocompleteRootBaseProps {
|
|
|
21
19
|
|
|
22
20
|
export interface AutocompleteRootProps extends AutocompleteRootBaseProps {
|
|
23
21
|
children?: React.ReactNode;
|
|
24
|
-
render?: (props: AutocompleteRootInjectedProps) => React.ReactElement;
|
|
25
22
|
styles?: AutocompleteStyles;
|
|
26
23
|
style?: StyleProp<ViewStyle>;
|
|
27
24
|
}
|
|
@@ -41,19 +38,21 @@ export function AutocompleteRoot(props: AutocompleteRootProps) {
|
|
|
41
38
|
const [contentLayout, setContentLayout] = useState<LayoutRectangle>(DEFAULT_LAYOUT);
|
|
42
39
|
const [inputPosition, setInputPosition] = useState<LayoutPosition>(DEFAULT_POSITION);
|
|
43
40
|
const [options, setOptions] = useState<Array<AutocompleteOption>>([]);
|
|
44
|
-
const [
|
|
45
|
-
const [setInputDisplayValue, setInputDisplayValueSetter] = useState<(value: string) => void>(() => () => {});
|
|
41
|
+
const [inputRef, setInputRef] = useState<TextInputRef | null>(null);
|
|
46
42
|
|
|
47
43
|
useEffect(() => {
|
|
44
|
+
if (!inputRef) return;
|
|
48
45
|
if (props.value) {
|
|
49
46
|
const selectedOption = options.find((opt) => opt.value === props.value);
|
|
50
47
|
if (selectedOption) {
|
|
51
48
|
props.setInputValue?.(selectedOption.label);
|
|
49
|
+
setInnerInputValue(inputRef, selectedOption.label);
|
|
52
50
|
}
|
|
53
51
|
} else {
|
|
54
52
|
props.setInputValue?.("");
|
|
53
|
+
setInnerInputValue(inputRef, "");
|
|
55
54
|
}
|
|
56
|
-
}, [props.value, options]);
|
|
55
|
+
}, [props.value, options, inputRef]);
|
|
57
56
|
|
|
58
57
|
const state = calculateState(props, isOpen);
|
|
59
58
|
const composedStyles = calculateComposedStyles(props.styles, state, "root", props.style);
|
|
@@ -75,10 +74,8 @@ export function AutocompleteRoot(props: AutocompleteRootProps) {
|
|
|
75
74
|
options,
|
|
76
75
|
setOptions,
|
|
77
76
|
openOnFocus: props.openOnFocus ?? true,
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
setInputDisplayValue,
|
|
81
|
-
setInputDisplayValueSetter,
|
|
77
|
+
inputRef,
|
|
78
|
+
setInputRef,
|
|
82
79
|
state,
|
|
83
80
|
isDisabled: props.isDisabled ?? false,
|
|
84
81
|
styles: props.styles ?? null,
|
|
@@ -96,16 +93,13 @@ export function AutocompleteRoot(props: AutocompleteRootProps) {
|
|
|
96
93
|
inputPosition,
|
|
97
94
|
contentLayout,
|
|
98
95
|
options,
|
|
99
|
-
blurInput,
|
|
100
|
-
setInputDisplayValue,
|
|
101
96
|
state,
|
|
102
97
|
],
|
|
103
98
|
);
|
|
104
99
|
|
|
105
|
-
const Component = props.render ?? View;
|
|
106
100
|
return (
|
|
107
101
|
<AutocompleteContext.Provider value={contextValue}>
|
|
108
|
-
<
|
|
102
|
+
<View style={composedStyles}>{props.children}</View>
|
|
109
103
|
</AutocompleteContext.Provider>
|
|
110
104
|
);
|
|
111
105
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { LayoutPosition } from "@/hooks";
|
|
2
|
-
import
|
|
2
|
+
import type { TextInputRef } from "@/types/element.types";
|
|
3
|
+
import React, { createContext, type Dispatch, useContext } from "react";
|
|
3
4
|
import type { LayoutRectangle } from "react-native";
|
|
4
5
|
import type { AutocompleteOption, AutocompleteState, AutocompleteStyles } from "./types";
|
|
5
6
|
|
|
@@ -23,10 +24,8 @@ export interface AutocompleteContext {
|
|
|
23
24
|
|
|
24
25
|
openOnFocus: boolean;
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
setInputDisplayValueSetter: Dispatch<React.SetStateAction<(value: string) => void>>;
|
|
29
|
-
setInputDisplayValue: (value: string) => void;
|
|
27
|
+
inputRef: TextInputRef | null;
|
|
28
|
+
setInputRef: Dispatch<React.SetStateAction<TextInputRef | null>>;
|
|
30
29
|
|
|
31
30
|
isDisabled: boolean;
|
|
32
31
|
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { type StyleProp, type TextStyle
|
|
2
|
+
import { Text, type StyleProp, type TextStyle } from "react-native";
|
|
3
3
|
import { useAvatar } from "./context";
|
|
4
4
|
|
|
5
5
|
export interface AvatarFallbackProps {
|
|
6
6
|
children: string;
|
|
7
|
-
render?: (props: AvatarFallbackProps) => React.ReactNode;
|
|
8
7
|
style?: StyleProp<TextStyle>;
|
|
9
8
|
}
|
|
10
9
|
|
|
11
10
|
export function AvatarFallback(props: AvatarFallbackProps) {
|
|
12
11
|
const avatar = useAvatar();
|
|
13
12
|
const composedStyles = [avatar.styles?.fallback, props.style];
|
|
14
|
-
|
|
15
|
-
return <Component {...props} style={composedStyles} />;
|
|
13
|
+
return <Text {...props} style={composedStyles} />;
|
|
16
14
|
}
|
|
@@ -17,7 +17,7 @@ const calculateState = (
|
|
|
17
17
|
selected: Date | null | undefined,
|
|
18
18
|
isCurrentMonth: boolean,
|
|
19
19
|
isDisabled: boolean,
|
|
20
|
-
isHovered: boolean
|
|
20
|
+
isHovered: boolean,
|
|
21
21
|
): CalendarDayState => {
|
|
22
22
|
if (isDisabled) return "disabled";
|
|
23
23
|
if (selected && isDateSameDay(date, selected)) return "selected";
|
|
@@ -42,7 +42,6 @@ export function CalendarDay(props: CalendarDayProps) {
|
|
|
42
42
|
const state = calculateState(props.date, calendar.value, isCurrentMonth, isDisabled, isHovered);
|
|
43
43
|
|
|
44
44
|
const handlePress = () => {
|
|
45
|
-
console.log("Day pressed:", isDisabled, calendar.onChange);
|
|
46
45
|
if (isDisabled || !calendar.onChange) return;
|
|
47
46
|
calendar.onChange(props.date);
|
|
48
47
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useRelativePosition } from "@/hooks/use-relative-position";
|
|
2
|
+
import { useSafeAreaInsets } from "@/safe-area";
|
|
2
3
|
import React from "react";
|
|
3
4
|
import { type StyleProp, View, type ViewStyle } from "react-native";
|
|
4
5
|
import { usePopover } from "./context";
|
|
@@ -13,6 +14,7 @@ export interface PopoverContentProps {
|
|
|
13
14
|
|
|
14
15
|
export function PopoverContent(props: PopoverContentProps) {
|
|
15
16
|
const popover = usePopover();
|
|
17
|
+
const insets = useSafeAreaInsets();
|
|
16
18
|
|
|
17
19
|
const positionStyle = useRelativePosition({
|
|
18
20
|
align: "start",
|
|
@@ -22,6 +24,7 @@ export function PopoverContent(props: PopoverContentProps) {
|
|
|
22
24
|
alignOffset: 0,
|
|
23
25
|
side: "bottom",
|
|
24
26
|
sideOffset: 0,
|
|
27
|
+
insets,
|
|
25
28
|
});
|
|
26
29
|
|
|
27
30
|
const composedStyle = [positionStyle, popover.styles?.content, props.style];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
import type { SafeAreaContextValue } from "./types";
|
|
3
|
+
|
|
4
|
+
const SafeAreaContext = createContext<SafeAreaContextValue | null>(null);
|
|
5
|
+
|
|
6
|
+
export const useSafeAreaInsets = () => {
|
|
7
|
+
const context = useContext(SafeAreaContext);
|
|
8
|
+
if (!context) {
|
|
9
|
+
throw new Error("useSafeAreaInsets must be used within SafeAreaProvider");
|
|
10
|
+
}
|
|
11
|
+
return context.insets;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export { SafeAreaContext };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type PropsWithChildren } from "react";
|
|
2
|
+
import { SafeAreaContext } from "./context";
|
|
3
|
+
import type { SafeAreaInsets } from "./types";
|
|
4
|
+
|
|
5
|
+
export interface SafeAreaProviderProps extends PropsWithChildren {
|
|
6
|
+
insets?: SafeAreaInsets;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const SafeAreaProvider = ({ children, insets }: SafeAreaProviderProps) => {
|
|
10
|
+
const defaultInsets: SafeAreaInsets = {
|
|
11
|
+
top: 0,
|
|
12
|
+
bottom: 0,
|
|
13
|
+
left: 0,
|
|
14
|
+
right: 0,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const calculatedInsets: SafeAreaInsets = { ...defaultInsets, ...insets };
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<SafeAreaContext.Provider
|
|
21
|
+
value={{
|
|
22
|
+
insets: calculatedInsets,
|
|
23
|
+
}}
|
|
24
|
+
>
|
|
25
|
+
{children}
|
|
26
|
+
</SafeAreaContext.Provider>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { useEffect } from "react";
|
|
2
1
|
import type { Theme } from "@react-navigation/native";
|
|
3
|
-
import {
|
|
2
|
+
import { useEffect } from "react";
|
|
4
3
|
import { Platform } from "react-native";
|
|
4
|
+
import { useTheme } from "../provider";
|
|
5
5
|
|
|
6
6
|
export function useReactNavigationTheme(): Theme {
|
|
7
7
|
const theme = useTheme();
|
|
@@ -13,13 +13,14 @@ export function useReactNavigationTheme(): Theme {
|
|
|
13
13
|
styles.innerHTML = `
|
|
14
14
|
:root {
|
|
15
15
|
--expo-router-modal-border: 1px solid ${theme.colors.border};
|
|
16
|
+
--expo-router-modal-border-radius: ${theme.radius}px;
|
|
16
17
|
}
|
|
17
18
|
`;
|
|
18
19
|
document.head.appendChild(styles);
|
|
19
20
|
return () => {
|
|
20
21
|
document.head.removeChild(styles);
|
|
21
22
|
};
|
|
22
|
-
}, [theme.colors.border]);
|
|
23
|
+
}, [theme.colors.border, theme.radius]);
|
|
23
24
|
|
|
24
25
|
return {
|
|
25
26
|
dark: theme.colorScheme === "dark",
|