|
| 1 | +<script setup lang="ts"> |
| 2 | +import { rpc, useDevToolsState } from '@vue/devtools-core' |
| 3 | +import { useDevToolsColorMode, vTooltip, VueIcIcon } from '@vue/devtools-ui' |
| 4 | +import { defineModel } from 'vue' |
| 5 | +
|
| 6 | +defineProps<{ data: { |
| 7 | + id: string |
| 8 | + label: string |
| 9 | +}[] }>() |
| 10 | +
|
| 11 | +const emit = defineEmits(['select', 'clear']) |
| 12 | +const devtoolsState = useDevToolsState() |
| 13 | +const recordingState = computed(() => devtoolsState.timelineLayersState.value.recordingState) |
| 14 | +const timelineLayersState = computed(() => devtoolsState.timelineLayersState.value) |
| 15 | +const recordingTooltip = computed(() => recordingState.value ? 'Stop recording' : 'Start recording') |
| 16 | +const { colorMode } = useDevToolsColorMode() |
| 17 | +const isDark = computed(() => colorMode.value === 'dark') |
| 18 | +const selected = defineModel() |
| 19 | +function select(id: string) { |
| 20 | + selected.value = id |
| 21 | + emit('select', id) |
| 22 | + rpc.value.updateTimelineLayersState({ |
| 23 | + selected: id, |
| 24 | + }) |
| 25 | +} |
| 26 | +
|
| 27 | +watch(() => timelineLayersState.value.selected, (state: string) => { |
| 28 | + selected.value = state |
| 29 | +}, { |
| 30 | + immediate: true, |
| 31 | +}) |
| 32 | +
|
| 33 | +function getTimelineLayerEnabled(id: string) { |
| 34 | + return { |
| 35 | + 'mouse': timelineLayersState.value.mouseEventEnabled, |
| 36 | + 'keyboard': timelineLayersState.value.keyboardEventEnabled, |
| 37 | + 'component-event': timelineLayersState.value.componentEventEnabled, |
| 38 | + 'performance': timelineLayersState.value.performanceEventEnabled, |
| 39 | + }[id] |
| 40 | +} |
| 41 | +
|
| 42 | +function toggleRecordingState() { |
| 43 | + rpc.value.updateTimelineLayersState({ |
| 44 | + recordingState: !recordingState.value, |
| 45 | + }) |
| 46 | +} |
| 47 | +
|
| 48 | +function toggleTimelineLayerEnabled(id: string) { |
| 49 | + const normalizedId = { |
| 50 | + 'mouse': 'mouseEventEnabled', |
| 51 | + 'keyboard': 'keyboardEventEnabled', |
| 52 | + 'component-event': 'componentEventEnabled', |
| 53 | + 'performance': 'performanceEventEnabled', |
| 54 | + }[id] |
| 55 | + rpc.value.updateTimelineLayersState({ |
| 56 | + [normalizedId]: !getTimelineLayerEnabled(id), |
| 57 | + }) |
| 58 | +} |
| 59 | +</script> |
| 60 | + |
| 61 | +<template> |
| 62 | + <div h-full flex flex-col p2> |
| 63 | + <div class="mb-1 flex justify-end pb-1" border="b dashed base"> |
| 64 | + <div class="flex items-center gap-2 px-1"> |
| 65 | + <div v-tooltip.bottom-end="{ content: recordingTooltip }" class="flex items-center gap1" @click="toggleRecordingState"> |
| 66 | + <span v-if="recordingState" class="recording recording-btn bg-[#ef4444]" /> |
| 67 | + <span v-else class="recording-btn bg-black op70 dark:(bg-white) hover:op100" /> |
| 68 | + </div> |
| 69 | + <div v-tooltip.bottom-end="{ content: 'Clear all timelines' }" class="flex items-center gap1" @click="emit('clear')"> |
| 70 | + <VueIcIcon name="baseline-delete" cursor-pointer text-xl op70 hover:op100 /> |
| 71 | + </div> |
| 72 | + <div v-tooltip.bottom-end="{ content: '<p style=\'width: 285px\'>Timeline events can cause significant performance overhead in large applications, so we recommend enabling it only when needed and on-demand. </p>', html: true }" class="flex items-center gap1"> |
| 73 | + <VueIcIcon name="baseline-tips-and-updates" cursor-pointer text-xl op70 hover:op100 /> |
| 74 | + </div> |
| 75 | + </div> |
| 76 | + </div> |
| 77 | + <ul class="p2"> |
| 78 | + <li |
| 79 | + v-for="item in data" :key="item.id" |
| 80 | + class="group relative selectable-item" |
| 81 | + :class="{ active: item.id === selected }" |
| 82 | + @click="select(item.id)" |
| 83 | + > |
| 84 | + {{ item.label }} |
| 85 | + <span class="absolute right-2 rounded-1 bg-primary-500 px1 text-3 text-white op0 [.active_&]:(bg-primary-400 dark:bg-gray-600) group-hover:op80 hover:op100!" @click.stop="toggleTimelineLayerEnabled(item.id)"> |
| 86 | + {{ getTimelineLayerEnabled(item.id) ? 'Disabled' : 'Enabled' }} |
| 87 | + </span> |
| 88 | + </li> |
| 89 | + </ul> |
| 90 | + </div> |
| 91 | +</template> |
| 92 | + |
| 93 | +<style scoped> |
| 94 | +@keyframes pulse { |
| 95 | + 50% { |
| 96 | + opacity: 0.5; |
| 97 | + } |
| 98 | +} |
| 99 | +.recording-btn { |
| 100 | + --at-apply: w-3.5 h-3.5 inline-flex cursor-pointer rounded-50%; |
| 101 | +} |
| 102 | +.recording { |
| 103 | + animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; |
| 104 | + transition-duration: 1s; |
| 105 | + box-shadow: #ef4444 0 0 8px; |
| 106 | +} |
| 107 | +</style> |
0 commit comments