mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 20:01:09 +02:00
feat(ui): add left/right arrow buttons to PageShell drag bar
Arrow buttons appear on hover at the left/right edges of the drag bar to quickly reorder pages without dragging. Hidden when the page is already first (no left arrow) or last (no right arrow). - PageShell: onMoveLeft/onMoveRight props, CaretLeft/CaretRight icons - AppPage: pass through move callbacks - +page.svelte: handleMoveLeft/handleMoveRight with array swap + persist Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2529c91d58
commit
ad9bbec393
3 changed files with 107 additions and 1 deletions
|
|
@ -5,7 +5,15 @@
|
|||
-->
|
||||
<script lang="ts">
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { X, Minus, DotsSixVertical, CornersOut, CornersIn } from '@manacore/shared-icons';
|
||||
import {
|
||||
X,
|
||||
Minus,
|
||||
DotsSixVertical,
|
||||
CornersOut,
|
||||
CornersIn,
|
||||
CaretLeft,
|
||||
CaretRight,
|
||||
} from '@manacore/shared-icons';
|
||||
import type { Snippet, Component } from 'svelte';
|
||||
|
||||
interface Props {
|
||||
|
|
@ -16,6 +24,8 @@
|
|||
onMinimize?: () => void;
|
||||
onMaximize?: () => void;
|
||||
onResize?: (widthPx: number, heightPx?: number) => void;
|
||||
onMoveLeft?: () => void;
|
||||
onMoveRight?: () => void;
|
||||
// Default header
|
||||
title?: string;
|
||||
color?: string;
|
||||
|
|
@ -35,6 +45,8 @@
|
|||
onMinimize,
|
||||
onMaximize,
|
||||
onResize,
|
||||
onMoveLeft,
|
||||
onMoveRight,
|
||||
title = '',
|
||||
color = '#6B7280',
|
||||
icon: IconComponent,
|
||||
|
|
@ -114,7 +126,33 @@
|
|||
: ''}"
|
||||
>
|
||||
<div class="drag-handle-bar" draggable="true">
|
||||
{#if onMoveLeft}
|
||||
<button
|
||||
class="move-btn move-left"
|
||||
onclick={(e) => {
|
||||
e.stopPropagation();
|
||||
onMoveLeft();
|
||||
}}
|
||||
draggable="false"
|
||||
title="Nach links"
|
||||
>
|
||||
<CaretLeft size={12} />
|
||||
</button>
|
||||
{/if}
|
||||
<span class="drag-handle-icon"><DotsSixVertical size={14} /></span>
|
||||
{#if onMoveRight}
|
||||
<button
|
||||
class="move-btn move-right"
|
||||
onclick={(e) => {
|
||||
e.stopPropagation();
|
||||
onMoveRight();
|
||||
}}
|
||||
draggable="false"
|
||||
title="Nach rechts"
|
||||
>
|
||||
<CaretRight size={12} />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Header -->
|
||||
|
|
@ -243,6 +281,7 @@
|
|||
}
|
||||
|
||||
.drag-handle-bar {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
|
@ -269,6 +308,46 @@
|
|||
:global(.dark) .drag-handle-bar:active {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
.move-btn {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: transparent;
|
||||
color: #d1d5db;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
transition:
|
||||
opacity 0.15s,
|
||||
color 0.15s,
|
||||
background 0.15s;
|
||||
}
|
||||
.drag-handle-bar:hover .move-btn {
|
||||
opacity: 1;
|
||||
}
|
||||
.move-btn:hover {
|
||||
color: #6b7280;
|
||||
background: rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
.move-left {
|
||||
left: 0.5rem;
|
||||
}
|
||||
.move-right {
|
||||
right: 0.5rem;
|
||||
}
|
||||
:global(.dark) .move-btn {
|
||||
color: #3f3b38;
|
||||
}
|
||||
:global(.dark) .move-btn:hover {
|
||||
color: #9ca3af;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
.drag-handle-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@
|
|||
onMinimize?: () => void;
|
||||
onMaximize?: () => void;
|
||||
onResize?: (widthPx: number, heightPx?: number) => void;
|
||||
onMoveLeft?: () => void;
|
||||
onMoveRight?: () => void;
|
||||
}
|
||||
|
||||
let {
|
||||
|
|
@ -40,6 +42,8 @@
|
|||
onMinimize,
|
||||
onMaximize,
|
||||
onResize,
|
||||
onMoveLeft,
|
||||
onMoveRight,
|
||||
}: Props = $props();
|
||||
|
||||
let appEntry = $derived(getAppEntry(appId));
|
||||
|
|
@ -203,6 +207,8 @@
|
|||
{onMinimize}
|
||||
{onMaximize}
|
||||
{onResize}
|
||||
{onMoveLeft}
|
||||
{onMoveRight}
|
||||
>
|
||||
{#if loadError}
|
||||
<div class="load-state">
|
||||
|
|
|
|||
|
|
@ -135,6 +135,24 @@
|
|||
persistState();
|
||||
}
|
||||
|
||||
function handleMoveLeft(id: string) {
|
||||
const idx = openApps.findIndex((a) => a.appId === id);
|
||||
if (idx <= 0) return;
|
||||
const apps = [...openApps];
|
||||
[apps[idx - 1], apps[idx]] = [apps[idx], apps[idx - 1]];
|
||||
openApps = apps;
|
||||
persistState();
|
||||
}
|
||||
|
||||
function handleMoveRight(id: string) {
|
||||
const idx = openApps.findIndex((a) => a.appId === id);
|
||||
if (idx === -1 || idx >= openApps.length - 1) return;
|
||||
const apps = [...openApps];
|
||||
[apps[idx], apps[idx + 1]] = [apps[idx + 1], apps[idx]];
|
||||
openApps = apps;
|
||||
persistState();
|
||||
}
|
||||
|
||||
function handleReorder(fromId: string, toId: string) {
|
||||
const fromIdx = openApps.findIndex((a) => a.appId === fromId);
|
||||
const toIdx = openApps.findIndex((a) => a.appId === toId);
|
||||
|
|
@ -166,6 +184,7 @@
|
|||
addLabel="App hinzufügen"
|
||||
>
|
||||
{#snippet page(p)}
|
||||
{@const idx = openApps.findIndex((a) => a.appId === p.id)}
|
||||
<AppPage
|
||||
appId={p.id}
|
||||
widthPx={p.widthPx}
|
||||
|
|
@ -175,6 +194,8 @@
|
|||
onMinimize={() => handleMinimizeApp(p.id)}
|
||||
onMaximize={() => handleMaximizeApp(p.id)}
|
||||
onResize={(w, h) => handleResize(p.id, w, h)}
|
||||
onMoveLeft={idx > 0 ? () => handleMoveLeft(p.id) : undefined}
|
||||
onMoveRight={idx < openApps.length - 1 ? () => handleMoveRight(p.id) : undefined}
|
||||
/>
|
||||
{/snippet}
|
||||
{#snippet picker()}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue