From 62ef2bb59a8d7436bedb43f68a863c11e86c75d2 Mon Sep 17 00:00:00 2001 From: Rijk van Zanten Date: Mon, 11 May 2020 12:58:07 -0400 Subject: [PATCH] Optimize slug/db-safe modes on v-input, add slug interface (#548) * Optimize slug functionality * Add slug interface --- src/components/v-input/v-input.vue | 38 ++++++++++++++++++++++++++++++ src/interfaces/index.ts | 2 ++ src/interfaces/slug/index.ts | 10 ++++++++ src/interfaces/slug/slug.vue | 26 ++++++++++++++++++++ 4 files changed, 76 insertions(+) create mode 100644 src/interfaces/slug/index.ts create mode 100644 src/interfaces/slug/slug.vue diff --git a/src/components/v-input/v-input.vue b/src/components/v-input/v-input.vue index d020bcb20e..44a88a6a34 100644 --- a/src/components/v-input/v-input.vue +++ b/src/components/v-input/v-input.vue @@ -122,6 +122,7 @@ export default defineComponent({ const _listeners = computed(() => ({ ...listeners, input: emitValue, + keydown: processValue, })); const hasClick = computed(() => { @@ -130,11 +131,48 @@ export default defineComponent({ return { _listeners, hasClick, stepUp, stepDown, input }; + function processValue(event: KeyboardEvent) { + const key = event.key.toLowerCase(); + const systemKeys = ['meta', 'shift', 'alt', 'backspace']; + const value = (event.target as HTMLInputElement).value; + + if (props.slug === true) { + const slugSafeCharacters = 'abcdefghijklmnopqrstuvwxyz01234567890-_~ '.split(''); + + const isAllowed = slugSafeCharacters.includes(key) || systemKeys.includes(key); + + if (isAllowed === false) { + event.preventDefault(); + } + + if (key === ' ' && value.endsWith(props.slugSeparator)) { + event.preventDefault(); + } + } + + if (props.slug === true) { + const dbSafeCharacters = 'abcdefghijklmnopqrstuvwxyz01234567890-_~ '.split(''); + + const isAllowed = dbSafeCharacters.includes(key) || systemKeys.includes(key); + + if (isAllowed === false) { + event.preventDefault(); + } + + // Prevent leading number + if (value.length === 0 && '0123456789'.split('').includes(key)) { + event.preventDefault(); + } + } + } + function emitValue(event: InputEvent) { let value = (event.target as HTMLInputElement).value; if (props.slug === true) { + const endsWithSpace = value.endsWith(' '); value = slugify(value, { separator: props.slugSeparator }); + if (endsWithSpace) value += props.slugSeparator; } if (props.dbSafe === true) { diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts index ac1330544e..9d83fa4057 100644 --- a/src/interfaces/index.ts +++ b/src/interfaces/index.ts @@ -16,6 +16,7 @@ import InterfaceIcon from './icon'; import InterfaceManyToOne from './many-to-one'; import InterfaceOneToMany from './one-to-many'; import InterfaceHash from './hash'; +import InterfaceSlug from './slug'; export const interfaces = [ InterfaceTextInput, @@ -36,6 +37,7 @@ export const interfaces = [ InterfaceManyToOne, InterfaceOneToMany, InterfaceHash, + InterfaceSlug, ]; export default interfaces; diff --git a/src/interfaces/slug/index.ts b/src/interfaces/slug/index.ts new file mode 100644 index 0000000000..5456924470 --- /dev/null +++ b/src/interfaces/slug/index.ts @@ -0,0 +1,10 @@ +import { defineInterface } from '@/interfaces/define'; +import InterfaceSlug from './slug.vue'; + +export default defineInterface(({ i18n }) => ({ + id: 'slug', + name: i18n.t('slug'), + icon: 'link', + component: InterfaceSlug, + options: [], +})); diff --git a/src/interfaces/slug/slug.vue b/src/interfaces/slug/slug.vue new file mode 100644 index 0000000000..6d10c77b90 --- /dev/null +++ b/src/interfaces/slug/slug.vue @@ -0,0 +1,26 @@ + + + + +