@flamingo-stack/openframe-frontend-core 0.0.203 → 0.0.204
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-3YH2M76N.cjs → chunk-ARQ4XP64.cjs} +53 -47
- package/dist/chunk-ARQ4XP64.cjs.map +1 -0
- package/dist/{chunk-E6Q6UGDK.js → chunk-LY34ORX6.js} +53 -47
- package/dist/chunk-LY34ORX6.js.map +1 -0
- package/dist/components/features/index.cjs +2 -2
- package/dist/components/features/index.js +1 -1
- package/dist/components/index.cjs +2 -2
- package/dist/components/index.js +1 -1
- package/dist/components/navigation/index.cjs +2 -2
- package/dist/components/navigation/index.js +1 -1
- package/dist/components/navigation/navigation-sidebar.d.ts.map +1 -1
- package/dist/components/shared/product-release/product-release-card-skeleton.d.ts.map +1 -1
- package/dist/components/shared/product-release/product-release-card.d.ts.map +1 -1
- package/dist/components/ui/index.cjs +2 -2
- package/dist/components/ui/index.js +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/components/navigation/navigation-sidebar.tsx +3 -1
- package/src/components/shared/product-release/product-release-card-skeleton.tsx +58 -26
- package/src/components/shared/product-release/product-release-card.tsx +164 -127
- package/dist/chunk-3YH2M76N.cjs.map +0 -1
- package/dist/chunk-E6Q6UGDK.js.map +0 -1
|
@@ -157,46 +157,60 @@ export function ProductReleaseCard({
|
|
|
157
157
|
(changelogCounts?.breaking ?? 0)
|
|
158
158
|
|
|
159
159
|
// Build the metadata-grid cell array — mirrors the hub's
|
|
160
|
-
// EntityAuthorCard composition
|
|
161
|
-
//
|
|
162
|
-
//
|
|
160
|
+
// EntityAuthorCard composition. ALWAYS render all 3 value cells
|
|
161
|
+
// (Type / Status / Released) — missing values render as a plain
|
|
162
|
+
// em-dash + label so the grid keeps a fixed 4-cell shape (matching
|
|
163
|
+
// the skeleton). The Author cell is also always rendered below
|
|
164
|
+
// (effectiveAuthor falls back to a placeholder shape). This is
|
|
165
|
+
// load-to-resolve baseline parity: any conditional cell would
|
|
166
|
+
// introduce a reflow when the skeleton resolves.
|
|
167
|
+
//
|
|
168
|
+
// Plan note: em-dash placeholders read as plain text (NOT a colored
|
|
169
|
+
// StatusBadge for the Type cell) so empty badges don't look broken
|
|
170
|
+
// next to populated badges.
|
|
163
171
|
type ValueCell = {
|
|
164
172
|
value: string
|
|
165
173
|
label: string
|
|
166
174
|
uppercase: boolean
|
|
167
175
|
colorScheme?: 'error' | 'cyan' | 'success' | 'warning'
|
|
168
176
|
}
|
|
169
|
-
const valueCells: ValueCell[] = [
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
//
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
177
|
+
const valueCells: ValueCell[] = [
|
|
178
|
+
releaseType && releaseTypeBadgeColor
|
|
179
|
+
? {
|
|
180
|
+
value: releaseType.toUpperCase(),
|
|
181
|
+
label: 'Type',
|
|
182
|
+
uppercase: true,
|
|
183
|
+
colorScheme: releaseTypeBadgeColor,
|
|
184
|
+
}
|
|
185
|
+
: { value: '—', label: 'Type', uppercase: false },
|
|
186
|
+
releaseStatus
|
|
187
|
+
? {
|
|
188
|
+
value: releaseStatus.toUpperCase(),
|
|
189
|
+
label: 'Status',
|
|
190
|
+
uppercase: true,
|
|
191
|
+
}
|
|
192
|
+
: { value: '—', label: 'Status', uppercase: false },
|
|
193
|
+
formattedDate
|
|
194
|
+
? {
|
|
195
|
+
value: formattedDate,
|
|
196
|
+
label: 'Released',
|
|
197
|
+
uppercase: false,
|
|
198
|
+
}
|
|
199
|
+
: { value: '—', label: 'Released', uppercase: false },
|
|
200
|
+
]
|
|
201
|
+
// EMPTY_AUTHOR_PLACEHOLDER shape — mirrors the hub's
|
|
202
|
+
// EMPTY_AUTHOR_PLACEHOLDER constant exported from
|
|
203
|
+
// components/shared/entity-author-card.tsx (hub can't be imported
|
|
204
|
+
// here; the two are kept in lockstep per the inline-duplication
|
|
205
|
+
// policy documented in the catalog branch comment above).
|
|
206
|
+
const effectiveAuthor = author?.full_name
|
|
207
|
+
? author
|
|
208
|
+
: { full_name: '—', avatar_url: null, job_title: 'Unknown' }
|
|
209
|
+
// Fixed 4-cell grid (Type / Status / Released / Author) so the
|
|
210
|
+
// skeleton's shape matches the loaded card exactly. The earlier
|
|
211
|
+
// dynamic `gridColsClass` ternary collapsed missing cells and
|
|
212
|
+
// caused 28-56px reflow on resolve.
|
|
213
|
+
const gridColsClass = 'md:grid-cols-4'
|
|
200
214
|
const dividerClass = 'border-b md:border-b-0 md:border-r border-ods-border'
|
|
201
215
|
|
|
202
216
|
const frameClass = cn(
|
|
@@ -242,107 +256,130 @@ export function ProductReleaseCard({
|
|
|
242
256
|
v{version}
|
|
243
257
|
</span>
|
|
244
258
|
</div>
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
259
|
+
{/* Title — reserve a fixed 2-line height so cards with
|
|
260
|
+
1-line titles don't shrink and the catalog skeleton-to-
|
|
261
|
+
content transition is shift-free. Mirrors the
|
|
262
|
+
onboarding-guide catalog card. */}
|
|
263
|
+
<div className="min-h-[60px] md:min-h-[72px] flex items-start mb-3">
|
|
264
|
+
<h3 className="font-['Azeret_Mono'] font-semibold text-xl md:text-2xl text-ods-text-primary leading-tight line-clamp-2">
|
|
265
|
+
{title}
|
|
266
|
+
</h3>
|
|
267
|
+
</div>
|
|
268
|
+
{/* Summary — fixed 3-line height. `line-clamp-3` caps long
|
|
269
|
+
summaries at 3 lines; `min-h` reserves the same vertical
|
|
270
|
+
space when content is shorter, so the catalog grid stays
|
|
271
|
+
row-consistent regardless of per-card content length.
|
|
272
|
+
Heights derived from text-sm md:text-base × leading-relaxed
|
|
273
|
+
(1.625): 14×1.625×3 ≈ 68 px mobile, 16×1.625×3 ≈ 78 px desktop. */}
|
|
274
|
+
<div className="min-h-[68px] md:min-h-[78px]">
|
|
275
|
+
<p className="font-['DM_Sans'] text-sm md:text-base text-ods-text-secondary leading-relaxed line-clamp-3">
|
|
276
|
+
{summary ?? ''}
|
|
251
277
|
</p>
|
|
252
|
-
|
|
278
|
+
</div>
|
|
253
279
|
</div>
|
|
254
280
|
</div>
|
|
255
281
|
|
|
256
|
-
{/* CHANGELOG STRIP —
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
282
|
+
{/* CHANGELOG STRIP — ALWAYS rendered so the skeleton's
|
|
283
|
+
always-on changelog placeholder matches the loaded shape
|
|
284
|
+
(zero reflow on resolve). When `totalChangelog === 0`, an
|
|
285
|
+
empty-state line takes the same vertical space as the
|
|
286
|
+
populated row. */}
|
|
287
|
+
<div className="border-t border-ods-border pt-3 flex flex-wrap items-center gap-x-4 gap-y-1.5 font-['DM_Sans'] text-sm text-ods-text-secondary">
|
|
288
|
+
{totalChangelog > 0 && changelogCounts ? (
|
|
289
|
+
<>
|
|
290
|
+
{changelogCounts.features > 0 && (
|
|
291
|
+
<span className="inline-flex items-center gap-1.5">
|
|
292
|
+
<Sparkles className="w-3.5 h-3.5" />
|
|
293
|
+
{changelogCounts.features} {changelogCounts.features === 1 ? 'feature' : 'features'}
|
|
294
|
+
</span>
|
|
295
|
+
)}
|
|
296
|
+
{changelogCounts.fixes > 0 && (
|
|
297
|
+
<span className="inline-flex items-center gap-1.5">
|
|
298
|
+
<Wrench className="w-3.5 h-3.5" />
|
|
299
|
+
{changelogCounts.fixes} {changelogCounts.fixes === 1 ? 'fix' : 'fixes'}
|
|
300
|
+
</span>
|
|
301
|
+
)}
|
|
302
|
+
{changelogCounts.improvements > 0 && (
|
|
303
|
+
<span className="inline-flex items-center gap-1.5">
|
|
304
|
+
<TrendingUp className="w-3.5 h-3.5" />
|
|
305
|
+
{changelogCounts.improvements} {changelogCounts.improvements === 1 ? 'improvement' : 'improvements'}
|
|
306
|
+
</span>
|
|
307
|
+
)}
|
|
308
|
+
{changelogCounts.breaking > 0 && (
|
|
309
|
+
<span className="inline-flex items-center gap-1.5 text-[var(--ods-attention-yellow-warning)]">
|
|
310
|
+
<AlertTriangle className="w-3.5 h-3.5" />
|
|
311
|
+
{changelogCounts.breaking} breaking
|
|
312
|
+
</span>
|
|
313
|
+
)}
|
|
314
|
+
</>
|
|
315
|
+
) : (
|
|
316
|
+
<span className="text-sm text-ods-text-secondary">No changelog entries yet</span>
|
|
317
|
+
)}
|
|
318
|
+
</div>
|
|
285
319
|
|
|
286
|
-
{/* METADATA GRID FOOTER —
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
>
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
</div>
|
|
324
|
-
))}
|
|
325
|
-
{hasAuthorCell && author && (
|
|
326
|
-
<div className="bg-ods-card p-4 flex items-center gap-3">
|
|
327
|
-
<SquareAvatar
|
|
328
|
-
src={author.avatar_url ?? undefined}
|
|
329
|
-
alt={author.full_name}
|
|
330
|
-
fallback={author.full_name.charAt(0).toUpperCase()}
|
|
331
|
-
size="md"
|
|
332
|
-
variant="round"
|
|
333
|
-
/>
|
|
334
|
-
<div className="flex flex-col gap-0 flex-1 min-w-0">
|
|
335
|
-
<p className="text-h3 tracking-[-0.36px] text-ods-text-primary truncate">
|
|
336
|
-
{author.full_name}
|
|
337
|
-
</p>
|
|
338
|
-
<p className="font-['DM_Sans'] font-medium text-[14px] leading-[20px] text-ods-text-secondary">
|
|
339
|
-
{author.job_title || 'Author'}
|
|
320
|
+
{/* METADATA GRID FOOTER — fixed 4-cell shape (Type / Status /
|
|
321
|
+
Released / Author) so the skeleton mirrors the loaded card
|
|
322
|
+
exactly. Empty value cells render em-dash + label (plain
|
|
323
|
+
text, no colored badge — em-dash badges read as broken next
|
|
324
|
+
to populated ones); the Author cell falls back to the
|
|
325
|
+
EMPTY_AUTHOR_PLACEHOLDER shape declared above. */}
|
|
326
|
+
<div
|
|
327
|
+
className={cn(
|
|
328
|
+
'grid grid-cols-1',
|
|
329
|
+
gridColsClass,
|
|
330
|
+
'border border-ods-border rounded-md overflow-hidden w-full',
|
|
331
|
+
)}
|
|
332
|
+
>
|
|
333
|
+
{valueCells.map((cell, i) => (
|
|
334
|
+
<div
|
|
335
|
+
key={`${cell.label}-${i}`}
|
|
336
|
+
className={cn('bg-ods-card p-4 flex flex-col gap-3', dividerClass)}
|
|
337
|
+
>
|
|
338
|
+
<div className="flex flex-col gap-0">
|
|
339
|
+
{cell.colorScheme ? (
|
|
340
|
+
<StatusBadge
|
|
341
|
+
text={cell.value}
|
|
342
|
+
variant="card"
|
|
343
|
+
colorScheme={cell.colorScheme}
|
|
344
|
+
singleLine
|
|
345
|
+
className="self-start"
|
|
346
|
+
/>
|
|
347
|
+
) : (
|
|
348
|
+
<p
|
|
349
|
+
className={cn(
|
|
350
|
+
'text-h4',
|
|
351
|
+
// Em-dash placeholder reads as secondary text;
|
|
352
|
+
// populated values stay primary.
|
|
353
|
+
cell.value === '—' ? 'text-ods-text-secondary' : 'text-ods-text-primary',
|
|
354
|
+
)}
|
|
355
|
+
>
|
|
356
|
+
{cell.uppercase ? cell.value.toLocaleUpperCase() : cell.value}
|
|
340
357
|
</p>
|
|
341
|
-
|
|
358
|
+
)}
|
|
359
|
+
<p className="font-['DM_Sans'] font-medium text-[14px] leading-[20px] text-ods-text-secondary">
|
|
360
|
+
{cell.label}
|
|
361
|
+
</p>
|
|
342
362
|
</div>
|
|
343
|
-
|
|
363
|
+
</div>
|
|
364
|
+
))}
|
|
365
|
+
<div className="bg-ods-card p-4 flex items-center gap-3">
|
|
366
|
+
<SquareAvatar
|
|
367
|
+
src={effectiveAuthor.avatar_url ?? undefined}
|
|
368
|
+
alt={effectiveAuthor.full_name}
|
|
369
|
+
fallback={effectiveAuthor.full_name.charAt(0).toUpperCase()}
|
|
370
|
+
size="md"
|
|
371
|
+
variant="round"
|
|
372
|
+
/>
|
|
373
|
+
<div className="flex flex-col gap-0 flex-1 min-w-0">
|
|
374
|
+
<p className="text-h3 tracking-[-0.36px] text-ods-text-primary truncate">
|
|
375
|
+
{effectiveAuthor.full_name}
|
|
376
|
+
</p>
|
|
377
|
+
<p className="font-['DM_Sans'] font-medium text-[14px] leading-[20px] text-ods-text-secondary">
|
|
378
|
+
{effectiveAuthor.job_title || 'Author'}
|
|
379
|
+
</p>
|
|
380
|
+
</div>
|
|
344
381
|
</div>
|
|
345
|
-
|
|
382
|
+
</div>
|
|
346
383
|
|
|
347
384
|
{typeof viewCount === 'number' && viewCount > 0 && (
|
|
348
385
|
<div className="flex items-center gap-1.5 text-xs text-ods-text-secondary">
|