@conduction/docusaurus-preset 2.9.0 → 2.10.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conduction/docusaurus-preset",
3
- "version": "2.9.0",
3
+ "version": "2.10.1",
4
4
  "scripts": {
5
5
  "prepack": "node scripts/prepack-bundle-css.js"
6
6
  },
@@ -45,11 +45,25 @@
45
45
  */
46
46
 
47
47
  import React from 'react';
48
+ import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
48
49
  import HexBullet from '../primitives/HexBullet';
49
50
  import Button from '../primitives/Button';
51
+ import {deriveStability} from '../../theme/brand.jsx';
50
52
  import {downloadsForApp, formatDownloads} from '../../data/app-downloads';
51
53
  import styles from './DetailHero.module.css';
52
54
 
55
+ /**
56
+ * Per-stability hex-bullet colour. Keeps the hero badge and the
57
+ * navbar pill on the same maturity story without each site having
58
+ * to pass a colour explicitly.
59
+ */
60
+ const STABILITY_COLORS = {
61
+ Stable: 'var(--c-mint-500)',
62
+ Beta: 'var(--c-orange-knvb)',
63
+ RC: 'var(--c-blue-cobalt)',
64
+ Alpha: 'var(--c-red-vermillion)',
65
+ };
66
+
53
67
  export default function DetailHero({
54
68
  crumb,
55
69
  status,
@@ -76,6 +90,23 @@ export default function DetailHero({
76
90
  existing on-cream rendering used by connext apps detail pages. */
77
91
  const bgClass = background === 'cobalt' ? styles.bgCobalt : null;
78
92
 
93
+ /* Reconcile the hero's badge row with the navbar version pill so
94
+ they can't drift apart. When the caller doesn't pass `version`
95
+ and/or `status` props, fall back to the same customFields.appVersion
96
+ the navbar reads, and auto-derive Stable/Beta/RC/Alpha from the
97
+ SemVer string via deriveStability(). Sites can still pass explicit
98
+ props to override (e.g. a static-site demo that wants to show
99
+ "Preview" instead of the auto-derived label). */
100
+ const {siteConfig} = useDocusaurusContext();
101
+ const appVersion = siteConfig?.customFields?.appVersion;
102
+ const resolvedVersion = version || (appVersion ? `v${appVersion}` : undefined);
103
+ const resolvedStatus = status || (appVersion
104
+ ? {
105
+ label: deriveStability(appVersion),
106
+ color: STABILITY_COLORS[deriveStability(appVersion)],
107
+ }
108
+ : undefined);
109
+
79
110
  return (
80
111
  <section className={[styles.head, hasIllustration && styles.withIllustration, bgClass, className].filter(Boolean).join(' ')}>
81
112
  {crumb && Array.isArray(crumb) && (
@@ -99,15 +130,15 @@ export default function DetailHero({
99
130
 
100
131
  <div className={styles.headInner}>
101
132
  <div className={styles.copy}>
102
- {(status || version || locales || dlCount > 0) && (
133
+ {(resolvedStatus || resolvedVersion || locales || dlCount > 0) && (
103
134
  <div className={styles.badgeRow}>
104
- {status && (
135
+ {resolvedStatus && (
105
136
  <span className={styles.badge}>
106
- <HexBullet size="md" color={status.color || 'var(--c-mint-500)'} />
107
- {status.label}
137
+ <HexBullet size="md" color={resolvedStatus.color || STABILITY_COLORS[resolvedStatus.label] || 'var(--c-mint-500)'} />
138
+ {resolvedStatus.label}
108
139
  </span>
109
140
  )}
110
- {version && <span className={[styles.badge, styles.versionBadge].join(' ')}>{version}</span>}
141
+ {resolvedVersion && <span className={[styles.badge, styles.versionBadge].join(' ')}>{resolvedVersion}</span>}
111
142
  {locales && <span className={[styles.badge, styles.versionBadge].join(' ')}>{locales}</span>}
112
143
  {dlCount > 0 && (
113
144
  <span
@@ -87,6 +87,7 @@ export default function PlatformDiagram({workspace, lists = [], flows = []}) {
87
87
  name={item.name}
88
88
  meta={item.meta}
89
89
  desc={item.desc}
90
+ href={item.href}
90
91
  brand-color={item.brandColor}
91
92
  >
92
93
  {item.icon}
@@ -32,7 +32,6 @@ export const APPS_REGISTRY = {
32
32
  openconnector: {slug: 'openconnector', name: 'OpenConnector', productHref: '/apps/openconnector', docsHref: 'https://docs.conduction.nl/openconnector', academyHref: '/academy?app=openconnector'},
33
33
  docudesk: {slug: 'docudesk', name: 'DocuDesk', productHref: '/apps/docudesk', docsHref: 'https://docs.conduction.nl/docudesk', academyHref: '/academy?app=docudesk'},
34
34
  mydash: {slug: 'mydash', name: 'MyDash', productHref: '/apps/mydash', docsHref: 'https://docs.conduction.nl/mydash', academyHref: '/academy?app=mydash'},
35
- openwoo: {slug: 'openwoo', name: 'OpenWoo', productHref: '/apps/openwoo', docsHref: 'https://docs.conduction.nl/openwoo', academyHref: '/academy?app=openwoo'},
36
35
  zaakafhandelapp: {slug: 'zaakafhandelapp', name: 'ZaakAfhandelApp', productHref: '/apps/zaakafhandelapp', docsHref: 'https://docs.conduction.nl/zaakafhandelapp', academyHref: '/academy?app=zaakafhandelapp'},
37
36
  pipelinq: {slug: 'pipelinq', name: 'PipelinQ', productHref: '/apps/pipelinq', docsHref: 'https://docs.conduction.nl/pipelinq', academyHref: '/academy?app=pipelinq'},
38
37
  procest: {slug: 'procest', name: 'Procest', productHref: '/apps/procest', docsHref: 'https://docs.conduction.nl/procest', academyHref: '/academy?app=procest'},
@@ -40,6 +39,10 @@ export const APPS_REGISTRY = {
40
39
  softwarecatalog: {slug: 'softwarecatalog', name: 'SoftwareCatalog', productHref: '/apps/softwarecatalog', docsHref: 'https://docs.conduction.nl/softwarecatalog', academyHref: '/academy?app=softwarecatalog'},
41
40
  larpingapp: {slug: 'larpingapp', name: 'LarpingApp', productHref: '/apps/larpingapp', docsHref: 'https://docs.conduction.nl/larpingapp', academyHref: '/academy?app=larpingapp'},
42
41
  nldesign: {slug: 'nldesign', name: 'NLDesign', productHref: '/apps/nldesign', docsHref: 'https://docs.conduction.nl/nldesign', academyHref: '/academy?app=nldesign'},
42
+ shillinq: {slug: 'shillinq', name: 'Shillinq', productHref: '/apps/shillinq', docsHref: 'https://docs.conduction.nl/shillinq', academyHref: '/academy?app=shillinq'},
43
+ openbuilt: {slug: 'openbuilt', name: 'OpenBuilt', productHref: '/apps/openbuilt', docsHref: 'https://docs.conduction.nl/openbuilt', academyHref: '/academy?app=openbuilt'},
44
+ doriath: {slug: 'doriath', name: 'Doriath', productHref: '/apps/doriath', docsHref: 'https://docs.conduction.nl/doriath', academyHref: '/academy?app=doriath'},
45
+ 'app-versions': {slug: 'app-versions', name: 'App Versions', productHref: '/apps/app-versions', docsHref: 'https://docs.conduction.nl/app-versions', academyHref: '/academy?app=app-versions'},
43
46
  };
44
47
 
45
48
  export const APP_SLUGS = Object.keys(APPS_REGISTRY);
@@ -49,7 +49,7 @@ import useBaseUrl from '@docusaurus/useBaseUrl';
49
49
  import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
50
50
  import {useThemeConfig} from '@docusaurus/theme-common';
51
51
  import LocaleDropdownNavbarItem from '@theme/NavbarItem/LocaleDropdownNavbarItem';
52
- import {brandFor, productWordmark} from '../brand.jsx';
52
+ import {brandFor, productWordmark, deriveStability} from '../brand.jsx';
53
53
  import {ICONS} from '../../components/primitives/icons';
54
54
  import styles from './styles.module.css';
55
55
 
@@ -126,14 +126,18 @@ function NavItem({item, location, appVersion}) {
126
126
  return <Link to={to} className={className}>{content}</Link>;
127
127
  }
128
128
 
129
- /* Version pill: code-typeface "Stable · v{version}" chip. Source is
130
- customFields.appVersion (set by createConfig() from appinfo/info.xml
131
- or package.json). Hidden when no version is available so sites
132
- without an app version (Hydra, design-system itself) get a clean
133
- navbar instead of an empty pill. */
129
+ /* Version pill: code-typeface "{Stability} · v{version}" chip.
130
+ Source is customFields.appVersion (set by createConfig() from
131
+ appinfo/info.xml or package.json). The maturity prefix
132
+ (Stable/Beta/RC/Alpha) is auto-derived from the SemVer string
133
+ `0.1.0` Beta, `1.0.0-rc.2` RC, `1.2.3` → Stable. Sites can
134
+ still pass an explicit `prefix` to override. Hidden when no
135
+ version is available so sites without an app version (Hydra,
136
+ design-system itself) get a clean navbar instead of an empty
137
+ pill. */
134
138
  if (typeIs(item, 'versionPill')) {
135
139
  if (!appVersion) return null;
136
- const prefix = item.prefix || 'Stable';
140
+ const prefix = item.prefix || deriveStability(appVersion);
137
141
  return (
138
142
  <span className={styles.versionPill} title={`${prefix} · v${appVersion}`}>
139
143
  {prefix} · v{appVersion}
@@ -108,3 +108,36 @@ export function productWordmark(title, brandPrefix) {
108
108
  }
109
109
  return null;
110
110
  }
111
+
112
+ /**
113
+ * Derive the maturity label that prefixes the version pill and the
114
+ * DetailHero status badge. Driven by SemVer:
115
+ *
116
+ * 1.0.0+ → "Stable"
117
+ * 0.x.y → "Beta" (every 0.x is pre-1.0 by SemVer rule)
118
+ * *-alpha.N → "Alpha"
119
+ * *-beta.N → "Beta"
120
+ * *-rc.N → "RC"
121
+ * anything else → "Stable" (safe default for non-SemVer strings)
122
+ *
123
+ * Both the navbar pill (theme/Navbar) and DetailHero (components/
124
+ * DetailHero) call this so the chrome and the landing-page status
125
+ * badge can't drift apart. Sites can still pass an explicit string
126
+ * to override (`versionPill.prefix = 'Preview'` etc.).
127
+ */
128
+ export function deriveStability(version) {
129
+ if (!version || typeof version !== 'string') return 'Stable';
130
+ /* Pre-release tags win first — '1.0.0-beta.2' is Beta, not Stable.
131
+ Match case-insensitively because info.xml authors aren't
132
+ consistent (`-Beta`, `-BETA`, …). */
133
+ const pre = version.match(/-(alpha|beta|rc)(?:\.|$|-)/i);
134
+ if (pre) {
135
+ const tag = pre[1].toLowerCase();
136
+ if (tag === 'rc') return 'RC';
137
+ return tag === 'alpha' ? 'Alpha' : 'Beta';
138
+ }
139
+ /* No pre-release tag — 0.x is by SemVer convention not yet stable. */
140
+ const major = version.match(/^(\d+)\./);
141
+ if (major && parseInt(major[1], 10) === 0) return 'Beta';
142
+ return 'Stable';
143
+ }
@@ -96,8 +96,12 @@ platform-diagram .workspace:not(.box-wrap):not(.workspace-corner-hex) .role {
96
96
  margin-top: 14px;
97
97
  }
98
98
 
99
- /* ---- Box-wrap (positioning shell for each list) ---- */
100
- platform-diagram .box-wrap { position: relative; }
99
+ /* ---- Box-wrap (positioning shell for each list) ----
100
+ Hover raises the wrap's z-index so its absolute-positioned .desc
101
+ tooltip stacks above neighbouring lists (without this the tooltip
102
+ slides under the next .box-wrap and is unreadable). */
103
+ platform-diagram .box-wrap { position: relative; z-index: 1; }
104
+ platform-diagram .box-wrap:hover { z-index: 20; }
101
105
 
102
106
  /* ---- List-box ---- */
103
107
  platform-diagram .box {
@@ -138,6 +142,14 @@ platform-diagram .row .name {
138
142
  min-width: 0;
139
143
  }
140
144
  platform-diagram .row + .row { border-top: 1px solid var(--c-cobalt-50); }
145
+ platform-diagram a.row { text-decoration: none; color: inherit; cursor: pointer; }
146
+ platform-diagram a.row:hover { text-decoration: none; }
147
+ /* Hovered row escapes its own overflow:hidden clip and creates a
148
+ stacking context so the .desc tooltip can extend below the row AND
149
+ paint above later-DOM-order siblings. Without overflow:visible the
150
+ absolute-positioned .desc gets cropped to the row's bounding box;
151
+ without z-index the .desc has no stacking context to escape. */
152
+ platform-diagram .row:hover { overflow: visible; z-index: 10; }
141
153
 
142
154
  /* Multi-column box: rows are wrapped in <.col> so the .row+.row border only
143
155
  draws within a single column. The columns sit side by side with a thin
@@ -248,9 +260,12 @@ platform-diagram .workspace-corner-hex.fam-solutions { background: #E34234; }
248
260
  platform-diagram .fam-integrate .glyph,
249
261
  platform-diagram .workspace-corner-hex.fam-integrate { background: var(--c-cobalt-400); }
250
262
  platform-diagram .fam-builder .glyph,
251
- platform-diagram .workspace-corner-hex.fam-builder { background: var(--c-cobalt-300); }
252
- /* App Builder hex: KNVB orange (override fam-builder) */
253
- platform-diagram .workspace-corner-hex.app-builder { background: var(--c-orange-knvb); }
263
+ platform-diagram .workspace-corner-hex.fam-builder { background: var(--c-blue-cobalt); }
264
+ /* App Builder hex: KNVB orange placeholder treatment, only while the
265
+ cluster carries a `has-badge` class (COMING SOON state). Without
266
+ the badge the hex reverts to the shared workspace cobalt and looks
267
+ like any other Conduction surface. */
268
+ platform-diagram .workspace-corner-hex.app-builder.has-badge { background: var(--c-orange-knvb); }
254
269
  platform-diagram .fam-nextcloud .glyph,
255
270
  platform-diagram .workspace-corner-hex.fam-nextcloud { background: var(--c-nextcloud-blue); }
256
271
 
@@ -396,15 +411,17 @@ platform-diagram .box-wrap.app-builder {
396
411
  opacity: var(--pd-list-progress);
397
412
  }
398
413
 
399
- /* App Builder list: dashed coming-soon styling */
400
- platform-diagram .box-wrap.app-builder .box {
414
+ /* App Builder list: when a COMING SOON-style badge is attached we treat the
415
+ cluster as a placeholder (dashed border, faded text). When no badge is
416
+ set the cluster styles like every other list — OpenBuilt is a real app. */
417
+ platform-diagram .box-wrap.app-builder.has-badge .box {
401
418
  border-style: dashed;
402
419
  background: var(--c-cobalt-50);
403
420
  opacity: 0.92;
404
421
  }
405
- platform-diagram .box-wrap.app-builder .row { color: var(--c-cobalt-400); font-style: italic; }
406
- platform-diagram .box-wrap.app-builder .row .desc { font-style: normal; }
407
- platform-diagram .box-wrap.app-builder .row:hover .name { color: var(--c-cobalt-700); }
422
+ platform-diagram .box-wrap.app-builder.has-badge .row { color: var(--c-cobalt-400); font-style: italic; }
423
+ platform-diagram .box-wrap.app-builder.has-badge .row .desc { font-style: normal; }
424
+ platform-diagram .box-wrap.app-builder.has-badge .row:hover .name { color: var(--c-cobalt-700); }
408
425
 
409
426
  /* Badge (e.g. COMING SOON) — generic, attaches to any .box-wrap */
410
427
  platform-diagram .box-wrap .badge {
@@ -139,6 +139,7 @@
139
139
  name: itemEl.getAttribute('name') || '',
140
140
  meta: itemEl.getAttribute('meta') || '',
141
141
  desc: itemEl.getAttribute('desc') || '',
142
+ href: itemEl.getAttribute('href') || '',
142
143
  brand: itemEl.hasAttribute('brand') || itemEl.hasAttribute('brand-color'),
143
144
  brandColor: itemEl.getAttribute('brand-color') || '',
144
145
  glyph: svg ? svg.cloneNode(true) : null,
@@ -160,7 +161,8 @@
160
161
  const positionClass = POSITION_CLASS[list.position] || list.position;
161
162
  const familyClass = list.family ? `fam-${list.family}` : '';
162
163
  const colsClass = list.columns > 1 ? `cols-${list.columns}` : '';
163
- wrap.className = `box-wrap ${positionClass} ${familyClass} ${colsClass}`.trim();
164
+ const badgeClass = list.badge ? 'has-badge' : '';
165
+ wrap.className = `box-wrap ${positionClass} ${familyClass} ${colsClass} ${badgeClass}`.trim();
164
166
 
165
167
  if (list.badge) {
166
168
  const badge = document.createElement('span');
@@ -173,8 +175,9 @@
173
175
  box.className = 'box' + (list.columns > 1 ? ` cols-${list.columns}` : '');
174
176
 
175
177
  function makeRow(item) {
176
- const row = document.createElement('div');
178
+ const row = document.createElement(item.href ? 'a' : 'div');
177
179
  row.className = 'row';
180
+ if (item.href) row.setAttribute('href', item.href);
178
181
 
179
182
  const glyph = document.createElement('span');
180
183
  glyph.className = 'glyph' + (item.brand ? ' brand' : '');
@@ -230,8 +233,9 @@
230
233
  if (!list.label || list.position === 'top') return null;
231
234
  const positionClass = POSITION_CLASS[list.position] || list.position;
232
235
  const familyClass = list.family ? `fam-${list.family}` : '';
236
+ const badgeClass = list.badge ? 'has-badge' : '';
233
237
  const hex = document.createElement('div');
234
- hex.className = `workspace-corner-hex ${positionClass} ${familyClass}`.trim();
238
+ hex.className = `workspace-corner-hex ${positionClass} ${familyClass} ${badgeClass}`.trim();
235
239
  // Allow newline in label to render a <br>; otherwise straight text.
236
240
  if (list.label.includes('\n')) {
237
241
  list.label.split('\n').forEach((line, i) => {