Skip to content

Commit

Permalink
Copy button for Asset links #94
Browse files Browse the repository at this point in the history
  • Loading branch information
m-mohr committed May 22, 2024
1 parent 7ce7027 commit e8af4b3
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 34 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Option to hide deprecated elements from lists by default (prop: `hideDeprecatedByDefault`)
- Copy Asset URL

### Fixed

Expand Down
23 changes: 6 additions & 17 deletions components/SearchableList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
<script>
import Utils from '../utils';
import Loading from './internal/Loading.vue';
import CopyMixin from './internal/CopyMixin';
import Vue from 'vue';
export default {
Expand All @@ -60,6 +61,7 @@ export default {
Loading,
SearchBox: () => import('./SearchBox.vue')
},
mixins: [CopyMixin],
props: {
data: {
type: [Array, Object],
Expand Down Expand Up @@ -137,8 +139,7 @@ export default {
showDetails: {},
showList: this.collapsed ? null : true,
hideDeprecated: this.hideDeprecatedByDefault,
summaries: [],
canCopy: false
summaries: []
};
},
watch: {
Expand Down Expand Up @@ -201,9 +202,6 @@ export default {
created() {
this.filter();
},
mounted() {
this.canCopy = navigator && navigator.clipboard && typeof navigator.clipboard.writeText === 'function';
},
methods: {
hasActiveFilter() {
return this.searchTerm.length >= this.searchMinLength || this.hideDeprecated;
Expand All @@ -223,18 +221,9 @@ export default {
this.$emit('summaries', this.summaries);
},
copyIdentifier(event, summary) {
if (this.allowCopy && this.canCopy) {
let elem = event.composedPath()[0]; // event.target doesn't work in web components
navigator.clipboard.writeText(summary.identifier)
.then(() => this.toggleIcon(elem, ''))
.catch(() => this.toggleIcon(elem, ''))
}
},
toggleIcon(elem, newIcon) {
if (elem) {
let oldIcon = elem.innerText;
elem.innerText = newIcon;
setTimeout(() => elem.innerText = oldIcon, 2000);
if (this.allowCopy) {
const elem = event.composedPath()[0]; // event.target doesn't work in web components
this.copyText(summary.identifier, () => this.toggleIcon(elem, ''), () => this.toggleIcon(elem, ''));
}
},
generateSummaries() {
Expand Down
25 changes: 21 additions & 4 deletions components/base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -147,20 +147,20 @@
}
.vue-component .badges .badge a.badge-fill {
margin: -0.35em -0.5em;
padding: 0.35em 0.5em 0.25em 0.5em;
padding: 0.35em 0.5em;
display: block;
border-bottom: 0px;
}
.vue-component .badges.small .badge a.badge-fill {
margin: -0.2em -0.3em;
padding: 0.25em 0.35em 0.2em 0.35em;
padding: 0.25em 0.35em;
}
.vue-component .badges .badge a:hover {
color: #fff;
border-bottom-style: solid;
}
.vue-component .badges .default {
background-color: black;
background-color: #555;
}
.vue-component .badges .green {
background-color: green;
Expand All @@ -177,17 +177,34 @@
.vue-component .badges .option3 {
background-color: #936;
}
.vue-component .badges .option4 {
background-color: #963;
}
.vue-component .badges .deprecated {
background-color: red;
}
.vue-component .badges .experimental {
background-color: blueviolet;
}
.vue-component .badges .action {
background-color: chocolate;
border: 1px solid black;
background-color: white;
color: black;
margin: 0.33em;
cursor: pointer;
text-transform: none;

&:hover {
background-color: black;
color: white;

> a {
color: white;
}
}

> a {
color: black;
}
}
/* Badges for UDFs */
Expand Down
30 changes: 30 additions & 0 deletions components/internal/CopyMixin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export default {
data() {
return {
canCopy: false
}
},
mounted() {
this.canCopy = navigator && navigator.clipboard && typeof navigator.clipboard.writeText === 'function';
},
methods: {
copyText(text, onSuccess = null, onError = null) {
if (this.canCopy) {
const promise = navigator.clipboard.writeText(text)
if (onSuccess) {
promise.then(onSuccess);
}
if (onError) {
promise.catch(onError);
}
}
},
toggleIcon(elem, newIcon) {
if (elem) {
let oldIcon = elem.innerText;
elem.innerText = newIcon;
setTimeout(() => elem.innerText = oldIcon, 2000);
}
}
}
}
48 changes: 35 additions & 13 deletions components/internal/StacAsset.vue
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
<template>
<li class="vue-component asset">
<h4>
<ul class="badges actions">
<li class="badge action download">
<a class="badge-fill" :href="asset.href" target="_blank" download>
Download '{{ asset.title || id }}'
<template v-if="fileFormat">as {{ fileFormat }}</template>
</a>
</li>
</ul>
<ul v-if="Array.isArray(asset.roles)" class="badges">
<span>{{ asset.title || id }}</span>
<ul v-if="Array.isArray(asset.roles)" class="badges roles">
<li v-for="role in asset.roles" :key="role" class="badge" :class="role === 'data' ? 'green' : 'secondary'">{{ role }}</li>
</ul>
</h4>
<ul class="badges actions primary">
<li class="badge action download">
<a class="badge-fill" :href="asset.href" target="_blank" download>
<span class="icon">💾</span> Download <template v-if="fileFormat">{{ fileFormat }}</template>
</a>
</li>
<li class="badge action copy" @click.prevent.stop="copyURL($event, asset.href)">
<span class="icon">📋</span> Copy URL
</li>
</ul>
<Description v-if="asset.description" :description="asset.description" :compact="true" />
<StacFields type="Asset" :metadata="asset" :ignore="ignore" title="" :context="context" headingTag="h5" />
</li>
Expand All @@ -22,13 +25,15 @@
import { Formatters } from '@radiantearth/stac-fields';
import Description from '../Description.vue';
import StacFields from './StacFields.vue';
import CopyMixin from './CopyMixin';
export default {
name: 'Asset',
components: {
Description,
StacFields
},
mixins: [CopyMixin],
props: {
asset: {
type: Object,
Expand All @@ -55,25 +60,42 @@ export default {
}
return null;
}
},
methods: {
copyURL(event, url) {
const elem = event.composedPath()[0].querySelector('.icon'); // event.target doesn't work in web components
this.copyText(url, () => this.toggleIcon(elem, ''), () => this.toggleIcon(elem, ''));
}
}
}
</script>

<style lang="scss">
.vue-component.asset {
margin-bottom: 1.5em;
> * {
margin: 0.25em 0;
}
.actions > .action {
padding: 0.5em;
}
h4 {
font-size: 1.1em;
margin: 0;
font-weight: normal;
.download {
margin-right: 1em;
> .roles {
font-size: 0.95em;
margin-left: 0.3em;
}
}
h5 {
font-size: 1.1em;
font-weight: bold;
margin: 0.75em 0 0.5em;
margin: 0.5em 0;
}
.metadata {
Expand Down

0 comments on commit e8af4b3

Please sign in to comment.