v0.1.53 - 통계 기록 목록과 차트 드래그 보정
This commit is contained in:
@@ -304,7 +304,7 @@ onBeforeUnmount(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="planner-sheet__body flex flex-col gap-5 py-[10px] lg:flex-row lg:gap-4">
|
<div class="planner-sheet__body flex flex-col gap-5 py-[10px] lg:flex-row lg:gap-4">
|
||||||
<div class="planner-sheet__lists flex w-full flex-1 flex-col gap-[25px] lg:w-[394px]">
|
<div class="planner-sheet__lists flex w-full flex-1 flex-col gap-[21px] lg:w-[394px]">
|
||||||
<section>
|
<section>
|
||||||
<div class="flex items-center gap-2 text-muted">
|
<div class="flex items-center gap-2 text-muted">
|
||||||
<span class="shrink-0">TASKS</span>
|
<span class="shrink-0">TASKS</span>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
import GuideTooltip from './GuideTooltip.vue'
|
import GuideTooltip from './GuideTooltip.vue'
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
@@ -33,6 +34,41 @@ defineProps({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['update:range-start', 'update:range-end', 'quick-range'])
|
const emit = defineEmits(['update:range-start', 'update:range-end', 'quick-range'])
|
||||||
|
|
||||||
|
const flowScrollerRef = ref(null)
|
||||||
|
const flowDragState = ref(null)
|
||||||
|
|
||||||
|
function startFlowDrag(event) {
|
||||||
|
if (!flowScrollerRef.value || event.button !== 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
flowDragState.value = {
|
||||||
|
pointerId: event.pointerId,
|
||||||
|
startX: event.clientX,
|
||||||
|
scrollLeft: flowScrollerRef.value.scrollLeft,
|
||||||
|
}
|
||||||
|
flowScrollerRef.value.setPointerCapture?.(event.pointerId)
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveFlowDrag(event) {
|
||||||
|
if (!flowDragState.value || !flowScrollerRef.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault()
|
||||||
|
flowScrollerRef.value.scrollLeft =
|
||||||
|
flowDragState.value.scrollLeft - (event.clientX - flowDragState.value.startX)
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopFlowDrag(event) {
|
||||||
|
if (!flowDragState.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
flowScrollerRef.value?.releasePointerCapture?.(event.pointerId)
|
||||||
|
flowDragState.value = null
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -96,7 +132,7 @@ const emit = defineEmits(['update:range-start', 'update:range-end', 'quick-range
|
|||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid gap-6 xl:grid-cols-[minmax(0,1.3fr)_minmax(320px,0.7fr)]">
|
<div class="grid items-start gap-6 xl:grid-cols-[minmax(0,1.3fr)_minmax(320px,0.7fr)]">
|
||||||
<article class="rounded-[28px] border border-stone-200 bg-white/86 p-6">
|
<article class="rounded-[28px] border border-stone-200 bg-white/86 p-6">
|
||||||
<div class="flex items-end justify-between gap-4">
|
<div class="flex items-end justify-between gap-4">
|
||||||
<div>
|
<div>
|
||||||
@@ -113,7 +149,15 @@ const emit = defineEmits(['update:range-start', 'update:range-end', 'quick-range
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-8 flex min-h-[230px] items-stretch gap-3 overflow-x-auto border-b border-stone-200 pb-4">
|
<div
|
||||||
|
ref="flowScrollerRef"
|
||||||
|
class="mt-8 flex h-[260px] select-none items-stretch gap-3 overflow-x-auto overscroll-x-contain border-b border-stone-200 pb-4 cursor-grab active:cursor-grabbing"
|
||||||
|
@pointerdown="startFlowDrag"
|
||||||
|
@pointermove="moveFlowDrag"
|
||||||
|
@pointerup="stopFlowDrag"
|
||||||
|
@pointercancel="stopFlowDrag"
|
||||||
|
@pointerleave="stopFlowDrag"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
v-for="record in weeklyRecords"
|
v-for="record in weeklyRecords"
|
||||||
:key="record.key"
|
:key="record.key"
|
||||||
@@ -126,9 +170,9 @@ const emit = defineEmits(['update:range-start', 'update:range-end', 'quick-range
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<p class="text-[11px] font-bold tracking-[0.12em] text-stone-500">{{ record.dateLabel }}</p>
|
<p class="select-none text-[11px] font-bold tracking-[0.12em] text-stone-500">{{ record.dateLabel }}</p>
|
||||||
<p class="mt-1 text-[10px] font-bold tracking-[0.12em] text-stone-400">{{ record.weekday }}</p>
|
<p class="mt-1 select-none text-[10px] font-bold tracking-[0.12em] text-stone-400">{{ record.weekday }}</p>
|
||||||
<p class="mt-1 text-[11px] font-semibold text-stone-800">{{ record.focusedTime }}</p>
|
<p class="mt-1 select-none text-[11px] font-semibold text-stone-800">{{ record.focusedTime }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p
|
<p
|
||||||
@@ -162,38 +206,44 @@ const emit = defineEmits(['update:range-start', 'update:range-end', 'quick-range
|
|||||||
아직 통계를 보여줄 기록이 충분하지 않습니다.
|
아직 통계를 보여줄 기록이 충분하지 않습니다.
|
||||||
</p>
|
</p>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<article class="rounded-[28px] border border-stone-200 bg-white/86 p-6">
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<p class="text-[11px] font-bold tracking-[0.22em] text-stone-500">RECENT RECORDS</p>
|
|
||||||
<GuideTooltip
|
|
||||||
title="Recent Records"
|
|
||||||
description="선택 범위 안의 기록을 날짜 내림차순으로 최대 5개까지 보여줍니다."
|
|
||||||
:dismissible="false"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="mt-4 space-y-4">
|
|
||||||
<div
|
|
||||||
v-for="record in recentRecords"
|
|
||||||
:key="record.key"
|
|
||||||
class="rounded-2xl border border-stone-200 bg-stone-50/80 p-4"
|
|
||||||
>
|
|
||||||
<div class="flex items-start justify-between gap-4">
|
|
||||||
<div>
|
|
||||||
<p class="text-[12px] font-bold tracking-[0.08em] text-stone-900">{{ record.dateLabel }}</p>
|
|
||||||
<p class="mt-2 text-[11px] font-semibold leading-5 text-stone-600">
|
|
||||||
{{ record.comment || '코멘트 없음' }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="text-right">
|
|
||||||
<p class="text-sm font-semibold tracking-[-0.03em] text-stone-900">{{ record.focusedTime }}</p>
|
|
||||||
<p class="mt-1 text-[11px] font-semibold text-stone-500">{{ record.completionRate }}%</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<article class="rounded-[28px] border border-stone-200 bg-white/86 p-6">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<p class="text-[11px] font-bold tracking-[0.22em] text-stone-500">RECENT RECORDS</p>
|
||||||
|
<GuideTooltip
|
||||||
|
title="Recent Records"
|
||||||
|
description="선택 범위 안의 기록을 날짜 내림차순으로 최대 5개까지 보여줍니다."
|
||||||
|
:dismissible="false"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4 grid gap-4 md:grid-cols-2 xl:grid-cols-5">
|
||||||
|
<div
|
||||||
|
v-for="record in recentRecords"
|
||||||
|
:key="record.key"
|
||||||
|
class="rounded-2xl border border-stone-200 bg-stone-50/80 p-4"
|
||||||
|
>
|
||||||
|
<div class="flex h-full flex-col justify-between gap-4">
|
||||||
|
<div>
|
||||||
|
<p class="text-[12px] font-bold tracking-[0.08em] text-stone-900">{{ record.dateLabel }}</p>
|
||||||
|
<p class="mt-2 text-[11px] font-semibold leading-5 text-stone-600">
|
||||||
|
{{ record.comment || '코멘트 없음' }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-sm font-semibold tracking-[-0.03em] text-stone-900">{{ record.focusedTime }}</p>
|
||||||
|
<p class="mt-1 text-[11px] font-semibold text-stone-500">완료율 {{ record.completionRate }}%</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p
|
||||||
|
v-if="recentRecords.length === 0"
|
||||||
|
class="rounded-2xl border border-dashed border-stone-300 px-4 py-8 text-sm font-semibold text-stone-500"
|
||||||
|
>
|
||||||
|
선택 기간에 최근 기록이 없습니다.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user