Add metric toolbar with edit and delete actions for metric elements

Co-authored-by: nate <nate@buster.so>
This commit is contained in:
Cursor Agent 2025-08-08 13:11:14 +00:00
parent e9fb9f0199
commit dca78e9458
3 changed files with 85 additions and 7 deletions

View File

@ -312,6 +312,11 @@ export const NodeTypeLabels = {
keyboard: undefined,
keywords: []
},
editMetric: {
label: 'Edit metric',
keyboard: undefined,
keywords: []
},
caption: {
label: 'Caption',
keyboard: undefined,

View File

@ -12,6 +12,7 @@ import {
} from 'platejs/react';
import { ResizableProvider, useResizableValue } from '@platejs/resizable';
import { MetricEmbedPlaceholder } from './MetricPlaceholder';
import { MetricToolbar } from './MetricToolbar';
import { Caption, CaptionTextarea } from '../CaptionNode';
import { mediaResizeHandleVariants, Resizable, ResizeHandle } from '../ResizeHandle';
import { type TMetricElement } from '../../plugins/metric-plugin';
@ -31,13 +32,15 @@ export const MetricElement = withHOC(
const readOnly = useReadOnly();
const content = metricId ? (
<MetricResizeContainer>
<MetricContent
metricId={metricId}
metricVersionNumber={metricVersionNumber}
readOnly={readOnly}
/>
</MetricResizeContainer>
<MetricToolbar>
<MetricResizeContainer>
<MetricContent
metricId={metricId}
metricVersionNumber={metricVersionNumber}
readOnly={readOnly}
/>
</MetricResizeContainer>
</MetricToolbar>
) : (
<MetricEmbedPlaceholder />
);

View File

@ -0,0 +1,70 @@
import * as React from 'react';
import { useEditorRef, useEditorSelector, useElement, useReadOnly, useRemoveNodeButton, useSelected } from 'platejs/react';
import { Button } from '@/components/ui/buttons';
import { PopoverBase, PopoverAnchor, PopoverContent } from '@/components/ui/popover';
import { NodeTypeIcons } from '../../config/icons';
import { NodeTypeLabels } from '../../config/labels';
import { AddMetricModal } from '@/components/features/modal/AddMetricModal';
import { MetricPlugin, type TMetricElement } from '../../plugins/metric-plugin';
export function MetricToolbar({ children }: { children: React.ReactNode }) {
const editor = useEditorRef();
const readOnly = useReadOnly();
const selected = useSelected();
const selectionCollapsed = useEditorSelector((ed) => !ed.api.isExpanded(), []);
const isOpen = !readOnly && selected && selectionCollapsed;
const element = useElement<TMetricElement>();
const plugin = editor.getPlugin(MetricPlugin);
const { props: removeButtonProps } = useRemoveNodeButton({ element });
const [openEditModal, setOpenEditModal] = React.useState(false);
const onOpenEdit = React.useCallback(() => {
editor.tf.select();
setOpenEditModal(true);
}, [editor]);
const onCloseEdit = React.useCallback(() => {
setOpenEditModal(false);
}, []);
return (
<PopoverBase open={isOpen} modal={false}>
<PopoverAnchor>{children}</PopoverAnchor>
<PopoverContent className="w-auto p-1" onOpenAutoFocus={(e) => e.preventDefault()}>
<div className="box-content flex items-center">
<Button onClick={onOpenEdit} variant="ghost">
{NodeTypeLabels.editMetric?.label ?? 'Edit metric'}
</Button>
<Button prefix={<NodeTypeIcons.trash />} variant="ghost" {...removeButtonProps}></Button>
</div>
</PopoverContent>
<AddMetricModal
open={openEditModal}
loading={false}
selectedMetrics={[]}
onClose={onCloseEdit}
onAddMetrics={async (metrics) => {
const selectedMetricId = metrics?.[0]?.id;
if (!selectedMetricId) return onCloseEdit();
const at = editor.api.findPath(element);
if (!at) return onCloseEdit();
plugin.api.metric.updateMetric(selectedMetricId, { at });
onCloseEdit();
}}
selectionMode="single"
saveButtonText="Update metric"
/>
</PopoverBase>
);
}