Files
Website/resources/js/components/SMEditor.vue
2023-02-21 16:13:58 +10:00

816 lines
27 KiB
Vue

<template>
<div class="editor">
<label v-if="label" :class="{ required: required }">{{ label }}</label>
<editor id="tinymce" v-model="editorContent" :init="init" />
<input
:id="computedId"
type="hidden"
:name="inputName"
:value="editorContent" />
</div>
</template>
<script setup lang="ts">
import "tinymce/tinymce";
import Editor from "@tinymce/tinymce-vue";
import "tinymce/themes/silver";
import "tinymce/icons/default";
import "tinymce/models/dom";
import "tinymce/plugins/image";
import "tinymce/plugins/media";
import "tinymce/plugins/table";
import "tinymce/plugins/lists";
import "tinymce/plugins/advlist";
import "tinymce/plugins/link";
import "tinymce/plugins/autolink";
import "tinymce/plugins/lists";
import "tinymce/plugins/link";
import "tinymce/plugins/image";
import "tinymce/plugins/charmap";
import "tinymce/plugins/searchreplace";
import "tinymce/plugins/visualblocks";
import "tinymce/plugins/code";
import "tinymce/plugins/fullscreen";
import "tinymce/plugins/preview";
import "tinymce/plugins/anchor";
import "tinymce/plugins/insertdatetime";
import "tinymce/plugins/media";
import "tinymce/plugins/help";
import "tinymce/plugins/table";
import "tinymce/plugins/importcss";
import "tinymce/plugins/directionality";
import "tinymce/plugins/visualchars";
import "tinymce/plugins/template";
import "tinymce/plugins/codesample";
import "tinymce/plugins/pagebreak";
import "tinymce/plugins/nonbreaking";
import "tinymce/plugins/emoticons";
import "tinymce/plugins/autosave";
import { ref, watch, computed, onUnmounted } from "vue";
import { arrayHasBasicMatch } from "../helpers/array";
import DialogMedia from "./dialogs/SMDialogMedia.vue";
import { openDialog } from "vue3-promise-dialog";
import { routes } from "../router";
const props = defineProps({
disabledEditor: {
type: Boolean,
required: false,
default: false,
},
srcContent: {
type: String,
required: false,
default: "",
},
inputId: {
type: String,
required: false,
default: "",
},
inputName: {
type: String,
required: false,
default: "content",
},
placeholder: {
type: String,
required: false,
default: "",
},
label: {
type: String,
default: "",
required: false,
},
required: {
type: Boolean,
required: false,
},
mimeTypes: {
type: Array,
default() {
return [];
},
required: false,
},
removeButtons: {
type: Array,
required: false,
default() {
return [];
},
},
});
const init = {
promotion: false,
// emoticons_database_url: "/tinymce/plugins/emoticons/js/emojis.min.js",
skin_url: "/tinymce/skins/ui/oxide",
content_css: "/tinymce/skins/content/default/content.min.css",
height: 500,
plugins: [
"link",
"autolink",
"lists",
"advlist",
"image",
"table",
"charmap",
"searchreplace",
"visualblocks",
"code",
"fullscreen",
"preview",
"anchor",
"insertdatetime",
"media",
"help",
"codesample",
"pagebreak",
"nonbreaking",
"importcss",
"directionality",
"visualchars",
// "emoticons",
"autosave",
],
toolbar:
"undo redo | bold italic underline strikethrough | fontselect fontsizeselect formatselect | alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist | forecolor backcolor removeformat | pagebreak | charmap emoticons | fullscreen preview save print | insertfile image media template link anchor codesample | ltr rtl",
branding: false,
menubar: false,
toolbar_mode: "wrap",
link_list: [
{ title: "Tiny Home Page", value: "https://www.tiny.cloud" },
{ title: "Tiny Blog", value: "https://www.tiny.cloud/blog" },
{
title: "TinyMCE Documentation",
value: "https://www.tiny.cloud/docs/",
},
{
title: "TinyMCE on Stack Overflow",
value: "https://stackoverflow.com/questions/tagged/tinymce",
},
{ title: "TinyMCE GitHub", value: "https://github.com/tinymce/" },
],
// https://www.tiny.cloud/docs/configure/file-image-upload/#images_upload_handler
images_upload_handler: (blobInfo, success, failure) => {
console.log(blobInfo);
console.log(success);
console.log(failure);
const img = "data:image/jpeg;base64," + blobInfo.base64();
console.log(img);
success(img);
},
};
const trix = ref(null);
const editorContent = ref(props.srcContent);
const isActive = ref(null);
const isInitalized = ref(false);
const initalizeQueue = ref([]);
const emits = defineEmits([
"input",
"update",
"update:srcContent",
"trix-file-accept",
"trix-attachment-add",
"trix-attachment-remove",
"trix-selection-change",
"trix-initialize",
"trix-before-initialize",
"trix-focus",
"trix-blur",
]);
const handleContentChange = (event) => {
editorContent.value = event.srcElement
? event.srcElement.value
: event.target.value;
emits("input", editorContent.value);
};
const handleInitialize = () => {
isInitalized.value = true;
if (props.removeButtons) {
props.removeButtons.forEach((b) => {
trix.value.toolbarElement
.querySelectorAll(`[data-trix-attribute="${b}"]`)
.forEach((e) => e.remove());
trix.value.toolbarElement
.querySelectorAll(`[data-trix-action="${b}"]`)
.forEach((e) => e.remove());
});
}
// if(!props.allowMedia) {
// trix.value.toolbarElement.querySelectorAll('[data-trix-action="attachFiles"]').forEach(e => e.remove())
// }
initalizeQueue.value.forEach((item) => item());
decorateDisabledEditor(props.disabledEditor);
emits("trix-initialize");
};
const handleInitialContentChange = (newContent, oldContent) => {
newContent = newContent === undefined ? "" : newContent;
// if (trix.value && trix.value.innerHTML !== newContent) {
editorContent.value = newContent;
// }
// if (!isActive.value) {
// reloadEditorContent(editorContent.value);
// }
};
const emitEditorState = (value) => {
emits("update", editorContent.value);
emits("update:srcContent", editorContent.value);
};
const emitFileAccept = (event) => {
if (props.mimeTypes) {
if (!arrayHasBasicMatch(props.mimeTypes, event.file.type)) {
window.alert("That file type is not supported");
event.preventDefault();
return;
}
}
emits("trix-file-accept", event);
};
const emitAttachmentAdd = (event) => {
emits("trix-attachment-add", event);
};
const emitAttachmentRemove = (event) => {
emits("trix-attachment-remove", event);
};
const emitSelectionChange = (event) => {
emits("trix-selection-change", trix.value.editor, event);
};
const emitBeforeInitialize = async (event) => {
whenInitalized(() => {
emits("trix-before-initialize", trix.value.editor, event);
});
};
const processTrixFocus = (event) => {
isActive.value = true;
emits("trix-focus", trix.value.editor, event);
};
const processTrixBlur = (event) => {
isActive.value = false;
emits("trix-blur", trix.value.editor, event);
};
const whenInitalized = (func) => {
if (isInitalized.value) {
func();
} else {
initalizeQueue.value.push(func);
}
};
const reloadEditorContent = async (newContent) => {
whenInitalized(() => {
// trix.value.editor.loadHTML(newContent);
// trix.value.editor.setSelectedRange(getContentEndPosition());
// console.log(Trix.config);
// console.log(trix.value.toolbarElement);
});
};
const decorateDisabledEditor = async (editorState) => {
whenInitalized(() => {
if (editorState) {
trix.value.toolbarElement.style["pointer-events"] = "none";
trix.value.contentEditable = false;
trix.value.style["background"] = "#e9ecef";
} else {
trix.value.toolbarElement.style["pointer-events"] = "unset";
trix.value.style["pointer-events"] = "unset";
trix.value.style["background"] = "#ffffff";
}
});
};
const getContentEndPosition = () => {
return trix.value.editor.getDocument().toString().length - 1;
};
const randomId = () => {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
var r = (Math.random() * 16) | 0;
var v = c === "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
};
const generatedId = computed(() => {
return randomId();
});
const computedId = computed(() => {
return props.inputId || generatedId.value;
});
const initialContent = computed(() => {
return props.srcContent;
});
const isDisabled = computed(() => {
return props.disabledEditor;
});
watch(editorContent, emitEditorState);
watch(initialContent, handleInitialContentChange);
watch(isDisabled, decorateDisabledEditor);
/** Extra Toolbar Buttons */
const addToolbarButton = async (name, options, func) => {
if (props.removeButtons && props.removeButtons.includes(name)) {
return;
}
whenInitalized(() => {
options.type = options.type || "attribute";
options.icon = options.icon || "?";
options.group = options.group || "text";
options.position = options.position || "beforeend";
options.id = options.id || randomId();
options.attribute = options.attribute || name;
if (
options.trixAttribute &&
options.trixAttribute.type &&
options.trixAttribute.data &&
Trix.config[options.trixAttribute.type + "Attributes"]
) {
Trix.config[options.trixAttribute.type + "Attributes"][name] =
options.trixAttribute.data;
}
if (options.html) {
options.html = options.html.replace(/%id%/gi, options.id);
if (func) {
options.html = options.html.replace(
/%func\((.*?)\)%/gi,
`Trix.$extensions.${name}(event, '${name}', '${options.id}', $1)`
);
}
}
if (func) {
if (Trix.$extensions === undefined) Trix.$extensions = {};
Trix.$extensions[name] = func;
}
trix.value.toolbarElement
.querySelector(
`.trix-button-group.trix-button-group--${options.group}-tools`
)
.insertAdjacentHTML(
options.position,
`${
options.divWrap
? '<div style="position:relative" class="trix-button trix-button--icon">'
: ""
}<button type="button" ${
options.divWrap
? ""
: 'class="trix-button trix-button--icon"'
} data-trix-${options.type}="${
options.type == "attribute" ? name : name
}" ${
func
? `onClick="Trix.$extensions.${name}(event, '${name}', '${options.id}', 'click')"`
: ""
} ${options.title ? `title="${options.title}"` : ""}>${
options.icon
}</button>${options.html ? `${options.html}` : ""}${
options.divWrap ? "</div>" : ""
}`
);
if (options.type == "attribute" && options.dialog) {
trix.value.toolbarElement
.querySelector(`.trix-dialogs`)
.insertAdjacentHTML(
"beforeend",
`<div class="trix-dialog trix-dialog--${name}" data-trix-dialog="${name}" data-trix-dialog-attribute="${options.attribute}">${options.dialog}</div>`
);
}
});
};
/* Foreground and Background Colors - Based on https://github.com/basecamp/trix/issues/985 */
const foregroundColor = {
icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M512 256c0 .9 0 1.8 0 2.7c-.4 36.5-33.6 61.3-70.1 61.3H344c-26.5 0-48 21.5-48 48c0 3.4 .4 6.7 1 9.9c2.1 10.2 6.5 20 10.8 29.9c6.1 13.8 12.1 27.5 12.1 42c0 31.8-21.6 60.7-53.4 62c-3.5 .1-7 .2-10.6 .2C114.6 512 0 397.4 0 256S114.6 0 256 0S512 114.6 512 256zM128 288c0-17.7-14.3-32-32-32s-32 14.3-32 32s14.3 32 32 32s32-14.3 32-32zm0-96c17.7 0 32-14.3 32-32s-14.3-32-32-32s-32 14.3-32 32s14.3 32 32 32zM288 96c0-17.7-14.3-32-32-32s-32 14.3-32 32s14.3 32 32 32s32-14.3 32-32zm96 96c17.7 0 32-14.3 32-32s-14.3-32-32-32s-32 14.3-32 32s14.3 32 32 32z"/></svg>',
group: "text",
position: "beforeend",
title: "Text colour",
html: '<input type="color" style="position:absolute;top:0;left:0;height:100%;width:100%;opacity:0" id="%ID%-picker" onchange="%func(\'colorChanged\')%" />',
divWrap: true,
trixAttribute: {
type: "text",
data: {
styleProperty: "color",
inheritable: true,
},
},
};
const backgroundColor = {
icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M41.4 9.4C53.9-3.1 74.1-3.1 86.6 9.4L168 90.7l53.1-53.1c28.1-28.1 73.7-28.1 101.8 0L474.3 189.1c28.1 28.1 28.1 73.7 0 101.8L283.9 481.4c-37.5 37.5-98.3 37.5-135.8 0L30.6 363.9c-37.5-37.5-37.5-98.3 0-135.8L122.7 136 41.4 54.6c-12.5-12.5-12.5-32.8 0-45.3zm176 221.3L168 181.3 75.9 273.4c-4.2 4.2-7 9.3-8.4 14.6H386.7l42.3-42.3c3.1-3.1 3.1-8.2 0-11.3L277.7 82.9c-3.1-3.1-8.2-3.1-11.3 0L213.3 136l49.4 49.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0zM512 512c-35.3 0-64-28.7-64-64c0-25.2 32.6-79.6 51.2-108.7c6-9.4 19.5-9.4 25.5 0C543.4 368.4 576 422.8 576 448c0 35.3-28.7 64-64 64z"/></svg>',
group: "text",
position: "beforeend",
title: "Background colour",
html: '<input type="color" style="position:absolute;top:0;left:0;height:100%;width:100%;opacity:0" id="%ID%-picker" onchange="%func(\'colorChanged\')%" />',
divWrap: true,
trixAttribute: {
type: "text",
data: {
styleProperty: "backgroundColor",
inheritable: true,
},
},
};
const fgBgColorFunc = (event, name, id, data) => {
var picker = document.getElementById(id + "-picker");
if (data == "colorChanged") {
trix.value.editor.activateAttribute(name, picker.value);
}
};
addToolbarButton("foreground", foregroundColor, fgBgColorFunc);
addToolbarButton("background", backgroundColor, fgBgColorFunc);
/* Text align center button - No function needed for this button */
addToolbarButton("textAlignCenter", {
icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M352 64c0-17.7-14.3-32-32-32H128c-17.7 0-32 14.3-32 32s14.3 32 32 32H320c17.7 0 32-14.3 32-32zm96 128c0-17.7-14.3-32-32-32H32c-17.7 0-32 14.3-32 32s14.3 32 32 32H416c17.7 0 32-14.3 32-32zM0 448c0 17.7 14.3 32 32 32H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H32c-17.7 0-32 14.3-32 32zM352 320c0-17.7-14.3-32-32-32H128c-17.7 0-32 14.3-32 32s14.3 32 32 32H320c17.7 0 32-14.3 32-32z"/></svg>',
group: "block",
position: "beforeend",
title: "Align text center",
trixAttribute: {
type: "block",
data: {
tagName: "centered",
},
},
});
/* Remove all formatting button */
addToolbarButton(
"removeFormatting",
{
type: "action",
icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M290.7 57.4L57.4 290.7c-25 25-25 65.5 0 90.5l80 80c12 12 28.3 18.7 45.3 18.7H288h9.4H512c17.7 0 32-14.3 32-32s-14.3-32-32-32H387.9L518.6 285.3c25-25 25-65.5 0-90.5L381.3 57.4c-25-25-65.5-25-90.5 0zM297.4 416H288l-105.4 0-80-80L227.3 211.3 364.7 348.7 297.4 416z"/></svg>',
group: "text",
position: "beforeend",
title: "Remove formatting",
},
(event, name, id, data) => {
// let removeAttrs = ['bold', 'italic', 'strike', 'href', 'foreground', 'background', 'bullet']
// removeAttrs.forEach(attr => trix.value.editor.deactivateAttribute(attr))
Object.keys(Trix.config.textAttributes)
.concat(Object.keys(Trix.config.blockAttributes))
.forEach((attr) => trix.value.editor.deactivateAttribute(attr));
}
);
/** Media Selector */
addToolbarButton(
"media",
{
type: "action",
icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M256 0H576c35.3 0 64 28.7 64 64V288c0 35.3-28.7 64-64 64H256c-35.3 0-64-28.7-64-64V64c0-35.3 28.7-64 64-64zM476 106.7C471.5 100 464 96 456 96s-15.5 4-20 10.7l-56 84L362.7 169c-4.6-5.7-11.5-9-18.7-9s-14.2 3.3-18.7 9l-64 80c-5.8 7.2-6.9 17.1-2.9 25.4s12.4 13.6 21.6 13.6h80 48H552c8.9 0 17-4.9 21.2-12.7s3.7-17.3-1.2-24.6l-96-144zM336 96c0-17.7-14.3-32-32-32s-32 14.3-32 32s14.3 32 32 32s32-14.3 32-32zM64 128h96V384v32c0 17.7 14.3 32 32 32H320c17.7 0 32-14.3 32-32V384H512v64c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V192c0-35.3 28.7-64 64-64zm8 64c-8.8 0-16 7.2-16 16v16c0 8.8 7.2 16 16 16H88c8.8 0 16-7.2 16-16V208c0-8.8-7.2-16-16-16H72zm0 104c-8.8 0-16 7.2-16 16v16c0 8.8 7.2 16 16 16H88c8.8 0 16-7.2 16-16V312c0-8.8-7.2-16-16-16H72zm0 104c-8.8 0-16 7.2-16 16v16c0 8.8 7.2 16 16 16H88c8.8 0 16-7.2 16-16V416c0-8.8-7.2-16-16-16H72zm336 16v16c0 8.8 7.2 16 16 16h16c8.8 0 16-7.2 16-16V416c0-8.8-7.2-16-16-16H424c-8.8 0-16 7.2-16 16z"/></svg>',
group: "file",
title: "Insert Media",
},
async (event, name, id, data) => {
let result = await openDialog(DialogMedia);
if (result.url) {
trix.value.editor.insertHTML(`<img src="${result.url}" />`);
}
}
);
/* Update Link Button */
let hrefDialogObserver = null;
let hrefDialogObserverTarget = null;
onUnmounted(() => {
if (hrefDialogObserver != null && hrefDialogObserverTarget != null) {
hrefDialogObserver.unobserve(hrefDialogObserverTarget);
hrefDialogObserver = null;
hrefDialogObserverTarget = null;
}
});
const buildPageList = (
pageList,
routeEntries,
prefix_url = "",
prefix_title = ""
) => {
routeEntries.forEach((entry) => {
if ("path" in entry && "meta" in entry && "title" in entry.meta) {
const sep = entry.path.substring(0, 1) == "/" ? "" : "/";
pageList[prefix_url + sep + entry.path] =
prefix_title + sep + entry.meta.title;
}
if ("children" in entry) {
buildPageList(
pageList,
entry.children,
prefix_url + entry.path,
prefix_title + (entry.meta?.title || "")
);
}
});
};
/**
*
* @param obj
*/
function sortProperties(obj) {
// convert object into array
var sortable = [];
for (var key in obj)
if (obj.hasOwnProperty(key)) sortable.push([key, obj[key]]); // each item is an array in format [key, value]
// sort items by value
sortable.sort(function (a, b) {
var x = a[1].toLowerCase(),
y = b[1].toLowerCase();
return x < y ? -1 : x > y ? 1 : 0;
});
obj = {};
sortable.forEach((item) => {
obj[item[0]] = item[1];
});
return obj; // array in format [ [ key1, val1 ], [ key2, val2 ], ... ]
}
whenInitalized(() => {
const hrefDialog = trix.value.toolbarElement.querySelector(
".trix-dialogs .trix-dialog.trix-dialog--link"
);
const hrefInput = hrefDialog.querySelector(".trix-input--dialog");
const handleHref = (event, name, id, data) => {
if (id == "select") {
document.getElementById("href-hidden").value =
import.meta.env.APP_URL + event.target.value;
document.getElementById("href-input").value = "";
} else if (id == "input") {
document.getElementById("href-hidden").value = event.target.value;
document.getElementById("href-select").value = "";
} else {
/* empty */
}
};
hrefDialogObserverTarget = hrefDialog;
if (hrefDialogObserver == null && hrefDialogObserverTarget != null) {
hrefDialogObserver = new IntersectionObserver((entries, observer) => {
entries.forEach((entry) => {
if (entry.intersectionRatio === 1) {
const hidden = document.getElementById("href-hidden");
if (hidden != null) {
if (hidden.value.startsWith(import.meta.env.APP_URL)) {
document.getElementById("href-select").value =
hidden.value.substring(
import.meta.env.APP_URL.length
);
document.getElementById("href-input").value = "";
} else {
document.getElementById("href-select").value = "";
document.getElementById("href-input").value =
hidden.value;
}
}
}
});
}).observe(hrefDialogObserverTarget);
}
if (Trix.$extensions === undefined) Trix.$extensions = {};
Trix.$extensions["href"] = handleHref;
let pageRoutes = {};
buildPageList(pageRoutes, routes);
pageRoutes = sortProperties(pageRoutes);
hrefInput.removeAttribute("required");
hrefInput.setAttribute("id", "href-hidden");
hrefInput.setAttribute(
"oninput",
"Trix.$extensions.href(event, 'href', 'href', 'change')"
);
hrefInput.insertAdjacentHTML(
"afterend",
`<div class="form-group"><label>URL:</label><input id="href-input" type="url" onchange="Trix.$extensions.href(event, 'href', 'input', 'change') data-trix-input"/></div>
<div class="form-group"><label>Page:</label><select id="href-select" onchange="Trix.$extensions.href(event, 'href', 'select', 'change')">
<option value=""></option>
${Object.keys(pageRoutes)
.map(function (key) {
return (
"<option value='" +
key +
"'>" +
pageRoutes[key] +
"</option>"
);
})
.join("")}
</select></div>`
);
});
</script>
<style lang="scss">
/* For the added button above */
centered {
display: block;
text-align: center;
}
/* Extra Trix Styles to support the above code*/
.trix-button-group {
.trix-button {
text-align: -webkit-center;
text-align: -moz-center;
svg {
height: 20px;
width: 20px;
opacity: 0.6;
display: block;
}
/* For buttons inside divWrap option */
button {
display: block;
border: none;
background: transparent;
height: 100%;
width: 100%;
}
}
}
@media only screen and (max-width: 768px) {
trix-toolbar .trix-button--icon {
height: 1.6rem;
}
.trix-button-group .trix-button svg {
height: 14px;
width: 14px;
}
}
/* My own theme */
.editor {
width: 100%;
margin-bottom: 1rem;
trix-editor {
border-radius: 12px;
border-color: $border-color;
min-height: 20rem;
padding: map-get($spacer, 3) map-get($spacer, 2);
background-color: #fff;
a span {
color: $primary-color !important;
}
pre {
word-wrap: break-word;
white-space: pre-wrap;
}
}
trix-toolbar .trix-dialog__link-fields {
flex-direction: column;
#href-hidden {
// display: none;
}
.form-group {
display: flex;
align-items: center;
width: 100%;
input,
select,
label {
margin: 0;
}
label {
width: 3rem;
}
}
.trix-button-group {
width: 100%;
flex-direction: row-reverse;
border: 0;
gap: 0.5rem;
.trix-button {
border-radius: 12px;
color: white;
font-weight: 800;
border-width: 2px;
border-style: solid;
transition: background-color 0.1s, color 0.1s;
cursor: pointer;
background-color: $secondary-color;
border-color: $secondary-color;
&:hover:not(:disabled) {
text-decoration: none;
color: $secondary-color;
background-color: #fff;
}
&:first-of-type {
background-color: $primary-color;
border-color: $primary-color;
&:hover:not(:disabled) {
color: $primary-color;
}
}
}
}
}
.trix-button-row {
flex-wrap: wrap;
gap: 0 0.5rem;
justify-content: flex-start;
}
.trix-button-group {
border-radius: 10px;
border-color: $border-color;
background-color: #fff;
margin-left: 0 !important;
.trix-button {
border-bottom: 0;
border-color: $border-color;
&::before {
background-size: 50%;
}
&:first-child {
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
}
&:last-child {
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
}
&:hover:not(:disabled) {
background-color: rgba(0, 0, 0, 0.1);
}
}
}
}
</style>