@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,414 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* <platform-diagram> — declarative workspace + corner-hexes + lists + flows.
|
|
3
|
+
*
|
|
4
|
+
* Authoring shape:
|
|
5
|
+
* <platform-diagram>
|
|
6
|
+
* <pd-workspace logo="..." role="Workspace" alt="Nextcloud"></pd-workspace>
|
|
7
|
+
* <pd-list position="top" family="nextcloud">
|
|
8
|
+
* <pd-item name="Files" desc="Sync, share, and version any file.">
|
|
9
|
+
* <svg viewBox="0 0 24 24"><path d="..."/></svg>
|
|
10
|
+
* </pd-item>
|
|
11
|
+
* ...
|
|
12
|
+
* </pd-list>
|
|
13
|
+
* <pd-list position="top-left" family="core" label="Technical Core">
|
|
14
|
+
* <pd-item name="OpenRegister" meta="Data" desc="..."> <svg .../></pd-item>
|
|
15
|
+
* ...
|
|
16
|
+
* </pd-list>
|
|
17
|
+
* <pd-list position="bottom-center" family="builder" label="App Builder" badge="COMING SOON">
|
|
18
|
+
* ...
|
|
19
|
+
* </pd-list>
|
|
20
|
+
* <pd-list position="bottom-right" family="integrate" label="Integrated Apps" columns="2">
|
|
21
|
+
* ... <!-- columns≥2 splits items into N side-by-side columns -->
|
|
22
|
+
* </pd-list>
|
|
23
|
+
* <pd-flow from="top-left:left" to="bottom-left:left" shape="c-bracket-left"></pd-flow>
|
|
24
|
+
* <pd-flow from="bottom-center:left" to="bottom-left:bottom" shape="l-h"></pd-flow>
|
|
25
|
+
* </platform-diagram>
|
|
26
|
+
*
|
|
27
|
+
* Positions: top, top-left, top-right, bottom-left, bottom-right, bottom-center.
|
|
28
|
+
* "top" renders only a list (no hex tag) — used for the Nextcloud-workspace
|
|
29
|
+
* functions list. The other five render a hex tag plus the list.
|
|
30
|
+
*
|
|
31
|
+
* Flow shapes (orthogonal segments only):
|
|
32
|
+
* straight direct line — use only when from/to share an axis.
|
|
33
|
+
* l-h L-bend, horizontal first then vertical.
|
|
34
|
+
* l-v L-bend, vertical first then horizontal.
|
|
35
|
+
* c-bracket-left out left, parallel, back in right (use when both anchors
|
|
36
|
+
* are on the same vertical column, going around).
|
|
37
|
+
* c-bracket-right out right.
|
|
38
|
+
* c-bracket-top out top.
|
|
39
|
+
* c-bracket-bottom out bottom.
|
|
40
|
+
*
|
|
41
|
+
* Anchor format on `from`/`to`: "<position>:<side>" where side is one of
|
|
42
|
+
* left | right | top | bottom (the four hex side midpoints / peaks).
|
|
43
|
+
*
|
|
44
|
+
* The whole component is light-DOM by design — it renders into its own
|
|
45
|
+
* children, which means CSS scopes via `platform-diagram` selectors and the
|
|
46
|
+
* structure ports directly to React/Docusaurus by capitalising tag names.
|
|
47
|
+
*/
|
|
48
|
+
(function () {
|
|
49
|
+
const NS = 'http://www.w3.org/2000/svg';
|
|
50
|
+
|
|
51
|
+
// Maps the public position name to the legacy class used by the CSS layout.
|
|
52
|
+
// Keeping these stable means the diagram-overview page can opt to use the
|
|
53
|
+
// legacy class names if it ever wants to bypass the component.
|
|
54
|
+
const POSITION_CLASS = {
|
|
55
|
+
'top': 'nextcloud-workspace',
|
|
56
|
+
'top-left': 'tech-core',
|
|
57
|
+
'top-right': 'solutions',
|
|
58
|
+
'bottom-left': 'workspace',
|
|
59
|
+
'bottom-right': 'integrated',
|
|
60
|
+
'bottom-center': 'app-builder',
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Hex centres in workspace-relative coords (px).
|
|
64
|
+
// Derived from the layout in platform-diagram.css — keep in sync if the
|
|
65
|
+
// hex placement ever changes.
|
|
66
|
+
const HEX_CENTRES = {
|
|
67
|
+
'top-left': { cx: -204, cy: -104 },
|
|
68
|
+
'top-right': { cx: 204, cy: -104 },
|
|
69
|
+
'bottom-left': { cx: -204, cy: 104 },
|
|
70
|
+
'bottom-right': { cx: 204, cy: 104 },
|
|
71
|
+
'bottom-center': { cx: 0, cy: 232 },
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Hex side midpoints / peaks (160×185 pointy-top hex).
|
|
75
|
+
const HEX_SIDES = {
|
|
76
|
+
left: { dx: -80, dy: 0 },
|
|
77
|
+
right: { dx: 80, dy: 0 },
|
|
78
|
+
top: { dx: 0, dy: -92.5 },
|
|
79
|
+
bottom: { dx: 0, dy: 92.5 },
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
function anchor(spec) {
|
|
83
|
+
if (!spec || !spec.includes(':')) return null;
|
|
84
|
+
const [position, side] = spec.split(':');
|
|
85
|
+
const c = HEX_CENTRES[position];
|
|
86
|
+
const s = HEX_SIDES[side];
|
|
87
|
+
if (!c || !s) {
|
|
88
|
+
console.warn('[platform-diagram] unknown anchor:', spec);
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
return { x: c.cx + s.dx, y: c.cy + s.dy };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function pathFor(shape, a, b, clearance) {
|
|
95
|
+
/* `clearance` is how far past the further endpoint the bracket runs
|
|
96
|
+
(in workspace-relative px). Defaults to 36 — enough to clear an
|
|
97
|
+
adjacent hex's own outline. Bump it (e.g. 150) when routing around
|
|
98
|
+
a hex that sits between the two anchors. */
|
|
99
|
+
const c = (typeof clearance === 'number' && clearance > 0) ? clearance : 36;
|
|
100
|
+
switch (shape) {
|
|
101
|
+
case 'l-h':
|
|
102
|
+
case 'l-bend':
|
|
103
|
+
case 'l-bend-h':
|
|
104
|
+
return `M ${a.x} ${a.y} L ${b.x} ${a.y} L ${b.x} ${b.y}`;
|
|
105
|
+
case 'l-v':
|
|
106
|
+
case 'l-bend-v':
|
|
107
|
+
return `M ${a.x} ${a.y} L ${a.x} ${b.y} L ${b.x} ${b.y}`;
|
|
108
|
+
case 'c-bracket-left': {
|
|
109
|
+
const x = Math.min(a.x, b.x) - c;
|
|
110
|
+
return `M ${a.x} ${a.y} L ${x} ${a.y} L ${x} ${b.y} L ${b.x} ${b.y}`;
|
|
111
|
+
}
|
|
112
|
+
case 'c-bracket-right': {
|
|
113
|
+
const x = Math.max(a.x, b.x) + c;
|
|
114
|
+
return `M ${a.x} ${a.y} L ${x} ${a.y} L ${x} ${b.y} L ${b.x} ${b.y}`;
|
|
115
|
+
}
|
|
116
|
+
case 'c-bracket-top': {
|
|
117
|
+
const y = Math.min(a.y, b.y) - c;
|
|
118
|
+
return `M ${a.x} ${a.y} L ${a.x} ${y} L ${b.x} ${y} L ${b.x} ${b.y}`;
|
|
119
|
+
}
|
|
120
|
+
case 'c-bracket-bottom': {
|
|
121
|
+
const y = Math.max(a.y, b.y) + c;
|
|
122
|
+
return `M ${a.x} ${a.y} L ${a.x} ${y} L ${b.x} ${y} L ${b.x} ${b.y}`;
|
|
123
|
+
}
|
|
124
|
+
case 'straight':
|
|
125
|
+
default:
|
|
126
|
+
return `M ${a.x} ${a.y} L ${b.x} ${b.y}`;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Parses a <pd-list> (or any source element) into a config object. Reads
|
|
131
|
+
// its <pd-item> children eagerly so we can blow the source tree away after.
|
|
132
|
+
function parseList(el) {
|
|
133
|
+
const items = [];
|
|
134
|
+
for (const itemEl of el.querySelectorAll(':scope > pd-item')) {
|
|
135
|
+
// The item's icon is the first <svg> child. We snapshot a clone now so
|
|
136
|
+
// the original is safe to remove when we replace the host's innerHTML.
|
|
137
|
+
const svg = itemEl.querySelector(':scope > svg');
|
|
138
|
+
items.push({
|
|
139
|
+
name: itemEl.getAttribute('name') || '',
|
|
140
|
+
meta: itemEl.getAttribute('meta') || '',
|
|
141
|
+
desc: itemEl.getAttribute('desc') || '',
|
|
142
|
+
brand: itemEl.hasAttribute('brand') || itemEl.hasAttribute('brand-color'),
|
|
143
|
+
brandColor: itemEl.getAttribute('brand-color') || '',
|
|
144
|
+
glyph: svg ? svg.cloneNode(true) : null,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
const cols = parseInt(el.getAttribute('columns') || '1', 10);
|
|
148
|
+
return {
|
|
149
|
+
position: el.getAttribute('position'),
|
|
150
|
+
family: el.getAttribute('family') || '',
|
|
151
|
+
label: el.getAttribute('label') || '',
|
|
152
|
+
badge: el.getAttribute('badge') || '',
|
|
153
|
+
columns: Number.isFinite(cols) && cols > 1 ? cols : 1,
|
|
154
|
+
items,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function renderList(list) {
|
|
159
|
+
const wrap = document.createElement('div');
|
|
160
|
+
const positionClass = POSITION_CLASS[list.position] || list.position;
|
|
161
|
+
const familyClass = list.family ? `fam-${list.family}` : '';
|
|
162
|
+
const colsClass = list.columns > 1 ? `cols-${list.columns}` : '';
|
|
163
|
+
wrap.className = `box-wrap ${positionClass} ${familyClass} ${colsClass}`.trim();
|
|
164
|
+
|
|
165
|
+
if (list.badge) {
|
|
166
|
+
const badge = document.createElement('span');
|
|
167
|
+
badge.className = 'badge';
|
|
168
|
+
badge.textContent = list.badge;
|
|
169
|
+
wrap.appendChild(badge);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const box = document.createElement('div');
|
|
173
|
+
box.className = 'box' + (list.columns > 1 ? ` cols-${list.columns}` : '');
|
|
174
|
+
|
|
175
|
+
function makeRow(item) {
|
|
176
|
+
const row = document.createElement('div');
|
|
177
|
+
row.className = 'row';
|
|
178
|
+
|
|
179
|
+
const glyph = document.createElement('span');
|
|
180
|
+
glyph.className = 'glyph' + (item.brand ? ' brand' : '');
|
|
181
|
+
if (item.brandColor) glyph.style.color = item.brandColor;
|
|
182
|
+
if (item.glyph) glyph.appendChild(item.glyph);
|
|
183
|
+
row.appendChild(glyph);
|
|
184
|
+
|
|
185
|
+
const name = document.createElement('span');
|
|
186
|
+
name.className = 'name';
|
|
187
|
+
name.textContent = item.name;
|
|
188
|
+
row.appendChild(name);
|
|
189
|
+
|
|
190
|
+
if (item.meta) {
|
|
191
|
+
const meta = document.createElement('span');
|
|
192
|
+
meta.className = 'meta';
|
|
193
|
+
meta.textContent = item.meta;
|
|
194
|
+
row.appendChild(meta);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (item.desc) {
|
|
198
|
+
const desc = document.createElement('div');
|
|
199
|
+
desc.className = 'desc';
|
|
200
|
+
desc.textContent = item.desc;
|
|
201
|
+
row.appendChild(desc);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return row;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (list.columns > 1) {
|
|
208
|
+
// Split items across N columns, filling top-to-bottom (col 1 first).
|
|
209
|
+
// Each column wraps its rows so .row + .row borders only apply within
|
|
210
|
+
// a single column.
|
|
211
|
+
const perCol = Math.ceil(list.items.length / list.columns);
|
|
212
|
+
for (let c = 0; c < list.columns; c++) {
|
|
213
|
+
const col = document.createElement('div');
|
|
214
|
+
col.className = 'col';
|
|
215
|
+
const start = c * perCol;
|
|
216
|
+
for (const item of list.items.slice(start, start + perCol)) {
|
|
217
|
+
col.appendChild(makeRow(item));
|
|
218
|
+
}
|
|
219
|
+
if (col.childElementCount > 0) box.appendChild(col);
|
|
220
|
+
}
|
|
221
|
+
} else {
|
|
222
|
+
for (const item of list.items) box.appendChild(makeRow(item));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
wrap.appendChild(box);
|
|
226
|
+
return wrap;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function renderHex(list) {
|
|
230
|
+
if (!list.label || list.position === 'top') return null;
|
|
231
|
+
const positionClass = POSITION_CLASS[list.position] || list.position;
|
|
232
|
+
const familyClass = list.family ? `fam-${list.family}` : '';
|
|
233
|
+
const hex = document.createElement('div');
|
|
234
|
+
hex.className = `workspace-corner-hex ${positionClass} ${familyClass}`.trim();
|
|
235
|
+
// Allow newline in label to render a <br>; otherwise straight text.
|
|
236
|
+
if (list.label.includes('\n')) {
|
|
237
|
+
list.label.split('\n').forEach((line, i) => {
|
|
238
|
+
if (i) hex.appendChild(document.createElement('br'));
|
|
239
|
+
hex.appendChild(document.createTextNode(line));
|
|
240
|
+
});
|
|
241
|
+
} else {
|
|
242
|
+
hex.textContent = list.label;
|
|
243
|
+
}
|
|
244
|
+
return hex;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function renderFlows(flows) {
|
|
248
|
+
if (!flows.length) return null;
|
|
249
|
+
const svg = document.createElementNS(NS, 'svg');
|
|
250
|
+
svg.setAttribute('class', 'flow-overlay');
|
|
251
|
+
// Symmetric viewBox: x∈[-340,+340], y∈[-325,+325]. The SVG element is
|
|
252
|
+
// centred in the workspace's grid cell, so SVG (0,0) lands exactly on the
|
|
253
|
+
// workspace centre and path coords match the workspace-relative arithmetic.
|
|
254
|
+
svg.setAttribute('viewBox', '-340 -325 680 650');
|
|
255
|
+
svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
|
|
256
|
+
svg.setAttribute('aria-hidden', 'true');
|
|
257
|
+
for (const f of flows) {
|
|
258
|
+
const path = document.createElementNS(NS, 'path');
|
|
259
|
+
path.setAttribute('class', 'flow-line');
|
|
260
|
+
if (f.path) {
|
|
261
|
+
// Custom raw path overrides shape-based routing.
|
|
262
|
+
path.setAttribute('d', f.path);
|
|
263
|
+
} else {
|
|
264
|
+
const a = anchor(f.from);
|
|
265
|
+
const b = anchor(f.to);
|
|
266
|
+
if (!a || !b) continue;
|
|
267
|
+
path.setAttribute('d', pathFor(f.shape, a, b, f.clearance));
|
|
268
|
+
}
|
|
269
|
+
if (f.color) path.setAttribute('stroke', f.color);
|
|
270
|
+
svg.appendChild(path);
|
|
271
|
+
}
|
|
272
|
+
return svg;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
class PlatformDiagram extends HTMLElement {
|
|
276
|
+
connectedCallback() {
|
|
277
|
+
if (this._rendered) return;
|
|
278
|
+
this._rendered = true;
|
|
279
|
+
this._build();
|
|
280
|
+
this._setupScrollProgress();
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
disconnectedCallback() {
|
|
284
|
+
if (this._teardownScroll) this._teardownScroll();
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/* Scroll-linked entrance: writes --pd-hex-progress / --pd-list-progress /
|
|
288
|
+
--pd-flow-progress on `this` based on where the diagram sits in the
|
|
289
|
+
viewport. Scrolling up reverses the entrance because the same mapping
|
|
290
|
+
runs in reverse. rAF-throttled so we coalesce wheel/touch bursts. */
|
|
291
|
+
_setupScrollProgress() {
|
|
292
|
+
const reduced = window.matchMedia &&
|
|
293
|
+
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
294
|
+
if (reduced) return; /* CSS defaults to 1 — diagram stays settled */
|
|
295
|
+
|
|
296
|
+
let raf = null;
|
|
297
|
+
const tick = () => { raf = null; this._updateProgress(); };
|
|
298
|
+
const onScroll = () => { if (!raf) raf = requestAnimationFrame(tick); };
|
|
299
|
+
|
|
300
|
+
window.addEventListener('scroll', onScroll, { passive: true });
|
|
301
|
+
window.addEventListener('resize', onScroll, { passive: true });
|
|
302
|
+
this._teardownScroll = () => {
|
|
303
|
+
window.removeEventListener('scroll', onScroll);
|
|
304
|
+
window.removeEventListener('resize', onScroll);
|
|
305
|
+
if (raf) cancelAnimationFrame(raf);
|
|
306
|
+
};
|
|
307
|
+
this._updateProgress();
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
_updateProgress() {
|
|
311
|
+
/* Anchor on the workspace hex's centre — that's the user's "the diagram
|
|
312
|
+
is here" point. Anchoring on `this` is wrong because the host has
|
|
313
|
+
240px of top-padding before any visible content, so its rect.top
|
|
314
|
+
crosses the viewport long before the workspace appears. */
|
|
315
|
+
const target = this.querySelector('.workspace') || this;
|
|
316
|
+
const rect = target.getBoundingClientRect();
|
|
317
|
+
const vh = window.innerHeight || document.documentElement.clientHeight;
|
|
318
|
+
const centre = rect.top + rect.height / 2;
|
|
319
|
+
|
|
320
|
+
/* Trigger window: progress runs 0 → 1 as the workspace centre moves from
|
|
321
|
+
the viewport bottom (vh) up to the viewport centre (vh * 0.5).
|
|
322
|
+
Beyond the centre, progress stays at 1; below the viewport, 0.
|
|
323
|
+
Scrolling back up reverses the mapping. */
|
|
324
|
+
const start = vh;
|
|
325
|
+
const end = vh * 0.5;
|
|
326
|
+
const p = Math.max(0, Math.min(1, (start - centre) / (start - end)));
|
|
327
|
+
|
|
328
|
+
/* Stage progress (each stage clamped 0..1).
|
|
329
|
+
hex covers 0.00 → 0.40 (move + fade in)
|
|
330
|
+
list covers 0.30 → 0.75 (slide + fade in after hexes)
|
|
331
|
+
flow covers 0.75 → 1.00 (fade in last) */
|
|
332
|
+
const hex = Math.max(0, Math.min(1, (p - 0.00) / 0.40));
|
|
333
|
+
const list = Math.max(0, Math.min(1, (p - 0.30) / 0.45));
|
|
334
|
+
const flow = Math.max(0, Math.min(1, (p - 0.75) / 0.25));
|
|
335
|
+
|
|
336
|
+
this.style.setProperty('--pd-hex-progress', String(hex));
|
|
337
|
+
this.style.setProperty('--pd-list-progress', String(list));
|
|
338
|
+
this.style.setProperty('--pd-flow-progress', String(flow));
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
_build() {
|
|
342
|
+
// 1. Read declarative config from the light-DOM children.
|
|
343
|
+
let workspace = null;
|
|
344
|
+
const lists = [];
|
|
345
|
+
const flows = [];
|
|
346
|
+
|
|
347
|
+
for (const child of [...this.children]) {
|
|
348
|
+
const tag = child.tagName.toLowerCase();
|
|
349
|
+
if (tag === 'pd-workspace') {
|
|
350
|
+
workspace = {
|
|
351
|
+
logo: child.getAttribute('logo') || '',
|
|
352
|
+
role: child.getAttribute('role') || '',
|
|
353
|
+
alt: child.getAttribute('alt') || '',
|
|
354
|
+
};
|
|
355
|
+
} else if (tag === 'pd-list') {
|
|
356
|
+
lists.push(parseList(child));
|
|
357
|
+
} else if (tag === 'pd-flow') {
|
|
358
|
+
const cl = parseFloat(child.getAttribute('clearance'));
|
|
359
|
+
flows.push({
|
|
360
|
+
from: child.getAttribute('from'),
|
|
361
|
+
to: child.getAttribute('to'),
|
|
362
|
+
shape: child.getAttribute('shape') || 'l-h',
|
|
363
|
+
color: child.getAttribute('color') || '',
|
|
364
|
+
clearance: Number.isFinite(cl) ? cl : 0,
|
|
365
|
+
// Raw SVG `d` string for custom multi-bend routes that don't
|
|
366
|
+
// fit any built-in shape. Coords are workspace-relative (origin
|
|
367
|
+
// at workspace centre, same system as anchor(...)). When
|
|
368
|
+
// present, it overrides shape/from/to.
|
|
369
|
+
path: child.getAttribute('path') || '',
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// 2. Replace the source tree with the rendered diagram.
|
|
375
|
+
this.innerHTML = '';
|
|
376
|
+
const grid = document.createElement('div');
|
|
377
|
+
grid.className = 'diagram-grid';
|
|
378
|
+
|
|
379
|
+
if (workspace) {
|
|
380
|
+
const k = document.createElement('div');
|
|
381
|
+
k.className = 'workspace';
|
|
382
|
+
const img = document.createElement('img');
|
|
383
|
+
img.className = 'workspace-logo';
|
|
384
|
+
if (workspace.logo) img.src = workspace.logo;
|
|
385
|
+
img.alt = workspace.alt;
|
|
386
|
+
k.appendChild(img);
|
|
387
|
+
if (workspace.role) {
|
|
388
|
+
const r = document.createElement('div');
|
|
389
|
+
r.className = 'role';
|
|
390
|
+
r.textContent = workspace.role;
|
|
391
|
+
k.appendChild(r);
|
|
392
|
+
}
|
|
393
|
+
grid.appendChild(k);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Lists first, then their corresponding hex tags (so DOM order matches
|
|
397
|
+
// the original platform-overview layout: hex tags appear after lists).
|
|
398
|
+
for (const list of lists) grid.appendChild(renderList(list));
|
|
399
|
+
for (const list of lists) {
|
|
400
|
+
const hex = renderHex(list);
|
|
401
|
+
if (hex) grid.appendChild(hex);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const flowSvg = renderFlows(flows);
|
|
405
|
+
if (flowSvg) grid.appendChild(flowSvg);
|
|
406
|
+
|
|
407
|
+
this.appendChild(grid);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (!customElements.get('platform-diagram')) {
|
|
412
|
+
customElements.define('platform-diagram', PlatformDiagram);
|
|
413
|
+
}
|
|
414
|
+
})();
|