refactor(webui): adopt semantic workspace actions
This commit is contained in:
@@ -3,14 +3,13 @@ import {
|
|||||||
Eye,
|
Eye,
|
||||||
FileCheck2,
|
FileCheck2,
|
||||||
Plus,
|
Plus,
|
||||||
RefreshCw,
|
|
||||||
Save,
|
Save,
|
||||||
Send,
|
Send,
|
||||||
Trash2,
|
Trash2,
|
||||||
X
|
X
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { DialogSection, ToolbarGroup, ActionToolbar,
|
import { DialogSection, ActionToolbar,
|
||||||
ApiError,
|
ApiError,
|
||||||
ActionBlockerHint,
|
ActionBlockerHint,
|
||||||
Button,
|
Button,
|
||||||
@@ -30,6 +29,7 @@ import { DialogSection, ToolbarGroup, ActionToolbar,
|
|||||||
StatePanel,
|
StatePanel,
|
||||||
StatusBadge,
|
StatusBadge,
|
||||||
ToggleSwitch,
|
ToggleSwitch,
|
||||||
|
WorkspaceActionBar,
|
||||||
WorkspaceFrame,
|
WorkspaceFrame,
|
||||||
WorkspaceLayout,
|
WorkspaceLayout,
|
||||||
formatDateTime,
|
formatDateTime,
|
||||||
@@ -312,10 +312,14 @@ export default function TemplatesPage({ settings, auth }: Props) {
|
|||||||
contentLabel="Template workspace"
|
contentLabel="Template workspace"
|
||||||
contentClassName="templates-workspace"
|
contentClassName="templates-workspace"
|
||||||
primary={<>
|
primary={<>
|
||||||
<ActionToolbar surface="panel-header">
|
<WorkspaceActionBar
|
||||||
<strong>Template library</strong>
|
scope="collection-pane"
|
||||||
<IconButton label="Add template" icon={<Plus size={17} />} variant="primary" disabled={!canWrite} disabledReason={!canWrite ? TEMPLATES_I18N.writeReason : undefined} onClick={() => requestDiscard(() => setCreateOpen(true))} />
|
variant="collection"
|
||||||
</ActionToolbar>
|
refreshable
|
||||||
|
reloadAction={{ onReload: () => void reload(selectedId), loading: loading || busy }}
|
||||||
|
contextActions={<strong>Template library</strong>}
|
||||||
|
createAction={<IconButton label="Add template" icon={<Plus size={17} />} variant="primary" disabled={!canWrite} disabledReason={!canWrite ? TEMPLATES_I18N.writeReason : undefined} onClick={() => requestDiscard(() => setCreateOpen(true))} />}
|
||||||
|
/>
|
||||||
<FilterBar surface="panel"><input value={search} onChange={(event) => setSearch(event.target.value)} placeholder="Search templates" /></FilterBar>
|
<FilterBar surface="panel"><input value={search} onChange={(event) => setSearch(event.target.value)} placeholder="Search templates" /></FilterBar>
|
||||||
<SelectionList variant="navigation" label="Templates">
|
<SelectionList variant="navigation" label="Templates">
|
||||||
{visibleItems.map((item) => (
|
{visibleItems.map((item) => (
|
||||||
@@ -338,25 +342,34 @@ export default function TemplatesPage({ settings, auth }: Props) {
|
|||||||
</SelectionList>
|
</SelectionList>
|
||||||
</>}
|
</>}
|
||||||
>
|
>
|
||||||
<ActionToolbar surface="panel-header" className="templates-workspace-toolbar">
|
<WorkspaceActionBar
|
||||||
<span className="templates-current-title">
|
scope="editor-pane"
|
||||||
|
variant="editor"
|
||||||
|
state={busy ? "saving" : dirty ? "dirty" : "clean"}
|
||||||
|
className="templates-workspace-toolbar"
|
||||||
|
contextActions={<span className="templates-current-title">
|
||||||
<strong>{selected?.name ?? "Select a template"}</strong>
|
<strong>{selected?.name ?? "Select a template"}</strong>
|
||||||
<small>{selected ? `${typeLabel(selected.template_type)} · ${selected.revision.locale}` : ""}</small>
|
<small>{selected ? `${typeLabel(selected.template_type)} · ${selected.revision.locale}` : ""}</small>
|
||||||
</span>
|
</span>}
|
||||||
<ToolbarGroup align="end" className="templates-toolbar-actions">
|
helpAction={<DocumentationHelpLink reference={TEMPLATES_DOCUMENTATION} />}
|
||||||
<DocumentationHelpLink reference={TEMPLATES_DOCUMENTATION} />
|
primaryActions={<>
|
||||||
<IconButton label="Discard and reload" icon={<RefreshCw size={17} />} disabled={loading || busy} disabledReason={loading ? TEMPLATES_I18N.loading : busy ? TEMPLATES_I18N.busy : undefined} onClick={() => requestDiscard(() => void reload(selectedId))} />
|
|
||||||
<Button variant="primary" disabled={!selected || readOnly || !dirty || busy} disabledReason={busy ? TEMPLATES_I18N.busy : !selected ? TEMPLATES_I18N.noSelection : readOnly ? (canWrite ? TEMPLATES_I18N.readOnlyReason : TEMPLATES_I18N.writeReason) : !dirty ? TEMPLATES_I18N.noChanges : undefined} onClick={() => void save()}><Save size={16} /> Save revision</Button>
|
|
||||||
<Button disabled={!selected || !canPublish || dirty || busy} disabledReason={busy ? TEMPLATES_I18N.busy : !selected ? TEMPLATES_I18N.noSelection : !canPublish ? TEMPLATES_I18N.publishReason : dirty ? TEMPLATES_I18N.saveBeforeAction : undefined} onClick={() => setPublishOpen(true)}><FileCheck2 size={16} /> Publish</Button>
|
<Button disabled={!selected || !canPublish || dirty || busy} disabledReason={busy ? TEMPLATES_I18N.busy : !selected ? TEMPLATES_I18N.noSelection : !canPublish ? TEMPLATES_I18N.publishReason : dirty ? TEMPLATES_I18N.saveBeforeAction : undefined} onClick={() => setPublishOpen(true)}><FileCheck2 size={16} /> Publish</Button>
|
||||||
<IconButton label="Delete template" icon={<Trash2 size={17} />} variant="danger" disabled={!selected || readOnly} disabledReason={!selected ? TEMPLATES_I18N.noSelection : readOnly ? (canWrite ? TEMPLATES_I18N.readOnlyReason : TEMPLATES_I18N.writeReason) : undefined} onClick={() => setDeleteOpen(true)} />
|
|
||||||
<SegmentedControl
|
<SegmentedControl
|
||||||
value={view}
|
value={view}
|
||||||
onChange={setView}
|
onChange={setView}
|
||||||
options={[{ id: "definition", label: "Definition" }, { id: "preview", label: "Preview" }]}
|
options={[{ id: "definition", label: "Definition" }, { id: "preview", label: "Preview" }]}
|
||||||
ariaLabel="Template workspace"
|
ariaLabel="Template workspace"
|
||||||
/>
|
/>
|
||||||
</ToolbarGroup>
|
</>}
|
||||||
</ActionToolbar>
|
destructiveActions={<IconButton label="Delete template" icon={<Trash2 size={17} />} variant="danger" disabled={!selected || readOnly} disabledReason={!selected ? TEMPLATES_I18N.noSelection : readOnly ? (canWrite ? TEMPLATES_I18N.readOnlyReason : TEMPLATES_I18N.writeReason) : undefined} onClick={() => setDeleteOpen(true)} />}
|
||||||
|
discardAction={{ label: "Discard and reload", onClick: () => requestDiscard(() => void reload(selectedId)), disabled: !selected }}
|
||||||
|
saveAction={{
|
||||||
|
label: <><Save size={16} /> Save revision</>,
|
||||||
|
disabled: !selected || readOnly || busy,
|
||||||
|
disabledReason: busy ? TEMPLATES_I18N.busy : !selected ? TEMPLATES_I18N.noSelection : readOnly ? (canWrite ? TEMPLATES_I18N.readOnlyReason : TEMPLATES_I18N.writeReason) : undefined,
|
||||||
|
onClick: () => void save()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="templates-alerts">
|
<div className="templates-alerts">
|
||||||
{error && <DismissibleAlert tone="danger" resetKey={error}>{error}</DismissibleAlert>}
|
{error && <DismissibleAlert tone="danger" resetKey={error}>{error}</DismissibleAlert>}
|
||||||
|
|||||||
Reference in New Issue
Block a user