use-tiptap-toolbar.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import type { Editor } from '@tiptap/vue-3';
  2. import type { ShallowRef } from 'vue';
  3. import type { ToolbarAction, ToolbarMenuItem } from './types';
  4. import { cn } from '@vben-core/shared/utils';
  5. interface UseTiptapToolbarOptions {
  6. editable: () => boolean;
  7. editor: Readonly<ShallowRef<Editor | undefined>>;
  8. }
  9. export function useTiptapToolbar(options: UseTiptapToolbarOptions) {
  10. const getEditor = () => options.editor.value;
  11. function getActionIndicatorColor(action: ToolbarAction) {
  12. const currentEditor = getEditor();
  13. if (!currentEditor || !action.indicatorColor) {
  14. return undefined;
  15. }
  16. return action.indicatorColor(currentEditor);
  17. }
  18. function getPaletteCurrentColor(action: ToolbarAction) {
  19. const currentEditor = getEditor();
  20. if (!currentEditor || !action.palette?.currentColor) {
  21. return undefined;
  22. }
  23. return action.palette.currentColor(currentEditor);
  24. }
  25. function canRunAction(action: ToolbarAction) {
  26. const currentEditor = getEditor();
  27. if (!currentEditor || !options.editable()) {
  28. return false;
  29. }
  30. return action.can ? action.can(currentEditor) : true;
  31. }
  32. function canRunMenuItem(item: ToolbarMenuItem) {
  33. const currentEditor = getEditor();
  34. if (!currentEditor || !options.editable()) {
  35. return false;
  36. }
  37. return item.can ? item.can(currentEditor) : true;
  38. }
  39. function isActionActive(action: ToolbarAction) {
  40. const currentEditor = getEditor();
  41. if (!currentEditor) {
  42. return false;
  43. }
  44. if (action.isActive) {
  45. return action.isActive(currentEditor);
  46. }
  47. if (!action.active) {
  48. return false;
  49. }
  50. return currentEditor.isActive(action.active.name, action.active.attrs);
  51. }
  52. function isMenuItemActive(item: ToolbarMenuItem, currentEditor?: Editor) {
  53. const targetEditor = currentEditor ?? getEditor();
  54. if (!targetEditor || !item.isActive) {
  55. return false;
  56. }
  57. return item.isActive(targetEditor);
  58. }
  59. function runAction(action: ToolbarAction) {
  60. const currentEditor = getEditor();
  61. if (!currentEditor || !options.editable()) {
  62. return;
  63. }
  64. if (action.menu || action.palette) {
  65. return;
  66. }
  67. action.action(currentEditor);
  68. }
  69. function runMenuItem(item: ToolbarMenuItem) {
  70. const currentEditor = getEditor();
  71. if (!currentEditor || !options.editable()) {
  72. return;
  73. }
  74. item.action(currentEditor);
  75. }
  76. function applyPaletteColor(action: ToolbarAction, color: string) {
  77. const currentEditor = getEditor();
  78. if (!currentEditor || !action.palette) {
  79. return;
  80. }
  81. action.palette.apply(currentEditor, color);
  82. }
  83. function clearPaletteColor(action: ToolbarAction) {
  84. const currentEditor = getEditor();
  85. if (!currentEditor || !action.palette?.clear) {
  86. return;
  87. }
  88. action.palette.clear(currentEditor);
  89. }
  90. function getToolbarButtonClass(action: ToolbarAction) {
  91. return cn(
  92. 'text-muted-foreground relative rounded-[10px] border border-transparent bg-transparent shadow-none',
  93. 'transition-[transform,color,background-color,border-color,box-shadow] duration-200 ease-out',
  94. 'enabled:hover:border-border enabled:hover:-translate-y-px disabled:opacity-45',
  95. 'enabled:hover:bg-accent enabled:hover:text-foreground',
  96. isActionActive(action) &&
  97. 'bg-accent border-primary/30 shadow-primary text-primary',
  98. );
  99. }
  100. function getPaletteSwatchClass(action: ToolbarAction, color: string) {
  101. return cn(
  102. 'border-border inline-flex size-8 items-center justify-center rounded-full border',
  103. 'shadow-accent',
  104. 'transition-[transform,box-shadow,border-color] duration-200 ease-out',
  105. 'hover:-translate-y-px hover:scale-[1.04]',
  106. getPaletteCurrentColor(action) === color &&
  107. 'border-primary shadow-primary',
  108. );
  109. }
  110. function getMenuItemClass(item: ToolbarMenuItem) {
  111. return cn(
  112. 'flex items-center gap-2 rounded-lg p-2 text-left text-sm transition-colors',
  113. 'disabled:cursor-not-allowed disabled:opacity-45',
  114. isMenuItemActive(item)
  115. ? 'bg-accent text-foreground'
  116. : 'hover:bg-accent hover:text-foreground text-muted-foreground',
  117. );
  118. }
  119. return {
  120. applyPaletteColor,
  121. canRunAction,
  122. canRunMenuItem,
  123. clearPaletteColor,
  124. getActionIndicatorColor,
  125. getMenuItemClass,
  126. getPaletteCurrentColor,
  127. getPaletteSwatchClass,
  128. getToolbarButtonClass,
  129. isActionActive,
  130. isMenuItemActive,
  131. runAction,
  132. runMenuItem,
  133. };
  134. }