@korsolutions/ui 0.0.19 → 0.0.20

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 (41) hide show
  1. package/dist/components/index.d.mts +42 -4
  2. package/dist/components/index.mjs +119 -8
  3. package/dist/{index-BLsiF42Z.d.mts → index-CGY0mO6z.d.mts} +172 -9
  4. package/dist/index.d.mts +3 -3
  5. package/dist/index.mjs +2 -2
  6. package/dist/primitives/index.d.mts +2 -2
  7. package/dist/primitives/index.mjs +2 -2
  8. package/dist/{primitives-CyDqzNcp.mjs → primitives-P_8clvQr.mjs} +433 -13
  9. package/dist/{toast-manager-BOORCQn8.mjs → toast-manager-DSo9oN8w.mjs} +1 -1
  10. package/package.json +1 -1
  11. package/src/components/button/button.tsx +7 -4
  12. package/src/components/dropdown-menu/dropdown-menu.tsx +49 -0
  13. package/src/components/dropdown-menu/variants/default.tsx +40 -0
  14. package/src/components/dropdown-menu/variants/index.ts +5 -0
  15. package/src/components/index.ts +2 -0
  16. package/src/components/popover/popover.tsx +51 -0
  17. package/src/components/popover/variants/default.tsx +26 -0
  18. package/src/components/popover/variants/index.ts +5 -0
  19. package/src/hooks/useRelativePosition.ts +188 -0
  20. package/src/primitives/button/button-root.tsx +2 -4
  21. package/src/primitives/dropdown-menu/context.ts +25 -0
  22. package/src/primitives/dropdown-menu/dropdown-menu-button.tsx +47 -0
  23. package/src/primitives/dropdown-menu/dropdown-menu-content.tsx +39 -0
  24. package/src/primitives/dropdown-menu/dropdown-menu-divider.tsx +18 -0
  25. package/src/primitives/dropdown-menu/dropdown-menu-overlay.tsx +29 -0
  26. package/src/primitives/dropdown-menu/dropdown-menu-portal.tsx +21 -0
  27. package/src/primitives/dropdown-menu/dropdown-menu-root.tsx +35 -0
  28. package/src/primitives/dropdown-menu/dropdown-menu-trigger.tsx +47 -0
  29. package/src/primitives/dropdown-menu/index.ts +26 -0
  30. package/src/primitives/dropdown-menu/types.ts +13 -0
  31. package/src/primitives/index.ts +2 -0
  32. package/src/primitives/popover/context.ts +25 -0
  33. package/src/primitives/popover/index.ts +24 -0
  34. package/src/primitives/popover/popover-close.tsx +29 -0
  35. package/src/primitives/popover/popover-content.tsx +39 -0
  36. package/src/primitives/popover/popover-overlay.tsx +37 -0
  37. package/src/primitives/popover/popover-portal.tsx +21 -0
  38. package/src/primitives/popover/popover-root.tsx +35 -0
  39. package/src/primitives/popover/popover-trigger.tsx +47 -0
  40. package/src/primitives/popover/types.ts +7 -0
  41. package/src/utils/get-ref-layout.ts +16 -0
