@asteby/metacore-runtime-react 27.2.0 → 27.3.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/CHANGELOG.md +18 -0
- package/dist/dashboard-empty-mockup.d.ts +9 -0
- package/dist/dashboard-empty-mockup.d.ts.map +1 -0
- package/dist/dashboard-empty-mockup.js +78 -0
- package/dist/dashboard-grid.d.ts.map +1 -1
- package/dist/dashboard-grid.js +4 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/package.json +1 -1
- package/src/__tests__/dashboard-grid.test.tsx +10 -0
- package/src/dashboard-empty-mockup.tsx +122 -0
- package/src/dashboard-grid.tsx +12 -12
- package/src/index.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @asteby/metacore-runtime-react
|
|
2
2
|
|
|
3
|
+
## 27.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 8b46e55: El empty state del Tablero ahora muestra un mockup animado en vez de un ícono
|
|
8
|
+
estático. Cuando no hay widgets (o ninguno visible tras el gating de permisos),
|
|
9
|
+
`DashboardGrid` pinta una silueta del propio dashboard —tiles skeleton (un
|
|
10
|
+
gráfico grande, un par de stat cards, una barra de progreso y una lista)— que
|
|
11
|
+
se desplazan y reacomodan suavemente entre sí, como piezas encajando en su
|
|
12
|
+
lugar. El mensaje pasa de "no hay nada" a "acá va a vivir tu tablero".
|
|
13
|
+
|
|
14
|
+
Detalles: animación CSS pura (un loop lento de ~11s, translate+scale sutil, sin
|
|
15
|
+
dependencias nuevas), tokens de tema (`bg-muted`) para light/dark automáticos,
|
|
16
|
+
`prefers-reduced-motion: reduce` congela todo, y el mock es decorativo
|
|
17
|
+
(`aria-hidden`). Se exporta el componente `DashboardEmptyMockup` por si un host
|
|
18
|
+
quiere reutilizarlo. Copy por defecto del empty ajustado al nuevo framing
|
|
19
|
+
(override vía `strings`).
|
|
20
|
+
|
|
3
21
|
## 27.2.0
|
|
4
22
|
|
|
5
23
|
### Minor Changes
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Animated skeleton of a dashboard (chart + stats + bar + list) whose tiles
|
|
4
|
+
* slowly drift and settle. Purely decorative — always `aria-hidden`.
|
|
5
|
+
*/
|
|
6
|
+
export declare function DashboardEmptyMockup({ className }: {
|
|
7
|
+
className?: string;
|
|
8
|
+
}): React.JSX.Element;
|
|
9
|
+
//# sourceMappingURL=dashboard-empty-mockup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard-empty-mockup.d.ts","sourceRoot":"","sources":["../src/dashboard-empty-mockup.tsx"],"names":[],"mappings":"AAqBA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAoD9B;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,qBA4CzE"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// DashboardEmptyMockup — the animated empty state for the modular dashboard.
|
|
3
|
+
//
|
|
4
|
+
// Instead of a static "nothing here" card, we paint a silhouette of the
|
|
5
|
+
// dashboard itself: skeleton tiles (a big chart, a couple of stat cards, a
|
|
6
|
+
// bar, a list) that slowly drift and swap places, like puzzle pieces settling
|
|
7
|
+
// into a layout. The metaphor is "your board is getting ready", not "empty".
|
|
8
|
+
//
|
|
9
|
+
// Design constraints (kept deliberately simple so it ships from a package):
|
|
10
|
+
// - Pure CSS keyframes, no dependencies. One slow ~11s loop, subtle motion
|
|
11
|
+
// (small translate + scale, never opacity strobing).
|
|
12
|
+
// - Theme tokens only (bg-muted / border tokens) → light & dark for free.
|
|
13
|
+
// - `prefers-reduced-motion: reduce` freezes everything (tiles rest in place).
|
|
14
|
+
// - The whole mock is decorative → aria-hidden; the copy below carries the
|
|
15
|
+
// accessible meaning.
|
|
16
|
+
//
|
|
17
|
+
// The keyframes ship inline in a <style> tag rather than as Tailwind utilities:
|
|
18
|
+
// consumer apps scan only their OWN source for Tailwind classes, so arbitrary
|
|
19
|
+
// animation utilities declared here would never be generated in their build.
|
|
20
|
+
// Static tokens (bg-muted, rounded-lg…) are standard classes the host emits
|
|
21
|
+
// anyway, so those stay as className.
|
|
22
|
+
import * as React from 'react';
|
|
23
|
+
import { cn } from '@asteby/metacore-ui/lib';
|
|
24
|
+
// Scoped, collision-proof keyframe + class names (prefixed, unlikely to clash).
|
|
25
|
+
const STYLE_ID = 'mc-dashboard-empty-mockup-style';
|
|
26
|
+
const MOCKUP_CSS = `
|
|
27
|
+
.mc-demock{--mc-demock-dur:11s}
|
|
28
|
+
.mc-demock-tile{will-change:transform;transform-origin:center;
|
|
29
|
+
animation-duration:var(--mc-demock-dur);animation-timing-function:cubic-bezier(.65,0,.35,1);
|
|
30
|
+
animation-iteration-count:infinite}
|
|
31
|
+
.mc-demock-t1{animation-name:mc-demock-drift-a}
|
|
32
|
+
.mc-demock-t2{animation-name:mc-demock-drift-b;animation-delay:-1.4s}
|
|
33
|
+
.mc-demock-t3{animation-name:mc-demock-drift-c;animation-delay:-3.1s}
|
|
34
|
+
.mc-demock-t4{animation-name:mc-demock-drift-d;animation-delay:-2.2s}
|
|
35
|
+
.mc-demock-t5{animation-name:mc-demock-drift-e;animation-delay:-4.6s}
|
|
36
|
+
.mc-demock-shimmer{position:relative;overflow:hidden}
|
|
37
|
+
.mc-demock-shimmer::after{content:"";position:absolute;inset:0;
|
|
38
|
+
background:linear-gradient(105deg,transparent 30%,currentColor 50%,transparent 70%);
|
|
39
|
+
opacity:.05;transform:translateX(-100%);
|
|
40
|
+
animation:mc-demock-sheen var(--mc-demock-dur) ease-in-out infinite}
|
|
41
|
+
@keyframes mc-demock-drift-a{0%,100%{transform:translate3d(0,0,0) scale(1)}
|
|
42
|
+
50%{transform:translate3d(3%,4%,0) scale(1.015)}}
|
|
43
|
+
@keyframes mc-demock-drift-b{0%,100%{transform:translate3d(0,0,0) scale(1)}
|
|
44
|
+
50%{transform:translate3d(-4%,-3%,0) scale(.985)}}
|
|
45
|
+
@keyframes mc-demock-drift-c{0%,100%{transform:translate3d(0,0,0) scale(1)}
|
|
46
|
+
50%{transform:translate3d(5%,-2%,0) scale(1.02)}}
|
|
47
|
+
@keyframes mc-demock-drift-d{0%,100%{transform:translate3d(0,0,0) scale(1)}
|
|
48
|
+
50%{transform:translate3d(-3%,3%,0) scale(.99)}}
|
|
49
|
+
@keyframes mc-demock-drift-e{0%,100%{transform:translate3d(0,0,0) scale(1)}
|
|
50
|
+
50%{transform:translate3d(2%,-4%,0) scale(1.01)}}
|
|
51
|
+
@keyframes mc-demock-sheen{0%,100%{transform:translateX(-120%)}55%,70%{transform:translateX(120%)}}
|
|
52
|
+
@media (prefers-reduced-motion:reduce){
|
|
53
|
+
.mc-demock-tile{animation:none!important}
|
|
54
|
+
.mc-demock-shimmer::after{animation:none!important;opacity:0}
|
|
55
|
+
}
|
|
56
|
+
`;
|
|
57
|
+
/** Injects the keyframes once per document (idempotent, SSR-safe). */
|
|
58
|
+
function useMockupStyles() {
|
|
59
|
+
React.useInsertionEffect(() => {
|
|
60
|
+
if (typeof document === 'undefined')
|
|
61
|
+
return;
|
|
62
|
+
if (document.getElementById(STYLE_ID))
|
|
63
|
+
return;
|
|
64
|
+
const el = document.createElement('style');
|
|
65
|
+
el.id = STYLE_ID;
|
|
66
|
+
el.textContent = MOCKUP_CSS;
|
|
67
|
+
document.head.appendChild(el);
|
|
68
|
+
}, []);
|
|
69
|
+
}
|
|
70
|
+
const tile = 'rounded-lg bg-muted mc-demock-tile mc-demock-shimmer text-foreground';
|
|
71
|
+
/**
|
|
72
|
+
* Animated skeleton of a dashboard (chart + stats + bar + list) whose tiles
|
|
73
|
+
* slowly drift and settle. Purely decorative — always `aria-hidden`.
|
|
74
|
+
*/
|
|
75
|
+
export function DashboardEmptyMockup({ className }) {
|
|
76
|
+
useMockupStyles();
|
|
77
|
+
return (_jsx("div", { "aria-hidden": "true", "data-testid": "dashboard-empty-mockup", className: cn('mc-demock pointer-events-none w-full max-w-md select-none', className), children: _jsxs("div", { className: "grid grid-cols-3 grid-rows-[repeat(3,minmax(0,1fr))] gap-3 [aspect-ratio:16/11]", children: [_jsx("div", { className: cn(tile, 'mc-demock-t1 col-span-2 row-span-2 flex flex-col justify-end gap-2 p-3'), children: _jsxs("div", { className: "flex items-end gap-1.5", children: [_jsx("div", { className: "h-6 w-full rounded-sm bg-foreground/10" }), _jsx("div", { className: "h-10 w-full rounded-sm bg-foreground/10" }), _jsx("div", { className: "h-7 w-full rounded-sm bg-foreground/10" }), _jsx("div", { className: "h-12 w-full rounded-sm bg-foreground/10" }), _jsx("div", { className: "h-9 w-full rounded-sm bg-foreground/10" })] }) }), _jsxs("div", { className: cn(tile, 'mc-demock-t2 flex flex-col justify-center gap-2 p-3'), children: [_jsx("div", { className: "h-2 w-1/2 rounded-full bg-foreground/15" }), _jsx("div", { className: "h-4 w-2/3 rounded bg-foreground/15" })] }), _jsxs("div", { className: cn(tile, 'mc-demock-t3 flex flex-col justify-center gap-2 p-3'), children: [_jsx("div", { className: "h-2 w-1/2 rounded-full bg-foreground/15" }), _jsx("div", { className: "h-4 w-1/2 rounded bg-foreground/15" })] }), _jsxs("div", { className: cn(tile, 'mc-demock-t4 flex flex-col justify-center gap-2 p-3'), children: [_jsx("div", { className: "h-2 w-full rounded-full bg-foreground/12" }), _jsx("div", { className: "h-2 w-4/5 rounded-full bg-foreground/12" }), _jsx("div", { className: "h-2 w-2/3 rounded-full bg-foreground/12" })] }), _jsx("div", { className: cn(tile, 'mc-demock-t5 col-span-2 flex items-center px-3'), children: _jsx("div", { className: "h-2.5 w-full rounded-full bg-foreground/10", children: _jsx("div", { className: "h-full w-2/5 rounded-full bg-foreground/20" }) }) })] }) }));
|
|
78
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard-grid.d.ts","sourceRoot":"","sources":["../src/dashboard-grid.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"dashboard-grid.d.ts","sourceRoot":"","sources":["../src/dashboard-grid.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,OAAO,KAAK,EACR,kBAAkB,EAElB,oBAAoB,EACpB,mBAAmB,EACtB,MAAM,mBAAmB,CAAA;AAY1B,uEAAuE;AACvE,wBAAgB,eAAe,CAC3B,MAAM,CAAC,EAAE,oBAAoB,EAAE,EAC/B,OAAO,CAAC,EAAE,mBAAmB,EAAE,GAChC,oBAAoB,EAAE,CAgBxB;AASD,wBAAgB,aAAa,CAAC,EAC1B,MAAM,EACN,OAAO,EACP,QAAQ,EACR,OAAO,EACP,MAAM,EACN,QAAQ,EACR,SAAS,EACT,OAAO,GACV,EAAE,kBAAkB,qBAiJpB"}
|
package/dist/dashboard-grid.js
CHANGED
|
@@ -8,11 +8,11 @@ import * as React from 'react';
|
|
|
8
8
|
import { useTranslation } from 'react-i18next';
|
|
9
9
|
import { cn } from '@asteby/metacore-ui/lib';
|
|
10
10
|
import { useCan, usePermissionsActive } from './permissions-context';
|
|
11
|
-
import { DynamicIcon } from './dynamic-icon';
|
|
12
11
|
import { WidgetRenderer, WidgetSkeleton, isTallWidget } from './widgets/widget-renderer';
|
|
12
|
+
import { DashboardEmptyMockup } from './dashboard-empty-mockup';
|
|
13
13
|
const DEFAULT_STRINGS = {
|
|
14
|
-
emptyTitle: 'Your dashboard is
|
|
15
|
-
emptyDescription: 'Install an addon with dashboard widgets
|
|
14
|
+
emptyTitle: 'Your dashboard is taking shape',
|
|
15
|
+
emptyDescription: 'Install an addon with dashboard widgets and your metrics will start living here.',
|
|
16
16
|
widgetError: 'Could not load this widget.',
|
|
17
17
|
widgetEmpty: 'No data yet.',
|
|
18
18
|
};
|
|
@@ -120,7 +120,7 @@ export function DashboardGrid({ groups, widgets, loadData, isAdmin, locale, curr
|
|
|
120
120
|
}, [visibleGroups]);
|
|
121
121
|
// Global empty state (no widgets at all / none visible after gating).
|
|
122
122
|
if (visibleGroups.length === 0) {
|
|
123
|
-
return (_jsxs("div", { "data-testid": "dashboard-empty", className: cn('flex min-h-[40vh] flex-col items-center justify-center rounded-xl border border-dashed border-border/60 p-10 text-center', className), children: [_jsx("div", { className: "
|
|
123
|
+
return (_jsxs("div", { "data-testid": "dashboard-empty", className: cn('flex min-h-[40vh] flex-col items-center justify-center gap-6 rounded-xl border border-dashed border-border/60 p-10 text-center', className), children: [_jsx(DashboardEmptyMockup, {}), _jsxs("div", { className: "flex flex-col items-center", children: [_jsx("h3", { className: "text-base font-semibold text-foreground", children: tr(undefined, s.emptyTitle) || s.emptyTitle }), _jsx("p", { className: "mt-1 max-w-sm text-sm text-muted-foreground", children: s.emptyDescription })] })] }));
|
|
124
124
|
}
|
|
125
125
|
return (_jsx("div", { "data-testid": "dashboard-grid", className: cn(
|
|
126
126
|
// Masonry: balanced CSS columns. Cards take their natural height
|
package/dist/index.d.ts
CHANGED
|
@@ -58,6 +58,7 @@ export { ActivityDiff, type ActivityEvent, type ActivityDiffProps, } from './act
|
|
|
58
58
|
export { RecordHistory, type RecordHistoryProps, } from './record-history';
|
|
59
59
|
export { ActivityTimeline, type ActivityTimelineProps, } from './activity-timeline';
|
|
60
60
|
export { DashboardGrid, normalizeGroups, } from './dashboard-grid';
|
|
61
|
+
export { DashboardEmptyMockup } from './dashboard-empty-mockup';
|
|
61
62
|
export type { WidgetKind, WidgetSize, WidgetFormat, WidgetAccent, WidgetAggregate, WidgetWhereOp, DashboardWidgetQuery, DashboardWidgetCompare, DashboardWidgetSpec, WidgetSeriesPoint, WidgetData, DashboardWidgetGroup, LoadWidgetData, DashboardGridProps, DashboardGridStrings, } from './dashboard-types';
|
|
62
63
|
export { StatWidget, BarWidget, LineWidget, AreaWidget, PieWidget, DonutWidget, ListWidget, ProgressWidget, type WidgetRenderProps, } from './widgets/renderers';
|
|
63
64
|
export { WidgetRenderer, WidgetSkeleton, SIZE_SPAN, SIZE_CLASS, type WidgetRendererProps, } from './widgets/widget-renderer';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -60,6 +60,7 @@ export { ActivityDiff, } from './activity-diff';
|
|
|
60
60
|
export { RecordHistory, } from './record-history';
|
|
61
61
|
export { ActivityTimeline, } from './activity-timeline';
|
|
62
62
|
export { DashboardGrid, normalizeGroups, } from './dashboard-grid';
|
|
63
|
+
export { DashboardEmptyMockup } from './dashboard-empty-mockup';
|
|
63
64
|
export { StatWidget, BarWidget, LineWidget, AreaWidget, PieWidget, DonutWidget, ListWidget, ProgressWidget, } from './widgets/renderers';
|
|
64
65
|
export { WidgetRenderer, WidgetSkeleton, SIZE_SPAN, SIZE_CLASS, } from './widgets/widget-renderer';
|
|
65
66
|
export { WidgetCard, DeltaChip, WidgetEmpty, WidgetError, } from './widgets/widget-card';
|
package/package.json
CHANGED
|
@@ -156,6 +156,16 @@ describe('DashboardGrid render', () => {
|
|
|
156
156
|
expect(screen.getByTestId('dashboard-empty')).toBeTruthy()
|
|
157
157
|
})
|
|
158
158
|
|
|
159
|
+
it('renders the animated mockup (decorative) in the empty state', () => {
|
|
160
|
+
render(<DashboardGrid widgets={[]} loadData={loaderOf({})} />)
|
|
161
|
+
const mock = screen.getByTestId('dashboard-empty-mockup')
|
|
162
|
+
expect(mock).toBeTruthy()
|
|
163
|
+
// Purely decorative → hidden from the a11y tree.
|
|
164
|
+
expect(mock.getAttribute('aria-hidden')).toBe('true')
|
|
165
|
+
// Keyframes are injected once into the document head.
|
|
166
|
+
expect(document.getElementById('mc-dashboard-empty-mockup-style')).toBeTruthy()
|
|
167
|
+
})
|
|
168
|
+
|
|
159
169
|
it('survives an empty → populated transition (React #310 regression)', async () => {
|
|
160
170
|
// The flatten/order useMemo must run BEFORE the empty-state early return.
|
|
161
171
|
// When it sat after the return, an empty render called one fewer hook
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// DashboardEmptyMockup — the animated empty state for the modular dashboard.
|
|
2
|
+
//
|
|
3
|
+
// Instead of a static "nothing here" card, we paint a silhouette of the
|
|
4
|
+
// dashboard itself: skeleton tiles (a big chart, a couple of stat cards, a
|
|
5
|
+
// bar, a list) that slowly drift and swap places, like puzzle pieces settling
|
|
6
|
+
// into a layout. The metaphor is "your board is getting ready", not "empty".
|
|
7
|
+
//
|
|
8
|
+
// Design constraints (kept deliberately simple so it ships from a package):
|
|
9
|
+
// - Pure CSS keyframes, no dependencies. One slow ~11s loop, subtle motion
|
|
10
|
+
// (small translate + scale, never opacity strobing).
|
|
11
|
+
// - Theme tokens only (bg-muted / border tokens) → light & dark for free.
|
|
12
|
+
// - `prefers-reduced-motion: reduce` freezes everything (tiles rest in place).
|
|
13
|
+
// - The whole mock is decorative → aria-hidden; the copy below carries the
|
|
14
|
+
// accessible meaning.
|
|
15
|
+
//
|
|
16
|
+
// The keyframes ship inline in a <style> tag rather than as Tailwind utilities:
|
|
17
|
+
// consumer apps scan only their OWN source for Tailwind classes, so arbitrary
|
|
18
|
+
// animation utilities declared here would never be generated in their build.
|
|
19
|
+
// Static tokens (bg-muted, rounded-lg…) are standard classes the host emits
|
|
20
|
+
// anyway, so those stay as className.
|
|
21
|
+
|
|
22
|
+
import * as React from 'react'
|
|
23
|
+
import { cn } from '@asteby/metacore-ui/lib'
|
|
24
|
+
|
|
25
|
+
// Scoped, collision-proof keyframe + class names (prefixed, unlikely to clash).
|
|
26
|
+
const STYLE_ID = 'mc-dashboard-empty-mockup-style'
|
|
27
|
+
|
|
28
|
+
const MOCKUP_CSS = `
|
|
29
|
+
.mc-demock{--mc-demock-dur:11s}
|
|
30
|
+
.mc-demock-tile{will-change:transform;transform-origin:center;
|
|
31
|
+
animation-duration:var(--mc-demock-dur);animation-timing-function:cubic-bezier(.65,0,.35,1);
|
|
32
|
+
animation-iteration-count:infinite}
|
|
33
|
+
.mc-demock-t1{animation-name:mc-demock-drift-a}
|
|
34
|
+
.mc-demock-t2{animation-name:mc-demock-drift-b;animation-delay:-1.4s}
|
|
35
|
+
.mc-demock-t3{animation-name:mc-demock-drift-c;animation-delay:-3.1s}
|
|
36
|
+
.mc-demock-t4{animation-name:mc-demock-drift-d;animation-delay:-2.2s}
|
|
37
|
+
.mc-demock-t5{animation-name:mc-demock-drift-e;animation-delay:-4.6s}
|
|
38
|
+
.mc-demock-shimmer{position:relative;overflow:hidden}
|
|
39
|
+
.mc-demock-shimmer::after{content:"";position:absolute;inset:0;
|
|
40
|
+
background:linear-gradient(105deg,transparent 30%,currentColor 50%,transparent 70%);
|
|
41
|
+
opacity:.05;transform:translateX(-100%);
|
|
42
|
+
animation:mc-demock-sheen var(--mc-demock-dur) ease-in-out infinite}
|
|
43
|
+
@keyframes mc-demock-drift-a{0%,100%{transform:translate3d(0,0,0) scale(1)}
|
|
44
|
+
50%{transform:translate3d(3%,4%,0) scale(1.015)}}
|
|
45
|
+
@keyframes mc-demock-drift-b{0%,100%{transform:translate3d(0,0,0) scale(1)}
|
|
46
|
+
50%{transform:translate3d(-4%,-3%,0) scale(.985)}}
|
|
47
|
+
@keyframes mc-demock-drift-c{0%,100%{transform:translate3d(0,0,0) scale(1)}
|
|
48
|
+
50%{transform:translate3d(5%,-2%,0) scale(1.02)}}
|
|
49
|
+
@keyframes mc-demock-drift-d{0%,100%{transform:translate3d(0,0,0) scale(1)}
|
|
50
|
+
50%{transform:translate3d(-3%,3%,0) scale(.99)}}
|
|
51
|
+
@keyframes mc-demock-drift-e{0%,100%{transform:translate3d(0,0,0) scale(1)}
|
|
52
|
+
50%{transform:translate3d(2%,-4%,0) scale(1.01)}}
|
|
53
|
+
@keyframes mc-demock-sheen{0%,100%{transform:translateX(-120%)}55%,70%{transform:translateX(120%)}}
|
|
54
|
+
@media (prefers-reduced-motion:reduce){
|
|
55
|
+
.mc-demock-tile{animation:none!important}
|
|
56
|
+
.mc-demock-shimmer::after{animation:none!important;opacity:0}
|
|
57
|
+
}
|
|
58
|
+
`
|
|
59
|
+
|
|
60
|
+
/** Injects the keyframes once per document (idempotent, SSR-safe). */
|
|
61
|
+
function useMockupStyles() {
|
|
62
|
+
React.useInsertionEffect(() => {
|
|
63
|
+
if (typeof document === 'undefined') return
|
|
64
|
+
if (document.getElementById(STYLE_ID)) return
|
|
65
|
+
const el = document.createElement('style')
|
|
66
|
+
el.id = STYLE_ID
|
|
67
|
+
el.textContent = MOCKUP_CSS
|
|
68
|
+
document.head.appendChild(el)
|
|
69
|
+
}, [])
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const tile = 'rounded-lg bg-muted mc-demock-tile mc-demock-shimmer text-foreground'
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Animated skeleton of a dashboard (chart + stats + bar + list) whose tiles
|
|
76
|
+
* slowly drift and settle. Purely decorative — always `aria-hidden`.
|
|
77
|
+
*/
|
|
78
|
+
export function DashboardEmptyMockup({ className }: { className?: string }) {
|
|
79
|
+
useMockupStyles()
|
|
80
|
+
return (
|
|
81
|
+
<div
|
|
82
|
+
aria-hidden="true"
|
|
83
|
+
data-testid="dashboard-empty-mockup"
|
|
84
|
+
className={cn('mc-demock pointer-events-none w-full max-w-md select-none', className)}
|
|
85
|
+
>
|
|
86
|
+
<div className="grid grid-cols-3 grid-rows-[repeat(3,minmax(0,1fr))] gap-3 [aspect-ratio:16/11]">
|
|
87
|
+
{/* Big chart tile */}
|
|
88
|
+
<div className={cn(tile, 'mc-demock-t1 col-span-2 row-span-2 flex flex-col justify-end gap-2 p-3')}>
|
|
89
|
+
<div className="flex items-end gap-1.5">
|
|
90
|
+
<div className="h-6 w-full rounded-sm bg-foreground/10" />
|
|
91
|
+
<div className="h-10 w-full rounded-sm bg-foreground/10" />
|
|
92
|
+
<div className="h-7 w-full rounded-sm bg-foreground/10" />
|
|
93
|
+
<div className="h-12 w-full rounded-sm bg-foreground/10" />
|
|
94
|
+
<div className="h-9 w-full rounded-sm bg-foreground/10" />
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
{/* Stat card */}
|
|
98
|
+
<div className={cn(tile, 'mc-demock-t2 flex flex-col justify-center gap-2 p-3')}>
|
|
99
|
+
<div className="h-2 w-1/2 rounded-full bg-foreground/15" />
|
|
100
|
+
<div className="h-4 w-2/3 rounded bg-foreground/15" />
|
|
101
|
+
</div>
|
|
102
|
+
{/* Stat card */}
|
|
103
|
+
<div className={cn(tile, 'mc-demock-t3 flex flex-col justify-center gap-2 p-3')}>
|
|
104
|
+
<div className="h-2 w-1/2 rounded-full bg-foreground/15" />
|
|
105
|
+
<div className="h-4 w-1/2 rounded bg-foreground/15" />
|
|
106
|
+
</div>
|
|
107
|
+
{/* List tile */}
|
|
108
|
+
<div className={cn(tile, 'mc-demock-t4 flex flex-col justify-center gap-2 p-3')}>
|
|
109
|
+
<div className="h-2 w-full rounded-full bg-foreground/12" />
|
|
110
|
+
<div className="h-2 w-4/5 rounded-full bg-foreground/12" />
|
|
111
|
+
<div className="h-2 w-2/3 rounded-full bg-foreground/12" />
|
|
112
|
+
</div>
|
|
113
|
+
{/* Progress bar tile */}
|
|
114
|
+
<div className={cn(tile, 'mc-demock-t5 col-span-2 flex items-center px-3')}>
|
|
115
|
+
<div className="h-2.5 w-full rounded-full bg-foreground/10">
|
|
116
|
+
<div className="h-full w-2/5 rounded-full bg-foreground/20" />
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
)
|
|
122
|
+
}
|
package/src/dashboard-grid.tsx
CHANGED
|
@@ -8,7 +8,6 @@ import * as React from 'react'
|
|
|
8
8
|
import { useTranslation } from 'react-i18next'
|
|
9
9
|
import { cn } from '@asteby/metacore-ui/lib'
|
|
10
10
|
import { useCan, usePermissionsActive } from './permissions-context'
|
|
11
|
-
import { DynamicIcon } from './dynamic-icon'
|
|
12
11
|
import type {
|
|
13
12
|
DashboardGridProps,
|
|
14
13
|
DashboardGridStrings,
|
|
@@ -16,11 +15,12 @@ import type {
|
|
|
16
15
|
DashboardWidgetSpec,
|
|
17
16
|
} from './dashboard-types'
|
|
18
17
|
import { WidgetRenderer, WidgetSkeleton, isTallWidget } from './widgets/widget-renderer'
|
|
18
|
+
import { DashboardEmptyMockup } from './dashboard-empty-mockup'
|
|
19
19
|
|
|
20
20
|
const DEFAULT_STRINGS: DashboardGridStrings = {
|
|
21
|
-
emptyTitle: 'Your dashboard is
|
|
21
|
+
emptyTitle: 'Your dashboard is taking shape',
|
|
22
22
|
emptyDescription:
|
|
23
|
-
'Install an addon with dashboard widgets
|
|
23
|
+
'Install an addon with dashboard widgets and your metrics will start living here.',
|
|
24
24
|
widgetError: 'Could not load this widget.',
|
|
25
25
|
widgetEmpty: 'No data yet.',
|
|
26
26
|
}
|
|
@@ -154,19 +154,19 @@ export function DashboardGrid({
|
|
|
154
154
|
<div
|
|
155
155
|
data-testid="dashboard-empty"
|
|
156
156
|
className={cn(
|
|
157
|
-
'flex min-h-[40vh] flex-col items-center justify-center rounded-xl border border-dashed border-border/60 p-10 text-center',
|
|
157
|
+
'flex min-h-[40vh] flex-col items-center justify-center gap-6 rounded-xl border border-dashed border-border/60 p-10 text-center',
|
|
158
158
|
className,
|
|
159
159
|
)}
|
|
160
160
|
>
|
|
161
|
-
<
|
|
162
|
-
|
|
161
|
+
<DashboardEmptyMockup />
|
|
162
|
+
<div className="flex flex-col items-center">
|
|
163
|
+
<h3 className="text-base font-semibold text-foreground">
|
|
164
|
+
{tr(undefined, s.emptyTitle) || s.emptyTitle}
|
|
165
|
+
</h3>
|
|
166
|
+
<p className="mt-1 max-w-sm text-sm text-muted-foreground">
|
|
167
|
+
{s.emptyDescription}
|
|
168
|
+
</p>
|
|
163
169
|
</div>
|
|
164
|
-
<h3 className="text-base font-semibold text-foreground">
|
|
165
|
-
{tr(undefined, s.emptyTitle) || s.emptyTitle}
|
|
166
|
-
</h3>
|
|
167
|
-
<p className="mt-1 max-w-sm text-sm text-muted-foreground">
|
|
168
|
-
{s.emptyDescription}
|
|
169
|
-
</p>
|
|
170
170
|
</div>
|
|
171
171
|
)
|
|
172
172
|
}
|