feat(17-02): repeater field and dependent dropdown states
- RepeaterField: rows with drag handle, index, titleFrom title, error chip, move up/down (buttons, ArrowUp/ArrowDown on the handle, native drag and drop), expand/collapse, remove (asks only for a row holding a value), add with nested defaults, maxItems counter, empty state, collapse all; every change edits form state only - 422 row paths (content.3.answer) open the row, mark it and focus the nested control; the banner no longer repeats row messages - DropdownField dependsOn: waiting, loading, not used, empty, loaded with depth indent, failed with retry; cached per sibling value, last request wins, a stale value is cleared after a sibling change and announced - FormView provides form feedback (errors and notices by row path, the polite live region, its confirm dialog) and the options cache; FormGrid provides the sibling scope for repeater rows - repeater.* and form.dependent_* phrase keys (en, pl), smoke specs, docs
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
// provider (settings pages) gets the defaults of the injecting control.
|
||||
import { ref, type InjectionKey, type Ref } from 'vue'
|
||||
import type { FileRoutes } from '../../api/files'
|
||||
import type { AdminRecord } from '../../api/types'
|
||||
import type { AdminDependentOptionsResult, AdminRecord, FormField } from '../../api/types'
|
||||
|
||||
/** Read-only view of the form's current values. */
|
||||
export const FORM_VALUES: InjectionKey<Readonly<Ref<AdminRecord>>> = Symbol('summer.form.values')
|
||||
@@ -113,3 +113,39 @@ export const FIELD_UPLOAD_RESULT_EVENT = 'summer-upload-result'
|
||||
|
||||
/** How long a widget waits for its custom element to be defined. */
|
||||
export const WIDGET_TIMEOUT = 5000
|
||||
|
||||
/**
|
||||
* The record form's feedback (Phase 17, D-07, D-08): errors and save notices
|
||||
* keyed by field name or row path (`<repeater>.<index>.<nested>`), the form's
|
||||
* single polite live region, and its confirm dialog. FormView provides it; a
|
||||
* control without it (settings pages) announces nothing and never asks.
|
||||
*/
|
||||
export interface FormFeedback {
|
||||
errors: Readonly<Ref<Record<string, string[]>>>
|
||||
notices: Readonly<Ref<Record<string, string[]>>>
|
||||
/** Drops the error lines of one key: the administrator edited that value. */
|
||||
clearError(key: string): void
|
||||
/** Announces text in the form's polite live region. */
|
||||
announce(text: string): void
|
||||
/** Asks through the form's ConfirmDialog; resolves true on confirm. */
|
||||
confirm(request: { message: string; confirmLabel?: string; danger?: boolean }): Promise<boolean>
|
||||
}
|
||||
|
||||
export const FORM_FEEDBACK: InjectionKey<FormFeedback> = Symbol('summer.form.feedback')
|
||||
|
||||
/**
|
||||
* The fields a FormGrid renders and their values (D-08): a dependent
|
||||
* dropdown reads its sibling here. A repeater row's grid provides the row's
|
||||
* fields and values, and `path` names a field for its routes
|
||||
* (`<repeater>.<field>` in a row).
|
||||
*/
|
||||
export interface FieldScope {
|
||||
fields: Readonly<Ref<readonly FormField[]>>
|
||||
values: Readonly<Ref<AdminRecord>>
|
||||
path(name: string): string
|
||||
}
|
||||
|
||||
export const FIELD_SCOPE: InjectionKey<FieldScope> = Symbol('summer.form.fieldScope')
|
||||
|
||||
/** Dependent options per field path and sibling value, for the life of a form view. */
|
||||
export const DEPENDENT_CACHE: InjectionKey<Map<string, AdminDependentOptionsResult>> = Symbol('summer.form.dependentCache')
|
||||
|
||||
Reference in New Issue
Block a user