mirror of https://github.com/nextcloud/calendar
feat(editor): make links clickable in locations and description areas
Signed-off-by: Richard Steinmetz <richard@steinmetz.cloud>
This commit is contained in:
parent
b40506adf6
commit
8fa9d99ea7
|
@ -29,3 +29,4 @@
|
|||
@import 'import.scss';
|
||||
@import 'print.scss';
|
||||
@import 'public.scss';
|
||||
@import 'props-linkify-links.scss';
|
||||
|
|
|
@ -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: ' ↗';
|
||||
}
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
},
|
||||
},
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue