Add custom syntax block option to markdown

And fix v-form overrides
This commit is contained in:
rijkvanzanten
2021-01-15 11:43:27 -05:00
parent 596230107a
commit b30324a06c
14 changed files with 180 additions and 42 deletions

View File

@@ -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. |

View File

@@ -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 };

View File

@@ -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',
},
},
],
},
},
},
],
}));

View File

@@ -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 || '');

View File

@@ -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;

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -524,7 +524,7 @@ export default defineComponent({
}
.form-grid {
--v-form-vertical-gap: 24px;
--form-vertical-gap: 24px;
@include form-grid;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}