diff --git a/packages/core/src/extensions/background.js b/packages/core/src/extensions/background.js index 075ffdb..f14253b 100644 --- a/packages/core/src/extensions/background.js +++ b/packages/core/src/extensions/background.js @@ -1,4 +1,3 @@ -import { prefixClass } from "@/utils/prefix.js"; import { Mark, markInputRule, @@ -23,59 +22,19 @@ export default Mark.create({ return { name: "background", desc: "", + type: "default", // picker multicolor: true, HTMLAttributes: {}, - command: ({ editor, color = "" }) => { - if (editor.isActive("background", { color }) || !color) { + command: ({ editor, background = "" }) => { + if (editor.isActive("background", { background }) || !background) { editor.chain().focus().unsetBackground().run(); } else { - editor.chain().focus().setBackground({ color }).run(); + editor.chain().focus().setBackground({ background }).run(); } }, isDisabled: ({ editor }) => !editor.can().toggleBackground(), - isActive: ({ editor, color }) => editor.isActive("background", { color }), - colors: [ - { - name: "white", - color: `var(--${prefixClass}-background-white)`, - }, - { - name: "black", - color: `var(--${prefixClass}-background-black)`, - }, - { - name: "purple", - color: `var(--${prefixClass}-background-purple)`, - }, - { - name: "red", - color: `var(--${prefixClass}-background-red)`, - }, - { - name: "yellow", - color: `var(--${prefixClass}-background-yellow)`, - }, - { - name: "blue", - color: `var(--${prefixClass}-background-blue)`, - }, - { - name: "green", - color: `var(--${prefixClass}-background-green)`, - }, - { - name: "orange", - color: `var(--${prefixClass}-background-orange)`, - }, - { - name: "pink", - color: `var(--${prefixClass}-background-pink)`, - }, - { - name: "gray", - color: `var(--${prefixClass}-background-gray)`, - }, - ], + isActive: ({ editor, background }) => + editor.isActive("background", { background }), shortcutkeys: "Mod-Shift-H", }; }, @@ -86,18 +45,19 @@ export default Mark.create({ } return { - color: { + background: { default: null, parseHTML: (element) => - element.getAttribute("data-color") || element.style.backgroundColor, + element.getAttribute("data-background-color") || + element.style.backgroundColor, renderHTML: (attributes) => { - if (!attributes.color) { + if (!attributes.background) { return {}; } return { - "data-color": attributes.color, - style: `background-color: ${attributes.color}; color: inherit`, + "data-background-color": attributes.background, + style: `background-color: ${attributes.background}; color: inherit`, }; }, }, diff --git a/packages/core/src/extensions/color.js b/packages/core/src/extensions/color.js index 9a18ceb..977ba39 100644 --- a/packages/core/src/extensions/color.js +++ b/packages/core/src/extensions/color.js @@ -1,4 +1,3 @@ -import { prefixClass } from "@/utils/prefix.js"; import { Extension } from "@tiptap/core"; export default Extension.create({ @@ -7,10 +6,11 @@ export default Extension.create({ addOptions() { return { types: ["textStyle"], + type: "default", // picker title: "color", desc: "", command: ({ editor, color = "" }) => { - if (editor.isActive("textStyle", { color })) { + if (editor.isActive("textStyle", { color }) || !color) { editor.chain().focus().unsetColor().run(); } else { editor.chain().focus().setColor(color).run(); @@ -18,48 +18,6 @@ export default Extension.create({ }, isDisabled: ({ editor }) => !editor.can().setColor(), isActive: ({ editor, color }) => editor.isActive("textStyle", { color }), - colors: [ - { - name: "white", - color: `var(--${prefixClass}-text-white)`, - }, - { - name: "black", - color: `var(--${prefixClass}-text-black)`, - }, - { - name: "purple", - color: `var(--${prefixClass}-text-purple)`, - }, - { - name: "red", - color: `var(--${prefixClass}-text-red)`, - }, - { - name: "yellow", - color: `var(--${prefixClass}-text-yellow)`, - }, - { - name: "blue", - color: `var(--${prefixClass}-text-blue)`, - }, - { - name: "green", - color: `var(--${prefixClass}-text-green)`, - }, - { - name: "orange", - color: `var(--${prefixClass}-text-orange)`, - }, - { - name: "pink", - color: `var(--${prefixClass}-text-pink)`, - }, - { - name: "gray", - color: `var(--${prefixClass}-text-gray)`, - }, - ], }; }, diff --git a/packages/core/src/locales/en.json b/packages/core/src/locales/en.json index f54049f..6cf093a 100644 --- a/packages/core/src/locales/en.json +++ b/packages/core/src/locales/en.json @@ -70,5 +70,13 @@ "huge": "Huge" }, + "colors": { + "defaultColor": "Default Color", + "baseColor": "Base Color", + "standardColor": "Standard Color", + "recentUse": "Recent Use", + "palette": "Palette" + }, + "placeholder": "Write something ..." } diff --git a/packages/core/src/locales/zh.json b/packages/core/src/locales/zh.json index 0d9ff25..08b62fc 100644 --- a/packages/core/src/locales/zh.json +++ b/packages/core/src/locales/zh.json @@ -70,5 +70,13 @@ "huge": "超大" }, + "colors":{ + "defaultColor": "默认颜色", + "baseColor": "基础颜色", + "standardColor": "标准颜色", + "recentUse": "最近使用", + "palette": "调色板" + }, + "placeholder": "写点什么 ..." } diff --git a/packages/vue3/src/components/special-button/button-background.js b/packages/vue3/src/components/special-button/button-background.js index 4f46222..8768c61 100644 --- a/packages/vue3/src/components/special-button/button-background.js +++ b/packages/vue3/src/components/special-button/button-background.js @@ -1,7 +1,7 @@ import { defineComponent, ref, h, computed } from "vue"; import { prefixClass, t } from "@isle-editor/core"; -import { getIcon } from "@/utils/icon"; -import { ITooltip, IButton, ITrigger } from "@/components/ui"; +import { getIcon, getColors } from "@/utils"; +import { ITooltip, IButton, ITrigger, IColorPicker } from "@/components/ui"; export default defineComponent({ name: "ButtonBackground", @@ -19,11 +19,80 @@ export default defineComponent({ const isShown = ref(false); const activeColor = computed(() => { - return props.menu.colors.find((v) => - props.menu.isActive({ editor: props.editor, color: v.color }), - ); + const attrs = props.editor.getAttributes("background"); + return attrs?.background || ""; }); + function defaultModelRender() { + return h( + "div", + { + class: `${prefixClass}-special-button__background`, + }, + [ + h( + "div", + { class: `${prefixClass}-special-button__background-title` }, + [ + h( + "span", + { + class: `${prefixClass}-special-button__background-title-text`, + }, + t(props.menu.name), + ), + ], + ), + h("div", { class: `${prefixClass}-special-button__background-box` }, [ + ...getColors().map(({ background, color }) => + h("div", { + class: `${prefixClass}-special-button__background-box-item`, + style: { + backgroundColor: background, + borderColor: activeColor.value === background ? color : "", + }, + onClick: () => + props.menu.command({ + background, + editor: props.editor, + }), + }), + ), + ]), + ], + ); + } + + function pickerModelRender() { + return h( + "div", + { + class: `${prefixClass}-special-button__background`, + }, + [ + h(IColorPicker, { + defaultColorName: t("colors.defaultColor"), + standardColorName: t("colors.standardColor"), + recentUseName: t("colors.recentUse"), + storageKey: "ICOLORPICKER-RECENT-BACKGROUND", + onChange: (color) => { + if (color) { + props.menu.command({ + background: color, + editor: props.editor, + }); + } else { + props.menu.command({ + background: "", + editor: props.editor, + }); + } + }, + }), + ], + ); + } + return () => h( ITrigger, @@ -62,7 +131,7 @@ export default defineComponent({ { class: `${prefixClass}-special-button__icon-box`, style: { - backgroundColor: activeColor.value?.color, + backgroundColor: activeColor.value, }, }, [ @@ -86,49 +155,9 @@ export default defineComponent({ }, ), content: () => - h( - "div", - { - class: `${prefixClass}-special-button__background`, - }, - [ - h( - "div", - { class: `${prefixClass}-special-button__background-title` }, - [ - h( - "span", - { - class: `${prefixClass}-special-button__background-title-text`, - }, - t(props.menu.name), - ), - h("span", { - class: `${prefixClass}-special-button__background-title-default`, - onClick: () => - props.editor.chain().focus().unsetBackground().run(), - }), - ], - ), - h( - "div", - { class: `${prefixClass}-special-button__background-box` }, - [ - ...props.menu.colors.map(({ color }) => - h("div", { - class: `${prefixClass}-special-button__background-box-item`, - style: { backgroundColor: color }, - onClick: () => - props.menu.command({ - color: color, - editor: props.editor, - }), - }), - ), - ], - ), - ], - ), + props.menu?.type === "picker" + ? pickerModelRender() + : defaultModelRender(), }, ); }, diff --git a/packages/vue3/src/components/special-button/button-color.js b/packages/vue3/src/components/special-button/button-color.js index ee2eacb..3a9ea67 100644 --- a/packages/vue3/src/components/special-button/button-color.js +++ b/packages/vue3/src/components/special-button/button-color.js @@ -1,7 +1,7 @@ import { defineComponent, ref, h, computed } from "vue"; import { prefixClass, t } from "@isle-editor/core"; -import { getIcon } from "@/utils/icon"; -import { ITooltip, IButton, ITrigger } from "@/components/ui"; +import { getIcon, getColors } from "@/utils"; +import { ITooltip, IButton, ITrigger, IColorPicker } from "@/components/ui"; export default defineComponent({ name: "ButtonColor", @@ -19,11 +19,74 @@ export default defineComponent({ const isShown = ref(false); const activeColor = computed(() => { - return props.menu.colors.find((v) => - props.menu.isActive({ editor: props.editor, color: v.color }), - ); + const attrs = props.editor.getAttributes("textStyle"); + return attrs?.color || ""; }); + function defaultModelRender() { + return h("div", { class: `${prefixClass}-special-button__color` }, [ + h("div", { class: `${prefixClass}-special-button__color-title` }, [ + h( + "span", + { + class: `${prefixClass}-special-button__color-title-text`, + }, + t(props.menu.name), + ), + ]), + h("div", { class: `${prefixClass}-special-button__color-box` }, [ + ...getColors().map(({ color }) => + h( + "div", + { + class: `${prefixClass}-special-button__color-box-item`, + style: { + color, + borderColor: activeColor.value === color ? color : "", + }, + onClick: () => + props.menu.command({ + color: color, + editor: props.editor, + }), + }, + [ + h(getIcon(props.menu.name), { + class: `${prefixClass}-special-button__color-box-item-icon`, + size: 14, + strokeWidth: 2, + }), + ], + ), + ), + ]), + ]); + } + + function pickerModelRender() { + return h("div", { class: `${prefixClass}-special-button__color` }, [ + h(IColorPicker, { + defaultColorName: t("colors.defaultColor"), + standardColorName: t("colors.standardColor"), + recentUseName: t("colors.recentUse"), + storageKey: "ICOLORPICKER-RECENT-COLOR", + onChange: (color) => { + if (color) { + props.menu.command({ + color: color, + editor: props.editor, + }); + } else { + props.menu.command({ + color: "", + editor: props.editor, + }); + } + }, + }), + ]); + } + return () => h( ITrigger, @@ -61,7 +124,7 @@ export default defineComponent({ "div", { class: `${prefixClass}-special-button__icon-box`, - style: { color: activeColor.value?.color }, + style: { color: activeColor.value }, }, [ h(getIcon(props.menu.name || "color"), { @@ -84,49 +147,9 @@ export default defineComponent({ }, ), content: () => - h("div", { class: `${prefixClass}-special-button__color` }, [ - h( - "div", - { class: `${prefixClass}-special-button__color-title` }, - [ - h( - "span", - { - class: `${prefixClass}-special-button__color-title-text`, - }, - t(props.menu.name), - ), - h("span", { - class: `${prefixClass}-special-button__color-title-default`, - onClick: () => - props.editor.chain().focus().unsetColor().run(), - }), - ], - ), - h("div", { class: `${prefixClass}-special-button__color-box` }, [ - ...props.menu.colors.map(({ color }) => - h( - "div", - { - class: `${prefixClass}-special-button__color-box-item`, - style: { color }, - onClick: () => - props.menu.command({ - color: color, - editor: props.editor, - }), - }, - [ - h(getIcon(props.menu.name), { - class: `${prefixClass}-special-button__color-box-item-icon`, - size: 14, - strokeWidth: 2, - }), - ], - ), - ), - ]), - ]), + props.menu?.type === "picker" + ? pickerModelRender() + : defaultModelRender(), }, ); }, diff --git a/packages/vue3/src/components/ui/color-picker.js b/packages/vue3/src/components/ui/color-picker.js index d7a3aa3..87531ee 100644 --- a/packages/vue3/src/components/ui/color-picker.js +++ b/packages/vue3/src/components/ui/color-picker.js @@ -1,6 +1,11 @@ -import { defineComponent, h, ref, onMounted } from "vue"; +import { defineComponent, h } from "vue"; import { prefixClass } from "@isle-editor/core"; -import { getAllColors, standardColors } from "@/utils"; +import { + getPickerAllColorsFlat, + getAllStandardColors, + getRecentColors, + addRecentColor, +} from "@/utils"; export default defineComponent({ name: "IColorPicker", @@ -17,7 +22,7 @@ export default defineComponent({ type: String, default: "Standard Color", }, - recentColorName: { + recentUseName: { type: String, default: "Recent Use", }, @@ -26,10 +31,10 @@ export default defineComponent({ default: "ICOLORPICKER-RECENTCOLORS", }, }, - emits: ["select"], + emits: ["change"], setup(props, { emit }) { - const recentColors = ref([]); + const recentColors = getRecentColors(props.storageKey); const isShowRecentColor = computed(() => recentColors.value.length); function recentColorsRender() { @@ -40,7 +45,7 @@ export default defineComponent({ { class: `${prefixClass}-color-picker__title`, }, - props.recentColorName, + props.recentUseName, ), h( "div", @@ -59,37 +64,14 @@ export default defineComponent({ : []; } - onMounted(() => { - const stored = localStorage.getItem(props.storageKey); - if (stored && Array.isArray(JSON.parse(stored))) { - recentColors.value = JSON.parse(stored).slice(0, 9); - } else { - recentColors.value = []; - } - }); - const handleColorSelect = (color) => { if (color) { - const colorIndex = recentColors.value.indexOf(color); - if (colorIndex > -1) { - recentColors.value.splice(colorIndex, 1); - } - - recentColors.value.unshift(color); - - if (recentColors.value.length > 9) { - recentColors.value = recentColors.value.slice(0, 9); - } - - localStorage.setItem( - props.storageKey, - JSON.stringify(recentColors.value), - ); + addRecentColor(color, props.storageKey); } - emit("select", color); + emit("change", color); }; - const allColors = getAllColors(); + const allColors = getPickerAllColorsFlat(); return () => h( @@ -131,7 +113,7 @@ export default defineComponent({ { class: `${prefixClass}-color-picker__standard`, }, - standardColors.map((color) => { + getAllStandardColors().map((color) => { return h("div", { class: [`${prefixClass}-color-picker__standard-item`], style: { backgroundColor: color }, diff --git a/packages/vue3/src/styles/_vars.scss b/packages/vue3/src/styles/_vars.scss index d30dae3..543f881 100644 --- a/packages/vue3/src/styles/_vars.scss +++ b/packages/vue3/src/styles/_vars.scss @@ -1,51 +1,52 @@ -@use 'config' as *; +@use "config" as *; :root { + --#{$prefix}-color-base: var(--#{$prefix}-color-black); --#{$prefix}-color-white: rgba(253, 253, 253, 1); --#{$prefix}-color-black: rgba(3, 3, 5, 1); + --#{$prefix}-color-purple: #ae3ec9; + --#{$prefix}-color-red: #f03e3e; + --#{$prefix}-color-yellow: #f59f00; + --#{$prefix}-color-blue: #3b82f6; + --#{$prefix}-color-blue-val: 59, 130, 246; + --#{$prefix}-color-green: #37b24d; + --#{$prefix}-color-orange: #f76707; + --#{$prefix}-color-pink: #d6336c; + --#{$prefix}-color-gray: #787774; + --#{$prefix}-color-brown: #9f6b53; - --#{$prefix}-theme-primary: var(--#{$prefix}-text-blue); - --#{$prefix}-theme-primary-val: var(--#{$prefix}-text-blue-val); - - --#{$prefix}-text-white: var(--#{$prefix}-color-white); - --#{$prefix}-text-black: var(--#{$prefix}-color-black); - --#{$prefix}-text-purple: rgba(147, 51, 234, 1); - --#{$prefix}-text-red: rgba(225, 29, 72, 1); - --#{$prefix}-text-yellow: rgba(234, 179, 8, 1); - --#{$prefix}-text-blue: rgba(59, 130, 246, 1); - --#{$prefix}-text-blue-val: 59, 130, 246; - --#{$prefix}-text-green: rgba(22, 163, 74, 1); - --#{$prefix}-text-orange: rgba(249, 115, 22, 1); - --#{$prefix}-text-pink: rgba(186, 64, 129, 1); - --#{$prefix}-text-gray: rgba(168, 162, 158, 1); - + --#{$prefix}-background-base: var(--#{$prefix}-color-white); --#{$prefix}-background-white: var(--#{$prefix}-color-white); --#{$prefix}-background-black: var(--#{$prefix}-color-black); --#{$prefix}-background-purple: rgba(246, 243, 248, 1); --#{$prefix}-background-red: rgba(253, 235, 235, 1); - --#{$prefix}-background-yellow: rgba(251, 244, 162, 1); - --#{$prefix}-background-blue: rgba(193, 236, 249, 1); - --#{$prefix}-background-green: rgba(172, 247, 159, 1); + --#{$prefix}-background-yellow: rgba(251, 243, 219, 1); + --#{$prefix}-background-blue: rgba(231, 243, 248, 1); + --#{$prefix}-background-green: rgba(237, 243, 236, 1); --#{$prefix}-background-orange: rgba(250, 235, 221, 1); --#{$prefix}-background-pink: rgba(250, 241, 245, 1); --#{$prefix}-background-gray: rgba(241, 241, 239, 1); + --#{$prefix}-background-brown: rgba(244, 238, 238, 1); + + --#{$prefix}-theme-primary: var(--#{$prefix}-color-blue); + --#{$prefix}-theme-primary-val: var(--#{$prefix}-color-blue-val); - --#{$prefix}-color-border-val: 228, 228, 231; - --#{$prefix}-color-border: rgba(228, 228, 231, 1); - --#{$prefix}-color-border-inverse: rgba(39, 39, 42, 1); + --#{$prefix}-border-color-val: 228, 228, 231; + --#{$prefix}-border-color: rgba(228, 228, 231, 1); + --#{$prefix}-border-color-inverse: rgba(39, 39, 42, 1); - --#{$prefix}-color-text: var(--#{$prefix}-color-black); - --#{$prefix}-color-text-1: rgba(51, 51, 51, 1); - --#{$prefix}-color-text-2: rgba(113, 113, 122, 1); - --#{$prefix}-color-text-3: rgba(115, 115, 115, 1); - --#{$prefix}-color-text-inverse: var(--#{$prefix}-color-white); + --#{$prefix}-text-color: var(--#{$prefix}-color-black); + --#{$prefix}-text-color-1: rgba(51, 51, 51, 1); + --#{$prefix}-text-color-2: rgba(113, 113, 122, 1); + --#{$prefix}-text-color-3: rgba(135, 135, 134, 1); + --#{$prefix}-text-color-inverse: var(--#{$prefix}-color-white); - --#{$prefix}-color-background: var(--#{$prefix}-color-white); - --#{$prefix}-color-background-1: rgba(247, 247, 247, 1); - --#{$prefix}-color-background-2: rgba(239, 239, 239, 1); - --#{$prefix}-color-background-inverse: var(--#{$prefix}-color-black); + --#{$prefix}-background-color: var(--#{$prefix}-color-white); + --#{$prefix}-background-color-1: rgba(247, 247, 247, 1); + --#{$prefix}-background-color-2: rgba(239, 239, 239, 1); + --#{$prefix}-background-color-inverse: var(--#{$prefix}-color-black); - --#{$prefix}-color-shadow: rgba(0, 0, 0, 0.5); + --#{$prefix}-shadow-color: rgba(0, 0, 0, 0.5); --#{$prefix}-font-size: 1rem; --#{$prefix}-line-height: 1.5; @@ -53,45 +54,46 @@ --#{$prefix}-margin-mini: 0.5rem; } -[isle-theme='dark'] { - --#{$prefix}-text-white: var(--#{$prefix}-color-white); - --#{$prefix}-text-black: var(--#{$prefix}-color-black); - --#{$prefix}-text-purple: rgba(147, 51, 234, 1); - --#{$prefix}-text-red: rgba(225, 29, 72, 1); - --#{$prefix}-text-yellow: rgba(234, 179, 8, 1); - // --#{$prefix}-text-blue: rgba(59, 130, 246, 1); - --#{$prefix}-text-blue: rgba(96, 165, 250, 1); - --#{$prefix}-text-blue-val: 96, 165, 250; - --#{$prefix}-text-green: rgba(34, 197, 94, 1); - --#{$prefix}-text-orange: rgba(234, 88, 12, 1); - --#{$prefix}-text-pink: rgba(186, 64, 129, 1); - --#{$prefix}-text-gray: rgba(168, 162, 158, 1); +[isle-theme="dark"] { + --#{$prefix}-color-base: var(--#{$prefix}-color-white); + --#{$prefix}-color-purple: #9d68d3; + --#{$prefix}-color-red: #df5452; + --#{$prefix}-color-yellow: #ca9849; + --#{$prefix}-color-blue: #60a5fa; + --#{$prefix}-color-blue-val: 96, 165, 250; + --#{$prefix}-color-green: #529e72; + --#{$prefix}-color-orange: #c77d48; + --#{$prefix}-color-pink: #ba4081; + --#{$prefix}-color-gray: #a8a29e; + --#{$prefix}-color-brown: #ba856f; + --#{$prefix}-background-base: var(--#{$prefix}-color-black); --#{$prefix}-background-white: var(--#{$prefix}-color-white); --#{$prefix}-background-black: var(--#{$prefix}-color-black); - --#{$prefix}-background-purple: rgba(63, 44, 75, 1); - --#{$prefix}-background-red: rgba(92, 26, 26, 1); - --#{$prefix}-background-yellow: rgba(92, 75, 26, 1); - --#{$prefix}-background-blue: rgba(26, 61, 92, 1); - --#{$prefix}-background-green: rgba(26, 92, 32, 1); - --#{$prefix}-background-orange: rgba(92, 58, 26, 1); - --#{$prefix}-background-pink: rgba(92, 26, 58, 1); - --#{$prefix}-background-gray: rgba(58, 58, 58, 1); + --#{$prefix}-background-purple: rgba(60, 45, 73, 1); + --#{$prefix}-background-red: rgba(82, 46, 42, 1); + --#{$prefix}-background-yellow: rgba(86, 67, 40, 1); + --#{$prefix}-background-blue: rgba(20, 58, 78, 1); + --#{$prefix}-background-green: rgba(36, 61, 48, 1); + --#{$prefix}-background-orange: rgba(92, 59, 35, 1); + --#{$prefix}-background-pink: rgba(78, 44, 60, 1); + --#{$prefix}-background-gray: rgba(47, 47, 47, 1); + --#{$prefix}-background-brown: rgba(74, 50, 40, 1); - --#{$prefix}-color-border-val: 39, 39, 42; - --#{$prefix}-color-border: rgba(39, 39, 42, 1); - --#{$prefix}-color-border-inverse: rgba(229, 229, 229, 1); + --#{$prefix}-border-color-val: 39, 39, 42; + --#{$prefix}-border-color: rgba(39, 39, 42, 1); + --#{$prefix}-border-color-inverse: rgba(229, 229, 229, 1); - --#{$prefix}-color-text: var(--#{$prefix}-color-white); - --#{$prefix}-color-text-1: rgba(229, 229, 229, 1); - --#{$prefix}-color-text-2: rgba(212, 212, 212, 1); - --#{$prefix}-color-text-3: rgba(161, 161, 170, 1); - --#{$prefix}-color-text-inverse: var(--#{$prefix}-color-black); + --#{$prefix}-text-color: var(--#{$prefix}-color-white); + --#{$prefix}-text-color-1: rgba(229, 229, 229, 1); + --#{$prefix}-text-color-2: rgba(212, 212, 212, 1); + --#{$prefix}-text-color-3: rgba(161, 161, 170, 1); + --#{$prefix}-text-color-inverse: var(--#{$prefix}-color-black); - --#{$prefix}-color-background: var(--#{$prefix}-color-black); - --#{$prefix}-color-background-1: rgba(39, 39, 42, 1); - --#{$prefix}-color-background-2: rgba(55, 55, 65, 1); - --#{$prefix}-color-background-inverse: var(--#{$prefix}-color-white); + --#{$prefix}-background-color: var(--#{$prefix}-color-black); + --#{$prefix}-background-color-1: rgba(39, 39, 42, 1); + --#{$prefix}-background-color-2: rgba(55, 55, 65, 1); + --#{$prefix}-background-color-inverse: var(--#{$prefix}-color-white); - --#{$prefix}-color-shadow: rgba(255, 255, 255, 0.5); + --#{$prefix}-shadow-color: rgba(255, 255, 255, 0.5); } diff --git a/packages/vue3/src/styles/blockquote.scss b/packages/vue3/src/styles/blockquote.scss index ed7f73a..7738d69 100644 --- a/packages/vue3/src/styles/blockquote.scss +++ b/packages/vue3/src/styles/blockquote.scss @@ -1,10 +1,10 @@ -@use 'config' as *; +@use "config" as *; .#{$prefix}.ProseMirror { blockquote { padding-left: 0.6rem !important; - border-left: 0.3rem solid var(--#{$prefix}-color-border); - color: var(--#{$prefix}-color-text-secondary) !important; + border-left: 0.3rem solid var(--#{$prefix}-border-color); + color: var(--#{$prefix}-text-color-secondary) !important; margin: var(--#{$prefix}-margin) 0; } } diff --git a/packages/vue3/src/styles/code.scss b/packages/vue3/src/styles/code.scss index cf56024..5c349a4 100644 --- a/packages/vue3/src/styles/code.scss +++ b/packages/vue3/src/styles/code.scss @@ -1,4 +1,4 @@ -@use 'config' as *; +@use "config" as *; .#{$prefix}.ProseMirror { code, @@ -7,8 +7,8 @@ line-height: normal; font-size: 90%; border-radius: 0.25rem; - background-color: rgba(var(--#{$prefix}-color-border-val), 0.55); - color: var(--#{$prefix}-text-red); + background-color: rgba(var(--#{$prefix}-border-color-val), 0.55); + color: var(--#{$prefix}-text-color); box-decoration-break: clone; } -} \ No newline at end of file +} diff --git a/packages/vue3/src/styles/divider.scss b/packages/vue3/src/styles/divider.scss index 1e35862..b142b54 100644 --- a/packages/vue3/src/styles/divider.scss +++ b/packages/vue3/src/styles/divider.scss @@ -1,9 +1,9 @@ -@use 'config' as *; +@use "config" as *; .#{$prefix}.ProseMirror { hr { border: none; - border-top: 1px solid var(--#{$prefix}-color-border); + border-top: 1px solid var(--#{$prefix}-border-color); margin: var(--#{$prefix}-margin) 0; } } diff --git a/packages/vue3/src/styles/link.scss b/packages/vue3/src/styles/link.scss index ffd107e..24ef7c3 100644 --- a/packages/vue3/src/styles/link.scss +++ b/packages/vue3/src/styles/link.scss @@ -1,9 +1,8 @@ -@use 'config' as *; +@use "config" as *; .#{$prefix}.ProseMirror { a { color: var(--#{$prefix}-theme-primary); - // color: var(--#{$prefix}-color-text-secondary); font-weight: bold; text-decoration: underline; cursor: pointer; diff --git a/packages/vue3/src/styles/list.scss b/packages/vue3/src/styles/list.scss index 057fa86..ed4b61b 100644 --- a/packages/vue3/src/styles/list.scss +++ b/packages/vue3/src/styles/list.scss @@ -1,4 +1,4 @@ -@use 'config' as *; +@use "config" as *; .#{$prefix}.ProseMirror { ol, @@ -47,14 +47,14 @@ ::marker { font-weight: 400; - color: var(--#{$prefix}-color-text-1); + color: var(--#{$prefix}-text-color-1); font-size: 0.875rem; font-weight: 700; } } // 任务列表 - ul[data-type='taskList'] { + ul[data-type="taskList"] { list-style: none; padding: 0 0; box-sizing: border-box; @@ -75,35 +75,34 @@ // margin-top: 0; // } - ul[data-type='taskList'] li > label { + ul[data-type="taskList"] li > label { user-select: none; } - ul[data-type='taskList'] li > label input[type='checkbox'] { + ul[data-type="taskList"] li > label input[type="checkbox"] { -webkit-appearance: none; appearance: none; background-color: transparent; - color: var(--#{$prefix}-color-text-1); + color: var(--#{$prefix}-text-color-1); cursor: pointer; width: 1.1em; height: 1.1em; position: relative; top: calc( - (var(--#{$prefix}-line-height) * var(--#{$prefix}-font-size) - 1.05em) / - 2 + (var(--#{$prefix}-line-height) * var(--#{$prefix}-font-size) - 1.05em) / 2 ); // border-radius: 0.15em; - border: 2px solid rgba(var(--#{$prefix}-color-border-val), 1); + border: 2px solid rgba(var(--#{$prefix}-border-color-val), 1); margin: 0 0.5rem 0 0; display: grid; place-content: center; &:active { - background-color: rgba(var(--#{$prefix}-color-border-val), 0.6); + background-color: rgba(var(--#{$prefix}-border-color-val), 0.6); } &::before { - content: ''; + content: ""; width: 0.7em; height: 0.7em; transform: scale(0); @@ -118,16 +117,16 @@ } } - ul[data-type='taskList'] - li[data-checked='true'] + ul[data-type="taskList"] + li[data-checked="true"] > label - input[type='checkbox'] { + input[type="checkbox"] { border: 2px solid var(--#{$prefix}-theme-primary); background-color: var(--#{$prefix}-theme-primary); color: var(--#{$prefix}-color-white); } - ul[data-type='taskList'] li[data-checked='true'] > div > p { + ul[data-type="taskList"] li[data-checked="true"] > div > p { text-decoration: line-through; text-decoration-thickness: 2px; color: var(--#{$prefix}-color-text-2); diff --git a/packages/vue3/src/styles/menu/bubble.scss b/packages/vue3/src/styles/menu/bubble.scss index f0b6256..087c8a0 100644 --- a/packages/vue3/src/styles/menu/bubble.scss +++ b/packages/vue3/src/styles/menu/bubble.scss @@ -9,8 +9,8 @@ flex-direction: row; align-items: center; gap: 0.125rem; - background-color: var(--#{$prefix}-color-background); - border: 1px solid var(--#{$prefix}-color-border); + background-color: var(--#{$prefix}-background-color); + border: 1px solid var(--#{$prefix}-border-color); box-sizing: border-box; &__btn-text { diff --git a/packages/vue3/src/styles/menu/slash.scss b/packages/vue3/src/styles/menu/slash.scss index 058b679..912fb62 100644 --- a/packages/vue3/src/styles/menu/slash.scss +++ b/packages/vue3/src/styles/menu/slash.scss @@ -10,8 +10,8 @@ flex-direction: column; align-items: flex-start; gap: 0.25rem; - background-color: var(--#{$prefix}-color-background); - border: 1px solid var(--#{$prefix}-color-border); + background-color: var(--#{$prefix}-background-color); + border: 1px solid var(--#{$prefix}-border-color); box-sizing: border-box; overflow-x: hidden; overflow-y: auto; @@ -35,18 +35,18 @@ font-weight: 500; padding: 0 0.375rem; border-radius: 0.375rem; - color: var(--#{$prefix}-color-text-2); + color: var(--#{$prefix}-text-color-2); box-sizing: border-box; cursor: pointer; &:hover { - background-color: rgba(var(--#{$prefix}-color-border-val), 0.5); - color: var(--#{$prefix}-color-text); + background-color: rgba(var(--#{$prefix}-border-color-val), 0.5); + color: var(--#{$prefix}-text-color); } &.active { - background-color: rgba(var(--#{$prefix}-color-border-val), 0.5); - color: var(--#{$prefix}-color-text); + background-color: rgba(var(--#{$prefix}-border-color-val), 0.5); + color: var(--#{$prefix}-text-color); } &-left { @@ -54,9 +54,6 @@ align-items: center; gap: 0.5rem; - &-icon { - } - &-title { font-size: 0.875rem; font-weight: 500; diff --git a/packages/vue3/src/styles/menu/special-button.scss b/packages/vue3/src/styles/menu/special-button.scss index 6830272..2864fbf 100644 --- a/packages/vue3/src/styles/menu/special-button.scss +++ b/packages/vue3/src/styles/menu/special-button.scss @@ -8,7 +8,7 @@ align-items: center; justify-content: center; border-radius: 0.3rem; - border: 1px solid var(--#{$prefix}-color-border); + border: 1px solid var(--#{$prefix}-border-color); box-sizing: border-box; } @@ -34,8 +34,8 @@ flex-direction: row; align-items: center; gap: 0.125rem; - background-color: var(--#{$prefix}-color-background); - border: 1px solid var(--#{$prefix}-color-border); + background-color: var(--#{$prefix}-background-color); + border: 1px solid var(--#{$prefix}-border-color); box-sizing: border-box; } @@ -45,7 +45,7 @@ height: 32px; border: none; outline: none; - background-color: rgba(var(--#{$prefix}-color-border-val), 0.5); + background-color: rgba(var(--#{$prefix}-border-color-val), 0.5); display: flex; align-items: center; justify-content: center; @@ -55,7 +55,7 @@ &-icon { margin-right: 0.5rem; - color: var(--#{$prefix}-color-text-1); + color: var(--#{$prefix}-text-color-1); } &-inner { @@ -92,23 +92,23 @@ } &-text { - font-size: 10px; - font-weight: 700; - color: var(--#{$prefix}-color-text-2); + font-size: 0.85em; + font-weight: 500; + color: var(--#{$prefix}-text-color-2); } &-default { width: 24px; height: 14px; border-radius: 0.175rem; - border: 1px solid var(--#{$prefix}-color-border); + border: 1px solid var(--#{$prefix}-border-color); cursor: pointer; box-sizing: border-box; position: relative; overflow: hidden; &:hover { - background-color: rgba(var(--#{$prefix}-color-border-val), 0.5); + background-color: rgba(var(--#{$prefix}-border-color-val), 0.5); } &::after, @@ -124,8 +124,8 @@ background: linear-gradient( to bottom right, transparent calc(50% - 0.5px), - var(--#{$prefix}-color-border) calc(50% - 0.5px), - var(--#{$prefix}-color-border) calc(50% + 0.5px), + var(--#{$prefix}-border-color) calc(50% - 0.5px), + var(--#{$prefix}-border-color) calc(50% + 0.5px), transparent calc(50% + 0.5px) ); } @@ -133,8 +133,8 @@ background: linear-gradient( to top right, transparent calc(50% - 0.5px), - var(--#{$prefix}-color-border) calc(50% - 0.5px), - var(--#{$prefix}-color-border) calc(50% + 0.5px), + var(--#{$prefix}-border-color) calc(50% - 0.5px), + var(--#{$prefix}-border-color) calc(50% + 0.5px), transparent calc(50% + 0.5px) ); } @@ -155,15 +155,16 @@ align-items: center; justify-content: center; border-radius: 0.375rem; - border: 1px solid var(--#{$prefix}-color-border); + border: 1px solid var(--#{$prefix}-border-color); box-sizing: border-box; &:hover { - background-color: rgba(var(--#{$prefix}-color-border-val), 0.5); + border-width: 2.5px; + box-sizing: border-box; } svg { - filter: drop-shadow(0 0 1px var(--#{$prefix}-color-shadow)); + filter: drop-shadow(0 0 1px var(--#{$prefix}-shadow-color)); } } } diff --git a/packages/vue3/src/styles/menu/toc.scss b/packages/vue3/src/styles/menu/toc.scss index 47648d2..3a305bb 100644 --- a/packages/vue3/src/styles/menu/toc.scss +++ b/packages/vue3/src/styles/menu/toc.scss @@ -12,7 +12,7 @@ font-weight: 700; text-transform: uppercase; margin-bottom: 0.5rem; - color: var(--#{$prefix}-color-text-2); + color: var(--#{$prefix}-text-color-2); padding: 0.35rem; box-sizing: border-box; } @@ -27,7 +27,7 @@ font-size: 0.875rem; line-height: 1.25rem; padding: 0.35rem; - color: var(--#{$prefix}-color-text-3); + color: var(--#{$prefix}-text-color-3); cursor: pointer; border-radius: 0.35rem; box-sizing: border-box; @@ -37,13 +37,13 @@ white-space: nowrap; &:hover { - color: var(--#{$prefix}-color-text-1); - background-color: rgba(var(--#{$prefix}-color-border-val), 0.5); + color: var(--#{$prefix}-text-color-1); + background-color: rgba(var(--#{$prefix}-border-color-val), 0.5); } &.active { - color: var(--#{$prefix}-color-text) !important; + color: var(--#{$prefix}-text-color) !important; background-color: rgba( - var(--#{$prefix}-color-border-val), + var(--#{$prefix}-border-color-val), 0.5 ) !important; } diff --git a/packages/vue3/src/styles/placeholder.scss b/packages/vue3/src/styles/placeholder.scss index 80119a2..e67fe20 100644 --- a/packages/vue3/src/styles/placeholder.scss +++ b/packages/vue3/src/styles/placeholder.scss @@ -1,10 +1,10 @@ -@use 'config' as *; +@use "config" as *; .#{$prefix}.ProseMirror { .#{$prefix}-empty:first-child::before { content: attr(data-placeholder); float: left; - color: var(--#{$prefix}-color-text-2); + color: var(--#{$prefix}-text-color-2); pointer-events: none; width: 100%; height: 0; @@ -14,7 +14,7 @@ .#{$prefix}-node-empty::before { content: attr(data-placeholder); float: left; - color: var(--#{$prefix}-color-text-2); + color: var(--#{$prefix}-text-color-2); pointer-events: none; width: 100%; height: 0; diff --git a/packages/vue3/src/styles/ui/button.scss b/packages/vue3/src/styles/ui/button.scss index 0a832cc..0bc9eea 100644 --- a/packages/vue3/src/styles/ui/button.scss +++ b/packages/vue3/src/styles/ui/button.scss @@ -11,23 +11,23 @@ background-color: transparent; box-sizing: border-box; cursor: pointer; - color: var(--#{$prefix}-color-text-1); + color: var(--#{$prefix}-text-color-1); &.active { - background-color: rgba(var(--#{$prefix}-color-border-val), 0.5); + background-color: rgba(var(--#{$prefix}-border-color-val), 0.5); color: var(--#{$prefix}-theme-primary) !important; } &.semi-active { - background-color: rgba(var(--#{$prefix}-color-border-val), 0.5); + background-color: rgba(var(--#{$prefix}-border-color-val), 0.5); } &.danger { - color: var(--#{$prefix}-text-red) !important; + color: var(--#{$prefix}-color-red) !important; } &.success { - color: var(--#{$prefix}-text-green) !important; + color: var(--#{$prefix}-color-green) !important; } &.disabled { - color: var(--#{$prefix}-color-border) !important; + color: var(--#{$prefix}-border-color) !important; background-color: transparent !important; cursor: not-allowed; } @@ -38,7 +38,7 @@ } &:hover { - background-color: rgba(var(--#{$prefix}-color-border-val), 0.5); - color: var(--#{$prefix}-color-text); + background-color: rgba(var(--#{$prefix}-border-color-val), 0.5); + color: var(--#{$prefix}-text-color); } } diff --git a/packages/vue3/src/styles/ui/color-picker.scss b/packages/vue3/src/styles/ui/color-picker.scss index 44c8aa8..353ecb6 100644 --- a/packages/vue3/src/styles/ui/color-picker.scss +++ b/packages/vue3/src/styles/ui/color-picker.scss @@ -6,7 +6,7 @@ &__title { font-size: 0.85em; font-weight: 500; - color: var(--#{$prefix}-color-text-2); + color: var(--#{$prefix}-text-color-3); margin-top: 0.7rem; margin-bottom: 0.4rem; } @@ -16,18 +16,18 @@ height: 28px; font-size: 0.85em; border-radius: 0.3rem; - border: 1px solid var(--#{$prefix}-color-border); + border: 1px solid var(--#{$prefix}-border-color); box-sizing: border-box; display: flex; align-items: center; justify-content: center; cursor: pointer; margin-bottom: 0.7rem; - color: var(--#{$prefix}-color-text-1); + color: var(--#{$prefix}-text-color-1); &:hover { - color: var(--#{$prefix}-color-text); - background-color: rgba(var(--#{$prefix}-color-border-val), 0.5); + color: var(--#{$prefix}-text-color); + background-color: rgba(var(--#{$prefix}-border-color-val), 0.5); } } @@ -46,17 +46,12 @@ border-radius: 0.3rem; cursor: pointer; transition: transform 0.2s ease; - border: 1px solid rgba(var(--#{$prefix}-color-border-val), 0.5); + border: 1px solid rgba(var(--#{$prefix}-border-color-val), 0.5); box-sizing: border-box; &:hover { transform: scale(1.15); } - - &--active { - border: 2px solid var(--#{$prefix}-color-primary); - transform: scale(1.1); - } } } diff --git a/packages/vue3/src/styles/ui/divider.scss b/packages/vue3/src/styles/ui/divider.scss index 9e3c540..8aa0435 100644 --- a/packages/vue3/src/styles/ui/divider.scss +++ b/packages/vue3/src/styles/ui/divider.scss @@ -6,14 +6,14 @@ &--horizontal { border: none; height: 0px; - border-top: 1px solid var(--#{$prefix}-color-border); + border-top: 1px solid var(--#{$prefix}-border-color); margin: 0.3rem 0; } &--vertical { border: none; width: 0px; - border-left: 1px solid var(--#{$prefix}-color-border); + border-left: 1px solid var(--#{$prefix}-border-color); margin: 0 0.3rem; } diff --git a/packages/vue3/src/styles/ui/tooltip.scss b/packages/vue3/src/styles/ui/tooltip.scss index 19051df..e00b8dd 100644 --- a/packages/vue3/src/styles/ui/tooltip.scss +++ b/packages/vue3/src/styles/ui/tooltip.scss @@ -5,9 +5,9 @@ height: auto; padding: 0.25rem 0.25rem; border-radius: 0.35rem; - background-color: var(--#{$prefix}-color-background-inverse); - color: var(--#{$prefix}-color-text-inverse); - border: 1px solid var(--#{$prefix}-color-border-inverse); + background-color: var(--#{$prefix}-background-color-inverse); + color: var(--#{$prefix}-text-color-inverse); + border: 1px solid var(--#{$prefix}-border-color-inverse); box-sizing: border-box; display: flex; align-items: center; @@ -29,7 +29,7 @@ align-items: center; padding: 0.2rem 0.35rem; border-radius: 0.2rem; - background-color: var(--#{$prefix}-color-border-inverse); + background-color: var(--#{$prefix}-background-color-inverse); box-sizing: border-box; } } diff --git a/packages/vue3/src/utils/color.js b/packages/vue3/src/utils/color.js index 6f2bffb..4a94301 100644 --- a/packages/vue3/src/utils/color.js +++ b/packages/vue3/src/utils/color.js @@ -1,5 +1,8 @@ +import { prefixClass } from "@isle-editor/core"; +import { ref } from "vue"; + // https://tailwindcss.com/docs/customizing-colors -export const standardColors = [ +const pickerStandardColors = [ "#f03e3e", "#f76707", "#f59f00", @@ -10,7 +13,7 @@ export const standardColors = [ "#d6336c", "#495057", ]; -export const baseColors = [ +const pickerBaseColors = [ { name: "black", shades: ["#000000", "#212529", "#868e96", "#dee2e6", "#f1f3f5", "#ffffff"], @@ -49,16 +52,84 @@ export const baseColors = [ }, ]; -// 获取所有颜色的扁平数组 -export const getAllColors = () => { - return baseColors.reduce((acc, color) => { +const colors = [ + { + name: "base", + color: `var(--${prefixClass}-color-base)`, + background: `var(--${prefixClass}-background-base)`, + }, + { + name: "gray", + color: `var(--${prefixClass}-color-gray)`, + background: `var(--${prefixClass}-background-gray)`, + }, + { + name: "purple", + color: `var(--${prefixClass}-color-purple)`, + background: `var(--${prefixClass}-background-purple)`, + }, + { + name: "red", + color: `var(--${prefixClass}-color-red)`, + background: `var(--${prefixClass}-background-red)`, + }, + { + name: "yellow", + color: `var(--${prefixClass}-color-yellow)`, + background: `var(--${prefixClass}-background-yellow)`, + }, + { + name: "blue", + color: `var(--${prefixClass}-color-blue)`, + background: `var(--${prefixClass}-background-blue)`, + }, + { + name: "green", + color: `var(--${prefixClass}-color-green)`, + background: `var(--${prefixClass}-background-green)`, + }, + { + name: "orange", + color: `var(--${prefixClass}-color-orange)`, + background: `var(--${prefixClass}-background-orange)`, + }, + { + name: "pink", + color: `var(--${prefixClass}-color-pink)`, + background: `var(--${prefixClass}-background-pink)`, + }, + { + name: "brown", + color: `var(--${prefixClass}-color-brown)`, + background: `var(--${prefixClass}-background-brown)`, + }, +]; + +// 获取所有颜色 +export const getColors = () => { + return colors; +}; + +// 获取所有标准颜色 +export const getAllStandardColors = () => { + return pickerStandardColors; +}; + +// 获取所有基础颜色 +export const getPickerAllColors = () => { + return pickerBaseColors; +}; + +// 获取所有基础颜色的扁平数组 +export const getPickerAllColorsFlat = () => { + return pickerBaseColors.reduce((acc, color) => { return acc.concat(color.shades); }, []); }; // 获取特定色阶的所有颜色 -export const getColorsByShade = (shade) => { - return baseColors +export const getPickerColorsByShade = (shade) => { + return pickerBaseColors .map((color) => { const found = color.shades.find((c) => c === shade); return found || null; @@ -67,7 +138,49 @@ export const getColorsByShade = (shade) => { }; // 获取某个颜色系列的所有色阶 -export const getShadesByColor = (colorName) => { - const color = baseColors.find((c) => c.name === colorName); +export const getPickerShadesByColor = (colorName) => { + const color = pickerBaseColors.find((c) => c.name === colorName); return color ? color.shades : []; }; + +const recentColorsMap = new Map(); +// 获取或创建一个最近使用颜色的响应式引用 +export function getRecentColors(key = "default", maxItems = 9) { + if (!recentColorsMap.has(key)) { + const colors = ref([]); + + // 初始化时从 localStorage 读取数据 + const stored = localStorage.getItem(key); + if (stored && Array.isArray(JSON.parse(stored))) { + colors.value = JSON.parse(stored).slice(0, maxItems); + } + + recentColorsMap.set(key, colors); + } + + return recentColorsMap.get(key); +} + +// 添加最近使用的颜色 +export function addRecentColor(color, key = "default", maxItems = 9) { + const recentColors = getRecentColors(key, maxItems); + + // 如果颜色已存在,先移除它 + const index = recentColors.value.indexOf(color); + if (index > -1) { + recentColors.value.splice(index, 1); + } + + // 添加到开头 + recentColors.value.unshift(color); + + // 限制数量 + if (recentColors.value.length > maxItems) { + recentColors.value = recentColors.value.slice(0, maxItems); + } + + // 保存到 localStorage + localStorage.setItem(key, JSON.stringify(recentColors.value)); + + return recentColors; +}