feat: built-in type read-only view,s chema list, states, spec files
This commit is contained in:
1 parent
3c67cd2314
commit
f514146fed
24 files changed
+1220
-88
No files matched your search
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
Reference in new issue
Block a user