mirror of https://github.com/buster-so/buster.git
Merge pull request #683 from buster-so/cursor/add-floating-toolbar-for-metric-element-1f54
Add floating toolbar for metric element
This commit is contained in:
commit
09f69bbd45
|
@ -312,6 +312,11 @@ export const NodeTypeLabels = {
|
|||
keyboard: undefined,
|
||||
keywords: []
|
||||
},
|
||||
editMetric: {
|
||||
label: 'Edit metric',
|
||||
keyboard: undefined,
|
||||
keywords: []
|
||||
},
|
||||
caption: {
|
||||
label: 'Caption',
|
||||
keyboard: undefined,
|
||||
|
|
|
@ -5,6 +5,8 @@ import { useChatLayoutContextSelector } from '@/layouts/ChatLayout';
|
|||
import { assetParamsToRoute } from '@/lib/assets/assetParamsToRoute';
|
||||
import React, { useMemo, useRef } from 'react';
|
||||
import { useMetricContentThreeDotMenuItems } from './useMetricContentThreeDotMenuItems';
|
||||
import { useFocused, useSelected } from 'platejs/react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export const MetricContent = React.memo(
|
||||
({
|
||||
|
@ -22,6 +24,8 @@ export const MetricContent = React.memo(
|
|||
const reportId = useChatLayoutContextSelector((x) => x.reportId) || '';
|
||||
const reportVersionNumber = useChatLayoutContextSelector((x) => x.reportVersionNumber);
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const isSelected = useSelected();
|
||||
const isFocused = useFocused();
|
||||
|
||||
const [inViewport] = useInViewport(ref, {
|
||||
threshold: 0.33
|
||||
|
@ -72,6 +76,9 @@ export const MetricContent = React.memo(
|
|||
|
||||
return (
|
||||
<MetricCard
|
||||
className={cn('transition-all duration-200', {
|
||||
'ring-ring ring-1 ring-offset-3': isSelected && isFocused
|
||||
})}
|
||||
ref={ref}
|
||||
metricLink={link}
|
||||
animate={!isExportMode}
|
||||
|
|
|
@ -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-kit';
|
||||
|
@ -33,14 +34,16 @@ export const MetricElement = withHOC(
|
|||
const mode = props.editor.getOption(GlobalVariablePlugin, 'mode');
|
||||
|
||||
const content = metricId ? (
|
||||
<MetricResizeContainer>
|
||||
<MetricContent
|
||||
metricId={metricId}
|
||||
metricVersionNumber={metricVersionNumber}
|
||||
readOnly={readOnly}
|
||||
isExportMode={mode === 'export'}
|
||||
/>
|
||||
</MetricResizeContainer>
|
||||
<MetricToolbar selectedMetricId={metricId}>
|
||||
<MetricResizeContainer>
|
||||
<MetricContent
|
||||
metricId={metricId}
|
||||
metricVersionNumber={metricVersionNumber}
|
||||
readOnly={readOnly}
|
||||
isExportMode={mode === 'export'}
|
||||
/>
|
||||
</MetricResizeContainer>
|
||||
</MetricToolbar>
|
||||
) : (
|
||||
<MetricEmbedPlaceholder />
|
||||
);
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
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-kit';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { CaptionButton } from '../CaptionNode';
|
||||
|
||||
export function MetricToolbar({
|
||||
children,
|
||||
selectedMetricId
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
selectedMetricId?: string;
|
||||
}) {
|
||||
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 preselectedMetrics = React.useMemo(() => {
|
||||
return selectedMetricId ? [{ id: selectedMetricId, name: '' }] : [];
|
||||
}, [selectedMetricId]);
|
||||
|
||||
const onOpenEdit = React.useCallback(() => {
|
||||
editor.tf.select();
|
||||
setOpenEditModal(true);
|
||||
}, [editor]);
|
||||
|
||||
const onCloseEdit = React.useCallback(() => {
|
||||
setOpenEditModal(false);
|
||||
}, []);
|
||||
|
||||
const handleAddMetrics = React.useCallback(
|
||||
async (metrics: { id: string; name: string }[]) => {
|
||||
const id = metrics?.[0]?.id;
|
||||
const at = editor.api.findPath(element);
|
||||
if (!id || !at) return onCloseEdit();
|
||||
plugin.api.metric.updateMetric(id, { at });
|
||||
onCloseEdit();
|
||||
},
|
||||
[editor, element, onCloseEdit, plugin.api.metric]
|
||||
);
|
||||
|
||||
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>
|
||||
|
||||
<CaptionButton variant="ghost">{NodeTypeLabels.caption.label}</CaptionButton>
|
||||
|
||||
<Separator orientation="vertical" className="mx-1 h-6" />
|
||||
|
||||
<Button prefix={<NodeTypeIcons.trash />} variant="ghost" {...removeButtonProps}></Button>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
|
||||
<AddMetricModal
|
||||
open={openEditModal}
|
||||
loading={false}
|
||||
selectedMetrics={preselectedMetrics}
|
||||
onClose={onCloseEdit}
|
||||
onAddMetrics={handleAddMetrics}
|
||||
selectionMode="single"
|
||||
saveButtonText="Update metric"
|
||||
/>
|
||||
</PopoverBase>
|
||||
);
|
||||
}
|
|
@ -17,5 +17,3 @@ export const downloadFile = async (url: string, filename: string) => {
|
|||
};
|
||||
|
||||
export default downloadFile;
|
||||
|
||||
|
||||
|
|
|
@ -29,5 +29,3 @@ export const exportToImage = async ({
|
|||
};
|
||||
|
||||
export default exportToImage;
|
||||
|
||||
|
||||
|
|
|
@ -54,5 +54,3 @@ export const getCanvas = async (editor: PlateEditor) => {
|
|||
};
|
||||
|
||||
export default getCanvas;
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue