@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnitend/dashboard-for-laravel",
3
- "version": "0.18.1",
3
+ "version": "0.19.1",
4
4
  "description": "Vue 3 dashboard components for Laravel with Bootstrap Vue Next",
5
5
  "type": "module",
6
6
  "main": "./dist/dashboard-for-laravel.umd.cjs",
@@ -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 bg-white border-bottom">
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 inner label (beside the checkbox) always names the control, so
39
- hideLabel drops only the redundant outer row label; horizontalAttrs
40
- then reserves no label column, letting the control span full width. -->
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 switch renders its own label inside the control, so hideLabel
126
- drops only the redundant outer row label; horizontalAttrs then
127
- reserves no label column, letting the toggle span full width. -->
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 :label="switchText" />
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
- <DFormText v-if="resolvedHint || $slots.hint" class="text-muted">
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
- ? labelColsAttrs(props.labelCols ?? 3)
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 @submit.prevent="handleSubmit">
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"