@@ -0,0 +1,29 @@
1
+ import React from "react";
2
+ import { Pressable, PressableProps, StyleProp, ViewStyle } from "react-native";
3
+ import { usePopover } from "./context";
4
+
5
+ export interface PopoverCloseProps extends Omit<PressableProps, "onPress"> {
6
+ children?: React.ReactNode;
7
+
8
+ onPress?: () => void;
9
+
10
+ render?: (props: PopoverCloseProps) => React.ReactNode;
11
+
12
+ style?: StyleProp<ViewStyle>;
13
+ }
14
+
15
+ export function PopoverClose(props: PopoverCloseProps) {
16
+ const popover = usePopover();
17
+
18
+ const Component = props.render ?? Pressable;
19
+ return (
20
+ <Component
21
+ {...props}
22
+ onPress={() => {
23
+ popover.setIsOpen(false);
24
+ props.onPress?.();
25
+ }}
26
+ style={props.style}
27
+ />
28
+ );
29
+ }
@@ -0,0 +1,39 @@
1
+ import React from "react";
2
+ import { StyleProp, View, ViewStyle } from "react-native";
3
+ import { usePopover } from "./context";
4
+ import { useRelativePosition } from "@/hooks/useRelativePosition";
5
+
6
+ export interface PopoverContentProps {
7
+ children?: React.ReactNode;
8
+
9
+ render?: (props: PopoverContentProps) => React.ReactNode;
10
+
11
+ style?: StyleProp<ViewStyle>;
12
+ }
13
+
14
+ export function PopoverContent(props: PopoverContentProps) {
15
+ const popover = usePopover();
16
+
17
+ const positionStyle = useRelativePosition({
18
+ align: "start",
19
+ avoidCollisions: true,
20
+ triggerPosition: popover.triggerPosition,
21
+ contentLayout: popover.contentLayout,
22
+ alignOffset: 0,
23
+ side: "bottom",
24
+ sideOffset: 0,
25
+ });
26
+
27
+ const composedStyle = [positionStyle, popover.styles?.content, props.style];
28
+
29
+ const Component = props.render ?? View;
30
+ return (
31
+ <Component
32
+ {...props}
33
+ onLayout={(e) => {
34
+ popover.setContentLayout(e.nativeEvent.layout);
35
+ }}
36
+ style={composedStyle}
37
+ />
38
+ );
39
+ }
@@ -0,0 +1,37 @@
1
+ import React from "react";
2
+ import { usePopover } from "./context";
3
+ import { Pressable, PressableProps, StyleProp, StyleSheet, ViewStyle } from "react-native";
4
+
5
+ export interface PopoverOverlayProps extends Omit<PressableProps, "onPress"> {
6
+ children?: React.ReactNode;
7
+
8
+ onPress?: () => void;
9
+ closeOnPress?: boolean;
10
+
11
+ render?: (props: PopoverOverlayProps) => React.ReactElement;
12
+
13
+ style?: StyleProp<ViewStyle>;
14
+ }
15
+
16
+ export function PopoverOverlay(props: PopoverOverlayProps) {
17
+ const { closeOnPress = true, ...restProps } = props;
18
+ const popover = usePopover();
19
+
20
+ const composedStyle = [StyleSheet.absoluteFill, popover.styles?.overlay, props.style];
21
+
22
+ const Component = props.render ?? Pressable;
23
+ return (
24
+ <Component
25
+ {...restProps}
26
+ onPress={() => {
27
+ if (closeOnPress) {
28
+ popover.setIsOpen(false);
29
+ }
30
+ props.onPress?.();
31
+ }}
32
+ style={composedStyle}
33
+ >
34
+ {props.children}
35
+ </Component>
36
+ );
37
+ }
@@ -0,0 +1,21 @@
1
+ import React from "react";
2
+ import { Portal } from "../portal";
3
+ import { usePopover, PopoverContext } from "./context";
4
+
5
+ export interface PopoverPortalProps {
6
+ children?: React.ReactNode;
7
+ }
8
+
9
+ export function PopoverPortal(props: PopoverPortalProps) {
10
+ const popover = usePopover();
11
+
12
+ if (!popover.isOpen) {
13
+ return null;
14
+ }
15
+
16
+ return (
17
+ <Portal name="popover-portal">
18
+ <PopoverContext.Provider value={popover}>{props.children}</PopoverContext.Provider>
19
+ </Portal>
20
+ );
21
+ }
@@ -0,0 +1,35 @@
1
+ import React, { useState } from "react";
2
+ import { PopoverContext } from "./context";
3
+ import { PopoverStyles } from "./types";
4
+ import { LayoutRectangle } from "react-native";
5
+ import { DEFAULT_LAYOUT, DEFAULT_POSITION, LayoutPosition } from "@/hooks/useRelativePosition";
6
+
7
+ export interface PopoverRootProps {
8
+ children?: React.ReactNode;
9
+
10
+ render?: (props: PopoverRootProps) => React.ReactNode;
11
+
12
+ styles?: PopoverStyles;
13
+ }
14
+
15
+ export function PopoverRoot(props: PopoverRootProps) {
16
+ const [isOpen, setIsOpen] = useState(false);
17
+ const [contentLayout, setContentLayout] = useState<LayoutRectangle>(DEFAULT_LAYOUT);
18
+ const [triggerPosition, setTriggerPosition] = useState<LayoutPosition>(DEFAULT_POSITION);
19
+
20
+ return (
21
+ <PopoverContext.Provider
22
+ value={{
23
+ isOpen,
24
+ setIsOpen,
25
+ contentLayout,
26
+ setContentLayout,
27
+ triggerPosition,
28
+ setTriggerPosition,
29
+ styles: props.styles,
30
+ }}
31
+ >
32
+ {props.children}
33
+ </PopoverContext.Provider>
34
+ );
35
+ }
@@ -0,0 +1,47 @@
1
+ import React, { forwardRef, RefAttributes, useImperativeHandle, useRef } from "react";
2
+ import { PressableProps, View } from "react-native";
3
+ import { usePopover } from "./context";
4
+
5
+ export interface PopoverTriggerProps extends PressableProps {
6
+ children: React.ReactElement<RefAttributes<View> & PressableProps>;
7
+ }
8
+
9
+ export interface PopoverTriggerRef {
10
+ open: () => void;
11
+ close: () => void;
12
+ }
13
+
14
+ export const PopoverTrigger = forwardRef<PopoverTriggerRef, PopoverTriggerProps>((props, ref) => {
15
+ const popover = usePopover();
16
+ const triggerRef = useRef<View>(null);
17
+
18
+ const onTriggerPress = async () => {
19
+ triggerRef.current?.measure((_x, _y, width, height, pageX, pageY) => {
20
+ popover.setTriggerPosition({
21
+ height,
22
+ width,
23
+ pageX,
24
+ pageY,
25
+ });
26
+ });
27
+
28
+ popover.setIsOpen((prev) => !prev);
29
+ };
30
+
31
+ useImperativeHandle(ref, () => ({
32
+ open: () => popover.setIsOpen(true),
33
+ close: () => popover.setIsOpen(false),
34
+ }));
35
+
36
+ return React.cloneElement(props.children, {
37
+ ref: triggerRef,
38
+ onPress: onTriggerPress,
39
+ role: "button",
40
+ accessible: true,
41
+ accessibilityRole: "button",
42
+ accessibilityState: { expanded: popover.isOpen },
43
+ ...props.children.props,
44
+ });
45
+ });
46
+
47
+ PopoverTrigger.displayName = "PopoverTrigger";
@@ -0,0 +1,7 @@
1
+ import { PopoverContentProps } from "./popover-content";
2
+ import { PopoverOverlayProps } from "./popover-overlay";
3
+
4
+ export interface PopoverStyles {
5
+ overlay?: PopoverOverlayProps["style"];
6
+ content?: PopoverContentProps["style"];
7
+ }
@@ -0,0 +1,16 @@
1
+ import { LayoutRectangle, View } from "react-native";
2
+
3
+ const DEFAULT_LAYOUT: LayoutRectangle = { x: 0, y: 0, width: 0, height: 0 };
4
+
5
+ export const getRefLayout = async (ref: React.RefObject<View | null>) => {
6
+ return new Promise<LayoutRectangle>((resolve) => {
7
+ if (!ref?.current?.["measureInWindow"]) {
8
+ console.warn("getRefLayout.NoRef");
9
+ resolve(DEFAULT_LAYOUT);
10
+ return;
11
+ }
12
+ ref.current.measureInWindow((x, y, width, height) => {
13
+ resolve({ x, y, width, height });
14
+ });
15
+ });
16
+ };