@omnitend/dashboard-for-laravel 0.9.2 → 0.11.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/extended/DXDashboard.vue.d.ts +12 -2
- package/dist/components/extended/DXDashboardNavbar.vue.d.ts +12 -10
- package/dist/components/extended/DXDashboardSidebar.vue.d.ts +9 -0
- package/dist/components/extended/DXTable.vue.d.ts +15 -0
- package/dist/dashboard-for-laravel.js +5916 -5880
- package/dist/dashboard-for-laravel.js.map +1 -1
- package/dist/dashboard-for-laravel.umd.cjs +7 -7
- package/dist/dashboard-for-laravel.umd.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/dist/types/navigation.d.ts +7 -0
- package/docs/public/api-reference.json +364 -140
- package/docs/public/docs-map.md +1 -1
- package/package.json +1 -1
- package/resources/js/components/extended/DXBasicForm.vue +4 -1
- package/resources/js/components/extended/DXDashboard.vue +28 -2
- package/resources/js/components/extended/DXDashboardNavbar.vue +77 -13
- package/resources/js/components/extended/DXDashboardSidebar.vue +138 -43
- package/resources/js/components/extended/DXField.vue +73 -0
- package/resources/js/components/extended/DXFieldLabel.vue +5 -0
- package/resources/js/components/extended/DXForm.vue +22 -1
- package/resources/js/components/extended/DXRepeater.vue +14 -1
- package/resources/js/components/extended/DXTable.vue +102 -0
- package/resources/js/types/navigation.ts +7 -0
|
@@ -1,6 +1,17 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
Renders a single `FieldDefinition` of any type (text, select, checkbox, switch, repeater, currency, percentage, file/image, component, etc.) with its label, validation error, hint and help. Used by `DXForm` and `DXTable`'s edit modal; exposes `value`, `span`, `info`, `hint` and `repeater-row` slots for per-field customisation.
|
|
4
|
+
-->
|
|
1
5
|
<template>
|
|
2
6
|
<!-- Full-width span field: delegate entirely to the #span slot -->
|
|
3
7
|
<div v-if="field.span" :class="field.class || 'mb-3'">
|
|
8
|
+
<!--
|
|
9
|
+
@slot Replaces the entire field with custom full-width content when `field.span` is set, bypassing the label and built-in control.
|
|
10
|
+
@binding {FieldDefinition} field The field definition being rendered.
|
|
11
|
+
@binding {any} model The model passed to field predicates (defaults to the live form data).
|
|
12
|
+
@binding {any} value The current field value.
|
|
13
|
+
@binding {(value: any) => void} update Setter that writes the value back and clears the field's validation error.
|
|
14
|
+
-->
|
|
4
15
|
<slot
|
|
5
16
|
name="span"
|
|
6
17
|
:field="field"
|
|
@@ -12,6 +23,13 @@
|
|
|
12
23
|
|
|
13
24
|
<!-- Checkbox: no label wrapper, label sits beside the control -->
|
|
14
25
|
<div v-else-if="field.type === 'checkbox'" :class="field.class || 'mb-3'">
|
|
26
|
+
<!--
|
|
27
|
+
@slot Replaces the built-in control with a custom value editor.
|
|
28
|
+
@binding {FieldDefinition} field The field definition being rendered.
|
|
29
|
+
@binding {any} model The model passed to field predicates (defaults to the live form data).
|
|
30
|
+
@binding {any} value The current field value.
|
|
31
|
+
@binding {(value: any) => void} update Setter that writes the value back and clears the field's validation error.
|
|
32
|
+
-->
|
|
15
33
|
<slot
|
|
16
34
|
v-if="$slots.value"
|
|
17
35
|
name="value"
|
|
@@ -32,8 +50,18 @@
|
|
|
32
50
|
<DFormInvalidFeedback v-if="form.hasError(errorKey)" force-show>
|
|
33
51
|
{{ form.getError(errorKey) }}
|
|
34
52
|
</DFormInvalidFeedback>
|
|
53
|
+
<!--
|
|
54
|
+
@slot Rich info block rendered below the control.
|
|
55
|
+
@binding {FieldDefinition} field The field definition being rendered.
|
|
56
|
+
@binding {any} model The model passed to field predicates (defaults to the live form data).
|
|
57
|
+
-->
|
|
35
58
|
<slot name="info" :field="field" :model="model" />
|
|
36
59
|
<DFormText v-if="resolvedHint || $slots.hint" class="text-muted">
|
|
60
|
+
<!--
|
|
61
|
+
@slot Overrides the field's hint text shown below the control.
|
|
62
|
+
@binding {FieldDefinition} field The field definition being rendered.
|
|
63
|
+
@binding {any} model The model passed to field predicates (defaults to the live form data).
|
|
64
|
+
-->
|
|
37
65
|
<slot name="hint" :field="field" :model="model">{{ resolvedHint }}</slot>
|
|
38
66
|
</DFormText>
|
|
39
67
|
<DFormText v-if="field.help">{{ field.help }}</DFormText>
|
|
@@ -44,6 +72,13 @@
|
|
|
44
72
|
v-else-if="field.type === 'switch'"
|
|
45
73
|
:class="[field.class || 'mb-3', 'dx-switch', { 'dx-switch--on': switchIsOn }]"
|
|
46
74
|
>
|
|
75
|
+
<!--
|
|
76
|
+
@slot Replaces the built-in control with a custom value editor.
|
|
77
|
+
@binding {FieldDefinition} field The field definition being rendered.
|
|
78
|
+
@binding {any} model The model passed to field predicates (defaults to the live form data).
|
|
79
|
+
@binding {any} value The current field value.
|
|
80
|
+
@binding {(value: any) => void} update Setter that writes the value back and clears the field's validation error.
|
|
81
|
+
-->
|
|
47
82
|
<slot
|
|
48
83
|
v-if="$slots.value"
|
|
49
84
|
name="value"
|
|
@@ -65,8 +100,18 @@
|
|
|
65
100
|
<DFormInvalidFeedback v-if="form.hasError(errorKey)" force-show>
|
|
66
101
|
{{ form.getError(errorKey) }}
|
|
67
102
|
</DFormInvalidFeedback>
|
|
103
|
+
<!--
|
|
104
|
+
@slot Rich info block rendered below the control.
|
|
105
|
+
@binding {FieldDefinition} field The field definition being rendered.
|
|
106
|
+
@binding {any} model The model passed to field predicates (defaults to the live form data).
|
|
107
|
+
-->
|
|
68
108
|
<slot name="info" :field="field" :model="model" />
|
|
69
109
|
<DFormText v-if="resolvedHint || $slots.hint" class="text-muted">
|
|
110
|
+
<!--
|
|
111
|
+
@slot Overrides the field's hint text shown below the control.
|
|
112
|
+
@binding {FieldDefinition} field The field definition being rendered.
|
|
113
|
+
@binding {any} model The model passed to field predicates (defaults to the live form data).
|
|
114
|
+
-->
|
|
70
115
|
<slot name="hint" :field="field" :model="model">{{ resolvedHint }}</slot>
|
|
71
116
|
</DFormText>
|
|
72
117
|
<DFormText v-if="field.help">{{ field.help }}</DFormText>
|
|
@@ -86,12 +131,23 @@
|
|
|
86
131
|
>
|
|
87
132
|
<!-- Forward repeater row slot for custom row layouts -->
|
|
88
133
|
<template v-if="$slots['repeater-row']" #row="rowProps">
|
|
134
|
+
<!-- @slot Custom layout for a single repeater row, forwarded to `DXRepeater`'s `row` slot with its row props (row index, item, remove handler, etc.). -->
|
|
89
135
|
<slot name="repeater-row" v-bind="rowProps" />
|
|
90
136
|
</template>
|
|
91
137
|
</DXRepeater>
|
|
92
138
|
</DFormGroup>
|
|
139
|
+
<!--
|
|
140
|
+
@slot Rich info block rendered below the control.
|
|
141
|
+
@binding {FieldDefinition} field The field definition being rendered.
|
|
142
|
+
@binding {any} model The model passed to field predicates (defaults to the live form data).
|
|
143
|
+
-->
|
|
93
144
|
<slot name="info" :field="field" :model="model" />
|
|
94
145
|
<DFormText v-if="resolvedHint || $slots.hint" class="text-muted">
|
|
146
|
+
<!--
|
|
147
|
+
@slot Overrides the field's hint text shown below the control.
|
|
148
|
+
@binding {FieldDefinition} field The field definition being rendered.
|
|
149
|
+
@binding {any} model The model passed to field predicates (defaults to the live form data).
|
|
150
|
+
-->
|
|
95
151
|
<slot name="hint" :field="field" :model="model">{{ resolvedHint }}</slot>
|
|
96
152
|
</DFormText>
|
|
97
153
|
<DFormText v-if="field.help">{{ field.help }}</DFormText>
|
|
@@ -105,6 +161,13 @@
|
|
|
105
161
|
</template>
|
|
106
162
|
|
|
107
163
|
<!-- Custom value slot overrides the built-in control -->
|
|
164
|
+
<!--
|
|
165
|
+
@slot Replaces the built-in control with a custom value editor.
|
|
166
|
+
@binding {FieldDefinition} field The field definition being rendered.
|
|
167
|
+
@binding {any} model The model passed to field predicates (defaults to the live form data).
|
|
168
|
+
@binding {any} value The current field value.
|
|
169
|
+
@binding {(value: any) => void} update Setter that writes the value back and clears the field's validation error.
|
|
170
|
+
-->
|
|
108
171
|
<slot
|
|
109
172
|
v-if="$slots.value"
|
|
110
173
|
name="value"
|
|
@@ -224,10 +287,20 @@
|
|
|
224
287
|
</DFormInvalidFeedback>
|
|
225
288
|
|
|
226
289
|
<!-- Optional rich info block -->
|
|
290
|
+
<!--
|
|
291
|
+
@slot Rich info block rendered below the control.
|
|
292
|
+
@binding {FieldDefinition} field The field definition being rendered.
|
|
293
|
+
@binding {any} model The model passed to field predicates (defaults to the live form data).
|
|
294
|
+
-->
|
|
227
295
|
<slot name="info" :field="field" :model="model" />
|
|
228
296
|
|
|
229
297
|
<!-- Hint (dynamic, slot-overridable) -->
|
|
230
298
|
<DFormText v-if="resolvedHint || $slots.hint" class="text-muted">
|
|
299
|
+
<!--
|
|
300
|
+
@slot Overrides the field's hint text shown below the control.
|
|
301
|
+
@binding {FieldDefinition} field The field definition being rendered.
|
|
302
|
+
@binding {any} model The model passed to field predicates (defaults to the live form data).
|
|
303
|
+
-->
|
|
231
304
|
<slot name="hint" :field="field" :model="model">{{ resolvedHint }}</slot>
|
|
232
305
|
</DFormText>
|
|
233
306
|
|
|
@@ -33,7 +33,11 @@
|
|
|
33
33
|
:lazy="tab.lazy"
|
|
34
34
|
:active="index === 0"
|
|
35
35
|
>
|
|
36
|
-
<!--
|
|
36
|
+
<!--
|
|
37
|
+
@slot Replaces the entire body of a tab, keyed by tab (slot name `tab-content(<tabKey>)`).
|
|
38
|
+
@binding {FormTab} tab The tab definition being rendered.
|
|
39
|
+
@binding {object} model Live form data merged with `context`, for predicates.
|
|
40
|
+
-->
|
|
37
41
|
<slot
|
|
38
42
|
v-if="$slots[`tab-content(${tab.key})`]"
|
|
39
43
|
:name="`tab-content(${tab.key})`"
|
|
@@ -42,6 +46,11 @@
|
|
|
42
46
|
/>
|
|
43
47
|
|
|
44
48
|
<div v-else class="pt-3">
|
|
49
|
+
<!--
|
|
50
|
+
@slot Content inserted above a tab's fields, keyed by tab (slot name `tab-before(<tabKey>)`).
|
|
51
|
+
@binding {FormTab} tab The tab definition being rendered.
|
|
52
|
+
@binding {object} model Live form data merged with `context`, for predicates.
|
|
53
|
+
-->
|
|
45
54
|
<slot :name="`tab-before(${tab.key})`" :tab="tab" :model="model" />
|
|
46
55
|
|
|
47
56
|
<DXField
|
|
@@ -56,10 +65,16 @@
|
|
|
56
65
|
:key="target"
|
|
57
66
|
#[target]="slotProps"
|
|
58
67
|
>
|
|
68
|
+
<!-- @slot Per-field overrides forwarded to DXField, keyed by field key: `value(<key>)`, `span(<key>)`, `info(<key>)`, `hint(<key>)`, `repeater-row(<key>)`. -->
|
|
59
69
|
<slot :name="slotName" v-bind="slotProps" />
|
|
60
70
|
</template>
|
|
61
71
|
</DXField>
|
|
62
72
|
|
|
73
|
+
<!--
|
|
74
|
+
@slot Content inserted below a tab's fields, keyed by tab (slot name `tab-after(<tabKey>)`).
|
|
75
|
+
@binding {FormTab} tab The tab definition being rendered.
|
|
76
|
+
@binding {object} model Live form data merged with `context`, for predicates.
|
|
77
|
+
-->
|
|
63
78
|
<slot :name="`tab-after(${tab.key})`" :tab="tab" :model="model" />
|
|
64
79
|
</div>
|
|
65
80
|
</DTab>
|
|
@@ -79,6 +94,7 @@
|
|
|
79
94
|
:key="target"
|
|
80
95
|
#[target]="slotProps"
|
|
81
96
|
>
|
|
97
|
+
<!-- @slot Per-field overrides forwarded to DXField, keyed by field key: `value(<key>)`, `span(<key>)`, `info(<key>)`, `hint(<key>)`, `repeater-row(<key>)`. -->
|
|
82
98
|
<slot :name="slotName" v-bind="slotProps" />
|
|
83
99
|
</template>
|
|
84
100
|
</DXField>
|
|
@@ -96,6 +112,10 @@
|
|
|
96
112
|
<span v-else>{{ submitText }}</span>
|
|
97
113
|
</DButton>
|
|
98
114
|
|
|
115
|
+
<!--
|
|
116
|
+
@slot Content rendered below the submit button (e.g. a cancel link or secondary actions).
|
|
117
|
+
@binding {UseFormReturn} form The resolved form instance (state, errors, submit helpers).
|
|
118
|
+
-->
|
|
99
119
|
<slot name="footer" :form="resolvedForm" />
|
|
100
120
|
</BForm>
|
|
101
121
|
</template>
|
|
@@ -154,6 +174,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
154
174
|
});
|
|
155
175
|
|
|
156
176
|
const emit = defineEmits<{
|
|
177
|
+
/** Emitted when the form is submitted, after the native submit is prevented. */
|
|
157
178
|
submit: [];
|
|
158
179
|
}>();
|
|
159
180
|
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
DXRepeater renders a nested, repeatable sub-form for a repeater field: a list
|
|
4
|
+
of rows (each a group of sub-fields) with add/remove controls, honouring the
|
|
5
|
+
field's `minItems`/`maxItems` limits.
|
|
6
|
+
-->
|
|
1
7
|
<template>
|
|
2
8
|
<div class="dx-repeater">
|
|
3
9
|
<div
|
|
@@ -5,7 +11,14 @@
|
|
|
5
11
|
:key="rowKey(index)"
|
|
6
12
|
class="dx-repeater-row"
|
|
7
13
|
>
|
|
8
|
-
<!--
|
|
14
|
+
<!--
|
|
15
|
+
@slot Custom layout for a single repeater row, replacing the default sub-field stack.
|
|
16
|
+
@binding {object} row The current row's data object.
|
|
17
|
+
@binding {number} index The row's zero-based index.
|
|
18
|
+
@binding {FieldDefinition[]} fields The sub-field definitions for the row.
|
|
19
|
+
@binding {function} remove Removes this row (respects `minItems`).
|
|
20
|
+
@binding {string} path The dot path into `form.data` for this row (e.g. `lines.0`).
|
|
21
|
+
-->
|
|
9
22
|
<slot
|
|
10
23
|
v-if="$slots.row"
|
|
11
24
|
name="row"
|
|
@@ -1,9 +1,17 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
Full-featured dashboard data table. Wraps `DTable` and adds three data modes
|
|
4
|
+
(Inertia items, client-side, and API/provider), inline per-column filters,
|
|
5
|
+
single-column sorting, pagination with a per-page selector, and an optional
|
|
6
|
+
edit/create/delete modal driven by `DXForm`.
|
|
7
|
+
-->
|
|
1
8
|
<template>
|
|
2
9
|
<DContainer :fluid="fluid" :class="containerClass">
|
|
3
10
|
<DRow class="justify-content-center">
|
|
4
11
|
<DCol :md="columnSize">
|
|
5
12
|
<DCard>
|
|
6
13
|
<template v-if="title || createUrl || $slots.header" #header>
|
|
14
|
+
<!-- @slot Card header content; overrides the default title heading and the "New {item}" button. -->
|
|
7
15
|
<slot name="header">
|
|
8
16
|
<div class="d-flex justify-content-between align-items-center">
|
|
9
17
|
<h4 class="mb-0">{{ title }}</h4>
|
|
@@ -114,6 +122,13 @@
|
|
|
114
122
|
v-for="(_, name) in $slots"
|
|
115
123
|
#[name]="slotProps"
|
|
116
124
|
>
|
|
125
|
+
<!--
|
|
126
|
+
@slot Custom rendering for a column's cell. Name it `cell(<fieldKey>)` to target a field; receives the underlying table cell scope.
|
|
127
|
+
@binding {object} item The row data object for this cell.
|
|
128
|
+
@binding {any} value The cell value for the field.
|
|
129
|
+
@binding {number} index The zero-based row index.
|
|
130
|
+
@binding {object} field The field definition for the column.
|
|
131
|
+
-->
|
|
117
132
|
<slot
|
|
118
133
|
v-if="typeof name === 'string' && name.startsWith('cell')"
|
|
119
134
|
:name="name"
|
|
@@ -203,6 +218,13 @@
|
|
|
203
218
|
v-for="(_, name) in $slots"
|
|
204
219
|
#[name]="slotProps"
|
|
205
220
|
>
|
|
221
|
+
<!--
|
|
222
|
+
@slot Custom rendering for a column's cell. Name it `cell(<fieldKey>)` to target a field; receives the underlying table cell scope.
|
|
223
|
+
@binding {object} item The row data object for this cell.
|
|
224
|
+
@binding {any} value The cell value for the field.
|
|
225
|
+
@binding {number} index The zero-based row index.
|
|
226
|
+
@binding {object} field The field definition for the column.
|
|
227
|
+
-->
|
|
206
228
|
<slot
|
|
207
229
|
v-if="typeof name === 'string' && name.startsWith('cell')"
|
|
208
230
|
:name="name"
|
|
@@ -293,6 +315,13 @@
|
|
|
293
315
|
v-for="(_, name) in $slots"
|
|
294
316
|
#[name]="slotProps"
|
|
295
317
|
>
|
|
318
|
+
<!--
|
|
319
|
+
@slot Custom rendering for a column's cell. Name it `cell(<fieldKey>)` to target a field; receives the underlying table cell scope.
|
|
320
|
+
@binding {object} item The row data object for this cell.
|
|
321
|
+
@binding {any} value The cell value for the field.
|
|
322
|
+
@binding {number} index The zero-based row index.
|
|
323
|
+
@binding {object} field The field definition for the column.
|
|
324
|
+
-->
|
|
296
325
|
<slot
|
|
297
326
|
v-if="typeof name === 'string' && name.startsWith('cell')"
|
|
298
327
|
:name="name"
|
|
@@ -472,6 +501,13 @@
|
|
|
472
501
|
:key="`ev-${key}`"
|
|
473
502
|
#[`value(${key})`]="sp"
|
|
474
503
|
>
|
|
504
|
+
<!--
|
|
505
|
+
@slot Custom input for field `<key>` in the edit/create modal, forwarded to DXForm. Name it `edit-value(<fieldKey>)`.
|
|
506
|
+
@binding {object} item The row being edited (null in create mode).
|
|
507
|
+
@binding {any} value The current field value.
|
|
508
|
+
@binding {Function} update Call with a new value to update the field.
|
|
509
|
+
@binding {object} field The field definition.
|
|
510
|
+
-->
|
|
475
511
|
<slot
|
|
476
512
|
:name="`edit-value(${key})`"
|
|
477
513
|
:item="selectedItem"
|
|
@@ -487,6 +523,13 @@
|
|
|
487
523
|
:key="`es-${key}`"
|
|
488
524
|
#[`span(${key})`]="sp"
|
|
489
525
|
>
|
|
526
|
+
<!--
|
|
527
|
+
@slot Full-width custom content for field `<key>` in the edit/create modal, forwarded to DXForm's span slot. Name it `edit-span(<fieldKey>)`.
|
|
528
|
+
@binding {object} item The row being edited (null in create mode).
|
|
529
|
+
@binding {any} value The current field value.
|
|
530
|
+
@binding {Function} update Call with a new value to update the field.
|
|
531
|
+
@binding {Function} close Call to close the edit modal.
|
|
532
|
+
-->
|
|
490
533
|
<slot
|
|
491
534
|
:name="`edit-span(${key})`"
|
|
492
535
|
:item="selectedItem"
|
|
@@ -502,6 +545,11 @@
|
|
|
502
545
|
:key="`tc-${key}`"
|
|
503
546
|
#[`tab-content(${key})`]="sp"
|
|
504
547
|
>
|
|
548
|
+
<!--
|
|
549
|
+
@slot Replaces the auto-rendered fields of edit-modal tab `<key>` with custom content. Name it `tab-content(<tabKey>)`.
|
|
550
|
+
@binding {object} item The row being edited (null in create mode).
|
|
551
|
+
@binding {object} tab The tab definition.
|
|
552
|
+
-->
|
|
505
553
|
<slot :name="`tab-content(${key})`" :item="selectedItem" :tab="sp.tab" />
|
|
506
554
|
</template>
|
|
507
555
|
<template
|
|
@@ -509,6 +557,11 @@
|
|
|
509
557
|
:key="`tb-${key}`"
|
|
510
558
|
#[`tab-before(${key})`]="sp"
|
|
511
559
|
>
|
|
560
|
+
<!--
|
|
561
|
+
@slot Custom content rendered before the fields of edit-modal tab `<key>`. Name it `tab-before(<tabKey>)`.
|
|
562
|
+
@binding {object} item The row being edited (null in create mode).
|
|
563
|
+
@binding {object} tab The tab definition.
|
|
564
|
+
-->
|
|
512
565
|
<slot :name="`tab-before(${key})`" :item="selectedItem" :tab="sp.tab" />
|
|
513
566
|
</template>
|
|
514
567
|
<template
|
|
@@ -516,6 +569,11 @@
|
|
|
516
569
|
:key="`taf-${key}`"
|
|
517
570
|
#[`tab-after(${key})`]="sp"
|
|
518
571
|
>
|
|
572
|
+
<!--
|
|
573
|
+
@slot Custom content rendered after the fields of edit-modal tab `<key>`. Name it `tab-after(<tabKey>)`.
|
|
574
|
+
@binding {object} item The row being edited (null in create mode).
|
|
575
|
+
@binding {object} tab The tab definition.
|
|
576
|
+
-->
|
|
519
577
|
<slot :name="`tab-after(${key})`" :item="selectedItem" :tab="sp.tab" />
|
|
520
578
|
</template>
|
|
521
579
|
</DXForm>
|
|
@@ -736,6 +794,16 @@ export interface Props<TItem = any> {
|
|
|
736
794
|
/** API endpoint pattern for deletions (e.g., "/api/products/:id") */
|
|
737
795
|
deleteUrl?: string;
|
|
738
796
|
|
|
797
|
+
/**
|
|
798
|
+
* Guard run when Delete is clicked, before the confirm dialog and request.
|
|
799
|
+
* Return a message for a non-deletable item to show it immediately (as a
|
|
800
|
+
* toast) and skip both the confirm and the delete request; return
|
|
801
|
+
* `null`/`undefined` to proceed with the normal confirm + delete. Lets you
|
|
802
|
+
* short-circuit a doomed delete (e.g. a record with dependents that the
|
|
803
|
+
* server would reject) with an immediate, specific reason.
|
|
804
|
+
*/
|
|
805
|
+
deleteGuard?: (item: TItem) => string | null | undefined;
|
|
806
|
+
|
|
739
807
|
/** API endpoint for creating new items (e.g., "/api/products") — enables "New" button */
|
|
740
808
|
createUrl?: string;
|
|
741
809
|
|
|
@@ -771,20 +839,35 @@ const props = withDefaults(defineProps<Props<T>>(), {
|
|
|
771
839
|
});
|
|
772
840
|
|
|
773
841
|
const emit = defineEmits<{
|
|
842
|
+
/** Emitted when the user navigates to a different page (all modes). Payload is the new 1-based page number. */
|
|
774
843
|
pageChange: [page: number];
|
|
844
|
+
/** Emitted (Inertia mode) when the sort column or direction changes, with the active field key and order. */
|
|
775
845
|
sortChange: [sort: { key: string; order: 'asc' | 'desc' }];
|
|
846
|
+
/** Emitted when the inline column filter values change (debounced for server modes, immediate for client-side). Payload is the full filter map. */
|
|
776
847
|
filterChange: [filters: Record<string, string>];
|
|
848
|
+
/** Emitted when the per-page selector value changes. Payload is the new page size. */
|
|
777
849
|
perPageChange: [perPage: number];
|
|
850
|
+
/** Emitted when a table row is clicked, with the row item, its index, and the click event. */
|
|
778
851
|
rowClicked: [item: T, index: number, event: MouseEvent];
|
|
852
|
+
/** Emitted after a new item is successfully created via `createUrl`, with the created item and the raw response. */
|
|
779
853
|
rowCreated: [item: any, response: any];
|
|
854
|
+
/** Emitted when a create request fails, with the validation errors or error object. */
|
|
780
855
|
createError: [error: any];
|
|
856
|
+
/** Emitted after a row is successfully updated (or, with no `editUrl`, when the modal is saved), with the item and the response/form data. */
|
|
781
857
|
rowUpdated: [item: T, response: any];
|
|
858
|
+
/** Emitted when an update request fails, with the edited item and the error. */
|
|
782
859
|
editError: [item: T, error: any];
|
|
860
|
+
/** Emitted after a row is successfully deleted via `deleteUrl`, with the deleted item and the response. */
|
|
783
861
|
rowDeleted: [item: T, response: any];
|
|
862
|
+
/** Emitted when a delete request fails, with the item and the error. */
|
|
784
863
|
deleteError: [item: T, error: any];
|
|
864
|
+
/** `v-model:sortBy` update, emitted with the normalized single-column sort array when sorting changes. */
|
|
785
865
|
'update:sortBy': [sortBy: BTableSortBy[]];
|
|
866
|
+
/** `v-model:filters` update, emitted with the new filter map when a column filter changes. */
|
|
786
867
|
'update:filters': [filters: Record<string, string>];
|
|
868
|
+
/** `v-model:perPage` update, emitted with the new page size when the per-page selector changes. */
|
|
787
869
|
'update:perPage': [perPage: number];
|
|
870
|
+
/** `v-model:busy` update, forwarded from the underlying table's provider loading state (provider mode). */
|
|
788
871
|
'update:busy': [busy: boolean];
|
|
789
872
|
}>();
|
|
790
873
|
|
|
@@ -1660,6 +1743,19 @@ const handleEditCancel = () => {
|
|
|
1660
1743
|
const handleDelete = async () => {
|
|
1661
1744
|
if (!editForm.value || !selectedItem.value || !props.deleteUrl) return;
|
|
1662
1745
|
|
|
1746
|
+
// Delete guard: a non-null message means this item can't be deleted — show
|
|
1747
|
+
// it immediately and skip the confirm and the request entirely.
|
|
1748
|
+
const guardMessage = props.deleteGuard?.(selectedItem.value as T);
|
|
1749
|
+
if (guardMessage) {
|
|
1750
|
+
createToast?.({
|
|
1751
|
+
title: 'Cannot delete',
|
|
1752
|
+
body: guardMessage,
|
|
1753
|
+
variant: 'danger',
|
|
1754
|
+
modelValue: 5000,
|
|
1755
|
+
});
|
|
1756
|
+
return;
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1663
1759
|
// Confirm deletion
|
|
1664
1760
|
const itemName = (selectedItem.value as any).name || (selectedItem.value as any).title || singularItemName.value;
|
|
1665
1761
|
const confirmed = window.confirm(`Are you sure you want to delete "${itemName}"? This action cannot be undone.`);
|
|
@@ -1713,6 +1809,12 @@ const handleDelete = async () => {
|
|
|
1713
1809
|
|
|
1714
1810
|
defineExpose({
|
|
1715
1811
|
refresh,
|
|
1812
|
+
/**
|
|
1813
|
+
* Open the built-in create modal (same as clicking the default "New {item}"
|
|
1814
|
+
* button). Lets the create action live outside the table card — e.g. in a
|
|
1815
|
+
* page header or the dashboard navbar. No-op unless `editFields` are set.
|
|
1816
|
+
*/
|
|
1817
|
+
openCreate: handleCreateNew,
|
|
1716
1818
|
});
|
|
1717
1819
|
</script>
|
|
1718
1820
|
|
|
@@ -10,6 +10,13 @@ export interface NavigationItem {
|
|
|
10
10
|
|
|
11
11
|
export interface NavigationGroup {
|
|
12
12
|
label?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Stable identifier for the group's collapsible open/closed state. Defaults
|
|
15
|
+
* to `label` (then the array index). Set this when a menu can be reordered at
|
|
16
|
+
* runtime, or two groups share a label, so a manually-opened group keeps its
|
|
17
|
+
* state attached to the right group.
|
|
18
|
+
*/
|
|
19
|
+
key?: string;
|
|
13
20
|
items: NavigationItem[];
|
|
14
21
|
/**
|
|
15
22
|
* Per-group override for the sidebar's `collapsibleGroups` behaviour.
|