@conduction/docusaurus-preset 3.33.0 → 3.34.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/MISSING_COMPONENTS.md +5 -4
- package/package.json +1 -1
- 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 +439 -0
- package/src/components/AppMock/__tests__/AppMock.render.test.js +181 -0
- package/src/components/AppMock/variants/LarpingAppMock.jsx +23 -4
- 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/PipelinQMock.jsx +12 -2
- package/src/components/AppMock/variants/PortaliqMock.jsx +21 -9
- package/src/components/AppMock/variants/ScholiqMock.jsx +12 -6
- 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
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
.am .figure { margin: 0; display: inline-flex; flex-direction: column; gap: var(--space-2); width: 100%; max-width: 100%; }
|
|
31
31
|
|
|
32
32
|
.am .frame {
|
|
33
|
+
/* Shared animation timeline token. Every variant keyframe below runs
|
|
34
|
+
on var(--am-dur); a variant overrides it on its own body class
|
|
35
|
+
(e.g. `.nldesign { --am-dur: 6s; }`). The KanbanMock convention. */
|
|
36
|
+
--am-dur: 8s;
|
|
33
37
|
width: 100%;
|
|
34
38
|
max-width: 720px;
|
|
35
39
|
aspect-ratio: 16 / 10;
|
|
@@ -700,3 +704,438 @@
|
|
|
700
704
|
.am .procest .timeline .step.todo .h { background: var(--c-cobalt-100); }
|
|
701
705
|
.am .procest .timeline .step.now .h { background: var(--c-orange-knvb); }
|
|
702
706
|
.am .procest .timeline .step .label { height: 3px; width: 60%; background: var(--c-cobalt-400); border-radius: 1px; }
|
|
707
|
+
|
|
708
|
+
/* ======================================================================
|
|
709
|
+
Variant animations
|
|
710
|
+
======================================================================
|
|
711
|
+
|
|
712
|
+
Every timeline runs on var(--am-dur) (declared on `.am .frame`,
|
|
713
|
+
overridden per variant body class). Conventions, copied from
|
|
714
|
+
KanbanMock.module.css:
|
|
715
|
+
|
|
716
|
+
- Base styles are the animation's meaningful END state, so freezing
|
|
717
|
+
(running={false} → `.static` on the frame, or the user's
|
|
718
|
+
prefers-reduced-motion) is `animation: none` plus a couple of
|
|
719
|
+
explicit overrides.
|
|
720
|
+
- Loops reset via opacity swaps (fade out at ~92–98%, re-enter from
|
|
721
|
+
the 0% frame), never a visible rewind. 0% and 100% frames are both
|
|
722
|
+
hidden or identical, so per-element `animation-delay` staggers stay
|
|
723
|
+
seamless in steady state.
|
|
724
|
+
- `animation-fill-mode: both` keeps delayed elements on their hidden
|
|
725
|
+
0% frame during the initial delay.
|
|
726
|
+
- Tokens only; no new orange is introduced by any animation. */
|
|
727
|
+
|
|
728
|
+
/* ---- Portaliq — a visitor fills the form, submits, the message
|
|
729
|
+
lands in the inbox. Timeline (8s): focus+fill field 1 (3–19%),
|
|
730
|
+
field 2 (21–37%), field 3 (39–55%), submit press (58–63%), mint
|
|
731
|
+
confirm sweep across the form (63–72%), inbox row arrives (74–80%),
|
|
732
|
+
hold, fade-reset (94–100%). ---- */
|
|
733
|
+
|
|
734
|
+
.am .portaliq .field {
|
|
735
|
+
position: relative;
|
|
736
|
+
height: 14px;
|
|
737
|
+
background: white;
|
|
738
|
+
border: 1px solid var(--c-cobalt-200);
|
|
739
|
+
border-radius: 3px;
|
|
740
|
+
overflow: hidden;
|
|
741
|
+
}
|
|
742
|
+
.am .portaliq .field.tall { height: 26px; }
|
|
743
|
+
.am .portaliq .fieldFill {
|
|
744
|
+
position: absolute;
|
|
745
|
+
left: 3px;
|
|
746
|
+
top: 4px;
|
|
747
|
+
height: 4px;
|
|
748
|
+
border-radius: 1px;
|
|
749
|
+
background: var(--c-cobalt-300);
|
|
750
|
+
transform-origin: left center;
|
|
751
|
+
}
|
|
752
|
+
.am .portaliq .field-1 .fieldFill { width: 62%; animation: amPqFill1 var(--am-dur) linear infinite both; }
|
|
753
|
+
.am .portaliq .field-2 .fieldFill { width: 48%; animation: amPqFill2 var(--am-dur) linear infinite both; }
|
|
754
|
+
.am .portaliq .field-3 .fieldFill { width: 78%; animation: amPqFill3 var(--am-dur) linear infinite both; }
|
|
755
|
+
.am .portaliq .field-1 { animation: amPqFocus1 var(--am-dur) linear infinite; }
|
|
756
|
+
.am .portaliq .field-2 { animation: amPqFocus2 var(--am-dur) linear infinite; }
|
|
757
|
+
.am .portaliq .field-3 { animation: amPqFocus3 var(--am-dur) linear infinite; }
|
|
758
|
+
|
|
759
|
+
@keyframes amPqFocus1 {
|
|
760
|
+
0%, 3% { border-color: var(--c-cobalt-200); box-shadow: 0 0 0 0 rgba(31, 68, 156, 0); }
|
|
761
|
+
6%, 17% { border-color: var(--c-blue-cobalt); box-shadow: 0 0 0 2px var(--c-cobalt-100); }
|
|
762
|
+
20%, 100% { border-color: var(--c-cobalt-200); box-shadow: 0 0 0 0 rgba(31, 68, 156, 0); }
|
|
763
|
+
}
|
|
764
|
+
@keyframes amPqFocus2 {
|
|
765
|
+
0%, 21% { border-color: var(--c-cobalt-200); box-shadow: 0 0 0 0 rgba(31, 68, 156, 0); }
|
|
766
|
+
24%, 35% { border-color: var(--c-blue-cobalt); box-shadow: 0 0 0 2px var(--c-cobalt-100); }
|
|
767
|
+
38%, 100% { border-color: var(--c-cobalt-200); box-shadow: 0 0 0 0 rgba(31, 68, 156, 0); }
|
|
768
|
+
}
|
|
769
|
+
@keyframes amPqFocus3 {
|
|
770
|
+
0%, 39% { border-color: var(--c-cobalt-200); box-shadow: 0 0 0 0 rgba(31, 68, 156, 0); }
|
|
771
|
+
42%, 53% { border-color: var(--c-blue-cobalt); box-shadow: 0 0 0 2px var(--c-cobalt-100); }
|
|
772
|
+
56%, 100% { border-color: var(--c-cobalt-200); box-shadow: 0 0 0 0 rgba(31, 68, 156, 0); }
|
|
773
|
+
}
|
|
774
|
+
@keyframes amPqFill1 {
|
|
775
|
+
0%, 8% { transform: scaleX(0); opacity: 1; }
|
|
776
|
+
19%, 93% { transform: scaleX(1); opacity: 1; }
|
|
777
|
+
98% { transform: scaleX(1); opacity: 0; }
|
|
778
|
+
100% { transform: scaleX(0); opacity: 0; }
|
|
779
|
+
}
|
|
780
|
+
@keyframes amPqFill2 {
|
|
781
|
+
0%, 26% { transform: scaleX(0); opacity: 1; }
|
|
782
|
+
37%, 93% { transform: scaleX(1); opacity: 1; }
|
|
783
|
+
98% { transform: scaleX(1); opacity: 0; }
|
|
784
|
+
100% { transform: scaleX(0); opacity: 0; }
|
|
785
|
+
}
|
|
786
|
+
@keyframes amPqFill3 {
|
|
787
|
+
0%, 44% { transform: scaleX(0); opacity: 1; }
|
|
788
|
+
55%, 93% { transform: scaleX(1); opacity: 1; }
|
|
789
|
+
98% { transform: scaleX(1); opacity: 0; }
|
|
790
|
+
100% { transform: scaleX(0); opacity: 0; }
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
.am .portaliq .submit { animation: amPqPress var(--am-dur) ease-in-out infinite; }
|
|
794
|
+
@keyframes amPqPress {
|
|
795
|
+
0%, 58% { transform: scale(1); }
|
|
796
|
+
60% { transform: scale(0.92); }
|
|
797
|
+
63%, 100% { transform: scale(1); }
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/* Mint confirmation sweep — a soft light band crossing the form panel
|
|
801
|
+
right after the submit press. Hidden at rest and in static frames. */
|
|
802
|
+
.am .portaliq .formPanel { position: relative; overflow: hidden; }
|
|
803
|
+
.am .portaliq .confirmSweep {
|
|
804
|
+
position: absolute;
|
|
805
|
+
inset: 0;
|
|
806
|
+
background: linear-gradient(100deg, transparent 32%, var(--c-mint-300) 50%, transparent 68%);
|
|
807
|
+
opacity: 0;
|
|
808
|
+
transform: translateX(-120%);
|
|
809
|
+
pointer-events: none;
|
|
810
|
+
animation: amPqSweep var(--am-dur) linear infinite;
|
|
811
|
+
}
|
|
812
|
+
@keyframes amPqSweep {
|
|
813
|
+
0%, 63% { transform: translateX(-120%); opacity: 0; }
|
|
814
|
+
64% { opacity: 0.9; }
|
|
815
|
+
71% { opacity: 0.9; }
|
|
816
|
+
72%, 100% { transform: translateX(120%); opacity: 0; }
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/* The arriving inbox row (its default mint avatar reads as the
|
|
820
|
+
delivered/confirmed state). Visible in static frames. */
|
|
821
|
+
.am .portaliq .newItem { animation: amPqRow var(--am-dur) ease-out infinite both; }
|
|
822
|
+
@keyframes amPqRow {
|
|
823
|
+
0%, 74% { opacity: 0; transform: translateY(-5px); }
|
|
824
|
+
80%, 93% { opacity: 1; transform: none; }
|
|
825
|
+
98%, 100% { opacity: 0; transform: none; }
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
/* ---- NLDesign — a theme swatch is selected, the components retheme
|
|
829
|
+
in a cascade (button, then status pill), and the type scale
|
|
830
|
+
restretches. Timeline (6s): ring on the lavender swatch (5–10%),
|
|
831
|
+
button recolours (12–20%), pill recolours (14–22%), type bars
|
|
832
|
+
restretch (30–50%), hold, revert crossfade (84–100%). ---- */
|
|
833
|
+
|
|
834
|
+
.am .nldesign { --am-dur: 6s; }
|
|
835
|
+
|
|
836
|
+
.am .nldesign .swatch { display: inline-block; width: 22px; height: 22px; border-radius: 4px; }
|
|
837
|
+
.am .nldesign .swatchSel { animation: amNldSel var(--am-dur) ease-in-out infinite; }
|
|
838
|
+
@keyframes amNldSel {
|
|
839
|
+
0%, 5% { box-shadow: 0 0 0 0 white, 0 0 0 0 rgba(31, 68, 156, 0); }
|
|
840
|
+
10%, 86% { box-shadow: 0 0 0 2px white, 0 0 0 4px var(--c-blue-cobalt); }
|
|
841
|
+
94%, 100% { box-shadow: 0 0 0 0 white, 0 0 0 0 rgba(31, 68, 156, 0); }
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
.am .nldesign .actions .btn:not(.ghost) { animation: amNldBtn var(--am-dur) ease-in-out infinite; }
|
|
845
|
+
@keyframes amNldBtn {
|
|
846
|
+
0%, 12% { background-color: var(--c-blue-cobalt); }
|
|
847
|
+
20%, 84% { background-color: var(--c-lavender-500); }
|
|
848
|
+
92%, 100% { background-color: var(--c-blue-cobalt); }
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
.am .nldesign .statusPill:not(.beta) { animation: amNldPillBg var(--am-dur) ease-in-out infinite; }
|
|
852
|
+
.am .nldesign .statusPill:not(.beta) .h,
|
|
853
|
+
.am .nldesign .statusPill:not(.beta) .t { animation: amNldPillInk var(--am-dur) ease-in-out infinite; }
|
|
854
|
+
@keyframes amNldPillBg {
|
|
855
|
+
0%, 14% { background-color: var(--c-mint-300); }
|
|
856
|
+
22%, 84% { background-color: var(--c-lavender-300); }
|
|
857
|
+
92%, 100% { background-color: var(--c-mint-300); }
|
|
858
|
+
}
|
|
859
|
+
@keyframes amNldPillInk {
|
|
860
|
+
0%, 14% { background-color: var(--c-mint-500); }
|
|
861
|
+
22%, 84% { background-color: var(--c-lavender-500); }
|
|
862
|
+
92%, 100% { background-color: var(--c-mint-500); }
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
.am .nldesign .typeRow > * {
|
|
866
|
+
transform-origin: left center;
|
|
867
|
+
animation: amNldType var(--am-dur) ease-in-out infinite;
|
|
868
|
+
}
|
|
869
|
+
.am .nldesign .typeRow > *:nth-child(2) { animation-delay: 0.05s; }
|
|
870
|
+
.am .nldesign .typeRow > *:nth-child(3) { animation-delay: 0.1s; }
|
|
871
|
+
.am .nldesign .typeRow > *:nth-child(4) { animation-delay: 0.15s; }
|
|
872
|
+
.am .nldesign .typeRow > *:nth-child(5) { animation-delay: 0.2s; }
|
|
873
|
+
@keyframes amNldType {
|
|
874
|
+
0%, 30% { transform: scaleX(1); }
|
|
875
|
+
42% { transform: scaleX(1.32); }
|
|
876
|
+
50%, 84% { transform: scaleX(1.18); }
|
|
877
|
+
94%, 100% { transform: scaleX(1); }
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/* ---- Scholiq — course progress bars fill in a stagger, then the
|
|
881
|
+
completed course's credential chip pops with an overshoot.
|
|
882
|
+
Timeline (6s): bars fill (3–25% + per-bar delay), chip pop
|
|
883
|
+
(44–57%), hold, fade-reset (91–100%). Per-bar delays are inline
|
|
884
|
+
(i × 250ms) so the keyframes stay shared. ---- */
|
|
885
|
+
|
|
886
|
+
.am .scholiq { --am-dur: 6s; }
|
|
887
|
+
|
|
888
|
+
.am .scholiq .progress { height: 4px; background: var(--c-cobalt-100); border-radius: 2px; }
|
|
889
|
+
.am .scholiq .progressFill {
|
|
890
|
+
height: 4px;
|
|
891
|
+
background: var(--c-forest-500);
|
|
892
|
+
border-radius: 2px;
|
|
893
|
+
transform-origin: left center;
|
|
894
|
+
animation: amSchFill var(--am-dur) cubic-bezier(0.2, 0.7, 0.3, 1) infinite both;
|
|
895
|
+
}
|
|
896
|
+
@keyframes amSchFill {
|
|
897
|
+
0%, 3% { transform: scaleX(0); opacity: 1; }
|
|
898
|
+
25%, 91% { transform: scaleX(1); opacity: 1; }
|
|
899
|
+
97% { transform: scaleX(1); opacity: 0; }
|
|
900
|
+
100% { transform: scaleX(0); opacity: 0; }
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
.am .scholiq .credChip {
|
|
904
|
+
display: inline-flex;
|
|
905
|
+
align-items: center;
|
|
906
|
+
gap: 3px;
|
|
907
|
+
padding: 2px 6px;
|
|
908
|
+
border-radius: 999px;
|
|
909
|
+
background: var(--c-forest-300);
|
|
910
|
+
align-self: flex-start;
|
|
911
|
+
transform-origin: left center;
|
|
912
|
+
animation: amSchChip var(--am-dur) ease-in-out infinite both;
|
|
913
|
+
}
|
|
914
|
+
.am .scholiq .credChip .credHex { width: 6px; height: 7px; clip-path: var(--hex-pointy-top); background: var(--c-forest-500); }
|
|
915
|
+
.am .scholiq .credChip .credBar { height: 3px; width: 22px; background: var(--c-forest-500); border-radius: 1px; }
|
|
916
|
+
@keyframes amSchChip {
|
|
917
|
+
0%, 44% { transform: scale(0); opacity: 0; }
|
|
918
|
+
52% { transform: scale(1.18); opacity: 1; }
|
|
919
|
+
57%, 91% { transform: scale(1); opacity: 1; }
|
|
920
|
+
97%, 100% { transform: scale(1); opacity: 0; }
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
/* ---- LaunchPad · Widgets — the dashboard assembles: widgets arrive
|
|
924
|
+
one by one on a 900ms stagger (inline delays), hold, fade-reset.
|
|
925
|
+
Timeline (10s). ---- */
|
|
926
|
+
|
|
927
|
+
.am .lpWidgets { --am-dur: 10s; }
|
|
928
|
+
.am .lpWidgets .w { animation: amLpwArrive var(--am-dur) ease-out infinite both; }
|
|
929
|
+
@keyframes amLpwArrive {
|
|
930
|
+
0% { opacity: 0; transform: translateY(10px) scale(0.98); }
|
|
931
|
+
4% { opacity: 1; transform: none; }
|
|
932
|
+
93% { opacity: 1; transform: none; }
|
|
933
|
+
98%, 100% { opacity: 0; transform: none; }
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
/* ---- LaunchPad · Tiles — the sub-grid tile is pressed and unfolds
|
|
937
|
+
its four sub-tiles in a stagger. Timeline (7s): press (5–11%),
|
|
938
|
+
cells unfold (10–20% + nth-child delay), hold, fade-reset. ---- */
|
|
939
|
+
|
|
940
|
+
.am .lpTiles { --am-dur: 7s; }
|
|
941
|
+
.am .lpTiles .w-subgrid { transform-origin: center; animation: amLptPress var(--am-dur) ease-in-out infinite; }
|
|
942
|
+
@keyframes amLptPress {
|
|
943
|
+
0%, 5% { transform: scale(1); }
|
|
944
|
+
8% { transform: scale(0.96); }
|
|
945
|
+
11%, 100% { transform: scale(1); }
|
|
946
|
+
}
|
|
947
|
+
.am .lpTiles .w-subgrid .cell { animation: amLptCell var(--am-dur) cubic-bezier(0.2, 0.7, 0.3, 1) infinite both; }
|
|
948
|
+
.am .lpTiles .w-subgrid .cell:nth-child(2) { animation-delay: 0.12s; }
|
|
949
|
+
.am .lpTiles .w-subgrid .cell:nth-child(3) { animation-delay: 0.24s; }
|
|
950
|
+
.am .lpTiles .w-subgrid .cell:nth-child(4) { animation-delay: 0.36s; }
|
|
951
|
+
@keyframes amLptCell {
|
|
952
|
+
0%, 10% { opacity: 0; transform: scale(0.4); }
|
|
953
|
+
16% { opacity: 1; transform: scale(1.08); }
|
|
954
|
+
20%, 92% { opacity: 1; transform: scale(1); }
|
|
955
|
+
97%, 100% { opacity: 0; transform: scale(1); }
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
/* ---- LaunchPad · BI — the charts draw themselves: bars grow from
|
|
959
|
+
the axis in a stagger, the line draws along its path and its end
|
|
960
|
+
dot pops, the donut sweeps in, the KPI deltas fade up. Timeline
|
|
961
|
+
(9s): bars (2–16% + nth-child delay), donut (8–26%), line draw
|
|
962
|
+
(10–38%), dot pop (38–49%), deltas (46–54% + inline delay), hold,
|
|
963
|
+
fade-reset (92–100%). The line's stroke path carries
|
|
964
|
+
pathLength="1" so the dash arithmetic is geometry-independent
|
|
965
|
+
(the FlowMock run-line technique). ---- */
|
|
966
|
+
|
|
967
|
+
.am .lpBi { --am-dur: 9s; }
|
|
968
|
+
|
|
969
|
+
.am .lpBi .w-graph-bar .bar {
|
|
970
|
+
transform-origin: bottom;
|
|
971
|
+
animation: amBiBar var(--am-dur) cubic-bezier(0.2, 0.7, 0.3, 1) infinite both;
|
|
972
|
+
}
|
|
973
|
+
.am .lpBi .w-graph-bar .bar:nth-child(2) { animation-delay: 0.12s; }
|
|
974
|
+
.am .lpBi .w-graph-bar .bar:nth-child(3) { animation-delay: 0.24s; }
|
|
975
|
+
.am .lpBi .w-graph-bar .bar:nth-child(4) { animation-delay: 0.36s; }
|
|
976
|
+
.am .lpBi .w-graph-bar .bar:nth-child(5) { animation-delay: 0.48s; }
|
|
977
|
+
.am .lpBi .w-graph-bar .bar:nth-child(6) { animation-delay: 0.6s; }
|
|
978
|
+
@keyframes amBiBar {
|
|
979
|
+
0%, 2% { transform: scaleY(0); opacity: 1; }
|
|
980
|
+
16%, 92% { transform: scaleY(1); opacity: 1; }
|
|
981
|
+
97% { transform: scaleY(1); opacity: 0; }
|
|
982
|
+
100% { transform: scaleY(0); opacity: 0; }
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
.am .lpBi .w-graph-line .stroke {
|
|
986
|
+
stroke-dasharray: 1;
|
|
987
|
+
animation: amBiLine var(--am-dur) ease-in-out infinite both;
|
|
988
|
+
}
|
|
989
|
+
@keyframes amBiLine {
|
|
990
|
+
0%, 10% { stroke-dashoffset: 1; opacity: 1; }
|
|
991
|
+
38%, 92% { stroke-dashoffset: 0; opacity: 1; }
|
|
992
|
+
97% { stroke-dashoffset: 0; opacity: 0; }
|
|
993
|
+
100% { stroke-dashoffset: 1; opacity: 0; }
|
|
994
|
+
}
|
|
995
|
+
.am .lpBi .w-graph-line .fill { animation: amBiFill var(--am-dur) linear infinite both; }
|
|
996
|
+
@keyframes amBiFill {
|
|
997
|
+
0%, 30% { opacity: 0; }
|
|
998
|
+
44%, 92% { opacity: 1; }
|
|
999
|
+
98%, 100% { opacity: 0; }
|
|
1000
|
+
}
|
|
1001
|
+
.am .lpBi .w-graph-line .dot {
|
|
1002
|
+
transform-box: fill-box;
|
|
1003
|
+
transform-origin: center;
|
|
1004
|
+
animation: amBiDot var(--am-dur) ease-out infinite both;
|
|
1005
|
+
}
|
|
1006
|
+
@keyframes amBiDot {
|
|
1007
|
+
0%, 38% { opacity: 0; transform: scale(0); }
|
|
1008
|
+
44% { opacity: 1; transform: scale(1.7); }
|
|
1009
|
+
49%, 92% { opacity: 1; transform: scale(1); }
|
|
1010
|
+
98%, 100% { opacity: 0; transform: scale(1); }
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
.am .lpBi .donut { animation: amBiDonut var(--am-dur) ease-out infinite both; }
|
|
1014
|
+
@keyframes amBiDonut {
|
|
1015
|
+
0%, 8% { opacity: 0; transform: rotate(-80deg) scale(0.85); }
|
|
1016
|
+
26%, 92% { opacity: 1; transform: none; }
|
|
1017
|
+
98%, 100% { opacity: 0; transform: none; }
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
.am .lpBi .biDelta { animation: amBiDelta var(--am-dur) ease-out infinite both; }
|
|
1021
|
+
@keyframes amBiDelta {
|
|
1022
|
+
0%, 46% { opacity: 0; transform: translateY(4px); }
|
|
1023
|
+
54%, 92% { opacity: 1; transform: none; }
|
|
1024
|
+
98%, 100% { opacity: 0; transform: none; }
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
/* ---- PipelinQ — a deal closes: the proposal-column card collapses,
|
|
1028
|
+
the won card scales in with its mint bar, and the pipeline-value
|
|
1029
|
+
KPI widens. Timeline (7s): collapse (14–22%), won pop (17–29%),
|
|
1030
|
+
KPI widen (26–34%), hold, opacity-swap reset (91–100%). Static
|
|
1031
|
+
frames show the closed state (won card in place, proposal card
|
|
1032
|
+
gone). ---- */
|
|
1033
|
+
|
|
1034
|
+
.am .plq { --am-dur: 7s; }
|
|
1035
|
+
|
|
1036
|
+
.am .plq .dealGone { transform-origin: center; animation: amPlqGone var(--am-dur) ease-in-out infinite; }
|
|
1037
|
+
@keyframes amPlqGone {
|
|
1038
|
+
0%, 14% { opacity: 1; transform: scaleY(1); }
|
|
1039
|
+
22%, 91% { opacity: 0; transform: scaleY(0); }
|
|
1040
|
+
97%, 100% { opacity: 1; transform: scaleY(1); }
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
.am .plq .dealWon {
|
|
1044
|
+
border-left-color: var(--c-mint-500);
|
|
1045
|
+
transform-origin: center;
|
|
1046
|
+
animation: amPlqWon var(--am-dur) ease-out infinite both;
|
|
1047
|
+
}
|
|
1048
|
+
@keyframes amPlqWon {
|
|
1049
|
+
0%, 17% { opacity: 0; transform: scale(0.5); }
|
|
1050
|
+
25% { opacity: 1; transform: scale(1.1); }
|
|
1051
|
+
29%, 91% { opacity: 1; transform: scale(1); }
|
|
1052
|
+
97%, 100% { opacity: 0; transform: scale(1); }
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
.am .plq .kpiRow .kpi:first-child .num {
|
|
1056
|
+
transform-origin: left center;
|
|
1057
|
+
animation: amPlqKpi var(--am-dur) ease-in-out infinite;
|
|
1058
|
+
}
|
|
1059
|
+
@keyframes amPlqKpi {
|
|
1060
|
+
0%, 26% { transform: scaleX(1); }
|
|
1061
|
+
34%, 90% { transform: scaleX(1.3); }
|
|
1062
|
+
97%, 100% { transform: scaleX(1); }
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
/* ======================================================================
|
|
1066
|
+
Static frames — running={false} adds `.static` next to `.am .frame`;
|
|
1067
|
+
the media query below mirrors it for prefers-reduced-motion. Selectors
|
|
1068
|
+
repeat the animated ones with `.static` inserted so they always win
|
|
1069
|
+
on specificity. Base styles are the meaningful end state, so beyond
|
|
1070
|
+
`animation: none` only a few explicit overrides are needed.
|
|
1071
|
+
====================================================================== */
|
|
1072
|
+
|
|
1073
|
+
.am .static .portaliq .field-1,
|
|
1074
|
+
.am .static .portaliq .field-2,
|
|
1075
|
+
.am .static .portaliq .field-3,
|
|
1076
|
+
.am .static .portaliq .field-1 .fieldFill,
|
|
1077
|
+
.am .static .portaliq .field-2 .fieldFill,
|
|
1078
|
+
.am .static .portaliq .field-3 .fieldFill,
|
|
1079
|
+
.am .static .portaliq .submit,
|
|
1080
|
+
.am .static .portaliq .confirmSweep,
|
|
1081
|
+
.am .static .portaliq .newItem,
|
|
1082
|
+
.am .static .nldesign .swatchSel,
|
|
1083
|
+
.am .static .nldesign .actions .btn:not(.ghost),
|
|
1084
|
+
.am .static .nldesign .statusPill:not(.beta),
|
|
1085
|
+
.am .static .nldesign .statusPill:not(.beta) .h,
|
|
1086
|
+
.am .static .nldesign .statusPill:not(.beta) .t,
|
|
1087
|
+
.am .static .nldesign .typeRow > *,
|
|
1088
|
+
.am .static .scholiq .progressFill,
|
|
1089
|
+
.am .static .scholiq .credChip,
|
|
1090
|
+
.am .static .lpWidgets .w,
|
|
1091
|
+
.am .static .lpTiles .w-subgrid,
|
|
1092
|
+
.am .static .lpTiles .w-subgrid .cell,
|
|
1093
|
+
.am .static .lpBi .w-graph-bar .bar,
|
|
1094
|
+
.am .static .lpBi .w-graph-line .stroke,
|
|
1095
|
+
.am .static .lpBi .w-graph-line .fill,
|
|
1096
|
+
.am .static .lpBi .w-graph-line .dot,
|
|
1097
|
+
.am .static .lpBi .donut,
|
|
1098
|
+
.am .static .lpBi .biDelta,
|
|
1099
|
+
.am .static .plq .dealGone,
|
|
1100
|
+
.am .static .plq .dealWon,
|
|
1101
|
+
.am .static .plq .kpiRow .kpi:first-child .num {
|
|
1102
|
+
animation: none;
|
|
1103
|
+
}
|
|
1104
|
+
.am .static .lpBi .w-graph-line .stroke { stroke-dasharray: none; stroke-dashoffset: 0; }
|
|
1105
|
+
.am .static .plq .dealGone { display: none; }
|
|
1106
|
+
|
|
1107
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1108
|
+
.am .portaliq .field-1,
|
|
1109
|
+
.am .portaliq .field-2,
|
|
1110
|
+
.am .portaliq .field-3,
|
|
1111
|
+
.am .portaliq .field-1 .fieldFill,
|
|
1112
|
+
.am .portaliq .field-2 .fieldFill,
|
|
1113
|
+
.am .portaliq .field-3 .fieldFill,
|
|
1114
|
+
.am .portaliq .submit,
|
|
1115
|
+
.am .portaliq .confirmSweep,
|
|
1116
|
+
.am .portaliq .newItem,
|
|
1117
|
+
.am .nldesign .swatchSel,
|
|
1118
|
+
.am .nldesign .actions .btn:not(.ghost),
|
|
1119
|
+
.am .nldesign .statusPill:not(.beta),
|
|
1120
|
+
.am .nldesign .statusPill:not(.beta) .h,
|
|
1121
|
+
.am .nldesign .statusPill:not(.beta) .t,
|
|
1122
|
+
.am .nldesign .typeRow > *,
|
|
1123
|
+
.am .scholiq .progressFill,
|
|
1124
|
+
.am .scholiq .credChip,
|
|
1125
|
+
.am .lpWidgets .w,
|
|
1126
|
+
.am .lpTiles .w-subgrid,
|
|
1127
|
+
.am .lpTiles .w-subgrid .cell,
|
|
1128
|
+
.am .lpBi .w-graph-bar .bar,
|
|
1129
|
+
.am .lpBi .w-graph-line .stroke,
|
|
1130
|
+
.am .lpBi .w-graph-line .fill,
|
|
1131
|
+
.am .lpBi .w-graph-line .dot,
|
|
1132
|
+
.am .lpBi .donut,
|
|
1133
|
+
.am .lpBi .biDelta,
|
|
1134
|
+
.am .plq .dealGone,
|
|
1135
|
+
.am .plq .dealWon,
|
|
1136
|
+
.am .plq .kpiRow .kpi:first-child .num {
|
|
1137
|
+
animation: none;
|
|
1138
|
+
}
|
|
1139
|
+
.am .lpBi .w-graph-line .stroke { stroke-dasharray: none; stroke-dashoffset: 0; }
|
|
1140
|
+
.am .plq .dealGone { display: none; }
|
|
1141
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AppMock.render.test.js — renders the real <AppMock> JSX to static
|
|
3
|
+
* markup (the SSR path Docusaurus takes at build time) and asserts on
|
|
4
|
+
* the output: every variant slug renders, the P0 `running` prop
|
|
5
|
+
* plumbing puts `.static` on the frame, the animated variants expose
|
|
6
|
+
* their animation hooks, the `openwoo` slug aliases to the
|
|
7
|
+
* OpenCatalogi mock, and no rendered inline style reaches for the
|
|
8
|
+
* reserved coral/gold families.
|
|
9
|
+
*
|
|
10
|
+
* Uses the same esbuild-bundle-then-renderToStaticMarkup technique as
|
|
11
|
+
* KanbanMock.render.test.js. CSS modules are stubbed with an identity
|
|
12
|
+
* proxy (styles.foo → 'foo'), so class assertions read as the source
|
|
13
|
+
* class names.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const test = require('node:test');
|
|
19
|
+
const {before, after} = test;
|
|
20
|
+
const assert = require('node:assert/strict');
|
|
21
|
+
const path = require('node:path');
|
|
22
|
+
const fs = require('node:fs/promises');
|
|
23
|
+
const {build} = require('esbuild');
|
|
24
|
+
const React = require('react');
|
|
25
|
+
const {renderToStaticMarkup} = require('react-dom/server');
|
|
26
|
+
|
|
27
|
+
const COMPONENT = path.resolve(__dirname, '..', 'AppMock.jsx');
|
|
28
|
+
const PRESET_ROOT = path.resolve(__dirname, '..', '..', '..', '..');
|
|
29
|
+
const SCRATCH = path.join(PRESET_ROOT, '.tmp-app-mock-test');
|
|
30
|
+
|
|
31
|
+
/* Must stay in sync with the VARIANTS map in AppMock.jsx. */
|
|
32
|
+
const SLUGS = [
|
|
33
|
+
'launchpad', 'launchpad-tiles', 'launchpad-bi', 'launchpad-widgets',
|
|
34
|
+
'openbuild', 'opencatalogi', 'openconnector', 'openregister',
|
|
35
|
+
'procest', 'decidesk', 'docudesk', 'larpingapp', 'nldesign',
|
|
36
|
+
'openwoo', 'pipelinq', 'softwarecatalog', 'zaakafhandelapp',
|
|
37
|
+
'hermiq', 'portaliq', 'scholiq', 'shillinq', 'doriath', 'planix',
|
|
38
|
+
'hrmq', 'app-versions',
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
const cssModuleStub = {
|
|
42
|
+
name: 'css-module-stub',
|
|
43
|
+
setup(b) {
|
|
44
|
+
b.onResolve({filter: /\.module\.css$/}, (args) => ({path: args.path, namespace: 'css-stub'}));
|
|
45
|
+
b.onLoad({filter: /.*/, namespace: 'css-stub'}, () => ({
|
|
46
|
+
contents: 'export default new Proxy({}, {get: (_, p) => p});',
|
|
47
|
+
loader: 'js',
|
|
48
|
+
}));
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
let AppMock;
|
|
53
|
+
|
|
54
|
+
before(async () => {
|
|
55
|
+
await fs.mkdir(SCRATCH, {recursive: true});
|
|
56
|
+
const tmpDir = await fs.mkdtemp(path.join(SCRATCH, 'run-'));
|
|
57
|
+
const outFile = path.join(tmpDir, 'bundle.cjs');
|
|
58
|
+
await build({
|
|
59
|
+
entryPoints: [COMPONENT],
|
|
60
|
+
outfile: outFile,
|
|
61
|
+
bundle: true,
|
|
62
|
+
format: 'cjs',
|
|
63
|
+
jsx: 'automatic',
|
|
64
|
+
jsxImportSource: 'react',
|
|
65
|
+
tsconfigRaw: {compilerOptions: {jsx: 'react-jsx', jsxImportSource: 'react'}},
|
|
66
|
+
platform: 'node',
|
|
67
|
+
external: ['react'],
|
|
68
|
+
plugins: [cssModuleStub],
|
|
69
|
+
logLevel: 'warning',
|
|
70
|
+
});
|
|
71
|
+
delete require.cache[require.resolve(outFile)];
|
|
72
|
+
AppMock = require(outFile).default;
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
after(async () => {
|
|
76
|
+
await fs.rm(SCRATCH, {recursive: true, force: true});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
function render(props) {
|
|
80
|
+
return renderToStaticMarkup(React.createElement(AppMock, props));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
test('SSR smoke: every variant slug renders a frame, none falls through to Unknown app', () => {
|
|
84
|
+
for (const app of SLUGS) {
|
|
85
|
+
const html = render({app});
|
|
86
|
+
assert.match(html, /class="frame size-md"/, `${app}: frame missing`);
|
|
87
|
+
assert.doesNotMatch(html, /Unknown app/, `${app}: fell through to the unknown-variant branch`);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('P0 plumbing: running={false} adds the static class to the frame; default omits it', () => {
|
|
92
|
+
assert.match(render({app: 'portaliq', running: false}), /class="frame size-md static"/);
|
|
93
|
+
assert.doesNotMatch(render({app: 'portaliq'}), /\bstatic\b/);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('P0 plumbing: static frame works on every variant slug', () => {
|
|
97
|
+
for (const app of SLUGS) {
|
|
98
|
+
assert.match(render({app, running: false}), /class="frame size-md static"/, `${app}: no static frame`);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('openwoo slug is an alias for the OpenCatalogi mock', () => {
|
|
103
|
+
const openwoo = render({app: 'openwoo'});
|
|
104
|
+
const opencatalogi = render({app: 'opencatalogi'});
|
|
105
|
+
assert.equal(openwoo, opencatalogi);
|
|
106
|
+
assert.equal(
|
|
107
|
+
render({app: 'openwoo', caption: true}).match(/<figcaption[^>]*>([^<]*)</)[1],
|
|
108
|
+
'OpenCatalogi'
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('portaliq: form fields, fills, submit, confirm sweep, and the arriving inbox row are all present', () => {
|
|
113
|
+
const html = render({app: 'portaliq'});
|
|
114
|
+
assert.match(html, /class="body decidesk portaliq"/);
|
|
115
|
+
assert.match(html, /field field-1/);
|
|
116
|
+
assert.match(html, /field field-2/);
|
|
117
|
+
assert.match(html, /field field-3 tall/);
|
|
118
|
+
assert.equal((html.match(/class="fieldFill"/g) || []).length, 3);
|
|
119
|
+
assert.match(html, /btn submit/);
|
|
120
|
+
assert.match(html, /class="confirmSweep"/);
|
|
121
|
+
assert.match(html, /item newItem/);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('scholiq: staggered progress fills (inline delays) and the credential chip', () => {
|
|
125
|
+
const html = render({app: 'scholiq'});
|
|
126
|
+
assert.equal((html.match(/class="progressFill"/g) || []).length, 6);
|
|
127
|
+
for (const d of [0, 250, 500, 750, 1000, 1250]) {
|
|
128
|
+
assert.match(html, new RegExp(`animation-delay:${d}ms`), `missing ${d}ms bar delay`);
|
|
129
|
+
}
|
|
130
|
+
assert.match(html, /class="credChip"/);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test('launchpad-widgets: seven widgets arrive on a 900ms stagger', () => {
|
|
134
|
+
const html = render({app: 'launchpad-widgets'});
|
|
135
|
+
assert.match(html, /class="body launchpad lpWidgets"/);
|
|
136
|
+
for (const d of [0, 900, 1800, 2700, 3600, 4500, 5400]) {
|
|
137
|
+
assert.match(html, new RegExp(`animation-delay:${d}ms`), `missing ${d}ms widget delay`);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test('launchpad-bi: the line chart stroke carries pathLength="1" for the draw animation', () => {
|
|
142
|
+
const html = render({app: 'launchpad-bi'});
|
|
143
|
+
assert.match(html, /class="body launchpad lpBi"/);
|
|
144
|
+
assert.match(html, /class="stroke" pathLength="1"/);
|
|
145
|
+
assert.equal((html.match(/class="biDelta"/g) || []).length, 3);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test('pipelinq: the closing deal (dealGone) and the won card (dealWon) are both in the board', () => {
|
|
149
|
+
const html = render({app: 'pipelinq'});
|
|
150
|
+
assert.match(html, /class="body decidesk plq"/);
|
|
151
|
+
assert.match(html, /card dealGone/);
|
|
152
|
+
assert.match(html, /card dealWon/);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('nldesign: selected swatch, type row, and the actions wrapper around the preview buttons', () => {
|
|
156
|
+
const html = render({app: 'nldesign'});
|
|
157
|
+
assert.match(html, /class="body openregister nldesign"/);
|
|
158
|
+
assert.equal((html.match(/class="swatch(?: |")/g) || []).length, 8);
|
|
159
|
+
assert.match(html, /swatch swatchSel/);
|
|
160
|
+
assert.match(html, /class="typeRow"/);
|
|
161
|
+
// The preview buttons need the .actions parent to be styled at all.
|
|
162
|
+
assert.match(html, /class="actions"[^>]*><div class="btn"><\/div><div class="btn ghost">/);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test('larpingapp: the scene timeline renders five steps (was an empty shell)', () => {
|
|
166
|
+
const html = render({app: 'larpingapp'});
|
|
167
|
+
assert.match(html, /class="procest"/);
|
|
168
|
+
assert.match(html, /class="timeline"/);
|
|
169
|
+
assert.equal((html.match(/class="step[ "]/g) || []).length, 5);
|
|
170
|
+
assert.equal((html.match(/step now/g) || []).length, 1, 'exactly one active (orange) step');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test('reserved accent families: no variant inline-styles coral or gold', () => {
|
|
174
|
+
// SKILL.md reserves the coral and gold families; the one-orange rule
|
|
175
|
+
// is enforced per component by review, but coral/gold must never
|
|
176
|
+
// appear at all. Class-based colours live in the stylesheet; this
|
|
177
|
+
// guards the inline styles the variants set directly.
|
|
178
|
+
for (const app of SLUGS) {
|
|
179
|
+
assert.doesNotMatch(render({app}), /--c-coral|--c-gold/, `${app}: reserved family in inline style`);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
@@ -33,10 +33,29 @@ export default function LarpingAppMock() {
|
|
|
33
33
|
))}
|
|
34
34
|
</div>
|
|
35
35
|
<div className={styles.col}>
|
|
36
|
-
{/* Scene timeline (one done, one active in orange
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
{/* Scene timeline (one done, one active in orange — the
|
|
37
|
+
variant's single orange — three to-do). Reuses the
|
|
38
|
+
procest timeline atoms; the wrapper div provides the
|
|
39
|
+
`.procest` scope those styles require. The previous
|
|
40
|
+
markup here was an empty w-graph-bar shell that rendered
|
|
41
|
+
nothing but its header. */}
|
|
42
|
+
<div className={styles.procest}>
|
|
43
|
+
<div className={styles.timeline}>
|
|
44
|
+
<div className={styles.step}>
|
|
45
|
+
<div className={styles.h}></div><div className={styles.label}></div>
|
|
46
|
+
</div>
|
|
47
|
+
<div className={[styles.step, styles.now].join(' ')}>
|
|
48
|
+
<div className={styles.h}></div><div className={styles.label}></div>
|
|
49
|
+
</div>
|
|
50
|
+
<div className={[styles.step, styles.todo].join(' ')}>
|
|
51
|
+
<div className={styles.h}></div><div className={styles.label}></div>
|
|
52
|
+
</div>
|
|
53
|
+
<div className={[styles.step, styles.todo].join(' ')}>
|
|
54
|
+
<div className={styles.h}></div><div className={styles.label}></div>
|
|
55
|
+
</div>
|
|
56
|
+
<div className={[styles.step, styles.todo].join(' ')}>
|
|
57
|
+
<div className={styles.h}></div><div className={styles.label}></div>
|
|
58
|
+
</div>
|
|
40
59
|
</div>
|
|
41
60
|
</div>
|
|
42
61
|
{/* Character grid */}
|