@korsolutions/ui 0.0.58 → 0.0.59

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 (92) hide show
  1. package/dist/module/components/icon/icon.js +3 -3
  2. package/dist/module/components/icon/icon.js.map +1 -1
  3. package/dist/module/components/menu/components/menu-checkbox-item.js +51 -0
  4. package/dist/module/components/menu/components/menu-checkbox-item.js.map +1 -0
  5. package/dist/module/components/menu/components/menu-content.js +1 -1
  6. package/dist/module/components/menu/components/menu-content.js.map +1 -1
  7. package/dist/module/components/menu/components/menu-group.js +18 -0
  8. package/dist/module/components/menu/components/menu-group.js.map +1 -0
  9. package/dist/module/components/menu/components/menu-item.js +14 -6
  10. package/dist/module/components/menu/components/menu-item.js.map +1 -1
  11. package/dist/module/components/menu/components/menu-label.js +17 -0
  12. package/dist/module/components/menu/components/menu-label.js.map +1 -0
  13. package/dist/module/components/menu/components/menu-overlay.js +1 -1
  14. package/dist/module/components/menu/components/menu-overlay.js.map +1 -1
  15. package/dist/module/components/menu/components/menu-radio-group.js +24 -0
  16. package/dist/module/components/menu/components/menu-radio-group.js.map +1 -0
  17. package/dist/module/components/menu/components/menu-radio-item.js +54 -0
  18. package/dist/module/components/menu/components/menu-radio-item.js.map +1 -0
  19. package/dist/module/components/menu/components/menu-separator.js +17 -0
  20. package/dist/module/components/menu/components/menu-separator.js.map +1 -0
  21. package/dist/module/components/menu/components/menu-shortcut.js +17 -0
  22. package/dist/module/components/menu/components/menu-shortcut.js.map +1 -0
  23. package/dist/module/components/menu/context.js +8 -0
  24. package/dist/module/components/menu/context.js.map +1 -1
  25. package/dist/module/components/menu/index.js +15 -1
  26. package/dist/module/components/menu/index.js.map +1 -1
  27. package/dist/module/components/menu/use-organized-children.js +39 -0
  28. package/dist/module/components/menu/use-organized-children.js.map +1 -0
  29. package/dist/module/components/menu/variants/default.js +82 -6
  30. package/dist/module/components/menu/variants/default.js.map +1 -1
  31. package/dist/module/components/portal/portal.js.map +1 -1
  32. package/dist/module/themes/provider.js.map +1 -1
  33. package/dist/module/utils/element-utils.js +11 -0
  34. package/dist/module/utils/element-utils.js.map +1 -0
  35. package/dist/typescript/src/components/icon/icon.d.ts +2 -2
  36. package/dist/typescript/src/components/icon/icon.d.ts.map +1 -1
  37. package/dist/typescript/src/components/menu/components/menu-checkbox-item.d.ts +13 -0
  38. package/dist/typescript/src/components/menu/components/menu-checkbox-item.d.ts.map +1 -0
  39. package/dist/typescript/src/components/menu/components/menu-content.d.ts.map +1 -1
  40. package/dist/typescript/src/components/menu/components/menu-group.d.ts +9 -0
  41. package/dist/typescript/src/components/menu/components/menu-group.d.ts.map +1 -0
  42. package/dist/typescript/src/components/menu/components/menu-item.d.ts +3 -3
  43. package/dist/typescript/src/components/menu/components/menu-item.d.ts.map +1 -1
  44. package/dist/typescript/src/components/menu/components/menu-label.d.ts +9 -0
  45. package/dist/typescript/src/components/menu/components/menu-label.d.ts.map +1 -0
  46. package/dist/typescript/src/components/menu/components/menu-overlay.d.ts.map +1 -1
  47. package/dist/typescript/src/components/menu/components/menu-radio-group.d.ts +10 -0
  48. package/dist/typescript/src/components/menu/components/menu-radio-group.d.ts.map +1 -0
  49. package/dist/typescript/src/components/menu/components/menu-radio-item.d.ts +12 -0
  50. package/dist/typescript/src/components/menu/components/menu-radio-item.d.ts.map +1 -0
  51. package/dist/typescript/src/components/menu/components/menu-separator.d.ts +8 -0
  52. package/dist/typescript/src/components/menu/components/menu-separator.d.ts.map +1 -0
  53. package/dist/typescript/src/components/menu/components/menu-shortcut.d.ts +9 -0
  54. package/dist/typescript/src/components/menu/components/menu-shortcut.d.ts.map +1 -0
  55. package/dist/typescript/src/components/menu/context.d.ts +7 -1
  56. package/dist/typescript/src/components/menu/context.d.ts.map +1 -1
  57. package/dist/typescript/src/components/menu/index.d.ts +21 -0
  58. package/dist/typescript/src/components/menu/index.d.ts.map +1 -1
  59. package/dist/typescript/src/components/menu/types.d.ts +17 -2
  60. package/dist/typescript/src/components/menu/types.d.ts.map +1 -1
  61. package/dist/typescript/src/components/menu/use-organized-children.d.ts +3 -0
  62. package/dist/typescript/src/components/menu/use-organized-children.d.ts.map +1 -0
  63. package/dist/typescript/src/components/menu/variants/default.d.ts.map +1 -1
  64. package/dist/typescript/src/components/portal/portal.d.ts +2 -2
  65. package/dist/typescript/src/components/portal/portal.d.ts.map +1 -1
  66. package/dist/typescript/src/themes/provider.d.ts +2 -2
  67. package/dist/typescript/src/themes/provider.d.ts.map +1 -1
  68. package/dist/typescript/src/types/element.types.d.ts +2 -0
  69. package/dist/typescript/src/types/element.types.d.ts.map +1 -1
  70. package/dist/typescript/src/utils/element-utils.d.ts +3 -0
  71. package/dist/typescript/src/utils/element-utils.d.ts.map +1 -0
  72. package/package.json +1 -1
  73. package/src/components/icon/icon.tsx +9 -3
  74. package/src/components/menu/components/menu-checkbox-item.tsx +74 -0
  75. package/src/components/menu/components/menu-content.tsx +2 -1
  76. package/src/components/menu/components/menu-group.tsx +25 -0
  77. package/src/components/menu/components/menu-item.tsx +25 -9
  78. package/src/components/menu/components/menu-label.tsx +21 -0
  79. package/src/components/menu/components/menu-overlay.tsx +11 -2
  80. package/src/components/menu/components/menu-radio-group.tsx +35 -0
  81. package/src/components/menu/components/menu-radio-item.tsx +80 -0
  82. package/src/components/menu/components/menu-separator.tsx +22 -0
  83. package/src/components/menu/components/menu-shortcut.tsx +21 -0
  84. package/src/components/menu/context.ts +18 -1
  85. package/src/components/menu/index.ts +21 -0
  86. package/src/components/menu/types.ts +25 -2
  87. package/src/components/menu/use-organized-children.tsx +38 -0
  88. package/src/components/menu/variants/default.tsx +82 -6
  89. package/src/components/portal/portal.tsx +23 -5
  90. package/src/themes/provider.tsx +2 -4
  91. package/src/types/element.types.ts +10 -1
  92. package/src/utils/element-utils.ts +12 -0
@@ -0,0 +1,21 @@
1
+ import React from "react";
2
+ import { Text, type StyleProp, type TextStyle } from "react-native";
3
+ import { useMenu } from "../context";
4
+
5
+ export interface MenuLabelProps {
6
+ children: string;
7
+ render?: (props: MenuLabelProps) => React.ReactNode;
8
+ style?: StyleProp<TextStyle>;
9
+ }
10
+
11
+ export function MenuLabel(props: MenuLabelProps) {
12
+ const menu = useMenu();
13
+ const composedStyle = [menu.styles?.label, props.style];
14
+
15
+ const Component = props.render ?? Text;
16
+ return (
17
+ <Component {...props} style={composedStyle}>
18
+ {props.children}
19
+ </Component>
20
+ );
21
+ }
@@ -1,5 +1,10 @@
1
1
  import React from "react";
2
- import { Pressable, type StyleProp, StyleSheet, type ViewStyle } from "react-native";
2
+ import {
3
+ Pressable,
4
+ type StyleProp,
5
+ StyleSheet,
6
+ type ViewStyle,
7
+ } from "react-native";
3
8
  import { useMenu } from "../context";
4
9
 
5
10
  export interface MenuOverlayProps {
@@ -13,7 +18,11 @@ export interface MenuOverlayProps {
13
18
  export function MenuOverlay(props: MenuOverlayProps) {
14
19
  const menu = useMenu();
15
20
 
16
- const composedStyle = [StyleSheet.absoluteFill, menu.styles?.overlay, props.style];
21
+ const composedStyle = StyleSheet.flatten([
22
+ StyleSheet.absoluteFill,
23
+ menu.styles?.overlay,
24
+ props.style,
25
+ ]);
17
26
 
18
27
  const Component = props.render ?? Pressable;
19
28
  return (
@@ -0,0 +1,35 @@
1
+ import React, { useMemo } from "react";
2
+ import { View, type StyleProp, type ViewStyle } from "react-native";
3
+ import { MenuRadioGroupContext, useMenu } from "../context";
4
+
5
+ export interface MenuRadioGroupProps {
6
+ children?: React.ReactNode;
7
+ value: string;
8
+ onValueChange: (value: string) => void;
9
+ style?: StyleProp<ViewStyle>;
10
+ }
11
+
12
+ export function MenuRadioGroup(props: MenuRadioGroupProps) {
13
+ const menu = useMenu();
14
+ const composedStyle = [menu.styles?.radioGroup, props.style];
15
+
16
+ const contextValue = useMemo(
17
+ () => ({
18
+ value: props.value,
19
+ onValueChange: props.onValueChange,
20
+ }),
21
+ [props.value, props.onValueChange],
22
+ );
23
+
24
+ return (
25
+ <MenuRadioGroupContext.Provider value={contextValue}>
26
+ <View
27
+ accessibilityRole="radiogroup"
28
+ role="radiogroup"
29
+ style={composedStyle}
30
+ >
31
+ {props.children}
32
+ </View>
33
+ </MenuRadioGroupContext.Provider>
34
+ );
35
+ }
@@ -0,0 +1,80 @@
1
+ import React, { useState } from "react";
2
+ import { Pressable, View, type StyleProp, type ViewStyle } from "react-native";
3
+ import { useMenu, useMenuRadioGroup } from "../context";
4
+ import type { MenuRadioItemState } from "../types";
5
+ import { useOrganizedChildren } from "../use-organized-children";
6
+
7
+ export interface MenuRadioItemProps {
8
+ children: React.ReactNode;
9
+ value: string;
10
+ disabled?: boolean;
11
+ closeOnPress?: boolean;
12
+ render?: (props: MenuRadioItemProps) => React.ReactNode;
13
+ style?: StyleProp<ViewStyle>;
14
+ }
15
+
16
+ const calculateState = (
17
+ isHovered: boolean,
18
+ isSelected: boolean,
19
+ disabled?: boolean,
20
+ ): MenuRadioItemState => {
21
+ if (disabled) return "disabled";
22
+ if (isSelected) return "selected";
23
+ if (isHovered) return "hovered";
24
+ return "default";
25
+ };
26
+
27
+ export function MenuRadioItem(props: MenuRadioItemProps) {
28
+ const menu = useMenu();
29
+ const radioGroup = useMenuRadioGroup();
30
+ const [isHovered, setIsHovered] = useState(false);
31
+ const isSelected = radioGroup.value === props.value;
32
+ const state = calculateState(isHovered, isSelected, props.disabled);
33
+
34
+ const composedStyle = [
35
+ menu.styles?.radioItem?.default,
36
+ menu.styles?.radioItem?.[state],
37
+ props.style,
38
+ ];
39
+
40
+ const indicatorStyle = [
41
+ menu.styles?.radioIndicator?.default,
42
+ isSelected ? menu.styles?.radioIndicator?.selected : undefined,
43
+ ];
44
+
45
+ const handlePress = () => {
46
+ if (props.disabled) return;
47
+ radioGroup.onValueChange(props.value);
48
+ if (props.closeOnPress) {
49
+ menu.setIsOpen(false);
50
+ }
51
+ };
52
+
53
+ const organizedChildren = useOrganizedChildren(props.children);
54
+
55
+ if (props.render) {
56
+ return (
57
+ <>
58
+ {props.render({
59
+ ...props,
60
+ children: organizedChildren,
61
+ })}
62
+ </>
63
+ );
64
+ }
65
+
66
+ return (
67
+ <Pressable
68
+ onPress={handlePress}
69
+ onPointerEnter={() => setIsHovered(true)}
70
+ onPointerLeave={() => setIsHovered(false)}
71
+ disabled={props.disabled}
72
+ accessibilityRole="radio"
73
+ accessibilityState={{ checked: isSelected, disabled: props.disabled }}
74
+ style={composedStyle}
75
+ >
76
+ {organizedChildren}
77
+ <View style={indicatorStyle} />
78
+ </Pressable>
79
+ );
80
+ }
@@ -0,0 +1,22 @@
1
+ import React from "react";
2
+ import { View, type StyleProp, type ViewStyle } from "react-native";
3
+ import { useMenu } from "../context";
4
+
5
+ export interface MenuSeparatorProps {
6
+ render?: (props: MenuSeparatorProps) => React.ReactNode;
7
+ style?: StyleProp<ViewStyle>;
8
+ }
9
+
10
+ export function MenuSeparator(props: MenuSeparatorProps) {
11
+ const menu = useMenu();
12
+ const composedStyle = [menu.styles?.separator, props.style];
13
+
14
+ const Component = props.render ?? View;
15
+ return (
16
+ <Component
17
+ {...props}
18
+ role="separator"
19
+ style={composedStyle}
20
+ />
21
+ );
22
+ }
@@ -0,0 +1,21 @@
1
+ import React from "react";
2
+ import { Text, type StyleProp, type TextStyle } from "react-native";
3
+ import { useMenu } from "../context";
4
+
5
+ export interface MenuShortcutProps {
6
+ children: string;
7
+ render?: (props: MenuShortcutProps) => React.ReactNode;
8
+ style?: StyleProp<TextStyle>;
9
+ }
10
+
11
+ export function MenuShortcut(props: MenuShortcutProps) {
12
+ const menu = useMenu();
13
+ const composedStyle = [menu.styles?.shortcut, props.style];
14
+
15
+ const Component = props.render ?? Text;
16
+ return (
17
+ <Component {...props} style={composedStyle}>
18
+ {props.children}
19
+ </Component>
20
+ );
21
+ }
@@ -1,6 +1,6 @@
1
- import type { LayoutPosition } from "../../hooks/use-relative-position";
2
1
  import { createContext, type Dispatch, useContext } from "react";
3
2
  import type { LayoutRectangle } from "react-native";
3
+ import type { LayoutPosition } from "../../hooks/use-relative-position";
4
4
  import type { MenuStyles } from "./types";
5
5
 
6
6
  export interface MenuContext {
@@ -23,3 +23,20 @@ export const useMenu = () => {
23
23
  }
24
24
  return context;
25
25
  };
26
+
27
+ export interface MenuRadioGroupContextValue {
28
+ value: string;
29
+ onValueChange: (value: string) => void;
30
+ }
31
+
32
+ export const MenuRadioGroupContext = createContext<
33
+ MenuRadioGroupContextValue | undefined
34
+ >(undefined);
35
+
36
+ export const useMenuRadioGroup = () => {
37
+ const context = useContext(MenuRadioGroupContext);
38
+ if (!context) {
39
+ throw new Error("useMenuRadioGroup must be used within a Menu.RadioGroup");
40
+ }
41
+ return context;
42
+ };
@@ -1,8 +1,15 @@
1
+ import { MenuCheckboxItem } from "./components/menu-checkbox-item";
1
2
  import { MenuContent } from "./components/menu-content";
3
+ import { MenuGroup } from "./components/menu-group";
2
4
  import { MenuItem } from "./components/menu-item";
5
+ import { MenuLabel } from "./components/menu-label";
3
6
  import { MenuOverlay } from "./components/menu-overlay";
4
7
  import { MenuPortal } from "./components/menu-portal";
8
+ import { MenuRadioGroup } from "./components/menu-radio-group";
9
+ import { MenuRadioItem } from "./components/menu-radio-item";
5
10
  import { MenuRoot } from "./components/menu-root";
11
+ import { MenuSeparator } from "./components/menu-separator";
12
+ import { MenuShortcut } from "./components/menu-shortcut";
6
13
  import { MenuTrigger } from "./components/menu-trigger";
7
14
 
8
15
  export const Menu = {
@@ -12,12 +19,26 @@ export const Menu = {
12
19
  Overlay: MenuOverlay,
13
20
  Content: MenuContent,
14
21
  Item: MenuItem,
22
+ Group: MenuGroup,
23
+ Label: MenuLabel,
24
+ Separator: MenuSeparator,
25
+ CheckboxItem: MenuCheckboxItem,
26
+ RadioGroup: MenuRadioGroup,
27
+ RadioItem: MenuRadioItem,
28
+ Shortcut: MenuShortcut,
15
29
  };
16
30
 
31
+ export type { MenuCheckboxItemProps } from "./components/menu-checkbox-item";
17
32
  export type { MenuContentProps } from "./components/menu-content";
33
+ export type { MenuGroupProps } from "./components/menu-group";
18
34
  export type { MenuItemProps } from "./components/menu-item";
35
+ export type { MenuLabelProps } from "./components/menu-label";
19
36
  export type { MenuOverlayProps } from "./components/menu-overlay";
20
37
  export type { MenuPortalProps } from "./components/menu-portal";
38
+ export type { MenuRadioGroupProps } from "./components/menu-radio-group";
39
+ export type { MenuRadioItemProps } from "./components/menu-radio-item";
21
40
  export type { MenuRootProps } from "./components/menu-root";
41
+ export type { MenuSeparatorProps } from "./components/menu-separator";
42
+ export type { MenuShortcutProps } from "./components/menu-shortcut";
22
43
  export type { MenuTriggerProps } from "./components/menu-trigger";
23
44
  export type { MenuStyles } from "./types";
@@ -1,11 +1,34 @@
1
+ import type { StyleProp, TextStyle, ViewStyle } from "react-native";
2
+ import type { SvgProps } from "../../types/props.types";
1
3
  import type { MenuContentProps } from "./components/menu-content";
2
- import type { MenuItemProps } from "./components/menu-item";
3
4
  import type { MenuOverlayProps } from "./components/menu-overlay";
4
5
 
5
6
  export type MenuButtonState = "default" | "hovered";
7
+ export type MenuCheckboxItemState = "default" | "hovered" | "disabled";
8
+ export type MenuRadioItemState =
9
+ | "default"
10
+ | "hovered"
11
+ | "selected"
12
+ | "disabled";
13
+
14
+ export type MenuRadioIndicatorState = "default" | "selected";
6
15
 
7
16
  export interface MenuStyles {
8
17
  content?: MenuContentProps["style"];
9
- item?: Partial<Record<MenuButtonState, MenuItemProps["style"]>>;
18
+ item?: Partial<Record<MenuButtonState, StyleProp<ViewStyle>>>;
19
+ itemText?: StyleProp<TextStyle>;
20
+ itemIcon?: SvgProps;
10
21
  overlay?: MenuOverlayProps["style"];
22
+
23
+ group?: StyleProp<ViewStyle>;
24
+ label?: StyleProp<TextStyle>;
25
+ separator?: StyleProp<ViewStyle>;
26
+ checkboxItem?: Partial<Record<MenuCheckboxItemState, StyleProp<ViewStyle>>>;
27
+ checkboxIndicator?: StyleProp<TextStyle>;
28
+ radioGroup?: StyleProp<ViewStyle>;
29
+ radioItem?: Partial<Record<MenuRadioItemState, StyleProp<ViewStyle>>>;
30
+ radioIndicator?: Partial<
31
+ Record<MenuRadioIndicatorState, StyleProp<ViewStyle>>
32
+ >;
33
+ shortcut?: StyleProp<TextStyle>;
11
34
  }
@@ -0,0 +1,38 @@
1
+ import React, { useMemo } from "react";
2
+ import { Text } from "react-native";
3
+ import { getElementProp } from "../../utils/element-utils";
4
+ import { Icon } from "../icon";
5
+ import { useMenu } from "./context";
6
+
7
+ export function useOrganizedChildren(children: React.ReactNode) {
8
+ const menu = useMenu();
9
+
10
+ const organizedChildren = useMemo(() => {
11
+ if (typeof children === "string") {
12
+ return <Text style={menu.styles?.itemText}>{children}</Text>;
13
+ }
14
+ if (Array.isArray(children)) {
15
+ return children.map((child, index) => {
16
+ if (typeof child === "string") {
17
+ return (
18
+ <Text key={index} style={menu.styles?.itemText}>
19
+ {child}
20
+ </Text>
21
+ );
22
+ } else if (React.isValidElement(child) && child.type === Icon) {
23
+ return React.cloneElement(child as React.ReactElement<any>, {
24
+ key: child.key,
25
+ ...menu.styles?.itemIcon,
26
+ style: [
27
+ menu.styles?.itemIcon?.style,
28
+ getElementProp(child, "style"),
29
+ ],
30
+ });
31
+ }
32
+ return child;
33
+ });
34
+ }
35
+ return children;
36
+ }, [children, menu.styles?.itemIcon]);
37
+ return organizedChildren;
38
+ }
@@ -4,9 +4,7 @@ import { useThemedStyles } from "../../../utils/use-themed-styles";
4
4
  export const useMenuVariantDefault = (): MenuStyles => {
5
5
  return useThemedStyles(
6
6
  ({ colors, radius, fontFamily, fontSize }): MenuStyles => ({
7
- overlay: {
8
- backgroundColor: "rgba(0, 0, 0, 0.5)",
9
- },
7
+ overlay: {},
10
8
  content: {
11
9
  overflow: "hidden",
12
10
  backgroundColor: colors.surface,
@@ -21,16 +19,94 @@ export const useMenuVariantDefault = (): MenuStyles => {
21
19
  },
22
20
  item: {
23
21
  default: {
22
+ flexDirection: "row",
23
+ alignItems: "center",
24
24
  paddingVertical: 12,
25
25
  paddingHorizontal: 16,
26
- fontFamily: fontFamily,
27
- fontSize: fontSize,
28
- color: colors.foreground,
26
+ gap: 8,
29
27
  },
30
28
  hovered: {
31
29
  backgroundColor: colors.muted,
32
30
  },
33
31
  },
32
+ itemText: {
33
+ fontFamily: fontFamily,
34
+ fontSize: fontSize,
35
+ color: colors.foreground,
36
+ },
37
+ itemIcon: {
38
+ color: colors.foreground,
39
+ size: fontSize * 1.25,
40
+ },
41
+ label: {
42
+ paddingVertical: 8,
43
+ paddingHorizontal: 16,
44
+ fontFamily: fontFamily,
45
+ fontSize: fontSize * 0.75,
46
+ fontWeight: "600",
47
+ color: colors.mutedForeground,
48
+ },
49
+ separator: {
50
+ height: 1,
51
+ backgroundColor: colors.border,
52
+ marginVertical: 4,
53
+ marginHorizontal: 8,
54
+ },
55
+ checkboxItem: {
56
+ default: {
57
+ flexDirection: "row",
58
+ alignItems: "center",
59
+ paddingVertical: 12,
60
+ paddingHorizontal: 16,
61
+ gap: 8,
62
+ },
63
+ hovered: {
64
+ backgroundColor: colors.muted,
65
+ },
66
+ disabled: {
67
+ opacity: 0.5,
68
+ },
69
+ },
70
+ checkboxIndicator: {
71
+ fontSize: fontSize * 0.75,
72
+ fontWeight: "bold",
73
+ color: colors.foreground,
74
+ width: fontSize,
75
+ textAlign: "center",
76
+ marginLeft: "auto",
77
+ },
78
+ radioItem: {
79
+ default: {
80
+ flexDirection: "row",
81
+ alignItems: "center",
82
+ paddingVertical: 12,
83
+ paddingHorizontal: 16,
84
+ gap: 8,
85
+ },
86
+ hovered: {
87
+ backgroundColor: colors.muted,
88
+ },
89
+ disabled: {
90
+ opacity: 0.5,
91
+ },
92
+ },
93
+ radioIndicator: {
94
+ default: {
95
+ width: 8,
96
+ height: 8,
97
+ borderRadius: 4,
98
+ backgroundColor: "transparent",
99
+ marginLeft: "auto",
100
+ },
101
+ selected: {
102
+ backgroundColor: colors.foreground,
103
+ },
104
+ },
105
+ shortcut: {
106
+ fontSize: fontSize * 0.75,
107
+ fontFamily: fontFamily,
108
+ color: colors.mutedForeground,
109
+ },
34
110
  }),
35
111
  );
36
112
  };
@@ -1,6 +1,10 @@
1
1
  import { useEffect, useSyncExternalStore } from "react";
2
2
  import { Platform, View } from "react-native";
3
- import { DEFAULT_PORTAL_HOST, type PortalHostProps, type PortalProps } from "./portal.constants";
3
+ import {
4
+ DEFAULT_PORTAL_HOST,
5
+ type PortalHostProps,
6
+ type PortalProps,
7
+ } from "./portal.constants";
4
8
 
5
9
  type PortalMap = Map<string, React.ReactNode>;
6
10
  type PortalHostMap = Map<string, PortalMap>;
@@ -11,7 +15,10 @@ type PortalStore = {
11
15
  };
12
16
 
13
17
  const store: PortalStore = {
14
- map: new Map<string, PortalMap>().set(DEFAULT_PORTAL_HOST, new Map<string, React.ReactNode>()),
18
+ map: new Map<string, PortalMap>().set(
19
+ DEFAULT_PORTAL_HOST,
20
+ new Map<string, React.ReactNode>(),
21
+ ),
15
22
  listeners: new Set(),
16
23
  };
17
24
 
@@ -30,7 +37,11 @@ function subscribe(cb: () => void) {
30
37
  };
31
38
  }
32
39
 
33
- function updatePortal(hostName: string, name: string, children: React.ReactNode) {
40
+ function updatePortal(
41
+ hostName: string,
42
+ name: string,
43
+ children: React.ReactNode,
44
+ ) {
34
45
  const next = new Map(store.map);
35
46
  const portal = next.get(hostName) ?? new Map<string, React.ReactNode>();
36
47
  portal.set(name, children);
@@ -48,7 +59,10 @@ function removePortal(hostName: string, name: string) {
48
59
  emit();
49
60
  }
50
61
 
51
- export function PortalHost({ name = DEFAULT_PORTAL_HOST, container }: PortalHostProps) {
62
+ export function PortalHost({
63
+ name = DEFAULT_PORTAL_HOST,
64
+ container,
65
+ }: PortalHostProps) {
52
66
  const map = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
53
67
  const portalMap = map.get(name) ?? new Map<string, React.ReactNode>();
54
68
  if (portalMap.size === 0) return null;
@@ -75,7 +89,11 @@ export function PortalHost({ name = DEFAULT_PORTAL_HOST, container }: PortalHost
75
89
  return <Container>{Array.from(portalMap.values())}</Container>;
76
90
  }
77
91
 
78
- export function Portal({ name, hostName = DEFAULT_PORTAL_HOST, children }: PortalProps) {
92
+ export function Portal({
93
+ name,
94
+ hostName = DEFAULT_PORTAL_HOST,
95
+ children,
96
+ }: PortalProps) {
79
97
  useEffect(() => {
80
98
  updatePortal(hostName, name, children);
81
99
  }, [hostName, name, children]);
@@ -7,7 +7,7 @@ import {
7
7
  } from "react";
8
8
  import { useColorScheme } from "react-native";
9
9
  import type { ToastVariants } from "../components/toast/variants";
10
- import type { SvgProps } from "../types/props.types";
10
+ import type { IconComponent } from "../types/element.types";
11
11
  import type { DeepPartial } from "../types/util.types";
12
12
  import { defaultThemeAssets } from "./default";
13
13
  import type {
@@ -34,9 +34,7 @@ interface ThemeContext {
34
34
 
35
35
  export interface ComponentsConfig {
36
36
  toast?: {
37
- icons?: Partial<
38
- Record<keyof typeof ToastVariants, (props: SvgProps) => React.ReactNode>
39
- >;
37
+ icons?: Partial<Record<keyof typeof ToastVariants, IconComponent>>;
40
38
  };
41
39
  }
42
40
 
@@ -1,4 +1,5 @@
1
1
  import type { HostInstance } from "react-native";
2
+ import type { SvgProps } from "./props.types";
2
3
 
3
4
  export type ViewRef = HostInstance;
4
5
  export type TextInputRef = HostInstance & {
@@ -10,5 +11,13 @@ export type TextInputRef = HostInstance & {
10
11
  setSelection: (start: number, end?: number) => void;
11
12
  };
12
13
 
13
- export type TextChildren = string | number | boolean | null | undefined | string[];
14
+ export type TextChildren =
15
+ | string
16
+ | number
17
+ | boolean
18
+ | null
19
+ | undefined
20
+ | string[];
14
21
  export type ElementChildren = React.ReactElement | React.ReactElement[];
22
+
23
+ export type IconComponent = (props: SvgProps) => React.ReactElement;
@@ -0,0 +1,12 @@
1
+ import React from "react";
2
+
3
+ export const getElementProp = <T extends React.ElementType>(
4
+ element: React.ReactNode,
5
+ propName: string,
6
+ ): React.ComponentProps<T>[string] | undefined => {
7
+ if (!React.isValidElement(element)) return undefined;
8
+ if (!element.props) return undefined;
9
+ if (typeof element.props !== "object") return undefined;
10
+ if (!(propName in element.props)) return undefined;
11
+ return (element.props as any)[propName];
12
+ };