add context menu, add context menu to video component
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
parent
0efd9ed1f8
commit
ed53ae1ea1
@ -0,0 +1,62 @@
|
|||||||
|
import useContextMenuStore from "@/hooks/useContextMenuStore";
|
||||||
|
import { Component } from "@/typings/component";
|
||||||
|
import { Listbox, ListboxItem } from "@nextui-org/listbox";
|
||||||
|
import { useCallback, useEffect } from "react";
|
||||||
|
|
||||||
|
const Menu: Component = () => {
|
||||||
|
const shouldShow = useContextMenuStore((state) => state.show);
|
||||||
|
const menu = useContextMenuStore((state) => state.items);
|
||||||
|
const hide = useContextMenuStore((state) => state.hide);
|
||||||
|
|
||||||
|
const location = useContextMenuStore((state) => state.location);
|
||||||
|
|
||||||
|
const handleClick = useCallback(() => {
|
||||||
|
if (shouldShow) hide();
|
||||||
|
}, [hide, shouldShow]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.addEventListener("click", handleClick);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("click", handleClick);
|
||||||
|
};
|
||||||
|
}, [hide, shouldShow]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
top: location.y,
|
||||||
|
left: location.x,
|
||||||
|
display: shouldShow ? "block" : "none"
|
||||||
|
}}
|
||||||
|
className="bg-background border-small max-w-xs px-1 py-2 rounded-small border-default-200 absolute z-10"
|
||||||
|
>
|
||||||
|
<Listbox aria-label="Context Menu">
|
||||||
|
{menu.map((item) => (
|
||||||
|
<ListboxItem
|
||||||
|
onClick={() => {
|
||||||
|
if (item.onClick) {
|
||||||
|
item.onClick();
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
showDivider={item.showDivider}
|
||||||
|
key={item.key}
|
||||||
|
href={item.href}
|
||||||
|
>
|
||||||
|
{item.title}
|
||||||
|
</ListboxItem>
|
||||||
|
))}
|
||||||
|
</Listbox>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ContextMenuProvider: Component = ({ children }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{children}
|
||||||
|
<Menu />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
@ -1,23 +1,22 @@
|
|||||||
|
import useContextMenuStore from "@/hooks/useContextMenuStore";
|
||||||
import { Component } from "@/typings/component";
|
import { Component } from "@/typings/component";
|
||||||
import { Listbox, ListboxItem } from "@nextui-org/listbox";
|
import { ContextMenuItem } from "@/typings/contextMenu";
|
||||||
|
|
||||||
export interface ContextMenuItem {
|
|
||||||
title: string;
|
|
||||||
key: string;
|
|
||||||
href?: string;
|
|
||||||
onClick?: () => any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ContextMenu: Component<{ menu: ContextMenuItem[] }> = ({
|
export const ContextMenu: Component<{ menu: ContextMenuItem[] }> = ({
|
||||||
menu
|
menu,
|
||||||
|
children
|
||||||
}) => {
|
}) => {
|
||||||
|
const showContextMenu = useContextMenuStore((state) => state.showContextMenu);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Listbox aria-label="Context Menu">
|
<div
|
||||||
{menu.map((item) => (
|
onContextMenu={(e) => {
|
||||||
<ListboxItem onClick={item.onClick} key={item.key} href={item.href}>
|
e.preventDefault();
|
||||||
{item.title}
|
|
||||||
</ListboxItem>
|
showContextMenu(e.pageX, e.pageY, menu);
|
||||||
))}
|
}}
|
||||||
</Listbox>
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
import { ContextMenuItem } from "@/typings/contextMenu";
|
||||||
|
import { create } from "zustand";
|
||||||
|
|
||||||
|
interface Location {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ContextMenuStore {
|
||||||
|
show: boolean;
|
||||||
|
location: Location;
|
||||||
|
items: ContextMenuItem[];
|
||||||
|
showContextMenu: (x: number, y: number, items: ContextMenuItem[]) => void;
|
||||||
|
hide: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const useContextMenuStore = create<ContextMenuStore>((set) => ({
|
||||||
|
show: false,
|
||||||
|
location: { x: 0, y: 0 },
|
||||||
|
items: [],
|
||||||
|
showContextMenu(x, y, items) {
|
||||||
|
set({ show: true, location: { x, y }, items });
|
||||||
|
},
|
||||||
|
hide: () => set({ show: false })
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default useContextMenuStore;
|
@ -0,0 +1,7 @@
|
|||||||
|
export interface ContextMenuItem {
|
||||||
|
title: string;
|
||||||
|
key: string;
|
||||||
|
showDivider?: boolean;
|
||||||
|
href?: string;
|
||||||
|
onClick?: () => any;
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
import { DateTime } from "luxon";
|
||||||
|
|
||||||
|
const formatUploadedTime = (uploaded: Date): string => {
|
||||||
|
return DateTime.fromJSDate(uploaded).toRelative() ?? "";
|
||||||
|
};
|
||||||
|
|
||||||
|
export default formatUploadedTime;
|
Loading…
Reference in new issue