@flamingo-stack/openframe-frontend-core 0.0.770 → 0.0.772
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-C66DWLCR.cjs → chunk-2MAENZL3.cjs} +37 -37
- package/dist/{chunk-2MND64QJ.cjs → chunk-5HBGBLJT.cjs} +50 -63
- package/dist/{chunk-4EJU462O.cjs → chunk-5V7BIUGA.cjs} +2 -2
- package/dist/{chunk-HK6UQVOI.cjs → chunk-7FYOVKQD.cjs} +12 -12
- package/dist/{chunk-MYA6NR2C.cjs → chunk-CTRJNTYU.cjs} +2 -2
- package/dist/{chunk-S5EUREOB.cjs → chunk-E25TJPWV.cjs} +3 -3
- package/dist/{chunk-HQF62LAU.cjs → chunk-FPCGKH5K.cjs} +28 -28
- package/dist/{chunk-VATCQOVC.cjs → chunk-FT5NPAZY.cjs} +17 -17
- package/dist/{chunk-I2TSCJT2.cjs → chunk-HFNWBNLE.cjs} +4 -4
- package/dist/{chunk-MFRIFVDV.cjs → chunk-IHCHU355.cjs} +41 -41
- package/dist/{chunk-NZELWPKO.cjs → chunk-ILZEXCJH.cjs} +98 -98
- package/dist/{chunk-DCBZMWLY.cjs → chunk-J5ZNEAGY.cjs} +24 -24
- package/dist/{chunk-BLTNKLAX.cjs → chunk-JJNOAYD4.cjs} +4 -4
- package/dist/{chunk-QS4G7CHU.cjs → chunk-KSCEAIBS.cjs} +2 -2
- package/dist/{chunk-Q575DR36.cjs → chunk-QOEOY7BJ.cjs} +33 -33
- package/dist/{chunk-SL7KFZWJ.cjs → chunk-QPK2433W.cjs} +24 -24
- package/dist/{chunk-VL44GB2I.cjs → chunk-REFL3YNE.cjs} +13 -13
- package/dist/{chunk-37XVY2RE.cjs → chunk-S3NEBAKD.cjs} +6 -6
- package/dist/{chunk-TBKXXMBU.cjs → chunk-UR3I3J5K.cjs} +90 -86
- package/dist/{chunk-MZLWB5AH.cjs → chunk-V3W56L2D.cjs} +18 -9
- package/dist/{chunk-4BRO3RZ6.cjs → chunk-VZAOLKKF.cjs} +72 -80
- package/dist/{chunk-QMUKTHWM.cjs → chunk-W5LX7DFB.cjs} +3 -3
- package/dist/{chunk-ZVNL5OK6.cjs → chunk-X5Z4HRH7.cjs} +2 -2
- package/dist/{chunk-WUYL4BMD.cjs → chunk-YI3CDGKM.cjs} +70 -70
- package/dist/{chunk-BZLHU4EK.cjs → chunk-ZVKJKWAB.cjs} +713 -695
- package/dist/{chunk-QSLKTDZ4.cjs → chunk-ZYGC2A7G.cjs} +4 -4
- package/dist/{cloud-tenants-IAZQL2JF.cjs → cloud-tenants-CUNC5KBD.cjs} +8 -8
- package/dist/components/case-studies/index.cjs +32 -30
- package/dist/components/case-studies/quote-cards-strip.d.ts +7 -2
- package/dist/components/case-studies/quote-cards-strip.d.ts.map +1 -1
- package/dist/components/case-studies/quote-cards-strip.js +7 -5
- package/dist/components/categories-cart.d.ts +9 -2
- package/dist/components/categories-cart.d.ts.map +1 -1
- package/dist/components/categories-cart.js +9 -4
- package/dist/components/chat/entity-cards/admin-content-card-grid.d.ts +5 -1
- package/dist/components/chat/entity-cards/admin-content-card-grid.d.ts.map +1 -1
- package/dist/components/chat/entity-cards/program-card.d.ts.map +1 -1
- package/dist/components/chat/entity-cards/program-card.js +2 -2
- package/dist/components/chat/index.cjs +5 -5
- package/dist/components/contact/index.cjs +9 -9
- package/dist/components/docs/index.cjs +12 -12
- package/dist/components/embeds/index.cjs +9 -9
- package/dist/components/faq/faq-section.d.ts +7 -4
- package/dist/components/faq/faq-section.d.ts.map +1 -1
- package/dist/components/faq/index.cjs +10 -10
- package/dist/components/faq-accordion.d.ts +2 -4
- package/dist/components/faq-accordion.d.ts.map +1 -1
- package/dist/components/features/capability-explorer.d.ts +8 -2
- package/dist/components/features/capability-explorer.d.ts.map +1 -1
- package/dist/components/features/capability-explorer.js +7 -3
- package/dist/components/features/cards-strip.d.ts +2 -2
- package/dist/components/features/index.cjs +8 -8
- package/dist/components/features/model-token-rates.js +2 -2
- package/dist/components/features/remote-session/remote-desktop-chat-panel.js +2 -2
- package/dist/components/help-center-pages/index.cjs +94 -103
- package/dist/components/help-center-pages/trust-center-page.d.ts +1 -1
- package/dist/components/help-center-pages/trust-center-sections.d.ts +7 -4
- package/dist/components/help-center-pages/trust-center-sections.d.ts.map +1 -1
- package/dist/components/help-center-pages/trust-center-sections.js +5 -14
- package/dist/components/index.cjs +66 -61
- package/dist/components/logs-list.d.ts.map +1 -1
- package/dist/components/logs-list.js +3 -2
- package/dist/components/meeting-scheduler/index.cjs +47 -47
- package/dist/components/navigation/index.cjs +11 -11
- package/dist/components/navigation/multi-level-navigation.d.ts +6 -7
- package/dist/components/navigation/multi-level-navigation.d.ts.map +1 -1
- package/dist/components/onboarding-guides/index.cjs +11 -11
- package/dist/components/onboarding-guides/onboarding-guides-catalog-skeleton.d.ts +5 -5
- package/dist/components/onboarding-guides/onboarding-guides-catalog-skeleton.d.ts.map +1 -1
- package/dist/components/onboarding-guides/onboarding-guides-catalog-skeleton.js +3 -3
- package/dist/components/product-demo/index.cjs +18 -18
- package/dist/components/related-content/index.cjs +5 -5
- package/dist/components/related-content/related-content-section.d.ts.map +1 -1
- package/dist/components/related-content/related-content-section.js +4 -17
- package/dist/components/shared/dev-section/dev-card-row.d.ts +3 -3
- package/dist/components/shared/dev-section/dev-card-row.d.ts.map +1 -1
- package/dist/components/shared/product-release/release-detail-page.js +2 -2
- package/dist/components/shared/roadmap/roadmap-grid-skeleton.d.ts +6 -1
- package/dist/components/shared/roadmap/roadmap-grid-skeleton.d.ts.map +1 -1
- package/dist/components/shared/roadmap/roadmap-grid-skeleton.js +3 -1
- package/dist/components/shared/roadmap/roadmap-grid.d.ts +10 -3
- package/dist/components/shared/roadmap/roadmap-grid.d.ts.map +1 -1
- package/dist/components/shared/roadmap/roadmap-grid.js +6 -3
- package/dist/components/tickets/help-center-list.js +4 -4
- package/dist/components/tickets/hooks/use-tickets-list.d.ts +2 -0
- package/dist/components/tickets/hooks/use-tickets-list.d.ts.map +1 -1
- package/dist/components/tickets/hooks/use-tickets-list.js +3 -2
- package/dist/components/tickets/index.cjs +14 -14
- package/dist/components/tickets/ticket-center.d.ts.map +1 -1
- package/dist/components/tickets/ticket-center.js +3 -9
- package/dist/components/ui/autocomplete.d.ts +5 -0
- package/dist/components/ui/autocomplete.d.ts.map +1 -1
- package/dist/components/ui/autocomplete.js +3 -2
- package/dist/components/ui/data-table/data-table-body.d.ts +5 -2
- package/dist/components/ui/data-table/data-table-body.d.ts.map +1 -1
- package/dist/components/ui/data-table/data-table-body.js +3 -1
- package/dist/components/ui/data-table/data-table-infinite-footer.d.ts +5 -2
- package/dist/components/ui/data-table/data-table-infinite-footer.d.ts.map +1 -1
- package/dist/components/ui/data-table/data-table-infinite-footer.js +5 -2
- package/dist/components/ui/data-table/utils.d.ts +21 -0
- package/dist/components/ui/data-table/utils.d.ts.map +1 -1
- package/dist/components/ui/data-table/utils.js +7 -0
- package/dist/components/ui/index.cjs +8 -8
- package/dist/components/ui/search-input.d.ts +3 -1
- package/dist/components/ui/search-input.d.ts.map +1 -1
- package/dist/components/ui/search-input.js +3 -2
- package/dist/components/ui/searchable-select.d.ts +2 -0
- package/dist/components/ui/searchable-select.d.ts.map +1 -1
- package/dist/components/ui/searchable-select.js +10 -6
- package/dist/components/ui/select.d.ts +11 -1
- package/dist/components/ui/select.d.ts.map +1 -1
- package/dist/components/ui/select.js +13 -5
- package/dist/components/ui/table/table-skeleton.d.ts +2 -2
- package/dist/components/ui/table/table.d.ts +1 -1
- package/dist/components/ui/table/table.d.ts.map +1 -1
- package/dist/components/ui/table/table.js +2 -1
- package/dist/components/ui/table/types.d.ts +1 -0
- package/dist/components/ui/table/types.d.ts.map +1 -1
- package/dist/{devices-7FDK2CGV.cjs → devices-TO4VUO7F.cjs} +9 -9
- package/dist/{embeddable-chat-3FR2RCHM.cjs → embeddable-chat-CEAP424I.cjs} +73 -73
- package/dist/index.cjs +15 -14
- package/dist/{knowledge-ZLHYBG4L.cjs → knowledge-CYJP4XYY.cjs} +9 -9
- package/dist/{logs-G6AFN7A6.cjs → logs-NL3ZBQKC.cjs} +16 -16
- package/dist/{policies-SEOK4IQO.cjs → policies-WTUV4EIT.cjs} +12 -12
- package/dist/{remote-session-WVORQV7V.cjs → remote-session-TC3BZDBZ.cjs} +4 -4
- package/dist/{scripts-HH4YVUBO.cjs → scripts-EE6MLBQP.cjs} +15 -15
- package/dist/{software-update-4A3XJZQB.cjs → software-update-HX6PN4SB.cjs} +12 -12
- package/dist/types/category.d.ts +5 -0
- package/dist/types/category.d.ts.map +1 -1
- package/dist/types/logs.types.d.ts +2 -0
- package/dist/types/logs.types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/case-studies/quote-cards-strip.tsx +16 -7
- package/src/components/categories-cart.tsx +16 -5
- package/src/components/chat/entity-cards/admin-content-card-grid.tsx +5 -1
- package/src/components/chat/entity-cards/program-card.tsx +6 -3
- package/src/components/faq/faq-section.tsx +7 -4
- package/src/components/faq-accordion.tsx +2 -4
- package/src/components/features/capability-explorer.tsx +16 -4
- package/src/components/features/cards-strip.tsx +3 -3
- package/src/components/features/model-token-rates.tsx +4 -4
- package/src/components/features/remote-session/remote-desktop-chat-panel.tsx +4 -4
- package/src/components/help-center-pages/trust-center-page.tsx +1 -1
- package/src/components/help-center-pages/trust-center-sections.tsx +15 -22
- package/src/components/logs-list.tsx +6 -5
- package/src/components/navigation/multi-level-navigation.tsx +6 -7
- package/src/components/onboarding-guides/onboarding-guides-catalog-skeleton.tsx +6 -6
- package/src/components/related-content/related-content-section.tsx +6 -27
- package/src/components/shared/dev-section/dev-card-row.tsx +3 -3
- package/src/components/shared/product-release/release-detail-page.tsx +2 -2
- package/src/components/shared/roadmap/roadmap-grid-skeleton.tsx +8 -2
- package/src/components/shared/roadmap/roadmap-grid.tsx +14 -4
- package/src/components/tickets/help-center-list.tsx +4 -4
- package/src/components/tickets/hooks/use-tickets-list.ts +3 -2
- package/src/components/tickets/ticket-center.tsx +4 -13
- package/src/components/ui/autocomplete.tsx +12 -3
- package/src/components/ui/data-table/data-table-body.tsx +8 -2
- package/src/components/ui/data-table/data-table-infinite-footer.tsx +10 -3
- package/src/components/ui/data-table/utils.ts +18 -0
- package/src/components/ui/search-input.tsx +9 -3
- package/src/components/ui/searchable-select.tsx +18 -9
- package/src/components/ui/select.tsx +28 -7
- package/src/components/ui/table/table-skeleton.tsx +2 -2
- package/src/components/ui/table/table.tsx +4 -1
- package/src/components/ui/table/types.ts +2 -1
- package/src/types/category.ts +5 -0
- package/src/types/logs.types.ts +2 -0
|
@@ -90,7 +90,7 @@ export interface FaqSectionProps {
|
|
|
90
90
|
ask?: false | FaqAskOptions;
|
|
91
91
|
/**
|
|
92
92
|
* The loading state's groups of questions, in order: THE one way to say what the skeleton draws (see
|
|
93
|
-
* `FaqSkeletonShape`). Absent: one group with no category heading, of `minResults` rows (else
|
|
93
|
+
* `FaqSkeletonShape`). Absent: one group with no category heading, of `minResults` rows (else a typical count).
|
|
94
94
|
*/
|
|
95
95
|
skeletonShape?: FaqSkeletonShape;
|
|
96
96
|
/**
|
|
@@ -133,14 +133,17 @@ const CATEGORY_SECTION_CLASS = 'scroll-mt-24';
|
|
|
133
133
|
* the host declared its FAQs categorised (`skeletonNav`): then every group it draws is named, loading and loaded.
|
|
134
134
|
*/
|
|
135
135
|
const CATEGORY_HEADING_CLASS = cn(SECTION_HEADING_CLASS, 'pb-4');
|
|
136
|
-
/**
|
|
136
|
+
/**
|
|
137
|
+
* Questions the block's skeleton draws when the host states no count (`minResults`): the accordion's typical count. A
|
|
138
|
+
* host that fetches with `minResults` gets exactly that many rows.
|
|
139
|
+
*/
|
|
137
140
|
export const FAQ_SECTION_SKELETON_QUESTIONS = FAQ_ACCORDION_SKELETON_ROWS;
|
|
138
141
|
|
|
139
142
|
/** One group of questions the FAQ's loading state draws. */
|
|
140
143
|
export interface FaqSkeletonGroup {
|
|
141
144
|
/** The group is a named category: drawn with its heading bar (and counted for the category nav). Default false. */
|
|
142
145
|
heading?: boolean;
|
|
143
|
-
/** Question rows the group draws. Default `FAQ_SECTION_SKELETON_QUESTIONS`,
|
|
146
|
+
/** Question rows the group draws: the group's count. Default `FAQ_SECTION_SKELETON_QUESTIONS`, a typical count. */
|
|
144
147
|
rows?: number;
|
|
145
148
|
}
|
|
146
149
|
|
|
@@ -657,7 +660,7 @@ export interface FaqSectionSkeletonProps extends Pick<
|
|
|
657
660
|
FaqSectionProps,
|
|
658
661
|
'heading' | 'topic' | 'showTopic' | 'aside' | 'className' | 'id'
|
|
659
662
|
> {
|
|
660
|
-
/** The groups drawn, in order. Default one group with no heading, of
|
|
663
|
+
/** The groups drawn, in order. Default one group with no heading, of `FAQ_SECTION_SKELETON_QUESTIONS` rows. */
|
|
661
664
|
shape?: FaqSkeletonShape;
|
|
662
665
|
/** Draw the category nav's row (one line of pills) whatever `sections` is: the host's FAQs are categorised. */
|
|
663
666
|
navRow?: boolean;
|
|
@@ -25,10 +25,8 @@ const ROW_HEADER_CLASS = 'flex w-full items-center gap-6 px-6 py-4 text-left con
|
|
|
25
25
|
const QUESTION_CLASS = 'min-w-0 flex-1 break-words';
|
|
26
26
|
const CHEVRON_CLASS = 'shrink-0 text-ods-text-primary';
|
|
27
27
|
/**
|
|
28
|
-
* Rows the accordion's skeleton draws when not told how many:
|
|
29
|
-
*
|
|
30
|
-
* viewport once the block's heading sits above them) and about one and a half lines on a phone (about 62px: 12 under
|
|
31
|
-
* an 844px one). 12 fills the first screen at both widths without drawing a second.
|
|
28
|
+
* Rows the accordion's skeleton draws when not told how many: a typical count for a list of questions whose host
|
|
29
|
+
* states no count (a host that fetches with a count passes it, and the skeleton draws exactly that, at every width).
|
|
32
30
|
*/
|
|
33
31
|
export const FAQ_ACCORDION_SKELETON_ROWS = 12;
|
|
34
32
|
/**
|
|
@@ -85,7 +85,12 @@ export interface CapabilityExplorerProps {
|
|
|
85
85
|
responderMark?: ReactNode;
|
|
86
86
|
/** The jobs are being read (the host's query runs): the explorer draws its own skeleton. A page's loading scope does too. */
|
|
87
87
|
loading?: boolean;
|
|
88
|
-
/**
|
|
88
|
+
/**
|
|
89
|
+
* The jobs the skeleton's list draws: the number of jobs the host reads (its limit). Default
|
|
90
|
+
* `CAPABILITY_EXPLORER_SKELETON_JOBS`, a typical count for a host that states none.
|
|
91
|
+
*/
|
|
92
|
+
skeletonCount?: number;
|
|
93
|
+
/** @deprecated Use `skeletonCount`. */
|
|
89
94
|
fold?: number;
|
|
90
95
|
className?: string;
|
|
91
96
|
}
|
|
@@ -241,6 +246,9 @@ function JobHeadSkeleton() {
|
|
|
241
246
|
);
|
|
242
247
|
}
|
|
243
248
|
|
|
249
|
+
/** The jobs the explorer's skeleton lists when its host states no count: a typical count. */
|
|
250
|
+
const CAPABILITY_EXPLORER_SKELETON_JOBS = 8;
|
|
251
|
+
|
|
244
252
|
/**
|
|
245
253
|
* The explorer while its data loads, drawn as both layouts in their own boxes
|
|
246
254
|
* (the width decides which shows, by CSS, since it is not known yet): the list
|
|
@@ -251,11 +259,12 @@ function JobHeadSkeleton() {
|
|
|
251
259
|
export function CapabilityExplorerSkeleton({
|
|
252
260
|
region = true,
|
|
253
261
|
className,
|
|
254
|
-
count =
|
|
262
|
+
count = CAPABILITY_EXPLORER_SKELETON_JOBS,
|
|
255
263
|
}: {
|
|
256
264
|
/** The loading state announces itself (`LoadingRegion`). `false` inside another loading region. Default `true`. */
|
|
257
265
|
region?: boolean;
|
|
258
266
|
className?: string;
|
|
267
|
+
/** The jobs the list draws: the jobs the host reads. Default a typical count. */
|
|
259
268
|
count?: number;
|
|
260
269
|
}) {
|
|
261
270
|
return (
|
|
@@ -355,6 +364,7 @@ export function CapabilityExplorer({
|
|
|
355
364
|
requesterMark,
|
|
356
365
|
responderMark,
|
|
357
366
|
loading = false,
|
|
367
|
+
skeletonCount,
|
|
358
368
|
fold,
|
|
359
369
|
className,
|
|
360
370
|
}: CapabilityExplorerProps) {
|
|
@@ -364,10 +374,12 @@ export function CapabilityExplorer({
|
|
|
364
374
|
|
|
365
375
|
// While the page is read its jobs are not known (none in a loading state): the explorer is its skeleton, never
|
|
366
376
|
// left out. The page's loading region announces it, so this one does not; a host's own read does.
|
|
367
|
-
if (inScope || loading)
|
|
377
|
+
if (inScope || loading) {
|
|
378
|
+
return <CapabilityExplorerSkeleton region={!inScope} count={skeletonCount ?? fold} className={className} />;
|
|
379
|
+
}
|
|
368
380
|
if (items.length === 0) return null;
|
|
369
381
|
// Unknown until measured: one box, so neither layout's ids are in the page twice.
|
|
370
|
-
if (wide === undefined) return <CapabilityExplorerSkeleton className={className} />;
|
|
382
|
+
if (wide === undefined) return <CapabilityExplorerSkeleton count={items.length} className={className} />;
|
|
371
383
|
|
|
372
384
|
if (!wide) {
|
|
373
385
|
return (
|
|
@@ -120,8 +120,8 @@ interface CardsStripEngineProps {
|
|
|
120
120
|
className?: string;
|
|
121
121
|
/**
|
|
122
122
|
* The strip in a page's loading state, when its items are read (none handed in yet): `count` copies of `card`, the
|
|
123
|
-
* strip's cards' own skeleton (in children mode each in the managed cell's width). `count` is the
|
|
124
|
-
* the
|
|
123
|
+
* strip's cards' own skeleton (in children mode each in the managed cell's width). `count` is the number of cards
|
|
124
|
+
* the host reads (its limit), drawn at every width. Absent: an empty strip draws nothing, in a scope too.
|
|
125
125
|
*/
|
|
126
126
|
skeleton?: { card: React.ReactNode; count: number };
|
|
127
127
|
}
|
|
@@ -807,7 +807,7 @@ export function CardsStrip<T = unknown>(props: CardsStripProps<T>): React.ReactE
|
|
|
807
807
|
// Outside a loading state an empty strip is nothing; inside one, a strip whose host states a skeleton draws it
|
|
808
808
|
// (a host that states none expects no strip, e.g. a record with no bites).
|
|
809
809
|
if (!inScope || !skeleton) return null;
|
|
810
|
-
// The items are read: the heading (the page's own words), the slot, and
|
|
810
|
+
// The items are read: the heading (the page's own words), the slot, and `count` of the cards' own skeleton.
|
|
811
811
|
return (
|
|
812
812
|
<div className={cn(STRIP_ROOT_CLASS, className)}>
|
|
813
813
|
{heading}
|
|
@@ -184,7 +184,7 @@ export interface ModelTokenRatesProps {
|
|
|
184
184
|
className?: string;
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
/**
|
|
187
|
+
/** Rows the table's skeleton draws: a typical count (a provider's tab lists every rate it has, with no page or limit). */
|
|
188
188
|
const TABLE_SKELETON_ROWS = 8;
|
|
189
189
|
|
|
190
190
|
/**
|
|
@@ -414,8 +414,8 @@ export interface ModelTokenExchangeProps {
|
|
|
414
414
|
export const MODEL_TOKEN_EXCHANGE_BALANCE = 1_000_000;
|
|
415
415
|
export const MODEL_TOKEN_EXCHANGE_RANGE = { min: 1_000_000, max: 50_000_000, step: 1_000_000 } as const;
|
|
416
416
|
|
|
417
|
-
/** The provider rows drawn while the providers load:
|
|
418
|
-
const
|
|
417
|
+
/** The provider rows drawn while the providers load: a typical count (the host lists every provider with a rate). */
|
|
418
|
+
const PROVIDER_SKELETON_ROWS = 3;
|
|
419
419
|
|
|
420
420
|
/** A model as a picker option: its name, its provider under it, the provider's mark beside it. */
|
|
421
421
|
function modelOption(rate: ModelTokenRate): AutocompleteOption<string> {
|
|
@@ -575,7 +575,7 @@ export function ModelTokenExchange({
|
|
|
575
575
|
{/* While the providers load the selector draws its own skeleton (its rows, the option row's own boxes). */}
|
|
576
576
|
<PushButtonSelector<string>
|
|
577
577
|
isLoading={providers.length === 0 && !failed}
|
|
578
|
-
skeletonCount={
|
|
578
|
+
skeletonCount={PROVIDER_SKELETON_ROWS}
|
|
579
579
|
optionDescriptions={false}
|
|
580
580
|
options={providers.map(providerType => {
|
|
581
581
|
// What the server stated for the provider rides its models' rows.
|
|
@@ -8,10 +8,10 @@ import { RemoteDesktopChatMessageRow, RemoteDesktopChatMessageRowSkeleton } from
|
|
|
8
8
|
import type { RemoteSessionChatMessage } from './types';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* Message rows the panel's loading state draws:
|
|
12
|
-
*
|
|
11
|
+
* Message rows the panel's loading state draws: a typical count. The panel draws the whole history its host hands it
|
|
12
|
+
* (no page, no limit), so there is no page size to draw.
|
|
13
13
|
*/
|
|
14
|
-
const
|
|
14
|
+
const CHAT_PANEL_SKELETON_ROWS = 10;
|
|
15
15
|
|
|
16
16
|
/** Who is typing on the technician's side of the chat: shown on the technician's rows. */
|
|
17
17
|
export interface RemoteDesktopChatTechnician {
|
|
@@ -78,7 +78,7 @@ export function RemoteDesktopChatPanel({
|
|
|
78
78
|
// The chat while it is read: a screen of the panel's own message rows, stacked by a one-column grid
|
|
79
79
|
// (the thread above is already the column flexbox: WEBVIEW-001).
|
|
80
80
|
<LoadingRegion label="Loading the chat" className="grid grid-cols-1 gap-[var(--spacing-system-xsf)]">
|
|
81
|
-
{Array.from({ length:
|
|
81
|
+
{Array.from({ length: CHAT_PANEL_SKELETON_ROWS }, (_, i) => (
|
|
82
82
|
// biome-ignore lint/suspicious/noArrayIndexKey: skeleton rows never reorder
|
|
83
83
|
<RemoteDesktopChatMessageRowSkeleton key={i} />
|
|
84
84
|
))}
|
|
@@ -112,7 +112,7 @@ export interface TrustCenterPageProps {
|
|
|
112
112
|
faqSkeletonShape?: FaqSkeletonShape;
|
|
113
113
|
/**
|
|
114
114
|
* Rows each panel's loading state draws, for a host whose lists are curated (it knows their lengths: the frameworks
|
|
115
|
-
* it is audited against). Absent:
|
|
115
|
+
* it is audited against). Absent: a typical count, at every width.
|
|
116
116
|
*/
|
|
117
117
|
panelSkeletonRows?: Partial<Record<TrustCenterPanelId, TrustPanelRows>>;
|
|
118
118
|
}
|
|
@@ -143,7 +143,7 @@ export function AiSection({
|
|
|
143
143
|
skeletonRows,
|
|
144
144
|
}: {
|
|
145
145
|
practices: TrustCenterAiPractice[];
|
|
146
|
-
/** Rows the loading state draws (`TrustPanelRows`). Absent:
|
|
146
|
+
/** Rows the loading state draws (`TrustPanelRows`). Absent: a typical count. */
|
|
147
147
|
skeletonRows?: TrustPanelRows;
|
|
148
148
|
}) {
|
|
149
149
|
if (useLoadingScope()) return <TrustPanelSkeleton panel="ai" rows={skeletonRows} />;
|
|
@@ -415,7 +415,7 @@ export function DocumentsSection({
|
|
|
415
415
|
skeletonRows,
|
|
416
416
|
}: {
|
|
417
417
|
documents: TrustCenterDocument[];
|
|
418
|
-
/** Rows the loading state draws (`TrustPanelRows`). Absent:
|
|
418
|
+
/** Rows the loading state draws (`TrustPanelRows`). Absent: a typical count. */
|
|
419
419
|
skeletonRows?: TrustPanelRows;
|
|
420
420
|
/** Where a public document opens (`trustCenterDocumentUrl`); `null` → offer a request instead. */
|
|
421
421
|
documentUrl: (document: TrustCenterDocument) => string | null;
|
|
@@ -554,7 +554,7 @@ export function SubprocessorsSection({
|
|
|
554
554
|
skeletonRows,
|
|
555
555
|
}: {
|
|
556
556
|
subprocessors: TrustCenterSubprocessor[];
|
|
557
|
-
/** Rows the loading state draws (`TrustPanelRows`). Absent:
|
|
557
|
+
/** Rows the loading state draws (`TrustPanelRows`). Absent: a typical count. */
|
|
558
558
|
skeletonRows?: TrustPanelRows;
|
|
559
559
|
}) {
|
|
560
560
|
if (useLoadingScope()) return <TrustPanelSkeleton panel="subprocessors" rows={skeletonRows} />;
|
|
@@ -634,17 +634,19 @@ export function ContactSection({ onContact }: { onContact: () => void }) {
|
|
|
634
634
|
// ---------------------------------------------------------------------------
|
|
635
635
|
|
|
636
636
|
/**
|
|
637
|
-
* Rows a panel's skeleton draws
|
|
638
|
-
*
|
|
639
|
-
*
|
|
640
|
-
* host whose list is curated passes its length (each section's `skeletonRows`, the page's `panelSkeletonRows`).
|
|
637
|
+
* Rows a panel's skeleton draws when its host states no count. A panel lists every record the host's data holds (no
|
|
638
|
+
* page, no limit), so this is a typical count, drawn at every width; a host whose list is curated passes its length
|
|
639
|
+
* (each section's `skeletonRows`, the page's `panelSkeletonRows`).
|
|
641
640
|
*/
|
|
642
|
-
const
|
|
641
|
+
const TRUST_PANEL_SKELETON_ROWS = 5;
|
|
643
642
|
|
|
644
643
|
/** The panels whose rows `TrustPanelSkeleton` draws (Controls, Contact and the FAQ draw their own). */
|
|
645
644
|
export type TrustCenterPanelId = 'compliance' | 'documents' | 'subprocessors' | 'ai';
|
|
646
645
|
|
|
647
|
-
/**
|
|
646
|
+
/**
|
|
647
|
+
* Rows a panel's skeleton draws: one count, at every width. The `{ phone, wide }` form is kept for hosts that pass
|
|
648
|
+
* it and draws the larger of the two everywhere (a skeleton never hides rows per breakpoint).
|
|
649
|
+
*/
|
|
648
650
|
export type TrustPanelRows = number | { phone: number; wide: number };
|
|
649
651
|
|
|
650
652
|
/**
|
|
@@ -663,9 +665,9 @@ const TRUST_PANEL_SHAPES: Record<
|
|
|
663
665
|
ai: { mark: 'glyph', action: 'none', lines: { phone: 6, wide: 2 } },
|
|
664
666
|
};
|
|
665
667
|
|
|
666
|
-
function
|
|
667
|
-
if (rows === undefined) return
|
|
668
|
-
return typeof rows === 'number' ?
|
|
668
|
+
function panelRowCount(rows: TrustPanelRows | undefined): number {
|
|
669
|
+
if (rows === undefined) return TRUST_PANEL_SKELETON_ROWS;
|
|
670
|
+
return typeof rows === 'number' ? rows : Math.max(rows.phone, rows.wide);
|
|
669
671
|
}
|
|
670
672
|
|
|
671
673
|
/**
|
|
@@ -675,17 +677,8 @@ function panelFold(rows: TrustPanelRows | undefined): { phone: number; wide: num
|
|
|
675
677
|
*/
|
|
676
678
|
function TrustPanelSkeleton({ panel, rows }: { panel: TrustCenterPanelId; rows?: TrustPanelRows }) {
|
|
677
679
|
const shape = TRUST_PANEL_SHAPES[panel];
|
|
678
|
-
const
|
|
679
|
-
const panelRows: PanelRow[] = Array.from({ length: Math.max(fold.phone, fold.wide) }, (_, index) => ({
|
|
680
|
+
const panelRows: PanelRow[] = Array.from({ length: panelRowCount(rows) }, (_, index) => ({
|
|
680
681
|
id: `skeleton-${index}`,
|
|
681
|
-
// A row past a breakpoint's fold is not drawn there; the last row drawn on a phone drops its divider like the
|
|
682
|
-
// panel's last row (`last:` cannot see a hidden sibling).
|
|
683
|
-
className: cn(
|
|
684
|
-
index >= fold.phone && 'hidden content-md:flex',
|
|
685
|
-
index >= fold.wide && 'content-md:hidden',
|
|
686
|
-
fold.phone < fold.wide && index === fold.phone - 1 && 'content-max-md:border-b-0',
|
|
687
|
-
fold.wide < fold.phone && index === fold.wide - 1 && 'content-md:border-b-0',
|
|
688
|
-
),
|
|
689
682
|
columns: [
|
|
690
683
|
{
|
|
691
684
|
// The loaded rows' own cell (leading mark, `text-h4` value over a wrapping `text-h6` label): the panel lays
|
|
@@ -18,10 +18,10 @@ const LOG_META_ROW_CLASS = 'flex items-center gap-2';
|
|
|
18
18
|
const LOG_SCROLL_CLASS = 'flex-1 overflow-y-auto px-4 py-3';
|
|
19
19
|
const LOG_FRAME_CLASS = 'relative flex flex-col overflow-hidden rounded-lg border border-ods-border bg-ods-card';
|
|
20
20
|
/**
|
|
21
|
-
* Rows the list's skeleton draws
|
|
22
|
-
*
|
|
21
|
+
* Rows the list's skeleton draws when its host states no `skeletonRows`: a typical count. A host that reads its logs
|
|
22
|
+
* with a page size or a limit passes it, and the skeleton draws exactly that many rows.
|
|
23
23
|
*/
|
|
24
|
-
const
|
|
24
|
+
const LOGS_LIST_SKELETON_ROWS = 6;
|
|
25
25
|
|
|
26
26
|
/** A log row while it is read: the severity dot's box, the title and the time as bars. */
|
|
27
27
|
function LogCardSkeleton() {
|
|
@@ -91,6 +91,7 @@ export const LogsList = forwardRef<HTMLDivElement, LogsListProps>(
|
|
|
91
91
|
showConnector = true,
|
|
92
92
|
onLogClick,
|
|
93
93
|
loading = false,
|
|
94
|
+
skeletonRows = LOGS_LIST_SKELETON_ROWS,
|
|
94
95
|
emptyMessage = 'No logs to display',
|
|
95
96
|
className,
|
|
96
97
|
},
|
|
@@ -111,11 +112,11 @@ export const LogsList = forwardRef<HTMLDivElement, LogsListProps>(
|
|
|
111
112
|
};
|
|
112
113
|
|
|
113
114
|
if (loading) {
|
|
114
|
-
// The list's own frame and scroller with a
|
|
115
|
+
// The list's own frame and scroller with a full page of row skeletons.
|
|
115
116
|
return (
|
|
116
117
|
<div ref={ref} className={cn(LOG_FRAME_CLASS, getContainerClasses(), className)} style={getContainerStyles()}>
|
|
117
118
|
<LoadingRegion label="Loading logs" className={LOG_SCROLL_CLASS}>
|
|
118
|
-
{Array.from({ length:
|
|
119
|
+
{Array.from({ length: skeletonRows }, (_, i) => (
|
|
119
120
|
// biome-ignore lint/suspicious/noArrayIndexKey: skeleton rows never reorder
|
|
120
121
|
<LogCardSkeleton key={i} />
|
|
121
122
|
))}
|
|
@@ -60,13 +60,12 @@ const NAV_ROW_HEIGHT = {
|
|
|
60
60
|
mobile: { row: 'h-11', toggle: 'h-11 w-11', toggleRoom: 'pr-11' },
|
|
61
61
|
} as const;
|
|
62
62
|
/**
|
|
63
|
-
* How many rows each skeleton draws (the desktop tree, the mobile dropdown)
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* past the first screen anyway, and the tree's other root entries are list growth below it.
|
|
63
|
+
* How many rows each skeleton draws (the desktop tree, the mobile dropdown) when its host states no count. The tree
|
|
64
|
+
* lists every entry its source holds (no page, no limit), so this is a typical count, drawn at every width. The tree
|
|
65
|
+
* opens on the reader's repository, so the skeleton draws that shape: one root entry open, the rest its entries
|
|
66
|
+
* (indented as the tree indents them). It is drawn open FIRST: the root order is the source's path sort, and which
|
|
67
|
+
* repositories sort before the reader's needs the read, so a page in a later repository differs by one collapsed
|
|
68
|
+
* root row above the open one. A host that knows its entry count passes it as `rows`.
|
|
70
69
|
*/
|
|
71
70
|
export const MULTI_LEVEL_NAVIGATION_SKELETON_ROWS = 17;
|
|
72
71
|
export const MOBILE_NAVIGATION_SKELETON_ROWS = 17;
|
|
@@ -14,13 +14,13 @@ export const CATALOG_SECTION_COUNT_CLASS =
|
|
|
14
14
|
export const CATALOG_LIST_CLASS = 'flex flex-col gap-4';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* Catalog cards
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* Catalog cards the skeleton's section draws: a typical count, at every width. The catalog reads every guide (no
|
|
18
|
+
* page, no limit), so there is no page size to draw. The section count is the data's too: the skeleton draws ONE
|
|
19
|
+
* section, and every loaded section is a `data-loading-item`.
|
|
20
20
|
*/
|
|
21
|
-
export const
|
|
21
|
+
export const ONBOARDING_CATALOG_SKELETON_CARDS = 3;
|
|
22
22
|
/**
|
|
23
|
-
* The catalog's sections while the guides are read: ONE section of `
|
|
23
|
+
* The catalog's sections while the guides are read: ONE section of `ONBOARDING_CATALOG_SKELETON_CARDS` cards in the view's
|
|
24
24
|
* own classes (its heading and count bars). The view draws it itself (`useLoadingScope() || loading`): its search bar
|
|
25
25
|
* is real and its section pills draw their own skeleton.
|
|
26
26
|
*/
|
|
@@ -35,7 +35,7 @@ export function OnboardingCatalogSectionsSkeleton() {
|
|
|
35
35
|
</span>
|
|
36
36
|
</h2>
|
|
37
37
|
<ul className={CATALOG_LIST_CLASS}>
|
|
38
|
-
{Array.from({ length:
|
|
38
|
+
{Array.from({ length: ONBOARDING_CATALOG_SKELETON_CARDS }, (_, i) => (
|
|
39
39
|
// biome-ignore lint/suspicious/noArrayIndexKey: skeleton cards never reorder
|
|
40
40
|
<li key={i}>
|
|
41
41
|
<OnboardingGuideCardSkeleton size="catalog" />
|
|
@@ -318,33 +318,13 @@ const GROUP_LIST_CLASS = 'space-y-4';
|
|
|
318
318
|
const GROUP_HEADING_CLASS = 'font-semibold text-ods-text-secondary text-h5';
|
|
319
319
|
// A column with a gap, not space-y: the region's screen-reader status (absolutely positioned) must not take a margin.
|
|
320
320
|
// The rail is a page section (a `<section>`, loaded and loading). Its groups and each group's cards are read lists
|
|
321
|
-
// (`data-loading-item`): how many there are is the data's, and the skeleton draws
|
|
321
|
+
// (`data-loading-item`): how many there are is the data's, and the skeleton draws ONE FULL PAGE of each group.
|
|
322
322
|
const RAIL_CLASS = 'flex flex-col gap-8';
|
|
323
|
-
/** Cards per group a skeleton draws when the host states no count. */
|
|
324
|
-
const RAIL_SKELETON_CARDS = 3;
|
|
325
|
-
/**
|
|
326
|
-
* One screen of a group, the most its skeleton draws (the host's count is the upper bound): list groups stack
|
|
327
|
-
* full-width cards, two of which fill a screen; grid groups show two rows at their widest column count, and the
|
|
328
|
-
* cards past two rows at a narrower step are hidden by the same breakpoints the grid uses (`gridClassFor`).
|
|
329
|
-
*/
|
|
330
|
-
const RAIL_LIST_FOLD = 2;
|
|
331
|
-
const RAIL_GRID_FOLD_ROWS = 2;
|
|
332
323
|
/**
|
|
333
324
|
* The card an unknown-type group draws while its type is read: the rail's grid content card (a cover over a title
|
|
334
325
|
* and an excerpt), the shape the read types' grid cards share, so the group holds a grid card's height.
|
|
335
326
|
*/
|
|
336
327
|
const UNKNOWN_TYPE_CARD = 'blog_post_existing';
|
|
337
|
-
/** The grid's column count per step, as `gridClassFor` sets it: [phone, sm, lg]. */
|
|
338
|
-
const GRID_COLUMNS_BY_STEP: Record<2 | 3, readonly [number, number, number]> = { 2: [1, 2, 2], 3: [1, 2, 3] };
|
|
339
|
-
|
|
340
|
-
/** A grid skeleton card's visibility: shown only at the steps where it is within the fold's rows. */
|
|
341
|
-
function gridFoldClass(index: number, columns: 2 | 3): string | undefined {
|
|
342
|
-
const [phone, sm, lg] = GRID_COLUMNS_BY_STEP[columns].map(c => c * RAIL_GRID_FOLD_ROWS);
|
|
343
|
-
if (index < phone) return undefined;
|
|
344
|
-
if (index < sm) return 'hidden sm:block';
|
|
345
|
-
if (index < lg) return 'hidden lg:block';
|
|
346
|
-
return 'hidden';
|
|
347
|
-
}
|
|
348
328
|
|
|
349
329
|
/** A group's heading: the type's label (static copy), loaded and loading. */
|
|
350
330
|
function GroupHeading({ type }: { type: string }) {
|
|
@@ -848,9 +828,9 @@ export function RelatedContentSectionSkeleton({
|
|
|
848
828
|
<TextLineSkeleton type="inherit" as="span" className="w-24" />
|
|
849
829
|
</h3>
|
|
850
830
|
<div className={gridClassFor(columns)}>
|
|
851
|
-
{Array.from({ length:
|
|
831
|
+
{Array.from({ length: GROUP_PAGE_SIZE }, (_, i) => (
|
|
852
832
|
// biome-ignore lint/suspicious/noArrayIndexKey: skeleton cards never reorder
|
|
853
|
-
<div key={i}
|
|
833
|
+
<div key={i} data-loading-item>
|
|
854
834
|
{renderSkeletonForType(UNKNOWN_TYPE_CARD, 'default', adminCampaignCard, extras)}
|
|
855
835
|
</div>
|
|
856
836
|
))}
|
|
@@ -868,15 +848,14 @@ export function RelatedContentSectionSkeleton({
|
|
|
868
848
|
const config = resolveGroupConfig(type);
|
|
869
849
|
const sameType = canonicalEntityType !== null && canonicalContentRefType(type) === canonicalEntityType;
|
|
870
850
|
const isList = config.layout === 'list';
|
|
871
|
-
|
|
851
|
+
// One full page of the group: the count the host fetches, never more than a group's page.
|
|
872
852
|
const count = Math.min(
|
|
873
853
|
GROUP_PAGE_SIZE,
|
|
874
|
-
|
|
875
|
-
(sameType ? (sameTypeMinResults ?? minResults) : minResults) ?? RAIL_SKELETON_CARDS,
|
|
854
|
+
(sameType ? (sameTypeMinResults ?? minResults) : minResults) ?? GROUP_PAGE_SIZE,
|
|
876
855
|
);
|
|
877
856
|
const cards = Array.from({ length: count }, (_, i) => (
|
|
878
857
|
// biome-ignore lint/suspicious/noArrayIndexKey: skeleton cards never reorder
|
|
879
|
-
<div key={i}
|
|
858
|
+
<div key={i} data-loading-item>
|
|
880
859
|
{renderSkeletonForType(type, config.gridSize, adminCampaignCard, extras)}
|
|
881
860
|
</div>
|
|
882
861
|
));
|
|
@@ -120,9 +120,8 @@ export function DevCardRowSkeleton() {
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
/**
|
|
123
|
-
* The
|
|
124
|
-
*
|
|
125
|
-
* tickets (`tickets-list.tsx`) mount this directly.
|
|
123
|
+
* The skeleton rows inside a bordered card: the loading state of every list shell. A list draws ONE FULL PAGE, so
|
|
124
|
+
* a paged list passes its page size (the tickets list passes its own); `5` is the typical count for a list with none.
|
|
126
125
|
*/
|
|
127
126
|
export function DevCardRowSkeletonList({
|
|
128
127
|
region = true,
|
|
@@ -130,6 +129,7 @@ export function DevCardRowSkeletonList({
|
|
|
130
129
|
}: {
|
|
131
130
|
/** The loading state announces itself (`LoadingRegion`). `false` inside another loading region. Default `true`. */
|
|
132
131
|
region?: boolean;
|
|
132
|
+
/** Rows to draw: the list's page size. Default `5`. */
|
|
133
133
|
rows?: number;
|
|
134
134
|
}) {
|
|
135
135
|
return (
|
|
@@ -159,7 +159,7 @@ const SUMMARY_CLASS = 'text-ods-text-primary text-h4';
|
|
|
159
159
|
* release carries more tags than a phone's line holds (three badges), so the row is capped to that line and drawn the
|
|
160
160
|
* same while it is read: the badges and the "+N" never move the page under them.
|
|
161
161
|
*/
|
|
162
|
-
const
|
|
162
|
+
const RELEASE_TAG_ROW_MAX = 3;
|
|
163
163
|
/**
|
|
164
164
|
* A release tag's typical label width while it is read: a release is tagged with product areas, longer than most tags
|
|
165
165
|
* (sampled badges measure 64 to 155px, typically about 105px: ten characters of the badge's type), so its row wraps
|
|
@@ -400,7 +400,7 @@ export function ReleaseDetailPage({
|
|
|
400
400
|
{/* Tags — flat product_release_tags[] from entity_tags */}
|
|
401
401
|
<EntityTagBadges
|
|
402
402
|
tags={view.product_release_tags as TagAssoc[] | undefined}
|
|
403
|
-
max={
|
|
403
|
+
max={RELEASE_TAG_ROW_MAX}
|
|
404
404
|
skeletonItemClassName={RELEASE_TAG_SKELETON_WIDTH_CLASS}
|
|
405
405
|
/>
|
|
406
406
|
|
|
@@ -17,10 +17,16 @@ export function roadmapGridClass(columns: 1 | 2, showLeftMargin: boolean): strin
|
|
|
17
17
|
}`;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Cards the grid's skeleton draws when its host states no count: a typical count. The roadmap lists every item it
|
|
22
|
+
* holds (no page, no limit); a host that reads a known number of items passes it as `count`.
|
|
23
|
+
*/
|
|
24
|
+
export const ROADMAP_GRID_SKELETON_CARDS = 4;
|
|
25
|
+
|
|
20
26
|
export interface RoadmapGridSkeletonProps {
|
|
21
27
|
/** The loading state announces itself (`LoadingRegion`). `false` inside another loading region. Default `true`. */
|
|
22
28
|
region?: boolean;
|
|
23
|
-
/**
|
|
29
|
+
/** Skeleton cards: the number of items the host reads. Default `ROADMAP_GRID_SKELETON_CARDS`, a typical count. */
|
|
24
30
|
count?: number;
|
|
25
31
|
/** Show the desktop left margin (~120px) that aligns the grid with
|
|
26
32
|
* the page hero's title block. Default `true`. Related-content rails
|
|
@@ -32,7 +38,7 @@ export interface RoadmapGridSkeletonProps {
|
|
|
32
38
|
|
|
33
39
|
export function RoadmapGridSkeleton({
|
|
34
40
|
region = true,
|
|
35
|
-
count =
|
|
41
|
+
count = ROADMAP_GRID_SKELETON_CARDS,
|
|
36
42
|
showLeftMargin = true,
|
|
37
43
|
columns = 2,
|
|
38
44
|
}: RoadmapGridSkeletonProps) {
|
|
@@ -126,7 +126,12 @@ export interface RoadmapGridProps {
|
|
|
126
126
|
quartersToKeepClosed?: number;
|
|
127
127
|
/** The items are being read (the host's query runs): the grid draws its own skeleton. A page's loading scope does too. */
|
|
128
128
|
isLoading?: boolean;
|
|
129
|
-
/**
|
|
129
|
+
/**
|
|
130
|
+
* The cards the grid's skeleton draws: the number of items the host reads (its limit), drawn at every width.
|
|
131
|
+
* Default `ROADMAP_GRID_SKELETON_CARDS`, a typical count for a roadmap that lists everything it holds.
|
|
132
|
+
*/
|
|
133
|
+
skeletonCount?: number;
|
|
134
|
+
/** @deprecated Use `skeletonCount`. */
|
|
130
135
|
fold?: number;
|
|
131
136
|
}
|
|
132
137
|
|
|
@@ -184,9 +189,12 @@ const QUARTER_CONTENT_INNER_CLASS = 'pb-4';
|
|
|
184
189
|
export function RoadmapGridQuarterSkeleton({
|
|
185
190
|
showLeftMargin = true,
|
|
186
191
|
columns = 2,
|
|
192
|
+
count,
|
|
187
193
|
}: {
|
|
188
194
|
showLeftMargin?: boolean;
|
|
189
195
|
columns?: 1 | 2;
|
|
196
|
+
/** The quarter's skeleton cards. Default the grid skeleton's own. */
|
|
197
|
+
count?: number;
|
|
190
198
|
}) {
|
|
191
199
|
return (
|
|
192
200
|
<div className={QUARTER_LIST_CLASS} aria-hidden>
|
|
@@ -198,7 +206,7 @@ export function RoadmapGridQuarterSkeleton({
|
|
|
198
206
|
{/* The quarter's content box: the `AccordionContent` padding (`pt-4` here, the content's own `pb-4` inside). */}
|
|
199
207
|
<div className={QUARTER_CONTENT_CLASS}>
|
|
200
208
|
<div className={QUARTER_CONTENT_INNER_CLASS}>
|
|
201
|
-
<RoadmapGridSkeleton region={false} showLeftMargin={showLeftMargin} columns={columns} />
|
|
209
|
+
<RoadmapGridSkeleton region={false} count={count} showLeftMargin={showLeftMargin} columns={columns} />
|
|
202
210
|
</div>
|
|
203
211
|
</div>
|
|
204
212
|
</div>
|
|
@@ -217,8 +225,10 @@ export function RoadmapGrid({
|
|
|
217
225
|
hasActiveFilters = false,
|
|
218
226
|
quartersToKeepClosed = 2,
|
|
219
227
|
isLoading = false,
|
|
228
|
+
skeletonCount,
|
|
220
229
|
fold,
|
|
221
230
|
}: RoadmapGridProps) {
|
|
231
|
+
const skeletonCards = skeletonCount ?? fold;
|
|
222
232
|
// ── Voting (shared across all quarters) ──
|
|
223
233
|
const { getVote, toggleVote } = useRoadmapVoting(votingOptions);
|
|
224
234
|
const inScope = useLoadingScope();
|
|
@@ -313,9 +323,9 @@ export function RoadmapGrid({
|
|
|
313
323
|
// empty state. The page's loading region announces a scope's; a host's own read announces itself.
|
|
314
324
|
if (inScope || isLoading) {
|
|
315
325
|
return groupByQuarter ? (
|
|
316
|
-
<RoadmapGridQuarterSkeleton showLeftMargin={showLeftMargin} columns={columns} />
|
|
326
|
+
<RoadmapGridQuarterSkeleton showLeftMargin={showLeftMargin} columns={columns} count={skeletonCards} />
|
|
317
327
|
) : (
|
|
318
|
-
<RoadmapGridSkeleton region={!inScope} count={
|
|
328
|
+
<RoadmapGridSkeleton region={!inScope} count={skeletonCards} showLeftMargin={showLeftMargin} columns={columns} />
|
|
319
329
|
);
|
|
320
330
|
}
|
|
321
331
|
|
|
@@ -48,7 +48,7 @@ import { UnifiedPagination } from '../unified-pagination';
|
|
|
48
48
|
import { HelpCenterCard } from './help-center-card';
|
|
49
49
|
import { HelpCenterCreateForm, HelpCenterCreateFormSkeleton } from './help-center-create-form';
|
|
50
50
|
import { useTicketActions } from './hooks/use-ticket-actions';
|
|
51
|
-
import { useTicketsList } from './hooks/use-tickets-list';
|
|
51
|
+
import { TICKETS_PAGE_SIZE, useTicketsList } from './hooks/use-tickets-list';
|
|
52
52
|
import type { AnyTicket, OptimisticTicket, TicketsCacheSlot } from './types';
|
|
53
53
|
import { isOptimistic } from './types';
|
|
54
54
|
|
|
@@ -105,7 +105,7 @@ export function HelpCenterList({ toast = defaultToast, backButton, title, shell
|
|
|
105
105
|
shell={shell}
|
|
106
106
|
preControls={<HelpCenterCreateFormSkeleton />}
|
|
107
107
|
>
|
|
108
|
-
<DevCardRowSkeletonList />
|
|
108
|
+
<DevCardRowSkeletonList rows={TICKETS_PAGE_SIZE} />
|
|
109
109
|
</DevSectionPage>
|
|
110
110
|
);
|
|
111
111
|
}
|
|
@@ -206,7 +206,7 @@ function HelpCenterListAuthed({
|
|
|
206
206
|
[searchParams, router, pathname],
|
|
207
207
|
);
|
|
208
208
|
|
|
209
|
-
const { tickets, isLoading, isFetching, error, refetch, totalPages } = useTicketsList({
|
|
209
|
+
const { tickets, isLoading, isFetching, error, refetch, totalPages, pageSize } = useTicketsList({
|
|
210
210
|
// `sessionEmail` is drilled in from the parent — see the same
|
|
211
211
|
// pattern + race-cause rationale documented in
|
|
212
212
|
// `HelpCenterCreateForm.sessionName/sessionEmail`. Calling
|
|
@@ -330,7 +330,7 @@ function HelpCenterListAuthed({
|
|
|
330
330
|
{!error && (
|
|
331
331
|
<div className="w-full">
|
|
332
332
|
{isLoading ? (
|
|
333
|
-
<DevCardRowSkeletonList />
|
|
333
|
+
<DevCardRowSkeletonList rows={pageSize} />
|
|
334
334
|
) : !hasResults && isFetching ? (
|
|
335
335
|
// Bridge state — background refetch in flight and the
|
|
336
336
|
// optimistic placeholder was just removed by the mutation
|
|
@@ -27,7 +27,8 @@ import { PAGE_PARAM_LIMITS, pageCount, positiveInt } from '../../../utils/search
|
|
|
27
27
|
import type { TicketData } from '../types';
|
|
28
28
|
|
|
29
29
|
const FIND_TICKET_ENDPOINT = '/api/chat/agent/find-ticket';
|
|
30
|
-
|
|
30
|
+
/** The tickets a page of the list loads unless the host asks for another size; also its skeleton's row count. */
|
|
31
|
+
export const TICKETS_PAGE_SIZE = 20;
|
|
31
32
|
|
|
32
33
|
interface FindTicketResponse {
|
|
33
34
|
tickets?: TicketData[];
|
|
@@ -101,7 +102,7 @@ export function useTicketsList(filters: UseTicketsListFilters): UseTicketsListRe
|
|
|
101
102
|
const status = (filters.status ?? '').trim().toLowerCase();
|
|
102
103
|
const statusFilter = status && status !== 'all' ? status : '';
|
|
103
104
|
const page = positiveInt(filters.page, 1);
|
|
104
|
-
const pageSize = positiveInt(filters.pageSize,
|
|
105
|
+
const pageSize = positiveInt(filters.pageSize, TICKETS_PAGE_SIZE, { max: PAGE_PARAM_LIMITS.maxPageSize });
|
|
105
106
|
|
|
106
107
|
// `customerEmail` is the source of truth — parent (HelpCenterList)
|
|
107
108
|
// already gates on `identity.user?.email` being truthy before
|