@omnitend/dashboard-for-laravel 0.41.0 → 0.41.2
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/DButton.vue.d.ts +9 -0
- package/dist/components/base/DModal.vue.d.ts +26 -2
- package/dist/components/extended/DXLoading.vue.d.ts +9 -0
- package/dist/components/extended/DXModalActions.vue.d.ts +12 -0
- package/dist/components/extended/DXSwitch.vue.d.ts +1 -1
- package/dist/components/extended/DXTable.vue.d.ts +4 -0
- package/dist/dashboard-for-laravel.js +3066 -2998
- 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 +3 -0
- package/dist/style.css +2 -2
- package/docs/public/api-reference.json +80 -8
- package/docs/public/docs-map.md +1 -1
- package/docs/public/llms.txt +5 -2
- package/package.json +1 -1
- package/resources/css/theme.scss +44 -39
- package/resources/js/components/base/DAutocomplete.vue +3 -3
- package/resources/js/components/base/DButton.vue +39 -5
- package/resources/js/components/base/DModal.vue +13 -2
- package/resources/js/components/extended/DXLoading.vue +47 -0
- package/resources/js/components/extended/DXModalActions.vue +13 -0
- package/resources/js/components/extended/DXSwitch.vue +16 -10
- package/resources/js/components/extended/DXTable.vue +97 -95
- package/resources/js/components/extended/DXTablePagination.vue +1 -1
- package/resources/js/index.ts +3 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generated": "2026-
|
|
2
|
+
"generated": "2026-09-30T05:13:05.340Z",
|
|
3
3
|
"package": {
|
|
4
4
|
"name": "@omnitend/dashboard-for-laravel",
|
|
5
|
-
"version": "0.41.
|
|
5
|
+
"version": "0.41.2"
|
|
6
6
|
},
|
|
7
7
|
"components": {
|
|
8
8
|
"base": [
|
|
@@ -246,6 +246,13 @@
|
|
|
246
246
|
"required": false,
|
|
247
247
|
"description": "Leading Bootstrap Icons glyph name, rendered as `<i class=\"bi bi-{icon}\">`.\ne.g. `icon=\"save\"`. Hidden while the loading spinner is visible.\n\nDiverges from bvn: `BButton`'s native `icon` is a `boolean` (icon-button\nstyling mode). We redefine it as a glyph-name `string`, so bvn's boolean\nicon mode is unreachable through `DButton`. See DIVERGENCES.md."
|
|
248
248
|
},
|
|
249
|
+
{
|
|
250
|
+
"name": "iconOnly",
|
|
251
|
+
"type": "boolean",
|
|
252
|
+
"required": false,
|
|
253
|
+
"default": "false",
|
|
254
|
+
"description": "Square icon control at the same height as a normal button of this size.\nUse `icon` or an icon in the default slot, and provide `aria-label`.\nTakes precedence over `block`. While loading, the spinner replaces the\nicon/slot without changing dimensions; `loadingText` is not shown."
|
|
255
|
+
},
|
|
249
256
|
{
|
|
250
257
|
"name": "block",
|
|
251
258
|
"type": "boolean",
|
|
@@ -904,6 +911,16 @@
|
|
|
904
911
|
"props": [],
|
|
905
912
|
"events": [],
|
|
906
913
|
"slots": [
|
|
914
|
+
{
|
|
915
|
+
"name": "default",
|
|
916
|
+
"description": "",
|
|
917
|
+
"bindings": []
|
|
918
|
+
},
|
|
919
|
+
{
|
|
920
|
+
"name": "actions",
|
|
921
|
+
"description": "",
|
|
922
|
+
"bindings": []
|
|
923
|
+
},
|
|
907
924
|
{
|
|
908
925
|
"name": "name",
|
|
909
926
|
"description": "",
|
|
@@ -2730,6 +2747,47 @@
|
|
|
2730
2747
|
"slots": [],
|
|
2731
2748
|
"methods": []
|
|
2732
2749
|
},
|
|
2750
|
+
{
|
|
2751
|
+
"name": "DXLoading",
|
|
2752
|
+
"category": "extended",
|
|
2753
|
+
"filePath": "resources/js/components/extended/DXLoading.vue",
|
|
2754
|
+
"description": "",
|
|
2755
|
+
"props": [
|
|
2756
|
+
{
|
|
2757
|
+
"name": "text",
|
|
2758
|
+
"type": "string",
|
|
2759
|
+
"required": false,
|
|
2760
|
+
"default": "\"Loading…\"",
|
|
2761
|
+
"description": ""
|
|
2762
|
+
},
|
|
2763
|
+
{
|
|
2764
|
+
"name": "delay",
|
|
2765
|
+
"type": "number",
|
|
2766
|
+
"required": false,
|
|
2767
|
+
"default": "300",
|
|
2768
|
+
"description": ""
|
|
2769
|
+
}
|
|
2770
|
+
],
|
|
2771
|
+
"events": [],
|
|
2772
|
+
"slots": [],
|
|
2773
|
+
"methods": []
|
|
2774
|
+
},
|
|
2775
|
+
{
|
|
2776
|
+
"name": "DXModalActions",
|
|
2777
|
+
"category": "extended",
|
|
2778
|
+
"filePath": "resources/js/components/extended/DXModalActions.vue",
|
|
2779
|
+
"description": "Shared secondary modal actions; distinct from the modal footer controls.",
|
|
2780
|
+
"props": [],
|
|
2781
|
+
"events": [],
|
|
2782
|
+
"slots": [
|
|
2783
|
+
{
|
|
2784
|
+
"name": "default",
|
|
2785
|
+
"description": "",
|
|
2786
|
+
"bindings": []
|
|
2787
|
+
}
|
|
2788
|
+
],
|
|
2789
|
+
"methods": []
|
|
2790
|
+
},
|
|
2733
2791
|
{
|
|
2734
2792
|
"name": "DXNumericField",
|
|
2735
2793
|
"category": "extended",
|
|
@@ -3072,7 +3130,7 @@
|
|
|
3072
3130
|
"name": "DXSwitch",
|
|
3073
3131
|
"category": "extended",
|
|
3074
3132
|
"filePath": "resources/js/components/extended/DXSwitch.vue",
|
|
3075
|
-
"description": "DXSwitch — a \"filled box\" toggle switch. An opinionated switch built on `DFormCheckbox` (`switch`): a bordered box sized like a `.form-control` with the label on the left and the toggle on the right, colour-coding its state. By default (`onVariant=\"success\"`) it reads the house green-on / red-off style
|
|
3133
|
+
"description": "DXSwitch — a \"filled box\" toggle switch. An opinionated switch built on `DFormCheckbox` (`switch`): a bordered box sized like a `.form-control` with the label on the left and the toggle on the right, colour-coding its state. By default (`onVariant=\"success\"`) it reads the house green-on / red-off style: a light-red box with a muted red-grey toggle when off, a soft-green box with a muted green-grey toggle when on. `onVariant=\"neutral\"` (brand primary on / grey off) is for semantically-mixed switches that shouldn't imply good/bad (#158). Used standalone or, internally, by `DXField`'s `switch` field type so a form switch and a lone switch share one look. Usage: ```vue <DXSwitch v-model=\"isCurrent\" label=\"Product is current\" /> ``` For a form field, prefer a `DXField`/`DXForm` `type: 'switch'`; reach for `DXSwitch` directly when you want the styled toggle outside a form. Pass a `label` prop for simple text, or the default slot for richer label content.",
|
|
3076
3134
|
"props": [
|
|
3077
3135
|
{
|
|
3078
3136
|
"name": "modelValue",
|
|
@@ -3110,7 +3168,7 @@
|
|
|
3110
3168
|
"name": "size",
|
|
3111
3169
|
"type": "\"sm\"",
|
|
3112
3170
|
"required": false,
|
|
3113
|
-
"description": "`\"sm\"` renders a compact
|
|
3171
|
+
"description": "`\"sm\"` renders a compact rounded pill (a few px shorter than the default, which\nmatches the input height) for dense rows that repeat the switch per item (a\nper-line visibility toggle in a modal). Omit for the standard size, which\nlines the switch up with the `.form-control`\nheight so a switch lines up with inputs beside it."
|
|
3114
3172
|
}
|
|
3115
3173
|
],
|
|
3116
3174
|
"events": [
|
|
@@ -3596,7 +3654,7 @@
|
|
|
3596
3654
|
},
|
|
3597
3655
|
{
|
|
3598
3656
|
"name": "thead-top",
|
|
3599
|
-
"description": "
|
|
3657
|
+
"description": "Rows above the column headers, such as grouped-column banners or pinned totals. Give it `<tr>`s.",
|
|
3600
3658
|
"bindings": [
|
|
3601
3659
|
{
|
|
3602
3660
|
"title": "binding",
|
|
@@ -3616,6 +3674,20 @@
|
|
|
3616
3674
|
}
|
|
3617
3675
|
]
|
|
3618
3676
|
},
|
|
3677
|
+
{
|
|
3678
|
+
"name": "thead-sub",
|
|
3679
|
+
"description": "",
|
|
3680
|
+
"bindings": [
|
|
3681
|
+
{
|
|
3682
|
+
"name": "field",
|
|
3683
|
+
"title": "binding"
|
|
3684
|
+
},
|
|
3685
|
+
{
|
|
3686
|
+
"name": "v-bind",
|
|
3687
|
+
"title": "binding"
|
|
3688
|
+
}
|
|
3689
|
+
]
|
|
3690
|
+
},
|
|
3619
3691
|
{
|
|
3620
3692
|
"name": "`head-end(${field.key})`",
|
|
3621
3693
|
"description": "head-end(<fieldKey>) Additive content at the end of a column's own header — a period total above a numeric column, a small badge — keeping DXTable's sort indicator and field hint. Not a `head()` override.",
|
|
@@ -3979,7 +4051,7 @@
|
|
|
3979
4051
|
"name": "DXTablePagination",
|
|
3980
4052
|
"category": "extended",
|
|
3981
4053
|
"filePath": "resources/js/components/extended/DXTablePagination.vue",
|
|
3982
|
-
"description": "
|
|
4054
|
+
"description": "Pagination + per-page footer shared by `DXTable` and standalone tables. Renders the pager, the per-page selector, and the \"X to Y out of Z items\" info line (with the \"Filtered from N\" note when a column filter is active). Extracted because `DXTable` used to carry three byte-identical copies of this footer — one per data mode (client-side / inertia / provider) — that differed only in which `PaginationData` object and page-change handler they used, so every footer fix had to be written three times (#123). `DXTable` now derives the active pagination + handler and renders this once. It owns its own `:deep(.pagination …)` styling (rather than leaving it in `DXTable`), because a scoped `:deep()` in `DXTable` would not reach the pager once it lives in a child component — the scope-id doesn't cross the boundary. Anchoring the rules on this component's own `.dx-table-pagination` root (a plain element it owns) gives the scope-id a deterministic host. In a row too narrow for the full windowed pager, the number strip collapses to a \"current / last\" status with Previous/Next either side (#162). The decision is CONTAINER-driven, not viewport-driven — see `compactThreshold`.",
|
|
3983
4055
|
"props": [
|
|
3984
4056
|
{
|
|
3985
4057
|
"name": "pagination",
|
|
@@ -4142,8 +4214,8 @@
|
|
|
4142
4214
|
]
|
|
4143
4215
|
},
|
|
4144
4216
|
"stats": {
|
|
4145
|
-
"totalComponents":
|
|
4217
|
+
"totalComponents": 86,
|
|
4146
4218
|
"baseComponents": 60,
|
|
4147
|
-
"extendedComponents":
|
|
4219
|
+
"extendedComponents": 26
|
|
4148
4220
|
}
|
|
4149
4221
|
}
|
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-
|
|
4
|
+
> Last updated: 2026-09-30T05:13:05.394Z
|
|
5
5
|
|
|
6
6
|
This file provides a complete map of all available documentation for AI agents and developers.
|
|
7
7
|
|
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 76 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 (16 components)
|
|
88
88
|
|
|
89
89
|
Custom dashboard components with advanced functionality beyond Bootstrap Vue Next.
|
|
90
90
|
|
|
@@ -96,10 +96,13 @@ 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
|
+
- [DXLoading](/components/extended/dxloading): Extended dashboard component
|
|
100
|
+
- [DXModalActions](/components/extended/dxmodalactions): Extended dashboard component
|
|
99
101
|
- [DXRepeater](/components/extended/dxrepeater): Repeatable nested sub-form (field array) primitive
|
|
100
102
|
- [DXStatCard](/components/extended/dxstatcard): Extended dashboard component
|
|
101
103
|
- [DXSwitch](/components/extended/dxswitch): Extended dashboard component
|
|
102
104
|
- [DXTable](/components/extended/dxtable): Data table with pagination, filtering, and sorting
|
|
105
|
+
- [DXTablePagination](/components/extended/dxtablepagination): Extended dashboard component
|
|
103
106
|
- [DXUserAvatar](/components/extended/dxuseravatar): Extended dashboard component
|
|
104
107
|
|
|
105
108
|
## Composables
|
package/package.json
CHANGED
package/resources/css/theme.scss
CHANGED
|
@@ -100,20 +100,12 @@ $btn-padding-x-lg: 1.5rem; // 24px
|
|
|
100
100
|
// Forms
|
|
101
101
|
// ----------------------------------------------------------------------------
|
|
102
102
|
|
|
103
|
-
|
|
103
|
+
// Inputs, selects, input-group labels and buttons share one height at each
|
|
104
|
+
// size. Otherwise adding a button stretches a default input group by 8px.
|
|
105
|
+
$input-padding-y: $btn-padding-y;
|
|
104
106
|
$input-padding-x: 1rem;
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
// (PO18). Bootstrap sizes every child of an `.input-group-sm`/`-lg` — buttons
|
|
108
|
-
// INCLUDED — with the INPUT's sized padding, so leaving these at Bootstrap's
|
|
109
|
-
// defaults (0.25rem / 0.5rem) made a sized input-group sit ~8px / ~4px shorter
|
|
110
|
-
// than a `.btn-sm`/`.btn-lg` beside it. Aligning them makes sized controls share
|
|
111
|
-
// one height by construction. NB: the BASE `$input-padding-y` (0.375rem) stays
|
|
112
|
-
// deliberately more compact than `$btn-padding-y` (0.625rem) — see the comment
|
|
113
|
-
// above ("0.625rem read oversized"); only the SIZED variants are equalised,
|
|
114
|
-
// because those are the ones whose padding Bootstrap leaks onto grouped buttons.
|
|
115
|
-
$input-padding-y-sm: 0.5rem; // == $btn-padding-y-sm (was Bootstrap's 0.25rem)
|
|
116
|
-
$input-padding-y-lg: 0.75rem; // == $btn-padding-y-lg (was Bootstrap's 0.5rem)
|
|
107
|
+
$input-padding-y-sm: $btn-padding-y-sm;
|
|
108
|
+
$input-padding-y-lg: $btn-padding-y-lg;
|
|
117
109
|
$input-border-radius: $border-radius;
|
|
118
110
|
$input-border-color: #d1d5db; // Gray-300
|
|
119
111
|
$input-focus-border-color: $primary;
|
|
@@ -288,6 +280,7 @@ $bootstrap-icons-font-src: url("bootstrap-icons/font/fonts/bootstrap-icons.woff2
|
|
|
288
280
|
// and expose it as a var so such components can reference the single source
|
|
289
281
|
// of truth instead of duplicating the formula.
|
|
290
282
|
:root {
|
|
283
|
+
--d-input-border-color: #{$input-border-color};
|
|
291
284
|
// $input-border-width resolves to a CSS var() (Bootstrap 5.3+), which Sass
|
|
292
285
|
// can't multiply directly — fold the *2 into the calc() itself instead.
|
|
293
286
|
--dx-input-height: calc(
|
|
@@ -295,6 +288,16 @@ $bootstrap-icons-font-src: url("bootstrap-icons/font/fonts/bootstrap-icons.woff2
|
|
|
295
288
|
);
|
|
296
289
|
}
|
|
297
290
|
|
|
291
|
+
// Bootstrap sizes grouped buttons with padding properties, but leaves their
|
|
292
|
+
// padding variables unchanged. Keep computed icon-only dimensions in sync.
|
|
293
|
+
.input-group-sm > .btn {
|
|
294
|
+
--bs-btn-padding-y: #{$input-padding-y-sm};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.input-group-lg > .btn {
|
|
298
|
+
--bs-btn-padding-y: #{$input-padding-y-lg};
|
|
299
|
+
}
|
|
300
|
+
|
|
298
301
|
// Horizontal ("label-left") forms: a compact settings-form rhythm, with the
|
|
299
302
|
// label + control top-aligned so a checkbox/switch sits on the label's first
|
|
300
303
|
// line rather than floating above it (the label column carries a top offset to
|
|
@@ -305,12 +308,8 @@ $bootstrap-icons-font-src: url("bootstrap-icons/font/fonts/bootstrap-icons.woff2
|
|
|
305
308
|
align-items: flex-start;
|
|
306
309
|
}
|
|
307
310
|
|
|
308
|
-
.col-form-label {
|
|
309
|
-
padding-top: 0.4rem;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
311
|
.form-check {
|
|
313
|
-
margin-top:
|
|
312
|
+
margin-top: calc(#{$input-padding-y} + #{$input-border-width});
|
|
314
313
|
}
|
|
315
314
|
|
|
316
315
|
.mb-3 {
|
|
@@ -689,6 +688,17 @@ $bootstrap-icons-font-src: url("bootstrap-icons/font/fonts/bootstrap-icons.woff2
|
|
|
689
688
|
// independent of the sort indicator's presence. Scoped to `.table > thead` so a
|
|
690
689
|
// consumer who genuinely wants bottom-aligned headers can still opt back in on a
|
|
691
690
|
// specific table.
|
|
691
|
+
// Body cells inherit this alignment; explicit .align-top remains available
|
|
692
|
+
// on a table, row or cell for long-form content that needs top alignment.
|
|
693
|
+
.table {
|
|
694
|
+
vertical-align: middle;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// Column help uses the table's header colour rather than the form-label ink.
|
|
698
|
+
.table > thead th .dx-field-label__text {
|
|
699
|
+
color: inherit;
|
|
700
|
+
}
|
|
701
|
+
|
|
692
702
|
.table > thead {
|
|
693
703
|
vertical-align: middle;
|
|
694
704
|
}
|
|
@@ -702,13 +712,9 @@ $bootstrap-icons-font-src: url("bootstrap-icons/font/fonts/bootstrap-icons.woff2
|
|
|
702
712
|
// as the header token above), sized and inset with `padding-left` so text
|
|
703
713
|
// clears it.
|
|
704
714
|
//
|
|
705
|
-
//
|
|
706
|
-
//
|
|
707
|
-
|
|
708
|
-
// `.d-autocomplete .input-group .b-autocomplete-input-wrapper` — so it is not a
|
|
709
|
-
// direct child and correctly gets NO glyph (it has its own chevron affordance).
|
|
710
|
-
// `:not([type="date"])` further excludes the date filter (its own picker icon).
|
|
711
|
-
.filter-row > th > input.form-control:not([type="date"]) {
|
|
715
|
+
// Select filters keep their chevron without a second icon or extra text inset.
|
|
716
|
+
// Date inputs keep their own picker affordance.
|
|
717
|
+
.dx-table-filter-cell > input.form-control:not([type="date"]) {
|
|
712
718
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='%237c8293' viewBox='0 0 16 16'%3e%3cpath d='M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0'/%3e%3c/svg%3e");
|
|
713
719
|
background-repeat: no-repeat;
|
|
714
720
|
background-position: left 0.65rem center;
|
|
@@ -749,7 +755,7 @@ $bootstrap-icons-font-src: url("bootstrap-icons/font/fonts/bootstrap-icons.woff2
|
|
|
749
755
|
// theme `@import`s `bootstrap-icons/font/bootstrap-icons` (declaring the
|
|
750
756
|
// `@font-face`), and #77's post-build extraction ships that woff2 with the
|
|
751
757
|
// stylesheet — so this needs no new dependency and no data-URI. Values signed
|
|
752
|
-
// off after one padding iteration in the
|
|
758
|
+
// off after one padding iteration in the interim UI; the glyph metrics
|
|
753
759
|
// are tunable via the variables below.
|
|
754
760
|
$dx-dropdown-caret-size: 0.75em; // glyph size relative to the label
|
|
755
761
|
$dx-dropdown-caret-gap: 0.4em; // space between label and chevron
|
|
@@ -805,22 +811,16 @@ $dx-switch-on-soft: map.get(map.get($dx-variants, "success"), "soft-bg"); // #cd
|
|
|
805
811
|
--dx-switch-off-well: #{color.mix($dx-switch-off, #fff, 10%)};
|
|
806
812
|
--dx-switch-off-line: #{color.mix($dx-switch-off, #fff, 45%)};
|
|
807
813
|
--dx-switch-off-ink: #{color.mix($dx-switch-off, #000, 30%)};
|
|
808
|
-
--dx-switch-pill: var(--bs-gray-400, #ced4da);
|
|
814
|
+
--dx-switch-pill: var(--bs-gray-400, #ced4da);
|
|
815
|
+
--dx-switch-off-pill: #{color.mix(map.get(map.get($dx-variants, "danger"), "soft-text"), $gray-400, 50%)};
|
|
816
|
+
--dx-switch-on-pill: #{color.mix(map.get(map.get($dx-variants, "success"), "soft-text"), $gray-400, 50%)};
|
|
809
817
|
}
|
|
810
818
|
|
|
811
|
-
//
|
|
812
|
-
//
|
|
813
|
-
//
|
|
814
|
-
// reads visibly HIGH beside the text (same optical-metrics issue as the B14
|
|
815
|
-
// dropdown caret). `0.325em` lands the box centre on Poppins' visual centre —
|
|
816
|
-
// judged against a rendered filmstrip (0.25/0.30/0.325/0.35em) with Poppins
|
|
817
|
-
// loaded, not from the maths. A top MARGIN (not flex-centring) is deliberate:
|
|
818
|
-
// it aligns to the first line, so a multi-line label keeps the box on line one
|
|
819
|
-
// rather than floating to the block's vertical centre. DXSwitch is unaffected —
|
|
820
|
-
// it flex-centres and zeroes this margin on its inner input at higher
|
|
821
|
-
// specificity (`.dx-switch :deep(.form-check-input) { margin: 0 }`).
|
|
819
|
+
// Centre checkboxes/radios on the first label line using their actual size.
|
|
820
|
+
// The old em offset assumed a 1em box; these controls are now 1.25rem tall.
|
|
821
|
+
// A margin keeps wrapped labels aligned to line one rather than the whole block.
|
|
822
822
|
.form-check-input {
|
|
823
|
-
margin-top:
|
|
823
|
+
margin-top: calc((1lh - #{$form-check-input-width}) / 2);
|
|
824
824
|
}
|
|
825
825
|
|
|
826
826
|
.form-switch .form-check-input {
|
|
@@ -1046,3 +1046,8 @@ $dx-chart-palette-dark: (#60a5fa, #a3e635, #a78bfa, #2dd4bf, #fb923c, #22d3ee, #
|
|
|
1046
1046
|
margin-left: 0;
|
|
1047
1047
|
}
|
|
1048
1048
|
}
|
|
1049
|
+
|
|
1050
|
+
// Keep the compact filter-cell spacing when BTable supplies the subheader row.
|
|
1051
|
+
thead tr:has(.dx-table-filter-cell) > td {
|
|
1052
|
+
padding: 0.5rem;
|
|
1053
|
+
}
|
|
@@ -203,13 +203,13 @@ const noClearButton = computed(
|
|
|
203
203
|
*/
|
|
204
204
|
.d-autocomplete :deep(.b-autocomplete-trigger) {
|
|
205
205
|
--bs-btn-bg: var(--bs-body-bg);
|
|
206
|
-
--bs-btn-border-color: var(--bs-border-color);
|
|
206
|
+
--bs-btn-border-color: var(--d-input-border-color, var(--bs-border-color));
|
|
207
207
|
--bs-btn-color: var(--bs-secondary-color);
|
|
208
208
|
--bs-btn-hover-bg: var(--bs-tertiary-bg);
|
|
209
|
-
--bs-btn-hover-border-color: var(--bs-border-color);
|
|
209
|
+
--bs-btn-hover-border-color: var(--d-input-border-color, var(--bs-border-color));
|
|
210
210
|
--bs-btn-hover-color: var(--bs-body-color);
|
|
211
211
|
--bs-btn-active-bg: var(--bs-secondary-bg);
|
|
212
|
-
--bs-btn-active-border-color: var(--bs-border-color);
|
|
212
|
+
--bs-btn-active-border-color: var(--d-input-border-color, var(--bs-border-color));
|
|
213
213
|
--bs-btn-active-color: var(--bs-body-color);
|
|
214
214
|
flex: 0 0 auto;
|
|
215
215
|
}
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* DButton - A type-safe wrapper around Bootstrap Vue Next's BButton component
|
|
4
4
|
*
|
|
5
|
-
* Adds
|
|
5
|
+
* Adds base-level button behaviours on top of `variant`/`size`:
|
|
6
6
|
* - `loading` — disables the button and shows a trailing spinner while an async
|
|
7
7
|
* action is in flight, with anti-flash timing (no spinner for sub-`spinnerDelay`
|
|
8
8
|
* actions; a `minSpinnerTime` floor once shown so it never strobes).
|
|
9
9
|
* - `icon` — a leading Bootstrap Icons glyph (e.g. `icon="save"` → `bi bi-save`).
|
|
10
|
+
* - `iconOnly` — square icon control at the standard height for its size.
|
|
10
11
|
* - `block` — full-width button (the BS5 replacement for the dropped `.btn-block`).
|
|
11
12
|
*
|
|
12
13
|
* @component
|
|
@@ -15,6 +16,7 @@
|
|
|
15
16
|
* <DButton variant="primary" size="lg">Click Me</DButton>
|
|
16
17
|
* <DButton icon="save" variant="primary">Save</DButton>
|
|
17
18
|
* <DButton :loading="isSaving" loading-text="Saving…">Save</DButton>
|
|
19
|
+
* <DButton icon="trash" icon-only aria-label="Delete" />
|
|
18
20
|
* <DButton block>Continue</DButton>
|
|
19
21
|
* ```
|
|
20
22
|
*/
|
|
@@ -88,6 +90,15 @@ interface Props {
|
|
|
88
90
|
*/
|
|
89
91
|
icon?: string;
|
|
90
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Square icon control at the same height as a normal button of this size.
|
|
95
|
+
* Use `icon` or an icon in the default slot, and provide `aria-label`.
|
|
96
|
+
* Takes precedence over `block`. While loading, the spinner replaces the
|
|
97
|
+
* icon/slot without changing dimensions; `loadingText` is not shown.
|
|
98
|
+
* @default false
|
|
99
|
+
*/
|
|
100
|
+
iconOnly?: boolean;
|
|
101
|
+
|
|
91
102
|
/**
|
|
92
103
|
* Full-width button (BS5 `w-100` idiom, replacing the dropped `.btn-block`).
|
|
93
104
|
* @default false
|
|
@@ -114,6 +125,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
114
125
|
disabled: false,
|
|
115
126
|
loading: false,
|
|
116
127
|
block: false,
|
|
128
|
+
iconOnly: false,
|
|
117
129
|
spinnerDelay: 500,
|
|
118
130
|
minSpinnerTime: 100,
|
|
119
131
|
});
|
|
@@ -189,7 +201,7 @@ const passthroughSlotNames = computed(() =>
|
|
|
189
201
|
// `:deep()`-on-a-B-root trap. Only add the flex row when there's an adornment
|
|
190
202
|
// to space out.
|
|
191
203
|
const hasAdornment = computed(
|
|
192
|
-
() => Boolean(props.icon) || showSpinner.value,
|
|
204
|
+
() => props.iconOnly || Boolean(props.icon) || showSpinner.value,
|
|
193
205
|
);
|
|
194
206
|
</script>
|
|
195
207
|
|
|
@@ -201,7 +213,7 @@ const hasAdornment = computed(
|
|
|
201
213
|
:aria-busy="loading || showSpinner || undefined"
|
|
202
214
|
:class="[
|
|
203
215
|
{ 'd-inline-flex align-items-center justify-content-center gap-2': hasAdornment },
|
|
204
|
-
{ 'w-100': block },
|
|
216
|
+
{ 'w-100': block && !iconOnly, 'd-button--icon-only': iconOnly },
|
|
205
217
|
]"
|
|
206
218
|
v-bind="$attrs"
|
|
207
219
|
>
|
|
@@ -210,8 +222,8 @@ const hasAdornment = computed(
|
|
|
210
222
|
:class="['bi', `bi-${icon}`]"
|
|
211
223
|
aria-hidden="true"
|
|
212
224
|
></i>
|
|
213
|
-
<template v-if="showSpinner && loadingText">{{ loadingText }}</template>
|
|
214
|
-
<slot v-else />
|
|
225
|
+
<template v-if="showSpinner && loadingText && !iconOnly">{{ loadingText }}</template>
|
|
226
|
+
<slot v-else-if="!iconOnly || !showSpinner" />
|
|
215
227
|
<DSpinner v-if="showSpinner" small />
|
|
216
228
|
|
|
217
229
|
<!-- Forward any other named slots untouched, with their slot props. -->
|
|
@@ -224,3 +236,25 @@ const hasAdornment = computed(
|
|
|
224
236
|
</template>
|
|
225
237
|
</BButton>
|
|
226
238
|
</template>
|
|
239
|
+
|
|
240
|
+
<style scoped>
|
|
241
|
+
.d-button--icon-only {
|
|
242
|
+
--d-button-icon-size: calc(
|
|
243
|
+
1em * var(--bs-btn-line-height) + 2 * var(--bs-btn-padding-y) +
|
|
244
|
+
2 * var(--bs-btn-border-width)
|
|
245
|
+
);
|
|
246
|
+
width: var(--d-button-icon-size);
|
|
247
|
+
height: var(--d-button-icon-size);
|
|
248
|
+
padding: 0;
|
|
249
|
+
flex-shrink: 0;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.input-group > .d-button--icon-only {
|
|
253
|
+
padding: 0;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.d-button--icon-only > .bi {
|
|
257
|
+
font-size: 1.25em;
|
|
258
|
+
line-height: 1;
|
|
259
|
+
}
|
|
260
|
+
</style>
|
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<BModal v-bind="$attrs">
|
|
3
|
-
<template
|
|
2
|
+
<BModal size="lg" v-bind="$attrs">
|
|
3
|
+
<template #default="slotProps">
|
|
4
|
+
<slot v-bind="slotProps" />
|
|
5
|
+
<DXModalActions v-if="$slots.actions" class="mt-3">
|
|
6
|
+
<slot name="actions" v-bind="slotProps" />
|
|
7
|
+
</DXModalActions>
|
|
8
|
+
</template>
|
|
9
|
+
<template v-for="name in forwardedSlots" #[name]="slotProps">
|
|
4
10
|
<slot :name="name" v-bind="slotProps" />
|
|
5
11
|
</template>
|
|
6
12
|
</BModal>
|
|
7
13
|
</template>
|
|
8
14
|
|
|
9
15
|
<script setup lang="ts">
|
|
16
|
+
import { computed, useSlots } from "vue";
|
|
10
17
|
import { BModal } from "bootstrap-vue-next";
|
|
18
|
+
import DXModalActions from "../extended/DXModalActions.vue";
|
|
19
|
+
|
|
20
|
+
const slots = useSlots();
|
|
21
|
+
const forwardedSlots = computed(() => Object.keys(slots).filter(name => name !== "default" && name !== "actions"));
|
|
11
22
|
</script>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
v-if="isVisible"
|
|
4
|
+
class="dx-loading text-center text-muted py-5"
|
|
5
|
+
role="status"
|
|
6
|
+
aria-live="polite"
|
|
7
|
+
>
|
|
8
|
+
<DSpinner variant="primary" aria-hidden="true" />
|
|
9
|
+
<div class="mt-2">{{ text }}</div>
|
|
10
|
+
</div>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup lang="ts">
|
|
14
|
+
import { onBeforeUnmount, onMounted, ref } from "vue";
|
|
15
|
+
import DSpinner from "../base/DSpinner.vue";
|
|
16
|
+
|
|
17
|
+
const props = withDefaults(
|
|
18
|
+
defineProps<{
|
|
19
|
+
text?: string;
|
|
20
|
+
delay?: number;
|
|
21
|
+
}>(),
|
|
22
|
+
{
|
|
23
|
+
text: "Loading…",
|
|
24
|
+
delay: 300,
|
|
25
|
+
},
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const isVisible = ref(false);
|
|
29
|
+
let showTimer: ReturnType<typeof setTimeout> | undefined;
|
|
30
|
+
|
|
31
|
+
onMounted(() => {
|
|
32
|
+
if (props.delay <= 0) {
|
|
33
|
+
isVisible.value = true;
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
showTimer = setTimeout(() => {
|
|
38
|
+
isVisible.value = true;
|
|
39
|
+
}, props.delay);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
onBeforeUnmount(() => {
|
|
43
|
+
if (showTimer !== undefined) {
|
|
44
|
+
clearTimeout(showTimer);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
</script>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!-- Shared secondary modal actions; distinct from the modal footer controls. -->
|
|
2
|
+
<template>
|
|
3
|
+
<div class="dx-modal-actions"><slot /></div>
|
|
4
|
+
</template>
|
|
5
|
+
|
|
6
|
+
<style scoped>
|
|
7
|
+
.dx-modal-actions {
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-direction: column;
|
|
10
|
+
align-items: stretch;
|
|
11
|
+
gap: 0.75rem;
|
|
12
|
+
}
|
|
13
|
+
</style>
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
An opinionated switch built on `DFormCheckbox` (`switch`): a bordered box sized
|
|
6
6
|
like a `.form-control` with the label on the left and the toggle on the right,
|
|
7
7
|
colour-coding its state. By default (`onVariant="success"`) it reads the house
|
|
8
|
-
green-on / red-off style
|
|
9
|
-
box
|
|
8
|
+
green-on / red-off style: a light-red box with a muted red-grey toggle when off, a
|
|
9
|
+
soft-green box with a muted green-grey toggle when on. `onVariant="neutral"` (brand primary on / grey off)
|
|
10
10
|
is for semantically-mixed switches that shouldn't imply good/bad (#158). Used
|
|
11
11
|
standalone or, internally, by `DXField`'s `switch` field type so a form switch
|
|
12
12
|
and a lone switch share one look.
|
|
@@ -64,7 +64,7 @@ interface Props {
|
|
|
64
64
|
*/
|
|
65
65
|
onVariant?: "success" | "neutral";
|
|
66
66
|
/**
|
|
67
|
-
* `"sm"` renders a compact
|
|
67
|
+
* `"sm"` renders a compact rounded pill (a few px shorter than the default, which
|
|
68
68
|
* matches the input height) for dense rows that repeat the switch per item (a
|
|
69
69
|
* per-line visibility toggle in a modal). Omit for the standard size, which
|
|
70
70
|
* lines the switch up with the `.form-control`
|
|
@@ -147,6 +147,7 @@ defineOptions({
|
|
|
147
147
|
chrome tightens. */
|
|
148
148
|
.dx-switch--sm :deep(.form-check) {
|
|
149
149
|
font-size: 0.875rem;
|
|
150
|
+
border-radius: var(--bs-border-radius-pill, 50rem);
|
|
150
151
|
min-height: auto;
|
|
151
152
|
padding: 0.25rem 0.625rem;
|
|
152
153
|
}
|
|
@@ -168,11 +169,15 @@ defineOptions({
|
|
|
168
169
|
cursor: pointer;
|
|
169
170
|
}
|
|
170
171
|
|
|
171
|
-
/* Filled box (#158): the WHOLE control carries the colour and the pill
|
|
172
|
-
|
|
172
|
+
/* Filled box (#158): the WHOLE control carries the colour and the pill uses a
|
|
173
|
+
muted state tint — the box IS the state, so it reads strongest at a
|
|
173
174
|
glance (the house style). `success` (default) is green ON / light-red OFF;
|
|
174
175
|
`neutral` keeps a brand primary panel for mixed-semantics switches. Pair with
|
|
175
176
|
`textWhenTrue`/`textWhenFalse` so the LABEL also names each state. */
|
|
177
|
+
.dx-switch--success {
|
|
178
|
+
--dx-switch-pill: var(--dx-switch-off-pill);
|
|
179
|
+
}
|
|
180
|
+
|
|
176
181
|
.dx-switch--success :deep(.form-check) {
|
|
177
182
|
background-color: var(--dx-switch-off-bg);
|
|
178
183
|
border-color: var(--dx-switch-off-line);
|
|
@@ -189,12 +194,14 @@ defineOptions({
|
|
|
189
194
|
|
|
190
195
|
.dx-switch--success.dx-switch--on :deep(.form-check-label) {
|
|
191
196
|
color: var(--dx-switch-on-ink);
|
|
192
|
-
font-weight: 500;
|
|
193
197
|
}
|
|
194
198
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
199
|
+
.dx-switch--success.dx-switch--on {
|
|
200
|
+
--dx-switch-pill: var(--dx-switch-on-pill);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* Muted pill with a white sliding thumb: green-grey ON, red-grey OFF.
|
|
204
|
+
The stronger box colour and position continue to communicate the state. Explicit `:checked`/`:focus` selectors so they out-specify the global
|
|
198
205
|
`.form-switch .form-check-input:checked` rule. */
|
|
199
206
|
.dx-switch--success :deep(.form-check-input),
|
|
200
207
|
.dx-switch--success :deep(.form-check-input:checked),
|
|
@@ -217,6 +224,5 @@ defineOptions({
|
|
|
217
224
|
|
|
218
225
|
.dx-switch--neutral.dx-switch--on :deep(.form-check-label) {
|
|
219
226
|
color: var(--bs-primary-text-emphasis);
|
|
220
|
-
font-weight: 500;
|
|
221
227
|
}
|
|
222
228
|
</style>
|