@omnitend/dashboard-for-laravel 0.7.1 → 0.9.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 +13 -0
- package/dist/components/extended/DXDashboardSidebar.vue.d.ts +15 -0
- package/dist/components/extended/DXField.vue.d.ts +33 -16
- package/dist/components/extended/DXFieldLabel.vue.d.ts +8 -0
- package/dist/dashboard-for-laravel.js +7740 -7489
- 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/index.d.ts +1 -0
- package/dist/style.css +1 -1
- package/dist/types/index.d.ts +28 -1
- package/dist/types/navigation.d.ts +6 -0
- package/docs/public/api-reference.json +59 -4
- package/docs/public/docs-map.md +1 -1
- package/docs/public/llms.txt +3 -2
- package/package.json +1 -1
- package/resources/css/theme.scss +62 -1
- package/resources/js/components/extended/DXDashboard.vue +17 -0
- package/resources/js/components/extended/DXDashboardSidebar.vue +302 -41
- package/resources/js/components/extended/DXField.vue +145 -6
- package/resources/js/components/extended/DXFieldLabel.vue +72 -0
- package/resources/js/components/extended/DXForm.vue +8 -1
- package/resources/js/components/extended/DXRepeater.vue +1 -0
- package/resources/js/components/extended/DXTable.vue +26 -5
- package/resources/js/composables/defineForm.ts +1 -0
- package/resources/js/index.ts +1 -0
- package/resources/js/types/index.ts +39 -2
- package/resources/js/types/navigation.ts +6 -0
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
<DTab
|
|
30
30
|
v-for="(tab, index) in visibleTabs"
|
|
31
31
|
:key="tab.key"
|
|
32
|
-
:title="tab
|
|
32
|
+
:title="resolveTabLabel(tab)"
|
|
33
33
|
:lazy="tab.lazy"
|
|
34
34
|
:active="index === 0"
|
|
35
35
|
>
|
|
@@ -207,6 +207,13 @@ function resolvePredicate(
|
|
|
207
207
|
return typeof when === "function" ? when(model.value) : when;
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
+
/** Resolve a tab's (possibly function-valued) label against the live model. */
|
|
211
|
+
function resolveTabLabel(tab: FormTab): string {
|
|
212
|
+
const label =
|
|
213
|
+
typeof tab.label === "function" ? tab.label(model.value) : tab.label;
|
|
214
|
+
return label || tab.key;
|
|
215
|
+
}
|
|
216
|
+
|
|
210
217
|
function isFieldVisible(field: FieldDefinition): boolean {
|
|
211
218
|
const whenOk = resolvePredicate(field.when, true);
|
|
212
219
|
const showOk = field.show ? field.show() : true;
|
|
@@ -529,7 +529,7 @@
|
|
|
529
529
|
:disabled="editForm?.processing"
|
|
530
530
|
@click="handleDelete"
|
|
531
531
|
>
|
|
532
|
-
{{
|
|
532
|
+
{{ pendingAction === 'delete' ? 'Deleting...' : 'Delete' }}
|
|
533
533
|
</DButton>
|
|
534
534
|
</div>
|
|
535
535
|
<div class="d-flex gap-2">
|
|
@@ -542,10 +542,10 @@
|
|
|
542
542
|
@click="handleEditSave"
|
|
543
543
|
>
|
|
544
544
|
<template v-if="isCreateMode">
|
|
545
|
-
{{
|
|
545
|
+
{{ pendingAction === 'save' ? 'Creating...' : 'Create' }}
|
|
546
546
|
</template>
|
|
547
547
|
<template v-else>
|
|
548
|
-
{{
|
|
548
|
+
{{ pendingAction === 'save' ? 'Saving...' : 'Save Changes' }}
|
|
549
549
|
</template>
|
|
550
550
|
</DButton>
|
|
551
551
|
</div>
|
|
@@ -621,8 +621,12 @@ export interface EditTab {
|
|
|
621
621
|
/** Unique key for this tab */
|
|
622
622
|
key: string;
|
|
623
623
|
|
|
624
|
-
/**
|
|
625
|
-
|
|
624
|
+
/**
|
|
625
|
+
* Display label (optional, auto-derived from key if omitted). May be a
|
|
626
|
+
* function of the model (the edited row merged with the live form data),
|
|
627
|
+
* e.g. `label: (item) => \`Products (${item.products_count ?? 0})\``.
|
|
628
|
+
*/
|
|
629
|
+
label?: string | ((item: any) => string);
|
|
626
630
|
|
|
627
631
|
/** Field keys to display in this tab (from editFields) */
|
|
628
632
|
fieldKeys: string[];
|
|
@@ -1380,6 +1384,11 @@ const editForm = ref<any>(null);
|
|
|
1380
1384
|
const activeTabIndex = ref(0);
|
|
1381
1385
|
const isCreateMode = ref(false);
|
|
1382
1386
|
|
|
1387
|
+
// Which modal action is in flight, so the Save and Delete buttons show their
|
|
1388
|
+
// own loading label independently. `editForm.processing` is shared by every
|
|
1389
|
+
// request the form makes, so it can't tell Save from Delete on its own.
|
|
1390
|
+
const pendingAction = ref<'save' | 'delete' | null>(null);
|
|
1391
|
+
|
|
1383
1392
|
// Toast (may not be available in test environment)
|
|
1384
1393
|
let createToast: ((obj: any) => any) | undefined;
|
|
1385
1394
|
try {
|
|
@@ -1521,6 +1530,15 @@ const handleCreateNew = () => {
|
|
|
1521
1530
|
const handleEditSave = async () => {
|
|
1522
1531
|
if (!editForm.value) return;
|
|
1523
1532
|
|
|
1533
|
+
pendingAction.value = 'save';
|
|
1534
|
+
try {
|
|
1535
|
+
await performSave();
|
|
1536
|
+
} finally {
|
|
1537
|
+
pendingAction.value = null;
|
|
1538
|
+
}
|
|
1539
|
+
};
|
|
1540
|
+
|
|
1541
|
+
const performSave = async () => {
|
|
1524
1542
|
// Create mode: POST to createUrl
|
|
1525
1543
|
if (isCreateMode.value && props.createUrl) {
|
|
1526
1544
|
try {
|
|
@@ -1648,6 +1666,7 @@ const handleDelete = async () => {
|
|
|
1648
1666
|
|
|
1649
1667
|
if (!confirmed) return;
|
|
1650
1668
|
|
|
1669
|
+
pendingAction.value = 'delete';
|
|
1651
1670
|
try {
|
|
1652
1671
|
const itemId = (selectedItem.value as any).id;
|
|
1653
1672
|
const url = props.deleteUrl.replace(':id', itemId);
|
|
@@ -1687,6 +1706,8 @@ const handleDelete = async () => {
|
|
|
1687
1706
|
});
|
|
1688
1707
|
} catch (error) {
|
|
1689
1708
|
emit('deleteError', selectedItem.value as T, error);
|
|
1709
|
+
} finally {
|
|
1710
|
+
pendingAction.value = null;
|
|
1690
1711
|
}
|
|
1691
1712
|
};
|
|
1692
1713
|
|
package/resources/js/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ export * from 'bootstrap-vue-next';
|
|
|
8
8
|
export { default as DXDashboard } from "./components/extended/DXDashboard.vue";
|
|
9
9
|
export { default as DXForm } from "./components/extended/DXForm.vue";
|
|
10
10
|
export { default as DXField } from "./components/extended/DXField.vue";
|
|
11
|
+
export { default as DXFieldLabel } from "./components/extended/DXFieldLabel.vue";
|
|
11
12
|
export { default as DXRepeater } from "./components/extended/DXRepeater.vue";
|
|
12
13
|
/**
|
|
13
14
|
* @deprecated Use `DXForm`. `DXBasicForm` is a thin wrapper around `DXForm`
|
|
@@ -11,6 +11,8 @@ import type { Component } from "vue";
|
|
|
11
11
|
* - `image` / `file` — file input (`image` additionally shows a preview).
|
|
12
12
|
* - `component` — escape hatch that renders `field.component`.
|
|
13
13
|
* - `repeater` — nested, repeatable sub-form driven by `field.fields`.
|
|
14
|
+
* - `switch` — a toggle checkbox with contextual on/off text and an
|
|
15
|
+
* on-state (filled) style; see `textWhenTrue` / `textWhenFalse`.
|
|
14
16
|
*/
|
|
15
17
|
export type FieldType =
|
|
16
18
|
| "text"
|
|
@@ -28,6 +30,7 @@ export type FieldType =
|
|
|
28
30
|
| "textarea"
|
|
29
31
|
| "select"
|
|
30
32
|
| "checkbox"
|
|
33
|
+
| "switch"
|
|
31
34
|
| "radio"
|
|
32
35
|
| "image"
|
|
33
36
|
| "file"
|
|
@@ -105,6 +108,15 @@ export interface FieldDefinition {
|
|
|
105
108
|
/** Symbol shown for `currency` fields (default: the locale's, "£"). */
|
|
106
109
|
currencySymbol?: string;
|
|
107
110
|
|
|
111
|
+
/**
|
|
112
|
+
* For `percentage` fields: treat the underlying model value as a 0–1
|
|
113
|
+
* fraction while showing/editing it as a 0–100 percentage. The model keeps
|
|
114
|
+
* the fraction (e.g. `0.2`), the input shows `20`. Off by default (the value
|
|
115
|
+
* is taken as a whole percentage). Use for fields stored as ratios (VAT
|
|
116
|
+
* rates, discounts, …).
|
|
117
|
+
*/
|
|
118
|
+
asFraction?: boolean;
|
|
119
|
+
|
|
108
120
|
/** `accept` attribute for `image`/`file` inputs (e.g. "image/*"). */
|
|
109
121
|
accept?: string;
|
|
110
122
|
|
|
@@ -117,6 +129,26 @@ export interface FieldDefinition {
|
|
|
117
129
|
*/
|
|
118
130
|
hint?: MaybeFn<string>;
|
|
119
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Longer help text revealed in a popover from a small info affordance
|
|
134
|
+
* on the field's label (on hover/focus). Complements `hint` (which is
|
|
135
|
+
* always-visible muted text below the control). May be a function of
|
|
136
|
+
* the model. For rich content, use the `#info` slot instead.
|
|
137
|
+
*/
|
|
138
|
+
info?: MaybeFn<string>;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* For `switch` fields: contextual label shown when the toggle is on.
|
|
142
|
+
* Falls back to `label` when omitted. May be a function of the model.
|
|
143
|
+
*/
|
|
144
|
+
textWhenTrue?: MaybeFn<string>;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* For `switch` fields: contextual label shown when the toggle is off.
|
|
148
|
+
* Falls back to `label` when omitted. May be a function of the model.
|
|
149
|
+
*/
|
|
150
|
+
textWhenFalse?: MaybeFn<string>;
|
|
151
|
+
|
|
120
152
|
/** CSS class for the form group */
|
|
121
153
|
class?: string;
|
|
122
154
|
|
|
@@ -191,8 +223,13 @@ export interface FormTab {
|
|
|
191
223
|
/** Unique key for this tab */
|
|
192
224
|
key: string;
|
|
193
225
|
|
|
194
|
-
/**
|
|
195
|
-
|
|
226
|
+
/**
|
|
227
|
+
* Display label (optional, defaults to the key). May be a function of the
|
|
228
|
+
* form model — the live form data merged with any `context` (e.g. the row
|
|
229
|
+
* DXTable is editing) — so a tab title can reflect the record, such as
|
|
230
|
+
* `label: (model) => \`Products (${model.products_count ?? 0})\``.
|
|
231
|
+
*/
|
|
232
|
+
label?: MaybeFn<string>;
|
|
196
233
|
|
|
197
234
|
/** Field keys (from the form's fields) to render in this tab */
|
|
198
235
|
fieldKeys: string[];
|
|
@@ -11,7 +11,13 @@ export interface NavigationItem {
|
|
|
11
11
|
export interface NavigationGroup {
|
|
12
12
|
label?: string;
|
|
13
13
|
items: NavigationItem[];
|
|
14
|
+
/**
|
|
15
|
+
* Per-group override for the sidebar's `collapsibleGroups` behaviour.
|
|
16
|
+
* Set to `false` to keep a group permanently expanded (no toggle header)
|
|
17
|
+
* even when the sidebar has collapsible groups enabled. Defaults to `true`.
|
|
18
|
+
*/
|
|
14
19
|
collapsible?: boolean;
|
|
20
|
+
/** Reserved for a future explicit initial-open hint; not currently wired. */
|
|
15
21
|
collapsed?: boolean;
|
|
16
22
|
visible?: boolean;
|
|
17
23
|
}
|