@omnitend/dashboard-for-laravel 0.16.2 → 0.16.4
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 +4 -4
- package/dist/composables/useToast.d.ts +2 -1
- package/dist/dashboard-for-laravel.js +7495 -7435
- 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/style.css +1 -1
- package/dist/types/index.d.ts +8 -0
- package/docs/public/api-reference.json +2 -2
- package/docs/public/docs-map.md +1 -1
- package/package.json +1 -1
- package/resources/css/theme.scss +36 -13
- package/resources/js/components/extended/DXField.vue +82 -9
- package/resources/js/composables/useToast.ts +53 -1
- package/resources/js/types/index.ts +9 -0
package/dist/types/index.d.ts
CHANGED
|
@@ -75,6 +75,14 @@ export interface FieldDefinition {
|
|
|
75
75
|
max?: number | string;
|
|
76
76
|
/** Symbol shown for `currency` fields (default: the locale's, "£"). */
|
|
77
77
|
currencySymbol?: string;
|
|
78
|
+
/**
|
|
79
|
+
* For `currency` fields: minor-unit decimal places the *display* value is
|
|
80
|
+
* formatted to on blur and on initial seed (default: 2, e.g. GBP/EUR/USD —
|
|
81
|
+
* pass 0 for a currency with no minor unit, e.g. JPY). The model stays a
|
|
82
|
+
* plain number throughout; only the input's shown text is padded (`3.8` →
|
|
83
|
+
* `3.80`). Typing is never reformatted mid-edit.
|
|
84
|
+
*/
|
|
85
|
+
decimals?: number;
|
|
78
86
|
/**
|
|
79
87
|
* For `percentage` fields: treat the underlying model value as a 0–1
|
|
80
88
|
* fraction while showing/editing it as a 0–100 percentage. The model keeps
|
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-07-
|
|
4
|
+
> Last updated: 2026-07-06T20:01:04.293Z
|
|
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
|
@@ -152,6 +152,25 @@ $dashboard-navbar-height: 64px;
|
|
|
152
152
|
|
|
153
153
|
@import "bootstrap-vue-next/dist/bootstrap-vue-next.css";
|
|
154
154
|
|
|
155
|
+
// ----------------------------------------------------------------------------
|
|
156
|
+
// Forms
|
|
157
|
+
// ----------------------------------------------------------------------------
|
|
158
|
+
// Bootstrap's compiled `.form-control`/`.form-select` height isn't exposed as
|
|
159
|
+
// a CSS custom property, so a component that needs to match input height
|
|
160
|
+
// exactly (e.g. DXField's `switch` box, which isn't itself a `.form-control`)
|
|
161
|
+
// would otherwise have to hardcode the same padding-y/line-height/border-width
|
|
162
|
+
// formula a second time — and silently drift from this theme if those
|
|
163
|
+
// variables ever change here. Compute it once, from the real theme variables,
|
|
164
|
+
// and expose it as a var so such components can reference the single source
|
|
165
|
+
// of truth instead of duplicating the formula.
|
|
166
|
+
:root {
|
|
167
|
+
// $input-border-width resolves to a CSS var() (Bootstrap 5.3+), which Sass
|
|
168
|
+
// can't multiply directly — fold the *2 into the calc() itself instead.
|
|
169
|
+
--dx-input-height: calc(
|
|
170
|
+
#{$input-line-height * 1em} + #{$input-padding-y * 2} + (2 * #{$input-border-width})
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
155
174
|
// ----------------------------------------------------------------------------
|
|
156
175
|
// Dashboard Custom Styles
|
|
157
176
|
// ----------------------------------------------------------------------------
|
|
@@ -202,17 +221,24 @@ $dashboard-navbar-height: 64px;
|
|
|
202
221
|
// Toasts
|
|
203
222
|
// ----------------------------------------------------------------------------
|
|
204
223
|
// Bootstrap Vue Next renders toasts with Bootstrap's `.text-bg-{variant}`
|
|
205
|
-
// utility, which fills the whole toast with a saturated colour and
|
|
206
|
-
// loud "candy" blocks that are wrong for a dashboard. Restyle
|
|
207
|
-
// neutral surface with a subtle semantic tint
|
|
208
|
-
//
|
|
209
|
-
//
|
|
224
|
+
// utility by default, which fills the whole toast with a saturated colour and
|
|
225
|
+
// white text — loud "candy" blocks that are wrong for a dashboard. Restyle
|
|
226
|
+
// them as a calm, neutral surface with a subtle semantic tint. Colour still
|
|
227
|
+
// signals the toast type, but the bold palette stays where it belongs
|
|
228
|
+
// (actions, badges, selected states).
|
|
229
|
+
//
|
|
230
|
+
// `useToast()` (resources/js/composables/useToast.ts) intercepts a themed
|
|
231
|
+
// `variant` and swaps it for a `toast-{variant}` class instead of BVN's own
|
|
232
|
+
// `variant` prop, so these rules drive Bootstrap's own `--bs-toast-*`
|
|
233
|
+
// variables — no `!important` needed to fight the `.text-bg-*` utility, which
|
|
234
|
+
// sets its colours `!important` by design. An untouched (non-themed) variant
|
|
235
|
+
// still falls through to BVN's default `.text-bg-*` styling.
|
|
210
236
|
|
|
211
237
|
.toast {
|
|
212
238
|
overflow: hidden; // clip the bottom progress bar to the rounded corners
|
|
213
239
|
border: 1px solid $border-color;
|
|
214
240
|
border-radius: $border-radius;
|
|
215
|
-
|
|
241
|
+
--bs-toast-bg: #{$white};
|
|
216
242
|
box-shadow:
|
|
217
243
|
0 10px 15px -3px rgba(0, 0, 0, 0.1),
|
|
218
244
|
0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
|
@@ -241,19 +267,16 @@ $dashboard-navbar-height: 64px;
|
|
|
241
267
|
display: none;
|
|
242
268
|
}
|
|
243
269
|
|
|
244
|
-
// Semantic variants: subtle tinted surface
|
|
245
|
-
// `.
|
|
246
|
-
// muted body from the base rules above stay.
|
|
270
|
+
// Semantic variants: subtle tinted surface, driven through the CSS variable
|
|
271
|
+
// the base `.toast` rule (above, and Bootstrap's own) already reads.
|
|
247
272
|
@each $name, $color in (
|
|
248
273
|
"success": $success,
|
|
249
274
|
"danger": $danger,
|
|
250
275
|
"warning": $warning,
|
|
251
276
|
"info": $info
|
|
252
277
|
) {
|
|
253
|
-
&.
|
|
254
|
-
|
|
255
|
-
// saturated background-color !important that BVN applies per variant.
|
|
256
|
-
background-color: mix($color, $white, 8%) !important;
|
|
278
|
+
&.toast-#{$name} {
|
|
279
|
+
--bs-toast-bg: #{mix($color, $white, 8%)};
|
|
257
280
|
}
|
|
258
281
|
}
|
|
259
282
|
}
|
|
@@ -247,12 +247,34 @@
|
|
|
247
247
|
<template v-if="field.type === 'currency'" #prepend>
|
|
248
248
|
<span class="input-group-text">{{ field.currencySymbol || "£" }}</span>
|
|
249
249
|
</template>
|
|
250
|
+
|
|
251
|
+
<!-- Currency: the displayed text is padded to the minor-unit
|
|
252
|
+
precision on blur/seed (e.g. `3.8` -> `3.80`); the model stays
|
|
253
|
+
a plain number and typing is never reformatted mid-edit. -->
|
|
254
|
+
<DFormInput
|
|
255
|
+
v-if="field.type === 'currency'"
|
|
256
|
+
:model-value="currencyDisplayValue"
|
|
257
|
+
type="number"
|
|
258
|
+
:required="field.required"
|
|
259
|
+
:placeholder="field.placeholder"
|
|
260
|
+
:step="field.step ?? '0.01'"
|
|
261
|
+
:min="field.min"
|
|
262
|
+
:max="field.max"
|
|
263
|
+
:state="fieldState"
|
|
264
|
+
:disabled="isDisabled"
|
|
265
|
+
:readonly="isReadonly"
|
|
266
|
+
v-bind="field.inputProps"
|
|
267
|
+
@input="handleCurrencyInput"
|
|
268
|
+
@focus="handleCurrencyFocus"
|
|
269
|
+
@blur="handleCurrencyBlur"
|
|
270
|
+
/>
|
|
250
271
|
<DFormInput
|
|
272
|
+
v-else
|
|
251
273
|
v-model="numericInputValue"
|
|
252
274
|
type="number"
|
|
253
275
|
:required="field.required"
|
|
254
276
|
:placeholder="field.placeholder"
|
|
255
|
-
:step="field.step
|
|
277
|
+
:step="field.step"
|
|
256
278
|
:min="field.min"
|
|
257
279
|
:max="field.max"
|
|
258
280
|
:state="fieldState"
|
|
@@ -260,6 +282,7 @@
|
|
|
260
282
|
:readonly="isReadonly"
|
|
261
283
|
v-bind="field.inputProps"
|
|
262
284
|
/>
|
|
285
|
+
|
|
263
286
|
<template v-if="field.type === 'percentage'" #append>
|
|
264
287
|
<span class="input-group-text">%</span>
|
|
265
288
|
</template>
|
|
@@ -478,6 +501,50 @@ function setValue(value: any): void {
|
|
|
478
501
|
props.form.clearError(errorKey.value);
|
|
479
502
|
}
|
|
480
503
|
|
|
504
|
+
// ————————————————— currency display formatting (#69)
|
|
505
|
+
|
|
506
|
+
// The model stays a plain number throughout; only the input's *displayed*
|
|
507
|
+
// text is padded to the minor-unit precision (`3.8` -> `3.80`), on blur and on
|
|
508
|
+
// initial seed. Reformatting reactively on every keystroke (rather than on
|
|
509
|
+
// blur) would fight the user mid-edit — e.g. rounding "3." to "3.00" before
|
|
510
|
+
// they've typed the fractional digits — so a local ref tracks the shown text
|
|
511
|
+
// independently of the numeric model, and is only resynced from the model
|
|
512
|
+
// while the input isn't focused.
|
|
513
|
+
const currencyDecimals = computed(() => props.field.decimals ?? 2);
|
|
514
|
+
const isCurrencyFocused = ref(false);
|
|
515
|
+
const currencyDisplayValue = ref("");
|
|
516
|
+
|
|
517
|
+
function formatCurrency(value: any): string {
|
|
518
|
+
if (value === null || value === undefined || value === "") return "";
|
|
519
|
+
const num = Number(value);
|
|
520
|
+
if (!Number.isFinite(num)) return String(value);
|
|
521
|
+
return num.toFixed(currencyDecimals.value);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
watch(
|
|
525
|
+
() => fieldValue.value,
|
|
526
|
+
(value) => {
|
|
527
|
+
if (isCurrencyFocused.value) return;
|
|
528
|
+
currencyDisplayValue.value = formatCurrency(value);
|
|
529
|
+
},
|
|
530
|
+
{ immediate: true },
|
|
531
|
+
);
|
|
532
|
+
|
|
533
|
+
function handleCurrencyFocus(): void {
|
|
534
|
+
isCurrencyFocused.value = true;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function handleCurrencyInput(event: Event): void {
|
|
538
|
+
const raw = (event.target as HTMLInputElement).value;
|
|
539
|
+
currencyDisplayValue.value = raw;
|
|
540
|
+
setValue(raw);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
function handleCurrencyBlur(): void {
|
|
544
|
+
isCurrencyFocused.value = false;
|
|
545
|
+
currencyDisplayValue.value = formatCurrency(fieldValue.value);
|
|
546
|
+
}
|
|
547
|
+
|
|
481
548
|
const fieldState = computed(() => props.form.getState(errorKey.value));
|
|
482
549
|
|
|
483
550
|
const resolvedLabel = computed(
|
|
@@ -623,17 +690,23 @@ onBeforeUnmount(() => {
|
|
|
623
690
|
border-radius: var(--bs-border-radius);
|
|
624
691
|
}
|
|
625
692
|
|
|
626
|
-
/* Switch field: a
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
693
|
+
/* Switch field: a compact "filled box" toggle, sized to its content — like the
|
|
694
|
+
plain `checkbox` field type, it doesn't stretch to fill the form row — but
|
|
695
|
+
matching standard input height so it lines up with sibling text/select
|
|
696
|
+
fields. Colour-codes its state: a neutral box when off, a filled success
|
|
697
|
+
green when on. The label sits on the left and the toggle on the right.
|
|
698
|
+
Renders the underlying `.form-check` (from the switch checkbox) as the box.
|
|
699
|
+
`justify-content: space-between` on a full-width box used to leave a wide
|
|
700
|
+
unclickable gap between the label and the toggle (only the tiny toggle
|
|
701
|
+
itself was a real click target); shrinking the box to fit its content
|
|
702
|
+
removes that dead zone entirely. */
|
|
630
703
|
.dx-switch :deep(.form-check) {
|
|
631
|
-
display: flex;
|
|
704
|
+
display: inline-flex;
|
|
632
705
|
align-items: center;
|
|
633
|
-
justify-content: space-between;
|
|
634
706
|
gap: 0.75rem;
|
|
635
|
-
width:
|
|
636
|
-
|
|
707
|
+
width: fit-content;
|
|
708
|
+
max-width: 100%; /* wrap/ellipsize rather than overflow a narrow field */
|
|
709
|
+
min-height: var(--dx-input-height, calc(1.5em + 0.75rem + 2px)); /* match .form-control height */
|
|
637
710
|
margin: 0;
|
|
638
711
|
/* Override Bootstrap `.form-switch`'s 2.5em left padding for the floated input. */
|
|
639
712
|
padding: 0.375rem 0.75rem;
|
|
@@ -1 +1,53 @@
|
|
|
1
|
-
|
|
1
|
+
import { isRef } from "vue";
|
|
2
|
+
import { useToast as useBvnToast } from "bootstrap-vue-next";
|
|
3
|
+
import type {
|
|
4
|
+
OrchestratorCreateOptions,
|
|
5
|
+
ToastOrchestratorCreateParam,
|
|
6
|
+
} from "bootstrap-vue-next";
|
|
7
|
+
|
|
8
|
+
// Bootstrap Vue Next maps a toast's `variant` prop straight to Bootstrap's
|
|
9
|
+
// `.text-bg-{variant}` utility class, which sets background/text colour
|
|
10
|
+
// `!important` (all Bootstrap utilities are `!important` by design) — so
|
|
11
|
+
// restyling it can only ever fight that with `!important` too. The variants
|
|
12
|
+
// this theme restyles (see theme.scss) are instead applied as a `toast-{variant}`
|
|
13
|
+
// class, driving Bootstrap's own `--bs-toast-*` variables cleanly. An
|
|
14
|
+
// unrecognised variant (e.g. "primary", "dark", or none) falls through to
|
|
15
|
+
// BVN's default `.text-bg-*` styling unchanged.
|
|
16
|
+
const THEMED_TOAST_VARIANTS = new Set(["success", "danger", "warning", "info"]);
|
|
17
|
+
|
|
18
|
+
function themeToastParam(param: Record<string, unknown>): Record<string, unknown> {
|
|
19
|
+
const variant = param.variant as string | null | undefined;
|
|
20
|
+
if (!variant || !THEMED_TOAST_VARIANTS.has(variant)) return param;
|
|
21
|
+
|
|
22
|
+
const { variant: _variant, toastClass, ...rest } = param;
|
|
23
|
+
return {
|
|
24
|
+
...rest,
|
|
25
|
+
toastClass: [toastClass, `toast-${variant}`],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Thin wrapper around Bootstrap Vue Next's `useToast()`. Public API is
|
|
31
|
+
* identical (`create({ title, body, variant })`, `show(...)`) — a themed
|
|
32
|
+
* `variant` is translated internally into a `toast-{variant}` class instead
|
|
33
|
+
* of being passed through to BVN, so it's styled via `--bs-toast-*` variables
|
|
34
|
+
* (see theme.scss) rather than an `!important`-overridden utility class.
|
|
35
|
+
*/
|
|
36
|
+
// A ref-wrapped param (rare — most callers pass a plain object) is forwarded
|
|
37
|
+
// untouched: reading through it once here would snapshot every field, losing
|
|
38
|
+
// reactivity for the rest of the toast, not just its variant.
|
|
39
|
+
function maybeThemeParam(obj?: ToastOrchestratorCreateParam): ToastOrchestratorCreateParam | undefined {
|
|
40
|
+
if (!obj || isRef(obj)) return obj;
|
|
41
|
+
return themeToastParam(obj as Record<string, unknown>) as unknown as ToastOrchestratorCreateParam;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function useToast(): ReturnType<typeof useBvnToast> {
|
|
45
|
+
const toast = useBvnToast();
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
...toast,
|
|
49
|
+
create: (obj?: ToastOrchestratorCreateParam, options?: OrchestratorCreateOptions) =>
|
|
50
|
+
toast.create(maybeThemeParam(obj), options),
|
|
51
|
+
show: (obj?: ToastOrchestratorCreateParam) => toast.show(maybeThemeParam(obj)),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -114,6 +114,15 @@ export interface FieldDefinition {
|
|
|
114
114
|
/** Symbol shown for `currency` fields (default: the locale's, "£"). */
|
|
115
115
|
currencySymbol?: string;
|
|
116
116
|
|
|
117
|
+
/**
|
|
118
|
+
* For `currency` fields: minor-unit decimal places the *display* value is
|
|
119
|
+
* formatted to on blur and on initial seed (default: 2, e.g. GBP/EUR/USD —
|
|
120
|
+
* pass 0 for a currency with no minor unit, e.g. JPY). The model stays a
|
|
121
|
+
* plain number throughout; only the input's shown text is padded (`3.8` →
|
|
122
|
+
* `3.80`). Typing is never reformatted mid-edit.
|
|
123
|
+
*/
|
|
124
|
+
decimals?: number;
|
|
125
|
+
|
|
117
126
|
/**
|
|
118
127
|
* For `percentage` fields: treat the underlying model value as a 0–1
|
|
119
128
|
* fraction while showing/editing it as a 0–100 percentage. The model keeps
|