Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd146651

Browse files
committed
feat: add shadcnui popover, deprecate MUI popover
1 parent202f7f7 commitd146651

File tree

36 files changed

+663
-300
lines changed

36 files changed

+663
-300
lines changed

‎site/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"@mui/x-tree-view":"7.18.0",
5353
"@radix-ui/react-dialog":"1.1.2",
5454
"@radix-ui/react-label":"2.1.0",
55+
"@radix-ui/react-popover":"1.1.3",
5556
"@radix-ui/react-slider":"1.2.1",
5657
"@radix-ui/react-slot":"1.1.0",
5758
"@radix-ui/react-switch":"1.1.1",

‎site/pnpm-lock.yaml‎

Lines changed: 267 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎site/src/components/FeatureStageBadge/FeatureStageBadge.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { Interpolation, Theme } from "@emotion/react";
22
importLinkfrom"@mui/material/Link";
33
import{visuallyHidden}from"@mui/utils";
44
import{HelpTooltipContent}from"components/HelpTooltip/HelpTooltip";
5-
import{Popover,PopoverTrigger}from"components/Popover/Popover";
5+
import{Popover,PopoverTrigger}from"components/deprecated/Popover/Popover";
66
importtype{FC,HTMLAttributes,ReactNode}from"react";
77
import{docs}from"utils/docs";
88

‎site/src/components/HelpTooltip/HelpTooltip.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ import {
88
importHelpIconfrom"@mui/icons-material/HelpOutline";
99
importOpenInNewIconfrom"@mui/icons-material/OpenInNew";
1010
importLinkfrom"@mui/material/Link";
11+
import{Stack}from"components/Stack/Stack";
1112
import{
1213
Popover,
1314
PopoverContent,
1415
typePopoverContentProps,
1516
typePopoverProps,
1617
PopoverTrigger,
1718
usePopover,
18-
}from"components/Popover/Popover";
19-
import{Stack}from"components/Stack/Stack";
19+
}from"components/deprecated/Popover/Popover";
2020
import{
2121
typeFC,
2222
typeHTMLAttributes,

‎site/src/components/IconField/IconField.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ import { visuallyHidden } from "@mui/utils";
66
import{DropdownArrow}from"components/DropdownArrow/DropdownArrow";
77
import{ExternalImage}from"components/ExternalImage/ExternalImage";
88
import{Loader}from"components/Loader/Loader";
9+
import{Stack}from"components/Stack/Stack";
910
import{
1011
Popover,
1112
PopoverContent,
1213
PopoverTrigger,
13-
}from"components/Popover/Popover";
14-
import{Stack}from"components/Stack/Stack";
14+
}from"components/deprecated/Popover/Popover";
1515
import{typeFC,Suspense,lazy,useState}from"react";
1616

1717
// See: https://github.com/missive/emoji-mart/issues/51#issuecomment-287353222

‎site/src/components/Popover/Popover.stories.tsx‎

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
importButtonfrom"@mui/material/Button";
21
importtype{Meta,StoryObj}from"@storybook/react";
32
import{expect,screen,userEvent,waitFor,within}from"@storybook/test";
3+
import{Button}from"components/Button/Button";
44
import{Popover,PopoverContent,PopoverTrigger}from"./Popover";
55

