@omnitend/dashboard-for-laravel 0.41.0 → 0.41.1
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/extended/DXTable.vue.d.ts +2 -0
- package/dist/dashboard-for-laravel.js +2325 -2322
- package/dist/dashboard-for-laravel.js.map +1 -1
- package/dist/dashboard-for-laravel.umd.cjs +4 -4
- package/dist/dashboard-for-laravel.umd.cjs.map +1 -1
- package/dist/style.css +2 -2
- package/docs/public/api-reference.json +25 -4
- package/docs/public/docs-map.md +1 -1
- package/package.json +1 -1
- package/resources/css/theme.scss +33 -26
- package/resources/js/components/base/DAutocomplete.vue +3 -3
- package/resources/js/components/base/DButton.vue +39 -5
- package/resources/js/components/extended/DXSwitch.vue +10 -7
- package/resources/js/components/extended/DXTable.vue +83 -88
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generated": "2026-
|
|
2
|
+
"generated": "2026-09-27T10:14:02.393Z",
|
|
3
3
|
"package": {
|
|
4
4
|
"name": "@omnitend/dashboard-for-laravel",
|
|
5
|
-
"version": "0.41.
|
|
5
|
+
"version": "0.41.1"
|
|
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",
|
|
@@ -3072,7 +3079,7 @@
|
|
|
3072
3079
|
"name": "DXSwitch",
|
|
3073
3080
|
"category": "extended",
|
|
3074
3081
|
"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
|
|
3082
|
+
"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 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
3083
|
"props": [
|
|
3077
3084
|
{
|
|
3078
3085
|
"name": "modelValue",
|
|
@@ -3596,7 +3603,7 @@
|
|
|
3596
3603
|
},
|
|
3597
3604
|
{
|
|
3598
3605
|
"name": "thead-top",
|
|
3599
|
-
"description": "
|
|
3606
|
+
"description": "Rows above the column headers, such as grouped-column banners or pinned totals. Give it `<tr>`s.",
|
|
3600
3607
|
"bindings": [
|
|
3601
3608
|
{
|
|
3602
3609
|
"title": "binding",
|
|
@@ -3616,6 +3623,20 @@
|
|
|
3616
3623
|
}
|
|
3617
3624
|
]
|
|
3618
3625
|
},
|
|
3626
|
+
{
|
|
3627
|
+
"name": "thead-sub",
|
|
3628
|
+
"description": "",
|
|
3629
|
+
"bindings": [
|
|
3630
|
+
{
|
|
3631
|
+
"name": "field",
|
|
3632
|
+
"title": "binding"
|
|
3633
|
+
},
|
|
3634
|
+
{
|
|
3635
|
+
"name": "v-bind",
|
|
3636
|
+
"title": "binding"
|
|
3637
|
+
}
|
|
3638
|
+
]
|
|
3639
|
+
},
|
|
3619
3640
|
{
|
|
3620
3641
|
"name": "`head-end(${field.key})`",
|
|
3621
3642
|
"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.",
|
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-27T10:14:02.445Z
|
|
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
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,12 @@ $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
|
+
|
|
692
697
|
.table > thead {
|
|
693
698
|
vertical-align: middle;
|
|
694
699
|
}
|
|
@@ -702,13 +707,9 @@ $bootstrap-icons-font-src: url("bootstrap-icons/font/fonts/bootstrap-icons.woff2
|
|
|
702
707
|
// as the header token above), sized and inset with `padding-left` so text
|
|
703
708
|
// clears it.
|
|
704
709
|
//
|
|
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"]) {
|
|
710
|
+
// Select filters keep their chevron without a second icon or extra text inset.
|
|
711
|
+
// Date inputs keep their own picker affordance.
|
|
712
|
+
.dx-table-filter-cell > input.form-control:not([type="date"]) {
|
|
712
713
|
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
714
|
background-repeat: no-repeat;
|
|
714
715
|
background-position: left 0.65rem center;
|
|
@@ -805,7 +806,8 @@ $dx-switch-on-soft: map.get(map.get($dx-variants, "success"), "soft-bg"); // #cd
|
|
|
805
806
|
--dx-switch-off-well: #{color.mix($dx-switch-off, #fff, 10%)};
|
|
806
807
|
--dx-switch-off-line: #{color.mix($dx-switch-off, #fff, 45%)};
|
|
807
808
|
--dx-switch-off-ink: #{color.mix($dx-switch-off, #000, 30%)};
|
|
808
|
-
--dx-switch-pill: var(--bs-gray-400, #ced4da); // neutral pill
|
|
809
|
+
--dx-switch-pill: var(--bs-gray-400, #ced4da); // neutral OFF pill
|
|
810
|
+
--dx-switch-on-pill: #{color.mix(map.get(map.get($dx-variants, "success"), "soft-text"), $gray-400, 50%)};
|
|
809
811
|
}
|
|
810
812
|
|
|
811
813
|
// Optically centre the check/radio/switch box against its label's FIRST line.
|
|
@@ -1046,3 +1048,8 @@ $dx-chart-palette-dark: (#60a5fa, #a3e635, #a78bfa, #2dd4bf, #fb923c, #22d3ee, #
|
|
|
1046
1048
|
margin-left: 0;
|
|
1047
1049
|
}
|
|
1048
1050
|
}
|
|
1051
|
+
|
|
1052
|
+
// Keep the compact filter-cell spacing when BTable supplies the subheader row.
|
|
1053
|
+
thead tr:has(.dx-table-filter-cell) > td {
|
|
1054
|
+
padding: 0.5rem;
|
|
1055
|
+
}
|
|
@@ -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>
|
|
@@ -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 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.
|
|
@@ -168,8 +168,8 @@ defineOptions({
|
|
|
168
168
|
cursor: pointer;
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
-
/* Filled box (#158): the WHOLE control carries the colour and the pill
|
|
172
|
-
|
|
171
|
+
/* Filled box (#158): the WHOLE control carries the colour and the pill uses a
|
|
172
|
+
muted state tint — the box IS the state, so it reads strongest at a
|
|
173
173
|
glance (the house style). `success` (default) is green ON / light-red OFF;
|
|
174
174
|
`neutral` keeps a brand primary panel for mixed-semantics switches. Pair with
|
|
175
175
|
`textWhenTrue`/`textWhenFalse` so the LABEL also names each state. */
|
|
@@ -192,9 +192,12 @@ defineOptions({
|
|
|
192
192
|
font-weight: 500;
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
195
|
+
.dx-switch--success.dx-switch--on {
|
|
196
|
+
--dx-switch-pill: var(--dx-switch-on-pill);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* Muted pill with a white sliding thumb: green-grey ON, neutral grey OFF.
|
|
200
|
+
The stronger box colour and position continue to communicate the state. Explicit `:checked`/`:focus` selectors so they out-specify the global
|
|
198
201
|
`.form-switch .form-check-input:checked` rule. */
|
|
199
202
|
.dx-switch--success :deep(.form-check-input),
|
|
200
203
|
.dx-switch--success :deep(.form-check-input:checked),
|
|
@@ -72,8 +72,7 @@
|
|
|
72
72
|
supplied their own `table-colgroup` (forwarded generically) —
|
|
73
73
|
so a table with no widths gets no colgroup and renders exactly
|
|
74
74
|
as before. `<col>` widths are authoritative under
|
|
75
|
-
`table-layout: fixed`,
|
|
76
|
-
is the width-determining first row. -->
|
|
75
|
+
`table-layout: fixed`, regardless of any consumer header banners. -->
|
|
77
76
|
<template
|
|
78
77
|
v-if="fieldsHaveWidths && !$slots['table-colgroup']"
|
|
79
78
|
#table-colgroup
|
|
@@ -85,92 +84,90 @@
|
|
|
85
84
|
/>
|
|
86
85
|
</template>
|
|
87
86
|
|
|
88
|
-
|
|
89
|
-
consumer's own thead-top content is COMPOSED above it rather
|
|
90
|
-
than being dropped: a grouped column-header banner or a pinned
|
|
91
|
-
totals row sits above the headers where it belongs (#120). -->
|
|
92
|
-
<template v-if="hasFilters || $slots['thead-top']" #thead-top="theadScope">
|
|
87
|
+
<template v-if="$slots['thead-top']" #thead-top="theadScope">
|
|
93
88
|
<!--
|
|
94
|
-
@slot
|
|
89
|
+
@slot Rows above the column headers, such as grouped-column banners or pinned totals. Give it `<tr>`s.
|
|
95
90
|
@binding {object} columns The number of columns in the table.
|
|
96
91
|
@binding {object} fields The table's fields.
|
|
97
92
|
-->
|
|
98
93
|
<slot name="thead-top" v-bind="theadScope" />
|
|
94
|
+
</template>
|
|
99
95
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
96
|
+
<!-- The table supplies the below-header row and one cell per field. -->
|
|
97
|
+
<template v-if="hasFilters || $slots['thead-sub']" #thead-sub="{ field, ...filterScope }">
|
|
98
|
+
<div v-if="hasFilters" class="dx-table-filter-cell">
|
|
99
|
+
<!-- Text Filter -->
|
|
100
|
+
<DFormInput
|
|
101
|
+
v-if="field.filter === 'text'"
|
|
102
|
+
:model-value="effectiveFilters[filterKeyFor(field)] || ''"
|
|
103
|
+
:placeholder="field.filterPlaceholder || `Search ${field.label || field.key}...`"
|
|
104
|
+
size="sm"
|
|
105
|
+
@update:model-value="handleFilterChange(filterKeyFor(field), $event as string)"
|
|
106
|
+
/>
|
|
107
|
+
|
|
108
|
+
<!-- Multi-value Select Filter (#51): several values at once; the filters entry is an array -->
|
|
109
|
+
<DAutocomplete
|
|
110
|
+
v-else-if="field.filter === 'select' && field.filterMultiple"
|
|
111
|
+
:model-value="multiFilterValueFor(field)"
|
|
112
|
+
:options="getFieldFilterOptions(field)"
|
|
113
|
+
:placeholder="field.filterPlaceholder || filterAllLabelFor(field)"
|
|
114
|
+
size="sm"
|
|
115
|
+
multiple
|
|
116
|
+
open-on-focus
|
|
117
|
+
@update:model-value="handleMultiSelectFilterChange(field, $event)"
|
|
118
|
+
/>
|
|
119
|
+
|
|
120
|
+
<!-- Select Filter: typeahead — browse the full list on focus, or type to narrow; clear (✕) resets to "no filter" -->
|
|
121
|
+
<DAutocomplete
|
|
122
|
+
v-else-if="field.filter === 'select'"
|
|
123
|
+
:model-value="(effectiveFilters[filterKeyFor(field)] as string) || ''"
|
|
124
|
+
:options="getFieldFilterOptions(field)"
|
|
125
|
+
:placeholder="field.filterPlaceholder || filterAllLabelFor(field)"
|
|
126
|
+
size="sm"
|
|
127
|
+
open-on-focus
|
|
128
|
+
@update:model-value="handleSelectFilterChange(field, $event)"
|
|
129
|
+
/>
|
|
130
|
+
|
|
131
|
+
<!-- Native Select Filter (S3b): a plain <select> for consumers who
|
|
132
|
+
want OS-native behaviour (full-height menu, native keyboard/scroll)
|
|
133
|
+
instead of the DAutocomplete typeahead. Shares the select filter's
|
|
134
|
+
option list (getFieldFilterOptions) and change handler
|
|
135
|
+
(handleSelectFilterChange) EXACTLY — so it behaves identically to
|
|
136
|
+
filter:"select" in every way but the control. Single-select only:
|
|
137
|
+
`filterMultiple` is ignored here. The model maps "no filter" to the
|
|
138
|
+
FILTER_ALL_VALUE sentinel (see nativeSelectFilterValue) so the "All …"
|
|
139
|
+
option shows selected in a native control. -->
|
|
140
|
+
<DFormSelect
|
|
141
|
+
v-else-if="field.filter === 'select-native'"
|
|
142
|
+
:model-value="nativeSelectFilterValue(field)"
|
|
143
|
+
:options="getFieldFilterOptions(field) as unknown as Record<string, unknown>[]"
|
|
144
|
+
size="sm"
|
|
145
|
+
@update:model-value="handleSelectFilterChange(field, $event)"
|
|
146
|
+
/>
|
|
147
|
+
|
|
148
|
+
<!-- Number Filter -->
|
|
149
|
+
<DFormInput
|
|
150
|
+
v-else-if="field.filter === 'number'"
|
|
151
|
+
:model-value="effectiveFilters[filterKeyFor(field)] || ''"
|
|
152
|
+
:placeholder="field.filterPlaceholder || `Filter ${field.label || field.key}...`"
|
|
153
|
+
type="number"
|
|
154
|
+
size="sm"
|
|
155
|
+
@update:model-value="handleFilterChange(filterKeyFor(field), $event as string)"
|
|
156
|
+
/>
|
|
157
|
+
|
|
158
|
+
<!-- Date Filter -->
|
|
159
|
+
<DFormInput
|
|
160
|
+
v-else-if="field.filter === 'date'"
|
|
161
|
+
:model-value="effectiveFilters[filterKeyFor(field)] || ''"
|
|
162
|
+
type="date"
|
|
163
|
+
size="sm"
|
|
164
|
+
@update:model-value="handleFilterChange(filterKeyFor(field), $event as string)"
|
|
165
|
+
/>
|
|
169
166
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
167
|
+
<!-- No filter for this column -->
|
|
168
|
+
<div v-else></div>
|
|
169
|
+
</div>
|
|
170
|
+
<slot name="thead-sub" v-bind="{ ...filterScope, field }" />
|
|
174
171
|
</template>
|
|
175
172
|
|
|
176
173
|
<!-- Custom headers for all fields -->
|
|
@@ -1318,9 +1315,8 @@ const dottedCellFields = (slots: Record<string, unknown>): TableField[] =>
|
|
|
1318
1315
|
/*
|
|
1319
1316
|
* Per-column widths (#156). A field's `width`/`minWidth` is applied through a
|
|
1320
1317
|
* `<colgroup>` `<col>`, NOT the header `<th>`: with `table-layout: fixed` the
|
|
1321
|
-
* column widths are taken from the FIRST row of cells,
|
|
1322
|
-
*
|
|
1323
|
-
* would be ignored whenever filters are present. A `<col>` width is
|
|
1318
|
+
* column widths are taken from the FIRST row of cells, which may be a
|
|
1319
|
+
* consumer banner rather than the column labels. A `<col>` width is
|
|
1324
1320
|
* authoritative regardless of row order, in both fixed and auto layout.
|
|
1325
1321
|
*/
|
|
1326
1322
|
const sizeToCss = (value: string | number): string =>
|
|
@@ -2262,7 +2258,7 @@ const refresh = () => {
|
|
|
2262
2258
|
*
|
|
2263
2259
|
* - `head(<key>)` — DXTable draws its own column headers (sort indicators,
|
|
2264
2260
|
* field hints). Forwarding a consumer's would silently drop those.
|
|
2265
|
-
* - `thead-top`
|
|
2261
|
+
* - `thead-top` / `thead-sub` — composed with the below-header filter cells.
|
|
2266
2262
|
*
|
|
2267
2263
|
* `head-end(<key>)` is DXTable's OWN additive header slot (#99) and is likewise
|
|
2268
2264
|
* not forwarded: the inner table has no such slot, and the prefixes below are
|
|
@@ -2312,11 +2308,10 @@ const isTableSlot = (name: string) =>
|
|
|
2312
2308
|
* invisible (#114). Called from the template, it re-evaluates every render.
|
|
2313
2309
|
*
|
|
2314
2310
|
* It also has to EXCLUDE the slots DXTable renders itself: declaring `#thead-top`
|
|
2315
|
-
*
|
|
2316
|
-
* template, silently dropping the filter row and the consumer's banner (#120).
|
|
2311
|
+
* or `#thead-sub` here would override the composed header/filter slots.
|
|
2317
2312
|
*/
|
|
2318
2313
|
const forwardableSlotNames = (slots: Record<string, unknown>): string[] =>
|
|
2319
|
-
Object.keys(slots).filter((name) => isTableSlot(name));
|
|
2314
|
+
Object.keys(slots).filter((name) => name !== 'thead-sub' && isTableSlot(name));
|
|
2320
2315
|
|
|
2321
2316
|
const tableSlotSignature = (slots: Record<string, unknown>): string => {
|
|
2322
2317
|
// DXTable's OWN dotted-cell slots count too (#121). bvn captures its cell
|