@dxos/react-ui 0.6.13 → 0.6.14-main.69511f5
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/lib/browser/index.mjs +731 -198
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +2925 -0
- package/dist/lib/node/index.cjs.map +7 -0
- package/dist/lib/node/meta.json +1 -0
- package/dist/lib/node-esm/index.mjs +2924 -0
- package/dist/lib/node-esm/index.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -0
- package/dist/types/src/components/Avatars/Avatar.stories.d.ts +15 -1
- package/dist/types/src/components/Avatars/Avatar.stories.d.ts.map +1 -1
- package/dist/types/src/components/Avatars/AvatarGroup.stories.d.ts +6 -1
- package/dist/types/src/components/Avatars/AvatarGroup.stories.d.ts.map +1 -1
- package/dist/types/src/components/Breadcrumb/Breadcrumb.stories.d.ts +9 -1
- package/dist/types/src/components/Breadcrumb/Breadcrumb.stories.d.ts.map +1 -1
- package/dist/types/src/components/Buttons/Button.d.ts +1 -1
- package/dist/types/src/components/Buttons/Button.d.ts.map +1 -1
- package/dist/types/src/components/Buttons/Button.stories.d.ts +12 -17
- package/dist/types/src/components/Buttons/Button.stories.d.ts.map +1 -1
- package/dist/types/src/components/Buttons/Toggle.stories.d.ts +2 -1
- package/dist/types/src/components/Buttons/Toggle.stories.d.ts.map +1 -1
- package/dist/types/src/components/Buttons/ToggleGroup.stories.d.ts +20 -1
- package/dist/types/src/components/Buttons/ToggleGroup.stories.d.ts.map +1 -1
- package/dist/types/src/components/DensityProvider/DensityProvider.d.ts.map +1 -1
- package/dist/types/src/components/Dialogs/AlertDialog.stories.d.ts +12 -1
- package/dist/types/src/components/Dialogs/AlertDialog.stories.d.ts.map +1 -1
- package/dist/types/src/components/Dialogs/Dialog.stories.d.ts +11 -1
- package/dist/types/src/components/Dialogs/Dialog.stories.d.ts.map +1 -1
- package/dist/types/src/components/Icon/Icon.d.ts +1 -1
- package/dist/types/src/components/Icon/Icon.d.ts.map +1 -1
- package/dist/types/src/components/Input/Input.d.ts.map +1 -1
- package/dist/types/src/components/Input/Input.stories.d.ts +16 -1
- package/dist/types/src/components/Input/Input.stories.d.ts.map +1 -1
- package/dist/types/src/components/Lists/List.stories.d.ts +2 -3
- package/dist/types/src/components/Lists/List.stories.d.ts.map +1 -1
- package/dist/types/src/components/Lists/Tree.stories.d.ts +5 -1
- package/dist/types/src/components/Lists/Tree.stories.d.ts.map +1 -1
- package/dist/types/src/components/Lists/Treegrid.stories.d.ts +3 -2
- package/dist/types/src/components/Lists/Treegrid.stories.d.ts.map +1 -1
- package/dist/types/src/components/Main/Main.stories.d.ts +5 -1
- package/dist/types/src/components/Main/Main.stories.d.ts.map +1 -1
- package/dist/types/src/components/Menus/ContextMenu.stories.d.ts +29 -1
- package/dist/types/src/components/Menus/ContextMenu.stories.d.ts.map +1 -1
- package/dist/types/src/components/Menus/DropdownMenu.d.ts +105 -44
- package/dist/types/src/components/Menus/DropdownMenu.d.ts.map +1 -1
- package/dist/types/src/components/Menus/DropdownMenu.stories.d.ts +29 -1
- package/dist/types/src/components/Menus/DropdownMenu.stories.d.ts.map +1 -1
- package/dist/types/src/components/Message/Message.stories.d.ts +6 -1
- package/dist/types/src/components/Message/Message.stories.d.ts.map +1 -1
- package/dist/types/src/components/Popover/Popover.d.ts +87 -21
- package/dist/types/src/components/Popover/Popover.d.ts.map +1 -1
- package/dist/types/src/components/Popover/Popover.stories.d.ts +20 -1
- package/dist/types/src/components/Popover/Popover.stories.d.ts.map +1 -1
- package/dist/types/src/components/ScrollArea/ScrollArea.stories.d.ts +20 -1
- package/dist/types/src/components/ScrollArea/ScrollArea.stories.d.ts.map +1 -1
- package/dist/types/src/components/Select/Select.d.ts.map +1 -1
- package/dist/types/src/components/Select/Select.stories.d.ts +10 -11
- package/dist/types/src/components/Select/Select.stories.d.ts.map +1 -1
- package/dist/types/src/components/Status/Status.stories.d.ts +0 -3
- package/dist/types/src/components/Status/Status.stories.d.ts.map +1 -1
- package/dist/types/src/components/ThemeProvider/ThemeProvider.d.ts +4 -5
- package/dist/types/src/components/ThemeProvider/ThemeProvider.d.ts.map +1 -1
- package/dist/types/src/components/ThemeProvider/TranslationsProvider.d.ts +4 -4
- package/dist/types/src/components/Toast/Toast.d.ts.map +1 -1
- package/dist/types/src/components/Toast/Toast.stories.d.ts +20 -1
- package/dist/types/src/components/Toast/Toast.stories.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.d.ts +4 -2
- package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +30 -1
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
- package/dist/types/src/components/Tooltip/Tooltip.stories.d.ts +13 -1
- package/dist/types/src/components/Tooltip/Tooltip.stories.d.ts.map +1 -1
- package/dist/types/src/hooks/useThemeContext.d.ts.map +1 -1
- package/dist/types/src/playground/Controls.stories.d.ts +2 -6
- package/dist/types/src/playground/Controls.stories.d.ts.map +1 -1
- package/dist/types/src/playground/Surfaces.stories.d.ts +6 -2
- package/dist/types/src/playground/Surfaces.stories.d.ts.map +1 -1
- package/dist/types/src/playground/Typography.stories.d.ts +1 -1
- package/dist/types/src/testing/decorators/index.d.ts +1 -0
- package/dist/types/src/testing/decorators/index.d.ts.map +1 -1
- package/dist/types/src/testing/decorators/withVariants.d.ts +13 -0
- package/dist/types/src/testing/decorators/withVariants.d.ts.map +1 -0
- package/package.json +28 -17
- package/src/components/Avatars/Avatar.stories.tsx +3 -2
- package/src/components/Avatars/AvatarGroup.stories.tsx +3 -2
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +3 -2
- package/src/components/Buttons/Button.stories.tsx +34 -63
- package/src/components/Buttons/Button.tsx +46 -36
- package/src/components/Buttons/Toggle.stories.tsx +3 -2
- package/src/components/Buttons/ToggleGroup.stories.tsx +3 -2
- package/src/components/DensityProvider/DensityProvider.tsx +1 -1
- package/src/components/Dialogs/AlertDialog.stories.tsx +3 -2
- package/src/components/Dialogs/Dialog.stories.tsx +3 -2
- package/src/components/Icon/Icon.tsx +11 -9
- package/src/components/Input/Input.stories.tsx +4 -3
- package/src/components/Link/Link.stories.tsx +1 -1
- package/src/components/Lists/List.stories.tsx +4 -4
- package/src/components/Lists/Tree.stories.tsx +3 -2
- package/src/components/Lists/Treegrid.stories.tsx +7 -5
- package/src/components/Main/Main.stories.tsx +3 -2
- package/src/components/Menus/ContextMenu.stories.tsx +3 -2
- package/src/components/Menus/DropdownMenu.stories.tsx +43 -3
- package/src/components/Menus/DropdownMenu.tsx +518 -69
- package/src/components/Message/Message.stories.tsx +3 -2
- package/src/components/Popover/Popover.stories.tsx +27 -3
- package/src/components/Popover/Popover.tsx +524 -55
- package/src/components/ScrollArea/ScrollArea.stories.tsx +3 -2
- package/src/components/Select/Select.stories.tsx +14 -31
- package/src/components/Select/Select.tsx +9 -10
- package/src/components/Status/Status.stories.tsx +1 -2
- package/src/components/Tag/Tag.stories.tsx +1 -1
- package/src/components/ThemeProvider/ThemeProvider.tsx +17 -18
- package/src/components/Toast/Toast.stories.tsx +3 -2
- package/src/components/Toast/Toast.tsx +1 -4
- package/src/components/Toolbar/Toolbar.stories.tsx +3 -2
- package/src/components/Toolbar/Toolbar.tsx +21 -1
- package/src/components/Tooltip/Tooltip.stories.tsx +3 -2
- package/src/hooks/useThemeContext.ts +3 -1
- package/src/playground/Controls.stories.tsx +7 -10
- package/src/playground/Surfaces.stories.tsx +4 -3
- package/src/playground/Typography.stories.tsx +2 -2
- package/src/testing/decorators/index.ts +1 -0
- package/src/testing/decorators/withVariants.tsx +45 -0
- package/dist/types/src/playground/helpers.d.ts +0 -6
- package/dist/types/src/playground/helpers.d.ts.map +0 -1
- package/src/playground/helpers.tsx +0 -32
|
@@ -28,8 +28,9 @@ const StoryMessage = ({ valence, title, body }: StoryMessageProps) => (
|
|
|
28
28
|
);
|
|
29
29
|
|
|
30
30
|
export default {
|
|
31
|
-
title: 'react-ui/Message',
|
|
32
|
-
component:
|
|
31
|
+
title: 'ui/react-ui-core/Message',
|
|
32
|
+
component: Message,
|
|
33
|
+
render: StoryMessage,
|
|
33
34
|
decorators: [withTheme],
|
|
34
35
|
parameters: { chromatic: { disableSnapshot: false } },
|
|
35
36
|
};
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
|
-
import React, { type PropsWithChildren, type ReactNode } from 'react';
|
|
7
|
+
import React, { type PropsWithChildren, type ReactNode, useRef, useState } from 'react';
|
|
8
8
|
|
|
9
9
|
import { faker } from '@dxos/random';
|
|
10
10
|
|
|
@@ -29,8 +29,9 @@ const StorybookPopover = ({ openTrigger, children }: PropsWithChildren<{ openTri
|
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
export default {
|
|
32
|
-
title: 'react-ui/Popover',
|
|
33
|
-
component:
|
|
32
|
+
title: 'ui/react-ui-core/Popover',
|
|
33
|
+
component: Popover,
|
|
34
|
+
render: StorybookPopover,
|
|
34
35
|
decorators: [withTheme],
|
|
35
36
|
parameters: { chromatic: { disableSnapshot: false } },
|
|
36
37
|
};
|
|
@@ -41,3 +42,26 @@ export const Default = {
|
|
|
41
42
|
children: faker.lorem.paragraphs(3),
|
|
42
43
|
},
|
|
43
44
|
};
|
|
45
|
+
|
|
46
|
+
export const VirtualTrigger = {
|
|
47
|
+
render: () => {
|
|
48
|
+
const [open, setOpen] = useState(true);
|
|
49
|
+
const buttonRef = useRef<HTMLButtonElement | null>(null);
|
|
50
|
+
return (
|
|
51
|
+
<>
|
|
52
|
+
<Button onClick={() => setOpen(true)} ref={buttonRef}>
|
|
53
|
+
Open popover
|
|
54
|
+
</Button>
|
|
55
|
+
<Popover.Root open={open} onOpenChange={setOpen}>
|
|
56
|
+
<Popover.VirtualTrigger virtualRef={buttonRef} />
|
|
57
|
+
<Popover.Content>
|
|
58
|
+
<Popover.Viewport>
|
|
59
|
+
<p className='pli-2 plb-1 min-is-[18rem] max-is-[38rem]'>{faker.lorem.paragraphs(3)}</p>
|
|
60
|
+
</Popover.Viewport>
|
|
61
|
+
<Popover.Arrow />
|
|
62
|
+
</Popover.Content>
|
|
63
|
+
</Popover.Root>
|
|
64
|
+
</>
|
|
65
|
+
);
|
|
66
|
+
},
|
|
67
|
+
};
|
|
@@ -2,82 +2,543 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
PopoverClose as PopoverClosePrimitive,
|
|
20
|
-
} from '@radix-ui/react-popover';
|
|
5
|
+
// This is based upon `@radix-ui/react-popover` fetched 25 Oct 2024 at https://github.com/radix-ui/primitives at commit 374c7d7.
|
|
6
|
+
|
|
7
|
+
import { composeEventHandlers } from '@radix-ui/primitive';
|
|
8
|
+
import { useComposedRefs } from '@radix-ui/react-compose-refs';
|
|
9
|
+
import { createContextScope } from '@radix-ui/react-context';
|
|
10
|
+
import type { Scope } from '@radix-ui/react-context';
|
|
11
|
+
import { DismissableLayer } from '@radix-ui/react-dismissable-layer';
|
|
12
|
+
import { useFocusGuards } from '@radix-ui/react-focus-guards';
|
|
13
|
+
import { FocusScope } from '@radix-ui/react-focus-scope';
|
|
14
|
+
import { useId } from '@radix-ui/react-id';
|
|
15
|
+
import * as PopperPrimitive from '@radix-ui/react-popper';
|
|
16
|
+
import { createPopperScope } from '@radix-ui/react-popper';
|
|
17
|
+
import { Portal as PortalPrimitive } from '@radix-ui/react-portal';
|
|
18
|
+
import { Presence } from '@radix-ui/react-presence';
|
|
21
19
|
import { Primitive } from '@radix-ui/react-primitive';
|
|
22
20
|
import { Slot } from '@radix-ui/react-slot';
|
|
23
|
-
import
|
|
21
|
+
import { useControllableState } from '@radix-ui/react-use-controllable-state';
|
|
22
|
+
import { hideOthers } from 'aria-hidden';
|
|
23
|
+
import React, {
|
|
24
|
+
type ComponentPropsWithRef,
|
|
25
|
+
forwardRef,
|
|
26
|
+
type ElementRef,
|
|
27
|
+
type RefObject,
|
|
28
|
+
type ReactNode,
|
|
29
|
+
useRef,
|
|
30
|
+
useCallback,
|
|
31
|
+
type ComponentPropsWithoutRef,
|
|
32
|
+
type FC,
|
|
33
|
+
useState,
|
|
34
|
+
useEffect,
|
|
35
|
+
type MutableRefObject,
|
|
36
|
+
} from 'react';
|
|
37
|
+
import { RemoveScroll } from 'react-remove-scroll';
|
|
24
38
|
|
|
25
39
|
import { useThemeContext } from '../../hooks';
|
|
26
40
|
import { type ThemedClassName } from '../../util';
|
|
27
|
-
import { ElevationProvider } from '../ElevationProvider';
|
|
28
41
|
|
|
29
|
-
|
|
42
|
+
/* -------------------------------------------------------------------------------------------------
|
|
43
|
+
* Popover
|
|
44
|
+
* ----------------------------------------------------------------------------------------------- */
|
|
45
|
+
|
|
46
|
+
const POPOVER_NAME = 'Popover';
|
|
47
|
+
|
|
48
|
+
type ScopedProps<P> = P & { __scopePopover?: Scope };
|
|
49
|
+
const [createPopoverContext, createPopoverScope] = createContextScope(POPOVER_NAME, [createPopperScope]);
|
|
50
|
+
const usePopperScope = createPopperScope();
|
|
51
|
+
|
|
52
|
+
type PopoverContextValue = {
|
|
53
|
+
triggerRef: MutableRefObject<HTMLButtonElement>;
|
|
54
|
+
contentId: string;
|
|
55
|
+
open: boolean;
|
|
56
|
+
onOpenChange(open: boolean): void;
|
|
57
|
+
onOpenToggle(): void;
|
|
58
|
+
hasCustomAnchor: boolean;
|
|
59
|
+
onCustomAnchorAdd(): void;
|
|
60
|
+
onCustomAnchorRemove(): void;
|
|
61
|
+
modal: boolean;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const [PopoverProvider, usePopoverContext] = createPopoverContext<PopoverContextValue>(POPOVER_NAME);
|
|
65
|
+
|
|
66
|
+
interface PopoverRootProps {
|
|
67
|
+
children?: ReactNode;
|
|
68
|
+
open?: boolean;
|
|
69
|
+
defaultOpen?: boolean;
|
|
70
|
+
onOpenChange?: (open: boolean) => void;
|
|
71
|
+
modal?: boolean;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const PopoverRoot: FC<PopoverRootProps> = (props: ScopedProps<PopoverRootProps>) => {
|
|
75
|
+
const { __scopePopover, children, open: openProp, defaultOpen, onOpenChange, modal = false } = props;
|
|
76
|
+
const popperScope = usePopperScope(__scopePopover);
|
|
77
|
+
const triggerRef = useRef<HTMLButtonElement>(null);
|
|
78
|
+
const [hasCustomAnchor, setHasCustomAnchor] = useState(false);
|
|
79
|
+
const [open = false, setOpen] = useControllableState({
|
|
80
|
+
prop: openProp,
|
|
81
|
+
defaultProp: defaultOpen,
|
|
82
|
+
onChange: onOpenChange,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<PopperPrimitive.Root {...popperScope}>
|
|
87
|
+
<PopoverProvider
|
|
88
|
+
scope={__scopePopover}
|
|
89
|
+
contentId={useId()}
|
|
90
|
+
triggerRef={triggerRef as MutableRefObject<HTMLButtonElement>}
|
|
91
|
+
open={open}
|
|
92
|
+
onOpenChange={setOpen}
|
|
93
|
+
onOpenToggle={useCallback(() => setOpen((prevOpen) => !prevOpen), [setOpen])}
|
|
94
|
+
hasCustomAnchor={hasCustomAnchor}
|
|
95
|
+
onCustomAnchorAdd={useCallback(() => setHasCustomAnchor(true), [])}
|
|
96
|
+
onCustomAnchorRemove={useCallback(() => setHasCustomAnchor(false), [])}
|
|
97
|
+
modal={modal}
|
|
98
|
+
>
|
|
99
|
+
{children}
|
|
100
|
+
</PopoverProvider>
|
|
101
|
+
</PopperPrimitive.Root>
|
|
102
|
+
);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
PopoverRoot.displayName = POPOVER_NAME;
|
|
30
106
|
|
|
31
|
-
|
|
107
|
+
/* -------------------------------------------------------------------------------------------------
|
|
108
|
+
* PopoverAnchor
|
|
109
|
+
* ----------------------------------------------------------------------------------------------- */
|
|
32
110
|
|
|
33
|
-
|
|
111
|
+
const ANCHOR_NAME = 'PopoverAnchor';
|
|
34
112
|
|
|
35
|
-
|
|
113
|
+
type PopoverAnchorElement = ElementRef<typeof PopperPrimitive.Anchor>;
|
|
114
|
+
type PopperAnchorProps = ComponentPropsWithoutRef<typeof PopperPrimitive.Anchor>;
|
|
115
|
+
interface PopoverAnchorProps extends PopperAnchorProps {}
|
|
36
116
|
|
|
37
|
-
|
|
117
|
+
const PopoverAnchor = forwardRef<PopoverAnchorElement, PopoverAnchorProps>(
|
|
118
|
+
(props: ScopedProps<PopoverAnchorProps>, forwardedRef) => {
|
|
119
|
+
const { __scopePopover, ...anchorProps } = props;
|
|
120
|
+
const context = usePopoverContext(ANCHOR_NAME, __scopePopover);
|
|
121
|
+
const popperScope = usePopperScope(__scopePopover);
|
|
122
|
+
const { onCustomAnchorAdd, onCustomAnchorRemove } = context;
|
|
38
123
|
|
|
39
|
-
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
onCustomAnchorAdd();
|
|
126
|
+
return () => onCustomAnchorRemove();
|
|
127
|
+
}, [onCustomAnchorAdd, onCustomAnchorRemove]);
|
|
40
128
|
|
|
41
|
-
|
|
129
|
+
return <PopperPrimitive.Anchor {...popperScope} {...anchorProps} ref={forwardedRef} />;
|
|
130
|
+
},
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
PopoverAnchor.displayName = ANCHOR_NAME;
|
|
134
|
+
|
|
135
|
+
/* -------------------------------------------------------------------------------------------------
|
|
136
|
+
* PopoverTrigger
|
|
137
|
+
* ----------------------------------------------------------------------------------------------- */
|
|
42
138
|
|
|
43
|
-
const
|
|
139
|
+
const TRIGGER_NAME = 'PopoverTrigger';
|
|
44
140
|
|
|
45
|
-
type
|
|
141
|
+
type PopoverTriggerElement = ElementRef<typeof Primitive.button>;
|
|
142
|
+
type PrimitiveButtonProps = ComponentPropsWithoutRef<typeof Primitive.button>;
|
|
143
|
+
interface PopoverTriggerProps extends PrimitiveButtonProps {}
|
|
46
144
|
|
|
47
|
-
const
|
|
145
|
+
const PopoverTrigger = forwardRef<PopoverTriggerElement, PopoverTriggerProps>(
|
|
146
|
+
(props: ScopedProps<PopoverTriggerProps>, forwardedRef) => {
|
|
147
|
+
const { __scopePopover, ...triggerProps } = props;
|
|
148
|
+
const context = usePopoverContext(TRIGGER_NAME, __scopePopover);
|
|
149
|
+
const popperScope = usePopperScope(__scopePopover);
|
|
150
|
+
const composedTriggerRef = useComposedRefs(forwardedRef, context.triggerRef);
|
|
48
151
|
|
|
49
|
-
|
|
152
|
+
const trigger = (
|
|
153
|
+
<Primitive.button
|
|
154
|
+
type='button'
|
|
155
|
+
aria-haspopup='dialog'
|
|
156
|
+
aria-expanded={context.open}
|
|
157
|
+
aria-controls={context.contentId}
|
|
158
|
+
data-state={getState(context.open)}
|
|
159
|
+
{...triggerProps}
|
|
160
|
+
ref={composedTriggerRef}
|
|
161
|
+
onClick={composeEventHandlers(props.onClick, context.onOpenToggle)}
|
|
162
|
+
/>
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
return context.hasCustomAnchor ? (
|
|
166
|
+
trigger
|
|
167
|
+
) : (
|
|
168
|
+
<PopperPrimitive.Anchor asChild {...popperScope}>
|
|
169
|
+
{trigger}
|
|
170
|
+
</PopperPrimitive.Anchor>
|
|
171
|
+
);
|
|
172
|
+
},
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
PopoverTrigger.displayName = TRIGGER_NAME;
|
|
176
|
+
|
|
177
|
+
/* -------------------------------------------------------------------------------------------------
|
|
178
|
+
* PopoverVirtualTrigger
|
|
179
|
+
* ----------------------------------------------------------------------------------------------- */
|
|
180
|
+
|
|
181
|
+
const VIRTUAL_TRIGGER_NAME = 'PopoverVirtualTrigger';
|
|
182
|
+
|
|
183
|
+
interface PopoverVirtualTriggerProps {
|
|
184
|
+
virtualRef: RefObject<PopoverTriggerElement>;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const PopoverVirtualTrigger = (props: ScopedProps<PopoverVirtualTriggerProps>) => {
|
|
188
|
+
const { __scopePopover, virtualRef } = props;
|
|
189
|
+
const context = usePopoverContext(VIRTUAL_TRIGGER_NAME, __scopePopover);
|
|
190
|
+
const popperScope = usePopperScope(__scopePopover);
|
|
191
|
+
useEffect(() => {
|
|
192
|
+
if (virtualRef.current) {
|
|
193
|
+
context.triggerRef.current = virtualRef.current;
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
return <PopperPrimitive.Anchor {...popperScope} virtualRef={virtualRef} />;
|
|
197
|
+
};
|
|
50
198
|
|
|
51
|
-
|
|
52
|
-
|
|
199
|
+
PopoverVirtualTrigger.displayName = VIRTUAL_TRIGGER_NAME;
|
|
200
|
+
|
|
201
|
+
/* -------------------------------------------------------------------------------------------------
|
|
202
|
+
* PopoverPortal
|
|
203
|
+
* ----------------------------------------------------------------------------------------------- */
|
|
204
|
+
|
|
205
|
+
const PORTAL_NAME = 'PopoverPortal';
|
|
206
|
+
|
|
207
|
+
type PortalContextValue = { forceMount?: true };
|
|
208
|
+
const [PortalProvider, usePortalContext] = createPopoverContext<PortalContextValue>(PORTAL_NAME, {
|
|
209
|
+
forceMount: undefined,
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
type PortalProps = ComponentPropsWithoutRef<typeof PortalPrimitive>;
|
|
213
|
+
interface PopoverPortalProps {
|
|
214
|
+
children?: ReactNode;
|
|
215
|
+
/**
|
|
216
|
+
* Specify a container element to portal the content into.
|
|
217
|
+
*/
|
|
218
|
+
container?: PortalProps['container'];
|
|
219
|
+
/**
|
|
220
|
+
* Used to force mounting when more control is needed. Useful when
|
|
221
|
+
* controlling animation with React animation libraries.
|
|
222
|
+
*/
|
|
223
|
+
forceMount?: true;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const PopoverPortal: FC<PopoverPortalProps> = (props: ScopedProps<PopoverPortalProps>) => {
|
|
227
|
+
const { __scopePopover, forceMount, children, container } = props;
|
|
228
|
+
const context = usePopoverContext(PORTAL_NAME, __scopePopover);
|
|
53
229
|
return (
|
|
54
|
-
<
|
|
55
|
-
{
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
230
|
+
<PortalProvider scope={__scopePopover} forceMount={forceMount}>
|
|
231
|
+
<Presence present={forceMount || context.open}>
|
|
232
|
+
<PortalPrimitive asChild container={container}>
|
|
233
|
+
{children}
|
|
234
|
+
</PortalPrimitive>
|
|
235
|
+
</Presence>
|
|
236
|
+
</PortalProvider>
|
|
59
237
|
);
|
|
60
|
-
}
|
|
238
|
+
};
|
|
61
239
|
|
|
62
|
-
|
|
240
|
+
PopoverPortal.displayName = PORTAL_NAME;
|
|
241
|
+
|
|
242
|
+
/* -------------------------------------------------------------------------------------------------
|
|
243
|
+
* PopoverContent
|
|
244
|
+
* ----------------------------------------------------------------------------------------------- */
|
|
245
|
+
|
|
246
|
+
const CONTENT_NAME = 'PopoverContent';
|
|
247
|
+
|
|
248
|
+
interface PopoverContentProps extends PopoverContentTypeProps {
|
|
249
|
+
/**
|
|
250
|
+
* Used to force mounting when more control is needed. Useful when
|
|
251
|
+
* controlling animation with React animation libraries.
|
|
252
|
+
*/
|
|
253
|
+
forceMount?: true;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const PopoverContent = forwardRef<PopoverContentTypeElement, PopoverContentProps>(
|
|
257
|
+
(props: ScopedProps<PopoverContentProps>, forwardedRef) => {
|
|
258
|
+
const portalContext = usePortalContext(CONTENT_NAME, props.__scopePopover);
|
|
259
|
+
const { forceMount = portalContext.forceMount, ...contentProps } = props;
|
|
260
|
+
const context = usePopoverContext(CONTENT_NAME, props.__scopePopover);
|
|
261
|
+
return (
|
|
262
|
+
<Presence present={forceMount || context.open}>
|
|
263
|
+
{context.modal ? (
|
|
264
|
+
<PopoverContentModal {...contentProps} ref={forwardedRef} />
|
|
265
|
+
) : (
|
|
266
|
+
<PopoverContentNonModal {...contentProps} ref={forwardedRef} />
|
|
267
|
+
)}
|
|
268
|
+
</Presence>
|
|
269
|
+
);
|
|
270
|
+
},
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
PopoverContent.displayName = CONTENT_NAME;
|
|
274
|
+
|
|
275
|
+
/* ----------------------------------------------------------------------------------------------- */
|
|
276
|
+
|
|
277
|
+
type PopoverContentTypeElement = PopoverContentImplElement;
|
|
278
|
+
interface PopoverContentTypeProps extends Omit<PopoverContentImplProps, 'trapFocus' | 'disableOutsidePointerEvents'> {}
|
|
279
|
+
|
|
280
|
+
const PopoverContentModal = forwardRef<PopoverContentTypeElement, PopoverContentTypeProps>(
|
|
281
|
+
(props: ScopedProps<PopoverContentTypeProps>, forwardedRef) => {
|
|
282
|
+
const context = usePopoverContext(CONTENT_NAME, props.__scopePopover);
|
|
283
|
+
const contentRef = useRef<HTMLDivElement>(null);
|
|
284
|
+
const composedRefs = useComposedRefs(forwardedRef, contentRef);
|
|
285
|
+
const isRightClickOutsideRef = useRef(false);
|
|
286
|
+
|
|
287
|
+
// aria-hide everything except the content (better supported equivalent to setting aria-modal)
|
|
288
|
+
useEffect(() => {
|
|
289
|
+
const content = contentRef.current;
|
|
290
|
+
if (content) {
|
|
291
|
+
return hideOthers(content);
|
|
292
|
+
}
|
|
293
|
+
}, []);
|
|
294
|
+
|
|
295
|
+
return (
|
|
296
|
+
<RemoveScroll as={Slot} allowPinchZoom>
|
|
297
|
+
<PopoverContentImpl
|
|
298
|
+
{...props}
|
|
299
|
+
ref={composedRefs}
|
|
300
|
+
// we make sure we're not trapping once it's been closed
|
|
301
|
+
// (closed !== unmounted when animating out)
|
|
302
|
+
trapFocus={context.open}
|
|
303
|
+
disableOutsidePointerEvents
|
|
304
|
+
onCloseAutoFocus={composeEventHandlers(props.onCloseAutoFocus, (event) => {
|
|
305
|
+
event.preventDefault();
|
|
306
|
+
if (!isRightClickOutsideRef.current) {
|
|
307
|
+
context.triggerRef.current?.focus();
|
|
308
|
+
}
|
|
309
|
+
})}
|
|
310
|
+
onPointerDownOutside={composeEventHandlers(
|
|
311
|
+
props.onPointerDownOutside,
|
|
312
|
+
(event) => {
|
|
313
|
+
const originalEvent = event.detail.originalEvent;
|
|
314
|
+
const ctrlLeftClick = originalEvent.button === 0 && originalEvent.ctrlKey === true;
|
|
315
|
+
const isRightClick = originalEvent.button === 2 || ctrlLeftClick;
|
|
316
|
+
|
|
317
|
+
isRightClickOutsideRef.current = isRightClick;
|
|
318
|
+
},
|
|
319
|
+
{ checkForDefaultPrevented: false },
|
|
320
|
+
)}
|
|
321
|
+
// When focus is trapped, a `focusout` event may still happen.
|
|
322
|
+
// We make sure we don't trigger our `onDismiss` in such case.
|
|
323
|
+
onFocusOutside={composeEventHandlers(props.onFocusOutside, (event) => event.preventDefault(), {
|
|
324
|
+
checkForDefaultPrevented: false,
|
|
325
|
+
})}
|
|
326
|
+
/>
|
|
327
|
+
</RemoveScroll>
|
|
328
|
+
);
|
|
329
|
+
},
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
const PopoverContentNonModal = forwardRef<PopoverContentTypeElement, PopoverContentTypeProps>(
|
|
333
|
+
(props: ScopedProps<PopoverContentTypeProps>, forwardedRef) => {
|
|
334
|
+
const context = usePopoverContext(CONTENT_NAME, props.__scopePopover);
|
|
335
|
+
const hasInteractedOutsideRef = useRef(false);
|
|
336
|
+
const hasPointerDownOutsideRef = useRef(false);
|
|
63
337
|
|
|
64
|
-
const PopoverContent = forwardRef<HTMLDivElement, PopoverContentProps>(
|
|
65
|
-
({ classNames, children, ...props }, forwardedRef) => {
|
|
66
|
-
const { tx } = useThemeContext();
|
|
67
338
|
return (
|
|
68
|
-
<
|
|
69
|
-
sideOffset={4}
|
|
70
|
-
collisionPadding={8}
|
|
339
|
+
<PopoverContentImpl
|
|
71
340
|
{...props}
|
|
72
|
-
className={tx('popover.content', 'popover', {}, classNames)}
|
|
73
341
|
ref={forwardedRef}
|
|
342
|
+
trapFocus={false}
|
|
343
|
+
disableOutsidePointerEvents={false}
|
|
344
|
+
onCloseAutoFocus={(event) => {
|
|
345
|
+
props.onCloseAutoFocus?.(event);
|
|
346
|
+
|
|
347
|
+
if (!event.defaultPrevented) {
|
|
348
|
+
if (!hasInteractedOutsideRef.current) {
|
|
349
|
+
context.triggerRef.current?.focus();
|
|
350
|
+
}
|
|
351
|
+
// Always prevent auto focus because we either focus manually or want user agent focus
|
|
352
|
+
event.preventDefault();
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
hasInteractedOutsideRef.current = false;
|
|
356
|
+
hasPointerDownOutsideRef.current = false;
|
|
357
|
+
}}
|
|
358
|
+
onInteractOutside={(event) => {
|
|
359
|
+
props.onInteractOutside?.(event);
|
|
360
|
+
|
|
361
|
+
if (!event.defaultPrevented) {
|
|
362
|
+
hasInteractedOutsideRef.current = true;
|
|
363
|
+
if (event.detail.originalEvent.type === 'pointerdown') {
|
|
364
|
+
hasPointerDownOutsideRef.current = true;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Prevent dismissing when clicking the trigger.
|
|
369
|
+
// As the trigger is already setup to close, without doing so would
|
|
370
|
+
// cause it to close and immediately open.
|
|
371
|
+
const target = event.target as HTMLElement;
|
|
372
|
+
const targetIsTrigger = context.triggerRef.current?.contains(target);
|
|
373
|
+
if (targetIsTrigger) {
|
|
374
|
+
event.preventDefault();
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// On Safari if the trigger is inside a container with tabIndex={0}, when clicked
|
|
378
|
+
// we will get the pointer down outside event on the trigger, but then a subsequent
|
|
379
|
+
// focus outside event on the container, we ignore any focus outside event when we've
|
|
380
|
+
// already had a pointer down outside event.
|
|
381
|
+
if (event.detail.originalEvent.type === 'focusin' && hasPointerDownOutsideRef.current) {
|
|
382
|
+
event.preventDefault();
|
|
383
|
+
}
|
|
384
|
+
}}
|
|
385
|
+
/>
|
|
386
|
+
);
|
|
387
|
+
},
|
|
388
|
+
);
|
|
389
|
+
|
|
390
|
+
/* ----------------------------------------------------------------------------------------------- */
|
|
391
|
+
|
|
392
|
+
type PopoverContentImplElement = ElementRef<typeof PopperPrimitive.Content>;
|
|
393
|
+
type FocusScopeProps = ComponentPropsWithoutRef<typeof FocusScope>;
|
|
394
|
+
type DismissableLayerProps = ComponentPropsWithoutRef<typeof DismissableLayer>;
|
|
395
|
+
type PopperContentProps = ThemedClassName<ComponentPropsWithoutRef<typeof PopperPrimitive.Content>>;
|
|
396
|
+
interface PopoverContentImplProps
|
|
397
|
+
extends Omit<PopperContentProps, 'onPlaced'>,
|
|
398
|
+
Omit<DismissableLayerProps, 'onDismiss'> {
|
|
399
|
+
/**
|
|
400
|
+
* Whether focus should be trapped within the `Popover`
|
|
401
|
+
* (default: false)
|
|
402
|
+
*/
|
|
403
|
+
trapFocus?: FocusScopeProps['trapped'];
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Event handler called when auto-focusing on open.
|
|
407
|
+
* Can be prevented.
|
|
408
|
+
*/
|
|
409
|
+
onOpenAutoFocus?: FocusScopeProps['onMountAutoFocus'];
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Event handler called when auto-focusing on close.
|
|
413
|
+
* Can be prevented.
|
|
414
|
+
*/
|
|
415
|
+
onCloseAutoFocus?: FocusScopeProps['onUnmountAutoFocus'];
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const PopoverContentImpl = forwardRef<PopoverContentImplElement, PopoverContentImplProps>(
|
|
419
|
+
(props: ScopedProps<PopoverContentImplProps>, forwardedRef) => {
|
|
420
|
+
const {
|
|
421
|
+
__scopePopover,
|
|
422
|
+
trapFocus,
|
|
423
|
+
onOpenAutoFocus,
|
|
424
|
+
onCloseAutoFocus,
|
|
425
|
+
disableOutsidePointerEvents,
|
|
426
|
+
onEscapeKeyDown,
|
|
427
|
+
onPointerDownOutside,
|
|
428
|
+
onFocusOutside,
|
|
429
|
+
onInteractOutside,
|
|
430
|
+
classNames,
|
|
431
|
+
...contentProps
|
|
432
|
+
} = props;
|
|
433
|
+
const context = usePopoverContext(CONTENT_NAME, __scopePopover);
|
|
434
|
+
const popperScope = usePopperScope(__scopePopover);
|
|
435
|
+
const { tx } = useThemeContext();
|
|
436
|
+
|
|
437
|
+
// Make sure the whole tree has focus guards as our `Popover` may be
|
|
438
|
+
// the last element in the DOM (because of the `Portal`)
|
|
439
|
+
useFocusGuards();
|
|
440
|
+
|
|
441
|
+
return (
|
|
442
|
+
<FocusScope
|
|
443
|
+
asChild
|
|
444
|
+
loop
|
|
445
|
+
trapped={trapFocus}
|
|
446
|
+
onMountAutoFocus={onOpenAutoFocus}
|
|
447
|
+
onUnmountAutoFocus={onCloseAutoFocus}
|
|
74
448
|
>
|
|
75
|
-
<
|
|
76
|
-
|
|
449
|
+
<DismissableLayer
|
|
450
|
+
asChild
|
|
451
|
+
disableOutsidePointerEvents={disableOutsidePointerEvents}
|
|
452
|
+
onInteractOutside={onInteractOutside}
|
|
453
|
+
onEscapeKeyDown={onEscapeKeyDown}
|
|
454
|
+
onPointerDownOutside={onPointerDownOutside}
|
|
455
|
+
onFocusOutside={onFocusOutside}
|
|
456
|
+
onDismiss={() => context.onOpenChange(false)}
|
|
457
|
+
>
|
|
458
|
+
<PopperPrimitive.Content
|
|
459
|
+
data-state={getState(context.open)}
|
|
460
|
+
role='dialog'
|
|
461
|
+
id={context.contentId}
|
|
462
|
+
{...popperScope}
|
|
463
|
+
{...contentProps}
|
|
464
|
+
className={tx('popover.content', 'popover', {}, classNames)}
|
|
465
|
+
ref={forwardedRef}
|
|
466
|
+
style={{
|
|
467
|
+
...contentProps.style,
|
|
468
|
+
// re-namespace exposed content custom properties
|
|
469
|
+
...{
|
|
470
|
+
'--radix-popover-content-transform-origin': 'var(--radix-popper-transform-origin)',
|
|
471
|
+
'--radix-popover-content-available-width': 'var(--radix-popper-available-width)',
|
|
472
|
+
'--radix-popover-content-available-height': 'var(--radix-popper-available-height)',
|
|
473
|
+
'--radix-popover-trigger-width': 'var(--radix-popper-anchor-width)',
|
|
474
|
+
'--radix-popover-trigger-height': 'var(--radix-popper-anchor-height)',
|
|
475
|
+
},
|
|
476
|
+
}}
|
|
477
|
+
/>
|
|
478
|
+
</DismissableLayer>
|
|
479
|
+
</FocusScope>
|
|
480
|
+
);
|
|
481
|
+
},
|
|
482
|
+
);
|
|
483
|
+
|
|
484
|
+
/* -------------------------------------------------------------------------------------------------
|
|
485
|
+
* PopoverClose
|
|
486
|
+
* ----------------------------------------------------------------------------------------------- */
|
|
487
|
+
|
|
488
|
+
const CLOSE_NAME = 'PopoverClose';
|
|
489
|
+
|
|
490
|
+
type PopoverCloseElement = ElementRef<typeof Primitive.button>;
|
|
491
|
+
interface PopoverCloseProps extends PrimitiveButtonProps {}
|
|
492
|
+
|
|
493
|
+
const PopoverClose = forwardRef<PopoverCloseElement, PopoverCloseProps>(
|
|
494
|
+
(props: ScopedProps<PopoverCloseProps>, forwardedRef) => {
|
|
495
|
+
const { __scopePopover, ...closeProps } = props;
|
|
496
|
+
const context = usePopoverContext(CLOSE_NAME, __scopePopover);
|
|
497
|
+
return (
|
|
498
|
+
<Primitive.button
|
|
499
|
+
type='button'
|
|
500
|
+
{...closeProps}
|
|
501
|
+
ref={forwardedRef}
|
|
502
|
+
onClick={composeEventHandlers(props.onClick, () => context.onOpenChange(false))}
|
|
503
|
+
/>
|
|
504
|
+
);
|
|
505
|
+
},
|
|
506
|
+
);
|
|
507
|
+
|
|
508
|
+
PopoverClose.displayName = CLOSE_NAME;
|
|
509
|
+
|
|
510
|
+
/* -------------------------------------------------------------------------------------------------
|
|
511
|
+
* PopoverArrow
|
|
512
|
+
* ----------------------------------------------------------------------------------------------- */
|
|
513
|
+
|
|
514
|
+
const ARROW_NAME = 'PopoverArrow';
|
|
515
|
+
|
|
516
|
+
type PopoverArrowElement = ElementRef<typeof PopperPrimitive.Arrow>;
|
|
517
|
+
type PopperArrowProps = ThemedClassName<ComponentPropsWithoutRef<typeof PopperPrimitive.Arrow>>;
|
|
518
|
+
interface PopoverArrowProps extends PopperArrowProps {}
|
|
519
|
+
|
|
520
|
+
const PopoverArrow = forwardRef<PopoverArrowElement, PopoverArrowProps>(
|
|
521
|
+
(props: ScopedProps<PopoverArrowProps>, forwardedRef) => {
|
|
522
|
+
const { __scopePopover, classNames, ...arrowProps } = props;
|
|
523
|
+
const popperScope = usePopperScope(__scopePopover);
|
|
524
|
+
const { tx } = useThemeContext();
|
|
525
|
+
return (
|
|
526
|
+
<PopperPrimitive.Arrow
|
|
527
|
+
{...popperScope}
|
|
528
|
+
{...arrowProps}
|
|
529
|
+
className={tx('popover.arrow', 'popover__arrow', {}, classNames)}
|
|
530
|
+
ref={forwardedRef}
|
|
531
|
+
/>
|
|
77
532
|
);
|
|
78
533
|
},
|
|
79
534
|
);
|
|
80
535
|
|
|
536
|
+
PopoverArrow.displayName = ARROW_NAME;
|
|
537
|
+
|
|
538
|
+
/* -------------------------------------------------------------------------------------------------
|
|
539
|
+
* PopoverViewport
|
|
540
|
+
* ----------------------------------------------------------------------------------------------- */
|
|
541
|
+
|
|
81
542
|
type PopoverViewportProps = ThemedClassName<ComponentPropsWithRef<typeof Primitive.div>> & {
|
|
82
543
|
asChild?: boolean;
|
|
83
544
|
constrainInline?: boolean;
|
|
@@ -100,24 +561,32 @@ const PopoverViewport = forwardRef<HTMLDivElement, PopoverViewportProps>(
|
|
|
100
561
|
},
|
|
101
562
|
);
|
|
102
563
|
|
|
564
|
+
/* ----------------------------------------------------------------------------------------------- */
|
|
565
|
+
|
|
566
|
+
const getState = (open: boolean) => (open ? 'open' : 'closed');
|
|
567
|
+
|
|
103
568
|
export const Popover = {
|
|
104
569
|
Root: PopoverRoot,
|
|
105
|
-
Portal: PopoverPortal,
|
|
106
|
-
Trigger: PopoverTrigger,
|
|
107
570
|
Anchor: PopoverAnchor,
|
|
108
|
-
|
|
109
|
-
|
|
571
|
+
Trigger: PopoverTrigger,
|
|
572
|
+
VirtualTrigger: PopoverVirtualTrigger,
|
|
573
|
+
Portal: PopoverPortal,
|
|
110
574
|
Content: PopoverContent,
|
|
575
|
+
Close: PopoverClose,
|
|
576
|
+
Arrow: PopoverArrow,
|
|
111
577
|
Viewport: PopoverViewport,
|
|
112
578
|
};
|
|
113
579
|
|
|
580
|
+
export { createPopoverScope };
|
|
581
|
+
|
|
114
582
|
export type {
|
|
115
583
|
PopoverRootProps,
|
|
116
|
-
PopoverPortalProps,
|
|
117
|
-
PopoverTriggerProps,
|
|
118
584
|
PopoverAnchorProps,
|
|
119
|
-
|
|
120
|
-
|
|
585
|
+
PopoverTriggerProps,
|
|
586
|
+
PopoverVirtualTriggerProps,
|
|
587
|
+
PopoverPortalProps,
|
|
121
588
|
PopoverContentProps,
|
|
589
|
+
PopoverCloseProps,
|
|
590
|
+
PopoverArrowProps,
|
|
122
591
|
PopoverViewportProps,
|
|
123
592
|
};
|