1010 {{ t('tables', 'You can not insert any links in this field. Please configure at least one link provider in the column configuration.') }}
1111 </NcNoteCard >
1212 </div >
13- <div class =" col-4" >
14- <NcSelect v-model =" localValue"
13+ <div class =" link-input" >
14+ <NcTextField v-if =" isPlainUrl" :value.sync =" plainLink" :placeholder =" t('tables', 'URL')" />
15+ <NcSelect v-else v-model =" localValue"
1516 :options =" results"
1617 :clearable =" true"
1718 label =" title"
2223 <LinkWidget :thumbnail-url =" props.thumbnailUrl" :icon-url =" props.icon" :title =" props.title" :subline =" props.subline" :icon-size =" 40" />
2324 </template >
2425 <template #selected-option =" props " >
25- <LinkWidget :thumbnail-url =" props.thumbnailUrl" :icon-url =" props.icon" :title =" props.title" :subline = " props.subline " : icon-size =" 40 " />
26+ <LinkWidget :thumbnail-url =" props.thumbnailUrl" :icon-url =" props.icon" :title =" props.title" :icon-size =" 24 " />
2627 </template >
2728 </NcSelect >
29+ <NcButton type =" tertiary" :disabled =" !localValue" :title =" t('tables', 'Copy link')" @click =" copyLink" >
30+ <template #icon >
31+ <ContentCopy v-if =" !copied" :size =" 20" />
32+ <ClipboardCheckMultipleOutline v-else :size =" 20" />
33+ </template >
34+ </NcButton >
35+ <NcButton type =" tertiary" :disabled =" !localValue" :title =" t('tables', 'Open link')" @click =" openLink" >
36+ <template #icon >
37+ <OpenInNew :size =" 20" />
38+ </template >
39+ </NcButton >
2840 </div >
41+ <NcReferenceList :text =" localValue?.value" :limit =" 1" :interactive =" false" />
2942 </div >
3043 </RowFormWrapper >
3144</template >
3245
3346<script >
47+ import ContentCopy from ' vue-material-design-icons/ContentCopy.vue'
48+ import ClipboardCheckMultipleOutline from ' vue-material-design-icons/ClipboardCheckMultipleOutline.vue'
49+ import OpenInNew from ' vue-material-design-icons/OpenInNew.vue'
50+ import { NcReferenceList } from ' @nextcloud/vue/dist/Components/NcRichText.js'
3451import RowFormWrapper from ' ./RowFormWrapper.vue'
3552import axios from ' @nextcloud/axios'
3653import { generateOcsUrl } from ' @nextcloud/router'
3754import displayError from ' ../../../../utils/displayError.js'
38- import { NcNoteCard , NcSelect } from ' @nextcloud/vue'
55+ import { NcButton , NcTextField , NcNoteCard , NcSelect } from ' @nextcloud/vue'
3956import debounce from ' debounce'
4057import generalHelper from ' ../../../../mixins/generalHelper.js'
58+ import copyToClipboard from ' ../../../../mixins/copyToClipboard.js'
4159import LinkWidget from ' ../LinkWidget.vue'
4260import { translate as t } from ' @nextcloud/l10n'
4361
4462export default {
4563
4664 components: {
65+ ContentCopy,
66+ ClipboardCheckMultipleOutline,
67+ OpenInNew,
68+ NcButton,
4769 NcNoteCard,
48- RowFormWrapper,
4970 NcSelect,
71+ NcReferenceList,
72+ NcTextField,
73+ RowFormWrapper,
5074 LinkWidget,
5175 },
5276
53- mixins: [generalHelper],
77+ mixins: [generalHelper, copyToClipboard ],
5478
5579 props: {
5680 column: {
@@ -69,10 +93,24 @@ export default {
6993 results: [],
7094 term: ' ' ,
7195 providerLoading: {},
96+ copied: false ,
7297 }
7398 },
7499
75100 computed: {
101+ plainLink: {
102+ get () {
103+ return this .localValue ? .value ?? ' '
104+ },
105+ set (v ) {
106+ this .$emit (' update:value' , JSON .stringify ({
107+ title: v,
108+ subline: t (' tables' , ' URL' ),
109+ providerId: ' url' ,
110+ value: v,
111+ }))
112+ },
113+ },
76114 localValue: {
77115 get () {
78116 // if we got an old value (string not object as json)
@@ -95,6 +133,9 @@ export default {
95133 this .$emit (' update:value' , value)
96134 },
97135 },
136+ isPlainUrl () {
137+ return this .providers ? .length === 1 && this .providers [0 ] === ' url'
138+ },
98139 isLoadingResults () {
99140 for (const [key , value ] of Object .entries (this .providerLoading )) {
100141 console .debug (' is still loading results at least for: ' + key, value)
@@ -155,7 +196,9 @@ export default {
155196 this .removeResultsByProviderId (providerId)
156197
157198 if (providerId === ' url' ) {
158- this .addUrlResult (term)
199+ if (this .isValidUrl (term)) {
200+ this .addUrlResult (term)
201+ }
159202 this .setProviderLoading (providerId, false )
160203 return
161204 }
@@ -186,6 +229,14 @@ export default {
186229 this .setProviderLoading (providerId, false )
187230 },
188231
232+ isValidUrl (string ) {
233+ try {
234+ return new URL (string)
235+ } catch (err) {
236+ return false
237+ }
238+ },
239+
189240 addUrlResult (term ) {
190241 this .results .push ({
191242 title: term,
@@ -198,6 +249,42 @@ export default {
198249 removeResultsByProviderId (providerId ) {
199250 this .results = this .results .filter (item => item .providerId !== providerId)
200251 },
252+
253+ copyLink () {
254+ if (this .localValue ) {
255+ if (this .copied !== false ) {
256+ clearTimeout (this .copied )
257+ }
258+
259+ this .copied = this .copyToClipboard (this .localValue .value )
260+ ? setTimeout (() => {
261+ this .copied = false
262+ }, 3000 )
263+ : false
264+ }
265+ },
266+
267+ openLink () {
268+ if (this .localValue ) {
269+ window .open (this .localValue .value , ' _blank' )
270+ }
271+ },
201272 },
202273}
203274< / script>
275+ < style lang= " scss" scoped>
276+ .link - input {
277+ display: flex;
278+ align- items: center;
279+ margin- bottom: var (-- default- grid- baseline);
280+
281+ : deep (.v - select: not (.vs -- open) .vs__search ) {
282+ position: absolute;
283+ }
284+
285+ : deep (.vs__selected ) {
286+ flex- grow: 1 ;
287+ height: auto ! important;
288+ }
289+ }
290+ < / style>
0 commit comments