@omnitend/dashboard-for-laravel 0.35.0 → 0.36.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/charts.js +137 -97
- package/dist/charts.js.map +1 -1
- package/dist/charts.umd.cjs +1 -1
- package/dist/charts.umd.cjs.map +1 -1
- package/dist/components/charts/chartTheme.d.ts +29 -3
- package/dist/components/charts/useThemedChart.d.ts +7 -2
- package/dist/components/extended/DXTable.vue.d.ts +9 -10
- package/dist/components/extended/DXTablePagination.vue.d.ts +22 -1
- package/dist/dashboard-for-laravel.js +3931 -3888
- package/dist/dashboard-for-laravel.js.map +1 -1
- package/dist/dashboard-for-laravel.umd.cjs +6 -6
- package/dist/dashboard-for-laravel.umd.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/docs/public/api-reference.json +12 -5
- package/docs/public/docs-map.md +1 -1
- package/package.json +1 -1
- package/resources/css/theme.scss +16 -2
- package/resources/js/components/charts/DXBarChart.vue +7 -2
- package/resources/js/components/charts/DXDoughnutChart.vue +33 -10
- package/resources/js/components/charts/DXLineChart.vue +7 -2
- package/resources/js/components/charts/chartTheme.ts +106 -11
- package/resources/js/components/charts/useThemedChart.ts +39 -21
- package/resources/js/components/extended/DXTable.vue +9 -10
- package/resources/js/components/extended/DXTablePagination.vue +146 -5
- package/resources/js/components/extended/DXTableShell.vue +49 -14
|
@@ -15,17 +15,32 @@
|
|
|
15
15
|
once it lives in a child component — the scope-id doesn't cross the boundary.
|
|
16
16
|
Anchoring the rules on this component's own `.dx-table-pagination` root (a
|
|
17
17
|
plain element it owns) gives the scope-id a deterministic host.
|
|
18
|
+
|
|
19
|
+
In a row too narrow for the full windowed pager, the number strip collapses to
|
|
20
|
+
a "current / last" status with Previous/Next either side (#162). The decision
|
|
21
|
+
is CONTAINER-driven, not viewport-driven — see `compactThreshold`.
|
|
18
22
|
-->
|
|
19
23
|
<template>
|
|
20
24
|
<div class="dx-table-pagination mt-3">
|
|
21
|
-
<!-- Top row: Pagination and Per-page selector
|
|
22
|
-
|
|
25
|
+
<!-- Top row: Pagination and Per-page selector.
|
|
26
|
+
`containerRef` measures THIS row (the space the pager actually
|
|
27
|
+
competes for with the per-page selector) rather than the window —
|
|
28
|
+
a table narrowed by a sidebar or a modal is cramped even on a wide
|
|
29
|
+
viewport, which no media query can see. See `useContainerWidth`. -->
|
|
30
|
+
<div
|
|
31
|
+
ref="containerRef"
|
|
32
|
+
class="dx-pager-row d-flex justify-content-between align-items-center gap-2 mb-2"
|
|
33
|
+
>
|
|
23
34
|
<!-- Windowed pager (#155): «Previous / leading / … / window / … /
|
|
24
35
|
trailing / Next». Client-computed from current + last page, so it
|
|
25
|
-
works in every DXTable mode. Only shown when >1 page.
|
|
36
|
+
works in every DXTable mode. Only shown when >1 page.
|
|
37
|
+
In a row too narrow to lay the whole thing out, the number
|
|
38
|
+
window collapses to a "current / last" status (#162) — see
|
|
39
|
+
`isCompact`. -->
|
|
26
40
|
<nav
|
|
27
41
|
v-if="showPagination && pagination.total > pagination.per_page"
|
|
28
42
|
class="dx-pager d-flex flex-wrap align-items-center gap-1"
|
|
43
|
+
:class="{ 'dx-pager--compact': isCompact }"
|
|
29
44
|
aria-label="Pagination"
|
|
30
45
|
>
|
|
31
46
|
<DButton
|
|
@@ -35,7 +50,28 @@
|
|
|
35
50
|
@click="goToPage(currentPage - 1)"
|
|
36
51
|
>« Previous</DButton>
|
|
37
52
|
|
|
38
|
-
|
|
53
|
+
<!-- Compact: one status in place of the whole number window.
|
|
54
|
+
With no page buttons there is no `aria-current` to carry
|
|
55
|
+
"you are here", so the position becomes a live `status`
|
|
56
|
+
region — it is announced when paging changes it, which the
|
|
57
|
+
numbered pager achieved through focus moving to the newly
|
|
58
|
+
active button. The terse "11 / 45" is for the eye only
|
|
59
|
+
(a screen reader would say "eleven slash forty-five"); the
|
|
60
|
+
announced text is the visually-hidden long form. -->
|
|
61
|
+
<span
|
|
62
|
+
v-if="isCompact"
|
|
63
|
+
class="dx-pager-status small"
|
|
64
|
+
role="status"
|
|
65
|
+
aria-live="polite"
|
|
66
|
+
>
|
|
67
|
+
<span class="visually-hidden">Page {{ currentPage }} of {{ lastPage }}</span>
|
|
68
|
+
<span aria-hidden="true">{{ currentPage }} / {{ lastPage }}</span>
|
|
69
|
+
</span>
|
|
70
|
+
|
|
71
|
+
<template
|
|
72
|
+
v-for="(item, index) in visiblePageItems"
|
|
73
|
+
:key="item.type === 'page' ? `p${item.page}` : `e${index}`"
|
|
74
|
+
>
|
|
39
75
|
<DButton
|
|
40
76
|
v-if="item.type === 'page'"
|
|
41
77
|
size="sm"
|
|
@@ -94,6 +130,7 @@
|
|
|
94
130
|
import { computed } from "vue";
|
|
95
131
|
import DButton from "../base/DButton.vue";
|
|
96
132
|
import DFormSelect from "../base/DFormSelect.vue";
|
|
133
|
+
import { useContainerWidth } from "../../composables/useContainerWidth";
|
|
97
134
|
import type { PaginationData } from "./DXTable.vue";
|
|
98
135
|
|
|
99
136
|
interface Props {
|
|
@@ -115,9 +152,31 @@ interface Props {
|
|
|
115
152
|
pluralItemName: string;
|
|
116
153
|
/** Whether a column filter is active — gates the "Filtered from N" note. */
|
|
117
154
|
hasActiveFilters: boolean;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Upper bound (px) on the pager ROW width at which the number window may
|
|
158
|
+
* collapse to the compact "current / last" status (#162). At or above it
|
|
159
|
+
* the full windowed pager always renders, whatever the page count.
|
|
160
|
+
*
|
|
161
|
+
* Default **576** — Bootstrap's `sm` breakpoint, i.e. the width the rest of
|
|
162
|
+
* the design system already treats as phone-sized. Its job is to GUARANTEE
|
|
163
|
+
* that a desktop-width table is never quietly downgraded: a 45-page window
|
|
164
|
+
* that doesn't quite fit at 800px should wrap (today's behaviour), not lose
|
|
165
|
+
* its numbers.
|
|
166
|
+
*
|
|
167
|
+
* Below it, compact engages only when the full pager genuinely would not
|
|
168
|
+
* fit on one line (see `fullPagerWidth`) — so a three-page pager still
|
|
169
|
+
* shows `1 2 3` on a phone, where "2 / 3" would be strictly worse.
|
|
170
|
+
*
|
|
171
|
+
* Measured on the ROW, not the viewport, so a table squeezed by a sidebar
|
|
172
|
+
* or inside a modal collapses even though the window is wide.
|
|
173
|
+
*/
|
|
174
|
+
compactThreshold?: number;
|
|
118
175
|
}
|
|
119
176
|
|
|
120
|
-
const props = defineProps<Props>()
|
|
177
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
178
|
+
compactThreshold: 576,
|
|
179
|
+
});
|
|
121
180
|
|
|
122
181
|
const emit = defineEmits<{
|
|
123
182
|
/** The user picked a page. Payload is the new 1-based page number. */
|
|
@@ -182,6 +241,71 @@ const pageItems = computed<PageItem[]>(() => {
|
|
|
182
241
|
return items;
|
|
183
242
|
});
|
|
184
243
|
|
|
244
|
+
// Compact pager (#162). Container-driven, not viewport-driven — see the
|
|
245
|
+
// `compactThreshold` prop.
|
|
246
|
+
//
|
|
247
|
+
// Approximate intrinsic widths of the pager's parts, in rem, mirroring this
|
|
248
|
+
// component's own CSS and button labels: a page key is `min-width: 2.5rem`, the
|
|
249
|
+
// row is `gap-1` (0.25rem), the ellipsis is a glyph plus 0.15rem side padding,
|
|
250
|
+
// and "« Previous" + "Next »" together come to about 10rem at `.btn-sm`.
|
|
251
|
+
// Deliberately an ESTIMATE, not a measurement: being wrong by a few px only
|
|
252
|
+
// shifts the collapse by one page key, which is cosmetic — whereas measuring
|
|
253
|
+
// the rendered strip would mean measuring the very thing this decision
|
|
254
|
+
// removes, i.e. a feedback loop.
|
|
255
|
+
const PAGE_KEY_REM = 2.5;
|
|
256
|
+
const ITEM_GAP_REM = 0.25;
|
|
257
|
+
const ELLIPSIS_REM = 1;
|
|
258
|
+
const PREV_NEXT_REM = 10;
|
|
259
|
+
|
|
260
|
+
/** px per rem, so a consumer scaling the root font size is tracked. */
|
|
261
|
+
function rootFontSizePx(): number {
|
|
262
|
+
// Only ever reached in a browser (see `isCompact`), but stay SSR-safe.
|
|
263
|
+
if (typeof document === "undefined") return 16;
|
|
264
|
+
const size = Number.parseFloat(
|
|
265
|
+
getComputedStyle(document.documentElement).fontSize,
|
|
266
|
+
);
|
|
267
|
+
return Number.isFinite(size) && size > 0 ? size : 16;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** Width the row needs to lay the full windowed pager out on ONE line. */
|
|
271
|
+
const fullPagerWidth = computed(() => {
|
|
272
|
+
const remPx = rootFontSizePx();
|
|
273
|
+
let widthRem = PREV_NEXT_REM;
|
|
274
|
+
for (const item of pageItems.value) {
|
|
275
|
+
widthRem +=
|
|
276
|
+
(item.type === "page" ? PAGE_KEY_REM : ELLIPSIS_REM) + ITEM_GAP_REM;
|
|
277
|
+
}
|
|
278
|
+
return widthRem * remPx;
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
const { containerRef, hasMeasured, isBelow } = useContainerWidth({
|
|
282
|
+
// Compact means "narrower than the breakpoint AND too narrow for the full
|
|
283
|
+
// pager", which is exactly `width < min(threshold, needed)` — expressing it
|
|
284
|
+
// as one threshold rather than two conditions keeps the hysteresis below
|
|
285
|
+
// applied to the WHOLE decision instead of just half of it. A getter, so
|
|
286
|
+
// both a reactive `compactThreshold` and a changing page count re-evaluate
|
|
287
|
+
// instead of latching the mount-time value.
|
|
288
|
+
threshold: () => Math.min(props.compactThreshold, fullPagerWidth.value),
|
|
289
|
+
// The compact row is SHORTER than the wrapped full pager, so an ancestor with
|
|
290
|
+
// `overflow:auto` can lose its vertical scrollbar when we collapse, widening
|
|
291
|
+
// this row by ~15-17px, back over the threshold, and it flips forever. A band
|
|
292
|
+
// wider than any scrollbar makes the crossing one-way.
|
|
293
|
+
hysteresis: 24,
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Gated on `hasMeasured` so an UNMEASURED render (the first frame, and every
|
|
298
|
+
* SSR render, where there is no `ResizeObserver`) is byte-identical to the
|
|
299
|
+
* pre-#162 full pager. `initialWidth` defaults to 0, i.e. "assume narrowest",
|
|
300
|
+
* which would otherwise make every desktop render start compact and flip.
|
|
301
|
+
*/
|
|
302
|
+
const isCompact = computed(() => hasMeasured.value && isBelow.value);
|
|
303
|
+
|
|
304
|
+
/** Empty in compact mode — the status replaces the whole number window. */
|
|
305
|
+
const visiblePageItems = computed<PageItem[]>(() =>
|
|
306
|
+
isCompact.value ? [] : pageItems.value,
|
|
307
|
+
);
|
|
308
|
+
|
|
185
309
|
const canPrev = computed(() => currentPage.value > 1);
|
|
186
310
|
const canNext = computed(() => currentPage.value < lastPage.value);
|
|
187
311
|
|
|
@@ -223,6 +347,23 @@ const goToPage = (page: number) => {
|
|
|
223
347
|
min-width: auto;
|
|
224
348
|
}
|
|
225
349
|
|
|
350
|
+
/* Compact pager status (#162): "11 / 45" standing in for the number window.
|
|
351
|
+
A plain element this component owns, so it needs no `:deep()`. It is not a
|
|
352
|
+
button — deliberately, since there is nothing to click — so it is styled to
|
|
353
|
+
read as a label rather than a disabled key. */
|
|
354
|
+
.dx-pager--compact .dx-pager-status {
|
|
355
|
+
padding: 0 0.35rem;
|
|
356
|
+
color: var(--bs-secondary-color);
|
|
357
|
+
white-space: nowrap;
|
|
358
|
+
user-select: none;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/* Nothing can wrap in compact mode — the row is three items wide by design, and
|
|
362
|
+
a wrap here would defeat the whole point of collapsing it. */
|
|
363
|
+
.dx-pager--compact {
|
|
364
|
+
flex-wrap: nowrap;
|
|
365
|
+
}
|
|
366
|
+
|
|
226
367
|
.dx-pager-ellipsis {
|
|
227
368
|
display: inline-flex;
|
|
228
369
|
align-items: flex-end;
|
|
@@ -17,16 +17,16 @@
|
|
|
17
17
|
block, table, pagination — become direct children of `.card`, which is already
|
|
18
18
|
a `flex-direction: column` container, so they still stack in order:
|
|
19
19
|
|
|
20
|
-
.card
|
|
20
|
+
.card
|
|
21
21
|
├─ .card-header (padded by Bootstrap's card-cap padding)
|
|
22
|
-
├─ .table-responsive ← FLUSH
|
|
22
|
+
├─ .table-responsive ← FLUSH, rounded when it is at a card corner
|
|
23
23
|
│ └─ table
|
|
24
24
|
└─ .dx-table-pagination (pads itself — see DXTablePagination.vue)
|
|
25
25
|
|
|
26
26
|
Bootstrap 5 has no `.card > .table` rounding rules (that was BS4), so the
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
flush table's square corners have to be rounded here. The rounding is applied
|
|
28
|
+
to the TABLE REGION, never to the card: clipping the whole card would cut off
|
|
29
|
+
any non-teleported positioned content a consumer puts in a slot (#166).
|
|
30
30
|
-->
|
|
31
31
|
<template>
|
|
32
32
|
<DCard v-if="card" no-body class="dx-table-card">
|
|
@@ -82,21 +82,56 @@ withDefaults(defineProps<Props>(), {
|
|
|
82
82
|
*/
|
|
83
83
|
|
|
84
84
|
/*
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
Round the flush table region so its square corners follow the card border.
|
|
86
|
+
|
|
87
|
+
This used to be `overflow: hidden` on the WHOLE card, which clipped every
|
|
88
|
+
non-teleported positioned descendant — including a consumer's own
|
|
89
|
+
`position: absolute` popover in a `#header` or `cell(<key>)` slot (#166).
|
|
90
|
+
The clip now sits on the table region itself, and only on the corners the
|
|
91
|
+
region actually occupies: `.card-header` and `.dx-table-pagination` round the
|
|
92
|
+
corners themselves when they are present, so the radius is applied via
|
|
93
|
+
`:first-child` / `:last-child` rather than unconditionally. Vue renders a
|
|
94
|
+
false `v-if` as a comment node, which `:first-child` ignores, so the error
|
|
95
|
+
alert and spinner branches don't disturb the match.
|
|
96
|
+
|
|
97
|
+
These rules stay in this global block with the rest of the card-mode layout: a
|
|
87
98
|
scoped rule WOULD reach DCard's root (the renderer walks the component-root
|
|
88
99
|
chain), but that forwarding is the exact thing CLAUDE.md records as unreliable
|
|
89
100
|
across build pipelines, and a silently-unclipped card is invisible in review.
|
|
90
101
|
The lower specificity also leaves consumers free to override it.
|
|
102
|
+
*/
|
|
103
|
+
.dx-table-card > .table-responsive:first-child,
|
|
104
|
+
.dx-table-card > .table:first-child {
|
|
105
|
+
border-top-left-radius: var(--bs-card-inner-border-radius);
|
|
106
|
+
border-top-right-radius: var(--bs-card-inner-border-radius);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.dx-table-card > .table-responsive:last-child,
|
|
110
|
+
.dx-table-card > .table:last-child {
|
|
111
|
+
border-bottom-left-radius: var(--bs-card-inner-border-radius);
|
|
112
|
+
border-bottom-right-radius: var(--bs-card-inner-border-radius);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/*
|
|
116
|
+
`.table-responsive` needs NO `overflow` of its own: Bootstrap already gives it
|
|
117
|
+
`overflow-x: auto`, which computes `overflow-y` to `auto` as well, so it is
|
|
118
|
+
already a clipping context and the radius above rounds that clip. Measured in
|
|
119
|
+
a real browser (see DXTable-CardFlush.test.ts): without the radius a hit-test
|
|
120
|
+
3px inside the card's corner lands on a `<th>`; with it, on the card.
|
|
121
|
+
Do NOT add `overflow: hidden` here — it would kill the horizontal scrolling
|
|
122
|
+
that makes a wide table usable, and the flush look would survive to hide it.
|
|
91
123
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
124
|
+
A bare `<table>` (`:responsive="false"`) is a different story: it is not a
|
|
125
|
+
clipping context, and `border-radius` on it leaves the collapsed-border cell
|
|
126
|
+
backgrounds square (measured — the corner still hit-tests to a `<th>`). That
|
|
127
|
+
one needs an explicit clip, scoped to the flush table so it is the table
|
|
128
|
+
region and not the card that clips. Note this restores the pre-0.35 behaviour
|
|
129
|
+
where a `:responsive="false"` table too wide for its card overflows visibly
|
|
130
|
+
rather than being silently cut off — that mode opts out of a scroll container
|
|
131
|
+
by definition.
|
|
98
132
|
*/
|
|
99
|
-
.dx-table-card
|
|
133
|
+
.dx-table-card > .table:first-child,
|
|
134
|
+
.dx-table-card > .table:last-child {
|
|
100
135
|
overflow: hidden;
|
|
101
136
|
}
|
|
102
137
|
|