Skip to content

Commit

Permalink
Merge pull request #1408 from PrefectHQ/nicholas/enhancement/p-timeli…
Browse files Browse the repository at this point in the history
…ne-generics-2024-08-14

Enhancement: Use generics for PTimeline
  • Loading branch information
znicholasbrown authored Aug 14, 2024
2 parents 76bfa24 + 63d1a6c commit 2070bec
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 29 deletions.
31 changes: 20 additions & 11 deletions demo/sections/components/Timeline.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</template>

<template #no-icons>
<p-timeline :items="itemsNoData" v-bind="{ layout }" />
<p-timeline :items="itemsNoData" v-bind="{ layout }" item-key="title" />
</template>

<template #no-slots>
Expand All @@ -29,7 +29,7 @@
{{ item.title }}
</p-heading>

<p-markdown-renderer :text="item.body" />
<p-markdown-renderer v-if="item.body" :text="item.body" />
</template>
</p-timeline>
</template>
Expand All @@ -54,7 +54,7 @@

<template #custom-side>
<p-timeline :items="itemsReversed" class="ordered-list__custom-side" v-bind="{ layout }">
<template #content="{ item }: { item: TimelineItem }">
<template #content="{ item }">
<p-card
:flat="!expandedList.includes(item.id)"
class="ordered-list__custom-side__card"
Expand All @@ -75,13 +75,13 @@

<template #target-a-specific-slot>
<p-timeline :items="itemsReversed" class="ordered-list__target-specific" item-key="id" v-bind="{ layout }">
<template #date="{ item }: { item: TimelineItem }">
<template #date="{ item }">
<div class="ordered-list__target-specific__content" @mouseover="handleMouseoverItem(item)" @mouseout="handleMouseoutItem">
{{ item.title }}
</div>
</template>

<template v-if="hoveredItemSlotKey" #[hoveredItemSlotKey]="{ item }: { item: TimelineItem }">
<template v-if="hoveredItemSlotKey" #[hoveredItemSlotKey]="{ item }">
<template v-if="item.date">
<span class="ordered-list__date">{{ item.date }}</span>
</template>
Expand Down Expand Up @@ -164,13 +164,22 @@
},
]
const itemsNoData: TimelineItem[] = Array.from({ length: 3 }, () => ({
type Item = TimelineItem & {
id: string,
icon?: string,
title?: string,
date?: string,
body?: string,
}
const itemsNoData: Item[] = Array.from({ length: 3 }, (_, index) => ({
id: crypto.randomUUID(),
index,
}))
const items: TimelineItem[] = [
const items: Item[] = [
{
id: 0,
id: '0',
title: 'Born',
body: 'I was born in Shangzhou, China.',
},
Expand Down Expand Up @@ -204,14 +213,14 @@
const itemsReversed = [...items].reverse()
const itemsManyData: TimelineItem[] = Array.from({ length: 1000 }, (item, index) => ({
const itemsManyData: Item[] = Array.from({ length: 1000 }, (item, index) => ({
id: crypto.randomUUID(),
title: `Item ${index}`,
}))
const expandedList = ref<string[]>([])
function expand(item: TimelineItem): void {
function expand(item: Item): void {
if (expandedList.value.includes(item.id)) {
expandedList.value = expandedList.value.filter((id) => id !== item.id)
} else {
Expand All @@ -228,7 +237,7 @@
return ''
})
const handleMouseoverItem = (item: TimelineItem): void => {
const handleMouseoverItem = (item: Item): void => {
hoveredItem.value = item.id
}
Expand Down
35 changes: 19 additions & 16 deletions src/components/Timeline/PTimeline.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
<template>
<!-- eslint-disable vue/no-extra-parens -->
<PVirtualScroller
v-bind="{ items, itemKey }"
:items
:item-key="(itemKey as K)"
class="p-timeline"
element="ol"
>
<template #default="{ item, index }: { item: TimelineItem, index: number }">
<!-- eslint-enable vue/no-extra-parens -->
<template #default="{ item, index }">
<slot v-bind="{ item, index }">
<slot :name="getItemSlotName(item, index)" v-bind="{ item, index }">
<PTimelineItem :layout="getItemLayout(item, index)">
Expand Down Expand Up @@ -40,7 +43,7 @@
</PVirtualScroller>
</template>

<script lang="ts" setup>
<script lang="ts" setup generic="T extends TimelineItem, K extends keyof T | undefined">
import { computed } from 'vue'
import PTimelineItem from '@/components/Timeline/PTimelineItem.vue'
import PTimelinePoint from '@/components/Timeline/PTimelinePoint.vue'
Expand All @@ -49,48 +52,48 @@
import { kebabCase } from '@/utilities/strings'
const props = defineProps<{
items: TimelineItem[],
itemKey?: string,
items: T[],
itemKey?: K,
layout?: TimelineLayout | TimelineLayoutFunction,
}>()
const layout = computed(() => props.layout ?? 'date-left')
const internalLayout = computed(() => props.layout ?? 'date-left')
function getItemId(item: TimelineItem, index: number): string | number {
function getItemId(item: T, index: number): string | number {
return props.itemKey ? kebabCase(`${item[props.itemKey]}`) : index
}
function getItemSlotName(item: TimelineItem, index: number): string {
function getItemSlotName(item: T, index: number): string {
const base = getItemId(item, index)
return `item-${base}`
}
function getPointSlotName(item: TimelineItem, index: number): string {
function getPointSlotName(item: T, index: number): string {
const base = getItemSlotName(item, index)
return `${base}-point`
}
function getPointContentSlotName(item: TimelineItem, index: number): string {
function getPointContentSlotName(item: T, index: number): string {
const base = getItemSlotName(item, index)
return `${base}-point-content`
}
function getDateSlotName(item: TimelineItem, index: number): string {
function getDateSlotName(item: T, index: number): string {
const base = getItemSlotName(item, index)
return `${base}-date`
}
function getContentSlotName(item: TimelineItem, index: number): string {
function getContentSlotName(item: T, index: number): string {
const base = getItemSlotName(item, index)
return `${base}-content`
}
function getItemLayout(item: TimelineItem, index: number): TimelineLayout {
if (typeof layout.value === 'function') {
return layout.value(item, index)
function getItemLayout(item: T, index: number): TimelineLayout {
if (typeof internalLayout.value === 'function') {
return internalLayout.value(item, index)
}
return layout.value
return internalLayout.value
}
</script>

Expand Down
1 change: 1 addition & 0 deletions src/components/VirtualScroller/PVirtualScroller.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<template>
<component :is="element" class="p-virtual-scroller">
{{ itemKey }}
<template v-for="(chunk, chunkIndex) in chunks" :key="chunkIndex">
<VirtualScrollerChunk :height="itemEstimateHeight * chunk.length" v-bind="{ observerOptions }">
<template v-for="(item, itemChunkIndex) in chunk" :key="item[itemKey]">
Expand Down
2 changes: 0 additions & 2 deletions src/types/timeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import { Icon } from '@/types/icon'

export type TimelineItem = {
icon?: Icon,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[key: string]: any,
}

export const timelineLayouts = ['date-left', 'date-right', 'stacked-left', 'stacked-right', 'stacked-center'] as const
Expand Down

0 comments on commit 2070bec

Please sign in to comment.