177 lines
4.4 KiB
TypeScript
177 lines
4.4 KiB
TypeScript
import type { Editor } from '@tiptap/vue-3';
|
|
|
|
import type { ShallowRef } from 'vue';
|
|
|
|
import type { ToolbarAction, ToolbarMenuItem } from './types';
|
|
|
|
import { cn } from '@vben-core/shared/utils';
|
|
|
|
interface UseTiptapToolbarOptions {
|
|
editable: () => boolean;
|
|
editor: Readonly<ShallowRef<Editor | undefined>>;
|
|
}
|
|
|
|
export function useTiptapToolbar(options: UseTiptapToolbarOptions) {
|
|
const getEditor = () => options.editor.value;
|
|
|
|
function getActionIndicatorColor(action: ToolbarAction) {
|
|
const currentEditor = getEditor();
|
|
|
|
if (!currentEditor || !action.indicatorColor) {
|
|
return undefined;
|
|
}
|
|
|
|
return action.indicatorColor(currentEditor);
|
|
}
|
|
|
|
function getPaletteCurrentColor(action: ToolbarAction) {
|
|
const currentEditor = getEditor();
|
|
|
|
if (!currentEditor || !action.palette?.currentColor) {
|
|
return undefined;
|
|
}
|
|
|
|
return action.palette.currentColor(currentEditor);
|
|
}
|
|
|
|
function canRunAction(action: ToolbarAction) {
|
|
const currentEditor = getEditor();
|
|
|
|
if (!currentEditor || !options.editable()) {
|
|
return false;
|
|
}
|
|
|
|
return action.can ? action.can(currentEditor) : true;
|
|
}
|
|
|
|
function canRunMenuItem(item: ToolbarMenuItem) {
|
|
const currentEditor = getEditor();
|
|
|
|
if (!currentEditor || !options.editable()) {
|
|
return false;
|
|
}
|
|
|
|
return item.can ? item.can(currentEditor) : true;
|
|
}
|
|
|
|
function isActionActive(action: ToolbarAction) {
|
|
const currentEditor = getEditor();
|
|
|
|
if (!currentEditor) {
|
|
return false;
|
|
}
|
|
|
|
if (action.isActive) {
|
|
return action.isActive(currentEditor);
|
|
}
|
|
|
|
if (!action.active) {
|
|
return false;
|
|
}
|
|
|
|
return currentEditor.isActive(action.active.name, action.active.attrs);
|
|
}
|
|
|
|
function isMenuItemActive(item: ToolbarMenuItem, currentEditor?: Editor) {
|
|
const targetEditor = currentEditor ?? getEditor();
|
|
|
|
if (!targetEditor || !item.isActive) {
|
|
return false;
|
|
}
|
|
|
|
return item.isActive(targetEditor);
|
|
}
|
|
|
|
function runAction(action: ToolbarAction) {
|
|
const currentEditor = getEditor();
|
|
|
|
if (!currentEditor || !options.editable()) {
|
|
return;
|
|
}
|
|
|
|
if (action.menu || action.palette) {
|
|
return;
|
|
}
|
|
|
|
action.action(currentEditor);
|
|
}
|
|
|
|
function runMenuItem(item: ToolbarMenuItem) {
|
|
const currentEditor = getEditor();
|
|
|
|
if (!currentEditor || !options.editable()) {
|
|
return;
|
|
}
|
|
|
|
item.action(currentEditor);
|
|
}
|
|
|
|
function applyPaletteColor(action: ToolbarAction, color: string) {
|
|
const currentEditor = getEditor();
|
|
|
|
if (!currentEditor || !action.palette) {
|
|
return;
|
|
}
|
|
|
|
action.palette.apply(currentEditor, color);
|
|
}
|
|
|
|
function clearPaletteColor(action: ToolbarAction) {
|
|
const currentEditor = getEditor();
|
|
|
|
if (!currentEditor || !action.palette?.clear) {
|
|
return;
|
|
}
|
|
|
|
action.palette.clear(currentEditor);
|
|
}
|
|
|
|
function getToolbarButtonClass(action: ToolbarAction) {
|
|
return cn(
|
|
'text-muted-foreground relative rounded-[10px] border border-transparent bg-transparent shadow-none',
|
|
'transition-[transform,color,background-color,border-color,box-shadow] duration-200 ease-out',
|
|
'enabled:hover:border-border enabled:hover:-translate-y-px disabled:opacity-45',
|
|
'enabled:hover:bg-accent enabled:hover:text-foreground',
|
|
isActionActive(action) &&
|
|
'bg-accent border-primary/30 shadow-primary text-primary',
|
|
);
|
|
}
|
|
|
|
function getPaletteSwatchClass(action: ToolbarAction, color: string) {
|
|
return cn(
|
|
'border-border inline-flex size-8 items-center justify-center rounded-full border',
|
|
'shadow-accent',
|
|
'transition-[transform,box-shadow,border-color] duration-200 ease-out',
|
|
'hover:-translate-y-px hover:scale-[1.04]',
|
|
getPaletteCurrentColor(action) === color &&
|
|
'border-primary shadow-primary',
|
|
);
|
|
}
|
|
|
|
function getMenuItemClass(item: ToolbarMenuItem) {
|
|
return cn(
|
|
'flex items-center gap-2 rounded-lg p-2 text-left text-sm transition-colors',
|
|
'disabled:cursor-not-allowed disabled:opacity-45',
|
|
isMenuItemActive(item)
|
|
? 'bg-accent text-foreground'
|
|
: 'hover:bg-accent hover:text-foreground text-muted-foreground',
|
|
);
|
|
}
|
|
|
|
return {
|
|
applyPaletteColor,
|
|
canRunAction,
|
|
canRunMenuItem,
|
|
clearPaletteColor,
|
|
getActionIndicatorColor,
|
|
getMenuItemClass,
|
|
getPaletteCurrentColor,
|
|
getPaletteSwatchClass,
|
|
getToolbarButtonClass,
|
|
isActionActive,
|
|
isMenuItemActive,
|
|
runAction,
|
|
runMenuItem,
|
|
};
|
|
}
|