Refactor metadata inspector with shared tab list and output panel.
Reuse InlineTabList for original/stripped tabs and a single pre block component to match patterns used elsewhere on the upload page.
This commit is contained in:
@@ -0,0 +1,36 @@
|
|||||||
|
<template>
|
||||||
|
<div class="inline-flex w-fit rounded-md border border-border p-0.5">
|
||||||
|
<button
|
||||||
|
v-for="option in options"
|
||||||
|
:key="option.value"
|
||||||
|
type="button"
|
||||||
|
class="rounded px-3 py-1.5 text-xs font-medium transition-colors"
|
||||||
|
:class="
|
||||||
|
modelValue === option.value
|
||||||
|
? 'bg-muted text-foreground'
|
||||||
|
: 'text-muted-foreground hover:text-foreground'
|
||||||
|
"
|
||||||
|
@click="select(option.value)"
|
||||||
|
>
|
||||||
|
{{ option.label }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" generic="T extends string">
|
||||||
|
import { type PropType } from 'vue';
|
||||||
|
import type { InlineTabOption } from '@/types/ui/InlineTabOption';
|
||||||
|
|
||||||
|
const modelValue = defineModel<T>({ required: true });
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
options: {
|
||||||
|
type: Array as PropType<InlineTabOption<T>[]>,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const select = (value: T): void => {
|
||||||
|
modelValue.value = value;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="flex min-h-48 flex-col rounded-lg border border-border bg-card" aria-live="polite">
|
<section class="flex min-h-48 flex-col rounded-lg border border-border bg-card">
|
||||||
<header class="border-b border-border px-4 py-3">
|
<header class="border-b border-border px-4 py-3">
|
||||||
<h2 class="text-sm font-medium text-card-foreground">Metadata Inspector</h2>
|
<h2 class="text-sm font-medium text-card-foreground">Metadata Inspector</h2>
|
||||||
<p v-if="fileName" class="mt-0.5 truncate text-xs text-muted-foreground">{{ fileName }}</p>
|
<p v-if="fileName" class="mt-0.5 truncate text-xs text-muted-foreground">{{ fileName }}</p>
|
||||||
@@ -10,56 +10,16 @@
|
|||||||
|
|
||||||
<template v-else-if="entry.status === 'error'">
|
<template v-else-if="entry.status === 'error'">
|
||||||
<p class="text-sm text-destructive">{{ entry.message }}</p>
|
<p class="text-sm text-destructive">{{ entry.message }}</p>
|
||||||
<template v-if="entry.output">
|
<p v-if="entry.output" class="text-xs font-medium text-foreground">Original</p>
|
||||||
<p class="text-xs font-medium text-foreground">Original</p>
|
<MetadataOutputPanel v-if="entry.output">{{ entry.output }}</MetadataOutputPanel>
|
||||||
<pre
|
|
||||||
class="max-h-96 min-h-32 flex-1 overflow-auto rounded-md bg-muted p-3 text-xs leading-relaxed whitespace-pre-wrap text-foreground"
|
|
||||||
>{{ entry.output }}</pre
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else-if="entry.status === 'ready'">
|
<template v-else-if="entry.status === 'ready'">
|
||||||
<div
|
<InlineTabList v-model="activeTab" :options="metadataTabOptions" />
|
||||||
class="inline-flex w-fit rounded-md border border-border p-0.5"
|
|
||||||
role="tablist"
|
|
||||||
aria-label="Metadata view"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
role="tab"
|
|
||||||
class="rounded px-3 py-1.5 text-xs font-medium transition-colors"
|
|
||||||
:class="
|
|
||||||
activeTab === 'original'
|
|
||||||
? 'bg-muted text-foreground'
|
|
||||||
: 'text-muted-foreground hover:text-foreground'
|
|
||||||
"
|
|
||||||
:aria-selected="activeTab === 'original'"
|
|
||||||
@click="activeTab = 'original'"
|
|
||||||
>
|
|
||||||
Original
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
role="tab"
|
|
||||||
class="rounded px-3 py-1.5 text-xs font-medium transition-colors"
|
|
||||||
:class="
|
|
||||||
activeTab === 'stripped'
|
|
||||||
? 'bg-muted text-foreground'
|
|
||||||
: 'text-muted-foreground hover:text-foreground'
|
|
||||||
"
|
|
||||||
:aria-selected="activeTab === 'stripped'"
|
|
||||||
@click="activeTab = 'stripped'"
|
|
||||||
>
|
|
||||||
Stripped
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<pre
|
<MetadataOutputPanel>
|
||||||
role="tabpanel"
|
{{ activeTab === 'original' ? entry.output : entry.afterStripOutput }}
|
||||||
class="max-h-96 min-h-32 flex-1 overflow-auto rounded-md bg-muted p-3 text-xs leading-relaxed whitespace-pre-wrap text-foreground"
|
</MetadataOutputPanel>
|
||||||
>{{ activeTab === 'original' ? entry.output : entry.afterStripOutput }}</pre
|
|
||||||
>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<p v-else class="text-sm text-muted-foreground">{{ entry.message }}</p>
|
<p v-else class="text-sm text-muted-foreground">{{ entry.message }}</p>
|
||||||
@@ -71,6 +31,12 @@
|
|||||||
import { ref, watch, type PropType } from 'vue';
|
import { ref, watch, type PropType } from 'vue';
|
||||||
import type { MetadataPreviewEntry } from '@/types/metadata/MetadataPreview';
|
import type { MetadataPreviewEntry } from '@/types/metadata/MetadataPreview';
|
||||||
import type { MetadataTab } from '@/types/metadata/MetadataTab';
|
import type { MetadataTab } from '@/types/metadata/MetadataTab';
|
||||||
|
import type { InlineTabOption } from '@/types/ui/InlineTabOption';
|
||||||
|
|
||||||
|
const metadataTabOptions: InlineTabOption<MetadataTab>[] = [
|
||||||
|
{ value: 'original', label: 'Original' },
|
||||||
|
{ value: 'stripped', label: 'Stripped' }
|
||||||
|
];
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
fileName: {
|
fileName: {
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<template>
|
||||||
|
<pre
|
||||||
|
class="max-h-96 min-h-32 flex-1 overflow-auto rounded-md bg-muted p-3 text-xs leading-relaxed whitespace-pre-wrap text-foreground"
|
||||||
|
><slot /></pre>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export type InlineTabOption<T extends string = string> = {
|
||||||
|
value: T;
|
||||||
|
label: string;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user