66
constmeta:Meta<typeofPopover>={
@@ -17,15 +17,15 @@ Its wings are too small to get its fat little body off the ground. The bee, of c
1717
flies anyway because bees don't care what humans think is impossible.
1818
`;
1919

20-
exportconstExample:Story={
20+
exportconstDefault:Story={
2121
args:{
2222
children:(
23-
<>
24-
<PopoverTrigger>
25-
<Button>Click here!</Button>
23+
<Popover>
24+
<PopoverTriggerasChild>
25+
<ButtonclassName="ml-20">Click here!</Button>
2626
</PopoverTrigger>
2727
<PopoverContent>{content}</PopoverContent>
28-
</>
28+
</Popover>
2929
),
3030
},
3131
play:async({ canvasElement, step})=>{
@@ -42,16 +42,16 @@ export const Example: Story = {
4242
},
4343
};
4444

45-
exportconstHorizontal:Story={
45+
exportconstAlignStart:Story={
4646
args:{
4747
children:(
48-
<>
49-
<PopoverTrigger>
50-
<Button>Click here!</Button>
48+
<Popover>
49+
<PopoverTriggerasChild>
50+
<ButtonclassName="ml-20">Click here!</Button>
5151
</PopoverTrigger>
52-
<PopoverContenthorizontal="right">{content}</PopoverContent>
53-
</>
52+
<PopoverContentalign="start">{content}</PopoverContent>
53+
</Popover>
5454
),
5555
},
56-
play:Example.play,
56+
play:Default.play,
5757
};
Lines changed: 32 additions & 237 deletions
Original file line numberDiff line numberDiff line change
@@ -1,240 +1,35 @@
1-
importMuiPopover,{
2-
typePopoverPropsasMuiPopoverProps,
3-
// biome-ignore lint/nursery/noRestrictedImports: This is the base component that our custom popover is based on
4-
}from"@mui/material/Popover";
1+
import*asPopoverPrimitivefrom"@radix-ui/react-popover";
52
import{
6-
typeFC,
7-
typeHTMLAttributes,
8-
typePointerEvent,
9-
typePointerEventHandler,
10-
typeReactElement,
11-
typeReactNode,
12-
typeRefObject,
13-
cloneElement,
14-
createContext,
15-
useContext,
16-
useEffect,
17-
useId,
18-
useRef,
19-
useState,
3+
typeComponentPropsWithoutRef,
4+
typeElementRef,
5+
forwardRef,
206
}from"react";
21-
22-
typeTriggerMode="hover"|"click";
23-
24-
typeTriggerRef=RefObject<HTMLElement>;
25-
26-
// Have to append ReactNode type to satisfy React's cloneElement function. It
27-
// has absolutely no bearing on what happens at runtime
28-
typeTriggerElement=ReactNode&
29-
ReactElement<{
30-
ref:TriggerRef;
31-
onClick?:()=>void;
32-
}>;
33-
34-
typePopoverContextValue={
35-
id:string;
36-
open:boolean;
37-
setOpen:(open:boolean)=>void;
38-
triggerRef:TriggerRef;
39-
mode:TriggerMode;
40-
};
41-
42-
constPopoverContext=createContext<PopoverContextValue|undefined>(
43-
undefined,
44-
);
45-
46-
typeBasePopoverProps={
47-
children:ReactNode;
48-
mode?:TriggerMode;
49-
};
50-
51-
// By separating controlled and uncontrolled props, we achieve more accurate
52-
// type inference.
53-
typeUncontrolledPopoverProps=BasePopoverProps&{
54-
open?:undefined;
55-
onOpenChange?:undefined;
56-
};
57-
58-
typeControlledPopoverProps=BasePopoverProps&{
59-
open:boolean;
60-
onOpenChange:(open:boolean)=>void;
61-
};
62-
63-
exporttypePopoverProps=UncontrolledPopoverProps|ControlledPopoverProps;
64-
65-
exportconstPopover:FC<PopoverProps>=(props)=>{
66-
consthookId=useId();
67-
const[uncontrolledOpen,setUncontrolledOpen]=useState(false);
68-
consttriggerRef:TriggerRef=useRef(null);
69-
70-
// Helps makes sure that popovers close properly when the user switches to
71-
// a different tab. This won't help with controlled instances of the
72-
// component, but this is basically the most we can do from here
73-
useEffect(()=>{
74-
constcloseOnTabSwitch=()=>setUncontrolledOpen(false);
75-
window.addEventListener("blur",closeOnTabSwitch);
76-
return()=>window.removeEventListener("blur",closeOnTabSwitch);
77-
},[]);
78-
79-
constvalue:PopoverContextValue={
80-
triggerRef,
81-
id:`${hookId}-popover`,
82-
mode:props.mode??"click",
83-
open:props.open??uncontrolledOpen,
84-
setOpen:props.onOpenChange??setUncontrolledOpen,
85-
};
86-
87-
return(
88-
<PopoverContext.Providervalue={value}>
89-
{props.children}
90-
</PopoverContext.Provider>
91-
);
92-
};
93-
94-
exportconstusePopover=()=>{
95-
constcontext=useContext(PopoverContext);
96-
if(!context){
97-
thrownewError(
98-
"Popover compound components cannot be rendered outside the Popover component",
99-
);
100-
}
101-
returncontext;
102-
};
103-
104-
typePopoverTriggerRenderProps=Readonly<{
105-
isOpen:boolean;
106-
}>;
107-
108-
typePopoverTriggerProps=Readonly<
109-
Omit<HTMLAttributes<HTMLElement>,"children">&{
110-
children:
111-
|TriggerElement
112-
|((props:PopoverTriggerRenderProps)=>TriggerElement);
113-
}
114-
>;
115-
116-
exportconstPopoverTrigger:FC<PopoverTriggerProps>=(props)=>{
117-
constpopover=usePopover();
118-
const{ children, onClick, onPointerEnter, onPointerLeave, ...elementProps}=
119-
props;
120-
121-
constclickProps={
122-
onClick:(event:PointerEvent<HTMLElement>)=>{
123-
popover.setOpen(true);
124-
onClick?.(event);
125-
},
126-
};
127-
128-
consthoverProps={
129-
onPointerEnter:(event:PointerEvent<HTMLElement>)=>{
130-
popover.setOpen(true);
131-
onPointerEnter?.(event);
132-
},
133-
onPointerLeave:(event:PointerEvent<HTMLElement>)=>{
134-
popover.setOpen(false);
135-
onPointerLeave?.(event);
136-
},
137-
};
138-
139-
constevaluatedChildren=
140-
typeofchildren==="function"
141-
?children({isOpen:popover.open})
142-
:children;
143-
144-
returncloneElement(evaluatedChildren,{
145-
...elementProps,
146-
...(popover.mode==="click" ?clickProps :hoverProps),
147-
"aria-haspopup":true,
148-
"aria-owns":popover.id,
149-
"aria-expanded":popover.open,
150-
ref:popover.triggerRef,
151-
});
152-
};
153-
154-
typeHorizontal="left"|"right";
155-
156-
exporttypePopoverContentProps=Omit<
157-
MuiPopoverProps,
158-
"open"|"onClose"|"anchorEl"
159-
>&{
160-
horizontal?:Horizontal;
161-
};
162-
163-
exportconstPopoverContent:FC<PopoverContentProps>=({
164-
horizontal="left",
165-
onPointerEnter,
166-
onPointerLeave,
167-
...popoverProps
168-
})=>{
169-
constpopover=usePopover();
170-
consthoverMode=popover.mode==="hover";
171-
172-
return(
173-
<MuiPopover
174-
disablePortal
175-
css={{
176-
// When it is on hover mode, and the mode is moving from the trigger to
177-
// the popover, if there is any space, the popover will be closed. I
178-
// found this is a limitation on how MUI structured the component. It is
179-
// not a big issue for now but we can re-evaluate it in the future.
180-
marginTop:hoverMode ?undefined :8,
181-
pointerEvents:hoverMode ?"none" :undefined,
182-
"& .MuiPaper-root":{
183-
minWidth:320,
184-
fontSize:14,
185-
pointerEvents:hoverMode ?"auto" :undefined,
186-
},
187-
}}
188-
{...horizontalProps(horizontal)}
189-
{...modeProps(popover,onPointerEnter,onPointerLeave)}
190-
{...popoverProps}
191-
id={popover.id}
192-
open={popover.open}
193-
onClose={()=>popover.setOpen(false)}
194-
anchorEl={popover.triggerRef.current}
7+
import{cn}from"utils/cn";
8+
9+
exportconstPopover=PopoverPrimitive.Root;
10+
11+
exportconstPopoverTrigger=PopoverPrimitive.Trigger;
12+
13+
exportconstPopoverContent=forwardRef<
14+
ElementRef<typeofPopoverPrimitive.Content>,
15+
ComponentPropsWithoutRef<typeofPopoverPrimitive.Content>
16+
>(({ className, align="center", sideOffset=4, ...props},ref)=>(
17+
<PopoverPrimitive.Portal>
18+
<PopoverPrimitive.Content
19+
ref={ref}
20+
align={align}
21+
sideOffset={sideOffset}
22+
className={cn(
23+
`z-50 w-72 rounded-md border border-solid bg-surface-primary p-4
24+
text-content-primary shadow-md outline-none
25+
data-[state=open]:animate-in data-[state=closed]:animate-out
26+
data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0
27+
data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95
28+
data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2
29+
data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2`,
30+
className,
31+
)}
32+
{...props}
19533
/>
196-
);
197-
};
198-
199-
constmodeProps=(
200-
popover:PopoverContextValue,
201-
externalOnPointerEnter:PointerEventHandler<HTMLDivElement>|undefined,
202-
externalOnPointerLeave:PointerEventHandler<HTMLDivElement>|undefined,
203-
)=>{
204-
if(popover.mode==="hover"){
205-
return{
206-
onPointerEnter:(event:PointerEvent<HTMLDivElement>)=>{
207-
popover.setOpen(true);
208-
externalOnPointerEnter?.(event);
209-
},
210-
onPointerLeave:(event:PointerEvent<HTMLDivElement>)=>{
211-
popover.setOpen(false);
212-
externalOnPointerLeave?.(event);
213-
},
214-
};
215-
}
216-
217-
return{};
218-
};
219-
220-
consthorizontalProps=(horizontal:Horizontal)=>{
221-
if(horizontal==="right"){
222-
return{
223-
anchorOrigin:{
224-
vertical:"bottom",
225-
horizontal:"right",
226-
},
227-
transformOrigin:{
228-
vertical:"top",
229-
horizontal:"right",
230-
},
231-
}asconst;
232-
}
233-
234-
return{
235-
anchorOrigin:{
236-
vertical:"bottom",
237-
horizontal:"left",
238-
},
239-
}asconst;
240-
};
34+
</PopoverPrimitive.Portal>
35+
));

‎site/src/components/SelectMenu/SelectMenu.tsx‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ import Button, { type ButtonProps } from "@mui/material/Button";
33
importMenuItem,{typeMenuItemProps}from"@mui/material/MenuItem";
44
importMenuList,{typeMenuListProps}from"@mui/material/MenuList";
55
import{DropdownArrow}from"components/DropdownArrow/DropdownArrow";
6-
import{
7-
Popover,
8-
PopoverContent,
9-
PopoverTrigger,
10-
}from"components/Popover/Popover";
116
import{
127
SearchField,
138
typeSearchFieldProps,
149
}from"components/SearchField/SearchField";
10+
import{
11+
Popover,
12+
PopoverContent,
13+
PopoverTrigger,
14+
}from"components/deprecated/Popover/Popover";
1515
import{
1616
Children,
1717
typeFC,

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp