mirror of
https://github.com/directus/directus.git
synced 2026-01-31 15:07:56 -05:00
Add custom syntax block option to markdown
And fix v-form overrides
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
# Form
|
||||
|
||||
Renders a form using interfaces based on the passed collection name.
|
||||
|
||||
## Usage
|
||||
|
||||
```html
|
||||
<v-form
|
||||
collection="articles"
|
||||
@@ -13,8 +15,9 @@ Renders a form using interfaces based on the passed collection name.
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
| Prop | Description | Default |
|
||||
|-----------------|-------------------------------------------------------------------------------------------------------------------------|---------|
|
||||
| --------------- | ----------------------------------------------------------------------------------------------------------------------- | ------- |
|
||||
| `collection` | The collection of which you want to render the fields | -- |
|
||||
| `fields` | Array of fields to render. This can be used instead of the collection prop | -- |
|
||||
| `initialValues` | Object of the starting values of the fields | -- |
|
||||
@@ -24,17 +27,11 @@ Renders a form using interfaces based on the passed collection name.
|
||||
**Note**: You have to pass either the collection or fields prop.
|
||||
|
||||
## Slots
|
||||
|
||||
n/a
|
||||
|
||||
## Events
|
||||
| Event | Description |
|
||||
|---------|--------------------------|
|
||||
| `input` | Edits have been updated. |
|
||||
|
||||
## CSS Variables
|
||||
| Variable | Default |
|
||||
|---------------------------|----------------------------------------|
|
||||
| `--v-form-column-width` | `300px` |
|
||||
| `--v-form-row-max-height` | `calc(var(--v-form-column-width) * 2)` |
|
||||
| `--v-form-horizontal-gap` | `12px` |
|
||||
| `--v-form-vertical-gap` | `52px` |
|
||||
| Event | Description |
|
||||
| ------- | ------------------------ |
|
||||
| `input` | Edits have been updated. |
|
||||
|
||||
@@ -12,7 +12,8 @@ type Alteration =
|
||||
| 'blockquote'
|
||||
| 'code'
|
||||
| 'link'
|
||||
| 'table';
|
||||
| 'table'
|
||||
| 'custom';
|
||||
|
||||
type AlterationFunctions = Record<
|
||||
Alteration,
|
||||
@@ -23,7 +24,15 @@ type AlterationFunctions = Record<
|
||||
) => { newSelection: string; newCursor: Position; highlight?: { from: Position; to: Position } }
|
||||
>;
|
||||
|
||||
export function useEdit(codemirror: Ref<CodeMirror.EditorFromTextArea | null>) {
|
||||
export type CustomSyntax = {
|
||||
name: string;
|
||||
icon: string;
|
||||
prefix: string;
|
||||
suffix: string;
|
||||
box: 'inline' | 'block';
|
||||
};
|
||||
|
||||
export function useEdit(codemirror: Ref<CodeMirror.EditorFromTextArea | null>, customSyntaxBlocks: CustomSyntax[]) {
|
||||
const alterations: AlterationFunctions = {
|
||||
heading(selection, { cursorTo }, options) {
|
||||
const level = options?.level || 3;
|
||||
@@ -218,6 +227,37 @@ export function useEdit(codemirror: Ref<CodeMirror.EditorFromTextArea | null>) {
|
||||
|
||||
return { newSelection: table, newCursor: cursors.cursorFrom };
|
||||
},
|
||||
custom(selection, { cursorTo, cursorHead }, options) {
|
||||
if (!options) return { newSelection: selection, newCursor: cursorHead };
|
||||
|
||||
if (options.box === 'block') {
|
||||
// Multiline
|
||||
let newSelection = selection;
|
||||
let newCursor = cursorTo;
|
||||
|
||||
if (selection.startsWith(options.prefix) && selection.endsWith(options.suffix)) {
|
||||
newSelection = selection.substring(options.prefix.length, selection.length - options.suffix.length);
|
||||
} else {
|
||||
newSelection = `${options.prefix}\n${newSelection}\n${options.suffix}`;
|
||||
newCursor.line = newCursor.line + 1;
|
||||
}
|
||||
|
||||
return { newSelection, newCursor };
|
||||
} else {
|
||||
// Inline
|
||||
let newSelection = selection;
|
||||
let newCursor = cursorTo;
|
||||
|
||||
if (selection.startsWith(options.prefix) && selection.endsWith(options.suffix)) {
|
||||
newSelection = selection.substring(1, selection.length - 1);
|
||||
} else {
|
||||
newSelection = `${options.prefix}${selection}${options.suffix}`;
|
||||
newCursor.ch = newCursor.ch + 1;
|
||||
}
|
||||
|
||||
return { newSelection, newCursor };
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return { edit };
|
||||
|
||||
@@ -14,12 +14,88 @@ export default defineInterface(({ i18n }) => ({
|
||||
name: i18n.t('placeholder'),
|
||||
type: 'string',
|
||||
meta: {
|
||||
width: 'half',
|
||||
interface: 'text-input',
|
||||
width: 'full',
|
||||
interface: 'textarea',
|
||||
options: {
|
||||
placeholder: i18n.t('enter_a_placeholder'),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'customSyntax',
|
||||
name: i18n.t('interfaces.markdown.customSyntax'),
|
||||
label: i18n.t('interfaces.markdown.customSyntax_label'),
|
||||
type: 'json',
|
||||
meta: {
|
||||
width: 'full',
|
||||
interface: 'repeater',
|
||||
options: {
|
||||
addLabel: i18n.t('interfaces.markdown.customSyntax_add'),
|
||||
template: '{{ name }}',
|
||||
fields: [
|
||||
{
|
||||
field: 'name',
|
||||
type: 'string',
|
||||
name: i18n.t('name'),
|
||||
meta: {
|
||||
interface: 'text-input',
|
||||
width: 'half',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'icon',
|
||||
type: 'string',
|
||||
name: i18n.t('icon'),
|
||||
meta: {
|
||||
interface: 'icon',
|
||||
width: 'half',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'prefix',
|
||||
type: 'string',
|
||||
name: i18n.t('prefix'),
|
||||
meta: {
|
||||
interface: 'text-input',
|
||||
width: 'half',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'suffix',
|
||||
type: 'string',
|
||||
name: i18n.t('suffix'),
|
||||
meta: {
|
||||
interface: 'text-input',
|
||||
width: 'half',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'box',
|
||||
type: 'string',
|
||||
name: i18n.t('interfaces.markdown.box'),
|
||||
meta: {
|
||||
interface: 'radio-buttons',
|
||||
width: 'half',
|
||||
options: {
|
||||
choices: [
|
||||
{
|
||||
text: i18n.t('inline'),
|
||||
value: 'inline',
|
||||
},
|
||||
{
|
||||
text: i18n.t('block'),
|
||||
value: 'block',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
schema: {
|
||||
default_value: 'inline',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
@@ -73,6 +73,17 @@
|
||||
</template>
|
||||
</v-menu>
|
||||
|
||||
<v-button
|
||||
v-for="custom in customSyntax"
|
||||
small
|
||||
icon
|
||||
:key="custom.name"
|
||||
@click="edit('custom', custom)"
|
||||
v-tooltip="custom.name"
|
||||
>
|
||||
<v-icon :name="custom.icon" />
|
||||
</v-button>
|
||||
|
||||
<div class="spacer"></div>
|
||||
|
||||
<v-button-group class="view" mandatory v-model="view" rounded>
|
||||
@@ -88,14 +99,24 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, ref, onMounted, onUnmounted, watch, reactive } from '@vue/composition-api';
|
||||
import {
|
||||
defineComponent,
|
||||
computed,
|
||||
ref,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
watch,
|
||||
reactive,
|
||||
PropType,
|
||||
} from '@vue/composition-api';
|
||||
import { sanitize } from 'dompurify';
|
||||
import marked from 'marked';
|
||||
|
||||
import CodeMirror from 'codemirror';
|
||||
import 'codemirror/mode/markdown/markdown';
|
||||
import 'codemirror/addon/display/placeholder.js';
|
||||
|
||||
import { useEdit } from './composables/use-edit';
|
||||
import { useEdit, CustomSyntax } from './composables/use-edit';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@@ -111,6 +132,10 @@ export default defineComponent({
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
customSyntax: {
|
||||
type: Array as PropType<CustomSyntax[]>,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const codemirrorEl = ref<HTMLTextAreaElement | null>(null);
|
||||
@@ -124,6 +149,7 @@ export default defineComponent({
|
||||
mode: 'markdown',
|
||||
configureMouse: () => ({ addNew: false }),
|
||||
lineWrapping: true,
|
||||
placeholder: props.placeholder,
|
||||
});
|
||||
|
||||
codemirror.value.setValue(props.value || '');
|
||||
@@ -150,7 +176,7 @@ export default defineComponent({
|
||||
}
|
||||
);
|
||||
|
||||
const { edit } = useEdit(codemirror);
|
||||
const { edit } = useEdit(codemirror, props.customSyntax);
|
||||
|
||||
const html = computed(() => {
|
||||
const html = marked(props.value || '');
|
||||
|
||||
@@ -31,8 +31,8 @@ export default defineComponent({
|
||||
@import '@/styles/mixins/type-styles.scss';
|
||||
|
||||
.form {
|
||||
--v-form-vertical-gap: 24px;
|
||||
--v-form-horizontal-gap: 24px;
|
||||
--form-vertical-gap: 24px;
|
||||
--form-horizontal-gap: 24px;
|
||||
|
||||
padding: 12px;
|
||||
padding-top: 0;
|
||||
|
||||
@@ -26,6 +26,8 @@ connection_fair: Fair Connection
|
||||
connection_poor: Poor Connection
|
||||
rename_folder: Rename Folder
|
||||
delete_folder: Delete Folder
|
||||
prefix: Prefix
|
||||
suffix: Suffix
|
||||
reset_bookmark: Reset Bookmark
|
||||
rename_bookmark: Rename Bookmark
|
||||
update_bookmark: Update Bookmark
|
||||
@@ -707,6 +709,8 @@ generate_and_save_uuid: Generate and Save UUID
|
||||
save_current_user_id: Save Current User ID
|
||||
save_current_user_role: Save Current User Role
|
||||
save_current_datetime: Save Current Date/Time
|
||||
block: Block
|
||||
inline: Inline
|
||||
comment: Comment
|
||||
continue: Continue
|
||||
continue_as: >-
|
||||
@@ -865,10 +869,10 @@ interfaces:
|
||||
markdown:
|
||||
markdown: Markdown
|
||||
description: Enter and preview markdown
|
||||
tabbed: Tabbed
|
||||
tabbed_label: Show preview in separate tab
|
||||
edit: Edit
|
||||
preview: Preview
|
||||
customSyntax: Custom Blocks
|
||||
customSyntax_label: Add custom syntax types
|
||||
customSyntax_add: Add custom syntax
|
||||
box: Block / Inline
|
||||
notice:
|
||||
notice: Notice
|
||||
description: Display a short notice
|
||||
|
||||
@@ -90,8 +90,8 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
.form {
|
||||
--v-form-vertical-gap: 32px;
|
||||
--v-form-horizontal-gap: 32px;
|
||||
--form-vertical-gap: 32px;
|
||||
--form-horizontal-gap: 32px;
|
||||
|
||||
@include form-grid;
|
||||
}
|
||||
|
||||
@@ -404,8 +404,8 @@ export default defineComponent({
|
||||
@import '@/styles/mixins/form-grid';
|
||||
|
||||
.form {
|
||||
--v-form-vertical-gap: 32px;
|
||||
--v-form-horizontal-gap: 32px;
|
||||
--form-vertical-gap: 32px;
|
||||
--form-horizontal-gap: 32px;
|
||||
|
||||
@include form-grid;
|
||||
}
|
||||
|
||||
@@ -524,7 +524,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
.form-grid {
|
||||
--v-form-vertical-gap: 24px;
|
||||
--form-vertical-gap: 24px;
|
||||
|
||||
@include form-grid;
|
||||
}
|
||||
|
||||
@@ -568,7 +568,7 @@ export default defineComponent({
|
||||
--sidebar-detail-icon-color: var(--warning);
|
||||
--sidebar-detail-color: var(--warning);
|
||||
--sidebar-detail-color-active: var(--warning);
|
||||
--v-form-vertical-gap: 24px;
|
||||
--form-vertical-gap: 24px;
|
||||
}
|
||||
|
||||
.portal-contents {
|
||||
@@ -576,7 +576,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
.layout-options ::v-deep {
|
||||
--v-form-vertical-gap: 24px;
|
||||
--form-vertical-gap: 24px;
|
||||
|
||||
.type-label {
|
||||
font-size: 1rem;
|
||||
|
||||
@@ -7,12 +7,7 @@
|
||||
<v-card-text>
|
||||
<div class="form-grid">
|
||||
<div class="field full">
|
||||
<v-input
|
||||
v-model="roleName"
|
||||
autofocus
|
||||
@keyup.enter="save"
|
||||
:placeholder="$t('role_name') + '...'"
|
||||
/>
|
||||
<v-input v-model="roleName" autofocus @keyup.enter="save" :placeholder="$t('role_name') + '...'" />
|
||||
</div>
|
||||
|
||||
<div class="field half">
|
||||
@@ -92,8 +87,8 @@ export default defineComponent({
|
||||
@import '@/styles/mixins/form-grid';
|
||||
|
||||
.form-grid {
|
||||
--v-form-horizontal-gap: 12px;
|
||||
--v-form-vertical-gap: 24px;
|
||||
--form-horizontal-gap: 12px;
|
||||
--form-vertical-gap: 24px;
|
||||
|
||||
@include form-grid;
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ export default defineComponent({
|
||||
@import '@/styles/mixins/form-grid';
|
||||
|
||||
.fields {
|
||||
--v-form-vertical-gap: 24px;
|
||||
--form-vertical-gap: 24px;
|
||||
|
||||
@include form-grid;
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
.layout-options ::v-deep {
|
||||
--v-form-vertical-gap: 24px;
|
||||
--form-vertical-gap: 24px;
|
||||
|
||||
.type-label {
|
||||
font-size: 1rem;
|
||||
|
||||
@@ -107,7 +107,7 @@ export default defineComponent({
|
||||
@import '@/styles/mixins/form-grid';
|
||||
|
||||
.grid {
|
||||
--v-form-vertical-gap: 20px;
|
||||
--form-vertical-gap: 20px;
|
||||
|
||||
@include form-grid;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user