@itfin/components 1.3.4 → 1.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itfin/components",
3
- "version": "1.3.4",
3
+ "version": "1.3.5",
4
4
  "author": "Vitalii Savchuk <esvit666@gmail.com>",
5
5
  "scripts": {
6
6
  "serve": "vue-cli-service serve",
@@ -19,6 +19,20 @@
19
19
  "src/"
20
20
  ],
21
21
  "dependencies": {
22
+ "@calumk/editorjs-codecup": "^1.1.1",
23
+ "@editorjs/attaches": "^1.3.0",
24
+ "@editorjs/code": "^2.9.0",
25
+ "@editorjs/delimiter": "^1.4.0",
26
+ "@editorjs/editorjs": "^2.29.0",
27
+ "@editorjs/embed": "^2.7.0",
28
+ "@editorjs/header": "^2.8.1",
29
+ "@editorjs/image": "^2.9.0",
30
+ "@editorjs/inline-code": "^1.5.0",
31
+ "@editorjs/link": "^2.6.2",
32
+ "@editorjs/marker": "^1.4.0",
33
+ "@editorjs/nested-list": "^1.4.2",
34
+ "@editorjs/quote": "^2.6.0",
35
+ "@editorjs/table": "^2.3.0",
22
36
  "@egjs/vue-flicking": "^4.10.4",
23
37
  "@mdi/js": "^7.2.96",
24
38
  "@popperjs/core": "^2.11.8",
@@ -26,7 +40,7 @@
26
40
  "@vue/cli-service": "^5.0.1",
27
41
  "@vue/composition-api": "^1.7.1",
28
42
  "air-datepicker": "^3.3.5",
29
- "bootstrap": "^5.2.3",
43
+ "bootstrap": "^5.3.x",
30
44
  "core-js": "^3.7.0",
31
45
  "debug": "^4.2.0",
32
46
  "intersection-observer": "^0.12.2",
@@ -40,10 +54,10 @@
40
54
  "vue-virtual-scroller": "^1.1.2"
41
55
  },
