Add v-md directive (#6835)

* Use stricter Vue eslint setting

* Implement v-md directive

Replaces old uses of v-html + md util

* Fix typo
This commit is contained in:
Rijk van Zanten
2021-07-15 20:01:30 +02:00
committed by GitHub
parent 5f7dce54e2
commit d38681c2ee
27 changed files with 52 additions and 87 deletions

View File

@@ -54,7 +54,7 @@
</v-card>
</v-dialog>
<small v-if="field.meta && field.meta.note" class="note" v-html="md(field.meta.note)" />
<small v-if="field.meta && field.meta.note" v-md="field.meta.note" class="note" />
<small v-if="validationError" class="validation-error">
{{ validationMessage }}
@@ -66,7 +66,6 @@
import { useI18n } from 'vue-i18n';
import { defineComponent, PropType, computed, ref } from 'vue';
import { Field } from '@/types/';
import { md } from '@/utils/md';
import FormFieldLabel from './form-field-label.vue';
import FormFieldMenu from './form-field-menu.vue';
import FormFieldInterface from './form-field-interface.vue';
@@ -158,7 +157,7 @@ export default defineComponent({
}
});
return { t, isDisabled, md, internalValue, emitValue, showRaw, rawValue, validationMessage, isEdited };
return { t, isDisabled, internalValue, emitValue, showRaw, rawValue, validationMessage, isEdited };
function emitValue(value: any) {
if (

View File

@@ -64,7 +64,6 @@ import { defineComponent, PropType, computed, ref, provide } from 'vue';
import { useFieldsStore } from '@/stores/';
import { Field, FieldRaw } from '@/types';
import { clone, cloneDeep, isNil, merge, omit } from 'lodash';
import { md } from '@/utils/md';
import useFormFields from '@/composables/use-form-fields';
import { ValidationError } from '@/types';
import { useElementSize } from '@/composables/use-element-size';
@@ -180,7 +179,6 @@ export default defineComponent({
batchActiveFields,
toggleBatchField,
unsetValue,
md,
unknownValidationErrors,
firstEditableFieldIndex,
isNil,

View File

@@ -0,0 +1,10 @@
import { Directive } from 'vue';
import { md } from '@/utils/md';
const Markdown: Directive = {
beforeMount(el, binding) {
el.innerHTML = md(binding.value ?? '');
},
};
export default Markdown;

View File

@@ -2,9 +2,11 @@ import { App } from 'vue';
import ClickOutside from './click-outside/click-outside';
import Focus from './focus/focus';
import Tooltip from './tooltip/tooltip';
import Markdown from './markdown';
export function registerDirectives(app: App): void {
app.directive('focus', Focus);
app.directive('tooltip', Tooltip);
app.directive('click-outside', ClickOutside);
app.directive('md', Markdown);
}

View File

@@ -129,7 +129,7 @@
<div ref="codemirrorEl"></div>
<div v-if="view[0] === 'preview'" class="preview-box" v-html="html"></div>
<div v-if="view[0] === 'preview'" v-md="markdownString" class="preview-box"></div>
<v-dialog :model-value="imageDialogOpen" @esc="imageDialogOpen = null" @update:model-value="imageDialogOpen = null">
<v-card>
@@ -155,7 +155,6 @@ import 'codemirror/addon/display/placeholder.js';
import { applyEdit, CustomSyntax, Alteration } from './edits';
import { getPublicURL } from '@/utils/get-root-path';
import { md } from '@/utils/md';
import { addTokenToURL } from '@/api';
import escapeStringRegexp from 'escape-string-regexp';
import useShortcut from '@/composables/use-shortcut';
@@ -232,7 +231,7 @@ export default defineComponent({
}
);
const html = computed(() => {
const markdownString = computed(() => {
let mdString = props.value || '';
if (!props.imageToken) {
@@ -246,9 +245,7 @@ export default defineComponent({
}
}
const html = md(mdString);
return html;
return mdString;
});
const table = reactive({
@@ -274,7 +271,7 @@ export default defineComponent({
codemirrorEl,
edit,
view,
html,
markdownString,
table,
onImageUpload,
imageDialogOpen,

View File

@@ -1,14 +1,13 @@
<template>
<div class="presentation-notice">
<v-notice :icon="icon" :type="color">
<div v-html="md(text)" />
<div v-md="text" />
</v-notice>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { md } from '@/utils/md';
export default defineComponent({
props: {
@@ -25,8 +24,5 @@ export default defineComponent({
default: 'No text configured...',
},
},
setup() {
return { md };
},
});
</script>

View File

@@ -24,7 +24,7 @@
<template #sidebar>
<sidebar-detail icon="info_outline" :title="t('information')" close>
<div class="page-description" v-html="md(t('page_help_activity_collection'))" />
<div v-md="t('page_help_activity_collection')" class="page-description" />
</sidebar-detail>
<layout-sidebar-detail v-model="layout" />
<component :is="`layout-sidebar-${layout}`" />
@@ -38,7 +38,6 @@ import { defineComponent, computed, ref, reactive } from 'vue';
import ActivityNavigation from '../components/navigation.vue';
import usePreset from '@/composables/use-preset';
import { useLayout } from '@/composables/use-layout';
import { md } from '@/utils/md';
import FilterSidebarDetail from '@/views/private/components/filter-sidebar-detail';
import LayoutSidebarDetail from '@/views/private/components/layout-sidebar-detail';
import SearchInput from '@/views/private/components/search-input';
@@ -72,7 +71,7 @@ export default defineComponent({
})
);
return { t, breadcrumb, md, layout, layoutOptions, layoutQuery, searchQuery, filters };
return { t, breadcrumb, layout, layoutOptions, layoutQuery, searchQuery, filters };
function useBreadcrumb() {
const breadcrumb = computed(() => {

View File

@@ -210,14 +210,8 @@
<template #sidebar>
<sidebar-detail icon="info_outline" :title="t('information')" close>
<div
v-md="t('page_help_collections_collection', { collection: currentCollection.name })"
class="page-description"
v-html="
md(
t('page_help_collections_collection', {
collection: currentCollection.name,
})
)
"
/>
</sidebar-detail>
<layout-sidebar-detail v-model="layout" />
@@ -255,7 +249,6 @@ import SearchInput from '@/views/private/components/search-input';
import BookmarkAdd from '@/views/private/components/bookmark-add';
import BookmarkEdit from '@/views/private/components/bookmark-edit';
import { useRouter } from 'vue-router';
import { md } from '@/utils/md';
import { usePermissionsStore, useUserStore } from '@/stores';
import DrawerBatch from '@/views/private/components/drawer-batch';
import { unexpectedError } from '@/utils/unexpected-error';
@@ -386,7 +379,6 @@ export default defineComponent({
editingBookmark,
editBookmark,
breadcrumb,
md,
clearFilters,
confirmArchive,
archive,

View File

@@ -172,7 +172,7 @@
<template #sidebar>
<sidebar-detail icon="info_outline" :title="t('information')" close>
<div class="page-description" v-html="md(t('page_help_collections_item'))" />
<div v-md="t('page_help_collections_item')" class="page-description" />
</sidebar-detail>
<revisions-drawer-detail
v-if="isNew === false && internalPrimaryKey && revisionsAllowed && accountabilityScope === 'all'"
@@ -203,7 +203,6 @@ import RevisionsDrawerDetail from '@/views/private/components/revisions-drawer-d
import CommentsSidebarDetail from '@/views/private/components/comments-sidebar-detail';
import useItem from '@/composables/use-item';
import SaveOptions from '@/views/private/components/save-options';
import { md } from '@/utils/md';
import useShortcut from '@/composables/use-shortcut';
import { useRouter, onBeforeRouteUpdate, onBeforeRouteLeave, NavigationGuard } from 'vue-router';
import { usePermissions } from '@/composables/use-permissions';
@@ -394,7 +393,6 @@ export default defineComponent({
breadcrumb,
title,
revisionsDrawerDetail,
md,
refresh,
confirmLeave,
leaveTo,

View File

@@ -38,7 +38,7 @@
<template #sidebar>
<sidebar-detail icon="info_outline" :title="t('information')" close>
<div class="page-description" v-html="md(t('page_help_collections_overview'))" />
<div v-md="t('page_help_collections_overview')" class="page-description" />
</sidebar-detail>
</template>
</private-view>
@@ -53,8 +53,6 @@ import useNavigation, { NavItem } from '../composables/use-navigation';
import { useRouter } from 'vue-router';
import { useUserStore } from '@/stores';
import { md } from '@/utils/md';
export default defineComponent({
name: 'CollectionsOverview',
components: {
@@ -92,7 +90,7 @@ export default defineComponent({
const isAdmin = computed(() => userStore.currentUser?.role.admin_access === true);
return { t, tableHeaders, navItems, navigateToCollection, isAdmin, md };
return { t, tableHeaders, navItems, navigateToCollection, isAdmin };
function navigateToCollection(navItem: NavItem) {
router.push(navItem.to);

View File

@@ -1,4 +1,5 @@
<template>
<!-- eslint-disable-next-line vue/no-v-html -->
<div class="md" :class="pageClass" @click="onClick" v-html="html" />
</template>

View File

@@ -7,7 +7,7 @@
</template>
<template #title>
<h1 class="type-title" v-html="title"></h1>
<h1 class="type-title">{{ title }}</h1>
</template>
<template #navigation>
@@ -20,7 +20,7 @@
<template #sidebar>
<sidebar-detail icon="info_outline" :title="t('information')" close>
<div class="page-description" v-html="md(t('page_help_docs_global'))" />
<div v-md="t('page_help_docs_global')" class="page-description" />
</sidebar-detail>
</template>
</private-view>
@@ -31,7 +31,6 @@ import { useI18n } from 'vue-i18n';
import { defineComponent, defineAsyncComponent, ref, computed, watch } from 'vue';
import { useRoute, RouteLocation } from 'vue-router';
import DocsNavigation from '../components/navigation.vue';
import { md } from '@/utils/md';
const Markdown = defineAsyncComponent(() => import('../components/markdown.vue'));
@@ -84,7 +83,7 @@ export default defineComponent({
{ immediate: true, flush: 'post' }
);
return { t, route, markdown, title, markdownWithoutTitle, md };
return { t, route, markdown, title, markdownWithoutTitle };
},
});
</script>

View File

@@ -112,7 +112,7 @@
<v-divider />
<div class="page-description" v-html="md(t('page_help_files_item'))" />
<div v-md="t('page_help_files_item')" class="page-description" />
</sidebar-detail>
</template>
@@ -121,7 +121,6 @@ import { useI18n } from 'vue-i18n';
import { defineComponent, computed, ref, watch } from 'vue';
import readableMimeType from '@/utils/readable-mime-type';
import bytes from 'bytes';
import { md } from '@/utils/md';
import localizedFormat from '@/utils/localized-format';
import api, { addTokenToURL } from '@/api';
import { getRootPath } from '@/utils/get-root-path';
@@ -167,7 +166,6 @@ export default defineComponent({
userCreated,
userModified,
folder,
md,
folderLink,
getRootPath,
fileLink,

View File

@@ -135,7 +135,7 @@
<template #sidebar>
<sidebar-detail icon="info_outline" :title="t('information')" close>
<div class="page-description" v-html="md(t('page_help_files_collection'))" />
<div v-md="t('page_help_files_collection')" class="page-description" />
</sidebar-detail>
<layout-sidebar-detail v-model="layout" />
<component :is="`layout-sidebar-${layout}`" />
@@ -160,7 +160,6 @@ import FilterSidebarDetail from '@/views/private/components/filter-sidebar-detai
import LayoutSidebarDetail from '@/views/private/components/layout-sidebar-detail';
import AddFolder from '../components/add-folder.vue';
import SearchInput from '@/views/private/components/search-input';
import { md } from '@/utils/md';
import FolderPicker from '../components/folder-picker.vue';
import emitter, { Events } from '@/events';
import { useRouter } from 'vue-router';
@@ -314,7 +313,6 @@ export default defineComponent({
layout,
filtersWithFolderAndType,
searchQuery,
md,
moveToDialogActive,
moveToFolder,
moving,

View File

@@ -87,7 +87,7 @@
<template #sidebar>
<sidebar-detail icon="info_outline" :title="t('information')" close>
<div class="page-description" v-html="md(t('page_help_settings_datamodel_collections'))" />
<div v-md="t('page_help_settings_datamodel_collections')" class="page-description" />
</sidebar-detail>
<collections-filter v-model="activeTypes" />
</template>
@@ -105,7 +105,6 @@ import { useRouter } from 'vue-router';
import { sortBy } from 'lodash';
import CollectionOptions from './components/collection-options.vue';
import CollectionsFilter from './components/collections-filter.vue';
import { md } from '@/utils/md';
const activeTypes = ref(['visible', 'hidden', 'unmanaged']);
@@ -143,7 +142,7 @@ export default defineComponent({
const { items } = useItems();
return { t, tableHeaders, items, openCollection, activeTypes, md };
return { t, tableHeaders, items, openCollection, activeTypes };
function useItems() {
const visible = computed(() => {

View File

@@ -77,7 +77,7 @@
<template #sidebar>
<sidebar-detail icon="info_outline" :title="t('information')" close>
<div class="page-description" v-html="md(t('page_help_settings_datamodel_fields'))" />
<div v-md="t('page_help_settings_datamodel_fields')" class="page-description" />
</sidebar-detail>
</template>
</private-view>
@@ -93,7 +93,6 @@ import FieldsManagement from './components/fields-management.vue';
import useItem from '@/composables/use-item';
import { useRouter } from 'vue-router';
import { useCollectionsStore, useFieldsStore } from '@/stores';
import { md } from '@/utils/md';
export default defineComponent({
components: { SettingsNavigation, FieldsManagement },
@@ -151,7 +150,6 @@ export default defineComponent({
deleteAndQuit,
saveAndQuit,
hasEdits,
md,
};
async function deleteAndQuit() {

View File

@@ -13,7 +13,7 @@
<v-divider />
<div class="page-description" v-html="md(t('page_help_settings_presets_collection'))" />
<div v-md="t('page_help_settings_presets_collection')" class="page-description" />
</sidebar-detail>
</template>
@@ -21,7 +21,6 @@
import { useI18n } from 'vue-i18n';
import { defineComponent, ref } from 'vue';
import api from '@/api';
import { md } from '@/utils/md';
import { unexpectedError } from '@/utils/unexpected-error';
export default defineComponent({
@@ -34,7 +33,7 @@ export default defineComponent({
fetchCounts();
return { t, bookmarksCount, presetsCount, md };
return { t, bookmarksCount, presetsCount };
async function fetchCounts() {
loading.value = true;

View File

@@ -78,7 +78,7 @@
<template #sidebar>
<sidebar-detail icon="info_outline" :title="t('information')" close>
<div class="page-description" v-html="md(t('page_help_settings_presets_item'))" />
<div v-md="t('page_help_settings_presets_item')" class="page-description" />
</sidebar-detail>
<div class="layout-sidebar">
@@ -108,7 +108,6 @@ import api from '@/api';
import { useCollectionsStore, usePresetsStore } from '@/stores';
import { getLayouts } from '@/layouts';
import { useRouter } from 'vue-router';
import { md } from '@/utils/md';
import { unexpectedError } from '@/utils/unexpected-error';
import { useLayout } from '@/composables/use-layout';
@@ -193,7 +192,6 @@ export default defineComponent({
deleting,
deleteAndQuit,
confirmDelete,
md,
updateFilters,
searchQuery,
};

View File

@@ -33,14 +33,13 @@
<v-divider />
<div class="page-description" v-html="md(t('page_help_settings_project'))" />
<div v-md="t('page_help_settings_project')" class="page-description" />
</sidebar-detail>
</template>
<script lang="ts">
import { useI18n } from 'vue-i18n';
import { defineComponent } from 'vue';
import { md } from '@/utils/md';
import { useProjectInfo } from '../../../composables/use-project-info';
export default defineComponent({
@@ -49,7 +48,7 @@ export default defineComponent({
const { parsedInfo } = useProjectInfo();
return { t, parsedInfo, md };
return { t, parsedInfo };
},
});
</script>

View File

@@ -20,7 +20,7 @@
<template #sidebar>
<sidebar-detail icon="info_outline" :title="t('information')" close>
<div class="page-description" v-html="md(t('page_help_settings_roles_collection'))" />
<div v-md="t('page_help_settings_roles_collection')" class="page-description" />
</sidebar-detail>
</template>
@@ -62,7 +62,6 @@ import { defineComponent, computed, ref } from 'vue';
import SettingsNavigation from '../../components/navigation.vue';
import api from '@/api';
import { md } from '@/utils/md';
import { Header as TableHeader } from '@/components/v-table/types';
import ValueNull from '@/views/private/components/value-null';
import { useRouter } from 'vue-router';
@@ -124,7 +123,7 @@ export default defineComponent({
return `/settings/roles/+`;
});
return { t, md, loading, roles, tableHeaders, addNewLink, navigateToRole };
return { t, loading, roles, tableHeaders, addNewLink, navigateToRole };
async function fetchRoles() {
loading.value = true;

View File

@@ -9,14 +9,13 @@
<v-divider />
<div class="page-description" v-html="md(t('page_help_settings_roles_item'))" />
<div v-md="t('page_help_settings_roles_item')" class="page-description" />
</sidebar-detail>
</template>
<script lang="ts">
import { useI18n } from 'vue-i18n';
import { defineComponent, PropType } from 'vue';
import { md } from '@/utils/md';
export default defineComponent({
props: {
@@ -32,7 +31,7 @@ export default defineComponent({
setup() {
const { t } = useI18n();
return { t, md };
return { t };
},
});
</script>

View File

@@ -76,7 +76,7 @@
<template #sidebar>
<sidebar-detail icon="info_outline" :title="t('information')" close>
<div class="page-description" v-html="md(t('page_help_settings_webhooks_collection'))" />
<div v-md="t('page_help_settings_webhooks_collection')" class="page-description" />
</sidebar-detail>
<layout-sidebar-detail />
<component :is="`layout-sidebar-${layout}`" />
@@ -89,7 +89,6 @@ import { useI18n } from 'vue-i18n';
import { defineComponent, computed, ref, reactive } from 'vue';
import SettingsNavigation from '../../components/navigation.vue';
import LayoutSidebarDetail from '@/views/private/components/layout-sidebar-detail';
import { md } from '@/utils/md';
import { usePreset } from '@/composables/use-preset';
import { useLayout } from '@/composables/use-layout';
import api from '@/api';
@@ -138,7 +137,6 @@ export default defineComponent({
layoutQuery,
layout,
searchQuery,
md,
clearFilters,
};

View File

@@ -60,7 +60,7 @@
<template #sidebar>
<sidebar-detail icon="info_outline" :title="t('information')" close>
<div class="page-description" v-html="md(t('page_help_settings_webhooks_item'))" />
<div v-md="t('page_help_settings_webhooks_item')" class="page-description" />
</sidebar-detail>
<revisions-drawer-detail v-if="isNew === false" collection="directus_webhooks" :primary-key="primaryKey" />
</template>
@@ -76,7 +76,6 @@ import { useRouter } from 'vue-router';
import RevisionsDrawerDetail from '@/views/private/components/revisions-drawer-detail';
import useItem from '@/composables/use-item';
import SaveOptions from '@/views/private/components/save-options';
import { md } from '@/utils/md';
export default defineComponent({
name: 'WebhooksItem',
@@ -135,7 +134,6 @@ export default defineComponent({
saveAndAddNew,
saveAsCopyAndNavigate,
isBatch,
md,
title,
validationErrors,
};

View File

@@ -31,14 +31,13 @@
<v-divider />
<div class="page-description" v-html="md(t('page_help_users_item'))" />
<div v-md="t('page_help_users_item')" class="page-description" />
</sidebar-detail>
</template>
<script lang="ts">
import { useI18n } from 'vue-i18n';
import { defineComponent, ref, watch } from 'vue';
import { md } from '@/utils/md';
import localizedFormat from '@/utils/localized-format';
export default defineComponent({
@@ -69,7 +68,7 @@ export default defineComponent({
{ immediate: true }
);
return { t, md, lastAccessDate };
return { t, lastAccessDate };
},
});
</script>

View File

@@ -118,7 +118,7 @@
<template #sidebar>
<sidebar-detail icon="info_outline" :title="t('information')" close>
<div class="page-description" v-html="md(t('page_help_users_collection'))" />
<div v-md="t('page_help_users_collection')" class="page-description" />
</sidebar-detail>
<layout-sidebar-detail v-model="layout" />
<component :is="`layout-sidebar-${layout}`" />
@@ -137,7 +137,6 @@ import usePreset from '@/composables/use-preset';
import LayoutSidebarDetail from '@/views/private/components/layout-sidebar-detail';
import SearchInput from '@/views/private/components/search-input';
import { useUserStore, usePermissionsStore } from '@/stores';
import { md } from '@/utils/md';
import useNavigation from '../composables/use-navigation';
import { useLayout } from '@/composables/use-layout';
import DrawerBatch from '@/views/private/components/drawer-batch';
@@ -237,7 +236,6 @@ export default defineComponent({
layoutQuery,
layout,
searchQuery,
md,
clearFilters,
userInviteModalActive,
refresh,

View File

@@ -23,7 +23,7 @@
</v-textarea>
<div v-else class="content">
<span class="selectable" v-html="htmlContent" />
<span v-md="activity.comment" class="selectable" />
<!-- @TODO: Dynamically add element below if the comment overflows -->
<!-- <div v-if="activity.id == 204" class="expand-text">
@@ -35,10 +35,9 @@
<script lang="ts">
import { useI18n } from 'vue-i18n';
import { defineComponent, PropType, ref, computed, watch, ComponentPublicInstance } from 'vue';
import { defineComponent, PropType, ref, watch, ComponentPublicInstance } from 'vue';
import { Activity } from './types';
import CommentItemHeader from './comment-item-header.vue';
import { md } from '@/utils/md';
import useShortcut from '@/composables/use-shortcut';
import api from '@/api';
@@ -60,13 +59,12 @@ export default defineComponent({
const { t } = useI18n();
const textarea = ref<ComponentPublicInstance>();
const htmlContent = computed(() => (props.activity.comment ? md(props.activity.comment) : null));
const { edits, editing, savingEdits, saveEdits, cancelEditing } = useEdits();
useShortcut('meta+enter', saveEdits, textarea);
return { t, htmlContent, edits, editing, savingEdits, saveEdits, cancelEditing, textarea };
return { t, edits, editing, savingEdits, saveEdits, cancelEditing, textarea };
function useEdits() {
const edits = ref(props.activity.comment);

View File

@@ -26,7 +26,7 @@
<img v-if="foregroundURL" class="foreground" :src="foregroundURL" :alt="branding && branding.project_name" />
</transition>
<div class="note-container">
<div v-if="branding && branding.public_note" class="note" v-html="md(branding.public_note)" />
<div v-if="branding && branding.public_note" v-md="branding.public_note" class="note" />
</div>
</div>
</div>
@@ -36,7 +36,6 @@
import { version } from '../../../package.json';
import { defineComponent, computed } from 'vue';
import { useServerStore } from '@/stores';
import { md } from '@/utils/md';
import { getRootPath } from '@/utils/get-root-path';
export default defineComponent({
@@ -84,7 +83,6 @@ export default defineComponent({
version,
artStyles,
branding: serverStore.info?.project,
md,
foregroundURL,
logoURL,
isBranded,