A simple to-do app focused on tasks that can be completed within a specific time span.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

extract types of TimeDisplays

ToBinio 729f52a0 4d5fa34f

+123 -27
+6 -26
app/components/Todo/TimeDisplay.vue app/components/Todo/TimeDisplay/TimeDisplayRange.vue
··· 1 1 <script setup lang="ts"> 2 - import { 3 - getLocalTimeZone, 4 - parseAbsolute, 5 - parseAbsoluteToLocal, 6 - parseDate, 7 - } from "@internationalized/date"; 2 + import { getLocalTimeZone, parseDate } from "@internationalized/date"; 8 3 import { addDays, sanitizeDate } from "~~/shared/date"; 9 - import type { TimePoint, TimeRange } from "~~/shared/types"; 4 + import type { TimeRange } from "~~/shared/types"; 10 5 11 - const props = defineProps<{ timeframe: TimeRange | TimePoint }>(); 6 + const { range } = defineProps<{ range: TimeRange }>(); 12 7 13 8 const timeFrameString = computed(() => { 14 - if ("time" in props.timeframe) { 15 - return props.timeframe.time; 16 - } 17 - 18 - return `${props.timeframe.start.toWellFormed()} - ${props.timeframe.end.toWellFormed()}`; 9 + return `${range.start.toWellFormed()} - ${range.end.toWellFormed()}`; 19 10 }); 20 11 21 12 const time = computed(() => { 22 - if ("time" in props.timeframe) { 23 - let time = parseAbsoluteToLocal(props.timeframe.time).toDate().getTime(); 24 - 25 - return { 26 - start: time - 1000 * 60 * 30, 27 - end: time + 1000 * 60 * 30, 28 - }; 29 - } 30 - 31 13 return { 32 - start: parseDate(props.timeframe.start) 33 - .toDate(getLocalTimeZone()) 34 - .getTime(), 35 - end: parseDate(props.timeframe.end) 14 + start: parseDate(range.start).toDate(getLocalTimeZone()).getTime(), 15 + end: parseDate(range.end) 36 16 .add({ days: 1 }) 37 17 .toDate(getLocalTimeZone()) 38 18 .getTime(),
+24
app/components/Todo/TimeDisplay/TimeDisplay.vue
··· 1 + <script setup lang="ts"> 2 + import type { TimePoint, TimeRange } from "~~/shared/types"; 3 + import TimeDisplayPoint from "./TimeDisplayPoint.vue"; 4 + import TimeDisplayRange from "./TimeDisplayRange.vue"; 5 + 6 + const props = defineProps<{ timeframe: TimeRange | TimePoint }>(); 7 + 8 + const point = computed(() => { 9 + if ("type" in props.timeframe) { 10 + return props.timeframe as TimePoint; 11 + } 12 + }); 13 + 14 + const range = computed(() => { 15 + if ("start" in props.timeframe) { 16 + return props.timeframe as TimeRange; 17 + } 18 + }); 19 + </script> 20 + 21 + <template> 22 + <TimeDisplayPoint v-if="point" :point="point" /> 23 + <TimeDisplayRange v-if="range" :range="range" /> 24 + </template>
+92
app/components/Todo/TimeDisplay/TimeDisplayPoint.vue
··· 1 + <script setup lang="ts"> 2 + import { parseAbsoluteToLocal } from "@internationalized/date"; 3 + import { addDays, sanitizeDate } from "~~/shared/date"; 4 + import type { TimePoint } from "~~/shared/types"; 5 + 6 + const { point } = defineProps<{ point: TimePoint }>(); 7 + 8 + const timeFrameString = computed(() => { 9 + return point.time; 10 + }); 11 + 12 + const time = computed(() => { 13 + let time = parseAbsoluteToLocal(point.time).toDate().getTime(); 14 + 15 + return { 16 + start: time - 1000 * 60 * 30, 17 + end: time + 1000 * 60 * 30, 18 + }; 19 + }); 20 + 21 + function capTime(time: number): number { 22 + return Math.min( 23 + Math.max(time, sanitizeDate(new Date()).getTime()), 24 + addDays(sanitizeDate(new Date()), 7).getTime(), 25 + ); 26 + } 27 + 28 + const cappedTime = computed(() => { 29 + return { 30 + start: capTime(time.value.start), 31 + end: capTime(time.value.end), 32 + }; 33 + }); 34 + 35 + const width = computed(() => { 36 + const timeDiff = cappedTime.value.end - cappedTime.value.start; 37 + const numberOfDays = timeDiff / (24 * 60 * 60 * 1000.0); 38 + const percentage = numberOfDays / 7.0; 39 + return percentage; 40 + }); 41 + 42 + const offset = computed(() => { 43 + const now = new Date(); 44 + now.setHours(0, 0, 0, 0); 45 + 46 + const timeDiff = cappedTime.value.start - now.getTime(); 47 + const numberOfDays = timeDiff / (24 * 60 * 60 * 1000.0); 48 + const percentage = numberOfDays / 7.0; 49 + return percentage; 50 + }); 51 + 52 + const isCappedRight = computed(() => { 53 + return time.value.end > addDays(sanitizeDate(new Date()), 7).getTime(); 54 + }); 55 + 56 + const isCappedLeft = computed(() => { 57 + return time.value.start < sanitizeDate(new Date()).getTime(); 58 + }); 59 + </script> 60 + 61 + <template> 62 + <div 63 + class="relative flex h-2 flex-col justify-center" 64 + :title="timeFrameString" 65 + > 66 + <div 67 + class="absolute top-1/2 flex h-2 w-full -translate-y-1/2 justify-evenly" 68 + > 69 + <div 70 + v-for="n in 6" 71 + :key="n" 72 + class="bg-secondary h-2 w-0.5 rounded-full" 73 + /> 74 + </div> 75 + <div 76 + class="bg-primary absolute top-1/2 h-1 -translate-y-1/2 rounded-full" 77 + :style="{ 78 + width: `${width * 100}%`, 79 + left: `${offset * 100}%`, 80 + }" 81 + /> 82 + <div 83 + v-if="isCappedRight" 84 + class="to-background absolute top-1/2 right-0 h-1 w-12 -translate-y-1/2 bg-linear-to-r from-transparent" 85 + /> 86 + <div 87 + v-if="isCappedLeft" 88 + class="to-background absolute top-1/2 left-0 h-1 w-12 -translate-y-1/2 bg-linear-to-l from-transparent" 89 + /> 90 + <div class="bg-secondary h-0.5 w-full rounded-full" /> 91 + </div> 92 + </template>
+1 -1
app/components/Todo/Todo.vue
··· 5 5 import type { Label as LabelType, Todo } from "~~/shared/types"; 6 6 7 7 import TagLabel from "../Utils/Label/TagLabel.vue"; 8 - import TimeDisplay from "./TimeDisplay.vue"; 8 + import TimeDisplay from "./TimeDisplay/TimeDisplay.vue"; 9 9 10 10 const props = defineProps<{ data: Todo }>(); 11 11