@conduction/docusaurus-preset 0.1.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 +109 -0
- package/README.md +171 -0
- package/package.json +59 -0
- package/src/components/AgentTrace/AgentTrace.jsx +128 -0
- package/src/components/AgentTrace/AgentTrace.module.css +115 -0
- package/src/components/AppMock/AppMock.jsx +86 -0
- package/src/components/AppMock/AppMock.module.css +629 -0
- package/src/components/AppMock/variants/DeciDeskMock.jsx +71 -0
- package/src/components/AppMock/variants/DocuDeskMock.jsx +69 -0
- package/src/components/AppMock/variants/LarpingAppMock.jsx +59 -0
- package/src/components/AppMock/variants/MyDashBiMock.jsx +135 -0
- package/src/components/AppMock/variants/MyDashMock.jsx +96 -0
- package/src/components/AppMock/variants/MyDashTilesMock.jsx +103 -0
- package/src/components/AppMock/variants/MyDashWidgetsMock.jsx +123 -0
- package/src/components/AppMock/variants/NLDesignMock.jsx +70 -0
- package/src/components/AppMock/variants/OpenCatalogiMock.jsx +61 -0
- package/src/components/AppMock/variants/OpenConnectorMock.jsx +83 -0
- package/src/components/AppMock/variants/OpenRegisterMock.jsx +100 -0
- package/src/components/AppMock/variants/OpenWooMock.jsx +61 -0
- package/src/components/AppMock/variants/PipelinQMock.jsx +88 -0
- package/src/components/AppMock/variants/ProcestMock.jsx +87 -0
- package/src/components/AppMock/variants/SoftwareCatalogMock.jsx +71 -0
- package/src/components/AppMock/variants/ZaakAfhandelAppMock.jsx +71 -0
- package/src/components/AppsGrid/AppsGrid.jsx +84 -0
- package/src/components/AppsGrid/AppsGrid.module.css +46 -0
- package/src/components/AppsPreview/AppsPreview.jsx +85 -0
- package/src/components/AppsPreview/AppsPreview.module.css +128 -0
- package/src/components/Clients/Clients.jsx +205 -0
- package/src/components/Clients/Clients.module.css +166 -0
- package/src/components/ComposeBlock/ComposeBlock.jsx +70 -0
- package/src/components/ComposeBlock/ComposeBlock.module.css +74 -0
- package/src/components/ConductionBg/ConductionBg.jsx +150 -0
- package/src/components/ConductionBg/ConductionBg.module.css +41 -0
- package/src/components/ContentCard/ContentCard.jsx +126 -0
- package/src/components/ContentCard/ContentCard.module.css +84 -0
- package/src/components/ContentDetailHero/ContentDetailHero.jsx +136 -0
- package/src/components/ContentDetailHero/ContentDetailHero.module.css +96 -0
- package/src/components/ContentTypeFilter/ContentTypeFilter.jsx +103 -0
- package/src/components/ContentTypeFilter/ContentTypeFilter.module.css +60 -0
- package/src/components/ContentTypeFilter/contentTypes.js +58 -0
- package/src/components/CookieCli/CookieCli.jsx +223 -0
- package/src/components/CookieCli/CookieCli.module.css +166 -0
- package/src/components/CtaBanner/CtaBanner.jsx +61 -0
- package/src/components/CtaBanner/CtaBanner.module.css +65 -0
- package/src/components/DetailHero/DetailHero.jsx +143 -0
- package/src/components/DetailHero/DetailHero.module.css +154 -0
- package/src/components/Diagrams/Diagrams.jsx +148 -0
- package/src/components/EmployeeCard/EmployeeCard.jsx +127 -0
- package/src/components/EmployeeCard/EmployeeCard.module.css +144 -0
- package/src/components/ExternalAppShelf/ExternalAppShelf.jsx +61 -0
- package/src/components/ExternalAppShelf/ExternalAppShelf.module.css +90 -0
- package/src/components/FAQ/FAQ.jsx +42 -0
- package/src/components/FAQ/FAQ.module.css +74 -0
- package/src/components/FacetedFilters/FacetedFilters.jsx +125 -0
- package/src/components/FacetedFilters/FacetedFilters.module.css +133 -0
- package/src/components/FeatureGrid/FeatureGrid.jsx +94 -0
- package/src/components/FeatureGrid/FeatureGrid.module.css +114 -0
- package/src/components/FeatureList/FeatureList.jsx +54 -0
- package/src/components/FeatureList/FeatureList.module.css +52 -0
- package/src/components/FeaturedCard/FeaturedCard.jsx +101 -0
- package/src/components/FeaturedCard/FeaturedCard.module.css +98 -0
- package/src/components/GameModal/GameModal.jsx +197 -0
- package/src/components/GameModal/GameModal.module.css +184 -0
- package/src/components/Hero/Hero.jsx +101 -0
- package/src/components/Hero/Hero.module.css +95 -0
- package/src/components/HexBackground/HexBackground.jsx +56 -0
- package/src/components/HexBackground/HexBackground.module.css +73 -0
- package/src/components/HexNetwork/HexNetwork.jsx +141 -0
- package/src/components/HexNetwork/HexNetwork.module.css +187 -0
- package/src/components/HexRain/HexRain.jsx +81 -0
- package/src/components/HowSteps/HowSteps.jsx +57 -0
- package/src/components/HowSteps/HowSteps.module.css +52 -0
- package/src/components/ManagedCommonGround/ManagedCommonGround.jsx +78 -0
- package/src/components/ManagedCommonGround/ManagedCommonGround.module.css +16 -0
- package/src/components/NewsletterCta/NewsletterCta.jsx +83 -0
- package/src/components/NewsletterCta/NewsletterCta.module.css +103 -0
- package/src/components/PairCard/PairCard.jsx +58 -0
- package/src/components/PairCard/PairCard.module.css +54 -0
- package/src/components/PartnerCard/PartnerCard.jsx +130 -0
- package/src/components/PartnerCard/PartnerCard.module.css +198 -0
- package/src/components/PartnerDirectory/PartnerDirectory.jsx +122 -0
- package/src/components/PartnerDirectory/PartnerDirectory.module.css +25 -0
- package/src/components/PartnerSidecard/PartnerSidecard.jsx +116 -0
- package/src/components/PartnerSidecard/PartnerSidecard.module.css +185 -0
- package/src/components/Pipeline/Pipeline.jsx +198 -0
- package/src/components/Pipeline/Pipeline.module.css +206 -0
- package/src/components/PlatformDiagram/PlatformDiagram.jsx +110 -0
- package/src/components/PlatformOverview/PlatformOverview.jsx +68 -0
- package/src/components/PlatformOverview/PlatformOverview.module.css +71 -0
- package/src/components/ReferenceCard/ReferenceCard.jsx +44 -0
- package/src/components/ReferenceCard/ReferenceCard.module.css +57 -0
- package/src/components/RelatedPosts/RelatedPosts.jsx +58 -0
- package/src/components/RelatedPosts/RelatedPosts.module.css +51 -0
- package/src/components/RotatingCards/RotatingCards.jsx +98 -0
- package/src/components/RotatingCards/RotatingCards.module.css +153 -0
- package/src/components/Showcase/Showcase.jsx +129 -0
- package/src/components/Showcase/Showcase.module.css +168 -0
- package/src/components/SolutionCard/SolutionCard.jsx +83 -0
- package/src/components/SolutionCard/SolutionCard.module.css +99 -0
- package/src/components/StatsStrip/StatsStrip.jsx +38 -0
- package/src/components/StatsStrip/StatsStrip.module.css +53 -0
- package/src/components/WidgetShelf/WidgetShelf.jsx +67 -0
- package/src/components/WidgetShelf/WidgetShelf.module.css +73 -0
- package/src/components/index.js +96 -0
- package/src/components/primitives/AuthorByline.jsx +85 -0
- package/src/components/primitives/AuthorByline.module.css +57 -0
- package/src/components/primitives/BrandCitation.jsx +71 -0
- package/src/components/primitives/Button.jsx +46 -0
- package/src/components/primitives/Button.module.css +88 -0
- package/src/components/primitives/Card.jsx +42 -0
- package/src/components/primitives/Card.module.css +42 -0
- package/src/components/primitives/Eyebrow.jsx +37 -0
- package/src/components/primitives/Eyebrow.module.css +19 -0
- package/src/components/primitives/HexBullet.jsx +37 -0
- package/src/components/primitives/HexBullet.module.css +16 -0
- package/src/components/primitives/HexThumbnail.jsx +70 -0
- package/src/components/primitives/HexThumbnail.module.css +45 -0
- package/src/components/primitives/Pill.jsx +42 -0
- package/src/components/primitives/Pill.module.css +30 -0
- package/src/components/primitives/Section.jsx +51 -0
- package/src/components/primitives/Section.module.css +31 -0
- package/src/components/primitives/SectionHead.jsx +36 -0
- package/src/components/primitives/SectionHead.module.css +43 -0
- package/src/components/primitives/index.js +22 -0
- package/src/css/brand.css +158 -0
- package/src/css/tokens.css +12 -0
- package/src/data/app-downloads.js +42 -0
- package/src/diagrams/README.md +74 -0
- package/src/diagrams/cn-domain-tree.js +105 -0
- package/src/diagrams/cn-hex-prism.js +163 -0
- package/src/diagrams/cn-hex.js +181 -0
- package/src/diagrams/cn-honeycomb-bg.js +135 -0
- package/src/diagrams/cn-pipeline.js +150 -0
- package/src/diagrams/cn-platform.js +156 -0
- package/src/diagrams/cn-side-box.js +104 -0
- package/src/diagrams/index.js +28 -0
- package/src/index.js +183 -0
- package/src/theme/Footer/index.jsx +516 -0
- package/src/theme/MDXPage/index.jsx +134 -0
- package/src/theme/Navbar/index.jsx +120 -0
- package/src/theme/Navbar/styles.module.css +114 -0
- package/src/theme/brand.jsx +63 -0
- package/src/theme.js +45 -0
- package/src/utils/lazyScript.js +37 -0
- package/static/img/favicon.svg +14 -0
- package/static/img/honeycomb-scatter.svg +23 -0
- package/static/img/honeycomb-watermark.svg +108 -0
- package/static/img/logo-dark.svg +11 -0
- package/static/img/logo.svg +14 -0
- package/static/img/nextcloud-logo.svg +5 -0
- package/static/lib/canal-footer.css +418 -0
- package/static/lib/canal-footer.js +499 -0
- package/static/lib/clients-flow.js +317 -0
- package/static/lib/conduction-bg.css +50 -0
- package/static/lib/conduction-bg.js +122 -0
- package/static/lib/hex-rain.css +128 -0
- package/static/lib/hex-rain.js +284 -0
- package/static/lib/kade-cyclist.css +264 -0
- package/static/lib/kade-cyclist.js +420 -0
- package/static/lib/logo-memory.css +219 -0
- package/static/lib/logo-memory.js +540 -0
- package/static/lib/platform-diagram.css +458 -0
- package/static/lib/platform-diagram.js +414 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <PartnerSidecard /> styles.
|
|
3
|
+
* Narrow rail (~300px) shown to the right of partner-detail body content.
|
|
4
|
+
* Card backgrounds + chip styling mirror <PartnerCard> so the directory
|
|
5
|
+
* and the detail-rail tell the same visual story per tier.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
.rail {
|
|
9
|
+
font-family: var(--conduction-typography-font-family-body);
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: column;
|
|
12
|
+
gap: var(--space-5);
|
|
13
|
+
width: 100%;
|
|
14
|
+
max-width: 320px;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* ---------- Tier card ---------- */
|
|
18
|
+
|
|
19
|
+
.tierCard {
|
|
20
|
+
background: white;
|
|
21
|
+
border: 1px solid var(--c-cobalt-100);
|
|
22
|
+
border-radius: var(--radius-lg);
|
|
23
|
+
padding: var(--space-5);
|
|
24
|
+
position: relative;
|
|
25
|
+
box-shadow: var(--shadow-1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.tier-strategic .tierCard {
|
|
29
|
+
background: var(--c-blue-cobalt);
|
|
30
|
+
border-color: var(--c-blue-cobalt);
|
|
31
|
+
color: white;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.tier-certified .tierCard {
|
|
35
|
+
/* white card, but a hairline gold stripe + the gold avatar reads it as
|
|
36
|
+
"Conduction-issued credential" rather than just another Partner. */
|
|
37
|
+
border-top: 4px solid var(--c-gold-500);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.tierBadge {
|
|
41
|
+
display: block;
|
|
42
|
+
width: 44px;
|
|
43
|
+
height: 44px;
|
|
44
|
+
margin: 0 0 var(--space-3);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.tierLabel {
|
|
48
|
+
font-family: var(--conduction-typography-font-family-code);
|
|
49
|
+
font-size: 11px;
|
|
50
|
+
letter-spacing: 0.12em;
|
|
51
|
+
text-transform: uppercase;
|
|
52
|
+
color: var(--c-cobalt-700);
|
|
53
|
+
margin-bottom: var(--space-2);
|
|
54
|
+
font-weight: 600;
|
|
55
|
+
}
|
|
56
|
+
.tier-strategic .tierLabel { color: rgba(255, 255, 255, 0.78); }
|
|
57
|
+
.tier-certified .tierLabel { color: var(--c-gold-600, #8B6914); }
|
|
58
|
+
|
|
59
|
+
.tierBlurb {
|
|
60
|
+
font-size: 13px;
|
|
61
|
+
line-height: 1.5;
|
|
62
|
+
color: var(--c-cobalt-700);
|
|
63
|
+
margin: 0;
|
|
64
|
+
}
|
|
65
|
+
.tier-strategic .tierBlurb { color: rgba(255, 255, 255, 0.85); }
|
|
66
|
+
|
|
67
|
+
/* ---------- Generic section ---------- */
|
|
68
|
+
|
|
69
|
+
.section {
|
|
70
|
+
background: white;
|
|
71
|
+
border: 1px solid var(--c-cobalt-100);
|
|
72
|
+
border-radius: var(--radius-lg);
|
|
73
|
+
padding: var(--space-5);
|
|
74
|
+
box-shadow: var(--shadow-1);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.sectionTitle {
|
|
78
|
+
font-family: var(--conduction-typography-font-family-code);
|
|
79
|
+
font-size: 11px;
|
|
80
|
+
letter-spacing: 0.12em;
|
|
81
|
+
text-transform: uppercase;
|
|
82
|
+
color: var(--c-cobalt-400);
|
|
83
|
+
font-weight: 600;
|
|
84
|
+
margin: 0 0 var(--space-3);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* ---------- Apps chip row ---------- */
|
|
88
|
+
|
|
89
|
+
.chipRow {
|
|
90
|
+
display: flex;
|
|
91
|
+
gap: 6px;
|
|
92
|
+
flex-wrap: wrap;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.chip {
|
|
96
|
+
display: inline-flex;
|
|
97
|
+
align-items: center;
|
|
98
|
+
background: var(--c-cobalt-50);
|
|
99
|
+
color: var(--c-cobalt-700);
|
|
100
|
+
padding: 4px 10px;
|
|
101
|
+
border-radius: var(--radius-pill);
|
|
102
|
+
font-size: 12px;
|
|
103
|
+
font-family: var(--conduction-typography-font-family-code);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* ---------- Solutions list ---------- */
|
|
107
|
+
|
|
108
|
+
.chipColumn {
|
|
109
|
+
display: flex;
|
|
110
|
+
flex-direction: column;
|
|
111
|
+
gap: var(--space-2);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.solutionLink {
|
|
115
|
+
display: flex;
|
|
116
|
+
align-items: center;
|
|
117
|
+
gap: var(--space-3);
|
|
118
|
+
padding: var(--space-2) var(--space-3);
|
|
119
|
+
border-radius: var(--radius-md);
|
|
120
|
+
background: var(--c-cobalt-50);
|
|
121
|
+
color: var(--c-cobalt-900);
|
|
122
|
+
text-decoration: none;
|
|
123
|
+
transition: background 160ms, color 160ms;
|
|
124
|
+
}
|
|
125
|
+
.solutionLink:hover {
|
|
126
|
+
background: var(--c-blue-cobalt);
|
|
127
|
+
color: white;
|
|
128
|
+
text-decoration: none;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.solutionIcon {
|
|
132
|
+
flex: 0 0 auto;
|
|
133
|
+
width: 22px;
|
|
134
|
+
height: 22px;
|
|
135
|
+
display: inline-flex;
|
|
136
|
+
align-items: center;
|
|
137
|
+
justify-content: center;
|
|
138
|
+
color: currentColor;
|
|
139
|
+
}
|
|
140
|
+
.solutionIcon svg {
|
|
141
|
+
width: 22px;
|
|
142
|
+
height: 22px;
|
|
143
|
+
stroke: currentColor;
|
|
144
|
+
stroke-width: 1.6;
|
|
145
|
+
fill: none;
|
|
146
|
+
stroke-linecap: round;
|
|
147
|
+
stroke-linejoin: round;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.solutionTitle {
|
|
151
|
+
font-size: 13px;
|
|
152
|
+
font-weight: 500;
|
|
153
|
+
line-height: 1.3;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* ---------- CTA ---------- */
|
|
157
|
+
|
|
158
|
+
.cta {
|
|
159
|
+
display: inline-flex;
|
|
160
|
+
align-items: center;
|
|
161
|
+
justify-content: center;
|
|
162
|
+
background: var(--c-blue-cobalt);
|
|
163
|
+
color: white;
|
|
164
|
+
padding: 12px 18px;
|
|
165
|
+
border-radius: var(--radius-md);
|
|
166
|
+
font-family: inherit;
|
|
167
|
+
font-size: 14px;
|
|
168
|
+
font-weight: 600;
|
|
169
|
+
letter-spacing: 0.02em;
|
|
170
|
+
text-decoration: none;
|
|
171
|
+
transition: background 160ms;
|
|
172
|
+
}
|
|
173
|
+
.cta:hover {
|
|
174
|
+
background: var(--c-cobalt-900);
|
|
175
|
+
color: white;
|
|
176
|
+
text-decoration: none;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/* ---------- Responsive ---------- */
|
|
180
|
+
|
|
181
|
+
@media (max-width: 900px) {
|
|
182
|
+
.rail {
|
|
183
|
+
max-width: 100%;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <Pipeline /> + <PipelineStep /> + <IconList />
|
|
3
|
+
*
|
|
4
|
+
* Horizontal pipeline of flat hex tiles, family-coloured per step,
|
|
5
|
+
* mirroring preview/components/pipeline-flow.html. Each step has a
|
|
6
|
+
* stage number + uppercase kicker above the hex, an explanatory
|
|
7
|
+
* name + caption below, and (optionally) a small pill inside the
|
|
8
|
+
* hex (e.g. "files · users · auth" on a workspace step). A single
|
|
9
|
+
* animated dotted flow line runs full-width behind the hex row,
|
|
10
|
+
* threading through every hex equator.
|
|
11
|
+
*
|
|
12
|
+
* Family palette follows the locked PRISM-FAMILY POLICY in tokens.css:
|
|
13
|
+
* mint integrate / connect (OpenConnector)
|
|
14
|
+
* forest data / registers (OpenRegister)
|
|
15
|
+
* terracotta documents / search (OpenCatalogi)
|
|
16
|
+
* lavender process / views (MyDash)
|
|
17
|
+
* workspace Nextcloud workspace (centre platform hex)
|
|
18
|
+
*
|
|
19
|
+
* Sources / consumers end-boxes use <IconList />, also exported on its
|
|
20
|
+
* own so the same kit-styled "label + bordered list of icon-tagged
|
|
21
|
+
* items" pattern can be dropped anywhere outside the pipeline.
|
|
22
|
+
*
|
|
23
|
+
* Each item can be a plain string or an object:
|
|
24
|
+
* - string → default doc icon + label
|
|
25
|
+
* - {label, icon: 'doc'} → built-in glyph by name (see GLYPHS)
|
|
26
|
+
* - {label, icon: <svg/>} → custom React node (rendered as-is,
|
|
27
|
+
* without the bordered tile wrapper)
|
|
28
|
+
*
|
|
29
|
+
* Usage in MDX:
|
|
30
|
+
*
|
|
31
|
+
* <Pipeline
|
|
32
|
+
* start={{label: 'Sources', items: [
|
|
33
|
+
* {label: 'Spreadsheets', icon: 'doc'},
|
|
34
|
+
* {label: 'Databases', icon: 'db'},
|
|
35
|
+
* {label: 'REST / SOAP', icon: 'api'},
|
|
36
|
+
* {label: 'Legacy', icon: 'legacy'},
|
|
37
|
+
* ]}}
|
|
38
|
+
* steps={[
|
|
39
|
+
* {number: '01', kicker: 'Ingest', name: 'OpenConnector',
|
|
40
|
+
* caption: 'Pulls schemas, REST, SOAP, files.'},
|
|
41
|
+
* {number: 'PLATFORM', name: 'Nextcloud', family: 'workspace',
|
|
42
|
+
* pill: 'files · users · auth'},
|
|
43
|
+
* {number: '02', kicker: 'Present', name: 'OpenCatalogi',
|
|
44
|
+
* caption: 'Indexes every register.'},
|
|
45
|
+
* ]}
|
|
46
|
+
* end={{label: 'Consumers', items: [
|
|
47
|
+
* {label: 'Citizens', icon: 'person'},
|
|
48
|
+
* {label: 'APIs', icon: 'api'},
|
|
49
|
+
* {label: 'Open data', icon: 'download'},
|
|
50
|
+
* ]}}
|
|
51
|
+
* />
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
import React from 'react';
|
|
55
|
+
import styles from './Pipeline.module.css';
|
|
56
|
+
|
|
57
|
+
/* ===== Family palette =====
|
|
58
|
+
Locked PRISM-FAMILY POLICY (tokens.css). fill = -300, ink = -700. */
|
|
59
|
+
const FAMILY_COLORS = {
|
|
60
|
+
mint: { fill: '#87CFA8', ink: '#155234' },
|
|
61
|
+
forest: { fill: '#7DAA7C', ink: '#1E461C' },
|
|
62
|
+
terracotta: { fill: '#DA9D8A', ink: '#6E2A1C' },
|
|
63
|
+
lavender: { fill: '#B7A7E3', ink: '#483982' },
|
|
64
|
+
workspace: { fill: '#67BEEA', ink: '#014C77' },
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const FAMILY_LABEL = {
|
|
68
|
+
mint: 'INTEGRATE',
|
|
69
|
+
forest: 'DATA',
|
|
70
|
+
terracotta: 'SEARCH',
|
|
71
|
+
lavender: 'VIEWS',
|
|
72
|
+
workspace: 'WORKSPACE',
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
function inferFamily(name) {
|
|
76
|
+
const n = (name || '').toLowerCase();
|
|
77
|
+
if (n.includes('connector')) return 'mint';
|
|
78
|
+
if (n.includes('register')) return 'forest';
|
|
79
|
+
if (n.includes('catalog')) return 'terracotta';
|
|
80
|
+
if (n.includes('mydash') || n.includes('dashboard')) return 'lavender';
|
|
81
|
+
if (n.includes('nextcloud') || n.includes('workspace') || n.includes('platform')) return 'workspace';
|
|
82
|
+
return 'forest';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* ===== Hex tile ===== */
|
|
86
|
+
/* Flat pointy-top hex (no extruded sides). Geometry mirrors the kit's
|
|
87
|
+
top-face polygon so the silhouette matches the rest of the brand.
|
|
88
|
+
When `pill` is supplied the family sub-label is replaced with a
|
|
89
|
+
small white pill — kit-identical to the workspace hex's
|
|
90
|
+
"files · users · auth" tag. */
|
|
91
|
+
function HexTile({name, family, pill}) {
|
|
92
|
+
const c = FAMILY_COLORS[family] || FAMILY_COLORS.forest;
|
|
93
|
+
const sub = FAMILY_LABEL[family];
|
|
94
|
+
return (
|
|
95
|
+
<svg viewBox="0 0 120 86" className={styles.prism} aria-hidden="true">
|
|
96
|
+
<polygon points="60,4 96,24 96,62 60,82 24,62 24,24" fill={c.fill} />
|
|
97
|
+
<text x="60" y="40" textAnchor="middle" fontSize="10" fontWeight="700" fill={c.ink} letterSpacing="-0.02em">{name}</text>
|
|
98
|
+
{pill ? (
|
|
99
|
+
<>
|
|
100
|
+
<rect x="22" y="48" width="76" height="14" rx="7" fill="white" fillOpacity="0.94" />
|
|
101
|
+
<text x="60" y="58" textAnchor="middle" fontSize="7" fontWeight="600" fill={c.ink}>{pill}</text>
|
|
102
|
+
</>
|
|
103
|
+
) : sub ? (
|
|
104
|
+
<text x="60" y="58" textAnchor="middle" fontFamily="ui-monospace, Menlo, monospace" fontSize="7" letterSpacing="1.4" fill={c.ink}>{sub}</text>
|
|
105
|
+
) : null}
|
|
106
|
+
</svg>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function PipelineStep({number, kicker, name, caption, family, pill, className}) {
|
|
111
|
+
const fam = family || inferFamily(name);
|
|
112
|
+
return (
|
|
113
|
+
<div className={[styles.step, className].filter(Boolean).join(' ')}>
|
|
114
|
+
{number && <div className={styles.num}>{number}</div>}
|
|
115
|
+
{kicker && <div className={styles.kicker}>{kicker}</div>}
|
|
116
|
+
<HexTile name={name} family={fam} pill={pill} />
|
|
117
|
+
{name && <div className={styles.name}>{name}</div>}
|
|
118
|
+
{caption && <div className={styles.caption}>{caption}</div>}
|
|
119
|
+
</div>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* ===== Item icons =====
|
|
124
|
+
Built-in glyph presets with kit-identical colours: blue-cobalt
|
|
125
|
+
stroke (#21468B) on a 22x22 white tile with a cobalt-200 hairline
|
|
126
|
+
border. Match the four-icon source / four-icon consumer set used
|
|
127
|
+
by preview/components/pipeline-flow.html. */
|
|
128
|
+
const STROKE = 'var(--c-blue-cobalt)';
|
|
129
|
+
const GLYPHS = {
|
|
130
|
+
/* Sources */
|
|
131
|
+
doc: <path d="M6 8h10 M6 11h10 M6 14h6" stroke={STROKE} strokeWidth="1.4" fill="none" strokeLinecap="round" />,
|
|
132
|
+
db: <g stroke={STROKE} strokeWidth="1.4" fill="none" strokeLinecap="round"><rect x="6" y="7" width="10" height="9"/><path d="M8 11h6 M8 13h6"/></g>,
|
|
133
|
+
api: <path d="M11 8a3 3 0 0 1 3 3h-6a3 3 0 0 1 3-3z M7 14h8" stroke={STROKE} strokeWidth="1.4" fill="none" strokeLinecap="round" />,
|
|
134
|
+
legacy: <path d="M7 16V9l4-2 4 2v7z" stroke={STROKE} strokeWidth="1.4" fill="none" strokeLinecap="round" />,
|
|
135
|
+
/* Consumers */
|
|
136
|
+
person: <g stroke={STROKE} strokeWidth="1.4" fill="none" strokeLinecap="round"><circle cx="11" cy="9" r="2.5"/><path d="M7 17a4 4 0 0 1 8 0"/></g>,
|
|
137
|
+
download: <path d="M11 6v8 M7 11l4 4 4-4 M7 17h8" stroke={STROKE} strokeWidth="1.4" fill="none" strokeLinecap="round" />,
|
|
138
|
+
embed: <g stroke={STROKE} strokeWidth="1.4" fill="none" strokeLinecap="round"><rect x="5" y="6" width="12" height="10" rx="1"/><path d="M5 9h12"/></g>,
|
|
139
|
+
link: <g stroke={STROKE} strokeWidth="1.4" fill="none" strokeLinecap="round"><path d="M9 13a4 4 0 0 1 0-6l2-2"/><path d="M13 9a4 4 0 0 1 0 6l-2 2"/></g>,
|
|
140
|
+
globe: <g stroke={STROKE} strokeWidth="1.4" fill="none" strokeLinecap="round"><circle cx="11" cy="11" r="5"/><path d="M6 11h10 M11 6a8 8 0 0 1 0 10 M11 6a8 8 0 0 0 0 10"/></g>,
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/* Bordered icon tile (white fill, cobalt-200 stroke) wrapping a glyph. */
|
|
144
|
+
function IconTile({glyph}) {
|
|
145
|
+
return (
|
|
146
|
+
<svg viewBox="0 0 22 22" aria-hidden="true">
|
|
147
|
+
<rect x="0.5" y="0.5" width="21" height="21" rx="3" fill="white" stroke="var(--c-cobalt-200)" strokeWidth="1" />
|
|
148
|
+
{glyph}
|
|
149
|
+
</svg>
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function resolveIcon(spec) {
|
|
154
|
+
if (!spec) return <IconTile glyph={GLYPHS.doc} />;
|
|
155
|
+
if (typeof spec === 'string') {
|
|
156
|
+
const g = GLYPHS[spec];
|
|
157
|
+
return <IconTile glyph={g || GLYPHS.doc} />;
|
|
158
|
+
}
|
|
159
|
+
if (React.isValidElement(spec)) return spec;
|
|
160
|
+
return <IconTile glyph={GLYPHS.doc} />;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* ===== IconList =====
|
|
164
|
+
Reusable bordered list with a label header and icon-tagged items.
|
|
165
|
+
Used by Pipeline as its source / consumer end-boxes; exported so
|
|
166
|
+
the same pattern can be dropped in standalone elsewhere. */
|
|
167
|
+
export function IconList({label, items = [], className}) {
|
|
168
|
+
return (
|
|
169
|
+
<div className={[styles.end, className].filter(Boolean).join(' ')}>
|
|
170
|
+
{label && <div className={styles.endLabel}>{label}</div>}
|
|
171
|
+
<ul className={styles.endList}>
|
|
172
|
+
{items.map((it, i) => {
|
|
173
|
+
const item = typeof it === 'string' ? {label: it} : (it || {});
|
|
174
|
+
return (
|
|
175
|
+
<li key={i}>
|
|
176
|
+
<span className={styles.endIcon}>{resolveIcon(item.icon)}</span>
|
|
177
|
+
<span>{item.label}</span>
|
|
178
|
+
</li>
|
|
179
|
+
);
|
|
180
|
+
})}
|
|
181
|
+
</ul>
|
|
182
|
+
</div>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export default function Pipeline({start, end, steps, children, className}) {
|
|
187
|
+
const stepNodes = steps
|
|
188
|
+
? steps.map((s, i) => <PipelineStep key={i} {...s} />)
|
|
189
|
+
: children;
|
|
190
|
+
|
|
191
|
+
return (
|
|
192
|
+
<div className={[styles.pipeline, className].filter(Boolean).join(' ')}>
|
|
193
|
+
{start && <IconList {...start} />}
|
|
194
|
+
<div className={styles.steps}>{stepNodes}</div>
|
|
195
|
+
{end && <IconList {...end} />}
|
|
196
|
+
</div>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <Pipeline /> styles. Layout-grid version of the pipeline-flow.html
|
|
3
|
+
* pattern. Horizontal at >=1100px, stacks vertically below.
|
|
4
|
+
*
|
|
5
|
+
* Each step renders a flat pointy-top hex coloured by family, with
|
|
6
|
+
* the "01" stage number and uppercase kicker stacked above and an
|
|
7
|
+
* explanatory name + caption below. A single animated dotted flow
|
|
8
|
+
* line runs full-width behind the hex row, threading through the hex
|
|
9
|
+
* equator. The hexes sit above the line via z-index so their fills
|
|
10
|
+
* occlude the dashes behind them.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
.pipeline {
|
|
14
|
+
display: grid;
|
|
15
|
+
grid-template-columns: minmax(160px, 220px) 1fr minmax(160px, 220px);
|
|
16
|
+
gap: 24px;
|
|
17
|
+
align-items: start;
|
|
18
|
+
font-family: var(--conduction-typography-font-family-body);
|
|
19
|
+
max-width: 1280px;
|
|
20
|
+
margin: 0 auto;
|
|
21
|
+
}
|
|
22
|
+
@media (max-width: 1100px) {
|
|
23
|
+
.pipeline { grid-template-columns: 1fr; }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* Steps row. Position relative so the ::before line can absolute-
|
|
27
|
+
position itself across the row. */
|
|
28
|
+
.steps {
|
|
29
|
+
position: relative;
|
|
30
|
+
display: flex;
|
|
31
|
+
align-items: flex-start;
|
|
32
|
+
justify-content: space-between;
|
|
33
|
+
gap: 8px;
|
|
34
|
+
}
|
|
35
|
+
@media (max-width: 1100px) {
|
|
36
|
+
.steps { flex-direction: column; align-items: stretch; gap: 24px; }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* Full-width animated dotted line. Sits at the hex's vertical centre
|
|
40
|
+
so it threads through every hex tile. Hexes use z-index:1 so they
|
|
41
|
+
render above the line and visually occlude the dashes behind them. */
|
|
42
|
+
.steps::before {
|
|
43
|
+
content: '';
|
|
44
|
+
position: absolute;
|
|
45
|
+
left: 0;
|
|
46
|
+
right: 0;
|
|
47
|
+
/* Hex midline calc: viewBox 0 0 120 86, midline y=43, prism renders
|
|
48
|
+
at ~100px tall (max-width 140 × aspect 86/120) so midline is ~50px
|
|
49
|
+
from SVG top. Above the SVG the step stacks num (~14) + kicker
|
|
50
|
+
(~14) + 8px margin = ~36px → midline lands at ~86px from steps top.
|
|
51
|
+
Less 1px so the 2px line is centred. */
|
|
52
|
+
top: 85px;
|
|
53
|
+
height: 2px;
|
|
54
|
+
background-image: repeating-linear-gradient(
|
|
55
|
+
to right,
|
|
56
|
+
var(--c-cobalt-300) 0 5px,
|
|
57
|
+
transparent 5px 10px
|
|
58
|
+
);
|
|
59
|
+
background-size: 10px 100%;
|
|
60
|
+
background-repeat: repeat-x;
|
|
61
|
+
opacity: 0.7;
|
|
62
|
+
pointer-events: none;
|
|
63
|
+
z-index: 0;
|
|
64
|
+
animation: flow-shift 0.9s linear infinite;
|
|
65
|
+
}
|
|
66
|
+
@keyframes flow-shift {
|
|
67
|
+
from { background-position: 0 0; }
|
|
68
|
+
to { background-position: 10px 0; }
|
|
69
|
+
}
|
|
70
|
+
@media (max-width: 1100px) {
|
|
71
|
+
.steps::before {
|
|
72
|
+
top: 0;
|
|
73
|
+
bottom: 0;
|
|
74
|
+
left: 50%;
|
|
75
|
+
right: auto;
|
|
76
|
+
width: 2px;
|
|
77
|
+
height: auto;
|
|
78
|
+
background-image: repeating-linear-gradient(
|
|
79
|
+
to bottom,
|
|
80
|
+
var(--c-cobalt-300) 0 5px,
|
|
81
|
+
transparent 5px 10px
|
|
82
|
+
);
|
|
83
|
+
background-size: 100% 10px;
|
|
84
|
+
background-repeat: repeat-y;
|
|
85
|
+
transform: translateX(-50%);
|
|
86
|
+
animation: flow-shift-vert 0.9s linear infinite;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
@keyframes flow-shift-vert {
|
|
90
|
+
from { background-position: 0 0; }
|
|
91
|
+
to { background-position: 0 10px; }
|
|
92
|
+
}
|
|
93
|
+
@media (prefers-reduced-motion: reduce) {
|
|
94
|
+
.steps::before { animation: none; }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Step: stage above hex, hex in middle, name + caption below. */
|
|
98
|
+
.step {
|
|
99
|
+
position: relative;
|
|
100
|
+
z-index: 1;
|
|
101
|
+
flex: 1 1 0;
|
|
102
|
+
min-width: 0;
|
|
103
|
+
text-align: center;
|
|
104
|
+
display: flex;
|
|
105
|
+
flex-direction: column;
|
|
106
|
+
align-items: center;
|
|
107
|
+
gap: 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.num {
|
|
111
|
+
font-family: var(--conduction-typography-font-family-code);
|
|
112
|
+
font-size: 10px;
|
|
113
|
+
letter-spacing: 0.16em;
|
|
114
|
+
color: var(--c-cobalt-400);
|
|
115
|
+
font-weight: 500;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.kicker {
|
|
119
|
+
font-family: var(--conduction-typography-font-family-code);
|
|
120
|
+
font-size: 10px;
|
|
121
|
+
letter-spacing: 0.16em;
|
|
122
|
+
text-transform: uppercase;
|
|
123
|
+
color: var(--c-cobalt-400);
|
|
124
|
+
margin-bottom: 8px;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.prism {
|
|
128
|
+
width: 100%;
|
|
129
|
+
max-width: 140px;
|
|
130
|
+
height: auto;
|
|
131
|
+
display: block;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* Explanatory text under the hex — bold app name + caption sentence,
|
|
135
|
+
matching the kit's `.captions .col` block beneath pipeline-flow.html. */
|
|
136
|
+
.name {
|
|
137
|
+
font-size: 15px;
|
|
138
|
+
font-weight: 700;
|
|
139
|
+
letter-spacing: -0.01em;
|
|
140
|
+
color: var(--c-cobalt-900);
|
|
141
|
+
margin-top: 16px;
|
|
142
|
+
}
|
|
143
|
+
.caption {
|
|
144
|
+
font-size: 13px;
|
|
145
|
+
color: var(--c-cobalt-700);
|
|
146
|
+
line-height: 1.5;
|
|
147
|
+
max-width: 26ch;
|
|
148
|
+
margin: 6px auto 0;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/* End boxes (sources / consumers). Cobalt-50 ground + cobalt-100
|
|
152
|
+
border, kit-identical. Each item gets a small rounded-square icon
|
|
153
|
+
next to the label. */
|
|
154
|
+
.end {
|
|
155
|
+
background: var(--c-cobalt-50);
|
|
156
|
+
border: 1px solid var(--c-cobalt-100);
|
|
157
|
+
border-radius: var(--radius-lg);
|
|
158
|
+
padding: 18px 20px;
|
|
159
|
+
align-self: start;
|
|
160
|
+
display: flex;
|
|
161
|
+
flex-direction: column;
|
|
162
|
+
/* Pull the wrapper down so the SOURCES/CONSUMERS label sits roughly
|
|
163
|
+
level with the per-step kicker text (which lands ~36px below the
|
|
164
|
+
pipeline top). */
|
|
165
|
+
margin-top: 18px;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.endLabel {
|
|
169
|
+
font-family: var(--conduction-typography-font-family-code);
|
|
170
|
+
font-size: 10px;
|
|
171
|
+
letter-spacing: 0.14em;
|
|
172
|
+
text-transform: uppercase;
|
|
173
|
+
color: var(--c-cobalt-400);
|
|
174
|
+
margin-bottom: 14px;
|
|
175
|
+
font-weight: 500;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.endList {
|
|
179
|
+
list-style: none;
|
|
180
|
+
padding: 0;
|
|
181
|
+
margin: 0;
|
|
182
|
+
display: flex;
|
|
183
|
+
flex-direction: column;
|
|
184
|
+
gap: 10px;
|
|
185
|
+
}
|
|
186
|
+
.endList li {
|
|
187
|
+
display: flex;
|
|
188
|
+
align-items: center;
|
|
189
|
+
gap: 10px;
|
|
190
|
+
font-size: 13px;
|
|
191
|
+
color: var(--c-cobalt-700);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.endIcon {
|
|
195
|
+
flex-shrink: 0;
|
|
196
|
+
width: 22px;
|
|
197
|
+
height: 22px;
|
|
198
|
+
display: inline-flex;
|
|
199
|
+
align-items: center;
|
|
200
|
+
justify-content: center;
|
|
201
|
+
}
|
|
202
|
+
.endIcon svg {
|
|
203
|
+
width: 22px;
|
|
204
|
+
height: 22px;
|
|
205
|
+
display: block;
|
|
206
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <PlatformDiagram />
|
|
3
|
+
*
|
|
4
|
+
* React wrapper around the bespoke <platform-diagram> web component
|
|
5
|
+
* that ships in the kit (preview/components/_lib/platform-diagram.{js,css}).
|
|
6
|
+
*
|
|
7
|
+
* The web component does the heavy lifting: it reads its <pd-workspace>,
|
|
8
|
+
* <pd-list>, <pd-item>, <pd-flow> children, lays them out on the
|
|
9
|
+
* 1fr/360px/1fr grid, and draws the SVG flow lines between boxes.
|
|
10
|
+
*
|
|
11
|
+
* This wrapper accepts a typed prop graph instead of forcing callers
|
|
12
|
+
* to inline 13+ <pd-item> SVG elements in MDX. It generates the same
|
|
13
|
+
* DOM the web component expects.
|
|
14
|
+
*
|
|
15
|
+
* Lazy-loads the runtime CSS+JS from /lib/platform-diagram.* on the
|
|
16
|
+
* client; sites that already include those assets via <Head> won't
|
|
17
|
+
* double-load (the script's IIFE is no-op on re-import, the link tag
|
|
18
|
+
* is dedupe'd by href).
|
|
19
|
+
*
|
|
20
|
+
* Usage in MDX:
|
|
21
|
+
*
|
|
22
|
+
* <PlatformDiagram
|
|
23
|
+
* workspace={{logo: '/img/nextcloud-logo.svg', alt: 'Nextcloud', role: 'Workspace'}}
|
|
24
|
+
* lists={[
|
|
25
|
+
* {position: 'top', family: 'nextcloud', items: [
|
|
26
|
+
* {name: 'Files', meta: 'Files', desc: '...', icon: <svg>...</svg>},
|
|
27
|
+
* ...
|
|
28
|
+
* ]},
|
|
29
|
+
* {position: 'top-left', family: 'core', label: 'Technical\nCore', items: [...]},
|
|
30
|
+
* {position: 'top-right', family: 'solutions', label: 'Solutions', items: [...]},
|
|
31
|
+
* {position: 'bottom-left', family: 'workspace', label: 'Workspace\nApps', items: [...]},
|
|
32
|
+
* {position: 'bottom-right', family: 'integrate', label: 'Integrated\nApps',
|
|
33
|
+
* columns: 2, items: [...]},
|
|
34
|
+
* {position: 'bottom-center', family: 'builder', label: 'App\nBuilder',
|
|
35
|
+
* badge: 'COMING SOON', items: [...]},
|
|
36
|
+
* ]}
|
|
37
|
+
* flows={[
|
|
38
|
+
* {from: 'top-left:bottom', to: 'bottom-left:top', shape: 'straight'},
|
|
39
|
+
* {from: 'bottom-center:left', to: 'bottom-left:bottom', shape: 'l-h'},
|
|
40
|
+
* {from: 'bottom-right:bottom', to: 'bottom-left:bottom', shape: 'c-bracket-bottom', clearance: 280},
|
|
41
|
+
* {from: 'top-left:top', to: 'top-right:top', shape: 'c-bracket-top', clearance: 240},
|
|
42
|
+
* ]}
|
|
43
|
+
* />
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
import React from 'react';
|
|
47
|
+
import Head from '@docusaurus/Head';
|
|
48
|
+
import {useLazyScript} from '../../utils/lazyScript';
|
|
49
|
+
|
|
50
|
+
const ASSET_BASE = '/lib';
|
|
51
|
+
|
|
52
|
+
export default function PlatformDiagram({workspace, lists = [], flows = []}) {
|
|
53
|
+
/* platform-diagram.js is loaded post-hydration. The runtime registers
|
|
54
|
+
a custom-element definition that upgrades any <platform-diagram>
|
|
55
|
+
element on the page; running it after React hydration prevents the
|
|
56
|
+
custom-element constructor from mutating the DOM mid-hydration and
|
|
57
|
+
producing #418 / #423 warnings. See utils/lazyScript.js. */
|
|
58
|
+
useLazyScript(ASSET_BASE + '/platform-diagram.js', 'platform-diagram');
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<>
|
|
62
|
+
<Head>
|
|
63
|
+
<link rel="stylesheet" href={ASSET_BASE + '/platform-diagram.css'} />
|
|
64
|
+
</Head>
|
|
65
|
+
|
|
66
|
+
<platform-diagram>
|
|
67
|
+
{workspace && (
|
|
68
|
+
<pd-workspace
|
|
69
|
+
logo={workspace.logo}
|
|
70
|
+
alt={workspace.alt}
|
|
71
|
+
role={workspace.role || 'Workspace'}
|
|
72
|
+
/>
|
|
73
|
+
)}
|
|
74
|
+
|
|
75
|
+
{lists.map((list, i) => (
|
|
76
|
+
<pd-list
|
|
77
|
+
key={i}
|
|
78
|
+
position={list.position}
|
|
79
|
+
family={list.family}
|
|
80
|
+
label={list.label}
|
|
81
|
+
badge={list.badge}
|
|
82
|
+
columns={list.columns}
|
|
83
|
+
>
|
|
84
|
+
{(list.items || []).map((item, j) => (
|
|
85
|
+
<pd-item
|
|
86
|
+
key={j}
|
|
87
|
+
name={item.name}
|
|
88
|
+
meta={item.meta}
|
|
89
|
+
desc={item.desc}
|
|
90
|
+
brand-color={item.brandColor}
|
|
91
|
+
>
|
|
92
|
+
{item.icon}
|
|
93
|
+
</pd-item>
|
|
94
|
+
))}
|
|
95
|
+
</pd-list>
|
|
96
|
+
))}
|
|
97
|
+
|
|
98
|
+
{flows.map((flow, i) => (
|
|
99
|
+
<pd-flow
|
|
100
|
+
key={i}
|
|
101
|
+
from={flow.from}
|
|
102
|
+
to={flow.to}
|
|
103
|
+
shape={flow.shape}
|
|
104
|
+
clearance={flow.clearance}
|
|
105
|
+
/>
|
|
106
|
+
))}
|
|
107
|
+
</platform-diagram>
|
|
108
|
+
</>
|
|
109
|
+
);
|
|
110
|
+
}
|