epic context menu
This commit is contained in:
parent
d300366018
commit
ad61294642
5 changed files with 168 additions and 56 deletions
|
|
@ -35,9 +35,15 @@ function WindowFrame() {
|
|||
});
|
||||
|
||||
return (
|
||||
<div data-tauri-drag-region class="flex flex-row justify-end items-center w-screen h-8 bg-secondary gap-0.5 pr-0.5">
|
||||
<TitlebarButton icon={MinusIcon} onClick={() => minimize()} />
|
||||
<TitlebarButton icon={XCloseIcon} onClick={() => setModalVisible(true)} danger />
|
||||
<div data-tauri-drag-region class="flex flex-row justify-between items-center w-screen h-8 bg-secondary gap-0.5 pr-0.5">
|
||||
<div class="flex flex-row items-center">
|
||||
{/* Empty */}
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center justify-end">
|
||||
<TitlebarButton icon={MinusIcon} onClick={() => minimize()} />
|
||||
<TitlebarButton icon={XCloseIcon} onClick={() => setModalVisible(true)} danger />
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
title="Close OneLauncher?"
|
||||
|
|
|
|||
67
src/ui/components/overlay/ContextMenu.tsx
Normal file
67
src/ui/components/overlay/ContextMenu.tsx
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import { type Accessor, type JSX, createEffect, createSignal, onMount } from 'solid-js';
|
||||
import Popup from './Popup';
|
||||
|
||||
type ContextMenuProps = {
|
||||
pos: Accessor<{ x: number; y: number }>;
|
||||
} & Popup.PopupProps;
|
||||
|
||||
function ContextMenu(props: ContextMenuProps) {
|
||||
const [pos, setPos] = createSignal({ x: 0, y: 0 });
|
||||
const [ref, setRef] = createSignal<HTMLDivElement>();
|
||||
|
||||
createEffect(() => {
|
||||
if (ref() === undefined)
|
||||
return;
|
||||
|
||||
const rect = ref()!.getBoundingClientRect();
|
||||
|
||||
let x = props.pos().x;
|
||||
let y = props.pos().y;
|
||||
|
||||
if (x + rect.width > window.innerWidth)
|
||||
x = window.innerWidth - rect.width - 10;
|
||||
|
||||
if (y + rect.height > window.innerHeight)
|
||||
y = window.innerHeight - rect.height - 10;
|
||||
|
||||
setPos({ x, y });
|
||||
});
|
||||
|
||||
return (
|
||||
<Popup
|
||||
style={{ top: `${pos().y}px`, left: `${pos().x}px` }}
|
||||
visible={props.visible}
|
||||
setVisible={props.setVisible}
|
||||
>
|
||||
<div ref={setRef} class="bg-secondary rounded-xl border border-gray-0.10 p-1 shadow-lg shadow-black/50">
|
||||
<div class="flex flex-col gap-y-1 text-fg-primary">
|
||||
{props.children}
|
||||
</div>
|
||||
</div>
|
||||
</Popup>
|
||||
);
|
||||
}
|
||||
|
||||
ContextMenu.Seperator = function () {
|
||||
return <div class="border-b mx-1 border-gray-0.05" />;
|
||||
};
|
||||
|
||||
interface ContextMenuRowProps {
|
||||
icon: JSX.Element;
|
||||
text: string;
|
||||
onClick?: (e: MouseEvent) => any;
|
||||
}
|
||||
|
||||
ContextMenu.Row = function (props: ContextMenuRowProps) {
|
||||
return (
|
||||
<div
|
||||
onClick={e => props.onClick?.(e)}
|
||||
class="flex items-center gap-x-2 m-px px-1.5 py-0.5 rounded-mdlg hover:bg-gray-0.05 active:bg-gray-0.10 [&>svg]:w-[18px]"
|
||||
>
|
||||
{props.icon}
|
||||
{props.text}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContextMenu;
|
||||
|
|
@ -1,13 +1,12 @@
|
|||
import type {
|
||||
Accessor,
|
||||
ParentProps,
|
||||
Setter,
|
||||
} from 'solid-js';
|
||||
import {
|
||||
Show,
|
||||
createEffect,
|
||||
createSignal,
|
||||
} from 'solid-js';
|
||||
createSignal } from 'solid-js';
|
||||
import type {
|
||||
Accessor,
|
||||
JSX,
|
||||
ParentProps,
|
||||
Setter } from 'solid-js';
|
||||
import { Portal } from 'solid-js/web';
|
||||
|
||||
// eslint-disable-next-line ts/no-namespace
|
||||
|
|
@ -16,6 +15,7 @@ declare namespace Popup {
|
|||
visible: Accessor<boolean>;
|
||||
setVisible: Setter<boolean>;
|
||||
class?: string;
|
||||
style?: JSX.CSSProperties | string;
|
||||
mount?: Node;
|
||||
}
|
||||
}
|
||||
|
|
@ -54,7 +54,7 @@ function Popup(props: Popup.PopupProps) {
|
|||
return (
|
||||
<Show when={localVisible()}>
|
||||
<Portal mount={props.mount || document.body}>
|
||||
<div ref={popupRef} class={`absolute z-[1000] ${animate()} ${props.class || ''}`}>
|
||||
<div ref={popupRef} style={props.style || ''} class={`absolute z-[1000] ${animate()} ${props.class || ''}`}>
|
||||
{props.children}
|
||||
</div>
|
||||
</Portal>
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@ import {
|
|||
CheckIcon,
|
||||
DotsVerticalIcon,
|
||||
Download01Icon,
|
||||
EyeIcon,
|
||||
PlayIcon,
|
||||
PlusIcon,
|
||||
SearchMdIcon,
|
||||
} from '@untitled-theme/icons-solid';
|
||||
import { For, createSignal } from 'solid-js';
|
||||
import type { Accessor, Setter } from 'solid-js';
|
||||
import { For, createSignal, untrack } from 'solid-js';
|
||||
import { DragDropProvider, DragDropSensors, SortableProvider, closestCenter, createSortable } from '@thisbeyond/solid-dnd';
|
||||
import type { DragEventHandler, Id } from '@thisbeyond/solid-dnd';
|
||||
import { useNavigate } from '@solidjs/router';
|
||||
|
|
@ -17,6 +19,7 @@ import Tag from '../components/base/Tag';
|
|||
import TextField from '../components/base/TextField';
|
||||
import defaultCover from '../../assets/images/default_instance_cover.jpg';
|
||||
import Popup from '../components/overlay/Popup';
|
||||
import ContextMenu from '../components/overlay/ContextMenu';
|
||||
|
||||
// TODO: Replace this into it's own component
|
||||
function OneConfigLogo() {
|
||||
|
|
@ -81,6 +84,40 @@ declare module 'solid-js' {
|
|||
}
|
||||
}
|
||||
|
||||
interface InstanceCardContextMenuProps {
|
||||
pos: Accessor<{ x: number; y: number }>;
|
||||
contextMenuVisible: Accessor<boolean>;
|
||||
setContextMenuVisible: Setter<boolean>;
|
||||
id: string;
|
||||
}
|
||||
|
||||
function InstanceCardContextMenu(props: InstanceCardContextMenuProps) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<ContextMenu
|
||||
pos={props.pos}
|
||||
visible={props.contextMenuVisible}
|
||||
setVisible={props.setContextMenuVisible}
|
||||
>
|
||||
<ContextMenu.Row
|
||||
icon={<EyeIcon />}
|
||||
text="View"
|
||||
onClick={() => {
|
||||
navigate(`/instances/?id=${props.id}`);
|
||||
}}
|
||||
/>
|
||||
|
||||
<ContextMenu.Seperator />
|
||||
|
||||
<ContextMenu.Row
|
||||
icon={<PlayIcon />}
|
||||
text="Launch"
|
||||
/>
|
||||
</ContextMenu>
|
||||
);
|
||||
}
|
||||
|
||||
function InstanceCard(props: InstanceCardProps) {
|
||||
const navigate = useNavigate();
|
||||
const [pos, setPos] = createSignal({ x: 0, y: 0 });
|
||||
|
|
@ -89,7 +126,6 @@ function InstanceCard(props: InstanceCardProps) {
|
|||
const id = () => props.elementId as Id;
|
||||
let ref!: HTMLDivElement;
|
||||
|
||||
// eslint-disable-next-line solid/reactivity
|
||||
const sortable = createSortable(id());
|
||||
|
||||
function didDrag() {
|
||||
|
|
@ -131,51 +167,50 @@ function InstanceCard(props: InstanceCardProps) {
|
|||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
use:sortable={sortable}
|
||||
onClick={e => openInstancePage(e)}
|
||||
onMouseDown={e => onMouseDown(e)}
|
||||
onMouseUp={e => onMouseUp(e)}
|
||||
onContextMenu={e => openContextMenu(e)}
|
||||
ref={ref}
|
||||
class="relative h-[152px] group flex flex-col rounded-xl bg-component-bg hover:bg-component-bg-hover active:bg-component-bg-pressed border border-gray-0.05"
|
||||
>
|
||||
<div class="flex-1 relative overflow-hidden rounded-t-xl">
|
||||
<div
|
||||
class="absolute h-full w-full group-hover:!scale-110 transition-transform"
|
||||
style={{ '-webkit-transform': 'translateZ(0)' }} // WebKit starts flickering whenever we try scrolling. This is a 'fix'
|
||||
>
|
||||
<img
|
||||
class={`object-cover h-full w-full ${props.cover ? '' : ' filter grayscale'}`}
|
||||
src={props.cover || defaultCover}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="z-10 p-3 flex flex-row items-center justify-between">
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<p class="h-4 font-semibold">{props.name}</p>
|
||||
<p class="h-4 text-xs">
|
||||
{props.clientType}
|
||||
{' '}
|
||||
{props.version}
|
||||
{' '}
|
||||
{props.mods && `• ${props.mods} mods`}
|
||||
</p>
|
||||
</div>
|
||||
<Button onClick={e => openContextMenu(e)} styleType="icon" class="w-8 h-8">
|
||||
<DotsVerticalIcon />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Popup visible={contextMenuVisible} setVisible={setContextMenuVisible}>
|
||||
<div class="bg-secondary rounded-xl border border-gray-0.10 w-72 p-2 shadow-lg shadow-black/50">
|
||||
<div class="flex flex-col gap-y-2 text-fg-primary">
|
||||
<p>hello world</p>
|
||||
|
||||
<>
|
||||
<div
|
||||
use:sortable={sortable}
|
||||
onClick={e => openInstancePage(e)}
|
||||
onMouseDown={e => onMouseDown(e)}
|
||||
onMouseUp={e => onMouseUp(e)}
|
||||
onContextMenu={e => openContextMenu(e)}
|
||||
ref={ref}
|
||||
class="relative h-[152px] group flex flex-col rounded-xl bg-component-bg hover:bg-component-bg-hover active:bg-component-bg-pressed border border-gray-0.05"
|
||||
>
|
||||
<div class="flex-1 relative overflow-hidden rounded-t-xl">
|
||||
<div
|
||||
class="absolute h-full w-full group-hover:!scale-110 transition-transform"
|
||||
style={{ '-webkit-transform': 'translateZ(0)' }}
|
||||
>
|
||||
<img
|
||||
class={`object-cover h-full w-full ${props.cover ? '' : ' filter grayscale'}`}
|
||||
src={props.cover || defaultCover}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Popup>
|
||||
</div>
|
||||
<div class="z-10 p-3 flex flex-row items-center justify-between">
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<p class="h-4 font-semibold">{props.name}</p>
|
||||
<p class="h-4 text-xs">
|
||||
{props.clientType}
|
||||
{' '}
|
||||
{props.version}
|
||||
{' '}
|
||||
{props.mods && `• ${props.mods} mods`}
|
||||
</p>
|
||||
</div>
|
||||
<Button onClick={e => openContextMenu(e)} styleType="icon" class="w-8 h-8">
|
||||
<DotsVerticalIcon />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<InstanceCardContextMenu
|
||||
contextMenuVisible={contextMenuVisible}
|
||||
setContextMenuVisible={setContextMenuVisible}
|
||||
pos={pos}
|
||||
id={props.id}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -88,6 +88,10 @@ const config: Config = {
|
|||
transparent: 'transparent',
|
||||
},
|
||||
|
||||
borderRadius: {
|
||||
mdlg: '0.438rem',
|
||||
},
|
||||
|
||||
animation: {
|
||||
'fade-in': 'fadeIn 150ms easeOutExpo forwards',
|
||||
'fade-out': 'fadeOut 150ms easeOutExpo forwards',
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue