@antscorp/antsomi-ui 1.3.7-beta.25 → 1.3.7-beta.26

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.
@@ -9,7 +9,7 @@ import { Selection, Focus } from '@tiptap/extensions';
9
9
  import { useEditor } from '@tiptap/react';
10
10
  import StarterKit from '@tiptap/starter-kit';
11
11
  import clsx from 'clsx';
12
- import { isBoolean, omit } from 'lodash';
12
+ import { isBoolean } from 'lodash';
13
13
  import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, } from 'react';
14
14
  import { useDebouncedCallback } from 'use-debounce';
15
15
  import { DEFAULT_TEXT_STYLE } from './constants';
@@ -23,6 +23,7 @@ import { Indent } from './extensions/Indent';
23
23
  import { LetterSpacing } from './extensions/LetterSpacing';
24
24
  import { LineHeight } from './extensions/LineHeight';
25
25
  import { CustomLink } from './extensions/Link';
26
+ import { CustomListItem } from './extensions/ListItem';
26
27
  import { CustomOrderedList } from './extensions/OrderedList';
27
28
  import { SmartTag } from './extensions/SmartTag';
28
29
  import { TextTransform } from './extensions/TextTransform';
@@ -104,15 +105,12 @@ const TextEditorInternal = memo(forwardRef((props, ref) => {
104
105
  link: false,
105
106
  bulletList: false,
106
107
  orderedList: false,
107
- listItem: {
108
- HTMLAttributes: {
109
- style: 'line-height:normal;',
110
- },
111
- },
108
+ listItem: false,
112
109
  undoRedo: {
113
110
  depth: 100,
114
111
  },
115
112
  }),
113
+ CustomListItem,
116
114
  CustomUnorderedList,
117
115
  CustomOrderedList,
118
116
  FontWeight.configure({
@@ -307,7 +305,7 @@ const TextEditorInternal = memo(forwardRef((props, ref) => {
307
305
  }
308
306
  return (_jsxs(_Fragment, { children: [_jsx(StyledEditorContent, { className: clsx(`${ANTSOMI_COMPONENT_PREFIX_CLS}-text-editor`, className), "$textStyle": defaultTextStyle, style: {
309
307
  // Inline styles apply to inner html elements (support email client)
310
- ...omit(defaultTextStyle, ['fontSize']),
308
+ ...defaultTextStyle,
311
309
  ...style,
312
310
  }, id: id, ref: contentRef, editor: editor, ...dataAttributes }), _jsx(LinkPopover, { editor: editor }), !linkFormVisible && (_jsxs(_Fragment, { children: [_jsx(BubbleMenu, { pluginKey: "linkPreviewBubbleMenu", ...bubbleMenuProps, appendTo: bubbleMenuContainer.current, editor: editor, shouldShow: shouldShowLinkPreview, children: _jsx(LinkPreviewToolbar, { editor: editor, linkHanlder: {
313
311
  onUpsert: () => {
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Custom ListItem extension that extends the default TipTap ListItem
3
+ * with custom Backspace behavior to prevent merging paragraphs into lists
4
+ *
5
+ * This extension only overrides keyboard shortcuts while maintaining
6
+ * all other default ListItem functionality (HTML parsing, rendering, etc.)
7
+ *
8
+ * @see https://www.tiptap.dev/api/nodes/list-item
9
+ */
10
+ export declare const CustomListItem: import("@tiptap/core").Node<import("@tiptap/extension-list").ListItemOptions, any>;
@@ -0,0 +1,93 @@
1
+ import { ListItem } from '@tiptap/extension-list-item';
2
+ /**
3
+ * Custom ListItem extension that extends the default TipTap ListItem
4
+ * with custom Backspace behavior to prevent merging paragraphs into lists
5
+ *
6
+ * This extension only overrides keyboard shortcuts while maintaining
7
+ * all other default ListItem functionality (HTML parsing, rendering, etc.)
8
+ *
9
+ * @see https://www.tiptap.dev/api/nodes/list-item
10
+ */
11
+ export const CustomListItem = ListItem.extend({
12
+ addOptions() {
13
+ return {
14
+ HTMLAttributes: {
15
+ style: 'line-height:normal;',
16
+ },
17
+ bulletListTypeName: 'customUnorderedList',
18
+ orderedListTypeName: 'customOrderedList',
19
+ };
20
+ },
21
+ addKeyboardShortcuts() {
22
+ return {
23
+ // Keep default keyboard shortcuts from parent ListItem
24
+ Enter: () => this.editor.commands.splitListItem(this.name),
25
+ Tab: () => this.editor.commands.sinkListItem(this.name),
26
+ 'Shift-Tab': () => this.editor.commands.liftListItem(this.name),
27
+ /**
28
+ * Custom Backspace handler to prevent merging empty paragraphs into lists
29
+ *
30
+ * Behavior:
31
+ * 1. Check if cursor is at the start of a paragraph
32
+ * 2. Check if the node before is a list (ordered or unordered)
33
+ * 3. If both conditions are true and paragraph is empty, delete the paragraph
34
+ * 4. Otherwise, use default backspace behavior
35
+ */
36
+ Backspace: () => {
37
+ const { state } = this.editor;
38
+ const { selection } = state;
39
+ const { $from, empty } = selection;
40
+ // Only handle if selection is empty (cursor position, not text selection)
41
+ if (!empty) {
42
+ return false;
43
+ }
44
+ // Check if cursor is at the start of current node
45
+ const isAtStart = $from.parentOffset === 0;
46
+ if (!isAtStart) {
47
+ return false;
48
+ }
49
+ // Get current node (should be a paragraph)
50
+ const currentNode = $from.parent;
51
+ // Only handle paragraphs
52
+ if (currentNode.type.name !== 'paragraph') {
53
+ return false;
54
+ }
55
+ // Get the index of current paragraph in its parent (usually doc)
56
+ const paragraphDepth = $from.depth;
57
+ const indexInParent = $from.index(paragraphDepth - 1);
58
+ // Check if there's a node before (index > 0)
59
+ if (indexInParent === 0) {
60
+ return false;
61
+ }
62
+ // Get parent node (usually doc) and the node before current paragraph
63
+ const parent = $from.node(paragraphDepth - 1);
64
+ const nodeBefore = parent.child(indexInParent - 1);
65
+ if (!nodeBefore) {
66
+ return false;
67
+ }
68
+ // Check if the node before is a list (ordered or unordered)
69
+ const isListBefore = nodeBefore.type.name === this.options.orderedListTypeName ||
70
+ nodeBefore.type.name === this.options.bulletListTypeName;
71
+ if (!isListBefore) {
72
+ return false;
73
+ }
74
+ // Check if current paragraph is empty
75
+ const isParagraphEmpty = currentNode.content.size === 0;
76
+ if (!isParagraphEmpty) {
77
+ // If paragraph has content, allow default backspace to handle
78
+ return false;
79
+ }
80
+ // Calculate the exact positions of the paragraph node
81
+ // We need to delete from the position before the paragraph open tag
82
+ // to the position after the paragraph close tag
83
+ const paragraphPos = $from.before();
84
+ const paragraphEndPos = $from.after();
85
+ // Delete the entire paragraph node using deleteRange
86
+ return this.editor.commands.deleteRange({
87
+ from: paragraphPos,
88
+ to: paragraphEndPos,
89
+ });
90
+ },
91
+ };
92
+ },
93
+ });
@@ -12,6 +12,7 @@ export const StyledEditorContent = styled(EditorContent) `
12
12
 
13
13
  // Some apps have global p styles, so we need to override it
14
14
 
15
+ font-size: ${p => p.$textStyle.fontSize};
15
16
  line-height: ${p => p.$textStyle.lineHeight};
16
17
  font-family: ${p => p.$textStyle.fontFamily};
17
18
  color: ${p => p.$textStyle.color};
@@ -52,9 +52,7 @@ export const TextAlignSelect = (props) => {
52
52
  editor: editor,
53
53
  selector: ({ editor: editorInstance }) => {
54
54
  const alignment = ['left', 'center', 'right', 'justify'].find(alignment => editorInstance?.isActive({ textAlign: alignment }));
55
- return {
56
- currentAlignment: alignment || 'left',
57
- };
55
+ return { currentAlignment: alignment };
58
56
  },
59
57
  });
60
58
  const selectedOption = useMemo(() => textAlignOptions.find(option => option.value === currentAlignment), [currentAlignment]);
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useTextEditorStore } from '../../../provider';
3
3
  import { FontSizeInput, } from '@antscorp/antsomi-ui/es/components/molecules';
4
4
  import styled from 'styled-components';
5
- import { memo, useCallback, useMemo, useRef, useState } from 'react';
5
+ import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
6
6
  import { useThrottledCallback } from 'use-debounce';
7
7
  import { useEditorState } from '@tiptap/react';
8
8
  import { toString } from 'lodash';
@@ -30,6 +30,11 @@ export const FontSizeAction = memo(({ editor, throttled = 100, defaultFontSize =
30
30
  },
31
31
  });
32
32
  const [currentFontSize, setCurrentFontSize] = useState(numberFontSize);
33
+ useEffect(() => {
34
+ if (numberFontSize) {
35
+ setCurrentFontSize(numberFontSize);
36
+ }
37
+ }, [numberFontSize]);
33
38
  const updateEditorFontSize = useThrottledCallback((v) => {
34
39
  if (v === numberFontSize)
35
40
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antscorp/antsomi-ui",
3
- "version": "1.3.7-beta.25",
3
+ "version": "1.3.7-beta.26",
4
4
  "description": "An enterprise-class UI design language and React UI library.",
5
5
  "sideEffects": [
6
6
  "dist/*",