@beyondwork/docx-react-component 1.0.36 → 1.0.38
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/README.md +103 -13
- package/package.json +1 -1
- package/src/api/package-version.ts +13 -0
- package/src/api/public-types.ts +402 -1
- package/src/core/commands/index.ts +18 -1
- package/src/core/commands/section-layout-commands.ts +58 -0
- package/src/core/commands/table-grid.ts +431 -0
- package/src/core/commands/table-structure-commands.ts +815 -55
- package/src/core/selection/mapping.ts +6 -0
- package/src/io/docx-session.ts +24 -9
- package/src/io/export/build-app-properties-xml.ts +88 -0
- package/src/io/export/serialize-comments.ts +6 -1
- package/src/io/export/serialize-footnotes.ts +10 -9
- package/src/io/export/serialize-headers-footers.ts +11 -10
- package/src/io/export/serialize-main-document.ts +328 -50
- package/src/io/export/serialize-numbering.ts +114 -24
- package/src/io/export/serialize-tables.ts +87 -11
- package/src/io/export/table-properties-xml.ts +174 -20
- package/src/io/export/twip.ts +66 -0
- package/src/io/normalize/normalize-text.ts +20 -0
- package/src/io/ooxml/parse-footnotes.ts +62 -1
- package/src/io/ooxml/parse-headers-footers.ts +62 -1
- package/src/io/ooxml/parse-main-document.ts +158 -1
- package/src/io/ooxml/parse-tables.ts +249 -0
- package/src/legal/bookmarks.ts +78 -0
- package/src/model/canonical-document.ts +45 -0
- package/src/review/store/scope-tag-diff.ts +130 -0
- package/src/runtime/document-layout.ts +4 -2
- package/src/runtime/document-navigation.ts +2 -306
- package/src/runtime/document-runtime.ts +287 -11
- package/src/runtime/layout/default-page-format.ts +96 -0
- package/src/runtime/layout/docx-font-loader.ts +143 -0
- package/src/runtime/layout/index.ts +233 -0
- package/src/runtime/layout/inert-layout-facet.ts +59 -0
- package/src/runtime/layout/layout-engine-instance.ts +628 -0
- package/src/runtime/layout/layout-invalidation.ts +257 -0
- package/src/runtime/layout/layout-measurement-provider.ts +175 -0
- package/src/runtime/layout/margin-preset-catalog.ts +178 -0
- package/src/runtime/layout/measurement-backend-canvas.ts +307 -0
- package/src/runtime/layout/measurement-backend-empirical.ts +208 -0
- package/src/runtime/layout/page-format-catalog.ts +233 -0
- package/src/runtime/layout/page-fragment-mapper.ts +179 -0
- package/src/runtime/layout/page-graph.ts +452 -0
- package/src/runtime/layout/page-layout-snapshot-adapter.ts +70 -0
- package/src/runtime/layout/page-story-resolver.ts +195 -0
- package/src/runtime/layout/paginated-layout-engine.ts +921 -0
- package/src/runtime/layout/project-block-fragments.ts +91 -0
- package/src/runtime/layout/public-facet.ts +1398 -0
- package/src/runtime/layout/resolved-formatting-document.ts +317 -0
- package/src/runtime/layout/resolved-formatting-state.ts +430 -0
- package/src/runtime/layout/table-render-plan.ts +229 -0
- package/src/runtime/render/block-fragment-projection.ts +35 -0
- package/src/runtime/render/decoration-resolver.ts +189 -0
- package/src/runtime/render/index.ts +57 -0
- package/src/runtime/render/pending-op-delta-reader.ts +129 -0
- package/src/runtime/render/render-frame-types.ts +317 -0
- package/src/runtime/render/render-kernel.ts +755 -0
- package/src/runtime/scope-tag-registry.ts +95 -0
- package/src/runtime/surface-projection.ts +1 -0
- package/src/runtime/text-ack-range.ts +49 -0
- package/src/runtime/view-state.ts +67 -0
- package/src/runtime/workflow-markup.ts +1 -5
- package/src/runtime/workflow-rail-segments.ts +280 -0
- package/src/ui/WordReviewEditor.tsx +99 -15
- package/src/ui/editor-runtime-boundary.ts +10 -1
- package/src/ui/editor-shell-view.tsx +6 -0
- package/src/ui/editor-surface-controller.tsx +3 -0
- package/src/ui/headless/chrome-registry.ts +501 -0
- package/src/ui/headless/scoped-chrome-policy.ts +183 -0
- package/src/ui/headless/selection-tool-context.ts +2 -0
- package/src/ui/headless/selection-tool-resolver.ts +36 -17
- package/src/ui/headless/selection-tool-types.ts +10 -0
- package/src/ui-tailwind/chrome/chrome-preset-model.ts +23 -2
- package/src/ui-tailwind/chrome/role-action-sets.ts +74 -0
- package/src/ui-tailwind/chrome/tw-detach-handle.tsx +147 -0
- package/src/ui-tailwind/chrome/tw-selection-anchor-resolver.ts +163 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +57 -92
- package/src/ui-tailwind/chrome/tw-selection-tool-placement.ts +149 -0
- package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +15 -4
- package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +274 -138
- package/src/ui-tailwind/chrome-overlay/chrome-overlay-projector.ts +90 -0
- package/src/ui-tailwind/chrome-overlay/index.ts +22 -0
- package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +86 -0
- package/src/ui-tailwind/chrome-overlay/tw-scope-rail-layer.tsx +178 -0
- package/src/ui-tailwind/chrome-overlay/tw-workspace-view-switcher.tsx +95 -0
- package/src/ui-tailwind/editor-surface/fast-text-edit-lane.ts +337 -0
- package/src/ui-tailwind/editor-surface/local-edit-session-state.ts +100 -0
- package/src/ui-tailwind/editor-surface/perf-probe.ts +27 -1
- package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +20 -2
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +93 -23
- package/src/ui-tailwind/editor-surface/predicted-position-map.ts +78 -0
- package/src/ui-tailwind/editor-surface/predicted-tag-preflight.ts +63 -0
- package/src/ui-tailwind/editor-surface/predicted-tx-gate.ts +39 -0
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +176 -6
- package/src/ui-tailwind/index.ts +33 -0
- package/src/ui-tailwind/review/tw-comment-sidebar.tsx +2 -2
- package/src/ui-tailwind/review/tw-rail-card.tsx +150 -0
- package/src/ui-tailwind/review/tw-review-rail-footer.tsx +52 -0
- package/src/ui-tailwind/review/tw-review-rail.tsx +166 -11
- package/src/ui-tailwind/review/tw-workflow-tab.tsx +108 -0
- package/src/ui-tailwind/theme/editor-theme.css +505 -144
- package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +559 -0
- package/src/ui-tailwind/toolbar/tw-scope-posture-menu.tsx +182 -0
- package/src/ui-tailwind/toolbar/tw-shell-header.tsx +162 -0
- package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +2 -2
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +304 -166
- package/src/ui-tailwind/tw-review-workspace.tsx +163 -2
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scope posture menu — replaces the old "Mark section" button with a
|
|
3
|
+
* topnav dropdown listing the seven `ScopeRailPosture` values so
|
|
4
|
+
* editors can mark regions with an explicit workflow mode instead of a
|
|
5
|
+
* single "marked" flag.
|
|
6
|
+
*
|
|
7
|
+
* Per runtime-rendering-and-chrome-phase.md §6.4, the menu lives inline
|
|
8
|
+
* in the editor role's primary action region (not in the review queue
|
|
9
|
+
* strip). Postures align 1:1 with the rail vocabulary so the rail
|
|
10
|
+
* updates visually as soon as the user picks one.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import React, { useState } from "react";
|
|
14
|
+
import * as Popover from "@radix-ui/react-popover";
|
|
15
|
+
import {
|
|
16
|
+
BookmarkPlus,
|
|
17
|
+
ChevronDown,
|
|
18
|
+
Eye,
|
|
19
|
+
Flag,
|
|
20
|
+
Lock,
|
|
21
|
+
MessageCircle,
|
|
22
|
+
Pencil,
|
|
23
|
+
Sparkles,
|
|
24
|
+
} from "lucide-react";
|
|
25
|
+
|
|
26
|
+
import type { ScopeRailPosture } from "../../api/public-types";
|
|
27
|
+
import { preserveEditorSelectionMouseDown } from "../../ui/headless/preserve-editor-selection";
|
|
28
|
+
|
|
29
|
+
export interface TwScopePostureMenuProps {
|
|
30
|
+
disabled?: boolean;
|
|
31
|
+
/** Called when the user picks a posture. Host decides the scope to mark. */
|
|
32
|
+
onSelect: (posture: ScopeRailPosture) => void;
|
|
33
|
+
/** Optional explicit label override (defaults to "Mark…"). */
|
|
34
|
+
label?: string;
|
|
35
|
+
"data-testid"?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface PostureEntry {
|
|
39
|
+
posture: ScopeRailPosture;
|
|
40
|
+
label: string;
|
|
41
|
+
hint: string;
|
|
42
|
+
icon: React.ComponentType<{ className?: string }>;
|
|
43
|
+
tone: "accent" | "warning" | "comment" | "secondary" | "danger";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Posture catalog. Mirrors `POSTURE_STYLES` in `tw-scope-rail-layer.tsx`
|
|
48
|
+
* but with lucide icon components (the rail uses CSS pseudo-element
|
|
49
|
+
* glyphs via the `data-icon` attribute). Extract both into a single
|
|
50
|
+
* source of truth in a follow-up.
|
|
51
|
+
*/
|
|
52
|
+
const POSTURE_ENTRIES: readonly PostureEntry[] = [
|
|
53
|
+
{
|
|
54
|
+
posture: "edit",
|
|
55
|
+
label: "Edit scope",
|
|
56
|
+
hint: "Full authoring inside this region",
|
|
57
|
+
icon: Pencil,
|
|
58
|
+
tone: "accent",
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
posture: "suggest",
|
|
62
|
+
label: "Suggest scope",
|
|
63
|
+
hint: "Tracked-change suggestions only",
|
|
64
|
+
icon: Sparkles,
|
|
65
|
+
tone: "warning",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
posture: "comment",
|
|
69
|
+
label: "Comment scope",
|
|
70
|
+
hint: "Comments only; body is read-only",
|
|
71
|
+
icon: MessageCircle,
|
|
72
|
+
tone: "comment",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
posture: "view",
|
|
76
|
+
label: "In scope",
|
|
77
|
+
hint: "Read-only, in scope of review",
|
|
78
|
+
icon: Eye,
|
|
79
|
+
tone: "secondary",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
posture: "candidate",
|
|
83
|
+
label: "Propose scope",
|
|
84
|
+
hint: "Candidate — not yet committed",
|
|
85
|
+
icon: Flag,
|
|
86
|
+
tone: "warning",
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
posture: "preserve-only",
|
|
90
|
+
label: "Preserve only",
|
|
91
|
+
hint: "Blocked — export-preserving only",
|
|
92
|
+
icon: Lock,
|
|
93
|
+
tone: "danger",
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
posture: "blocked-import",
|
|
97
|
+
label: "Blocked import",
|
|
98
|
+
hint: "Blocked — imported region is locked",
|
|
99
|
+
icon: Lock,
|
|
100
|
+
tone: "danger",
|
|
101
|
+
},
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
export function TwScopePostureMenu(props: TwScopePostureMenuProps): React.JSX.Element {
|
|
105
|
+
const [open, setOpen] = useState(false);
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<Popover.Root open={open} onOpenChange={setOpen}>
|
|
109
|
+
<Popover.Trigger asChild>
|
|
110
|
+
<button
|
|
111
|
+
type="button"
|
|
112
|
+
aria-label={`${props.label ?? "Mark"} section`}
|
|
113
|
+
aria-expanded={open}
|
|
114
|
+
disabled={props.disabled}
|
|
115
|
+
onMouseDown={preserveEditorSelectionMouseDown}
|
|
116
|
+
className="inline-flex h-6 items-center gap-1 rounded-md border border-border bg-canvas px-2 text-[11px] font-medium text-primary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-canvas"
|
|
117
|
+
data-testid={props["data-testid"] ?? "scope-posture-menu-trigger"}
|
|
118
|
+
>
|
|
119
|
+
<BookmarkPlus className="h-3.5 w-3.5" />
|
|
120
|
+
<span>{props.label ?? "Mark…"}</span>
|
|
121
|
+
<ChevronDown className="h-3.5 w-3.5 text-tertiary" />
|
|
122
|
+
</button>
|
|
123
|
+
</Popover.Trigger>
|
|
124
|
+
<Popover.Portal>
|
|
125
|
+
<Popover.Content
|
|
126
|
+
className="z-50 w-[260px] rounded-lg bg-canvas p-1 shadow-lg ring-1 ring-border"
|
|
127
|
+
sideOffset={8}
|
|
128
|
+
align="start"
|
|
129
|
+
data-testid="scope-posture-menu-content"
|
|
130
|
+
>
|
|
131
|
+
<div className="mb-1 px-2 py-1 text-[10px] font-semibold uppercase tracking-[0.12em] text-tertiary">
|
|
132
|
+
Mark section with posture
|
|
133
|
+
</div>
|
|
134
|
+
{POSTURE_ENTRIES.map((entry) => (
|
|
135
|
+
<Popover.Close key={entry.posture} asChild>
|
|
136
|
+
<button
|
|
137
|
+
type="button"
|
|
138
|
+
aria-label={`Mark section as ${entry.label}`}
|
|
139
|
+
onMouseDown={preserveEditorSelectionMouseDown}
|
|
140
|
+
onClick={() => {
|
|
141
|
+
props.onSelect(entry.posture);
|
|
142
|
+
}}
|
|
143
|
+
className="flex w-full items-start gap-2 rounded-md px-2 py-1.5 text-left text-[11px] transition-colors hover:bg-surface focus-visible:outline-none focus-visible:bg-surface"
|
|
144
|
+
data-testid={`scope-posture-option-${entry.posture}`}
|
|
145
|
+
data-posture={entry.posture}
|
|
146
|
+
>
|
|
147
|
+
<entry.icon
|
|
148
|
+
className={[
|
|
149
|
+
"mt-0.5 h-3.5 w-3.5 shrink-0",
|
|
150
|
+
toneClass(entry.tone),
|
|
151
|
+
].join(" ")}
|
|
152
|
+
/>
|
|
153
|
+
<span className="flex flex-col">
|
|
154
|
+
<span className="font-medium text-primary">{entry.label}</span>
|
|
155
|
+
<span className="text-[10px] text-secondary">{entry.hint}</span>
|
|
156
|
+
</span>
|
|
157
|
+
</button>
|
|
158
|
+
</Popover.Close>
|
|
159
|
+
))}
|
|
160
|
+
</Popover.Content>
|
|
161
|
+
</Popover.Portal>
|
|
162
|
+
</Popover.Root>
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function toneClass(tone: PostureEntry["tone"]): string {
|
|
167
|
+
switch (tone) {
|
|
168
|
+
case "accent":
|
|
169
|
+
return "text-accent";
|
|
170
|
+
case "warning":
|
|
171
|
+
return "text-warning";
|
|
172
|
+
case "comment":
|
|
173
|
+
return "text-comment";
|
|
174
|
+
case "danger":
|
|
175
|
+
return "text-danger";
|
|
176
|
+
case "secondary":
|
|
177
|
+
default:
|
|
178
|
+
return "text-secondary";
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export default TwScopePostureMenu;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import React, { type ReactNode } from "react";
|
|
2
|
+
import * as Tabs from "@radix-ui/react-tabs";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* TwShellHeader — the top "app chrome" bar above the document canvas.
|
|
6
|
+
*
|
|
7
|
+
* Anatomy matches the editorial reference mock:
|
|
8
|
+
* ┌─────────────────────────────────────────────────────────────────────┐
|
|
9
|
+
* │ [brand] Edit | Review | Workflow | More [⋯] [CTA] │
|
|
10
|
+
* └─────────────────────────────────────────────────────────────────────┘
|
|
11
|
+
*
|
|
12
|
+
* All three regions are optional slots — hosts opt in by supplying the
|
|
13
|
+
* corresponding prop. When nothing is supplied the header renders empty but
|
|
14
|
+
* preserves layout height so the document canvas does not jump when a CTA
|
|
15
|
+
* appears.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export type ShellHeaderMode = "edit" | "review" | "workflow" | "more";
|
|
19
|
+
|
|
20
|
+
export interface ShellHeaderModeOption {
|
|
21
|
+
id: ShellHeaderMode;
|
|
22
|
+
label: string;
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ShellHeaderPrimaryAction {
|
|
27
|
+
label: string;
|
|
28
|
+
onClick: () => void;
|
|
29
|
+
tone?: "accent" | "neutral";
|
|
30
|
+
disabled?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ShellHeaderIconAction {
|
|
34
|
+
id: string;
|
|
35
|
+
label: string;
|
|
36
|
+
icon: ReactNode;
|
|
37
|
+
onClick?: () => void;
|
|
38
|
+
href?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface TwShellHeaderProps {
|
|
42
|
+
brand?: ReactNode;
|
|
43
|
+
modes?: readonly ShellHeaderModeOption[];
|
|
44
|
+
activeMode?: ShellHeaderMode;
|
|
45
|
+
onModeChange?: (mode: ShellHeaderMode) => void;
|
|
46
|
+
iconActions?: readonly ShellHeaderIconAction[];
|
|
47
|
+
primaryAction?: ShellHeaderPrimaryAction;
|
|
48
|
+
/** Thin bottom border appears only when the document has scrolled. */
|
|
49
|
+
isScrolled?: boolean;
|
|
50
|
+
className?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const focusRingClass =
|
|
54
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1 focus-visible:ring-offset-canvas";
|
|
55
|
+
|
|
56
|
+
export function TwShellHeader(props: TwShellHeaderProps) {
|
|
57
|
+
const hasContent =
|
|
58
|
+
props.brand ||
|
|
59
|
+
(props.modes && props.modes.length > 0) ||
|
|
60
|
+
(props.iconActions && props.iconActions.length > 0) ||
|
|
61
|
+
props.primaryAction;
|
|
62
|
+
|
|
63
|
+
if (!hasContent) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const className = [
|
|
68
|
+
"flex h-12 shrink-0 items-center justify-between gap-4 px-4 bg-canvas/92 backdrop-blur-sm",
|
|
69
|
+
props.isScrolled ? "border-b border-border" : "border-b border-transparent",
|
|
70
|
+
"transition-colors duration-[var(--motion-fast)]",
|
|
71
|
+
props.className,
|
|
72
|
+
]
|
|
73
|
+
.filter(Boolean)
|
|
74
|
+
.join(" ");
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<header className={className} data-testid="tw-shell-header">
|
|
78
|
+
<div className="flex min-w-0 items-center gap-3">
|
|
79
|
+
{props.brand ? (
|
|
80
|
+
<div
|
|
81
|
+
className="font-[family-name:var(--font-legal-serif)] text-[15px] font-semibold text-primary truncate"
|
|
82
|
+
data-testid="tw-shell-header__brand"
|
|
83
|
+
>
|
|
84
|
+
{props.brand}
|
|
85
|
+
</div>
|
|
86
|
+
) : null}
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
{props.modes && props.modes.length > 0 && props.activeMode ? (
|
|
90
|
+
<Tabs.Root
|
|
91
|
+
value={props.activeMode}
|
|
92
|
+
onValueChange={(v: string) =>
|
|
93
|
+
props.onModeChange?.(v as ShellHeaderMode)
|
|
94
|
+
}
|
|
95
|
+
>
|
|
96
|
+
<Tabs.List
|
|
97
|
+
aria-label="Workspace modes"
|
|
98
|
+
className="flex items-center gap-1"
|
|
99
|
+
>
|
|
100
|
+
{props.modes.map((mode) => (
|
|
101
|
+
<Tabs.Trigger
|
|
102
|
+
key={mode.id}
|
|
103
|
+
value={mode.id}
|
|
104
|
+
disabled={mode.disabled}
|
|
105
|
+
className={`wre-rail-tab ${focusRingClass}`}
|
|
106
|
+
data-testid={`tw-shell-header__mode-${mode.id}`}
|
|
107
|
+
>
|
|
108
|
+
{mode.label}
|
|
109
|
+
</Tabs.Trigger>
|
|
110
|
+
))}
|
|
111
|
+
</Tabs.List>
|
|
112
|
+
</Tabs.Root>
|
|
113
|
+
) : (
|
|
114
|
+
<div aria-hidden="true" />
|
|
115
|
+
)}
|
|
116
|
+
|
|
117
|
+
<div className="flex items-center gap-1">
|
|
118
|
+
{props.iconActions?.map((action) => {
|
|
119
|
+
const commonProps = {
|
|
120
|
+
key: action.id,
|
|
121
|
+
"aria-label": action.label,
|
|
122
|
+
title: action.label,
|
|
123
|
+
className: `inline-flex h-8 w-8 items-center justify-center rounded-sm text-secondary transition-colors hover:bg-surface-hover hover:text-primary ${focusRingClass}`,
|
|
124
|
+
} as const;
|
|
125
|
+
|
|
126
|
+
if (action.href) {
|
|
127
|
+
return (
|
|
128
|
+
<a {...commonProps} href={action.href}>
|
|
129
|
+
<span aria-hidden="true">{action.icon}</span>
|
|
130
|
+
</a>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<button {...commonProps} type="button" onClick={action.onClick}>
|
|
136
|
+
<span aria-hidden="true">{action.icon}</span>
|
|
137
|
+
</button>
|
|
138
|
+
);
|
|
139
|
+
})}
|
|
140
|
+
|
|
141
|
+
{props.primaryAction ? (
|
|
142
|
+
<button
|
|
143
|
+
type="button"
|
|
144
|
+
disabled={props.primaryAction.disabled}
|
|
145
|
+
onClick={props.primaryAction.onClick}
|
|
146
|
+
data-tone={props.primaryAction.tone ?? "accent"}
|
|
147
|
+
className={[
|
|
148
|
+
"ml-2 inline-flex h-8 items-center rounded-sm px-3 text-xs font-semibold transition-colors disabled:opacity-40",
|
|
149
|
+
props.primaryAction.tone === "neutral"
|
|
150
|
+
? "bg-surface text-primary hover:bg-surface-hover"
|
|
151
|
+
: "bg-accent text-white hover:bg-[color-mix(in_srgb,var(--color-accent)_85%,#000)]",
|
|
152
|
+
focusRingClass,
|
|
153
|
+
].join(" ")}
|
|
154
|
+
data-testid="tw-shell-header__primary-action"
|
|
155
|
+
>
|
|
156
|
+
{props.primaryAction.label}
|
|
157
|
+
</button>
|
|
158
|
+
) : null}
|
|
159
|
+
</div>
|
|
160
|
+
</header>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
@@ -26,7 +26,7 @@ export function TwToolbarIconButton(props: TwToolbarIconButtonProps) {
|
|
|
26
26
|
onMouseDown={preserveEditorSelectionMouseDown}
|
|
27
27
|
onClick={props.onClick}
|
|
28
28
|
className={[
|
|
29
|
-
"inline-flex h-
|
|
29
|
+
"inline-flex h-6 w-6 items-center justify-center rounded-md border border-transparent transition-colors outline-none",
|
|
30
30
|
"disabled:opacity-30 disabled:cursor-not-allowed",
|
|
31
31
|
props.emphasis
|
|
32
32
|
? "text-accent hover:border-border/60 hover:bg-surface"
|
|
@@ -36,7 +36,7 @@ export function TwToolbarIconButton(props: TwToolbarIconButtonProps) {
|
|
|
36
36
|
focusRingClass,
|
|
37
37
|
].join(" ")}
|
|
38
38
|
>
|
|
39
|
-
<props.icon className="h-
|
|
39
|
+
<props.icon className="h-3.5 w-3.5" />
|
|
40
40
|
</button>
|
|
41
41
|
</Tooltip.Trigger>
|
|
42
42
|
<Tooltip.Portal>
|