first stab at virtual branch UI

This commit is contained in:
Kiril Videlov 2023-06-02 11:46:47 +02:00 committed by Kiril Videlov
parent b52ad2d55f
commit 02e81b71dd
8 changed files with 158 additions and 3 deletions

View File

@ -61,6 +61,7 @@
"inter-ui": "^3.19.3",
"leven": "^4.0.0",
"marked": "^5.0.2",
"mm-jsr": "^3.0.2",
"nanoevents": "^7.0.1",
"nanoid": "^4.0.1",
"postcss": "^8.4.14",
@ -71,6 +72,7 @@
"prettier-plugin-tailwindcss": "^0.3.0",
"svelte": "~3.55.1",
"svelte-check": "^3.0.1",
"svelte-dnd-action": "^0.9.22",
"svelte-floating-ui": "^1.5.2",
"svelte-french-toast": "^1.0.3",
"svelte-loadable-store": "^1.2.3",
@ -88,7 +90,6 @@
"xterm-addon-fit": "^0.7.0",
"xterm-addon-ligatures": "^0.6.0",
"xterm-addon-unicode11": "^0.5.0",
"xterm-addon-webgl": "^0.14.0",
"mm-jsr": "^3.0.2"
"xterm-addon-webgl": "^0.14.0"
}
}

View File

@ -169,6 +169,9 @@ devDependencies:
svelte-check:
specifier: ^3.0.1
version: 3.0.3(postcss-load-config@4.0.1)(postcss@8.4.21)(svelte@3.55.1)
svelte-dnd-action:
specifier: ^0.9.22
version: 0.9.22(svelte@3.55.1)
svelte-floating-ui:
specifier: ^1.5.2
version: 1.5.2
@ -3683,6 +3686,14 @@ packages:
- sugarss
dev: true
/svelte-dnd-action@0.9.22(svelte@3.55.1):
resolution: {integrity: sha512-lOQJsNLM1QWv5mdxIkCVtk6k4lHCtLgfE59y8rs7iOM6erchbLC9hMEFYSveZz7biJV0mpg7yDSs4bj/RT/YkA==}
peerDependencies:
svelte: '>=3.23.0'
dependencies:
svelte: 3.55.1
dev: true
/svelte-eslint-parser@0.29.0(svelte@3.55.1):
resolution: {integrity: sha512-2uzOw9vRpSO3fo6NkbH7UynfCopQbMz/7LO9KT05YPvkB0uuFvFHex8+Ccv3gSrxHRvKS7FwJmV4H8WNWIzgWQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}

8
src/global.d.ts vendored Normal file
View File

@ -0,0 +1,8 @@
declare type Item = import('svelte-dnd-action').Item;
declare type DndEvent<ItemType = Item> = import('svelte-dnd-action').DndEvent<ItemType>;
declare namespace svelte.JSX {
interface HTMLAttributes<T> {
onconsider?: (event: CustomEvent<DndEvent<ItemType>> & { target: EventTarget & T }) => void;
onfinalize?: (event: CustomEvent<DndEvent<ItemType>> & { target: EventTarget & T }) => void;
}
}

View File

@ -36,7 +36,8 @@
hotkeys.on('Meta+T', () => goto(`/projects/${$project.id}/terminal/`)),
hotkeys.on('Meta+P', () => goto(`/projects/${$project.id}/`)),
hotkeys.on('Meta+Shift+,', () => goto(`/projects/${$project.id}/settings/`)),
hotkeys.on('Meta+R', () => goto(`/projects/${$project.id}/player/`))
hotkeys.on('Meta+R', () => goto(`/projects/${$project.id}/player/`)),
hotkeys.on('l f g', () => goto(`/projects_new/${$project.id}`))
)
);
</script>

View File

@ -0,0 +1,48 @@
<script lang="ts">
import Board from './Board.svelte';
import type { BranchLane } from './board';
let columnsData: BranchLane[] = [
{
id: 'c1',
name: 'TODO',
items: [
{ id: 1, name: 'item41' },
{ id: 2, name: 'item42' },
{ id: 3, name: 'item43' },
{ id: 4, name: 'item44' },
{ id: 5, name: 'item45' },
{ id: 6, name: 'item46' },
{ id: 7, name: 'item47' },
{ id: 8, name: 'item48' },
{ id: 9, name: 'item49' }
]
},
{
id: 'c2',
name: 'DOING',
items: [
{ id: 10, name: 'item50' },
{ id: 11, name: 'item51' }
]
},
{
id: 'c3',
name: 'DONE',
items: [{ id: 13, name: 'item52' }]
}
];
function handleBoardUpdated(newColumnsData: BranchLane[]) {
// TODO: API calls
columnsData = newColumnsData;
}
</script>
<Board columns={columnsData} onFinalUpdate={handleBoardUpdated} />
<style>
:global(*) {
box-sizing: border-box;
margin: 0;
}
</style>

View File

@ -0,0 +1,38 @@
<script lang="ts">
import { flip } from 'svelte/animate';
import { dndzone } from 'svelte-dnd-action';
import type { FileCard, BranchLane } from './board';
import Lane from './Lane.svelte';
const flipDurationMs = 300;
export let columns: BranchLane[];
export let onFinalUpdate: (newColumns: BranchLane[]) => void;
function handleDndConsiderColumns(e: { detail: { items: BranchLane[] } }) {
columns = e.detail.items;
}
function handleDndFinalizeColumns(e: { detail: { items: BranchLane[] } }) {
onFinalUpdate(e.detail.items);
}
function handleItemFinalize(columnIdx: number, newItems: FileCard[]) {
columns[columnIdx].items = newItems;
onFinalUpdate([...columns]);
}
</script>
<section
class="w-100"
style="height: 90vh;"
use:dndzone={{ items: columns, flipDurationMs, type: 'column' }}
on:consider={handleDndConsiderColumns}
on:finalize={handleDndFinalizeColumns}
>
{#each columns as { id, name, items }, idx (id)}
<div
class="float-left m-2 flex h-full w-64 border border-zinc-700 bg-zinc-900/50 p-2"
animate:flip={{ duration: flipDurationMs }}
>
<Lane {name} {items} onDrop={(newItems) => handleItemFinalize(idx, newItems)} />
</div>
{/each}
</section>

View File

@ -0,0 +1,38 @@
<script lang="ts">
import { flip } from 'svelte/animate';
import { dndzone } from 'svelte-dnd-action';
import type { FileCard } from './board';
const flipDurationMs = 150;
export let name: string;
export let items: FileCard[];
export let onDrop: (items: FileCard[]) => void;
function handleDndConsiderCards(e: { detail: { items: FileCard[] } }) {
console.warn('got consider', name);
items = e.detail.items;
}
function handleDndFinalizeCards(e: { detail: { items: FileCard[] } }) {
onDrop(e.detail.items);
}
</script>
<div class="h-full w-full overflow-y-hidden">
<div class="flex h-12 items-center justify-center font-bold">
{name}
</div>
<div
class="overflow-y-scroll"
style="height: calc(100% - 2.5em);"
use:dndzone={{ items, flipDurationMs, zoneTabIndex: -1 }}
on:consider={handleDndConsiderCards}
on:finalize={handleDndFinalizeCards}
>
{#each items as item (item.id)}
<div
class="my-2 flex h-14 w-full items-center justify-center rounded border border-zinc-600 bg-zinc-700"
>
{item.name}
</div>
{/each}
</div>
</div>

View File

@ -0,0 +1,10 @@
export type FileCard = {
id: number;
name: string;
};
export type BranchLane = {
id: string;
name: string;
items: FileCard[];
};