@hexdspace/react 0.1.29 → 0.1.31

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,306 @@ 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 React13 from "react";
2744
+
2745
+ // src/ui/utils/refs.ts
2746
+ import * as React12 from "react";
2747
+ function useComposedRefs(...refs) {
2748
+ return React12.useCallback(
2749
+ (node) => {
2750
+ for (const ref of refs) {
2751
+ if (!ref) continue;
2752
+ if (typeof ref === "function") {
2753
+ ref(node);
2754
+ } else if (ref) {
2755
+ ref.current = node;
2756
+ }
2757
+ }
2758
+ },
2759
+ [refs]
2760
+ );
2761
+ }
2762
+
2763
+ // src/ui/components/DropdownMenu.tsx
2764
+ import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
2765
+ var dropdownMenuContentVariants = cva7(
2766
+ cn(
2767
+ "min-w-[14rem] p-2 text-sm z-5000",
2768
+ "text-[color:var(--text-1)]",
2769
+ "shadow-[var(--shadow-popover)]",
2770
+ "overflow-hidden",
2771
+ "origin-center",
2772
+ "[--menu-x:0px] [--menu-y:6px]",
2773
+ "data-[side=bottom]:[--menu-y:-6px]",
2774
+ "data-[side=left]:[--menu-x:6px] data-[side=left]:[--menu-y:0px]",
2775
+ "data-[side=right]:[--menu-x:-6px] data-[side=right]:[--menu-y:0px]"
2776
+ ),
2777
+ {
2778
+ variants: {
2779
+ chrome: {
2780
+ flat: cn(
2781
+ "rounded-[var(--radius-card)]",
2782
+ "border border-[color:color-mix(in_oklab,var(--border),transparent_55%)]",
2783
+ "bg-[color:var(--surface-2)]"
2784
+ ),
2785
+ glass: cn(
2786
+ "rounded-[var(--radius-card)]",
2787
+ "border border-[color:color-mix(in_oklab,var(--border),transparent_55%)]",
2788
+ "bg-[color:color-mix(in_oklab,var(--surface-2),transparent_25%)]",
2789
+ "backdrop-blur-[6px]",
2790
+ "shadow-[var(--shadow-popover)]"
2791
+ ),
2792
+ glow: cn(
2793
+ "rounded-[var(--radius-card)]",
2794
+ "border border-[color:color-mix(in_oklab,var(--border),transparent_60%)]",
2795
+ "bg-[color:var(--surface-2)]",
2796
+ "shadow-[0_0_0_1px_color-mix(in_oklab,var(--border),transparent_55%),0_12px_28px_color-mix(in_oklab,var(--border),transparent_65%)]"
2797
+ )
2798
+ }
2799
+ },
2800
+ defaultVariants: {
2801
+ chrome: "flat"
2802
+ }
2803
+ }
2804
+ );
2805
+ var dropdownMenuItemVariants = cva7(
2806
+ cn(
2807
+ "state-layer",
2808
+ "flex items-center gap-3 rounded-[calc(var(--radius-card)-8px)] px-3.5 py-2.5 text-sm outline-none",
2809
+ "text-[color:var(--text-1)] cursor-pointer",
2810
+ "data-[highlighted]:bg-[color:var(--surface-hover)] data-[highlighted]:text-[color:var(--text-1)]",
2811
+ "data-[highlighted]:shadow-[0_0_0_1px_color-mix(in_oklab,var(--border),transparent_55%)]",
2812
+ "data-[highlighted]:translate-x-[1px]",
2813
+ "active:bg-[color:var(--surface-active)]",
2814
+ "data-[disabled]:text-[color:var(--disabled-fg)] data-[disabled]:pointer-events-none",
2815
+ "data-[disabled]:cursor-not-allowed",
2816
+ "transition-[background-color,color,box-shadow,transform] duration-[var(--motion-fast)] ease-out"
2817
+ ),
2818
+ {
2819
+ variants: {
2820
+ inset: { true: "pl-8" },
2821
+ destructive: {
2822
+ true: cn(
2823
+ "text-[color:var(--danger)]",
2824
+ "data-[highlighted]:text-[color:var(--danger)]",
2825
+ "data-[highlighted]:bg-[color:color-mix(in_oklab,var(--danger),transparent_85%)]"
2826
+ )
2827
+ }
2828
+ }
2829
+ }
2830
+ );
2831
+ function useWindowTimeout() {
2832
+ const timeoutRef = React13.useRef(null);
2833
+ const clear = React13.useCallback(() => {
2834
+ if (timeoutRef.current !== null) {
2835
+ window.clearTimeout(timeoutRef.current);
2836
+ timeoutRef.current = null;
2837
+ }
2838
+ }, []);
2839
+ const set = React13.useCallback(
2840
+ (fn, ms) => {
2841
+ clear();
2842
+ timeoutRef.current = window.setTimeout(() => {
2843
+ timeoutRef.current = null;
2844
+ fn();
2845
+ }, ms);
2846
+ },
2847
+ [clear]
2848
+ );
2849
+ React13.useEffect(() => clear, [clear]);
2850
+ return { set, clear };
2851
+ }
2852
+ function DropdownMenu({
2853
+ trigger,
2854
+ children,
2855
+ open: open2,
2856
+ defaultOpen,
2857
+ onOpenChange,
2858
+ hoverOpen = false,
2859
+ hoverCloseDelayMs = 50,
2860
+ modal = false,
2861
+ side = "bottom",
2862
+ align = "start",
2863
+ sideOffset = 8,
2864
+ alignOffset = 0,
2865
+ collisionPadding = 8,
2866
+ portalContainer,
2867
+ ariaLabel = "Menu",
2868
+ ariaLabelledBy,
2869
+ chrome,
2870
+ className,
2871
+ style,
2872
+ contentProps
2873
+ }) {
2874
+ const [uncontrolledOpen, setUncontrolledOpen] = React13.useState(defaultOpen ?? false);
2875
+ const isControlled = open2 !== void 0;
2876
+ const resolvedOpen = isControlled ? open2 : uncontrolledOpen;
2877
+ const motionDuration = useMotionDuration("--motion-fast", 0.14);
2878
+ const closeTimer = useWindowTimeout();
2879
+ const triggerRef = React13.useRef(null);
2880
+ const contentRef = React13.useRef(null);
2881
+ const openedByHoverRef = React13.useRef(false);
2882
+ const lastInputRef = React13.useRef("unknown");
2883
+ const handleOpenChange = React13.useCallback(
2884
+ (nextOpen) => {
2885
+ closeTimer.clear();
2886
+ if (!nextOpen && hoverOpen && openedByHoverRef.current) {
2887
+ openedByHoverRef.current = false;
2888
+ }
2889
+ if (!isControlled) setUncontrolledOpen(nextOpen);
2890
+ onOpenChange?.(nextOpen);
2891
+ },
2892
+ [closeTimer, hoverOpen, isControlled, onOpenChange]
2893
+ );
2894
+ const isTargetInside = React13.useCallback((target) => {
2895
+ if (!(target instanceof Node)) return false;
2896
+ return Boolean(triggerRef.current?.contains(target) || contentRef.current?.contains(target));
2897
+ }, []);
2898
+ const scheduleClose = React13.useCallback(() => {
2899
+ if (!hoverOpen) return;
2900
+ closeTimer.set(() => handleOpenChange(false), hoverCloseDelayMs);
2901
+ }, [closeTimer, handleOpenChange, hoverCloseDelayMs, hoverOpen]);
2902
+ const triggerElement = trigger;
2903
+ const triggerProps = triggerElement.props;
2904
+ const composedTriggerRef = useComposedRefs(triggerRef, triggerProps.ref);
2905
+ const resolvedTrigger = hoverOpen ? React13.cloneElement(triggerElement, {
2906
+ ref: composedTriggerRef,
2907
+ onPointerEnter: (event) => {
2908
+ triggerProps.onPointerEnter?.(event);
2909
+ openedByHoverRef.current = true;
2910
+ lastInputRef.current = "pointer";
2911
+ closeTimer.clear();
2912
+ handleOpenChange(true);
2913
+ },
2914
+ onPointerDown: (event) => {
2915
+ triggerProps.onPointerDown?.(event);
2916
+ lastInputRef.current = "pointer";
2917
+ },
2918
+ onPointerLeave: (event) => {
2919
+ triggerProps.onPointerLeave?.(event);
2920
+ if (isTargetInside(event.relatedTarget)) return;
2921
+ scheduleClose();
2922
+ },
2923
+ onKeyDown: (event) => {
2924
+ triggerProps.onKeyDown?.(event);
2925
+ lastInputRef.current = "keyboard";
2926
+ }
2927
+ }) : trigger;
2928
+ const contentPropsWithRef = contentProps;
2929
+ const {
2930
+ className: contentClassName,
2931
+ style: contentStyle,
2932
+ ref: contentPropRef,
2933
+ ...restContentProps
2934
+ } = contentPropsWithRef ?? {};
2935
+ const composedContentRef = useComposedRefs(contentRef, contentPropRef);
2936
+ const onPointerDownOutside = (event) => {
2937
+ restContentProps.onPointerDownOutside?.(event);
2938
+ };
2939
+ const onInteractOutside = (event) => {
2940
+ restContentProps.onInteractOutside?.(event);
2941
+ };
2942
+ const resolvedContentProps = hoverOpen ? {
2943
+ ...restContentProps,
2944
+ onPointerEnter: (event) => {
2945
+ restContentProps.onPointerEnter?.(event);
2946
+ closeTimer.clear();
2947
+ },
2948
+ onPointerLeave: (event) => {
2949
+ restContentProps.onPointerLeave?.(event);
2950
+ if (isTargetInside(event.relatedTarget)) return;
2951
+ scheduleClose();
2952
+ },
2953
+ onFocus: (event) => {
2954
+ restContentProps.onFocus?.(event);
2955
+ closeTimer.clear();
2956
+ },
2957
+ onPointerDownOutside,
2958
+ onInteractOutside
2959
+ } : restContentProps;
2960
+ return /* @__PURE__ */ jsxs11(DropdownMenuPrimitive.Root, { open: resolvedOpen, onOpenChange: handleOpenChange, modal, children: [
2961
+ /* @__PURE__ */ jsx19(DropdownMenuPrimitive.Trigger, { asChild: true, children: resolvedTrigger }),
2962
+ /* @__PURE__ */ jsx19(DropdownMenuPrimitive.Portal, { forceMount: true, container: portalContainer, children: /* @__PURE__ */ jsx19(AnimatePresence4, { children: resolvedOpen ? /* @__PURE__ */ jsx19(
2963
+ DropdownMenuPrimitive.Content,
2964
+ {
2965
+ asChild: true,
2966
+ forceMount: true,
2967
+ side,
2968
+ align,
2969
+ sideOffset,
2970
+ alignOffset,
2971
+ collisionPadding,
2972
+ "aria-label": ariaLabelledBy ? void 0 : ariaLabel,
2973
+ "aria-labelledby": ariaLabelledBy,
2974
+ ...resolvedContentProps,
2975
+ children: /* @__PURE__ */ jsx19(
2976
+ motion4.div,
2977
+ {
2978
+ className: cn(dropdownMenuContentVariants({ chrome }), className, contentClassName),
2979
+ ref: composedContentRef,
2980
+ initial: {
2981
+ opacity: 0,
2982
+ scale: 0.98,
2983
+ x: "var(--menu-x)",
2984
+ y: "var(--menu-y)"
2985
+ },
2986
+ animate: { opacity: 1, scale: 1, x: 0, y: 0 },
2987
+ exit: {
2988
+ opacity: 0,
2989
+ scale: 0.98,
2990
+ x: "var(--menu-x)",
2991
+ y: "var(--menu-y)",
2992
+ transition: { duration: motionDuration, ease: "easeIn" }
2993
+ },
2994
+ transition: { duration: motionDuration, ease: "easeOut" },
2995
+ style: { ...style, ...contentStyle },
2996
+ children
2997
+ }
2998
+ )
2999
+ }
3000
+ ) : null }) })
3001
+ ] });
3002
+ }
3003
+ var DropdownMenuItem = React13.forwardRef(
3004
+ ({ className, inset, destructive, ...props }, ref) => /* @__PURE__ */ jsx19(
3005
+ DropdownMenuPrimitive.Item,
3006
+ {
3007
+ ref,
3008
+ className: cn(dropdownMenuItemVariants({ inset, destructive }), className),
3009
+ ...props
3010
+ }
3011
+ )
3012
+ );
3013
+ DropdownMenuItem.displayName = "DropdownMenuItem";
3014
+ var DropdownMenuSeparator = React13.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx19(DropdownMenuPrimitive.Separator, { ref, className: cn("my-1 h-px bg-(--divider)", className), ...props }));
3015
+ DropdownMenuSeparator.displayName = "DropdownMenuSeparator";
3016
+
3017
+ // src/ui/components/Kbd.tsx
3018
+ import * as React14 from "react";
3019
+ import { jsx as jsx20 } from "react/jsx-runtime";
3020
+ var Kbd = React14.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx20(
3021
+ "span",
3022
+ {
3023
+ ref,
3024
+ className: cn(
3025
+ "inline-flex min-w-7 items-center justify-center gap-1",
3026
+ "rounded-(--shape-radius-10) px-2 pt-1 pb-1.5",
3027
+ "bg-(--surface-3) text-(--text-1)",
3028
+ "border border-[color-mix(in_oklab,var(--border),transparent_45%)]",
3029
+ "shadow-[inset_0_-2px_0_color-mix(in_oklab,var(--border),transparent_50%),0_3px_4px_color-mix(in_oklab,var(--border),transparent_80%)]",
3030
+ "text-[0.75rem] font-(--font-mono) leading-none tracking-[0.04em]",
3031
+ "transition-colors duration-(--motion-fast)",
3032
+ className
3033
+ ),
3034
+ ...props
3035
+ }
3036
+ ));
3037
+ Kbd.displayName = "Kbd";
2738
3038
  export {
2739
3039
  AuthController,
2740
3040
  AuthControllerCtx,
@@ -2751,10 +3051,14 @@ export {
2751
3051
  DialogContent,
2752
3052
  DialogController,
2753
3053
  DialogHost,
3054
+ DropdownMenu,
3055
+ DropdownMenuItem,
3056
+ DropdownMenuSeparator,
2754
3057
  Field,
2755
3058
  HttpError,
2756
3059
  InfoDialog,
2757
3060
  Input,
3061
+ Kbd,
2758
3062
  MockAuthHttpClient,
2759
3063
  MockHttpClient,
2760
3064
  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.31",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",