uhc-portal-components-react 0.2.21 → 0.2.23

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/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.2.23 - 2026-10-22
6
+
7
+ - Keep the shared header usable in server-rendered and test hosts that do not
8
+ expose the optional browser `matchMedia` API.
9
+
10
+ ## 0.2.22 - 2026-10-22
11
+
12
+ - Place the verified login identifier beneath the application name, keep the
13
+ current subject immediately before the account control and render the
14
+ account trigger as an icon only across Personal and Professional hosts.
15
+
5
16
  ## 0.2.21 - 2026-10-21
6
17
 
7
18
  - Move the card preview, profile recovery, elapsed-operation status and
@@ -1,4 +1,5 @@
1
1
  import { type PortalBrand, type PortalAccountOption, type PortalLanguageOption, type PortalMenuAction, type PortalShellCopy } from './contracts.js';
2
+ import { type PortalCurrentSubjectRegistration } from './PortalCurrentSubject.js';
2
3
  export type PortalHeaderProps = Readonly<{
3
4
  brand: PortalBrand;
4
5
  copy: PortalShellCopy;
@@ -11,6 +12,7 @@ export type PortalHeaderProps = Readonly<{
11
12
  menuActions?: readonly PortalMenuAction[];
12
13
  onSignOut?: () => void | Promise<void>;
13
14
  storageNamespace?: string;
15
+ currentSubject?: PortalCurrentSubjectRegistration;
14
16
  }>;
15
17
  /**
16
18
  * Shared account/preferences header.
@@ -18,5 +20,5 @@ export type PortalHeaderProps = Readonly<{
18
20
  * Authentication is deliberately supplied as data and callbacks. The
19
21
  * component never imports Firebase and never treats browser state as a role.
20
22
  */
21
- export declare function PortalHeader({ brand, copy, accountIdentifier, languageHref, languageOptions, guestActionHref, accountOptions, onSelectAccount, menuActions, onSignOut, storageNamespace, }: PortalHeaderProps): import("react").JSX.Element;
23
+ export declare function PortalHeader({ brand, copy, accountIdentifier, languageHref, languageOptions, guestActionHref, accountOptions, onSelectAccount, menuActions, onSignOut, storageNamespace, currentSubject, }: PortalHeaderProps): import("react").JSX.Element;
22
24
  //# sourceMappingURL=PortalHeader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PortalHeader.d.ts","sourceRoot":"","sources":["../src/PortalHeader.tsx"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EAGrB,MAAM,gBAAgB,CAAA;AASvB,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IACvC,KAAK,EAAE,WAAW,CAAA;IAClB,IAAI,EAAE,eAAe,CAAA;IACrB,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,eAAe,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAA;IACjD,eAAe,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAA;IAC/C,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IACtC,WAAW,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAA;IACzC,SAAS,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B,CAAC,CAAA;AAEF;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,IAAI,EACJ,iBAAiB,EACjB,YAAY,EACZ,eAAoB,EACpB,eAAe,EACf,cAAmB,EACnB,eAAe,EACf,WAAgB,EAChB,SAAS,EACT,gBAA+B,GAChC,EAAE,iBAAiB,+BAoKnB"}
1
+ {"version":3,"file":"PortalHeader.d.ts","sourceRoot":"","sources":["../src/PortalHeader.tsx"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EAGrB,MAAM,gBAAgB,CAAA;AAOvB,OAAO,EAGL,KAAK,gCAAgC,EACtC,MAAM,2BAA2B,CAAA;AAElC,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IACvC,KAAK,EAAE,WAAW,CAAA;IAClB,IAAI,EAAE,eAAe,CAAA;IACrB,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,eAAe,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAA;IACjD,eAAe,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAA;IAC/C,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IACtC,WAAW,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAA;IACzC,SAAS,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtC,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,cAAc,CAAC,EAAE,gCAAgC,CAAA;CAClD,CAAC,CAAA;AAEF;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,IAAI,EACJ,iBAAiB,EACjB,YAAY,EACZ,eAAoB,EACpB,eAAe,EACf,cAAmB,EACnB,eAAe,EACf,WAAgB,EAChB,SAAS,EACT,gBAA+B,EAC/B,cAAc,GACf,EAAE,iBAAiB,+BA2KnB"}
@@ -4,14 +4,14 @@ import { useEffect, useRef, useState } from 'react';
4
4
  import { portalUiDensities, portalUiThemes, } from './contracts.js';
5
5
  import { resolvePortalUiDensity, resolvePortalUiTheme, resolveRenderedPortalTheme, } from './preferences.js';
6
6
  import { PortalBrandArtwork } from './PortalBrandArtwork.js';
7
- import { PortalCurrentSubjectSlot } from './PortalCurrentSubject.js';
7
+ import { PortalCurrentSubjectControl, PortalCurrentSubjectSlot, } from './PortalCurrentSubject.js';
8
8
  /**
9
9
  * Shared account/preferences header.
10
10
  *
11
11
  * Authentication is deliberately supplied as data and callbacks. The
12
12
  * component never imports Firebase and never treats browser state as a role.
13
13
  */
14
- export function PortalHeader({ brand, copy, accountIdentifier, languageHref, languageOptions = [], guestActionHref, accountOptions = [], onSelectAccount, menuActions = [], onSignOut, storageNamespace = 'uhc-portal', }) {
14
+ export function PortalHeader({ brand, copy, accountIdentifier, languageHref, languageOptions = [], guestActionHref, accountOptions = [], onSelectAccount, menuActions = [], onSignOut, storageNamespace = 'uhc-portal', currentSubject, }) {
15
15
  const [open, setOpen] = useState(false);
16
16
  const [density, setDensity] = useState('medium');
17
17
  const [theme, setTheme] = useState('system');
@@ -26,15 +26,17 @@ export function PortalHeader({ brand, copy, accountIdentifier, languageHref, lan
26
26
  document.documentElement.dataset.density = savedDensity;
27
27
  }, [densityKey, themeKey]);
28
28
  useEffect(() => {
29
- const media = matchMedia('(prefers-color-scheme: dark)');
29
+ const media = typeof window.matchMedia === 'function'
30
+ ? window.matchMedia('(prefers-color-scheme: dark)')
31
+ : undefined;
30
32
  const apply = () => {
31
- const rendered = resolveRenderedPortalTheme(theme, media.matches);
33
+ const rendered = resolveRenderedPortalTheme(theme, media?.matches === true);
32
34
  document.documentElement.dataset.theme = rendered;
33
35
  document.documentElement.style.colorScheme = rendered;
34
36
  };
35
37
  apply();
36
- media.addEventListener('change', apply);
37
- return () => media.removeEventListener('change', apply);
38
+ media?.addEventListener('change', apply);
39
+ return () => media?.removeEventListener('change', apply);
38
40
  }, [theme]);
39
41
  useEffect(() => {
40
42
  const closeOutside = (event) => {
@@ -61,7 +63,9 @@ export function PortalHeader({ brand, copy, accountIdentifier, languageHref, lan
61
63
  setTheme(value);
62
64
  localStorage.setItem(themeKey, value);
63
65
  }
64
- return (_jsxs("header", { className: "uhcp-header", style: brand.variables, children: [_jsxs("a", { className: "uhcp-brand", href: brand.homeHref, "aria-label": copy.home, children: [_jsx(PortalBrandArtwork, { artwork: brand.mark, className: "uhcp-brand-mark" }), _jsxs("span", { children: [_jsx("strong", { children: brand.productName }), _jsx("small", { children: brand.organizationName })] })] }), _jsx(PortalCurrentSubjectSlot, {}), _jsxs("div", { className: "uhcp-account-menu", ref: ref, children: [_jsxs("button", { type: "button", className: "uhcp-trigger", onClick: () => setOpen((value) => !value), "aria-label": copy.accountAndPreferences, "aria-expanded": open, children: [_jsx(PersonIcon, {}), accountIdentifier && _jsx("span", { className: "uhcp-account-id", children: accountIdentifier }), _jsx("span", { "aria-hidden": "true", children: "\u2304" })] }), open && (_jsxs("section", { className: "uhcp-menu", "aria-label": copy.accountAndPreferences, children: [_jsxs("div", { className: "uhcp-account-state", children: [_jsx("strong", { children: copy.accountAndPreferences }), _jsx("small", { children: accountIdentifier || copy.notSignedIn })] }), accountOptions.length > 0 && (_jsx("div", { className: "uhcp-account-options", children: accountOptions.map((option) => (_jsxs("button", { type: "button", disabled: option.disabled, "aria-pressed": Boolean(option.selected), onClick: () => {
66
+ return (_jsxs("header", { className: "uhcp-header", style: brand.variables, children: [_jsxs("a", { className: "uhcp-brand", href: brand.homeHref, "aria-label": copy.home, children: [_jsx(PortalBrandArtwork, { artwork: brand.mark, className: "uhcp-brand-mark" }), _jsxs("span", { children: [_jsx("strong", { children: brand.productName }), _jsx("small", { className: accountIdentifier ? 'uhcp-brand-account' : undefined, children: accountIdentifier || brand.organizationName })] })] }), currentSubject
67
+ ? _jsx(PortalCurrentSubjectControl, { ...currentSubject })
68
+ : _jsx(PortalCurrentSubjectSlot, {}), _jsxs("div", { className: "uhcp-account-menu", ref: ref, children: [_jsx("button", { type: "button", className: "uhcp-trigger", onClick: () => setOpen((value) => !value), "aria-label": copy.accountAndPreferences, "aria-expanded": open, children: _jsx(PersonIcon, {}) }), open && (_jsxs("section", { className: "uhcp-menu", "aria-label": copy.accountAndPreferences, children: [_jsxs("div", { className: "uhcp-account-state", children: [_jsx("strong", { children: copy.accountAndPreferences }), _jsx("small", { children: accountIdentifier || copy.notSignedIn })] }), accountOptions.length > 0 && (_jsx("div", { className: "uhcp-account-options", children: accountOptions.map((option) => (_jsxs("button", { type: "button", disabled: option.disabled, "aria-pressed": Boolean(option.selected), onClick: () => {
65
69
  onSelectAccount?.(option.id);
66
70
  setOpen(false);
67
71
  }, children: [_jsx("span", { children: option.label }), option.description && _jsx("small", { children: option.description })] }, option.id))) })), _jsx(PreferenceGroup, { label: copy.appearance, children: portalUiThemes.map((value) => (_jsxs("button", { type: "button", "aria-pressed": theme === value, onClick: () => changeTheme(value), children: [_jsx("span", { "aria-hidden": "true", children: value === 'light' ? '☀' : value === 'dark' ? '☾' : '◫' }), copy.themes[value]] }, value))) }), _jsx(PreferenceGroup, { label: copy.textSize, children: portalUiDensities.map((value) => (_jsx("button", { type: "button", "aria-pressed": density === value, onClick: () => changeDensity(value), children: value === 'small' ? 'S' : value === 'medium' ? 'M' : 'L' }, value))) }), _jsxs("div", { className: "uhcp-menu-actions", children: [languageOptions.length > 0
package/dist/styles.css CHANGED
@@ -51,7 +51,7 @@ html[data-theme='dark'] {
51
51
  }
52
52
  .uhcp-brand { align-items: center; color: var(--uhcp-text); display: flex; gap: .75rem; text-decoration: none; }
53
53
  .uhcp-brand > span:last-child { display: grid; line-height: 1.15; }
54
- .uhcp-brand small { color: var(--uhcp-muted); }
54
+ .uhcp-brand small { color: var(--uhcp-muted); max-width: min(22rem, 42vw); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
55
55
  .uhcp-brand-mark {
56
56
  align-items: center;
57
57
  display: inline-flex;
@@ -80,9 +80,10 @@ html[data-theme='dark'] .uhcp-brand-artwork-dark { display: block !important; }
80
80
  color: var(--uhcp-text);
81
81
  cursor: pointer;
82
82
  display: flex;
83
- gap: .5rem;
84
- max-width: min(19rem, 48vw);
85
- padding: .7rem .85rem;
83
+ justify-content: center;
84
+ min-height: 2.75rem;
85
+ min-width: 2.75rem;
86
+ padding: .7rem;
86
87
  }
87
88
  .uhcp-person-icon { height: 1.25rem; min-width: 1.25rem; width: 1.25rem; }
88
89
  .uhcp-account-id { font-size: .78rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@@ -709,7 +710,7 @@ html[data-theme='dark'] .uhcp-brand-artwork-dark { display: block !important; }
709
710
  .uhcp-floating-back { border-radius: 50%; bottom: 1.25rem; height: 3.5rem; left: 1.25rem; width: 3.5rem; }
710
711
  .uhcp-floating-assistant { border-radius: 999px; bottom: 1.25rem; gap: .5rem; padding: 1rem 1.25rem; right: 1.25rem; }
711
712
  @media (max-width: 620px) {
712
- .uhcp-account-id, .uhcp-brand small { display: none; }
713
+ .uhcp-brand small { max-width: 48vw; }
713
714
  .uhcp-entry-hero { grid-template-columns: 1fr; }
714
715
  .uhcp-access-grid { grid-template-columns: 1fr; }
715
716
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uhc-portal-components-react",
3
- "version": "0.2.21",
3
+ "version": "0.2.23",
4
4
  "description": "Brand-neutral React portal screens, account preferences and protected-profile UI.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",