@flamingo-stack/openframe-frontend-core 0.0.766 → 0.0.768
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/build/export-map.json +1 -1
- package/dist/{chunk-REJMFXGD.cjs → chunk-2MND64QJ.cjs} +46 -46
- package/dist/{chunk-F7R3U5EN.cjs → chunk-2TS3FEPQ.cjs} +4 -4
- package/dist/{chunk-WO3XO27C.cjs → chunk-2ZYCYLPL.cjs} +5 -5
- package/dist/{chunk-DGNBXZXT.cjs → chunk-4EJU462O.cjs} +57 -30
- package/dist/{chunk-JKQVKWLB.cjs → chunk-57KIBZFQ.cjs} +76 -76
- package/dist/{chunk-VYYLYZUC.cjs → chunk-5O6PHVKO.cjs} +4 -4
- package/dist/{chunk-XZ6DKGT4.cjs → chunk-5QP3K2CE.cjs} +5 -5
- package/dist/{chunk-NJMZ3N7Q.cjs → chunk-6EEE76VU.cjs} +24 -24
- package/dist/{chunk-YRFWOJF4.cjs → chunk-BLTNKLAX.cjs} +4 -4
- package/dist/{chunk-7U3ELNGC.cjs → chunk-DE7U72NZ.cjs} +26 -26
- package/dist/{chunk-GTONIRJU.cjs → chunk-EHJE3UWZ.cjs} +10 -16
- package/dist/{chunk-HNQRY6RU.cjs → chunk-GGORS7G6.cjs} +13 -13
- package/dist/{chunk-FZB4RMRK.cjs → chunk-HQF62LAU.cjs} +33 -33
- package/dist/{chunk-JTSUAHDL.cjs → chunk-HY6BCZH7.cjs} +4 -2
- package/dist/{chunk-MPTMT773.cjs → chunk-LJT2KRE2.cjs} +14 -14
- package/dist/{chunk-NBVZIHGS.cjs → chunk-MYA6NR2C.cjs} +2 -2
- package/dist/{chunk-NF3PBWDJ.cjs → chunk-NPVYID2L.cjs} +80 -80
- package/dist/{chunk-RTBYQWAV.cjs → chunk-OEVNX4OR.cjs} +44 -44
- package/dist/{chunk-UW7UWUZU.cjs → chunk-OGHLIUF4.cjs} +41 -41
- package/dist/{chunk-JGWN6GH5.cjs → chunk-PFY23KN7.cjs} +6 -6
- package/dist/{chunk-ZRDMGMY7.cjs → chunk-QS4G7CHU.cjs} +2 -2
- package/dist/{chunk-7XFVUUVZ.cjs → chunk-S4QS5E5A.cjs} +12 -12
- package/dist/{chunk-VRDHAR3X.cjs → chunk-STHQJNND.cjs} +517 -551
- package/dist/{chunk-INI4ADPT.cjs → chunk-U6UIG4OP.cjs} +31 -31
- package/dist/{chunk-7VI7TOGI.cjs → chunk-UBPSA3RM.cjs} +219 -181
- package/dist/{chunk-K3IHNXR4.cjs → chunk-VADRKMLE.cjs} +3 -3
- package/dist/{chunk-AAECWUNV.cjs → chunk-VFUMM6JC.cjs} +19 -11
- package/dist/{chunk-X7ECVSVN.cjs → chunk-VVBRYOSI.cjs} +19 -19
- package/dist/{chunk-ZV45VS6K.cjs → chunk-XI2CHCOD.cjs} +34 -43
- package/dist/{chunk-RMUSPTJN.cjs → chunk-ZVNL5OK6.cjs} +5 -5
- package/dist/{chunk-YL6ZB2SR.cjs → chunk-ZXFOMNI5.cjs} +183 -183
- package/dist/{cloud-tenants-U2KMEIO7.cjs → cloud-tenants-I5HABFNZ.cjs} +13 -13
- package/dist/components/case-studies/index.cjs +29 -29
- package/dist/components/case-studies/index.d.ts +1 -1
- package/dist/components/case-studies/index.d.ts.map +1 -1
- package/dist/components/case-studies/index.js +5 -1
- package/dist/components/chat/entity-cards/campaign-card-admin.d.ts.map +1 -1
- package/dist/components/chat/entity-cards/investor-update-card.d.ts.map +1 -1
- package/dist/components/chat/entity-cards/investor-update-card.js +9 -1
- package/dist/components/chat/hooks/use-ask-prompts.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +9 -9
- package/dist/components/chat/index.d.ts.map +1 -1
- package/dist/components/chat/index.js +18 -4
- package/dist/components/contact/index.cjs +14 -14
- package/dist/components/docs/index.cjs +17 -17
- package/dist/components/embeds/index.cjs +14 -14
- package/dist/components/embeds/rich-markdown-runtime.d.ts +4 -1
- package/dist/components/embeds/rich-markdown-runtime.d.ts.map +1 -1
- package/dist/components/embeds/rich-markdown-runtime.js +6 -5
- package/dist/components/faq/index.cjs +15 -15
- package/dist/components/features/cards-strip.d.ts.map +1 -1
- package/dist/components/features/command-box.d.ts.map +1 -1
- package/dist/components/features/command-box.js +2 -19
- package/dist/components/features/index.cjs +13 -13
- package/dist/components/features/paths-display.d.ts.map +1 -1
- package/dist/components/features/remote-session/remote-desktop-chat-message-row.d.ts.map +1 -1
- package/dist/components/features/remote-session/remote-desktop-chat-message-row.js +1 -10
- package/dist/components/features/walkthrough-video.d.ts.map +1 -1
- package/dist/components/features/walkthrough-video.js +3 -1
- package/dist/components/help-center-pages/index.cjs +98 -98
- package/dist/components/icon-display.d.ts.map +1 -1
- package/dist/components/icon-display.js +9 -1
- package/dist/components/index.cjs +74 -74
- package/dist/components/interactive-wrapper.d.ts.map +1 -1
- package/dist/components/interactive-wrapper.js +7 -1
- package/dist/components/layout/page-heading.d.ts.map +1 -1
- package/dist/components/layout/page-heading.js +8 -1
- package/dist/components/meeting-scheduler/directory.d.ts.map +1 -1
- package/dist/components/meeting-scheduler/index.cjs +73 -73
- package/dist/components/navigation/index.cjs +16 -16
- package/dist/components/onboarding-guides/index.cjs +16 -16
- package/dist/components/product-demo/index.cjs +24 -24
- package/dist/components/related-content/index.cjs +7 -7
- package/dist/components/results-count.d.ts.map +1 -1
- package/dist/components/results-count.js +2 -2
- package/dist/components/tickets/index.cjs +19 -19
- package/dist/components/ui/avatar-stack.d.ts.map +1 -1
- package/dist/components/ui/avatar-stack.js +17 -9
- package/dist/components/ui/cursor-pagination.d.ts.map +1 -1
- package/dist/components/ui/data-table/data-table-row-count.js +1 -1
- package/dist/components/ui/feature-card.d.ts.map +1 -1
- package/dist/components/ui/feature-list.d.ts.map +1 -1
- package/dist/components/ui/feature-list.js +7 -21
- package/dist/components/ui/incident-feed.d.ts +1 -1
- package/dist/components/ui/incident-feed.d.ts.map +1 -1
- package/dist/components/ui/incident-feed.js +1 -7
- package/dist/components/ui/index.cjs +13 -13
- package/dist/components/ui/leader-row.d.ts.map +1 -1
- package/dist/components/ui/markdown/rich/embed-overrides.d.ts.map +1 -1
- package/dist/components/ui/markdown/rich/embed-overrides.js +10 -1
- package/dist/components/ui/product-screen-frame.d.ts.map +1 -1
- package/dist/components/ui/product-screen-frame.js +4 -13
- package/dist/components/ui/status-badge.d.ts.map +1 -1
- package/dist/components/ui/status-badge.js +5 -1
- package/dist/components/ui/status-line.d.ts.map +1 -1
- package/dist/components/ui/status-line.js +1 -7
- package/dist/components/ui/strip-card.d.ts.map +1 -1
- package/dist/components/ui/tag.d.ts.map +1 -1
- package/dist/{devices-2D3XSIIE.cjs → devices-OS26UG7Y.cjs} +14 -14
- package/dist/{embeddable-chat-YL6ZSTDJ.cjs → embeddable-chat-BXEK3SPJ.cjs} +83 -83
- package/dist/index.cjs +22 -18
- package/dist/{knowledge-AJ36PMK2.cjs → knowledge-JD2OFKEL.cjs} +14 -14
- package/dist/{logs-DNE44ACE.cjs → logs-KGKNFROP.cjs} +21 -21
- package/dist/{policies-AJHSP6ZY.cjs → policies-J2XL6XQK.cjs} +17 -17
- package/dist/{remote-session-PPA5SXX7.cjs → remote-session-RHQ2K27H.cjs} +7 -7
- package/dist/{scripts-4YO2QL36.cjs → scripts-MJONVIWL.cjs} +20 -20
- package/dist/{software-update-KMI2EOO3.cjs → software-update-VSMTHJR4.cjs} +17 -17
- package/dist/{tickets-board-G3WBH4SV.cjs → tickets-board-YT25IX3O.cjs} +8 -8
- package/dist/utils/after-page-load.d.ts +8 -0
- package/dist/utils/after-page-load.d.ts.map +1 -0
- package/dist/utils/after-page-load.js +24 -0
- package/dist/utils/format.d.ts +13 -3
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/format.js +4 -2
- package/dist/utils/index.cjs +27 -1
- package/dist/utils/index.d.ts +2 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -0
- package/package.json +1 -1
- package/src/components/case-studies/entity-quotes-panel.tsx +18 -18
- package/src/components/case-studies/index.ts +8 -1
- package/src/components/chat/entity-cards/campaign-card-admin.tsx +1 -3
- package/src/components/chat/entity-cards/investor-update-card.tsx +6 -1
- package/src/components/chat/hooks/use-ask-prompts.ts +1 -2
- package/src/components/chat/index.ts +22 -5
- package/src/components/embeds/rich-markdown-runtime.tsx +10 -6
- package/src/components/features/cards-strip.tsx +3 -2
- package/src/components/features/command-box.tsx +2 -11
- package/src/components/features/paths-display.tsx +4 -1
- package/src/components/features/remote-session/remote-desktop-chat-message-row.tsx +1 -6
- package/src/components/features/walkthrough-video.tsx +8 -1
- package/src/components/icon-display.tsx +6 -1
- package/src/components/interactive-wrapper.tsx +4 -1
- package/src/components/layout/page-heading.tsx +15 -3
- package/src/components/meeting-scheduler/directory.tsx +2 -1
- package/src/components/results-count.tsx +3 -2
- package/src/components/ui/avatar-stack.tsx +5 -1
- package/src/components/ui/button/button-styles.ts +0 -1
- package/src/components/ui/cursor-pagination.tsx +8 -1
- package/src/components/ui/data-table/data-table-row-count.tsx +1 -1
- package/src/components/ui/feature-card.tsx +2 -10
- package/src/components/ui/feature-list.tsx +2 -8
- package/src/components/ui/incident-feed.tsx +2 -13
- package/src/components/ui/leader-row.tsx +0 -1
- package/src/components/ui/markdown/rich/embed-overrides.tsx +11 -1
- package/src/components/ui/product-screen-frame.tsx +1 -6
- package/src/components/ui/status-badge.tsx +5 -1
- package/src/components/ui/status-line.tsx +1 -4
- package/src/components/ui/strip-card.tsx +1 -3
- package/src/components/ui/tag.tsx +3 -1
- package/src/utils/after-page-load.ts +30 -0
- package/src/utils/format.ts +16 -4
- package/src/utils/index.ts +2 -0
|
@@ -260,11 +260,7 @@ function FeatureCardGridView({
|
|
|
260
260
|
</span>
|
|
261
261
|
)}
|
|
262
262
|
<h3 className={`text-ods-text-primary text-h2 ${titleClassName}`}>
|
|
263
|
-
{loading ?
|
|
264
|
-
<TextLineSkeleton type="inherit" as="span" className="w-1/2" />
|
|
265
|
-
) : (
|
|
266
|
-
item.title
|
|
267
|
-
)}
|
|
263
|
+
{loading ? <TextLineSkeleton type="inherit" as="span" className="w-1/2" /> : item.title}
|
|
268
264
|
</h3>
|
|
269
265
|
{item.subtitle && (
|
|
270
266
|
<p className={`text-h4 ${accentClassName} ${subtitleClassName}`}>
|
|
@@ -294,11 +290,7 @@ function FeatureCardGridView({
|
|
|
294
290
|
</h3>
|
|
295
291
|
{item.subtitle && (
|
|
296
292
|
<p className={`text-h4 ${accentClassName} ${subtitleClassName}`}>
|
|
297
|
-
{loading ?
|
|
298
|
-
<TextLineSkeleton type="inherit" as="span" className="w-3/4" />
|
|
299
|
-
) : (
|
|
300
|
-
item.subtitle
|
|
301
|
-
)}
|
|
293
|
+
{loading ? <TextLineSkeleton type="inherit" as="span" className="w-3/4" /> : item.subtitle}
|
|
302
294
|
</p>
|
|
303
295
|
)}
|
|
304
296
|
</div>
|
|
@@ -116,14 +116,8 @@ export function FeatureList({
|
|
|
116
116
|
return (
|
|
117
117
|
<div className={`${BOXED_LIST_CLASS} ${className}`}>
|
|
118
118
|
{items.map((item, index) => (
|
|
119
|
-
<div
|
|
120
|
-
|
|
121
|
-
className={`${BOXED_ROW_CLASS} ${index < items.length - 1 ? BOXED_ROW_DIVIDER_CLASS : ''}`}
|
|
122
|
-
>
|
|
123
|
-
<div
|
|
124
|
-
className={ICON_BOX_CLASS}
|
|
125
|
-
style={{ width: iconBoxSize, height: iconBoxSize }}
|
|
126
|
-
>
|
|
119
|
+
<div key={index} className={`${BOXED_ROW_CLASS} ${index < items.length - 1 ? BOXED_ROW_DIVIDER_CLASS : ''}`}>
|
|
120
|
+
<div className={ICON_BOX_CLASS} style={{ width: iconBoxSize, height: iconBoxSize }}>
|
|
127
121
|
{item.icon}
|
|
128
122
|
</div>
|
|
129
123
|
<div className={BOXED_TEXT_CLASS}>
|
|
@@ -69,11 +69,7 @@ const SOURCE_TAG_CLASS =
|
|
|
69
69
|
'inline-flex shrink-0 rounded-md border border-ods-border px-2 py-1 text-ods-text-secondary text-h5';
|
|
70
70
|
|
|
71
71
|
function SourceTag({ children }: { children: ReactNode }) {
|
|
72
|
-
return
|
|
73
|
-
<span className={SOURCE_TAG_CLASS}>
|
|
74
|
-
{children}
|
|
75
|
-
</span>
|
|
76
|
-
);
|
|
72
|
+
return <span className={SOURCE_TAG_CLASS}>{children}</span>;
|
|
77
73
|
}
|
|
78
74
|
|
|
79
75
|
/** The row above an item's title: where it was found and when. Nothing to show: no row. */
|
|
@@ -93,13 +89,7 @@ function ItemMeta({ item }: { item: IncidentFeedItem }) {
|
|
|
93
89
|
* to a single row each. An item that is waiting on a person stays open even
|
|
94
90
|
* after newer ones arrive, because it is still the thing to look at.
|
|
95
91
|
*/
|
|
96
|
-
export function IncidentFeed({
|
|
97
|
-
items,
|
|
98
|
-
statusLabels,
|
|
99
|
-
emptyLabel,
|
|
100
|
-
anchor = 'top',
|
|
101
|
-
className,
|
|
102
|
-
}: IncidentFeedProps) {
|
|
92
|
+
export function IncidentFeed({ items, statusLabels, emptyLabel, anchor = 'top', className }: IncidentFeedProps) {
|
|
103
93
|
const lastIndex = items.length - 1;
|
|
104
94
|
return (
|
|
105
95
|
<div className={cn(FEED_CLASS, anchor === 'bottom' && FEED_BOTTOM_CLASS, className)}>
|
|
@@ -146,4 +136,3 @@ export function IncidentFeed({
|
|
|
146
136
|
</div>
|
|
147
137
|
);
|
|
148
138
|
}
|
|
149
|
-
|
|
@@ -45,7 +45,6 @@ export function LeaderRow({ label, value, status, as: Row = 'div', className }:
|
|
|
45
45
|
);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
|
|
49
48
|
/**
|
|
50
49
|
* A row while it is read: the label (drawn as it is when the page states it, else a bar), the hairline drawn for
|
|
51
50
|
* real, a bar for the value, a status box. Every bar is one line of the ROW's type step, so a row sized with
|
|
@@ -18,7 +18,7 @@ import { MarkdownImage } from '../../../embeds/markdown-image';
|
|
|
18
18
|
import { OGLinkPreview, OGLinkErrorBoundary } from '../../../embeds/og-link-preview';
|
|
19
19
|
import { RedditEmbedClient } from '../../../embeds/reddit-embed-client';
|
|
20
20
|
import { TwitterEmbedClient } from '../../../embeds/twitter-embed-client';
|
|
21
|
-
import { Video } from '../../../features/video';
|
|
21
|
+
import { extractYouTubeId, Video } from '../../../features/video';
|
|
22
22
|
import { buildStandardLeafRenderers, hasRenderableSrc, type MdRenderProps } from '../base-components';
|
|
23
23
|
import type { TextSizeElement } from '../text-size';
|
|
24
24
|
|
|
@@ -224,6 +224,16 @@ export function buildRichEmbedOverrides({
|
|
|
224
224
|
return <MarkdownImage src={src.trim()} alt={alt} />;
|
|
225
225
|
},
|
|
226
226
|
|
|
227
|
+
// Raw <iframe> tags in stored content (a pasted YouTube embed). A YouTube one becomes the same click-to-play
|
|
228
|
+
// facade as the `youtube-embed` fence: the raw iframe loaded YouTube's whole player (about 470 KB of script) with
|
|
229
|
+
// the page, on every post that carried one (CU-17tkuw5xp4q). Any other iframe loads when it nears the screen.
|
|
230
|
+
iframe: ({ node: _node, src, ...rest }: MdRenderProps<'iframe'>) => {
|
|
231
|
+
if (!hasRenderableSrc(src)) return null;
|
|
232
|
+
const url = src.trim();
|
|
233
|
+
if (extractYouTubeId(url)) return <Video kind="youtube" url={url} />;
|
|
234
|
+
return <iframe {...rest} src={url} loading="lazy" />;
|
|
235
|
+
},
|
|
236
|
+
|
|
227
237
|
// Raw <video> tags in stored content (blog publisher injection) route
|
|
228
238
|
// to the <Video> SSOT (MuxPlayer) so HLS manifests play everywhere.
|
|
229
239
|
video: ({ src, poster, className }: MdRenderProps<'video'>) => {
|
|
@@ -117,12 +117,7 @@ export function ProductScreenFrame({
|
|
|
117
117
|
>
|
|
118
118
|
<PortalContainerContext.Provider value={portalHost}>
|
|
119
119
|
<CollisionBoundaryContext.Provider value={frame}>
|
|
120
|
-
<div
|
|
121
|
-
inert
|
|
122
|
-
aria-hidden
|
|
123
|
-
className="ods-content-area"
|
|
124
|
-
style={screenStyle}
|
|
125
|
-
>
|
|
120
|
+
<div inert aria-hidden className="ods-content-area" style={screenStyle}>
|
|
126
121
|
<div className="ods-content-scope">
|
|
127
122
|
{/* Until measured (the server's HTML, a page without scripts) the screen is its own loading state: every
|
|
128
123
|
component in it draws its skeleton, so the window shows the screen's shape instead of nothing, and
|
|
@@ -103,7 +103,11 @@ function StatusBadgeSkeleton({ variant, className }: { variant?: 'card' | 'butto
|
|
|
103
103
|
return (
|
|
104
104
|
<UnifiedSkeleton
|
|
105
105
|
as="span"
|
|
106
|
-
className={cn(
|
|
106
|
+
className={cn(
|
|
107
|
+
statusBadgeVariants({ variant, colorScheme: null }),
|
|
108
|
+
'box-content h-[1lh] w-12 border-transparent',
|
|
109
|
+
className,
|
|
110
|
+
)}
|
|
107
111
|
/>
|
|
108
112
|
);
|
|
109
113
|
}
|
|
@@ -41,10 +41,7 @@ export function StatusLine({ tone, label, className }: StatusLineProps) {
|
|
|
41
41
|
)}
|
|
42
42
|
>
|
|
43
43
|
{tone === 'working' && (
|
|
44
|
-
<span
|
|
45
|
-
className={cn('h-2 w-2 shrink-0 rounded-full bg-ods-flamingo-cyan', LIVE_PULSE_CLASS)}
|
|
46
|
-
aria-hidden
|
|
47
|
-
/>
|
|
44
|
+
<span className={cn('h-2 w-2 shrink-0 rounded-full bg-ods-flamingo-cyan', LIVE_PULSE_CLASS)} aria-hidden />
|
|
48
45
|
)}
|
|
49
46
|
{tone === 'success' && <CheckCircleIcon size={16} className="shrink-0 text-ods-success" aria-hidden />}
|
|
50
47
|
{tone === 'error' && <XmarkCircleIcon size={16} className="shrink-0 text-ods-error" aria-hidden />}
|
|
@@ -76,9 +76,7 @@ export function StripCard({
|
|
|
76
76
|
{caption && <span className="text-ods-text-secondary text-h6">{caption}</span>}
|
|
77
77
|
</div>
|
|
78
78
|
</div>
|
|
79
|
-
<div className={cn(bodyClass(roomy), bodyClassName)}>
|
|
80
|
-
{children}
|
|
81
|
-
</div>
|
|
79
|
+
<div className={cn(bodyClass(roomy), bodyClassName)}>{children}</div>
|
|
82
80
|
</div>
|
|
83
81
|
);
|
|
84
82
|
}
|
|
@@ -198,5 +198,7 @@ export function TagSkeleton({
|
|
|
198
198
|
size?: keyof typeof TAG_SIZE_CLASS;
|
|
199
199
|
className?: string;
|
|
200
200
|
}) {
|
|
201
|
-
return
|
|
201
|
+
return (
|
|
202
|
+
<UnifiedSkeleton as="span" className={cn(TAG_FRAME_CLASS, TAG_SIZE_CLASS[size], 'w-16 shrink-0', className)} />
|
|
203
|
+
);
|
|
202
204
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run `run` once the page has loaded and the browser is idle: the moment for work no visitor waits on (a widget
|
|
3
|
+
* that starts closed, a tracker). Whatever runs before then competes with the page's first paint and hydration, which
|
|
4
|
+
* is what slows a phone's Largest Contentful Paint. `maxWaitMs` bounds the idle wait, so a page that is never idle
|
|
5
|
+
* still gets it. Returns a cancel.
|
|
6
|
+
*/
|
|
7
|
+
export function afterPageLoad(run: () => void, maxWaitMs = 2000): () => void {
|
|
8
|
+
if (typeof window === 'undefined') return () => {};
|
|
9
|
+
type IdleWindow = Window & {
|
|
10
|
+
requestIdleCallback?: (cb: () => void, opts?: { timeout: number }) => number;
|
|
11
|
+
cancelIdleCallback?: (handle: number) => void;
|
|
12
|
+
};
|
|
13
|
+
const w = window as IdleWindow;
|
|
14
|
+
let idleHandle: number | null = null;
|
|
15
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
16
|
+
let cancelled = false;
|
|
17
|
+
const whenIdle = () => {
|
|
18
|
+
if (cancelled) return;
|
|
19
|
+
if (typeof w.requestIdleCallback === 'function') idleHandle = w.requestIdleCallback(run, { timeout: maxWaitMs });
|
|
20
|
+
else timer = setTimeout(run, 0);
|
|
21
|
+
};
|
|
22
|
+
if (document.readyState === 'complete') whenIdle();
|
|
23
|
+
else window.addEventListener('load', whenIdle, { once: true });
|
|
24
|
+
return () => {
|
|
25
|
+
cancelled = true;
|
|
26
|
+
window.removeEventListener('load', whenIdle);
|
|
27
|
+
if (idleHandle !== null) w.cancelIdleCallback?.(idleHandle);
|
|
28
|
+
if (timer !== null) clearTimeout(timer);
|
|
29
|
+
};
|
|
30
|
+
}
|
package/src/utils/format.ts
CHANGED
|
@@ -407,13 +407,25 @@ function toValidDate(date: DateInput): Date | null {
|
|
|
407
407
|
return Number.isNaN(d.getTime()) ? null : d;
|
|
408
408
|
}
|
|
409
409
|
|
|
410
|
+
/**
|
|
411
|
+
* The viewer's locale: the language the user put first in the browser's own
|
|
412
|
+
* Languages setting (`navigator.language`), not `Intl`'s default. The two
|
|
413
|
+
* differ on macOS, where Chrome's default locale is the OS language — a user
|
|
414
|
+
* with an English (US) Mac who chose English (Australia) in Chrome reads
|
|
415
|
+
* `en-AU` here and `en-US` from a bare `Intl.DateTimeFormat()`. Undefined off
|
|
416
|
+
* the client (SSR, a test without a navigator): the runtime's own default.
|
|
417
|
+
*/
|
|
418
|
+
export const viewerLocale = (): string | undefined =>
|
|
419
|
+
typeof navigator === 'undefined' ? undefined : navigator.language || undefined;
|
|
420
|
+
|
|
410
421
|
/**
|
|
411
422
|
* Render a date (and, for a date-time style, its time) in an explicit zone.
|
|
412
423
|
* Returns `''` for a missing or unparseable value.
|
|
413
424
|
*
|
|
414
|
-
* `options.viewerLocale` renders in the
|
|
415
|
-
* fixed `en-US` — for client-only UI that should read the way
|
|
416
|
-
* dates. The locale is never implied, for the same hydration
|
|
425
|
+
* `options.viewerLocale` renders in the viewer's LOCALE (`viewerLocale()`)
|
|
426
|
+
* instead of the fixed `en-US` — for client-only UI that should read the way
|
|
427
|
+
* its user writes dates. The locale is never implied, for the same hydration
|
|
428
|
+
* reason as the zone.
|
|
417
429
|
*/
|
|
418
430
|
export function formatDateWithTimezone(
|
|
419
431
|
date: DateInput,
|
|
@@ -424,7 +436,7 @@ export function formatDateWithTimezone(
|
|
|
424
436
|
const dateObj = toValidDate(date);
|
|
425
437
|
if (!dateObj) return '';
|
|
426
438
|
const { timeZone: _ignored, ...fields } = typeof style === 'string' ? ZONED_DATE_STYLES[style] : style;
|
|
427
|
-
return formatterFor(timezone, fields, options?.viewerLocale ?
|
|
439
|
+
return formatterFor(timezone, fields, options?.viewerLocale ? viewerLocale() : 'en-US').format(dateObj);
|
|
428
440
|
}
|
|
429
441
|
|
|
430
442
|
/**
|
package/src/utils/index.ts
CHANGED
|
@@ -138,6 +138,7 @@ export * from './announcement-storage';
|
|
|
138
138
|
export * from './announcement-surface';
|
|
139
139
|
// cookie id-match dismissal store (walkthrough video widget)
|
|
140
140
|
export * from './dismiss-cookie';
|
|
141
|
+
export * from './after-page-load';
|
|
141
142
|
export * from './dismissal-storage';
|
|
142
143
|
// walkthrough deep-link query param (widget default + host URL minters)
|
|
143
144
|
export * from './walkthrough-deep-link';
|
|
@@ -170,6 +171,7 @@ export {
|
|
|
170
171
|
formatDateWithTimezone,
|
|
171
172
|
formatDateTimeYmd,
|
|
172
173
|
VIEWER_TIMEZONE,
|
|
174
|
+
viewerLocale,
|
|
173
175
|
formatWebinarTimeMeta,
|
|
174
176
|
formatProgramDate,
|
|
175
177
|
formatProgramDateRange,
|