feat: unify design tokens, five font weights, radius scale, restrained motion, AA contrast
This commit is contained in:
+15
-8
@@ -111,6 +111,7 @@ const markdownPreview = ref(false)
|
||||
const taskNoteEditor = ref<HTMLTextAreaElement | null>(null)
|
||||
const SHOW_COMPLETED_STORAGE_KEY = 'dodo.show-completed'
|
||||
const showCompleted = ref(readStoredBoolean(window.localStorage, SHOW_COMPLETED_STORAGE_KEY, true))
|
||||
const completedFilterReveal = ref(false)
|
||||
const TODAY_SECTION_COLLAPSE_KEY = 'dodo.today-section-collapse.v1'
|
||||
const todaySectionCollapse = ref<TodaySectionCollapse>(readTodaySectionCollapse(window.localStorage, TODAY_SECTION_COLLAPSE_KEY))
|
||||
const page = ref(1)
|
||||
@@ -449,6 +450,10 @@ watch(taskReorderAvailable, () => {
|
||||
})
|
||||
watch(showCompleted, (value) => {
|
||||
writeStoredBoolean(window.localStorage, SHOW_COMPLETED_STORAGE_KEY, value)
|
||||
if (value) {
|
||||
completedFilterReveal.value = true
|
||||
window.setTimeout(() => { completedFilterReveal.value = false }, 500)
|
||||
}
|
||||
if (isTaskView(activeView.value)) { page.value = 1; loadAll() }
|
||||
})
|
||||
|
||||
@@ -1819,15 +1824,15 @@ onUnmounted(() => {
|
||||
<CompletedFilterPill v-model="showCompleted" class="list-inline-filter" />
|
||||
</section>
|
||||
<template v-if="activeView==='today'">
|
||||
<section v-if="overdueTaskTree.length" class="overdue-section today-collapsible-section">
|
||||
<button id="today-overdue-heading" class="today-section-toggle" type="button" :aria-expanded="!todaySectionCollapse.overdue" aria-controls="today-overdue" @click="toggleTodaySection('overdue')"><span class="today-section-title">逾期</span><span class="today-section-summary">{{overdueTaskTree.length}}</span><span class="today-section-chevron" aria-hidden="true">{{ todaySectionCollapse.overdue ? '›' : '⌄' }}</span></button>
|
||||
<div v-show="!todaySectionCollapse.overdue" id="today-overdue" class="task-list plain-list overdue-list" role="region" aria-labelledby="today-overdue-heading">
|
||||
<section v-if="overdueTaskTree.length" class="overdue-section today-collapsible-section" :class="{'is-collapsed': todaySectionCollapse.overdue}">
|
||||
<button id="today-overdue-heading" class="today-section-toggle" type="button" :aria-expanded="!todaySectionCollapse.overdue" aria-controls="today-overdue" @click="toggleTodaySection('overdue')"><span class="today-section-title">逾期</span><span class="today-section-summary">{{overdueTaskTree.length}}</span><span class="today-section-chevron" :class="{open: !todaySectionCollapse.overdue}" aria-hidden="true">›</span></button>
|
||||
<div id="today-overdue" class="task-list plain-list overdue-list" role="region" aria-labelledby="today-overdue-heading">
|
||||
<template v-for="node in overdueTaskTree" :key="`overdue-${node.task.id}`">
|
||||
<article :data-task-id="node.task.id" class="task-row overdue-task swipeable" :class="{'just-completed':justCompletedTaskIds.has(node.task.id),'completion-exiting':completionExitingTaskIds.has(node.task.id),ready:Math.abs(taskSwipeOffsets[node.task.id] ?? 0) >= 64}" :style="{ '--swipe-x': `${taskSwipeOffsets[node.task.id] ?? 0}px` }" @pointerdown="startTaskPointer(node.task, $event)" @pointermove="moveTaskPointer(node.task, $event)" @pointerup="finishTaskPointer(node.task, $event)" @pointercancel="cancelTaskPointer(node.task)" @touchstart.passive="startTaskSwipe(node.task, $event)" @touchmove.passive="moveTaskSwipe(node.task, $event)" @touchend="finishTaskSwipe(node.task, $event)" @touchcancel="cancelTaskSwipe(node.task)"><button class="task-check" :aria-label="`完成${node.task.title}`" :aria-pressed="false" @click.stop="toggle(node.task)"><span class="task-check-mark" :class="`p${node.task.priority}`"></span></button><div class="task-main" role="button" tabindex="0" @click="selectTaskUnlessSwiped(node.task)" @keydown.enter="selectTaskUnlessSwiped(node.task)" @keydown.space.prevent="selectTaskUnlessSwiped(node.task)"><strong :title="node.task.title">{{node.task.title}}</strong><span v-if="node.subtasks.length" class="meta"><span class="meta-item"><ListChecks/>{{node.subtasks.filter(t=>t.completed).length}}/{{node.subtasks.length}}</span></span></div><span v-if="node.task.due_at" class="task-tail"><TaskDueDisplay :due-at="node.task.due_at" :due-has-time="node.task.due_has_time" :completed="node.task.completed" :now-ms="taskDueNowMs" /></span></article>
|
||||
</template>
|
||||
</div>
|
||||
</section>
|
||||
<button id="today-tasks-heading" class="today-section-toggle today-section-anchor" type="button" :aria-expanded="!todaySectionCollapse.tasks" aria-controls="today-tasks" @click="toggleTodaySection('tasks')"><span class="today-section-title">今天</span><span class="today-section-summary">{{totalTasks}}</span><span class="today-section-chevron" aria-hidden="true">{{ todaySectionCollapse.tasks ? '›' : '⌄' }}</span></button>
|
||||
<button id="today-tasks-heading" class="today-section-toggle today-section-anchor" type="button" :aria-expanded="!todaySectionCollapse.tasks" aria-controls="today-tasks" @click="toggleTodaySection('tasks')"><span class="today-section-title">今天</span><span class="today-section-summary">{{totalTasks}}</span><span class="today-section-chevron" :class="{open: !todaySectionCollapse.tasks}" aria-hidden="true">›</span></button>
|
||||
</template>
|
||||
<section v-if="activeView==='trash'" class="trash-page-context">
|
||||
<div><h1 ref="trashPageTitle" class="trash-page-title" tabindex="-1">回收站</h1><p class="trash-page-summary">删除的任务保留在这里,可整组恢复或永久删除。</p></div>
|
||||
@@ -1846,9 +1851,10 @@ onUnmounted(() => {
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
<section v-else :id="activeView==='today' ? 'today-tasks' : undefined" class="task-list plain-list" :class="{loading}" v-show="activeView!=='today' || !todaySectionCollapse.tasks" :role="activeView==='today' ? 'region' : undefined" :aria-labelledby="activeView==='today' ? 'today-tasks-heading' : (activeView==='tasks' || activeView==='upcoming') ? 'task-list-title' : undefined" ref="taskListElement">
|
||||
<section v-else :id="activeView==='today' ? 'today-tasks' : undefined" class="task-list plain-list" :class="{loading, 'is-collapsed': todaySectionCollapse.tasks}" :role="activeView==='today' ? 'region' : undefined" :aria-labelledby="activeView==='today' ? 'today-tasks-heading' : (activeView==='tasks' || activeView==='upcoming') ? 'task-list-title' : undefined" ref="taskListElement">
|
||||
<div class="today-collapse-inner plain-list">
|
||||
<template v-for="node in taskTree" :key="node.task.id">
|
||||
<article :data-task-id="node.task.id" class="task-row swipeable" :class="{done:node.task.completed,'just-completed': justCompletedTaskIds.has(node.task.id),'completion-exiting':completionExitingTaskIds.has(node.task.id),selected:selectedTask?.id===node.task.id,ready:Math.abs(taskSwipeOffsets[node.task.id] ?? 0) >= 64,reordering:taskReorder?.id===node.task.id,'reorder-target':taskReorderTarget===node.task.id}" :style="{ '--swipe-x': `${taskSwipeOffsets[node.task.id] ?? 0}px`, '--reorder-y': `${taskReorder?.id === node.task.id ? taskReorder.offsetY : 0}px` }" @pointerdown="startTaskPointer(node.task, $event)" @pointermove="moveTaskPointer(node.task, $event)" @pointerup="finishTaskPointer(node.task, $event)" @pointercancel="cancelTaskPointer(node.task)" @touchstart.passive="startTaskSwipe(node.task, $event)" @touchmove.passive="moveTaskSwipe(node.task, $event)" @touchend="finishTaskSwipe(node.task, $event)" @touchcancel="cancelTaskSwipe(node.task)">
|
||||
<article :data-task-id="node.task.id" class="task-row swipeable" :class="{done:node.task.completed,'filter-reveal':completedFilterReveal&&node.task.completed,'just-completed': justCompletedTaskIds.has(node.task.id),'completion-exiting':completionExitingTaskIds.has(node.task.id),selected:selectedTask?.id===node.task.id,ready:Math.abs(taskSwipeOffsets[node.task.id] ?? 0) >= 64,reordering:taskReorder?.id===node.task.id,'reorder-target':taskReorderTarget===node.task.id}" :style="{ '--swipe-x': `${taskSwipeOffsets[node.task.id] ?? 0}px`, '--reorder-y': `${taskReorder?.id === node.task.id ? taskReorder.offsetY : 0}px` }" @pointerdown="startTaskPointer(node.task, $event)" @pointermove="moveTaskPointer(node.task, $event)" @pointerup="finishTaskPointer(node.task, $event)" @pointercancel="cancelTaskPointer(node.task)" @touchstart.passive="startTaskSwipe(node.task, $event)" @touchmove.passive="moveTaskSwipe(node.task, $event)" @touchend="finishTaskSwipe(node.task, $event)" @touchcancel="cancelTaskSwipe(node.task)">
|
||||
<button v-if="taskReorderMode" class="drag-handle task-drag-handle" :disabled="totalPages > 1" aria-label="上下拖动任务排序" title="上下拖动排序" @pointerdown.stop="startTaskReorder(node.task, $event)" @pointermove.stop="moveTaskReorder(node.task, $event)" @pointerup.stop="finishTaskReorder(node.task, $event)" @pointercancel.stop="cancelTaskReorder"><GripVertical/></button>
|
||||
<button class="task-check" :aria-label="node.task.completed ? `重新打开${node.task.title}` : `完成${node.task.title}`" :aria-pressed="node.task.completed" @click.stop="toggle(node.task)"><span class="task-check-mark" :class="`p${node.task.priority}`"><Check v-if="node.task.completed" /></span></button>
|
||||
<div class="task-main" role="button" tabindex="0" @click="selectTaskUnlessSwiped(node.task)" @keydown.enter="selectTaskUnlessSwiped(node.task)" @keydown.space.prevent="selectTaskUnlessSwiped(node.task)"><strong :title="node.task.title">{{node.task.title}}</strong><span v-if="node.subtasks.length" class="meta"><span class="meta-item"><ListChecks/>{{node.subtasks.filter(t=>t.completed).length}}/{{node.subtasks.length}}</span></span><span v-if="node.task.priority" class="priority" :class="`p${node.task.priority}`">{{['','低','中','高'][node.task.priority]}}</span></div>
|
||||
@@ -1857,11 +1863,12 @@ onUnmounted(() => {
|
||||
</template>
|
||||
<div v-if="activeView==='today' && hiddenCompletedTaskCount > 0 && !visibleTasks.length && !loading" class="today-filtered-empty-note"><span>已隐藏已完成任务</span><button type="button" class="link" @click="showCompleted=true">显示</button></div>
|
||||
<div v-else-if="!visibleTasks.length&&!loading" class="empty"><ListTodo/><b>{{ activeView==='trash' ? '回收站是空的' : hiddenCompletedTaskCount > 0 ? '已完成任务已隐藏' : '这里还很安静' }}</b><span>{{activeView==='trash' ? '删除的任务会显示在这里' : hiddenCompletedTaskCount > 0 ? '开启“显示已完成”即可查看。':'写下第一件想完成的小事吧'}}</span></div>
|
||||
</div>
|
||||
</section>
|
||||
<nav v-if="totalPages > 1 && (activeView!=='today' || !todaySectionCollapse.tasks)" class="pager" aria-label="任务分页"><button class="pager-button pager-button--previous" :disabled="page<=1 || loading" aria-label="上一页" @click="previousPage"><ChevronLeft aria-hidden="true"/><span>上一页</span></button><span class="pager-status" aria-live="polite"><strong>{{page}} / {{totalPages}}</strong><span>共 {{ totalTasks }} 项</span></span><button class="pager-button pager-button--next" :disabled="page>=totalPages || loading" aria-label="下一页" @click="nextPage"><span>下一页</span><ChevronRight aria-hidden="true"/></button></nav>
|
||||
<div v-if="activeView==='today'" class="today-habits-section today-section-anchor">
|
||||
<button id="today-habits-heading" class="today-section-toggle" type="button" :aria-expanded="!todaySectionCollapse.habits" aria-controls="today-habits" @click="toggleTodaySection('habits')"><span class="today-section-title">习惯</span><span class="today-section-summary">{{todayHabitTotal}}</span><span class="today-section-chevron" aria-hidden="true">{{ todaySectionCollapse.habits ? '›' : '⌄' }}</span></button>
|
||||
<div v-show="!todaySectionCollapse.habits" id="today-habits" role="region" aria-labelledby="today-habits-heading">
|
||||
<button id="today-habits-heading" class="today-section-toggle" type="button" :aria-expanded="!todaySectionCollapse.habits" aria-controls="today-habits" @click="toggleTodaySection('habits')"><span class="today-section-title">习惯</span><span class="today-section-summary">{{todayHabitTotal}}</span><span class="today-section-chevron" :class="{open: !todaySectionCollapse.habits}" aria-hidden="true">›</span></button>
|
||||
<div id="today-habits" :class="{'is-collapsed': todaySectionCollapse.habits}" role="region" aria-labelledby="today-habits-heading">
|
||||
<MvpPanel ref="habitComposer" view="today-habits" :show-completed="showCompleted" :compact-layout="compactLayout" @detail="handleHabitDetail" @changed="refreshAll" @notice="toast" @summary="updateTodayHabitSummary" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user