@conduction/docusaurus-preset 3.33.0 → 3.35.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/ANIMATION_ROADMAP.md +129 -0
- package/MISSING_COMPONENTS.md +15 -5
- package/package.json +3 -2
- package/src/components/AgentTrace/AgentTrace.jsx +26 -5
- package/src/components/AgentTrace/AgentTrace.module.css +41 -2
- package/src/components/AgentTrace/__tests__/AgentTrace.render.test.js +108 -0
- package/src/components/AppMock/AppMock.jsx +14 -5
- package/src/components/AppMock/AppMock.module.css +1702 -0
- package/src/components/AppMock/__tests__/AppMock.render.test.js +329 -0
- package/src/components/AppMock/variants/DeciDeskMock.jsx +46 -5
- package/src/components/AppMock/variants/DocuDeskMock.jsx +39 -9
- package/src/components/AppMock/variants/DoriathMock.jsx +28 -13
- package/src/components/AppMock/variants/HermiqMock.jsx +35 -15
- package/src/components/AppMock/variants/HrmqMock.jsx +46 -28
- package/src/components/AppMock/variants/LarpingAppMock.jsx +36 -7
- package/src/components/AppMock/variants/LaunchPadBiMock.jsx +12 -5
- package/src/components/AppMock/variants/LaunchPadTilesMock.jsx +6 -1
- package/src/components/AppMock/variants/LaunchPadWidgetsMock.jsx +17 -8
- package/src/components/AppMock/variants/NLDesignMock.jsx +15 -4
- package/src/components/AppMock/variants/OpenBuildMock.jsx +20 -5
- package/src/components/AppMock/variants/OpenCatalogiMock.jsx +23 -4
- package/src/components/AppMock/variants/OpenConnectorMock.jsx +25 -3
- package/src/components/AppMock/variants/OpenRegisterMock.jsx +13 -2
- package/src/components/AppMock/variants/PipelinQMock.jsx +12 -2
- package/src/components/AppMock/variants/PortaliqMock.jsx +21 -9
- package/src/components/AppMock/variants/ProcestMock.jsx +34 -7
- package/src/components/AppMock/variants/ScholiqMock.jsx +12 -6
- package/src/components/AppMock/variants/ShillinqMock.jsx +33 -17
- package/src/components/AppMock/variants/SoftwareCatalogMock.jsx +15 -5
- package/src/components/AppMock/variants/ZaakAfhandelAppMock.jsx +19 -4
- package/src/components/FlowMock/FlowMock.jsx +5 -3
- package/src/components/FlowMock/FlowMock.module.css +36 -9
- package/src/components/FlowMock/__tests__/FlowMock.render.test.js +83 -0
- package/src/components/LeafMock/LeafMock.jsx +23 -8
- package/src/components/LeafMock/LeafMock.module.css +123 -0
- package/src/components/LeafMock/__tests__/LeafMock.render.test.js +105 -0
- package/src/components/MockScene/MockScene.jsx +16 -2
- package/src/components/MockScene/MockScene.module.css +22 -2
- package/src/components/MockScene/__tests__/MockScene.render.test.js +101 -0
- package/src/components/SidebarMock/SidebarMock.jsx +29 -2
- package/src/components/SidebarMock/__tests__/SidebarMock.render.test.js +115 -0
- package/src/components/WidgetMock/WidgetMock.jsx +35 -14
- package/src/components/WidgetMock/__tests__/WidgetMock.render.test.js +125 -0
- package/src/components/WidgetMock/variants/DocuDeskAnonymise.jsx +6 -1
|
@@ -6,6 +6,13 @@
|
|
|
6
6
|
* (lead, qualified, proposal, won, lost) plus a KPI strip on top
|
|
7
7
|
* (pipeline value, win rate, deals this week). Left nav for kanban /
|
|
8
8
|
* customers / contacts / deals / quotes.
|
|
9
|
+
*
|
|
10
|
+
* Animated (7s loop, --am-dur): a deal closes — the proposal-column
|
|
11
|
+
* card collapses, the won card scales in with its mint bar, and the
|
|
12
|
+
* pipeline-value KPI widens. Resets by opacity swap. `running={false}`
|
|
13
|
+
* / prefers-reduced-motion show the closed state (won card in place,
|
|
14
|
+
* proposal card gone). Keyframes live in AppMock.module.css (`.plq`
|
|
15
|
+
* section).
|
|
9
16
|
*/
|
|
10
17
|
|
|
11
18
|
import React from 'react';
|
|
@@ -21,7 +28,7 @@ export default function PipelinQMock() {
|
|
|
21
28
|
<div className={styles.bell}></div>
|
|
22
29
|
<div className={styles.avatar}></div>
|
|
23
30
|
</div>
|
|
24
|
-
<div className={[styles.body, styles.decidesk].filter(Boolean).join(' ')}>
|
|
31
|
+
<div className={[styles.body, styles.decidesk, styles.plq].filter(Boolean).join(' ')}>
|
|
25
32
|
<div className={styles.nav}>
|
|
26
33
|
<div className={styles.navHead}><div className={styles.h}></div><div className={styles.l}></div></div>
|
|
27
34
|
{[true, false, false, false, false].map((active, i) => (
|
|
@@ -71,9 +78,12 @@ export default function PipelinQMock() {
|
|
|
71
78
|
<div className={styles.col}>
|
|
72
79
|
<div className={[styles.card, styles.c].join(' ')}></div>
|
|
73
80
|
<div className={styles.card}></div>
|
|
74
|
-
|
|
81
|
+
{/* The deal being closed — collapses out of proposal. */}
|
|
82
|
+
<div className={[styles.card, styles.dealGone].join(' ')}></div>
|
|
75
83
|
</div>
|
|
76
84
|
<div className={styles.col}>
|
|
85
|
+
{/* …and lands here as the won card (mint bar). */}
|
|
86
|
+
<div className={[styles.card, styles.dealWon].join(' ')}></div>
|
|
77
87
|
<div className={[styles.card, styles.c].join(' ')}></div>
|
|
78
88
|
</div>
|
|
79
89
|
<div className={styles.col}>
|
|
@@ -6,6 +6,14 @@
|
|
|
6
6
|
* row, then a submission form card next to a message inbox list. No
|
|
7
7
|
* left nav — the visitor gets one page, not an admin surface. Tones:
|
|
8
8
|
* cobalt for the frame, mint for the delivered/confirmed states.
|
|
9
|
+
*
|
|
10
|
+
* Animated (8s loop, --am-dur): a visitor works the form — focus ring
|
|
11
|
+
* and greeked value fill each field in turn, the submit button
|
|
12
|
+
* presses, a mint confirmation band sweeps the form card, and the
|
|
13
|
+
* message lands as a new row (mint avatar) at the top of the inbox.
|
|
14
|
+
* `running={false}` / prefers-reduced-motion show the static end
|
|
15
|
+
* state: filled form, delivered row. Keyframes live in
|
|
16
|
+
* AppMock.module.css (`.portaliq` section). No orange.
|
|
9
17
|
*/
|
|
10
18
|
|
|
11
19
|
import React from 'react';
|
|
@@ -21,7 +29,7 @@ export default function PortaliqMock() {
|
|
|
21
29
|
{Array.from({length: 3}).map((_, i) => <div key={i} className={styles.icon}></div>)}
|
|
22
30
|
<div className={styles.avatar}></div>
|
|
23
31
|
</div>
|
|
24
|
-
<div className={[styles.body, styles.decidesk].filter(Boolean).join(' ')}>
|
|
32
|
+
<div className={[styles.body, styles.decidesk, styles.portaliq].filter(Boolean).join(' ')}>
|
|
25
33
|
<div className={styles.col}>
|
|
26
34
|
{/* Portal masthead */}
|
|
27
35
|
<div className={styles.head}>
|
|
@@ -32,24 +40,28 @@ export default function PortaliqMock() {
|
|
|
32
40
|
</div>
|
|
33
41
|
</div>
|
|
34
42
|
<div className={styles.panelRow} style={{gridTemplateColumns: '1.2fr 1fr'}}>
|
|
35
|
-
{/* Submission form card
|
|
36
|
-
|
|
43
|
+
{/* Submission form card. Each .field carries a .fieldFill
|
|
44
|
+
bar that greeks the typed value; the sweep is the mint
|
|
45
|
+
confirmation band after submit. */}
|
|
46
|
+
<div className={[styles.panel, styles.formPanel].join(' ')}>
|
|
37
47
|
<div className={styles.head}><div className={styles.title}></div></div>
|
|
38
48
|
<div className={styles.row + ' ' + styles.head} style={{width: '25%', height: 4}}></div>
|
|
39
|
-
<div
|
|
49
|
+
<div className={[styles.field, styles['field-1']].join(' ')}><span className={styles.fieldFill}></span></div>
|
|
40
50
|
<div className={styles.row + ' ' + styles.head} style={{width: '30%', height: 4}}></div>
|
|
41
|
-
<div
|
|
51
|
+
<div className={[styles.field, styles['field-2']].join(' ')}><span className={styles.fieldFill}></span></div>
|
|
42
52
|
<div className={styles.row + ' ' + styles.head} style={{width: '20%', height: 4}}></div>
|
|
43
|
-
<div
|
|
53
|
+
<div className={[styles.field, styles['field-3'], styles.tall].join(' ')}><span className={styles.fieldFill}></span></div>
|
|
44
54
|
<div className={styles.actions} style={{justifyContent: 'flex-end', marginTop: 2}}>
|
|
45
|
-
<div className={styles.btn}></div>
|
|
55
|
+
<div className={[styles.btn, styles.submit].join(' ')}></div>
|
|
46
56
|
</div>
|
|
57
|
+
<span className={styles.confirmSweep} aria-hidden="true"></span>
|
|
47
58
|
</div>
|
|
48
|
-
{/* Message inbox list
|
|
59
|
+
{/* Message inbox list — the .newItem row is the submitted
|
|
60
|
+
message arriving (mint avatar = delivered). */}
|
|
49
61
|
<div className={[styles.panel, styles['w-mail']].join(' ')}>
|
|
50
62
|
<div className={styles.head}><div className={styles.title}></div></div>
|
|
51
63
|
<div className={styles.list}>
|
|
52
|
-
<div className={styles.item}><div className={styles.av}></div><div className={styles.lines}><div className={styles.l1}></div><div className={styles.l2}></div></div></div>
|
|
64
|
+
<div className={[styles.item, styles.newItem].join(' ')}><div className={styles.av}></div><div className={styles.lines}><div className={styles.l1}></div><div className={styles.l2}></div></div></div>
|
|
53
65
|
<div className={styles.item}><div className={styles.av}></div><div className={styles.lines}><div className={styles.l1}></div><div className={styles.l2}></div></div></div>
|
|
54
66
|
<div className={styles.item}><div className={styles.av}></div><div className={styles.lines}><div className={styles.l1}></div><div className={styles.l2}></div></div></div>
|
|
55
67
|
<div className={styles.item}><div className={styles.av}></div><div className={styles.lines}><div className={styles.l1}></div><div className={styles.l2}></div></div></div>
|
|
@@ -3,9 +3,15 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Inferred from app role (ZGW case-management for VTH and citizen
|
|
5
5
|
* processes): centre shows a canonical case detail with the timeline
|
|
6
|
-
* of stages running across the top (
|
|
6
|
+
* of stages running across the top (two done, one active, two to-do)
|
|
7
7
|
* plus a list of recent cases below. Left nav for cases, types,
|
|
8
8
|
* roles, decisions, archive.
|
|
9
|
+
*
|
|
10
|
+
* Animation (wave 2): the case advances one stage — ghost hexes
|
|
11
|
+
* overlaid on steps 2 and 3 replay the before-state (step 2 still
|
|
12
|
+
* orange-active, step 3 still to-do) and fade away to reveal the
|
|
13
|
+
* advanced base state; the advanced case's list row re-sorts to the
|
|
14
|
+
* top. Base styles are the advanced end state throughout.
|
|
9
15
|
*/
|
|
10
16
|
|
|
11
17
|
import React from 'react';
|
|
@@ -41,17 +47,21 @@ export default function ProcestMock({ sidebar = null }) {
|
|
|
41
47
|
<div className={styles.btn}></div>
|
|
42
48
|
</div>
|
|
43
49
|
</div>
|
|
50
|
+
{/* Base = advanced state: done, done, now, todo, todo.
|
|
51
|
+
Ghost hexes on steps 2+3 replay the before-state. */}
|
|
44
52
|
<div className={styles.timeline}>
|
|
45
53
|
<div className={styles.step}>
|
|
46
54
|
<div className={styles.h}></div>
|
|
47
55
|
<div className={styles.label}></div>
|
|
48
56
|
</div>
|
|
49
|
-
<div className={[styles.step, styles.
|
|
57
|
+
<div className={[styles.step, styles.advanceFrom].join(' ')}>
|
|
50
58
|
<div className={styles.h}></div>
|
|
59
|
+
<div className={[styles.ghost, styles.ghostNow].join(' ')}></div>
|
|
51
60
|
<div className={styles.label}></div>
|
|
52
61
|
</div>
|
|
53
|
-
<div className={[styles.step, styles.
|
|
62
|
+
<div className={[styles.step, styles.now, styles.advanceTo].join(' ')}>
|
|
54
63
|
<div className={styles.h}></div>
|
|
64
|
+
<div className={[styles.ghost, styles.ghostTodo].join(' ')}></div>
|
|
55
65
|
<div className={styles.label}></div>
|
|
56
66
|
</div>
|
|
57
67
|
<div className={[styles.step, styles.todo].join(' ')}>
|
|
@@ -66,16 +76,33 @@ export default function ProcestMock({ sidebar = null }) {
|
|
|
66
76
|
<div className={styles.panel}>
|
|
67
77
|
<div className={styles.head}><div className={styles.title}></div></div>
|
|
68
78
|
<div className={styles.stack}>
|
|
69
|
-
{
|
|
79
|
+
{/* DOM order = end state: the advanced case (status pill,
|
|
80
|
+
.sortUp) sits on top; the row it overtook (.sortDown)
|
|
81
|
+
is second. The keyframes replay the pre-sort order. */}
|
|
82
|
+
<div className={[styles.item, styles.sortUp].join(' ')}>
|
|
83
|
+
<div className={[styles.av, styles.b].join(' ')}></div>
|
|
84
|
+
<div className={styles.lines}>
|
|
85
|
+
<div className={styles.l1}></div>
|
|
86
|
+
<div className={styles.l2}></div>
|
|
87
|
+
</div>
|
|
88
|
+
<div className={styles.statusPill}>
|
|
89
|
+
<div className={styles.h}></div><div className={styles.t}></div>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
<div className={[styles.item, styles.sortDown].join(' ')}>
|
|
93
|
+
<div className={[styles.av, styles.a].filter(Boolean).join(' ')}></div>
|
|
94
|
+
<div className={styles.lines}>
|
|
95
|
+
<div className={styles.l1}></div>
|
|
96
|
+
<div className={styles.l2}></div>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
{['d','c','a'].map((cls, i) => (
|
|
70
100
|
<div key={i} className={styles.item}>
|
|
71
101
|
<div className={[styles.av, styles[cls]].join(' ')}></div>
|
|
72
102
|
<div className={styles.lines}>
|
|
73
103
|
<div className={styles.l1}></div>
|
|
74
104
|
<div className={styles.l2}></div>
|
|
75
105
|
</div>
|
|
76
|
-
<div className={[styles.statusPill, i === 1 && styles.beta].filter(Boolean).join(' ')}>
|
|
77
|
-
<div className={styles.h}></div><div className={styles.t}></div>
|
|
78
|
-
</div>
|
|
79
106
|
</div>
|
|
80
107
|
))}
|
|
81
108
|
</div>
|
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
* credential chip on the completed course. Left nav for courses /
|
|
7
7
|
* lessons / learners / certificates. Tone: forest — data you can
|
|
8
8
|
* trust a diploma to.
|
|
9
|
+
*
|
|
10
|
+
* Animated (6s loop, --am-dur): the six progress bars fill on a
|
|
11
|
+
* 250ms stagger (inline animation-delay), then the completed course's
|
|
12
|
+
* credential chip pops in with an overshoot. `running={false}` /
|
|
13
|
+
* prefers-reduced-motion show the filled bars and the chip. Keyframes
|
|
14
|
+
* live in AppMock.module.css (`.scholiq` section). No orange.
|
|
9
15
|
*/
|
|
10
16
|
|
|
11
17
|
import React from 'react';
|
|
@@ -30,7 +36,7 @@ export default function ScholiqMock() {
|
|
|
30
36
|
<div className={styles.bell}></div>
|
|
31
37
|
<div className={styles.avatar}></div>
|
|
32
38
|
</div>
|
|
33
|
-
<div className={[styles.body, styles.opencatalogi].filter(Boolean).join(' ')}>
|
|
39
|
+
<div className={[styles.body, styles.opencatalogi, styles.scholiq].filter(Boolean).join(' ')}>
|
|
34
40
|
<div className={styles.nav}>
|
|
35
41
|
<div className={styles.navHead}><div className={styles.h}></div><div className={styles.l}></div></div>
|
|
36
42
|
{[true, false, false, false].map((active, i) => (
|
|
@@ -54,14 +60,14 @@ export default function ScholiqMock() {
|
|
|
54
60
|
<div className={styles.ico}></div>
|
|
55
61
|
<div className={styles.row}></div>
|
|
56
62
|
<div className={styles.row + ' ' + styles.short}></div>
|
|
57
|
-
<div
|
|
58
|
-
<div style={{width: fill,
|
|
63
|
+
<div className={styles.progress}>
|
|
64
|
+
<div className={styles.progressFill} style={{width: fill, animationDelay: `${i * 250}ms`}}></div>
|
|
59
65
|
</div>
|
|
60
66
|
{done && (
|
|
61
67
|
/* Credential chip — course completed, certificate issued */
|
|
62
|
-
<div
|
|
63
|
-
<div
|
|
64
|
-
<div
|
|
68
|
+
<div className={styles.credChip}>
|
|
69
|
+
<div className={styles.credHex}></div>
|
|
70
|
+
<div className={styles.credBar}></div>
|
|
65
71
|
</div>
|
|
66
72
|
)}
|
|
67
73
|
</div>
|
|
@@ -6,15 +6,23 @@
|
|
|
6
6
|
* description, two amount columns (net / VAT), and a reconciliation
|
|
7
7
|
* tick column where a mint hex = matched against the bank and a
|
|
8
8
|
* muted pip = still open. Tone: terracotta — documents, human work.
|
|
9
|
+
*
|
|
10
|
+
* Animation (wave 2): a bank statement line slides down onto invoice
|
|
11
|
+
* row 3, both tick mint (reconciled), the totals line and the first
|
|
12
|
+
* KPI grow to absorb the match. Base styles are the reconciled end
|
|
13
|
+
* state; keyframes replay the unmatched before-state and the slide.
|
|
9
14
|
*/
|
|
10
15
|
|
|
11
16
|
import React from 'react';
|
|
12
17
|
import styles from '../AppMock.module.css';
|
|
13
18
|
|
|
19
|
+
/* Row 3 (index 2) is the row the bank line reconciles: its tick is
|
|
20
|
+
mint in the base (end) state and the amShqTick keyframe replays
|
|
21
|
+
the open cobalt-200 before-state. Row 5 stays open. */
|
|
14
22
|
const INVOICES = [
|
|
15
23
|
{amount: 26, vat: 16, matched: true},
|
|
16
24
|
{amount: 20, vat: 12, matched: true},
|
|
17
|
-
{amount: 30, vat: 18, matched:
|
|
25
|
+
{amount: 30, vat: 18, matched: true, reconciling: true},
|
|
18
26
|
{amount: 22, vat: 14, matched: true},
|
|
19
27
|
{amount: 18, vat: 10, matched: false},
|
|
20
28
|
];
|
|
@@ -29,7 +37,7 @@ export default function ShillinqMock() {
|
|
|
29
37
|
<div className={styles.bell}></div>
|
|
30
38
|
<div className={styles.avatar}></div>
|
|
31
39
|
</div>
|
|
32
|
-
<div className={[styles.body, styles.decidesk].filter(Boolean).join(' ')}>
|
|
40
|
+
<div className={[styles.body, styles.decidesk, styles.shillinq].filter(Boolean).join(' ')}>
|
|
33
41
|
<div className={styles.nav}>
|
|
34
42
|
<div className={styles.navHead}><div className={styles.h}></div><div className={styles.l}></div></div>
|
|
35
43
|
{[true, false, false, false, false].map((active, i) => (
|
|
@@ -47,14 +55,15 @@ export default function ShillinqMock() {
|
|
|
47
55
|
<div className={styles.btn}></div>
|
|
48
56
|
</div>
|
|
49
57
|
</div>
|
|
50
|
-
{/* VAT / summary strip
|
|
58
|
+
{/* VAT / summary strip. The first KPI's number widens when
|
|
59
|
+
the reconciliation lands. */}
|
|
51
60
|
<div className={styles.kpiRow}>
|
|
52
|
-
<div className={styles.kpi}>
|
|
53
|
-
<div className={styles.ico
|
|
61
|
+
<div className={[styles.kpi, styles.kpiGrow].join(' ')}>
|
|
62
|
+
<div className={[styles.ico, styles.terra].join(' ')}></div>
|
|
54
63
|
<div className={styles.meta}><div className={styles.num}></div><div className={styles.label}></div></div>
|
|
55
64
|
</div>
|
|
56
65
|
<div className={styles.kpi}>
|
|
57
|
-
<div className={styles.ico
|
|
66
|
+
<div className={[styles.ico, styles.terraSoft].join(' ')}></div>
|
|
58
67
|
<div className={styles.meta}><div className={styles.num}></div><div className={styles.label}></div></div>
|
|
59
68
|
</div>
|
|
60
69
|
<div className={styles.kpi}>
|
|
@@ -63,24 +72,31 @@ export default function ShillinqMock() {
|
|
|
63
72
|
</div>
|
|
64
73
|
</div>
|
|
65
74
|
{/* Invoice table: doc · description · net · VAT · reconciled */}
|
|
66
|
-
<div className={styles.panel
|
|
75
|
+
<div className={[styles.panel, styles.ledger].join(' ')}>
|
|
67
76
|
<div className={styles.head}><div className={styles.title}></div></div>
|
|
77
|
+
{/* Bank statement line — slides onto row 3 and dissolves
|
|
78
|
+
into the mint tick. Hidden at rest and in static frames. */}
|
|
79
|
+
<div className={styles.bankLine}>
|
|
80
|
+
<div className={styles.bIco}></div>
|
|
81
|
+
<div className={styles.bBar}></div>
|
|
82
|
+
<div className={styles.bAmt}></div>
|
|
83
|
+
</div>
|
|
68
84
|
<div className={styles.stack}>
|
|
69
|
-
{INVOICES.map(({amount, vat, matched}, i) => (
|
|
70
|
-
<div key={i} className={styles.item}>
|
|
71
|
-
<div
|
|
85
|
+
{INVOICES.map(({amount, vat, matched, reconciling}, i) => (
|
|
86
|
+
<div key={i} className={[styles.item, styles.inv, reconciling && styles.reconciling].filter(Boolean).join(' ')}>
|
|
87
|
+
<div className={styles.doc}></div>
|
|
72
88
|
<div className={styles.lines}><div className={styles.l1}></div></div>
|
|
73
|
-
<div style={{width: amount
|
|
74
|
-
<div style={{width: vat
|
|
89
|
+
<div className={styles.net} style={{width: amount}}></div>
|
|
90
|
+
<div className={styles.vat} style={{width: vat}}></div>
|
|
75
91
|
{/* Bank-reconciliation tick: mint hex = matched */}
|
|
76
|
-
<div
|
|
92
|
+
<div className={[styles.tick, !matched && styles.open].filter(Boolean).join(' ')}></div>
|
|
77
93
|
</div>
|
|
78
94
|
))}
|
|
79
95
|
</div>
|
|
80
|
-
{/* Totals line */}
|
|
81
|
-
<div
|
|
82
|
-
<div
|
|
83
|
-
<div
|
|
96
|
+
{/* Totals line — grows when the match lands */}
|
|
97
|
+
<div className={styles.totals}>
|
|
98
|
+
<div className={styles.totNet}></div>
|
|
99
|
+
<div className={styles.totVat}></div>
|
|
84
100
|
</div>
|
|
85
101
|
</div>
|
|
86
102
|
</div>
|
|
@@ -6,6 +6,13 @@
|
|
|
6
6
|
* (mint = stable, orange = update available, red = end-of-life) plus
|
|
7
7
|
* a graph card on top showing licence-renewal timeline. Left nav for
|
|
8
8
|
* apps / licences / contracts / dependencies / discovery / archive.
|
|
9
|
+
*
|
|
10
|
+
* Animation (wave 4, the CVE scan): a scan band sweeps down the app
|
|
11
|
+
* list, row 4's pip flips to red as the sweep crosses it (a CVE
|
|
12
|
+
* found), and the matching chart bar surges in vermillion. The chart
|
|
13
|
+
* keeps its single KNVB-orange accent bar; the CVE reading is
|
|
14
|
+
* red-family throughout so the component stays at one orange.
|
|
15
|
+
* Base styles are the post-scan end state (red pip, tall CVE bar).
|
|
9
16
|
*/
|
|
10
17
|
|
|
11
18
|
import React from 'react';
|
|
@@ -21,7 +28,7 @@ export default function SoftwareCatalogMock() {
|
|
|
21
28
|
<div className={styles.bell}></div>
|
|
22
29
|
<div className={styles.avatar}></div>
|
|
23
30
|
</div>
|
|
24
|
-
<div className={[styles.body, styles.openregister].filter(Boolean).join(' ')}>
|
|
31
|
+
<div className={[styles.body, styles.openregister, styles.swc].filter(Boolean).join(' ')}>
|
|
25
32
|
<div className={styles.nav}>
|
|
26
33
|
<div className={styles.navHead}><div className={styles.h}></div><div className={styles.l}></div></div>
|
|
27
34
|
{[true, false, false, false, false, false].map((active, i) => (
|
|
@@ -40,7 +47,8 @@ export default function SoftwareCatalogMock() {
|
|
|
40
47
|
<div className={styles.bars}>
|
|
41
48
|
<div className={styles.bar} style={{height: '30%'}}></div>
|
|
42
49
|
<div className={styles.bar} style={{height: '50%'}}></div>
|
|
43
|
-
|
|
50
|
+
{/* CVE bar — surges (vermillion, red family) post-scan */}
|
|
51
|
+
<div className={[styles.bar, styles.cve].join(' ')} style={{height: '64%'}}></div>
|
|
44
52
|
<div className={styles.bar} style={{height: '70%'}}></div>
|
|
45
53
|
<div className={[styles.bar, styles.accent].join(' ')} style={{height: '88%'}}></div>
|
|
46
54
|
<div className={styles.bar} style={{height: '60%'}}></div>
|
|
@@ -49,16 +57,18 @@ export default function SoftwareCatalogMock() {
|
|
|
49
57
|
</div>
|
|
50
58
|
<div className={styles.axis}></div>
|
|
51
59
|
</div>
|
|
52
|
-
{/* App list with status pips
|
|
53
|
-
|
|
60
|
+
{/* App list with status pips. The scan band sweeps this
|
|
61
|
+
panel; row 4 (.blocked, .cveHit) flips red as it passes. */}
|
|
62
|
+
<div className={[styles.w, styles['w-jira'], styles.scanPanel].join(' ')}>
|
|
54
63
|
<div className={styles.wHead}>
|
|
55
64
|
<div className={styles.h}></div><div className={styles.t}></div>
|
|
56
65
|
</div>
|
|
66
|
+
<div className={styles.scanBand}></div>
|
|
57
67
|
<div className={styles.list}>
|
|
58
68
|
<div className={styles.item}><div className={styles.id}></div><div className={styles.l}></div><div className={styles.pip}></div></div>
|
|
59
69
|
<div className={[styles.item, styles.review].join(' ')}><div className={styles.id}></div><div className={styles.l}></div><div className={styles.pip}></div></div>
|
|
60
70
|
<div className={styles.item}><div className={styles.id}></div><div className={styles.l}></div><div className={styles.pip}></div></div>
|
|
61
|
-
<div className={[styles.item, styles.blocked].join(' ')}><div className={styles.id}></div><div className={styles.l}></div><div className={styles.pip}></div></div>
|
|
71
|
+
<div className={[styles.item, styles.blocked, styles.cveHit].join(' ')}><div className={styles.id}></div><div className={styles.l}></div><div className={styles.pip}></div></div>
|
|
62
72
|
<div className={styles.item}><div className={styles.id}></div><div className={styles.l}></div><div className={styles.pip}></div></div>
|
|
63
73
|
<div className={styles.item}><div className={styles.id}></div><div className={styles.l}></div><div className={styles.pip}></div></div>
|
|
64
74
|
<div className={[styles.item, styles.todo].join(' ')}><div className={styles.id}></div><div className={styles.l}></div><div className={styles.pip}></div></div>
|
|
@@ -6,6 +6,13 @@
|
|
|
6
6
|
* (werkvoorraad widget) with stage pips, plus a citizen-side timeline
|
|
7
7
|
* of recent submissions. Left nav for queue / archive / public portal /
|
|
8
8
|
* audit / settings.
|
|
9
|
+
*
|
|
10
|
+
* Animation (wave 4): a citizen submission travels up from the
|
|
11
|
+
* submissions list into the werkvoorraad — the ghost row rises, and
|
|
12
|
+
* the new case row at the top of the queue gains its stage hex and
|
|
13
|
+
* assignee avatar as it lands. The late row's red stage pip pulses
|
|
14
|
+
* once per loop as the SLA reminder. Base styles are the assigned
|
|
15
|
+
* end state (new case present, fully dressed).
|
|
9
16
|
*/
|
|
10
17
|
|
|
11
18
|
import React from 'react';
|
|
@@ -21,7 +28,7 @@ export default function ZaakAfhandelAppMock() {
|
|
|
21
28
|
<div className={styles.bell}></div>
|
|
22
29
|
<div className={styles.avatar}></div>
|
|
23
30
|
</div>
|
|
24
|
-
<div className={[styles.body, styles.procest].filter(Boolean).join(' ')}>
|
|
31
|
+
<div className={[styles.body, styles.procest, styles.zaak].filter(Boolean).join(' ')}>
|
|
25
32
|
<div className={styles.nav}>
|
|
26
33
|
<div className={styles.navHead}><div className={styles.h}></div><div className={styles.l}></div></div>
|
|
27
34
|
{[true, false, false, false, false].map((active, i) => (
|
|
@@ -39,26 +46,34 @@ export default function ZaakAfhandelAppMock() {
|
|
|
39
46
|
<div className={styles.btn}></div>
|
|
40
47
|
</div>
|
|
41
48
|
</div>
|
|
42
|
-
{/* Case-worker werkvoorraad
|
|
49
|
+
{/* Case-worker werkvoorraad. The first row (.zkNew) is the
|
|
50
|
+
citizen submission after landing: its stage hex and
|
|
51
|
+
assignee avatar dress it as it arrives. */}
|
|
43
52
|
<div className={[styles.w, styles['w-werkvoorraad']].join(' ')}>
|
|
44
53
|
<div className={styles.wHead}>
|
|
45
54
|
<div className={styles.h}></div><div className={styles.t}></div>
|
|
46
55
|
</div>
|
|
47
56
|
<div className={styles.list}>
|
|
57
|
+
<div className={[styles.item, styles.zkNew].join(' ')}><div className={styles.stage}></div><div className={styles.l1}></div><div className={[styles.av, styles.b].join(' ')}></div></div>
|
|
48
58
|
<div className={[styles.item, styles.now].join(' ')}><div className={styles.stage}></div><div className={styles.l1}></div><div className={styles.av}></div></div>
|
|
49
59
|
<div className={styles.item}><div className={styles.stage}></div><div className={styles.l1}></div><div className={[styles.av, styles.b].join(' ')}></div></div>
|
|
50
60
|
<div className={[styles.item, styles.late].join(' ')}><div className={styles.stage}></div><div className={styles.l1}></div><div className={[styles.av, styles.c].join(' ')}></div></div>
|
|
51
61
|
<div className={styles.item}><div className={styles.stage}></div><div className={styles.l1}></div><div className={styles.av}></div></div>
|
|
52
|
-
<div className={styles.item}><div className={styles.stage}></div><div className={styles.l1}></div><div className={[styles.av, styles.b].join(' ')}></div></div>
|
|
53
62
|
</div>
|
|
54
63
|
</div>
|
|
64
|
+
{/* Travelling submission ghost — rises from the submissions
|
|
65
|
+
list into the werkvoorraad. Hidden at rest + static. */}
|
|
66
|
+
<div className={styles.zkGhost}>
|
|
67
|
+
<div className={styles.src}></div>
|
|
68
|
+
<div className={styles.gBar}></div>
|
|
69
|
+
</div>
|
|
55
70
|
{/* Citizen-side recent submissions list */}
|
|
56
71
|
<div className={[styles.w, styles['w-rss']].join(' ')}>
|
|
57
72
|
<div className={styles.wHead}>
|
|
58
73
|
<div className={styles.h}></div><div className={styles.t}></div>
|
|
59
74
|
</div>
|
|
60
75
|
<div className={styles.list}>
|
|
61
|
-
<div className={styles.item}><div className={styles.src}></div><div className={styles.title}></div><div className={styles.when}></div></div>
|
|
76
|
+
<div className={[styles.item, styles.zkSent].join(' ')}><div className={styles.src}></div><div className={styles.title}></div><div className={styles.when}></div></div>
|
|
62
77
|
<div className={styles.item}><div className={styles.src}></div><div className={styles.title}></div><div className={styles.when}></div></div>
|
|
63
78
|
<div className={styles.item}><div className={styles.src}></div><div className={styles.title}></div><div className={styles.when}></div></div>
|
|
64
79
|
<div className={styles.item}><div className={styles.src}></div><div className={styles.title}></div><div className={styles.when}></div></div>
|
|
@@ -12,11 +12,13 @@
|
|
|
12
12
|
* Five nodes: trigger → two parallel steps → approval (join) → end.
|
|
13
13
|
* A mint run-line travels the trigger → step → approval → end route
|
|
14
14
|
* on a slow loop, with a subtle halo pulse on each node as the line
|
|
15
|
-
* passes.
|
|
16
|
-
*
|
|
15
|
+
* passes. At the approval node the run HOLDS for ~600ms while an
|
|
16
|
+
* orange halo flashes — a human decides here. `prefers-reduced-motion:
|
|
17
|
+
* reduce` (and `running={false}`) renders the full route as a static
|
|
18
|
+
* mint path with no animation and no flash.
|
|
17
19
|
*
|
|
18
20
|
* Tokens only; no images; rectangles (the nodes *do* work, so no
|
|
19
|
-
* hexes);
|
|
21
|
+
* hexes); the approval hold flash is the single orange accent.
|
|
20
22
|
*
|
|
21
23
|
* Usage:
|
|
22
24
|
*
|
|
@@ -9,7 +9,10 @@
|
|
|
9
9
|
* transparent to layout.
|
|
10
10
|
*
|
|
11
11
|
* Tokens only. Node bars: mint = trigger, lavender = steps/approval,
|
|
12
|
-
* vermillion = end. The run-line is mint (--c-mint-500)
|
|
12
|
+
* vermillion = end. The run-line is mint (--c-mint-500). The run
|
|
13
|
+
* holds ~600ms at the approval node while an orange halo flashes
|
|
14
|
+
* there — "a human decides here" — and that flash is this
|
|
15
|
+
* component's single orange accent.
|
|
13
16
|
*/
|
|
14
17
|
|
|
15
18
|
.fm { display: contents; }
|
|
@@ -102,7 +105,13 @@
|
|
|
102
105
|
* travels the full route once per cycle. Animating the offset from
|
|
103
106
|
* 1.28 to 0 shifts the pattern forward exactly one period, so the
|
|
104
107
|
* loop is seamless (the segment exits the end node as its successor
|
|
105
|
-
* enters the trigger).
|
|
108
|
+
* enters the trigger).
|
|
109
|
+
*
|
|
110
|
+
* The offset keyframes are not linear end-to-end: the run reaches the
|
|
111
|
+
* approval node at 58% (offset 1.28 × (1 − 0.62) ≈ 0.487, the node
|
|
112
|
+
* sitting ~62% along the route) and HOLDS there for ~600ms (8.6% of
|
|
113
|
+
* the 7s cycle) — approval is where a human decides — before
|
|
114
|
+
* continuing to the end node. */
|
|
106
115
|
|
|
107
116
|
.fm .runline {
|
|
108
117
|
fill: none;
|
|
@@ -115,13 +124,17 @@
|
|
|
115
124
|
}
|
|
116
125
|
|
|
117
126
|
@keyframes fmRun {
|
|
118
|
-
|
|
119
|
-
|
|
127
|
+
0% { stroke-dashoffset: 1.28; }
|
|
128
|
+
58% { stroke-dashoffset: 0.487; } /* segment reaches the approval node */
|
|
129
|
+
66.6% { stroke-dashoffset: 0.487; } /* ~600ms hold: a human decides */
|
|
130
|
+
100% { stroke-dashoffset: 0; }
|
|
120
131
|
}
|
|
121
132
|
|
|
122
133
|
/* Halo pulse on the nodes the run-line passes. Delays approximate
|
|
123
|
-
where along the
|
|
124
|
-
approval
|
|
134
|
+
where along the (held) cycle each node sits: trigger 0%, step ~28%,
|
|
135
|
+
approval 58% (the hold), end ~87%. The approval halo is the
|
|
136
|
+
exception: it flashes KNVB orange for the length of the hold — the
|
|
137
|
+
component's one orange accent. */
|
|
125
138
|
.fm .halo {
|
|
126
139
|
fill: none;
|
|
127
140
|
stroke: var(--c-mint-500);
|
|
@@ -130,9 +143,13 @@
|
|
|
130
143
|
animation: fmPulse var(--fm-dur, 7s) linear infinite;
|
|
131
144
|
}
|
|
132
145
|
.fm .halo-0 { animation-delay: 0s; }
|
|
133
|
-
.fm .halo-1 { animation-delay: calc(var(--fm-dur, 7s) * 0.
|
|
134
|
-
.fm .halo-2 {
|
|
135
|
-
|
|
146
|
+
.fm .halo-1 { animation-delay: calc(var(--fm-dur, 7s) * 0.28); }
|
|
147
|
+
.fm .halo-2 {
|
|
148
|
+
stroke: var(--c-orange-knvb);
|
|
149
|
+
animation-name: fmPulseHold;
|
|
150
|
+
animation-delay: calc(var(--fm-dur, 7s) * 0.58);
|
|
151
|
+
}
|
|
152
|
+
.fm .halo-3 { animation-delay: calc(var(--fm-dur, 7s) * 0.87); }
|
|
136
153
|
|
|
137
154
|
@keyframes fmPulse {
|
|
138
155
|
0% { opacity: 0; }
|
|
@@ -141,6 +158,16 @@
|
|
|
141
158
|
100% { opacity: 0; }
|
|
142
159
|
}
|
|
143
160
|
|
|
161
|
+
/* The approval flash spans the whole ~600ms hold (8.6% of the cycle)
|
|
162
|
+
instead of the quick pass-through pulse. */
|
|
163
|
+
@keyframes fmPulseHold {
|
|
164
|
+
0% { opacity: 0; }
|
|
165
|
+
1.5% { opacity: 0.8; }
|
|
166
|
+
8% { opacity: 0.8; }
|
|
167
|
+
11% { opacity: 0; }
|
|
168
|
+
100% { opacity: 0; }
|
|
169
|
+
}
|
|
170
|
+
|
|
144
171
|
/* ---- Static renderings ----
|
|
145
172
|
*
|
|
146
173
|
* Both `running={false}` (the .static class) and the user's
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FlowMock.render.test.js — SSR-renders the real <FlowMock> and
|
|
3
|
+
* asserts the canvas structure the approval-hold animation relies on:
|
|
4
|
+
* the run-line with pathLength="1", the four halo rects (halo-2 being
|
|
5
|
+
* the approval flash), and the `running` prop's static class. Same
|
|
6
|
+
* esbuild harness as KanbanMock.render.test.js; CSS modules stubbed
|
|
7
|
+
* to identity.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
const test = require('node:test');
|
|
13
|
+
const {before, after} = test;
|
|
14
|
+
const assert = require('node:assert/strict');
|
|
15
|
+
const path = require('node:path');
|
|
16
|
+
const fs = require('node:fs/promises');
|
|
17
|
+
const {build} = require('esbuild');
|
|
18
|
+
const React = require('react');
|
|
19
|
+
const {renderToStaticMarkup} = require('react-dom/server');
|
|
20
|
+
|
|
21
|
+
const COMPONENT = path.resolve(__dirname, '..', 'FlowMock.jsx');
|
|
22
|
+
const PRESET_ROOT = path.resolve(__dirname, '..', '..', '..', '..');
|
|
23
|
+
const SCRATCH = path.join(PRESET_ROOT, '.tmp-flow-mock-test');
|
|
24
|
+
|
|
25
|
+
const cssModuleStub = {
|
|
26
|
+
name: 'css-module-stub',
|
|
27
|
+
setup(b) {
|
|
28
|
+
b.onResolve({filter: /\.module\.css$/}, (args) => ({path: args.path, namespace: 'css-stub'}));
|
|
29
|
+
b.onLoad({filter: /.*/, namespace: 'css-stub'}, () => ({
|
|
30
|
+
contents: 'export default new Proxy({}, {get: (_, p) => p});',
|
|
31
|
+
loader: 'js',
|
|
32
|
+
}));
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
let FlowMock;
|
|
37
|
+
|
|
38
|
+
before(async () => {
|
|
39
|
+
await fs.mkdir(SCRATCH, {recursive: true});
|
|
40
|
+
const tmpDir = await fs.mkdtemp(path.join(SCRATCH, 'run-'));
|
|
41
|
+
const outFile = path.join(tmpDir, 'bundle.cjs');
|
|
42
|
+
await build({
|
|
43
|
+
entryPoints: [COMPONENT],
|
|
44
|
+
outfile: outFile,
|
|
45
|
+
bundle: true,
|
|
46
|
+
format: 'cjs',
|
|
47
|
+
jsx: 'automatic',
|
|
48
|
+
jsxImportSource: 'react',
|
|
49
|
+
tsconfigRaw: {compilerOptions: {jsx: 'react-jsx', jsxImportSource: 'react'}},
|
|
50
|
+
platform: 'node',
|
|
51
|
+
external: ['react'],
|
|
52
|
+
plugins: [cssModuleStub],
|
|
53
|
+
logLevel: 'warning',
|
|
54
|
+
});
|
|
55
|
+
delete require.cache[require.resolve(outFile)];
|
|
56
|
+
FlowMock = require(outFile).default;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
after(async () => {
|
|
60
|
+
await fs.rm(SCRATCH, {recursive: true, force: true});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
function render(props) {
|
|
64
|
+
return renderToStaticMarkup(React.createElement(FlowMock, props));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
test('SSR smoke: renders the canvas with the run-line on pathLength="1"', () => {
|
|
68
|
+
const html = render({});
|
|
69
|
+
assert.match(html, /class="canvas"/);
|
|
70
|
+
assert.match(html, /class="runline"[^>]*pathLength="1"/);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('halos: four pulse rects, halo-2 (the approval hold flash) among them', () => {
|
|
74
|
+
const html = render({});
|
|
75
|
+
for (const i of [0, 1, 2, 3]) {
|
|
76
|
+
assert.match(html, new RegExp(`halo halo-${i}`), `missing halo-${i}`);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('running={false} renders the static frame class', () => {
|
|
81
|
+
assert.match(render({running: false}), /class="frame size-md static"/);
|
|
82
|
+
assert.doesNotMatch(render({}), /\bstatic\b/);
|
|
83
|
+
});
|