@omnitend/dashboard-for-laravel 0.21.0 → 0.22.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 +14 -4
- package/dist/components/extended/DXDashboardNavbar.vue.d.ts +16 -0
- package/dist/dashboard-for-laravel.js +667 -649
- package/dist/dashboard-for-laravel.js.map +1 -1
- package/dist/dashboard-for-laravel.umd.cjs +5 -5
- package/dist/dashboard-for-laravel.umd.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/style.css +1 -1
- package/dist/types/navigation.d.ts +13 -0
- package/docs/public/api-reference.json +46 -12
- package/docs/public/docs-map.md +1 -1
- package/package.json +4 -1
- package/resources/css/theme.scss +17 -2
- package/resources/js/components/extended/DXDashboard.vue +19 -7
- package/resources/js/components/extended/DXDashboardNavbar.vue +104 -26
- package/resources/js/index.ts +2 -0
- package/resources/js/types/navigation.ts +15 -0
|
@@ -28,3 +28,16 @@ export interface NavigationGroup {
|
|
|
28
28
|
visible?: boolean;
|
|
29
29
|
}
|
|
30
30
|
export type Navigation = NavigationGroup[];
|
|
31
|
+
/**
|
|
32
|
+
* Horizontal alignment of the navbar search slot content within its region:
|
|
33
|
+
* `"start"` = flush left (after the title from `md` up), `"center"` = centred.
|
|
34
|
+
* Shared by DXDashboard (which forwards it) and DXDashboardNavbar.
|
|
35
|
+
*/
|
|
36
|
+
export type NavbarSearchAlign = "start" | "center";
|
|
37
|
+
/**
|
|
38
|
+
* What the navbar actions slot does below the `md` breakpoint: `"wrap"` moves
|
|
39
|
+
* it to its own full-width row (the bar grows), `"hide"` removes it entirely
|
|
40
|
+
* (for apps that relocate page actions into the page on phones).
|
|
41
|
+
* Shared by DXDashboard (which forwards it) and DXDashboardNavbar.
|
|
42
|
+
*/
|
|
43
|
+
export type NavbarActionsOnMobile = "wrap" | "hide";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generated": "2026-07-
|
|
2
|
+
"generated": "2026-07-11T04:13:01.837Z",
|
|
3
3
|
"package": {
|
|
4
4
|
"name": "@omnitend/dashboard-for-laravel",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.21.0"
|
|
6
6
|
},
|
|
7
7
|
"components": {
|
|
8
8
|
"base": [
|
|
@@ -1394,7 +1394,14 @@
|
|
|
1394
1394
|
"name": "fluid",
|
|
1395
1395
|
"type": "boolean",
|
|
1396
1396
|
"required": false,
|
|
1397
|
-
"description": "Render the page content full-width and left-aligned instead of the default\ncentred, reading-width
|
|
1397
|
+
"description": "Render the page content full-width and left-aligned instead of the default\ncentred, reading-width column. Use for data-heavy admin pages (wide tables)."
|
|
1398
|
+
},
|
|
1399
|
+
{
|
|
1400
|
+
"name": "contentMaxWidth",
|
|
1401
|
+
"type": "string",
|
|
1402
|
+
"required": false,
|
|
1403
|
+
"default": "'1140px'",
|
|
1404
|
+
"description": "Max width of the centred content column (any CSS length). A genuine cap, so\nforms and text-heavy pages don't stretch to ~2000px on a wide display.\nIgnored when `fluid`. Set a large value (or use `fluid`) for full width."
|
|
1398
1405
|
},
|
|
1399
1406
|
{
|
|
1400
1407
|
"name": "contentClass",
|
|
@@ -1410,11 +1417,18 @@
|
|
|
1410
1417
|
"description": "User object for navbar dropdown"
|
|
1411
1418
|
},
|
|
1412
1419
|
{
|
|
1413
|
-
"name": "
|
|
1414
|
-
"type": "
|
|
1420
|
+
"name": "searchAlign",
|
|
1421
|
+
"type": "NavbarSearchAlign",
|
|
1415
1422
|
"required": false,
|
|
1416
|
-
"default": "'
|
|
1417
|
-
"description": "
|
|
1423
|
+
"default": "'start'",
|
|
1424
|
+
"description": "Horizontal alignment of the navbar search slot content (`\"start\"` = flush\nleft, `\"center\"` = centred), forwarded to DXDashboardNavbar."
|
|
1425
|
+
},
|
|
1426
|
+
{
|
|
1427
|
+
"name": "actionsOnMobile",
|
|
1428
|
+
"type": "NavbarActionsOnMobile",
|
|
1429
|
+
"required": false,
|
|
1430
|
+
"default": "'wrap'",
|
|
1431
|
+
"description": "What the navbar actions slot does below `md`: `\"wrap\"` to its own\nfull-width row, `\"hide\"` to remove it (relocate actions into the page on\nphones). Forwarded to DXDashboardNavbar."
|
|
1418
1432
|
},
|
|
1419
1433
|
{
|
|
1420
1434
|
"name": "collapsibleGroups",
|
|
@@ -1469,7 +1483,7 @@
|
|
|
1469
1483
|
"name": "DXDashboardNavbar",
|
|
1470
1484
|
"category": "extended",
|
|
1471
1485
|
"filePath": "resources/js/components/extended/DXDashboardNavbar.vue",
|
|
1472
|
-
"description": "Top navigation bar for the dashboard shell: menu toggle, page title,
|
|
1486
|
+
"description": "Top navigation bar for the dashboard shell: menu toggle, page title, search (aligned per `searchAlign`, left by default), page-level actions, and the user menu. Usually rendered by `DXDashboard`, which forwards these slots with a `navbar-` prefix.",
|
|
1473
1487
|
"props": [
|
|
1474
1488
|
{
|
|
1475
1489
|
"name": "user",
|
|
@@ -1484,6 +1498,20 @@
|
|
|
1484
1498
|
"required": false,
|
|
1485
1499
|
"default": "\"\"",
|
|
1486
1500
|
"description": "Page title shown at the left of the navbar (hidden below the `md` breakpoint)."
|
|
1501
|
+
},
|
|
1502
|
+
{
|
|
1503
|
+
"name": "searchAlign",
|
|
1504
|
+
"type": "NavbarSearchAlign",
|
|
1505
|
+
"required": false,
|
|
1506
|
+
"default": "\"start\"",
|
|
1507
|
+
"description": "Horizontal alignment of the search slot content within its region\n(`\"start\"` = flush left, `\"center\"` = centred). Applies at every size:\nthe region sits inline after the title from `md` up, and is its own\nfull-width row below."
|
|
1508
|
+
},
|
|
1509
|
+
{
|
|
1510
|
+
"name": "actionsOnMobile",
|
|
1511
|
+
"type": "NavbarActionsOnMobile",
|
|
1512
|
+
"required": false,
|
|
1513
|
+
"default": "\"wrap\"",
|
|
1514
|
+
"description": "What the actions slot does below the `md` breakpoint: `\"wrap\"` moves it\nto its own full-width row (the bar grows), `\"hide\"` removes it entirely\n(for apps that relocate page actions into the page on phones)."
|
|
1487
1515
|
}
|
|
1488
1516
|
],
|
|
1489
1517
|
"events": [
|
|
@@ -1501,12 +1529,12 @@
|
|
|
1501
1529
|
},
|
|
1502
1530
|
{
|
|
1503
1531
|
"name": "search",
|
|
1504
|
-
"description": "Search input or component.
|
|
1532
|
+
"description": "Search input or component. Aligned per `searchAlign` within its region (inline in the bar from `md` up; its own full-width row below).",
|
|
1505
1533
|
"bindings": []
|
|
1506
1534
|
},
|
|
1507
1535
|
{
|
|
1508
1536
|
"name": "actions",
|
|
1509
|
-
"description": "Page-level primary actions (e.g. a Create button)
|
|
1537
|
+
"description": "Page-level primary actions (e.g. a Create button). Right-aligned next to the user menu from `md` up; below, wraps to its own full-width row or is hidden entirely per `actionsOnMobile`.",
|
|
1510
1538
|
"bindings": [
|
|
1511
1539
|
{
|
|
1512
1540
|
"title": "binding",
|
|
@@ -1767,7 +1795,7 @@
|
|
|
1767
1795
|
"name": "DXField",
|
|
1768
1796
|
"category": "extended",
|
|
1769
1797
|
"filePath": "resources/js/components/extended/DXField.vue",
|
|
1770
|
-
"description": "Renders a single `FieldDefinition` of any type (text, select, autocomplete, 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.",
|
|
1798
|
+
"description": "Renders a single `FieldDefinition` of any type (text, select, autocomplete, 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`, `info-popover`, `hint` and `repeater-row` slots for per-field customisation.",
|
|
1771
1799
|
"props": [
|
|
1772
1800
|
{
|
|
1773
1801
|
"name": "field",
|
|
@@ -1968,7 +1996,13 @@
|
|
|
1968
1996
|
}
|
|
1969
1997
|
],
|
|
1970
1998
|
"events": [],
|
|
1971
|
-
"slots": [
|
|
1999
|
+
"slots": [
|
|
2000
|
+
{
|
|
2001
|
+
"name": "popover",
|
|
2002
|
+
"description": "Rich popover body. Overrides the plain `info` text — use",
|
|
2003
|
+
"bindings": []
|
|
2004
|
+
}
|
|
2005
|
+
],
|
|
1972
2006
|
"methods": []
|
|
1973
2007
|
},
|
|
1974
2008
|
{
|
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-07-
|
|
4
|
+
> Last updated: 2026-07-11T04:13:01.900Z
|
|
5
5
|
|
|
6
6
|
This file provides a complete map of all available documentation for AI agents and developers.
|
|
7
7
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omnitend/dashboard-for-laravel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.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",
|
|
@@ -31,8 +31,11 @@
|
|
|
31
31
|
"dev": "vite build --watch",
|
|
32
32
|
"build": "vite build && vue-tsc --declaration --emitDeclarationOnly --outDir dist",
|
|
33
33
|
"typecheck": "vue-tsc --noEmit",
|
|
34
|
+
"pretest": "vite build",
|
|
34
35
|
"test": "vitest",
|
|
36
|
+
"pretest:ui": "vite build",
|
|
35
37
|
"test:ui": "vitest --ui",
|
|
38
|
+
"pretest:headless": "vite build",
|
|
36
39
|
"test:headless": "vitest --run --browser.headless",
|
|
37
40
|
"docs:dev": "astro dev",
|
|
38
41
|
"docs:build": "npm run docs:generate:ai && astro build",
|
package/resources/css/theme.scss
CHANGED
|
@@ -287,11 +287,26 @@ $bootstrap-icons-font-src: url("bootstrap-icons/font/fonts/bootstrap-icons.woff2
|
|
|
287
287
|
}
|
|
288
288
|
|
|
289
289
|
.dashboard-navbar {
|
|
290
|
-
height:
|
|
290
|
+
// Floor, not a fixed height: below `md` the search (and actions) drop to
|
|
291
|
+
// their own rows and the bar must grow to contain them (#93). Single-row
|
|
292
|
+
// content is shorter than this, so desktop stays exactly this height,
|
|
293
|
+
// aligned with the sidebar header.
|
|
294
|
+
min-height: $dashboard-navbar-height;
|
|
291
295
|
// A light grey top bar over the white content panel (higher contrast than a
|
|
292
296
|
// grey-everywhere layout). The navbar markup drops its `bg-white` class so
|
|
293
297
|
// this wins without `!important`.
|
|
294
|
-
background-color:
|
|
298
|
+
background-color: var(--bs-light);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// The avatar dropdown trigger (DXDashboardNavbar sets this via toggle-class,
|
|
302
|
+
// so it lands on the BVN-rendered button — global rule, scoped CSS can't
|
|
303
|
+
// reach it). The themed $btn-padding-y (10px) would make it ~54px tall and
|
|
304
|
+
// blow the navbar's single-row content budget: 64px header minus the bar's
|
|
305
|
+
// 16px padding = 48px. 6px keeps a ~46px tap target (≥ the 44px WCAG
|
|
306
|
+
// guideline) while still fitting the budget.
|
|
307
|
+
.dashboard-navbar__user-menu-toggle {
|
|
308
|
+
padding-top: 0.375rem;
|
|
309
|
+
padding-bottom: 0.375rem;
|
|
295
310
|
}
|
|
296
311
|
|
|
297
312
|
// ----------------------------------------------------------------------------
|
|
@@ -36,7 +36,8 @@
|
|
|
36
36
|
<DXDashboardNavbar
|
|
37
37
|
:page-title="pageTitle"
|
|
38
38
|
:user="user"
|
|
39
|
-
:
|
|
39
|
+
:search-align="searchAlign"
|
|
40
|
+
:actions-on-mobile="actionsOnMobile"
|
|
40
41
|
@toggle-sidebar="toggleSidebar"
|
|
41
42
|
>
|
|
42
43
|
<!-- Dynamically forward all navbar-* slots by stripping the prefix -->
|
|
@@ -80,7 +81,7 @@ import DXDashboardNavbar from './DXDashboardNavbar.vue';
|
|
|
80
81
|
import DContainer from '../base/DContainer.vue';
|
|
81
82
|
import DRow from '../base/DRow.vue';
|
|
82
83
|
import DCol from '../base/DCol.vue';
|
|
83
|
-
import type { Navigation } from '../../types/navigation';
|
|
84
|
+
import type { Navigation, NavbarActionsOnMobile, NavbarSearchAlign } from '../../types/navigation';
|
|
84
85
|
|
|
85
86
|
const slots = useSlots();
|
|
86
87
|
|
|
@@ -116,8 +117,18 @@ interface Props {
|
|
|
116
117
|
/** User object for navbar dropdown */
|
|
117
118
|
user?: { name: string; email: string } | null;
|
|
118
119
|
|
|
119
|
-
/**
|
|
120
|
-
|
|
120
|
+
/**
|
|
121
|
+
* Horizontal alignment of the navbar search slot content (`"start"` = flush
|
|
122
|
+
* left, `"center"` = centred), forwarded to DXDashboardNavbar.
|
|
123
|
+
*/
|
|
124
|
+
searchAlign?: NavbarSearchAlign;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* What the navbar actions slot does below `md`: `"wrap"` to its own
|
|
128
|
+
* full-width row, `"hide"` to remove it (relocate actions into the page on
|
|
129
|
+
* phones). Forwarded to DXDashboardNavbar.
|
|
130
|
+
*/
|
|
131
|
+
actionsOnMobile?: NavbarActionsOnMobile;
|
|
121
132
|
|
|
122
133
|
/**
|
|
123
134
|
* Turn sidebar group headers into accordion toggles that collapse/expand
|
|
@@ -147,12 +158,13 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
147
158
|
title: 'Dashboard',
|
|
148
159
|
pageTitle: '',
|
|
149
160
|
user: null,
|
|
150
|
-
logoutUrl: '/logout',
|
|
151
161
|
collapsibleGroups: false,
|
|
152
162
|
autoCollapseInactiveGroups: true,
|
|
153
163
|
storageKey: 'dashboard-sidebar-hidden',
|
|
154
164
|
dashboardId: '',
|
|
155
165
|
contentMaxWidth: '1140px',
|
|
166
|
+
searchAlign: 'start',
|
|
167
|
+
actionsOnMobile: 'wrap',
|
|
156
168
|
});
|
|
157
169
|
|
|
158
170
|
const collapsed = ref(false);
|
|
@@ -245,7 +257,7 @@ const toggleSidebar = () => {
|
|
|
245
257
|
<style scoped>
|
|
246
258
|
.dashboard-layout {
|
|
247
259
|
min-height: 100vh;
|
|
248
|
-
background-color:
|
|
260
|
+
background-color: var(--bs-light);
|
|
249
261
|
}
|
|
250
262
|
|
|
251
263
|
.dashboard-content {
|
|
@@ -259,6 +271,6 @@ const toggleSidebar = () => {
|
|
|
259
271
|
max-width: 100%;
|
|
260
272
|
/* A white content panel under the light grey top bar / layout, so forms and
|
|
261
273
|
content sit on white (higher contrast than a grey-everywhere dashboard). */
|
|
262
|
-
background-color:
|
|
274
|
+
background-color: var(--bs-white);
|
|
263
275
|
}
|
|
264
276
|
</style>
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<!--
|
|
2
2
|
@component
|
|
3
|
-
Top navigation bar for the dashboard shell: menu toggle, page title,
|
|
4
|
-
|
|
5
|
-
`DXDashboard`, which forwards these slots
|
|
3
|
+
Top navigation bar for the dashboard shell: menu toggle, page title, search
|
|
4
|
+
(aligned per `searchAlign`, left by default), page-level actions, and the
|
|
5
|
+
user menu. Usually rendered by `DXDashboard`, which forwards these slots
|
|
6
|
+
with a `navbar-` prefix.
|
|
6
7
|
-->
|
|
7
8
|
<template>
|
|
8
9
|
<header class="dashboard-navbar border-bottom">
|
|
@@ -39,25 +40,35 @@
|
|
|
39
40
|
|
|
40
41
|
<div
|
|
41
42
|
v-if="$slots.search"
|
|
42
|
-
class="dashboard-navbar__search d-flex
|
|
43
|
+
class="dashboard-navbar__search d-flex"
|
|
44
|
+
:class="searchAlignClass"
|
|
43
45
|
>
|
|
44
|
-
<!-- @slot Search input or component.
|
|
46
|
+
<!-- @slot Search input or component. Aligned per `searchAlign` within its region (inline in the bar from `md` up; its own full-width row below). -->
|
|
45
47
|
<slot name="search" />
|
|
46
48
|
</div>
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
50
|
+
<!-- Page-level primary actions: right-aligned next to the user menu
|
|
51
|
+
from `md` up; their own wrappable full-width row below (#93),
|
|
52
|
+
or hidden below `md` when `actionsOnMobile` is "hide". -->
|
|
53
|
+
<div
|
|
54
|
+
v-if="$slots.actions"
|
|
55
|
+
class="dashboard-navbar__actions align-items-center gap-2"
|
|
56
|
+
:class="actionsDisplayClass"
|
|
57
|
+
>
|
|
58
|
+
<!--
|
|
59
|
+
@slot Page-level primary actions (e.g. a Create button). Right-aligned next to the user menu from `md` up; below, wraps to its own full-width row or is hidden entirely per `actionsOnMobile`.
|
|
60
|
+
@binding {string} pageTitle The current page title, for context.
|
|
61
|
+
-->
|
|
62
|
+
<slot name="actions" :pageTitle="pageTitle" />
|
|
63
|
+
</div>
|
|
60
64
|
|
|
65
|
+
<!-- Only render the user-menu cluster when it has content: an empty
|
|
66
|
+
flex item would still cost a bar gap, pushing actions off the
|
|
67
|
+
right edge in guest (user-less) layouts. -->
|
|
68
|
+
<div
|
|
69
|
+
v-if="$slots['user-menu'] || user"
|
|
70
|
+
class="dashboard-navbar__end d-flex align-items-center gap-3"
|
|
71
|
+
>
|
|
61
72
|
<!--
|
|
62
73
|
@slot Replaces the entire user menu (the default avatar dropdown).
|
|
63
74
|
@binding {object} user The signed-in user.
|
|
@@ -68,6 +79,7 @@
|
|
|
68
79
|
variant="link"
|
|
69
80
|
class="text-dark"
|
|
70
81
|
menu-class="dropdown-menu-end"
|
|
82
|
+
toggle-class="dashboard-navbar__user-menu-toggle"
|
|
71
83
|
no-caret
|
|
72
84
|
>
|
|
73
85
|
<template #button-content>
|
|
@@ -96,11 +108,13 @@
|
|
|
96
108
|
</template>
|
|
97
109
|
|
|
98
110
|
<script setup lang="ts">
|
|
111
|
+
import { computed } from "vue";
|
|
99
112
|
import DContainer from "../base/DContainer.vue";
|
|
100
113
|
import DButton from "../base/DButton.vue";
|
|
101
114
|
import DDropdown from "../base/DDropdown.vue";
|
|
115
|
+
import type { NavbarActionsOnMobile, NavbarSearchAlign } from "../../types/navigation";
|
|
102
116
|
|
|
103
|
-
withDefaults(
|
|
117
|
+
const props = withDefaults(
|
|
104
118
|
defineProps<{
|
|
105
119
|
/** The signed-in user shown in the avatar dropdown. `null` hides the menu. */
|
|
106
120
|
user?: {
|
|
@@ -110,13 +124,53 @@ withDefaults(
|
|
|
110
124
|
} | null;
|
|
111
125
|
/** Page title shown at the left of the navbar (hidden below the `md` breakpoint). */
|
|
112
126
|
pageTitle?: string;
|
|
127
|
+
/**
|
|
128
|
+
* Horizontal alignment of the search slot content within its region
|
|
129
|
+
* (`"start"` = flush left, `"center"` = centred). Applies at every size:
|
|
130
|
+
* the region sits inline after the title from `md` up, and is its own
|
|
131
|
+
* full-width row below.
|
|
132
|
+
*/
|
|
133
|
+
searchAlign?: NavbarSearchAlign;
|
|
134
|
+
/**
|
|
135
|
+
* What the actions slot does below the `md` breakpoint: `"wrap"` moves it
|
|
136
|
+
* to its own full-width row (the bar grows), `"hide"` removes it entirely
|
|
137
|
+
* (for apps that relocate page actions into the page on phones).
|
|
138
|
+
*/
|
|
139
|
+
actionsOnMobile?: NavbarActionsOnMobile;
|
|
113
140
|
}>(),
|
|
114
141
|
{
|
|
115
142
|
user: null,
|
|
116
143
|
pageTitle: "",
|
|
144
|
+
searchAlign: "start",
|
|
145
|
+
actionsOnMobile: "wrap",
|
|
117
146
|
},
|
|
118
147
|
);
|
|
119
148
|
|
|
149
|
+
// Explicit map (not string interpolation) so an out-of-union value at runtime
|
|
150
|
+
// falls back to a valid class instead of a silent Bootstrap no-op, and the
|
|
151
|
+
// full class names stay greppable.
|
|
152
|
+
const SEARCH_ALIGN_CLASSES: Record<NavbarSearchAlign, string> = {
|
|
153
|
+
start: "justify-content-start",
|
|
154
|
+
center: "justify-content-center",
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const searchAlignClass = computed(
|
|
158
|
+
() => SEARCH_ALIGN_CLASSES[props.searchAlign] ?? SEARCH_ALIGN_CLASSES.start,
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
// "hide" swaps d-flex for d-none/d-md-flex on the WRAPPER — hiding only the
|
|
162
|
+
// slot content would leave a zero-height full-width row still costing a flex
|
|
163
|
+
// row-gap on phones. Explicit map + fallback, same rationale as
|
|
164
|
+
// SEARCH_ALIGN_CLASSES above.
|
|
165
|
+
const ACTIONS_DISPLAY_CLASSES: Record<NavbarActionsOnMobile, string> = {
|
|
166
|
+
wrap: "d-flex",
|
|
167
|
+
hide: "d-none d-md-flex",
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const actionsDisplayClass = computed(
|
|
171
|
+
() => ACTIONS_DISPLAY_CLASSES[props.actionsOnMobile] ?? ACTIONS_DISPLAY_CLASSES.wrap,
|
|
172
|
+
);
|
|
173
|
+
|
|
120
174
|
defineEmits<{
|
|
121
175
|
/** Emitted when the hamburger menu button is clicked. */
|
|
122
176
|
toggleSidebar: [];
|
|
@@ -140,32 +194,56 @@ const getUserInitial = (user: { name: string } | null) => {
|
|
|
140
194
|
padding: 0.5rem 0;
|
|
141
195
|
}
|
|
142
196
|
|
|
143
|
-
/* Push the user-menu
|
|
197
|
+
/* Push the user-menu cluster to the right. (The actions region carries its
|
|
198
|
+
own auto margin from `md` up — see the sibling rule below.) */
|
|
144
199
|
.dashboard-navbar__end {
|
|
145
200
|
margin-left: auto;
|
|
146
201
|
}
|
|
147
202
|
|
|
148
203
|
/*
|
|
149
|
-
* Mobile-first: the search
|
|
150
|
-
* title / user-menu row (
|
|
151
|
-
*
|
|
152
|
-
*
|
|
204
|
+
* Mobile-first: the actions and the search each sit on their own full-width
|
|
205
|
+
* row below the toggle / title / user-menu row (flex-basis 100% forces the
|
|
206
|
+
* wrap; the bar and the sticky header grow to contain them — #93). The
|
|
207
|
+
* actions row itself wraps so a wide button group stacks instead of
|
|
208
|
+
* overflowing the viewport.
|
|
209
|
+
* From `md` up everything moves inline: search between the title and the
|
|
210
|
+
* actions (grows to fill the middle; `searchAlign` controls where its content
|
|
211
|
+
* sits), actions right-aligned next to the user menu. The actions' order 2
|
|
212
|
+
* holds at every size — only search and end change order across the
|
|
213
|
+
* breakpoint.
|
|
153
214
|
*/
|
|
215
|
+
.dashboard-navbar__actions {
|
|
216
|
+
order: 2;
|
|
217
|
+
flex: 0 0 100%;
|
|
218
|
+
flex-wrap: wrap;
|
|
219
|
+
}
|
|
220
|
+
|
|
154
221
|
.dashboard-navbar__search {
|
|
155
222
|
order: 3;
|
|
156
223
|
flex: 0 0 100%;
|
|
157
|
-
width: 100%;
|
|
158
224
|
}
|
|
159
225
|
|
|
160
226
|
@media (min-width: 768px) {
|
|
161
227
|
.dashboard-navbar__search {
|
|
162
228
|
order: 1;
|
|
163
229
|
flex: 1 1 auto;
|
|
164
|
-
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.dashboard-navbar__actions {
|
|
233
|
+
flex: 0 1 auto;
|
|
234
|
+
flex-wrap: nowrap;
|
|
235
|
+
margin-left: auto;
|
|
165
236
|
}
|
|
166
237
|
|
|
167
238
|
.dashboard-navbar__end {
|
|
168
|
-
order:
|
|
239
|
+
order: 3;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/* When the actions region is present it carries the push-right auto margin;
|
|
243
|
+
zero the user-menu cluster's so the two sit adjacent instead of having
|
|
244
|
+
the free space split between them. */
|
|
245
|
+
.dashboard-navbar__actions ~ .dashboard-navbar__end {
|
|
246
|
+
margin-left: 0;
|
|
169
247
|
}
|
|
170
248
|
}
|
|
171
249
|
|
package/resources/js/index.ts
CHANGED
|
@@ -30,3 +30,18 @@ export interface NavigationGroup {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export type Navigation = NavigationGroup[];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Horizontal alignment of the navbar search slot content within its region:
|
|
36
|
+
* `"start"` = flush left (after the title from `md` up), `"center"` = centred.
|
|
37
|
+
* Shared by DXDashboard (which forwards it) and DXDashboardNavbar.
|
|
38
|
+
*/
|
|
39
|
+
export type NavbarSearchAlign = "start" | "center";
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* What the navbar actions slot does below the `md` breakpoint: `"wrap"` moves
|
|
43
|
+
* it to its own full-width row (the bar grows), `"hide"` removes it entirely
|
|
44
|
+
* (for apps that relocate page actions into the page on phones).
|
|
45
|
+
* Shared by DXDashboard (which forwards it) and DXDashboardNavbar.
|
|
46
|
+
*/
|
|
47
|
+
export type NavbarActionsOnMobile = "wrap" | "hide";
|