Skip to content

Commit

Permalink
📝 Add draggable items example
Browse files Browse the repository at this point in the history
  • Loading branch information
laurens94 committed Jul 27, 2024
1 parent 2f1f315 commit a64094a
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 67 deletions.
1 change: 1 addition & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export default defineConfig({
{ text: 'Basic example', link: '/examples/basic-example' },
{ text: 'Custom timestamp labels', link: '/examples/custom-timestamp-labels' },
{ text: 'Synced timelines', link: '/examples/synced-timelines' },
{ text: 'Draggable Items', link: '/examples/draggable-items' },
]
}
],
Expand Down
11 changes: 11 additions & 0 deletions docs/examples/draggable-items.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script setup>
import Example from './draggable-items.vue'
</script>

# Draggable Items

<Example/>

## Code

<<< ./draggable-items.vue{vue}
101 changes: 101 additions & 0 deletions docs/examples/draggable-items.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<script setup lang="ts">
import { ref } from 'vue';
const items = ref([
{ id: 1, group: 1, type: 'range', cssVariables: { '--item-background': 'var(--color-2)' }, start: 1000000, end: 4500000 },
{ id: 2, group: 2, type: 'range', cssVariables: { '--item-background': 'var(--color-4)' }, start: 4500000, end: 6000000 },
{ id: 3, group: 3, type: 'range', start: 6000000, end: 8000000 },
]);
let previousDragTimePos = 0;
let currentDragAction: 'resize-start' | 'resize-both' | 'resize-end' | undefined;
let currentDragItemId = null;
function handleItemDrag ({ time, event, item }) {
if (event.type === 'pointerdown') {
if (!event.target.dataset.action) {
return;
}
currentDragAction = event.target.dataset.action as typeof currentDragAction;
currentDragItemId = item.id;
previousDragTimePos = time;
}
else if (event.type === 'pointermove') {
if (!currentDragAction) {
return;
}
const foundItem = items.value.find(i => i.id === currentDragItemId)!;
const delta = time - previousDragTimePos;
if (currentDragAction === 'resize-start' || currentDragAction === 'resize-both') {
foundItem.start += delta;
}
if (currentDragAction === 'resize-end' || currentDragAction === 'resize-both') {
foundItem.end += delta;
}
previousDragTimePos = time;
}
}
window.addEventListener('pointerup', () => {
currentDragAction = undefined;
}, { capture: true });
</script>

<template>
<Timeline
:items="items"
:groups="[{id: 1}, {id: 2}, {id: 3}]"
:viewportMin="0"
:viewportMax="8000000"
:initialViewportStart="0"
:initialViewportEnd="8000000"
@changeViewport="viewport = $event"
@pointermove="handleItemDrag"
@pointerdown="handleItemDrag"
>
<template #item>
<div class="draggable" data-action="resize-both">
<div class="draggable-handle" data-action="resize-start"></div>
<div class="draggable-handle" data-action="resize-end"></div>
</div>
</template>
</Timeline>
</template>

