@korsolutions/ui 0.0.57 → 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.
- package/dist/module/components/icon/icon.js +3 -3
- package/dist/module/components/icon/icon.js.map +1 -1
- package/dist/module/components/menu/components/menu-checkbox-item.js +51 -0
- package/dist/module/components/menu/components/menu-checkbox-item.js.map +1 -0
- package/dist/module/components/menu/components/menu-content.js +1 -1
- package/dist/module/components/menu/components/menu-content.js.map +1 -1
- package/dist/module/components/menu/components/menu-group.js +18 -0
- package/dist/module/components/menu/components/menu-group.js.map +1 -0
- package/dist/module/components/menu/components/menu-item.js +14 -6
- package/dist/module/components/menu/components/menu-item.js.map +1 -1
- package/dist/module/components/menu/components/menu-label.js +17 -0
- package/dist/module/components/menu/components/menu-label.js.map +1 -0
- package/dist/module/components/menu/components/menu-overlay.js +1 -1
- package/dist/module/components/menu/components/menu-overlay.js.map +1 -1
- package/dist/module/components/menu/components/menu-radio-group.js +24 -0
- package/dist/module/components/menu/components/menu-radio-group.js.map +1 -0
- package/dist/module/components/menu/components/menu-radio-item.js +54 -0
- package/dist/module/components/menu/components/menu-radio-item.js.map +1 -0
- package/dist/module/components/menu/components/menu-separator.js +17 -0
- package/dist/module/components/menu/components/menu-separator.js.map +1 -0
- package/dist/module/components/menu/components/menu-shortcut.js +17 -0
- package/dist/module/components/menu/components/menu-shortcut.js.map +1 -0
- package/dist/module/components/menu/context.js +8 -0
- package/dist/module/components/menu/context.js.map +1 -1
- package/dist/module/components/menu/index.js +15 -1
- package/dist/module/components/menu/index.js.map +1 -1
- package/dist/module/components/menu/use-organized-children.js +39 -0
- package/dist/module/components/menu/use-organized-children.js.map +1 -0
- package/dist/module/components/menu/variants/default.js +82 -6
- package/dist/module/components/menu/variants/default.js.map +1 -1
- package/dist/module/components/portal/portal.js.map +1 -1
- package/dist/module/components/touchable/touchable.js +1 -1
- package/dist/module/components/touchable/touchable.js.map +1 -1
- package/dist/module/components/touchable/variants/default.js +4 -2
- package/dist/module/components/touchable/variants/default.js.map +1 -1
- package/dist/module/themes/provider.js.map +1 -1
- package/dist/module/utils/element-utils.js +11 -0
- package/dist/module/utils/element-utils.js.map +1 -0
- package/dist/typescript/src/components/icon/icon.d.ts +2 -2
- package/dist/typescript/src/components/icon/icon.d.ts.map +1 -1
- package/dist/typescript/src/components/menu/components/menu-checkbox-item.d.ts +13 -0
- package/dist/typescript/src/components/menu/components/menu-checkbox-item.d.ts.map +1 -0
- package/dist/typescript/src/components/menu/components/menu-content.d.ts.map +1 -1
- package/dist/typescript/src/components/menu/components/menu-group.d.ts +9 -0
- package/dist/typescript/src/components/menu/components/menu-group.d.ts.map +1 -0
- package/dist/typescript/src/components/menu/components/menu-item.d.ts +3 -3
- package/dist/typescript/src/components/menu/components/menu-item.d.ts.map +1 -1
- package/dist/typescript/src/components/menu/components/menu-label.d.ts +9 -0
- package/dist/typescript/src/components/menu/components/menu-label.d.ts.map +1 -0
- package/dist/typescript/src/components/menu/components/menu-overlay.d.ts.map +1 -1
- package/dist/typescript/src/components/menu/components/menu-radio-group.d.ts +10 -0
- package/dist/typescript/src/components/menu/components/menu-radio-group.d.ts.map +1 -0
- package/dist/typescript/src/components/menu/components/menu-radio-item.d.ts +12 -0
- package/dist/typescript/src/components/menu/components/menu-radio-item.d.ts.map +1 -0
- package/dist/typescript/src/components/menu/components/menu-separator.d.ts +8 -0
- package/dist/typescript/src/components/menu/components/menu-separator.d.ts.map +1 -0
- package/dist/typescript/src/components/menu/components/menu-shortcut.d.ts +9 -0
- package/dist/typescript/src/components/menu/components/menu-shortcut.d.ts.map +1 -0
- package/dist/typescript/src/components/menu/context.d.ts +7 -1
- package/dist/typescript/src/components/menu/context.d.ts.map +1 -1
- package/dist/typescript/src/components/menu/index.d.ts +21 -0
- package/dist/typescript/src/components/menu/index.d.ts.map +1 -1
- package/dist/typescript/src/components/menu/types.d.ts +17 -2
- package/dist/typescript/src/components/menu/types.d.ts.map +1 -1
- package/dist/typescript/src/components/menu/use-organized-children.d.ts +3 -0
- package/dist/typescript/src/components/menu/use-organized-children.d.ts.map +1 -0
- package/dist/typescript/src/components/menu/variants/default.d.ts.map +1 -1
- package/dist/typescript/src/components/portal/portal.d.ts +2 -2
- package/dist/typescript/src/components/portal/portal.d.ts.map +1 -1
- package/dist/typescript/src/components/touchable/touchable.d.ts +2 -3
- package/dist/typescript/src/components/touchable/touchable.d.ts.map +1 -1
- package/dist/typescript/src/components/touchable/variants/default.d.ts.map +1 -1
- package/dist/typescript/src/themes/provider.d.ts +2 -2
- package/dist/typescript/src/themes/provider.d.ts.map +1 -1
- package/dist/typescript/src/types/element.types.d.ts +2 -0
- package/dist/typescript/src/types/element.types.d.ts.map +1 -1
- package/dist/typescript/src/utils/element-utils.d.ts +3 -0
- package/dist/typescript/src/utils/element-utils.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/components/icon/icon.tsx +9 -3
- package/src/components/menu/components/menu-checkbox-item.tsx +74 -0
- package/src/components/menu/components/menu-content.tsx +2 -1
- package/src/components/menu/components/menu-group.tsx +25 -0
- package/src/components/menu/components/menu-item.tsx +25 -9
- package/src/components/menu/components/menu-label.tsx +21 -0
- package/src/components/menu/components/menu-overlay.tsx +11 -2
- package/src/components/menu/components/menu-radio-group.tsx +35 -0
- package/src/components/menu/components/menu-radio-item.tsx +80 -0
- package/src/components/menu/components/menu-separator.tsx +22 -0
- package/src/components/menu/components/menu-shortcut.tsx +21 -0
- package/src/components/menu/context.ts +18 -1
- package/src/components/menu/index.ts +21 -0
- package/src/components/menu/types.ts +25 -2
- package/src/components/menu/use-organized-children.tsx +38 -0
- package/src/components/menu/variants/default.tsx +82 -6
- package/src/components/portal/portal.tsx +23 -5
- package/src/components/touchable/touchable.tsx +3 -9
- package/src/components/touchable/variants/default.tsx +2 -0
- package/src/themes/provider.tsx +2 -4
- package/src/types/element.types.ts +10 -1
- package/src/utils/element-utils.ts +12 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View, type StyleProp, type ViewStyle } from "react-native";
|
|
3
|
+
import { useMenu } from "../context";
|
|
4
|
+
|
|
5
|
+
export interface MenuGroupProps {
|
|
6
|
+
children?: React.ReactNode;
|
|
7
|
+
render?: (props: MenuGroupProps) => React.ReactNode;
|
|
8
|
+
style?: StyleProp<ViewStyle>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function MenuGroup(props: MenuGroupProps) {
|
|
12
|
+
const menu = useMenu();
|
|
13
|
+
const composedStyle = [menu.styles?.group, props.style];
|
|
14
|
+
|
|
15
|
+
const Component = props.render ?? View;
|
|
16
|
+
return (
|
|
17
|
+
<Component
|
|
18
|
+
{...props}
|
|
19
|
+
role="group"
|
|
20
|
+
style={composedStyle}
|
|
21
|
+
>
|
|
22
|
+
{props.children}
|
|
23
|
+
</Component>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
|
-
import { type StyleProp,
|
|
2
|
+
import { Pressable, type StyleProp, type ViewStyle } from "react-native";
|
|
3
3
|
import { useMenu } from "../context";
|
|
4
4
|
import type { MenuButtonState } from "../types";
|
|
5
|
+
import { useOrganizedChildren } from "../use-organized-children";
|
|
5
6
|
|
|
6
7
|
export interface MenuItemProps {
|
|
7
|
-
children:
|
|
8
|
+
children: React.ReactNode;
|
|
8
9
|
onPress?: () => void;
|
|
9
10
|
|
|
10
11
|
render?: (props: MenuItemProps) => React.ReactNode;
|
|
11
|
-
style?: StyleProp<
|
|
12
|
+
style?: StyleProp<ViewStyle>;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
const calculateState = (isHovered: boolean): MenuButtonState => {
|
|
@@ -22,23 +23,38 @@ export function MenuItem(props: MenuItemProps) {
|
|
|
22
23
|
const menu = useMenu();
|
|
23
24
|
const [isHovered, setIsHovered] = useState(false);
|
|
24
25
|
const state = calculateState(isHovered);
|
|
25
|
-
const composedStyle = [
|
|
26
|
+
const composedStyle = [
|
|
27
|
+
menu.styles?.item?.default,
|
|
28
|
+
menu.styles?.item?.[state],
|
|
29
|
+
props.style,
|
|
30
|
+
];
|
|
26
31
|
|
|
27
32
|
const handlePress = () => {
|
|
28
33
|
props.onPress?.();
|
|
29
34
|
menu.setIsOpen((prev) => !prev);
|
|
30
35
|
};
|
|
31
36
|
|
|
32
|
-
const
|
|
37
|
+
const organizedChildren = useOrganizedChildren(props.children);
|
|
38
|
+
|
|
39
|
+
if (props.render) {
|
|
40
|
+
return (
|
|
41
|
+
<>
|
|
42
|
+
{props.render({
|
|
43
|
+
...props,
|
|
44
|
+
children: organizedChildren,
|
|
45
|
+
})}
|
|
46
|
+
</>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
33
50
|
return (
|
|
34
|
-
<
|
|
35
|
-
{...props}
|
|
51
|
+
<Pressable
|
|
36
52
|
onPress={handlePress}
|
|
37
53
|
onPointerEnter={() => setIsHovered(true)}
|
|
38
54
|
onPointerLeave={() => setIsHovered(false)}
|
|
39
55
|
style={composedStyle}
|
|
40
56
|
>
|
|
41
|
-
{
|
|
42
|
-
</
|
|
57
|
+
{organizedChildren}
|
|
58
|
+
</Pressable>
|
|
43
59
|
);
|
|
44
60
|
}
|
|
@@ -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 {
|
|
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 =
|
|
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,
|
|
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
|
-
|
|
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 {
|
|
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(
|
|
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(
|
|
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({
|
|
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({
|
|
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]);
|
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
|
-
import {
|
|
3
|
-
Pressable,
|
|
4
|
-
type PressableProps,
|
|
5
|
-
type PressableStateCallbackType,
|
|
6
|
-
type ViewStyle,
|
|
7
|
-
} from "react-native";
|
|
2
|
+
import { Pressable, type PressableProps } from "react-native";
|
|
8
3
|
import type { PressableState } from "./types";
|
|
9
4
|
import { PressableVariants } from "./variants";
|
|
10
5
|
|
|
11
|
-
type ExtendablePressableProps = Omit<PressableProps, "
|
|
6
|
+
type ExtendablePressableProps = Omit<PressableProps, "disabled">;
|
|
12
7
|
|
|
13
8
|
export interface TouchableProps extends ExtendablePressableProps {
|
|
14
9
|
variant?: keyof typeof PressableVariants;
|
|
15
10
|
children?: React.ReactNode;
|
|
16
11
|
isDisabled?: boolean;
|
|
17
|
-
style?: (state: PressableStateCallbackType) => ViewStyle;
|
|
18
12
|
}
|
|
19
13
|
|
|
20
14
|
const calculateState = (
|
|
@@ -61,7 +55,7 @@ export function Touchable(props: TouchableProps) {
|
|
|
61
55
|
return [
|
|
62
56
|
variantStyles?.default,
|
|
63
57
|
variantStyles?.[currentState],
|
|
64
|
-
style ? style(styleState) :
|
|
58
|
+
typeof style === "function" ? style(styleState) : style,
|
|
65
59
|
];
|
|
66
60
|
}}
|
|
67
61
|
>
|
|
@@ -4,6 +4,7 @@ export const usePressableVariantDefault = (): PressableStyles => {
|
|
|
4
4
|
return {
|
|
5
5
|
default: {
|
|
6
6
|
cursor: "pointer",
|
|
7
|
+
pointerEvents: "box-only",
|
|
7
8
|
},
|
|
8
9
|
pressed: {
|
|
9
10
|
opacity: 0.8,
|
|
@@ -14,6 +15,7 @@ export const usePressableVariantDefault = (): PressableStyles => {
|
|
|
14
15
|
},
|
|
15
16
|
disabled: {
|
|
16
17
|
cursor: "not-allowed" as any,
|
|
18
|
+
pointerEvents: "none",
|
|
17
19
|
},
|
|
18
20
|
};
|
|
19
21
|
};
|
package/src/themes/provider.tsx
CHANGED
|
@@ -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 {
|
|
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
|
|