diff --git a/frontend/src/components/TextBlockBubbleMenu.vue b/frontend/src/components/TextBlockBubbleMenu.vue
index df44b032c..080543095 100644
--- a/frontend/src/components/TextBlockBubbleMenu.vue
+++ b/frontend/src/components/TextBlockBubbleMenu.vue
@@ -78,7 +78,6 @@
:class="{ 'bg-surface-gray-3': editor.isActive('strike') }">
-
-
-
-
-
-
-
-
+
+
+
+
+
-
-
+ @update:modelValue="setTextColor"
+ :show-input="true"
+ render-mode="inline" />
+
+
@@ -154,6 +148,14 @@ const settingLink = ref(false);
const textLink = ref("");
const openInNewTab = ref(false);
const linkInput = ref(null) as Ref;
+const colorSwatchBtn = ref(null);
+const colorPickerOpen = ref(false);
+const colorPickerWrapper = ref(null);
+const colorPickerStyle = ref>({
+ position: "fixed",
+ top: "0px",
+ left: "0px",
+});
const editorRef = computed(() => props.editor);
const isEditableRef = computed(() => props.isEditable);
@@ -165,9 +167,47 @@ const selectedColor = computed(() => {
return null;
});
+const toggleColorPicker = () => {
+ if (!colorPickerOpen.value) {
+ const rect = colorSwatchBtn.value?.getBoundingClientRect();
+ if (rect) {
+ colorPickerStyle.value = {
+ position: "fixed",
+ top: `${rect.bottom + 8}px`,
+ left: `${rect.left}px`,
+ };
+ }
+ }
+ colorPickerOpen.value = !colorPickerOpen.value;
+};
+
+const handleOutsideClick = (e: MouseEvent) => {
+ if (
+ colorPickerWrapper.value &&
+ !colorPickerWrapper.value.contains(e.target as Node) &&
+ !colorSwatchBtn.value?.contains(e.target as Node)
+ ) {
+ colorPickerOpen.value = false;
+ }
+};
+
+watch(colorPickerOpen, (open) => {
+ if (open) {
+ document.addEventListener("mousedown", handleOutsideClick);
+ } else {
+ document.removeEventListener("mousedown", handleOutsideClick);
+ }
+});
+
+watch(
+ () => props.isEditable,
+ (editable) => {
+ if (!editable) colorPickerOpen.value = false;
+ },
+);
+
const enableLinkInput = () => {
settingLink.value = true;
- // check if link is already set on selection
const link = props.editor?.isActive("link") ? props.editor?.getAttributes("link").href : null;
textLink.value = link || "";
openInNewTab.value = props.editor?.isActive("link")
@@ -208,38 +248,34 @@ const setHeading = (level: 1 | 2 | 3) => {
} else {
props.block.element = tag;
}
-
nextTick(() => {
props.block.selectBlock();
});
};
-// Text color functionality
const isEntireTextSelected = () => {
if (!props.editor) return false;
const { from, to } = props.editor.state.selection;
- const textContent = props.editor.state.doc.textContent;
- const textLength = textContent.length;
+ const textLength = props.editor.state.doc.textContent.length;
return from === 0 && to >= textLength;
};
const setTextColor = debounce((color: string | undefined) => {
+ if (!props.editor || props.editor.isDestroyed) return;
const colorValue = color as string;
if (!colorValue) {
- props.editor?.chain().focus().setColor(colorValue).run();
+ props.editor.chain().setColor("").run();
if (isEntireTextSelected()) {
props.block.setStyle("color", "");
}
return;
}
-
- props.editor?.chain().focus().setColor(colorValue).run();
+ props.editor.chain().setColor(colorValue).run();
if (isEntireTextSelected()) {
props.block.setStyle("color", colorValue);
}
}, 50);
-// Keyboard handling
const handleKeydown = (e: KeyboardEvent) => {
if (e.key === "k" && e.metaKey) {
const blockWarnings = {
@@ -247,7 +283,6 @@ const handleKeydown = (e: KeyboardEvent) => {
isLink: "You cannot add link inside a link block",
isButton: "You cannot add link inside a button block",
};
-
const blockType = Object.entries(blockWarnings).find(([type]) => (props.block as any)[type]());
if (blockType) {
toast.warning(blockType[1]);
@@ -294,4 +329,4 @@ const bubbleMenuOptions = {
defineExpose({
handleKeydown,
});
-
+
\ No newline at end of file