feat(editor): make links clickable in locations and description areas

Signed-off-by: Richard Steinmetz <richard@steinmetz.cloud>
This commit is contained in:
Sergey Mosin 2023-06-22 18:50:01 -04:00 committed by Richard Steinmetz
parent b40506adf6
commit 8fa9d99ea7
No known key found for this signature in database
GPG Key ID: 27137D9E7D273FB2
8 changed files with 120 additions and 11 deletions

View File

@ -29,3 +29,4 @@
@import 'import.scss';
@import 'print.scss';
@import 'public.scss';
@import 'props-linkify-links.scss';

View File

@ -0,0 +1,25 @@
.property-text__input--linkify {
flex-basis: min-content;
}
.linkify-links {
border: 2px solid var(--color-border-maxcontrast);
border-radius: var(--border-radius-large);
cursor: text;
width: 100% !important;
box-sizing: border-box;
margin-top: 1px !important;
padding: 12px;
white-space: pre-line;
overflow: auto;
line-height: normal;
word-break: break-word;
display: inline-block;
vertical-align: top;
max-height: 16em;
max-height: calc(100vh - 500px);
a.linkified::after {
content: '';
}
}

15
package-lock.json generated
View File

@ -39,6 +39,7 @@
"css-color-names": "^1.0.1",
"debounce": "^1.2.1",
"jstz": "^2.1.1",
"linkifyjs": "^4.1.1",
"lodash": "^4.17.21",
"md5": "^2.3.0",
"p-limit": "^4.0.0",
@ -11952,10 +11953,9 @@
}
},
"node_modules/linkifyjs": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.0.2.tgz",
"integrity": "sha512-/VSoCZiglX0VMsXmL5PN3lRg45M86lrD9PskdkA2abWaTKap1bIcJ11LS4EE55bcUl9ZOR4eZ792UtQ9E/5xLA==",
"peer": true
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.1.1.tgz",
"integrity": "sha512-zFN/CTVmbcVef+WaDXT63dNzzkfRBKT1j464NJQkV7iSgJU0sLBus9W0HBwnXK13/hf168pbrx/V/bjEHOXNHA=="
},
"node_modules/loader-runner": {
"version": "4.3.0",
@ -26602,10 +26602,9 @@
"requires": {}
},
"linkifyjs": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.0.2.tgz",
"integrity": "sha512-/VSoCZiglX0VMsXmL5PN3lRg45M86lrD9PskdkA2abWaTKap1bIcJ11LS4EE55bcUl9ZOR4eZ792UtQ9E/5xLA==",
"peer": true
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.1.1.tgz",
"integrity": "sha512-zFN/CTVmbcVef+WaDXT63dNzzkfRBKT1j464NJQkV7iSgJU0sLBus9W0HBwnXK13/hf168pbrx/V/bjEHOXNHA=="
},
"loader-runner": {
"version": "4.3.0",

View File

@ -66,6 +66,7 @@
"css-color-names": "^1.0.1",
"debounce": "^1.2.1",
"jstz": "^2.1.1",
"linkifyjs": "^4.1.1",
"lodash": "^4.17.21",
"md5": "^2.3.0",
"p-limit": "^4.0.0",

View File

@ -30,17 +30,22 @@
:class="{ 'property-text__icon--hidden': !showIcon }" />
<div class="property-text__input"
:class="{ 'property-text__input--readonly': isReadOnly }">
<textarea v-if="!isReadOnly"
:class="{ 'property-text__input--readonly': isReadOnly, 'property-text__input--linkify': showLinksClickable }">
<textarea v-if="!isReadOnly && !showLinksClickable"
v-autosize="true"
:placeholder="placeholder"
:rows="rows"
:title="readableName"
:value="value"
@focus="handleToggleTextareaFocus(true)"
@blur="handleToggleTextareaFocus(false)"
@input.prevent.stop="changeValue" />
<!-- eslint-disable-next-line vue/singleline-html-element-content-newline -->
<div v-else
v-linkify="{ text: value, linkify: true }" />
v-linkify="{ text: value, linkify: true }"
:class="{ 'linkify-links': linkifyLinks && !isReadOnly }"
:style="{ 'min-height': linkifyMinHeight }"
@click="handleShowTextarea" />
</div>
<div v-if="hasInfo"
@ -58,6 +63,7 @@ import PropertyMixin from '../../../mixins/PropertyMixin.js'
import linkify from '@nextcloud/vue/dist/Directives/Linkify.js'
import InformationVariant from 'vue-material-design-icons/InformationVariant.vue'
import PropertyLinksMixin from '../../../mixins/PropertyLinksMixin.js'
export default {
name: 'PropertyText',
@ -68,6 +74,7 @@ export default {
},
mixins: [
PropertyMixin,
PropertyLinksMixin,
],
computed: {
display() {

View File

@ -0,0 +1,72 @@
import { find as findLinks } from 'linkifyjs'
export default {
props: {
linkifyLinks: {
type: Boolean,
default: false,
},
},
computed: {
showLinksClickable() {
return this.linkifyLinks
&& this.textareaHasFocus === false
&& typeof this.value === 'string'
&& this.value.length > 4
&& findLinks(this.value).length > 0
},
linkifyMinHeight() {
return this.rows > 1 ? '68px' : '48px'
},
},
data() {
return {
textareaHasFocus: false,
}
},
methods: {
handleShowTextarea(evt) {
if (this.isReadOnly || this.linkifyLinks === false) {
// do nothing
return
}
if (evt.target && evt.target.tagName === 'A') {
// a link was clicked, do nothing
return
}
if (this.textareaHasFocus === true) {
// the textarea is shown already, should never happen
console.error('this.textareaHasFocus is true but click event is dispatched on div')
return
}
const parent = evt.currentTarget.parentElement
this.textareaHasFocus = true
this.$nextTick(() => {
if (parent && parent.firstElementChild) {
const textarea = parent.firstElementChild
textarea.focus()
if (this.value && this.value.length > 64) {
textarea.selectionStart = textarea.selectionEnd = 0
} else {
textarea.selectionStart = textarea.selectionEnd = 64
}
}
})
},
/**
* @param {boolean} hasFocus
*/
handleToggleTextareaFocus(hasFocus) {
if (this.linkifyLinks === false) {
// do nothing
return
}
this.textareaHasFocus = hasFocus
},
},
}

View File

@ -127,10 +127,12 @@
<PropertyText :is-read-only="isReadOnly"
:prop-model="rfcProps.location"
:value="location"
:linkify-links="true"
@update:value="updateLocation" />
<PropertyText :is-read-only="isReadOnly"
:prop-model="rfcProps.description"
:value="description"
:linkify-links="true"
@update:value="updateDescription" />
<PropertySelect :is-read-only="isReadOnly"

View File

@ -136,10 +136,12 @@
<PropertyText :is-read-only="isReadOnly"
:prop-model="rfcProps.location"
:value="location"
:linkify-links="true"
@update:value="updateLocation" />
<PropertyText :is-read-only="isReadOnly"
:prop-model="rfcProps.description"
:value="description"
:linkify-links="true"
@update:value="updateDescription" />
<InvitationResponseButtons v-if="isViewedByAttendee"