42
56
  "devDependencies": {
43
- "vue": "^2.6.12",
44
57
  "@babel/eslint-parser": "^7.19.1",
45
58
  "@babel/plugin-proposal-numeric-separator": "^7.18.6",
46
59
  "@babel/plugin-syntax-numeric-separator": "^7.10.4",
60
+ "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4",
47
61
  "@storybook/addon-docs": "=6.3.8",
48
62
  "@storybook/addon-viewport": "=6.3.8",
49
63
  "@storybook/vue": "=6.3.8",
@@ -61,6 +75,7 @@
61
75
  "marked": "^4.2.5",
62
76
  "sass": "^1.29.0",
63
77
  "sass-loader": "^10.0.5",
78
+ "vue": "^2.6.12",
64
79
  "vue-class-component": "^7.2.6",
65
80
  "vue-eslint-parser": "^9.1.0",
66
81
  "vue-template-compiler": "=2.6.14"
@@ -0,0 +1,215 @@
1
+ <template>
2
+ <div ref="container" class="editor" :class="{ readonly }" />
3
+ </template>
4
+ <style lang="scss">
5
+ @font-face {
6
+ font-family: "Virgil";
7
+ src: url("https://unpkg.com/@excalidraw/excalidraw@0.16.1/dist/excalidraw-assets/Virgil.woff2");
8
+ }
9
+ @font-face {
10
+ font-family: "Cascadia";
11
+ src: url("https://unpkg.com/@excalidraw/excalidraw@0.16.1/dist/excalidraw-assets/Cascadia.woff2");
12
+ }
13
+
14
+ .editor {
15
+ &.readonly {
16
+ .cdx-input {
17
+ border: 0 none;
18
+ -webkit-box-shadow: none;
19
+ box-shadow: none;
20
+ padding: 0;
21
+ }
22
+
23
+ .image-tool__caption {
24
+ font-style: italic;
25
+ text-align: center;
26
+ }
27
+ .cdx-quote {
28
+ border-left: 3px solid #222;
29
+ padding: 0.5rem 1rem;
30
+ margin-bottom: .5rem;
31
+ }
32
+ .cdx-quote__text {
33
+ border-radius: 0;
34
+ min-height: auto;
35
+ margin-bottom: 0;
36
+ }
37
+ .cdx-quote__caption {
38
+ font-style: italic;
39
+
40
+ &:not(:empty) {
41
+ margin-top: 10px;
42
+ }
43
+
44
+ &:not(:empty)::before {
45
+ content: '— ';
46
+ padding-right: 20px;
47
+ }
48
+ &:after {
49
+ display: none !important;
50
+ }
51
+ }
52
+ }
53
+ }
54
+ </style>
55
+ <script>
56
+ import { Vue, Model, Component, Watch, Prop } from 'vue-property-decorator';
57
+ import EditorJS from '@editorjs/editorjs';
58
+ import Header from '@editorjs/header';
59
+ import Embed from '@editorjs/embed';
60
+ import Quote from '@editorjs/quote';
61
+ import Marker from '@editorjs/marker';
62
+ import LinkTool from '@editorjs/link';
63
+ import Delimiter from '@editorjs/delimiter';
64
+ import InlineCode from '@editorjs/inline-code';
65
+ import Image from '@editorjs/image';
66
+ import Table from '@editorjs/table';
67
+ import AttachesTool from '@editorjs/attaches';
68
+ import NestedList from '@editorjs/nested-list';
69
+ import editorjsCodecup from './tools/highlightcode/codecup';
70
+ import Drawing from './tools/drawing/drawing';
71
+ import cloneDeep from 'lodash/cloneDeep';
72
+
73
+ export default @Component({
74
+ components: {
75
+ }
76
+ })
77
+ class itfEditor extends Vue {
78
+ @Model('input') value;
79
+ @Prop(Boolean) readonly;
80
+ @Prop(String) imageEndpoint;
81
+ @Prop(String) fetchEndpoint;
82
+
83
+ editor = null;
84
+
85
+ isIgnoreReload = false;
86
+
87
+ mounted() {
88
+ this.onValueChanged(this.value);
89
+ }
90
+
91
+ beforeDestroy() {
92
+ if (this.editor && this.editor.destroy) {
93
+ this.editor.destroy();
94
+ }
95
+ }
96
+
97
+ @Watch('value')
98
+ onValueChanged(val, oldVal) {
99
+ if (this.editor && (this.isIgnoreReload || JSON.stringify(val) === JSON.stringify(oldVal))) {
100
+ return;
101
+ }
102
+ setTimeout(() => {
103
+ this.initEditor();
104
+ }, 200);
105
+ }
106
+
107
+ @Watch('readonly')
108
+ onReadonlyChange(val, oldVal) {
109
+ this.editor.readOnly.toggle(this.readonly);
110
+ }
111
+
112
+ initEditor() {
113
+ if (!this.$refs.container) {
114
+ return;
115
+ }
116
+ if (this.editor && this.editor.destroy) {
117
+ this.editor.destroy();
118
+ }
119
+ this.$refs.container.innerHTML = '';
120
+ const node = document.createElement('div');
121
+ this.$refs.container.appendChild(node);
122
+
123
+ this.editor = new EditorJS({
124
+ tools: {
125
+ header: Header,
126
+ embed: Embed,
127
+ quote: Quote,
128
+ marker: Marker,
129
+ code: editorjsCodecup,
130
+ delimiter: Delimiter,
131
+ drawing: {
132
+ inlineToolbar: true,
133
+ class: Drawing
134
+ },
135
+ // warning: Warning,
136
+ table: {
137
+ inlineToolbar: true,
138
+ class: Table
139
+ },
140
+ inlineCode: {
141
+ class: InlineCode,
142
+ shortcut: 'CMD+SHIFT+M',
143
+ },
144
+ list: {
145
+ class: NestedList,
146
+ inlineToolbar: true,
147
+ config: {
148
+ defaultStyle: 'unordered'
149
+ },
150
+ },
151
+ linkTool: {
152
+ class: LinkTool,
153
+ config: {
154
+ endpoint: this.fetchEndpoint, // Your backend endpoint for url data fetching,
155
+ }
156
+ },
157
+ attaches: {
158
+ class: AttachesTool,
159
+ config: {
160
+ endpoint: this.imageEndpoint
161
+ }
162
+ },
163
+ image: {
164
+ class: Image,
165
+ inlineToolbar: true,
166
+ config: {
167
+ endpoints: {
168
+ byFile: this.imageEndpoint, // Your backend file uploader endpoint
169
+ byUrl: this.fetchEndpoint, // Your endpoint that provides uploading by Url
170
+ },
171
+ /* actions: [
172
+ {
173
+ toggle: true,
174
+ icon: '<svg width="256px" height="256px" viewBox="-32 -32 320 320" xmlns="http://www.w3.org/2000/svg"><g opacity="0.2"><rect x="72" y="56" width="112" height="56" rx="8"/></g><g opacity="0.2"><rect x="72" y="144" width="152" height="56" rx="8"/></g><path fill="currentColor" d="M216,136H80a16.01833,16.01833,0,0,0-16,16v40a16.01833,16.01833,0,0,0,16,16H216a16.01833,16.01833,0,0,0,16-16V152A16.01833,16.01833,0,0,0,216,136Zm0,56H80V152H216l.01025,39.99951ZM47.99414,39.99512v176a8,8,0,0,1-16,0v-176a8,8,0,0,1,16,0ZM80,120h96a16.01833,16.01833,0,0,0,16-16V64a16.01833,16.01833,0,0,0-16-16H80A16.01833,16.01833,0,0,0,64,64v40A16.01833,16.01833,0,0,0,80,120Zm0-56h96l.01025,39.99951L176,104H80Z"/></svg>\n',
175
+ name: 'left',
176
+ title: 'Left',
177
+ action: (name) => {
178
+ console.info(name)
179
+ }
180
+ },
181
+ {
182
+ toggle: true,
183
+ icon: '<svg width="256px" height="256px" viewBox="-32 -32 320 320" xmlns="http://www.w3.org/2000/svg"><g opacity="0.2"><rect x="64" y="56" width="128" height="56" rx="8"/></g><g opacity="0.2"><rect x="40" y="144" width="176" height="56" rx="8"/></g><path fill="currentColor" d="M208,136H136V120h48a16.01833,16.01833,0,0,0,16-16V64a16.01833,16.01833,0,0,0-16-16H136V31.99512a8,8,0,1,0-16,0V48H72A16.01833,16.01833,0,0,0,56,64v40a16.01833,16.01833,0,0,0,16,16h48v16H48a16.01833,16.01833,0,0,0-16,16v40a16.01833,16.01833,0,0,0,16,16h72v15.99512a8,8,0,0,0,16,0V208h72a16.01833,16.01833,0,0,0,16-16V152A16.01833,16.01833,0,0,0,208,136ZM72,64H184l.01025,39.99951L184,104H128.09668c-.03253-.00037-.064-.00488-.09668-.00488s-.06415.00451-.09668.00488H72ZM208,192H128.09668c-.03253-.00037-.064-.00488-.09668-.00488s-.06415.00451-.09668.00488H48V152H208l.01025,39.99951Z"/></svg>',
184
+ name: 'centered',
185
+ title: 'Centered'
186
+ },
187
+ {
188
+ toggle: true,
189
+ icon: '<svg width="256px" height="256px" viewBox="-32 -32 320 320" xmlns="http://www.w3.org/2000/svg"><g opacity="0.2"><rect x="72" y="56" width="112" height="56" rx="8" transform="translate(256 168) rotate(180)"/></g><g opacity="0.2"><rect x="32" y="144" width="152" height="56" rx="8"/></g><path fill="currentColor" d="M224.00586,39.99512v176a8,8,0,0,1-16,0v-176a8,8,0,0,1,16,0ZM192,64v40a16.01833,16.01833,0,0,1-16,16H80a16.01833,16.01833,0,0,1-16-16V64A16.01833,16.01833,0,0,1,80,48h96A16.01833,16.01833,0,0,1,192,64Zm-16,0H80v40h96Zm16,88v40a16.01833,16.01833,0,0,1-16,16H40a16.01833,16.01833,0,0,1-16-16V152a16.01833,16.01833,0,0,1,16-16H176A16.01833,16.01833,0,0,1,192,152Zm-16,0H40v40H176Z"/></svg>',
190
+ name: 'right',
191
+ title: 'Right'
192
+ }
193
+ ] */
194
+ }
195
+ }
196
+ },
197
+ readOnly: this.readonly,
198
+ holder: node,
199
+ data: cloneDeep(this.value || {}),
200
+ // autofocus: true,
201
+ minHeight: this.readonly ? 10 : 100,
202
+ placeholder: this.$t('workflows.clickToEdit'),
203
+
204
+ onChange: async (api, event) => {
205
+ if (!this.editor.save || this.readonly) {
206
+ return;
207
+ }
208
+ this.isIgnoreReload = true;
209
+ this.$emit('input', await this.editor.save());
210
+ this.$nextTick(() => { this.isIgnoreReload = false; });
211
+ }
212
+ });
213
+ }
214
+ }
215
+ </script>
@@ -0,0 +1,101 @@
1
+ import { storiesOf } from '@storybook/vue';
2
+ import itfEditor from './Editor.vue';
3
+
4
+ storiesOf('Common', module)
5
+ .add('Editor', () => ({
6
+ components: {
7
+ itfEditor
8
+ },
9
+ data() {
10
+ return {
11
+ readonly: false,
12
+ content: {
13
+ "time": 1706814343900,
14
+ "blocks": [
15
+ {
16
+ "id": "VgKnTNZEFP",
17
+ "type": "drawing",
18
+ "data": {
19
+ "elements": [
20
+ {
21
+ "id": "V_vhPwm2mjt3E43Xzi-WD",
22
+ "type": "rectangle",
23
+ "x": 254.015625,
24
+ "y": 163.17578125,
25
+ "width": 255.75,
26
+ "height": 174.87890625,
27
+ "angle": 0,
28
+ "strokeColor": "#1e1e1e",
29
+ "backgroundColor": "transparent",
30
+ "fillStyle": "hachure",
31
+ "strokeWidth": 1,
32
+ "strokeStyle": "solid",
33
+ "roughness": 1,
34
+ "opacity": 100,
35
+ "groupIds": [],
36
+ "frameId": null,
37
+ "roundness": {
38
+ "type": 3
39
+ },
40
+ "seed": 676042035,
41
+ "version": 16,
42
+ "versionNonce": 1604300829,
43
+ "isDeleted": false,
44
+ "boundElements": null,
45
+ "updated": 1706814337975,
46
+ "link": null,
47
+ "locked": false
48
+ },
49
+ {
50
+ "id": "V1K-7Khbdjtx9dp1WGSIN",
51
+ "type": "ellipse",
52
+ "x": 1249.26953125,
53
+ "y": 216.703125,
54
+ "width": 204.3515625,
55
+ "height": 151.8046875,
56
+ "angle": 0,
57
+ "strokeColor": "#1e1e1e",
58
+ "backgroundColor": "transparent",
59
+ "fillStyle": "hachure",
60
+ "strokeWidth": 1,
61
+ "strokeStyle": "solid",
62
+ "roughness": 1,
63
+ "opacity": 100,
64
+ "groupIds": [],
65
+ "frameId": null,
66
+ "roundness": {
67
+ "type": 2
68
+ },
69
+ "seed": 89881459,
70
+ "version": 19,
71
+ "versionNonce": 259089235,
72
+ "isDeleted": false,
73
+ "boundElements": null,
74
+ "updated": 1706814339583,
75
+ "link": null,
76
+ "locked": false
77
+ }
78
+ ],
79
+ libraryItems: [],
80
+ "files": {},
81
+ "svg": "<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1219.60546875 225.33203125\" width=\"2439.2109375\" height=\"450.6640625\">\n <!-- svg-source:excalidraw -->\n \n <defs>\n <style class=\"style-fonts\">\n @font-face {\n font-family: \"Virgil\";\n src: url(\"https://unpkg.com/@excalidraw/excalidraw@undefined/dist/excalidraw-assets/Virgil.woff2\");\n }\n @font-face {\n font-family: \"Cascadia\";\n src: url(\"https://unpkg.com/@excalidraw/excalidraw@undefined/dist/excalidraw-assets/Cascadia.woff2\");\n }\n </style>\n \n </defs>\n <rect x=\"0\" y=\"0\" width=\"1219.60546875\" height=\"225.33203125\" fill=\"#ffffff\"></rect><g stroke-linecap=\"round\" transform=\"translate(10 10) rotate(0 127.875 87.439453125)\"><path d=\"M32 0 M32 0 C73.21 -0.81, 109.23 0.05, 223.75 0 M32 0 C83.63 -2.22, 135.03 -2.09, 223.75 0 M223.75 0 C246.32 0.1, 254.56 9.54, 255.75 32 M223.75 0 C246.69 -0.1, 256.41 8.39, 255.75 32 M255.75 32 C256.43 73.42, 254.51 116.2, 255.75 142.88 M255.75 32 C254.9 69.61, 253.88 107.88, 255.75 142.88 M255.75 142.88 C257.06 165.23, 246.74 176.15, 223.75 174.88 M255.75 142.88 C256.85 165.96, 243.04 176.99, 223.75 174.88 M223.75 174.88 C164.9 177.2, 106.98 175.67, 32 174.88 M223.75 174.88 C183.58 175.77, 143.17 174.95, 32 174.88 M32 174.88 C12.55 173.29, -0.42 162.63, 0 142.88 M32 174.88 C9.95 176.99, 0.49 164.59, 0 142.88 M0 142.88 C1.49 101.84, 2.03 59.86, 0 32 M0 142.88 C0.97 119.84, 0.26 97.53, 0 32 M0 32 C0.13 9.92, 9.71 0.96, 32 0 M0 32 C-2.28 9.05, 9.54 -0.3, 32 0\" stroke=\"#1e1e1e\" stroke-width=\"1\" fill=\"none\"></path></g><g stroke-linecap=\"round\" transform=\"translate(1005.25390625 63.52734375) rotate(0 102.17578125 75.90234375)\"><path d=\"M67.97 3.96 C79.04 -0.36, 94.17 -0.83, 107.47 -0.18 C120.77 0.47, 135.87 3.43, 147.75 7.85 C159.63 12.26, 170.43 19.14, 178.77 26.3 C187.1 33.46, 193.49 41.71, 197.76 50.8 C202.02 59.89, 204.91 71.07, 204.37 80.84 C203.84 90.61, 200.44 100.92, 194.55 109.44 C188.66 117.95, 179.04 125.55, 169.05 131.94 C159.06 138.34, 146.82 144.59, 134.62 147.79 C122.42 150.98, 108.65 151.71, 95.85 151.12 C83.04 150.53, 69.52 148.46, 57.77 144.24 C46.03 140.02, 33.99 133.22, 25.38 125.8 C16.77 118.38, 10.41 108.88, 6.1 99.73 C1.78 90.58, -1.2 80.25, -0.52 70.89 C0.15 61.53, 4.38 52.24, 10.14 43.59 C15.9 34.95, 22.58 26.06, 34.04 19.01 C45.51 11.97, 69.62 4.07, 78.92 1.33 C88.23 -1.41, 89.44 1.32, 89.88 2.57 M91.2 0.17 C103.32 -1.86, 118.33 1.78, 130.89 4.38 C143.46 6.97, 156.27 10.07, 166.59 15.73 C176.91 21.39, 186.61 29.77, 192.79 38.32 C198.98 46.87, 202.51 57.4, 203.7 67.04 C204.9 76.68, 203.65 86.74, 199.95 96.19 C196.26 105.64, 189.72 116.09, 181.53 123.76 C173.34 131.44, 162.1 137.56, 150.82 142.23 C139.54 146.89, 126.71 150.83, 113.85 151.76 C100.99 152.68, 86.45 150.77, 73.66 147.76 C60.88 144.75, 47.56 139.8, 37.14 133.7 C26.73 127.6, 17.28 119.59, 11.18 111.16 C5.07 102.73, 1.66 92.39, 0.52 83.11 C-0.62 73.84, 0.49 64.65, 4.32 55.51 C8.15 46.36, 15.55 36.06, 23.49 28.22 C31.43 20.39, 40.75 13.38, 51.93 8.5 C63.12 3.62, 84.12 0.19, 90.61 -1.05 C97.11 -2.3, 90.57 -0.73, 90.92 1.01\" stroke=\"#1e1e1e\" stroke-width=\"1\" fill=\"none\"></path></g></svg>"
82
+ }
83
+ }
84
+ ],
85
+ "version": "2.29.0"
86
+ }
87
+ }
88
+ },
89
+ methods: {
90
+ },
91
+ template: `<div>
92
+ <h2>Editor</h2>
93
+ <itf-editor v-model="content" :readonly="readonly" />
94
+
95
+ {{readonly}}
96
+ <div @click="readonly = !readonly">Readonly</div>
97
+
98
+ <h2>Code</h2>
99
+ <pre v-text="content"></pre>
100
+ </div>`,
101
+ }));
@@ -0,0 +1,151 @@
1
+ import './drawing.scss';
2
+ import cloneDeep from 'lodash/cloneDeep';
3
+
4
+ const icon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="-8 -8 40 40">\n' +
5
+ ' <path d="m8.684,23.485c-.183.329-.523.515-.875.515-.164,0-.331-.04-.485-.126C2.669,21.288,0,16.96,0,12,0,5.383,5.383,0,12,0c.731,0,1.465.066,2.181.198.543.1.903.621.803,1.164-.1.544-.622.905-1.164.803-.597-.109-1.208-.165-1.819-.165C6.486,2,2,6.486,2,12c0,4.213,2.294,7.903,6.295,10.126.482.269.657.877.388,1.359Zm15.119-13.666c-.099-.543-.618-.907-1.164-.803-.543.1-.903.621-.803,1.164.109.595.165,1.207.165,1.819,0,3.309-2.691,6-6,6s-6-2.691-6-6c0-.553-.448-1-1-1s-1,.447-1,1c0,4.411,3.589,8,8,8s8-3.589,8-8c0-.733-.066-1.467-.198-2.181Zm-10.63-3.233L18.879.879c1.133-1.133,3.109-1.133,4.243,0,1.169,1.17,1.169,3.072,0,4.242l-5.707,5.707c-.755.756-1.76,1.172-2.828,1.172h-1.586c-.552,0-1-.447-1-1v-1.586c0-1.068.417-2.073,1.172-2.828Zm.828,3.414h.586c.526,0,1.042-.214,1.414-.586l5.707-5.707c.39-.39.39-1.024,0-1.414-.378-.379-1.037-.379-1.414,0l-5.708,5.707c-.378.378-.586.88-.586,1.414v.586Z"/>\n' +
6
+ '</svg>';
7
+
8
+ const content = '' +
9
+ '<!DOCTYPE html><html><head> <meta charset="UTF-8"/> <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script> <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script> <script type="text/javascript" src="https://unpkg.com/@excalidraw/excalidraw@0.16.1/dist/excalidraw.production.min.js"></script></head><body class="excalidraw-container"><div id="app"></div><style> body { display: flex; width: 100vw; height: 100vh; padding: 0; margin: 0;}#app { flex-grow: 1;}.excalidraw .App-menu_top > .Stack.Stack_vertical > div:first-child, .excalidraw .disable-zen-mode, .excalidraw .layer-ui__wrapper__footer-right, .excalidraw .layer-ui__wrapper__github-corner, .excalidraw button.ToolIcon_type_button.ToolIcon.ToolIcon_size_m.Shape.ToolIcon_type_button--show, .excalidraw .library-menu-items-container__items .library-menu-items-container__header, .excalidraw .library-menu-control-buttons, .excalidraw .ToolIcon__library { display: none;} </style><script type="text/javascript"> const App = () => { const data = window.saveData || {}; return React.createElement(React.Fragment, null, React.createElement("div", {style: {height: "100%"},}, React.createElement(ExcalidrawLib.Excalidraw, { initialData: { elements: data.elements || [], libraryItems: data.libraryItems || [], appState: {zenModeEnabled: false}, scrollToContent: false }, onChange: (elements, state, files) => { window.saveData = window.saveData || {}; window.saveData.elements = elements; window.saveData.files = files; ExcalidrawLib.exportToSvg({ elements, state, files }).then(svg => { svg.removeAttribute(\'width\'); svg.removeAttribute(\'height\'); window.saveData.svg = svg.outerHTML; }); }, onLibraryChange: (items) => { window.saveData = window.saveData || {}; window.saveData.libraryItems = items; } })));};const excalidrawWrapper = document.getElementById("app");const root = ReactDOM.createRoot(excalidrawWrapper);root.render(React.createElement(App)); </script></body></html>';
10
+
11
+ export default class Drawing {
12
+ data = null;
13
+ readOnly = false;
14
+
15
+ static get sanitize(){
16
+ return {
17
+ svg: true,
18
+ caption: {} // only tags from Inline Toolbar
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Allow to use this tool in read only mode
24
+ */
25
+ static get isReadOnlySupported() {
26
+ return true;
27
+ }
28
+
29
+ constructor({ data, readOnly }){
30
+ this.data = data;
31
+ this.readOnly = readOnly;
32
+ }
33
+
34
+ static get toolbox() {
35
+ return {
36
+ title: 'Drawing',
37
+ icon
38
+ };
39
+ }
40
+ render(){
41
+ const wrapper = createWrapper(this.data, this.readOnly);
42
+ if (!this.readOnly) {
43
+ wrapper.classList.remove('readonly');
44
+ wrapper.addEventListener('dblclick', async () => {
45
+ const modal = document.createElement('div');
46
+ modal.classList.add('itf-modal');
47
+ modal.classList.add('modal');
48
+ modal.classList.add('fade');
49
+ modal.classList.add('editorjs-drawing-modal');
50
+ const modalDialog = document.createElement('div');
51
+ modalDialog.classList.add('modal-dialog');
52
+ modalDialog.classList.add('modal-fullscreen');
53
+ const modalContent = document.createElement('div');
54
+ modalContent.classList.add('modal-content');
55
+ const modalHeader = document.createElement('div');
56
+ modalHeader.classList.add('modal-header');
57
+ const modalTitle = document.createElement('h5');
58
+ modalTitle.classList.add('modal-title');
59
+ modalTitle.textContent = 'Drawing';
60
+ modalHeader.appendChild(modalTitle);
61
+ const btnHolder = document.createElement('div');
62
+ btnHolder.classList.add('d-flex');
63
+ const modalSaveBtn = document.createElement('button');
64
+ modalSaveBtn.classList.add('btn');
65
+ modalSaveBtn.classList.add('btn-primary');
66
+ modalSaveBtn.textContent = 'Save & Close';
67
+ const modalCancelBtn = document.createElement('button');
68
+ modalCancelBtn.classList.add('btn');
69
+ modalCancelBtn.classList.add('btn-secondary');
70
+ modalCancelBtn.classList.add('me-2');
71
+ modalCancelBtn.textContent = 'Cancel';
72
+ btnHolder.appendChild(modalCancelBtn);
73
+ btnHolder.appendChild(modalSaveBtn);
74
+ modalHeader.appendChild(modalTitle);
75
+ modalHeader.appendChild(btnHolder);
76
+ modalContent.appendChild(modalHeader);
77
+ const modalBody = document.createElement('div');
78
+ modalBody.classList.add('modal-body');
79
+ const iframe = document.createElement('iframe');
80
+ modalBody.appendChild(iframe);
81
+ modalContent.appendChild(modalBody);
82
+ modalDialog.appendChild(modalContent);
83
+ modal.appendChild(modalDialog);
84
+ document.body.appendChild(modal);
85
+ const {default: Modal} = await import('../../../modal/modalSrc');
86
+ const modalEl = new Modal(modal, {context: document.body, backdrop: 'static'});
87
+ modalEl.show();
88
+
89
+ iframe.classList.add('modal-body-content');
90
+ iframe.contentWindow.document.open();
91
+ iframe.contentWindow.document.write(content);
92
+ iframe.contentWindow.document.close();
93
+
94
+ iframe.contentWindow.saveData = this.data;
95
+ modalSaveBtn.addEventListener('click', () => {
96
+ this.data = iframe.contentWindow.saveData;
97
+ if (this.data.svg) {
98
+ createSvg(wrapper, this.data, this.readOnly);
99
+ }
100
+
101
+ modalEl.hide();
102
+ });
103
+ modalCancelBtn.addEventListener('click', () => {
104
+ modalEl.hide();
105
+ });
106
+ });
107
+ } else {
108
+ wrapper.classList.add('readonly');
109
+ }
110
+ return wrapper;
111
+
112
+ function createWrapper(data, readOnly) {
113
+ const wrapper = document.createElement('div');
114
+ wrapper.classList.add('editorjs-drawing');
115
+ if (data.svg) {
116
+ wrapper.classList.remove('empty');
117
+ createSvg(wrapper, data, readOnly);
118
+ } else {
119
+ wrapper.classList.add('empty');
120
+ wrapper.innerHTML = `<div class="text-muted icon">${icon}</div><div class="text-muted text">Double click to start sketching out your thoughts.</div>`;
121
+ }
122
+ return wrapper;
123
+ }
124
+
125
+ function createSvg(wrapper, { svg, caption }, readOnly) {
126
+ const svgContainer = document.createElement('div');
127
+ svgContainer.innerHTML = svg;
128
+
129
+ wrapper.innerHTML = '';
130
+ wrapper.appendChild(svgContainer.childNodes[0]);
131
+
132
+ const captionEl = document.createElement('div');
133
+ captionEl.contentEditable = !readOnly;
134
+ captionEl.classList.add('editorjs-drawing-caption');
135
+ captionEl.textContent = caption;
136
+ if (!readOnly) {
137
+ captionEl.setAttribute('data-text', 'Caption');
138
+ captionEl.addEventListener('dblclick', (e) => {
139
+ e.stopPropagation();
140
+ });
141
+ }
142
+ wrapper.appendChild(captionEl);
143
+ }
144
+ }
145
+ save(blockContent){
146
+ const caption = blockContent.querySelector('[contenteditable]');
147
+ const data = cloneDeep(this.data);
148
+ data.caption = caption.innerHTML;
149
+ return data;
150
+ }
151
+ }
@@ -0,0 +1,63 @@
1
+ .editorjs-drawing {
2
+ --editorjs-drawing-border: #dcdfe6;
3
+
4
+ &.empty {
5
+ border: 1px dashed var(--editorjs-drawing-border);
6
+ min-height: 150px;
7
+ display: flex;
8
+ align-items: center;
9
+ justify-content: center;
10
+ }
11
+ position: relative;
12
+ z-index: 0;
13
+ margin-bottom: 10px;
14
+ border-radius: 5px;
15
+ height: max-content !important;
16
+
17
+ &:not(.readonly) {
18
+ cursor: pointer;
19
+
20
+ &:hover {
21
+ outline: 3px solid var(--editorjs-drawing-border);
22
+ }
23
+ }
24
+
25
+ .editorjs-drawing-caption {
26
+ margin-top: 1rem;
27
+ text-align: center;
28
+ &:empty:not(:focus):before {
29
+ content: attr(data-text);
30
+ opacity: .5;
31
+ }
32
+ }
33
+ svg {
34
+ width: 100%;
35
+ height: auto;
36
+ user-select: none;
37
+ pointer-events: none;
38
+ }
39
+ .icon {
40
+ width: 48px;
41
+ height: 48px;
42
+
43
+ path {
44
+ fill: currentColor;
45
+ stroke: none;
46
+ }
47
+ }
48
+ .text {
49
+
50
+ }
51
+ }
52
+
53
+ .editorjs-drawing-modal .modal-body {
54
+ padding: 0;
55
+ display: flex;
56
+ }
57
+ .editorjs-drawing-modal .modal-body-content {
58
+ flex: 1;
59
+ display: flex;
60
+ flex-direction: column;
61
+ justify-content: center;
62
+ align-items: center;
63
+ }