Globals
Instance Methods
App()
Application root: mounts the router behind the app-wide providers (tab management) and disables the context menu in production. The analytics SDK (services/analytics/analytics.js) self-initializes on import in index.jsx, so it needs no provider here.
Router()
History-API router variant - use when the deploy path is known (basename from VITE_BASE_URL). App.jsx picks one of the two (HashRouter.jsx is the current default, this one is commented out there).
Content + screensaver providers around the routes, pages guarded by the ErrorBoundary, same as HashRouter.jsx - but unlike HashRouter.jsx, this variant does NOT wrap the routes in Stage (the whole-app scale wrapper) and has no separate /print-map route. It has drifted out of sync with HashRouter.jsx's later additions; picking this variant today would run the kiosk unscaled and without the print-map dev tool.
Router()
Hash-based router variant - works regardless of the deploy path, so it's the default (see App.jsx). Stage (the whole-app scale wrapper, see Stage.jsx) sits inside rather than around it in App.jsx, because it reads its config via useConfig() -> useSearchParams(), which requires router context. Content + screensaver providers around the routes, pages guarded by the ErrorBoundary; the Screensaver stays mounted as a sibling so its fade works in both directions.
/print-map is a dev-only export tool (see PrintMap.jsx), not part of the visitor-facing kiosk - it's branched out here, before Stage/ContentProvider/ ScreensaverProvider, so it gets none of the kiosk chrome (fixed-resolution scaling, idle screensaver) and fetches its own content directly instead of going through ContentProvider's route-based file resolution (presentationModes.js has no entry for it).
Pages()
Route table of the exhibit: "/" is the app selection, each presentation mode has its own path, and GeneralPageTracking reports every route change. Value Advisory Services and Rad Enablement are linear SlidePage decks (optional :viewId slide segment); Value Partnership is a bespoke interactive sub-app with its own nested routes (see ValuePartnership.jsx).
resolveCountryColors(
countryDisplay?: Object,
fallbackColor: string,
): Object
Resolves value-partnership-globe-countries.json's continent-keyed and per-country config into the ccn3-keyed color map InteractiveGlobe's polygon layer needs. continents is applied first (colors every country in that continent), then countries (pre-populated with every ISO country, {cca3, name, state} - only cca3/state are read here, name is an editor-facing label) on top as per-country overrides - so e.g. continents.Europe="highlight" plus a DEU entry with state "hide" highlights all of Europe except Germany.
"normal" entries are treated as "no explicit choice" and skipped in both loops rather than written into the map - countries is pre-populated with all ~250 countries at "normal" by default (so an editor only has to change the ones they care about), and since "normal" is also the ultimate fallback color (defaultCountryColor), those untouched entries would otherwise silently overwrite a continent-level highlight/hide for every country in it.
Shared between the 3D globe (GlobePage.jsx) and the print map (PrintMap.jsx) so both render the exact same country colors from the same content.
Parameters
countryDisplay(Object, optional) —content.valuePartnership.countryDisplay.fallbackColor(string) — Used asdefaultCountryColorwhencountryDisplay.colors.normalisn't set (config-schema.js'sglobeFallbackCountryColor).
Returns
Object
countryMetaByCcn3(ccn3: string): Object | undefined
Country metadata for a given ISO numeric code, e.g. countryPolygons.js decorating each world-atlas feature.
Parameters
ccn3(string) — ISO numeric country code, e.g. "276" for Germany.
Returns
Object | undefined
countryMetaByCca3(cca3: string): Object | undefined
Country metadata for a given ISO alpha-3 code, e.g. resolving countryDisplay's countries entries (see countryColors.js).
Parameters
cca3(string) — ISO alpha-3 country code, e.g. "DEU" for Germany.
Returns
Object | undefined
countryMetaByName(rawName: string): Object | undefined
Country metadata by common name, case-insensitively and resolving COUNTRY_NAME_ALIASES first (e.g. content authored with a country's short/colloquial name instead of world-countries' commonName). Currently unused elsewhere in src - kept alongside the other by-* lookups.
Parameters
rawName(string) — Country name as authored in content.
Returns
Object | undefined
countriesByContinent(
continent: string,
): Array.<{ccn3: string, cca3: string, commonName: string, continent: string}>
All countries in a given continent/region - value-partnership-globe- countries.json's continents entries are resolved through this (see countryColors.js).
Parameters
continent(string) — world-countries'regionfield: "Africa", "Americas", "Antarctic", "Asia", "Europe", "Oceania".
Returns
Array.<{ccn3: string, cca3: string, commonName: string, continent: string}>
loadCountryPolygons(): Array.<Object>
Converts the world-atlas topology into GeoJSON country features (once, then cached in-module), decorated with the metadata (name/cca3/continent) countryMeta.js knows for each ccn3 - consumed by InteractiveGlobe's polygon layer. Also splits out French Guiana (from France's MultiPolygon) and Alaska (from the USA's) into their own features, see the inline comments below, so they can be colored independently via countryColors.js.
Returns
Array.<Object>— GeoJSON Feature objects, each withproperties: {ccn3, name, cca3, continent}.
RadHeader(props: Object)
Page header shown on every RadEnablement slide (title/subline from content.header); the story icon on the right follows whichever story the current view belongs to.
Parameters
props(Object)props.viewId(string) — Currently shown view id.
Other
CacheableVideo
Drop-in replacement for <video src="..."> that's cacheable by the service worker. Renders "Loading video..." until the fetch has completed.
Parameters
src(string) — URL of the video file to fetch and play.playing(boolean, optional) — When given, drives playback: true plays, false pauses and rewinds. Lets an always-mounted but hidden video (e.g. the screensaver behind opacity: 0) stop decoding while it's not visible.fallback(React.ReactNode, optional) — Rendered while the blob is being fetched, instead of the default "Loading video..." text.props(*) — Passed through to the underlying<video>element (autoPlay, loop, muted, playsInline, ...).
ComponentRenderer
Renders a single content block of a view. Looks the block's type up in the element registry (elements/registry.js) and reshapes the raw block into the component's props via the element's own adapter. Unknown types render a debug hint in dev mode and nothing in production.
Parameters
props(Object)props.block(Object) — A content block from a view'selementsarray, e.g.{ type: "headline", text: "..." }.props.selectedTargetViewId(string | null) — Currently armed target view (story-hub selection), passed through to selectWidget.props.onSelectTarget(function) — Arms/clears the target view selection.props.onNavigate(function) — Navigates to a view id (used by buttonList/buttonGrid).
IntroLoopVideo
Plays intro once, then switches to loop playing forever. Both videos are mounted from the start - the loop is fetched, decoded and paused at its first frame in the background while the intro plays, so the switch is just an opacity swap instead of a src change that would force a fresh fetch/decode at the exact moment of the handoff (visible as a stutter).
Parameters
intro(string) — URL of the video played once at the start.loop(string) — URL of the video played on loop afterwards.props(*) — Passed through to both underlying<video>elements.
Screensaver
Full-screen overlay shown while the app is idle (see useScreensaver). Stays mounted at all times so the opacity transition plays both when fading in and when fading out; visibility is toggled via CSS class. Dismissing it needs no handler here - any interaction is already picked up by the global listeners in useIdle and flips isIdle back to false.
The video shown depends on defaultMode (config-schema.js): each presentation mode's own content file (see PRESENTATION_MODES in presentationModes.js, loaded per-route by ContentProvider) defines its own screensaver under "screensavers", keyed by that mode's key. In multi-app/ navbar mode (defaultMode "all") or for an unrecognized defaultMode value, no entry matches and no video is shown.
SlideNavigation
Fixed bottom navigation of the slide pages, with three modes:
- closed: menu button + prev/next arrows (hidden entirely while the view has no adjacency, e.g. before content has loaded)
- menu: full-width bar with Home / Screensaver / Apps / Slides / close, plus an "Open Link" button when the current view has a
link/visibleLink(opened in a new tab via useTabs) - slides: same bar plus the full-screen SlidesOverlay
Parameters
props(Object)props.prevViewId(string | null) — Target of the "back" arrow, disables it when null.props.nextViewId(string | null) — Target of the "next" arrow, disables it when null.props.homeViewId(string) — View the menu's Home button navigates to.props.currentViewId(string) — Currently shown view (highlighted in the slides overlay).props.onNavigate(function) — Navigates within the current app, keeping config params.
SlidePage
Shared skeleton of the slide-based presentation pages (ValueAdvisoryServices, RadEnablement): viewId routing, slide navigation, element-bucket layout and the content error/redirect states. The pages themselves only supply their constants, their CSS (via classPrefix) and an optional header.
Parameters
basePath(string) — route prefix, e.g. "/rad-enablement"startViewId(string) — view shown at the bare route, e.g. "rad-intro"storyHubViewId(string) — the selectWidget hub view (e.g. "rad-select-story"), see getAdjacentRootViewshomeViewId(string, optional, default: "storyHubViewId") — menu's Home button target; defaults to storyHubViewId but can point elsewhere (e.g. an intro slide) when Home shouldn't land on the story pickerclassPrefix(string) — CSS class prefix, e.g. "rad" -> "rad-page", "rad-slide-content"renderHeader(function, optional) — optional per-page header, returning a React nodedisableAnimations(boolean, optional, default: false) — mirrored onto the page div asdata-disable-animationsso a page's own CSS can opt into reacting to it (e.g. RadEnablement.scss dropping its background image) - SlidePage itself has no opinion on what it should disable, see config-schema.js
SlidesOverlay
Full-screen overlay listing every slide of the loaded content file as a clickable thumbnail (from the view's optional thumbnail field, with a text placeholder as fallback), grouped by story. A toggle switch selects "All Slides" or a single story (the "intro" story is excluded from the switch entirely - its slides only show up under "All Slides"); it opens on the current view's story, or "All Slides" when that story isn't offered as its own tab. Rendered by SlideNavigation in its "slides" mode.
Parameters
props(Object)props.currentViewId(string) — View to highlight and to derive the initial story tab from.props.onSelectSlide(function) — Called with the clicked slide's view id.
Stage
Whole-app scale wrapper. Renders children inside a fixed-size box and scales that box down to fit whatever real screen it ends up on via CSS transform: scale().
Replaces the previous per-page calc(1rem / X) + html-root-font-size scaling scheme: instead of keeping dozens of components' em/rem units in sync with two independent, uncoordinated breakpoint systems (the app's own root font-size AND @ui-marcom/shared's internal viewport-width breakpoints), every pixel authored anywhere in children scales together as one image - which system decided a given size no longer matters once it's inside this box.
Each sales app has one fixed, content-driven orientation (confirmed with the content team: everything is landscape except Rad Enablement, which is portrait-only - see PRESENTATION_MODES' orientation field, matched against the current route via resolveModeForPath()). "/" (app selection) matches no mode and defaults to landscape.
The reference box has two independent axes, not one:
- WHICH physical kiosk this is (
stageDeviceconfig value: "4k" or "ipad") picks that device's own landscape width/height pair. A 4K panel and an iPad need separate pairs (not a shared one) because an iPad is a 2x Retina display - its CSS point resolution is roughly half its physical pixel count, very different from a 4K panel's 1:1 mapping. - Orientation then swaps THAT device's own pair for portrait routes - "a physically rotated panel" is only true within one device's pair, not across devices. Treating portrait as a single cross-device resolution was tried first and broke both ends: Rad Enablement letterboxed on the iPad (wrong aspect ratio), and would have upscaled/blurred on a rotated 4K panel (wrong magnitude) - see README.md's "Scaling (Stage)" section.
The scale factor is never allowed above 1 in practice: the reference resolution is deliberately the largest target device in its orientation, so real screens only ever scale down. Upscaling would blur the WebGL globe (InteractiveGlobe.jsx), whose canvas render resolution three.js derives from layout size, not from a CSS transform applied to an ancestor.
stage-inner also carries data-stage-device so per-device CSS overrides (e.g. RadEnablement.scss's iPad content-density correction, since the same em-authored content necessarily renders larger on the iPad's ~2x-smaller reference box) can target a device without their own JS.
Must be mounted inside the router (see HashRouter.jsx), not in App.jsx - it reads its config (stageDevice and the stageRefWidth/Height / stageIpadRefWidth/Height pairs) via useConfig() and the current route via useLocation(), both of which need router context and would silently fail (or throw) outside a .
AiObs
Before/after-style AI observation showcase ("aIObs" element type): a picture plus screenshot (optionally alongside a screens device frame image, which switches to a stacked "computer + data" layout instead of the default side-by-side one), with an optional badge row and add-on caption.
Parameters
props(Object)props.picture(string) — Primary image URL.props.pictureAddOn(string, optional) — Optional caption/second image rendered next topicture.props.screenshot(string) — Secondary image URL.props.screens(string, optional) — Optional device-frame image URL; when set, switches to the stacked layout.props.badges(Array.<{iconLink: string, text: string}>) — Badges rendered via BadgeList.
Avatar
Round avatar image, used for the portrait next to a Quote.
Parameters
props(Object)props.src(string) — Image URL.props.alt(string) — Alt text.
Badge
Single icon-plus-label pill ("badge" element type), also used standalone inside BadgeList. icon (a ui-marcom-icon name) takes precedence over iconLink (an arbitrary image URL) when both are given.
Parameters
props(Object)props.iconLink(string, optional) — Fallback icon image URL, used wheniconis not set.props.icon(string, optional) —ui-marcom-iconname; takes precedence overiconLink.props.text(string, optional) — Label text.
BadgeList
Row of Badges. Not a registry element itself - rendered internally by AiObs.
Parameters
props(Object)props.badges(Array.<{iconLink: string, text: string}>) — One entry per badge.
BodyText
Plain paragraph of body-sized text ("bodyText" element type) - an intro or description paragraph below a headline. Distinct from disclaimer (small, absolutely bottom-pinned legal print) and from headline (display-sized).
Parameters
props(Object)props.text(string) — Paragraph text. Supports<mark>...</mark>highlight spans, but only whenlevelis "1".props.level(string, optional) — Size variant; "1" renders the larger, mark-aware boxed style, anything else the plainbody-text--{level}paragraph.
ButtonGrid
Grid of logo tiles ("buttonGrid" element type), e.g. partner institutions. Like ButtonList, clicking reports the target view id to the page instead of navigating directly.
Parameters
props(Object)props.items(Array.<{content: {logo: string}, targetViewId: string}>) — One entry per tile.props.onNavigate(function) — Called with the clicked tile's target view id.
ButtonList
Vertical list of navigation buttons ("buttonList" element type). Buttons don't navigate themselves - they report the chosen view id to the page.
Parameters
props(Object)props.items(Array.<{content: {label: string}, targetViewId: string, icon: string}>) — One entry per button.iconis optional and rendered when present, but the registry adapter (index.js) never populates it from content today, so it's effectively unused via the normal content-authoring path.props.onNavigate(function) — Called with the clicked item's target view id.
Card
Simple text card with optional headline, body and footer line. Used as the detail popup of a Circle in the CircleInfographic.
Parameters
props(Object)props.headline(string, optional) — Card headline.props.text(string, optional) — Card body text.props.underline(string, optional) — Small footer line below the body.
CardGrid
Stat tiles plus an optional highlight card ("cardGrid" element type), e.g. "20% perceived time savings" alongside a customer statement.
Parameters
props(Object)props.stats(Array.<{value: string, label: string}>) — Stat tiles (value + caption).props.highlight(Object | null) — Optional emphasized card.
ChartGrid
One or two chart/diagram images side by side, each with an optional caption above it ("chartGrid" element type). Portrait: stacks to 1 column.
Parameters
props(Object)props.items(Array.<{title: (string|null), image: string}>) — 1-2 chart entries.
CircleChain
N labeled circles overlapping in a horizontal row ("circleChain" element type), brand-gradient colored left to right.
Parameters
props(Object)props.items(Array.<{text: string}>) — Circles, left to right.props.orientation("landscape" | "portrait", optional, default: "\"landscape\"") — Sizes each circle as a fraction of the measured ring diameter, per the Figma spec's portrait/landscape ratios.
Circle
One selectable circle of the CircleInfographic. Toggles its detail Card on tap and closes on tap-outside.
Parameters
props(Object)props.text(string) — Label inside the circle.props.color("black" | "blue" | "white" | "orange") — Color/position variant class.props.card(Object) — Detail card content.props.icons(Array.<{small: string, big: string}>) — Orbiting IconBubbles definitions.props.handleCircleSelect(function) — Notifies the parent to blur the other circles.props.close(function) — Notifies the parent to lift the blur again.
ARROW_BLACK
Interactive four-circle service infographic ("circleInfographic" element type): tapping a circle opens its detail Card and blurs the rest. Expects exactly four entries; render order is fixed to match the layout.
Parameters
props(Object)props.circle(Array.<{text: string, icons: Array.<{small: string, big: string}>, card: {headline: (string|null), text: (string|null), underline: (string|null)}}>) — The four circle definitions.props.headline(string) — Label shown in the center of the ring.
Disclaimer
Small-print legal/disclaimer line of a slide ("disclaimer" element type).
Parameters
props(Object)props.text(string) — Disclaimer text.
Headline
Semantic heading with app-wide typography classes. Renders the content JSON's "headline" (level 2) and "subheadline" (level 3) element types, and is used directly for page headers.
Parameters
props(Object)props.text(string) — Heading text.props.level(number, optional, default: 1) — Heading level 1-6, controls both tag (h1-h6) and size class.props.color("white" | "orange", optional, default: "\"white\"") — Color variant class.
IconBubbles
Decorative pair of icon bubbles (one big, one small) orbiting a Circle in the CircleInfographic.
Parameters
props(Object)props.action(function) — Called on click of either bubble; forwarded to the parent Circle as its select/close handler.props.num(number) — Index among the Circle's icon bubbles, used for per-bubble positioning (num--{n}class).props.big(string) — Icon URL for the big bubble.props.small(string) — Icon URL for the small bubble.
IconBulletPoints
Vertical list of icon-plus-text bullet points ("iconBulletPoints" element type).
Parameters
props(Object)props.points(Array.<{icon: string, text: string}>) — One entry per bullet point.
IconWheel
N icon nodes with text labels evenly spaced around a decorative ring ("iconWheel" element type) - unlike CircleInfographic/SelectWidget, purely decorative and non-interactive.
Parameters
props(Object)props.items(Array.<{icon: string, label: string}>) — Ring nodes.
ImageBtnWidget
Full-width image with an optional ButtonGrid below it ("imageBtnWidget" element type).
Parameters
props(Object)props.image(string) — Image URL.props.buttonGrid(Object, optional) — Adapted ButtonGrid props (see ButtonGrid/index.js), rendered whenitemsis present.
ImageCard
Card with an image and/or caption text. Rendered by ImageCardGrid.
Parameters
props(Object)props.image(string, optional) — Image URL.props.text(string, optional) — Caption text.
ImageCardGrid
Grid of ImageCards ("imageCardGrid" element type), e.g. product screenshots with captions.
Parameters
props(Object)props.items(Array.<{image: (string|null), text: (string|null)}>) — One entry per card.
InfoGridWidget
Labelled text box (or image) next to a grid of short labels ("infoGridWidget" element type), e.g. a nodule example paired with its measurable attributes. When textboxImage is set, it replaces the head/text box entirely.
Parameters
props(Object)props.select(string, optional) — Small caption above the text/image box (e.g. "Example").props.textboxHead(string, optional) — Headline inside the text box; ignored whentextboxImageis set.props.textboxImage(string, optional) — Image URL shown instead of the text box.props.textboxText(Array.<{item: string}>) — Body lines inside the text box; ignored whentextboxImageis set.props.grid(Array.<{item: string}>) — Grid tiles, one headline each.
InstitutionBadge
Country flag + institution logo pair ("institutionBadge" element type), shown on the "Discover real-world impact" slides.
Parameters
props(Object)props.flag(string) — Flag image URL.props.logo(string) — Institution logo URL.
LinkBtn
Standalone "open external link" control button, styled like the SlideNavigation menu button. Not a registry element (no index.js/schema.js) and currently not imported anywhere in src - kept here for the pattern used inline by SlideNavigation's own link/visibleLink buttons.
Parameters
props(Object)props.link(string) — URL opened in a new tab via useTabs.
PeerList
Grid of peer/benchmark cards ("peerList" element type), each with a logo, optional flag, optional stat tiles and an optional highlighted quote.
Parameters
props(Object)props.peers(Array.<{flag: ?string, logo: string, stats: Array.<{value: string, label: string}>, highlight: ?{headline: string, text: string}}>) — One entry per peer card.
ProcessFlow
N flat step images (photo/mockup with title+bullets already baked in) connected by arrows ("processFlow" element type).
Parameters
props(Object)props.items(Array.<{image: string, card: {headline: string, points: Array.<string>}}>) — Steps, left to right; 3 bullet points switch the card to the narrower--2layout, anything else uses--1.
Quote
Single quote row: avatar plus headline and quote text. Rendered by QuoteList.
Parameters
props(Object)props.item(Object) — Quote content.
QuoteList
Stack of customer quotes ("quoteList" element type).
Parameters
props(Object)props.items(Array.<{content: {headline: string, icon: string, text: string, source: string}}>) — One entry per quote;sourceis optional.props.level(number, optional) — Size/spacing variant class (quote-list--level{n}); defaults to 0 via the registry adapter.
SelectWidget
Circular story picker ("selectWidget" element type): options are spread evenly around a ring; selecting one shows its title in the center and arms the page's "next" navigation (it does not navigate by itself).
Parameters
props(Object)props.options(Array.<{id: string, text: string, icon: string}>, optional) — Selectable options;idis a view id.props.orientation("portrait" | "landscape", optional, default: "\"portrait\"") — Ring/item sizing preset (from the content JSON).props.selectedId(string | null) — Currently selected option id.props.onSelect(function) — Called with the tapped option's id, or with no argument on an outside click (deselects).
StepTracker
Horizontal dot progress indicator ("stepTracker" element type): one circle per step, connected by a line, the current step filled solid.
Parameters
props(Object)props.total(number) — Total number of steps.props.current(number) — Current step, 1-based.
StoryTitle
Full-bleed story opener ("storyTitle" element type): big title over an optional background artwork. When videoIntro/videoLoop are set and disableAnimations isn't, an animated background plays on top of the static background image instead of it.
Parameters
props(Object)props.title(string) — Story title text.props.background(string, optional) — Background image URL, also the fallback shown when the video is disabled/missing/still loading.props.videoIntro(string, optional) — Background video played once.props.videoLoop(string, optional) — Background video played after, on loop.
TextMediaWidget
Media block with an accompanying text box ("textMediaWidget" element type). Shows an image or a (service-worker-cacheable) video, plus optional text.
Parameters
props(Object)props.image(string | null) — Image URL; mutually exclusive with video in practice.props.video(string | null) — Video URL, played via CacheableVideo.props.text(string | null) — Text shown below the media.props.level(number | null, optional) — When1, wrapsimagein an ImageCard instead of a plain<img>.
useElementSize
Parameters
initialSize(Object, optional) — Value returned before the observer's first callback fires. Defaults to {0,0}; pass a best-guess (e.g. window.innerWidth/innerHeight) when the element is expected to roughly fill the viewport, to avoid a one-frame flash at size 0.
Returns
Array— A[ref, size]tuple: a callback ref to attach to the observed element, and its current{width, height}content-box size in CSS px.
useIdle
Parameters
idleTime(number) — Time in ms of inactivity after whichisIdlebecomes true.
Returns
Object—triggeris the event type that ended the last idle period ("init" on first mount), and is reset to null once idle again - useful for debugging what woke the app up.forceIdleshows the idle state immediately, without waiting for the timeout.
AppSelectionPage
Landing screen at "/": one tile per presentation mode (see PRESENTATION_MODES). Also the target of the idle session reset and of the menu's "Apps" button.
PrintMapPage
Dev-only export tool: renders the same country polygons and location points the 3D globe (InteractiveGlobe.jsx) uses, but as a flat, unclipped d3-geo projection instead of a 3D perspective - see the print-map decision doc referenced from the project chat. Not part of the visitor-facing kiosk; reachable directly at #/print-map (see HashRouter.jsx), outside the Stage/ContentProvider/Screensaver chain, so it fetches its own content instead of going through ContentProvider's route-based file resolution.
RadEnablementPage
"RadEnablement Services" presentation app - the shared SlidePage plus a persistent RadHeader (content: public/data/rad-content.json).
ValueAdvisoryServicesPage
"Value Advisory Services" presentation app - the shared SlidePage plus a persistent per-story icon badge (content: public/data/vas-content.json).
VasHeader
Persistent per-story icon badge shown top-right on every VAS slide of a story except its own storyTitle cover (which already fills the screen). Unlike RAD, VAS views author their own headline text per view (there is no app/story-wide title to repeat) - only the story icon is a constant, from stories..header.icon in vas-content.json.
Parameters
props(Object)props.viewId(string) — Currently shown view id.
GlobeBottomNav
Fixed bar for stepping through the currently visible globe markers without having to hunt for pins on the globe - Home/Prev/Next + a "N of total" jump-to-index select, plus the previous/current/next marker by name. Only rendered while a marker is selected (preview or full details open), at the same screen position as ValuePartnershipNav's own bottom bar - it overlays/replaces that bar for the duration of the selection rather than stacking alongside it.
Parameters
props(Object)props.locations(Array.<GlobeLocation>)props.selectedId(string | number | null)props.labels(Object) —content.valuePartnership.labels.props.onSelect(function) — Called with the newly selected location.props.onHome(function) — Clears the selection (closes the detail card).
GlobeDetailCard
Detail overlay for a clicked globe marker, ported from value-globe.js's card__big/card__details. Two views:
- "preview" (default): compact popup with a short blurb and a "Show details" button, shown right after clicking a marker.
- "details": the full card - a single "Overview" tab (headline/image/ keyfacts/partnership/quote) for outcome points; one tab per impact category for impact points instead, no separate "Overview" tab there - each category tab renders its own outcomes/impacts, key facts & team setup, and customer key-challenges/goals sections, see value-globe.js:900-983 for the ported-from markup.
Parameters
props(Object)props.point(Object) — The clicked location's full content entry.props.labels(Object) —content.valuePartnership.labels.props.onClose(function)
GlobePage
"Globe" tab of Value Partnership: the 3D InteractiveGlobe (lazy- loaded, see below) with its ValueCheckboxPanel filter/search drawer, GlobeBottomNav home/prev/next bar and, once a marker is selected, its GlobeDetailCard. isPointVisible (ported from the old app's value-globe.js) decides which of content.valuePartnership.points are shown, combining the checkbox panel's filters and search field.
ValueCheckboxPanel
Slide-in filter panel for the globe's markers, ported from the old value-checkbox-panel Lit component. Controlled component for filtering: filters is owned by GlobePage (see its isPointVisible-equivalent useMemo), this just renders it and reports changes via onChange. The open/collapsed state of the drawer itself is local UI state - it doesn't affect which markers are visible, so it has no reason to live in GlobePage.
Parameters
props(Object)props.filters(Object) —{ search, impactCasesActive, outcomeCasesActive, checkedImpactCases, checkedOutcomeCases, checkedStrategyCases }.props.onChange(function) — Called with the next filters object.
CategoryMenu
"Choose a category" slide-in side panel, ported from value-partnerships-page.js's .value-categories block: a Departments/Roles switch plus the matching list of clickable category buttons (content.partnerships.buttons or .roles). Same collapsible-drawer pattern as GlobePage's ValueCheckboxPanel - see that component for why the open/collapsed state is local UI state rather than a prop.
Parameters
props(Object)props.selectedView("departments" | "roles")props.onViewChange(function)props.categories(Array.<{id: string, title: string}>) — Buttons or roles for the current view.props.onSelectCategory(function)props.labels(Object)
CategoryOverlay
PPT-deck overlay for one department/role, ported from value-partnerships-page.js's renderOverlay/renderCard. Shows every content.valuePartnership.partnerships.ppt item whose location/roles match the selected category as a clickable preview card.
Parameters
props(Object)props.category(Object)props.selectedView("departments" | "roles")props.ppt(Array.<Object>) — Full presentation-deck list.props.onSelectPpt(function)props.onClose(function)
PartnershipsPage
Value Partnerships page: hospital-department/role browser + presentation deck viewer, ported from value-partnerships-page.js. See CategoryMenu, CategoryOverlay, PresentationBrowser and SlideView for the individual pieces this composes.
Parameters
props(Object)props.onActionsChange(function, optional) — Registers this page's "Show key slides"/"Show all presentations" buttons into ValuePartnershipNav's children slot (see ValuePartnership.jsx).
PresentationBrowser
Full-screen browse overlay, ported from value-slideview-filter.js (the old "SlideViewFilter" - a misleading name, it has nothing to do with filtering a single deck's slides). Two modes:
- "all": every presentation deck, grouped per content.valuePartnership's partnershipsFilterUS/partnershipsFilter link groups (hideUS picks which).
- "keySlides": a thumbnail grid of content.valuePartnership.partnerships .keySlides.overview, each opening its matching mini-deck from keySlides.ppt.
Parameters
props(Object)props.mode("all" | "keySlides")props.hideUS(boolean) — Accepted (per PartnershipsPage'slinkGroupsselection) but its own toggle switch is currently commented out below, so this is presently unused inside the component.props.onToggleHideUS(function) — SeehideUS- currently unused inside the component.props.linkGroups(Array) —content.valuePartnership.partnershipsFilterUS|Filter.groups.props.ppt(Array) — Full presentation-deck list (to resolve a link's ppt.id).props.keySlidesOverview(Array)props.keySlidesPpt(Array)props.onSelectPpt(function)props.onClose(function)props.onSwitchMode(function) — Jumps to the other mode ("all" <-> "keySlides").
QrCodeCanvas
Renders url as a QR code onto a canvas via the qrcode package (ported from value-slideview(-filter).js's showQRCode).
Parameters
props(Object)props.url(string) — URL/text to encode.props.size(number, optional, default: 220) — Canvas width/height in px.
COPYRIGHT_NOTICE
Fullscreen presentation-deck viewer, ported from value-slideview.js (minus the QR/congress-app "save highlights" checkbox mode, which lives in PresentationBrowser, and the manual <link rel=preload> injection, replaced by the browser's own image decoding/caching). Mount with key={ppt.id} so opening a different deck resets page cleanly instead of syncing it via effect.
Parameters
props(Object)props.ppt(Object)props.initialPage(number, optional, default: 1)props.labels(Object)props.onClose(function)
resolveWebLink
Normalizes the webLink field found on partnership/ppt/filter-option entries (value-partnership-partnerships.json) into a URL the QR code can encode. webLink is stored inconsistently as either a plain string or a one-element array, and is sometimes an empty string when no link exists.
Parameters
webLink(string | Array.<string>)
Returns
string | null— The resolved URL, ornullif none is set.
ValueDriverNav
Compact vertical quick-nav for the 6 drivers, shown in ValueExcellencePage's detail-view aside so the user can jump straight between driver pages instead of returning to the Venn-diagram overview each time.
Parameters
props(Object)props.drivers(Array.<{id: string, title: string}>) — content.valuePartnership.driversprops.selectedId(string) — id of the currently active driverprops.onSelect(function)props.onBack(function)props.labels(Object)
ValueExcellencePage
Value & Excellence Drivers page, ported from value-excellence-page.js: an overview (clickable Venn infographic) that switches to a driver detail view. The old page also duplicated the detail solutions as a mobile-only accordion (hidden above its "xl" breakpoint via CSS) - this kiosk always runs at one large landscape resolution (see presentationModes.js), so only the grid variant is ported; the DOM-measurement height hack for the grid titles is replaced by CSS Grid's own row-stretching.
ValueInfographic
Clickable Venn-diagram infographic of the 6 value/excellence drivers, ported from the old value-infografic Lit component. The SVG itself is a large static asset (see venn-diagram-svg.js) rendered via dangerouslySetInnerHTML - safe here since the markup is our own trusted, static file, not user input. Clicks are handled by delegation (each segment carries a data-driver-id attribute) instead of converting ~14 inline Lit event bindings into individual JSX handlers.
Parameters
props(Object)props.onSelect(function) — Called with the clicked driver's id.
ValuePartnershipPage
"Value Partnership" presentation app. Unlike ValueAdvisoryServices/ RadEnablement (linear SlidePage decks), this is a bespoke sub-app with 3 independent interactive sections (globe, excellence drivers, partnerships/ slideshow) sharing one nav bar and one content file - so it owns its own nested route table instead of going through SlidePage.
GlobePage is deliberately kept outside that route table and always rendered (just hidden via CSS - see .value-partnership-page__globe--hidden
- while another tab is active) instead of being mounted/unmounted per route like Excellence/Partnerships are. It owns a full Three.js/WebGL scene (react-globe.gl); tearing that down and rebuilding it on every tab switch is both slow and a known memory-leak risk class for this kind of library on repeated unmount. GlobePage's own
activeprop drives pausing its render loop while hidden instead (see InteractiveGlobe.jsx).
ValuePartnershipNav
Fixed nav bar shared by all 3 Value Partnership sections: section switcher (labels from content) + a burger toggle on the right that reveals the "Switch App" button back to the app selection (kept collapsed by default per Figma, unlike the sibling nav bars - ValueDriverNav/CategoryMenu/ ValueCheckboxPanel - which show it unconditionally). The section-switcher labels/logo area also carries the long-press-to-reload kiosk gesture (see Reload).
children, when passed, renders as a second button group alongside the section switcher - the lifted-state slot a routed sub-page (e.g. PartnershipsPage) uses to put its own page-scoped actions in this shared bar, mirroring how suppressNav/onSelectionChange let a sub-page affect this bar from below (see ValuePartnership.jsx).
Parameters
props(Object)props.children(*, optional) — React node(s) for the second button group.
useAnalytics
Returns the analytics singleton's tracking functions. Not a real React hook (no state/context involved) - kept as a hook-shaped function so existing call sites read the same as the old useTracking().
CONFIG_SCHEMA: Array.<{key: string, source: string, name: string, type: string, default: *, min: number, description: string}>
Declares every kiosk/device config value the app understands. Values come from URL params (source "param", read live by useConfig in config.jsx and kept alive across navigation), each with type, default and a German description shown in admin/debug contexts.
useConfig
React hook that resolves every entry in CONFIG_SCHEMA (config-schema.js) against the current URL search params / import.meta.env and returns them as a flat, typed { key: value } object.
Returns
Object— Config values keyed by their CONFIG_SCHEMAkey.
ContentContext
Context carrying { content, views, stories, error, getView, getAdjacentRootViews } - consume via useContent.
useContent
React hook to access the loaded content object. Returns {} until the content.json fetch in ContentProvider has resolved.
RETRY_DELAY_MS
Resolves the content file(s) for the current route, fetches them (once per file-list combination - already-fetched combinations are served from an in-memory cache so switching between apps doesn't refetch) and makes the merged result available to all descendants via ContentContext. On fetch failure, content stays {}, the failure is exposed as error (pages show a visible message instead of a blank screen) and the fetch is retried until it succeeds.
PRESENTATION_MODES: Array.<PresentationMode>
Single source of truth for the 3 presentation modes/sales apps. key doubles as the valid defaultMode config value (config-schema.js) and as the route path segment (see Routes.jsx, AppSelection.jsx). contentFile is the JSON this mode's route loads via ContentProvider (services/content.jsx); null means the page doesn't fetch any content (yet). A mode can use contentFiles (array) instead of contentFile (single string) when its content is split across several smaller files - ContentProvider fetches all of them and merges the results into one content object, so consumers still read e.g. content.valuePartnership.points regardless of which file it came from.
orientation is each app's fixed, content-driven physical orientation (confirmed with the content team, not device-dependent - Rad Enablement is portrait-only, everything else landscape) - consumed by Stage.jsx to pick a landscape vs. portrait reference resolution for the whole-app scale wrapper. resolveModeForPath() below is the one place that maps a route to its mode - reused by Stage and GeneralPageTracking so they can never disagree about which mode a given path belongs to.
resolveModeForPath
Resolves which PRESENTATION_MODES entry a route belongs to, or null for a path that isn't any mode's route (currently just "/", the app selection screen - always landscape, see Stage.jsx).
Parameters
pathname(string)
Returns
ScreensaverProvider
Tracks idle state and exposes it via ScreensaverContext.
useScreensaver
React hook to access { isIdle, trigger, screensaverFadeDuration, showNow }. Must be used within a <ScreensaverProvider>.
TabProvider
Provides openTab/closeTab (see useTabs) to the component tree, tracking open external tabs/windows per URL.
useTabs
React hook to access openTab/closeTab. Must be used within a <TabProvider>.
GeneralPageTracking
Invisible helper mounted next to the routes (see Routes.jsx): keeps document.title in sync with the current route. Page-view tracking itself is handled implicitly by the analytics SDK's History API listener (see services/analytics/AnalyticsSDK.js) - no explicit tracking call needed here.
parseConfigValue
Parses and validates a single raw config value against its schema definition.
Parameters
rawValue(string | null | undefined) — The raw value as read fromsearchParams.get(...)orimport.meta.env[...]. Always a string when set.definition(Object) — The matching entry from CONFIG_SCHEMA.definition.type("boolean" | "number" | "string") — How to interpret rawValue.definition.default(*) — Value to use when rawValue is missing/invalid.definition.min(number, optional) — Only for type "number": reject values below this.definition.max(number, optional) — Only for type "number": reject values above this.definition.key(string) — Config key name, used only for warning messages.
Returns
*— The parsed value, ordefinition.defaultif rawValue was empty or failed validation.
navigateWithTransition
Navigates with a native View Transition cross-fade. react-router's own viewTransition navigate option only works with the data router (createHashRouter/RouterProvider) - this app uses the plain declarative /, so the native API is driven manually. flushSync forces the DOM update to happen synchronously inside the callback, which the API requires.
Callers are responsible for keeping the current query string alive (kiosk/device config, see config-schema.js) by including it in path.
Parameters
navigate(function) — react-router'snavigatefunction (fromuseNavigate()).path(string) — Target path, including query string.