@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,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZaakAfhandelApp abstract — citizen + case-worker portal.
|
|
3
|
+
*
|
|
4
|
+
* Inferred from the app role (citizen-facing case portal, ZGW APIs,
|
|
5
|
+
* archive interfaces): centre shows the active case-worker queue
|
|
6
|
+
* (werkvoorraad widget) with stage pips, plus a citizen-side timeline
|
|
7
|
+
* of recent submissions. Left nav for queue / archive / public portal /
|
|
8
|
+
* audit / settings.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import React from 'react';
|
|
12
|
+
import styles from '../AppMock.module.css';
|
|
13
|
+
|
|
14
|
+
export default function ZaakAfhandelAppMock() {
|
|
15
|
+
return (
|
|
16
|
+
<>
|
|
17
|
+
<div className={styles.topbar}>
|
|
18
|
+
<div className={styles.logo}></div>
|
|
19
|
+
{Array.from({length: 14}).map((_, i) => <div key={i} className={styles.icon}></div>)}
|
|
20
|
+
<div className={styles.spacer}></div>
|
|
21
|
+
<div className={styles.bell}></div>
|
|
22
|
+
<div className={styles.avatar}></div>
|
|
23
|
+
</div>
|
|
24
|
+
<div className={[styles.body, styles.procest].filter(Boolean).join(' ')}>
|
|
25
|
+
<div className={styles.nav}>
|
|
26
|
+
<div className={styles.navHead}><div className={styles.h}></div><div className={styles.l}></div></div>
|
|
27
|
+
{[true, false, false, false, false].map((active, i) => (
|
|
28
|
+
<div key={i} className={[styles.item, active && styles.active].filter(Boolean).join(' ')}>
|
|
29
|
+
<div className={styles.ico}></div>
|
|
30
|
+
<div className={styles.l}></div>
|
|
31
|
+
</div>
|
|
32
|
+
))}
|
|
33
|
+
</div>
|
|
34
|
+
<div className={styles.col}>
|
|
35
|
+
<div className={styles.head}>
|
|
36
|
+
<div className={styles.row + ' ' + styles.head} style={{width: '35%'}}></div>
|
|
37
|
+
<div className={styles.actions}>
|
|
38
|
+
<div className={styles.btn + ' ' + styles.ghost}></div>
|
|
39
|
+
<div className={styles.btn}></div>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
{/* Case-worker werkvoorraad */}
|
|
43
|
+
<div className={[styles.w, styles['w-werkvoorraad']].join(' ')}>
|
|
44
|
+
<div className={styles.wHead}>
|
|
45
|
+
<div className={styles.h}></div><div className={styles.t}></div>
|
|
46
|
+
</div>
|
|
47
|
+
<div className={styles.list}>
|
|
48
|
+
<div className={[styles.item, styles.now].join(' ')}><div className={styles.stage}></div><div className={styles.l1}></div><div className={styles.av}></div></div>
|
|
49
|
+
<div className={styles.item}><div className={styles.stage}></div><div className={styles.l1}></div><div className={[styles.av, styles.b].join(' ')}></div></div>
|
|
50
|
+
<div className={[styles.item, styles.late].join(' ')}><div className={styles.stage}></div><div className={styles.l1}></div><div className={[styles.av, styles.c].join(' ')}></div></div>
|
|
51
|
+
<div className={styles.item}><div className={styles.stage}></div><div className={styles.l1}></div><div className={styles.av}></div></div>
|
|
52
|
+
<div className={styles.item}><div className={styles.stage}></div><div className={styles.l1}></div><div className={[styles.av, styles.b].join(' ')}></div></div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
{/* Citizen-side recent submissions list */}
|
|
56
|
+
<div className={[styles.w, styles['w-rss']].join(' ')}>
|
|
57
|
+
<div className={styles.wHead}>
|
|
58
|
+
<div className={styles.h}></div><div className={styles.t}></div>
|
|
59
|
+
</div>
|
|
60
|
+
<div className={styles.list}>
|
|
61
|
+
<div className={styles.item}><div className={styles.src}></div><div className={styles.title}></div><div className={styles.when}></div></div>
|
|
62
|
+
<div className={styles.item}><div className={styles.src}></div><div className={styles.title}></div><div className={styles.when}></div></div>
|
|
63
|
+
<div className={styles.item}><div className={styles.src}></div><div className={styles.title}></div><div className={styles.when}></div></div>
|
|
64
|
+
<div className={styles.item}><div className={styles.src}></div><div className={styles.title}></div><div className={styles.when}></div></div>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <AppsGrid />
|
|
3
|
+
*
|
|
4
|
+
* Filterable apps grid: a row of category chips above a 3-up grid of
|
|
5
|
+
* <AppCard/> tiles. Mirrors preview/components/apps-grid.html. Each
|
|
6
|
+
* app declares one or more categories; clicking a chip narrows the
|
|
7
|
+
* grid to apps in that category. The first chip is always "All".
|
|
8
|
+
*
|
|
9
|
+
* For non-interactive 3-up rows (e.g. "Most installed" on landing.html),
|
|
10
|
+
* use <AppsPreview/> instead. AppsGrid is the catalogue surface.
|
|
11
|
+
*
|
|
12
|
+
* Usage in MDX:
|
|
13
|
+
*
|
|
14
|
+
* <AppsGrid
|
|
15
|
+
* categories={['All', 'Data', 'Processes', 'Connectors', 'Documents', 'AI']}
|
|
16
|
+
* apps={[
|
|
17
|
+
* {
|
|
18
|
+
* name: 'OpenRegister',
|
|
19
|
+
* tagline: 'Schemas, registers, structured data objects.',
|
|
20
|
+
* status: 'STABLE', version: 'v3.1',
|
|
21
|
+
* href: '/apps/openregister',
|
|
22
|
+
* icon: <svg>...</svg>,
|
|
23
|
+
* categories: ['Data'],
|
|
24
|
+
* },
|
|
25
|
+
* // ...
|
|
26
|
+
* ]}
|
|
27
|
+
* />
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import React, {useState, useMemo} from 'react';
|
|
31
|
+
import {AppCard} from '../AppsPreview/AppsPreview';
|
|
32
|
+
import styles from './AppsGrid.module.css';
|
|
33
|
+
|
|
34
|
+
const ALL_KEY = 'All';
|
|
35
|
+
|
|
36
|
+
export default function AppsGrid({categories, apps = [], className}) {
|
|
37
|
+
const cats = categories && categories.length > 0
|
|
38
|
+
? categories
|
|
39
|
+
: [ALL_KEY, ...uniqueCategories(apps)];
|
|
40
|
+
|
|
41
|
+
const [active, setActive] = useState(cats[0] || ALL_KEY);
|
|
42
|
+
|
|
43
|
+
const visible = useMemo(() => {
|
|
44
|
+
if (!active || active === ALL_KEY) return apps;
|
|
45
|
+
return apps.filter(a => Array.isArray(a.categories) && a.categories.includes(active));
|
|
46
|
+
}, [apps, active]);
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<div className={[styles.section, className].filter(Boolean).join(' ')}>
|
|
50
|
+
<div className={styles.chips} role="tablist" aria-label="Filter apps by category">
|
|
51
|
+
{cats.map((c, i) => (
|
|
52
|
+
<button
|
|
53
|
+
key={i}
|
|
54
|
+
type="button"
|
|
55
|
+
role="tab"
|
|
56
|
+
aria-selected={c === active}
|
|
57
|
+
className={[styles.chip, c === active ? styles.chipActive : null].filter(Boolean).join(' ')}
|
|
58
|
+
onClick={() => setActive(c)}
|
|
59
|
+
>
|
|
60
|
+
{c}
|
|
61
|
+
</button>
|
|
62
|
+
))}
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<div className={styles.grid}>
|
|
66
|
+
{visible.map((app, i) => <AppCard key={i} app={app} />)}
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
{visible.length === 0 && (
|
|
70
|
+
<p className={styles.empty}>No apps match this filter.</p>
|
|
71
|
+
)}
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function uniqueCategories(apps) {
|
|
77
|
+
const seen = new Set();
|
|
78
|
+
for (const a of apps) {
|
|
79
|
+
if (Array.isArray(a.categories)) {
|
|
80
|
+
for (const c of a.categories) seen.add(c);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return Array.from(seen);
|
|
84
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <AppsGrid /> styles. Mirrors apps-grid.html chip-row + 3-up grid.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
.section { font-family: var(--conduction-typography-font-family-body); }
|
|
6
|
+
|
|
7
|
+
.chips {
|
|
8
|
+
display: flex;
|
|
9
|
+
gap: 8px;
|
|
10
|
+
margin-bottom: 32px;
|
|
11
|
+
flex-wrap: wrap;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.chip {
|
|
15
|
+
padding: 7px 16px;
|
|
16
|
+
border-radius: var(--radius-pill);
|
|
17
|
+
background: var(--c-cobalt-50);
|
|
18
|
+
color: var(--c-cobalt-700);
|
|
19
|
+
font-size: 14px;
|
|
20
|
+
font-weight: 500;
|
|
21
|
+
border: 1px solid transparent;
|
|
22
|
+
cursor: pointer;
|
|
23
|
+
font-family: inherit;
|
|
24
|
+
transition: background 140ms ease, color 140ms ease;
|
|
25
|
+
}
|
|
26
|
+
.chip:hover { background: var(--c-cobalt-100); }
|
|
27
|
+
.chipActive {
|
|
28
|
+
background: var(--c-blue-cobalt);
|
|
29
|
+
color: white;
|
|
30
|
+
}
|
|
31
|
+
.chipActive:hover { background: var(--c-cobalt-700); }
|
|
32
|
+
|
|
33
|
+
.grid {
|
|
34
|
+
display: grid;
|
|
35
|
+
grid-template-columns: repeat(3, 1fr);
|
|
36
|
+
gap: 20px;
|
|
37
|
+
}
|
|
38
|
+
@media (max-width: 900px) { .grid { grid-template-columns: repeat(2, 1fr); } }
|
|
39
|
+
@media (max-width: 600px) { .grid { grid-template-columns: 1fr; } }
|
|
40
|
+
|
|
41
|
+
.empty {
|
|
42
|
+
margin: 32px 0;
|
|
43
|
+
font-size: 14px;
|
|
44
|
+
color: var(--c-cobalt-400);
|
|
45
|
+
text-align: center;
|
|
46
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <AppsPreview />
|
|
3
|
+
*
|
|
4
|
+
* Featured apps section: 3-up app cards + "browse all" link.
|
|
5
|
+
* Section-head pattern (eyebrow + h2 + lede) above the cards.
|
|
6
|
+
*
|
|
7
|
+
* Mirrors the .apps-preview section in preview/pages/landing.html,
|
|
8
|
+
* with .app-card cards from preview/components/apps-grid.css.
|
|
9
|
+
*
|
|
10
|
+
* Composes from primitives:
|
|
11
|
+
* <SectionHead/> for the eyebrow + title + lede pattern
|
|
12
|
+
* <HexBullet/> for the status dot inside each <AppCard/> meta
|
|
13
|
+
*
|
|
14
|
+
* Usage in MDX:
|
|
15
|
+
*
|
|
16
|
+
* <AppsPreview
|
|
17
|
+
* eyebrow="Most installed"
|
|
18
|
+
* title="Three apps that ship the most outcomes."
|
|
19
|
+
* lede={<>Start with the apps that solve a concrete problem ...</>}
|
|
20
|
+
* apps={[
|
|
21
|
+
* {
|
|
22
|
+
* name: 'OpenCatalogi',
|
|
23
|
+
* tagline: 'Public software catalog. Every app, dataset...',
|
|
24
|
+
* status: 'STABLE',
|
|
25
|
+
* version: 'v2.4 · NL · EN',
|
|
26
|
+
* href: '/apps/opencatalogi',
|
|
27
|
+
* icon: <svg>...</svg>,
|
|
28
|
+
* },
|
|
29
|
+
* ...
|
|
30
|
+
* ]}
|
|
31
|
+
* seeAll={{label: 'Browse all twelve apps', href: '/apps'}}
|
|
32
|
+
* />
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import React from 'react';
|
|
36
|
+
import HexBullet from '../primitives/HexBullet';
|
|
37
|
+
import SectionHead from '../primitives/SectionHead';
|
|
38
|
+
import styles from './AppsPreview.module.css';
|
|
39
|
+
|
|
40
|
+
function AppCard({app}) {
|
|
41
|
+
const statusColor = (app.status || '').toLowerCase() === 'beta'
|
|
42
|
+
? 'var(--c-orange-knvb)'
|
|
43
|
+
: 'var(--c-mint-500)';
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<a href={app.href || '#'} className={styles.card}>
|
|
47
|
+
<div className={styles.iconWrap}>{app.icon}</div>
|
|
48
|
+
<div className={styles.name}>{app.name}</div>
|
|
49
|
+
<div className={styles.tagline}>{app.tagline}</div>
|
|
50
|
+
<div className={styles.meta}>
|
|
51
|
+
{app.status && (
|
|
52
|
+
<span className={styles.badge}>
|
|
53
|
+
<HexBullet size="sm" color={statusColor} />
|
|
54
|
+
{app.status}
|
|
55
|
+
</span>
|
|
56
|
+
)}
|
|
57
|
+
{app.version && <span className={styles.ver}>{app.version}</span>}
|
|
58
|
+
</div>
|
|
59
|
+
</a>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export default function AppsPreview({eyebrow, title, lede, apps = [], seeAll}) {
|
|
64
|
+
return (
|
|
65
|
+
<section className={styles.section}>
|
|
66
|
+
{(eyebrow || title || lede) && (
|
|
67
|
+
<SectionHead eyebrow={eyebrow} title={title} lede={lede} />
|
|
68
|
+
)}
|
|
69
|
+
|
|
70
|
+
<div className={styles.row}>
|
|
71
|
+
{apps.map((app, i) => (
|
|
72
|
+
<AppCard key={i} app={app} />
|
|
73
|
+
))}
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
{seeAll && (
|
|
77
|
+
<a href={seeAll.href || '#'} className={styles.seeAll}>
|
|
78
|
+
{seeAll.label} →
|
|
79
|
+
</a>
|
|
80
|
+
)}
|
|
81
|
+
</section>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export {AppCard};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <AppsPreview /> styles. Mirrors the .apps-preview section from
|
|
3
|
+
* preview/pages/landing.html plus .app-card from
|
|
4
|
+
* preview/components/apps-grid.css (CSS Module copy so it scopes).
|
|
5
|
+
*
|
|
6
|
+
* SectionHead + HexBullet primitives carry their own styling now;
|
|
7
|
+
* this module only owns the section frame, the card grid, and the
|
|
8
|
+
* .app-card rules.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
.section {
|
|
12
|
+
max-width: 1280px;
|
|
13
|
+
margin: 0 auto;
|
|
14
|
+
padding: 96px 64px;
|
|
15
|
+
font-family: var(--conduction-typography-font-family-body);
|
|
16
|
+
background: white;
|
|
17
|
+
}
|
|
18
|
+
@media (max-width: 900px) {
|
|
19
|
+
.section { padding: 64px 24px; }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* App card grid */
|
|
23
|
+
.row {
|
|
24
|
+
display: grid;
|
|
25
|
+
grid-template-columns: repeat(3, 1fr);
|
|
26
|
+
gap: 20px;
|
|
27
|
+
}
|
|
28
|
+
@media (max-width: 900px) {
|
|
29
|
+
.row { grid-template-columns: 1fr; }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* App card, mirrors .app-card from preview/components/apps-grid.css */
|
|
33
|
+
.card {
|
|
34
|
+
background: white;
|
|
35
|
+
border: 1px solid var(--c-cobalt-100);
|
|
36
|
+
border-radius: var(--radius-lg);
|
|
37
|
+
padding: 28px;
|
|
38
|
+
text-decoration: none;
|
|
39
|
+
color: inherit;
|
|
40
|
+
transition: all 160ms ease;
|
|
41
|
+
display: flex;
|
|
42
|
+
flex-direction: column;
|
|
43
|
+
gap: 16px;
|
|
44
|
+
}
|
|
45
|
+
.card:hover {
|
|
46
|
+
border-color: var(--c-blue-cobalt);
|
|
47
|
+
transform: translateY(-2px);
|
|
48
|
+
box-shadow: var(--shadow-2);
|
|
49
|
+
text-decoration: none;
|
|
50
|
+
color: inherit;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.iconWrap {
|
|
54
|
+
width: 56px;
|
|
55
|
+
height: 64px;
|
|
56
|
+
clip-path: var(--hex-pointy-top);
|
|
57
|
+
background: var(--c-blue-cobalt);
|
|
58
|
+
color: white;
|
|
59
|
+
display: flex;
|
|
60
|
+
align-items: center;
|
|
61
|
+
justify-content: center;
|
|
62
|
+
flex-shrink: 0;
|
|
63
|
+
}
|
|
64
|
+
.iconWrap svg {
|
|
65
|
+
width: 26px;
|
|
66
|
+
height: 26px;
|
|
67
|
+
stroke: currentColor;
|
|
68
|
+
stroke-width: 2;
|
|
69
|
+
fill: none;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.name {
|
|
73
|
+
font-size: 20px;
|
|
74
|
+
font-weight: 700;
|
|
75
|
+
letter-spacing: -0.01em;
|
|
76
|
+
color: var(--c-cobalt-900);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.tagline {
|
|
80
|
+
font-size: 15px;
|
|
81
|
+
color: var(--c-cobalt-700);
|
|
82
|
+
line-height: 1.5;
|
|
83
|
+
}
|
|
84
|
+
.tagline :global(.next-blue) { color: var(--c-nextcloud-blue); }
|
|
85
|
+
|
|
86
|
+
.meta {
|
|
87
|
+
display: flex;
|
|
88
|
+
align-items: center;
|
|
89
|
+
justify-content: space-between;
|
|
90
|
+
margin-top: auto;
|
|
91
|
+
padding-top: 20px;
|
|
92
|
+
border-top: 1px solid var(--c-cobalt-100);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.badge {
|
|
96
|
+
display: inline-flex;
|
|
97
|
+
align-items: center;
|
|
98
|
+
gap: 8px;
|
|
99
|
+
font-family: var(--conduction-typography-font-family-code);
|
|
100
|
+
font-size: 11px;
|
|
101
|
+
letter-spacing: 0.06em;
|
|
102
|
+
text-transform: uppercase;
|
|
103
|
+
color: var(--c-cobalt-700);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.ver {
|
|
107
|
+
font-family: var(--conduction-typography-font-family-code);
|
|
108
|
+
font-size: 12px;
|
|
109
|
+
color: var(--c-cobalt-400);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* "Browse all" link */
|
|
113
|
+
.seeAll {
|
|
114
|
+
margin-top: 32px;
|
|
115
|
+
display: inline-flex;
|
|
116
|
+
align-items: center;
|
|
117
|
+
gap: 8px;
|
|
118
|
+
color: var(--c-blue-cobalt);
|
|
119
|
+
font-weight: 600;
|
|
120
|
+
font-size: 15px;
|
|
121
|
+
text-decoration: none;
|
|
122
|
+
border-bottom: 2px solid var(--c-orange-knvb);
|
|
123
|
+
padding-bottom: 2px;
|
|
124
|
+
}
|
|
125
|
+
.seeAll:hover {
|
|
126
|
+
color: var(--c-orange-knvb);
|
|
127
|
+
text-decoration: none;
|
|
128
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <Clients />
|
|
3
|
+
*
|
|
4
|
+
* Public-sector and ecosystem logo wall. Trust-signal section for the
|
|
5
|
+
* Conduction landing, separate from <PartnerCard /> which is the
|
|
6
|
+
* commercial implementation-partner row on /partners.
|
|
7
|
+
*
|
|
8
|
+
* Two variants:
|
|
9
|
+
* - "grid" (default): static columns, used for the partners row
|
|
10
|
+
* and any short logo set
|
|
11
|
+
* - "marquee": three pointy-top hex rows, honeycomb stagger, all
|
|
12
|
+
* lanes scroll right-to-left at the same speed so the
|
|
13
|
+
* rows read as one continuous wall. Logos default to
|
|
14
|
+
* grayscale and restore colour on hover; the lane pauses
|
|
15
|
+
* on hover and respects prefers-reduced-motion. Below
|
|
16
|
+
* 720px it collapses to a single lane.
|
|
17
|
+
*
|
|
18
|
+
* Logos live in sites/www/static/img/clients/ and ship via the brand
|
|
19
|
+
* assets folder; the kit's downloads page mirrors the same set.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import React from 'react';
|
|
23
|
+
import Head from '@docusaurus/Head';
|
|
24
|
+
import SectionHead from '../primitives/SectionHead';
|
|
25
|
+
import {useLazyScript} from '../../utils/lazyScript';
|
|
26
|
+
import styles from './Clients.module.css';
|
|
27
|
+
|
|
28
|
+
const LOGO_MEMORY_BASE = '/lib';
|
|
29
|
+
|
|
30
|
+
export const DEFAULT_CLIENTS = [
|
|
31
|
+
{name: 'Albrandswaard', src: '/img/clients/albrandswaard.png'},
|
|
32
|
+
{name: 'Alkmaar', src: '/img/clients/alkmaar.png'},
|
|
33
|
+
{name: 'Amsterdam', src: '/img/clients/amsterdam.png'},
|
|
34
|
+
{name: 'Baarn', src: '/img/clients/baarn.png'},
|
|
35
|
+
{name: 'Barendrecht', src: '/img/clients/barendrecht.png'},
|
|
36
|
+
{name: 'Barneveld', src: '/img/clients/barneveld.svg'},
|
|
37
|
+
{name: 'Beek', src: '/img/clients/beek.svg'},
|
|
38
|
+
{name: 'Breda', src: '/img/clients/breda.png'},
|
|
39
|
+
{name: 'Buren', src: '/img/clients/buren.png'},
|
|
40
|
+
{name: 'DBP', src: '/img/clients/dbp.svg'},
|
|
41
|
+
{name: 'De Bilt', src: '/img/clients/de-bilt.svg'},
|
|
42
|
+
{name: 'Delft', src: '/img/clients/delft.png'},
|
|
43
|
+
{name: 'Dinkelland', src: '/img/clients/dinkelland.png'},
|
|
44
|
+
{name: 'Edam-Volendam', src: '/img/clients/edam-volendam.png'},
|
|
45
|
+
{name: 'Ede', src: '/img/clients/ede.svg'},
|
|
46
|
+
{name: 'Epe', src: '/img/clients/epe.png'},
|
|
47
|
+
{name: 'Gooise Meren', src: '/img/clients/gooise-meren.svg'},
|
|
48
|
+
{name: 'Gouda', src: '/img/clients/gouda.png'},
|
|
49
|
+
{name: 'Hoeksche Waard', src: '/img/clients/hoeksche-waard.png'},
|
|
50
|
+
{name: 'Hof van Twente', src: '/img/clients/hof-van-twente.svg'},
|
|
51
|
+
{name: 'Kansspelautoriteit', src: '/img/clients/ksa.svg'},
|
|
52
|
+
{name: 'Lansingerland', src: '/img/clients/lansingerland.svg'},
|
|
53
|
+
{name: 'Meppel', src: '/img/clients/meppel.svg'},
|
|
54
|
+
{name: 'Moerdijk', src: '/img/clients/moerdijk.svg'},
|
|
55
|
+
{name: 'Molenlanden', src: '/img/clients/molenlanden.png'},
|
|
56
|
+
{name: 'Noaberkracht', src: '/img/clients/noaberkracht.svg'},
|
|
57
|
+
{name: 'Noordwijk', src: '/img/clients/noordwijk.svg'},
|
|
58
|
+
{name: 'ODMH', src: '/img/clients/odmh.svg'},
|
|
59
|
+
{name: 'Oude IJsselstreek', src: '/img/clients/oude-ijsselstreek.png'},
|
|
60
|
+
{name: 'Overbetuwe', src: '/img/clients/over-betuwe.svg'},
|
|
61
|
+
{name: 'Provincie Zeeland', src: '/img/clients/provincie-zeeland.svg'},
|
|
62
|
+
{name: 'Ridderkerk', src: '/img/clients/ridderkerk.png'},
|
|
63
|
+
{name: 'Rijswijk', src: '/img/clients/rijswijk.png'},
|
|
64
|
+
{name: 'Roosendaal', src: '/img/clients/roosendaal.svg'},
|
|
65
|
+
{name: 'Rotterdam', src: '/img/clients/rotterdam.png'},
|
|
66
|
+
{name: 'Soest', src: '/img/clients/soest.svg'},
|
|
67
|
+
{name: 'Stichtse Vecht', src: '/img/clients/stichtse-vecht.svg'},
|
|
68
|
+
{name: 'SURF', src: '/img/clients/surf.svg'},
|
|
69
|
+
{name: 'Tilburg', src: '/img/clients/tilburg.png'},
|
|
70
|
+
{name: 'Tubbergen', src: '/img/clients/tubbergen.png'},
|
|
71
|
+
{name: 'VNG', src: '/img/clients/vng.png'},
|
|
72
|
+
{name: 'Zutphen', src: '/img/clients/zutphen.svg'},
|
|
73
|
+
{name: 'Zwolle', src: '/img/clients/zwolle.svg'},
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
export const DEFAULT_PARTNERS = [
|
|
77
|
+
{name: 'Nextcloud', src: '/img/clients/nextcloud.png'},
|
|
78
|
+
{name: 'iO', src: '/img/clients/io.webp'},
|
|
79
|
+
{name: 'Ritense', src: '/img/clients/ritense.png'},
|
|
80
|
+
{name: 'Shift2', src: '/img/clients/Shift2.png'},
|
|
81
|
+
{name: 'Open Webconcept', src: '/img/clients/open_webconcept.png'},
|
|
82
|
+
{name: 'SIDN Fonds', src: '/img/clients/SIDN.png'},
|
|
83
|
+
{name: 'BCT', src: '/img/clients/bct.png'},
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
function splitIntoRows(items, rowCount) {
|
|
87
|
+
const rows = Array.from({length: rowCount}, () => []);
|
|
88
|
+
items.forEach((item, i) => rows[i % rowCount].push(item));
|
|
89
|
+
/* Pad shorter rows with duplicates from their own front so every row
|
|
90
|
+
has the same length and the same track width. Without this, the
|
|
91
|
+
marquee's translateX(-50%) animation drifts the rows out of
|
|
92
|
+
honeycomb alignment because each track's 50% resolves to a
|
|
93
|
+
different pixel offset. */
|
|
94
|
+
const max = Math.max(...rows.map(r => r.length));
|
|
95
|
+
rows.forEach(r => {
|
|
96
|
+
let i = 0;
|
|
97
|
+
while (r.length < max) {
|
|
98
|
+
r.push(r[i % (r.length || 1)]);
|
|
99
|
+
i++;
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
return rows;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* ClientsMarquee
|
|
106
|
+
*
|
|
107
|
+
* Marquee variant body. The hex wall is now driven by the JS runtime
|
|
108
|
+
* lib/clients-flow.js: each row is a static relative band, the runtime
|
|
109
|
+
* spawns absolutely-positioned hex anchors on the right with random
|
|
110
|
+
* logos from the supplied pool, drifts them left, and culls them once
|
|
111
|
+
* they leave the viewport. We render only the empty row containers
|
|
112
|
+
* here and pass the client pool through as a data-clients JSON
|
|
113
|
+
* attribute. CSS Modules hashes the .hex / .hexLogo class names, so
|
|
114
|
+
* the runtime needs them via data-hex-class / data-hex-logo-class to
|
|
115
|
+
* style its spawned children consistently.
|
|
116
|
+
*
|
|
117
|
+
* Logo Memory's click trigger still works the same way — the runtime
|
|
118
|
+
* spawns real <a class={styles.hex}> elements, so e.target.closest('a')
|
|
119
|
+
* inside the click handler picks them up.
|
|
120
|
+
*/
|
|
121
|
+
function ClientsMarquee({head, clients, title, className}) {
|
|
122
|
+
useLazyScript(LOGO_MEMORY_BASE + '/clients-flow.js', 'clients-flow');
|
|
123
|
+
useLazyScript(LOGO_MEMORY_BASE + '/logo-memory.js', 'logo-memory');
|
|
124
|
+
React.useEffect(() => {
|
|
125
|
+
if (window.ConductionClientsFlow?.hydrate) window.ConductionClientsFlow.hydrate();
|
|
126
|
+
if (window.LogoMemory?.hydrate) window.LogoMemory.hydrate();
|
|
127
|
+
});
|
|
128
|
+
const clientsJson = React.useMemo(() => JSON.stringify(clients), [clients]);
|
|
129
|
+
return (
|
|
130
|
+
<>
|
|
131
|
+
<Head>
|
|
132
|
+
<link rel="stylesheet" href={LOGO_MEMORY_BASE + '/logo-memory.css'} />
|
|
133
|
+
</Head>
|
|
134
|
+
<section
|
|
135
|
+
className={[styles.section, className].filter(Boolean).join(' ')}
|
|
136
|
+
data-logo-memory
|
|
137
|
+
>
|
|
138
|
+
<div className={styles.inner}>{head}</div>
|
|
139
|
+
<div
|
|
140
|
+
className={styles.marquee}
|
|
141
|
+
data-memory-marquee
|
|
142
|
+
data-clients={clientsJson}
|
|
143
|
+
data-hex-class={styles.hex}
|
|
144
|
+
data-hex-logo-class={styles.hexLogo}
|
|
145
|
+
role="region"
|
|
146
|
+
aria-label={typeof title === 'string' ? title : 'Clients'}
|
|
147
|
+
>
|
|
148
|
+
<div data-flow-row className={[styles.row, styles.row1].join(' ')} />
|
|
149
|
+
<div data-flow-row className={[styles.row, styles.row2].join(' ')} />
|
|
150
|
+
<div data-flow-row className={[styles.row, styles.row3].join(' ')} />
|
|
151
|
+
</div>
|
|
152
|
+
</section>
|
|
153
|
+
</>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export default function Clients({
|
|
158
|
+
eyebrow = 'Who we work with',
|
|
159
|
+
title = 'Public sector, ecosystem, partners.',
|
|
160
|
+
lede,
|
|
161
|
+
clients = DEFAULT_CLIENTS,
|
|
162
|
+
variant = 'grid',
|
|
163
|
+
className,
|
|
164
|
+
}) {
|
|
165
|
+
const head = (
|
|
166
|
+
<SectionHead
|
|
167
|
+
eyebrow={eyebrow}
|
|
168
|
+
title={title}
|
|
169
|
+
align="stack"
|
|
170
|
+
lede={lede}
|
|
171
|
+
/>
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
if (variant === 'marquee') {
|
|
175
|
+
return (
|
|
176
|
+
<ClientsMarquee
|
|
177
|
+
head={head}
|
|
178
|
+
clients={clients}
|
|
179
|
+
title={title}
|
|
180
|
+
className={className}
|
|
181
|
+
/>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return (
|
|
186
|
+
<section className={[styles.section, className].filter(Boolean).join(' ')}>
|
|
187
|
+
<div className={styles.inner}>
|
|
188
|
+
{head}
|
|
189
|
+
<div className={styles.grid} role="list">
|
|
190
|
+
{clients.map((c, i) => (
|
|
191
|
+
<div key={i} className={styles.cell} role="listitem">
|
|
192
|
+
<img
|
|
193
|
+
src={c.src}
|
|
194
|
+
alt={c.name}
|
|
195
|
+
title={c.name}
|
|
196
|
+
loading="lazy"
|
|
197
|
+
className={styles.logo}
|
|
198
|
+
/>
|
|
199
|
+
</div>
|
|
200
|
+
))}
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
</section>
|
|
204
|
+
);
|
|
205
|
+
}
|