<style lang="scss" scoped>
.draggable {
position: absolute;
inset: 0;
display: flex;
justify-content: space-between;
cursor: move;
.draggable-handle {
position: relative;
width: 1.2rem;
height: 100%;
cursor: ew-resize;
opacity: .6;
&::before {
content: '';
border-inline: 1px solid white;
width: 4px;
height: 40%;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
position: absolute;
}
&:hover {
opacity: 1;
}
}
}
</style>
61 changes: 28 additions & 33 deletions docs/examples/synced-timelines.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,42 @@
const timeline = ref(null);
const items = [
{ group: '1', type: 'range', cssVariables: { '--item-background': 'var(--color-2)' }, start: 1000, end: 4500 },
{ group: '2', type: 'range', cssVariables: { '--item-background': 'var(--color-4)' }, start: 4500, end: 6000 },
{ group: '3', type: 'range', start: 6000, end: 8000 },
{ group: '1', type: 'range', cssVariables: { '--item-background': 'var(--color-2)' }, start: 100000, end: 450000 },
{ group: '2', type: 'range', cssVariables: { '--item-background': 'var(--color-4)' }, start: 450000, end: 600000 },
{ group: '3', type: 'range', start: 600000, end: 800000 },
];
const viewport = ref({ start: 4000, end: 7000 });
const totalRange = ref({ start: 0, end: 8000 });
const viewport = ref({ start: 400000, end: 700000 });
const totalRange = ref({ start: 0, end: 800000 });
let isDraggingMapViewport = false;
let previousDragTimePos = 0;
function handleViewportDrag ({ time, event, item }) {
switch (event.type) {
case 'pointerdown':
if (item?.id !== 'selection') {
return;
}
isDraggingMapViewport = true;
previousDragTimePos = time;
break;
case 'pointermove': {
if (!isDraggingMapViewport) {
return;
}
const delta = time - previousDragTimePos;
const length = viewport.value.end - viewport.value.start;
if (delta < 0) {
viewport.value.start = Math.max(viewport.value.start + delta, totalRange.value.start);
viewport.value.end = viewport.value.start + length;
}
else {
viewport.value.end = Math.min(viewport.value.end + delta, totalRange.value.end);
viewport.value.start = viewport.value.end - length;
}
previousDragTimePos = time;
break;
if (event.type === 'pointerdown') {
if (item?.id !== 'selection') {
return;
}
isDraggingMapViewport = true;
previousDragTimePos = time;
}
else if (event.type === 'pointermove') {
if (!isDraggingMapViewport) {
return;
}
const delta = time - previousDragTimePos;
const length = viewport.value.end - viewport.value.start;
if (delta < 0) {
viewport.value.start = Math.max(viewport.value.start + delta, totalRange.value.start);
viewport.value.end = viewport.value.start + length;
}
else {
viewport.value.start = viewport.value.end - length;
viewport.value.end = Math.min(viewport.value.end + delta, totalRange.value.end);
}
previousDragTimePos = time;
}
}
Expand Down
69 changes: 35 additions & 34 deletions sandbox/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
</template>
</Timeline>
</div>
<details class="debug">
<details class="debug" open>
<summary>
Debug
</summary>
Expand Down Expand Up @@ -87,54 +87,55 @@
const items = computed((): customTimelineItem[] => {
return [
{ group: 'group1', type: 'point', className: 'teal', start: 1691090880000, title: '21:28:00', id: 'k802b26e-c037-4c94-b70a-187479ad90d9',
{ group: 'group1', type: 'point', start: 1691090880000, title: '21:28:00', id: 'k802b26e-c037-4c94-b70a-187479ad90d9',
},
{ group: 'group1', type: 'point', className: 'teal', start: (new Date().valueOf() - 200000), title: '21:28:00', id: 'k802b26e-c037-4c94-b70a-187479ad90d9',
{ group: 'group1', type: 'point', start: (new Date().valueOf() - 200000), title: '21:28:00', id: 'k802b26e-c037-4c94-b70a-187479ad90d9',
},
{ group: 'group1', type: 'point', className: 'teal', start: (new Date().valueOf() + 40000200000), title: '21:28:00', id: 'k802b26e-c037-4c94-b70a-187479ad90d9',
{ group: 'group1', type: 'point', start: (new Date().valueOf() + 40000200000), title: '21:28:00', id: 'k802b26e-c037-4c94-b70a-187479ad90d9',
},
{ group: 'group3', type: 'marker', className: 'teal', start: 1691090970000, id: 'k80208db-54a7-4603-8850-5a6432431dcd',
{ group: 'group3', type: 'marker', start: 1691090970000, id: 'k80208db-54a7-4603-8850-5a6432431dcd',
},
{ group: 'group1', type: 'point', className: 'teal', start: 1691099529000, title: '23:52:09', id: 'k802fabb-5dc7-486a-b205-ab27fdbf35a8',
{ group: 'group1', type: 'point', start: 1691099529000, title: '23:52:09', id: 'k802fabb-5dc7-486a-b205-ab27fdbf35a8',
},
{ type: 'background', start: 1691089380000, end: 1691090280000, id: 'k802c277-de01-4366-8b45-2ff3aa11b75e',
},
{ type: 'background', start: 1691100120000, end: 1691101020000, id: 'k802b917-3b2c-48e9-9e38-ff4df8a26c19',
},
{ type: 'background', group: 'background', start: 1691100120000, end: 1691101020000,
},
{ group: 'group2', type: 'range', className: 'teal', start: 1691095214000, end: 1691095428000,
{ group: 'group2', type: 'range', start: 1691095214000, end: 1691095428000,
},
{ group: 'group2', type: 'range', className: 'pink', start: 1691091546000, end: 1691091615000,
{ group: 'group2', type: 'range', start: 1691091546000, end: 1691091615000,
cssVariables: {
'--height': '20%',
'--height': '50%',
'--item-background': 'var(--color-2)',
},
},
{ group: 'group2', type: 'range', className: 'teal', start: 1691097441000, end: 1691097514000,
{ group: 'group2', type: 'range', start: 1691097441000, end: 1691097514000,
},
{ group: 'group2', type: 'range', className: 'teal', start: 1691090985000, end: 1691091085000,
{ group: 'group2', type: 'range', start: 1691090985000, end: 1691091085000,
},
{ group: 'group2', type: 'range', className: 'teal', start: 1691093875000, end: 1691094107000,
{ group: 'group2', type: 'range', start: 1691093875000, end: 1691094107000,
},
{ group: 'group2', type: 'range', className: 'teal', start: 1691091720000, end: 1691091805000,
{ group: 'group2', type: 'range', start: 1691091720000, end: 1691091805000,
},
{ group: 'group2', type: 'range', className: 'teal', start: 1691094747000, end: 1691094873000,
{ group: 'group2', type: 'range', start: 1691094747000, end: 1691094873000,
},
{ group: 'group2', type: 'range', className: 'teal', start: 1691096492000, end: 1691096604000,
{ group: 'group2', type: 'range', start: 1691096492000, end: 1691096604000,
},
{ group: 'group2', type: 'range', className: 'teal', start: 1691093445000, end: 1691093515000,
{ group: 'group2', type: 'range', start: 1691093445000, end: 1691093515000,
},
{ group: 'group2', type: 'range', className: 'teal', start: 1691092246000, end: 1691092430000,
{ group: 'group2', type: 'range', start: 1691092246000, end: 1691092430000,
},
{ group: 'group2', type: 'range', className: 'blue', start: 1691096029000, end: 1691096293000,
{ group: 'group2', type: 'range', cssVariables: { '--item-background': 'var(--color-1)' }, start: 1691096029000, end: 1691096293000,
},
{ group: 'group2', type: 'range', className: 'teal', start: 1691097646000, end: 1691097805000,
{ group: 'group2', type: 'range', start: 1691097646000, end: 1691097805000,
},
{ group: 'group2', type: 'range', className: 'blue', start: 1691096693000, end: 1691096779000,
{ group: 'group2', type: 'range', cssVariables: { '--item-background': 'var(--color-1)' }, start: 1691096693000, end: 1691096779000,
},
{ group: 'group2', type: 'range', className: 'blue', start: 1691092544000, end: 1691092671000,
{ group: 'group2', type: 'range', cssVariables: { '--item-background': 'var(--color-1)' }, start: 1691092544000, end: 1691092671000,
},
{ group: 'group2', type: 'range', className: 'teal', start: 1691090867000, end: 1691090970000,
{ group: 'group2', type: 'range', start: 1691090867000, end: 1691090970000,
},
{
group: 'linechart',
Expand Down Expand Up @@ -265,6 +266,16 @@
});
</script>

<style>
:root {
--color-1: #8338ec;
--color-2: #ffbe0b;
--color-3: #3a86ff;
--color-4: #ff006e;
--item-background: var(--color-3);
}
</style>

<style lang="scss" scoped>
.timeline {
border: 1px solid color-mix(in srgb, currentcolor 10%, transparent);
Expand All @@ -273,8 +284,6 @@
// --gridline-border-left: 1px dashed rgba(255, 255, 255, 10%);
// --group-border-top: 1px solid rgba(255, 255, 255, 10%);
--group-height: 1.5rem;
// --group-padding-top: 0.5em;
// --group-padding-bottom: 0.5em;
Expand All @@ -288,14 +297,13 @@
// --label-padding: 0.2em 0.5em;
// --label-line-height: 1em;
--timestamps-background: color-mix(in srgb, white 50%, transparent);
// --timestamps-background: color-mix(in srgb, white 50%, transparent);
// --timestamps-color: rgb(255, 112, 255);
// --timestamp-line-height: 1.5em;
// --timestamp-padding-block: 0.2em;
// --timestamp-padding-inline: 0.4em;
background-color: color-mix(in srgb, currentcolor 10%, transparent);
border-radius: 0.5rem;
:deep(.group-label) {
Expand All @@ -313,16 +321,8 @@
}
:deep(.item) {
$colors: "blue", "red", "green", "indigo", "yellow", "orange", "teal", "purple", "pink", "gray";
opacity: 0.7;
@each $color in $colors {
&.#{$color} {
--item-background: linear-gradient(180deg, #{$color}, color-mix(in hsl, #{$color} 80%, white));
}
}
&.range {
height: var(--height, 100%);
bottom: 0;
Expand All @@ -343,6 +343,7 @@
&.red {
--item-marker-width: 1px;
--item-background: var(--color-4);
}
&.gray {
Expand Down

0 comments on commit a64094a

Please sign in to comment.