From 13d970c7ce0bf9b88c3553b561cef11cbba0e71a Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Wed, 19 Jun 2024 20:00:29 +0100
Subject: [PATCH] Lexical: Added button icon system

With a bunch of default icons
---
 dev/build/esbuild.js                          |  7 +++
 resources/icons/editor/align-center.svg       |  1 +
 resources/icons/editor/align-justify.svg      |  1 +
 resources/icons/editor/align-left.svg         |  1 +
 resources/icons/editor/align-right.svg        |  1 +
 resources/icons/editor/bold.svg               |  1 +
 resources/icons/editor/code-block.svg         |  1 +
 resources/icons/editor/code.svg               |  1 +
 resources/icons/editor/details.svg            |  1 +
 resources/icons/editor/format-clear.svg       |  1 +
 resources/icons/editor/help.svg               |  1 +
 resources/icons/editor/horizontal-rule.svg    |  1 +
 resources/icons/editor/image.svg              |  1 +
 resources/icons/editor/indent-decrease.svg    |  1 +
 resources/icons/editor/indent-increase.svg    |  1 +
 resources/icons/editor/italic.svg             |  1 +
 resources/icons/editor/link.svg               |  1 +
 resources/icons/editor/list-bullet.svg        |  1 +
 resources/icons/editor/list-check.svg         |  1 +
 resources/icons/editor/list-numbered.svg      |  1 +
 resources/icons/editor/redo.svg               |  1 +
 resources/icons/editor/source-view.svg        |  1 +
 resources/icons/editor/strikethrough.svg      |  1 +
 resources/icons/editor/subscript.svg          |  1 +
 resources/icons/editor/superscript.svg        |  1 +
 resources/icons/editor/underlined.svg         |  1 +
 resources/icons/editor/undo.svg               |  1 +
 resources/js/global.d.ts                      |  4 ++
 resources/js/wysiwyg/helpers.ts               |  6 ++-
 .../wysiwyg/ui/defaults/button-definitions.ts | 50 ++++++++++++++-----
 resources/js/wysiwyg/ui/framework/buttons.ts  | 12 ++++-
 resources/js/wysiwyg/ui/toolbars.ts           |  1 +
 resources/sass/_editor.scss                   |  5 ++
 tsconfig.json                                 |  4 +-
 34 files changed, 99 insertions(+), 16 deletions(-)
 create mode 100644 resources/icons/editor/align-center.svg
 create mode 100644 resources/icons/editor/align-justify.svg
 create mode 100644 resources/icons/editor/align-left.svg
 create mode 100644 resources/icons/editor/align-right.svg
 create mode 100644 resources/icons/editor/bold.svg
 create mode 100644 resources/icons/editor/code-block.svg
 create mode 100644 resources/icons/editor/code.svg
 create mode 100644 resources/icons/editor/details.svg
 create mode 100644 resources/icons/editor/format-clear.svg
 create mode 100644 resources/icons/editor/help.svg
 create mode 100644 resources/icons/editor/horizontal-rule.svg
 create mode 100644 resources/icons/editor/image.svg
 create mode 100644 resources/icons/editor/indent-decrease.svg
 create mode 100644 resources/icons/editor/indent-increase.svg
 create mode 100644 resources/icons/editor/italic.svg
 create mode 100644 resources/icons/editor/link.svg
 create mode 100644 resources/icons/editor/list-bullet.svg
 create mode 100644 resources/icons/editor/list-check.svg
 create mode 100644 resources/icons/editor/list-numbered.svg
 create mode 100644 resources/icons/editor/redo.svg
 create mode 100644 resources/icons/editor/source-view.svg
 create mode 100644 resources/icons/editor/strikethrough.svg
 create mode 100644 resources/icons/editor/subscript.svg
 create mode 100644 resources/icons/editor/superscript.svg
 create mode 100644 resources/icons/editor/underlined.svg
 create mode 100644 resources/icons/editor/undo.svg
 create mode 100644 resources/js/global.d.ts

