@omnitend/dashboard-for-laravel 0.18.1 → 0.19.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/extended/DXField.vue.d.ts +50 -35
- package/dist/dashboard-for-laravel.js +5627 -5579
- 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/package.json +1 -1
- package/resources/css/theme.scss +46 -1
- package/resources/js/components/base/DAutocomplete.vue +22 -0
- 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 +72 -14
- package/resources/js/components/extended/DXFieldLabel.vue +7 -0
- package/resources/js/components/extended/DXForm.vue +4 -1
package/package.json
CHANGED
package/resources/css/theme.scss
CHANGED
|
@@ -79,13 +79,31 @@ $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
|
+
|
|
94
|
+
// Bigger check/radio boxes (Bootstrap's 1em default reads small next to the
|
|
95
|
+
// theme's inputs and is a fiddly touch target). Drive it through Bootstrap's own
|
|
96
|
+
// `$form-check-input-width` — NOT a raw `.form-check-input { width }` override —
|
|
97
|
+
// so the container's `padding-left` and the input's negative `margin-left` (both
|
|
98
|
+
// derived from `$form-check-padding-start`) track the larger box. Overriding just
|
|
99
|
+
// the CSS width left the padding at `1em + .5em`, which the ~20px box then filled
|
|
100
|
+
// almost entirely, collapsing the box↔label gap to ~1px (cramped/flush). The
|
|
101
|
+
// explicit padding-start keeps a real gap and avoids a rem+em unit mix in
|
|
102
|
+
// Bootstrap's default `$form-check-input-width + .5em`. Switches are unaffected
|
|
103
|
+
// (they use the independent `$form-switch-*` variables).
|
|
104
|
+
$form-check-input-width: 1.25rem;
|
|
105
|
+
$form-check-padding-start: $form-check-input-width + 0.5rem;
|
|
106
|
+
|
|
89
107
|
// ----------------------------------------------------------------------------
|
|
90
108
|
// Cards
|
|
91
109
|
// ----------------------------------------------------------------------------
|
|
@@ -188,6 +206,29 @@ $bootstrap-icons-font-src: url("bootstrap-icons/font/fonts/bootstrap-icons.woff2
|
|
|
188
206
|
);
|
|
189
207
|
}
|
|
190
208
|
|
|
209
|
+
// Horizontal ("label-left") forms: a compact settings-form rhythm, with the
|
|
210
|
+
// label + control top-aligned so a checkbox/switch sits on the label's first
|
|
211
|
+
// line rather than floating above it (the label column carries a top offset to
|
|
212
|
+
// line up with text inputs; the checkbox is given the same offset). Vertical
|
|
213
|
+
// forms are unaffected.
|
|
214
|
+
.dx-form--horizontal {
|
|
215
|
+
.row {
|
|
216
|
+
align-items: flex-start;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.col-form-label {
|
|
220
|
+
padding-top: 0.4rem;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.form-check {
|
|
224
|
+
margin-top: 0.4rem;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.mb-3 {
|
|
228
|
+
margin-bottom: 0.5rem;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
191
232
|
// Bootstrap's `.input-group` defaults to `flex-wrap: wrap`, letting a
|
|
192
233
|
// narrow container (e.g. a compact table cell, a small screen) stack the
|
|
193
234
|
// prepend/append affix onto its own line below the input. That breaks the
|
|
@@ -243,6 +284,10 @@ $bootstrap-icons-font-src: url("bootstrap-icons/font/fonts/bootstrap-icons.woff2
|
|
|
243
284
|
|
|
244
285
|
.dashboard-navbar {
|
|
245
286
|
height: $dashboard-navbar-height;
|
|
287
|
+
// A light grey top bar over the white content panel (higher contrast than a
|
|
288
|
+
// grey-everywhere layout). The navbar markup drops its `bg-white` class so
|
|
289
|
+
// this wins without `!important`.
|
|
290
|
+
background-color: #f8f9fa;
|
|
246
291
|
}
|
|
247
292
|
|
|
248
293
|
// ----------------------------------------------------------------------------
|
|
@@ -68,4 +68,26 @@ defineOptions({
|
|
|
68
68
|
--bs-btn-active-color: var(--bs-body-color);
|
|
69
69
|
flex: 0 0 auto;
|
|
70
70
|
}
|
|
71
|
+
|
|
72
|
+
/*
|
|
73
|
+
The clear affordance renders as Bootstrap's `.btn-close` — a bold, filled ✕
|
|
74
|
+
that reads as a heavy black mark against a light field (and is noisy when
|
|
75
|
+
several autocompletes sit in a row, e.g. a table's column filters). Scale it
|
|
76
|
+
down via `font-size` (btn-close sizes both its box and its background SVG in
|
|
77
|
+
`em`, so this shrinks it proportionally without clipping) and rest it at a
|
|
78
|
+
lower opacity so it's a quiet clear affordance that only asserts on
|
|
79
|
+
hover/focus. Matches the softened trigger above.
|
|
80
|
+
*/
|
|
81
|
+
.d-autocomplete :deep(.b-autocomplete-clear.btn-close) {
|
|
82
|
+
font-size: 0.8em;
|
|
83
|
+
opacity: 0.4;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.d-autocomplete :deep(.b-autocomplete-clear.btn-close:hover) {
|
|
87
|
+
opacity: 0.7;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.d-autocomplete :deep(.b-autocomplete-clear.btn-close:focus) {
|
|
91
|
+
opacity: 0.9;
|
|
92
|
+
}
|
|
71
93
|
</style>
|
|
@@ -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,11 +35,20 @@
|
|
|
35
35
|
:class="field.class || 'mb-3'"
|
|
36
36
|
v-bind="horizontalAttrs"
|
|
37
37
|
>
|
|
38
|
-
<!-- The
|
|
39
|
-
|
|
40
|
-
|
|
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. -->
|
|
41
42
|
<template v-if="!hideLabel" #label>
|
|
42
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>
|
|
43
52
|
</template>
|
|
44
53
|
<slot
|
|
45
54
|
v-if="$slots.value"
|
|
@@ -55,14 +64,14 @@
|
|
|
55
64
|
:disabled="isDisabled || isReadonly"
|
|
56
65
|
v-bind="field.inputProps"
|
|
57
66
|
>
|
|
58
|
-
<DXFieldLabel :label="resolvedLabel" />
|
|
67
|
+
<DXFieldLabel :label="resolvedLabel" class="visually-hidden" />
|
|
59
68
|
</DFormCheckbox>
|
|
60
69
|
|
|
61
70
|
<DFormInvalidFeedback v-if="form.hasError(errorKey)" force-show>
|
|
62
71
|
{{ form.getError(errorKey) }}
|
|
63
72
|
</DFormInvalidFeedback>
|
|
64
73
|
<slot name="info" :field="field" :model="model" />
|
|
65
|
-
<DFormText v-if="resolvedHint || $slots.hint" class="text-muted">
|
|
74
|
+
<DFormText v-if="!hintInLabel && (resolvedHint || $slots.hint)" class="text-muted">
|
|
66
75
|
<slot name="hint" :field="field" :model="model">{{ resolvedHint }}</slot>
|
|
67
76
|
</DFormText>
|
|
68
77
|
<DFormText v-if="field.help">{{ field.help }}</DFormText>
|
|
@@ -122,11 +131,22 @@
|
|
|
122
131
|
:class="[field.class || 'mb-3', 'dx-switch', { 'dx-switch--on': switchIsOn }]"
|
|
123
132
|
v-bind="horizontalAttrs"
|
|
124
133
|
>
|
|
125
|
-
<!-- The
|
|
126
|
-
|
|
127
|
-
|
|
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. -->
|
|
128
140
|
<template v-if="!hideLabel" #label>
|
|
129
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>
|
|
130
150
|
</template>
|
|
131
151
|
<slot
|
|
132
152
|
v-if="$slots.value"
|
|
@@ -143,14 +163,17 @@
|
|
|
143
163
|
:disabled="isDisabled || isReadonly"
|
|
144
164
|
v-bind="field.inputProps"
|
|
145
165
|
>
|
|
146
|
-
<DXFieldLabel
|
|
166
|
+
<DXFieldLabel
|
|
167
|
+
:label="switchText"
|
|
168
|
+
:class="switchHasContextualText ? undefined : 'visually-hidden'"
|
|
169
|
+
/>
|
|
147
170
|
</DFormCheckbox>
|
|
148
171
|
|
|
149
172
|
<DFormInvalidFeedback v-if="form.hasError(errorKey)" force-show>
|
|
150
173
|
{{ form.getError(errorKey) }}
|
|
151
174
|
</DFormInvalidFeedback>
|
|
152
175
|
<slot name="info" :field="field" :model="model" />
|
|
153
|
-
<DFormText v-if="resolvedHint || $slots.hint" class="text-muted">
|
|
176
|
+
<DFormText v-if="!hintInLabel && (resolvedHint || $slots.hint)" class="text-muted">
|
|
154
177
|
<slot name="hint" :field="field" :model="model">{{ resolvedHint }}</slot>
|
|
155
178
|
</DFormText>
|
|
156
179
|
<DFormText v-if="field.help">{{ field.help }}</DFormText>
|
|
@@ -246,9 +269,19 @@
|
|
|
246
269
|
<DFormGroup v-else :class="field.class || 'mb-3'" v-bind="horizontalAttrs">
|
|
247
270
|
<!-- Label with optional info popover. Omitted entirely (not just
|
|
248
271
|
emptied) when hideLabel is set, so BFormGroup doesn't reserve a
|
|
249
|
-
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. -->
|
|
250
275
|
<template v-if="!hideLabel" #label>
|
|
251
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>
|
|
252
285
|
</template>
|
|
253
286
|
|
|
254
287
|
<!-- Custom value slot overrides the built-in control -->
|
|
@@ -435,8 +468,9 @@
|
|
|
435
468
|
-->
|
|
436
469
|
<slot name="info" :field="field" :model="model" />
|
|
437
470
|
|
|
438
|
-
<!-- Hint (dynamic, slot-overridable)
|
|
439
|
-
|
|
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">
|
|
440
474
|
<!--
|
|
441
475
|
@slot Overrides the field's hint text shown below the control.
|
|
442
476
|
@binding {FieldDefinition} field The field definition being rendered.
|
|
@@ -564,10 +598,34 @@ function labelColsAttrs(cols: LabelCols | undefined): Record<string, any> {
|
|
|
564
598
|
// with sibling row labels (#78).
|
|
565
599
|
const horizontalAttrs = computed<Record<string, any>>(() =>
|
|
566
600
|
isHorizontal.value && !props.hideLabel
|
|
567
|
-
?
|
|
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
|
+
}
|
|
568
608
|
: {},
|
|
569
609
|
);
|
|
570
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,
|
|
627
|
+
);
|
|
628
|
+
|
|
571
629
|
// A table-layout repeater is inherently wide — squeezing its own label into a
|
|
572
630
|
// narrow left column (as if it were a single-line input) reads badly, unlike
|
|
573
631
|
// a normal horizontal field. Its label always renders above, regardless of
|
|
@@ -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"
|