@flamingo-stack/openframe-frontend-core 0.0.503 → 0.0.504-snapshot.20260801184836
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/dist/{chunk-LILQJ3UZ.cjs → chunk-3ENGUBJP.cjs} +1 -1
- package/dist/chunk-3ENGUBJP.cjs.map +1 -0
- package/dist/{chunk-SFJRMBJC.js → chunk-5SVNA7IU.js} +2 -2
- package/dist/{chunk-VCPTU4IW.js → chunk-7CVRJPHL.js} +7 -7
- package/dist/{chunk-FTOVZ7O2.js → chunk-7LV55KB5.js} +7 -7
- package/dist/{chunk-RR3Z52X5.cjs → chunk-7XCEZFPP.cjs} +42 -42
- package/dist/{chunk-RR3Z52X5.cjs.map → chunk-7XCEZFPP.cjs.map} +1 -1
- package/dist/{chunk-SGWPW2AO.cjs → chunk-B35VOXDB.cjs} +18 -18
- package/dist/{chunk-SGWPW2AO.cjs.map → chunk-B35VOXDB.cjs.map} +1 -1
- package/dist/{chunk-OAWFYZZT.js → chunk-BTCNF6UL.js} +3 -3
- package/dist/{chunk-VO2ZONMR.js → chunk-BZFZDP34.js} +3 -3
- package/dist/{chunk-AWDHRIL3.cjs → chunk-CZK6MOF5.cjs} +75 -75
- package/dist/{chunk-AWDHRIL3.cjs.map → chunk-CZK6MOF5.cjs.map} +1 -1
- package/dist/{chunk-JGXAVNZQ.js → chunk-DKKFHGPC.js} +2511 -1886
- package/dist/chunk-DKKFHGPC.js.map +1 -0
- package/dist/{chunk-4SR2QHMB.js → chunk-GPYW5OLH.js} +3 -3
- package/dist/{chunk-F7AD5CUU.js → chunk-H3N4P3CG.js} +3 -3
- package/dist/chunk-H3N4P3CG.js.map +1 -0
- package/dist/{chunk-CLQEBWV7.js → chunk-KW6AALQM.js} +4 -4
- package/dist/{chunk-FFXNRJ5S.cjs → chunk-LOBR7IC6.cjs} +6 -6
- package/dist/chunk-LOBR7IC6.cjs.map +1 -0
- package/dist/{chunk-4UNQMYP7.cjs → chunk-O32Y4E4R.cjs} +3 -3
- package/dist/{chunk-4UNQMYP7.cjs.map → chunk-O32Y4E4R.cjs.map} +1 -1
- package/dist/{chunk-J4T5ZBWG.cjs → chunk-P35U4IUC.cjs} +19 -19
- package/dist/{chunk-J4T5ZBWG.cjs.map → chunk-P35U4IUC.cjs.map} +1 -1
- package/dist/{chunk-HR6F6IP4.js → chunk-P6KSCGPE.js} +78 -43
- package/dist/chunk-P6KSCGPE.js.map +1 -0
- package/dist/{chunk-G43CEISB.js → chunk-Q47BQIY2.js} +4 -4
- package/dist/{chunk-7L2RVINX.cjs → chunk-QJPNWK3A.cjs} +35 -35
- package/dist/{chunk-7L2RVINX.cjs.map → chunk-QJPNWK3A.cjs.map} +1 -1
- package/dist/{chunk-3T4SMJM5.cjs → chunk-QJRKUY5B.cjs} +181 -146
- package/dist/chunk-QJRKUY5B.cjs.map +1 -0
- package/dist/{chunk-72DF3QTA.cjs → chunk-QVCWDS3E.cjs} +14 -14
- package/dist/{chunk-72DF3QTA.cjs.map → chunk-QVCWDS3E.cjs.map} +1 -1
- package/dist/{chunk-WHJGRRK2.js → chunk-QW7SW5HY.js} +52 -43
- package/dist/chunk-QW7SW5HY.js.map +1 -0
- package/dist/{chunk-UCV4LFUG.cjs → chunk-RND7HWEA.cjs} +40 -40
- package/dist/{chunk-UCV4LFUG.cjs.map → chunk-RND7HWEA.cjs.map} +1 -1
- package/dist/{chunk-VYEDTVIZ.js → chunk-S2HMBVOE.js} +4 -4
- package/dist/{chunk-5RIHLXEP.cjs → chunk-SOVH7GXF.cjs} +33 -33
- package/dist/{chunk-5RIHLXEP.cjs.map → chunk-SOVH7GXF.cjs.map} +1 -1
- package/dist/{chunk-2OXJ6NHI.cjs → chunk-SRU5IAEG.cjs} +7 -7
- package/dist/{chunk-2OXJ6NHI.cjs.map → chunk-SRU5IAEG.cjs.map} +1 -1
- package/dist/{chunk-XV5SXALY.js → chunk-SVZLM4LJ.js} +3 -3
- package/dist/{chunk-L5CI57RM.cjs → chunk-T3BF6JTQ.cjs} +52 -43
- package/dist/chunk-T3BF6JTQ.cjs.map +1 -0
- package/dist/{chunk-BZF63JXP.js → chunk-VIUP7TKR.js} +6 -6
- package/dist/{chunk-JIZJDAXI.cjs → chunk-WQDTJHVQ.cjs} +12 -12
- package/dist/{chunk-JIZJDAXI.cjs.map → chunk-WQDTJHVQ.cjs.map} +1 -1
- package/dist/{chunk-6GT557KN.js → chunk-XKIA4XVE.js} +1 -1
- package/dist/chunk-XKIA4XVE.js.map +1 -0
- package/dist/{chunk-DMXB3VE2.cjs → chunk-ZUBCKADQ.cjs} +1884 -1259
- package/dist/chunk-ZUBCKADQ.cjs.map +1 -0
- package/dist/components/case-studies/index.cjs +11 -11
- package/dist/components/case-studies/index.js +5 -5
- package/dist/components/chat/index.cjs +5 -5
- package/dist/components/chat/index.js +4 -4
- package/dist/components/contact/index.cjs +6 -6
- package/dist/components/contact/index.js +5 -5
- package/dist/components/docs/index.cjs +8 -8
- package/dist/components/docs/index.js +7 -7
- package/dist/components/embeds/index.cjs +6 -6
- package/dist/components/embeds/index.js +5 -5
- package/dist/components/faq/index.cjs +7 -7
- package/dist/components/faq/index.js +6 -6
- package/dist/components/features/index.cjs +5 -5
- package/dist/components/features/index.js +4 -4
- package/dist/components/help-center-pages/index.cjs +24 -24
- package/dist/components/help-center-pages/index.js +15 -15
- package/dist/components/index.cjs +171 -165
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +19 -13
- package/dist/components/index.js.map +1 -1
- package/dist/components/navigation/app-header.d.ts +12 -0
- package/dist/components/navigation/app-header.d.ts.map +1 -1
- package/dist/components/navigation/header-button.d.ts +5 -1
- package/dist/components/navigation/header-button.d.ts.map +1 -1
- package/dist/components/navigation/header.d.ts.map +1 -1
- package/dist/components/navigation/index.cjs +11 -5
- package/dist/components/navigation/index.cjs.map +1 -1
- package/dist/components/navigation/index.d.ts +4 -0
- package/dist/components/navigation/index.d.ts.map +1 -1
- package/dist/components/navigation/index.js +10 -4
- package/dist/components/navigation/mingo-ai-button.d.ts.map +1 -1
- package/dist/components/navigation/navigation-sidebar-item.d.ts.map +1 -1
- package/dist/components/navigation/ticket-alerts-button.d.ts +40 -0
- package/dist/components/navigation/ticket-alerts-button.d.ts.map +1 -0
- package/dist/components/navigation/top-navigation.d.ts +5 -1
- package/dist/components/navigation/top-navigation.d.ts.map +1 -1
- package/dist/components/navigation/unread-dot.d.ts +30 -0
- package/dist/components/navigation/unread-dot.d.ts.map +1 -0
- package/dist/components/onboarding-guides/index.cjs +9 -9
- package/dist/components/onboarding-guides/index.js +8 -8
- package/dist/components/related-content/index.cjs +7 -7
- package/dist/components/related-content/index.js +6 -6
- package/dist/components/tickets/help-center-card.d.ts.map +1 -1
- package/dist/components/tickets/hooks/use-ticket-actions.d.ts.map +1 -1
- package/dist/components/tickets/hooks/use-ticket-engagements.d.ts +1 -9
- package/dist/components/tickets/hooks/use-ticket-engagements.d.ts.map +1 -1
- package/dist/components/tickets/hooks/use-tickets-list.d.ts +0 -7
- package/dist/components/tickets/hooks/use-tickets-list.d.ts.map +1 -1
- package/dist/components/tickets/index.cjs +15 -8
- package/dist/components/tickets/index.cjs.map +1 -1
- package/dist/components/tickets/index.d.ts +2 -1
- package/dist/components/tickets/index.d.ts.map +1 -1
- package/dist/components/tickets/index.js +14 -7
- package/dist/components/tickets/ticket-detail-drawer.d.ts.map +1 -1
- package/dist/components/tickets/ticket-live-provider.d.ts +62 -0
- package/dist/components/tickets/ticket-live-provider.d.ts.map +1 -0
- package/dist/components/tickets/types.d.ts +62 -11
- package/dist/components/tickets/types.d.ts.map +1 -1
- package/dist/components/ui/button/button.d.ts +6 -5
- package/dist/components/ui/button/button.d.ts.map +1 -1
- package/dist/components/ui/file-manager/index.cjs +50 -50
- package/dist/components/ui/file-manager/index.js +1 -1
- package/dist/components/ui/index.cjs +5 -5
- package/dist/components/ui/index.js +4 -4
- package/dist/contexts/chat-runtime-context.d.ts +8 -0
- package/dist/contexts/chat-runtime-context.d.ts.map +1 -1
- package/dist/contexts/index.cjs +2 -2
- package/dist/contexts/index.js +1 -1
- package/dist/hooks/index.cjs +3 -3
- package/dist/hooks/index.js +2 -2
- package/dist/hooks/ui/use-header-height.d.ts.map +1 -1
- package/dist/index.cjs +13 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +12 -4
- package/dist/types/navigation.d.ts +12 -0
- package/dist/types/navigation.d.ts.map +1 -1
- package/dist/utils/index.cjs +178 -0
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +178 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/sse-subscription.d.ts +60 -0
- package/dist/utils/sse-subscription.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/components/features/time-tracker/time-tracker-header-button.tsx +1 -1
- package/src/components/navigation/app-header.tsx +41 -20
- package/src/components/navigation/header-button.tsx +18 -2
- package/src/components/navigation/header-mingo-button.tsx +2 -2
- package/src/components/navigation/header-skeleton.tsx +4 -4
- package/src/components/navigation/header.tsx +42 -21
- package/src/components/navigation/index.ts +4 -0
- package/src/components/navigation/mingo-ai-button.tsx +23 -22
- package/src/components/navigation/mobile-burger-menu.tsx +3 -3
- package/src/components/navigation/navigation-sidebar-item.tsx +2 -3
- package/src/components/navigation/ticket-alerts-button.tsx +79 -0
- package/src/components/navigation/top-navigation.tsx +17 -1
- package/src/components/navigation/unread-dot.tsx +61 -0
- package/src/components/tickets/.types.md +1 -2
- package/src/components/tickets/help-center-card.tsx +16 -0
- package/src/components/tickets/help-center-list.tsx +6 -6
- package/src/components/tickets/hooks/.use-ticket-engagements.md +4 -13
- package/src/components/tickets/hooks/.use-tickets-list.md +0 -2
- package/src/components/tickets/hooks/use-ticket-actions.ts +9 -0
- package/src/components/tickets/hooks/use-ticket-engagements.ts +4 -13
- package/src/components/tickets/hooks/use-tickets-list.ts +2 -13
- package/src/components/tickets/index.ts +10 -0
- package/src/components/tickets/ticket-detail-drawer.tsx +45 -13
- package/src/components/tickets/ticket-live-provider.tsx +506 -0
- package/src/components/tickets/types.ts +66 -11
- package/src/components/ui/button/button.tsx +165 -133
- package/src/contexts/chat-runtime-context.tsx +8 -0
- package/src/hooks/ui/use-header-height.ts +3 -1
- package/src/stories/TopNavigation.stories.tsx +20 -1
- package/src/types/navigation.ts +12 -0
- package/src/utils/index.ts +12 -0
- package/src/utils/sse-subscription.ts +289 -0
- package/dist/chunk-3T4SMJM5.cjs.map +0 -1
- package/dist/chunk-6GT557KN.js.map +0 -1
- package/dist/chunk-DMXB3VE2.cjs.map +0 -1
- package/dist/chunk-F7AD5CUU.js.map +0 -1
- package/dist/chunk-FFXNRJ5S.cjs.map +0 -1
- package/dist/chunk-HR6F6IP4.js.map +0 -1
- package/dist/chunk-JGXAVNZQ.js.map +0 -1
- package/dist/chunk-L5CI57RM.cjs.map +0 -1
- package/dist/chunk-LILQJ3UZ.cjs.map +0 -1
- package/dist/chunk-WHJGRRK2.js.map +0 -1
- /package/dist/{chunk-SFJRMBJC.js.map → chunk-5SVNA7IU.js.map} +0 -0
- /package/dist/{chunk-VCPTU4IW.js.map → chunk-7CVRJPHL.js.map} +0 -0
- /package/dist/{chunk-FTOVZ7O2.js.map → chunk-7LV55KB5.js.map} +0 -0
- /package/dist/{chunk-OAWFYZZT.js.map → chunk-BTCNF6UL.js.map} +0 -0
- /package/dist/{chunk-VO2ZONMR.js.map → chunk-BZFZDP34.js.map} +0 -0
- /package/dist/{chunk-4SR2QHMB.js.map → chunk-GPYW5OLH.js.map} +0 -0
- /package/dist/{chunk-CLQEBWV7.js.map → chunk-KW6AALQM.js.map} +0 -0
- /package/dist/{chunk-G43CEISB.js.map → chunk-Q47BQIY2.js.map} +0 -0
- /package/dist/{chunk-VYEDTVIZ.js.map → chunk-S2HMBVOE.js.map} +0 -0
- /package/dist/{chunk-XV5SXALY.js.map → chunk-SVZLM4LJ.js.map} +0 -0
- /package/dist/{chunk-BZF63JXP.js.map → chunk-VIUP7TKR.js.map} +0 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch-based SSE subscription primitive.
|
|
3
|
+
*
|
|
4
|
+
* Why not `EventSource`: cross-origin embedders authenticate via the
|
|
5
|
+
* `embedAuthedFetch` adapter's HEADERS (OpenFrame bearer), which
|
|
6
|
+
* `EventSource` cannot carry. This reads `response.body` from a normal
|
|
7
|
+
* authed fetch and parses standard `event:`/`data:` frames.
|
|
8
|
+
*
|
|
9
|
+
* Lifecycle (never-terminating by design):
|
|
10
|
+
* - Infinite reconnects with capped exponential backoff (~30s max +
|
|
11
|
+
* jitter), reset on a successful open. Do NOT copy the terminating
|
|
12
|
+
* `maxRetries → exhausted` model of the hub's server-side
|
|
13
|
+
* `RealtimeRetryManager` — a long outage must self-heal when the
|
|
14
|
+
* backend returns, because there is NO polling fallback behind this.
|
|
15
|
+
* - Liveness by silence: the server keepalives every ~15s; any bytes
|
|
16
|
+
* (INCLUDING `: keepalive` comment lines — they reset the timer
|
|
17
|
+
* before frame parsing drops them) count as life. Silence beyond
|
|
18
|
+
* `silenceTimeoutMs` (default 45s = 3× keepalive) aborts + reconnects.
|
|
19
|
+
* - Terminal responses (NO retry): any 4xx except 408/429 — the
|
|
20
|
+
* `x-block-layer` header, when readable, is logged for attribution
|
|
21
|
+
* only, never used as a retry gate (proxies may strip it); and
|
|
22
|
+
* 204 → a distinct `no-stream` status (the caller decides when to
|
|
23
|
+
* try again). Backoff-retry is for transport errors, 408/429, and 5xx.
|
|
24
|
+
*
|
|
25
|
+
* THE common SSE client for lib + hosts — exported from the `utils`
|
|
26
|
+
* barrel. Consumers: `TicketLiveProvider` (lib) and the hub's
|
|
27
|
+
* workflow/invocation stream hooks (which replaced the hub's old
|
|
28
|
+
* EventSource-based `sse-client.ts`). Finite streams (workflows,
|
|
29
|
+
* invocations) end with a terminal event and a server-side close —
|
|
30
|
+
* their handlers MUST call `close()` on the terminal event, or the
|
|
31
|
+
* never-terminating reconnect loop re-opens the finished stream.
|
|
32
|
+
*/
|
|
33
|
+
/** Transport-level status. Server-sent `status` FRAMES (`subscribed` /
|
|
34
|
+
* `retrying` / `reconnect_failed`) arrive via `onEvent('status', …)` —
|
|
35
|
+
* callers derive their "connected" notion from those, not from these. */
|
|
36
|
+
export type SseTransportStatus = 'connecting' | 'open' | 'reconnecting' | 'no-stream' | 'terminal' | 'closed';
|
|
37
|
+
export interface SseSubscriptionOptions {
|
|
38
|
+
url: string;
|
|
39
|
+
/** Defaults to `embedAuthedFetch` (adapter headers + credentials). */
|
|
40
|
+
fetchImpl?: (url: string, init?: RequestInit) => Promise<Response>;
|
|
41
|
+
/** Called per parsed frame: `eventName` from `event:` (default
|
|
42
|
+
* 'message'), `data` JSON-parsed when possible (raw string otherwise). */
|
|
43
|
+
onEvent: (eventName: string, data: unknown) => void;
|
|
44
|
+
onStatusChange?: (status: SseTransportStatus) => void;
|
|
45
|
+
/** Silence threshold before the connection is presumed dead. MUST be
|
|
46
|
+
* ≥2.5× the server keepalive cadence or jitter causes false-disconnect
|
|
47
|
+
* churn (each one costs a full server invocation). */
|
|
48
|
+
silenceTimeoutMs?: number;
|
|
49
|
+
maxBackoffMs?: number;
|
|
50
|
+
initialBackoffMs?: number;
|
|
51
|
+
}
|
|
52
|
+
export interface SseSubscription {
|
|
53
|
+
/** Permanently stop — no further reconnects, no callbacks. */
|
|
54
|
+
close: () => void;
|
|
55
|
+
/** Drop the current connection (if any) and reconnect immediately,
|
|
56
|
+
* resetting backoff. No-op after `close()`. */
|
|
57
|
+
reconnectNow: () => void;
|
|
58
|
+
}
|
|
59
|
+
export declare function createSseSubscription(options: SseSubscriptionOptions): SseSubscription;
|
|
60
|
+
//# sourceMappingURL=sse-subscription.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse-subscription.d.ts","sourceRoot":"","sources":["../../src/utils/sse-subscription.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAIH;;0EAE0E;AAC1E,MAAM,MAAM,kBAAkB,GAC1B,YAAY,GACZ,MAAM,GACN,cAAc,GACd,WAAW,GACX,UAAU,GACV,QAAQ,CAAA;AAEZ,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,MAAM,CAAA;IACX,sEAAsE;IACtE,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;IAClE;+EAC2E;IAC3E,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACnD,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAA;IACrD;;2DAEuD;IACvD,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,8DAA8D;IAC9D,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB;oDACgD;IAChD,YAAY,EAAE,MAAM,IAAI,CAAA;CACzB;AAMD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,GAAG,eAAe,CAsNtF"}
|
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@ import { BellIcon } from '../icons-v2-generated/interface/bell-icon';
|
|
|
11
11
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, SquareAvatar } from '../ui';
|
|
12
12
|
import { HeaderButton } from './header-button';
|
|
13
13
|
import { HeaderGlobalSearch } from './header-global-search';
|
|
14
|
+
import { TicketAlertsButton } from './ticket-alerts-button';
|
|
14
15
|
import { HeaderMingoButton } from './header-mingo-button';
|
|
15
16
|
import { HeaderOrganizationFilter } from './header-organization-filter';
|
|
16
17
|
import { TopNavigation } from './top-navigation';
|
|
@@ -24,6 +25,18 @@ export interface AppHeaderProps {
|
|
|
24
25
|
onOrgChange?: (id: string) => void;
|
|
25
26
|
showNotifications?: boolean;
|
|
26
27
|
unreadCount?: number;
|
|
28
|
+
/** Render the support-ticket alerts cell (`TicketAlertsButton`).
|
|
29
|
+
* Attention-only: even when true it renders nothing unless a
|
|
30
|
+
* `<TicketLiveProvider>` is mounted, the viewer is signed in, AND
|
|
31
|
+
* there are unread support replies. */
|
|
32
|
+
showTicketAlerts?: boolean;
|
|
33
|
+
/** BASE path of the tickets surface (e.g. '/help-center/tickets').
|
|
34
|
+
* The cell appends `?ticket=<id>#ticket-<id>` for the newest-unread
|
|
35
|
+
* ticket before navigating. */
|
|
36
|
+
ticketAlertsHref?: string;
|
|
37
|
+
/** Host navigation (router push) — receives the FULL computed href.
|
|
38
|
+
* Defaults to `window.location.assign`. */
|
|
39
|
+
onTicketAlerts?: (href: string) => void;
|
|
27
40
|
/** Render the time-tracker button + popup. Requires wrapping the app in `<TimeTrackerProvider>`. */
|
|
28
41
|
showTimeTracker?: boolean;
|
|
29
42
|
/** Render the "Mingo AI" launcher button (drawer-style trigger for an
|
|
@@ -110,6 +123,9 @@ export const AppHeader = React.memo(function AppHeader({
|
|
|
110
123
|
onOrgChange,
|
|
111
124
|
showNotifications,
|
|
112
125
|
unreadCount = 0,
|
|
126
|
+
showTicketAlerts = false,
|
|
127
|
+
ticketAlertsHref,
|
|
128
|
+
onTicketAlerts,
|
|
113
129
|
showTimeTracker = false,
|
|
114
130
|
showMingoAI = false,
|
|
115
131
|
onMingoAI,
|
|
@@ -145,6 +161,7 @@ export const AppHeader = React.memo(function AppHeader({
|
|
|
145
161
|
showOrganizations && 'icon-lg',
|
|
146
162
|
showTimeTracker && 'icon',
|
|
147
163
|
showNotifications && 'icon',
|
|
164
|
+
showTicketAlerts && 'icon',
|
|
148
165
|
showUser && 'icon-md',
|
|
149
166
|
showMingoAI && 'wide',
|
|
150
167
|
].filter(Boolean) as HeaderLoadingCell[])
|
|
@@ -169,7 +186,7 @@ export const AppHeader = React.memo(function AppHeader({
|
|
|
169
186
|
<HeaderButton
|
|
170
187
|
onClick={onToggleMobileMenu}
|
|
171
188
|
isActive={isMobileMenuOpen}
|
|
172
|
-
icon={isMobileMenuOpen ? <XmarkIcon className="w-
|
|
189
|
+
icon={isMobileMenuOpen ? <XmarkIcon className="w-6 h-6" /> : <Menu01Icon className="w-6 h-6" />}
|
|
173
190
|
aria-label={isMobileMenuOpen ? 'Close menu' : 'Open menu'}
|
|
174
191
|
aria-expanded={isMobileMenuOpen}
|
|
175
192
|
className="border-r border-ods-border"
|
|
@@ -195,7 +212,7 @@ export const AppHeader = React.memo(function AppHeader({
|
|
|
195
212
|
{/* Mobile: Search button */}
|
|
196
213
|
{showSearch && (
|
|
197
214
|
<HeaderButton
|
|
198
|
-
icon={<SearchIcon className="w-
|
|
215
|
+
icon={<SearchIcon className="w-6 h-6" />}
|
|
199
216
|
aria-label="Search"
|
|
200
217
|
className={cn('md:hidden', cellDivider, dimmedClass)}
|
|
201
218
|
disabled={disabled}
|
|
@@ -224,6 +241,18 @@ export const AppHeader = React.memo(function AppHeader({
|
|
|
224
241
|
/>
|
|
225
242
|
)}
|
|
226
243
|
|
|
244
|
+
{/* Support-ticket alerts (Help Center) — attention-only: renders
|
|
245
|
+
nothing unless there are unread replies (and a
|
|
246
|
+
<TicketLiveProvider> is mounted). */}
|
|
247
|
+
{showTicketAlerts && (
|
|
248
|
+
<TicketAlertsButton
|
|
249
|
+
href={ticketAlertsHref ?? '/tickets'}
|
|
250
|
+
onNavigate={onTicketAlerts}
|
|
251
|
+
disabled={disabled}
|
|
252
|
+
className={cn(cellDivider, dimmedClass)}
|
|
253
|
+
/>
|
|
254
|
+
)}
|
|
255
|
+
|
|
227
256
|
{isMdUp && showUser && (
|
|
228
257
|
<DropdownMenu>
|
|
229
258
|
<DropdownMenuTrigger asChild disabled={disabled}>
|
|
@@ -322,18 +351,10 @@ function NotificationsHeaderButton({ fallbackUnreadCount, disabled, dimmedClass
|
|
|
322
351
|
|
|
323
352
|
return (
|
|
324
353
|
<HeaderButton
|
|
325
|
-
icon={
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
<div className="relative w-4 h-4 md:w-6 md:h-6">
|
|
330
|
-
<BellIcon className="w-full h-full" />
|
|
331
|
-
{hasUnread && (
|
|
332
|
-
<span className="absolute top-0 right-0 bg-ods-warning rounded-full w-1.5 h-1.5 md:w-2 md:h-2" />
|
|
333
|
-
)}
|
|
334
|
-
</div>
|
|
335
|
-
)
|
|
336
|
-
}
|
|
354
|
+
icon={isActive ? <XmarkIcon className="w-6 h-6" /> : <BellIcon className="w-6 h-6" />}
|
|
355
|
+
// Shared dot primitive on the cell — same markup every indicator
|
|
356
|
+
// cell renders (see HeaderButton.showUnreadDot / <UnreadDot>).
|
|
357
|
+
showUnreadDot={!isActive && hasUnread}
|
|
337
358
|
aria-label={isActive ? 'Close notifications' : 'Notifications'}
|
|
338
359
|
onClick={onClick}
|
|
339
360
|
isActive={isActive}
|
|
@@ -357,11 +378,11 @@ function HeaderCellSkeleton({ className }: { className?: string }) {
|
|
|
357
378
|
return (
|
|
358
379
|
<div
|
|
359
380
|
className={cn(
|
|
360
|
-
'flex h-full w-
|
|
381
|
+
'flex h-full w-14 shrink-0 items-center justify-center border-l border-ods-border',
|
|
361
382
|
className,
|
|
362
383
|
)}
|
|
363
384
|
>
|
|
364
|
-
<div className="h-
|
|
385
|
+
<div className="h-6 w-6 animate-pulse rounded bg-ods-border" />
|
|
365
386
|
</div>
|
|
366
387
|
);
|
|
367
388
|
}
|
|
@@ -376,8 +397,8 @@ function HeaderCellSkeleton({ className }: { className?: string }) {
|
|
|
376
397
|
*/
|
|
377
398
|
function HeaderWideCellSkeleton() {
|
|
378
399
|
return (
|
|
379
|
-
<div className="flex h-full w-
|
|
380
|
-
<div className="h-
|
|
400
|
+
<div className="flex h-full w-14 shrink-0 items-center justify-center gap-2 border-l border-ods-border md:w-[140px] md:px-4">
|
|
401
|
+
<div className="h-6 w-6 shrink-0 animate-pulse rounded bg-ods-border" />
|
|
381
402
|
<div className="hidden h-5 animate-pulse rounded bg-ods-border md:block md:w-[72px]" />
|
|
382
403
|
</div>
|
|
383
404
|
);
|
|
@@ -411,8 +432,8 @@ function AppHeaderSkeleton({
|
|
|
411
432
|
aria-busy="true"
|
|
412
433
|
leading={
|
|
413
434
|
// Burger cell: mobile only, in CSS.
|
|
414
|
-
<div className="flex h-full w-
|
|
415
|
-
<div className="h-
|
|
435
|
+
<div className="flex h-full w-14 shrink-0 items-center justify-center border-r border-ods-border md:hidden">
|
|
436
|
+
<div className="h-6 w-6 animate-pulse rounded bg-ods-border" />
|
|
416
437
|
</div>
|
|
417
438
|
}
|
|
418
439
|
logo={
|
|
@@ -2,12 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
import React from 'react'
|
|
4
4
|
import { cn } from '../../utils/cn'
|
|
5
|
+
import { UnreadDot } from './unread-dot'
|
|
5
6
|
|
|
6
7
|
export interface HeaderButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
7
8
|
/** Whether the button is in active/pressed state */
|
|
8
9
|
isActive?: boolean
|
|
9
10
|
/** Icon to display in the button */
|
|
10
11
|
icon: React.ReactNode
|
|
12
|
+
/** Render the shared `UnreadDot` at the icon's top-right (the
|
|
13
|
+
* notifications-bell treatment, promoted to the cell primitive so
|
|
14
|
+
* indicator cells never copy the dot markup). */
|
|
15
|
+
showUnreadDot?: boolean
|
|
11
16
|
/** Additional class names */
|
|
12
17
|
className?: string
|
|
13
18
|
}
|
|
@@ -15,6 +20,7 @@ export interface HeaderButtonProps extends React.ButtonHTMLAttributes<HTMLButton
|
|
|
15
20
|
export function HeaderButton({
|
|
16
21
|
isActive = false,
|
|
17
22
|
icon,
|
|
23
|
+
showUnreadDot = false,
|
|
18
24
|
className,
|
|
19
25
|
...props
|
|
20
26
|
}: HeaderButtonProps) {
|
|
@@ -23,7 +29,10 @@ export function HeaderButton({
|
|
|
23
29
|
className={cn(
|
|
24
30
|
"flex items-center justify-center shrink-0",
|
|
25
31
|
"transition-colors duration-200",
|
|
26
|
-
|
|
32
|
+
// Square cell width follows the TopNavigation size (56px small /
|
|
33
|
+
// 72px big) via the shell's --top-nav-cell var; 56px fallback for
|
|
34
|
+
// standalone use.
|
|
35
|
+
"w-[var(--top-nav-cell,3.5rem)] h-full",
|
|
27
36
|
isActive
|
|
28
37
|
? "text-ods-text-primary bg-ods-bg-active"
|
|
29
38
|
: // Transparent at rest so the cell inherits the bar's background
|
|
@@ -33,7 +42,14 @@ export function HeaderButton({
|
|
|
33
42
|
)}
|
|
34
43
|
{...props}
|
|
35
44
|
>
|
|
36
|
-
{
|
|
45
|
+
{showUnreadDot ? (
|
|
46
|
+
<span className="relative inline-flex">
|
|
47
|
+
{icon}
|
|
48
|
+
<UnreadDot />
|
|
49
|
+
</span>
|
|
50
|
+
) : (
|
|
51
|
+
icon
|
|
52
|
+
)}
|
|
37
53
|
</button>
|
|
38
54
|
)
|
|
39
55
|
}
|
|
@@ -57,7 +57,7 @@ export function HeaderMingoButton({
|
|
|
57
57
|
// Mobile, drawer open: the icon-only button doubles as the close
|
|
58
58
|
// affordance, so swap the Mingo logo for an X. Match the other header
|
|
59
59
|
// icons (e.g. notifications): secondary color, w-4 h-4 → md:w-6 h-6.
|
|
60
|
-
<XmarkIcon className="w-
|
|
60
|
+
<XmarkIcon className="w-6 h-6 shrink-0 text-ods-text-secondary" />
|
|
61
61
|
) : (
|
|
62
62
|
// Outer frame follows the button's text color (currentColor); the eyes
|
|
63
63
|
// and corner block are ODS cyan.
|
|
@@ -65,7 +65,7 @@ export function HeaderMingoButton({
|
|
|
65
65
|
color="currentColor"
|
|
66
66
|
eyesColor="var(--ods-flamingo-cyan-base)"
|
|
67
67
|
cornerColor="var(--ods-flamingo-cyan-base)"
|
|
68
|
-
className="w-
|
|
68
|
+
className="w-6 h-6 shrink-0"
|
|
69
69
|
/>
|
|
70
70
|
)}
|
|
71
71
|
{!iconOnly && (
|
|
@@ -21,20 +21,20 @@ export function HeaderSkeleton({ config }: HeaderSkeletonProps) {
|
|
|
21
21
|
<div className="sticky top-0 z-[50] w-full">
|
|
22
22
|
<header
|
|
23
23
|
className={cn(
|
|
24
|
-
'flex h-
|
|
24
|
+
'flex h-[72px] w-full items-center border-b border-t border-ods-border md:border-t-0',
|
|
25
25
|
config?.backgroundColor || 'bg-ods-card',
|
|
26
26
|
config?.className,
|
|
27
27
|
)}
|
|
28
28
|
>
|
|
29
29
|
{/* Leading cells: admin toggle / burger */}
|
|
30
30
|
{showLeftActions && (
|
|
31
|
-
<div className="flex h-full w-
|
|
31
|
+
<div className="flex h-full w-[72px] items-center justify-center border-r border-ods-border">
|
|
32
32
|
<div className="h-6 w-6 animate-pulse rounded bg-ods-border" />
|
|
33
33
|
</div>
|
|
34
34
|
)}
|
|
35
35
|
{showMobileMenu && (
|
|
36
|
-
<div className="flex h-full w-
|
|
37
|
-
<div className="h-
|
|
36
|
+
<div className="flex h-full w-[72px] items-center justify-center border-r border-ods-border lg:hidden">
|
|
37
|
+
<div className="h-6 w-6 animate-pulse rounded bg-ods-border" />
|
|
38
38
|
</div>
|
|
39
39
|
)}
|
|
40
40
|
|
|
@@ -8,6 +8,7 @@ import { Menu01Icon, XmarkIcon } from '../icons-v2-generated';
|
|
|
8
8
|
import { Button } from '../ui/button';
|
|
9
9
|
import { HeaderButton } from './header-button';
|
|
10
10
|
import { MingoAiButton } from './mingo-ai-button';
|
|
11
|
+
import { TicketAlertsButton } from './ticket-alerts-button';
|
|
11
12
|
import { MOBILE_NAV_PANEL_ID } from './mobile-nav-panel';
|
|
12
13
|
import { TopNavigation } from './top-navigation';
|
|
13
14
|
|
|
@@ -135,11 +136,12 @@ export function Header({ config, platform }: HeaderProps) {
|
|
|
135
136
|
[item.id]: !prev[item.id],
|
|
136
137
|
}));
|
|
137
138
|
}}
|
|
138
|
-
size="
|
|
139
|
+
size="default"
|
|
140
|
+
font="regular"
|
|
139
141
|
className={cn(
|
|
140
|
-
// Top-level nav links:
|
|
141
|
-
// (
|
|
142
|
-
|
|
142
|
+
// Top-level nav links: the default-size button with the h4
|
|
143
|
+
// (DM Sans 500) `font="regular"` label — Figma 133-740, added
|
|
144
|
+
// for navigation; dropdown children stay on the h6 step.
|
|
143
145
|
item.isActive && 'bg-ods-bg-hover', // Active items get subtle gray background
|
|
144
146
|
isOpen && 'bg-ods-bg-hover', // Open dropdowns get gray background
|
|
145
147
|
item.className,
|
|
@@ -227,9 +229,9 @@ export function Header({ config, platform }: HeaderProps) {
|
|
|
227
229
|
onClick={item.onClick} // Only for non-navigation actions
|
|
228
230
|
leftIcon={item.icon}
|
|
229
231
|
rightIcon={item.badge}
|
|
230
|
-
size="
|
|
232
|
+
size="default"
|
|
233
|
+
font="regular"
|
|
231
234
|
className={cn(
|
|
232
|
-
'text-h3 font-bold tracking-[-0.36px]',
|
|
233
235
|
'hover:bg-ods-bg-hover focus:bg-ods-bg-hover',
|
|
234
236
|
'whitespace-nowrap',
|
|
235
237
|
'text-ods-text-primary', // All items use primary text color
|
|
@@ -251,9 +253,9 @@ export function Header({ config, platform }: HeaderProps) {
|
|
|
251
253
|
onClick={item.onClick}
|
|
252
254
|
leftIcon={item.icon}
|
|
253
255
|
rightIcon={item.badge}
|
|
254
|
-
size="
|
|
256
|
+
size="default"
|
|
257
|
+
font="regular"
|
|
255
258
|
className={cn(
|
|
256
|
-
'text-h3 font-bold tracking-[-0.36px]',
|
|
257
259
|
'hover:bg-ods-bg-hover focus:bg-ods-bg-hover',
|
|
258
260
|
'whitespace-nowrap',
|
|
259
261
|
'text-ods-text-primary', // All items use primary text color
|
|
@@ -301,15 +303,16 @@ export function Header({ config, platform }: HeaderProps) {
|
|
|
301
303
|
style={config.style}
|
|
302
304
|
backgroundClassName={config.backgroundColor}
|
|
303
305
|
centerBreakpoint="lg"
|
|
306
|
+
size="big"
|
|
304
307
|
leading={
|
|
305
308
|
<>
|
|
306
309
|
{/* Length-guarded: platform configs often pass `left: []`, and an
|
|
307
310
|
empty array is truthy — without the guard the cell would render
|
|
308
311
|
as a bare divider + padding. */}
|
|
312
|
+
{/* No padding on the wrapper: leading cells (HeaderButton-based
|
|
313
|
+
admin toggles) size themselves to the bar's square cell. */}
|
|
309
314
|
{!!config.actions?.left?.length && (
|
|
310
|
-
<div className="flex h-full items-center border-r border-ods-border
|
|
311
|
-
{config.actions.left}
|
|
312
|
-
</div>
|
|
315
|
+
<div className="flex h-full items-center border-r border-ods-border">{config.actions.left}</div>
|
|
313
316
|
)}
|
|
314
317
|
{/* Mobile/tablet menu toggle — a leading cell per the ODS spec
|
|
315
318
|
(banded with the nav breakpoint `lg` so the desktop nav and the
|
|
@@ -328,8 +331,8 @@ export function Header({ config, platform }: HeaderProps) {
|
|
|
328
331
|
aria-controls={config.mobile?.isOpen ? MOBILE_NAV_PANEL_ID : undefined}
|
|
329
332
|
icon={
|
|
330
333
|
config.mobile?.isOpen
|
|
331
|
-
? config.mobile?.closeIcon || <XmarkIcon className="w-
|
|
332
|
-
: config.mobile?.menuIcon || <Menu01Icon className="w-
|
|
334
|
+
? config.mobile?.closeIcon || <XmarkIcon className="w-6 h-6" />
|
|
335
|
+
: config.mobile?.menuIcon || <Menu01Icon className="w-6 h-6" />
|
|
333
336
|
}
|
|
334
337
|
/>
|
|
335
338
|
)}
|
|
@@ -340,7 +343,11 @@ export function Header({ config, platform }: HeaderProps) {
|
|
|
340
343
|
{config.logo.element}
|
|
341
344
|
</Link>
|
|
342
345
|
}
|
|
343
|
-
|
|
346
|
+
// Logo inset for the hub sites: 12px on mobile (the shell's base p-m),
|
|
347
|
+
// 16px on tablet (mf token; the shell default is pl-l = 24px there),
|
|
348
|
+
// desktop unchanged — pl-xxl, collapsed to pl-l when an always-visible
|
|
349
|
+
// leading cell sits before the logo.
|
|
350
|
+
logoClassName={cn('md:pl-[var(--spacing-system-mf)]', hasLeftCells && 'lg:pl-[var(--spacing-system-l)]')}
|
|
344
351
|
center={
|
|
345
352
|
hasNav ? (
|
|
346
353
|
<nav
|
|
@@ -378,13 +385,27 @@ export function Header({ config, platform }: HeaderProps) {
|
|
|
378
385
|
}
|
|
379
386
|
ctaClassName="gap-3"
|
|
380
387
|
sideActions={
|
|
381
|
-
config.mingo?.enabled ? (
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
+
config.tickets || config.mingo?.enabled ? (
|
|
389
|
+
<>
|
|
390
|
+
{/* Support-ticket alerts cell — before Mingo, flush cell row.
|
|
391
|
+
Attention-only: renders nothing unless there are unread
|
|
392
|
+
replies (and the host mounted <TicketLiveProvider>). */}
|
|
393
|
+
{config.tickets && (
|
|
394
|
+
<TicketAlertsButton
|
|
395
|
+
href={config.tickets.href}
|
|
396
|
+
onNavigate={config.tickets.onClick}
|
|
397
|
+
className="border-l border-ods-border"
|
|
398
|
+
/>
|
|
399
|
+
)}
|
|
400
|
+
{config.mingo?.enabled && (
|
|
401
|
+
<MingoAiButton
|
|
402
|
+
source={config.mingo.source}
|
|
403
|
+
icon={config.mingo.icon}
|
|
404
|
+
label={config.mingo.label}
|
|
405
|
+
className={config.mingo.className}
|
|
406
|
+
/>
|
|
407
|
+
)}
|
|
408
|
+
</>
|
|
388
409
|
) : undefined
|
|
389
410
|
}
|
|
390
411
|
/>
|
|
@@ -49,6 +49,10 @@ export type { MobileBurgerMenuProps } from './mobile-burger-menu'
|
|
|
49
49
|
|
|
50
50
|
export { HeaderButton } from './header-button'
|
|
51
51
|
export type { HeaderButtonProps } from './header-button'
|
|
52
|
+
export { UnreadDot, UnreadCountBadge } from './unread-dot'
|
|
53
|
+
export type { UnreadDotProps, UnreadCountBadgeProps } from './unread-dot'
|
|
54
|
+
export { TicketAlertsButton } from './ticket-alerts-button'
|
|
55
|
+
export type { TicketAlertsButtonProps } from './ticket-alerts-button'
|
|
52
56
|
|
|
53
57
|
export { TopNavigation } from './top-navigation'
|
|
54
58
|
export type { TopNavigationProps, TopNavigationCenterBreakpoint } from './top-navigation'
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
'use client'
|
|
1
|
+
'use client';
|
|
2
2
|
|
|
3
|
-
import React from 'react'
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { cn } from '../../utils';
|
|
5
|
+
import { MingoIcon } from '../icons';
|
|
6
6
|
|
|
7
7
|
export interface MingoAiButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
8
|
-
source?: string
|
|
8
|
+
source?: string;
|
|
9
9
|
/** The platform's Mingo identity glyph — pass the SAME server-configured
|
|
10
10
|
* icon the chat panel renders (host-side: `EntityIcon` fed by the admin
|
|
11
11
|
* `assistantIcon`), so the launcher and the panel can never diverge.
|
|
12
12
|
* Falls back to the packaged Mingo mark when the server has none. */
|
|
13
|
-
icon?: React.ReactNode
|
|
13
|
+
icon?: React.ReactNode;
|
|
14
14
|
/** The launcher's wordmark + aria-label — pass the server-configured
|
|
15
15
|
* assistant name (same `assistantName` the chat panel shows) so the
|
|
16
16
|
* launcher never hardcodes an identity the admin has renamed. */
|
|
17
|
-
label?: string
|
|
17
|
+
label?: string;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -32,7 +32,7 @@ export interface MingoAiButtonProps extends React.ButtonHTMLAttributes<HTMLButto
|
|
|
32
32
|
* icon-only collapse that `Button` cannot express (same precedent as
|
|
33
33
|
* `header-mingo-button.tsx`).
|
|
34
34
|
*/
|
|
35
|
-
const MINGO_ACCENT = 'var(--ods-flamingo-cyan-base)'
|
|
35
|
+
const MINGO_ACCENT = 'var(--ods-flamingo-cyan-base)';
|
|
36
36
|
|
|
37
37
|
export function MingoAiButton({ source, icon, label = 'Mingo AI', className, onClick, ...props }: MingoAiButtonProps) {
|
|
38
38
|
return (
|
|
@@ -40,12 +40,12 @@ export function MingoAiButton({ source, icon, label = 'Mingo AI', className, onC
|
|
|
40
40
|
{...props}
|
|
41
41
|
type="button"
|
|
42
42
|
aria-label={label}
|
|
43
|
-
onClick={
|
|
43
|
+
onClick={e => {
|
|
44
44
|
// Coalesce to '' so a source-less mount still matches EmbeddableChat's
|
|
45
45
|
// own `runtime.source ?? ''` comparison (undefined !== '' would make
|
|
46
46
|
// the panel silently ignore the event).
|
|
47
|
-
window.dispatchEvent(new CustomEvent('ask-ai:open', { detail: { source: source ?? '' } }))
|
|
48
|
-
onClick?.(e)
|
|
47
|
+
window.dispatchEvent(new CustomEvent('ask-ai:open', { detail: { source: source ?? '' } }));
|
|
48
|
+
onClick?.(e);
|
|
49
49
|
}}
|
|
50
50
|
className={cn(
|
|
51
51
|
// Unified ODS top-navigation cell (Figma 2797-6808 desktop /
|
|
@@ -58,11 +58,11 @@ export function MingoAiButton({ source, icon, label = 'Mingo AI', className, onC
|
|
|
58
58
|
className,
|
|
59
59
|
)}
|
|
60
60
|
>
|
|
61
|
-
{/* Inner box = 1:1 twin of the
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
<span className="relative flex h-
|
|
61
|
+
{/* Inner box = 1:1 twin of the DEFAULT-size Button geometry (rounded-md,
|
|
62
|
+
h-11 md:h-12, px-m) — the same footprint as the header's "Try for
|
|
63
|
+
Free" CTA — so the animated ring traces exactly where a full button
|
|
64
|
+
border would sit inside the 72px bar. */}
|
|
65
|
+
<span className="relative flex h-11 items-center gap-[var(--spacing-system-xsf)] rounded-md px-[var(--spacing-system-m)] md:h-12">
|
|
66
66
|
{/* AI edge light (Apple-Intelligence-style): a rotating accent-
|
|
67
67
|
gradient arc clipped to a 3px ring on the box outline via CSS
|
|
68
68
|
mask (.mingo-edge-frame) — NO opaque cover, so the launcher
|
|
@@ -92,13 +92,14 @@ export function MingoAiButton({ source, icon, label = 'Mingo AI', className, onC
|
|
|
92
92
|
/>
|
|
93
93
|
)}
|
|
94
94
|
{/* Wordmark collapses below md — the mobile cell is icon-only per
|
|
95
|
-
spec.
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
spec. h3 bold, matching the default-size CTA label next to it
|
|
96
|
+
(Figma 2936-6825). */}
|
|
97
|
+
{/* Bare `text-h3` — the exact composite the default-size Button label
|
|
98
|
+
uses (bold 700 and -0.02em tracking are built into the utility). */}
|
|
99
|
+
<span className="relative hidden whitespace-nowrap text-h3 text-ods-text-primary md:inline">{label}</span>
|
|
99
100
|
</span>
|
|
100
101
|
</button>
|
|
101
|
-
)
|
|
102
|
+
);
|
|
102
103
|
}
|
|
103
104
|
|
|
104
|
-
export default MingoAiButton
|
|
105
|
+
export default MingoAiButton;
|
|
@@ -9,8 +9,8 @@ import { Logout02Icon, PenEditIcon, UserSearchIcon } from '../icons-v2-generated
|
|
|
9
9
|
import { Button, SquareAvatar } from '../ui'
|
|
10
10
|
import { OVERLAY_BACKDROP_CLASS } from '../ui/drawer'
|
|
11
11
|
|
|
12
|
-
// Header height constant
|
|
13
|
-
const HEADER_HEIGHT =
|
|
12
|
+
// Header height constant — the unified top-navigation `small` bar (56px on all screens)
|
|
13
|
+
const HEADER_HEIGHT = 56
|
|
14
14
|
|
|
15
15
|
export interface MobileBurgerMenuProps {
|
|
16
16
|
/** Whether the menu is open */
|
|
@@ -188,7 +188,7 @@ export const MobileBurgerMenu = React.memo(function MobileBurgerMenu({
|
|
|
188
188
|
fallback={user.userName}
|
|
189
189
|
size="lg"
|
|
190
190
|
variant="round"
|
|
191
|
-
className="w-
|
|
191
|
+
className="w-14 h-14 shrink-0"
|
|
192
192
|
/>
|
|
193
193
|
|
|
194
194
|
{/* User Info */}
|
|
@@ -4,6 +4,7 @@ import { cloneElement, memo, type ReactElement, type ReactNode } from 'react'
|
|
|
4
4
|
import Link from '../../embed-shims/next-link'
|
|
5
5
|
import { NavigationSidebarItem } from '../../types/navigation'
|
|
6
6
|
import { cn } from '../../utils'
|
|
7
|
+
import { UnreadDot } from './unread-dot'
|
|
7
8
|
|
|
8
9
|
interface IconProps {
|
|
9
10
|
color?: string
|
|
@@ -82,9 +83,7 @@ export const NavigationSidebarItemButton = memo(function NavigationSidebarItemBu
|
|
|
82
83
|
{cloneElement(item.icon as ReactElement<IconProps>, {
|
|
83
84
|
color: isActive && !disabled ? "text-ods-accent" : "text-ods-text-secondary",
|
|
84
85
|
})}
|
|
85
|
-
{hasUnread && !showLabel &&
|
|
86
|
-
<span className="absolute top-0 right-0 bg-ods-warning rounded-full w-2 h-2" />
|
|
87
|
-
)}
|
|
86
|
+
{hasUnread && !showLabel && <UnreadDot size="fixed" />}
|
|
88
87
|
</div>
|
|
89
88
|
|
|
90
89
|
<span
|