muzika-gromche/Frontend/src/components/timeline/clip/TimelineClipView.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>