@omnitend/dashboard-for-laravel 0.18.0 → 0.19.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/DXField.vue.d.ts +58 -39
- package/dist/dashboard-for-laravel.js +5751 -5686
- 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/style.css +2 -2
- package/dist/types/index.d.ts +10 -0
- package/package.json +1 -1
- package/resources/css/theme.scss +40 -1
- package/resources/js/components/extended/DXDashboard.vue +3 -0
- package/resources/js/components/extended/DXDashboardNavbar.vue +1 -1
- package/resources/js/components/extended/DXField.vue +97 -18
- package/resources/js/components/extended/DXFieldLabel.vue +7 -0
- package/resources/js/components/extended/DXForm.vue +6 -1
- package/resources/js/types/index.ts +11 -0
package/dist/types/index.d.ts
CHANGED
|
@@ -157,6 +157,16 @@ export interface FieldDefinition {
|
|
|
157
157
|
* layout. Falls back to the form-level `labelCols` when omitted.
|
|
158
158
|
*/
|
|
159
159
|
labelCols?: LabelCols;
|
|
160
|
+
/**
|
|
161
|
+
* Skip rendering the field's own label. In horizontal layout no label
|
|
162
|
+
* column is reserved either, so the control spans the full width. Useful
|
|
163
|
+
* for `switch` (and `checkbox`) fields, which render their own label
|
|
164
|
+
* inside the control — the outer row label would otherwise duplicate it,
|
|
165
|
+
* and the self-label then lines up with sibling row labels. Also used
|
|
166
|
+
* internally by `DXRepeater`'s `table` layout, where a column header
|
|
167
|
+
* already names the field.
|
|
168
|
+
*/
|
|
169
|
+
hideLabel?: boolean;
|
|
160
170
|
/**
|
|
161
171
|
* Component rendered for `type: "component"` fields. Receives
|
|
162
172
|
* `modelValue`, `field`, `model` props and emits `update:modelValue`.
|
package/package.json
CHANGED
package/resources/css/theme.scss
CHANGED
|
@@ -79,13 +79,18 @@ $btn-padding-x-lg: 1.5rem; // 24px
|
|
|
79
79
|
// Forms
|
|
80
80
|
// ----------------------------------------------------------------------------
|
|
81
81
|
|
|
82
|
-
$input-padding-y: 0.625rem
|
|
82
|
+
$input-padding-y: 0.375rem; // compact fields (Bootstrap's default; 0.625rem read oversized)
|
|
83
83
|
$input-padding-x: 1rem;
|
|
84
84
|
$input-border-radius: $border-radius;
|
|
85
85
|
$input-border-color: #d1d5db; // Gray-300
|
|
86
86
|
$input-focus-border-color: $primary;
|
|
87
87
|
$input-focus-box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
|
|
88
88
|
|
|
89
|
+
// Check/radio inputs default their border to `$border-color` (Gray-200), not
|
|
90
|
+
// `$input-border-color` — so a bare checkbox looks faint beside the (darker,
|
|
91
|
+
// Gray-300) text inputs. Share the input border colour across all controls.
|
|
92
|
+
$form-check-input-border: 1px solid $input-border-color;
|
|
93
|
+
|
|
89
94
|
// ----------------------------------------------------------------------------
|
|
90
95
|
// Cards
|
|
91
96
|
// ----------------------------------------------------------------------------
|
|
@@ -188,6 +193,36 @@ $bootstrap-icons-font-src: url("bootstrap-icons/font/fonts/bootstrap-icons.woff2
|
|
|
188
193
|
);
|
|
189
194
|
}
|
|
190
195
|
|
|
196
|
+
// Bigger check/radio boxes — Bootstrap's 1em default reads small next to the
|
|
197
|
+
// theme's inputs and is a fiddly touch target.
|
|
198
|
+
.form-check-input {
|
|
199
|
+
width: 1.25rem;
|
|
200
|
+
height: 1.25rem;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Horizontal ("label-left") forms: a compact settings-form rhythm, with the
|
|
204
|
+
// label + control top-aligned so a checkbox/switch sits on the label's first
|
|
205
|
+
// line rather than floating above it (the label column carries a top offset to
|
|
206
|
+
// line up with text inputs; the checkbox is given the same offset). Vertical
|
|
207
|
+
// forms are unaffected.
|
|
208
|
+
.dx-form--horizontal {
|
|
209
|
+
.row {
|
|
210
|
+
align-items: flex-start;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.col-form-label {
|
|
214
|
+
padding-top: 0.4rem;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.form-check {
|
|
218
|
+
margin-top: 0.4rem;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.mb-3 {
|
|
222
|
+
margin-bottom: 0.5rem;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
191
226
|
// Bootstrap's `.input-group` defaults to `flex-wrap: wrap`, letting a
|
|
192
227
|
// narrow container (e.g. a compact table cell, a small screen) stack the
|
|
193
228
|
// prepend/append affix onto its own line below the input. That breaks the
|
|
@@ -243,6 +278,10 @@ $bootstrap-icons-font-src: url("bootstrap-icons/font/fonts/bootstrap-icons.woff2
|
|
|
243
278
|
|
|
244
279
|
.dashboard-navbar {
|
|
245
280
|
height: $dashboard-navbar-height;
|
|
281
|
+
// A light grey top bar over the white content panel (higher contrast than a
|
|
282
|
+
// grey-everywhere layout). The navbar markup drops its `bg-white` class so
|
|
283
|
+
// this wins without `!important`.
|
|
284
|
+
background-color: #f8f9fa;
|
|
246
285
|
}
|
|
247
286
|
|
|
248
287
|
// ----------------------------------------------------------------------------
|
|
@@ -247,5 +247,8 @@ const toggleSidebar = () => {
|
|
|
247
247
|
.dashboard-main {
|
|
248
248
|
flex: 1;
|
|
249
249
|
max-width: 100%;
|
|
250
|
+
/* A white content panel under the light grey top bar / layout, so forms and
|
|
251
|
+
content sit on white (higher contrast than a grey-everywhere dashboard). */
|
|
252
|
+
background-color: #fff;
|
|
250
253
|
}
|
|
251
254
|
</style>
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
`DXDashboard`, which forwards these slots with a `navbar-` prefix.
|
|
6
6
|
-->
|
|
7
7
|
<template>
|
|
8
|
-
<header class="dashboard-navbar
|
|
8
|
+
<header class="dashboard-navbar border-bottom">
|
|
9
9
|
<DContainer fluid>
|
|
10
10
|
<!-- Flex bar that wraps: below `md` the search drops to its own
|
|
11
11
|
full-width row beneath the toggle/title/user-menu row. -->
|
|
@@ -35,8 +35,20 @@
|
|
|
35
35
|
:class="field.class || 'mb-3'"
|
|
36
36
|
v-bind="horizontalAttrs"
|
|
37
37
|
>
|
|
38
|
-
|
|
38
|
+
<!-- The row label (left column) names the control; the checkbox's own
|
|
39
|
+
label is kept for screen readers but hidden visually so it doesn't
|
|
40
|
+
duplicate the row label. In horizontal layout the hint sits beneath
|
|
41
|
+
the label here rather than below the control. -->
|
|
42
|
+
<template v-if="!hideLabel" #label>
|
|
39
43
|
<DXFieldLabel :label="resolvedLabel" :info="resolvedInfo" />
|
|
44
|
+
<small
|
|
45
|
+
v-if="isHorizontal && (resolvedHint || $slots.hint)"
|
|
46
|
+
class="form-text text-muted d-block dx-field-hint"
|
|
47
|
+
>
|
|
48
|
+
<slot name="hint" :field="field" :model="model">{{
|
|
49
|
+
resolvedHint
|
|
50
|
+
}}</slot>
|
|
51
|
+
</small>
|
|
40
52
|
</template>
|
|
41
53
|
<slot
|
|
42
54
|
v-if="$slots.value"
|
|
@@ -52,14 +64,14 @@
|
|
|
52
64
|
:disabled="isDisabled || isReadonly"
|
|
53
65
|
v-bind="field.inputProps"
|
|
54
66
|
>
|
|
55
|
-
<DXFieldLabel :label="resolvedLabel" />
|
|
67
|
+
<DXFieldLabel :label="resolvedLabel" class="visually-hidden" />
|
|
56
68
|
</DFormCheckbox>
|
|
57
69
|
|
|
58
70
|
<DFormInvalidFeedback v-if="form.hasError(errorKey)" force-show>
|
|
59
71
|
{{ form.getError(errorKey) }}
|
|
60
72
|
</DFormInvalidFeedback>
|
|
61
73
|
<slot name="info" :field="field" :model="model" />
|
|
62
|
-
<DFormText v-if="resolvedHint || $slots.hint" class="text-muted">
|
|
74
|
+
<DFormText v-if="!hintInLabel && (resolvedHint || $slots.hint)" class="text-muted">
|
|
63
75
|
<slot name="hint" :field="field" :model="model">{{ resolvedHint }}</slot>
|
|
64
76
|
</DFormText>
|
|
65
77
|
<DFormText v-if="field.help">{{ field.help }}</DFormText>
|
|
@@ -119,8 +131,22 @@
|
|
|
119
131
|
:class="[field.class || 'mb-3', 'dx-switch', { 'dx-switch--on': switchIsOn }]"
|
|
120
132
|
v-bind="horizontalAttrs"
|
|
121
133
|
>
|
|
122
|
-
|
|
134
|
+
<!-- The row label (left column) names the control. The switch's own
|
|
135
|
+
inner text is shown only when the field opts into contextual on/off
|
|
136
|
+
text (e.g. "Product is current"); otherwise it falls back to the row
|
|
137
|
+
label and is hidden visually (kept for screen readers). In horizontal
|
|
138
|
+
layout the hint sits beneath the label here rather than below the
|
|
139
|
+
control. -->
|
|
140
|
+
<template v-if="!hideLabel" #label>
|
|
123
141
|
<DXFieldLabel :label="resolvedLabel" :info="resolvedInfo" />
|
|
142
|
+
<small
|
|
143
|
+
v-if="isHorizontal && (resolvedHint || $slots.hint)"
|
|
144
|
+
class="form-text text-muted d-block dx-field-hint"
|
|
145
|
+
>
|
|
146
|
+
<slot name="hint" :field="field" :model="model">{{
|
|
147
|
+
resolvedHint
|
|
148
|
+
}}</slot>
|
|
149
|
+
</small>
|
|
124
150
|
</template>
|
|
125
151
|
<slot
|
|
126
152
|
v-if="$slots.value"
|
|
@@ -137,14 +163,17 @@
|
|
|
137
163
|
:disabled="isDisabled || isReadonly"
|
|
138
164
|
v-bind="field.inputProps"
|
|
139
165
|
>
|
|
140
|
-
<DXFieldLabel
|
|
166
|
+
<DXFieldLabel
|
|
167
|
+
:label="switchText"
|
|
168
|
+
:class="switchHasContextualText ? undefined : 'visually-hidden'"
|
|
169
|
+
/>
|
|
141
170
|
</DFormCheckbox>
|
|
142
171
|
|
|
143
172
|
<DFormInvalidFeedback v-if="form.hasError(errorKey)" force-show>
|
|
144
173
|
{{ form.getError(errorKey) }}
|
|
145
174
|
</DFormInvalidFeedback>
|
|
146
175
|
<slot name="info" :field="field" :model="model" />
|
|
147
|
-
<DFormText v-if="resolvedHint || $slots.hint" class="text-muted">
|
|
176
|
+
<DFormText v-if="!hintInLabel && (resolvedHint || $slots.hint)" class="text-muted">
|
|
148
177
|
<slot name="hint" :field="field" :model="model">{{ resolvedHint }}</slot>
|
|
149
178
|
</DFormText>
|
|
150
179
|
<DFormText v-if="field.help">{{ field.help }}</DFormText>
|
|
@@ -203,7 +232,7 @@
|
|
|
203
232
|
<!-- Repeater: nested, repeatable sub-form -->
|
|
204
233
|
<div v-else-if="field.type === 'repeater'" :class="field.class || 'mb-3'">
|
|
205
234
|
<DFormGroup v-bind="repeaterHorizontalAttrs">
|
|
206
|
-
<template #label>
|
|
235
|
+
<template v-if="!hideLabel" #label>
|
|
207
236
|
<DXFieldLabel :label="resolvedLabel" :info="resolvedInfo" />
|
|
208
237
|
</template>
|
|
209
238
|
<DXRepeater
|
|
@@ -240,9 +269,19 @@
|
|
|
240
269
|
<DFormGroup v-else :class="field.class || 'mb-3'" v-bind="horizontalAttrs">
|
|
241
270
|
<!-- Label with optional info popover. Omitted entirely (not just
|
|
242
271
|
emptied) when hideLabel is set, so BFormGroup doesn't reserve a
|
|
243
|
-
label column/row for it (see DXRepeater's table layout).
|
|
272
|
+
label column/row for it (see DXRepeater's table layout). In
|
|
273
|
+
horizontal layout the hint sits beneath the label here rather than
|
|
274
|
+
below the control. -->
|
|
244
275
|
<template v-if="!hideLabel" #label>
|
|
245
276
|
<DXFieldLabel :label="resolvedLabel" :info="resolvedInfo" />
|
|
277
|
+
<small
|
|
278
|
+
v-if="isHorizontal && (resolvedHint || $slots.hint)"
|
|
279
|
+
class="form-text text-muted d-block dx-field-hint"
|
|
280
|
+
>
|
|
281
|
+
<slot name="hint" :field="field" :model="model">{{
|
|
282
|
+
resolvedHint
|
|
283
|
+
}}</slot>
|
|
284
|
+
</small>
|
|
246
285
|
</template>
|
|
247
286
|
|
|
248
287
|
<!-- Custom value slot overrides the built-in control -->
|
|
@@ -429,8 +468,9 @@
|
|
|
429
468
|
-->
|
|
430
469
|
<slot name="info" :field="field" :model="model" />
|
|
431
470
|
|
|
432
|
-
<!-- Hint (dynamic, slot-overridable)
|
|
433
|
-
|
|
471
|
+
<!-- Hint (dynamic, slot-overridable). In horizontal layout it renders in
|
|
472
|
+
the label column instead (see the #label slot above). -->
|
|
473
|
+
<DFormText v-if="!hintInLabel && (resolvedHint || $slots.hint)" class="text-muted">
|
|
434
474
|
<!--
|
|
435
475
|
@slot Overrides the field's hint text shown below the control.
|
|
436
476
|
@binding {FieldDefinition} field The field definition being rendered.
|
|
@@ -503,10 +543,14 @@ interface Props {
|
|
|
503
543
|
labelCols?: LabelCols;
|
|
504
544
|
|
|
505
545
|
/**
|
|
506
|
-
* Skip rendering the field's own label
|
|
507
|
-
*
|
|
508
|
-
*
|
|
509
|
-
*
|
|
546
|
+
* Skip rendering the field's own (outer) label. Applies to the standard
|
|
547
|
+
* labelled-field branch and to the checkbox/switch/repeater branches. In
|
|
548
|
+
* horizontal layout no label column is reserved either, so the control
|
|
549
|
+
* spans the full width (a switch/checkbox self-label then lines up with
|
|
550
|
+
* sibling row labels). Used by DXRepeater's `table` layout (a column
|
|
551
|
+
* header already names the field) and by consumers hiding the redundant
|
|
552
|
+
* row label on a switch/checkbox, which render their own label inside the
|
|
553
|
+
* control (#78).
|
|
510
554
|
*/
|
|
511
555
|
hideLabel?: boolean;
|
|
512
556
|
}
|
|
@@ -545,9 +589,41 @@ function labelColsAttrs(cols: LabelCols | undefined): Record<string, any> {
|
|
|
545
589
|
}
|
|
546
590
|
|
|
547
591
|
// Default to a 3-column label when horizontal but no width was configured,
|
|
548
|
-
// so `layout: "horizontal"` alone is enough to see the effect.
|
|
592
|
+
// so `layout: "horizontal"` alone is enough to see the effect. When the
|
|
593
|
+
// field's label is hidden, reserve no label column at all — BFormGroup would
|
|
594
|
+
// otherwise still emit an empty label column (it goes horizontal whenever any
|
|
595
|
+
// labelCols are set, regardless of whether a label exists), leaving the
|
|
596
|
+
// control indented in the input column beside dead space. Dropping the column
|
|
597
|
+
// lets the control span full width, so a switch/checkbox self-label lines up
|
|
598
|
+
// with sibling row labels (#78).
|
|
549
599
|
const horizontalAttrs = computed<Record<string, any>>(() =>
|
|
550
|
-
isHorizontal.value
|
|
600
|
+
isHorizontal.value && !props.hideLabel
|
|
601
|
+
? {
|
|
602
|
+
...labelColsAttrs(props.labelCols ?? 3),
|
|
603
|
+
// Right-align the label column so the label (and the hint that
|
|
604
|
+
// sits beneath it in horizontal layout) line up against the
|
|
605
|
+
// control column, like a classic label-left settings form.
|
|
606
|
+
labelClass: "text-sm-end dx-field-label-col",
|
|
607
|
+
}
|
|
608
|
+
: {},
|
|
609
|
+
);
|
|
610
|
+
|
|
611
|
+
// In horizontal layout the hint moves up into the label column (beneath the
|
|
612
|
+
// label), rather than below the control — so a row reads "label + hint | control".
|
|
613
|
+
// Vertical layout (and a hidden label, which reserves no label column) keeps the
|
|
614
|
+
// hint below the control.
|
|
615
|
+
const hintInLabel = computed(() => isHorizontal.value && !props.hideLabel);
|
|
616
|
+
|
|
617
|
+
// A `switch` renders visible text inside the control only when the field opts
|
|
618
|
+
// into contextual on/off text (textWhenTrue/textWhenFalse) — e.g. "Product is
|
|
619
|
+
// current". Without it, `switchText` falls back to the row label, which would
|
|
620
|
+
// duplicate the outer label, so the inner label is kept for screen readers but
|
|
621
|
+
// hidden visually (the row label names the control). A plain `checkbox` never
|
|
622
|
+
// shows visible inner text for the same reason.
|
|
623
|
+
const switchHasContextualText = computed(
|
|
624
|
+
() =>
|
|
625
|
+
props.field.textWhenTrue !== undefined ||
|
|
626
|
+
props.field.textWhenFalse !== undefined,
|
|
551
627
|
);
|
|
552
628
|
|
|
553
629
|
// A table-layout repeater is inherently wide — squeezing its own label into a
|
|
@@ -846,9 +922,12 @@ onBeforeUnmount(() => {
|
|
|
846
922
|
`justify-content: space-between` on a full-width box used to leave a wide
|
|
847
923
|
unclickable gap between the label and the toggle (only the tiny toggle
|
|
848
924
|
itself was a real click target); shrinking the box to fit its content
|
|
849
|
-
removes that dead zone entirely.
|
|
925
|
+
removes that dead zone entirely. Uses block-level `flex` (not `inline-flex`)
|
|
926
|
+
so the box stays shrink-to-fit yet still takes its own line: an inline-level
|
|
927
|
+
box let a following `<small class="form-text">` hint flow inline beside it
|
|
928
|
+
instead of dropping below the control like every other field type (#79). */
|
|
850
929
|
.dx-switch :deep(.form-check) {
|
|
851
|
-
display:
|
|
930
|
+
display: flex;
|
|
852
931
|
align-items: center;
|
|
853
932
|
gap: 0.75rem;
|
|
854
933
|
width: fit-content;
|
|
@@ -53,6 +53,13 @@ const infoId = `dx-field-info-${useId()}`;
|
|
|
53
53
|
gap: 0.35rem;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
/* Soften the label from the near-black body colour so it doesn't dominate the
|
|
57
|
+
row — but keep it a step darker than the (lighter, secondary-grey) hint so the
|
|
58
|
+
label still leads. */
|
|
59
|
+
.dx-field-label__text {
|
|
60
|
+
color: #495057; /* gray-700 */
|
|
61
|
+
}
|
|
62
|
+
|
|
56
63
|
.dx-field-label__info {
|
|
57
64
|
display: inline-flex;
|
|
58
65
|
align-items: center;
|
|
@@ -12,7 +12,10 @@
|
|
|
12
12
|
(no Inertia required).
|
|
13
13
|
-->
|
|
14
14
|
<template>
|
|
15
|
-
<BForm
|
|
15
|
+
<BForm
|
|
16
|
+
@submit.prevent="handleSubmit"
|
|
17
|
+
:class="{ 'dx-form--horizontal': layout === 'horizontal' }"
|
|
18
|
+
>
|
|
16
19
|
<!-- Form-level error message -->
|
|
17
20
|
<DAlert
|
|
18
21
|
v-if="resolvedForm.shouldShowMessage"
|
|
@@ -88,6 +91,7 @@
|
|
|
88
91
|
:model="model"
|
|
89
92
|
:layout="field.layout ?? layout"
|
|
90
93
|
:label-cols="field.labelCols ?? labelCols"
|
|
94
|
+
:hide-label="field.hideLabel"
|
|
91
95
|
>
|
|
92
96
|
<template
|
|
93
97
|
v-for="(slotName, target) in fieldSlotMap(field.key)"
|
|
@@ -151,6 +155,7 @@
|
|
|
151
155
|
:model="model"
|
|
152
156
|
:layout="field.layout ?? layout"
|
|
153
157
|
:label-cols="field.labelCols ?? labelCols"
|
|
158
|
+
:hide-label="field.hideLabel"
|
|
154
159
|
>
|
|
155
160
|
<template
|
|
156
161
|
v-for="(slotName, target) in fieldSlotMap(field.key)"
|
|
@@ -207,6 +207,17 @@ export interface FieldDefinition {
|
|
|
207
207
|
*/
|
|
208
208
|
labelCols?: LabelCols;
|
|
209
209
|
|
|
210
|
+
/**
|
|
211
|
+
* Skip rendering the field's own label. In horizontal layout no label
|
|
212
|
+
* column is reserved either, so the control spans the full width. Useful
|
|
213
|
+
* for `switch` (and `checkbox`) fields, which render their own label
|
|
214
|
+
* inside the control — the outer row label would otherwise duplicate it,
|
|
215
|
+
* and the self-label then lines up with sibling row labels. Also used
|
|
216
|
+
* internally by `DXRepeater`'s `table` layout, where a column header
|
|
217
|
+
* already names the field.
|
|
218
|
+
*/
|
|
219
|
+
hideLabel?: boolean;
|
|
220
|
+
|
|
210
221
|
/**
|
|
211
222
|
* Component rendered for `type: "component"` fields. Receives
|
|
212
223
|
* `modelValue`, `field`, `model` props and emits `update:modelValue`.
|