mirror of
https://github.com/directus/directus.git
synced 2026-01-23 17:07:58 -05:00
Finish markdown interface v3
This commit is contained in:
@@ -4,6 +4,7 @@ import VAvatar from './v-avatar/';
|
||||
import VBadge from './v-badge/';
|
||||
import VBreadcrumb from './v-breadcrumb';
|
||||
import VButton from './v-button/';
|
||||
import VButtonGroup from './v-button-group/';
|
||||
import VCard, { VCardActions, VCardTitle, VCardSubtitle, VCardText } from './v-card';
|
||||
import VCheckbox from './v-checkbox/';
|
||||
import VChip from './v-chip/';
|
||||
@@ -43,6 +44,7 @@ Vue.component('v-avatar', VAvatar);
|
||||
Vue.component('v-badge', VBadge);
|
||||
Vue.component('v-breadcrumb', VBreadcrumb);
|
||||
Vue.component('v-button', VButton);
|
||||
Vue.component('v-button-group', VButtonGroup);
|
||||
Vue.component('v-card-actions', VCardActions);
|
||||
Vue.component('v-card-subtitle', VCardSubtitle);
|
||||
Vue.component('v-card-text', VCardText);
|
||||
|
||||
@@ -85,21 +85,21 @@ body {
|
||||
}
|
||||
|
||||
&.tile .v-item-group ::v-deep .v-button {
|
||||
&:first-child {
|
||||
&:first-child .button {
|
||||
--border-radius: 0px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
&:last-child .button {
|
||||
--border-radius: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
&.rounded:not(.tile) .v-item-group ::v-deep .v-button {
|
||||
&:first-child {
|
||||
&:first-child .button {
|
||||
--border-radius: var(--v-button-height) 0px 0px var(--v-button-height);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
&:last-child .button {
|
||||
--border-radius: 0px var(--v-button-height) var(--v-button-height) 0px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,12 +209,6 @@ body {
|
||||
border-color: var(--v-button-background-color-hover);
|
||||
}
|
||||
|
||||
&.activated {
|
||||
color: var(--v-button-color);
|
||||
background-color: var(--v-button-background-color);
|
||||
border-color: var(--v-button-background-color);
|
||||
}
|
||||
|
||||
&.align-left {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
@@ -267,9 +261,9 @@ body {
|
||||
--v-button-font-size: 12px;
|
||||
--v-button-font-weight: 600;
|
||||
--v-button-min-width: 60px;
|
||||
--border-radius: 4px;
|
||||
|
||||
padding: 0 12px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
&.small {
|
||||
@@ -336,7 +330,8 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
&.activated {
|
||||
&.activated,
|
||||
&.active {
|
||||
--v-button-color: var(--v-button-color-activated) !important;
|
||||
--v-button-background-color: var(--v-button-background-color-activated) !important;
|
||||
--v-button-background-color-hover: var(--v-button-background-color-activated) !important;
|
||||
|
||||
@@ -1,46 +1,234 @@
|
||||
import { Ref } from '@vue/composition-api';
|
||||
import { Ref, nextTick } from '@vue/composition-api';
|
||||
import { Position } from 'codemirror';
|
||||
import { cloneDeep } from 'lodash';
|
||||
|
||||
type Alteration = 'bold' | 'italic' | 'strikethrough';
|
||||
type Alteration =
|
||||
| 'bold'
|
||||
| 'italic'
|
||||
| 'strikethrough'
|
||||
| 'listBulleted'
|
||||
| 'listNumbered'
|
||||
| 'heading'
|
||||
| 'blockquote'
|
||||
| 'code'
|
||||
| 'link';
|
||||
|
||||
type AlterationFunctions = Record<Alteration, (selections: string[]) => string[]>;
|
||||
type AlterationFunctions = Record<
|
||||
Alteration,
|
||||
(
|
||||
selections: string,
|
||||
cursors: { cursorHead: Position; cursorFrom: Position; cursorTo: Position }
|
||||
) => { newSelection: string; newCursor: Position; highlight?: { from: Position; to: Position } }
|
||||
>;
|
||||
|
||||
export function useEdit(codemirror: Ref<CodeMirror.EditorFromTextArea | null>) {
|
||||
const alterations: AlterationFunctions = {
|
||||
bold(selections) {
|
||||
return selections.map((selection) => {
|
||||
if (selection.startsWith('**') && selection.endsWith('**')) {
|
||||
return selection.substring(2, selection.length - 2);
|
||||
} else {
|
||||
return `**${selection}**`;
|
||||
}
|
||||
});
|
||||
heading(selection, { cursorTo }) {
|
||||
let newSelection = selection;
|
||||
let newCursor = cursorTo;
|
||||
|
||||
if (selection.startsWith('# ')) {
|
||||
newSelection = selection.substring(2);
|
||||
} else {
|
||||
newSelection = `# ${selection}`;
|
||||
newCursor.ch = newCursor.ch + 2;
|
||||
}
|
||||
|
||||
return { newSelection, newCursor };
|
||||
},
|
||||
italic(selections) {
|
||||
return selections.map((selection) => {
|
||||
if (selection.startsWith('*') && selection.endsWith('*')) {
|
||||
return selection.substring(1, selection.length - 1);
|
||||
} else {
|
||||
return `*${selection}*`;
|
||||
}
|
||||
});
|
||||
bold(selection, { cursorTo }) {
|
||||
let newSelection = selection;
|
||||
let newCursor = cursorTo;
|
||||
|
||||
if (selection.startsWith('**') && selection.endsWith('**')) {
|
||||
newSelection = selection.substring(2, selection.length - 2);
|
||||
} else {
|
||||
newSelection = `**${selection}**`;
|
||||
newCursor.ch = newCursor.ch + 2;
|
||||
}
|
||||
|
||||
return { newSelection, newCursor };
|
||||
},
|
||||
strikethrough(selections) {
|
||||
return selections.map((selection) => {
|
||||
if (selection.startsWith('~~') && selection.endsWith('~~')) {
|
||||
return selection.substring(2, selection.length - 2);
|
||||
italic(selection, { cursorTo }) {
|
||||
let newSelection = selection;
|
||||
let newCursor = cursorTo;
|
||||
|
||||
if (selection.startsWith('*') && selection.endsWith('*')) {
|
||||
newSelection = selection.substring(1, selection.length - 1);
|
||||
} else {
|
||||
newSelection = `*${selection}*`;
|
||||
newCursor.ch = newCursor.ch + 1;
|
||||
}
|
||||
|
||||
return { newSelection, newCursor };
|
||||
},
|
||||
strikethrough(selection, { cursorTo }) {
|
||||
let newSelection = selection;
|
||||
let newCursor = cursorTo;
|
||||
|
||||
if (selection.startsWith('~~') && selection.endsWith('~~')) {
|
||||
newSelection = selection.substring(2, selection.length - 2);
|
||||
} else {
|
||||
newSelection = `~~${selection}~~`;
|
||||
newCursor.ch = newCursor.ch + 2;
|
||||
}
|
||||
|
||||
return { newSelection, newCursor };
|
||||
},
|
||||
listBulleted(selection, { cursorTo }) {
|
||||
let newSelection = selection;
|
||||
let newCursor = cursorTo;
|
||||
|
||||
const lines = selection.split('\n');
|
||||
|
||||
const isList = lines.every((line) => line.startsWith('- '));
|
||||
|
||||
if (isList) {
|
||||
newSelection = lines.map((line) => line.substring(2)).join('\n');
|
||||
} else {
|
||||
newSelection = lines.map((line) => `- ${line}`).join('\n');
|
||||
}
|
||||
|
||||
if (!selection) {
|
||||
newCursor.ch = newCursor.ch + 2;
|
||||
}
|
||||
|
||||
return { newSelection, newCursor };
|
||||
},
|
||||
listNumbered(selection, { cursorTo }) {
|
||||
let newSelection = selection;
|
||||
let newCursor = cursorTo;
|
||||
|
||||
const lines = selection.split('\n');
|
||||
|
||||
const isList = lines.every((line, index) => line.startsWith(`${index + 1}.`));
|
||||
|
||||
if (isList) {
|
||||
newSelection = lines.map((line) => line.substring(3)).join('\n');
|
||||
} else {
|
||||
newSelection = lines.map((line, index) => `${index + 1}. ${line}`).join('\n');
|
||||
}
|
||||
|
||||
if (!selection) {
|
||||
newCursor.ch = newCursor.ch + 2;
|
||||
}
|
||||
|
||||
return { newSelection, newCursor };
|
||||
},
|
||||
blockquote(selection, { cursorTo }) {
|
||||
let newSelection = selection;
|
||||
let newCursor = cursorTo;
|
||||
|
||||
const lines = selection.split('\n');
|
||||
|
||||
const isList = lines.every((line) => line.startsWith('> '));
|
||||
|
||||
if (isList) {
|
||||
newSelection = lines.map((line) => line.substring(2)).join('\n');
|
||||
} else {
|
||||
newSelection = lines.map((line) => `> ${line}`).join('\n');
|
||||
}
|
||||
|
||||
if (!selection) {
|
||||
newCursor.ch = newCursor.ch + 2;
|
||||
}
|
||||
|
||||
return { newSelection, newCursor };
|
||||
},
|
||||
code(selection, { cursorTo }) {
|
||||
if (selection.includes('\n')) {
|
||||
// Multiline
|
||||
let newSelection = selection;
|
||||
let newCursor = cursorTo;
|
||||
|
||||
if (selection.startsWith('```') && selection.endsWith('```')) {
|
||||
newSelection = selection.substring(3, selection.length - 3);
|
||||
} else {
|
||||
return `~~${selection}~~`;
|
||||
newSelection = '```\n' + newSelection + '\n```';
|
||||
newCursor.line = newCursor.line + 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { newSelection, newCursor };
|
||||
} else {
|
||||
// Inline
|
||||
let newSelection = selection;
|
||||
let newCursor = cursorTo;
|
||||
|
||||
if (selection.startsWith('`') && selection.endsWith('`')) {
|
||||
newSelection = selection.substring(1, selection.length - 1);
|
||||
} else {
|
||||
newSelection = `\`${selection}\``;
|
||||
newCursor.ch = newCursor.ch + 1;
|
||||
}
|
||||
|
||||
return { newSelection, newCursor };
|
||||
}
|
||||
},
|
||||
link(selection, { cursorFrom, cursorTo }) {
|
||||
let newSelection = selection;
|
||||
let newCursor = cursorTo;
|
||||
let highlight;
|
||||
|
||||
if (selection.endsWith('](url)')) {
|
||||
newSelection = selection.substring(1, selection.length - 6);
|
||||
} else if (selection.startsWith('http')) {
|
||||
newSelection = `[](${selection})`;
|
||||
newCursor.ch = cursorFrom.ch + 1;
|
||||
} else {
|
||||
newSelection = `[${selection}](url)`;
|
||||
|
||||
if (selection) {
|
||||
highlight = {
|
||||
from: {
|
||||
...cloneDeep(newCursor),
|
||||
ch: newCursor.ch + 3,
|
||||
},
|
||||
to: {
|
||||
...cloneDeep(newCursor),
|
||||
ch: newCursor.ch + 6,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
newCursor.ch = cursorFrom.ch + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return { newSelection, newCursor, highlight };
|
||||
},
|
||||
};
|
||||
|
||||
return { edit };
|
||||
|
||||
function edit(type: Alteration) {
|
||||
if (codemirror.value) {
|
||||
const selections = codemirror.value.getSelections();
|
||||
codemirror.value.replaceSelection(alterations[type](selections));
|
||||
const cursor = codemirror.value.getCursor('head');
|
||||
const cursorFrom = codemirror.value.getCursor('from');
|
||||
const cursorTo = codemirror.value.getCursor('to');
|
||||
|
||||
const wordRange = codemirror.value.findWordAt(cursor);
|
||||
const word = codemirror.value.getRange(wordRange.anchor, wordRange.head).trim();
|
||||
|
||||
const selection = codemirror.value.getSelection();
|
||||
|
||||
const { newSelection, newCursor, highlight } = alterations[type](selection || word, {
|
||||
cursorFrom: cloneDeep(selection ? cursorFrom : wordRange.anchor),
|
||||
cursorTo: cloneDeep(selection ? cursorTo : wordRange.head),
|
||||
cursorHead: cursor,
|
||||
});
|
||||
|
||||
if (word && !selection) {
|
||||
codemirror.value.replaceRange(newSelection, wordRange.anchor, wordRange.head);
|
||||
} else {
|
||||
codemirror.value.replaceSelection(newSelection);
|
||||
}
|
||||
|
||||
codemirror.value.setCursor(newCursor);
|
||||
|
||||
if (highlight) {
|
||||
codemirror.value.setSelection(highlight.from, highlight.to);
|
||||
}
|
||||
|
||||
codemirror.value.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,34 @@
|
||||
<template>
|
||||
<div class="interface-markdown">
|
||||
<div class="interface-markdown" :class="view[0]">
|
||||
<div class="toolbar">
|
||||
<v-button icon @click="edit('bold')"><v-icon name="format_bold" /></v-button>
|
||||
<v-button icon @click="edit('italic')"><v-icon name="format_italic" /></v-button>
|
||||
<v-button icon @click="edit('strikethrough')"><v-icon name="format_strikethrough" /></v-button>
|
||||
<v-button small icon @click="edit('heading')"><v-icon name="title" /></v-button>
|
||||
<v-button small icon @click="edit('bold')"><v-icon name="format_bold" /></v-button>
|
||||
<v-button small icon @click="edit('italic')"><v-icon name="format_italic" /></v-button>
|
||||
<v-button small icon @click="edit('strikethrough')"><v-icon name="format_strikethrough" /></v-button>
|
||||
<v-button small icon @click="edit('listBulleted')"><v-icon name="format_list_bulleted" /></v-button>
|
||||
<v-button small icon @click="edit('listNumbered')"><v-icon name="format_list_numbered" /></v-button>
|
||||
<v-button small icon @click="edit('blockquote')"><v-icon name="format_quote" /></v-button>
|
||||
<v-button small icon @click="edit('code')"><v-icon name="code" /></v-button>
|
||||
<v-button small icon @click="edit('link')"><v-icon name="insert_link" /></v-button>
|
||||
|
||||
<div class="spacer"></div>
|
||||
|
||||
<v-button-group class="view" mandatory v-model="view" rounded>
|
||||
<v-button x-small value="editor">Editor</v-button>
|
||||
<v-button x-small value="preview">Preview</v-button>
|
||||
</v-button-group>
|
||||
</div>
|
||||
|
||||
<textarea ref="codemirrorEl" :value="value || ''" />
|
||||
|
||||
<div class="preview"></div>
|
||||
<div v-show="view[0] === 'preview'" class="preview-box" v-html="html"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, ref, onMounted, onUnmounted } from '@vue/composition-api';
|
||||
import { defineComponent, computed, ref, onMounted, onUnmounted, watch } from '@vue/composition-api';
|
||||
import { sanitize } from 'dompurify';
|
||||
import marked from 'marked';
|
||||
|
||||
import CodeMirror from 'codemirror';
|
||||
import 'codemirror/mode/markdown/markdown';
|
||||
@@ -35,17 +50,27 @@ export default defineComponent({
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
setup(props, { emit }) {
|
||||
const codemirrorEl = ref<HTMLTextAreaElement | null>(null);
|
||||
const codemirror = ref<CodeMirror.EditorFromTextArea | null>(null);
|
||||
|
||||
const view = ref(['editor']);
|
||||
|
||||
onMounted(async () => {
|
||||
if (codemirrorEl.value) {
|
||||
codemirror.value = CodeMirror.fromTextArea(codemirrorEl.value, {
|
||||
mode: 'markdown'
|
||||
mode: 'markdown',
|
||||
configureMouse: () => ({ addNew: false }),
|
||||
});
|
||||
|
||||
codemirror.value.setValue(props.value || '');
|
||||
|
||||
codemirror.value.on('change', (cm, { origin }) => {
|
||||
if (origin === 'setValue') return;
|
||||
|
||||
const content = cm.getValue();
|
||||
emit('input', content);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -53,12 +78,259 @@ export default defineComponent({
|
||||
codemirror.value?.toTextArea();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(newValue) => {
|
||||
if (codemirror.value?.getValue() !== newValue) {
|
||||
codemirror.value?.setValue(props.value || '');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const { edit } = useEdit(codemirror);
|
||||
|
||||
return { codemirrorEl, edit };
|
||||
const html = computed(() => {
|
||||
const html = marked(props.value || '');
|
||||
const htmlSanitized = sanitize(html);
|
||||
return htmlSanitized;
|
||||
});
|
||||
|
||||
return { codemirrorEl, edit, view, html };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.interface-markdown {
|
||||
--v-button-background-color: transparent;
|
||||
--v-button-color: var(--foreground-normal);
|
||||
--v-button-background-color-hover: var(--border-normal);
|
||||
--v-button-color-hover: var(--foreground-normal);
|
||||
|
||||
min-height: 300px;
|
||||
overflow: hidden;
|
||||
border: 2px solid var(--border-normal);
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
textarea {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.interface-markdown ::v-deep .CodeMirror {
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
|
||||
.CodeMirror-lines {
|
||||
padding: 0 20px;
|
||||
|
||||
&:first-of-type {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.CodeMirror-scroll {
|
||||
min-height: 300px - 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.interface-markdown.preview {
|
||||
::v-deep .CodeMirror {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
padding: 0 4px;
|
||||
background-color: var(--background-subdued);
|
||||
border-bottom: 2px solid var(--border-normal);
|
||||
|
||||
.v-button + .v-button {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.view {
|
||||
--v-button-background-color: var(--border-subdued);
|
||||
--v-button-color: var(--foreground-subdued);
|
||||
--v-button-background-color-hover: var(--border-normal);
|
||||
--v-button-color-hover: var(--foreground-normal);
|
||||
--v-button-background-color-activated: var(--border-normal);
|
||||
--v-button-color-activated: var(--foreground-normal);
|
||||
}
|
||||
}
|
||||
|
||||
.preview-box {
|
||||
padding: 20px;
|
||||
|
||||
::v-deep {
|
||||
h1 {
|
||||
margin-bottom: 0;
|
||||
font-weight: 300;
|
||||
font-size: 44px;
|
||||
font-family: var(--font-serif), serif;
|
||||
line-height: 52px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 60px;
|
||||
margin-bottom: 0;
|
||||
font-weight: 600;
|
||||
font-size: 34px;
|
||||
line-height: 38px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 40px;
|
||||
margin-bottom: 0;
|
||||
font-weight: 600;
|
||||
font-size: 26px;
|
||||
line-height: 31px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-top: 40px;
|
||||
margin-bottom: 0;
|
||||
font-weight: 600;
|
||||
font-size: 22px;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
h5 {
|
||||
margin-top: 40px;
|
||||
margin-bottom: 0;
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
h6 {
|
||||
margin-top: 40px;
|
||||
margin-bottom: 0;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
font-size: 16px;
|
||||
font-family: var(--font-serif), serif;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #546e7a;
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
margin: 24px 0;
|
||||
font-size: 18px;
|
||||
font-family: var(--font-serif), serif;
|
||||
line-height: 34px;
|
||||
}
|
||||
|
||||
ul ul,
|
||||
ol ol,
|
||||
ul ol,
|
||||
ol ul {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
code {
|
||||
padding: 2px 4px;
|
||||
font-size: 18px;
|
||||
font-family: var(--family-monospace), monospace;
|
||||
line-height: 34px;
|
||||
overflow-wrap: break-word;
|
||||
background-color: #eceff1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 20px;
|
||||
overflow: auto;
|
||||
font-size: 18px;
|
||||
font-family: var(--family-monospace), monospace;
|
||||
line-height: 24px;
|
||||
background-color: #eceff1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin-left: -10px;
|
||||
padding-left: 10px;
|
||||
font-size: 18px;
|
||||
font-family: var(--font-serif), serif;
|
||||
font-style: italic;
|
||||
line-height: 34px;
|
||||
border-left: 2px solid #546e7a;
|
||||
|
||||
blockquote {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
video,
|
||||
iframe,
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
hr {
|
||||
margin-top: 52px;
|
||||
margin-bottom: 56px;
|
||||
text-align: center;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
hr::after {
|
||||
font-size: 28px;
|
||||
line-height: 0;
|
||||
letter-spacing: 16px;
|
||||
content: '...';
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table th,
|
||||
table td {
|
||||
padding: 0.4rem;
|
||||
border: 1px solid #cfd8dc;
|
||||
}
|
||||
|
||||
figure {
|
||||
display: table;
|
||||
margin: 1rem auto;
|
||||
}
|
||||
|
||||
figure figcaption {
|
||||
display: block;
|
||||
margin-top: 0.25rem;
|
||||
color: #999;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
}
|
||||
|
||||
.tox .tox-tbtn {
|
||||
margin: 2px 2px 4px 0;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
margin: 0;
|
||||
color: var(--foreground-normal);
|
||||
}
|
||||
|
||||
@@ -108,8 +110,9 @@
|
||||
.tox .tox-toolbar,
|
||||
.tox .tox-toolbar__primary,
|
||||
.tox .tox-toolbar__overflow {
|
||||
background: url("data:image/svg+xml;charset=utf8,%3Csvg height='40px' viewBox='0 0 40 40px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='2' fill='%23cfd8dc'/%3E%3C/svg%3E")
|
||||
left 0 top 0 var(--background-subdued);
|
||||
height: 40px;
|
||||
background: var(--background-subdued);
|
||||
border-bottom: 2px solid var(--border-normal);
|
||||
}
|
||||
|
||||
.tox .tox-pop__dialog .tox-toolbar {
|
||||
@@ -149,6 +152,10 @@ body.dark .tox .tox-toolbar__overflow {
|
||||
background: var(--border-normal);
|
||||
}
|
||||
|
||||
.tox .tox-tbtn + .tox .tox-tbtn {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.tox .tox-swatches__picker-btn:hover {
|
||||
background: transparent;
|
||||
border: none;
|
||||
|
||||
65
package-lock.json
generated
65
package-lock.json
generated
@@ -14,7 +14,8 @@
|
||||
"@directus/sdk-js": "file:packages/sdk-js",
|
||||
"@directus/specs": "file:packages/specs",
|
||||
"create-directus-project": "file:packages/create-directus-project",
|
||||
"directus": "file:api"
|
||||
"directus": "file:api",
|
||||
"dompurify": "^2.2.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@apidevtools/swagger-cli": "^4.0.4",
|
||||
@@ -42,13 +43,14 @@
|
||||
"@types/bytes": "^3.1.0",
|
||||
"@types/chai": "^4.2.14",
|
||||
"@types/clear": "^0.1.0",
|
||||
"@types/codemirror": "^0.0.98",
|
||||
"@types/codemirror": "^0.0.106",
|
||||
"@types/color": "^3.0.1",
|
||||
"@types/color-string": "^1.5.0",
|
||||
"@types/cookie-parser": "^1.4.2",
|
||||
"@types/cors": "^2.8.7",
|
||||
"@types/debug": "^4.1.5",
|
||||
"@types/diff": "^4.0.2",
|
||||
"@types/dompurify": "^2.2.1",
|
||||
"@types/express": "^4.17.9",
|
||||
"@types/express-pino-logger": "^4.0.2",
|
||||
"@types/express-session": "^1.17.2",
|
||||
@@ -182,6 +184,7 @@
|
||||
}
|
||||
},
|
||||
"api": {
|
||||
"name": "directus",
|
||||
"version": "9.0.0-rc.28",
|
||||
"license": "GPL-3.0-only",
|
||||
"dependencies": {
|
||||
@@ -448,6 +451,7 @@
|
||||
}
|
||||
},
|
||||
"app": {
|
||||
"name": "@directus/app",
|
||||
"version": "9.0.0-rc.28",
|
||||
"dependencies": {
|
||||
"@directus/format-title": "file:../packages/format-title"
|
||||
@@ -575,6 +579,7 @@
|
||||
}
|
||||
},
|
||||
"docs": {
|
||||
"name": "@directus/docs",
|
||||
"version": "9.0.0-rc.28",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
@@ -7465,9 +7470,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/codemirror": {
|
||||
"version": "0.0.98",
|
||||
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.98.tgz",
|
||||
"integrity": "sha512-cbty5LPayy2vNSeuUdjNA9tggG+go5vAxmnLDRWpiZI5a+RDBi9dlozy4/jW/7P/gletbBWbQREEa7A81YxstA==",
|
||||
"version": "0.0.106",
|
||||
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.106.tgz",
|
||||
"integrity": "sha512-o2bJWaI56+J1IuzUyb1KKNLs0Tm1sqdosGeSneicQZpg9s59++8Nz70KOD6IACiZWI6b+H0H+UE+JRkgTUO3Ww==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/tern": "*"
|
||||
@@ -7549,6 +7554,15 @@
|
||||
"integrity": "sha512-mIenTfsIe586/yzsyfql69KRnA75S8SVXQbTLpDejRrjH0QSJcpu3AUOi/Vjnt9IOsXKxPhJfGpQUNMueIU1fQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/dompurify": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.2.1.tgz",
|
||||
"integrity": "sha512-3JwbEeRVQ3n6+JgBW/hCdkydRk9/vWT+UEglcXEJqLJEcUganDH37zlfLznxPKTZZfDqA9K229l1qN458ubcOQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/trusted-types": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/eslint-visitor-keys": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
|
||||
@@ -8061,6 +8075,12 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/trusted-types": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.0.tgz",
|
||||
"integrity": "sha512-I8MnZqNXsOLHsU111oHbn3khtvKMi5Bn4qVFsIWSJcCP1KKDiXX5AEw8UPk0nSopeC+Hvxt6yAy1/a5PailFqg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/tunnel": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.0.tgz",
|
||||
@@ -18039,6 +18059,11 @@
|
||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "2.2.6",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.2.6.tgz",
|
||||
"integrity": "sha512-7b7ZArhhH0SP6W2R9cqK6RjaU82FZ2UPM7RO8qN1b1wyvC/NY1FNWcX1Pu00fFOAnzEORtwXe4bPaClg6pUybQ=="
|
||||
},
|
||||
"node_modules/domutils": {
|
||||
"version": "2.4.4",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.4.4.tgz",
|
||||
@@ -44738,6 +44763,7 @@
|
||||
}
|
||||
},
|
||||
"packages/format-title": {
|
||||
"name": "@directus/format-title",
|
||||
"version": "9.0.0-rc.28",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
@@ -44763,6 +44789,7 @@
|
||||
}
|
||||
},
|
||||
"packages/schema": {
|
||||
"name": "@directus/schema",
|
||||
"version": "9.0.0-rc.28",
|
||||
"license": "GPL-3.0",
|
||||
"devDependencies": {
|
||||
@@ -44774,6 +44801,7 @@
|
||||
}
|
||||
},
|
||||
"packages/sdk-js": {
|
||||
"name": "@directus/sdk-js",
|
||||
"version": "9.0.0-rc.28",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -44782,6 +44810,7 @@
|
||||
}
|
||||
},
|
||||
"packages/specs": {
|
||||
"name": "@directus/specs",
|
||||
"version": "9.0.0-rc.28",
|
||||
"license": "GPL-3.0",
|
||||
"devDependencies": {
|
||||
@@ -50621,9 +50650,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/codemirror": {
|
||||
"version": "0.0.98",
|
||||
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.98.tgz",
|
||||
"integrity": "sha512-cbty5LPayy2vNSeuUdjNA9tggG+go5vAxmnLDRWpiZI5a+RDBi9dlozy4/jW/7P/gletbBWbQREEa7A81YxstA==",
|
||||
"version": "0.0.106",
|
||||
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.106.tgz",
|
||||
"integrity": "sha512-o2bJWaI56+J1IuzUyb1KKNLs0Tm1sqdosGeSneicQZpg9s59++8Nz70KOD6IACiZWI6b+H0H+UE+JRkgTUO3Ww==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/tern": "*"
|
||||
@@ -50705,6 +50734,15 @@
|
||||
"integrity": "sha512-mIenTfsIe586/yzsyfql69KRnA75S8SVXQbTLpDejRrjH0QSJcpu3AUOi/Vjnt9IOsXKxPhJfGpQUNMueIU1fQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/dompurify": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.2.1.tgz",
|
||||
"integrity": "sha512-3JwbEeRVQ3n6+JgBW/hCdkydRk9/vWT+UEglcXEJqLJEcUganDH37zlfLznxPKTZZfDqA9K229l1qN458ubcOQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/trusted-types": "*"
|
||||
}
|
||||
},
|
||||
"@types/eslint-visitor-keys": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
|
||||
@@ -51219,6 +51257,12 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/trusted-types": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.0.tgz",
|
||||
"integrity": "sha512-I8MnZqNXsOLHsU111oHbn3khtvKMi5Bn4qVFsIWSJcCP1KKDiXX5AEw8UPk0nSopeC+Hvxt6yAy1/a5PailFqg==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/tunnel": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.0.tgz",
|
||||
@@ -59480,6 +59524,11 @@
|
||||
"domelementtype": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"dompurify": {
|
||||
"version": "2.2.6",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.2.6.tgz",
|
||||
"integrity": "sha512-7b7ZArhhH0SP6W2R9cqK6RjaU82FZ2UPM7RO8qN1b1wyvC/NY1FNWcX1Pu00fFOAnzEORtwXe4bPaClg6pUybQ=="
|
||||
},
|
||||
"domutils": {
|
||||
"version": "2.4.4",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.4.4.tgz",
|
||||
|
||||
@@ -45,13 +45,14 @@
|
||||
"@types/bytes": "^3.1.0",
|
||||
"@types/chai": "^4.2.14",
|
||||
"@types/clear": "^0.1.0",
|
||||
"@types/codemirror": "^0.0.98",
|
||||
"@types/codemirror": "^0.0.106",
|
||||
"@types/color": "^3.0.1",
|
||||
"@types/color-string": "^1.5.0",
|
||||
"@types/cookie-parser": "^1.4.2",
|
||||
"@types/cors": "^2.8.7",
|
||||
"@types/debug": "^4.1.5",
|
||||
"@types/diff": "^4.0.2",
|
||||
"@types/dompurify": "^2.2.1",
|
||||
"@types/express": "^4.17.9",
|
||||
"@types/express-pino-logger": "^4.0.2",
|
||||
"@types/express-session": "^1.17.2",
|
||||
@@ -190,7 +191,8 @@
|
||||
"@directus/sdk-js": "file:packages/sdk-js",
|
||||
"@directus/specs": "file:packages/specs",
|
||||
"create-directus-project": "file:packages/create-directus-project",
|
||||
"directus": "file:api"
|
||||
"directus": "file:api",
|
||||
"dompurify": "^2.2.6"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
|
||||
Reference in New Issue
Block a user