@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.
Files changed (112) hide show
  1. package/dist/module/components/button/variants/default.js +0 -1
  2. package/dist/module/components/button/variants/default.js.map +1 -1
  3. package/dist/module/components/button/variants/secondary.js +2 -3
  4. package/dist/module/components/button/variants/secondary.js.map +1 -1
  5. package/dist/module/components/toast/toast-manager.js +7 -4
  6. package/dist/module/components/toast/toast-manager.js.map +1 -1
  7. package/dist/module/hooks/use-relative-position.js.map +1 -1
  8. package/dist/module/index.js +14 -5
  9. package/dist/module/index.js.map +1 -1
  10. package/dist/module/primitives/autocomplete/autocomplete-content.js +9 -5
  11. package/dist/module/primitives/autocomplete/autocomplete-content.js.map +1 -1
  12. package/dist/module/primitives/autocomplete/autocomplete-input.js +1 -7
  13. package/dist/module/primitives/autocomplete/autocomplete-input.js.map +1 -1
  14. package/dist/module/primitives/autocomplete/autocomplete-option.js +15 -7
  15. package/dist/module/primitives/autocomplete/autocomplete-option.js.map +1 -1
  16. package/dist/module/primitives/autocomplete/autocomplete-overlay.js +10 -6
  17. package/dist/module/primitives/autocomplete/autocomplete-overlay.js.map +1 -1
  18. package/dist/module/primitives/autocomplete/autocomplete-portal.js +2 -10
  19. package/dist/module/primitives/autocomplete/autocomplete-portal.js.map +1 -1
  20. package/dist/module/primitives/autocomplete/autocomplete-root.js +10 -10
  21. package/dist/module/primitives/autocomplete/autocomplete-root.js.map +1 -1
  22. package/dist/module/primitives/autocomplete/context.js.map +1 -1
  23. package/dist/module/primitives/avatar/avatar-fallback.js +2 -3
  24. package/dist/module/primitives/avatar/avatar-fallback.js.map +1 -1
  25. package/dist/module/primitives/calendar/calendar-day.js +0 -1
  26. package/dist/module/primitives/calendar/calendar-day.js.map +1 -1
  27. package/dist/module/primitives/popover/popover-content.js +4 -1
  28. package/dist/module/primitives/popover/popover-content.js.map +1 -1
  29. package/dist/module/safe-area/context.js +13 -0
  30. package/dist/module/safe-area/context.js.map +1 -0
  31. package/dist/module/safe-area/index.js +5 -0
  32. package/dist/module/safe-area/index.js.map +1 -0
  33. package/dist/module/safe-area/provider.js +26 -0
  34. package/dist/module/safe-area/provider.js.map +1 -0
  35. package/dist/module/safe-area/types.js +2 -0
  36. package/dist/module/safe-area/types.js.map +1 -0
  37. package/dist/module/themes/adapters/react-navigation.js +3 -2
  38. package/dist/module/themes/adapters/react-navigation.js.map +1 -1
  39. package/dist/module/themes/default/colors.js +28 -28
  40. package/dist/module/themes/default/colors.js.map +1 -1
  41. package/dist/module/themes/provider.js +13 -11
  42. package/dist/module/themes/provider.js.map +1 -1
  43. package/dist/module/themes/utils.js +21 -0
  44. package/dist/module/themes/utils.js.map +1 -0
  45. package/dist/typescript/src/components/button/variants/default.d.ts.map +1 -1
  46. package/dist/typescript/src/components/button/variants/secondary.d.ts.map +1 -1
  47. package/dist/typescript/src/components/toast/toast-manager.d.ts.map +1 -1
  48. package/dist/typescript/src/hooks/use-relative-position.d.ts +2 -2
  49. package/dist/typescript/src/hooks/use-relative-position.d.ts.map +1 -1
  50. package/dist/typescript/src/index.d.ts +7 -1
  51. package/dist/typescript/src/index.d.ts.map +1 -1
  52. package/dist/typescript/src/primitives/autocomplete/autocomplete-content.d.ts.map +1 -1
  53. package/dist/typescript/src/primitives/autocomplete/autocomplete-input.d.ts.map +1 -1
  54. package/dist/typescript/src/primitives/autocomplete/autocomplete-option.d.ts.map +1 -1
  55. package/dist/typescript/src/primitives/autocomplete/autocomplete-overlay.d.ts +0 -1
  56. package/dist/typescript/src/primitives/autocomplete/autocomplete-overlay.d.ts.map +1 -1
  57. package/dist/typescript/src/primitives/autocomplete/autocomplete-portal.d.ts +1 -1
  58. package/dist/typescript/src/primitives/autocomplete/autocomplete-portal.d.ts.map +1 -1
  59. package/dist/typescript/src/primitives/autocomplete/autocomplete-root.d.ts +0 -5
  60. package/dist/typescript/src/primitives/autocomplete/autocomplete-root.d.ts.map +1 -1
  61. package/dist/typescript/src/primitives/autocomplete/context.d.ts +5 -6
  62. package/dist/typescript/src/primitives/autocomplete/context.d.ts.map +1 -1
  63. package/dist/typescript/src/primitives/avatar/avatar-fallback.d.ts +0 -1
  64. package/dist/typescript/src/primitives/avatar/avatar-fallback.d.ts.map +1 -1
  65. package/dist/typescript/src/primitives/calendar/calendar-day.d.ts.map +1 -1
  66. package/dist/typescript/src/primitives/popover/popover-content.d.ts.map +1 -1
  67. package/dist/typescript/src/safe-area/context.d.ts +5 -0
  68. package/dist/typescript/src/safe-area/context.d.ts.map +1 -0
  69. package/dist/typescript/src/safe-area/index.d.ts +4 -0
  70. package/dist/typescript/src/safe-area/index.d.ts.map +1 -0
  71. package/dist/typescript/src/safe-area/provider.d.ts +7 -0
  72. package/dist/typescript/src/safe-area/provider.d.ts.map +1 -0
  73. package/dist/typescript/src/safe-area/types.d.ts +10 -0
  74. package/dist/typescript/src/safe-area/types.d.ts.map +1 -0
  75. package/dist/typescript/src/themes/adapters/react-navigation.d.ts.map +1 -1
  76. package/dist/typescript/src/themes/provider.d.ts +5 -4
  77. package/dist/typescript/src/themes/provider.d.ts.map +1 -1
  78. package/dist/typescript/src/themes/types.d.ts +0 -1
  79. package/dist/typescript/src/themes/types.d.ts.map +1 -1
  80. package/dist/typescript/src/themes/utils.d.ts +3 -0
  81. package/dist/typescript/src/themes/utils.d.ts.map +1 -0
  82. package/nodemon.json +14 -0
  83. package/package.json +3 -1
  84. package/src/components/button/variants/default.tsx +1 -2
  85. package/src/components/button/variants/secondary.tsx +3 -4
  86. package/src/components/toast/toast-manager.tsx +5 -4
  87. package/src/hooks/use-relative-position.ts +4 -4
  88. package/src/index.tsx +16 -9
  89. package/src/primitives/autocomplete/autocomplete-content.tsx +12 -3
  90. package/src/primitives/autocomplete/autocomplete-input.tsx +1 -7
  91. package/src/primitives/autocomplete/autocomplete-option.tsx +16 -8
  92. package/src/primitives/autocomplete/autocomplete-overlay.tsx +16 -8
  93. package/src/primitives/autocomplete/autocomplete-portal.tsx +2 -12
  94. package/src/primitives/autocomplete/autocomplete-root.tsx +10 -16
  95. package/src/primitives/autocomplete/context.ts +4 -5
  96. package/src/primitives/avatar/avatar-fallback.tsx +2 -4
  97. package/src/primitives/calendar/calendar-day.tsx +1 -2
  98. package/src/primitives/popover/popover-content.tsx +3 -0
  99. package/src/safe-area/context.ts +14 -0
  100. package/src/safe-area/index.ts +3 -0
  101. package/src/safe-area/provider.tsx +28 -0
  102. package/src/safe-area/types.ts +10 -0
  103. package/src/themes/adapters/react-navigation.ts +4 -3
  104. package/src/themes/default/colors.ts +28 -28
  105. package/src/themes/provider.tsx +17 -15
  106. package/src/themes/types.ts +0 -1
  107. package/src/themes/utils.ts +14 -0
  108. package/dist/module/themes/themes.js +0 -7
  109. package/dist/module/themes/themes.js.map +0 -1
  110. package/dist/typescript/src/themes/themes.d.ts +0 -3
  111. package/dist/typescript/src/themes/themes.d.ts.map +0 -1
  112. 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.41",
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.foreground,
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.primaryForeground,
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 { View, StyleSheet } from "react-native";
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 LayoutRectangle, type ScaledSize } from "react-native";
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: ScaledSize;
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: ScaledSize
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 { ThemeProvider } from "@/themes";
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
- <ThemeProvider>
16
- <ToastContainer />
17
- {children}
18
- <PortalHost container={portalContainer} />
19
- <AsyncAlertDialogManager />
20
- </ThemeProvider>
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.setBlurInput(() => () => {
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 Component = props.render ?? Pressable;
17
+ const handlePress = () => {
18
+ props.onPress?.();
19
+ autocomplete.setIsOpen(false);
20
+ autocomplete.inputRef?.blur();
21
+ };
22
+
19
23
  return (
20
- <Component
21
- onPress={() => {
22
- autocomplete.setIsOpen(false);
23
- }}
24
- style={[StyleSheet.absoluteFill, composedStyles]}
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
- </Component>
35
+ </Pressable>
28
36
  );
29
37
  }
@@ -1,6 +1,6 @@
1
- import React, { useEffect } from "react";
1
+ import React from "react";
2
2
  import { Portal } from "../portal";
3
- import { useAutocomplete, AutocompleteContext } from "./context";
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 [blurInput, setBlurInput] = useState<() => void>(() => () => {});
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
- blurInput,
79
- setBlurInput,
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
- <Component style={composedStyles}>{props.children}</Component>
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 { createContext, type Dispatch, useContext } from "react";
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
- blurInput: () => void;
27
- setBlurInput: Dispatch<React.SetStateAction<() => void>>;
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, View } from "react-native";
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
- const Component = props.render ?? View;
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,3 @@
1
+ export { SafeAreaProvider } from "./provider";
2
+ export { useSafeAreaInsets } from "./context";
3
+ export type { SafeAreaInsets, SafeAreaContextValue } from "./types";
@@ -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
+ };
@@ -0,0 +1,10 @@
1
+ export interface SafeAreaInsets {
2
+ top: number;
3
+ bottom: number;
4
+ left: number;
5
+ right: number;
6
+ }
7
+
8
+ export interface SafeAreaContextValue {
9
+ insets: SafeAreaInsets;
10
+ }
@@ -1,7 +1,7 @@
1
- import { useEffect } from "react";
2
1
  import type { Theme } from "@react-navigation/native";
3
- import { useTheme } from "../provider";
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",