@hexdspace/react 0.1.29 → 0.1.30

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/index.d.ts CHANGED
@@ -6,6 +6,7 @@ import React__default, { CSSProperties, Dispatch, ReactNode } from 'react';
6
6
  import * as react_jsx_runtime from 'react/jsx-runtime';
7
7
  import * as class_variance_authority_types from 'class-variance-authority/types';
8
8
  import { VariantProps } from 'class-variance-authority';
9
+ import { DropdownMenu as DropdownMenu$1 } from 'radix-ui';
9
10
 
10
11
  type NotificationVariant = 'success' | 'warning' | 'error' | 'info';
11
12
  declare const DEFAULT_NOTIFICATION_CHANNEL = "app.notifications";
@@ -666,4 +667,48 @@ interface PopoverProps extends VariantProps<typeof popoverContentVariants> {
666
667
  }
667
668
  declare function Popover({ trigger, children, open, defaultOpen, onOpenChange, modal, side, align, sideOffset, alignOffset, collisionPadding, withArrow, arrowSizePx, arrowClassName, portalContainer, ariaLabel, ariaLabelledBy, chrome, className, style, }: PopoverProps): react_jsx_runtime.JSX.Element;
668
669
 
669
- export { AuthController, AuthControllerCtx, type AuthControllerDeps, AuthControllerProvider, AuthDispatchCtx, AuthProvider, type AuthState, AuthStateCtx, Button, type ButtonProps, type CacheInstruction, ConfirmDialog, type ConfirmDialogProps, type ConfirmPayload, CustomDialog, type CustomDialogPayload, type CustomDialogProps, type CustomInstruction, DEFAULT_NOTIFICATION_CHANNEL, type DefaultDialogPayloads, type DefaultDialogTemplateKey, DefaultDialogTemplateKeys, Dialog, DialogContent, DialogController, DialogHost, type DialogInstance, type DialogOptions, type DialogProps, type DialogTemplate, type ErrorResponse, Field, type FieldProps, type GenericResponse, type HttpClient, HttpError, type HttpMethod, type HttpResponse, InfoDialog, type InfoDialogProps, type InfoPayload, Input, type InputProps, type Instruction, type InstructionContext, MockAuthHttpClient, MockHttpClient, type MutationFn, type Notification, type NotificationAction, NotificationHost, type NotificationInstruction, type NotificationVariant, NotifierController, type OnSuccessFn, type OptimisticSnapshot, type OptimisticUpdateFn, Popover, type PopoverProps, RedirectIfAuthed, type RedirectIfAuthedProps, type RequestConfig, RequireAuth, type RequireAuthProps, type ResolvedToastTheme, type ResponsiveMutation, Skeleton, type SkeletonProps, Textarea, type TextareaProps, type ToastActionTheme, type ToastTheme, type ToastTransition, type ToastifyCSSVars, Tooltip, type UIFail, type UIOk, type UIResult, type User, authController, controllerFactory, createAuthController, dialogController, httpClient as fetchHttpClient, getDialogTemplate, notifierController, registerDefaultDialogs, registerDialogTemplate, resolveToastTheme, ui, unregisterDialogTemplate, useAuth, useAuthActions, useAuthController, useAuthDispatch, useAuthedUser, useDialog, useResponsiveMutation };
670
+ declare const dropdownMenuContentVariants: (props?: ({
671
+ chrome?: "flat" | "glass" | "glow" | null | undefined;
672
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
673
+ type DropdownMenuContentControlledProps = 'asChild' | 'children' | 'side' | 'align' | 'sideOffset' | 'alignOffset' | 'collisionPadding' | 'className' | 'style' | 'aria-label' | 'aria-labelledby';
674
+ type DropdownMenuContentProps = Omit<React.ComponentPropsWithoutRef<typeof DropdownMenu$1.Content>, DropdownMenuContentControlledProps> & {
675
+ className?: string;
676
+ style?: React.CSSProperties;
677
+ ref?: React.Ref<HTMLDivElement>;
678
+ };
679
+ declare const dropdownMenuItemVariants: (props?: ({
680
+ inset?: boolean | null | undefined;
681
+ destructive?: boolean | null | undefined;
682
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
683
+ interface DropdownMenuProps extends VariantProps<typeof dropdownMenuContentVariants> {
684
+ trigger: React.ReactElement;
685
+ children: React.ReactNode;
686
+ open?: boolean;
687
+ defaultOpen?: boolean;
688
+ onOpenChange?: (open: boolean) => void;
689
+ hoverOpen?: boolean;
690
+ hoverCloseDelayMs?: number;
691
+ modal?: boolean;
692
+ side?: 'top' | 'right' | 'bottom' | 'left';
693
+ align?: 'start' | 'center' | 'end';
694
+ sideOffset?: number;
695
+ alignOffset?: number;
696
+ collisionPadding?: number;
697
+ portalContainer?: Element | null;
698
+ ariaLabel?: string;
699
+ ariaLabelledBy?: string;
700
+ className?: string;
701
+ style?: React.CSSProperties;
702
+ contentProps?: DropdownMenuContentProps;
703
+ }
704
+ declare function DropdownMenu({ trigger, children, open, defaultOpen, onOpenChange, hoverOpen, hoverCloseDelayMs, modal, side, align, sideOffset, alignOffset, collisionPadding, portalContainer, ariaLabel, ariaLabelledBy, chrome, className, style, contentProps, }: DropdownMenuProps): react_jsx_runtime.JSX.Element;
705
+ interface DropdownMenuItemProps extends React.ComponentPropsWithoutRef<typeof DropdownMenu$1.Item>, VariantProps<typeof dropdownMenuItemVariants> {
706
+ }
707
+ declare const DropdownMenuItem: React.ForwardRefExoticComponent<DropdownMenuItemProps & React.RefAttributes<HTMLDivElement>>;
708
+ declare const DropdownMenuSeparator: React.ForwardRefExoticComponent<Omit<DropdownMenu$1.DropdownMenuSeparatorProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
709
+
710
+ interface KbdProps extends React.HTMLAttributes<HTMLSpanElement> {
711
+ }
712
+ declare const Kbd: React.ForwardRefExoticComponent<KbdProps & React.RefAttributes<HTMLSpanElement>>;
713
+
714
+ export { AuthController, AuthControllerCtx, type AuthControllerDeps, AuthControllerProvider, AuthDispatchCtx, AuthProvider, type AuthState, AuthStateCtx, Button, type ButtonProps, type CacheInstruction, ConfirmDialog, type ConfirmDialogProps, type ConfirmPayload, CustomDialog, type CustomDialogPayload, type CustomDialogProps, type CustomInstruction, DEFAULT_NOTIFICATION_CHANNEL, type DefaultDialogPayloads, type DefaultDialogTemplateKey, DefaultDialogTemplateKeys, Dialog, DialogContent, DialogController, DialogHost, type DialogInstance, type DialogOptions, type DialogProps, type DialogTemplate, DropdownMenu, DropdownMenuItem, type DropdownMenuItemProps, type DropdownMenuProps, DropdownMenuSeparator, type ErrorResponse, Field, type FieldProps, type GenericResponse, type HttpClient, HttpError, type HttpMethod, type HttpResponse, InfoDialog, type InfoDialogProps, type InfoPayload, Input, type InputProps, type Instruction, type InstructionContext, Kbd, type KbdProps, MockAuthHttpClient, MockHttpClient, type MutationFn, type Notification, type NotificationAction, NotificationHost, type NotificationInstruction, type NotificationVariant, NotifierController, type OnSuccessFn, type OptimisticSnapshot, type OptimisticUpdateFn, Popover, type PopoverProps, RedirectIfAuthed, type RedirectIfAuthedProps, type RequestConfig, RequireAuth, type RequireAuthProps, type ResolvedToastTheme, type ResponsiveMutation, Skeleton, type SkeletonProps, Textarea, type TextareaProps, type ToastActionTheme, type ToastTheme, type ToastTransition, type ToastifyCSSVars, Tooltip, type UIFail, type UIOk, type UIResult, type User, authController, controllerFactory, createAuthController, dialogController, httpClient as fetchHttpClient, getDialogTemplate, notifierController, registerDefaultDialogs, registerDialogTemplate, resolveToastTheme, ui, unregisterDialogTemplate, useAuth, useAuthActions, useAuthController, useAuthDispatch, useAuthedUser, useDialog, useResponsiveMutation };
package/dist/index.js CHANGED
@@ -2735,6 +2735,315 @@ function Popover({
2735
2735
  ) : null }) })
2736
2736
  ] });
2737
2737
  }
2738
+
2739
+ // src/ui/components/DropdownMenu.tsx
2740
+ import { cva as cva7 } from "class-variance-authority";
2741
+ import { AnimatePresence as AnimatePresence4, motion as motion4 } from "motion/react";
2742
+ import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui";
2743
+ import * as React12 from "react";
2744
+ import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
2745
+ var dropdownMenuContentVariants = cva7(
2746
+ cn(
2747
+ "min-w-[14rem] p-2 text-sm z-5000",
2748
+ "text-[color:var(--text-1)]",
2749
+ "shadow-[var(--shadow-popover)]",
2750
+ "overflow-hidden",
2751
+ "origin-center",
2752
+ "[--menu-x:0px] [--menu-y:6px]",
2753
+ "data-[side=bottom]:[--menu-y:-6px]",
2754
+ "data-[side=left]:[--menu-x:6px] data-[side=left]:[--menu-y:0px]",
2755
+ "data-[side=right]:[--menu-x:-6px] data-[side=right]:[--menu-y:0px]"
2756
+ ),
2757
+ {
2758
+ variants: {
2759
+ chrome: {
2760
+ flat: cn(
2761
+ "rounded-[var(--radius-card)]",
2762
+ "border border-[color:color-mix(in_oklab,var(--border),transparent_55%)]",
2763
+ "bg-[color:var(--surface-2)]"
2764
+ ),
2765
+ glass: cn(
2766
+ "rounded-[var(--radius-card)]",
2767
+ "border border-[color:color-mix(in_oklab,var(--border),transparent_55%)]",
2768
+ "bg-[color:color-mix(in_oklab,var(--surface-2),transparent_25%)]",
2769
+ "backdrop-blur-[6px]",
2770
+ "shadow-[var(--shadow-popover)]"
2771
+ ),
2772
+ glow: cn(
2773
+ "rounded-[var(--radius-card)]",
2774
+ "border border-[color:color-mix(in_oklab,var(--border),transparent_60%)]",
2775
+ "bg-[color:var(--surface-2)]",
2776
+ "shadow-[0_0_0_1px_color-mix(in_oklab,var(--border),transparent_55%),0_12px_28px_color-mix(in_oklab,var(--border),transparent_65%)]"
2777
+ )
2778
+ }
2779
+ },
2780
+ defaultVariants: {
2781
+ chrome: "flat"
2782
+ }
2783
+ }
2784
+ );
2785
+ var dropdownMenuItemVariants = cva7(
2786
+ cn(
2787
+ "state-layer",
2788
+ "flex items-center gap-3 rounded-[calc(var(--radius-card)-8px)] px-3.5 py-2.5 text-sm outline-none",
2789
+ "text-[color:var(--text-1)] cursor-pointer",
2790
+ "data-[highlighted]:bg-[color:var(--surface-hover)] data-[highlighted]:text-[color:var(--text-1)]",
2791
+ "data-[highlighted]:shadow-[0_0_0_1px_color-mix(in_oklab,var(--border),transparent_55%)]",
2792
+ "data-[highlighted]:translate-x-[1px]",
2793
+ "active:bg-[color:var(--surface-active)]",
2794
+ "data-[disabled]:text-[color:var(--disabled-fg)] data-[disabled]:pointer-events-none",
2795
+ "data-[disabled]:cursor-not-allowed",
2796
+ "transition-[background-color,color,box-shadow,transform] duration-[var(--motion-fast)] ease-out"
2797
+ ),
2798
+ {
2799
+ variants: {
2800
+ inset: { true: "pl-8" },
2801
+ destructive: {
2802
+ true: cn(
2803
+ "text-[color:var(--danger)]",
2804
+ "data-[highlighted]:text-[color:var(--danger)]",
2805
+ "data-[highlighted]:bg-[color:color-mix(in_oklab,var(--danger),transparent_85%)]"
2806
+ )
2807
+ }
2808
+ }
2809
+ }
2810
+ );
2811
+ function useWindowTimeout() {
2812
+ const timeoutRef = React12.useRef(null);
2813
+ const clear = React12.useCallback(() => {
2814
+ if (timeoutRef.current !== null) {
2815
+ window.clearTimeout(timeoutRef.current);
2816
+ timeoutRef.current = null;
2817
+ }
2818
+ }, []);
2819
+ const set = React12.useCallback(
2820
+ (fn, ms) => {
2821
+ clear();
2822
+ timeoutRef.current = window.setTimeout(() => {
2823
+ timeoutRef.current = null;
2824
+ fn();
2825
+ }, ms);
2826
+ },
2827
+ [clear]
2828
+ );
2829
+ React12.useEffect(() => clear, [clear]);
2830
+ return { set, clear };
2831
+ }
2832
+ function DropdownMenu({
2833
+ trigger,
2834
+ children,
2835
+ open: open2,
2836
+ defaultOpen,
2837
+ onOpenChange,
2838
+ hoverOpen = false,
2839
+ hoverCloseDelayMs = 50,
2840
+ modal = false,
2841
+ side = "bottom",
2842
+ align = "start",
2843
+ sideOffset = 8,
2844
+ alignOffset = 0,
2845
+ collisionPadding = 8,
2846
+ portalContainer,
2847
+ ariaLabel = "Menu",
2848
+ ariaLabelledBy,
2849
+ chrome,
2850
+ className,
2851
+ style,
2852
+ contentProps
2853
+ }) {
2854
+ const [uncontrolledOpen, setUncontrolledOpen] = React12.useState(defaultOpen ?? false);
2855
+ const isControlled = open2 !== void 0;
2856
+ const resolvedOpen = isControlled ? open2 : uncontrolledOpen;
2857
+ const motionDuration = useMotionDuration("--motion-fast", 0.14);
2858
+ const suppressDurationMs = Math.max(hoverCloseDelayMs, Math.round(motionDuration * 1e3) + 40);
2859
+ const closeTimer = useWindowTimeout();
2860
+ const triggerRef = React12.useRef(null);
2861
+ const contentRef = React12.useRef(null);
2862
+ const openedByHoverRef = React12.useRef(false);
2863
+ const suppressUntilRef = React12.useRef(0);
2864
+ const lastInputRef = React12.useRef("unknown");
2865
+ const isSuppressed = React12.useCallback(() => {
2866
+ if (!hoverOpen) return false;
2867
+ return Date.now() < suppressUntilRef.current;
2868
+ }, [hoverOpen]);
2869
+ const suppressReopen = React12.useCallback(() => {
2870
+ if (!hoverOpen) return;
2871
+ suppressUntilRef.current = Date.now() + suppressDurationMs;
2872
+ }, [hoverOpen, suppressDurationMs]);
2873
+ const handleOpenChange = React12.useCallback(
2874
+ (nextOpen) => {
2875
+ closeTimer.clear();
2876
+ if (!nextOpen && hoverOpen && openedByHoverRef.current) {
2877
+ suppressReopen();
2878
+ openedByHoverRef.current = false;
2879
+ }
2880
+ if (!isControlled) setUncontrolledOpen(nextOpen);
2881
+ onOpenChange?.(nextOpen);
2882
+ },
2883
+ [closeTimer, hoverOpen, isControlled, onOpenChange, suppressReopen]
2884
+ );
2885
+ const isTargetInside = React12.useCallback((target) => {
2886
+ if (!(target instanceof Node)) return false;
2887
+ return Boolean(triggerRef.current?.contains(target) || contentRef.current?.contains(target));
2888
+ }, []);
2889
+ const scheduleClose = React12.useCallback(() => {
2890
+ if (!hoverOpen) return;
2891
+ closeTimer.set(() => handleOpenChange(false), hoverCloseDelayMs);
2892
+ }, [closeTimer, handleOpenChange, hoverCloseDelayMs, hoverOpen]);
2893
+ const setRef = (ref, node) => {
2894
+ if (typeof ref === "function") {
2895
+ ref(node);
2896
+ } else if (ref && typeof ref === "object") {
2897
+ ref.current = node;
2898
+ }
2899
+ };
2900
+ const triggerElement = trigger;
2901
+ const triggerProps = triggerElement.props;
2902
+ const resolvedTrigger = hoverOpen ? React12.cloneElement(triggerElement, {
2903
+ ref: (node) => {
2904
+ triggerRef.current = node;
2905
+ setRef(triggerProps.ref, node);
2906
+ },
2907
+ onPointerEnter: (event) => {
2908
+ triggerProps.onPointerEnter?.(event);
2909
+ if (isSuppressed()) return;
2910
+ openedByHoverRef.current = true;
2911
+ lastInputRef.current = "pointer";
2912
+ suppressUntilRef.current = 0;
2913
+ closeTimer.clear();
2914
+ handleOpenChange(true);
2915
+ },
2916
+ onPointerDown: (event) => {
2917
+ triggerProps.onPointerDown?.(event);
2918
+ lastInputRef.current = "pointer";
2919
+ },
2920
+ onPointerLeave: (event) => {
2921
+ triggerProps.onPointerLeave?.(event);
2922
+ if (isTargetInside(event.relatedTarget)) return;
2923
+ scheduleClose();
2924
+ },
2925
+ onFocus: (event) => {
2926
+ triggerProps.onFocus?.(event);
2927
+ if (lastInputRef.current === "pointer") return;
2928
+ if (isSuppressed()) return;
2929
+ openedByHoverRef.current = false;
2930
+ closeTimer.clear();
2931
+ handleOpenChange(true);
2932
+ },
2933
+ onKeyDown: (event) => {
2934
+ triggerProps.onKeyDown?.(event);
2935
+ lastInputRef.current = "keyboard";
2936
+ }
2937
+ }) : trigger;
2938
+ const contentPropsWithRef = contentProps;
2939
+ const { className: contentClassName, style: contentStyle, ref: contentPropRef, ...restContentProps } = contentPropsWithRef ?? {};
2940
+ const onPointerDownOutside = (event) => {
2941
+ restContentProps.onPointerDownOutside?.(event);
2942
+ if (openedByHoverRef.current) suppressReopen();
2943
+ };
2944
+ const onInteractOutside = (event) => {
2945
+ restContentProps.onInteractOutside?.(event);
2946
+ if (openedByHoverRef.current) suppressReopen();
2947
+ };
2948
+ const resolvedContentProps = hoverOpen ? {
2949
+ ...restContentProps,
2950
+ onPointerEnter: (event) => {
2951
+ restContentProps.onPointerEnter?.(event);
2952
+ closeTimer.clear();
2953
+ },
2954
+ onPointerLeave: (event) => {
2955
+ restContentProps.onPointerLeave?.(event);
2956
+ if (isTargetInside(event.relatedTarget)) return;
2957
+ scheduleClose();
2958
+ },
2959
+ onFocus: (event) => {
2960
+ restContentProps.onFocus?.(event);
2961
+ closeTimer.clear();
2962
+ },
2963
+ onPointerDownOutside,
2964
+ onInteractOutside
2965
+ } : restContentProps;
2966
+ return /* @__PURE__ */ jsxs11(DropdownMenuPrimitive.Root, { open: resolvedOpen, onOpenChange: handleOpenChange, modal, children: [
2967
+ /* @__PURE__ */ jsx19(DropdownMenuPrimitive.Trigger, { asChild: true, children: resolvedTrigger }),
2968
+ /* @__PURE__ */ jsx19(DropdownMenuPrimitive.Portal, { forceMount: true, container: portalContainer, children: /* @__PURE__ */ jsx19(AnimatePresence4, { children: resolvedOpen ? /* @__PURE__ */ jsx19(
2969
+ DropdownMenuPrimitive.Content,
2970
+ {
2971
+ asChild: true,
2972
+ forceMount: true,
2973
+ side,
2974
+ align,
2975
+ sideOffset,
2976
+ alignOffset,
2977
+ collisionPadding,
2978
+ "aria-label": ariaLabelledBy ? void 0 : ariaLabel,
2979
+ "aria-labelledby": ariaLabelledBy,
2980
+ ...resolvedContentProps,
2981
+ children: /* @__PURE__ */ jsx19(
2982
+ motion4.div,
2983
+ {
2984
+ className: cn(dropdownMenuContentVariants({ chrome }), className, contentClassName),
2985
+ ref: (node) => {
2986
+ contentRef.current = node;
2987
+ setRef(contentPropRef, node);
2988
+ },
2989
+ initial: {
2990
+ opacity: 0,
2991
+ scale: 0.98,
2992
+ x: "var(--menu-x)",
2993
+ y: "var(--menu-y)"
2994
+ },
2995
+ animate: { opacity: 1, scale: 1, x: 0, y: 0 },
2996
+ exit: {
2997
+ opacity: 0,
2998
+ scale: 0.98,
2999
+ x: "var(--menu-x)",
3000
+ y: "var(--menu-y)",
3001
+ transition: { duration: motionDuration, ease: "easeIn" }
3002
+ },
3003
+ transition: { duration: motionDuration, ease: "easeOut" },
3004
+ style: { ...style, ...contentStyle },
3005
+ children
3006
+ }
3007
+ )
3008
+ }
3009
+ ) : null }) })
3010
+ ] });
3011
+ }
3012
+ var DropdownMenuItem = React12.forwardRef(
3013
+ ({ className, inset, destructive, ...props }, ref) => /* @__PURE__ */ jsx19(
3014
+ DropdownMenuPrimitive.Item,
3015
+ {
3016
+ ref,
3017
+ className: cn(dropdownMenuItemVariants({ inset, destructive }), className),
3018
+ ...props
3019
+ }
3020
+ )
3021
+ );
3022
+ DropdownMenuItem.displayName = "DropdownMenuItem";
3023
+ var DropdownMenuSeparator = React12.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx19(DropdownMenuPrimitive.Separator, { ref, className: cn("my-1 h-px bg-(--divider)", className), ...props }));
3024
+ DropdownMenuSeparator.displayName = "DropdownMenuSeparator";
3025
+
3026
+ // src/ui/components/Kbd.tsx
3027
+ import * as React13 from "react";
3028
+ import { jsx as jsx20 } from "react/jsx-runtime";
3029
+ var Kbd = React13.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx20(
3030
+ "span",
3031
+ {
3032
+ ref,
3033
+ className: cn(
3034
+ "inline-flex min-w-7 items-center justify-center gap-1",
3035
+ "rounded-(--shape-radius-10) px-2 pt-1 pb-1.5",
3036
+ "bg-(--surface-3) text-(--text-1)",
3037
+ "border border-[color-mix(in_oklab,var(--border),transparent_45%)]",
3038
+ "shadow-[inset_0_-2px_0_color-mix(in_oklab,var(--border),transparent_50%),0_3px_4px_color-mix(in_oklab,var(--border),transparent_80%)]",
3039
+ "text-[0.75rem] font-(--font-mono) leading-none tracking-[0.04em]",
3040
+ "transition-colors duration-(--motion-fast)",
3041
+ className
3042
+ ),
3043
+ ...props
3044
+ }
3045
+ ));
3046
+ Kbd.displayName = "Kbd";
2738
3047
  export {
2739
3048
  AuthController,
2740
3049
  AuthControllerCtx,
@@ -2751,10 +3060,14 @@ export {
2751
3060
  DialogContent,
2752
3061
  DialogController,
2753
3062
  DialogHost,
3063
+ DropdownMenu,
3064
+ DropdownMenuItem,
3065
+ DropdownMenuSeparator,
2754
3066
  Field,
2755
3067
  HttpError,
2756
3068
  InfoDialog,
2757
3069
  Input,
3070
+ Kbd,
2758
3071
  MockAuthHttpClient,
2759
3072
  MockHttpClient,
2760
3073
  NotificationHost,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hexdspace/react",
3
- "version": "0.1.29",
3
+ "version": "0.1.30",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",