~koehr/raffle-me-that

fd3ac2c9fc27e468c76849656543ae2bf365cfdd — Norman Köhring 1 year, 10 months ago 1645b3b
store raffles, wheel of fortune
5 files changed, 127 insertions(+), 5 deletions(-)

M src/assets/base.css
M src/composables/useRaffle.ts
M src/pages/Home.vue
M src/pages/Raffle.vue
M src/types.d.ts
M src/assets/base.css => src/assets/base.css +2 -2
@@ 3,12 3,12 @@
@tailwind utilities;

body {
  @apply min-h-screen bg-slate-900 text-slate-100 leading-relaxed;
  @apply min-h-screen m-0 p-0 bg-slate-900 text-slate-100 leading-relaxed;
  transition: color 0.3s, background-color 0.2s;
}

#app {
  @apply flex justify-around items-center w-screen min-h-screen m-0 p-4;
  @apply flex justify-around items-center w-screen min-h-screen;
}

button, input {

M src/composables/useRaffle.ts => src/composables/useRaffle.ts +1 -0
@@ 11,6 11,7 @@ export default function useRaffle() {
  const raffleStore = useStorage<Raffle[]>('', EMPTY_STORE)

  const newRaffle = ref<Raffle>({
    id: genId(),
    title: '',
    date: new Date().toLocaleDateString('de'),
    participants: [],

M src/pages/Home.vue => src/pages/Home.vue +16 -1
@@ 1,5 1,6 @@
<script setup lang="ts">
import { useHead } from '@vueuse/head'
import { useRouter } from 'vue-router'
import useRaffle from '../composables/useRaffle'

useHead({


@@ 19,13 20,27 @@ const showNewRaffleForm = $ref(false)

function startRaffle() {
  if (!readyToRaffle) return
  //

  raffleStore.value.push(newRaffle.value)
  const router = useRouter()
  router.push(`/raffle/${newRaffle.value.id}`)
}
</script>

<template>
  <header>
    <h1 class="text-4xl">Raffle Me That!</h1>

    <div class="mt-8" v-if="raffleStore.length">
      <strong>Select Existing:</strong>
      <ul>
        <li v-for="raffle in raffleStore">
          <router-link :to="`/raffle/${raffle.id}`">
            {{ raffle.title }}
          </router-link>
        </li>
      </ul>
    </div>
  </header>

  <main>

M src/pages/Raffle.vue => src/pages/Raffle.vue +107 -2
@@ 1,6 1,111 @@
<script setup lang="ts">
import { onMounted } from 'vue'
import { useHead } from '@vueuse/head'
useHead({
  title: 'Lets go!',
import { useStorage } from '@vueuse/core'
import { useRoute } from 'vue-router'

const route = useRoute()
const raffleId = route.params.id as string

const raffleStore = useStorage<Raffle[]>('', [])
const raffle = raffleStore.value.find(r => r.id === raffleId)

const participants = $computed(() => {
  return raffle ? raffle.participants : []
})

const amount = $computed(() => participants.length)
let aligned = $ref(false) // used for animation

function degrees(index: number) {
  if (!aligned) return 0
  return (360 / amount) * index
}

useHead({ title: raffle ? raffle.title : 'Lets Go' })

onMounted(() => {
  setTimeout(() => {
    aligned = true
  }, 100)
})
</script>

<template>
  <div class="relative w-screen h-screen overflow-hidden flex justify-end items-center">
    <ol class="absolute top-0 -left-1/2 w-screen h-screen transition-transform duration-500 ease-in" :class="aligned ? 'aligned' : 'shifted'">
      <li v-for="(participant, i) in participants"
        class="slice"
        :style="`
          transform: rotate(${degrees(i)}deg);
        `"
      >
        <div>
          {{ participant }}bcdef ghijklmn
        </div>
      </li>
      <div class="absolute top-1/2 left-1/2 w-32 h-32 -ml-16 -mt-16 rounded-full bg-black"></div>
    </ol>
  </div>
</template>

<style scoped>
.shifted {
  transform: translateX(-100%);
}
.aligned {
  transform: translateX(0);
}
.slice {
  --w: 50vw;
  --h: calc(6.283185307179586 * var(--w) / v-bind(amount));
  position: absolute;
  width: var(--w);
  height: var(--h);
  left: 50%;
  top: 28%;
  padding: 0;
  text-align: right;
  transform-origin: left center;
  transition: transform 1s ease-out .4s;
  color: white;
  font-weight: bold;
  font-size: 2em;
}
.slice:nth-child(even) {
  color: black;
}
.slice::before, .slice::after {
  content: '';
  display: block;
  width: 0;
  height: 0;
  border-style: solid;
}
.slice::before {
  margin-bottom: -1px;
  border-width: 0 0 calc(var(--h) / 2) calc(var(--w) * .97);
  border-color: transparent transparent #0074D9 transparent;
}
.slice:nth-child(even)::before {
  border-color: transparent transparent #2ECC40 transparent;
}
.slice::after {
  border-width: 0 calc(var(--w) * .97) calc(var(--h) / 2) 0;
  border-color: transparent #0074D9 transparent transparent;
}
.slice:nth-child(even)::after {
  border-color: transparent #2ECC40 transparent transparent;
}

.slice > div {
  position: absolute;
  top: 0;
  bottom: 0;
  display: flex;
  justify-content: flex-end;
  align-items: center;
  width: 85%;
  height: 100%;
}
</style>

M src/types.d.ts => src/types.d.ts +1 -0
@@ 1,5 1,6 @@
declare global {
  type Raffle = {
    id: string
    title: string
    date: string
    participants: string[]