Files
miniminu/src/components/ResponsiveTimeUntil.vue
Julien Calixte 9c49abfff2 fix: swap open url and copy url button order
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 00:45:28 +01:00

221 lines
4.5 KiB
Vue

<script setup lang="ts">
import { useTitle, useUrlSearchParams } from "@vueuse/core"
import { computed, ref, watch } from "vue"
import { useTimeUntil } from "../hooks/useTimeUntil.hooks"
const props = defineProps<{ project?: string; target?: string }>()
const searchParams = useUrlSearchParams<{ project?: string; target?: string }>(
"history"
)
const projectTitle = ref(props.project)
const targetInput = ref(props.target)
watch(
projectTitle,
() => {
searchParams.project = projectTitle.value
},
{
immediate: true,
}
)
watch(
targetInput,
() => {
searchParams.target = targetInput.value
},
{
immediate: true,
}
)
const target = computed(() => targetInput.value)
if (projectTitle.value) {
useTitle(projectTitle.value)
}
const targetDate = computed(() =>
targetInput.value
? new Date(targetInput.value).toLocaleDateString(undefined, {
dateStyle: "long",
})
: null
)
const {
timeUntilTarget,
hasTargetPassed,
isYearsDisplayed,
isMonthsDisplayed,
isDaysDisplayed,
isHoursDisplayed,
isMinutesDisplayed,
isSecondsDisplayed,
yearsUntil,
monthsUntil,
daysUntil,
hoursUntil,
minutesUntil,
secondsUntil,
} = useTimeUntil(target)
const url = computed(() => {
const newUrl = new URL(document.location.toString())
if (projectTitle.value) {
newUrl.searchParams.set("project", projectTitle.value)
}
if (targetInput.value) {
newUrl.searchParams.set("target", targetInput.value)
}
return newUrl.toString()
})
const copyUrl = () => {
if (navigator.clipboard) {
navigator.clipboard.writeText(url.value)
}
}
</script>
<template>
<div class="responsive-time-until">
<h1 v-if="projectTitle">{{ projectTitle }}</h1>
<section class="time" v-if="timeUntilTarget">
<div v-if="isYearsDisplayed" class="count">
<span class="number">{{ yearsUntil }}</span>
<span class="moment">years</span>
</div>
<div v-if="isMonthsDisplayed" class="count">
<span class="number">{{ monthsUntil }}</span>
<span class="moment">months</span>
</div>
<div v-if="isDaysDisplayed" class="count">
<span class="number">{{ daysUntil }}</span>
<span class="moment">days</span>
</div>
<div v-if="isHoursDisplayed" class="count">
<span class="number">{{ hoursUntil }}</span>
<span class="moment">hours</span>
</div>
<div v-if="isMinutesDisplayed" class="count">
<span class="number">{{ minutesUntil }}</span>
<span class="moment">minutes</span>
</div>
<div v-if="isSecondsDisplayed" class="count">
<span class="number">{{ secondsUntil }}</span>
<span class="moment">seconds</span>
</div>
</section>
<section v-else class="no-target">
No target set. Expand window to set a target.
</section>
<section v-if="targetDate" class="target-date">
<div v-if="hasTargetPassed" class="has-target-passed">🎊</div>
<hr v-else />
{{ targetDate }}
</section>
<form @submit.prevent>
<div>
<label for="title">Title:</label>
<input
type="text"
id="title"
v-model="projectTitle"
autocomplete="false"
/>
</div>
<div>
<label for="target">Target date:</label>
<input
type="date"
id="target"
v-model="targetInput"
autocomplete="false"
/>
</div>
<div>
<a :href="url" target="_blank" rel="noopener noreferrer"><button type="button">open url</button></a>
<button @click="copyUrl">copy url</button>
</div>
<p class="hint">Shrink the window to hide this config.</p>
</form>
</div>
</template>
<style scoped>
.responsive-time-until {
display: flex;
flex: 1;
flex-direction: column;
justify-content: space-around;
align-items: center;
height: 100vh;
width: 100vw;
}
.time {
display: flex;
justify-content: center;
gap: 1rem;
}
.count {
display: flex;
flex-direction: column;
}
.number {
font-size: 36px;
}
.target-date {
margin-bottom: 1rem;
}
.no-target {
padding: 1rem;
}
.has-target-passed {
font-size: 72px;
}
form {
display: none;
padding: 1rem;
justify-content: center;
align-items: center;
gap: 1rem;
max-width: 800px;
margin: auto;
padding: 1rem;
flex-wrap: wrap;
}
label {
margin-right: 0.3rem;
}
input {
padding: 0.5rem;
}
@media (min-width: 600px) {
form {
display: flex;
}
}
.hint {
font-size: 0.75rem;
opacity: 0.5;
width: 100%;
text-align: center;
margin: 0;
}
</style>