From 14aa5e3b28388a629e8b2f57bca4e9386ca4aab9 Mon Sep 17 00:00:00 2001 From: James Collins Date: Mon, 10 Jul 2023 16:05:00 +1000 Subject: [PATCH] additons --- package-lock.json | 74 ++++ package.json | 5 + resources/js/components/SMEditor.vue | 396 ++++++++++++++++-- resources/js/components/SMTab.vue | 5 + resources/js/components/SMTabGroup.vue | 25 +- .../js/components/dialogs/SMDialogMedia.vue | 308 ++++++-------- resources/js/extensions/danger.ts | 60 +++ resources/js/extensions/success.ts | 60 +++ resources/js/extensions/warning.ts | 60 +++ resources/views/app.blade.php | 70 +--- 10 files changed, 804 insertions(+), 259 deletions(-) create mode 100644 resources/js/extensions/danger.ts create mode 100644 resources/js/extensions/success.ts create mode 100644 resources/js/extensions/warning.ts diff --git a/package-lock.json b/package-lock.json index bf424c5..adcc240 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,11 @@ "packages": { "": { "dependencies": { + "@tiptap/extension-highlight": "^2.0.3", + "@tiptap/extension-image": "^2.0.3", + "@tiptap/extension-link": "^2.0.3", + "@tiptap/extension-subscript": "^2.0.3", + "@tiptap/extension-superscript": "^2.0.3", "@tiptap/extension-text-align": "^2.0.3", "@tiptap/extension-underline": "^2.0.3", "@tiptap/pm": "^2.0.3", @@ -1536,6 +1541,18 @@ "@tiptap/core": "^2.0.0" } }, + "node_modules/@tiptap/extension-highlight": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-2.0.3.tgz", + "integrity": "sha512-NrtibY8cZkIjZMQuHRrKd4php+plOvAoSo8g3uVFu275I/Ixt5HqJ53R4voCXs8W8BOBRs2HS2QX8Cjh79XhtA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, "node_modules/@tiptap/extension-history": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.0.3.tgz", @@ -1562,6 +1579,18 @@ "@tiptap/pm": "^2.0.0" } }, + "node_modules/@tiptap/extension-image": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.0.3.tgz", + "integrity": "sha512-hS9ZJwz0md07EHsC+o4NuuJkhCZsZn7TuRz/2CvRSj2fWFIz+40CyNAHf/2J0qNugG9ommXaemetsADeEZP9ag==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, "node_modules/@tiptap/extension-italic": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.0.3.tgz", @@ -1574,6 +1603,22 @@ "@tiptap/core": "^2.0.0" } }, + "node_modules/@tiptap/extension-link": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.0.3.tgz", + "integrity": "sha512-H72tXQ5rkVCkAhFaf08fbEU7EBUCK0uocsqOF+4th9sOlrhfgyJtc8Jv5EXPDpxNgG5jixSqWBo0zKXQm9s9eg==", + "dependencies": { + "linkifyjs": "^4.1.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0", + "@tiptap/pm": "^2.0.0" + } + }, "node_modules/@tiptap/extension-list-item": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.0.3.tgz", @@ -1622,6 +1667,30 @@ "@tiptap/core": "^2.0.0" } }, + "node_modules/@tiptap/extension-subscript": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-subscript/-/extension-subscript-2.0.3.tgz", + "integrity": "sha512-XFAEUaKxWRmTq7ePEF4aj7knelJPr2fTz0y/iSXydtS094LKwBHBzxatIZY3phrgfpDc+f51ycwarsgz27UJfg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-superscript": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-superscript/-/extension-superscript-2.0.3.tgz", + "integrity": "sha512-5EBjUvkw2SXL1e8C1i0UF26/GBNHxEbiNQKw7Shy88omVa4HTY+D8KWC/j29ZW/IomUbGPlbpXp1z+1TETzmyw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, "node_modules/@tiptap/extension-text": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.0.3.tgz", @@ -4465,6 +4534,11 @@ "uc.micro": "^1.0.1" } }, + "node_modules/linkifyjs": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.1.1.tgz", + "integrity": "sha512-zFN/CTVmbcVef+WaDXT63dNzzkfRBKT1j464NJQkV7iSgJU0sLBus9W0HBwnXK13/hf168pbrx/V/bjEHOXNHA==" + }, "node_modules/local-pkg": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", diff --git a/package.json b/package.json index 42b1e77..e65e72e 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,11 @@ "vitest": "^0.32.0" }, "dependencies": { + "@tiptap/extension-highlight": "^2.0.3", + "@tiptap/extension-image": "^2.0.3", + "@tiptap/extension-link": "^2.0.3", + "@tiptap/extension-subscript": "^2.0.3", + "@tiptap/extension-superscript": "^2.0.3", "@tiptap/extension-text-align": "^2.0.3", "@tiptap/extension-underline": "^2.0.3", "@tiptap/pm": "^2.0.3", diff --git a/resources/js/components/SMEditor.vue b/resources/js/components/SMEditor.vue index 5f6f474..1ccc605 100644 --- a/resources/js/components/SMEditor.vue +++ b/resources/js/components/SMEditor.vue @@ -2,21 +2,8 @@
- -
+ class="flex flex-wrap bg-white border border-gray rounded-t-2"> +
Heading 6 + + + +
-
+
+
-
+
+ + + + +
+
+ + +
+
+ + +
+
-
+
+ +
+
-
+
+ class="rounded-b-2 bg-white p-4 border-x border-b border-gray h-128 overflow-auto sm-editor" />
@@ -391,7 +648,18 @@ import { useEditor, EditorContent } from "@tiptap/vue-3"; import StarterKit from "@tiptap/starter-kit"; import Underline from "@tiptap/extension-underline"; import TextAlign from "@tiptap/extension-text-align"; +import Highlight from "@tiptap/extension-highlight"; import { Info } from "../extensions/info"; +import { Success } from "../extensions/success"; +import { Warning } from "../extensions/warning"; +import { Danger } from "../extensions/danger"; +import Subscript from "@tiptap/extension-subscript"; +import Superscript from "@tiptap/extension-superscript"; +import Link from "@tiptap/extension-link"; +import Image from "@tiptap/extension-image"; +import { openDialog } from "./SMDialog"; +import SMDialogMedia from "./dialogs/SMDialogMedia.vue"; +import { Media } from "../helpers/api.types"; const props = defineProps({ modelValue: { @@ -404,7 +672,31 @@ const emits = defineEmits(["update:modelValue"]); const editor = useEditor({ content: props.modelValue, - extensions: [StarterKit, Underline, TextAlign, Info], + extensions: [ + StarterKit, + Underline, + TextAlign.configure({ + types: [ + "heading", + "paragraph", + "info", + "success", + "warning", + "danger", + ], + }), + Highlight, + Info, + Success, + Warning, + Danger, + Subscript, + Superscript, + Link.configure({ + openOnClick: false, + }), + Image, + ], onUpdate: () => { emits("update:modelValue", editor.value.getHTML()); }, @@ -434,10 +726,60 @@ const updateNode = (event) => { case "h6": editor.value.chain().focus().setHeading({ level: 6 }).run(); break; + case "info": + editor.value.chain().focus().toggleInfo().run(); + break; + case "success": + editor.value.chain().focus().toggleSuccess().run(); + break; + case "warning": + editor.value.chain().focus().toggleWarning().run(); + break; + case "danger": + editor.value.chain().focus().toggleDanger().run(); + break; } } }; +const setLink = () => { + const previousUrl = editor.value.getAttributes("link").href; + const url = window.prompt("URL", previousUrl); + + // cancelled + if (url === null) { + return; + } + + // empty + if (url === "") { + editor.value.chain().focus().extendMarkRange("link").unsetLink().run(); + return; + } + + // update link + editor.value + .chain() + .focus() + .extendMarkRange("link") + .setLink({ href: url }) + .run(); +}; + +const setImage = async () => { + let result = await openDialog(SMDialogMedia); + if (result) { + const mediaResult = result as Media; + editor.value + .chain() + .focus() + .setImage({ + src: mediaResult.url, + }) + .run(); + } +}; + onBeforeUnmount(() => { editor.value.destroy(); }); diff --git a/resources/js/components/SMTab.vue b/resources/js/components/SMTab.vue index 13053ac..f9e686c 100644 --- a/resources/js/components/SMTab.vue +++ b/resources/js/components/SMTab.vue @@ -18,6 +18,11 @@ defineProps({ type: String, required: true, }, + hide: { + type: Boolean, + default: true, + required: false, + }, }); const selectedTab = inject("selectedTab"); diff --git a/resources/js/components/SMTabGroup.vue b/resources/js/components/SMTabGroup.vue index 6015a1f..ead39fa 100644 --- a/resources/js/components/SMTabGroup.vue +++ b/resources/js/components/SMTabGroup.vue @@ -42,17 +42,22 @@ const emits = defineEmits(["tabChanged", "update:modelValue"]); const slots = useSlots(); const tabs = ref( - slots.default().map((tab) => { - const { label, id } = tab.props; - return { - label, - id, - }; - }) + slots + .default() + .map((tab) => { + const { label, id, hide } = tab.props; + if (hide !== true) { + return { + label, + id, + }; + } + }) + .filter(Boolean), ); const selectedTab = ref( - props.modelValue.length == 0 ? tabs.value[0].id : props.modelValue + props.modelValue.length == 0 ? tabs.value[0].id : props.modelValue, ); if (props.modelValue.length == 0) { @@ -64,14 +69,14 @@ watch( (newValue) => { emits("tabChanged", newValue); emits("update:modelValue", newValue); - } + }, ); watch( () => props.modelValue, (newValue) => { selectedTab.value = newValue; - } + }, ); provide("selectedTab", selectedTab); diff --git a/resources/js/components/dialogs/SMDialogMedia.vue b/resources/js/components/dialogs/SMDialogMedia.vue index 80b14fb..5bb8ca9 100644 --- a/resources/js/components/dialogs/SMDialogMedia.vue +++ b/resources/js/components/dialogs/SMDialogMedia.vue @@ -1,28 +1,76 @@