feat: built-in type read-only view,s chema list, states, spec files

This commit is contained in:
Florian Sylvain committed 2026-10-19 22:31:13 +02:00
1 parent 3c67cd2314
commit f514146fed
24 files changed
+1220 -88

No files matched your search

+74
View File
@@ -0,0 +1,74 @@
<script setup lang="ts">
import { t } from '@/i18n'
import { computed } from 'vue'
import { useMediaQuery } from '@vueuse/core'
import type { BuiltinField } from '@/composables/useContentTypes'
import { fieldIcon, fieldRules, fieldTypeLabel } from '@/lib/schemaFields'
import { SCHEMA_TABLE_QUERY } from '@/lib/schemaRows'
const NO_RULES = '–'
const COLUMNS = '28px minmax(0,1fr) minmax(0,1fr) 120px minmax(0,1.2fr)'
const props = defineProps<{ fields: BuiltinField[] }>()
const isTable = useMediaQuery(SCHEMA_TABLE_QUERY)
const rows = computed(() => props.fields.map((field) => ({ field, rules: fieldRules(field) })))
</script>
<template>
<div
:role="isTable ? 'table' : 'list'"
:aria-label="t('Fields')"
class="border border-default bg-default"
>
<div
v-if="isTable"
role="row"
class="grid h-9 items-center gap-3 bg-elevated px-3 font-medium text-muted"
:style="{ gridTemplateColumns: COLUMNS }"
>
<span role="presentation" />
<span role="columnheader">{{ t('Label') }}</span>
<span role="columnheader">{{ t('Key') }}</span>
<span role="columnheader">{{ t('Type') }}</span>
<span role="columnheader">{{ t('Rules') }}</span>
</div>
<template v-for="{ field, rules } in rows" :key="field.key">
<div
v-if="isTable"
role="row"
data-testid="schema-field-row"
class="grid h-12 items-center gap-3 border-t border-default px-3"
:style="{ gridTemplateColumns: COLUMNS }"
>
<span role="cell" class="flex">
<UIcon :name="fieldIcon(field.type)" class="size-4 text-toned" />
</span>
<span role="cell" class="truncate font-medium">{{ field.label }}</span>
<span role="cell" class="truncate font-mono text-xs text-muted">{{
field.key
}}</span>
<span role="cell">{{ fieldTypeLabel(field.type) }}</span>
<span role="cell" class="text-muted">{{ rules || NO_RULES }}</span>
</div>
<div
v-else
role="listitem"
data-testid="schema-field-card"
class="flex min-h-16 items-center gap-3 border-t border-default px-4 py-3"
>
<span class="flex size-7 shrink-0 items-center justify-center bg-elevated">
<UIcon :name="fieldIcon(field.type)" class="size-4" />
</span>
<div class="flex min-w-0 flex-1 flex-col">
<span class="font-medium">{{ field.label }}</span>
<span class="text-xs/5 text-muted"
><span class="font-mono">{{ field.key }}</span>
<template v-if="rules"> · {{ rules }}</template></span
>
</div>
</div>
</template>
</div>
</template>
+33 -10
View File
@@ -1,11 +1,11 @@
<script setup lang="ts">
import { t } from '@/i18n'
import { RouterLink } from 'vue-router'
import { useMediaQuery } from '@vueuse/core'
import DirectoryBadge from '@/components/DirectoryBadge.vue'
import { fieldCountLabel, type SchemaRow } from '@/lib/schemaRows'
import { SCHEMA_TABLE_QUERY, fieldCountLabel, type SchemaRow } from '@/lib/schemaRows'
const TABLE_QUERY = '(min-width: 40rem)'
const COLUMNS = '24px minmax(0,1.4fr) minmax(0,1fr) 90px 150px 24px'
const SKELETON_ROWS = 6
@@ -15,7 +15,7 @@ defineProps<{
loading: boolean
}>()
const isTable = useMediaQuery(TABLE_QUERY)
const isTable = useMediaQuery(SCHEMA_TABLE_QUERY)
</script>
<template>
@@ -75,11 +75,19 @@ const isTable = useMediaQuery(TABLE_QUERY)
</span>
</div>
<template v-for="row in group" :key="row.id">
<div
<component
:is="row.to ? RouterLink : 'div'"
v-if="isTable"
role="row"
:to="row.to"
:aria-label="row.to ? row.name : undefined"
:role="row.to ? undefined : 'row'"
data-testid="schema-row"
class="grid h-12 items-center gap-3 border-t border-default px-3"
:class="
row.to
? 'hover:bg-elevated focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-primary'
: ''
"
:style="{ gridTemplateColumns: COLUMNS }"
>
<span role="cell" class="flex">
@@ -99,12 +107,21 @@ const isTable = useMediaQuery(TABLE_QUERY)
}}</span>
<span role="cell">{{ row.fieldCount }}</span>
<span role="cell" class="text-muted">{{ row.updated }}</span>
</div>
<div
<UIcon
v-if="row.to"
name="i-lucide-chevron-right"
class="size-4 justify-self-end text-muted"
/>
</component>
<component
:is="row.to ? RouterLink : 'div'"
v-else
role="listitem"
:to="row.to"
:aria-label="row.to ? row.name : undefined"
:role="row.to ? undefined : 'listitem'"
data-testid="schema-card"
class="flex min-h-17 items-center gap-3 border-t border-default px-4 py-3"
class="flex min-h-17 items-center gap-3 border-t border-default py-3 pl-4"
:class="row.to ? 'pr-2' : 'pr-4'"
>
<div class="flex min-w-0 flex-1 flex-col gap-1">
<div class="flex flex-wrap items-center gap-2">
@@ -121,7 +138,13 @@ const isTable = useMediaQuery(TABLE_QUERY)
></span
>
</div>
</div>
<span
v-if="row.to"
class="flex h-11 w-9 shrink-0 items-center justify-center text-muted"
>
<UIcon name="i-lucide-chevron-right" class="size-4.5" />
</span>
</component>
</template>
</template>
</div>
+20
View File
@@ -0,0 +1,20 @@
<script setup lang="ts">
import { t } from '@/i18n'
</script>
<template>
<div class="flex min-h-0 flex-1 items-center justify-center px-4 py-6">
<UEmpty
variant="naked"
icon="i-lucide-shapes"
:title="t('Content type not found')"
:description="t('It may have been deleted, or the address may be mistyped.')"
:ui="{
root: 'w-full max-w-105 gap-3 p-0 sm:p-0 lg:p-0',
avatar: 'mb-0 size-11 rounded-none bg-elevated text-toned',
title: 'text-title font-semibold text-wrap',
description: 'mt-1 w-full max-w-none px-0 text-base/5 text-wrap text-muted',
}"
/>
</div>
</template>