@team-monolith/cds 1.3.2 → 1.4.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.
- package/README.md +2 -2
- package/dist/CodleDesignSystemProvider.d.ts +3 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/patterns/LexicalEditor/LexicalEditor.d.ts +14 -0
- package/dist/patterns/LexicalEditor/LexicalEditor.js +39 -0
- package/dist/patterns/LexicalEditor/Plugins.d.ts +10 -0
- package/dist/patterns/LexicalEditor/Plugins.js +81 -0
- package/dist/patterns/LexicalEditor/hr.svg +3 -0
- package/dist/patterns/LexicalEditor/index.d.ts +2 -0
- package/dist/patterns/LexicalEditor/index.js +2 -0
- package/dist/patterns/LexicalEditor/nodes/ImageComponent.d.ts +18 -0
- package/dist/patterns/LexicalEditor/nodes/ImageComponent.js +143 -0
- package/dist/patterns/LexicalEditor/nodes/ImageNode.d.ts +56 -0
- package/dist/patterns/LexicalEditor/nodes/ImageNode.js +117 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/ComponentAdder.d.ts +10 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/ComponentAdder.js +68 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/ComponentAdderPlugin.d.ts +11 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/ComponentAdderPlugin.js +214 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/getContextMenuOptions.d.ts +6 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/getContextMenuOptions.js +90 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/index.d.ts +1 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/index.js +1 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/menu.svg +8 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/plus.svg +3 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/useDraggableBlockMenu.d.ts +16 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/useDraggableBlockMenu.js +234 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/useFloatingMenu.d.ts +20 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/useFloatingMenu.js +158 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuItem.d.ts +10 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuItem.js +40 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuList.d.ts +9 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuList.js +46 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuPlugin.d.ts +26 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuPlugin.js +174 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/index.d.ts +3 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/index.js +3 -0
- package/dist/patterns/LexicalEditor/plugins/FloatingLinkEditorPlugin/FloatingLinkEditor.d.ts +11 -0
- package/dist/patterns/LexicalEditor/plugins/FloatingLinkEditorPlugin/FloatingLinkEditor.js +201 -0
- package/dist/patterns/LexicalEditor/plugins/FloatingLinkEditorPlugin/index.d.ts +6 -0
- package/dist/patterns/LexicalEditor/plugins/FloatingLinkEditorPlugin/index.js +62 -0
- package/dist/patterns/LexicalEditor/plugins/FloatingTextFormatToolbarPlugin/FloatingTextFormatPopup.d.ts +13 -0
- package/dist/patterns/LexicalEditor/plugins/FloatingTextFormatToolbarPlugin/FloatingTextFormatPopup.js +60 -0
- package/dist/patterns/LexicalEditor/plugins/FloatingTextFormatToolbarPlugin/index.d.ts +4 -0
- package/dist/patterns/LexicalEditor/plugins/FloatingTextFormatToolbarPlugin/index.js +190 -0
- package/dist/patterns/LexicalEditor/plugins/ImagesPlugin/InsertImageDialog.d.ts +7 -0
- package/dist/patterns/LexicalEditor/plugins/ImagesPlugin/InsertImageDialog.js +39 -0
- package/dist/patterns/LexicalEditor/plugins/ImagesPlugin/InsertImageUploadedDialogBody.d.ts +5 -0
- package/dist/patterns/LexicalEditor/plugins/ImagesPlugin/InsertImageUploadedDialogBody.js +49 -0
- package/dist/patterns/LexicalEditor/plugins/ImagesPlugin/InsertImageUriDialogBody.d.ts +5 -0
- package/dist/patterns/LexicalEditor/plugins/ImagesPlugin/InsertImageUriDialogBody.js +24 -0
- package/dist/patterns/LexicalEditor/plugins/ImagesPlugin/index.d.ts +14 -0
- package/dist/patterns/LexicalEditor/plugins/ImagesPlugin/index.js +151 -0
- package/dist/patterns/LexicalEditor/plugins/ListMaxIndentLevelPlugin/index.d.ts +12 -0
- package/dist/patterns/LexicalEditor/plugins/ListMaxIndentLevelPlugin/index.js +49 -0
- package/dist/patterns/LexicalEditor/plugins/MarkdownTransformers/index.d.ts +12 -0
- package/dist/patterns/LexicalEditor/plugins/MarkdownTransformers/index.js +184 -0
- package/dist/patterns/LexicalEditor/theme.d.ts +24 -0
- package/dist/patterns/LexicalEditor/theme.js +178 -0
- package/dist/patterns/LexicalEditor/utils/getDOMRangeRect.d.ts +8 -0
- package/dist/patterns/LexicalEditor/utils/getDOMRangeRect.js +22 -0
- package/dist/patterns/LexicalEditor/utils/getSelectedNode.d.ts +2 -0
- package/dist/patterns/LexicalEditor/utils/getSelectedNode.js +24 -0
- package/dist/patterns/LexicalEditor/utils/guard.d.ts +8 -0
- package/dist/patterns/LexicalEditor/utils/guard.js +10 -0
- package/dist/patterns/LexicalEditor/utils/point.d.ts +21 -0
- package/dist/patterns/LexicalEditor/utils/point.js +41 -0
- package/dist/patterns/LexicalEditor/utils/rect.d.ts +45 -0
- package/dist/patterns/LexicalEditor/utils/rect.js +99 -0
- package/dist/patterns/LexicalEditor/utils/setFloatingElemPosition.d.ts +1 -0
- package/dist/patterns/LexicalEditor/utils/setFloatingElemPosition.js +36 -0
- package/dist/patterns/LexicalEditor/utils/setFloatingElemPositionForLinkEditor.d.ts +1 -0
- package/dist/patterns/LexicalEditor/utils/setFloatingElemPositionForLinkEditor.js +32 -0
- package/dist/patterns/LexicalEditor/utils/url.d.ts +9 -0
- package/dist/patterns/LexicalEditor/utils/url.js +34 -0
- package/dist/patterns/SegmentedControl/SegmentedControlButton.js +19 -6
- package/dist/patterns/SegmentedControl/SegmentedControlSquareButton.d.ts +1 -1
- package/dist/patterns/SegmentedControl/SegmentedControlSquareButton.js +35 -17
- package/package.json +6 -3
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
/** @jsxImportSource @emotion/react */
|
|
9
|
+
import { $getRoot } from "lexical";
|
|
10
|
+
import { useEffect, useState } from "react";
|
|
11
|
+
import { isHTMLElement } from "../../utils/guard";
|
|
12
|
+
import { Point } from "../../utils/point";
|
|
13
|
+
import { Rect } from "../../utils/rect";
|
|
14
|
+
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
15
|
+
const SPACE = 4;
|
|
16
|
+
const Downward = 1;
|
|
17
|
+
const Upward = -1;
|
|
18
|
+
const Indeterminate = 0;
|
|
19
|
+
let prevIndex = Infinity;
|
|
20
|
+
function getCurrentIndex(keysLength) {
|
|
21
|
+
if (keysLength === 0) {
|
|
22
|
+
return Infinity;
|
|
23
|
+
}
|
|
24
|
+
if (prevIndex >= 0 && prevIndex < keysLength) {
|
|
25
|
+
return prevIndex;
|
|
26
|
+
}
|
|
27
|
+
return Math.floor(keysLength / 2);
|
|
28
|
+
}
|
|
29
|
+
function getTopLevelNodeKeys(editor) {
|
|
30
|
+
return editor.getEditorState().read(() => $getRoot().getChildrenKeys());
|
|
31
|
+
}
|
|
32
|
+
function getCollapsedMargins(elem) {
|
|
33
|
+
const getMargin = (element, margin) => element ? parseFloat(window.getComputedStyle(element)[margin]) : 0;
|
|
34
|
+
const { marginTop, marginBottom } = window.getComputedStyle(elem);
|
|
35
|
+
const prevElemSiblingMarginBottom = getMargin(elem.previousElementSibling, "marginBottom");
|
|
36
|
+
const nextElemSiblingMarginTop = getMargin(elem.nextElementSibling, "marginTop");
|
|
37
|
+
const collapsedTopMargin = Math.max(parseFloat(marginTop), prevElemSiblingMarginBottom);
|
|
38
|
+
const collapsedBottomMargin = Math.max(parseFloat(marginBottom), nextElemSiblingMarginTop);
|
|
39
|
+
return { marginBottom: collapsedBottomMargin, marginTop: collapsedTopMargin };
|
|
40
|
+
}
|
|
41
|
+
export function getBlockElement(anchorElem, editor, event, useEdgeAsDefault = false) {
|
|
42
|
+
const anchorElementRect = anchorElem.getBoundingClientRect();
|
|
43
|
+
const topLevelNodeKeys = getTopLevelNodeKeys(editor);
|
|
44
|
+
let blockElem = null;
|
|
45
|
+
editor.getEditorState().read(() => {
|
|
46
|
+
if (useEdgeAsDefault) {
|
|
47
|
+
const [firstNode, lastNode] = [
|
|
48
|
+
editor.getElementByKey(topLevelNodeKeys[0]),
|
|
49
|
+
editor.getElementByKey(topLevelNodeKeys[topLevelNodeKeys.length - 1]),
|
|
50
|
+
];
|
|
51
|
+
const [firstNodeRect, lastNodeRect] = [
|
|
52
|
+
firstNode === null || firstNode === void 0 ? void 0 : firstNode.getBoundingClientRect(),
|
|
53
|
+
lastNode === null || lastNode === void 0 ? void 0 : lastNode.getBoundingClientRect(),
|
|
54
|
+
];
|
|
55
|
+
if (firstNodeRect && lastNodeRect) {
|
|
56
|
+
if (event.y < firstNodeRect.top) {
|
|
57
|
+
blockElem = firstNode;
|
|
58
|
+
}
|
|
59
|
+
else if (event.y > lastNodeRect.bottom) {
|
|
60
|
+
blockElem = lastNode;
|
|
61
|
+
}
|
|
62
|
+
if (blockElem) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
let index = getCurrentIndex(topLevelNodeKeys.length);
|
|
68
|
+
let direction = Indeterminate;
|
|
69
|
+
while (index >= 0 && index < topLevelNodeKeys.length) {
|
|
70
|
+
const key = topLevelNodeKeys[index];
|
|
71
|
+
const elem = editor.getElementByKey(key);
|
|
72
|
+
if (elem === null) {
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
const point = new Point(event.x, event.y);
|
|
76
|
+
const domRect = Rect.fromDOM(elem);
|
|
77
|
+
const { marginTop, marginBottom } = getCollapsedMargins(elem);
|
|
78
|
+
const rect = domRect.generateNewRect({
|
|
79
|
+
bottom: domRect.bottom + marginBottom,
|
|
80
|
+
left: anchorElementRect.left,
|
|
81
|
+
right: anchorElementRect.right,
|
|
82
|
+
top: domRect.top - marginTop,
|
|
83
|
+
});
|
|
84
|
+
const { result, reason: { isOnTopSide, isOnBottomSide }, } = rect.contains(point);
|
|
85
|
+
if (result) {
|
|
86
|
+
blockElem = elem;
|
|
87
|
+
prevIndex = index;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
if (direction === Indeterminate) {
|
|
91
|
+
if (isOnTopSide) {
|
|
92
|
+
direction = Upward;
|
|
93
|
+
}
|
|
94
|
+
else if (isOnBottomSide) {
|
|
95
|
+
direction = Downward;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
// stop search block element
|
|
99
|
+
direction = Infinity;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
index += direction;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
return blockElem;
|
|
106
|
+
}
|
|
107
|
+
function setMenuPosition(targetElem, floatingElem, anchorElem) {
|
|
108
|
+
if (!targetElem) {
|
|
109
|
+
floatingElem.style.opacity = "0";
|
|
110
|
+
floatingElem.style.transform = "translate(-10000px, -10000px)";
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const targetRect = targetElem.getBoundingClientRect();
|
|
114
|
+
const targetStyle = window.getComputedStyle(targetElem);
|
|
115
|
+
const floatingElemRect = floatingElem.getBoundingClientRect();
|
|
116
|
+
const anchorElementRect = anchorElem.getBoundingClientRect();
|
|
117
|
+
const top = targetRect.top +
|
|
118
|
+
(parseInt(targetStyle.lineHeight, 10) - floatingElemRect.height) / 2 -
|
|
119
|
+
anchorElementRect.top;
|
|
120
|
+
const left = SPACE;
|
|
121
|
+
floatingElem.style.opacity = "1";
|
|
122
|
+
floatingElem.style.transform = `translate(${left}px, ${top}px)`;
|
|
123
|
+
}
|
|
124
|
+
export function useFloatingMenu(props) {
|
|
125
|
+
const { anchorElem, menuRef, isOnMenu } = props;
|
|
126
|
+
const scrollerElem = anchorElem.parentElement;
|
|
127
|
+
const [editor] = useLexicalComposerContext();
|
|
128
|
+
const [blockElem, setBlockElem] = useState(null);
|
|
129
|
+
useEffect(() => {
|
|
130
|
+
function onMouseMove(event) {
|
|
131
|
+
const target = event.target;
|
|
132
|
+
if (!isHTMLElement(target)) {
|
|
133
|
+
setBlockElem(null);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (isOnMenu(target)) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const _blockElem = getBlockElement(anchorElem, editor, event);
|
|
140
|
+
setBlockElem(_blockElem);
|
|
141
|
+
}
|
|
142
|
+
function onMouseLeave() {
|
|
143
|
+
setBlockElem(null);
|
|
144
|
+
}
|
|
145
|
+
scrollerElem === null || scrollerElem === void 0 ? void 0 : scrollerElem.addEventListener("mousemove", onMouseMove);
|
|
146
|
+
scrollerElem === null || scrollerElem === void 0 ? void 0 : scrollerElem.addEventListener("mouseleave", onMouseLeave);
|
|
147
|
+
return () => {
|
|
148
|
+
scrollerElem === null || scrollerElem === void 0 ? void 0 : scrollerElem.removeEventListener("mousemove", onMouseMove);
|
|
149
|
+
scrollerElem === null || scrollerElem === void 0 ? void 0 : scrollerElem.removeEventListener("mouseleave", onMouseLeave);
|
|
150
|
+
};
|
|
151
|
+
}, [scrollerElem, anchorElem, editor, isOnMenu]);
|
|
152
|
+
useEffect(() => {
|
|
153
|
+
if (menuRef.current) {
|
|
154
|
+
setMenuPosition(blockElem, menuRef.current, anchorElem);
|
|
155
|
+
}
|
|
156
|
+
}, [anchorElem, blockElem, menuRef]);
|
|
157
|
+
return { blockElem, setBlockElem };
|
|
158
|
+
}
|
package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuItem.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** @jsxImportSource @emotion/react */
|
|
2
|
+
import { ComponentPickerOption } from "./ComponentPickerMenuPlugin";
|
|
3
|
+
export interface ComponentPickerMenuItemProps {
|
|
4
|
+
index: number;
|
|
5
|
+
isSelected: boolean;
|
|
6
|
+
onClick: () => void;
|
|
7
|
+
onMouseEnter: () => void;
|
|
8
|
+
option: ComponentPickerOption;
|
|
9
|
+
}
|
|
10
|
+
export declare function ComponentPickerMenuItem(props: ComponentPickerMenuItemProps): import("@emotion/react/jsx-runtime").JSX.Element;
|
package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuItem.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
2
|
+
import styled from "@emotion/styled";
|
|
3
|
+
export function ComponentPickerMenuItem(props) {
|
|
4
|
+
const { index, isSelected, onClick, onMouseEnter, option } = props;
|
|
5
|
+
return (_jsxs(ItemContainer, Object.assign({ tabIndex: -1, ref: option.setRefElement, role: "option", "aria-selected": isSelected, id: "typeahead-item-" + index, onMouseEnter: onMouseEnter, onClick: onClick, isSelected: isSelected }, { children: [_jsx(IconContainer, { children: option.icon }), _jsx(Title, { children: option.title })] }), option.key));
|
|
6
|
+
}
|
|
7
|
+
const ItemContainer = styled.li `
|
|
8
|
+
display: flex;
|
|
9
|
+
padding: 3px 2px;
|
|
10
|
+
align-items: center;
|
|
11
|
+
gap: 10px;
|
|
12
|
+
width: 100%;
|
|
13
|
+
|
|
14
|
+
${({ isSelected }) => isSelected && "background: #eee;"}
|
|
15
|
+
`;
|
|
16
|
+
const IconContainer = styled.div `
|
|
17
|
+
display: flex;
|
|
18
|
+
width: 26px;
|
|
19
|
+
height: 26px;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
align-items: center;
|
|
22
|
+
|
|
23
|
+
border-radius: 5px;
|
|
24
|
+
|
|
25
|
+
background: ${({ theme }) => theme.color.foreground.neutralAlt};
|
|
26
|
+
box-shadow: 0px 0px 0px 1px rgba(201, 201, 204, 0.48);
|
|
27
|
+
|
|
28
|
+
svg {
|
|
29
|
+
width: 18px;
|
|
30
|
+
height: 18px;
|
|
31
|
+
}
|
|
32
|
+
`;
|
|
33
|
+
const Title = styled.span `
|
|
34
|
+
color: ${({ theme }) => theme.color.foreground.neutralBase};
|
|
35
|
+
font-family: ${({ theme }) => theme.fontFamily.ui};
|
|
36
|
+
font-size: 14px;
|
|
37
|
+
font-style: normal;
|
|
38
|
+
font-weight: 500;
|
|
39
|
+
line-height: 20px; /* 142.857% */
|
|
40
|
+
`;
|
package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuList.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { ComponentPickerOption } from "./ComponentPickerMenuPlugin";
|
|
3
|
+
export interface ComponentPickerMenuListProps {
|
|
4
|
+
options: ComponentPickerOption[];
|
|
5
|
+
selectedIndex: number | null;
|
|
6
|
+
selectOptionAndCleanUp: (option: ComponentPickerOption) => void;
|
|
7
|
+
setHighlightedIndex: (index: number) => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function ComponentPickerMenuList(props: ComponentPickerMenuListProps): JSX.Element;
|
package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuList.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import styled from "@emotion/styled";
|
|
3
|
+
import { ComponentPickerMenuItem } from "./ComponentPickerMenuItem";
|
|
4
|
+
export function ComponentPickerMenuList(props) {
|
|
5
|
+
const { options, selectedIndex, selectOptionAndCleanUp, setHighlightedIndex, } = props;
|
|
6
|
+
return (_jsx(Container, { children: _jsx(ListContainer, { children: options.map((option, i) => (_jsx(ComponentPickerMenuItem, { index: i, isSelected: selectedIndex === i, onClick: () => {
|
|
7
|
+
setHighlightedIndex(i);
|
|
8
|
+
selectOptionAndCleanUp(option);
|
|
9
|
+
}, onMouseEnter: () => {
|
|
10
|
+
setHighlightedIndex(i);
|
|
11
|
+
}, option: option }, option.key))) }) }));
|
|
12
|
+
}
|
|
13
|
+
const Container = styled.div `
|
|
14
|
+
position: absolute;
|
|
15
|
+
|
|
16
|
+
display: flex;
|
|
17
|
+
width: 200px;
|
|
18
|
+
padding: 4px;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
align-items: flex-start;
|
|
21
|
+
gap: 5px;
|
|
22
|
+
|
|
23
|
+
border-radius: 8px;
|
|
24
|
+
border: 1px solid #e8e8eb;
|
|
25
|
+
background: ${({ theme }) => theme.color.background.neutralBase};
|
|
26
|
+
box-shadow: 0px 3px 15px -3px rgba(13, 20, 33, 0.13);
|
|
27
|
+
`;
|
|
28
|
+
const ListContainer = styled.ul `
|
|
29
|
+
padding: 0;
|
|
30
|
+
list-style: none;
|
|
31
|
+
margin: 0;
|
|
32
|
+
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-direction: column;
|
|
35
|
+
align-items: flex-start;
|
|
36
|
+
gap: 1px;
|
|
37
|
+
align-self: stretch;
|
|
38
|
+
|
|
39
|
+
overflow-y: scroll;
|
|
40
|
+
|
|
41
|
+
scrollbar-width: none;
|
|
42
|
+
-ms-overflow-style: none;
|
|
43
|
+
&::-webkit-scrollbar {
|
|
44
|
+
display: none;
|
|
45
|
+
}
|
|
46
|
+
`;
|
package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuPlugin.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
/** @jsxImportSource @emotion/react */
|
|
9
|
+
/// <reference types="react" />
|
|
10
|
+
import { MenuOption } from "@lexical/react/LexicalTypeaheadMenuPlugin";
|
|
11
|
+
import { LexicalEditor } from "lexical";
|
|
12
|
+
export declare class ComponentPickerOption extends MenuOption {
|
|
13
|
+
title: string;
|
|
14
|
+
icon?: JSX.Element;
|
|
15
|
+
keywords: Array<string>;
|
|
16
|
+
keyboardShortcut?: string;
|
|
17
|
+
onSelect: (queryString: string) => void;
|
|
18
|
+
constructor(title: string, options: {
|
|
19
|
+
icon?: JSX.Element;
|
|
20
|
+
keywords?: Array<string>;
|
|
21
|
+
keyboardShortcut?: string;
|
|
22
|
+
onSelect: (queryString: string) => void;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
export declare function getBaseOptions(editor: LexicalEditor, setOpen: (open: boolean) => void): ComponentPickerOption[];
|
|
26
|
+
export declare function ComponentPickerMenuPlugin(): JSX.Element;
|
package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuPlugin.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*
|
|
8
|
+
*/
|
|
9
|
+
/** @jsxImportSource @emotion/react */
|
|
10
|
+
import { $createCodeNode } from "@lexical/code";
|
|
11
|
+
import { INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND, } from "@lexical/list";
|
|
12
|
+
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
13
|
+
import { INSERT_HORIZONTAL_RULE_COMMAND } from "@lexical/react/LexicalHorizontalRuleNode";
|
|
14
|
+
import { LexicalTypeaheadMenuPlugin, MenuOption, useBasicTypeaheadTriggerMatch, } from "@lexical/react/LexicalTypeaheadMenuPlugin";
|
|
15
|
+
import { $createHeadingNode, $createQuoteNode } from "@lexical/rich-text";
|
|
16
|
+
import { $setBlocksType } from "@lexical/selection";
|
|
17
|
+
import { INSERT_TABLE_COMMAND } from "@lexical/table";
|
|
18
|
+
import { $createParagraphNode, $getSelection, $isRangeSelection, } from "lexical";
|
|
19
|
+
import { useCallback, useMemo, useState } from "react";
|
|
20
|
+
import * as ReactDOM from "react-dom";
|
|
21
|
+
import { css as cssToClassName } from "@emotion/css";
|
|
22
|
+
import { ComponentPickerMenuList } from "./ComponentPickerMenuList";
|
|
23
|
+
import { InsertImageDialog } from "../ImagesPlugin/InsertImageDialog";
|
|
24
|
+
import { TextIcon, H1Icon, H2Icon, H3Icon, ListUnorderedIcon, ListOrderedIcon, DoubleQuotesLIcon, CodeViewIcon, SeparatorIcon, ImageLineIcon, } from "../../../../icons";
|
|
25
|
+
// import useModal from "../../hooks/useModal";
|
|
26
|
+
// import catTypingGif from "../../images/cat-typing.gif";
|
|
27
|
+
// import { INSERT_IMAGE_COMMAND, InsertImageDialog } from "../ImagesPlugin";
|
|
28
|
+
export class ComponentPickerOption extends MenuOption {
|
|
29
|
+
constructor(title, options) {
|
|
30
|
+
super(title);
|
|
31
|
+
this.title = title;
|
|
32
|
+
this.keywords = options.keywords || [];
|
|
33
|
+
this.icon = options.icon;
|
|
34
|
+
this.keyboardShortcut = options.keyboardShortcut;
|
|
35
|
+
this.onSelect = options.onSelect.bind(this);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function getDynamicOptions(editor, queryString) {
|
|
39
|
+
const options = [];
|
|
40
|
+
if (queryString == null) {
|
|
41
|
+
return options;
|
|
42
|
+
}
|
|
43
|
+
const tableMatch = queryString.match(/^([1-9]\d?)(?:x([1-9]\d?)?)?$/);
|
|
44
|
+
if (tableMatch !== null) {
|
|
45
|
+
const rows = tableMatch[1];
|
|
46
|
+
const colOptions = tableMatch[2]
|
|
47
|
+
? [tableMatch[2]]
|
|
48
|
+
: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(String);
|
|
49
|
+
options.push(...colOptions.map((columns) => new ComponentPickerOption(`${rows}x${columns} Table`, {
|
|
50
|
+
icon: _jsx("i", { className: "icon table" }),
|
|
51
|
+
keywords: ["table"],
|
|
52
|
+
onSelect: () => editor.dispatchCommand(INSERT_TABLE_COMMAND, { columns, rows }),
|
|
53
|
+
})));
|
|
54
|
+
}
|
|
55
|
+
return options;
|
|
56
|
+
}
|
|
57
|
+
export function getBaseOptions(editor, setOpen) {
|
|
58
|
+
return [
|
|
59
|
+
new ComponentPickerOption("본문", {
|
|
60
|
+
icon: _jsx(TextIcon, {}),
|
|
61
|
+
keywords: ["normal", "paragraph", "p", "text", "본문", "단락", "내용"],
|
|
62
|
+
onSelect: () => editor.update(() => {
|
|
63
|
+
const selection = $getSelection();
|
|
64
|
+
if ($isRangeSelection(selection)) {
|
|
65
|
+
$setBlocksType(selection, () => $createParagraphNode());
|
|
66
|
+
}
|
|
67
|
+
}),
|
|
68
|
+
}),
|
|
69
|
+
...[1, 2, 3].map((n) => {
|
|
70
|
+
const titleMap = {
|
|
71
|
+
1: "큰 제목",
|
|
72
|
+
2: "중간 제목",
|
|
73
|
+
3: "작은 제목",
|
|
74
|
+
};
|
|
75
|
+
const iconMap = {
|
|
76
|
+
1: _jsx(H1Icon, {}),
|
|
77
|
+
2: _jsx(H2Icon, {}),
|
|
78
|
+
3: _jsx(H3Icon, {}),
|
|
79
|
+
};
|
|
80
|
+
return new ComponentPickerOption(titleMap[n], {
|
|
81
|
+
icon: iconMap[n],
|
|
82
|
+
keywords: ["heading", "header", `h${n}`, titleMap[n]],
|
|
83
|
+
onSelect: () => editor.update(() => {
|
|
84
|
+
const selection = $getSelection();
|
|
85
|
+
if ($isRangeSelection(selection)) {
|
|
86
|
+
$setBlocksType(selection, () => $createHeadingNode(`h${n}`));
|
|
87
|
+
}
|
|
88
|
+
}),
|
|
89
|
+
});
|
|
90
|
+
}),
|
|
91
|
+
new ComponentPickerOption("일반 목록", {
|
|
92
|
+
icon: _jsx(ListUnorderedIcon, {}),
|
|
93
|
+
keywords: ["bulleted list", "unordered list", "ul"],
|
|
94
|
+
onSelect: () => editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, undefined),
|
|
95
|
+
}),
|
|
96
|
+
new ComponentPickerOption("숫자 목록", {
|
|
97
|
+
icon: _jsx(ListOrderedIcon, {}),
|
|
98
|
+
keywords: ["numbered list", "ordered list", "ol"],
|
|
99
|
+
onSelect: () => editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, undefined),
|
|
100
|
+
}),
|
|
101
|
+
new ComponentPickerOption("인용 블록", {
|
|
102
|
+
icon: _jsx(DoubleQuotesLIcon, {}),
|
|
103
|
+
keywords: ["block quote"],
|
|
104
|
+
onSelect: () => editor.update(() => {
|
|
105
|
+
const selection = $getSelection();
|
|
106
|
+
if ($isRangeSelection(selection)) {
|
|
107
|
+
$setBlocksType(selection, () => $createQuoteNode());
|
|
108
|
+
}
|
|
109
|
+
}),
|
|
110
|
+
}),
|
|
111
|
+
new ComponentPickerOption("코드 블록", {
|
|
112
|
+
icon: _jsx(CodeViewIcon, {}),
|
|
113
|
+
keywords: ["javascript", "python", "js", "codeblock"],
|
|
114
|
+
onSelect: () => editor.update(() => {
|
|
115
|
+
const selection = $getSelection();
|
|
116
|
+
if ($isRangeSelection(selection)) {
|
|
117
|
+
if (selection.isCollapsed()) {
|
|
118
|
+
$setBlocksType(selection, () => $createCodeNode());
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
// Will this ever happen?
|
|
122
|
+
const textContent = selection.getTextContent();
|
|
123
|
+
const codeNode = $createCodeNode();
|
|
124
|
+
selection.insertNodes([codeNode]);
|
|
125
|
+
selection.insertRawText(textContent);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}),
|
|
129
|
+
}),
|
|
130
|
+
new ComponentPickerOption("구분선", {
|
|
131
|
+
icon: _jsx(SeparatorIcon, {}),
|
|
132
|
+
keywords: ["horizontal rule", "divider", "hr"],
|
|
133
|
+
onSelect: () => editor.dispatchCommand(INSERT_HORIZONTAL_RULE_COMMAND, undefined),
|
|
134
|
+
}),
|
|
135
|
+
new ComponentPickerOption("이미지", {
|
|
136
|
+
icon: _jsx(ImageLineIcon, {}),
|
|
137
|
+
keywords: ["image", "photo", "picture", "file"],
|
|
138
|
+
onSelect: () => setOpen(true),
|
|
139
|
+
}),
|
|
140
|
+
];
|
|
141
|
+
}
|
|
142
|
+
export function ComponentPickerMenuPlugin() {
|
|
143
|
+
const [editor] = useLexicalComposerContext();
|
|
144
|
+
const [queryString, setQueryString] = useState(null);
|
|
145
|
+
const [open, setOpen] = useState(false);
|
|
146
|
+
const checkForTriggerMatch = useBasicTypeaheadTriggerMatch("/", {
|
|
147
|
+
minLength: 0,
|
|
148
|
+
});
|
|
149
|
+
const options = useMemo(() => {
|
|
150
|
+
const baseOptions = getBaseOptions(editor, setOpen);
|
|
151
|
+
if (!queryString) {
|
|
152
|
+
return baseOptions;
|
|
153
|
+
}
|
|
154
|
+
const regex = new RegExp(queryString, "i");
|
|
155
|
+
return [
|
|
156
|
+
...getDynamicOptions(editor, queryString),
|
|
157
|
+
...baseOptions.filter((option) => regex.test(option.title) ||
|
|
158
|
+
option.keywords.some((keyword) => regex.test(keyword))),
|
|
159
|
+
];
|
|
160
|
+
}, [editor, queryString, setOpen]);
|
|
161
|
+
const onSelectOption = useCallback((selectedOption, nodeToRemove, closeMenu, matchingString) => {
|
|
162
|
+
editor.update(() => {
|
|
163
|
+
nodeToRemove === null || nodeToRemove === void 0 ? void 0 : nodeToRemove.remove();
|
|
164
|
+
selectedOption.onSelect(matchingString);
|
|
165
|
+
closeMenu();
|
|
166
|
+
});
|
|
167
|
+
}, [editor]);
|
|
168
|
+
return (_jsxs(_Fragment, { children: [_jsx(InsertImageDialog, { open: open, activeEditor: editor, onClose: () => setOpen(false) }), _jsx(LexicalTypeaheadMenuPlugin, { onQueryChange: setQueryString, onSelectOption: onSelectOption, triggerFn: checkForTriggerMatch, options: options, anchorClassName: cssToClassName `
|
|
169
|
+
// TODO 어드민에서 기본 z-index가 1이라 추가함. 없앨 수도 있음
|
|
170
|
+
z-index: 1;
|
|
171
|
+
`, menuRenderFn: (anchorElementRef, { selectedIndex, selectOptionAndCleanUp, setHighlightedIndex }) => anchorElementRef.current && options.length
|
|
172
|
+
? ReactDOM.createPortal(_jsx(ComponentPickerMenuList, { options: options, selectedIndex: selectedIndex, selectOptionAndCleanUp: selectOptionAndCleanUp, setHighlightedIndex: setHighlightedIndex }), anchorElementRef.current)
|
|
173
|
+
: null })] }));
|
|
174
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Dispatch } from "react";
|
|
2
|
+
import { LexicalEditor } from "lexical";
|
|
3
|
+
export interface FloatingLinkEditorProps {
|
|
4
|
+
editor: LexicalEditor;
|
|
5
|
+
isLink: boolean;
|
|
6
|
+
setIsLink: Dispatch<boolean>;
|
|
7
|
+
anchorElem: HTMLElement;
|
|
8
|
+
isLinkEditMode: boolean;
|
|
9
|
+
setIsLinkEditMode: Dispatch<boolean>;
|
|
10
|
+
}
|
|
11
|
+
export declare function FloatingLinkEditor(props: FloatingLinkEditorProps): JSX.Element;
|