@omnitend/dashboard-for-laravel 0.42.2 → 0.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/base/DFormSelect.vue.d.ts +1 -1
- package/dist/components/extended/DXChoiceField.vue.d.ts +3 -3
- package/dist/components/extended/DXCurrencyInput.vue.d.ts +2 -2
- package/dist/components/extended/DXDashboard.vue.d.ts +14 -6
- package/dist/components/extended/DXDashboardNavbar.vue.d.ts +12 -3
- package/dist/components/extended/DXDashboardSidebar.vue.d.ts +21 -1
- package/dist/components/extended/DXForm.vue.d.ts +66 -1
- package/dist/components/extended/DXFormErrorSummary.vue.d.ts +70 -0
- package/dist/components/extended/DXFormField.vue.d.ts +3 -0
- package/dist/components/extended/DXStackingTable.vue.d.ts +25 -0
- package/dist/components/extended/DXTable.vue.d.ts +20 -0
- package/dist/components/extended/DXTableEditorModal.vue.d.ts +14 -10
- package/dist/composables/useAsyncOptions.d.ts +14 -1
- package/dist/composables/useForm.d.ts +26 -0
- package/dist/dashboard-for-laravel.js +12002 -11223
- package/dist/dashboard-for-laravel.js.map +1 -1
- package/dist/dashboard-for-laravel.umd.cjs +8 -8
- package/dist/dashboard-for-laravel.umd.cjs.map +1 -1
- package/dist/index.d.ts +7 -2
- package/dist/style.css +1 -1
- package/dist/types/index.d.ts +12 -0
- package/dist/types/navigation.d.ts +8 -0
- package/dist/utils/formErrorTargets.d.ts +86 -0
- package/dist/utils/pageScrollLock.d.ts +9 -0
- package/docs/public/api-reference.json +223 -9
- package/docs/public/docs-map.md +4 -2
- package/docs/public/llms.txt +4 -2
- package/package.json +9 -8
- package/resources/css/theme.scss +508 -36
- package/resources/js/components/base/DFormSelect.vue +5 -2
- package/resources/js/components/base/DModal.vue +20 -2
- package/resources/js/components/extended/DXChoiceField.vue +42 -26
- package/resources/js/components/extended/DXCurrencyInput.vue +3 -1
- package/resources/js/components/extended/DXDashboard.vue +236 -43
- package/resources/js/components/extended/DXDashboardNavbar.vue +23 -13
- package/resources/js/components/extended/DXDashboardSidebar.vue +103 -29
- package/resources/js/components/extended/DXField.vue +15 -9
- package/resources/js/components/extended/DXForm.vue +676 -28
- package/resources/js/components/extended/DXFormErrorSummary.vue +232 -0
- package/resources/js/components/extended/DXFormField.vue +11 -2
- package/resources/js/components/extended/DXRepeater.vue +30 -4
- package/resources/js/components/extended/DXStackingTable.vue +264 -0
- package/resources/js/components/extended/DXTable.vue +36 -4
- package/resources/js/components/extended/DXTableEditorModal.vue +29 -1
- package/resources/js/composables/useAsyncOptions.ts +30 -5
- package/resources/js/composables/useContainerWidth.ts +12 -3
- package/resources/js/composables/useForm.ts +196 -18
- package/resources/js/composables/useResourceEditor.ts +140 -107
- package/resources/js/index.ts +17 -0
- package/resources/js/types/index.ts +13 -0
- package/resources/js/types/navigation.ts +9 -0
- package/resources/js/utils/formErrorTargets.ts +317 -0
- package/resources/js/utils/pageScrollLock.ts +27 -0
- package/scripts/mcp-server.mjs +58 -6
|
@@ -13,10 +13,10 @@ interface Props {
|
|
|
13
13
|
/** Escape-hatch control props, already guard-merged by DXField. */
|
|
14
14
|
controlProps?: Record<string, any>;
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* The field's first async options load is still in flight. A searchable
|
|
17
|
+
* select then shows "Loading…" instead of its value's raw id.
|
|
18
18
|
*/
|
|
19
|
-
|
|
19
|
+
optionsPending?: boolean;
|
|
20
20
|
}
|
|
21
21
|
declare const _default: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
22
22
|
"update:modelValue": (value: any) => any;
|
|
@@ -17,9 +17,9 @@ interface Props {
|
|
|
17
17
|
* slot overrides it when the affix needs to be more than a string. */
|
|
18
18
|
append?: string;
|
|
19
19
|
}
|
|
20
|
-
declare var
|
|
20
|
+
declare var __VLS_13: {};
|
|
21
21
|
type __VLS_Slots = {} & {
|
|
22
|
-
append?: (props: typeof
|
|
22
|
+
append?: (props: typeof __VLS_13) => any;
|
|
23
23
|
};
|
|
24
24
|
declare const __VLS_component: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
25
25
|
"update:modelValue": (value: number | null) => any;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Navigation, NavbarActionsOnMobile, NavbarSearchAlign } from '../../types/navigation';
|
|
1
|
+
import type { Navigation, NavbarActionsOnMobile, NavbarSearchAlign, SidebarVariant } from '../../types/navigation';
|
|
2
2
|
interface Props {
|
|
3
3
|
/** Navigation structure for sidebar */
|
|
4
4
|
navigation: Navigation;
|
|
@@ -50,6 +50,13 @@ interface Props {
|
|
|
50
50
|
* (single-open accordion). `false`: all groups start open, toggled independently.
|
|
51
51
|
*/
|
|
52
52
|
autoCollapseInactiveGroups?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Sidebar colour scheme, forwarded to DXDashboardSidebar as `variant`:
|
|
55
|
+
* `'light'` (default: white pane, tinted group headers, green active item,
|
|
56
|
+
* Poppins) or `'dark'` (the navy rail). Colours come from the
|
|
57
|
+
* `--dx-sidebar-*` custom properties, so either can be rebranded in CSS.
|
|
58
|
+
*/
|
|
59
|
+
sidebarVariant?: SidebarVariant;
|
|
53
60
|
/** LocalStorage key for sidebar state persistence */
|
|
54
61
|
storageKey?: string;
|
|
55
62
|
/**
|
|
@@ -59,15 +66,15 @@ interface Props {
|
|
|
59
66
|
*/
|
|
60
67
|
dashboardId?: string;
|
|
61
68
|
}
|
|
62
|
-
declare var
|
|
69
|
+
declare var __VLS_13: string, __VLS_14: any, __VLS_24: string, __VLS_25: any, __VLS_30: {}, __VLS_41: {};
|
|
63
70
|
type __VLS_Slots = {} & {
|
|
64
|
-
[K in NonNullable<typeof
|
|
71
|
+
[K in NonNullable<typeof __VLS_13>]?: (props: typeof __VLS_14) => any;
|
|
65
72
|
} & {
|
|
66
|
-
[K in NonNullable<typeof
|
|
73
|
+
[K in NonNullable<typeof __VLS_24>]?: (props: typeof __VLS_25) => any;
|
|
67
74
|
} & {
|
|
68
|
-
default?: (props: typeof
|
|
75
|
+
default?: (props: typeof __VLS_30) => any;
|
|
69
76
|
} & {
|
|
70
|
-
default?: (props: typeof
|
|
77
|
+
default?: (props: typeof __VLS_41) => any;
|
|
71
78
|
};
|
|
72
79
|
declare const __VLS_component: import("vue").DefineComponent<Props, {
|
|
73
80
|
/**
|
|
@@ -93,6 +100,7 @@ declare const __VLS_component: import("vue").DefineComponent<Props, {
|
|
|
93
100
|
actionsOnMobile: NavbarActionsOnMobile;
|
|
94
101
|
userMenuLabel: string;
|
|
95
102
|
contentMaxWidth: string;
|
|
103
|
+
sidebarVariant: SidebarVariant;
|
|
96
104
|
storageKey: string;
|
|
97
105
|
dashboardId: string;
|
|
98
106
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -10,9 +10,8 @@ type __VLS_Props = {
|
|
|
10
10
|
pageTitle?: string;
|
|
11
11
|
/**
|
|
12
12
|
* Horizontal alignment of the search slot content within its region
|
|
13
|
-
* (`"start"` = flush left, `"center"` = centred).
|
|
14
|
-
*
|
|
15
|
-
* full-width row below.
|
|
13
|
+
* (`"start"` = flush left, `"center"` = centred). The region sits inline
|
|
14
|
+
* between the toggle/title and the user menu at every width.
|
|
16
15
|
*/
|
|
17
16
|
searchAlign?: NavbarSearchAlign;
|
|
18
17
|
/**
|
|
@@ -26,6 +25,14 @@ type __VLS_Props = {
|
|
|
26
25
|
* announces the trigger as the avatar's initial ("J") rather than as a menu.
|
|
27
26
|
*/
|
|
28
27
|
userMenuLabel?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Whether the sidebar the toggle controls is showing, exposed as the
|
|
30
|
+
* toggle's `aria-expanded`. Left unset, the toggle carries none.
|
|
31
|
+
* DXDashboard sets it.
|
|
32
|
+
*/
|
|
33
|
+
sidebarExpanded?: boolean;
|
|
34
|
+
/** `id` of the sidebar the toggle controls (its `aria-controls`). */
|
|
35
|
+
sidebarId?: string;
|
|
29
36
|
};
|
|
30
37
|
declare var __VLS_11: {}, __VLS_13: {}, __VLS_15: {
|
|
31
38
|
pageTitle: string;
|
|
@@ -76,6 +83,8 @@ declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, {}, {}
|
|
|
76
83
|
searchAlign: NavbarSearchAlign;
|
|
77
84
|
actionsOnMobile: NavbarActionsOnMobile;
|
|
78
85
|
userMenuLabel: string;
|
|
86
|
+
sidebarExpanded: boolean;
|
|
87
|
+
sidebarId: string;
|
|
79
88
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
80
89
|
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
81
90
|
export default _default;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Navigation } from '../../types/navigation';
|
|
1
|
+
import type { Navigation, SidebarVariant } from '../../types/navigation';
|
|
2
2
|
type __VLS_Props = {
|
|
3
3
|
/** Grouped navigation to render: an array of groups, each with a label and items. */
|
|
4
4
|
navigation: Navigation;
|
|
@@ -22,6 +22,20 @@ type __VLS_Props = {
|
|
|
22
22
|
* `false`: all groups start open and toggle independently.
|
|
23
23
|
*/
|
|
24
24
|
autoCollapseInactiveGroups?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Colour scheme: `'light'` (default) or `'dark'`. Colours come from the
|
|
27
|
+
* `--dx-sidebar-*` custom properties (see the theming guide), so either
|
|
28
|
+
* scheme can be rebranded in CSS.
|
|
29
|
+
*/
|
|
30
|
+
variant?: SidebarVariant;
|
|
31
|
+
/**
|
|
32
|
+
* Below `sm`, show the open sidebar as a full-screen menu over the page,
|
|
33
|
+
* with a close button (which emits `close`), instead of a rail beside it;
|
|
34
|
+
* hidden there until opened. The owner must answer `close`, Escape and
|
|
35
|
+
* `navigate`: DXDashboard sets this and does. Off by default, so a
|
|
36
|
+
* standalone sidebar keeps its rail on phones.
|
|
37
|
+
*/
|
|
38
|
+
phoneMenu?: boolean;
|
|
25
39
|
};
|
|
26
40
|
declare var __VLS_1: {
|
|
27
41
|
collapsed: boolean;
|
|
@@ -42,15 +56,21 @@ type __VLS_Slots = {} & {
|
|
|
42
56
|
footer?: (props: typeof __VLS_9) => any;
|
|
43
57
|
};
|
|
44
58
|
declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
59
|
+
close: () => any;
|
|
45
60
|
toggle: () => any;
|
|
61
|
+
navigate: () => any;
|
|
46
62
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
63
|
+
onClose?: (() => any) | undefined;
|
|
47
64
|
onToggle?: (() => any) | undefined;
|
|
65
|
+
onNavigate?: (() => any) | undefined;
|
|
48
66
|
}>, {
|
|
49
67
|
hidden: boolean;
|
|
50
68
|
title: string;
|
|
51
69
|
collapsed: boolean;
|
|
52
70
|
collapsibleGroups: boolean;
|
|
53
71
|
autoCollapseInactiveGroups: boolean;
|
|
72
|
+
variant: SidebarVariant;
|
|
73
|
+
phoneMenu: boolean;
|
|
54
74
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
55
75
|
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
56
76
|
export default _default;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { type ErrorSummarySelection } from "./DXFormErrorSummary.vue";
|
|
2
|
+
import { type ErrorTarget } from "../../utils/formErrorTargets";
|
|
1
3
|
import type { UseFormReturn } from "../../composables/useForm";
|
|
2
4
|
import type { DefineFormReturn } from "../../composables/defineForm";
|
|
3
5
|
import type { FieldDefinition, FormTab, LabelCols } from "../../types";
|
|
@@ -91,16 +93,77 @@ interface Props {
|
|
|
91
93
|
* `sm` up and stacks above the input below `sm`.
|
|
92
94
|
*/
|
|
93
95
|
labelCols?: LabelCols;
|
|
96
|
+
/**
|
|
97
|
+
* Where to list what the last failed submit returned (`form.submitFailure`:
|
|
98
|
+
* the server's message and every validation message, with readable
|
|
99
|
+
* labels):
|
|
100
|
+
*
|
|
101
|
+
* - `"footer"` (default) — directly above the submit button, below any
|
|
102
|
+
* tabs, next to the control the user just pressed.
|
|
103
|
+
* - `"top"` — above the fields, where the form-level alert sits.
|
|
104
|
+
* - `"external"` — the host renders a `DXFormErrorSummary` of its own
|
|
105
|
+
* elsewhere (beside a submit button outside the form, as DXTable's
|
|
106
|
+
* modal does). DXForm renders neither a summary nor, while a failure
|
|
107
|
+
* is listed, the form-level alert.
|
|
108
|
+
* - `false` — no summary; the form-level alert behaves as before.
|
|
109
|
+
*
|
|
110
|
+
* While a summary shows a failure the form-level alert does not render,
|
|
111
|
+
* so a failure shows exactly one alert. Each row naming a visible field is
|
|
112
|
+
* a button that selects the field's tab and focuses it.
|
|
113
|
+
*/
|
|
114
|
+
errorSummary?: "footer" | "top" | "external" | false;
|
|
115
|
+
/**
|
|
116
|
+
* The summary's heading when the failure has field errors (default
|
|
117
|
+
* "Couldn't save. Please check:"). A failure with none (a message-only
|
|
118
|
+
* 422, a 500) is headed by the server's message instead.
|
|
119
|
+
*/
|
|
120
|
+
errorSummaryTitle?: string;
|
|
121
|
+
/**
|
|
122
|
+
* The summary's sub-heading over the rows no visible field owns (default
|
|
123
|
+
* "Other problems"), shown only when some rows ARE owned.
|
|
124
|
+
*/
|
|
125
|
+
errorSummaryOtherTitle?: string;
|
|
126
|
+
/**
|
|
127
|
+
* After a failed submit, scroll the first field with an error into view
|
|
128
|
+
* (once its tab is selected and rendered). Never moves focus. On by
|
|
129
|
+
* default.
|
|
130
|
+
*/
|
|
131
|
+
scrollToError?: boolean;
|
|
94
132
|
}
|
|
95
133
|
type __VLS_Props = Props;
|
|
96
134
|
type __VLS_Slots = Record<string, (props: any) => any>;
|
|
135
|
+
/** Select the first visible tab owning a visible errored field, if any. */
|
|
97
136
|
declare function goToErrorTab(): void;
|
|
137
|
+
/**
|
|
138
|
+
* Take the user to an error a summary lists: select its tab, wait for the
|
|
139
|
+
* pane to render, look the field up in the form (its marker, anywhere
|
|
140
|
+
* inside the form's element, a teleport target inside the form included),
|
|
141
|
+
* scroll it into view and focus its control: an editable control that can
|
|
142
|
+
* take focus, waiting (about a second) while none can, e.g. an async editor
|
|
143
|
+
* still loading or a widget rendered disabled until its data arrives;
|
|
144
|
+
* anything else focusable only as a fallback. A focus counts only once
|
|
145
|
+
* `document.activeElement` is the control. The field is looked up again by
|
|
146
|
+
* its key whenever its element leaves the DOM (a re-render replacing it),
|
|
147
|
+
* and a moment with none at all is waited out. When no marker renders
|
|
148
|
+
* within the field's render frames, or nothing in it takes focus, focus
|
|
149
|
+
* moves to the selected tab's button. Exposed so a summary rendered OUTSIDE
|
|
150
|
+
* the form (DXTable's modal footer) can drive it.
|
|
151
|
+
*/
|
|
152
|
+
declare function focusErrorTarget(target: ErrorSummarySelection): Promise<void>;
|
|
98
153
|
type __VLS_PublicProps = __VLS_Props & {
|
|
99
|
-
/**
|
|
154
|
+
/**
|
|
155
|
+
* v-model for the active tab, as an index into the VISIBLE tabs. It is a
|
|
156
|
+
* view of `activeTabKey` (below), which is what DXForm actually tracks.
|
|
157
|
+
*/
|
|
100
158
|
"activeTab"?: number;
|
|
101
159
|
};
|
|
102
160
|
declare const __VLS_component: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
161
|
+
/** Select the first visible tab owning a visible errored field, if any. */
|
|
103
162
|
goToErrorTab: typeof goToErrorTab;
|
|
163
|
+
/** Select an error's tab, then scroll to and focus its field. */
|
|
164
|
+
focusErrorTarget: typeof focusErrorTarget;
|
|
165
|
+
/** What the last failed submit returned, resolved by this form's rules. */
|
|
166
|
+
errorTargets: import("vue").ComputedRef<ErrorTarget[]>;
|
|
104
167
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
105
168
|
submit: () => any;
|
|
106
169
|
"update:activeTab": (value: number) => any;
|
|
@@ -118,6 +181,8 @@ declare const __VLS_component: import("vue").DefineComponent<__VLS_PublicProps,
|
|
|
118
181
|
autoErrorTab: boolean;
|
|
119
182
|
cardTabs: boolean;
|
|
120
183
|
layoutThreshold: number;
|
|
184
|
+
errorSummary: "footer" | "top" | "external" | false;
|
|
185
|
+
scrollToError: boolean;
|
|
121
186
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
122
187
|
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
123
188
|
export default _default;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { UseFormReturn } from "../../composables/useForm";
|
|
2
|
+
import type { DefineFormReturn } from "../../composables/defineForm";
|
|
3
|
+
import type { FieldDefinition, FormTab } from "../../types";
|
|
4
|
+
import { type ErrorTarget } from "../../utils/formErrorTargets";
|
|
5
|
+
/** What a summary row points at (emitted by `select-target`). */
|
|
6
|
+
export interface ErrorSummarySelection {
|
|
7
|
+
/** The owning field's key. */
|
|
8
|
+
fieldKey: string;
|
|
9
|
+
/** The tab holding that field, or `null` in a flat form. */
|
|
10
|
+
tabKey: string | null;
|
|
11
|
+
/** The error key as the server sent it. */
|
|
12
|
+
errorKey: string;
|
|
13
|
+
}
|
|
14
|
+
interface Props {
|
|
15
|
+
/** The form: a `useForm` return or a `defineForm` return. */
|
|
16
|
+
form: UseFormReturn<any> | DefineFormReturn<any>;
|
|
17
|
+
/** Field definitions (optional when `form` is a defineForm return). */
|
|
18
|
+
fields?: FieldDefinition[];
|
|
19
|
+
/** The form's tabs, so rows follow tab order and know their tab. */
|
|
20
|
+
tabs?: FormTab[];
|
|
21
|
+
/**
|
|
22
|
+
* Extra context merged under the form data when evaluating `when` and
|
|
23
|
+
* function labels, as on DXForm.
|
|
24
|
+
*/
|
|
25
|
+
context?: Record<string, any>;
|
|
26
|
+
/**
|
|
27
|
+
* Field visibility, replacing the default (`when`/`show` against form
|
|
28
|
+
* data + `context`). A host form passes its own predicate so the summary
|
|
29
|
+
* agrees with what it renders.
|
|
30
|
+
*/
|
|
31
|
+
isFieldVisible?: (field: FieldDefinition) => boolean;
|
|
32
|
+
/** Tab visibility, replacing the default (the tab's `when`). */
|
|
33
|
+
isTabVisible?: (tab: FormTab) => boolean;
|
|
34
|
+
/**
|
|
35
|
+
* A field's label, replacing the default (its `label`, resolved against
|
|
36
|
+
* `labelModel`: the form model, or the row for a repeater sub-field).
|
|
37
|
+
*/
|
|
38
|
+
resolveLabel?: (field: FieldDefinition, labelModel: Record<string, any>) => string | null | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Rows already resolved by the host (from `resolveErrorTargets` over
|
|
41
|
+
* `form.submitFailure.errors`). When given, the summary lists these and
|
|
42
|
+
* resolves nothing itself; the message still comes from
|
|
43
|
+
* `form.submitFailure`, and nothing renders while that is null.
|
|
44
|
+
*/
|
|
45
|
+
targets?: ErrorTarget[] | null;
|
|
46
|
+
/**
|
|
47
|
+
* The heading when the failure has at least one field error. The
|
|
48
|
+
* server's own message is not used then: Laravel's default 422 message
|
|
49
|
+
* is the first error plus "(and N more errors)", which repeats the first
|
|
50
|
+
* row. A failure with no field errors (a message-only 422, a 500, a
|
|
51
|
+
* network failure) keeps the server's message as the heading, as it is
|
|
52
|
+
* the only information.
|
|
53
|
+
*/
|
|
54
|
+
title?: string;
|
|
55
|
+
/**
|
|
56
|
+
* The sub-heading over the rows no visible field owns, listed after the
|
|
57
|
+
* owned rows. Shown only when there are both kinds; when no row is owned
|
|
58
|
+
* they are listed under the heading alone.
|
|
59
|
+
*/
|
|
60
|
+
otherTitle?: string;
|
|
61
|
+
}
|
|
62
|
+
declare const _default: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
63
|
+
"select-target": (selection: ErrorSummarySelection) => any;
|
|
64
|
+
}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
|
|
65
|
+
"onSelect-target"?: ((selection: ErrorSummarySelection) => any) | undefined;
|
|
66
|
+
}>, {
|
|
67
|
+
title: string;
|
|
68
|
+
otherTitle: string;
|
|
69
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
70
|
+
export default _default;
|
|
@@ -15,6 +15,9 @@ interface Props {
|
|
|
15
15
|
declare var __VLS_2: `field(${string})`, __VLS_3: {
|
|
16
16
|
field: FieldDefinition;
|
|
17
17
|
model: Record<string, any>;
|
|
18
|
+
targetAttrs: {
|
|
19
|
+
"data-dx-field-key": string;
|
|
20
|
+
};
|
|
18
21
|
}, __VLS_6: `field-before(${string})`, __VLS_7: {
|
|
19
22
|
field: FieldDefinition;
|
|
20
23
|
model: Record<string, any>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
/**
|
|
3
|
+
* Stack when the wrapper's content width is below this many px. 576 is
|
|
4
|
+
* Bootstrap's `sm` breakpoint, but measured on the container.
|
|
5
|
+
*/
|
|
6
|
+
stackBelow?: number;
|
|
7
|
+
}
|
|
8
|
+
declare var __VLS_1: {
|
|
9
|
+
stacked: boolean;
|
|
10
|
+
};
|
|
11
|
+
type __VLS_Slots = {} & {
|
|
12
|
+
default?: (props: typeof __VLS_1) => any;
|
|
13
|
+
};
|
|
14
|
+
declare const __VLS_component: import("vue").DefineComponent<Props, {
|
|
15
|
+
isStacked: import("vue").ComputedRef<boolean>;
|
|
16
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {
|
|
17
|
+
stackBelow: number;
|
|
18
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
19
|
+
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
20
|
+
export default _default;
|
|
21
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
22
|
+
new (): {
|
|
23
|
+
$slots: S;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
@@ -317,6 +317,17 @@ export interface Props<TItem = any> {
|
|
|
317
317
|
createText?: string;
|
|
318
318
|
/** Override the modal's Delete button label (default: "Delete {item}"). */
|
|
319
319
|
deleteText?: string;
|
|
320
|
+
/**
|
|
321
|
+
* Heading of the modal's failed-save summary when the failure has field
|
|
322
|
+
* errors (default "Couldn't save. Please check:"). A failure with none
|
|
323
|
+
* is headed by the server's message instead.
|
|
324
|
+
*/
|
|
325
|
+
errorSummaryTitle?: string;
|
|
326
|
+
/**
|
|
327
|
+
* Sub-heading over the summary rows no edit field owns (default "Other
|
|
328
|
+
* problems"), shown only when some rows ARE owned.
|
|
329
|
+
*/
|
|
330
|
+
errorSummaryOtherTitle?: string;
|
|
320
331
|
/** API endpoint pattern for updates (e.g., "/api/products/:id") */
|
|
321
332
|
editUrl?: string;
|
|
322
333
|
/**
|
|
@@ -416,6 +427,15 @@ export interface Props<TItem = any> {
|
|
|
416
427
|
* doesn't *look* clickable, and a click on it can't quietly do something.
|
|
417
428
|
*/
|
|
418
429
|
rowClickable?: (item: TItem, index: number) => boolean;
|
|
430
|
+
/**
|
|
431
|
+
* Whether clicking a row opens the edit modal (when `editFields` are set).
|
|
432
|
+
* Defaults to `true`. Set `false` when `editFields` exist only for the
|
|
433
|
+
* create modal (`openCreate()` / the create button) and a row click does
|
|
434
|
+
* something else, such as opening the record's own page from `row-clicked`:
|
|
435
|
+
* the row then emits `row-clicked` without also opening the edit modal, and
|
|
436
|
+
* only a `row-clicked` listener makes rows look clickable (#200).
|
|
437
|
+
*/
|
|
438
|
+
editOnRowClick?: boolean;
|
|
419
439
|
/** Enable client-side filtering, sorting, and pagination on items array */
|
|
420
440
|
clientSide?: boolean;
|
|
421
441
|
/**
|
|
@@ -46,37 +46,41 @@ interface Props {
|
|
|
46
46
|
createText?: string;
|
|
47
47
|
/** Override the Delete button label. */
|
|
48
48
|
deleteText?: string;
|
|
49
|
+
/** The footer summary's heading for a failure with field errors. */
|
|
50
|
+
errorSummaryTitle?: string;
|
|
51
|
+
/** The footer summary's sub-heading over rows no field owns. */
|
|
52
|
+
errorSummaryOtherTitle?: string;
|
|
49
53
|
}
|
|
50
|
-
declare var
|
|
54
|
+
declare var __VLS_23: `edit-value(${string})`, __VLS_24: {
|
|
51
55
|
item: any;
|
|
52
56
|
value: any;
|
|
53
57
|
update: any;
|
|
54
58
|
field: any;
|
|
55
|
-
},
|
|
59
|
+
}, __VLS_27: `edit-span(${string})`, __VLS_28: {
|
|
56
60
|
item: any;
|
|
57
61
|
value: any;
|
|
58
62
|
update: any;
|
|
59
63
|
close: () => void;
|
|
60
|
-
},
|
|
64
|
+
}, __VLS_31: `tab-content(${string})`, __VLS_32: {
|
|
61
65
|
item: any;
|
|
62
66
|
tab: any;
|
|
63
|
-
},
|
|
67
|
+
}, __VLS_35: `tab-before(${string})`, __VLS_36: {
|
|
64
68
|
item: any;
|
|
65
69
|
tab: any;
|
|
66
|
-
},
|
|
70
|
+
}, __VLS_39: `tab-after(${string})`, __VLS_40: {
|
|
67
71
|
item: any;
|
|
68
72
|
tab: any;
|
|
69
73
|
};
|
|
70
74
|
type __VLS_Slots = {} & {
|
|
71
|
-
[K in NonNullable<typeof
|
|
75
|
+
[K in NonNullable<typeof __VLS_23>]?: (props: typeof __VLS_24) => any;
|
|
72
76
|
} & {
|
|
73
|
-
[K in NonNullable<typeof
|
|
77
|
+
[K in NonNullable<typeof __VLS_27>]?: (props: typeof __VLS_28) => any;
|
|
74
78
|
} & {
|
|
75
|
-
[K in NonNullable<typeof
|
|
79
|
+
[K in NonNullable<typeof __VLS_31>]?: (props: typeof __VLS_32) => any;
|
|
76
80
|
} & {
|
|
77
|
-
[K in NonNullable<typeof
|
|
81
|
+
[K in NonNullable<typeof __VLS_35>]?: (props: typeof __VLS_36) => any;
|
|
78
82
|
} & {
|
|
79
|
-
[K in NonNullable<typeof
|
|
83
|
+
[K in NonNullable<typeof __VLS_39>]?: (props: typeof __VLS_40) => any;
|
|
80
84
|
};
|
|
81
85
|
declare const __VLS_component: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
82
86
|
cancel: () => any;
|
|
@@ -7,13 +7,26 @@ import type { FieldDefinition, FieldOption } from "../types";
|
|
|
7
7
|
* out-of-order responses clobbering newer ones; loader failures are swallowed
|
|
8
8
|
* so the last good options (or the static `field.options` fallback) stay put.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* Known limit: replacing a field's `optionsLoader` at runtime does NOT restart
|
|
11
|
+
* the "first load" state (loaded options and `firstLoadSettled` carry over).
|
|
12
|
+
* Loader identity is not a stable signal: an inline loader in a computed
|
|
13
|
+
* `fields` array is a new function on every recompute, so treating each new
|
|
14
|
+
* function as a new source would blank the field and refetch on every
|
|
15
|
+
* unrelated edit.
|
|
16
|
+
*
|
|
17
|
+
* Extracted from DXField's inline logic (#135).
|
|
11
18
|
* `getField` is an accessor (props aren't destructurable without losing
|
|
12
19
|
* reactivity); `effectiveModel` is the model passed to the loader.
|
|
13
20
|
*/
|
|
14
21
|
export interface UseAsyncOptionsReturn {
|
|
15
22
|
/** Options returned by the most recent successful load, or null if none yet. */
|
|
16
23
|
loadedOptions: Ref<FieldOption[] | null>;
|
|
24
|
+
/**
|
|
25
|
+
* False until the first load has SETTLED (succeeded or failed). Only that
|
|
26
|
+
* first load leaves a field with nothing to show; a later reload keeps the
|
|
27
|
+
* options it has, or the static fallback after a failure.
|
|
28
|
+
*/
|
|
29
|
+
firstLoadSettled: Ref<boolean>;
|
|
17
30
|
/** Trigger a load (no-op when the field has no `optionsLoader`). */
|
|
18
31
|
loadOptions: () => Promise<void>;
|
|
19
32
|
}
|
|
@@ -4,6 +4,18 @@ export interface ValidationErrors {
|
|
|
4
4
|
[key: string]: string[];
|
|
5
5
|
}
|
|
6
6
|
export type FormError = ApiError;
|
|
7
|
+
/**
|
|
8
|
+
* What the most recent failed submit returned (#194): the server's message and
|
|
9
|
+
* a COPY of its validation errors, frozen at the moment the submit failed.
|
|
10
|
+
* The live `form.errors` empties as the user fixes fields; this does not, so a
|
|
11
|
+
* summary built from it keeps listing every message until the next submit.
|
|
12
|
+
*/
|
|
13
|
+
export interface SubmitFailure {
|
|
14
|
+
/** A message fit to show a user (never empty, never a raw exception). */
|
|
15
|
+
message: string;
|
|
16
|
+
/** The validation errors the submit returned (`{}` when none). */
|
|
17
|
+
errors: ValidationErrors;
|
|
18
|
+
}
|
|
7
19
|
export interface FormSubmitOptions<TPayload = unknown, TResponse = unknown> {
|
|
8
20
|
onSuccess?: (data: TResponse) => void;
|
|
9
21
|
onError?: (error: FormError) => void;
|
|
@@ -29,6 +41,19 @@ export interface FormState<TData extends Record<string, any>> {
|
|
|
29
41
|
*/
|
|
30
42
|
wasSuccessful: boolean;
|
|
31
43
|
shouldShowMessage: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* What the most recent submit failed with, or `null`. Set when a submit
|
|
46
|
+
* fails (validation or otherwise; an aborted submit does not count),
|
|
47
|
+
* cleared when a submit starts and when one succeeds. Untouched by
|
|
48
|
+
* `clearError`, `clearErrors`, `setErrors` and field edits.
|
|
49
|
+
*/
|
|
50
|
+
submitFailure: SubmitFailure | null;
|
|
51
|
+
/**
|
|
52
|
+
* How many submits have failed, counting every failure (including a
|
|
53
|
+
* `preserveErrors` resubmit that returned the same errors), so a watcher
|
|
54
|
+
* can react to "a submit just failed" even when nothing else changed.
|
|
55
|
+
*/
|
|
56
|
+
failedSubmitCount: number;
|
|
32
57
|
}
|
|
33
58
|
export interface UseFormReturn<TData extends Record<string, any>> extends FormState<TData> {
|
|
34
59
|
hasErrors: ComputedRef<boolean>;
|
|
@@ -50,4 +75,5 @@ export interface UseFormReturn<TData extends Record<string, any>> extends FormSt
|
|
|
50
75
|
patch: <TResponse = unknown>(url: string, options?: FormSubmitOptions<TData, TResponse>) => Promise<TResponse>;
|
|
51
76
|
delete: <TResponse = unknown>(url: string, options?: FormSubmitOptions<TData, TResponse>) => Promise<TResponse>;
|
|
52
77
|
}
|
|
78
|
+
export declare function isAbortError(error: unknown): boolean;
|
|
53
79
|
export declare function useForm<TData extends Record<string, any>>(initialData?: TData): UseFormReturn<TData>;
|