@kopexa/tiptap 17.7.0 → 17.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.js +2059 -1347
  2. package/dist/index.mjs +2063 -1322
  3. package/package.json +24 -24
package/dist/index.mjs CHANGED
@@ -991,7 +991,7 @@ var ImageNode = TiptapImage.extend({
991
991
  return ReactNodeViewRenderer2(ImageNodeView, {
992
992
  stopEvent: ({ event }) => {
993
993
  const target = event.target;
994
- if (target.closest("button, [role='button'], input, a")) {
994
+ if (target instanceof HTMLElement && target.closest("button, [role='button'], input, a")) {
995
995
  return true;
996
996
  }
997
997
  return false;
@@ -1350,7 +1350,7 @@ var ImageUploadNode = Node2.create({
1350
1350
  return ReactNodeViewRenderer3(ImageUploadNodeView, {
1351
1351
  stopEvent: ({ event }) => {
1352
1352
  const target = event.target;
1353
- if (target.closest("button, [role='button'], input, a")) {
1353
+ if (target instanceof HTMLElement && target.closest("button, [role='button'], input, a")) {
1354
1354
  return true;
1355
1355
  }
1356
1356
  return false;
@@ -2743,6 +2743,7 @@ import * as React6 from "react";
2743
2743
 
2744
2744
  // src/ui/suggestion-menu/suggestion-menu.tsx
2745
2745
  import { FloatingPortal, flip, offset, shift, size } from "@floating-ui/react";
2746
+ import { useTiptapEditor } from "@kopexa/editor-utils";
2746
2747
  import { PluginKey } from "@tiptap/pm/state";
2747
2748
  import {
2748
2749
  Suggestion,
@@ -2928,35 +2929,6 @@ function useMenuNavigation({
2928
2929
  };
2929
2930
  }
2930
2931
 
2931
- // src/hooks/use-tiptap-editor.ts
2932
- import { useCurrentEditor, useEditorState as useEditorState7 } from "@tiptap/react";
2933
- import { useMemo as useMemo12 } from "react";
2934
- function useTiptapEditor(providedEditor) {
2935
- const { editor: coreEditor } = useCurrentEditor();
2936
- const mainEditor = useMemo12(
2937
- () => providedEditor || coreEditor,
2938
- [providedEditor, coreEditor]
2939
- );
2940
- const editorState = useEditorState7({
2941
- editor: mainEditor,
2942
- selector(context) {
2943
- if (!context.editor) {
2944
- return {
2945
- editor: null,
2946
- editorState: void 0,
2947
- canCommand: void 0
2948
- };
2949
- }
2950
- return {
2951
- editor: context.editor,
2952
- editorState: context.editor.state,
2953
- canCommand: context.editor.can
2954
- };
2955
- }
2956
- });
2957
- return editorState || { editor: null };
2958
- }
2959
-
2960
2932
  // src/ui/suggestion-menu/suggestion-menu-utils.ts
2961
2933
  function calculateStartPosition(cursorPosition, previousNode, triggerChar) {
2962
2934
  if (!(previousNode == null ? void 0 : previousNode.text) || !triggerChar) {
@@ -3434,11 +3406,290 @@ var Link = TiptapLink.extend({
3434
3406
  }
3435
3407
  });
3436
3408
 
3409
+ // src/extensions/node-alignment/index.ts
3410
+ import { getSelectedNodesOfType, updateNodesAttr } from "@kopexa/editor-utils";
3411
+ import { Extension } from "@tiptap/core";
3412
+ function getToggleValue(targets, attributeName, inputValue) {
3413
+ var _a, _b;
3414
+ if (targets.length === 0) return null;
3415
+ for (const target of targets) {
3416
+ const currentValue = (_b = (_a = target.node.attrs) == null ? void 0 : _a[attributeName]) != null ? _b : null;
3417
+ if (currentValue !== inputValue) {
3418
+ return inputValue;
3419
+ }
3420
+ }
3421
+ return null;
3422
+ }
3423
+ var NodeAlignment = Extension.create({
3424
+ name: "nodeAlignment",
3425
+ addOptions() {
3426
+ return {
3427
+ types: ["paragraph", "heading", "blockquote", "tableCell", "tableHeader"],
3428
+ useStyle: true,
3429
+ textAlignValues: ["left", "center", "right", "justify"],
3430
+ verticalAlignValues: ["top", "middle", "bottom"]
3431
+ };
3432
+ },
3433
+ addGlobalAttributes() {
3434
+ return [
3435
+ {
3436
+ types: this.options.types,
3437
+ attributes: {
3438
+ nodeTextAlign: {
3439
+ default: null,
3440
+ parseHTML: (element) => {
3441
+ var _a;
3442
+ const styleAlign = (_a = element.style) == null ? void 0 : _a.textAlign;
3443
+ if (styleAlign && this.options.textAlignValues.includes(styleAlign)) {
3444
+ return styleAlign;
3445
+ }
3446
+ const dataAlign = element.getAttribute("data-node-text-align");
3447
+ if (dataAlign && this.options.textAlignValues.includes(dataAlign)) {
3448
+ return dataAlign;
3449
+ }
3450
+ return null;
3451
+ },
3452
+ renderHTML: (attributes) => {
3453
+ const align = attributes.nodeTextAlign;
3454
+ if (!align || !this.options.textAlignValues.includes(align))
3455
+ return {};
3456
+ if (this.options.useStyle) {
3457
+ return { style: `text-align: ${align}` };
3458
+ } else {
3459
+ return { "data-node-text-align": align };
3460
+ }
3461
+ }
3462
+ },
3463
+ nodeVerticalAlign: {
3464
+ default: null,
3465
+ parseHTML: (element) => {
3466
+ var _a;
3467
+ const styleVAlign = (_a = element.style) == null ? void 0 : _a.verticalAlign;
3468
+ if (styleVAlign && this.options.verticalAlignValues.includes(styleVAlign)) {
3469
+ return styleVAlign;
3470
+ }
3471
+ const dataVAlign = element.getAttribute(
3472
+ "data-node-vertical-align"
3473
+ );
3474
+ if (dataVAlign && this.options.verticalAlignValues.includes(dataVAlign)) {
3475
+ return dataVAlign;
3476
+ }
3477
+ return null;
3478
+ },
3479
+ renderHTML: (attributes) => {
3480
+ const vAlign = attributes.nodeVerticalAlign;
3481
+ if (!vAlign || !this.options.verticalAlignValues.includes(vAlign))
3482
+ return {};
3483
+ if (this.options.useStyle) {
3484
+ return { style: `vertical-align: ${vAlign}` };
3485
+ } else {
3486
+ return { "data-node-vertical-align": vAlign };
3487
+ }
3488
+ }
3489
+ }
3490
+ }
3491
+ }
3492
+ ];
3493
+ },
3494
+ addCommands() {
3495
+ const executeAlignmentCommand = (attributeName, getTargetValue) => {
3496
+ return (inputValue) => ({ state, tr }) => {
3497
+ const targets = getSelectedNodesOfType(
3498
+ state.selection,
3499
+ this.options.types
3500
+ );
3501
+ if (targets.length === 0) return false;
3502
+ const targetValue = getTargetValue(targets, inputValue);
3503
+ return updateNodesAttr(tr, targets, attributeName, targetValue);
3504
+ };
3505
+ };
3506
+ return {
3507
+ // TEXT ALIGN
3508
+ setNodeTextAlign: executeAlignmentCommand(
3509
+ "nodeTextAlign",
3510
+ (_, inputValue) => {
3511
+ if (!inputValue || !this.options.textAlignValues.includes(inputValue))
3512
+ return null;
3513
+ return inputValue;
3514
+ }
3515
+ ),
3516
+ unsetNodeTextAlign: executeAlignmentCommand("nodeTextAlign", () => null),
3517
+ toggleNodeTextAlign: executeAlignmentCommand(
3518
+ "nodeTextAlign",
3519
+ (targets, inputValue) => {
3520
+ if (!inputValue || !this.options.textAlignValues.includes(inputValue))
3521
+ return null;
3522
+ return getToggleValue(targets, "nodeTextAlign", inputValue);
3523
+ }
3524
+ ),
3525
+ // VERTICAL ALIGN
3526
+ setNodeVAlign: executeAlignmentCommand(
3527
+ "nodeVerticalAlign",
3528
+ (_, inputValue) => {
3529
+ if (!inputValue || !this.options.verticalAlignValues.includes(inputValue))
3530
+ return null;
3531
+ return inputValue;
3532
+ }
3533
+ ),
3534
+ unsetNodeVAlign: executeAlignmentCommand("nodeVerticalAlign", () => null),
3535
+ toggleNodeVAlign: executeAlignmentCommand(
3536
+ "nodeVerticalAlign",
3537
+ (targets, inputValue) => {
3538
+ if (!inputValue || !this.options.verticalAlignValues.includes(inputValue))
3539
+ return null;
3540
+ return getToggleValue(targets, "nodeVerticalAlign", inputValue);
3541
+ }
3542
+ ),
3543
+ // BOTH
3544
+ setNodeAlignment: (textAlign, verticalAlign) => ({ state, tr }) => {
3545
+ const targets = getSelectedNodesOfType(
3546
+ state.selection,
3547
+ this.options.types
3548
+ );
3549
+ if (targets.length === 0) return false;
3550
+ let hasChanges = false;
3551
+ for (const { node, pos } of targets) {
3552
+ const newAttrs = { ...node.attrs };
3553
+ if (textAlign && this.options.textAlignValues.includes(textAlign)) {
3554
+ newAttrs.nodeTextAlign = textAlign;
3555
+ hasChanges = true;
3556
+ }
3557
+ if (verticalAlign && this.options.verticalAlignValues.includes(verticalAlign)) {
3558
+ newAttrs.nodeVerticalAlign = verticalAlign;
3559
+ hasChanges = true;
3560
+ }
3561
+ if (hasChanges) tr.setNodeMarkup(pos, void 0, newAttrs);
3562
+ }
3563
+ return hasChanges;
3564
+ },
3565
+ unsetNodeAlignment: () => ({ state, tr }) => {
3566
+ var _a, _b, _c, _d;
3567
+ const targets = getSelectedNodesOfType(
3568
+ state.selection,
3569
+ this.options.types
3570
+ );
3571
+ if (targets.length === 0) return false;
3572
+ let hasChanges = false;
3573
+ for (const { node, pos } of targets) {
3574
+ const hasText = (_b = (_a = node.attrs) == null ? void 0 : _a.nodeTextAlign) != null ? _b : null;
3575
+ const hasV = (_d = (_c = node.attrs) == null ? void 0 : _c.nodeVerticalAlign) != null ? _d : null;
3576
+ if (hasText || hasV) {
3577
+ const newAttrs = {
3578
+ ...node.attrs,
3579
+ nodeTextAlign: null,
3580
+ nodeVerticalAlign: null
3581
+ };
3582
+ tr.setNodeMarkup(pos, void 0, newAttrs);
3583
+ hasChanges = true;
3584
+ }
3585
+ }
3586
+ return hasChanges;
3587
+ }
3588
+ };
3589
+ }
3590
+ });
3591
+
3592
+ // src/extensions/node-background/index.ts
3593
+ import { getSelectedNodesOfType as getSelectedNodesOfType2, updateNodesAttr as updateNodesAttr2 } from "@kopexa/editor-utils";
3594
+ import { Extension as Extension2 } from "@tiptap/core";
3595
+ function getToggleColor(targets, inputColor) {
3596
+ var _a, _b;
3597
+ if (targets.length === 0) return null;
3598
+ for (const target of targets) {
3599
+ const currentColor = (_b = (_a = target.node.attrs) == null ? void 0 : _a.backgroundColor) != null ? _b : null;
3600
+ if (currentColor !== inputColor) {
3601
+ return inputColor;
3602
+ }
3603
+ }
3604
+ return null;
3605
+ }
3606
+ var NodeBackground = Extension2.create({
3607
+ name: "nodeBackground",
3608
+ addOptions() {
3609
+ return {
3610
+ types: [
3611
+ "paragraph",
3612
+ "heading",
3613
+ "blockquote",
3614
+ "taskList",
3615
+ "bulletList",
3616
+ "orderedList",
3617
+ "tableCell",
3618
+ "tableHeader"
3619
+ ],
3620
+ useStyle: true
3621
+ };
3622
+ },
3623
+ addGlobalAttributes() {
3624
+ return [
3625
+ {
3626
+ types: this.options.types,
3627
+ attributes: {
3628
+ backgroundColor: {
3629
+ default: null,
3630
+ parseHTML: (element) => {
3631
+ var _a;
3632
+ const styleColor = (_a = element.style) == null ? void 0 : _a.backgroundColor;
3633
+ if (styleColor) return styleColor;
3634
+ const dataColor = element.getAttribute("data-background-color");
3635
+ return dataColor || null;
3636
+ },
3637
+ renderHTML: (attributes) => {
3638
+ const color = attributes.backgroundColor;
3639
+ if (!color) return {};
3640
+ if (this.options.useStyle) {
3641
+ return {
3642
+ style: `background-color: ${color}`
3643
+ };
3644
+ } else {
3645
+ return {
3646
+ "data-background-color": color
3647
+ };
3648
+ }
3649
+ }
3650
+ }
3651
+ }
3652
+ }
3653
+ ];
3654
+ },
3655
+ addCommands() {
3656
+ const executeBackgroundCommand = (getTargetColor) => {
3657
+ return (inputColor) => ({ state, tr }) => {
3658
+ const targets = getSelectedNodesOfType2(
3659
+ state.selection,
3660
+ this.options.types
3661
+ );
3662
+ if (targets.length === 0) return false;
3663
+ const targetColor = getTargetColor(targets, inputColor);
3664
+ return updateNodesAttr2(tr, targets, "backgroundColor", targetColor);
3665
+ };
3666
+ };
3667
+ return {
3668
+ /**
3669
+ * Set background color to specific value
3670
+ */
3671
+ setNodeBackgroundColor: executeBackgroundCommand(
3672
+ (_, inputColor) => inputColor || null
3673
+ ),
3674
+ /**
3675
+ * Remove background color
3676
+ */
3677
+ unsetNodeBackgroundColor: executeBackgroundCommand(() => null),
3678
+ /**
3679
+ * Toggle background color (set if different/missing, unset if all have it)
3680
+ */
3681
+ toggleNodeBackgroundColor: executeBackgroundCommand(
3682
+ (targets, inputColor) => getToggleColor(targets, inputColor || "")
3683
+ )
3684
+ };
3685
+ }
3686
+ });
3687
+
3437
3688
  // src/extensions/selection/index.ts
3438
3689
  import { Plugin as Plugin2, PluginKey as PluginKey4 } from "@tiptap/pm/state";
3439
3690
  import { Decoration, DecorationSet } from "@tiptap/pm/view";
3440
- import { Extension, isNodeSelection } from "@tiptap/react";
3441
- var Selection = Extension.create({
3691
+ import { Extension as Extension3, isNodeSelection } from "@tiptap/react";
3692
+ var Selection = Extension3.create({
3442
3693
  name: "selection",
3443
3694
  addProseMirrorPlugins() {
3444
3695
  const { editor } = this;
@@ -3470,7 +3721,7 @@ var Selection = Extension.create({
3470
3721
 
3471
3722
  // src/extensions/trailing-node/index.ts
3472
3723
  import { Plugin as Plugin3, PluginKey as PluginKey5 } from "@tiptap/pm/state";
3473
- import { Extension as Extension2 } from "@tiptap/react";
3724
+ import { Extension as Extension4 } from "@tiptap/react";
3474
3725
  function nodeEqualsType({
3475
3726
  types,
3476
3727
  node
@@ -3481,7 +3732,7 @@ function nodeEqualsType({
3481
3732
  }
3482
3733
  return node.type === types;
3483
3734
  }
3484
- var TrailingNode = Extension2.create({
3735
+ var TrailingNode = Extension4.create({
3485
3736
  name: "trailingNode",
3486
3737
  addOptions() {
3487
3738
  return {
@@ -3527,7 +3778,7 @@ var TrailingNode = Extension2.create({
3527
3778
  });
3528
3779
 
3529
3780
  // src/extensions/ui-state/index.ts
3530
- import { Extension as Extension3 } from "@tiptap/core";
3781
+ import { Extension as Extension5 } from "@tiptap/core";
3531
3782
  var defaultUiState = {
3532
3783
  aiGenerationIsSelection: false,
3533
3784
  aiGenerationIsLoading: false,
@@ -3537,7 +3788,7 @@ var defaultUiState = {
3537
3788
  lockDragHandle: false,
3538
3789
  isDragging: false
3539
3790
  };
3540
- var UiState = Extension3.create({
3791
+ var UiState = Extension5.create({
3541
3792
  name: "uiState",
3542
3793
  addStorage() {
3543
3794
  return {
@@ -3862,6 +4113,8 @@ function getExtensions({
3862
4113
  }),
3863
4114
  UiState,
3864
4115
  TableKit,
4116
+ NodeAlignment,
4117
+ NodeBackground,
3865
4118
  TocNode,
3866
4119
  CalloutNode,
3867
4120
  MathBlock,
@@ -4010,6 +4263,11 @@ async function handleFileUpload(editor, file, fileHandler, pos) {
4010
4263
  }
4011
4264
 
4012
4265
  // src/presets/basic/index.tsx
4266
+ import {
4267
+ TableCellHandleMenu,
4268
+ TableHandle,
4269
+ TableSelectionOverlay
4270
+ } from "@kopexa/extension-table";
4013
4271
  import {
4014
4272
  editorBasic,
4015
4273
  editorSpinner
@@ -4018,17 +4276,17 @@ import {
4018
4276
  EditorContent,
4019
4277
  EditorContext
4020
4278
  } from "@tiptap/react";
4021
- import { useCallback as useCallback36, useContext as useContext4 } from "react";
4279
+ import { useCallback as useCallback38, useContext as useContext4 } from "react";
4022
4280
 
4023
4281
  // src/context/editor-context.ts
4024
4282
  import { createContext as createContext4 } from "@kopexa/react-utils";
4025
4283
  var [EditorUIProvider, useEditorUIContext] = createContext4();
4026
4284
 
4027
4285
  // src/hooks/use-ui-editor-state.ts
4028
- import { useEditorState as useEditorState8 } from "@tiptap/react";
4286
+ import { useEditorState as useEditorState7 } from "@tiptap/react";
4029
4287
  function useUiEditorState(editor) {
4030
4288
  var _a;
4031
- return (_a = useEditorState8({
4289
+ return (_a = useEditorState7({
4032
4290
  editor,
4033
4291
  selector: ({ editor: editor2 }) => {
4034
4292
  if (!editor2) return defaultUiState;
@@ -4050,6 +4308,7 @@ import { BubbleMenu as TiptapBubbleMenu } from "@tiptap/react/menus";
4050
4308
 
4051
4309
  // src/ui/link-popover/link-popover.tsx
4052
4310
  import { IconButton as IconButton6 } from "@kopexa/button";
4311
+ import { useTiptapEditor as useTiptapEditor3 } from "@kopexa/editor-utils";
4053
4312
  import {
4054
4313
  CheckIcon,
4055
4314
  EditIcon as EditIcon2,
@@ -4063,7 +4322,7 @@ import { ToolbarButton } from "@kopexa/toolbar";
4063
4322
  import { useCallback as useCallback14, useEffect as useEffect16, useState as useState15 } from "react";
4064
4323
 
4065
4324
  // src/ui/link-popover/use-link-popover.ts
4066
- import { isMarkInSchema } from "@kopexa/editor-utils";
4325
+ import { isMarkInSchema, useTiptapEditor as useTiptapEditor2 } from "@kopexa/editor-utils";
4067
4326
  import { LinkIcon } from "@kopexa/icons";
4068
4327
  import * as React7 from "react";
4069
4328
 
@@ -4269,7 +4528,7 @@ function useLinkPopover(config) {
4269
4528
  hideWhenUnavailable = false,
4270
4529
  onSetLink
4271
4530
  } = config || {};
4272
- const { editor } = useTiptapEditor(providedEditor);
4531
+ const { editor } = useTiptapEditor2(providedEditor);
4273
4532
  const { isVisible, canSet, isActive } = useLinkState({
4274
4533
  editor,
4275
4534
  hideWhenUnavailable
@@ -4432,7 +4691,7 @@ function LinkPopover({
4432
4691
  children,
4433
4692
  ...buttonProps
4434
4693
  }) {
4435
- const { editor } = useTiptapEditor(providedEditor);
4694
+ const { editor } = useTiptapEditor3(providedEditor);
4436
4695
  const [isOpen, setIsOpen] = useState15(false);
4437
4696
  const {
4438
4697
  isVisible,
@@ -4519,7 +4778,11 @@ function LinkPopover({
4519
4778
  LinkButton.displayName = "LinkButton";
4520
4779
 
4521
4780
  // src/ui/mark-button/index.tsx
4522
- import { isMarkInSchema as isMarkInSchema2, isNodeTypeSelected } from "@kopexa/editor-utils";
4781
+ import {
4782
+ isMarkInSchema as isMarkInSchema2,
4783
+ isNodeTypeSelected,
4784
+ useTiptapEditor as useTiptapEditor4
4785
+ } from "@kopexa/editor-utils";
4523
4786
  import {
4524
4787
  BoldIcon,
4525
4788
  CodeIcon,
@@ -4531,7 +4794,7 @@ import {
4531
4794
  } from "@kopexa/icons";
4532
4795
  import { ToolbarButton as ToolbarButton2 } from "@kopexa/toolbar";
4533
4796
  import { isNodeSelection as isNodeSelection2 } from "@tiptap/react";
4534
- import { useCallback as useCallback15, useMemo as useMemo14 } from "react";
4797
+ import { useCallback as useCallback15, useMemo as useMemo13 } from "react";
4535
4798
  import { jsx as jsx17 } from "react/jsx-runtime";
4536
4799
  var markIcons = {
4537
4800
  bold: BoldIcon,
@@ -4614,7 +4877,7 @@ var MarkButton = ({
4614
4877
  children,
4615
4878
  ...buttonProps
4616
4879
  }) => {
4617
- const { editor } = useTiptapEditor(providedEditor);
4880
+ const { editor } = useTiptapEditor4(providedEditor);
4618
4881
  const {
4619
4882
  markInSchema,
4620
4883
  isDisabled,
@@ -4632,7 +4895,7 @@ var MarkButton = ({
4632
4895
  },
4633
4896
  [onClick, isDisabled, editor, type]
4634
4897
  );
4635
- const show = useMemo14(() => {
4898
+ const show = useMemo13(() => {
4636
4899
  return shouldShowMarkButton({
4637
4900
  editor,
4638
4901
  type,
@@ -4707,7 +4970,7 @@ function BubbleMenu({ editor }) {
4707
4970
  }
4708
4971
 
4709
4972
  // src/ui/copy-anchor-link-button/use-scroll-to-hash.ts
4710
- import { getEditorExtension } from "@kopexa/editor-utils";
4973
+ import { getEditorExtension, useTiptapEditor as useTiptapEditor5 } from "@kopexa/editor-utils";
4711
4974
  import * as React9 from "react";
4712
4975
 
4713
4976
  // src/hooks/use-floating-toolbar-visibility.ts
@@ -4732,7 +4995,7 @@ function useScrollToHash(config = {}) {
4732
4995
  onTargetNotFound = () => {
4733
4996
  }
4734
4997
  } = config;
4735
- const { editor } = useTiptapEditor(providedEditor);
4998
+ const { editor } = useTiptapEditor5(providedEditor);
4736
4999
  const scrollToNode = React9.useCallback(
4737
5000
  (id) => {
4738
5001
  var _a, _b, _c;
@@ -4997,7 +5260,11 @@ import {
4997
5260
  import * as React10 from "react";
4998
5261
 
4999
5262
  // src/ui/table-button/use-table.ts
5000
- import { isNodeInSchema, isNodeTypeSelected as isNodeTypeSelected2 } from "@kopexa/editor-utils";
5263
+ import {
5264
+ isNodeInSchema,
5265
+ isNodeTypeSelected as isNodeTypeSelected2,
5266
+ useTiptapEditor as useTiptapEditor6
5267
+ } from "@kopexa/editor-utils";
5001
5268
  import { TableIcon } from "@kopexa/icons";
5002
5269
  import { isNodeSelection as isNodeSelection4 } from "@tiptap/react";
5003
5270
  import { useCallback as useCallback18, useEffect as useEffect20, useState as useState18 } from "react";
@@ -5043,7 +5310,7 @@ function useTableBlock(config) {
5043
5310
  hideWhenUnavailable = false,
5044
5311
  onToggled
5045
5312
  } = config || {};
5046
- const { editor } = useTiptapEditor(providedEditor);
5313
+ const { editor } = useTiptapEditor6(providedEditor);
5047
5314
  const [isVisible, setIsVisible] = useState18(true);
5048
5315
  const canToggleState = canToggle(editor);
5049
5316
  const isActive = (editor == null ? void 0 : editor.isActive("table")) || false;
@@ -5604,6 +5871,7 @@ var List = ({
5604
5871
  };
5605
5872
 
5606
5873
  // src/presets/basic/editor-header.tsx
5874
+ import { useTiptapEditor as useTiptapEditor26 } from "@kopexa/editor-utils";
5607
5875
  import { MoreVerticalIcon } from "@kopexa/icons";
5608
5876
  import { Popover as Popover3 } from "@kopexa/popover";
5609
5877
  import {
@@ -5612,8 +5880,8 @@ import {
5612
5880
  ToolbarGroup as ToolbarGroup2,
5613
5881
  ToolbarSeparator as ToolbarSeparator3
5614
5882
  } from "@kopexa/toolbar";
5615
- import { useIsMobile as useIsMobile2 } from "@kopexa/use-is-mobile";
5616
- import { useEffect as useEffect29, useRef as useRef11, useState as useState29 } from "react";
5883
+ import { useIsMobile as useIsMobile3 } from "@kopexa/use-is-mobile";
5884
+ import { useEffect as useEffect31, useRef as useRef11, useState as useState30 } from "react";
5617
5885
 
5618
5886
  // src/hooks/use-cursor-visibility.ts
5619
5887
  import * as React13 from "react";
@@ -5717,99 +5985,126 @@ function useCursorVisibility({
5717
5985
  return rect;
5718
5986
  }
5719
5987
 
5720
- // src/ui/blockquote-button/blockquote-button.tsx
5988
+ // src/ui/color-highlight-popover/color-highlight-popover.tsx
5989
+ import { IconButton as IconButton8 } from "@kopexa/button";
5990
+ import { useTiptapEditor as useTiptapEditor9 } from "@kopexa/editor-utils";
5991
+ import { BanIcon, HighlighterIcon as HighlighterIcon2 } from "@kopexa/icons";
5992
+ import { Popover as Popover2 } from "@kopexa/popover";
5993
+ import { ToolbarSeparator as ToolbarSeparator2 } from "@kopexa/toolbar";
5994
+ import { useMemo as useMemo16, useRef as useRef10, useState as useState22 } from "react";
5995
+
5996
+ // src/ui/color-highlight-button/color-highlight-button.tsx
5997
+ import { useTiptapEditor as useTiptapEditor8 } from "@kopexa/editor-utils";
5998
+ import { colorHighlightButton } from "@kopexa/theme";
5721
5999
  import { ToolbarButton as ToolbarButton3 } from "@kopexa/toolbar";
5722
- import * as React15 from "react";
6000
+ import { useCallback as useCallback22, useMemo as useMemo15 } from "react";
5723
6001
 
5724
- // src/ui/blockquote-button/use-blockquote.ts
6002
+ // src/ui/color-highlight-button/use-color-highlight.ts
5725
6003
  import {
5726
- findNodePosition,
5727
- isNodeInSchema as isNodeInSchema3,
6004
+ isMarkInSchema as isMarkInSchema3,
5728
6005
  isNodeTypeSelected as isNodeTypeSelected3,
5729
- isValidPosition
6006
+ useTiptapEditor as useTiptapEditor7
5730
6007
  } from "@kopexa/editor-utils";
5731
- import { BlockquoteIcon as BlockquoteIcon2 } from "@kopexa/icons";
5732
- import { NodeSelection as NodeSelection2, TextSelection as TextSelection2 } from "@tiptap/pm/state";
6008
+ import { HighlighterIcon } from "@kopexa/icons";
6009
+ import { useIsMobile } from "@kopexa/use-is-mobile";
5733
6010
  import * as React14 from "react";
5734
- var BLOCKQUOTE_SHORTCUT_KEY = "mod+shift+b";
5735
- function canToggleBlockquote(editor, turnInto = true) {
5736
- var _a;
5737
- if (!editor || !editor.isEditable) return false;
5738
- if (!isNodeInSchema3("blockquote", editor) || isNodeTypeSelected3(editor, ["image"]))
5739
- return false;
5740
- if (!turnInto) {
5741
- return editor.can().toggleWrap("blockquote");
5742
- }
5743
- try {
5744
- const view = editor.view;
5745
- const state = view.state;
5746
- const selection = state.selection;
5747
- if (selection.empty || selection instanceof TextSelection2) {
5748
- const pos = (_a = findNodePosition({
5749
- editor,
5750
- node: state.selection.$anchor.node(1)
5751
- })) == null ? void 0 : _a.pos;
5752
- if (!isValidPosition(pos)) return false;
5753
- }
5754
- return true;
5755
- } catch {
5756
- return false;
6011
+ import { useHotkeys } from "react-hotkeys-hook";
6012
+ var COLOR_HIGHLIGHT_SHORTCUT_KEY = "mod+shift+h";
6013
+ var HIGHLIGHT_COLORS = [
6014
+ {
6015
+ label: "Default background",
6016
+ value: "var(--tt-bg-color)",
6017
+ border: "var(--tt-bg-color-contrast)"
6018
+ },
6019
+ {
6020
+ label: "Gray background",
6021
+ value: "var(--tt-color-highlight-gray)",
6022
+ border: "var(--tt-color-highlight-gray-contrast)"
6023
+ },
6024
+ {
6025
+ label: "Brown background",
6026
+ value: "var(--tt-color-highlight-brown)",
6027
+ border: "var(--tt-color-highlight-brown-contrast)"
6028
+ },
6029
+ {
6030
+ label: "Orange background",
6031
+ value: "var(--tt-color-highlight-orange)",
6032
+ border: "var(--tt-color-highlight-orange-contrast)"
6033
+ },
6034
+ {
6035
+ label: "Yellow background",
6036
+ value: "var(--tt-color-highlight-yellow)",
6037
+ border: "var(--tt-color-highlight-yellow-contrast)"
6038
+ },
6039
+ {
6040
+ label: "Green background",
6041
+ value: "var(--tt-color-highlight-green)",
6042
+ border: "var(--tt-color-highlight-green-contrast)"
6043
+ },
6044
+ {
6045
+ label: "Blue background",
6046
+ value: "var(--tt-color-highlight-blue)",
6047
+ border: "var(--tt-color-highlight-blue-contrast)"
6048
+ },
6049
+ {
6050
+ label: "Purple background",
6051
+ value: "var(--tt-color-highlight-purple)",
6052
+ border: "var(--tt-color-highlight-purple-contrast)"
6053
+ },
6054
+ {
6055
+ label: "Pink background",
6056
+ value: "var(--tt-color-highlight-pink)",
6057
+ border: "var(--tt-color-highlight-pink-contrast)"
6058
+ },
6059
+ {
6060
+ label: "Red background",
6061
+ value: "var(--tt-color-highlight-red)",
6062
+ border: "var(--tt-color-highlight-red-contrast)"
5757
6063
  }
6064
+ ];
6065
+ function pickHighlightColorsByValue(values) {
6066
+ const colorMap = new Map(
6067
+ HIGHLIGHT_COLORS.map((color) => [color.value, color])
6068
+ );
6069
+ return values.map((value) => colorMap.get(value)).filter((color) => !!color);
5758
6070
  }
5759
- function toggleBlockquote(editor) {
5760
- var _a, _b, _c;
6071
+ function canColorHighlight(editor) {
5761
6072
  if (!editor || !editor.isEditable) return false;
5762
- if (!canToggleBlockquote(editor)) return false;
5763
- try {
5764
- const view = editor.view;
5765
- let state = view.state;
5766
- let tr = state.tr;
5767
- if (state.selection.empty || state.selection instanceof TextSelection2) {
5768
- const pos = (_a = findNodePosition({
5769
- editor,
5770
- node: state.selection.$anchor.node(1)
5771
- })) == null ? void 0 : _a.pos;
5772
- if (!isValidPosition(pos)) return false;
5773
- tr = tr.setSelection(NodeSelection2.create(state.doc, pos));
5774
- view.dispatch(tr);
5775
- state = view.state;
5776
- }
5777
- const selection = state.selection;
5778
- let chain = editor.chain().focus();
5779
- if (selection instanceof NodeSelection2) {
5780
- const firstChild = (_b = selection.node.firstChild) == null ? void 0 : _b.firstChild;
5781
- const lastChild = (_c = selection.node.lastChild) == null ? void 0 : _c.lastChild;
5782
- const from = firstChild ? selection.from + firstChild.nodeSize : selection.from + 1;
5783
- const to = lastChild ? selection.to - lastChild.nodeSize : selection.to - 1;
5784
- chain = chain.setTextSelection({ from, to }).clearNodes();
5785
- }
5786
- const toggle = editor.isActive("blockquote") ? chain.lift("blockquote") : chain.wrapIn("blockquote");
5787
- toggle.run();
5788
- editor.chain().focus().selectTextblockEnd().run();
5789
- return true;
5790
- } catch {
6073
+ if (!isMarkInSchema3("highlight", editor) || isNodeTypeSelected3(editor, ["image"]))
5791
6074
  return false;
5792
- }
6075
+ return editor.can().setMark("highlight");
5793
6076
  }
5794
- function shouldShowButton2(props) {
5795
- const { editor, hideWhenUnavailable } = props;
6077
+ function isColorHighlightActive(editor, highlightColor) {
5796
6078
  if (!editor || !editor.isEditable) return false;
5797
- if (!isNodeInSchema3("blockquote", editor)) return false;
5798
- if (hideWhenUnavailable && !editor.isActive("code")) {
5799
- return canToggleBlockquote(editor);
5800
- }
6079
+ return highlightColor ? editor.isActive("highlight", { color: highlightColor }) : editor.isActive("highlight");
6080
+ }
6081
+ function removeHighlight(editor) {
6082
+ if (!editor || !editor.isEditable) return false;
6083
+ if (!canColorHighlight(editor)) return false;
6084
+ return editor.chain().focus().unsetMark("highlight").run();
6085
+ }
6086
+ function shouldShowButton2(props) {
6087
+ const { editor, hideWhenUnavailable } = props;
6088
+ if (!editor || !editor.isEditable) return false;
6089
+ if (!isMarkInSchema3("highlight", editor)) return false;
6090
+ if (hideWhenUnavailable && !editor.isActive("code")) {
6091
+ return canColorHighlight(editor);
6092
+ }
5801
6093
  return true;
5802
6094
  }
5803
- function useBlockquote(config) {
6095
+ function useColorHighlight(config) {
5804
6096
  const {
5805
6097
  editor: providedEditor,
6098
+ label,
6099
+ highlightColor,
5806
6100
  hideWhenUnavailable = false,
5807
- onToggled
5808
- } = config || {};
5809
- const { editor } = useTiptapEditor(providedEditor);
6101
+ onApplied
6102
+ } = config;
6103
+ const { editor } = useTiptapEditor7(providedEditor);
6104
+ const isMobile = useIsMobile();
5810
6105
  const [isVisible, setIsVisible] = React14.useState(true);
5811
- const canToggle3 = canToggleBlockquote(editor);
5812
- const isActive = (editor == null ? void 0 : editor.isActive("blockquote")) || false;
6106
+ const canColorHighlightState = canColorHighlight(editor);
6107
+ const isActive = isColorHighlightActive(editor, highlightColor);
5813
6108
  React14.useEffect(() => {
5814
6109
  if (!editor) return;
5815
6110
  const handleSelectionUpdate = () => {
@@ -5821,1062 +6116,1320 @@ function useBlockquote(config) {
5821
6116
  editor.off("selectionUpdate", handleSelectionUpdate);
5822
6117
  };
5823
6118
  }, [editor, hideWhenUnavailable]);
5824
- const handleToggle = React14.useCallback(() => {
5825
- if (!editor) return false;
5826
- const success = toggleBlockquote(editor);
6119
+ const handleColorHighlight = React14.useCallback(() => {
6120
+ if (!editor || !canColorHighlightState || !highlightColor || !label)
6121
+ return false;
6122
+ if (editor.state.storedMarks) {
6123
+ const highlightMarkType = editor.schema.marks.highlight;
6124
+ if (highlightMarkType) {
6125
+ editor.view.dispatch(
6126
+ editor.state.tr.removeStoredMark(highlightMarkType)
6127
+ );
6128
+ }
6129
+ }
6130
+ setTimeout(() => {
6131
+ const success = editor.chain().focus().toggleMark("highlight", { color: highlightColor }).run();
6132
+ if (success) {
6133
+ onApplied == null ? void 0 : onApplied({ color: highlightColor, label });
6134
+ }
6135
+ return success;
6136
+ }, 0);
6137
+ }, [canColorHighlightState, highlightColor, editor, label, onApplied]);
6138
+ const handleRemoveHighlight = React14.useCallback(() => {
6139
+ const success = removeHighlight(editor);
5827
6140
  if (success) {
5828
- onToggled == null ? void 0 : onToggled();
6141
+ onApplied == null ? void 0 : onApplied({ color: "", label: "Remove highlight" });
5829
6142
  }
5830
6143
  return success;
5831
- }, [editor, onToggled]);
6144
+ }, [editor, onApplied]);
6145
+ useHotkeys(
6146
+ COLOR_HIGHLIGHT_SHORTCUT_KEY,
6147
+ (event) => {
6148
+ event.preventDefault();
6149
+ handleColorHighlight();
6150
+ },
6151
+ {
6152
+ enabled: isVisible && canColorHighlightState,
6153
+ enableOnContentEditable: !isMobile,
6154
+ enableOnFormTags: true
6155
+ }
6156
+ );
5832
6157
  return {
5833
6158
  isVisible,
5834
6159
  isActive,
5835
- handleToggle,
5836
- canToggle: canToggle3,
5837
- label: "Blockquote",
5838
- shortcutKeys: BLOCKQUOTE_SHORTCUT_KEY,
5839
- Icon: BlockquoteIcon2
6160
+ handleColorHighlight,
6161
+ handleRemoveHighlight,
6162
+ canColorHighlight: canColorHighlightState,
6163
+ label: label || `Highlight`,
6164
+ shortcutKeys: COLOR_HIGHLIGHT_SHORTCUT_KEY,
6165
+ Icon: HighlighterIcon
5840
6166
  };
5841
6167
  }
5842
6168
 
5843
- // src/ui/blockquote-button/blockquote-button.tsx
6169
+ // src/ui/color-highlight-button/color-highlight-button.tsx
5844
6170
  import { Fragment as Fragment4, jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
5845
- var BlockquoteButton = ({
6171
+ var ColorHighlightButton = ({
5846
6172
  editor: providedEditor,
6173
+ highlightColor,
5847
6174
  text,
5848
6175
  hideWhenUnavailable = false,
5849
- onToggled,
6176
+ onApplied,
5850
6177
  showShortcut = false,
5851
6178
  onClick,
5852
6179
  children,
6180
+ style,
6181
+ className,
5853
6182
  ...buttonProps
5854
6183
  }) => {
5855
- const { editor } = useTiptapEditor(providedEditor);
6184
+ const { editor } = useTiptapEditor8(providedEditor);
5856
6185
  const {
5857
6186
  isVisible,
5858
- canToggle: canToggle3,
6187
+ canColorHighlight: canColorHighlight2,
5859
6188
  isActive,
5860
- handleToggle,
6189
+ handleColorHighlight,
5861
6190
  label,
5862
- shortcutKeys,
5863
- Icon
5864
- } = useBlockquote({
6191
+ shortcutKeys
6192
+ } = useColorHighlight({
5865
6193
  editor,
6194
+ highlightColor,
6195
+ label: text || `Toggle highlight (${highlightColor})`,
5866
6196
  hideWhenUnavailable,
5867
- onToggled
6197
+ onApplied
5868
6198
  });
5869
- const handleClick = React15.useCallback(
6199
+ const handleClick = useCallback22(
5870
6200
  (event) => {
5871
6201
  onClick == null ? void 0 : onClick(event);
5872
6202
  if (event.defaultPrevented) return;
5873
- handleToggle();
6203
+ handleColorHighlight();
5874
6204
  },
5875
- [handleToggle, onClick]
6205
+ [handleColorHighlight, onClick]
6206
+ );
6207
+ const buttonStyle = useMemo15(
6208
+ () => ({
6209
+ ...style,
6210
+ "--highlight-color": highlightColor
6211
+ }),
6212
+ [highlightColor, style]
5876
6213
  );
5877
6214
  if (!isVisible) {
5878
6215
  return null;
5879
6216
  }
5880
- return /* @__PURE__ */ jsx21(
6217
+ const styles = colorHighlightButton();
6218
+ return /* @__PURE__ */ jsxs16(
5881
6219
  ToolbarButton3,
5882
6220
  {
5883
6221
  type: "button",
6222
+ disabled: !canColorHighlight2,
6223
+ "data-disabled": !canColorHighlight2,
5884
6224
  variant: "ghost",
5885
6225
  color: "default",
5886
- disabled: !canToggle3,
5887
- "data-disabled": !canToggle3,
5888
6226
  "data-active-state": isActive ? "on" : "off",
5889
6227
  tabIndex: -1,
5890
- "aria-label": "blockquote",
5891
- "aria-pressed": isActive,
5892
- title: label,
6228
+ "aria-label": label,
5893
6229
  shortcutKeys,
6230
+ "aria-pressed": isActive,
5894
6231
  onClick: handleClick,
5895
- isIconOnly: !text && !children,
6232
+ style: buttonStyle,
6233
+ className: styles.button({ className }),
6234
+ isIconOnly: true,
5896
6235
  ...buttonProps,
5897
- children: children != null ? children : /* @__PURE__ */ jsxs16(Fragment4, { children: [
5898
- /* @__PURE__ */ jsx21(Icon, {}),
5899
- text && /* @__PURE__ */ jsx21("span", { children: text })
5900
- ] })
6236
+ children: [
6237
+ /* @__PURE__ */ jsx21(
6238
+ "span",
6239
+ {
6240
+ "data-active-state": isActive ? "on" : "off",
6241
+ className: styles.mark()
6242
+ }
6243
+ ),
6244
+ children || /* @__PURE__ */ jsxs16(Fragment4, { children: [
6245
+ /* @__PURE__ */ jsx21(
6246
+ "span",
6247
+ {
6248
+ style: { "--highlight-color": highlightColor }
6249
+ }
6250
+ ),
6251
+ text
6252
+ ] })
6253
+ ]
5901
6254
  }
5902
6255
  );
5903
6256
  };
5904
6257
 
5905
- // src/ui/codeblock-button/code-block-button.tsx
5906
- import { CodeblockIcon as CodeblockIcon2 } from "@kopexa/icons";
5907
- import { ToolbarButton as ToolbarButton4 } from "@kopexa/toolbar";
5908
- import { useCallback as useCallback24 } from "react";
5909
-
5910
- // src/ui/codeblock-button/use-code-block.ts
5911
- import {
5912
- findNodePosition as findNodePosition2,
5913
- isNodeInSchema as isNodeInSchema4,
5914
- isNodeTypeSelected as isNodeTypeSelected4,
5915
- isValidPosition as isValidPosition2
5916
- } from "@kopexa/editor-utils";
5917
- import { CodeblockIcon } from "@kopexa/icons";
5918
- import { NodeSelection as NodeSelection3, TextSelection as TextSelection3 } from "@tiptap/pm/state";
5919
- import * as React16 from "react";
5920
- var CODE_BLOCK_SHORTCUT_KEY = "mod+alt+c";
5921
- function canToggle2(editor, turnInto = true) {
5922
- var _a;
5923
- if (!editor || !editor.isEditable) return false;
5924
- if (!isNodeInSchema4("codeBlock", editor) || isNodeTypeSelected4(editor, ["image"]))
5925
- return false;
5926
- if (!turnInto) {
5927
- return editor.can().toggleNode("codeBlock", "paragraph");
5928
- }
5929
- try {
5930
- const view = editor.view;
5931
- const state = view.state;
5932
- const selection = state.selection;
5933
- if (selection.empty || selection instanceof TextSelection3) {
5934
- const pos = (_a = findNodePosition2({
5935
- editor,
5936
- node: state.selection.$anchor.node(1)
5937
- })) == null ? void 0 : _a.pos;
5938
- if (!isValidPosition2(pos)) return false;
5939
- }
5940
- return true;
5941
- } catch {
5942
- return false;
6258
+ // src/ui/color-highlight-popover/color-highlight-popover.tsx
6259
+ import { jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
6260
+ var ColorHighlightPopoverButton = ({
6261
+ className,
6262
+ children,
6263
+ ...props
6264
+ }) => /* @__PURE__ */ jsx22(
6265
+ IconButton8,
6266
+ {
6267
+ type: "button",
6268
+ className,
6269
+ variant: "ghost",
6270
+ color: "default",
6271
+ tabIndex: -1,
6272
+ "aria-label": "Highlight text",
6273
+ tooltip: "Highlight",
6274
+ isIconOnly: !children,
6275
+ ...props,
6276
+ children: children != null ? children : /* @__PURE__ */ jsx22(HighlighterIcon2, {})
5943
6277
  }
6278
+ );
6279
+ function ColorHighlightPopoverContent({
6280
+ editor,
6281
+ colors = pickHighlightColorsByValue([
6282
+ "var(--tt-color-highlight-green)",
6283
+ "var(--tt-color-highlight-blue)",
6284
+ "var(--tt-color-highlight-red)",
6285
+ "var(--tt-color-highlight-purple)",
6286
+ "var(--tt-color-highlight-yellow)"
6287
+ ])
6288
+ }) {
6289
+ const { handleRemoveHighlight } = useColorHighlight({ editor });
6290
+ const containerRef = useRef10(null);
6291
+ const menuItems = useMemo16(
6292
+ () => [...colors, { label: "Remove highlight", value: "none" }],
6293
+ [colors]
6294
+ );
6295
+ const { selectedIndex } = useMenuNavigation({
6296
+ containerRef,
6297
+ items: menuItems,
6298
+ orientation: "both",
6299
+ onSelect: (item) => {
6300
+ if (!containerRef.current) return false;
6301
+ const highlightedElement = containerRef.current.querySelector(
6302
+ '[data-highlighted="true"]'
6303
+ );
6304
+ if (highlightedElement) highlightedElement.click();
6305
+ if (item.value === "none") handleRemoveHighlight();
6306
+ },
6307
+ autoSelectFirstItem: false
6308
+ });
6309
+ return /* @__PURE__ */ jsxs17("div", { ref: containerRef, className: "flex gap-1 items-center", children: [
6310
+ /* @__PURE__ */ jsx22(
6311
+ "div",
6312
+ {
6313
+ className: "flex items-center gap-1 outline-none",
6314
+ "data-orientation": "horizontal",
6315
+ children: colors.map((color, index) => /* @__PURE__ */ jsx22(
6316
+ ColorHighlightButton,
6317
+ {
6318
+ editor,
6319
+ highlightColor: color.value,
6320
+ "aria-label": `${color.label} highlight color`,
6321
+ tabIndex: index === selectedIndex ? 0 : -1,
6322
+ "data-highlighted": selectedIndex === index
6323
+ },
6324
+ color.value
6325
+ ))
6326
+ }
6327
+ ),
6328
+ /* @__PURE__ */ jsx22(ToolbarSeparator2, { orientation: "vertical" }),
6329
+ /* @__PURE__ */ jsx22("div", { className: "tiptap-button-group", children: /* @__PURE__ */ jsx22(
6330
+ IconButton8,
6331
+ {
6332
+ onClick: handleRemoveHighlight,
6333
+ "aria-label": "Remove highlight",
6334
+ tabIndex: selectedIndex === colors.length ? 0 : -1,
6335
+ type: "button",
6336
+ role: "menuitem",
6337
+ variant: "ghost",
6338
+ color: "default",
6339
+ "data-highlighted": selectedIndex === colors.length,
6340
+ children: /* @__PURE__ */ jsx22(BanIcon, {})
6341
+ }
6342
+ ) })
6343
+ ] });
5944
6344
  }
5945
- function toggleCodeBlock(editor) {
5946
- var _a, _b, _c;
5947
- if (!editor || !editor.isEditable) return false;
5948
- if (!canToggle2(editor)) return false;
5949
- try {
5950
- const view = editor.view;
5951
- let state = view.state;
5952
- let tr = state.tr;
5953
- if (state.selection.empty || state.selection instanceof TextSelection3) {
5954
- const pos = (_a = findNodePosition2({
5955
- editor,
5956
- node: state.selection.$anchor.node(1)
5957
- })) == null ? void 0 : _a.pos;
5958
- if (!isValidPosition2(pos)) return false;
5959
- tr = tr.setSelection(NodeSelection3.create(state.doc, pos));
5960
- view.dispatch(tr);
5961
- state = view.state;
5962
- }
5963
- const selection = state.selection;
5964
- let chain = editor.chain().focus();
5965
- if (selection instanceof NodeSelection3) {
5966
- const firstChild = (_b = selection.node.firstChild) == null ? void 0 : _b.firstChild;
5967
- const lastChild = (_c = selection.node.lastChild) == null ? void 0 : _c.lastChild;
5968
- const from = firstChild ? selection.from + firstChild.nodeSize : selection.from + 1;
5969
- const to = lastChild ? selection.to - lastChild.nodeSize : selection.to - 1;
5970
- chain = chain.setTextSelection({ from, to }).clearNodes();
5971
- }
5972
- const toggle = editor.isActive("codeBlock") ? chain.setNode("paragraph") : chain.toggleNode("codeBlock", "paragraph");
5973
- toggle.run();
5974
- editor.chain().focus().selectTextblockEnd().run();
5975
- return true;
5976
- } catch {
6345
+ function ColorHighlightPopover({
6346
+ editor: providedEditor,
6347
+ colors = pickHighlightColorsByValue([
6348
+ "var(--tt-color-highlight-green)",
6349
+ "var(--tt-color-highlight-blue)",
6350
+ "var(--tt-color-highlight-red)",
6351
+ "var(--tt-color-highlight-purple)",
6352
+ "var(--tt-color-highlight-yellow)"
6353
+ ]),
6354
+ hideWhenUnavailable = false,
6355
+ onApplied,
6356
+ ...props
6357
+ }) {
6358
+ const { editor } = useTiptapEditor9(providedEditor);
6359
+ const [isOpen, setIsOpen] = useState22(false);
6360
+ const { isVisible, canColorHighlight: canColorHighlight2, isActive, label } = useColorHighlight({
6361
+ editor,
6362
+ hideWhenUnavailable,
6363
+ onApplied
6364
+ });
6365
+ if (!isVisible) return null;
6366
+ return /* @__PURE__ */ jsxs17(Popover2.Root, { open: isOpen, onOpenChange: setIsOpen, spacing: "dense", children: [
6367
+ /* @__PURE__ */ jsx22(
6368
+ Popover2.Trigger,
6369
+ {
6370
+ render: /* @__PURE__ */ jsx22(
6371
+ ColorHighlightPopoverButton,
6372
+ {
6373
+ disabled: !canColorHighlight2,
6374
+ "data-disabled": !canColorHighlight2,
6375
+ "data-active-state": isActive ? "on" : "off",
6376
+ "aria-pressed": isActive,
6377
+ "aria-label": label,
6378
+ title: label,
6379
+ ...props
6380
+ }
6381
+ )
6382
+ }
6383
+ ),
6384
+ /* @__PURE__ */ jsx22(Popover2.Content, { "aria-label": "Highlight colors", children: /* @__PURE__ */ jsx22(ColorHighlightPopoverContent, { editor, colors }) })
6385
+ ] });
6386
+ }
6387
+
6388
+ // src/ui/list-dropdown-menu/index.tsx
6389
+ import { Button as Button8 } from "@kopexa/button";
6390
+ import { DropdownMenu } from "@kopexa/dropdown-menu";
6391
+ import { isNodeInSchema as isNodeInSchema4, useTiptapEditor as useTiptapEditor11 } from "@kopexa/editor-utils";
6392
+ import { ChevronDownIcon, ListIcon as ListIcon4 } from "@kopexa/icons";
6393
+ import { isNodeSelection as isNodeSelection6 } from "@tiptap/react";
6394
+ import { useCallback as useCallback24, useMemo as useMemo18, useState as useState23 } from "react";
6395
+
6396
+ // src/ui/list-button/index.tsx
6397
+ import { Button as Button7 } from "@kopexa/button";
6398
+ import { isNodeInSchema as isNodeInSchema3, useTiptapEditor as useTiptapEditor10 } from "@kopexa/editor-utils";
6399
+ import { ListIcon as ListIcon3, ListOrderedIcon as ListOrderedIcon2, ListTodoIcon as ListTodoIcon2 } from "@kopexa/icons";
6400
+ import { isNodeSelection as isNodeSelection5 } from "@tiptap/react";
6401
+ import { useCallback as useCallback23, useMemo as useMemo17 } from "react";
6402
+ import { jsx as jsx23 } from "react/jsx-runtime";
6403
+ var listOptions = [
6404
+ {
6405
+ label: "Bullet List",
6406
+ type: "bulletList",
6407
+ icon: ListIcon3
6408
+ },
6409
+ {
6410
+ label: "Ordered List",
6411
+ type: "orderedList",
6412
+ icon: ListOrderedIcon2
6413
+ },
6414
+ {
6415
+ label: "Task List",
6416
+ type: "taskList",
6417
+ icon: ListTodoIcon2
6418
+ }
6419
+ ];
6420
+ var listShortcutKeys = {
6421
+ bulletList: "Ctrl-Shift-8",
6422
+ orderedList: "Ctrl-Shift-7",
6423
+ taskList: "Ctrl-Shift-9"
6424
+ };
6425
+ function canToggleList(editor, type) {
6426
+ if (!editor) {
5977
6427
  return false;
5978
6428
  }
6429
+ switch (type) {
6430
+ case "bulletList":
6431
+ return editor.can().toggleBulletList();
6432
+ case "orderedList":
6433
+ return editor.can().toggleOrderedList();
6434
+ case "taskList":
6435
+ return editor.can().toggleList("taskList", "taskItem");
6436
+ default:
6437
+ return false;
6438
+ }
5979
6439
  }
5980
- function shouldShowButton3(props) {
5981
- const { editor, hideWhenUnavailable } = props;
5982
- if (!editor || !editor.isEditable) return false;
5983
- if (!isNodeInSchema4("codeBlock", editor)) return false;
5984
- if (hideWhenUnavailable && !editor.isActive("code")) {
5985
- return canToggle2(editor);
6440
+ function isListActive(editor, type) {
6441
+ if (!editor) return false;
6442
+ switch (type) {
6443
+ case "bulletList":
6444
+ return editor.isActive("bulletList");
6445
+ case "orderedList":
6446
+ return editor.isActive("orderedList");
6447
+ case "taskList":
6448
+ return editor.isActive("taskList");
6449
+ default:
6450
+ return false;
5986
6451
  }
5987
- return true;
5988
6452
  }
5989
- function useCodeBlock(config) {
5990
- const {
5991
- editor: providedEditor,
5992
- hideWhenUnavailable = false,
5993
- onToggled
5994
- } = config || {};
5995
- const { editor } = useTiptapEditor(providedEditor);
5996
- const [isVisible, setIsVisible] = React16.useState(true);
5997
- const canToggleState = canToggle2(editor);
5998
- const isActive = (editor == null ? void 0 : editor.isActive("codeBlock")) || false;
5999
- React16.useEffect(() => {
6000
- if (!editor) return;
6001
- const handleSelectionUpdate = () => {
6002
- setIsVisible(shouldShowButton3({ editor, hideWhenUnavailable }));
6003
- };
6004
- handleSelectionUpdate();
6005
- editor.on("selectionUpdate", handleSelectionUpdate);
6006
- return () => {
6007
- editor.off("selectionUpdate", handleSelectionUpdate);
6008
- };
6009
- }, [editor, hideWhenUnavailable]);
6010
- const handleToggle = React16.useCallback(() => {
6011
- if (!editor) return false;
6012
- const success = toggleCodeBlock(editor);
6013
- if (success) {
6014
- onToggled == null ? void 0 : onToggled();
6453
+ function toggleList(editor, type) {
6454
+ if (!editor) return;
6455
+ switch (type) {
6456
+ case "bulletList":
6457
+ editor.chain().focus().toggleBulletList().run();
6458
+ break;
6459
+ case "orderedList":
6460
+ editor.chain().focus().toggleOrderedList().run();
6461
+ break;
6462
+ case "taskList":
6463
+ editor.chain().focus().toggleList("taskList", "taskItem").run();
6464
+ break;
6465
+ }
6466
+ }
6467
+ function getListOption(type) {
6468
+ return listOptions.find((option) => option.type === type);
6469
+ }
6470
+ function shouldShowListButton(params) {
6471
+ const { editor, type, hideWhenUnavailable, listInSchema } = params;
6472
+ if (!listInSchema || !editor) {
6473
+ return false;
6474
+ }
6475
+ if (hideWhenUnavailable) {
6476
+ if (isNodeSelection5(editor.state.selection) || !canToggleList(editor, type)) {
6477
+ return false;
6015
6478
  }
6016
- return success;
6017
- }, [editor, onToggled]);
6479
+ }
6480
+ return true;
6481
+ }
6482
+ function useListState(editor, type) {
6483
+ const listInSchema = isNodeInSchema3(type, editor);
6484
+ const listOption = getListOption(type);
6485
+ const isActive = isListActive(editor, type);
6486
+ const shortcutKey = listShortcutKeys[type];
6018
6487
  return {
6019
- isVisible,
6488
+ listInSchema,
6489
+ listOption,
6020
6490
  isActive,
6021
- handleToggle,
6022
- canToggle: canToggleState,
6023
- label: "Code Block",
6024
- shortcutKeys: CODE_BLOCK_SHORTCUT_KEY,
6025
- Icon: CodeblockIcon
6491
+ shortcutKey
6026
6492
  };
6027
6493
  }
6028
-
6029
- // src/ui/codeblock-button/code-block-button.tsx
6030
- import { jsx as jsx22 } from "react/jsx-runtime";
6031
- var CodeBlockButton = ({
6494
+ var ListButton = ({
6032
6495
  editor: providedEditor,
6033
- text,
6496
+ type,
6034
6497
  hideWhenUnavailable = false,
6035
- onToggled,
6036
- showShortcut = false,
6498
+ className = "",
6037
6499
  onClick,
6500
+ text,
6038
6501
  children,
6502
+ ref,
6039
6503
  ...buttonProps
6040
6504
  }) => {
6041
- const { editor } = useTiptapEditor(providedEditor);
6042
- const { isVisible, canToggle: canToggle3, isActive, handleToggle, label, shortcutKeys } = useCodeBlock({
6505
+ const { editor } = useTiptapEditor10(providedEditor);
6506
+ const { listInSchema, listOption, isActive, shortcutKey } = useListState(
6043
6507
  editor,
6044
- hideWhenUnavailable,
6045
- onToggled
6046
- });
6047
- const handleClick = useCallback24(
6048
- (event) => {
6049
- onClick == null ? void 0 : onClick(event);
6050
- if (event.defaultPrevented) return;
6051
- handleToggle();
6508
+ type
6509
+ );
6510
+ const Icon = (listOption == null ? void 0 : listOption.icon) || ListIcon3;
6511
+ const handleClick = useCallback23(
6512
+ (e) => {
6513
+ onClick == null ? void 0 : onClick(e);
6514
+ if (!e.defaultPrevented && editor) {
6515
+ toggleList(editor, type);
6516
+ }
6052
6517
  },
6053
- [handleToggle, onClick]
6518
+ [onClick, editor, type]
6054
6519
  );
6055
- if (!isVisible) {
6520
+ const show = useMemo17(() => {
6521
+ return shouldShowListButton({
6522
+ editor,
6523
+ type,
6524
+ hideWhenUnavailable,
6525
+ listInSchema
6526
+ });
6527
+ }, [editor, type, hideWhenUnavailable, listInSchema]);
6528
+ if (!show || !editor || !editor.isEditable) {
6056
6529
  return null;
6057
6530
  }
6058
- return /* @__PURE__ */ jsx22(
6059
- ToolbarButton4,
6531
+ return /* @__PURE__ */ jsx23(
6532
+ Button7,
6060
6533
  {
6061
6534
  type: "button",
6535
+ className: className.trim(),
6062
6536
  variant: "ghost",
6063
6537
  color: "default",
6064
- disabled: !canToggle3,
6065
- "data-disabled": !canToggle3,
6066
6538
  "data-active-state": isActive ? "on" : "off",
6067
6539
  tabIndex: -1,
6068
- "aria-label": "codeBlock",
6540
+ "aria-label": (listOption == null ? void 0 : listOption.label) || type,
6069
6541
  "aria-pressed": isActive,
6070
- title: label,
6071
- shortcutKeys,
6542
+ tooltip: (listOption == null ? void 0 : listOption.label) || type,
6543
+ shortcutKeys: shortcutKey,
6072
6544
  onClick: handleClick,
6073
- isIconOnly: true,
6545
+ startContent: /* @__PURE__ */ jsx23(Icon, {}),
6074
6546
  ...buttonProps,
6075
- children: /* @__PURE__ */ jsx22(CodeblockIcon2, {})
6547
+ ref,
6548
+ children: children || text
6076
6549
  }
6077
6550
  );
6078
6551
  };
6079
6552
 
6080
- // src/ui/color-highlight-popover/color-highlight-popover.tsx
6081
- import { IconButton as IconButton8 } from "@kopexa/button";
6082
- import { BanIcon, HighlighterIcon as HighlighterIcon2 } from "@kopexa/icons";
6083
- import { Popover as Popover2 } from "@kopexa/popover";
6084
- import { ToolbarSeparator as ToolbarSeparator2 } from "@kopexa/toolbar";
6085
- import { useMemo as useMemo17, useRef as useRef10, useState as useState24 } from "react";
6086
-
6087
- // src/ui/color-highlight-button/color-highlight-button.tsx
6088
- import { colorHighlightButton } from "@kopexa/theme";
6089
- import { ToolbarButton as ToolbarButton5 } from "@kopexa/toolbar";
6090
- import { useCallback as useCallback26, useMemo as useMemo16 } from "react";
6091
-
6092
- // src/ui/color-highlight-button/use-color-highlight.ts
6093
- import { isMarkInSchema as isMarkInSchema3, isNodeTypeSelected as isNodeTypeSelected5 } from "@kopexa/editor-utils";
6094
- import { HighlighterIcon } from "@kopexa/icons";
6095
- import { useIsMobile } from "@kopexa/use-is-mobile";
6096
- import * as React17 from "react";
6097
- import { useHotkeys } from "react-hotkeys-hook";
6098
- var COLOR_HIGHLIGHT_SHORTCUT_KEY = "mod+shift+h";
6099
- var HIGHLIGHT_COLORS = [
6100
- {
6101
- label: "Default background",
6102
- value: "var(--tt-bg-color)",
6103
- border: "var(--tt-bg-color-contrast)"
6104
- },
6105
- {
6106
- label: "Gray background",
6107
- value: "var(--tt-color-highlight-gray)",
6108
- border: "var(--tt-color-highlight-gray-contrast)"
6109
- },
6110
- {
6111
- label: "Brown background",
6112
- value: "var(--tt-color-highlight-brown)",
6113
- border: "var(--tt-color-highlight-brown-contrast)"
6114
- },
6115
- {
6116
- label: "Orange background",
6117
- value: "var(--tt-color-highlight-orange)",
6118
- border: "var(--tt-color-highlight-orange-contrast)"
6119
- },
6120
- {
6121
- label: "Yellow background",
6122
- value: "var(--tt-color-highlight-yellow)",
6123
- border: "var(--tt-color-highlight-yellow-contrast)"
6124
- },
6125
- {
6126
- label: "Green background",
6127
- value: "var(--tt-color-highlight-green)",
6128
- border: "var(--tt-color-highlight-green-contrast)"
6129
- },
6130
- {
6131
- label: "Blue background",
6132
- value: "var(--tt-color-highlight-blue)",
6133
- border: "var(--tt-color-highlight-blue-contrast)"
6134
- },
6135
- {
6136
- label: "Purple background",
6137
- value: "var(--tt-color-highlight-purple)",
6138
- border: "var(--tt-color-highlight-purple-contrast)"
6139
- },
6140
- {
6141
- label: "Pink background",
6142
- value: "var(--tt-color-highlight-pink)",
6143
- border: "var(--tt-color-highlight-pink-contrast)"
6144
- },
6145
- {
6146
- label: "Red background",
6147
- value: "var(--tt-color-highlight-red)",
6148
- border: "var(--tt-color-highlight-red-contrast)"
6149
- }
6150
- ];
6151
- function pickHighlightColorsByValue(values) {
6152
- const colorMap = new Map(
6153
- HIGHLIGHT_COLORS.map((color) => [color.value, color])
6154
- );
6155
- return values.map((value) => colorMap.get(value)).filter((color) => !!color);
6156
- }
6157
- function canColorHighlight(editor) {
6158
- if (!editor || !editor.isEditable) return false;
6159
- if (!isMarkInSchema3("highlight", editor) || isNodeTypeSelected5(editor, ["image"]))
6160
- return false;
6161
- return editor.can().setMark("highlight");
6553
+ // src/ui/list-dropdown-menu/index.tsx
6554
+ import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
6555
+ function canToggleAnyList(editor, listTypes) {
6556
+ if (!editor) return false;
6557
+ return listTypes.some((type) => canToggleList(editor, type));
6162
6558
  }
6163
- function isColorHighlightActive(editor, highlightColor) {
6164
- if (!editor || !editor.isEditable) return false;
6165
- return highlightColor ? editor.isActive("highlight", { color: highlightColor }) : editor.isActive("highlight");
6559
+ function isAnyListActive(editor, listTypes) {
6560
+ if (!editor) return false;
6561
+ return listTypes.some((type) => isListActive(editor, type));
6166
6562
  }
6167
- function removeHighlight(editor) {
6168
- if (!editor || !editor.isEditable) return false;
6169
- if (!canColorHighlight(editor)) return false;
6170
- return editor.chain().focus().unsetMark("highlight").run();
6563
+ function getFilteredListOptions(availableTypes) {
6564
+ return listOptions.filter(
6565
+ (option) => !option.type || availableTypes.includes(option.type)
6566
+ );
6171
6567
  }
6172
- function shouldShowButton4(props) {
6173
- const { editor, hideWhenUnavailable } = props;
6174
- if (!editor || !editor.isEditable) return false;
6175
- if (!isMarkInSchema3("highlight", editor)) return false;
6176
- if (hideWhenUnavailable && !editor.isActive("code")) {
6177
- return canColorHighlight(editor);
6568
+ function shouldShowListDropdown(params) {
6569
+ const { editor, hideWhenUnavailable, listInSchema, canToggleAny } = params;
6570
+ if (!listInSchema || !editor) {
6571
+ return false;
6178
6572
  }
6179
- return true;
6180
- }
6181
- function useColorHighlight(config) {
6182
- const {
6183
- editor: providedEditor,
6184
- label,
6185
- highlightColor,
6186
- hideWhenUnavailable = false,
6187
- onApplied
6188
- } = config;
6189
- const { editor } = useTiptapEditor(providedEditor);
6190
- const isMobile = useIsMobile();
6191
- const [isVisible, setIsVisible] = React17.useState(true);
6192
- const canColorHighlightState = canColorHighlight(editor);
6193
- const isActive = isColorHighlightActive(editor, highlightColor);
6194
- React17.useEffect(() => {
6195
- if (!editor) return;
6196
- const handleSelectionUpdate = () => {
6197
- setIsVisible(shouldShowButton4({ editor, hideWhenUnavailable }));
6198
- };
6199
- handleSelectionUpdate();
6200
- editor.on("selectionUpdate", handleSelectionUpdate);
6201
- return () => {
6202
- editor.off("selectionUpdate", handleSelectionUpdate);
6203
- };
6204
- }, [editor, hideWhenUnavailable]);
6205
- const handleColorHighlight = React17.useCallback(() => {
6206
- if (!editor || !canColorHighlightState || !highlightColor || !label)
6573
+ if (hideWhenUnavailable) {
6574
+ if (isNodeSelection6(editor.state.selection) || !canToggleAny) {
6207
6575
  return false;
6208
- if (editor.state.storedMarks) {
6209
- const highlightMarkType = editor.schema.marks.highlight;
6210
- if (highlightMarkType) {
6211
- editor.view.dispatch(
6212
- editor.state.tr.removeStoredMark(highlightMarkType)
6213
- );
6214
- }
6215
- }
6216
- setTimeout(() => {
6217
- const success = editor.chain().focus().toggleMark("highlight", { color: highlightColor }).run();
6218
- if (success) {
6219
- onApplied == null ? void 0 : onApplied({ color: highlightColor, label });
6220
- }
6221
- return success;
6222
- }, 0);
6223
- }, [canColorHighlightState, highlightColor, editor, label, onApplied]);
6224
- const handleRemoveHighlight = React17.useCallback(() => {
6225
- const success = removeHighlight(editor);
6226
- if (success) {
6227
- onApplied == null ? void 0 : onApplied({ color: "", label: "Remove highlight" });
6228
6576
  }
6229
- return success;
6230
- }, [editor, onApplied]);
6231
- useHotkeys(
6232
- COLOR_HIGHLIGHT_SHORTCUT_KEY,
6233
- (event) => {
6234
- event.preventDefault();
6235
- handleColorHighlight();
6577
+ }
6578
+ return true;
6579
+ }
6580
+ function useListDropdownState(editor, availableTypes) {
6581
+ const [isOpen, setIsOpen] = useState23(false);
6582
+ const listInSchema = availableTypes.some(
6583
+ (type) => isNodeInSchema4(type, editor)
6584
+ );
6585
+ const filteredLists = useMemo18(
6586
+ () => getFilteredListOptions(availableTypes),
6587
+ [availableTypes]
6588
+ );
6589
+ const canToggleAny = canToggleAnyList(editor, availableTypes);
6590
+ const isAnyActive = isAnyListActive(editor, availableTypes);
6591
+ const handleOpenChange = useCallback24(
6592
+ (open, callback) => {
6593
+ setIsOpen(open);
6594
+ callback == null ? void 0 : callback(open);
6236
6595
  },
6237
- {
6238
- enabled: isVisible && canColorHighlightState,
6239
- enableOnContentEditable: !isMobile,
6240
- enableOnFormTags: true
6241
- }
6596
+ []
6242
6597
  );
6243
6598
  return {
6244
- isVisible,
6245
- isActive,
6246
- handleColorHighlight,
6247
- handleRemoveHighlight,
6248
- canColorHighlight: canColorHighlightState,
6249
- label: label || `Highlight`,
6250
- shortcutKeys: COLOR_HIGHLIGHT_SHORTCUT_KEY,
6251
- Icon: HighlighterIcon
6599
+ isOpen,
6600
+ setIsOpen,
6601
+ listInSchema,
6602
+ filteredLists,
6603
+ canToggleAny,
6604
+ isAnyActive,
6605
+ handleOpenChange
6252
6606
  };
6253
6607
  }
6608
+ function useActiveListIcon(editor, filteredLists) {
6609
+ return useCallback24(() => {
6610
+ const activeOption = filteredLists.find(
6611
+ (option) => isListActive(editor, option.type)
6612
+ );
6613
+ return activeOption ? /* @__PURE__ */ jsx24(activeOption.icon, {}) : /* @__PURE__ */ jsx24(ListIcon4, {});
6614
+ }, [editor, filteredLists]);
6615
+ }
6616
+ function ListDropdownMenu({
6617
+ editor: providedEditor,
6618
+ types = ["bulletList", "orderedList", "taskList"],
6619
+ hideWhenUnavailable = false,
6620
+ onOpenChange,
6621
+ ...props
6622
+ }) {
6623
+ const { editor } = useTiptapEditor11(providedEditor);
6624
+ const {
6625
+ isOpen,
6626
+ listInSchema,
6627
+ filteredLists,
6628
+ canToggleAny,
6629
+ isAnyActive,
6630
+ handleOpenChange
6631
+ } = useListDropdownState(editor, types);
6632
+ const getActiveIcon = useActiveListIcon(editor, filteredLists);
6633
+ const show = useMemo18(() => {
6634
+ return shouldShowListDropdown({
6635
+ editor,
6636
+ listTypes: types,
6637
+ hideWhenUnavailable,
6638
+ listInSchema,
6639
+ canToggleAny
6640
+ });
6641
+ }, [editor, types, hideWhenUnavailable, listInSchema, canToggleAny]);
6642
+ const handleOnOpenChange = useCallback24(
6643
+ (open) => handleOpenChange(open, onOpenChange),
6644
+ [handleOpenChange, onOpenChange]
6645
+ );
6646
+ if (!show || !editor || !editor.isEditable) {
6647
+ return null;
6648
+ }
6649
+ return /* @__PURE__ */ jsxs18(DropdownMenu.Root, { open: isOpen, onOpenChange: handleOnOpenChange, children: [
6650
+ /* @__PURE__ */ jsx24(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsx24(
6651
+ Button8,
6652
+ {
6653
+ type: "button",
6654
+ variant: "ghost",
6655
+ color: "default",
6656
+ "data-active-state": isAnyActive ? "on" : "off",
6657
+ tabIndex: -1,
6658
+ "aria-label": "List options",
6659
+ tooltip: "List",
6660
+ endContent: /* @__PURE__ */ jsx24(ChevronDownIcon, {}),
6661
+ ...props,
6662
+ children: getActiveIcon()
6663
+ }
6664
+ ) }),
6665
+ /* @__PURE__ */ jsx24(DropdownMenu.Content, { children: /* @__PURE__ */ jsx24(DropdownMenu.Group, { children: filteredLists.map((option) => /* @__PURE__ */ jsx24(DropdownMenu.Item, { asChild: true, children: /* @__PURE__ */ jsx24(
6666
+ ListButton,
6667
+ {
6668
+ editor,
6669
+ type: option.type,
6670
+ text: option.label,
6671
+ hideWhenUnavailable,
6672
+ tooltip: "",
6673
+ fullWidth: true,
6674
+ spacing: "start"
6675
+ }
6676
+ ) }, option.type)) }) })
6677
+ ] });
6678
+ }
6254
6679
 
6255
- // src/ui/color-highlight-button/color-highlight-button.tsx
6256
- import { Fragment as Fragment5, jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
6257
- var ColorHighlightButton = ({
6680
+ // src/ui/table-button/index.tsx
6681
+ import { useTiptapEditor as useTiptapEditor12 } from "@kopexa/editor-utils";
6682
+ import { ToolbarButton as ToolbarButton4 } from "@kopexa/toolbar";
6683
+ import { useCallback as useCallback25 } from "react";
6684
+ import { Fragment as Fragment5, jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
6685
+ var TableButton = ({
6258
6686
  editor: providedEditor,
6259
- highlightColor,
6260
6687
  text,
6261
6688
  hideWhenUnavailable = false,
6262
- onApplied,
6263
- showShortcut = false,
6689
+ onToggled,
6264
6690
  onClick,
6265
6691
  children,
6266
- style,
6267
- className,
6268
6692
  ...buttonProps
6269
6693
  }) => {
6270
- const { editor } = useTiptapEditor(providedEditor);
6694
+ const { editor } = useTiptapEditor12(providedEditor);
6271
6695
  const {
6272
6696
  isVisible,
6273
- canColorHighlight: canColorHighlight2,
6697
+ canToggle: canToggle3,
6274
6698
  isActive,
6275
- handleColorHighlight,
6699
+ handleToggle,
6276
6700
  label,
6277
- shortcutKeys
6278
- } = useColorHighlight({
6701
+ // shortcutKeys,
6702
+ Icon
6703
+ } = useTableBlock({
6279
6704
  editor,
6280
- highlightColor,
6281
- label: text || `Toggle highlight (${highlightColor})`,
6282
6705
  hideWhenUnavailable,
6283
- onApplied
6706
+ onToggled
6284
6707
  });
6285
- const handleClick = useCallback26(
6708
+ const handleClick = useCallback25(
6286
6709
  (event) => {
6287
6710
  onClick == null ? void 0 : onClick(event);
6288
6711
  if (event.defaultPrevented) return;
6289
- handleColorHighlight();
6712
+ handleToggle();
6290
6713
  },
6291
- [handleColorHighlight, onClick]
6292
- );
6293
- const buttonStyle = useMemo16(
6294
- () => ({
6295
- ...style,
6296
- "--highlight-color": highlightColor
6297
- }),
6298
- [highlightColor, style]
6714
+ [handleToggle, onClick]
6299
6715
  );
6300
6716
  if (!isVisible) {
6301
6717
  return null;
6302
6718
  }
6303
- const styles = colorHighlightButton();
6304
- return /* @__PURE__ */ jsxs17(
6305
- ToolbarButton5,
6719
+ return /* @__PURE__ */ jsx25(
6720
+ ToolbarButton4,
6306
6721
  {
6307
6722
  type: "button",
6308
- disabled: !canColorHighlight2,
6309
- "data-disabled": !canColorHighlight2,
6310
6723
  variant: "ghost",
6311
6724
  color: "default",
6312
6725
  "data-active-state": isActive ? "on" : "off",
6726
+ disabled: !canToggle3,
6727
+ "data-disabled": !canToggle3,
6313
6728
  tabIndex: -1,
6314
6729
  "aria-label": label,
6315
- shortcutKeys,
6316
6730
  "aria-pressed": isActive,
6731
+ title: label,
6317
6732
  onClick: handleClick,
6318
- style: buttonStyle,
6319
- className: styles.button({ className }),
6320
- isIconOnly: true,
6733
+ isIconOnly: !text && !children,
6321
6734
  ...buttonProps,
6322
- children: [
6323
- /* @__PURE__ */ jsx23(
6324
- "span",
6325
- {
6326
- "data-active-state": isActive ? "on" : "off",
6327
- className: styles.mark()
6328
- }
6329
- ),
6330
- children || /* @__PURE__ */ jsxs17(Fragment5, { children: [
6331
- /* @__PURE__ */ jsx23(
6332
- "span",
6333
- {
6334
- style: { "--highlight-color": highlightColor }
6335
- }
6336
- ),
6337
- text
6338
- ] })
6339
- ]
6735
+ children: children || /* @__PURE__ */ jsxs19(Fragment5, { children: [
6736
+ /* @__PURE__ */ jsx25(Icon, {}),
6737
+ text && /* @__PURE__ */ jsx25("span", { children: text })
6738
+ ] })
6340
6739
  }
6341
6740
  );
6342
6741
  };
6343
6742
 
6344
- // src/ui/color-highlight-popover/color-highlight-popover.tsx
6345
- import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
6346
- var ColorHighlightPopoverButton = ({
6347
- className,
6348
- children,
6349
- ...props
6350
- }) => /* @__PURE__ */ jsx24(
6351
- IconButton8,
6352
- {
6353
- type: "button",
6354
- className,
6355
- variant: "ghost",
6356
- color: "default",
6357
- tabIndex: -1,
6358
- "aria-label": "Highlight text",
6359
- tooltip: "Highlight",
6360
- isIconOnly: !children,
6361
- ...props,
6362
- children: children != null ? children : /* @__PURE__ */ jsx24(HighlighterIcon2, {})
6363
- }
6364
- );
6365
- function ColorHighlightPopoverContent({
6366
- editor,
6367
- colors = pickHighlightColorsByValue([
6368
- "var(--tt-color-highlight-green)",
6369
- "var(--tt-color-highlight-blue)",
6370
- "var(--tt-color-highlight-red)",
6371
- "var(--tt-color-highlight-purple)",
6372
- "var(--tt-color-highlight-yellow)"
6373
- ])
6374
- }) {
6375
- const { handleRemoveHighlight } = useColorHighlight({ editor });
6376
- const containerRef = useRef10(null);
6377
- const menuItems = useMemo17(
6378
- () => [...colors, { label: "Remove highlight", value: "none" }],
6379
- [colors]
6380
- );
6381
- const { selectedIndex } = useMenuNavigation({
6382
- containerRef,
6383
- items: menuItems,
6384
- orientation: "both",
6385
- onSelect: (item) => {
6386
- if (!containerRef.current) return false;
6387
- const highlightedElement = containerRef.current.querySelector(
6388
- '[data-highlighted="true"]'
6389
- );
6390
- if (highlightedElement) highlightedElement.click();
6391
- if (item.value === "none") handleRemoveHighlight();
6392
- },
6393
- autoSelectFirstItem: false
6394
- });
6395
- return /* @__PURE__ */ jsxs18("div", { ref: containerRef, className: "flex gap-1 items-center", children: [
6396
- /* @__PURE__ */ jsx24(
6397
- "div",
6398
- {
6399
- className: "flex items-center gap-1 outline-none",
6400
- "data-orientation": "horizontal",
6401
- children: colors.map((color, index) => /* @__PURE__ */ jsx24(
6402
- ColorHighlightButton,
6403
- {
6404
- editor,
6405
- highlightColor: color.value,
6406
- "aria-label": `${color.label} highlight color`,
6407
- tabIndex: index === selectedIndex ? 0 : -1,
6408
- "data-highlighted": selectedIndex === index
6409
- },
6410
- color.value
6411
- ))
6412
- }
6413
- ),
6414
- /* @__PURE__ */ jsx24(ToolbarSeparator2, { orientation: "vertical" }),
6415
- /* @__PURE__ */ jsx24("div", { className: "tiptap-button-group", children: /* @__PURE__ */ jsx24(
6416
- IconButton8,
6417
- {
6418
- onClick: handleRemoveHighlight,
6419
- "aria-label": "Remove highlight",
6420
- tabIndex: selectedIndex === colors.length ? 0 : -1,
6421
- type: "button",
6422
- role: "menuitem",
6423
- variant: "ghost",
6424
- color: "default",
6425
- "data-highlighted": selectedIndex === colors.length,
6426
- children: /* @__PURE__ */ jsx24(BanIcon, {})
6427
- }
6428
- ) })
6429
- ] });
6430
- }
6431
- function ColorHighlightPopover({
6432
- editor: providedEditor,
6433
- colors = pickHighlightColorsByValue([
6434
- "var(--tt-color-highlight-green)",
6435
- "var(--tt-color-highlight-blue)",
6436
- "var(--tt-color-highlight-red)",
6437
- "var(--tt-color-highlight-purple)",
6438
- "var(--tt-color-highlight-yellow)"
6439
- ]),
6440
- hideWhenUnavailable = false,
6441
- onApplied,
6442
- ...props
6443
- }) {
6444
- const { editor } = useTiptapEditor(providedEditor);
6445
- const [isOpen, setIsOpen] = useState24(false);
6446
- const { isVisible, canColorHighlight: canColorHighlight2, isActive, label } = useColorHighlight({
6447
- editor,
6448
- hideWhenUnavailable,
6449
- onApplied
6450
- });
6451
- if (!isVisible) return null;
6452
- return /* @__PURE__ */ jsxs18(Popover2.Root, { open: isOpen, onOpenChange: setIsOpen, spacing: "dense", children: [
6453
- /* @__PURE__ */ jsx24(
6454
- Popover2.Trigger,
6455
- {
6456
- render: /* @__PURE__ */ jsx24(
6457
- ColorHighlightPopoverButton,
6458
- {
6459
- disabled: !canColorHighlight2,
6460
- "data-disabled": !canColorHighlight2,
6461
- "data-active-state": isActive ? "on" : "off",
6462
- "aria-pressed": isActive,
6463
- "aria-label": label,
6464
- title: label,
6465
- ...props
6466
- }
6467
- )
6468
- }
6469
- ),
6470
- /* @__PURE__ */ jsx24(Popover2.Content, { "aria-label": "Highlight colors", children: /* @__PURE__ */ jsx24(ColorHighlightPopoverContent, { editor, colors }) })
6471
- ] });
6472
- }
6473
-
6474
- // src/ui/heading-dropdown-menu/index.tsx
6475
- import { Button as Button8 } from "@kopexa/button";
6476
- import { DropdownMenu } from "@kopexa/dropdown-menu";
6477
- import { isNodeInSchema as isNodeInSchema6 } from "@kopexa/editor-utils";
6478
- import { ChevronDownIcon, HeadingIcon } from "@kopexa/icons";
6479
- import { isNodeSelection as isNodeSelection6 } from "@tiptap/react";
6480
- import * as React19 from "react";
6743
+ // src/ui/text-align-button/text-align-button.tsx
6744
+ import { IconButton as IconButton9 } from "@kopexa/button";
6745
+ import { useTiptapEditor as useTiptapEditor14 } from "@kopexa/editor-utils";
6746
+ import { useCallback as useCallback27 } from "react";
6481
6747
 
6482
- // src/ui/heading-button/index.tsx
6483
- import { Button as Button7 } from "@kopexa/button";
6484
- import { isNodeInSchema as isNodeInSchema5 } from "@kopexa/editor-utils";
6748
+ // src/ui/text-align-button/use-text-align.ts
6485
6749
  import {
6486
- HeadingFiveIcon,
6487
- HeadingFourIcon,
6488
- HeadingOneIcon as HeadingOneIcon2,
6489
- HeadingSixIcon,
6490
- HeadingThreeIcon as HeadingThreeIcon2,
6491
- HeadingTwoIcon as HeadingTwoIcon2
6750
+ isExtensionAvailable as isExtensionAvailable2,
6751
+ isNodeTypeSelected as isNodeTypeSelected4,
6752
+ useTiptapEditor as useTiptapEditor13
6753
+ } from "@kopexa/editor-utils";
6754
+ import {
6755
+ AlignCenterIcon,
6756
+ AlignJustifyIcon,
6757
+ AlignLeftIcon,
6758
+ AlignRightIcon
6492
6759
  } from "@kopexa/icons";
6493
- import * as React18 from "react";
6494
-
6495
- // src/ui/heading-button/utils.ts
6496
- import { isNodeSelection as isNodeSelection5 } from "@tiptap/react";
6497
- var headingShortcutKeys = {
6498
- 1: "Ctrl-Alt-1",
6499
- 2: "Ctrl-Alt-2",
6500
- 3: "Ctrl-Alt-3",
6501
- 4: "Ctrl-Alt-4",
6502
- 5: "Ctrl-Alt-5",
6503
- 6: "Ctrl-Alt-6"
6760
+ import { useCallback as useCallback26, useEffect as useEffect25, useState as useState24 } from "react";
6761
+ var TEXT_ALIGN_SHORTCUT_KEYS = {
6762
+ left: "mod+shift+l",
6763
+ center: "mod+shift+e",
6764
+ right: "mod+shift+r",
6765
+ justify: "mod+shift+j"
6504
6766
  };
6505
- function canToggleHeading(editor, level) {
6506
- if (!editor) return false;
6507
- try {
6508
- return editor.can().toggleNode("heading", "paragraph", { level });
6509
- } catch {
6767
+ var textAlignIcons = {
6768
+ left: AlignLeftIcon,
6769
+ center: AlignCenterIcon,
6770
+ right: AlignRightIcon,
6771
+ justify: AlignJustifyIcon
6772
+ };
6773
+ var textAlignLabels = {
6774
+ left: "Align left",
6775
+ center: "Align center",
6776
+ right: "Align right",
6777
+ justify: "Align justify"
6778
+ };
6779
+ function canSetTextAlign(editor, align) {
6780
+ if (!editor || !editor.isEditable) return false;
6781
+ if (!isExtensionAvailable2(editor, "textAlign") || isNodeTypeSelected4(editor, ["image"]))
6510
6782
  return false;
6511
- }
6783
+ return editor.can().setTextAlign(align);
6512
6784
  }
6513
- function isHeadingActive(editor, level) {
6514
- if (!editor) return false;
6515
- return editor.isActive("heading", { level });
6785
+ function hasSetTextAlign(commands) {
6786
+ return "setTextAlign" in commands;
6516
6787
  }
6517
- function toggleHeading(editor, level) {
6518
- if (!editor) return;
6519
- if (editor.isActive("heading", { level })) {
6520
- editor.chain().focus().setNode("paragraph").run();
6521
- } else {
6522
- editor.chain().focus().toggleNode("heading", "paragraph", { level }).run();
6523
- }
6788
+ function isTextAlignActive(editor, align) {
6789
+ if (!editor || !editor.isEditable) return false;
6790
+ return editor.isActive({ textAlign: align });
6524
6791
  }
6525
- function isHeadingButtonDisabled(editor, level, userDisabled = false) {
6526
- if (!editor) return true;
6527
- if (userDisabled) return true;
6528
- if (!canToggleHeading(editor, level)) return true;
6792
+ function setTextAlign(editor, align) {
6793
+ if (!editor || !editor.isEditable) return false;
6794
+ if (!canSetTextAlign(editor, align)) return false;
6795
+ const chain = editor.chain().focus();
6796
+ if (hasSetTextAlign(chain)) {
6797
+ return chain.setTextAlign(align).run();
6798
+ }
6529
6799
  return false;
6530
6800
  }
6531
- function shouldShowHeadingButton(params) {
6532
- const { editor, hideWhenUnavailable, headingInSchema } = params;
6533
- if (!headingInSchema || !editor) {
6534
- return false;
6535
- }
6536
- if (hideWhenUnavailable) {
6537
- if (isNodeSelection5(editor.state.selection)) {
6538
- return false;
6801
+ function shouldShowButton3(props) {
6802
+ const { editor, hideWhenUnavailable, align } = props;
6803
+ if (!editor || !editor.isEditable) return false;
6804
+ if (!isExtensionAvailable2(editor, "textAlign")) return false;
6805
+ if (hideWhenUnavailable && !editor.isActive("code")) {
6806
+ return canSetTextAlign(editor, align);
6807
+ }
6808
+ return true;
6809
+ }
6810
+ function useTextAlign(config) {
6811
+ const {
6812
+ editor: providedEditor,
6813
+ align,
6814
+ hideWhenUnavailable = false,
6815
+ onAligned
6816
+ } = config;
6817
+ const { editor } = useTiptapEditor13(providedEditor);
6818
+ const [isVisible, setIsVisible] = useState24(true);
6819
+ const canAlign = canSetTextAlign(editor, align);
6820
+ const isActive = isTextAlignActive(editor, align);
6821
+ useEffect25(() => {
6822
+ if (!editor) return;
6823
+ const handleSelectionUpdate = () => {
6824
+ setIsVisible(shouldShowButton3({ editor, align, hideWhenUnavailable }));
6825
+ };
6826
+ handleSelectionUpdate();
6827
+ editor.on("selectionUpdate", handleSelectionUpdate);
6828
+ return () => {
6829
+ editor.off("selectionUpdate", handleSelectionUpdate);
6830
+ };
6831
+ }, [editor, hideWhenUnavailable, align]);
6832
+ const handleTextAlign = useCallback26(() => {
6833
+ if (!editor) return false;
6834
+ const success = setTextAlign(editor, align);
6835
+ if (success) {
6836
+ onAligned == null ? void 0 : onAligned();
6837
+ }
6838
+ return success;
6839
+ }, [editor, align, onAligned]);
6840
+ return {
6841
+ isVisible,
6842
+ isActive,
6843
+ handleTextAlign,
6844
+ canAlign,
6845
+ label: textAlignLabels[align],
6846
+ shortcutKeys: TEXT_ALIGN_SHORTCUT_KEYS[align],
6847
+ Icon: textAlignIcons[align]
6848
+ };
6849
+ }
6850
+
6851
+ // src/ui/text-align-button/text-align-button.tsx
6852
+ import { jsx as jsx26 } from "react/jsx-runtime";
6853
+ var TextAlignButton = ({
6854
+ editor: providedEditor,
6855
+ align,
6856
+ text,
6857
+ hideWhenUnavailable = false,
6858
+ onAligned,
6859
+ showShortcut = false,
6860
+ onClick,
6861
+ children,
6862
+ ...buttonProps
6863
+ }) => {
6864
+ const { editor } = useTiptapEditor14(providedEditor);
6865
+ const {
6866
+ isVisible,
6867
+ handleTextAlign,
6868
+ label,
6869
+ canAlign,
6870
+ isActive,
6871
+ Icon,
6872
+ shortcutKeys
6873
+ } = useTextAlign({
6874
+ editor,
6875
+ align,
6876
+ hideWhenUnavailable,
6877
+ onAligned
6878
+ });
6879
+ const handleClick = useCallback27(
6880
+ (e) => {
6881
+ onClick == null ? void 0 : onClick(e);
6882
+ if (e.defaultPrevented) return;
6883
+ handleTextAlign();
6884
+ },
6885
+ [handleTextAlign, onClick]
6886
+ );
6887
+ if (!isVisible) {
6888
+ return null;
6889
+ }
6890
+ return /* @__PURE__ */ jsx26(
6891
+ IconButton9,
6892
+ {
6893
+ type: "button",
6894
+ disabled: canAlign,
6895
+ variant: "ghost",
6896
+ color: "default",
6897
+ "data-active-state": isActive ? "on" : "off",
6898
+ "data-disabled": canAlign,
6899
+ tabIndex: -1,
6900
+ "aria-label": label,
6901
+ "aria-pressed": isActive,
6902
+ tooltip: label,
6903
+ shortcutKeys,
6904
+ onClick: handleClick,
6905
+ ...buttonProps,
6906
+ children: /* @__PURE__ */ jsx26(Icon, {})
6907
+ }
6908
+ );
6909
+ };
6910
+
6911
+ // src/ui/turn-into-dropdown/turn-into-dropdown.tsx
6912
+ import { Button as Button11 } from "@kopexa/button";
6913
+ import { DropdownMenu as DropdownMenu2 } from "@kopexa/dropdown-menu";
6914
+ import { useTiptapEditor as useTiptapEditor23 } from "@kopexa/editor-utils";
6915
+ import { forwardRef } from "react";
6916
+
6917
+ // src/ui/blockquote-button/blockquote-button.tsx
6918
+ import { useTiptapEditor as useTiptapEditor16 } from "@kopexa/editor-utils";
6919
+ import { ToolbarButton as ToolbarButton5 } from "@kopexa/toolbar";
6920
+ import * as React16 from "react";
6921
+
6922
+ // src/ui/blockquote-button/use-blockquote.ts
6923
+ import {
6924
+ findNodePosition,
6925
+ isNodeInSchema as isNodeInSchema5,
6926
+ isNodeTypeSelected as isNodeTypeSelected5,
6927
+ isValidPosition,
6928
+ useTiptapEditor as useTiptapEditor15
6929
+ } from "@kopexa/editor-utils";
6930
+ import { BlockquoteIcon as BlockquoteIcon2 } from "@kopexa/icons";
6931
+ import { NodeSelection as NodeSelection2, TextSelection as TextSelection2 } from "@tiptap/pm/state";
6932
+ import * as React15 from "react";
6933
+ var BLOCKQUOTE_SHORTCUT_KEY = "mod+shift+b";
6934
+ function canToggleBlockquote(editor, turnInto = true) {
6935
+ var _a;
6936
+ if (!editor || !editor.isEditable) return false;
6937
+ if (!isNodeInSchema5("blockquote", editor) || isNodeTypeSelected5(editor, ["image"]))
6938
+ return false;
6939
+ if (!turnInto) {
6940
+ return editor.can().toggleWrap("blockquote");
6941
+ }
6942
+ try {
6943
+ const view = editor.view;
6944
+ const state = view.state;
6945
+ const selection = state.selection;
6946
+ if (selection.empty || selection instanceof TextSelection2) {
6947
+ const pos = (_a = findNodePosition({
6948
+ editor,
6949
+ node: state.selection.$anchor.node(1)
6950
+ })) == null ? void 0 : _a.pos;
6951
+ if (!isValidPosition(pos)) return false;
6952
+ }
6953
+ return true;
6954
+ } catch {
6955
+ return false;
6956
+ }
6957
+ }
6958
+ function toggleBlockquote(editor) {
6959
+ var _a, _b, _c;
6960
+ if (!editor || !editor.isEditable) return false;
6961
+ if (!canToggleBlockquote(editor)) return false;
6962
+ try {
6963
+ const view = editor.view;
6964
+ let state = view.state;
6965
+ let tr = state.tr;
6966
+ if (state.selection.empty || state.selection instanceof TextSelection2) {
6967
+ const pos = (_a = findNodePosition({
6968
+ editor,
6969
+ node: state.selection.$anchor.node(1)
6970
+ })) == null ? void 0 : _a.pos;
6971
+ if (!isValidPosition(pos)) return false;
6972
+ tr = tr.setSelection(NodeSelection2.create(state.doc, pos));
6973
+ view.dispatch(tr);
6974
+ state = view.state;
6975
+ }
6976
+ const selection = state.selection;
6977
+ let chain = editor.chain().focus();
6978
+ if (selection instanceof NodeSelection2) {
6979
+ const firstChild = (_b = selection.node.firstChild) == null ? void 0 : _b.firstChild;
6980
+ const lastChild = (_c = selection.node.lastChild) == null ? void 0 : _c.lastChild;
6981
+ const from = firstChild ? selection.from + firstChild.nodeSize : selection.from + 1;
6982
+ const to = lastChild ? selection.to - lastChild.nodeSize : selection.to - 1;
6983
+ chain = chain.setTextSelection({ from, to }).clearNodes();
6984
+ }
6985
+ const toggle = editor.isActive("blockquote") ? chain.lift("blockquote") : chain.wrapIn("blockquote");
6986
+ toggle.run();
6987
+ editor.chain().focus().selectTextblockEnd().run();
6988
+ return true;
6989
+ } catch {
6990
+ return false;
6991
+ }
6992
+ }
6993
+ function shouldShowButton4(props) {
6994
+ const { editor, hideWhenUnavailable } = props;
6995
+ if (!editor || !editor.isEditable) return false;
6996
+ if (!isNodeInSchema5("blockquote", editor)) return false;
6997
+ if (hideWhenUnavailable && !editor.isActive("code")) {
6998
+ return canToggleBlockquote(editor);
6999
+ }
7000
+ return true;
7001
+ }
7002
+ function useBlockquote(config) {
7003
+ const {
7004
+ editor: providedEditor,
7005
+ hideWhenUnavailable = false,
7006
+ onToggled
7007
+ } = config || {};
7008
+ const { editor } = useTiptapEditor15(providedEditor);
7009
+ const [isVisible, setIsVisible] = React15.useState(true);
7010
+ const canToggle3 = canToggleBlockquote(editor);
7011
+ const isActive = (editor == null ? void 0 : editor.isActive("blockquote")) || false;
7012
+ React15.useEffect(() => {
7013
+ if (!editor) return;
7014
+ const handleSelectionUpdate = () => {
7015
+ setIsVisible(shouldShowButton4({ editor, hideWhenUnavailable }));
7016
+ };
7017
+ handleSelectionUpdate();
7018
+ editor.on("selectionUpdate", handleSelectionUpdate);
7019
+ return () => {
7020
+ editor.off("selectionUpdate", handleSelectionUpdate);
7021
+ };
7022
+ }, [editor, hideWhenUnavailable]);
7023
+ const handleToggle = React15.useCallback(() => {
7024
+ if (!editor) return false;
7025
+ const success = toggleBlockquote(editor);
7026
+ if (success) {
7027
+ onToggled == null ? void 0 : onToggled();
7028
+ }
7029
+ return success;
7030
+ }, [editor, onToggled]);
7031
+ return {
7032
+ isVisible,
7033
+ isActive,
7034
+ handleToggle,
7035
+ canToggle: canToggle3,
7036
+ label: "Blockquote",
7037
+ shortcutKeys: BLOCKQUOTE_SHORTCUT_KEY,
7038
+ Icon: BlockquoteIcon2
7039
+ };
7040
+ }
7041
+
7042
+ // src/ui/blockquote-button/blockquote-button.tsx
7043
+ import { Fragment as Fragment6, jsx as jsx27, jsxs as jsxs20 } from "react/jsx-runtime";
7044
+ var BlockquoteButton = ({
7045
+ editor: providedEditor,
7046
+ text,
7047
+ hideWhenUnavailable = false,
7048
+ onToggled,
7049
+ showShortcut = false,
7050
+ onClick,
7051
+ children,
7052
+ ...buttonProps
7053
+ }) => {
7054
+ const { editor } = useTiptapEditor16(providedEditor);
7055
+ const {
7056
+ isVisible,
7057
+ canToggle: canToggle3,
7058
+ isActive,
7059
+ handleToggle,
7060
+ label,
7061
+ shortcutKeys,
7062
+ Icon
7063
+ } = useBlockquote({
7064
+ editor,
7065
+ hideWhenUnavailable,
7066
+ onToggled
7067
+ });
7068
+ const handleClick = React16.useCallback(
7069
+ (event) => {
7070
+ onClick == null ? void 0 : onClick(event);
7071
+ if (event.defaultPrevented) return;
7072
+ handleToggle();
7073
+ },
7074
+ [handleToggle, onClick]
7075
+ );
7076
+ if (!isVisible) {
7077
+ return null;
7078
+ }
7079
+ return /* @__PURE__ */ jsx27(
7080
+ ToolbarButton5,
7081
+ {
7082
+ type: "button",
7083
+ variant: "ghost",
7084
+ color: "default",
7085
+ disabled: !canToggle3,
7086
+ "data-disabled": !canToggle3,
7087
+ "data-active-state": isActive ? "on" : "off",
7088
+ tabIndex: -1,
7089
+ "aria-label": "blockquote",
7090
+ "aria-pressed": isActive,
7091
+ title: label,
7092
+ shortcutKeys,
7093
+ onClick: handleClick,
7094
+ isIconOnly: !text && !children,
7095
+ ...buttonProps,
7096
+ children: children != null ? children : /* @__PURE__ */ jsxs20(Fragment6, { children: [
7097
+ /* @__PURE__ */ jsx27(Icon, {}),
7098
+ text && /* @__PURE__ */ jsx27("span", { children: text })
7099
+ ] })
7100
+ }
7101
+ );
7102
+ };
7103
+
7104
+ // src/ui/codeblock-button/code-block-button.tsx
7105
+ import { useTiptapEditor as useTiptapEditor18 } from "@kopexa/editor-utils";
7106
+ import { CodeblockIcon as CodeblockIcon2 } from "@kopexa/icons";
7107
+ import { ToolbarButton as ToolbarButton6 } from "@kopexa/toolbar";
7108
+ import { useCallback as useCallback31 } from "react";
7109
+
7110
+ // src/ui/codeblock-button/use-code-block.ts
7111
+ import {
7112
+ findNodePosition as findNodePosition2,
7113
+ isNodeInSchema as isNodeInSchema6,
7114
+ isNodeTypeSelected as isNodeTypeSelected6,
7115
+ isValidPosition as isValidPosition2,
7116
+ useTiptapEditor as useTiptapEditor17
7117
+ } from "@kopexa/editor-utils";
7118
+ import { CodeblockIcon } from "@kopexa/icons";
7119
+ import { NodeSelection as NodeSelection3, TextSelection as TextSelection3 } from "@tiptap/pm/state";
7120
+ import * as React17 from "react";
7121
+ var CODE_BLOCK_SHORTCUT_KEY = "mod+alt+c";
7122
+ function canToggle2(editor, turnInto = true) {
7123
+ var _a;
7124
+ if (!editor || !editor.isEditable) return false;
7125
+ if (!isNodeInSchema6("codeBlock", editor) || isNodeTypeSelected6(editor, ["image"]))
7126
+ return false;
7127
+ if (!turnInto) {
7128
+ return editor.can().toggleNode("codeBlock", "paragraph");
7129
+ }
7130
+ try {
7131
+ const view = editor.view;
7132
+ const state = view.state;
7133
+ const selection = state.selection;
7134
+ if (selection.empty || selection instanceof TextSelection3) {
7135
+ const pos = (_a = findNodePosition2({
7136
+ editor,
7137
+ node: state.selection.$anchor.node(1)
7138
+ })) == null ? void 0 : _a.pos;
7139
+ if (!isValidPosition2(pos)) return false;
6539
7140
  }
7141
+ return true;
7142
+ } catch {
7143
+ return false;
7144
+ }
7145
+ }
7146
+ function toggleCodeBlock(editor) {
7147
+ var _a, _b, _c;
7148
+ if (!editor || !editor.isEditable) return false;
7149
+ if (!canToggle2(editor)) return false;
7150
+ try {
7151
+ const view = editor.view;
7152
+ let state = view.state;
7153
+ let tr = state.tr;
7154
+ if (state.selection.empty || state.selection instanceof TextSelection3) {
7155
+ const pos = (_a = findNodePosition2({
7156
+ editor,
7157
+ node: state.selection.$anchor.node(1)
7158
+ })) == null ? void 0 : _a.pos;
7159
+ if (!isValidPosition2(pos)) return false;
7160
+ tr = tr.setSelection(NodeSelection3.create(state.doc, pos));
7161
+ view.dispatch(tr);
7162
+ state = view.state;
7163
+ }
7164
+ const selection = state.selection;
7165
+ let chain = editor.chain().focus();
7166
+ if (selection instanceof NodeSelection3) {
7167
+ const firstChild = (_b = selection.node.firstChild) == null ? void 0 : _b.firstChild;
7168
+ const lastChild = (_c = selection.node.lastChild) == null ? void 0 : _c.lastChild;
7169
+ const from = firstChild ? selection.from + firstChild.nodeSize : selection.from + 1;
7170
+ const to = lastChild ? selection.to - lastChild.nodeSize : selection.to - 1;
7171
+ chain = chain.setTextSelection({ from, to }).clearNodes();
7172
+ }
7173
+ const toggle = editor.isActive("codeBlock") ? chain.setNode("paragraph") : chain.toggleNode("codeBlock", "paragraph");
7174
+ toggle.run();
7175
+ editor.chain().focus().selectTextblockEnd().run();
7176
+ return true;
7177
+ } catch {
7178
+ return false;
7179
+ }
7180
+ }
7181
+ function shouldShowButton5(props) {
7182
+ const { editor, hideWhenUnavailable } = props;
7183
+ if (!editor || !editor.isEditable) return false;
7184
+ if (!isNodeInSchema6("codeBlock", editor)) return false;
7185
+ if (hideWhenUnavailable && !editor.isActive("code")) {
7186
+ return canToggle2(editor);
6540
7187
  }
6541
7188
  return true;
6542
7189
  }
6543
- function getFormattedHeadingName(level) {
6544
- return `Heading ${level}`;
6545
- }
6546
-
6547
- // src/ui/heading-button/index.tsx
6548
- import { jsx as jsx25 } from "react/jsx-runtime";
6549
- var headingIcons = {
6550
- 1: HeadingOneIcon2,
6551
- 2: HeadingTwoIcon2,
6552
- 3: HeadingThreeIcon2,
6553
- 4: HeadingFourIcon,
6554
- 5: HeadingFiveIcon,
6555
- 6: HeadingSixIcon
6556
- };
6557
- function useHeadingState(editor, level, disabled = false) {
6558
- const headingInSchema = isNodeInSchema5("heading", editor);
6559
- const isDisabled = isHeadingButtonDisabled(editor, level, disabled);
6560
- const isActive = isHeadingActive(editor, level);
6561
- const Icon = headingIcons[level];
6562
- const shortcutKey = headingShortcutKeys[level];
6563
- const formattedName = getFormattedHeadingName(level);
7190
+ function useCodeBlock(config) {
7191
+ const {
7192
+ editor: providedEditor,
7193
+ hideWhenUnavailable = false,
7194
+ onToggled
7195
+ } = config || {};
7196
+ const { editor } = useTiptapEditor17(providedEditor);
7197
+ const [isVisible, setIsVisible] = React17.useState(true);
7198
+ const canToggleState = canToggle2(editor);
7199
+ const isActive = (editor == null ? void 0 : editor.isActive("codeBlock")) || false;
7200
+ React17.useEffect(() => {
7201
+ if (!editor) return;
7202
+ const handleSelectionUpdate = () => {
7203
+ setIsVisible(shouldShowButton5({ editor, hideWhenUnavailable }));
7204
+ };
7205
+ handleSelectionUpdate();
7206
+ editor.on("selectionUpdate", handleSelectionUpdate);
7207
+ return () => {
7208
+ editor.off("selectionUpdate", handleSelectionUpdate);
7209
+ };
7210
+ }, [editor, hideWhenUnavailable]);
7211
+ const handleToggle = React17.useCallback(() => {
7212
+ if (!editor) return false;
7213
+ const success = toggleCodeBlock(editor);
7214
+ if (success) {
7215
+ onToggled == null ? void 0 : onToggled();
7216
+ }
7217
+ return success;
7218
+ }, [editor, onToggled]);
6564
7219
  return {
6565
- headingInSchema,
6566
- isDisabled,
7220
+ isVisible,
6567
7221
  isActive,
6568
- Icon,
6569
- shortcutKey,
6570
- formattedName
7222
+ handleToggle,
7223
+ canToggle: canToggleState,
7224
+ label: "Code Block",
7225
+ shortcutKeys: CODE_BLOCK_SHORTCUT_KEY,
7226
+ Icon: CodeblockIcon
6571
7227
  };
6572
7228
  }
6573
- var HeadingButton = ({
7229
+
7230
+ // src/ui/codeblock-button/code-block-button.tsx
7231
+ import { Fragment as Fragment7, jsx as jsx28, jsxs as jsxs21 } from "react/jsx-runtime";
7232
+ var CodeBlockButton = ({
6574
7233
  editor: providedEditor,
6575
- level,
6576
7234
  text,
6577
7235
  hideWhenUnavailable = false,
6578
- className = "",
6579
- disabled,
7236
+ onToggled,
7237
+ showShortcut = false,
6580
7238
  onClick,
6581
7239
  children,
6582
- ref,
6583
7240
  ...buttonProps
6584
7241
  }) => {
6585
- const { editor } = useTiptapEditor(providedEditor);
6586
- const {
6587
- headingInSchema,
6588
- isDisabled,
6589
- isActive,
6590
- Icon,
6591
- shortcutKey,
6592
- formattedName
6593
- } = useHeadingState(editor, level, disabled);
6594
- const handleClick = React18.useCallback(
6595
- (e) => {
6596
- onClick == null ? void 0 : onClick(e);
6597
- if (!e.defaultPrevented && !isDisabled && editor) {
6598
- toggleHeading(editor, level);
6599
- }
7242
+ const { editor } = useTiptapEditor18(providedEditor);
7243
+ const { isVisible, canToggle: canToggle3, isActive, handleToggle, label, shortcutKeys } = useCodeBlock({
7244
+ editor,
7245
+ hideWhenUnavailable,
7246
+ onToggled
7247
+ });
7248
+ const handleClick = useCallback31(
7249
+ (event) => {
7250
+ onClick == null ? void 0 : onClick(event);
7251
+ if (event.defaultPrevented) return;
7252
+ handleToggle();
6600
7253
  },
6601
- [onClick, isDisabled, editor, level]
7254
+ [handleToggle, onClick]
6602
7255
  );
6603
- const show = React18.useMemo(() => {
6604
- return shouldShowHeadingButton({
6605
- editor,
6606
- level,
6607
- hideWhenUnavailable,
6608
- headingInSchema
6609
- });
6610
- }, [editor, level, hideWhenUnavailable, headingInSchema]);
6611
- if (!show || !editor || !editor.isEditable) {
7256
+ if (!isVisible) {
6612
7257
  return null;
6613
7258
  }
6614
- return /* @__PURE__ */ jsx25(
6615
- Button7,
7259
+ return /* @__PURE__ */ jsx28(
7260
+ ToolbarButton6,
6616
7261
  {
6617
7262
  type: "button",
6618
- className: className.trim(),
6619
- disabled: isDisabled,
6620
7263
  variant: "ghost",
6621
7264
  color: "default",
7265
+ disabled: !canToggle3,
7266
+ "data-disabled": !canToggle3,
6622
7267
  "data-active-state": isActive ? "on" : "off",
6623
- "data-disabled": isDisabled,
6624
7268
  tabIndex: -1,
6625
- "aria-label": formattedName,
7269
+ "aria-label": "codeBlock",
6626
7270
  "aria-pressed": isActive,
6627
- tooltip: formattedName,
6628
- shortcutKeys: shortcutKey,
7271
+ title: label,
7272
+ shortcutKeys,
6629
7273
  onClick: handleClick,
6630
- startContent: /* @__PURE__ */ jsx25(Icon, {}),
7274
+ isIconOnly: !text && !children,
6631
7275
  ...buttonProps,
6632
- ref,
6633
- children: children || text
7276
+ children: children != null ? children : /* @__PURE__ */ jsxs21(Fragment7, { children: [
7277
+ /* @__PURE__ */ jsx28(CodeblockIcon2, {}),
7278
+ text && /* @__PURE__ */ jsx28("span", { children: text })
7279
+ ] })
6634
7280
  }
6635
7281
  );
6636
7282
  };
6637
7283
 
6638
- // src/ui/heading-dropdown-menu/index.tsx
6639
- import { jsx as jsx26, jsxs as jsxs19 } from "react/jsx-runtime";
6640
- function HeadingDropdownMenu({
6641
- editor: providedEditor,
6642
- levels = [1, 2, 3, 4, 5, 6],
6643
- hideWhenUnavailable = false,
6644
- onOpenChange,
6645
- ...props
6646
- }) {
6647
- var _a;
6648
- const [isOpen, setIsOpen] = React19.useState(false);
6649
- const { editor } = useTiptapEditor(providedEditor);
6650
- const headingInSchema = isNodeInSchema6("heading", editor);
6651
- const handleOnOpenChange = React19.useCallback(
6652
- (open) => {
6653
- setIsOpen(open);
6654
- onOpenChange == null ? void 0 : onOpenChange(open);
6655
- },
6656
- [onOpenChange]
6657
- );
6658
- const getActiveIcon = React19.useCallback(() => {
6659
- if (!editor) return /* @__PURE__ */ jsx26(HeadingIcon, {});
6660
- const activeLevel = levels.find(
6661
- (level) => editor.isActive("heading", { level })
6662
- );
6663
- if (!activeLevel) return /* @__PURE__ */ jsx26(HeadingIcon, {});
6664
- const ActiveIcon = headingIcons[activeLevel];
6665
- return /* @__PURE__ */ jsx26(ActiveIcon, {});
6666
- }, [editor, levels]);
6667
- const canToggleAnyHeading = React19.useCallback(() => {
6668
- if (!editor) return false;
6669
- return levels.some(
6670
- (level) => editor.can().toggleNode("heading", "paragraph", { level })
6671
- );
6672
- }, [editor, levels]);
6673
- const isDisabled = !canToggleAnyHeading();
6674
- const isAnyHeadingActive = (_a = editor == null ? void 0 : editor.isActive("heading")) != null ? _a : false;
6675
- const show = React19.useMemo(() => {
6676
- if (!headingInSchema || !editor) {
6677
- return false;
6678
- }
6679
- if (hideWhenUnavailable) {
6680
- if (isNodeSelection6(editor.state.selection) || !canToggleAnyHeading()) {
6681
- return false;
6682
- }
6683
- }
6684
- return true;
6685
- }, [headingInSchema, editor, hideWhenUnavailable, canToggleAnyHeading]);
6686
- if (!show || !editor || !editor.isEditable) {
6687
- return null;
6688
- }
6689
- return /* @__PURE__ */ jsxs19(DropdownMenu.Root, { open: isOpen, onOpenChange: handleOnOpenChange, children: [
6690
- /* @__PURE__ */ jsx26(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsx26(
6691
- Button8,
6692
- {
6693
- type: "button",
6694
- disabled: isDisabled,
6695
- variant: "ghost",
6696
- color: "default",
6697
- "data-active-state": isAnyHeadingActive ? "on" : "off",
6698
- "data-disabled": isDisabled,
6699
- tabIndex: -1,
6700
- "aria-label": "Format text as heading",
6701
- "aria-pressed": isAnyHeadingActive,
6702
- tooltip: "Heading",
6703
- endContent: /* @__PURE__ */ jsx26(ChevronDownIcon, {}),
6704
- ...props,
6705
- children: getActiveIcon()
6706
- }
6707
- ) }),
6708
- /* @__PURE__ */ jsx26(DropdownMenu.Content, { children: /* @__PURE__ */ jsx26(DropdownMenu.Group, { children: levels.map((level) => /* @__PURE__ */ jsx26(DropdownMenu.Item, { asChild: true, children: /* @__PURE__ */ jsx26(
6709
- HeadingButton,
6710
- {
6711
- editor,
6712
- level,
6713
- text: getFormattedHeadingName(level),
6714
- tooltip: "",
6715
- fullWidth: true,
6716
- spacing: "start"
6717
- }
6718
- ) }, `heading-${level}`)) }) })
6719
- ] });
6720
- }
6721
-
6722
- // src/ui/list-dropdown-menu/index.tsx
6723
- import { Button as Button10 } from "@kopexa/button";
6724
- import { DropdownMenu as DropdownMenu2 } from "@kopexa/dropdown-menu";
6725
- import { isNodeInSchema as isNodeInSchema8 } from "@kopexa/editor-utils";
6726
- import { ChevronDownIcon as ChevronDownIcon2, ListIcon as ListIcon4 } from "@kopexa/icons";
6727
- import { isNodeSelection as isNodeSelection8 } from "@tiptap/react";
6728
- import { useCallback as useCallback30, useMemo as useMemo21, useState as useState26 } from "react";
6729
-
6730
- // src/ui/list-button/index.tsx
7284
+ // src/ui/heading-button/index.tsx
6731
7285
  import { Button as Button9 } from "@kopexa/button";
6732
- import { isNodeInSchema as isNodeInSchema7 } from "@kopexa/editor-utils";
6733
- import { ListIcon as ListIcon3, ListOrderedIcon as ListOrderedIcon2, ListTodoIcon as ListTodoIcon2 } from "@kopexa/icons";
7286
+ import { isNodeInSchema as isNodeInSchema7, useTiptapEditor as useTiptapEditor19 } from "@kopexa/editor-utils";
7287
+ import {
7288
+ HeadingFiveIcon,
7289
+ HeadingFourIcon,
7290
+ HeadingOneIcon as HeadingOneIcon2,
7291
+ HeadingSixIcon,
7292
+ HeadingThreeIcon as HeadingThreeIcon2,
7293
+ HeadingTwoIcon as HeadingTwoIcon2
7294
+ } from "@kopexa/icons";
7295
+ import * as React18 from "react";
7296
+
7297
+ // src/ui/heading-button/utils.ts
6734
7298
  import { isNodeSelection as isNodeSelection7 } from "@tiptap/react";
6735
- import { useCallback as useCallback29, useMemo as useMemo20 } from "react";
6736
- import { jsx as jsx27 } from "react/jsx-runtime";
6737
- var listOptions = [
6738
- {
6739
- label: "Bullet List",
6740
- type: "bulletList",
6741
- icon: ListIcon3
6742
- },
6743
- {
6744
- label: "Ordered List",
6745
- type: "orderedList",
6746
- icon: ListOrderedIcon2
6747
- },
6748
- {
6749
- label: "Task List",
6750
- type: "taskList",
6751
- icon: ListTodoIcon2
6752
- }
6753
- ];
6754
- var listShortcutKeys = {
6755
- bulletList: "Ctrl-Shift-8",
6756
- orderedList: "Ctrl-Shift-7",
6757
- taskList: "Ctrl-Shift-9"
7299
+ var headingShortcutKeys = {
7300
+ 1: "Ctrl-Alt-1",
7301
+ 2: "Ctrl-Alt-2",
7302
+ 3: "Ctrl-Alt-3",
7303
+ 4: "Ctrl-Alt-4",
7304
+ 5: "Ctrl-Alt-5",
7305
+ 6: "Ctrl-Alt-6"
6758
7306
  };
6759
- function canToggleList(editor, type) {
6760
- if (!editor) {
7307
+ function canToggleHeading(editor, level) {
7308
+ if (!editor) return false;
7309
+ try {
7310
+ return editor.can().toggleNode("heading", "paragraph", { level });
7311
+ } catch {
6761
7312
  return false;
6762
7313
  }
6763
- switch (type) {
6764
- case "bulletList":
6765
- return editor.can().toggleBulletList();
6766
- case "orderedList":
6767
- return editor.can().toggleOrderedList();
6768
- case "taskList":
6769
- return editor.can().toggleList("taskList", "taskItem");
6770
- default:
6771
- return false;
6772
- }
6773
7314
  }
6774
- function isListActive(editor, type) {
7315
+ function isHeadingActive(editor, level) {
6775
7316
  if (!editor) return false;
6776
- switch (type) {
6777
- case "bulletList":
6778
- return editor.isActive("bulletList");
6779
- case "orderedList":
6780
- return editor.isActive("orderedList");
6781
- case "taskList":
6782
- return editor.isActive("taskList");
6783
- default:
6784
- return false;
6785
- }
7317
+ return editor.isActive("heading", { level });
6786
7318
  }
6787
- function toggleList(editor, type) {
7319
+ function toggleHeading(editor, level) {
6788
7320
  if (!editor) return;
6789
- switch (type) {
6790
- case "bulletList":
6791
- editor.chain().focus().toggleBulletList().run();
6792
- break;
6793
- case "orderedList":
6794
- editor.chain().focus().toggleOrderedList().run();
6795
- break;
6796
- case "taskList":
6797
- editor.chain().focus().toggleList("taskList", "taskItem").run();
6798
- break;
7321
+ if (editor.isActive("heading", { level })) {
7322
+ editor.chain().focus().setNode("paragraph").run();
7323
+ } else {
7324
+ editor.chain().focus().toggleNode("heading", "paragraph", { level }).run();
6799
7325
  }
6800
7326
  }
6801
- function getListOption(type) {
6802
- return listOptions.find((option) => option.type === type);
7327
+ function isHeadingButtonDisabled(editor, level, userDisabled = false) {
7328
+ if (!editor) return true;
7329
+ if (userDisabled) return true;
7330
+ if (!canToggleHeading(editor, level)) return true;
7331
+ return false;
6803
7332
  }
6804
- function shouldShowListButton(params) {
6805
- const { editor, type, hideWhenUnavailable, listInSchema } = params;
6806
- if (!listInSchema || !editor) {
7333
+ function shouldShowHeadingButton(params) {
7334
+ const { editor, hideWhenUnavailable, headingInSchema } = params;
7335
+ if (!headingInSchema || !editor) {
6807
7336
  return false;
6808
7337
  }
6809
7338
  if (hideWhenUnavailable) {
6810
- if (isNodeSelection7(editor.state.selection) || !canToggleList(editor, type)) {
7339
+ if (isNodeSelection7(editor.state.selection)) {
6811
7340
  return false;
6812
7341
  }
6813
7342
  }
6814
7343
  return true;
6815
7344
  }
6816
- function useListState(editor, type) {
6817
- const listInSchema = isNodeInSchema7(type, editor);
6818
- const listOption = getListOption(type);
6819
- const isActive = isListActive(editor, type);
6820
- const shortcutKey = listShortcutKeys[type];
7345
+ function getFormattedHeadingName(level) {
7346
+ return `Heading ${level}`;
7347
+ }
7348
+
7349
+ // src/ui/heading-button/index.tsx
7350
+ import { jsx as jsx29 } from "react/jsx-runtime";
7351
+ var headingIcons = {
7352
+ 1: HeadingOneIcon2,
7353
+ 2: HeadingTwoIcon2,
7354
+ 3: HeadingThreeIcon2,
7355
+ 4: HeadingFourIcon,
7356
+ 5: HeadingFiveIcon,
7357
+ 6: HeadingSixIcon
7358
+ };
7359
+ function useHeadingState(editor, level, disabled = false) {
7360
+ const headingInSchema = isNodeInSchema7("heading", editor);
7361
+ const isDisabled = isHeadingButtonDisabled(editor, level, disabled);
7362
+ const isActive = isHeadingActive(editor, level);
7363
+ const Icon = headingIcons[level];
7364
+ const shortcutKey = headingShortcutKeys[level];
7365
+ const formattedName = getFormattedHeadingName(level);
6821
7366
  return {
6822
- listInSchema,
6823
- listOption,
7367
+ headingInSchema,
7368
+ isDisabled,
6824
7369
  isActive,
6825
- shortcutKey
7370
+ Icon,
7371
+ shortcutKey,
7372
+ formattedName
6826
7373
  };
6827
7374
  }
6828
- var ListButton = ({
7375
+ var HeadingButton = ({
6829
7376
  editor: providedEditor,
6830
- type,
7377
+ level,
7378
+ text,
6831
7379
  hideWhenUnavailable = false,
6832
7380
  className = "",
7381
+ disabled,
6833
7382
  onClick,
6834
- text,
6835
7383
  children,
6836
7384
  ref,
6837
7385
  ...buttonProps
6838
7386
  }) => {
6839
- const { editor } = useTiptapEditor(providedEditor);
6840
- const { listInSchema, listOption, isActive, shortcutKey } = useListState(
6841
- editor,
6842
- type
6843
- );
6844
- const Icon = (listOption == null ? void 0 : listOption.icon) || ListIcon3;
6845
- const handleClick = useCallback29(
7387
+ const { editor } = useTiptapEditor19(providedEditor);
7388
+ const {
7389
+ headingInSchema,
7390
+ isDisabled,
7391
+ isActive,
7392
+ Icon,
7393
+ shortcutKey,
7394
+ formattedName
7395
+ } = useHeadingState(editor, level, disabled);
7396
+ const handleClick = React18.useCallback(
6846
7397
  (e) => {
6847
7398
  onClick == null ? void 0 : onClick(e);
6848
- if (!e.defaultPrevented && editor) {
6849
- toggleList(editor, type);
7399
+ if (!e.defaultPrevented && !isDisabled && editor) {
7400
+ toggleHeading(editor, level);
6850
7401
  }
6851
7402
  },
6852
- [onClick, editor, type]
7403
+ [onClick, isDisabled, editor, level]
6853
7404
  );
6854
- const show = useMemo20(() => {
6855
- return shouldShowListButton({
7405
+ const show = React18.useMemo(() => {
7406
+ return shouldShowHeadingButton({
6856
7407
  editor,
6857
- type,
7408
+ level,
6858
7409
  hideWhenUnavailable,
6859
- listInSchema
7410
+ headingInSchema
6860
7411
  });
6861
- }, [editor, type, hideWhenUnavailable, listInSchema]);
7412
+ }, [editor, level, hideWhenUnavailable, headingInSchema]);
6862
7413
  if (!show || !editor || !editor.isEditable) {
6863
7414
  return null;
6864
7415
  }
6865
- return /* @__PURE__ */ jsx27(
7416
+ return /* @__PURE__ */ jsx29(
6866
7417
  Button9,
6867
7418
  {
6868
7419
  type: "button",
6869
7420
  className: className.trim(),
7421
+ disabled: isDisabled,
6870
7422
  variant: "ghost",
6871
7423
  color: "default",
6872
7424
  "data-active-state": isActive ? "on" : "off",
7425
+ "data-disabled": isDisabled,
6873
7426
  tabIndex: -1,
6874
- "aria-label": (listOption == null ? void 0 : listOption.label) || type,
7427
+ "aria-label": formattedName,
6875
7428
  "aria-pressed": isActive,
6876
- tooltip: (listOption == null ? void 0 : listOption.label) || type,
7429
+ tooltip: formattedName,
6877
7430
  shortcutKeys: shortcutKey,
6878
7431
  onClick: handleClick,
6879
- startContent: /* @__PURE__ */ jsx27(Icon, {}),
7432
+ startContent: /* @__PURE__ */ jsx29(Icon, {}),
6880
7433
  ...buttonProps,
6881
7434
  ref,
6882
7435
  children: children || text
@@ -6884,161 +7437,196 @@ var ListButton = ({
6884
7437
  );
6885
7438
  };
6886
7439
 
6887
- // src/ui/list-dropdown-menu/index.tsx
6888
- import { jsx as jsx28, jsxs as jsxs20 } from "react/jsx-runtime";
6889
- function canToggleAnyList(editor, listTypes) {
7440
+ // src/ui/text-button/text-button.tsx
7441
+ import { Button as Button10 } from "@kopexa/button";
7442
+ import { Chip } from "@kopexa/chip";
7443
+ import { useTiptapEditor as useTiptapEditor21 } from "@kopexa/editor-utils";
7444
+ import { parseShortcutKeys } from "@kopexa/shared-utils";
7445
+ import { useCallback as useCallback34 } from "react";
7446
+
7447
+ // src/ui/text-button/use-text.ts
7448
+ import {
7449
+ findNodePosition as findNodePosition3,
7450
+ getSelectedBlockNodes,
7451
+ isNodeInSchema as isNodeInSchema8,
7452
+ isValidPosition as isValidPosition3,
7453
+ selectionWithinConvertibleTypes,
7454
+ useTiptapEditor as useTiptapEditor20
7455
+ } from "@kopexa/editor-utils";
7456
+ import { TypeIcon as TypeIcon2 } from "@kopexa/icons";
7457
+ import { useIsMobile as useIsMobile2 } from "@kopexa/use-is-mobile";
7458
+ import { NodeSelection as NodeSelection4, TextSelection as TextSelection4 } from "@tiptap/pm/state";
7459
+ import { useCallback as useCallback33, useEffect as useEffect28, useState as useState27 } from "react";
7460
+ import { useHotkeys as useHotkeys2 } from "react-hotkeys-hook";
7461
+ var TEXT_SHORTCUT_KEY = "mod+alt+0";
7462
+ function canToggleText(editor, turnInto = true) {
6890
7463
  if (!editor) return false;
6891
- return listTypes.some((type) => canToggleList(editor, type));
7464
+ if (!editor.schema.nodes.paragraph) return false;
7465
+ if (!turnInto) {
7466
+ return editor.can().setNode("paragraph");
7467
+ }
7468
+ if (!selectionWithinConvertibleTypes(editor, [
7469
+ "paragraph",
7470
+ "heading",
7471
+ "bulletList",
7472
+ "orderedList",
7473
+ "taskList",
7474
+ "blockquote",
7475
+ "codeBlock"
7476
+ ]))
7477
+ return false;
7478
+ return editor.can().setNode("paragraph") || editor.can().clearNodes();
6892
7479
  }
6893
- function isAnyListActive(editor, listTypes) {
7480
+ function isParagraphActive(editor) {
6894
7481
  if (!editor) return false;
6895
- return listTypes.some((type) => isListActive(editor, type));
7482
+ return editor.isActive("paragraph");
6896
7483
  }
6897
- function getFilteredListOptions(availableTypes) {
6898
- return listOptions.filter(
6899
- (option) => !option.type || availableTypes.includes(option.type)
6900
- );
6901
- }
6902
- function shouldShowListDropdown(params) {
6903
- const { editor, hideWhenUnavailable, listInSchema, canToggleAny } = params;
6904
- if (!listInSchema || !editor) {
7484
+ function toggleParagraph(editor) {
7485
+ var _a, _b, _c;
7486
+ if (!editor || !editor.isEditable) return false;
7487
+ if (!canToggleText(editor)) return false;
7488
+ try {
7489
+ const view = editor.view;
7490
+ let state = view.state;
7491
+ let tr = state.tr;
7492
+ const blocks = getSelectedBlockNodes(editor);
7493
+ const isPossibleToTurnInto = selectionWithinConvertibleTypes(editor, [
7494
+ "paragraph",
7495
+ "heading",
7496
+ "bulletList",
7497
+ "orderedList",
7498
+ "taskList",
7499
+ "blockquote",
7500
+ "codeBlock"
7501
+ ]) && blocks.length === 1;
7502
+ if ((state.selection.empty || state.selection instanceof TextSelection4) && isPossibleToTurnInto) {
7503
+ const pos = (_a = findNodePosition3({
7504
+ editor,
7505
+ node: state.selection.$anchor.node(1)
7506
+ })) == null ? void 0 : _a.pos;
7507
+ if (!isValidPosition3(pos)) return false;
7508
+ tr = tr.setSelection(NodeSelection4.create(state.doc, pos));
7509
+ view.dispatch(tr);
7510
+ state = view.state;
7511
+ }
7512
+ const selection = state.selection;
7513
+ let chain = editor.chain().focus();
7514
+ if (selection instanceof NodeSelection4) {
7515
+ const firstChild = (_b = selection.node.firstChild) == null ? void 0 : _b.firstChild;
7516
+ const lastChild = (_c = selection.node.lastChild) == null ? void 0 : _c.lastChild;
7517
+ const from = firstChild ? selection.from + firstChild.nodeSize : selection.from + 1;
7518
+ const to = lastChild ? selection.to - lastChild.nodeSize : selection.to - 1;
7519
+ const resolvedFrom = state.doc.resolve(from);
7520
+ const resolvedTo = state.doc.resolve(to);
7521
+ chain = chain.setTextSelection(TextSelection4.between(resolvedFrom, resolvedTo)).clearNodes();
7522
+ }
7523
+ if (!editor.isActive("paragraph")) {
7524
+ chain.setNode("paragraph").run();
7525
+ }
7526
+ editor.chain().focus().selectTextblockEnd().run();
7527
+ return true;
7528
+ } catch {
6905
7529
  return false;
6906
7530
  }
6907
- if (hideWhenUnavailable) {
6908
- if (isNodeSelection8(editor.state.selection) || !canToggleAny) {
6909
- return false;
6910
- }
7531
+ }
7532
+ function shouldShowButton6(props) {
7533
+ const { editor, hideWhenUnavailable } = props;
7534
+ if (!editor || !editor.isEditable) return false;
7535
+ if (!hideWhenUnavailable) {
7536
+ return true;
7537
+ }
7538
+ if (!isNodeInSchema8("paragraph", editor)) return false;
7539
+ if (!editor.isActive("code")) {
7540
+ return canToggleText(editor);
6911
7541
  }
6912
7542
  return true;
6913
7543
  }
6914
- function useListDropdownState(editor, availableTypes) {
6915
- const [isOpen, setIsOpen] = useState26(false);
6916
- const listInSchema = availableTypes.some(
6917
- (type) => isNodeInSchema8(type, editor)
6918
- );
6919
- const filteredLists = useMemo21(
6920
- () => getFilteredListOptions(availableTypes),
6921
- [availableTypes]
6922
- );
6923
- const canToggleAny = canToggleAnyList(editor, availableTypes);
6924
- const isAnyActive = isAnyListActive(editor, availableTypes);
6925
- const handleOpenChange = useCallback30(
6926
- (open, callback) => {
6927
- setIsOpen(open);
6928
- callback == null ? void 0 : callback(open);
6929
- },
6930
- []
6931
- );
6932
- return {
6933
- isOpen,
6934
- setIsOpen,
6935
- listInSchema,
6936
- filteredLists,
6937
- canToggleAny,
6938
- isAnyActive,
6939
- handleOpenChange
6940
- };
6941
- }
6942
- function useActiveListIcon(editor, filteredLists) {
6943
- return useCallback30(() => {
6944
- const activeOption = filteredLists.find(
6945
- (option) => isListActive(editor, option.type)
6946
- );
6947
- return activeOption ? /* @__PURE__ */ jsx28(activeOption.icon, {}) : /* @__PURE__ */ jsx28(ListIcon4, {});
6948
- }, [editor, filteredLists]);
6949
- }
6950
- function ListDropdownMenu({
6951
- editor: providedEditor,
6952
- types = ["bulletList", "orderedList", "taskList"],
6953
- hideWhenUnavailable = false,
6954
- onOpenChange,
6955
- ...props
6956
- }) {
6957
- const { editor } = useTiptapEditor(providedEditor);
7544
+ function useText(config) {
6958
7545
  const {
6959
- isOpen,
6960
- listInSchema,
6961
- filteredLists,
6962
- canToggleAny,
6963
- isAnyActive,
6964
- handleOpenChange
6965
- } = useListDropdownState(editor, types);
6966
- const getActiveIcon = useActiveListIcon(editor, filteredLists);
6967
- const show = useMemo21(() => {
6968
- return shouldShowListDropdown({
6969
- editor,
6970
- listTypes: types,
6971
- hideWhenUnavailable,
6972
- listInSchema,
6973
- canToggleAny
6974
- });
6975
- }, [editor, types, hideWhenUnavailable, listInSchema, canToggleAny]);
6976
- const handleOnOpenChange = useCallback30(
6977
- (open) => handleOpenChange(open, onOpenChange),
6978
- [handleOpenChange, onOpenChange]
6979
- );
6980
- if (!show || !editor || !editor.isEditable) {
6981
- return null;
6982
- }
6983
- return /* @__PURE__ */ jsxs20(DropdownMenu2.Root, { open: isOpen, onOpenChange: handleOnOpenChange, children: [
6984
- /* @__PURE__ */ jsx28(DropdownMenu2.Trigger, { asChild: true, children: /* @__PURE__ */ jsx28(
6985
- Button10,
6986
- {
6987
- type: "button",
6988
- variant: "ghost",
6989
- color: "default",
6990
- "data-active-state": isAnyActive ? "on" : "off",
6991
- tabIndex: -1,
6992
- "aria-label": "List options",
6993
- tooltip: "List",
6994
- endContent: /* @__PURE__ */ jsx28(ChevronDownIcon2, {}),
6995
- ...props,
6996
- children: getActiveIcon()
6997
- }
6998
- ) }),
6999
- /* @__PURE__ */ jsx28(DropdownMenu2.Content, { children: /* @__PURE__ */ jsx28(DropdownMenu2.Group, { children: filteredLists.map((option) => /* @__PURE__ */ jsx28(DropdownMenu2.Item, { asChild: true, children: /* @__PURE__ */ jsx28(
7000
- ListButton,
7001
- {
7002
- editor,
7003
- type: option.type,
7004
- text: option.label,
7005
- hideWhenUnavailable,
7006
- tooltip: "",
7007
- fullWidth: true,
7008
- spacing: "start"
7009
- }
7010
- ) }, option.type)) }) })
7011
- ] });
7546
+ editor: providedEditor,
7547
+ hideWhenUnavailable = false,
7548
+ onToggled
7549
+ } = config || {};
7550
+ const { editor } = useTiptapEditor20(providedEditor);
7551
+ const isMobile = useIsMobile2();
7552
+ const [isVisible, setIsVisible] = useState27(true);
7553
+ const canToggle3 = canToggleText(editor);
7554
+ const isActive = isParagraphActive(editor);
7555
+ useEffect28(() => {
7556
+ if (!editor) return;
7557
+ const handleSelectionUpdate = () => {
7558
+ setIsVisible(shouldShowButton6({ editor, hideWhenUnavailable }));
7559
+ };
7560
+ handleSelectionUpdate();
7561
+ editor.on("selectionUpdate", handleSelectionUpdate);
7562
+ return () => {
7563
+ editor.off("selectionUpdate", handleSelectionUpdate);
7564
+ };
7565
+ }, [editor, hideWhenUnavailable]);
7566
+ const handleToggle = useCallback33(() => {
7567
+ if (!editor) return false;
7568
+ const success = toggleParagraph(editor);
7569
+ if (success) {
7570
+ onToggled == null ? void 0 : onToggled();
7571
+ }
7572
+ return success;
7573
+ }, [editor, onToggled]);
7574
+ useHotkeys2(
7575
+ TEXT_SHORTCUT_KEY,
7576
+ (event) => {
7577
+ event.preventDefault();
7578
+ handleToggle();
7579
+ },
7580
+ {
7581
+ enabled: isVisible && canToggle3,
7582
+ enableOnContentEditable: !isMobile,
7583
+ enableOnFormTags: true
7584
+ }
7585
+ );
7586
+ return {
7587
+ isVisible,
7588
+ isActive,
7589
+ handleToggle,
7590
+ canToggle: canToggle3,
7591
+ label: "Text",
7592
+ shortcutKeys: TEXT_SHORTCUT_KEY,
7593
+ Icon: TypeIcon2
7594
+ };
7012
7595
  }
7013
7596
 
7014
- // src/ui/table-button/index.tsx
7015
- import { ToolbarButton as ToolbarButton6 } from "@kopexa/toolbar";
7016
- import { useCallback as useCallback31 } from "react";
7017
- import { Fragment as Fragment6, jsx as jsx29, jsxs as jsxs21 } from "react/jsx-runtime";
7018
- var TableButton = ({
7597
+ // src/ui/text-button/text-button.tsx
7598
+ import { Fragment as Fragment8, jsx as jsx30, jsxs as jsxs22 } from "react/jsx-runtime";
7599
+ function TextShortcutBadge({
7600
+ shortcutKeys = TEXT_SHORTCUT_KEY
7601
+ }) {
7602
+ return /* @__PURE__ */ jsx30(Chip, { children: parseShortcutKeys({ shortcutKeys }) });
7603
+ }
7604
+ var TextButton = ({
7019
7605
  editor: providedEditor,
7020
7606
  text,
7021
7607
  hideWhenUnavailable = false,
7022
7608
  onToggled,
7609
+ showShortcut = false,
7023
7610
  onClick,
7024
7611
  children,
7612
+ ref,
7025
7613
  ...buttonProps
7026
7614
  }) => {
7027
- const { editor } = useTiptapEditor(providedEditor);
7615
+ const { editor } = useTiptapEditor21(providedEditor);
7028
7616
  const {
7029
7617
  isVisible,
7030
7618
  canToggle: canToggle3,
7031
7619
  isActive,
7032
7620
  handleToggle,
7033
7621
  label,
7034
- // shortcutKeys,
7622
+ shortcutKeys,
7035
7623
  Icon
7036
- } = useTableBlock({
7624
+ } = useText({
7037
7625
  editor,
7038
7626
  hideWhenUnavailable,
7039
7627
  onToggled
7040
7628
  });
7041
- const handleClick = useCallback31(
7629
+ const handleClick = useCallback34(
7042
7630
  (event) => {
7043
7631
  onClick == null ? void 0 : onClick(event);
7044
7632
  if (event.defaultPrevented) return;
@@ -7049,201 +7637,343 @@ var TableButton = ({
7049
7637
  if (!isVisible) {
7050
7638
  return null;
7051
7639
  }
7052
- return /* @__PURE__ */ jsx29(
7053
- ToolbarButton6,
7640
+ return /* @__PURE__ */ jsx30(
7641
+ Button10,
7054
7642
  {
7055
7643
  type: "button",
7056
7644
  variant: "ghost",
7057
7645
  color: "default",
7058
7646
  "data-active-state": isActive ? "on" : "off",
7647
+ tabIndex: -1,
7059
7648
  disabled: !canToggle3,
7060
7649
  "data-disabled": !canToggle3,
7061
- tabIndex: -1,
7062
7650
  "aria-label": label,
7063
7651
  "aria-pressed": isActive,
7064
- title: label,
7652
+ tooltip: "Text",
7653
+ shortcutKeys,
7065
7654
  onClick: handleClick,
7066
- isIconOnly: !text && !children,
7655
+ startContent: /* @__PURE__ */ jsx30(Icon, {}),
7067
7656
  ...buttonProps,
7068
- children: children || /* @__PURE__ */ jsxs21(Fragment6, { children: [
7069
- /* @__PURE__ */ jsx29(Icon, {}),
7070
- text && /* @__PURE__ */ jsx29("span", { children: text })
7657
+ ref,
7658
+ children: children != null ? children : /* @__PURE__ */ jsxs22(Fragment8, { children: [
7659
+ text,
7660
+ showShortcut && /* @__PURE__ */ jsx30(TextShortcutBadge, { shortcutKeys })
7071
7661
  ] })
7072
7662
  }
7073
7663
  );
7074
7664
  };
7075
7665
 
7076
- // src/ui/text-align-button/text-align-button.tsx
7077
- import { IconButton as IconButton9 } from "@kopexa/button";
7078
- import { useCallback as useCallback33 } from "react";
7079
-
7080
- // src/ui/text-align-button/use-text-align.ts
7081
- import { isExtensionAvailable as isExtensionAvailable2, isNodeTypeSelected as isNodeTypeSelected6 } from "@kopexa/editor-utils";
7082
- import {
7083
- AlignCenterIcon,
7084
- AlignJustifyIcon,
7085
- AlignLeftIcon,
7086
- AlignRightIcon
7087
- } from "@kopexa/icons";
7088
- import { useCallback as useCallback32, useEffect as useEffect27, useState as useState27 } from "react";
7089
- var TEXT_ALIGN_SHORTCUT_KEYS = {
7090
- left: "mod+shift+l",
7091
- center: "mod+shift+e",
7092
- right: "mod+shift+r",
7093
- justify: "mod+shift+j"
7094
- };
7095
- var textAlignIcons = {
7096
- left: AlignLeftIcon,
7097
- center: AlignCenterIcon,
7098
- right: AlignRightIcon,
7099
- justify: AlignJustifyIcon
7100
- };
7101
- var textAlignLabels = {
7102
- left: "Align left",
7103
- center: "Align center",
7104
- right: "Align right",
7105
- justify: "Align justify"
7106
- };
7107
- function canSetTextAlign(editor, align) {
7666
+ // src/ui/turn-into-dropdown/use-turn-into-dropdown.ts
7667
+ import { useTiptapEditor as useTiptapEditor22 } from "@kopexa/editor-utils";
7668
+ import { ChevronDownIcon as ChevronDownIcon2 } from "@kopexa/icons";
7669
+ import { NodeSelection as NodeSelection5 } from "@tiptap/pm/state";
7670
+ import { useCallback as useCallback35, useEffect as useEffect29, useState as useState28 } from "react";
7671
+ var TURN_INTO_BLOCKS = [
7672
+ "paragraph",
7673
+ "heading",
7674
+ "bulletList",
7675
+ "orderedList",
7676
+ "taskList",
7677
+ "blockquote",
7678
+ "codeBlock"
7679
+ ];
7680
+ var blockTypeOptions = [
7681
+ {
7682
+ type: "paragraph",
7683
+ label: "Text",
7684
+ isActive: (editor) => editor.isActive("paragraph") && !editor.isActive("heading") && !editor.isActive("bulletList") && !editor.isActive("orderedList") && !editor.isActive("taskList") && !editor.isActive("blockquote") && !editor.isActive("codeBlock")
7685
+ },
7686
+ {
7687
+ type: "heading",
7688
+ label: "Heading 1",
7689
+ level: 1,
7690
+ isActive: (editor) => editor.isActive("heading", { level: 1 })
7691
+ },
7692
+ {
7693
+ type: "heading",
7694
+ label: "Heading 2",
7695
+ level: 2,
7696
+ isActive: (editor) => editor.isActive("heading", { level: 2 })
7697
+ },
7698
+ {
7699
+ type: "heading",
7700
+ label: "Heading 3",
7701
+ level: 3,
7702
+ isActive: (editor) => editor.isActive("heading", { level: 3 })
7703
+ },
7704
+ {
7705
+ type: "heading",
7706
+ label: "Heading 4",
7707
+ level: 4,
7708
+ isActive: (editor) => editor.isActive("heading", { level: 4 })
7709
+ },
7710
+ {
7711
+ type: "bulletList",
7712
+ label: "Bulleted list",
7713
+ isActive: (editor) => editor.isActive("bulletList")
7714
+ },
7715
+ {
7716
+ type: "orderedList",
7717
+ label: "Numbered list",
7718
+ isActive: (editor) => editor.isActive("orderedList")
7719
+ },
7720
+ {
7721
+ type: "taskList",
7722
+ label: "To-do list",
7723
+ isActive: (editor) => editor.isActive("taskList")
7724
+ },
7725
+ {
7726
+ type: "blockquote",
7727
+ label: "Blockquote",
7728
+ isActive: (editor) => editor.isActive("blockquote")
7729
+ },
7730
+ {
7731
+ type: "codeBlock",
7732
+ label: "Code block",
7733
+ isActive: (editor) => editor.isActive("codeBlock")
7734
+ }
7735
+ ];
7736
+ function canTurnInto(editor, allowedBlockTypes) {
7108
7737
  if (!editor || !editor.isEditable) return false;
7109
- if (!isExtensionAvailable2(editor, "textAlign") || isNodeTypeSelected6(editor, ["image"]))
7110
- return false;
7111
- return editor.can().setTextAlign(align);
7112
- }
7113
- function hasSetTextAlign(commands) {
7114
- return "setTextAlign" in commands;
7738
+ const blockTypes = allowedBlockTypes || TURN_INTO_BLOCKS;
7739
+ const { selection } = editor.state;
7740
+ if (selection instanceof NodeSelection5) {
7741
+ const nodeType2 = selection.node.type.name;
7742
+ return blockTypes.includes(nodeType2);
7743
+ }
7744
+ const { $anchor } = selection;
7745
+ const nodeType = $anchor.parent.type.name;
7746
+ return blockTypes.includes(nodeType);
7115
7747
  }
7116
- function isTextAlignActive(editor, align) {
7117
- if (!editor || !editor.isEditable) return false;
7118
- return editor.isActive({ textAlign: align });
7748
+ function getFilteredBlockTypeOptions(blockTypes) {
7749
+ if (!blockTypes) return blockTypeOptions;
7750
+ return blockTypeOptions.filter((option) => {
7751
+ return blockTypes.includes(option.type);
7752
+ });
7119
7753
  }
7120
- function setTextAlign(editor, align) {
7121
- if (!editor || !editor.isEditable) return false;
7122
- if (!canSetTextAlign(editor, align)) return false;
7123
- const chain = editor.chain().focus();
7124
- if (hasSetTextAlign(chain)) {
7125
- return chain.setTextAlign(align).run();
7126
- }
7127
- return false;
7754
+ function getActiveBlockType(editor, blockTypes) {
7755
+ if (!editor) return getFilteredBlockTypeOptions(blockTypes)[0];
7756
+ const filteredOptions = getFilteredBlockTypeOptions(blockTypes);
7757
+ const activeOption = filteredOptions.find(
7758
+ (option) => option.isActive(editor)
7759
+ );
7760
+ return activeOption || filteredOptions[0];
7128
7761
  }
7129
- function shouldShowButton5(props) {
7130
- const { editor, hideWhenUnavailable, align } = props;
7762
+ function shouldShowTurnInto(params) {
7763
+ const { editor, hideWhenUnavailable, blockTypes } = params;
7131
7764
  if (!editor || !editor.isEditable) return false;
7132
- if (!isExtensionAvailable2(editor, "textAlign")) return false;
7133
7765
  if (hideWhenUnavailable && !editor.isActive("code")) {
7134
- return canSetTextAlign(editor, align);
7766
+ return canTurnInto(editor, blockTypes);
7135
7767
  }
7136
7768
  return true;
7137
7769
  }
7138
- function useTextAlign(config) {
7770
+ function useTurnIntoDropdown(config) {
7139
7771
  const {
7140
7772
  editor: providedEditor,
7141
- align,
7142
7773
  hideWhenUnavailable = false,
7143
- onAligned
7144
- } = config;
7145
- const { editor } = useTiptapEditor(providedEditor);
7146
- const [isVisible, setIsVisible] = useState27(true);
7147
- const canAlign = canSetTextAlign(editor, align);
7148
- const isActive = isTextAlignActive(editor, align);
7149
- useEffect27(() => {
7774
+ blockTypes,
7775
+ onOpenChange
7776
+ } = config || {};
7777
+ const { editor } = useTiptapEditor22(providedEditor);
7778
+ const [isOpen, setIsOpen] = useState28(false);
7779
+ const [isVisible, setIsVisible] = useState28(true);
7780
+ const canToggle3 = canTurnInto(editor, blockTypes);
7781
+ const activeBlockType = getActiveBlockType(editor, blockTypes);
7782
+ const handleOpenChange = useCallback35(
7783
+ (open) => {
7784
+ if (!editor || !canToggle3) return;
7785
+ setIsOpen(open);
7786
+ onOpenChange == null ? void 0 : onOpenChange(open);
7787
+ },
7788
+ [canToggle3, editor, onOpenChange]
7789
+ );
7790
+ useEffect29(() => {
7150
7791
  if (!editor) return;
7151
7792
  const handleSelectionUpdate = () => {
7152
- setIsVisible(shouldShowButton5({ editor, align, hideWhenUnavailable }));
7793
+ setIsVisible(
7794
+ shouldShowTurnInto({
7795
+ editor,
7796
+ hideWhenUnavailable,
7797
+ blockTypes
7798
+ })
7799
+ );
7153
7800
  };
7154
7801
  handleSelectionUpdate();
7155
7802
  editor.on("selectionUpdate", handleSelectionUpdate);
7156
7803
  return () => {
7157
7804
  editor.off("selectionUpdate", handleSelectionUpdate);
7158
7805
  };
7159
- }, [editor, hideWhenUnavailable, align]);
7160
- const handleTextAlign = useCallback32(() => {
7161
- if (!editor) return false;
7162
- const success = setTextAlign(editor, align);
7163
- if (success) {
7164
- onAligned == null ? void 0 : onAligned();
7165
- }
7166
- return success;
7167
- }, [editor, align, onAligned]);
7806
+ }, [editor, hideWhenUnavailable, blockTypes]);
7168
7807
  return {
7169
7808
  isVisible,
7170
- isActive,
7171
- handleTextAlign,
7172
- canAlign,
7173
- label: textAlignLabels[align],
7174
- shortcutKeys: TEXT_ALIGN_SHORTCUT_KEYS[align],
7175
- Icon: textAlignIcons[align]
7809
+ canToggle: canToggle3,
7810
+ isOpen,
7811
+ setIsOpen,
7812
+ activeBlockType,
7813
+ handleOpenChange,
7814
+ filteredOptions: getFilteredBlockTypeOptions(blockTypes),
7815
+ label: `Turn into (current: ${(activeBlockType == null ? void 0 : activeBlockType.label) || "Text"})`,
7816
+ Icon: ChevronDownIcon2
7176
7817
  };
7177
7818
  }
7178
7819
 
7179
- // src/ui/text-align-button/text-align-button.tsx
7180
- import { jsx as jsx30 } from "react/jsx-runtime";
7181
- var TextAlignButton = ({
7182
- editor: providedEditor,
7183
- align,
7184
- text,
7185
- hideWhenUnavailable = false,
7186
- onAligned,
7187
- showShortcut = false,
7188
- onClick,
7189
- children,
7190
- ...buttonProps
7191
- }) => {
7192
- const { editor } = useTiptapEditor(providedEditor);
7193
- const {
7194
- isVisible,
7195
- handleTextAlign,
7196
- label,
7197
- canAlign,
7198
- isActive,
7199
- Icon,
7200
- shortcutKeys
7201
- } = useTextAlign({
7202
- editor,
7203
- align,
7204
- hideWhenUnavailable,
7205
- onAligned
7206
- });
7207
- const handleClick = useCallback33(
7208
- (e) => {
7209
- onClick == null ? void 0 : onClick(e);
7210
- if (e.defaultPrevented) return;
7211
- handleTextAlign();
7212
- },
7213
- [handleTextAlign, onClick]
7214
- );
7215
- if (!isVisible) {
7216
- return null;
7217
- }
7218
- return /* @__PURE__ */ jsx30(
7219
- IconButton9,
7220
- {
7221
- type: "button",
7222
- disabled: canAlign,
7223
- variant: "ghost",
7224
- color: "default",
7225
- "data-active-state": isActive ? "on" : "off",
7226
- "data-disabled": canAlign,
7227
- tabIndex: -1,
7228
- "aria-label": label,
7229
- "aria-pressed": isActive,
7230
- tooltip: label,
7231
- shortcutKeys,
7232
- onClick: handleClick,
7233
- ...buttonProps,
7234
- children: /* @__PURE__ */ jsx30(Icon, {})
7820
+ // src/ui/turn-into-dropdown/turn-into-dropdown.tsx
7821
+ import { jsx as jsx31, jsxs as jsxs23 } from "react/jsx-runtime";
7822
+ var TurnIntoDropdownContent = ({ blockTypes }) => {
7823
+ const filteredOptions = getFilteredBlockTypeOptions(blockTypes);
7824
+ return /* @__PURE__ */ jsx31(DropdownMenu2.Group, { children: filteredOptions.map(
7825
+ (option, index) => {
7826
+ var _a;
7827
+ return renderBlockTypeButton(
7828
+ option,
7829
+ `${option.type}-${(_a = option.level) != null ? _a : index}`
7830
+ );
7235
7831
  }
7236
- );
7832
+ ) });
7237
7833
  };
7834
+ function renderBlockTypeButton(option, key) {
7835
+ switch (option.type) {
7836
+ case "paragraph":
7837
+ return /* @__PURE__ */ jsx31(DropdownMenu2.Item, { asChild: true, children: /* @__PURE__ */ jsx31(
7838
+ TextButton,
7839
+ {
7840
+ fullWidth: true,
7841
+ spacing: "start",
7842
+ tooltip: "",
7843
+ text: option.label
7844
+ }
7845
+ ) }, key);
7846
+ case "heading":
7847
+ if (!option.level) {
7848
+ return null;
7849
+ }
7850
+ return /* @__PURE__ */ jsx31(DropdownMenu2.Item, { asChild: true, children: /* @__PURE__ */ jsx31(
7851
+ HeadingButton,
7852
+ {
7853
+ level: option.level || 1,
7854
+ fullWidth: true,
7855
+ spacing: "start",
7856
+ tooltip: "",
7857
+ text: option.label
7858
+ }
7859
+ ) }, key);
7860
+ case "bulletList":
7861
+ return /* @__PURE__ */ jsx31(DropdownMenu2.Item, { asChild: true, children: /* @__PURE__ */ jsx31(
7862
+ ListButton,
7863
+ {
7864
+ type: "bulletList",
7865
+ fullWidth: true,
7866
+ spacing: "start",
7867
+ tooltip: "",
7868
+ text: option.label
7869
+ }
7870
+ ) }, key);
7871
+ case "orderedList":
7872
+ return /* @__PURE__ */ jsx31(DropdownMenu2.Item, { asChild: true, children: /* @__PURE__ */ jsx31(
7873
+ ListButton,
7874
+ {
7875
+ type: "orderedList",
7876
+ fullWidth: true,
7877
+ spacing: "start",
7878
+ tooltip: "",
7879
+ text: option.label
7880
+ }
7881
+ ) }, key);
7882
+ case "taskList":
7883
+ return /* @__PURE__ */ jsx31(DropdownMenu2.Item, { asChild: true, children: /* @__PURE__ */ jsx31(
7884
+ ListButton,
7885
+ {
7886
+ type: "taskList",
7887
+ fullWidth: true,
7888
+ spacing: "start",
7889
+ tooltip: "",
7890
+ text: option.label
7891
+ }
7892
+ ) }, key);
7893
+ case "blockquote":
7894
+ return /* @__PURE__ */ jsx31(DropdownMenu2.Item, { asChild: true, children: /* @__PURE__ */ jsx31(
7895
+ BlockquoteButton,
7896
+ {
7897
+ className: "w-full justify-start",
7898
+ title: "",
7899
+ tooltip: "",
7900
+ text: option.label
7901
+ }
7902
+ ) }, key);
7903
+ case "codeBlock":
7904
+ return /* @__PURE__ */ jsx31(DropdownMenu2.Item, { asChild: true, children: /* @__PURE__ */ jsx31(
7905
+ CodeBlockButton,
7906
+ {
7907
+ className: "w-full justify-start",
7908
+ title: "",
7909
+ tooltip: "",
7910
+ text: option.label
7911
+ }
7912
+ ) }, key);
7913
+ default:
7914
+ return null;
7915
+ }
7916
+ }
7917
+ var TurnIntoDropdown = forwardRef(
7918
+ ({
7919
+ editor: providedEditor,
7920
+ hideWhenUnavailable = false,
7921
+ blockTypes,
7922
+ onOpenChange,
7923
+ children,
7924
+ ...buttonProps
7925
+ }, ref) => {
7926
+ const { editor } = useTiptapEditor23(providedEditor);
7927
+ const {
7928
+ isVisible,
7929
+ canToggle: canToggle3,
7930
+ isOpen,
7931
+ activeBlockType,
7932
+ handleOpenChange,
7933
+ label,
7934
+ Icon
7935
+ } = useTurnIntoDropdown({
7936
+ editor,
7937
+ hideWhenUnavailable,
7938
+ blockTypes,
7939
+ onOpenChange
7940
+ });
7941
+ if (!isVisible) {
7942
+ return null;
7943
+ }
7944
+ return /* @__PURE__ */ jsxs23(DropdownMenu2.Root, { open: isOpen, onOpenChange: handleOpenChange, children: [
7945
+ /* @__PURE__ */ jsx31(DropdownMenu2.Trigger, { asChild: true, children: /* @__PURE__ */ jsx31(
7946
+ Button11,
7947
+ {
7948
+ type: "button",
7949
+ variant: "ghost",
7950
+ color: "default",
7951
+ disabled: !canToggle3,
7952
+ "data-disabled": !canToggle3,
7953
+ tabIndex: -1,
7954
+ "aria-label": label,
7955
+ tooltip: "Turn into",
7956
+ endContent: /* @__PURE__ */ jsx31(Icon, {}),
7957
+ ...buttonProps,
7958
+ ref,
7959
+ children: children != null ? children : /* @__PURE__ */ jsx31("span", { children: (activeBlockType == null ? void 0 : activeBlockType.label) || "Text" })
7960
+ }
7961
+ ) }),
7962
+ /* @__PURE__ */ jsx31(DropdownMenu2.Content, { align: "start", children: /* @__PURE__ */ jsx31(TurnIntoDropdownContent, { blockTypes }) })
7963
+ ] });
7964
+ }
7965
+ );
7966
+ TurnIntoDropdown.displayName = "TurnIntoDropdown";
7238
7967
 
7239
7968
  // src/ui/undo-redo-button/undo-redo-button.tsx
7969
+ import { useTiptapEditor as useTiptapEditor25 } from "@kopexa/editor-utils";
7240
7970
  import { ToolbarButton as ToolbarButton7 } from "@kopexa/toolbar";
7241
- import { useCallback as useCallback35 } from "react";
7971
+ import { useCallback as useCallback37 } from "react";
7242
7972
 
7243
7973
  // src/ui/undo-redo-button/use-undo-redo.ts
7244
- import { isNodeTypeSelected as isNodeTypeSelected7 } from "@kopexa/editor-utils";
7974
+ import { isNodeTypeSelected as isNodeTypeSelected7, useTiptapEditor as useTiptapEditor24 } from "@kopexa/editor-utils";
7245
7975
  import { RedoIcon, UndoIcon } from "@kopexa/icons";
7246
- import { useCallback as useCallback34, useEffect as useEffect28, useState as useState28 } from "react";
7976
+ import { useCallback as useCallback36, useEffect as useEffect30, useState as useState29 } from "react";
7247
7977
  var UNDO_REDO_SHORTCUT_KEYS = {
7248
7978
  undo: "mod+z",
7249
7979
  redo: "mod+shift+z"
@@ -7267,7 +7997,7 @@ function executeUndoRedoAction(editor, action) {
7267
7997
  const chain = editor.chain().focus();
7268
7998
  return action === "undo" ? chain.undo().run() : chain.redo().run();
7269
7999
  }
7270
- function shouldShowButton6(props) {
8000
+ function shouldShowButton7(props) {
7271
8001
  const { editor, hideWhenUnavailable, action } = props;
7272
8002
  if (!editor || !editor.isEditable) return false;
7273
8003
  if (hideWhenUnavailable && !editor.isActive("code")) {
@@ -7282,13 +8012,13 @@ function useUndoRedo(config) {
7282
8012
  hideWhenUnavailable = false,
7283
8013
  onExecuted
7284
8014
  } = config;
7285
- const { editor } = useTiptapEditor(providedEditor);
7286
- const [isVisible, setIsVisible] = useState28(true);
8015
+ const { editor } = useTiptapEditor24(providedEditor);
8016
+ const [isVisible, setIsVisible] = useState29(true);
7287
8017
  const canExecute = canExecuteUndoRedoAction(editor, action);
7288
- useEffect28(() => {
8018
+ useEffect30(() => {
7289
8019
  if (!editor) return;
7290
8020
  const handleUpdate = () => {
7291
- setIsVisible(shouldShowButton6({ editor, hideWhenUnavailable, action }));
8021
+ setIsVisible(shouldShowButton7({ editor, hideWhenUnavailable, action }));
7292
8022
  };
7293
8023
  handleUpdate();
7294
8024
  editor.on("transaction", handleUpdate);
@@ -7296,7 +8026,7 @@ function useUndoRedo(config) {
7296
8026
  editor.off("transaction", handleUpdate);
7297
8027
  };
7298
8028
  }, [editor, hideWhenUnavailable, action]);
7299
- const handleAction = useCallback34(() => {
8029
+ const handleAction = useCallback36(() => {
7300
8030
  if (!editor) return false;
7301
8031
  const success = executeUndoRedoAction(editor, action);
7302
8032
  if (success) {
@@ -7315,7 +8045,7 @@ function useUndoRedo(config) {
7315
8045
  }
7316
8046
 
7317
8047
  // src/ui/undo-redo-button/undo-redo-button.tsx
7318
- import { jsx as jsx31 } from "react/jsx-runtime";
8048
+ import { jsx as jsx32 } from "react/jsx-runtime";
7319
8049
  var UndoRedoButton = ({
7320
8050
  editor: providedEditor,
7321
8051
  action,
@@ -7327,14 +8057,14 @@ var UndoRedoButton = ({
7327
8057
  children,
7328
8058
  ...buttonProps
7329
8059
  }) => {
7330
- const { editor } = useTiptapEditor(providedEditor);
8060
+ const { editor } = useTiptapEditor25(providedEditor);
7331
8061
  const { isVisible, handleAction, label, canExecute, Icon, shortcutKeys } = useUndoRedo({
7332
8062
  editor,
7333
8063
  action,
7334
8064
  hideWhenUnavailable,
7335
8065
  onExecuted
7336
8066
  });
7337
- const handleClick = useCallback35(
8067
+ const handleClick = useCallback37(
7338
8068
  (event) => {
7339
8069
  onClick == null ? void 0 : onClick(event);
7340
8070
  if (event.defaultPrevented) return;
@@ -7345,7 +8075,7 @@ var UndoRedoButton = ({
7345
8075
  if (!isVisible) {
7346
8076
  return null;
7347
8077
  }
7348
- return /* @__PURE__ */ jsx31(
8078
+ return /* @__PURE__ */ jsx32(
7349
8079
  ToolbarButton7,
7350
8080
  {
7351
8081
  type: "button",
@@ -7360,20 +8090,20 @@ var UndoRedoButton = ({
7360
8090
  onClick: handleClick,
7361
8091
  isIconOnly: true,
7362
8092
  ...buttonProps,
7363
- children: /* @__PURE__ */ jsx31(Icon, {})
8093
+ children: /* @__PURE__ */ jsx32(Icon, {})
7364
8094
  }
7365
8095
  );
7366
8096
  };
7367
8097
 
7368
8098
  // src/presets/basic/editor-header.tsx
7369
- import { Fragment as Fragment7, jsx as jsx32, jsxs as jsxs22 } from "react/jsx-runtime";
8099
+ import { Fragment as Fragment9, jsx as jsx33, jsxs as jsxs24 } from "react/jsx-runtime";
7370
8100
  var EditorHeader = ({
7371
8101
  editor: providedEditor,
7372
8102
  variant
7373
8103
  }) => {
7374
8104
  var _a, _b;
7375
- const { editor } = useTiptapEditor(providedEditor);
7376
- const isMobile = useIsMobile2();
8105
+ const { editor } = useTiptapEditor26(providedEditor);
8106
+ const isMobile = useIsMobile3();
7377
8107
  const windowSize = useWindowSize();
7378
8108
  const { styles } = useEditorUIContext();
7379
8109
  const toolbarRef = useRef11(null);
@@ -7385,7 +8115,7 @@ var EditorHeader = ({
7385
8115
  return null;
7386
8116
  }
7387
8117
  const ToolbarContent = variant === "comment" ? CommentToolbarContent : variant === "field" ? FieldToolbarContent : MainToolbarContent;
7388
- return /* @__PURE__ */ jsx32("div", { className: styles.toolbarContainer(), "data-slot": "editor-toolbar", children: /* @__PURE__ */ jsx32(
8118
+ return /* @__PURE__ */ jsx33("div", { className: styles.toolbarContainer(), "data-slot": "editor-toolbar", children: /* @__PURE__ */ jsx33(
7389
8119
  Toolbar2,
7390
8120
  {
7391
8121
  sticky: true,
@@ -7395,63 +8125,58 @@ var EditorHeader = ({
7395
8125
  bottom: `calc(100% - ${windowSize.height - bodyRect.y}px)`
7396
8126
  } : {},
7397
8127
  className: styles.toolbar(),
7398
- children: /* @__PURE__ */ jsx32(ToolbarContent, {})
8128
+ children: /* @__PURE__ */ jsx33(ToolbarContent, {})
7399
8129
  }
7400
8130
  ) });
7401
8131
  };
7402
- var MainToolbarContent = () => /* @__PURE__ */ jsxs22(Fragment7, { children: [
7403
- /* @__PURE__ */ jsxs22(ToolbarGroup2, { children: [
7404
- /* @__PURE__ */ jsx32(UndoRedoButton, { action: "undo" }),
7405
- /* @__PURE__ */ jsx32(UndoRedoButton, { action: "redo" })
8132
+ var MainToolbarContent = () => /* @__PURE__ */ jsxs24(Fragment9, { children: [
8133
+ /* @__PURE__ */ jsxs24(ToolbarGroup2, { children: [
8134
+ /* @__PURE__ */ jsx33(UndoRedoButton, { action: "undo" }),
8135
+ /* @__PURE__ */ jsx33(UndoRedoButton, { action: "redo" })
7406
8136
  ] }),
7407
- /* @__PURE__ */ jsx32(ToolbarSeparator3, {}),
7408
- /* @__PURE__ */ jsxs22(ToolbarGroup2, { children: [
7409
- /* @__PURE__ */ jsx32(HeadingDropdownMenu, { levels: [1, 2, 3, 4] }),
7410
- /* @__PURE__ */ jsx32(ListDropdownMenu, { types: ["bulletList", "orderedList", "taskList"] }),
7411
- /* @__PURE__ */ jsx32(BlockquoteButton, {}),
7412
- /* @__PURE__ */ jsx32(CodeBlockButton, {})
8137
+ /* @__PURE__ */ jsx33(ToolbarSeparator3, {}),
8138
+ /* @__PURE__ */ jsx33(ToolbarGroup2, { children: /* @__PURE__ */ jsx33(TurnIntoDropdown, {}) }),
8139
+ /* @__PURE__ */ jsx33(ToolbarSeparator3, {}),
8140
+ /* @__PURE__ */ jsxs24(ToolbarGroup2, { children: [
8141
+ /* @__PURE__ */ jsx33(MarkButton, { type: "bold" }),
8142
+ /* @__PURE__ */ jsx33(MarkButton, { type: "italic" }),
8143
+ /* @__PURE__ */ jsx33(MarkButton, { type: "strike" }),
8144
+ /* @__PURE__ */ jsx33(MarkButton, { type: "code" }),
8145
+ /* @__PURE__ */ jsx33(MarkButton, { type: "underline" }),
8146
+ /* @__PURE__ */ jsx33(ColorHighlightPopover, {}),
8147
+ /* @__PURE__ */ jsx33(LinkPopover, {})
7413
8148
  ] }),
7414
- /* @__PURE__ */ jsx32(ToolbarSeparator3, {}),
7415
- /* @__PURE__ */ jsxs22(ToolbarGroup2, { children: [
7416
- /* @__PURE__ */ jsx32(MarkButton, { type: "bold" }),
7417
- /* @__PURE__ */ jsx32(MarkButton, { type: "italic" }),
7418
- /* @__PURE__ */ jsx32(MarkButton, { type: "strike" }),
7419
- /* @__PURE__ */ jsx32(MarkButton, { type: "code" }),
7420
- /* @__PURE__ */ jsx32(MarkButton, { type: "underline" }),
7421
- /* @__PURE__ */ jsx32(ColorHighlightPopover, {}),
7422
- /* @__PURE__ */ jsx32(LinkPopover, {})
7423
- ] }),
7424
- /* @__PURE__ */ jsx32(ToolbarSeparator3, {}),
7425
- /* @__PURE__ */ jsx32(ToolbarGroup2, { children: /* @__PURE__ */ jsx32(TableButton, {}) }),
7426
- /* @__PURE__ */ jsx32(MoreOptions, { hideWhenUnavailable: true })
8149
+ /* @__PURE__ */ jsx33(ToolbarSeparator3, {}),
8150
+ /* @__PURE__ */ jsx33(ToolbarGroup2, { children: /* @__PURE__ */ jsx33(TableButton, {}) }),
8151
+ /* @__PURE__ */ jsx33(MoreOptions, { hideWhenUnavailable: true })
7427
8152
  ] });
7428
- var CommentToolbarContent = () => /* @__PURE__ */ jsxs22(Fragment7, { children: [
7429
- /* @__PURE__ */ jsxs22(ToolbarGroup2, { children: [
7430
- /* @__PURE__ */ jsx32(MarkButton, { type: "bold" }),
7431
- /* @__PURE__ */ jsx32(MarkButton, { type: "italic" }),
7432
- /* @__PURE__ */ jsx32(MarkButton, { type: "strike" }),
7433
- /* @__PURE__ */ jsx32(MarkButton, { type: "code" })
8153
+ var CommentToolbarContent = () => /* @__PURE__ */ jsxs24(Fragment9, { children: [
8154
+ /* @__PURE__ */ jsxs24(ToolbarGroup2, { children: [
8155
+ /* @__PURE__ */ jsx33(MarkButton, { type: "bold" }),
8156
+ /* @__PURE__ */ jsx33(MarkButton, { type: "italic" }),
8157
+ /* @__PURE__ */ jsx33(MarkButton, { type: "strike" }),
8158
+ /* @__PURE__ */ jsx33(MarkButton, { type: "code" })
7434
8159
  ] }),
7435
- /* @__PURE__ */ jsx32(ToolbarSeparator3, {}),
7436
- /* @__PURE__ */ jsxs22(ToolbarGroup2, { children: [
7437
- /* @__PURE__ */ jsx32(LinkPopover, {}),
7438
- /* @__PURE__ */ jsx32(ListDropdownMenu, { types: ["bulletList", "orderedList"] })
8160
+ /* @__PURE__ */ jsx33(ToolbarSeparator3, {}),
8161
+ /* @__PURE__ */ jsxs24(ToolbarGroup2, { children: [
8162
+ /* @__PURE__ */ jsx33(LinkPopover, {}),
8163
+ /* @__PURE__ */ jsx33(ListDropdownMenu, { types: ["bulletList", "orderedList"] })
7439
8164
  ] })
7440
8165
  ] });
7441
- var FieldToolbarContent = () => /* @__PURE__ */ jsxs22(ToolbarGroup2, { children: [
7442
- /* @__PURE__ */ jsx32(MarkButton, { type: "bold" }),
7443
- /* @__PURE__ */ jsx32(MarkButton, { type: "italic" }),
7444
- /* @__PURE__ */ jsx32(MarkButton, { type: "strike" }),
7445
- /* @__PURE__ */ jsx32(LinkPopover, {})
8166
+ var FieldToolbarContent = () => /* @__PURE__ */ jsxs24(ToolbarGroup2, { children: [
8167
+ /* @__PURE__ */ jsx33(MarkButton, { type: "bold" }),
8168
+ /* @__PURE__ */ jsx33(MarkButton, { type: "italic" }),
8169
+ /* @__PURE__ */ jsx33(MarkButton, { type: "strike" }),
8170
+ /* @__PURE__ */ jsx33(LinkPopover, {})
7446
8171
  ] });
7447
8172
  function MoreOptions({
7448
8173
  editor: providedEditor,
7449
8174
  hideWhenUnavailable = false,
7450
8175
  ...props
7451
8176
  }) {
7452
- const { editor } = useTiptapEditor(providedEditor);
7453
- const [show, setShow] = useState29(false);
7454
- useEffect29(() => {
8177
+ const { editor } = useTiptapEditor26(providedEditor);
8178
+ const [show, setShow] = useState30(false);
8179
+ useEffect31(() => {
7455
8180
  if (!editor) return;
7456
8181
  const handleSelectionUpdate = () => {
7457
8182
  setShow(
@@ -7470,13 +8195,13 @@ function MoreOptions({
7470
8195
  if (!show || !editor || !editor.isEditable) {
7471
8196
  return null;
7472
8197
  }
7473
- return /* @__PURE__ */ jsxs22(Fragment7, { children: [
7474
- /* @__PURE__ */ jsx32(ToolbarSeparator3, {}),
7475
- /* @__PURE__ */ jsx32(ToolbarGroup2, { children: /* @__PURE__ */ jsxs22(Popover3.Root, { spacing: "dense", width: "auto", children: [
7476
- /* @__PURE__ */ jsx32(
8198
+ return /* @__PURE__ */ jsxs24(Fragment9, { children: [
8199
+ /* @__PURE__ */ jsx33(ToolbarSeparator3, {}),
8200
+ /* @__PURE__ */ jsx33(ToolbarGroup2, { children: /* @__PURE__ */ jsxs24(Popover3.Root, { spacing: "dense", width: "auto", children: [
8201
+ /* @__PURE__ */ jsx33(
7477
8202
  Popover3.Trigger,
7478
8203
  {
7479
- render: /* @__PURE__ */ jsx32(
8204
+ render: /* @__PURE__ */ jsx33(
7480
8205
  ToolbarButton8,
7481
8206
  {
7482
8207
  type: "button",
@@ -7488,29 +8213,29 @@ function MoreOptions({
7488
8213
  title: "More options",
7489
8214
  isIconOnly: true,
7490
8215
  ...props,
7491
- children: /* @__PURE__ */ jsx32(MoreVerticalIcon, {})
8216
+ children: /* @__PURE__ */ jsx33(MoreVerticalIcon, {})
7492
8217
  }
7493
8218
  )
7494
8219
  }
7495
8220
  ),
7496
- /* @__PURE__ */ jsx32(
8221
+ /* @__PURE__ */ jsx33(
7497
8222
  Popover3.Content,
7498
8223
  {
7499
8224
  side: "top",
7500
8225
  align: "end",
7501
8226
  alignOffset: 4,
7502
8227
  sideOffset: 4,
7503
- children: /* @__PURE__ */ jsxs22(Toolbar2, { children: [
7504
- /* @__PURE__ */ jsxs22(ToolbarGroup2, { children: [
7505
- /* @__PURE__ */ jsx32(MarkButton, { type: "superscript" }),
7506
- /* @__PURE__ */ jsx32(MarkButton, { type: "subscript" })
8228
+ children: /* @__PURE__ */ jsxs24(Toolbar2, { children: [
8229
+ /* @__PURE__ */ jsxs24(ToolbarGroup2, { children: [
8230
+ /* @__PURE__ */ jsx33(MarkButton, { type: "superscript" }),
8231
+ /* @__PURE__ */ jsx33(MarkButton, { type: "subscript" })
7507
8232
  ] }),
7508
- /* @__PURE__ */ jsx32(ToolbarSeparator3, {}),
7509
- /* @__PURE__ */ jsxs22(ToolbarGroup2, { children: [
7510
- /* @__PURE__ */ jsx32(TextAlignButton, { align: "left" }),
7511
- /* @__PURE__ */ jsx32(TextAlignButton, { align: "center" }),
7512
- /* @__PURE__ */ jsx32(TextAlignButton, { align: "right" }),
7513
- /* @__PURE__ */ jsx32(TextAlignButton, { align: "justify" })
8233
+ /* @__PURE__ */ jsx33(ToolbarSeparator3, {}),
8234
+ /* @__PURE__ */ jsxs24(ToolbarGroup2, { children: [
8235
+ /* @__PURE__ */ jsx33(TextAlignButton, { align: "left" }),
8236
+ /* @__PURE__ */ jsx33(TextAlignButton, { align: "center" }),
8237
+ /* @__PURE__ */ jsx33(TextAlignButton, { align: "right" }),
8238
+ /* @__PURE__ */ jsx33(TextAlignButton, { align: "justify" })
7514
8239
  ] })
7515
8240
  ] })
7516
8241
  }
@@ -7542,7 +8267,7 @@ function shouldShowMoreOptions(params) {
7542
8267
  }
7543
8268
 
7544
8269
  // src/presets/basic/index.tsx
7545
- import { jsx as jsx33, jsxs as jsxs23 } from "react/jsx-runtime";
8270
+ import { Fragment as Fragment10, jsx as jsx34, jsxs as jsxs25 } from "react/jsx-runtime";
7546
8271
  var BasicEditor = ({
7547
8272
  variant,
7548
8273
  bordered,
@@ -7560,19 +8285,19 @@ var BasicEditor = ({
7560
8285
  ...options
7561
8286
  });
7562
8287
  const styles = editorBasic({ variant, bordered });
7563
- const resolveVariable = useCallback36(
8288
+ const resolveVariable = useCallback38(
7564
8289
  (name) => variableValues == null ? void 0 : variableValues[name],
7565
8290
  [variableValues]
7566
8291
  );
7567
8292
  if (!editor) {
7568
- return /* @__PURE__ */ jsx33(LoadingSpinner, {});
8293
+ return /* @__PURE__ */ jsx34(LoadingSpinner, {});
7569
8294
  }
7570
8295
  const isBottomToolbar = variant === "field";
7571
8296
  const hasVariables = variables && variables.length > 0;
7572
8297
  const hasPagination = !!pagesOptions;
7573
- const editorContent = /* @__PURE__ */ jsx33(EditorUIProvider, { value: { styles }, children: /* @__PURE__ */ jsx33("div", { className: styles.root(), "data-slot": "editor", children: /* @__PURE__ */ jsxs23(EditorContext.Provider, { value: { editor }, children: [
7574
- showToolbar && !isBottomToolbar && /* @__PURE__ */ jsx33(EditorHeader, { editor, variant }),
7575
- /* @__PURE__ */ jsx33(
8298
+ const editorContent = /* @__PURE__ */ jsx34(EditorUIProvider, { value: { styles }, children: /* @__PURE__ */ jsx34("div", { className: styles.root(), "data-slot": "editor", children: /* @__PURE__ */ jsxs25(EditorContext.Provider, { value: { editor }, children: [
8299
+ showToolbar && !isBottomToolbar && /* @__PURE__ */ jsx34(EditorHeader, { editor, variant }),
8300
+ /* @__PURE__ */ jsx34(
7576
8301
  EditorContentArea,
7577
8302
  {
7578
8303
  variant,
@@ -7580,10 +8305,10 @@ var BasicEditor = ({
7580
8305
  hasPagination
7581
8306
  }
7582
8307
  ),
7583
- showToolbar && isBottomToolbar && /* @__PURE__ */ jsx33(EditorHeader, { editor, variant })
8308
+ showToolbar && isBottomToolbar && /* @__PURE__ */ jsx34(EditorHeader, { editor, variant })
7584
8309
  ] }) }) });
7585
8310
  if (hasVariables) {
7586
- return /* @__PURE__ */ jsx33(VariableProvider, { variables, resolveVariable, children: editorContent });
8311
+ return /* @__PURE__ */ jsx34(VariableProvider, { variables, resolveVariable, children: editorContent });
7587
8312
  }
7588
8313
  return editorContent;
7589
8314
  };
@@ -7596,7 +8321,7 @@ var EditorContentArea = ({
7596
8321
  const { editor } = useContext4(EditorContext);
7597
8322
  const { isDragging } = useUiEditorState(editor);
7598
8323
  useScrollToHash();
7599
- const handleKeyDown = useCallback36(
8324
+ const handleKeyDown = useCallback38(
7600
8325
  (e) => {
7601
8326
  if (!(editor == null ? void 0 : editor.isFocused)) return;
7602
8327
  const isMod = e.metaKey || e.ctrlKey;
@@ -7638,8 +8363,8 @@ var EditorContentArea = ({
7638
8363
  const contentClassName = hasPagination ? "w-full min-w-0 h-auto min-h-0" : styles.content();
7639
8364
  return (
7640
8365
  // biome-ignore lint/a11y/noStaticElementInteractions: Wrapper intercepts keyboard shortcuts to prevent propagation
7641
- /* @__PURE__ */ jsxs23("div", { className: styles.wrapper(), onKeyDown: handleKeyDown, children: [
7642
- /* @__PURE__ */ jsxs23(
8366
+ /* @__PURE__ */ jsxs25("div", { className: styles.wrapper(), onKeyDown: handleKeyDown, children: [
8367
+ /* @__PURE__ */ jsxs25(
7643
8368
  EditorContent,
7644
8369
  {
7645
8370
  editor,
@@ -7649,20 +8374,36 @@ var EditorContentArea = ({
7649
8374
  cursor: isDragging ? "grabbing" : "auto"
7650
8375
  },
7651
8376
  children: [
7652
- isEditable && /* @__PURE__ */ jsx33(SlashDropdownMenu, {}),
7653
- isEditable && variables && variables.length > 0 && /* @__PURE__ */ jsx33(VariableSuggestion, { editor, variables })
8377
+ isEditable && /* @__PURE__ */ jsx34(SlashDropdownMenu, {}),
8378
+ isEditable && variables && variables.length > 0 && /* @__PURE__ */ jsx34(VariableSuggestion, { editor, variables })
7654
8379
  ]
7655
8380
  }
7656
8381
  ),
7657
- isEditable && /* @__PURE__ */ jsx33(BubbleMenu, { editor }),
7658
- isEditable && /* @__PURE__ */ jsx33(LinkBubble, { editor })
8382
+ isEditable && /* @__PURE__ */ jsx34(BubbleMenu, { editor }),
8383
+ isEditable && /* @__PURE__ */ jsx34(LinkBubble, { editor }),
8384
+ isEditable && /* @__PURE__ */ jsxs25(Fragment10, { children: [
8385
+ /* @__PURE__ */ jsx34(TableHandle, { editor }),
8386
+ /* @__PURE__ */ jsx34(
8387
+ TableSelectionOverlay,
8388
+ {
8389
+ showResizeHandles: true,
8390
+ cellMenu: (props) => /* @__PURE__ */ jsx34(
8391
+ TableCellHandleMenu,
8392
+ {
8393
+ editor: props.editor,
8394
+ onOpenChange: props.onOpenChange
8395
+ }
8396
+ )
8397
+ }
8398
+ )
8399
+ ] })
7659
8400
  ] })
7660
8401
  );
7661
8402
  };
7662
8403
  function LoadingSpinner({ text = "Connecting..." }) {
7663
8404
  const styles = editorSpinner();
7664
- return /* @__PURE__ */ jsx33("div", { className: styles.root(), children: /* @__PURE__ */ jsxs23("div", { className: styles.content(), children: [
7665
- /* @__PURE__ */ jsxs23(
8405
+ return /* @__PURE__ */ jsx34("div", { className: styles.root(), children: /* @__PURE__ */ jsxs25("div", { className: styles.content(), children: [
8406
+ /* @__PURE__ */ jsxs25(
7666
8407
  "svg",
7667
8408
  {
7668
8409
  className: styles.svg(),
@@ -7670,9 +8411,9 @@ function LoadingSpinner({ text = "Connecting..." }) {
7670
8411
  fill: "none",
7671
8412
  viewBox: "0 0 24 24",
7672
8413
  children: [
7673
- /* @__PURE__ */ jsx33("title", { children: "Loading Spinner" }),
7674
- /* @__PURE__ */ jsx33("circle", { className: styles.circle(), cx: "12", cy: "12", r: "10" }),
7675
- /* @__PURE__ */ jsx33(
8414
+ /* @__PURE__ */ jsx34("title", { children: "Loading Spinner" }),
8415
+ /* @__PURE__ */ jsx34("circle", { className: styles.circle(), cx: "12", cy: "12", r: "10" }),
8416
+ /* @__PURE__ */ jsx34(
7676
8417
  "path",
7677
8418
  {
7678
8419
  className: styles.path(),
@@ -7682,7 +8423,7 @@ function LoadingSpinner({ text = "Connecting..." }) {
7682
8423
  ]
7683
8424
  }
7684
8425
  ),
7685
- /* @__PURE__ */ jsx33("div", { className: styles.text(), children: text })
8426
+ /* @__PURE__ */ jsx34("div", { className: styles.text(), children: text })
7686
8427
  ] }) });
7687
8428
  }
7688
8429
  export {