feat(10.1-02): mount plugin widget elements and run their actions from the form
- pluginAssets loads controller scripts and stylesheets from {base}/assets/ only, once per URL
- WidgetField mounts the custom element with attributes only and posts summer-action through the typed client
- Only declared fill keys returned by the server are patched; the form turns dirty and nothing saves
- widget is a registered valueless type rendered on create and update, labelled as a group
- backend::lang.extension strings in en and pl; embedded dist rebuilt
This commit is contained in:
@@ -3,7 +3,9 @@
|
||||
// dashed box, so an unknown type never breaks the form. The relation manager
|
||||
// is registered like any control but holds no form value: it edits its
|
||||
// relation through its own endpoints and renders only on an existing record.
|
||||
// Phase 10.1 turns this seam into the plugin extension point.
|
||||
// Phase 10.1 adds the plugin extension types: a widget mounts a plugin custom
|
||||
// element (D-04, D-09). It holds no form value either, but it renders on
|
||||
// create and update.
|
||||
import type { Component } from 'vue'
|
||||
import CheckboxField from './fields/CheckboxField.vue'
|
||||
import DropdownField from './fields/DropdownField.vue'
|
||||
@@ -14,6 +16,7 @@ import SwitchField from './fields/SwitchField.vue'
|
||||
import TextField from './fields/TextField.vue'
|
||||
import TextareaField from './fields/TextareaField.vue'
|
||||
import UnsupportedField from './fields/UnsupportedField.vue'
|
||||
import WidgetField from './fields/WidgetField.vue'
|
||||
|
||||
// The control helpers live in ./control so the field components never import
|
||||
// this module: registry -> field -> registry would be an import cycle whose
|
||||
@@ -33,24 +36,34 @@ const renderers = new Map<string, Component>([
|
||||
['checkbox', CheckboxField],
|
||||
['relation', RelationField],
|
||||
[RELATION_MANAGER, RelationManager],
|
||||
['widget', WidgetField],
|
||||
])
|
||||
|
||||
/** Types whose control shows the label itself (toggle cards, relation manager). */
|
||||
const selfLabelled = new Set<string>(['switch', 'checkbox', RELATION_MANAGER])
|
||||
|
||||
/**
|
||||
* Types that need a saved record and hold no form value: never rendered on
|
||||
* create, never part of the save body (D-05, design screen 5).
|
||||
* Types that need a saved record: never rendered on create (D-05, design
|
||||
* screen 5).
|
||||
*/
|
||||
const recordBound = new Set<string>([RELATION_MANAGER])
|
||||
|
||||
/** Types that hold no form value: never part of the save body (D-09). */
|
||||
const valueless = new Set<string>([RELATION_MANAGER, 'widget'])
|
||||
|
||||
/**
|
||||
* Types whose control is a group rather than one focusable element: the
|
||||
* visible label is a span the group points at, not a label for an input.
|
||||
*/
|
||||
const groupLabelledTypes = new Set<string>(['widget'])
|
||||
|
||||
export function rendererFor(type: string): Component {
|
||||
return renderers.get(type) ?? UnsupportedField
|
||||
}
|
||||
|
||||
/** Whether the SPA can edit values of this field type. */
|
||||
export function isRegistered(type: string): boolean {
|
||||
return renderers.has(type) && !recordBound.has(type)
|
||||
return renderers.has(type) && !valueless.has(type)
|
||||
}
|
||||
|
||||
/** Whether a field type renders only on an existing record (relation manager). */
|
||||
@@ -61,3 +74,8 @@ export function needsRecord(type: string): boolean {
|
||||
export function ownsLabel(type: string): boolean {
|
||||
return selfLabelled.has(type)
|
||||
}
|
||||
|
||||
/** Whether a field type's label labels a group (widget) instead of a control. */
|
||||
export function groupLabelled(type: string): boolean {
|
||||
return groupLabelledTypes.has(type)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user