112 lines
3.5 KiB
Vue
112 lines
3.5 KiB
Vue
<script setup lang="ts">
|
|
import type { TimelineClipData, TimelineTrackData } from '@/lib/Timeline'
|
|
import { useCssVar, useElementBounding } from '@vueuse/core'
|
|
import { storeToRefs } from 'pinia'
|
|
import { computed, shallowRef, useTemplateRef } from 'vue'
|
|
import { timelineClipColor, toAbsoluteDuration, toAbsoluteTime } from '@/lib/Timeline'
|
|
import { toPx, usePx } from '@/lib/usePx'
|
|
import { useTimelineStore } from '@/store/TimelineStore'
|
|
import { getComponentFor } from '.'
|
|
|
|
const {
|
|
timelineTrack,
|
|
timelineClip,
|
|
} = defineProps<{
|
|
timelineTrack: TimelineTrackData
|
|
timelineClip: TimelineClipData
|
|
}>()
|
|
|
|
const timeline = useTimelineStore()
|
|
const { audioTrack } = storeToRefs(timeline)
|
|
|
|
const contentView = computed(() => getComponentFor(timelineTrack))
|
|
|
|
const left = computed(() => {
|
|
const t = toAbsoluteTime(audioTrack.value, timelineTrack.reference, timelineClip.clipIn)
|
|
const px = timeline.secondsToPixels(t)
|
|
return toPx(px)
|
|
})
|
|
|
|
const width = usePx(() => {
|
|
const t = toAbsoluteDuration(audioTrack.value, timelineTrack.reference, timelineClip.duration)
|
|
const px = timeline.secondsToPixels(t)
|
|
return px
|
|
})
|
|
|
|
const autorepeat = computed(() => timelineClip.autorepeat)
|
|
const color = computed(() => timelineClipColor(timelineTrack, timelineClip))
|
|
|
|
const isSelected = shallowRef(false)
|
|
function selectClip() {
|
|
// TODO: make selection manager
|
|
isSelected.value = !isSelected.value
|
|
}
|
|
|
|
// style:
|
|
// - always thin outer border style
|
|
// - regular (non-autorepeat):
|
|
// - outer border is 50% black
|
|
// - if not selected, do nothing
|
|
// - if selected, red (inner) outline
|
|
// - autorepeat:
|
|
// - outer border is transparent (but still occupies 1px of space)
|
|
// - always dashed thick outer border style
|
|
// - if not selected, custom colored border
|
|
// - if selected, red outline
|
|
/* NOTE: the following is "would do anything to avoid hardcoding 4px width limit" */
|
|
const selectionRef = useTemplateRef('selection')
|
|
const { width: selectionWidth } = useElementBounding(selectionRef)
|
|
const outlineSelectedWidth = useCssVar('--timeline-clip-outline-selected-width', selectionRef)
|
|
const innerBorderVisible = computed(() => outlineSelectedWidth.value ? selectionWidth.value > 2 * Number.parseInt(outlineSelectedWidth.value, 10) : false)
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
class="tw:absolute tw:h-full tw:border tw:rounded-(--timeline-clip-border-radius) tw:overflow-hidden"
|
|
:style="{
|
|
left,
|
|
width: width.string,
|
|
maxWidth: width.string,
|
|
borderColor: autorepeat ? 'transparent' : 'var(--timeline-clip-border-color)',
|
|
}"
|
|
@click="selectClip"
|
|
>
|
|
<!-- background color within outline borders -->
|
|
<div
|
|
v-if="!autorepeat"
|
|
class="tw:absolute tw:size-full"
|
|
:style="{ backgroundColor: color }"
|
|
/>
|
|
|
|
<component :is="contentView" :track="timelineTrack" :clip="timelineClip" :width="width.number" />
|
|
|
|
<!-- selection outline, above content -->
|
|
<div
|
|
v-if="isSelected || autorepeat" ref="selection"
|
|
class="tw:absolute tw:size-full tw:max-w-full tw:pointer-events-none tw:select-none"
|
|
:class="{ selection: isSelected, autorepeat }"
|
|
:style="!isSelected ? { borderColor: color } : null"
|
|
>
|
|
<div
|
|
v-if="!autorepeat && innerBorderVisible"
|
|
class="tw:absolute tw:size-full tw:max-w-full selection-inner"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.selection {
|
|
border: var(--timeline-clip-outline-selected);
|
|
}
|
|
|
|
.autorepeat {
|
|
border-width: var(--timeline-clip-outline-selected-width);
|
|
border-style: dashed;
|
|
}
|
|
|
|
.selection-inner {
|
|
border: 1px solid var(--timeline-clip-border-color-inner);
|
|
}
|
|
</style>
|