diff --git a/dev/build/esbuild.js b/dev/build/esbuild.js
index 7f180fc07..0680f4ac3 100644
--- a/dev/build/esbuild.js
+++ b/dev/build/esbuild.js
@@ -32,6 +32,13 @@ esbuild.build({
     format: 'esm',
     minify: isProd,
     logLevel: 'info',
+    loader: {
+        '.svg': 'text',
+    },
+    absWorkingDir: path.join(__dirname, '../..'),
+    alias: {
+        '@icons': './resources/icons',
+    },
     banner: {
         js: '// See the "/licenses" URI for full package license details',
         css: '/* See the "/licenses" URI for full package license details */',
diff --git a/resources/icons/editor/align-center.svg b/resources/icons/editor/align-center.svg
new file mode 100644
index 000000000..495ae000c
--- /dev/null
+++ b/resources/icons/editor/align-center.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M120-120v-80h720v80H120Zm160-160v-80h400v80H280ZM120-440v-80h720v80H120Zm160-160v-80h400v80H280ZM120-760v-80h720v80H120Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/align-justify.svg b/resources/icons/editor/align-justify.svg
new file mode 100644
index 000000000..bf8f61abb
--- /dev/null
+++ b/resources/icons/editor/align-justify.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M120-120v-80h720v80H120Zm0-160v-80h720v80H120Zm0-160v-80h720v80H120Zm0-160v-80h720v80H120Zm0-160v-80h720v80H120Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/align-left.svg b/resources/icons/editor/align-left.svg
new file mode 100644
index 000000000..811212755
--- /dev/null
+++ b/resources/icons/editor/align-left.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M120-120v-80h720v80H120Zm0-160v-80h480v80H120Zm0-160v-80h720v80H120Zm0-160v-80h480v80H120Zm0-160v-80h720v80H120Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/align-right.svg b/resources/icons/editor/align-right.svg
new file mode 100644
index 000000000..839110c42
--- /dev/null
+++ b/resources/icons/editor/align-right.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M120-760v-80h720v80H120Zm240 160v-80h480v80H360ZM120-440v-80h720v80H120Zm240 160v-80h480v80H360ZM120-120v-80h720v80H120Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/bold.svg b/resources/icons/editor/bold.svg
new file mode 100644
index 000000000..93cc44a3f
--- /dev/null
+++ b/resources/icons/editor/bold.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M272-200v-560h221q65 0 120 40t55 111q0 51-23 78.5T602-491q25 11 55.5 41t30.5 90q0 89-65 124.5T501-200H272Zm121-112h104q48 0 58.5-24.5T566-372q0-11-10.5-35.5T494-432H393v120Zm0-228h93q33 0 48-17t15-38q0-24-17-39t-44-15h-95v109Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/code-block.svg b/resources/icons/editor/code-block.svg
new file mode 100644
index 000000000..308db53b4
--- /dev/null
+++ b/resources/icons/editor/code-block.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="m384-336 56-57-87-87 87-87-56-57-144 144 144 144Zm192 0 144-144-144-144-56 57 87 87-87 87 56 57ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-80h560v-560H200v560Zm0-560v560-560Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/code.svg b/resources/icons/editor/code.svg
new file mode 100644
index 000000000..d8434b761
--- /dev/null
+++ b/resources/icons/editor/code.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M320-240 80-480l240-240 57 57-184 184 183 183-56 56Zm320 0-57-57 184-184-183-183 56-56 240 240-240 240Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/details.svg b/resources/icons/editor/details.svg
new file mode 100644
index 000000000..d86e8c423
--- /dev/null
+++ b/resources/icons/editor/details.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-80h560v-480H200v480Zm80-280v-80h400v80H280Zm0 160v-80h240v80H280Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/format-clear.svg b/resources/icons/editor/format-clear.svg
new file mode 100644
index 000000000..b6483fb56
--- /dev/null
+++ b/resources/icons/editor/format-clear.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="m528-546-93-93-121-121h486v120H568l-40 94ZM792-56 460-388l-80 188H249l119-280L56-792l56-56 736 736-56 56Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/help.svg b/resources/icons/editor/help.svg
new file mode 100644
index 000000000..8c3410b84
--- /dev/null
+++ b/resources/icons/editor/help.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M478-240q21 0 35.5-14.5T528-290q0-21-14.5-35.5T478-340q-21 0-35.5 14.5T428-290q0 21 14.5 35.5T478-240Zm-36-154h74q0-33 7.5-52t42.5-52q26-26 41-49.5t15-56.5q0-56-41-86t-97-30q-57 0-92.5 30T342-618l66 26q5-18 22.5-39t53.5-21q32 0 48 17.5t16 38.5q0 20-12 37.5T506-526q-44 39-54 59t-10 73Zm38 314q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/horizontal-rule.svg b/resources/icons/editor/horizontal-rule.svg
new file mode 100644
index 000000000..c70df0d6e
--- /dev/null
+++ b/resources/icons/editor/horizontal-rule.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M160-440v-80h640v80H160Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/image.svg b/resources/icons/editor/image.svg
new file mode 100644
index 000000000..81d04cea7
--- /dev/null
+++ b/resources/icons/editor/image.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h360v80H200v560h560v-360h80v360q0 33-23.5 56.5T760-120H200Zm480-480v-80h-80v-80h80v-80h80v80h80v80h-80v80h-80ZM240-280h480L570-480 450-320l-90-120-120 160Zm-40-480v560-560Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/indent-decrease.svg b/resources/icons/editor/indent-decrease.svg
new file mode 100644
index 000000000..af0caa862
--- /dev/null
+++ b/resources/icons/editor/indent-decrease.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M120-120v-80h720v80H120Zm320-160v-80h400v80H440Zm0-160v-80h400v80H440Zm0-160v-80h400v80H440ZM120-760v-80h720v80H120Zm160 440L120-480l160-160v320Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/indent-increase.svg b/resources/icons/editor/indent-increase.svg
new file mode 100644
index 000000000..aa6b4cb36
--- /dev/null
+++ b/resources/icons/editor/indent-increase.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M120-120v-80h720v80H120Zm320-160v-80h400v80H440Zm0-160v-80h400v80H440Zm0-160v-80h400v80H440ZM120-760v-80h720v80H120Zm0 440v-320l160 160-160 160Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/italic.svg b/resources/icons/editor/italic.svg
new file mode 100644
index 000000000..a98819427
--- /dev/null
+++ b/resources/icons/editor/italic.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M200-200v-100h160l120-360H320v-100h400v100H580L460-300h140v100H200Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/link.svg b/resources/icons/editor/link.svg
new file mode 100644
index 000000000..b29800dc3
--- /dev/null
+++ b/resources/icons/editor/link.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M680-160v-120H560v-80h120v-120h80v120h120v80H760v120h-80ZM440-280H280q-83 0-141.5-58.5T80-480q0-83 58.5-141.5T280-680h160v80H280q-50 0-85 35t-35 85q0 50 35 85t85 35h160v80ZM320-440v-80h320v80H320Zm560-40h-80q0-50-35-85t-85-35H520v-80h160q83 0 141.5 58.5T880-480Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/list-bullet.svg b/resources/icons/editor/list-bullet.svg
new file mode 100644
index 000000000..c073c6ff0
--- /dev/null
+++ b/resources/icons/editor/list-bullet.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M360-200v-80h480v80H360Zm0-240v-80h480v80H360Zm0-240v-80h480v80H360ZM200-160q-33 0-56.5-23.5T120-240q0-33 23.5-56.5T200-320q33 0 56.5 23.5T280-240q0 33-23.5 56.5T200-160Zm0-240q-33 0-56.5-23.5T120-480q0-33 23.5-56.5T200-560q33 0 56.5 23.5T280-480q0 33-23.5 56.5T200-400Zm0-240q-33 0-56.5-23.5T120-720q0-33 23.5-56.5T200-800q33 0 56.5 23.5T280-720q0 33-23.5 56.5T200-640Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/list-check.svg b/resources/icons/editor/list-check.svg
new file mode 100644
index 000000000..f30266b27
--- /dev/null
+++ b/resources/icons/editor/list-check.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="#5f6368"><path d="M222-200 80-342l56-56 85 85 170-170 56 57-225 226Zm0-320L80-662l56-56 85 85 170-170 56 57-225 226Zm298 240v-80h360v80H520Zm0-320v-80h360v80H520Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/list-numbered.svg b/resources/icons/editor/list-numbered.svg
new file mode 100644
index 000000000..92cdbf0ae
--- /dev/null
+++ b/resources/icons/editor/list-numbered.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="#5f6368"><path d="M120-80v-60h100v-30h-60v-60h60v-30H120v-60h120q17 0 28.5 11.5T280-280v40q0 17-11.5 28.5T240-200q17 0 28.5 11.5T280-160v40q0 17-11.5 28.5T240-80H120Zm0-280v-110q0-17 11.5-28.5T160-510h60v-30H120v-60h120q17 0 28.5 11.5T280-560v70q0 17-11.5 28.5T240-450h-60v30h100v60H120Zm60-280v-180h-60v-60h120v240h-60Zm180 440v-80h480v80H360Zm0-240v-80h480v80H360Zm0-240v-80h480v80H360Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/redo.svg b/resources/icons/editor/redo.svg
new file mode 100644
index 000000000..d542296c5
--- /dev/null
+++ b/resources/icons/editor/redo.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" ><path d="M396-200q-97 0-166.5-63T160-420q0-94 69.5-157T396-640h252L544-744l56-56 200 200-200 200-56-56 104-104H396q-63 0-109.5 40T240-420q0 60 46.5 100T396-280h284v80H396Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/source-view.svg b/resources/icons/editor/source-view.svg
new file mode 100644
index 000000000..5314c39da
--- /dev/null
+++ b/resources/icons/editor/source-view.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="m384-336 56-58-86-86 86-86-56-58-144 144 144 144Zm192 0 144-144-144-144-56 58 86 86-86 86 56 58ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h168q13-36 43.5-58t68.5-22q38 0 68.5 22t43.5 58h168q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-80h560v-560H200v560Zm280-590q13 0 21.5-8.5T510-820q0-13-8.5-21.5T480-850q-13 0-21.5 8.5T450-820q0 13 8.5 21.5T480-790ZM200-200v-560 560Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/strikethrough.svg b/resources/icons/editor/strikethrough.svg
new file mode 100644
index 000000000..92d14aa76
--- /dev/null
+++ b/resources/icons/editor/strikethrough.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M80-400v-80h800v80H80Zm340-160v-120H200v-120h560v120H540v120H420Zm0 400v-160h120v160H420Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/subscript.svg b/resources/icons/editor/subscript.svg
new file mode 100644
index 000000000..e877b3359
--- /dev/null
+++ b/resources/icons/editor/subscript.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M760-160v-80q0-17 11.5-28.5T800-280h80v-40H760v-40h120q17 0 28.5 11.5T920-320v40q0 17-11.5 28.5T880-240h-80v40h120v40H760Zm-525-80 185-291-172-269h106l124 200h4l123-200h107L539-531l186 291H618L482-457h-4L342-240H235Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/superscript.svg b/resources/icons/editor/superscript.svg
new file mode 100644
index 000000000..897ceddc2
--- /dev/null
+++ b/resources/icons/editor/superscript.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M760-600v-80q0-17 11.5-28.5T800-720h80v-40H760v-40h120q17 0 28.5 11.5T920-760v40q0 17-11.5 28.5T880-680h-80v40h120v40H760ZM235-160l185-291-172-269h106l124 200h4l123-200h107L539-451l186 291H618L482-377h-4L342-160H235Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/underlined.svg b/resources/icons/editor/underlined.svg
new file mode 100644
index 000000000..5d17ef6ef
--- /dev/null
+++ b/resources/icons/editor/underlined.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M200-120v-80h560v80H200Zm280-160q-101 0-157-63t-56-167v-330h103v336q0 56 28 91t82 35q54 0 82-35t28-91v-336h103v330q0 104-56 167t-157 63Z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/editor/undo.svg b/resources/icons/editor/undo.svg
new file mode 100644
index 000000000..4b9f22675
--- /dev/null
+++ b/resources/icons/editor/undo.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M280-200v-80h284q63 0 109.5-40T720-420q0-60-46.5-100T564-560H312l104 104-56 56-200-200 200-200 56 56-104 104h252q97 0 166.5 63T800-420q0 94-69.5 157T564-200H280Z"/></svg>
\ No newline at end of file
diff --git a/resources/js/global.d.ts b/resources/js/global.d.ts
new file mode 100644
index 000000000..c5aba8ee2
--- /dev/null
+++ b/resources/js/global.d.ts
@@ -0,0 +1,4 @@
+declare module '*.svg' {
+    const content: string;
+    export default content;
+}
\ No newline at end of file
diff --git a/resources/js/wysiwyg/helpers.ts b/resources/js/wysiwyg/helpers.ts
index 40379cc27..d7cd23a35 100644
--- a/resources/js/wysiwyg/helpers.ts
+++ b/resources/js/wysiwyg/helpers.ts
@@ -9,11 +9,13 @@ import {LexicalElementNodeCreator, LexicalNodeMatcher} from "./nodes";
 import {$getNearestBlockElementAncestorOrThrow} from "@lexical/utils";
 import {$setBlocksType} from "@lexical/selection";
 
-export function el(tag: string, attrs: Record<string, string> = {}, children: (string|HTMLElement)[] = []): HTMLElement {
+export function el(tag: string, attrs: Record<string, string|null> = {}, children: (string|HTMLElement)[] = []): HTMLElement {
     const el = document.createElement(tag);
     const attrKeys = Object.keys(attrs);
     for (const attr of attrKeys) {
-        el.setAttribute(attr, attrs[attr]);
+        if (attrs[attr] !== null) {
+            el.setAttribute(attr, attrs[attr] as string);
+        }
     }
 
     for (const child of children) {
diff --git a/resources/js/wysiwyg/ui/defaults/button-definitions.ts b/resources/js/wysiwyg/ui/defaults/button-definitions.ts
index 57460ef60..7fa1fb5f8 100644
--- a/resources/js/wysiwyg/ui/defaults/button-definitions.ts
+++ b/resources/js/wysiwyg/ui/defaults/button-definitions.ts
@@ -29,9 +29,27 @@ import {$isImageNode, ImageNode} from "../../nodes/image";
 import {$createDetailsNode, $isDetailsNode} from "../../nodes/details";
 import {getEditorContentAsHtml} from "../../actions";
 import {$isListNode, insertList, ListNode, ListType, removeList} from "@lexical/list";
+import undoIcon from "@icons/editor/undo.svg"
+import redoIcon from "@icons/editor/redo.svg"
+import boldIcon from "@icons/editor/bold.svg"
+import italicIcon from "@icons/editor/italic.svg"
+import underlinedIcon from "@icons/editor/underlined.svg"
+import strikethroughIcon from "@icons/editor/strikethrough.svg"
+import superscriptIcon from "@icons/editor/superscript.svg"
+import subscriptIcon from "@icons/editor/subscript.svg"
+import codeIcon from "@icons/editor/code.svg"
+import formatClearIcon from "@icons/editor/format-clear.svg"
+import listBulletIcon from "@icons/editor/list-bullet.svg"
+import listNumberedIcon from "@icons/editor/list-numbered.svg"
+import listCheckIcon from "@icons/editor/list-check.svg"
+import linkIcon from "@icons/editor/link.svg"
+import imageIcon from "@icons/editor/image.svg"
+import detailsIcon from "@icons/editor/details.svg"
+import sourceIcon from "@icons/editor/source-view.svg"
 
 export const undo: EditorButtonDefinition = {
     label: 'Undo',
+    icon: undoIcon,
     action(context: EditorUiContext) {
         context.editor.dispatchCommand(UNDO_COMMAND, undefined);
     },
@@ -42,6 +60,7 @@ export const undo: EditorButtonDefinition = {
 
 export const redo: EditorButtonDefinition = {
     label: 'Redo',
+    icon: redoIcon,
     action(context: EditorUiContext) {
         context.editor.dispatchCommand(REDO_COMMAND, undefined);
     },
@@ -116,9 +135,10 @@ export const paragraph: EditorButtonDefinition = {
     }
 }
 
-function buildFormatButton(label: string, format: TextFormatType): EditorButtonDefinition {
+function buildFormatButton(label: string, format: TextFormatType, icon: string): EditorButtonDefinition {
     return {
         label: label,
+        icon,
         action(context: EditorUiContext) {
             context.editor.dispatchCommand(FORMAT_TEXT_COMMAND, format);
         },
@@ -128,18 +148,19 @@ function buildFormatButton(label: string, format: TextFormatType): EditorButtonD
     };
 }
 
-export const bold: EditorButtonDefinition = buildFormatButton('Bold', 'bold');
-export const italic: EditorButtonDefinition = buildFormatButton('Italic', 'italic');
-export const underline: EditorButtonDefinition = buildFormatButton('Underline', 'underline');
+export const bold: EditorButtonDefinition = buildFormatButton('Bold', 'bold', boldIcon);
+export const italic: EditorButtonDefinition = buildFormatButton('Italic', 'italic', italicIcon);
+export const underline: EditorButtonDefinition = buildFormatButton('Underline', 'underline', underlinedIcon);
 export const textColor: EditorBasicButtonDefinition = {label: 'Text color'};
 export const highlightColor: EditorBasicButtonDefinition = {label: 'Highlight color'};
 
-export const strikethrough: EditorButtonDefinition = buildFormatButton('Strikethrough', 'strikethrough');
-export const superscript: EditorButtonDefinition = buildFormatButton('Superscript', 'superscript');
-export const subscript: EditorButtonDefinition = buildFormatButton('Subscript', 'subscript');
-export const code: EditorButtonDefinition = buildFormatButton('Inline Code', 'code');
+export const strikethrough: EditorButtonDefinition = buildFormatButton('Strikethrough', 'strikethrough', strikethroughIcon);
+export const superscript: EditorButtonDefinition = buildFormatButton('Superscript', 'superscript', superscriptIcon);
+export const subscript: EditorButtonDefinition = buildFormatButton('Subscript', 'subscript', subscriptIcon);
+export const code: EditorButtonDefinition = buildFormatButton('Inline Code', 'code', codeIcon);
 export const clearFormating: EditorButtonDefinition = {
     label: 'Clear formatting',
+    icon: formatClearIcon,
     action(context: EditorUiContext) {
         context.editor.update(() => {
             const selection = $getSelection();
@@ -155,9 +176,10 @@ export const clearFormating: EditorButtonDefinition = {
     }
 };
 
-function buildListButton(label: string, type: ListType): EditorButtonDefinition {
+function buildListButton(label: string, type: ListType, icon: string): EditorButtonDefinition {
     return {
         label,
+        icon,
         action(context: EditorUiContext) {
             context.editor.getEditorState().read(() => {
                 const selection = $getSelection();
@@ -176,13 +198,14 @@ function buildListButton(label: string, type: ListType): EditorButtonDefinition
     };
 }
 
-export const bulletList: EditorButtonDefinition = buildListButton('Bullet list', 'bullet');
-export const numberList: EditorButtonDefinition = buildListButton('Numbered list', 'number');
-export const taskList: EditorButtonDefinition = buildListButton('Task list', 'check');
+export const bulletList: EditorButtonDefinition = buildListButton('Bullet list', 'bullet', listBulletIcon);
+export const numberList: EditorButtonDefinition = buildListButton('Numbered list', 'number', listNumberedIcon);
+export const taskList: EditorButtonDefinition = buildListButton('Task list', 'check', listCheckIcon);
 
 
 export const link: EditorButtonDefinition = {
     label: 'Insert/edit link',
+    icon: linkIcon,
     action(context: EditorUiContext) {
         const linkModal = context.manager.createModal('link');
         context.editor.getEditorState().read(() => {
@@ -215,6 +238,7 @@ export const link: EditorButtonDefinition = {
 
 export const image: EditorButtonDefinition = {
     label: 'Insert/Edit Image',
+    icon: imageIcon,
     action(context: EditorUiContext) {
         const imageModal = context.manager.createModal('image');
         const selection = context.lastSelection;
@@ -247,6 +271,7 @@ export const image: EditorButtonDefinition = {
 
 export const details: EditorButtonDefinition = {
     label: 'Insert collapsible block',
+    icon: detailsIcon,
     action(context: EditorUiContext) {
         context.editor.update(() => {
             const selection = $getSelection();
@@ -274,6 +299,7 @@ export const details: EditorButtonDefinition = {
 
 export const source: EditorButtonDefinition = {
     label: 'Source code',
+    icon: sourceIcon,
     async action(context: EditorUiContext) {
         const modal = context.manager.createModal('source');
         const source = await getEditorContentAsHtml(context.editor);
diff --git a/resources/js/wysiwyg/ui/framework/buttons.ts b/resources/js/wysiwyg/ui/framework/buttons.ts
index c3ba533b3..332b35099 100644
--- a/resources/js/wysiwyg/ui/framework/buttons.ts
+++ b/resources/js/wysiwyg/ui/framework/buttons.ts
@@ -4,6 +4,7 @@ import {el} from "../../helpers";
 
 export interface EditorBasicButtonDefinition {
     label: string;
+    icon?: string|undefined;
 }
 
 export interface EditorButtonDefinition extends EditorBasicButtonDefinition {
@@ -21,10 +22,19 @@ export class EditorButton extends EditorUiElement {
     }
 
     protected buildDOM(): HTMLButtonElement {
+
+        const label = this.getLabel();
+        let child: string|HTMLElement = label;
+        if (this.definition.icon) {
+            child = el('span', {class: 'editor-button-icon'});
+            child.innerHTML = this.definition.icon;
+        }
+
         const button = el('button', {
             type: 'button',
             class: 'editor-button',
-        }, [this.getLabel()]) as HTMLButtonElement;
+            title: this.definition.icon ? label : null,
+        }, [child]) as HTMLButtonElement;
 
         button.addEventListener('click', this.onClick.bind(this));
 
diff --git a/resources/js/wysiwyg/ui/toolbars.ts b/resources/js/wysiwyg/ui/toolbars.ts
index fe19b94ed..559e9a87c 100644
--- a/resources/js/wysiwyg/ui/toolbars.ts
+++ b/resources/js/wysiwyg/ui/toolbars.ts
@@ -16,6 +16,7 @@ import {FormatPreviewButton} from "./framework/blocks/format-preview-button";
 import {EditorDropdownButton} from "./framework/blocks/dropdown-button";
 import {EditorColorPicker} from "./framework/blocks/color-picker";
 
+console.log(undo);
 
 export function getMainEditorFullToolbar(): EditorContainerUiElement {
     return new EditorSimpleClassContainer('editor-toolbar-main', [
diff --git a/resources/sass/_editor.scss b/resources/sass/_editor.scss
index 13d8e96f9..f8c895afd 100644
--- a/resources/sass/_editor.scss
+++ b/resources/sass/_editor.scss
@@ -29,6 +29,11 @@
   padding: 4px 6px;
   display: block;
 }
+.editor-button-icon svg {
+  width: 24px;
+  height: 24px;
+  fill: #000;
+}
 
 // Containers
 .editor-dropdown-menu-container {
diff --git a/tsconfig.json b/tsconfig.json
index e075f973c..40d930149 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -29,7 +29,9 @@
     // "rootDir": "./",                                  /* Specify the root folder within your source files. */
     // "moduleResolution": "node10",                     /* Specify how TypeScript looks up a file from a given module specifier. */
     // "baseUrl": "./",                                  /* Specify the base directory to resolve non-relative module names. */
-    // "paths": {},                                      /* Specify a set of entries that re-map imports to additional lookup locations. */
+    "paths": {                                           /* Specify a set of entries that re-map imports to additional lookup locations. */
+      "@icons/*": ["./resources/icons/*"]
+    },
     // "rootDirs": [],                                   /* Allow multiple folders to be treated as one when resolving modules. */
     // "typeRoots": [],                                  /* Specify multiple folders that act like './node_modules/@types'. */
     // "types": [],                                      /* Specify type package names to be included without being referenced in a source file. */