@softize/opus 27.0.0 → 28.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +73 -0
- package/bin/lib/check.mjs +139 -30
- package/docs/adr/0015-action-size-follows-interaction-density.md +2 -0
- package/docs/adr/0017-ui-assumes-a-fixed-desktop-layout.md +14 -8
- package/docs/adr/0029-page-header-is-the-single-page-introduction.md +3 -1
- package/docs/adr/0057-doc-browser-permite-busca-no-chrome-do-consumidor.md +39 -5
- package/docs/adr/0060-surface-composition-separates-chrome-and-intro.md +161 -0
- package/docs/adr/README.md +2 -0
- package/package.json +1 -1
- package/registry/skills/build-opus-ui/SKILL.md +11 -11
- package/registry/skills/build-opus-ui/references/evaluations.md +10 -0
- package/registry/skills/build-opus-ui/references/ui-patterns.md +17 -15
- package/src/ui/components/patterns/list.tsx +62 -53
- package/src/ui/components/patterns/page.tsx +146 -38
- package/src/ui/components/patterns/presentation.tsx +44 -59
- package/src/ui/components/patterns/structural-header.tsx +42 -6
- package/src/ui/components/patterns/surface-anatomy.ts +52 -22
- package/src/ui/components/patterns/surface.tsx +2 -3
- package/src/ui/components/primitives/control.ts +2 -2
- package/src/ui/components/primitives/dialog.tsx +7 -2
- package/src/ui/components/primitives/drawer.tsx +7 -2
- package/src/ui/components/primitives/icon-picker.tsx +271 -34
- package/src/ui/components/primitives/pagination.tsx +5 -4
- package/src/ui/components/primitives/tabs.tsx +15 -2
- package/src/ui/components/primitives/toggle.tsx +1 -1
- package/src/ui/docs/DocBrowser.tsx +122 -12
- package/src/ui/docs/content/action-list.md +11 -2
- package/src/ui/docs/content/button.md +7 -7
- package/src/ui/docs/content/compositions.md +35 -23
- package/src/ui/docs/content/dialog.md +18 -6
- package/src/ui/docs/content/documentation-portal.md +40 -1
- package/src/ui/docs/content/drawer.md +17 -5
- package/src/ui/docs/content/icon-picker.md +37 -19
- package/src/ui/docs/content/input.md +1 -1
- package/src/ui/docs/content/page.md +169 -69
- package/src/ui/docs/content/pagination.md +7 -8
- package/src/ui/docs/content/presentation.md +4 -3
- package/src/ui/docs/content/surface.md +8 -5
- package/src/ui/docs/content/tabs.md +12 -4
- package/src/ui/docs/types.ts +2 -0
- package/src/ui/meta.ts +1 -1
- package/src/ui/react.tsx +9 -1
|
@@ -31,15 +31,27 @@ import {
|
|
|
31
31
|
} from "./page-heading-context.tsx";
|
|
32
32
|
import {
|
|
33
33
|
StructuralHeader,
|
|
34
|
+
validateSurfaceIntro,
|
|
34
35
|
structuralHeaderClasses,
|
|
35
36
|
} from "./structural-header.tsx";
|
|
36
37
|
import { PageState, PageStatePresenceProvider } from "./page-state.tsx";
|
|
37
|
-
import { FillRoot,
|
|
38
|
+
import { FillRoot, useFillRegion } from "./fill-region.ts";
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
import {
|
|
41
|
+
surfaceHeaderRowClasses,
|
|
42
|
+
surfaceRegionClasses,
|
|
43
|
+
type SurfaceLayout,
|
|
44
|
+
} from "./surface-anatomy.ts";
|
|
45
|
+
|
|
46
|
+
interface PageContextValue {
|
|
47
|
+
surfaceLayout: SurfaceLayout;
|
|
48
|
+
contentClassName?: string;
|
|
49
|
+
}
|
|
40
50
|
|
|
41
51
|
const PageContext = createContext<PageContextValue | null>(null);
|
|
42
52
|
const PageHeaderContext = createContext(false);
|
|
53
|
+
const PageIntroContext = createContext(false);
|
|
54
|
+
const PageBodyContext = createContext(false);
|
|
43
55
|
const PageIntegralStateContext = createContext(false);
|
|
44
56
|
const PageActionsTargetContext = createContext<HTMLElement | null>(null);
|
|
45
57
|
interface PageShellContextValue {
|
|
@@ -101,7 +113,7 @@ export function PageShell({
|
|
|
101
113
|
<PageShellContext.Provider value={shell}>
|
|
102
114
|
<div
|
|
103
115
|
data-slot="page-shell-content"
|
|
104
|
-
className="min-h-0 min-w-0 flex-1 overflow-auto [&>*]:min-w-[64rem]"
|
|
116
|
+
className="flex min-h-0 min-w-0 flex-1 flex-col overflow-x-auto overflow-y-auto [&>*]:min-w-[64rem]"
|
|
105
117
|
>
|
|
106
118
|
{children}
|
|
107
119
|
</div>
|
|
@@ -136,13 +148,17 @@ interface PageBaseProps extends Omit<
|
|
|
136
148
|
HTMLAttributes<HTMLElement>,
|
|
137
149
|
"children" | "title"
|
|
138
150
|
> {
|
|
139
|
-
/** Classes do container.
|
|
151
|
+
/** Classes do container externo. O conteúdo do Body mantém o teto de 80rem. */
|
|
140
152
|
className?: string;
|
|
153
|
+
/** Classes do conteúdo centralizado, incluindo largura máxima e alinhamento. */
|
|
154
|
+
contentClassName?: string;
|
|
141
155
|
/**
|
|
142
|
-
* `
|
|
156
|
+
* `flow` mantém a rolagem no body; `fill` permite que regiões internas assumam a rolagem.
|
|
143
157
|
* A altura chega sozinha a uma `ActionList` composta por `PageBody` e `Surface` (ADR 0024).
|
|
144
158
|
*/
|
|
145
159
|
layout?: "flow" | "fill";
|
|
160
|
+
/** Divisão visual das regiões, independente da distribuição de altura. */
|
|
161
|
+
surfaceLayout?: SurfaceLayout;
|
|
146
162
|
}
|
|
147
163
|
|
|
148
164
|
interface PageShorthandProps extends PageBaseProps {
|
|
@@ -183,6 +199,7 @@ function countDirectChildrenOfType(
|
|
|
183
199
|
|
|
184
200
|
function countPageBodyHeadings(node: ReactNode): {
|
|
185
201
|
content: number;
|
|
202
|
+
intro: number;
|
|
186
203
|
state: number;
|
|
187
204
|
} {
|
|
188
205
|
return Children.toArray(node).reduce(
|
|
@@ -196,6 +213,7 @@ function countPageBodyHeadings(node: ReactNode): {
|
|
|
196
213
|
if (child.type === PageState && props.status !== "ready") {
|
|
197
214
|
return {
|
|
198
215
|
content: total.content,
|
|
216
|
+
intro: total.intro,
|
|
199
217
|
state: total.state + 1,
|
|
200
218
|
};
|
|
201
219
|
}
|
|
@@ -205,10 +223,11 @@ function countPageBodyHeadings(node: ReactNode): {
|
|
|
205
223
|
total.content +
|
|
206
224
|
(child.type === Surface && props.level === 1 ? 1 : 0) +
|
|
207
225
|
nested.content,
|
|
226
|
+
intro: total.intro + (child.type === PageIntro ? 1 : 0) + nested.intro,
|
|
208
227
|
state: total.state + nested.state,
|
|
209
228
|
};
|
|
210
229
|
},
|
|
211
|
-
{ content: 0, state: 0 },
|
|
230
|
+
{ content: 0, intro: 0, state: 0 },
|
|
212
231
|
);
|
|
213
232
|
}
|
|
214
233
|
|
|
@@ -229,12 +248,13 @@ export function Page({
|
|
|
229
248
|
description,
|
|
230
249
|
actions,
|
|
231
250
|
layout = "flow",
|
|
251
|
+
surfaceLayout = "sectioned",
|
|
252
|
+
contentClassName,
|
|
232
253
|
className,
|
|
233
254
|
children,
|
|
234
255
|
...props
|
|
235
256
|
}: PageProps): ReactElement {
|
|
236
257
|
const shell = useContext(PageShellContext);
|
|
237
|
-
const insideShell = shell !== null;
|
|
238
258
|
const shorthand = title !== undefined;
|
|
239
259
|
const nodes = flattenPageHeaderChildren(children);
|
|
240
260
|
const headers = nodes.filter(
|
|
@@ -298,6 +318,7 @@ export function Page({
|
|
|
298
318
|
);
|
|
299
319
|
}
|
|
300
320
|
const bodyHeadingSources = bodies.reduce<{
|
|
321
|
+
intro: number;
|
|
301
322
|
content: number;
|
|
302
323
|
state: number;
|
|
303
324
|
}>(
|
|
@@ -307,17 +328,20 @@ export function Page({
|
|
|
307
328
|
);
|
|
308
329
|
return {
|
|
309
330
|
content: total.content + count.content,
|
|
331
|
+
intro: total.intro + count.intro,
|
|
310
332
|
state: total.state + count.state,
|
|
311
333
|
};
|
|
312
334
|
},
|
|
313
|
-
{ content: 0, state: 0 },
|
|
335
|
+
{ content: 0, intro: 0, state: 0 },
|
|
314
336
|
);
|
|
315
337
|
const structuralHeadingSources =
|
|
316
338
|
headers.reduce<number>(
|
|
317
339
|
(total, header) =>
|
|
318
340
|
total + countDirectChildrenOfType(header as ReactElement, PageTitle),
|
|
319
341
|
0,
|
|
320
|
-
) +
|
|
342
|
+
) +
|
|
343
|
+
bodyHeadingSources.content +
|
|
344
|
+
bodyHeadingSources.intro;
|
|
321
345
|
const headingSources =
|
|
322
346
|
bodyHeadingSources.state > 0
|
|
323
347
|
? bodyHeadingSources.state + bodyHeadingSources.content
|
|
@@ -329,7 +353,7 @@ export function Page({
|
|
|
329
353
|
);
|
|
330
354
|
if (headingSources > 1 || (headingSources === 0 && !hasOpaqueBodyContent)) {
|
|
331
355
|
throw new Error(
|
|
332
|
-
"Page explícito exige exatamente um heading principal em PageHeader, Surface level={1} ou PageState ativo dentro de PageBody.",
|
|
356
|
+
"Page explícito exige exatamente um heading principal em PageHeader, PageIntro, Surface level={1} ou PageState ativo dentro de PageBody.",
|
|
333
357
|
);
|
|
334
358
|
}
|
|
335
359
|
}
|
|
@@ -338,20 +362,24 @@ export function Page({
|
|
|
338
362
|
if (integralState !== hasActiveIntegralState) return;
|
|
339
363
|
if (headingCount.current !== 1) {
|
|
340
364
|
throw new Error(
|
|
341
|
-
"Page explícito exige exatamente um heading principal em PageHeader, Surface level={1} ou PageState ativo dentro de PageBody.",
|
|
365
|
+
"Page explícito exige exatamente um heading principal em PageHeader, PageIntro, Surface level={1} ou PageState ativo dentro de PageBody.",
|
|
342
366
|
);
|
|
343
367
|
}
|
|
344
368
|
});
|
|
345
369
|
const content = shorthand ? (
|
|
346
370
|
<>
|
|
347
371
|
<PageHeader>
|
|
348
|
-
<PageTitle>{title}</PageTitle>
|
|
349
|
-
{description !== undefined && (
|
|
350
|
-
<PageDescription>{description}</PageDescription>
|
|
351
|
-
)}
|
|
352
372
|
{actions !== undefined && <PageActions>{actions}</PageActions>}
|
|
353
373
|
</PageHeader>
|
|
354
|
-
<PageBody>
|
|
374
|
+
<PageBody>
|
|
375
|
+
<PageIntro>
|
|
376
|
+
<PageTitle>{title}</PageTitle>
|
|
377
|
+
{description !== undefined && (
|
|
378
|
+
<PageDescription>{description}</PageDescription>
|
|
379
|
+
)}
|
|
380
|
+
</PageIntro>
|
|
381
|
+
{children}
|
|
382
|
+
</PageBody>
|
|
355
383
|
</>
|
|
356
384
|
) : (
|
|
357
385
|
<>
|
|
@@ -364,29 +392,22 @@ export function Page({
|
|
|
364
392
|
);
|
|
365
393
|
|
|
366
394
|
return (
|
|
367
|
-
<PageContext.Provider value={{}}>
|
|
395
|
+
<PageContext.Provider value={{ surfaceLayout, contentClassName }}>
|
|
368
396
|
<PageIntegralStateContext.Provider value={integralState}>
|
|
369
397
|
<PageStatePresenceProvider register={registerIntegralState}>
|
|
370
398
|
<PageHeadingPresenceProvider register={registerHeading}>
|
|
371
399
|
<main
|
|
372
400
|
data-slot="page"
|
|
373
401
|
data-layout={layout}
|
|
402
|
+
data-surface-layout={surfaceLayout}
|
|
374
403
|
className={cn(
|
|
375
|
-
"min-w-[64rem] flex-1",
|
|
376
|
-
insideShell && "min-h-full",
|
|
377
|
-
layout === "fill" &&
|
|
378
|
-
"flex h-full min-h-0 flex-col overflow-hidden",
|
|
379
|
-
integralState && "flex min-h-full flex-col",
|
|
404
|
+
"flex h-full min-h-0 min-w-[64rem] flex-1 flex-col overflow-hidden",
|
|
380
405
|
)}
|
|
381
406
|
{...props}
|
|
382
407
|
>
|
|
383
408
|
<div
|
|
384
409
|
className={cn(
|
|
385
|
-
"
|
|
386
|
-
layout === "fill" &&
|
|
387
|
-
"flex h-full min-h-0 w-full flex-1 flex-col",
|
|
388
|
-
integralState &&
|
|
389
|
-
"flex min-h-full w-full flex-1 flex-col space-y-0",
|
|
410
|
+
"flex h-full min-h-0 w-full flex-1 flex-col",
|
|
390
411
|
className,
|
|
391
412
|
)}
|
|
392
413
|
>
|
|
@@ -455,6 +476,7 @@ export function PageHeader({
|
|
|
455
476
|
if (
|
|
456
477
|
structuralChildren.length === 0 &&
|
|
457
478
|
mergedActions === null &&
|
|
479
|
+
localNavigation.length === 0 &&
|
|
458
480
|
shell?.navigation === undefined
|
|
459
481
|
) {
|
|
460
482
|
return <></>;
|
|
@@ -470,9 +492,25 @@ export function PageHeader({
|
|
|
470
492
|
description: PageDescription,
|
|
471
493
|
actions: PageActions,
|
|
472
494
|
}}
|
|
473
|
-
leadingPlacement=
|
|
495
|
+
leadingPlacement={
|
|
496
|
+
nodes.some(
|
|
497
|
+
(node) => isValidElement(node) && node.type === PageTitle,
|
|
498
|
+
) ||
|
|
499
|
+
(localNavigation.length === 0 &&
|
|
500
|
+
shell?.navigation === undefined &&
|
|
501
|
+
!nodes.some(
|
|
502
|
+
(node) => isValidElement(node) && node.type === PageBack,
|
|
503
|
+
))
|
|
504
|
+
? "above"
|
|
505
|
+
: "inline"
|
|
506
|
+
}
|
|
474
507
|
titleRequired={false}
|
|
475
|
-
|
|
508
|
+
rowClassName={surfaceHeaderRowClasses("page")}
|
|
509
|
+
className={cn(
|
|
510
|
+
"shrink-0",
|
|
511
|
+
surfaceRegionClasses("page", page!.surfaceLayout, "header"),
|
|
512
|
+
className,
|
|
513
|
+
)}
|
|
476
514
|
{...props}
|
|
477
515
|
>
|
|
478
516
|
{shell?.navigation === undefined ? (
|
|
@@ -487,12 +525,62 @@ export function PageHeader({
|
|
|
487
525
|
);
|
|
488
526
|
}
|
|
489
527
|
|
|
528
|
+
/** Primeiro bloco do corpo, separado dos controles do cabeçalho. */
|
|
529
|
+
export function PageIntro({
|
|
530
|
+
className,
|
|
531
|
+
children,
|
|
532
|
+
...props
|
|
533
|
+
}: HTMLAttributes<HTMLDivElement>): ReactElement {
|
|
534
|
+
requireParent(useContext(PageBodyContext), "PageIntro", "PageBody");
|
|
535
|
+
const integralState = useContext(PageIntegralStateContext);
|
|
536
|
+
if (integralState) return <></>;
|
|
537
|
+
return (
|
|
538
|
+
<PageIntroContext.Provider value>
|
|
539
|
+
<StructuralHeader
|
|
540
|
+
name="PageIntro"
|
|
541
|
+
slot="page-intro"
|
|
542
|
+
slots={{
|
|
543
|
+
title: PageTitle,
|
|
544
|
+
description: PageDescription,
|
|
545
|
+
actions: PageIntroActions,
|
|
546
|
+
}}
|
|
547
|
+
leadingPlacement="inline"
|
|
548
|
+
titleRequired
|
|
549
|
+
className={className}
|
|
550
|
+
{...props}
|
|
551
|
+
>
|
|
552
|
+
{children}
|
|
553
|
+
</StructuralHeader>
|
|
554
|
+
</PageIntroContext.Provider>
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/** Ações locais do conteúdo, distintas das ações da superfície em PageActions. */
|
|
559
|
+
export function PageIntroActions({
|
|
560
|
+
className,
|
|
561
|
+
...props
|
|
562
|
+
}: HTMLAttributes<HTMLDivElement>): ReactElement {
|
|
563
|
+
requireParent(useContext(PageIntroContext), "PageIntroActions", "PageIntro");
|
|
564
|
+
return (
|
|
565
|
+
<div
|
|
566
|
+
data-slot="page-intro-actions"
|
|
567
|
+
className={cn(structuralHeaderClasses.page.actions, className)}
|
|
568
|
+
{...props}
|
|
569
|
+
/>
|
|
570
|
+
);
|
|
571
|
+
}
|
|
572
|
+
|
|
490
573
|
export function PageTitle({
|
|
491
574
|
className,
|
|
492
575
|
...props
|
|
493
576
|
}: HTMLAttributes<HTMLHeadingElement>): ReactElement {
|
|
494
577
|
const insideHeader = useContext(PageHeaderContext);
|
|
495
|
-
|
|
578
|
+
const insideIntro = useContext(PageIntroContext);
|
|
579
|
+
requireParent(
|
|
580
|
+
insideHeader || insideIntro,
|
|
581
|
+
"PageTitle",
|
|
582
|
+
"PageHeader ou PageIntro",
|
|
583
|
+
);
|
|
496
584
|
usePageHeadingPresence();
|
|
497
585
|
return (
|
|
498
586
|
<h1
|
|
@@ -508,7 +596,9 @@ export function PageDescription({
|
|
|
508
596
|
className,
|
|
509
597
|
...props
|
|
510
598
|
}: HTMLAttributes<HTMLParagraphElement>): ReactElement {
|
|
511
|
-
|
|
599
|
+
const header = useContext(PageHeaderContext);
|
|
600
|
+
const intro = useContext(PageIntroContext);
|
|
601
|
+
requireParent(header || intro, "PageDescription", "PageHeader ou PageIntro");
|
|
512
602
|
return (
|
|
513
603
|
<p
|
|
514
604
|
data-slot="page-description"
|
|
@@ -574,7 +664,7 @@ export function PageBack({
|
|
|
574
664
|
typeof children === "string" ? children : "a página anterior";
|
|
575
665
|
const accessibleLabel = ariaLabel ?? `Voltar para ${destination}`;
|
|
576
666
|
const back = (
|
|
577
|
-
<Button asChild variant="ghost" size="icon">
|
|
667
|
+
<Button asChild variant="ghost" size="icon-sm">
|
|
578
668
|
<a
|
|
579
669
|
data-slot="page-back"
|
|
580
670
|
aria-label={accessibleLabel}
|
|
@@ -592,25 +682,42 @@ export function PageBack({
|
|
|
592
682
|
|
|
593
683
|
export function PageBody({
|
|
594
684
|
className,
|
|
685
|
+
contentClassName,
|
|
595
686
|
children,
|
|
596
687
|
...props
|
|
597
|
-
}: HTMLAttributes<HTMLDivElement>
|
|
688
|
+
}: HTMLAttributes<HTMLDivElement> & {
|
|
689
|
+
contentClassName?: string;
|
|
690
|
+
}): ReactElement {
|
|
598
691
|
const page = useContext(PageContext);
|
|
599
|
-
const integralState = useContext(PageIntegralStateContext);
|
|
600
692
|
const fill = useFillRegion();
|
|
601
693
|
requireParent(page !== null, "PageBody", "Page");
|
|
694
|
+
validateSurfaceIntro(children, PageIntro, "PageBody");
|
|
602
695
|
return (
|
|
603
696
|
<div
|
|
604
697
|
data-slot="page-body"
|
|
605
698
|
{...fill.attributes}
|
|
606
699
|
className={cn(
|
|
607
|
-
"flex
|
|
608
|
-
|
|
700
|
+
"flex min-h-0 flex-1 flex-col",
|
|
701
|
+
fill.filling ? "overflow-hidden" : "overflow-y-auto",
|
|
609
702
|
className,
|
|
610
703
|
)}
|
|
611
704
|
{...props}
|
|
612
705
|
>
|
|
613
|
-
|
|
706
|
+
<PageBodyContext.Provider value>
|
|
707
|
+
<div
|
|
708
|
+
data-slot="page-body-content"
|
|
709
|
+
{...fill.attributes}
|
|
710
|
+
className={cn(
|
|
711
|
+
"mx-auto w-full max-w-7xl",
|
|
712
|
+
surfaceRegionClasses("page", page!.surfaceLayout, "body"),
|
|
713
|
+
fill.filling ? "min-h-0 flex-1" : "min-h-full shrink-0",
|
|
714
|
+
page!.contentClassName,
|
|
715
|
+
contentClassName,
|
|
716
|
+
)}
|
|
717
|
+
>
|
|
718
|
+
{fill.provide(children)}
|
|
719
|
+
</div>
|
|
720
|
+
</PageBodyContext.Provider>
|
|
614
721
|
</div>
|
|
615
722
|
);
|
|
616
723
|
}
|
|
@@ -625,7 +732,8 @@ export function PageFooter({
|
|
|
625
732
|
<div
|
|
626
733
|
data-slot="page-footer"
|
|
627
734
|
className={cn(
|
|
628
|
-
"mt-auto flex h-[3.75rem] shrink-0 items-center justify-end
|
|
735
|
+
"mt-auto flex min-h-[3.75rem] shrink-0 items-center justify-end",
|
|
736
|
+
surfaceRegionClasses("page", page!.surfaceLayout, "footer"),
|
|
629
737
|
className,
|
|
630
738
|
)}
|
|
631
739
|
{...props}
|
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
DialogContent,
|
|
48
48
|
DialogFooter,
|
|
49
49
|
DialogHeader,
|
|
50
|
+
DialogIntro,
|
|
50
51
|
DialogNavigation,
|
|
51
52
|
DialogTitle,
|
|
52
53
|
} from "../primitives/dialog.tsx";
|
|
@@ -57,6 +58,7 @@ import {
|
|
|
57
58
|
DrawerContent,
|
|
58
59
|
DrawerFooter,
|
|
59
60
|
DrawerHeader,
|
|
61
|
+
DrawerIntro,
|
|
60
62
|
DrawerNavigation,
|
|
61
63
|
DrawerTitle,
|
|
62
64
|
} from "../primitives/drawer.tsx";
|
|
@@ -66,6 +68,7 @@ import {
|
|
|
66
68
|
PageBody,
|
|
67
69
|
PageFooter,
|
|
68
70
|
PageHeader,
|
|
71
|
+
PageIntro,
|
|
69
72
|
PageNavigation,
|
|
70
73
|
PageTitle,
|
|
71
74
|
useInsidePageShell,
|
|
@@ -114,11 +117,13 @@ const bodyClassName =
|
|
|
114
117
|
* a lista do body é filha direta dele e assume `fill` sem classe no consumidor.
|
|
115
118
|
*/
|
|
116
119
|
function PresentationBody({
|
|
120
|
+
page = false,
|
|
117
121
|
layout,
|
|
118
122
|
className,
|
|
119
123
|
children,
|
|
120
124
|
}: {
|
|
121
125
|
layout: "flow" | "fill";
|
|
126
|
+
page?: boolean;
|
|
122
127
|
className?: string;
|
|
123
128
|
children: ReactNode;
|
|
124
129
|
}): ReactElement {
|
|
@@ -128,7 +133,7 @@ function PresentationBody({
|
|
|
128
133
|
data-slot="presentation-body"
|
|
129
134
|
{...fill.attributes}
|
|
130
135
|
className={cn(
|
|
131
|
-
bodyClassName,
|
|
136
|
+
!page && bodyClassName,
|
|
132
137
|
(layout === "fill" || fill.filling) && fillRegionClassName,
|
|
133
138
|
className,
|
|
134
139
|
)}
|
|
@@ -195,14 +200,14 @@ function PresentationFrame({
|
|
|
195
200
|
</PresentationActions>
|
|
196
201
|
);
|
|
197
202
|
const body = (
|
|
198
|
-
<PresentationBody
|
|
203
|
+
<PresentationBody
|
|
204
|
+
page={surface === "page"}
|
|
205
|
+
layout={layout}
|
|
206
|
+
className={bodyClassNameProp}
|
|
207
|
+
>
|
|
199
208
|
{typeof children === "function" ? children(footerTarget) : children}
|
|
200
209
|
</PresentationBody>
|
|
201
210
|
);
|
|
202
|
-
// Viewport entre o PageBody e o conteúdo. Em `fill`, é coluna flexível para a altura
|
|
203
|
-
// chegar ao conteúdo; a rolagem continua disponível como recurso de última instância.
|
|
204
|
-
const viewportFillClassName =
|
|
205
|
-
layout === "fill" ? fillRegionClassName : undefined;
|
|
206
211
|
const withAssistant = (content: ReactElement): ReactElement =>
|
|
207
212
|
assistantLabel === undefined || assistantPanel === undefined ? (
|
|
208
213
|
content
|
|
@@ -248,18 +253,7 @@ function PresentationFrame({
|
|
|
248
253
|
)}
|
|
249
254
|
</PageHeader>
|
|
250
255
|
)}
|
|
251
|
-
<PageBody
|
|
252
|
-
{withAssistant(
|
|
253
|
-
<div
|
|
254
|
-
className={cn(
|
|
255
|
-
"h-full overflow-x-hidden overflow-y-auto",
|
|
256
|
-
viewportFillClassName,
|
|
257
|
-
)}
|
|
258
|
-
>
|
|
259
|
-
{content}
|
|
260
|
-
</div>,
|
|
261
|
-
)}
|
|
262
|
-
</PageBody>
|
|
256
|
+
<PageBody>{withAssistant(content)}</PageBody>
|
|
263
257
|
{hasFooter ? <PageFooter>{renderFooterActions()}</PageFooter> : null}
|
|
264
258
|
</Page>
|
|
265
259
|
);
|
|
@@ -278,7 +272,6 @@ function PresentationFrame({
|
|
|
278
272
|
{navigation === undefined ? null : (
|
|
279
273
|
<PageNavigation>{navigation}</PageNavigation>
|
|
280
274
|
)}
|
|
281
|
-
{contentOwnsHeading ? null : <PageTitle>{title}</PageTitle>}
|
|
282
275
|
{headerActions === undefined &&
|
|
283
276
|
assistantAction === undefined ? null : (
|
|
284
277
|
<PageActions>
|
|
@@ -290,36 +283,24 @@ function PresentationFrame({
|
|
|
290
283
|
)}
|
|
291
284
|
</PageHeader>
|
|
292
285
|
)}
|
|
293
|
-
<PageBody
|
|
294
|
-
{
|
|
295
|
-
<
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
layout === "fill" ? "overflow-y-hidden" : "overflow-y-auto",
|
|
299
|
-
viewportFillClassName,
|
|
300
|
-
)}
|
|
301
|
-
>
|
|
302
|
-
{body}
|
|
303
|
-
</div>,
|
|
286
|
+
<PageBody>
|
|
287
|
+
{contentOwnsHeading ? null : (
|
|
288
|
+
<PageIntro>
|
|
289
|
+
<PageTitle>{title}</PageTitle>
|
|
290
|
+
</PageIntro>
|
|
304
291
|
)}
|
|
292
|
+
{withAssistant(body)}
|
|
305
293
|
</PageBody>
|
|
306
294
|
{hasFooter ? <PageFooter>{renderFooterActions()}</PageFooter> : null}
|
|
307
295
|
</Page>
|
|
308
296
|
);
|
|
309
297
|
}
|
|
310
298
|
return (
|
|
311
|
-
<Page
|
|
312
|
-
layout={layout}
|
|
313
|
-
className={cn(
|
|
314
|
-
"flex min-h-full max-w-none flex-col space-y-0 px-0 py-0",
|
|
315
|
-
className,
|
|
316
|
-
)}
|
|
317
|
-
>
|
|
299
|
+
<Page layout={layout} className={className} contentClassName="max-w-none">
|
|
318
300
|
<PageHeader>
|
|
319
301
|
{navigation === undefined ? null : (
|
|
320
302
|
<PageNavigation>{navigation}</PageNavigation>
|
|
321
303
|
)}
|
|
322
|
-
<PageTitle className="text-lg leading-none">{title}</PageTitle>
|
|
323
304
|
{headerActions === undefined &&
|
|
324
305
|
assistantAction === undefined ? null : (
|
|
325
306
|
<PageActions>
|
|
@@ -330,17 +311,11 @@ function PresentationFrame({
|
|
|
330
311
|
</PageActions>
|
|
331
312
|
)}
|
|
332
313
|
</PageHeader>
|
|
333
|
-
<PageBody
|
|
334
|
-
|
|
335
|
-
<
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
viewportFillClassName,
|
|
339
|
-
)}
|
|
340
|
-
>
|
|
341
|
-
{body}
|
|
342
|
-
</div>,
|
|
343
|
-
)}
|
|
314
|
+
<PageBody>
|
|
315
|
+
<PageIntro>
|
|
316
|
+
<PageTitle>{title}</PageTitle>
|
|
317
|
+
</PageIntro>
|
|
318
|
+
{withAssistant(body)}
|
|
344
319
|
</PageBody>
|
|
345
320
|
{hasFooter ? <PageFooter>{renderFooterActions()}</PageFooter> : null}
|
|
346
321
|
</Page>
|
|
@@ -364,7 +339,6 @@ function PresentationFrame({
|
|
|
364
339
|
{navigation === undefined ? null : (
|
|
365
340
|
<DialogNavigation>{navigation}</DialogNavigation>
|
|
366
341
|
)}
|
|
367
|
-
<DialogTitle>{title}</DialogTitle>
|
|
368
342
|
{headerActions === undefined &&
|
|
369
343
|
assistantAction === undefined ? null : (
|
|
370
344
|
<DialogActions>
|
|
@@ -375,7 +349,14 @@ function PresentationFrame({
|
|
|
375
349
|
</DialogActions>
|
|
376
350
|
)}
|
|
377
351
|
</DialogHeader>
|
|
378
|
-
{withAssistant(
|
|
352
|
+
{withAssistant(
|
|
353
|
+
<DialogBody>
|
|
354
|
+
<DialogIntro>
|
|
355
|
+
<DialogTitle>{title}</DialogTitle>
|
|
356
|
+
</DialogIntro>
|
|
357
|
+
{body}
|
|
358
|
+
</DialogBody>,
|
|
359
|
+
)}
|
|
379
360
|
{hasFooter ? (
|
|
380
361
|
<DialogFooter>{renderFooterActions()}</DialogFooter>
|
|
381
362
|
) : null}
|
|
@@ -402,7 +383,6 @@ function PresentationFrame({
|
|
|
402
383
|
{navigation === undefined ? null : (
|
|
403
384
|
<DrawerNavigation>{navigation}</DrawerNavigation>
|
|
404
385
|
)}
|
|
405
|
-
<DrawerTitle>{title}</DrawerTitle>
|
|
406
386
|
{headerActions === undefined &&
|
|
407
387
|
assistantAction === undefined ? null : (
|
|
408
388
|
<DrawerActions>
|
|
@@ -413,7 +393,14 @@ function PresentationFrame({
|
|
|
413
393
|
</DrawerActions>
|
|
414
394
|
)}
|
|
415
395
|
</DrawerHeader>
|
|
416
|
-
{withAssistant(
|
|
396
|
+
{withAssistant(
|
|
397
|
+
<DrawerBody>
|
|
398
|
+
<DrawerIntro>
|
|
399
|
+
<DrawerTitle>{title}</DrawerTitle>
|
|
400
|
+
</DrawerIntro>
|
|
401
|
+
{body}
|
|
402
|
+
</DrawerBody>,
|
|
403
|
+
)}
|
|
417
404
|
{hasFooter ? (
|
|
418
405
|
<DrawerFooter>{renderFooterActions()}</DrawerFooter>
|
|
419
406
|
) : null}
|
|
@@ -635,9 +622,7 @@ export function Presentation({
|
|
|
635
622
|
string,
|
|
636
623
|
unknown
|
|
637
624
|
>;
|
|
638
|
-
//
|
|
639
|
-
// Seus comandos textuais usam a linha padrão; listas e grids continuam donos da
|
|
640
|
-
// densidade compacta de suas próprias ações.
|
|
625
|
+
// A região chamadora escolhe a escala; o footer conserva o botão padrão.
|
|
641
626
|
if (action.kind === "simple") {
|
|
642
627
|
return (
|
|
643
628
|
<PresentationCommandTrigger
|
|
@@ -784,7 +769,7 @@ export function Presentation({
|
|
|
784
769
|
|
|
785
770
|
const headerActions = definition.actions
|
|
786
771
|
.filter((command) => command.placement === "header")
|
|
787
|
-
.map((command) => renderCommand(command));
|
|
772
|
+
.map((command) => renderCommand(command, "sm"));
|
|
788
773
|
const footerActions = definition.actions
|
|
789
774
|
.filter((command) => command.placement === "footer")
|
|
790
775
|
.map((command) => renderCommand(command));
|
|
@@ -794,7 +779,7 @@ export function Presentation({
|
|
|
794
779
|
(invocation.surface === "page" || parentSurface !== "page");
|
|
795
780
|
const navigation = !canGoBack ? undefined : (
|
|
796
781
|
<Button
|
|
797
|
-
size="icon"
|
|
782
|
+
size="icon-sm"
|
|
798
783
|
variant="ghost"
|
|
799
784
|
disabled={surfaceBlocked}
|
|
800
785
|
aria-label="Voltar"
|
|
@@ -807,7 +792,7 @@ export function Presentation({
|
|
|
807
792
|
const assistantAction =
|
|
808
793
|
definition.assistant === undefined || assistantOpen ? undefined : (
|
|
809
794
|
<Button
|
|
810
|
-
size="icon"
|
|
795
|
+
size="icon-sm"
|
|
811
796
|
variant="ghost"
|
|
812
797
|
aria-label={definition.assistant.triggerLabel}
|
|
813
798
|
title={definition.assistant.triggerLabel}
|