@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,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <ExternalAppShelf /> styles. Responsive card grid; each card has
|
|
3
|
+
* a brand-coloured icon tile on the left and a name + meta + desc
|
|
4
|
+
* on the right.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
.shelf {
|
|
8
|
+
display: grid;
|
|
9
|
+
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
|
10
|
+
gap: var(--space-4);
|
|
11
|
+
margin-top: var(--space-8);
|
|
12
|
+
font-family: var(--conduction-typography-font-family-body);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.card {
|
|
16
|
+
background: white;
|
|
17
|
+
border: 1px solid var(--c-cobalt-100);
|
|
18
|
+
border-radius: var(--radius-lg);
|
|
19
|
+
padding: var(--space-4) var(--space-5);
|
|
20
|
+
display: flex;
|
|
21
|
+
align-items: flex-start;
|
|
22
|
+
gap: var(--space-4);
|
|
23
|
+
text-decoration: none;
|
|
24
|
+
color: inherit;
|
|
25
|
+
position: relative;
|
|
26
|
+
transition: transform 160ms, box-shadow 160ms, border-color 160ms;
|
|
27
|
+
}
|
|
28
|
+
.card.linked { cursor: pointer; }
|
|
29
|
+
.card.linked:hover {
|
|
30
|
+
transform: translateY(-2px);
|
|
31
|
+
box-shadow: var(--shadow-2);
|
|
32
|
+
border-color: var(--c-cobalt-200);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* Icon tile — brand colour wash with the project's logo SVG centred.
|
|
36
|
+
White stroke / fill on the icon for contrast against the brand. */
|
|
37
|
+
.iconTile {
|
|
38
|
+
width: 48px;
|
|
39
|
+
height: 55px;
|
|
40
|
+
clip-path: var(--hex-pointy-top);
|
|
41
|
+
background: var(--c-cobalt-700);
|
|
42
|
+
flex-shrink: 0;
|
|
43
|
+
display: flex;
|
|
44
|
+
align-items: center;
|
|
45
|
+
justify-content: center;
|
|
46
|
+
color: white;
|
|
47
|
+
}
|
|
48
|
+
.icon { display: inline-flex; align-items: center; justify-content: center; }
|
|
49
|
+
.icon svg { width: 22px; height: 22px; fill: currentColor; stroke: currentColor; stroke-width: 0; }
|
|
50
|
+
|
|
51
|
+
/* Body */
|
|
52
|
+
.body {
|
|
53
|
+
flex: 1;
|
|
54
|
+
display: flex;
|
|
55
|
+
flex-direction: column;
|
|
56
|
+
gap: 4px;
|
|
57
|
+
min-width: 0;
|
|
58
|
+
}
|
|
59
|
+
.name {
|
|
60
|
+
font-size: 16px;
|
|
61
|
+
font-weight: 600;
|
|
62
|
+
letter-spacing: -0.005em;
|
|
63
|
+
color: var(--c-cobalt-900);
|
|
64
|
+
margin: 0;
|
|
65
|
+
}
|
|
66
|
+
.meta {
|
|
67
|
+
font-family: var(--conduction-typography-font-family-code);
|
|
68
|
+
font-size: 11px;
|
|
69
|
+
letter-spacing: 0.06em;
|
|
70
|
+
text-transform: uppercase;
|
|
71
|
+
color: var(--c-cobalt-400);
|
|
72
|
+
}
|
|
73
|
+
.desc {
|
|
74
|
+
font-size: 14px;
|
|
75
|
+
line-height: 1.5;
|
|
76
|
+
color: var(--c-cobalt-700);
|
|
77
|
+
margin: 4px 0 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.arrow {
|
|
81
|
+
color: var(--c-cobalt-400);
|
|
82
|
+
font-size: 14px;
|
|
83
|
+
margin-left: auto;
|
|
84
|
+
transition: color 160ms, transform 160ms;
|
|
85
|
+
flex-shrink: 0;
|
|
86
|
+
}
|
|
87
|
+
.card.linked:hover .arrow {
|
|
88
|
+
color: var(--c-orange-knvb);
|
|
89
|
+
transform: translate(2px, -2px);
|
|
90
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <FAQ /> + <FAQItem />
|
|
3
|
+
*
|
|
4
|
+
* FAQ accordion ported from preview/pages/support.html (.faq, .faq details).
|
|
5
|
+
* Native <details>/<summary> for keyboard, screen-reader, and no-JS support.
|
|
6
|
+
* One question expands at a time on click; users can have several open.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
*
|
|
10
|
+
* <FAQ title="FAQ.">
|
|
11
|
+
* <FAQItem question="If the apps are free, what does support cost?" defaultOpen>
|
|
12
|
+
* Whatever the partner you pick charges. Conduction sets no minimum…
|
|
13
|
+
* </FAQItem>
|
|
14
|
+
* <FAQItem question="Why doesn't Conduction sell support directly?">
|
|
15
|
+
* Two reasons. First…
|
|
16
|
+
* </FAQItem>
|
|
17
|
+
* </FAQ>
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import React from 'react';
|
|
21
|
+
import styles from './FAQ.module.css';
|
|
22
|
+
|
|
23
|
+
export function FAQItem({question, defaultOpen, children, className}) {
|
|
24
|
+
return (
|
|
25
|
+
<details className={[styles.item, className].filter(Boolean).join(' ')} open={defaultOpen || undefined}>
|
|
26
|
+
<summary className={styles.summary}>{question}</summary>
|
|
27
|
+
{/* div not p: MDX wraps inline children in its own <p>, which would
|
|
28
|
+
be invalid nested inside another <p>. */}
|
|
29
|
+
<div className={styles.body}>{children}</div>
|
|
30
|
+
</details>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default function FAQ({title = 'FAQ.', children, className}) {
|
|
35
|
+
const composed = [styles.faq, className].filter(Boolean).join(' ');
|
|
36
|
+
return (
|
|
37
|
+
<section className={composed}>
|
|
38
|
+
{title && <h2 className={styles.heading}>{title}</h2>}
|
|
39
|
+
{children}
|
|
40
|
+
</section>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
.faq {
|
|
2
|
+
max-width: 880px;
|
|
3
|
+
margin: 96px auto;
|
|
4
|
+
padding: 0 64px;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.heading {
|
|
8
|
+
font-size: 32px;
|
|
9
|
+
font-weight: 700;
|
|
10
|
+
letter-spacing: -0.02em;
|
|
11
|
+
margin: 0 0 32px;
|
|
12
|
+
color: var(--c-cobalt-900);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.item {
|
|
16
|
+
border-bottom: 1px solid var(--c-cobalt-100);
|
|
17
|
+
padding: 20px 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.summary {
|
|
21
|
+
font-size: 17px;
|
|
22
|
+
font-weight: 600;
|
|
23
|
+
cursor: pointer;
|
|
24
|
+
list-style: none;
|
|
25
|
+
display: flex;
|
|
26
|
+
justify-content: space-between;
|
|
27
|
+
align-items: center;
|
|
28
|
+
color: var(--c-cobalt-900);
|
|
29
|
+
gap: var(--space-4);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.summary::-webkit-details-marker {
|
|
33
|
+
display: none;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.summary::after {
|
|
37
|
+
content: "+";
|
|
38
|
+
font-size: 22px;
|
|
39
|
+
color: var(--c-cobalt-400);
|
|
40
|
+
font-weight: 400;
|
|
41
|
+
transition: transform 200ms ease;
|
|
42
|
+
flex-shrink: 0;
|
|
43
|
+
line-height: 1;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.item[open] .summary::after {
|
|
47
|
+
transform: rotate(45deg);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.body {
|
|
51
|
+
font-size: 15px;
|
|
52
|
+
color: var(--c-cobalt-700);
|
|
53
|
+
line-height: 1.6;
|
|
54
|
+
margin: 12px 0 0;
|
|
55
|
+
max-width: 64ch;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.body p {
|
|
59
|
+
margin: 0 0 var(--space-3);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.body p:last-child {
|
|
63
|
+
margin-bottom: 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@media (max-width: 768px) {
|
|
67
|
+
.faq {
|
|
68
|
+
padding: 0 var(--space-5);
|
|
69
|
+
margin: 64px auto;
|
|
70
|
+
}
|
|
71
|
+
.heading {
|
|
72
|
+
font-size: 26px;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <FacetedFilters /> + <FilterChip />
|
|
3
|
+
*
|
|
4
|
+
* Sidebar facet panel from preview/components/faceted-filters.html.
|
|
5
|
+
* Each facet is a labelled checkbox list with optional counts; clicking
|
|
6
|
+
* an item toggles its selection. The companion <FilterChip/> renders
|
|
7
|
+
* an active-chips strip above the results so users can pop selections
|
|
8
|
+
* one by one.
|
|
9
|
+
*
|
|
10
|
+
* Controlled component: pass `selected` (object keyed by facet) and
|
|
11
|
+
* `onChange` to manage state externally. For ad-hoc usage without a
|
|
12
|
+
* parent state, the component falls back to internal state with the
|
|
13
|
+
* same shape.
|
|
14
|
+
*
|
|
15
|
+
* Usage in MDX:
|
|
16
|
+
*
|
|
17
|
+
* const [selected, setSelected] = useState({});
|
|
18
|
+
* const filtered = useMemo(() => filterSolutions(solutions, selected), [selected]);
|
|
19
|
+
*
|
|
20
|
+
* <Section background="default">
|
|
21
|
+
* <div style={{display: 'grid', gridTemplateColumns: '240px 1fr', gap: 56}}>
|
|
22
|
+
* <FacetedFilters
|
|
23
|
+
* facets={[
|
|
24
|
+
* {key: 'sector', label: 'Sector', items: [
|
|
25
|
+
* {value: 'public', label: 'Publieke sector', count: 5},
|
|
26
|
+
* {value: 'mkb', label: 'MKB', count: 3},
|
|
27
|
+
* {value: 'health', label: 'Zorg', count: 2},
|
|
28
|
+
* ]},
|
|
29
|
+
* {key: 'goal', label: 'Doel', items: [...]},
|
|
30
|
+
* ]}
|
|
31
|
+
* selected={selected}
|
|
32
|
+
* onChange={setSelected}
|
|
33
|
+
* />
|
|
34
|
+
* <SolutionGrid>{filtered.map(s => <SolutionCard {...s}/>)}</SolutionGrid>
|
|
35
|
+
* </div>
|
|
36
|
+
* </Section>
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
import React, {useState, useCallback} from 'react';
|
|
40
|
+
import styles from './FacetedFilters.module.css';
|
|
41
|
+
|
|
42
|
+
export function FilterChip({label, onRemove, className}) {
|
|
43
|
+
return (
|
|
44
|
+
<span className={[styles.chip, className].filter(Boolean).join(' ')}>
|
|
45
|
+
{label}
|
|
46
|
+
{onRemove && (
|
|
47
|
+
<button
|
|
48
|
+
type="button"
|
|
49
|
+
aria-label={`Remove ${label}`}
|
|
50
|
+
className={styles.chipRemove}
|
|
51
|
+
onClick={onRemove}
|
|
52
|
+
>
|
|
53
|
+
×
|
|
54
|
+
</button>
|
|
55
|
+
)}
|
|
56
|
+
</span>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export default function FacetedFilters({
|
|
61
|
+
facets = [],
|
|
62
|
+
selected,
|
|
63
|
+
onChange,
|
|
64
|
+
clearLabel = 'Clear all filters',
|
|
65
|
+
className,
|
|
66
|
+
}) {
|
|
67
|
+
const isControlled = selected !== undefined && onChange !== undefined;
|
|
68
|
+
const [internal, setInternal] = useState({});
|
|
69
|
+
const value = isControlled ? selected : internal;
|
|
70
|
+
const set = isControlled ? onChange : setInternal;
|
|
71
|
+
|
|
72
|
+
const toggle = useCallback((facetKey, itemValue) => {
|
|
73
|
+
set((prev) => {
|
|
74
|
+
const current = prev[facetKey] || [];
|
|
75
|
+
const next = current.includes(itemValue)
|
|
76
|
+
? current.filter(v => v !== itemValue)
|
|
77
|
+
: [...current, itemValue];
|
|
78
|
+
return {...prev, [facetKey]: next};
|
|
79
|
+
});
|
|
80
|
+
}, [set]);
|
|
81
|
+
|
|
82
|
+
const clearAll = useCallback(() => set({}), [set]);
|
|
83
|
+
|
|
84
|
+
const hasAny = Object.values(value).some(v => Array.isArray(v) && v.length > 0);
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<aside className={[styles.filters, className].filter(Boolean).join(' ')}>
|
|
88
|
+
{facets.map((facet) => {
|
|
89
|
+
const facetKey = facet.key || facet.label;
|
|
90
|
+
const sel = value[facetKey] || [];
|
|
91
|
+
return (
|
|
92
|
+
<fieldset key={facetKey} className={styles.facet}>
|
|
93
|
+
<h3 className={styles.facetTitle}>{facet.label}</h3>
|
|
94
|
+
<ul className={styles.facetList}>
|
|
95
|
+
{facet.items.map((item) => {
|
|
96
|
+
const checked = sel.includes(item.value);
|
|
97
|
+
const id = `f-${facetKey}-${item.value}`;
|
|
98
|
+
return (
|
|
99
|
+
<li key={item.value}>
|
|
100
|
+
<input
|
|
101
|
+
type="checkbox"
|
|
102
|
+
id={id}
|
|
103
|
+
checked={checked}
|
|
104
|
+
onChange={() => toggle(facetKey, item.value)}
|
|
105
|
+
/>
|
|
106
|
+
<label htmlFor={id}>{item.label}</label>
|
|
107
|
+
{typeof item.count === 'number' && (
|
|
108
|
+
<span className={styles.count}>{item.count}</span>
|
|
109
|
+
)}
|
|
110
|
+
</li>
|
|
111
|
+
);
|
|
112
|
+
})}
|
|
113
|
+
</ul>
|
|
114
|
+
</fieldset>
|
|
115
|
+
);
|
|
116
|
+
})}
|
|
117
|
+
|
|
118
|
+
{hasAny && (
|
|
119
|
+
<button type="button" className={styles.clearAll} onClick={clearAll}>
|
|
120
|
+
{clearLabel}
|
|
121
|
+
</button>
|
|
122
|
+
)}
|
|
123
|
+
</aside>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <FacetedFilters /> styles. Mirrors the .filters / .facet rules in
|
|
3
|
+
* preview/components/faceted-filters.html.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
.filters {
|
|
7
|
+
width: 240px;
|
|
8
|
+
font-family: var(--conduction-typography-font-family-body);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.facet {
|
|
12
|
+
margin-bottom: 28px;
|
|
13
|
+
border: none;
|
|
14
|
+
padding: 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.facetTitle {
|
|
18
|
+
font-family: var(--conduction-typography-font-family-code);
|
|
19
|
+
font-size: 11px;
|
|
20
|
+
letter-spacing: 0.14em;
|
|
21
|
+
text-transform: uppercase;
|
|
22
|
+
color: var(--c-cobalt-400);
|
|
23
|
+
font-weight: 500;
|
|
24
|
+
margin: 0 0 10px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.facetList {
|
|
28
|
+
list-style: none;
|
|
29
|
+
padding: 0;
|
|
30
|
+
margin: 0;
|
|
31
|
+
display: flex;
|
|
32
|
+
flex-direction: column;
|
|
33
|
+
gap: 8px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.facetList li {
|
|
37
|
+
display: grid;
|
|
38
|
+
grid-template-columns: 18px 1fr auto;
|
|
39
|
+
gap: 10px;
|
|
40
|
+
align-items: center;
|
|
41
|
+
font-size: 14px;
|
|
42
|
+
color: var(--c-cobalt-700);
|
|
43
|
+
padding: 4px 0;
|
|
44
|
+
}
|
|
45
|
+
.facetList li:hover { color: var(--c-blue-cobalt); }
|
|
46
|
+
|
|
47
|
+
.facetList input[type="checkbox"] {
|
|
48
|
+
appearance: none;
|
|
49
|
+
width: 16px;
|
|
50
|
+
height: 16px;
|
|
51
|
+
border: 1.5px solid var(--c-cobalt-300);
|
|
52
|
+
border-radius: 3px;
|
|
53
|
+
cursor: pointer;
|
|
54
|
+
position: relative;
|
|
55
|
+
background: white;
|
|
56
|
+
transition: all 120ms;
|
|
57
|
+
}
|
|
58
|
+
.facetList input[type="checkbox"]:hover { border-color: var(--c-blue-cobalt); }
|
|
59
|
+
.facetList input[type="checkbox"]:checked {
|
|
60
|
+
background: var(--c-blue-cobalt);
|
|
61
|
+
border-color: var(--c-blue-cobalt);
|
|
62
|
+
}
|
|
63
|
+
.facetList input[type="checkbox"]:checked::after {
|
|
64
|
+
content: "";
|
|
65
|
+
position: absolute;
|
|
66
|
+
left: 4px;
|
|
67
|
+
top: 1px;
|
|
68
|
+
width: 5px;
|
|
69
|
+
height: 9px;
|
|
70
|
+
border: solid white;
|
|
71
|
+
border-width: 0 1.6px 1.6px 0;
|
|
72
|
+
transform: rotate(45deg);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.facetList label {
|
|
76
|
+
cursor: pointer;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.count {
|
|
80
|
+
font-family: var(--conduction-typography-font-family-code);
|
|
81
|
+
font-size: 11px;
|
|
82
|
+
color: var(--c-cobalt-400);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.clearAll {
|
|
86
|
+
display: inline-flex;
|
|
87
|
+
align-items: center;
|
|
88
|
+
gap: 6px;
|
|
89
|
+
font-size: 13px;
|
|
90
|
+
color: var(--c-cobalt-400);
|
|
91
|
+
text-decoration: none;
|
|
92
|
+
margin-top: 8px;
|
|
93
|
+
border: none;
|
|
94
|
+
background: none;
|
|
95
|
+
padding: 0;
|
|
96
|
+
cursor: pointer;
|
|
97
|
+
font-family: inherit;
|
|
98
|
+
}
|
|
99
|
+
.clearAll:hover { color: var(--c-orange-knvb); }
|
|
100
|
+
|
|
101
|
+
/* Active filter chip (companion strip) */
|
|
102
|
+
.chip {
|
|
103
|
+
display: inline-flex;
|
|
104
|
+
align-items: center;
|
|
105
|
+
gap: 6px;
|
|
106
|
+
padding: 5px 8px 5px 12px;
|
|
107
|
+
background: var(--c-cobalt-50);
|
|
108
|
+
color: var(--c-cobalt-900);
|
|
109
|
+
font-size: 13px;
|
|
110
|
+
font-family: var(--conduction-typography-font-family-code);
|
|
111
|
+
border-radius: var(--radius-pill);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.chipRemove {
|
|
115
|
+
display: inline-flex;
|
|
116
|
+
align-items: center;
|
|
117
|
+
justify-content: center;
|
|
118
|
+
width: 18px;
|
|
119
|
+
height: 18px;
|
|
120
|
+
border: none;
|
|
121
|
+
background: var(--c-cobalt-100);
|
|
122
|
+
color: var(--c-cobalt-700);
|
|
123
|
+
border-radius: 50%;
|
|
124
|
+
cursor: pointer;
|
|
125
|
+
font-size: 14px;
|
|
126
|
+
line-height: 1;
|
|
127
|
+
font-family: inherit;
|
|
128
|
+
transition: all 120ms ease;
|
|
129
|
+
}
|
|
130
|
+
.chipRemove:hover {
|
|
131
|
+
background: var(--c-orange-knvb);
|
|
132
|
+
color: white;
|
|
133
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <FeatureGrid /> + <FeatureGridGroup /> + <FeatureItem />
|
|
3
|
+
*
|
|
4
|
+
* Compact multi-column list for spec-grade features. Use when an app
|
|
5
|
+
* has 10 to 60+ features that don't all warrant a full USP block.
|
|
6
|
+
* Each item shows a status hex + a short label; the full description
|
|
7
|
+
* reveals on hover and on keyboard focus.
|
|
8
|
+
*
|
|
9
|
+
* Mirrors preview/components/feature-grid.html exactly (auto-fill grid
|
|
10
|
+
* with min 240px columns, bottom-up tooltip with 280px max-width and
|
|
11
|
+
* a tail).
|
|
12
|
+
*
|
|
13
|
+
* Status modifiers on the hex bullet:
|
|
14
|
+
* - 'stable' (default mint)
|
|
15
|
+
* - 'beta' (orange)
|
|
16
|
+
* - 'soon' (cobalt-300, "coming soon")
|
|
17
|
+
*
|
|
18
|
+
* Optional <FeatureGridGroup label="..."> headers split the grid into
|
|
19
|
+
* named sections; sites can also pass an items array with `group: 'X'`
|
|
20
|
+
* entries that the component will section automatically.
|
|
21
|
+
*
|
|
22
|
+
* Usage in MDX:
|
|
23
|
+
*
|
|
24
|
+
* <FeatureGrid
|
|
25
|
+
* legend
|
|
26
|
+
* items={[
|
|
27
|
+
* {group: 'Core capabilities'},
|
|
28
|
+
* {label: 'JSON Schema validation', tip: 'Define a register shape...', status: 'stable'},
|
|
29
|
+
* {label: 'GraphQL endpoint', tip: 'Auto-generated...', status: 'stable'},
|
|
30
|
+
* {label: 'Schema versioning', tip: 'Migrations are version-stable.', status: 'beta'},
|
|
31
|
+
* {group: 'Integrations'},
|
|
32
|
+
* {label: 'OpenCatalogi indexing', tip: '...', status: 'stable'},
|
|
33
|
+
* ]}
|
|
34
|
+
* />
|
|
35
|
+
*
|
|
36
|
+
* Or compose with children (full MDX flexibility):
|
|
37
|
+
*
|
|
38
|
+
* <FeatureGrid>
|
|
39
|
+
* <FeatureGridGroup label="Core capabilities" />
|
|
40
|
+
* <FeatureItem label="JSON Schema validation" tip="..." status="stable" />
|
|
41
|
+
* ...
|
|
42
|
+
* </FeatureGrid>
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
import React from 'react';
|
|
46
|
+
import styles from './FeatureGrid.module.css';
|
|
47
|
+
|
|
48
|
+
const STATUS_CLASSES = {stable: '', beta: styles.beta, soon: styles.soon};
|
|
49
|
+
|
|
50
|
+
export function FeatureItem({label, tip, status = 'stable', className}) {
|
|
51
|
+
const hexClass = [styles.h, STATUS_CLASSES[status]].filter(Boolean).join(' ');
|
|
52
|
+
return (
|
|
53
|
+
<div
|
|
54
|
+
className={[styles.item, className].filter(Boolean).join(' ')}
|
|
55
|
+
tabIndex={0}
|
|
56
|
+
title={tip || label}
|
|
57
|
+
>
|
|
58
|
+
<span className={hexClass} aria-hidden="true" />
|
|
59
|
+
<span className={styles.label}>{label}</span>
|
|
60
|
+
{tip && <span className={styles.tip}>{tip}</span>}
|
|
61
|
+
</div>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function FeatureGridGroup({label, className}) {
|
|
66
|
+
return <h4 className={[styles.group, className].filter(Boolean).join(' ')}>{label}</h4>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default function FeatureGrid({items, legend = false, children, className}) {
|
|
70
|
+
return (
|
|
71
|
+
<div className={className}>
|
|
72
|
+
{legend && <Legend />}
|
|
73
|
+
<div className={styles.grid}>
|
|
74
|
+
{items
|
|
75
|
+
? items.map((it, i) => (
|
|
76
|
+
it.group
|
|
77
|
+
? <FeatureGridGroup key={i} label={it.group} />
|
|
78
|
+
: <FeatureItem key={i} {...it} />
|
|
79
|
+
))
|
|
80
|
+
: children}
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function Legend() {
|
|
87
|
+
return (
|
|
88
|
+
<div className={styles.legend}>
|
|
89
|
+
<span className={styles.legendItem}><span className={styles.h} aria-hidden="true" />Stable</span>
|
|
90
|
+
<span className={styles.legendItem}><span className={[styles.h, styles.beta].join(' ')} aria-hidden="true" />Beta</span>
|
|
91
|
+
<span className={styles.legendItem}><span className={[styles.h, styles.soon].join(' ')} aria-hidden="true" />Coming soon</span>
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <FeatureGrid /> styles. Mirrors feature-grid.css from the kit.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
.grid {
|
|
6
|
+
display: grid;
|
|
7
|
+
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
|
8
|
+
gap: 4px 32px;
|
|
9
|
+
align-items: start;
|
|
10
|
+
font-family: var(--conduction-typography-font-family-body);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.group {
|
|
14
|
+
grid-column: 1 / -1;
|
|
15
|
+
font-family: var(--conduction-typography-font-family-code);
|
|
16
|
+
font-size: 11px;
|
|
17
|
+
letter-spacing: 0.1em;
|
|
18
|
+
text-transform: uppercase;
|
|
19
|
+
color: var(--c-cobalt-400);
|
|
20
|
+
margin: 24px 0 8px;
|
|
21
|
+
padding-bottom: 8px;
|
|
22
|
+
border-bottom: 1px solid var(--c-cobalt-100);
|
|
23
|
+
font-weight: 500;
|
|
24
|
+
}
|
|
25
|
+
.group:first-child { margin-top: 0; }
|
|
26
|
+
|
|
27
|
+
.item {
|
|
28
|
+
position: relative;
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
gap: 12px;
|
|
32
|
+
padding: 8px 0;
|
|
33
|
+
font-size: 14px;
|
|
34
|
+
color: var(--c-cobalt-800);
|
|
35
|
+
cursor: help;
|
|
36
|
+
outline: none;
|
|
37
|
+
border-radius: var(--radius-sm);
|
|
38
|
+
transition: background 140ms ease, padding 140ms ease, margin 140ms ease;
|
|
39
|
+
}
|
|
40
|
+
.item:hover,
|
|
41
|
+
.item:focus-visible {
|
|
42
|
+
background: var(--c-cobalt-50);
|
|
43
|
+
padding-left: 8px;
|
|
44
|
+
padding-right: 8px;
|
|
45
|
+
margin-left: -8px;
|
|
46
|
+
margin-right: -8px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.h {
|
|
50
|
+
width: 10px;
|
|
51
|
+
height: 12px;
|
|
52
|
+
clip-path: var(--hex-pointy-top);
|
|
53
|
+
background: var(--c-mint-500);
|
|
54
|
+
flex-shrink: 0;
|
|
55
|
+
}
|
|
56
|
+
.beta { background: var(--c-orange-knvb); }
|
|
57
|
+
.soon { background: var(--c-cobalt-300); }
|
|
58
|
+
|
|
59
|
+
.label {
|
|
60
|
+
flex: 1;
|
|
61
|
+
line-height: 1.3;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* Tooltip: appears above the item on hover or keyboard focus */
|
|
65
|
+
.tip {
|
|
66
|
+
position: absolute;
|
|
67
|
+
bottom: calc(100% + 8px);
|
|
68
|
+
left: 0;
|
|
69
|
+
background: var(--c-cobalt-900);
|
|
70
|
+
color: white;
|
|
71
|
+
padding: 10px 14px;
|
|
72
|
+
border-radius: var(--radius-md);
|
|
73
|
+
font-size: 13px;
|
|
74
|
+
line-height: 1.5;
|
|
75
|
+
width: max-content;
|
|
76
|
+
max-width: 280px;
|
|
77
|
+
opacity: 0;
|
|
78
|
+
pointer-events: none;
|
|
79
|
+
transition: opacity 120ms ease, transform 120ms ease;
|
|
80
|
+
transform: translateY(4px);
|
|
81
|
+
z-index: 20;
|
|
82
|
+
box-shadow: var(--shadow-2);
|
|
83
|
+
font-weight: 400;
|
|
84
|
+
}
|
|
85
|
+
.tip::after {
|
|
86
|
+
content: "";
|
|
87
|
+
position: absolute;
|
|
88
|
+
top: 100%;
|
|
89
|
+
left: 16px;
|
|
90
|
+
border: 6px solid transparent;
|
|
91
|
+
border-top-color: var(--c-cobalt-900);
|
|
92
|
+
}
|
|
93
|
+
.item:hover .tip,
|
|
94
|
+
.item:focus-visible .tip {
|
|
95
|
+
opacity: 1;
|
|
96
|
+
transform: translateY(0);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* Legend row at the top of a FeatureGrid */
|
|
100
|
+
.legend {
|
|
101
|
+
display: flex;
|
|
102
|
+
gap: 24px;
|
|
103
|
+
margin-bottom: 24px;
|
|
104
|
+
font-family: var(--conduction-typography-font-family-code);
|
|
105
|
+
font-size: 11px;
|
|
106
|
+
letter-spacing: 0.06em;
|
|
107
|
+
text-transform: uppercase;
|
|
108
|
+
color: var(--c-cobalt-400);
|
|
109
|
+
}
|
|
110
|
+
.legendItem {
|
|
111
|
+
display: inline-flex;
|
|
112
|
+
align-items: center;
|
|
113
|
+
gap: 8px;
|
|
114
|
+
}
|