dragon-editor 2.0.0-beta.1.1 → 2.0.0-beta.1.3
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/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -8,7 +8,7 @@ const module = defineNuxtModule({
|
|
|
8
8
|
const resolver = createResolver(import.meta.url);
|
|
9
9
|
addComponent({
|
|
10
10
|
name: "DragonEditorComment",
|
|
11
|
-
filePath: resolver.resolve("./
|
|
11
|
+
filePath: resolver.resolve("./runtime/components/DragonEditorComment")
|
|
12
12
|
});
|
|
13
13
|
}
|
|
14
14
|
});
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="dragon-editor" @paste="pasteEvent" ref="$wrap" @mouseleave="deactiveMenuEvent"
|
|
3
|
+
@keydown="deactiveMenuEvent"
|
|
4
|
+
>
|
|
5
|
+
<div class="d-left-menu" :class="{'--active' : activeMenu}" :style="{top:`${leftMenuPosition}px`}">
|
|
6
|
+
<div class="d-add-block">
|
|
7
|
+
<button class="d-btn-menu-pop"></button>
|
|
8
|
+
|
|
9
|
+
<div class="d-block-list">
|
|
10
|
+
<button v-for="(row,count) in blockMenu" :key="count" class="d-btn-block" @click="row.action">
|
|
11
|
+
<SvgIcon v-if="row.hasIcon" :kind="row.icon"/>
|
|
12
|
+
<div v-else class="icon" v-html="row.icon"></div>
|
|
13
|
+
<p class="d-name">{{ row.name }}</p>
|
|
14
|
+
</button>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<div class="d-control-block">
|
|
19
|
+
<button class="d-btn-block-pop"></button>
|
|
20
|
+
|
|
21
|
+
<div class="d-block-list">
|
|
22
|
+
<button class="d-btn-block"></button>
|
|
23
|
+
<button class="d-btn-block"></button>
|
|
24
|
+
<button class="d-btn-block"></button>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<div class="d-style-menu" :class="{'--active' : activeMenu}" :style="{top:`${styleMenuPosition}px`}">
|
|
30
|
+
<div class="d-column">
|
|
31
|
+
<button class="d-btn" :class="{'--active' : checkAlignActive('d-align-left')}"
|
|
32
|
+
@click="setBlockEvent('alignLeft')"
|
|
33
|
+
>
|
|
34
|
+
<SvgIcon kind="alignLeft"/>
|
|
35
|
+
</button>
|
|
36
|
+
<button class="d-btn" :class="{'--active' : checkAlignActive('d-align-center')}"
|
|
37
|
+
@click="setBlockEvent('alignCenter')"
|
|
38
|
+
>
|
|
39
|
+
<SvgIcon kind="alignCenter"/>
|
|
40
|
+
</button>
|
|
41
|
+
<button class="d-btn" :class="{'--active' : checkAlignActive('d-align-right')}"
|
|
42
|
+
@click="setBlockEvent('alignRight')"
|
|
43
|
+
>
|
|
44
|
+
<SvgIcon kind="alignRight"/>
|
|
45
|
+
</button>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div class="d-column">
|
|
49
|
+
<button class="d-btn" :class="{'--active' : activeStyle.bold}"
|
|
50
|
+
@click="setBlockEvent('decorationBold')"
|
|
51
|
+
>
|
|
52
|
+
<SvgIcon kind="decorationBold"/>
|
|
53
|
+
</button>
|
|
54
|
+
|
|
55
|
+
<button class="d-btn" :class="{'--active' : activeStyle.italic}"
|
|
56
|
+
@click="setBlockEvent('decorationItalic')"
|
|
57
|
+
>
|
|
58
|
+
<SvgIcon kind="decorationItalic"/>
|
|
59
|
+
</button>
|
|
60
|
+
|
|
61
|
+
<button class="d-btn" :class="{'--active' : activeStyle.underline}"
|
|
62
|
+
@click="setBlockEvent('decorationUnderline')"
|
|
63
|
+
>
|
|
64
|
+
<SvgIcon kind="decorationUnderline"/>
|
|
65
|
+
</button>
|
|
66
|
+
|
|
67
|
+
<button class="d-btn" :class="{'--active' : activeStyle.through}"
|
|
68
|
+
@click="setBlockEvent('decorationStrikethrough')"
|
|
69
|
+
>
|
|
70
|
+
<SvgIcon kind="decorationStrikethrough"/>
|
|
71
|
+
</button>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<div class="d-column">
|
|
75
|
+
<button class="d-btn">
|
|
76
|
+
<SvgIcon kind="link"/>
|
|
77
|
+
</button>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<div class="d-column">
|
|
81
|
+
<button class="d-btn">
|
|
82
|
+
<SvgIcon kind="codeBlock"/>
|
|
83
|
+
</button>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<div v-if="customStyleMenu.length > 0" class="d-column">
|
|
87
|
+
<!-- customStyleMenu-->
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<div
|
|
92
|
+
class="d-row-block"
|
|
93
|
+
v-for="(row,count) in content"
|
|
94
|
+
:key="count"
|
|
95
|
+
@click="activeIdx = count"
|
|
96
|
+
@mouseenter="activeMenuEvent"
|
|
97
|
+
@mousemove="activeMenuEvent"
|
|
98
|
+
@mouseup="activeMenuEvent"
|
|
99
|
+
>
|
|
100
|
+
<component
|
|
101
|
+
ref="$child"
|
|
102
|
+
v-model="content[count]"
|
|
103
|
+
:is="setComponentKind(row.type)"
|
|
104
|
+
@addBlock="addBlockLocal"
|
|
105
|
+
/>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</template>
|
|
109
|
+
|
|
110
|
+
<script setup lang="ts">
|
|
111
|
+
import {ref, unref, onMounted} from "#imports";
|
|
112
|
+
import {createBlock, getClipboardData, getCursor} from "../../core/utils";
|
|
113
|
+
import {
|
|
114
|
+
editorOptions,
|
|
115
|
+
editorMenu,
|
|
116
|
+
editorContentType,
|
|
117
|
+
userCustomMenu,
|
|
118
|
+
styleActiveType,
|
|
119
|
+
userStyleMenu
|
|
120
|
+
} from "../../types/index";
|
|
121
|
+
|
|
122
|
+
// components
|
|
123
|
+
import SvgIcon from "../../core/components/SvgIcon.vue";
|
|
124
|
+
import textBlock from "../../core/components/editor/TextBlock.vue";
|
|
125
|
+
|
|
126
|
+
// props
|
|
127
|
+
const props = withDefaults(defineProps<{ modelValue: editorContentType, option?: editorOptions }>(), {
|
|
128
|
+
modelValue: () => [],
|
|
129
|
+
option: () => {
|
|
130
|
+
return {
|
|
131
|
+
blockMenu: ["text", "ol", "ul", "table", "quotation"]
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
const emit = defineEmits<{
|
|
136
|
+
(e: "update:modelValue", modelValue: editorContentType): void;
|
|
137
|
+
}>();
|
|
138
|
+
|
|
139
|
+
// Editor data
|
|
140
|
+
const $wrap = ref();
|
|
141
|
+
const $child = ref();
|
|
142
|
+
const activeMenu = ref<boolean>(false);
|
|
143
|
+
const leftMenuPosition = ref<number>(0);
|
|
144
|
+
const styleMenuPosition = ref<number>(0);
|
|
145
|
+
const iconList = ["textBlock", "imageBlock", "ulBlock", "olBlock", "quotationBlock", "tableBlock"];
|
|
146
|
+
const blockMenu = ref<editorMenu[]>([]);
|
|
147
|
+
const customStyleMenu = ref<userStyleMenu[]>([]);
|
|
148
|
+
const content = ref<editorContentType>([]);
|
|
149
|
+
const activeIdx = ref<number>(0);
|
|
150
|
+
const activeStyle = ref<styleActiveType>({
|
|
151
|
+
bold: false,
|
|
152
|
+
italic: false,
|
|
153
|
+
underline: false,
|
|
154
|
+
through: false,
|
|
155
|
+
link: false,
|
|
156
|
+
code: false,
|
|
157
|
+
});
|
|
158
|
+
// const activeItemId = ref<string>("");
|
|
159
|
+
// const selectItems = ref<string[]>([]);
|
|
160
|
+
|
|
161
|
+
// initial logic
|
|
162
|
+
onMounted(() => {
|
|
163
|
+
dataUpdateAction();
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// block menu setting
|
|
167
|
+
blockMenu.value = setEditorMenu(props.option.blockMenu as string[], unref(props.option.customBlockMenu) as userCustomMenu[]);
|
|
168
|
+
|
|
169
|
+
// 유저 커스텀 스타일 메뉴
|
|
170
|
+
if (props.option.customStyleMenu) {
|
|
171
|
+
customStyleMenu.value = unref(props.option.customStyleMenu);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// content data setting
|
|
175
|
+
if (props.modelValue && Array.isArray(props.modelValue)) {
|
|
176
|
+
if (props.modelValue.length == 0) {
|
|
177
|
+
addBlockLocal("text", true);
|
|
178
|
+
} else {
|
|
179
|
+
content.value = unref(props.modelValue) as editorContentType;
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
throw new Error("[DragonEditor]ERROR : You must set 'v-model' attribute and 'v-mode' type is must be Array.");
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// local logic
|
|
186
|
+
function checkAlignActive(className: string) {
|
|
187
|
+
const data = content.value[activeIdx.value];
|
|
188
|
+
let value = false;
|
|
189
|
+
|
|
190
|
+
switch (data.type) {
|
|
191
|
+
case "text":
|
|
192
|
+
value = data.classList.indexOf(className) > -1;
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return value;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function checkDecoActive() {
|
|
200
|
+
activeStyle.value.bold = hasClassNameCheckLogic("d-deco-bold");
|
|
201
|
+
activeStyle.value.italic = hasClassNameCheckLogic("d-deco-italic");
|
|
202
|
+
activeStyle.value.underline = hasClassNameCheckLogic("d-deco-underline");
|
|
203
|
+
activeStyle.value.through = hasClassNameCheckLogic("d-deco-through");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function hasClassNameCheckLogic(className: string) {
|
|
207
|
+
const cursorData = getCursor();
|
|
208
|
+
let value = false;
|
|
209
|
+
|
|
210
|
+
if (cursorData.type === "Caret") {
|
|
211
|
+
const type = (cursorData.startNode as Node).constructor.name;
|
|
212
|
+
let $target = cursorData.startNode as HTMLElement;
|
|
213
|
+
|
|
214
|
+
if (type === "Text") {
|
|
215
|
+
$target = (cursorData.startNode as HTMLElement).parentNode as HTMLElement;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if ($target) {
|
|
219
|
+
const classList = [...$target.classList];
|
|
220
|
+
|
|
221
|
+
if (classList.indexOf(className) > -1) {
|
|
222
|
+
value = true;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return value;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
function setEditorMenu(vanillaData: string[], customData?: userCustomMenu[]) {
|
|
232
|
+
const dataList: editorMenu[] = [];
|
|
233
|
+
|
|
234
|
+
vanillaData.forEach((name) => {
|
|
235
|
+
dataList.push({
|
|
236
|
+
name: name,
|
|
237
|
+
hasIcon: true,
|
|
238
|
+
icon: `${name}Block`,
|
|
239
|
+
action: () => {
|
|
240
|
+
addBlockLocal(name);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
if (customData) {
|
|
246
|
+
customData.forEach((row) => {
|
|
247
|
+
dataList.push({
|
|
248
|
+
name: row.name,
|
|
249
|
+
hasIcon: iconList.indexOf(row.icon) > -1,
|
|
250
|
+
icon: row.icon,
|
|
251
|
+
action: row.action
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return dataList;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
// event function
|
|
261
|
+
function activeMenuEvent(e: MouseEvent) {
|
|
262
|
+
setMenuPosition(e.currentTarget as HTMLElement);
|
|
263
|
+
checkDecoActive();
|
|
264
|
+
activeMenu.value = true;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function deactiveMenuEvent() {
|
|
268
|
+
activeMenu.value = false;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function dataUpdateAction() {
|
|
272
|
+
$child.value.forEach((row: any) => {
|
|
273
|
+
row.updateBlockData();
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
emit("update:modelValue", content.value);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function addBlockLocal(name: string, time: boolean = false) {
|
|
280
|
+
const block = createBlock(name);
|
|
281
|
+
|
|
282
|
+
content.value.splice(activeIdx.value + 1, 0, block);
|
|
283
|
+
|
|
284
|
+
if (time === false) {
|
|
285
|
+
activeIdx.value += 1;
|
|
286
|
+
|
|
287
|
+
setTimeout(() => { // waiting data set
|
|
288
|
+
if (name !== "image") {
|
|
289
|
+
$child.value[activeIdx.value].focus();
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
dataUpdateAction();
|
|
293
|
+
}, 100);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function pasteEvent(e: ClipboardEvent) {
|
|
298
|
+
e.preventDefault();
|
|
299
|
+
const data = getClipboardData(e.clipboardData as DataTransfer);
|
|
300
|
+
|
|
301
|
+
if (data.type === "text") {
|
|
302
|
+
const targetComponent = $child.value[activeIdx.value];
|
|
303
|
+
const componentType = targetComponent.getType();
|
|
304
|
+
|
|
305
|
+
if (componentType !== "image" && componentType !== "other") {
|
|
306
|
+
targetComponent.pasteEvent(data.value);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function setComponentKind(kind: string) {
|
|
312
|
+
switch (kind) {
|
|
313
|
+
case "text":
|
|
314
|
+
return textBlock;
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function setMenuPosition($target: HTMLElement) {
|
|
320
|
+
const parentNode = $wrap.value.parentNode;
|
|
321
|
+
const bodyRect = document.body.getBoundingClientRect();
|
|
322
|
+
const wrapRect = $wrap.value.getBoundingClientRect();
|
|
323
|
+
const targetRect = $target.getBoundingClientRect();
|
|
324
|
+
const parentNodeScrollY = parentNode.scrollTop;
|
|
325
|
+
const wrapTop = wrapRect.top - bodyRect.top;
|
|
326
|
+
const targetTop = targetRect.top - bodyRect.top;
|
|
327
|
+
const targetBottom = targetRect.bottom - bodyRect.top;
|
|
328
|
+
const top = ((targetTop - (wrapTop + 10)) - parentNodeScrollY) + 13;
|
|
329
|
+
const bottom = ((targetBottom - (wrapTop + 10)) - parentNodeScrollY) + 10;
|
|
330
|
+
|
|
331
|
+
styleMenuPosition.value = bottom;
|
|
332
|
+
leftMenuPosition.value = top;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function setBlockEvent(type: string) {
|
|
336
|
+
$child.value[activeIdx.value].setStyles(type);
|
|
337
|
+
setTimeout(() => {
|
|
338
|
+
checkDecoActive();
|
|
339
|
+
}, 100);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
// export function
|
|
344
|
+
// function checkStyleActive(className: string) {
|
|
345
|
+
// return hasClassNameCheckLogic(className);
|
|
346
|
+
// }
|
|
347
|
+
|
|
348
|
+
function addImageBlock() {
|
|
349
|
+
console.log("local image added event!");
|
|
350
|
+
console.log($child);
|
|
351
|
+
// contentData.value = value;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
defineExpose({
|
|
355
|
+
addImageBlock
|
|
356
|
+
});
|
|
357
|
+
</script>
|
|
358
|
+
|
|
359
|
+
<style>
|
|
360
|
+
@import "../../core/style/common.css";
|
|
361
|
+
</style>
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="dragon-editor">
|
|
3
|
+
<p class="d-text-block" :class="data.classList" contenteditable v-html="data.content" @keydown="textKeyboardEvent"
|
|
4
|
+
@paste="pasteEvent" ref="$block"></p>
|
|
5
|
+
</div>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script setup lang="ts">
|
|
9
|
+
import { ref, unref } from "#imports";
|
|
10
|
+
import {
|
|
11
|
+
keyboardEvent,
|
|
12
|
+
setCursor,
|
|
13
|
+
pasteText,
|
|
14
|
+
styleSettings,
|
|
15
|
+
getArrangementCursorData,
|
|
16
|
+
getClipboardData,
|
|
17
|
+
getCursor,
|
|
18
|
+
findEditableElement
|
|
19
|
+
} from "../../core/utils/index";
|
|
20
|
+
import { commentBlock } from "../../types/index";
|
|
21
|
+
|
|
22
|
+
const $block = ref();
|
|
23
|
+
const data = ref<commentBlock>({
|
|
24
|
+
type: "comment",
|
|
25
|
+
classList: [],
|
|
26
|
+
content: "",
|
|
27
|
+
});
|
|
28
|
+
const props = defineProps<{ modelValue: commentBlock }>();
|
|
29
|
+
const emit = defineEmits<{
|
|
30
|
+
(e: "update:modelValue", modelValue: commentBlock): void;
|
|
31
|
+
}>();
|
|
32
|
+
|
|
33
|
+
data.value = unref(props.modelValue) as commentBlock;
|
|
34
|
+
|
|
35
|
+
function textKeyboardEvent(e: KeyboardEvent) {
|
|
36
|
+
keyboardEvent("comment", e, emit);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function pasteEvent(e: ClipboardEvent) {
|
|
40
|
+
e.preventDefault();
|
|
41
|
+
const data = getClipboardData(e.clipboardData as DataTransfer);
|
|
42
|
+
|
|
43
|
+
if (data.type === "text") {
|
|
44
|
+
pasteText("text", data.value as string);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// export event
|
|
49
|
+
function updateBlockData() {
|
|
50
|
+
// 데이터 정규화 및 검수
|
|
51
|
+
const blockClassList = [...$block.value.classList];
|
|
52
|
+
blockClassList.splice(0, 1);
|
|
53
|
+
const pushList = blockClassList.filter(
|
|
54
|
+
(item) => data.value.classList.indexOf(item) === -1
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
data.value.classList = data.value.classList.concat(pushList);
|
|
58
|
+
|
|
59
|
+
// 커서위치 재지정
|
|
60
|
+
if ($block.value.innerHTML.length > 0) {
|
|
61
|
+
const cursorData = getArrangementCursorData();
|
|
62
|
+
|
|
63
|
+
data.value.content = $block.value.innerHTML;
|
|
64
|
+
emit("update:modelValue", data.value);
|
|
65
|
+
|
|
66
|
+
setTimeout(() => {
|
|
67
|
+
setCursor(
|
|
68
|
+
$block.value.childNodes[cursorData.childCount],
|
|
69
|
+
cursorData.length
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// 빈 태그 삭제
|
|
73
|
+
$block.value.childNodes.forEach((child: ChildNode) => {
|
|
74
|
+
if (
|
|
75
|
+
child.constructor.name !== "Text" &&
|
|
76
|
+
child.textContent === ""
|
|
77
|
+
) {
|
|
78
|
+
child.remove();
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}, 100);
|
|
82
|
+
} else {
|
|
83
|
+
emit("update:modelValue", data.value);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function focus() {
|
|
88
|
+
setCursor($block.value, 0);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function setStyles(kind: string) {
|
|
92
|
+
data.value = styleSettings(kind, data.value, $block.value);
|
|
93
|
+
setTimeout(() => {
|
|
94
|
+
updateBlockData();
|
|
95
|
+
}, 250);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function getCursorClassList(className: string) {
|
|
99
|
+
const cursorData = getCursor();
|
|
100
|
+
let value: string[] = [];
|
|
101
|
+
|
|
102
|
+
if (cursorData.type === "Caret") {
|
|
103
|
+
const type = (cursorData.startNode as Node).constructor.name;
|
|
104
|
+
const editableElement = findEditableElement(cursorData.startNode as Node);
|
|
105
|
+
let $target = cursorData.startNode as HTMLElement;
|
|
106
|
+
|
|
107
|
+
if (type === "Text") {
|
|
108
|
+
$target = (cursorData.startNode as HTMLElement).parentNode as HTMLElement;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if ($target !== editableElement) {
|
|
112
|
+
value = [...$target.classList];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
defineExpose({
|
|
120
|
+
updateBlockData,
|
|
121
|
+
focus,
|
|
122
|
+
setStyles,
|
|
123
|
+
getCursorClassList
|
|
124
|
+
});
|
|
125
|
+
</script>
|
|
126
|
+
|
|
127
|
+
<style>
|
|
128
|
+
@import "../../core/style/common.css";
|
|
129
|
+
</style>
|