feat(10.1-02): render header and form partials through an allowlisted node renderer

- partialNodes rebuilds the server node tree with h() under the server's tag, attribute and URL lists
- PartialHost owns the skeleton, empty and failure states and keeps nodes visible on refetch
- type: partial is a valueless group-labelled field rendered on create and update
- ListView shows headerPartial above the list card and refetches it after bulk delete
- summer-partial and summer-stats style kit in main.css, documented in the cabana README; dist rebuilt
This commit is contained in:
Jakub Zych
2026-09-29 02:10:18 +02:00
parent 107d820109
commit 9df9fae930
20 changed files with 870 additions and 22 deletions

View File

@@ -0,0 +1,31 @@
<script setup lang="ts">
import { t } from '../../../app/i18n'
import PartialHost from '../../partial/PartialHost.vue'
import ExtensionFailure from '../../ui/ExtensionFailure.vue'
import type { FieldControlProps } from '../control'
// A `type: partial` form row (D-09; UI-SPEC S2): the controller partial
// named by `path`, rendered on create (no record) and update (?id=). It holds
// no value. The row's visible label, when declared, names the group. A form
// without a controller (a settings page) has no partial route to call.
const props = defineProps<FieldControlProps>()
</script>
<template>
<div
:id="controlId"
role="group"
data-partial-field
:aria-labelledby="props.field.label ? `${controlId}-label` : undefined"
:aria-describedby="describedBy || undefined"
>
<PartialHost
v-if="source"
variant="field"
:source="source"
:name="props.field.path || props.field.name"
:record-id="recordId ?? null"
/>
<ExtensionFailure v-else data-partial-failed :text="t('backend::lang.extension.partial_failed')" />
</div>
</template>

View File

@@ -162,7 +162,7 @@ onBeforeUnmount(() => {
:id="controlId"
role="group"
data-widget-host
:aria-labelledby="`${controlId}-label`"
:aria-labelledby="field.label ? `${controlId}-label` : undefined"
:aria-describedby="describedBy || undefined"
:aria-busy="status === 'loading' ? 'true' : undefined"
class="flex min-h-input items-center"