@omnitend/dashboard-for-laravel 0.43.0 → 0.45.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/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/DXLoading.vue.d.ts +3 -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 +13605 -13058
- 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 +5 -1
- package/dist/style.css +1 -1
- package/dist/types/index.d.ts +12 -0
- package/dist/utils/formErrorTargets.d.ts +86 -0
- package/docs/public/api-reference.json +148 -8
- package/docs/public/docs-map.md +3 -2
- package/docs/public/llms.txt +3 -2
- package/package.json +9 -8
- package/resources/css/theme.scss +176 -0
- package/resources/js/components/base/DFormSelect.vue +5 -2
- package/resources/js/components/extended/DXChoiceField.vue +42 -26
- package/resources/js/components/extended/DXField.vue +51 -17
- 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/DXLoading.vue +18 -11
- package/resources/js/components/extended/DXRepeater.vue +30 -4
- package/resources/js/components/extended/DXTable.vue +38 -9
- package/resources/js/components/extended/DXTableEditorModal.vue +29 -1
- package/resources/js/composables/useAsyncOptions.ts +30 -5
- package/resources/js/composables/useForm.ts +196 -18
- package/resources/js/composables/useResourceEditor.ts +140 -107
- package/resources/js/index.ts +15 -0
- package/resources/js/types/index.ts +13 -0
- package/resources/js/utils/formErrorTargets.ts +317 -0
- package/scripts/mcp-server.mjs +58 -6
package/dist/types/index.d.ts
CHANGED
|
@@ -297,6 +297,17 @@ export interface FieldDefinition {
|
|
|
297
297
|
* declared.
|
|
298
298
|
*/
|
|
299
299
|
submit?: boolean;
|
|
300
|
+
/**
|
|
301
|
+
* Extra validation-error keys this field owns (#194), for a field whose
|
|
302
|
+
* server-side errors arrive under keys other than its own: a span or
|
|
303
|
+
* component field that edits nested data, say. Patterns, where `*`
|
|
304
|
+
* matches exactly one dot segment: `["lines.*", "lines.*.*"]` claims
|
|
305
|
+
* `lines.0` and `lines.0.price` but not `lines` or `lines.0.a.b`.
|
|
306
|
+
* Checked before the usual matching (the field's own key, then any key
|
|
307
|
+
* nested under it), so it decides which field an error belongs to: the
|
|
308
|
+
* label it is listed under in the error summary and the tab it selects.
|
|
309
|
+
*/
|
|
310
|
+
errorKeys?: string[];
|
|
300
311
|
/**
|
|
301
312
|
* Conditionally show or hide this field. When omitted the field is
|
|
302
313
|
* always visible. Boolean or a function of the form model; evaluated
|
|
@@ -330,6 +341,7 @@ export interface BaseFieldDef {
|
|
|
330
341
|
readonly?: MaybeFn<boolean>;
|
|
331
342
|
plaintext?: MaybeFn<boolean>;
|
|
332
343
|
submit?: boolean;
|
|
344
|
+
errorKeys?: string[];
|
|
333
345
|
when?: MaybeFn<boolean>;
|
|
334
346
|
show?: () => boolean;
|
|
335
347
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which field a validation-error key belongs to (#194).
|
|
3
|
+
*
|
|
4
|
+
* One pure function decides, so the error summary's rows, the tab a failed
|
|
5
|
+
* submit selects and the field it scrolls to can never disagree. No Vue: the
|
|
6
|
+
* caller injects visibility and label resolution, so DXForm can pass its own
|
|
7
|
+
* `when`/`context`-aware predicates and a standalone summary can use the
|
|
8
|
+
* defaults below.
|
|
9
|
+
*/
|
|
10
|
+
import type { FieldDefinition, FormTab } from "../types";
|
|
11
|
+
import type { ValidationErrors } from "../composables/useForm";
|
|
12
|
+
/** One error key and where it belongs. */
|
|
13
|
+
export interface ErrorTarget {
|
|
14
|
+
/** The key as the server sent it (`lines.0.price`). */
|
|
15
|
+
errorKey: string;
|
|
16
|
+
/** The server's messages for that key, as sent. */
|
|
17
|
+
messages: string[];
|
|
18
|
+
/**
|
|
19
|
+
* One display line per message: the message itself, or `"Label: message"`
|
|
20
|
+
* when the message does not already say which field it is about (see
|
|
21
|
+
* `resolveErrorTargets`).
|
|
22
|
+
*/
|
|
23
|
+
lines: string[];
|
|
24
|
+
/** The owning field's key, or `null` when no visible field owns it. */
|
|
25
|
+
fieldKey: string | null;
|
|
26
|
+
/** The tab holding the owning field, or `null` (flat form, or unowned). */
|
|
27
|
+
tabKey: string | null;
|
|
28
|
+
/** A readable name for the key: "Price (line 1)", "Delivery date". */
|
|
29
|
+
label: string;
|
|
30
|
+
}
|
|
31
|
+
export interface ResolveErrorTargetsOptions {
|
|
32
|
+
/** The form's field definitions. */
|
|
33
|
+
fields?: FieldDefinition[];
|
|
34
|
+
/**
|
|
35
|
+
* The form's tabs. When given (non-empty), only fields listed in a
|
|
36
|
+
* visible tab can own an error, as only those render. A tab key with no
|
|
37
|
+
* field definition (consumer-rendered content) can still own its own key
|
|
38
|
+
* and keys nested under it.
|
|
39
|
+
*/
|
|
40
|
+
tabs?: FormTab[];
|
|
41
|
+
/** Model for the default predicates and labels (form data + context). */
|
|
42
|
+
model?: Record<string, any>;
|
|
43
|
+
/** Field visibility. Default: `when` and `show` against `model`. */
|
|
44
|
+
isFieldVisible?: (field: FieldDefinition) => boolean;
|
|
45
|
+
/** Tab visibility. Default: the tab's `when` against `model`. */
|
|
46
|
+
isTabVisible?: (tab: FormTab) => boolean;
|
|
47
|
+
/**
|
|
48
|
+
* A field's label. Default: its `label`, called with `labelModel` when it
|
|
49
|
+
* is a function. `labelModel` is `model` for a form field and the ROW
|
|
50
|
+
* (`model.lines[0]`) for a repeater sub-field, as DXRepeater renders it.
|
|
51
|
+
* An empty result, or a label function that throws, falls back to the
|
|
52
|
+
* humanised key, so a broken label can never hide an error.
|
|
53
|
+
*/
|
|
54
|
+
resolveLabel?: (field: FieldDefinition, labelModel: Record<string, any>) => string | null | undefined;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* `delivery_date` → "Delivery date"; `lines.0.price` → "Lines price (line 1)".
|
|
58
|
+
* A trailing `id` word names the record, not the thing, so it is dropped
|
|
59
|
+
* (`supplier_id`, `supplierId` → "Supplier"); an `id` word that is the whole
|
|
60
|
+
* segment, or not at its end, reads "ID" (`id` → "ID"). Words that merely end
|
|
61
|
+
* in "id" (`paid`, `valid`) are untouched.
|
|
62
|
+
*/
|
|
63
|
+
export declare function humaniseErrorKey(key: string): string;
|
|
64
|
+
/**
|
|
65
|
+
* Resolve every key in `errors` to its owning field, tab and label, with the
|
|
66
|
+
* display line for each message. Rows come in tab order, then field order
|
|
67
|
+
* (the order of `tab.fieldKeys`, or of `fields` in a flat form), then the
|
|
68
|
+
* order the server sent the keys; keys no visible field owns come last.
|
|
69
|
+
*
|
|
70
|
+
* Ownership, first match wins: a field whose `errorKeys` pattern matches; a
|
|
71
|
+
* field whose key equals the error key; the field with the longest key that
|
|
72
|
+
* is a dot prefix of it (repeater rows, media maps). Hidden fields, and
|
|
73
|
+
* fields on hidden tabs or on no tab of a tabbed form, never own a key; it
|
|
74
|
+
* falls through to the next rule, or is unowned.
|
|
75
|
+
*
|
|
76
|
+
* Labels: the owning field's label; for `field.N.sub` the sub-field's label
|
|
77
|
+
* (when the field defines `fields` and one has that key) or the field's own,
|
|
78
|
+
* plus "(line N+1)"; for any other nested key (a media map's
|
|
79
|
+
* `image_media.<uuid>`) the field's label. An unowned key is humanised.
|
|
80
|
+
*
|
|
81
|
+
* Display lines: "Label: message", except that the label is left off when
|
|
82
|
+
* the message already contains it (case-insensitive) — unless the message
|
|
83
|
+
* contains a raw key (`_`, or a dot inside a word) or another row has the
|
|
84
|
+
* same message text, when the label is what tells the rows apart.
|
|
85
|
+
*/
|
|
86
|
+
export declare function resolveErrorTargets(errors: ValidationErrors | null | undefined, options?: ResolveErrorTargetsOptions): ErrorTarget[];
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generated": "2026-10-
|
|
2
|
+
"generated": "2026-10-11T09:08:08.777Z",
|
|
3
3
|
"package": {
|
|
4
4
|
"name": "@omnitend/dashboard-for-laravel",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.45.0"
|
|
6
6
|
},
|
|
7
7
|
"components": {
|
|
8
8
|
"base": [
|
|
@@ -1475,10 +1475,10 @@
|
|
|
1475
1475
|
"description": "Escape-hatch control props, already guard-merged by DXField."
|
|
1476
1476
|
},
|
|
1477
1477
|
{
|
|
1478
|
-
"name": "
|
|
1478
|
+
"name": "optionsPending",
|
|
1479
1479
|
"type": "boolean",
|
|
1480
1480
|
"required": false,
|
|
1481
|
-
"description": "
|
|
1481
|
+
"description": "The field's first async options load is still in flight. A searchable\nselect then shows \"Loading…\" instead of its value's raw id."
|
|
1482
1482
|
}
|
|
1483
1483
|
],
|
|
1484
1484
|
"events": [
|
|
@@ -2530,6 +2530,32 @@
|
|
|
2530
2530
|
"type": "LabelCols",
|
|
2531
2531
|
"required": false,
|
|
2532
2532
|
"description": "Label column width for horizontal layout (mirrors BFormGroup's\n`labelCols`/`labelCols*` props). Overridable per-field via\n`field.labelCols`. Ignored when `layout` is \"vertical\". Omitted (here\nand on the field): the label takes `--dx-form-label-width` (45%) from\n`sm` up and stacks above the input below `sm`."
|
|
2533
|
+
},
|
|
2534
|
+
{
|
|
2535
|
+
"name": "errorSummary",
|
|
2536
|
+
"type": "union",
|
|
2537
|
+
"required": false,
|
|
2538
|
+
"default": "\"footer\"",
|
|
2539
|
+
"description": "Where to list what the last failed submit returned (`form.submitFailure`:\nthe server's message and every validation message, with readable\nlabels):\n\n- `\"footer\"` (default) — directly above the submit button, below any\n tabs, next to the control the user just pressed.\n- `\"top\"` — above the fields, where the form-level alert sits.\n- `\"external\"` — the host renders a `DXFormErrorSummary` of its own\n elsewhere (beside a submit button outside the form, as DXTable's\n modal does). DXForm renders neither a summary nor, while a failure\n is listed, the form-level alert.\n- `false` — no summary; the form-level alert behaves as before.\n\nWhile a summary shows a failure the form-level alert does not render,\nso a failure shows exactly one alert. Each row naming a visible field is\na button that selects the field's tab and focuses it."
|
|
2540
|
+
},
|
|
2541
|
+
{
|
|
2542
|
+
"name": "errorSummaryTitle",
|
|
2543
|
+
"type": "string",
|
|
2544
|
+
"required": false,
|
|
2545
|
+
"description": "The summary's heading when the failure has field errors (default\n\"Couldn't save. Please check:\"). A failure with none (a message-only\n422, a 500) is headed by the server's message instead."
|
|
2546
|
+
},
|
|
2547
|
+
{
|
|
2548
|
+
"name": "errorSummaryOtherTitle",
|
|
2549
|
+
"type": "string",
|
|
2550
|
+
"required": false,
|
|
2551
|
+
"description": "The summary's sub-heading over the rows no visible field owns (default\n\"Other problems\"), shown only when some rows ARE owned."
|
|
2552
|
+
},
|
|
2553
|
+
{
|
|
2554
|
+
"name": "scrollToError",
|
|
2555
|
+
"type": "boolean",
|
|
2556
|
+
"required": false,
|
|
2557
|
+
"default": "true",
|
|
2558
|
+
"description": "After a failed submit, scroll the first field with an error into view\n(once its tab is selected and rendered). Never moves focus. On by\ndefault."
|
|
2533
2559
|
}
|
|
2534
2560
|
],
|
|
2535
2561
|
"events": [
|
|
@@ -2645,6 +2671,85 @@
|
|
|
2645
2671
|
],
|
|
2646
2672
|
"methods": []
|
|
2647
2673
|
},
|
|
2674
|
+
{
|
|
2675
|
+
"name": "DXFormErrorSummary",
|
|
2676
|
+
"category": "extended",
|
|
2677
|
+
"filePath": "resources/js/components/extended/DXFormErrorSummary.vue",
|
|
2678
|
+
"description": "Lists everything the last failed submit of a `useForm` returned: a heading, then one row per validation message with a readable label (\"Price (line 1): Must be positive.\"). The heading is `title` (\"Couldn't save. Please check:\") when the failure has field errors, and the server's message when it has none (a message-only 422, a 500, a network failure), since then the message is all there is. Built from `form.submitFailure`, so it survives field edits and `clearError`, and disappears when the next submit starts or one succeeds. Renders nothing before a failure. A row whose key belongs to a visible field is a button that emits `select-target`, for the host to select the field's tab and focus it; a key no visible field owns is listed as plain text, after the owned rows, under `otherTitle` (\"Other problems\") when both kinds are present. Place it next to the submit control.",
|
|
2679
|
+
"props": [
|
|
2680
|
+
{
|
|
2681
|
+
"name": "form",
|
|
2682
|
+
"type": "union",
|
|
2683
|
+
"required": true,
|
|
2684
|
+
"description": "The form: a `useForm` return or a `defineForm` return."
|
|
2685
|
+
},
|
|
2686
|
+
{
|
|
2687
|
+
"name": "fields",
|
|
2688
|
+
"type": "Array",
|
|
2689
|
+
"required": false,
|
|
2690
|
+
"description": "Field definitions (optional when `form` is a defineForm return)."
|
|
2691
|
+
},
|
|
2692
|
+
{
|
|
2693
|
+
"name": "tabs",
|
|
2694
|
+
"type": "Array",
|
|
2695
|
+
"required": false,
|
|
2696
|
+
"description": "The form's tabs, so rows follow tab order and know their tab."
|
|
2697
|
+
},
|
|
2698
|
+
{
|
|
2699
|
+
"name": "context",
|
|
2700
|
+
"type": "Record",
|
|
2701
|
+
"required": false,
|
|
2702
|
+
"description": "Extra context merged under the form data when evaluating `when` and\nfunction labels, as on DXForm."
|
|
2703
|
+
},
|
|
2704
|
+
{
|
|
2705
|
+
"name": "isFieldVisible",
|
|
2706
|
+
"type": "TSFunctionType",
|
|
2707
|
+
"required": false,
|
|
2708
|
+
"description": "Field visibility, replacing the default (`when`/`show` against form\ndata + `context`). A host form passes its own predicate so the summary\nagrees with what it renders."
|
|
2709
|
+
},
|
|
2710
|
+
{
|
|
2711
|
+
"name": "isTabVisible",
|
|
2712
|
+
"type": "TSFunctionType",
|
|
2713
|
+
"required": false,
|
|
2714
|
+
"description": "Tab visibility, replacing the default (the tab's `when`)."
|
|
2715
|
+
},
|
|
2716
|
+
{
|
|
2717
|
+
"name": "resolveLabel",
|
|
2718
|
+
"type": "TSFunctionType",
|
|
2719
|
+
"required": false,
|
|
2720
|
+
"description": "A field's label, replacing the default (its `label`, resolved against\n`labelModel`: the form model, or the row for a repeater sub-field)."
|
|
2721
|
+
},
|
|
2722
|
+
{
|
|
2723
|
+
"name": "targets",
|
|
2724
|
+
"type": "union",
|
|
2725
|
+
"required": false,
|
|
2726
|
+
"description": "Rows already resolved by the host (from `resolveErrorTargets` over\n`form.submitFailure.errors`). When given, the summary lists these and\nresolves nothing itself; the message still comes from\n`form.submitFailure`, and nothing renders while that is null."
|
|
2727
|
+
},
|
|
2728
|
+
{
|
|
2729
|
+
"name": "title",
|
|
2730
|
+
"type": "string",
|
|
2731
|
+
"required": false,
|
|
2732
|
+
"default": "\"Couldn't save. Please check:\"",
|
|
2733
|
+
"description": "The heading when the failure has at least one field error. The\nserver's own message is not used then: Laravel's default 422 message\nis the first error plus \"(and N more errors)\", which repeats the first\nrow. A failure with no field errors (a message-only 422, a 500, a\nnetwork failure) keeps the server's message as the heading, as it is\nthe only information."
|
|
2734
|
+
},
|
|
2735
|
+
{
|
|
2736
|
+
"name": "otherTitle",
|
|
2737
|
+
"type": "string",
|
|
2738
|
+
"required": false,
|
|
2739
|
+
"default": "\"Other problems\"",
|
|
2740
|
+
"description": "The sub-heading over the rows no visible field owns, listed after the\nowned rows. Shown only when there are both kinds; when no row is owned\nthey are listed under the heading alone."
|
|
2741
|
+
}
|
|
2742
|
+
],
|
|
2743
|
+
"events": [
|
|
2744
|
+
{
|
|
2745
|
+
"name": "select-target",
|
|
2746
|
+
"description": "A row was clicked: select `tabKey` (if any) and focus `fieldKey`.",
|
|
2747
|
+
"properties": []
|
|
2748
|
+
}
|
|
2749
|
+
],
|
|
2750
|
+
"slots": [],
|
|
2751
|
+
"methods": []
|
|
2752
|
+
},
|
|
2648
2753
|
{
|
|
2649
2754
|
"name": "DXFormField",
|
|
2650
2755
|
"category": "extended",
|
|
@@ -2699,6 +2804,10 @@
|
|
|
2699
2804
|
{
|
|
2700
2805
|
"name": "model",
|
|
2701
2806
|
"title": "binding"
|
|
2807
|
+
},
|
|
2808
|
+
{
|
|
2809
|
+
"name": "targetAttrs",
|
|
2810
|
+
"title": "binding"
|
|
2702
2811
|
}
|
|
2703
2812
|
]
|
|
2704
2813
|
},
|
|
@@ -2823,8 +2932,8 @@
|
|
|
2823
2932
|
"name": "delay",
|
|
2824
2933
|
"type": "number",
|
|
2825
2934
|
"required": false,
|
|
2826
|
-
"default": "
|
|
2827
|
-
"description": ""
|
|
2935
|
+
"default": "600",
|
|
2936
|
+
"description": "Milliseconds to wait before showing anything. Most loads finish\n sooner, and one that has already taken this long is likely to take\n longer, so a quick load never flashes a loader."
|
|
2828
2937
|
}
|
|
2829
2938
|
],
|
|
2830
2939
|
"events": [],
|
|
@@ -3688,6 +3797,18 @@
|
|
|
3688
3797
|
"required": false,
|
|
3689
3798
|
"description": "Override the modal's Delete button label (default: \"Delete {item}\")."
|
|
3690
3799
|
},
|
|
3800
|
+
{
|
|
3801
|
+
"name": "errorSummaryTitle",
|
|
3802
|
+
"type": "string",
|
|
3803
|
+
"required": false,
|
|
3804
|
+
"description": "Heading of the modal's failed-save summary when the failure has field\nerrors (default \"Couldn't save. Please check:\"). A failure with none\nis headed by the server's message instead."
|
|
3805
|
+
},
|
|
3806
|
+
{
|
|
3807
|
+
"name": "errorSummaryOtherTitle",
|
|
3808
|
+
"type": "string",
|
|
3809
|
+
"required": false,
|
|
3810
|
+
"description": "Sub-heading over the summary rows no edit field owns (default \"Other\nproblems\"), shown only when some rows ARE owned."
|
|
3811
|
+
},
|
|
3691
3812
|
{
|
|
3692
3813
|
"name": "editUrl",
|
|
3693
3814
|
"type": "string",
|
|
@@ -3769,6 +3890,13 @@
|
|
|
3769
3890
|
"required": false,
|
|
3770
3891
|
"description": "Whether a given row is actionable. Only relevant when rows are clickable\n(an `editFields` modal or a `row-clicked` listener). A row this returns\n`false` for gets no pointer cursor, no hover highlight, and does not fire\n`row-clicked` or open the edit modal — so a row that isn't clickable\ndoesn't *look* clickable, and a click on it can't quietly do something."
|
|
3771
3892
|
},
|
|
3893
|
+
{
|
|
3894
|
+
"name": "editOnRowClick",
|
|
3895
|
+
"type": "boolean",
|
|
3896
|
+
"required": false,
|
|
3897
|
+
"default": "true",
|
|
3898
|
+
"description": "Whether clicking a row opens the edit modal (when `editFields` are set).\nDefaults to `true`. Set `false` when `editFields` exist only for the\ncreate modal (`openCreate()` / the create button) and a row click does\nsomething else, such as opening the record's own page from `row-clicked`:\nthe row then emits `row-clicked` without also opening the edit modal, and\nonly a `row-clicked` listener makes rows look clickable (#200)."
|
|
3899
|
+
},
|
|
3772
3900
|
{
|
|
3773
3901
|
"name": "clientSide",
|
|
3774
3902
|
"type": "boolean",
|
|
@@ -4077,6 +4205,18 @@
|
|
|
4077
4205
|
"type": "string",
|
|
4078
4206
|
"required": false,
|
|
4079
4207
|
"description": "Override the Delete button label."
|
|
4208
|
+
},
|
|
4209
|
+
{
|
|
4210
|
+
"name": "errorSummaryTitle",
|
|
4211
|
+
"type": "string",
|
|
4212
|
+
"required": false,
|
|
4213
|
+
"description": "The footer summary's heading for a failure with field errors."
|
|
4214
|
+
},
|
|
4215
|
+
{
|
|
4216
|
+
"name": "errorSummaryOtherTitle",
|
|
4217
|
+
"type": "string",
|
|
4218
|
+
"required": false,
|
|
4219
|
+
"description": "The footer summary's sub-heading over rows no field owns."
|
|
4080
4220
|
}
|
|
4081
4221
|
],
|
|
4082
4222
|
"events": [
|
|
@@ -4439,8 +4579,8 @@
|
|
|
4439
4579
|
]
|
|
4440
4580
|
},
|
|
4441
4581
|
"stats": {
|
|
4442
|
-
"totalComponents":
|
|
4582
|
+
"totalComponents": 90,
|
|
4443
4583
|
"baseComponents": 60,
|
|
4444
|
-
"extendedComponents":
|
|
4584
|
+
"extendedComponents": 30
|
|
4445
4585
|
}
|
|
4446
4586
|
}
|
package/docs/public/docs-map.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Documentation Map
|
|
2
2
|
|
|
3
3
|
> Auto-generated hierarchical overview of all documentation
|
|
4
|
-
> Last updated: 2026-10-
|
|
4
|
+
> Last updated: 2026-10-11T09:08:08.837Z
|
|
5
5
|
|
|
6
6
|
This file provides a complete map of all available documentation for AI agents and developers.
|
|
7
7
|
|
|
@@ -79,6 +79,7 @@ This file provides a complete map of all available documentation for AI agents a
|
|
|
79
79
|
- [DXDashboardSidebar](/components/extended/DXDashboardSidebar): A collapsible sidebar navigation component for Laravel dashboards with support for navigation groups
|
|
80
80
|
- [DXField](/components/extended/DXField): The single-field renderer that powers `DXForm` and
|
|
81
81
|
- [DXForm](/components/extended/DXForm): The canonical form renderer. Drive it with field definitions; add `tabs` when
|
|
82
|
+
- [DXFormErrorSummary](/components/extended/DXFormErrorSummary): Lists everything the last failed submit of a `useForm` returned: a heading,
|
|
82
83
|
- [DXNumberStepper](/components/extended/DXNumberStepper): A number input between a decrease and an increase button, `[ − ][ 12 ][ + ]`,
|
|
83
84
|
- [DXRepeater](/components/extended/DXRepeater): A repeatable sub-form (field array) primitive for nested, dynamic collections —
|
|
84
85
|
- [DXSaveButton](/components/extended/DXSaveButton): A save button that confirms the save in place. While saving it shows
|
|
@@ -105,4 +106,4 @@ This file provides a complete map of all available documentation for AI agents a
|
|
|
105
106
|
|
|
106
107
|
---
|
|
107
108
|
|
|
108
|
-
**Total Pages**:
|
|
109
|
+
**Total Pages**: 82
|
package/docs/public/llms.txt
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> Vue 3 dashboard components for Laravel with Bootstrap Vue Next
|
|
4
4
|
>
|
|
5
|
-
> A dual-package library (NPM + Composer) providing
|
|
5
|
+
> A dual-package library (NPM + Composer) providing 80 Vue 3 components for building Laravel dashboards with Bootstrap Vue Next.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -84,7 +84,7 @@ Lightweight type-safe wrappers around Bootstrap Vue Next components providing AP
|
|
|
84
84
|
- [DToaster](/components/base/dtoaster): Bootstrap Vue Next Toaster wrapper
|
|
85
85
|
- [DTooltip](/components/base/dtooltip): Bootstrap Vue Next Tooltip wrapper
|
|
86
86
|
|
|
87
|
-
## Extended Components (
|
|
87
|
+
## Extended Components (20 components)
|
|
88
88
|
|
|
89
89
|
Custom dashboard components with advanced functionality beyond Bootstrap Vue Next.
|
|
90
90
|
|
|
@@ -96,6 +96,7 @@ Custom dashboard components with advanced functionality beyond Bootstrap Vue Nex
|
|
|
96
96
|
- [DXField](/components/extended/dxfield): Single-field renderer for any field type with value/span/info/hint slots
|
|
97
97
|
- [DXFieldLabel](/components/extended/dxfieldlabel): Extended dashboard component
|
|
98
98
|
- [DXForm](/components/extended/dxform): Form renderer driven by field definitions, with optional tabs, conditional fields, per-field slots, async options, nested repeaters, and auto error-tab switching
|
|
99
|
+
- [DXFormErrorSummary](/components/extended/dxformerrorsummary): One alert listing every message of the last failed submit, with readable labels, placed next to the submit control
|
|
99
100
|
- [DXLoading](/components/extended/dxloading): Extended dashboard component
|
|
100
101
|
- [DXModalActions](/components/extended/dxmodalactions): Extended dashboard component
|
|
101
102
|
- [DXNumberStepper](/components/extended/dxnumberstepper): Extended dashboard component
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omnitend/dashboard-for-laravel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.45.0",
|
|
4
4
|
"description": "Vue 3 dashboard components for Laravel with Bootstrap Vue Next",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/dashboard-for-laravel.umd.cjs",
|
|
@@ -55,12 +55,13 @@
|
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"@inertiajs/vue3": "^2.0.0",
|
|
58
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
58
59
|
"chart.js": "^4.4.0",
|
|
59
60
|
"vue": "^3.5.13",
|
|
60
61
|
"vue-chartjs": "^5.3.0"
|
|
61
62
|
},
|
|
62
63
|
"peerDependenciesMeta": {
|
|
63
|
-
"
|
|
64
|
+
"@modelcontextprotocol/sdk": {
|
|
64
65
|
"optional": true
|
|
65
66
|
},
|
|
66
67
|
"chart.js": {
|
|
@@ -71,13 +72,8 @@
|
|
|
71
72
|
}
|
|
72
73
|
},
|
|
73
74
|
"dependencies": {
|
|
74
|
-
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
75
75
|
"bootstrap-vue-next": "^0.45.6",
|
|
76
|
-
"
|
|
77
|
-
"pluralize": "^8.0.0",
|
|
78
|
-
"rehype-autolink-headings": "^7.1.0",
|
|
79
|
-
"rehype-slug": "^6.0.0",
|
|
80
|
-
"vue-docgen-api": "^4.79.2"
|
|
76
|
+
"pluralize": "^8.0.0"
|
|
81
77
|
},
|
|
82
78
|
"devDependencies": {
|
|
83
79
|
"@astrojs/markdown-remark": "^7.2.0",
|
|
@@ -87,6 +83,7 @@
|
|
|
87
83
|
"@iconify-json/lucide": "^1.2.71",
|
|
88
84
|
"@iconify/json": "^2.2.397",
|
|
89
85
|
"@inertiajs/vue3": "^2.2.15",
|
|
86
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
90
87
|
"@types/pluralize": "^0.0.33",
|
|
91
88
|
"@vitejs/plugin-vue": "^5.2.4",
|
|
92
89
|
"@vitest/browser": "^4.1.8",
|
|
@@ -97,8 +94,11 @@
|
|
|
97
94
|
"bootstrap-icons": "^1.13.1",
|
|
98
95
|
"chart.js": "^4.5.1",
|
|
99
96
|
"happy-dom": "^20.0.8",
|
|
97
|
+
"highlight.js": "^11.11.1",
|
|
100
98
|
"pagefind": "^1.4.0",
|
|
101
99
|
"playwright": "^1.56.1",
|
|
100
|
+
"rehype-autolink-headings": "^7.1.0",
|
|
101
|
+
"rehype-slug": "^6.0.0",
|
|
102
102
|
"sass-embedded": "^1.93.2",
|
|
103
103
|
"typescript": "^5.9.3",
|
|
104
104
|
"unplugin-icons": "^22.5.0",
|
|
@@ -107,6 +107,7 @@
|
|
|
107
107
|
"vitest": "^4.1.8",
|
|
108
108
|
"vitest-browser-vue": "^2.0.0",
|
|
109
109
|
"vue-chartjs": "^5.3.3",
|
|
110
|
+
"vue-docgen-api": "^4.79.2",
|
|
110
111
|
"vue-tsc": "^2.2.12"
|
|
111
112
|
},
|
|
112
113
|
"keywords": [
|
package/resources/css/theme.scss
CHANGED
|
@@ -654,6 +654,62 @@ $dx-display-only-row: '.dx-form--horizontal .row:has(> .col-form-label):not(:has
|
|
|
654
654
|
}
|
|
655
655
|
}
|
|
656
656
|
|
|
657
|
+
// DXFormErrorSummary (#194): a heading (the title, or the server's message
|
|
658
|
+
// when there are no field errors), then one row per validation message. The alert keeps the danger alert's subtle tint; a row
|
|
659
|
+
// that points at a field is a button drawn as underlined text in the alert's
|
|
660
|
+
// own colour (an alert-coloured link, not the brand navy), so the list reads
|
|
661
|
+
// as text and still says "you can click this". Global, not scoped: the rows
|
|
662
|
+
// are rendered inside DAlert's slot.
|
|
663
|
+
.dx-form-error-summary {
|
|
664
|
+
.dx-form-error-summary__message {
|
|
665
|
+
margin-bottom: 0;
|
|
666
|
+
font-weight: $font-weight-semibold;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
.dx-form-error-summary__list {
|
|
670
|
+
margin: map.get($spacers, 2) 0 0;
|
|
671
|
+
padding-inline-start: 1.25rem;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// "Other problems": the rows no field owns, after the owned ones. One
|
|
675
|
+
// step lighter than the heading (semibold), still above the body text,
|
|
676
|
+
// and tied to the list it introduces rather than the one above it.
|
|
677
|
+
.dx-form-error-summary__other-title {
|
|
678
|
+
margin: map.get($spacers, 2) 0 0;
|
|
679
|
+
font-weight: $font-weight-medium;
|
|
680
|
+
|
|
681
|
+
+ .dx-form-error-summary__list {
|
|
682
|
+
margin-top: map.get($spacers, 1);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
.dx-form-error-summary__target {
|
|
687
|
+
padding: 0;
|
|
688
|
+
border: 0;
|
|
689
|
+
background: none;
|
|
690
|
+
color: inherit;
|
|
691
|
+
font: inherit;
|
|
692
|
+
text-align: start;
|
|
693
|
+
text-decoration: underline;
|
|
694
|
+
text-underline-offset: 0.15em;
|
|
695
|
+
cursor: pointer;
|
|
696
|
+
// A button is an inline-block, and the list bullet sits on the line
|
|
697
|
+
// box's baseline, which a baseline-aligned inline-block takes from its
|
|
698
|
+
// LAST line: a row that wraps had its bullet beside its second line.
|
|
699
|
+
vertical-align: top;
|
|
700
|
+
|
|
701
|
+
&:hover {
|
|
702
|
+
text-decoration-thickness: 2px;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
&:focus-visible {
|
|
706
|
+
outline: var(--bs-focus-ring-width) solid currentColor;
|
|
707
|
+
outline-offset: 2px;
|
|
708
|
+
border-radius: var(--bs-border-radius-sm);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
657
713
|
// Switch-list option rows (DXField `switch-list`) are one line each: a label,
|
|
658
714
|
// a switch and an optional trailing control (a notes input in the
|
|
659
715
|
// `switch-list-item` slot). Centre all three in the row, instead of the
|
|
@@ -1936,3 +1992,123 @@ $dx-chart-edge-dark: $body-bg-dark;
|
|
|
1936
1992
|
thead tr:has(.dx-table-filter-cell) > td {
|
|
1937
1993
|
padding: 0.5rem;
|
|
1938
1994
|
}
|
|
1995
|
+
|
|
1996
|
+
// ————————————————— DXLoading (the soft loader)
|
|
1997
|
+
//
|
|
1998
|
+
// Modelled on Omni Tend's legacy loader: a small grey spinner beside the
|
|
1999
|
+
// text on one line, the text in the display face with a slow light sweep
|
|
2000
|
+
// across it, fading in after DXLoading's delay. Quieter than a navy spinner,
|
|
2001
|
+
// but the text keeps AA contrast (#6c757d, 4.69:1 on white; the legacy
|
|
2002
|
+
// gray-300 was 1.5:1). The sweep passes through a lighter grey, so a moment
|
|
2003
|
+
// of the text sits below AA while it moves. Global, not scoped: DXTable and
|
|
2004
|
+
// consumers render it, and its spinner is a bvn component.
|
|
2005
|
+
//
|
|
2006
|
+
// The `role="status"` box renders at once with its contents hidden, and
|
|
2007
|
+
// `.dx-loading--shown` reveals them after the delay. Two reasons: a live
|
|
2008
|
+
// region that is already in the page announces content added to it (one
|
|
2009
|
+
// inserted together with its text can go unannounced), and the space the
|
|
2010
|
+
// loader will take is held from the start, so nothing jumps when it appears.
|
|
2011
|
+
@mixin dx-loading-light-vars {
|
|
2012
|
+
--dx-loading-color: #{$gray-600};
|
|
2013
|
+
--dx-loading-sweep-color: #{$gray-500};
|
|
2014
|
+
--dx-loading-spinner-color: #{$gray-500};
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
// `[data-bs-theme="light"]` too, so a light section inside a dark page gets
|
|
2018
|
+
// the light set back (as the chart tokens do).
|
|
2019
|
+
:root,
|
|
2020
|
+
[data-bs-theme="light"] {
|
|
2021
|
+
@include dx-loading-light-vars;
|
|
2022
|
+
// Slower than Bootstrap's 0.75 s a turn: a calmer spin to match the soft
|
|
2023
|
+
// look (1.1 s chosen against 1.5 s and 2 s side by side).
|
|
2024
|
+
--dx-loading-spinner-speed: 1.1s;
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
[data-bs-theme="dark"] {
|
|
2028
|
+
--dx-loading-color: #{$gray-500};
|
|
2029
|
+
--dx-loading-sweep-color: #{$gray-600};
|
|
2030
|
+
--dx-loading-spinner-color: #{$gray-600};
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
.dx-loading {
|
|
2034
|
+
display: flex;
|
|
2035
|
+
justify-content: center;
|
|
2036
|
+
align-items: center;
|
|
2037
|
+
gap: 1rem;
|
|
2038
|
+
color: var(--dx-loading-color);
|
|
2039
|
+
|
|
2040
|
+
// Hidden, not removed: `visibility` keeps the box's size and keeps the
|
|
2041
|
+
// contents out of the accessibility tree until they are revealed.
|
|
2042
|
+
> * {
|
|
2043
|
+
visibility: hidden;
|
|
2044
|
+
}
|
|
2045
|
+
|
|
2046
|
+
// Set on the spinner itself: Bootstrap declares its speed on
|
|
2047
|
+
// `.spinner-border`, so a value inherited from `.dx-loading` would lose.
|
|
2048
|
+
.spinner-border {
|
|
2049
|
+
flex: none;
|
|
2050
|
+
color: var(--dx-loading-spinner-color);
|
|
2051
|
+
--bs-spinner-animation-speed: var(--dx-loading-spinner-speed);
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
.dx-loading--shown {
|
|
2056
|
+
animation: dx-loading-fade-in 0.4s ease-out both;
|
|
2057
|
+
|
|
2058
|
+
> * {
|
|
2059
|
+
visibility: visible;
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2063
|
+
.dx-loading__text {
|
|
2064
|
+
margin: 0;
|
|
2065
|
+
font-family: var(--dx-font-family-display);
|
|
2066
|
+
font-weight: 500;
|
|
2067
|
+
font-size: $h2-font-size;
|
|
2068
|
+
line-height: $headings-line-height;
|
|
2069
|
+
background-image: linear-gradient(
|
|
2070
|
+
to right,
|
|
2071
|
+
var(--dx-loading-color) 20%,
|
|
2072
|
+
var(--dx-loading-color) 40%,
|
|
2073
|
+
var(--dx-loading-sweep-color) 60%,
|
|
2074
|
+
var(--dx-loading-sweep-color) 80%,
|
|
2075
|
+
var(--dx-loading-color) 99%
|
|
2076
|
+
);
|
|
2077
|
+
background-size: 200% auto;
|
|
2078
|
+
-webkit-background-clip: text;
|
|
2079
|
+
background-clip: text;
|
|
2080
|
+
-webkit-text-fill-color: transparent;
|
|
2081
|
+
animation: dx-loading-sweep 3s linear infinite;
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
@keyframes dx-loading-fade-in {
|
|
2085
|
+
from {
|
|
2086
|
+
opacity: 0;
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
@keyframes dx-loading-sweep {
|
|
2091
|
+
to {
|
|
2092
|
+
background-position: 200% center;
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
|
|
2096
|
+
// For people who asked for less motion: no sweep and no fade (the text is
|
|
2097
|
+
// plain grey and appears at once), and the spinner slows to double its turn,
|
|
2098
|
+
// as Bootstrap's own reduced-motion rule does for its spinners. That rule
|
|
2099
|
+
// cannot reach this one, because the speed above out-ranks it.
|
|
2100
|
+
@media (prefers-reduced-motion: reduce) {
|
|
2101
|
+
.dx-loading--shown,
|
|
2102
|
+
.dx-loading__text {
|
|
2103
|
+
animation: none;
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
.dx-loading__text {
|
|
2107
|
+
background-image: none;
|
|
2108
|
+
-webkit-text-fill-color: currentColor;
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
.dx-loading .spinner-border {
|
|
2112
|
+
--bs-spinner-animation-speed: calc(2 * var(--dx-loading-spinner-speed));
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
@@ -18,8 +18,11 @@ import { BFormSelect } from "bootstrap-vue-next";
|
|
|
18
18
|
import { computed, useSlots } from "vue";
|
|
19
19
|
|
|
20
20
|
// Mirrors the shapes BVN's BFormSelect accepts per option (primitive, object, or
|
|
21
|
-
// group)
|
|
22
|
-
|
|
21
|
+
// group): bvn types them `object | string | number | boolean`. `object`, not
|
|
22
|
+
// `Record<string, unknown>`, so an option INTERFACE such as DXTable's
|
|
23
|
+
// `FilterOption` (no index signature) fits without a cast (#171). Only object
|
|
24
|
+
// options can carry a `null` value.
|
|
25
|
+
export type OptionItem = string | number | boolean | object;
|
|
23
26
|
|
|
24
27
|
export interface Props<TValue = unknown> {
|
|
25
28
|
modelValue?: TValue | TValue[] | null;
|