try to match to steam game
This commit is contained in:
@@ -6,6 +6,7 @@ import { useQuery } from "@tanstack/vue-query";
|
||||
import { eventPlanningQuery } from "@/queries";
|
||||
import type { PlanningSlotOut } from "./api/types.gen";
|
||||
import PlanningCover from "./components/PlanningCover.vue";
|
||||
import PlanningMultiSlot from "./components/PlanningMultiSlot.vue";
|
||||
|
||||
interface Props {
|
||||
eventPath: string;
|
||||
@@ -117,28 +118,32 @@ const layout = computed(() => {
|
||||
|
||||
// Unused columns are dropped
|
||||
const columnIds = [...new Set(list.map(({ slot }) => slot.column))].sort((a, b) => a - b);
|
||||
const columns = columnIds.map((id) =>
|
||||
list
|
||||
let left = AXIS_PX;
|
||||
const columns = columnIds.map((id) => {
|
||||
const placed = list
|
||||
.filter(({ slot }) => slot.column === id)
|
||||
.map<Placed>(({ slot, b, e }) => ({
|
||||
slot,
|
||||
top: yOf(b) + 3,
|
||||
height: yOf(e) - yOf(b) - 6,
|
||||
when: `${fmtTime.format(b)} – ${fmtTime.format(e)}`,
|
||||
})),
|
||||
);
|
||||
}));
|
||||
// The multi-anims column is the big one, twice as wide
|
||||
const width = placed.some(({ slot }) => slot.is_multi_anims) ? 2 * COL_PX + GAP_PX : COL_PX;
|
||||
const column = { left, width, placed };
|
||||
left += width + GAP_PX;
|
||||
return column;
|
||||
});
|
||||
|
||||
return {
|
||||
height: y,
|
||||
width: AXIS_PX + columns.length * (COL_PX + GAP_PX) - GAP_PX,
|
||||
width: left - GAP_PX,
|
||||
days,
|
||||
ticks,
|
||||
breaks,
|
||||
columns,
|
||||
};
|
||||
});
|
||||
|
||||
const columnLeft = (index: number) => AXIS_PX + index * (COL_PX + GAP_PX);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -159,10 +164,13 @@ const columnLeft = (index: number) => AXIS_PX + index * (COL_PX + GAP_PX);
|
||||
<div v-else class="overflow-x-auto pb-4">
|
||||
<div class="relative mx-auto" :style="{ width: `${layout.width}px`, height: `${layout.height}px` }">
|
||||
<!-- Time axis: a header row per day, hour ticks on the side -->
|
||||
<!-- Above the cards: slots running past midnight go over the header row -->
|
||||
<div v-for="day in layout.days" :key="`day-${day.y}`"
|
||||
class="absolute left-0 right-0 border-b border-base-content/20 flex items-end pb-1 text-sm font-semibold uppercase tracking-wide"
|
||||
class="absolute left-0 right-0 z-20 pointer-events-none border-b border-base-content/20 flex items-end text-sm font-semibold uppercase tracking-wide"
|
||||
:style="{ top: `${day.y}px`, height: `${DAY_PX}px` }">
|
||||
{{ day.label }}
|
||||
<!-- Ends at the time axis and grows to the left, never over the cards -->
|
||||
<span class="absolute bottom-0 whitespace-nowrap bg-base-300 rounded-t px-2 py-0.5"
|
||||
:style="{ right: `calc(100% - ${AXIS_PX}px)` }">{{ day.label }}</span>
|
||||
</div>
|
||||
<template v-for="tick in layout.ticks" :key="tick.y">
|
||||
<span class="absolute -translate-y-1/2 text-xs text-base-content/40 text-right pr-2"
|
||||
@@ -181,19 +189,12 @@ const columnLeft = (index: number) => AXIS_PX + index * (COL_PX + GAP_PX);
|
||||
</div>
|
||||
|
||||
<!-- Slots -->
|
||||
<div v-for="(column, index) in layout.columns" :key="index" class="absolute top-0 h-full"
|
||||
:style="{ left: `${columnLeft(index)}px`, width: `${COL_PX}px` }">
|
||||
<template v-for="{ slot, top, height, when } in column" :key="slot.id">
|
||||
<!-- Multi-anims: one long card, its games stay in view while scrolling -->
|
||||
<div v-if="slot.is_multi_anims" class="planning-multi absolute inset-x-0 rounded-md p-2 shadow-xl"
|
||||
:style="{ top: `${top}px`, height: `${height}px`, '--slot-color': slot.color }">
|
||||
<div class="font-bold uppercase text-sm tracking-wide mb-2">{{ slot.title }}</div>
|
||||
<component :is="game.store_url ? 'a' : 'div'" v-for="game in slot.games" :key="game.name"
|
||||
:href="game.store_url || undefined" target="_blank" rel="noopener noreferrer"
|
||||
class="sticky top-2 block aspect-[2/3] rounded overflow-hidden mb-3 shadow-lg bg-base-200">
|
||||
<PlanningCover :game="game" :color="slot.color" />
|
||||
</component>
|
||||
</div>
|
||||
<div v-for="column in layout.columns" :key="column.left" class="absolute top-0 h-full"
|
||||
:style="{ left: `${column.left}px`, width: `${column.width}px` }">
|
||||
<template v-for="{ slot, top, height, when } in column.placed" :key="slot.id">
|
||||
<!-- Multi-anims: one long shiny card, its games follow the scroll -->
|
||||
<PlanningMultiSlot v-if="slot.is_multi_anims" :entry="slot" class="absolute inset-x-0"
|
||||
:style="{ top: `${top}px`, height: `${height}px` }" />
|
||||
|
||||
<component :is="slot.games[0]?.store_url ? 'a' : 'div'" v-else
|
||||
:href="slot.games[0]?.store_url || undefined" target="_blank" rel="noopener noreferrer"
|
||||
@@ -218,10 +219,4 @@ const columnLeft = (index: number) => AXIS_PX + index * (COL_PX + GAP_PX);
|
||||
.planning-break {
|
||||
background: repeating-linear-gradient(45deg, color-mix(in srgb, currentColor 6%, transparent) 0 6px, transparent 6px 12px);
|
||||
}
|
||||
|
||||
.planning-multi {
|
||||
background: linear-gradient(180deg,
|
||||
color-mix(in srgb, var(--slot-color) 30%, var(--color-base-200)),
|
||||
var(--color-base-200));
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user