MessagePopover: Overflow Arrows
This commit is contained in:
parent
aa89a8cdd1
commit
0979c5c82c
4 changed files with 170 additions and 1 deletions
60
src/api/MessagePopover.css
Normal file
60
src/api/MessagePopover.css
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
.vc-message-popover-slot {
|
||||
inset-inline-start: 0;
|
||||
}
|
||||
|
||||
.vc-message-popover {
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.vc-message-popover .vc-message-popover-bar {
|
||||
display: flex;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.vc-popover-overflow {
|
||||
min-width: 0;
|
||||
overflow-x: auto;
|
||||
scrollbar-width: none;
|
||||
container-type: scroll-state;
|
||||
}
|
||||
|
||||
.vc-popover-overflow::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vc-popover-overflow-track {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
.vc-popover-overflow .vc-popover-overflow-arrow {
|
||||
display: none;
|
||||
position: sticky;
|
||||
z-index: 1;
|
||||
width: 24px;
|
||||
background: var(--background-surface-high);
|
||||
}
|
||||
|
||||
.vc-popover-overflow .vc-popover-overflow-arrow-left {
|
||||
left: 0;
|
||||
margin-inline-end: -24px;
|
||||
}
|
||||
|
||||
.vc-popover-overflow .vc-popover-overflow-arrow-right {
|
||||
right: 0;
|
||||
margin-inline-start: -24px;
|
||||
}
|
||||
|
||||
@container scroll-state(scrollable: left) {
|
||||
.vc-popover-overflow .vc-popover-overflow-arrow-left {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
@container scroll-state(scrollable: right) {
|
||||
.vc-popover-overflow .vc-popover-overflow-arrow-right {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
|
@ -16,16 +16,25 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import "./MessagePopover.css";
|
||||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { ChevronSmallLeftIcon, ChevronSmallRightIcon } from "@components/Icons";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { classes } from "@utils/misc";
|
||||
import { IconComponent } from "@utils/types";
|
||||
import { Channel, Message } from "@vencord/discord-types";
|
||||
import type { ComponentType, MouseEventHandler } from "react";
|
||||
import { findCssClassesLazy } from "@webpack";
|
||||
import { Clickable, useLayoutEffect, useRef } from "@webpack/common";
|
||||
import type { ComponentType, MouseEventHandler, ReactNode } from "react";
|
||||
|
||||
import { useSettings } from "./Settings";
|
||||
|
||||
const logger = new Logger("MessagePopover");
|
||||
|
||||
const ToolbarClasses = findCssClassesLazy("wrapper", "button", "selected", "separator", "disabled", "dangerous");
|
||||
const HoverBarClasses = findCssClassesLazy("popover", "hoverBarButton", "icon", "buttonContent");
|
||||
|
||||
export interface MessagePopoverButtonItem {
|
||||
key?: string,
|
||||
label: string,
|
||||
|
|
@ -91,6 +100,64 @@ function VencordPopoverButtons(props: { Component: React.ComponentType<MessagePo
|
|||
return <>{elements}</>;
|
||||
}
|
||||
|
||||
function OverflowArrow({ direction, onClick }: { direction: "left" | "right", onClick(): void; }) {
|
||||
const Icon = direction === "left" ? ChevronSmallLeftIcon : ChevronSmallRightIcon;
|
||||
|
||||
return (
|
||||
<div className={`vc-popover-overflow-arrow vc-popover-overflow-arrow-${direction}`}>
|
||||
<Clickable
|
||||
className={classes(ToolbarClasses.button, HoverBarClasses.hoverBarButton)}
|
||||
onClick={onClick}
|
||||
aria-label={direction === "left" ? "Scroll left" : "Scroll right"}
|
||||
>
|
||||
<Icon className={HoverBarClasses.icon} />
|
||||
</Clickable>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function PopoverOverflow({ children }: { children: ReactNode; }) {
|
||||
const scrollerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const scroller = scrollerRef.current;
|
||||
if (!scroller) return;
|
||||
scroller.scrollLeft = scroller.scrollWidth;
|
||||
|
||||
const onWheel = (e: WheelEvent) => {
|
||||
if (scroller.scrollWidth <= scroller.clientWidth) return;
|
||||
e.preventDefault();
|
||||
const delta = Math.abs(e.deltaX) > Math.abs(e.deltaY) ? e.deltaX : e.deltaY;
|
||||
scroller.scrollLeft += e.deltaMode === WheelEvent.DOM_DELTA_LINE ? delta * 16 : delta;
|
||||
};
|
||||
scroller.addEventListener("wheel", onWheel, { passive: false });
|
||||
return () => scroller.removeEventListener("wheel", onWheel);
|
||||
}, []);
|
||||
|
||||
const scroll = (direction: 1 | -1) => {
|
||||
const scroller = scrollerRef.current;
|
||||
scroller?.scrollBy({ left: direction * scroller.clientWidth * 0.75, behavior: "smooth" });
|
||||
};
|
||||
|
||||
return (
|
||||
<div ref={scrollerRef} className="vc-popover-overflow">
|
||||
<div className="vc-popover-overflow-track">
|
||||
<OverflowArrow direction="left" onClick={() => scroll(-1)} />
|
||||
{children}
|
||||
<OverflowArrow direction="right" onClick={() => scroll(1)} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function _wrapPopoverBar(children: ReactNode[]) {
|
||||
return (
|
||||
<ErrorBoundary fallback={() => <>{...children}</>}>
|
||||
<PopoverOverflow>{...children}</PopoverOverflow>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
||||
export function _buildPopoverElements(
|
||||
Component: React.ComponentType<MessagePopoverButtonItem>,
|
||||
message: Message
|
||||
|
|
|
|||
|
|
@ -791,6 +791,34 @@ export function ChevronSmallUpIcon(props: IconProps) {
|
|||
);
|
||||
}
|
||||
|
||||
export function ChevronSmallLeftIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill={props.fill || "currentColor"}
|
||||
d="M14.7 5.3a1 1 0 0 1 0 1.4L9.41 12l5.3 5.3a1 1 0 0 1-1.42 1.4l-6-6a1 1 0 0 1 0-1.4l6-6a1 1 0 0 1 1.42 0Z"
|
||||
/>
|
||||
</Icon>
|
||||
);
|
||||
}
|
||||
|
||||
export function ChevronSmallRightIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill={props.fill || "currentColor"}
|
||||
d="M9.3 5.3a1 1 0 0 0 0 1.4l5.29 5.3-5.3 5.3a1 1 0 1 0 1.42 1.4l6-6a1 1 0 0 0 0-1.4l-6-6a1 1 0 0 0-1.42 0Z"
|
||||
/>
|
||||
</Icon>
|
||||
);
|
||||
}
|
||||
|
||||
export function DownArrow(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
|
|
|
|||
|
|
@ -31,6 +31,20 @@ export default definePlugin({
|
|||
replace: (_, ReactButton, ButtonComponent, showReactButton, message) => "" +
|
||||
`]}):null,Vencord.Api.MessagePopover._buildPopoverElements(${ButtonComponent},${message}),${showReactButton}?${ReactButton}:null,`
|
||||
}
|
||||
},
|
||||
{
|
||||
find: "#{intl::MESSAGE_UTILITIES_A11Y_LABEL}",
|
||||
replacement: {
|
||||
match: /className:(\i\(\)\(\i\.className,.{0,80}?\)),(onClick:.{0,150}?children:\(0,\i\.jsxs?\)\(\i,\{className:)(\i\.innerClassName),children:(\[\i,\i\])/,
|
||||
replace: 'className:"vc-message-popover "+$1,$2$3+" vc-message-popover-bar",children:Vencord.Api.MessagePopover._wrapPopoverBar($4)'
|
||||
}
|
||||
},
|
||||
{
|
||||
find: 'role:"article",onMouseEnter',
|
||||
replacement: {
|
||||
match: /(?<=null!=(\i)\?\(0,\i\.jsxs?\)\("div",\{className:)\i\.\i(?=,children:\1\}\))/,
|
||||
replace: '$&+" vc-message-popover-slot"'
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue