- useSidebar: collapsed below 1100px (matchMedia) or by the admin's choice, persisted as a boolean under summer-admin.sidebar; the viewport never overwrites the stored choice - SectionPanel collapse and rail expand buttons; SectionFlyout (role menu) opens on hover, focus, Enter or ArrowDown on a rail item, closes on Esc or about 200 ms after leaving and returns focus to the rail item - Breadcrumbs with plugin, controller and record crumbs; UserMenu (Reka DropdownMenu) with initials, name, role and Wyloguj - useAuth.logout POSTs /auth/logout, clears user, navigation and settings and routes to login even when the call fails - applyColorScheme toggles .dark from prefers-color-scheme (no toggle, A6) - newest toast first, 200 ms fade and scale for dialogs and toasts - one relation-manager type constant in the registry - shell smoke tests; tests default to a desktop, light matchMedia
56 lines
1.7 KiB
Vue
56 lines
1.7 KiB
Vue
<script setup lang="ts">
|
|
import type { AdminRecord, ControllerParams, FormField as FormFieldSchema, RecordMeta } from '../../api/types'
|
|
import FormField from './FormField.vue'
|
|
import { RELATION_MANAGER } from './registry'
|
|
|
|
// Two-column form grid (design screen 4): span left -> column 1, right ->
|
|
// column 2, full (Winter's default) -> the whole row, auto and row -> the next
|
|
// free slot. Below 600px everything is one column. Gaps are 22px x 24px.
|
|
defineProps<{
|
|
fields: FormFieldSchema[]
|
|
values: AdminRecord
|
|
errors: Record<string, string[]>
|
|
labels?: RecordMeta['labels']
|
|
source?: ControllerParams | null
|
|
recordId?: number | null
|
|
idPrefix?: string
|
|
}>()
|
|
const emit = defineEmits<{ update: [name: string, value: unknown] }>()
|
|
|
|
function spanClass(field: FormFieldSchema): string {
|
|
// The relation manager always takes the whole row (design screen 5).
|
|
if (field.type === RELATION_MANAGER) {
|
|
return 'col-span-full'
|
|
}
|
|
switch (field.span) {
|
|
case 'left':
|
|
return 'min-[600px]:col-start-1'
|
|
case 'right':
|
|
return 'min-[600px]:col-start-2'
|
|
case 'auto':
|
|
case 'row':
|
|
return ''
|
|
default:
|
|
return 'col-span-full'
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="grid grid-cols-1 gap-x-6 gap-y-[22px] min-[600px]:grid-cols-2">
|
|
<FormField
|
|
v-for="field in fields"
|
|
:key="field.name"
|
|
:class="spanClass(field)"
|
|
:field="field"
|
|
:model-value="values[field.name]"
|
|
:errors="errors[field.name]"
|
|
:labels="labels?.[field.name]"
|
|
:source="source"
|
|
:record-id="recordId"
|
|
:id-prefix="idPrefix"
|
|
@update:model-value="(value: unknown) => emit('update', field.name, value)"
|
|
/>
|
|
</div>
|
|
</template>
|