|
57 | 57 | <ul> |
58 | 58 | <!-- Iterates over list of containers in a pod --> |
59 | 59 | <li v-for="(url, index) in urls" :key="index"> |
60 | | - <div class="card-panel folder"> |
| 60 | + <div |
| 61 | + v-if="loadingIndex === index" |
| 62 | + class="loading-spinner-container" |
| 63 | + > |
| 64 | + <div class="spinner"></div> |
| 65 | + <span class="loading-text">Loading access rights...</span> |
| 66 | + </div> |
| 67 | + <div v-else class="card-panel folder"> |
61 | 68 | <button |
62 | 69 | @click="toggleInfo(index, url)" |
63 | 70 | class="icon-button full-width" |
64 | 71 | > |
| 72 | + |
65 | 73 | <div class="icon-hash"> |
66 | 74 | <i class="material-icons not-colored left">{{ |
67 | 75 | containerCheck(url) ? "folder" : "description" |
68 | 76 | }}</i> |
69 | | - {{ url }} |
| 77 | + <span class="highlightable-text">{{ url }}</span> |
70 | 78 | </div> |
71 | 79 | <div class="info-icon"> |
72 | 80 | <i class="material-icons not-colored info-icon"> |
|
112 | 120 | v-if="info && info.linkedResources.describedby" |
113 | 121 | > |
114 | 122 | <strong class="info-label">Metadata:</strong> |
115 | | - <div class="info-value-container"> |
| 123 | + <div v-if="checkUrl(url)" class="info-value-container"> |
116 | 124 | <a |
117 | 125 | :href="info.linkedResources.describedby" |
118 | 126 | target="_blank" |
|
121 | 129 | >{{ info.linkedResources.describedby }}</a |
122 | 130 | > |
123 | 131 | </div> |
| 132 | + <div v-else class="info-value-container"> |
| 133 | + <span>{{ info.linkedResources.describedby }}</span> |
| 134 | + </div> |
124 | 135 | </div> |
125 | 136 | <!-- TODO: figure out if this works and what edit does --> |
126 | 137 | <div class="edit-delete"> |
@@ -163,6 +174,7 @@ import ContainerNav from "./ContainerNav.vue"; |
163 | 174 | import PodRegistration from "./PodRegistration.vue"; |
164 | 175 | import PodBrowserGuide from "./Guides/PodBrowserGuide.vue"; |
165 | 176 | import { useAuthStore } from "../stores/auth"; |
| 177 | +import { checkUrl } from "./privacyEdit"; |
166 | 178 |
|
167 | 179 | interface info { |
168 | 180 | sourceIri: string; |
@@ -197,6 +209,7 @@ export default { |
197 | 209 | renderKey: 0 as number, |
198 | 210 | deletionSuccess: false, |
199 | 211 | deletedItemType: "" as "Resource" | "Container" | "", |
| 212 | + loadingIndex: null as number | null, |
200 | 213 | }; |
201 | 214 | }, |
202 | 215 | computed: { |
@@ -383,19 +396,38 @@ export default { |
383 | 396 | this.newName = name; |
384 | 397 | } |
385 | 398 | }, |
386 | | - toggleInfo(index: number, url: string) { |
| 399 | + async toggleInfo(index: number, url: string) { |
387 | 400 | if (this.showInfoIndex === index) { |
388 | 401 | this.showInfoIndex = null; // Hide the form if it's already shown |
389 | 402 | } else { |
| 403 | + try { |
| 404 | + this.showInfoIndex = index; |
| 405 | + this.loadingIndex = index; |
| 406 | + await this.getItemInfo(url); |
| 407 | + } catch (error) { |
| 408 | + this.dirContents = null; |
| 409 | + this.info = { |
| 410 | + sourceIri: "error fetching info", |
| 411 | + linkedResources: { |
| 412 | + type: "error fetching info", |
| 413 | + describedby: "error fetching info", |
| 414 | + }, |
| 415 | + }; |
| 416 | + console.error("Error fetching item info:", error); |
| 417 | + } finally { |
| 418 | + this.loadingIndex = null; |
| 419 | + } |
390 | 420 | this.showInfoIndex = index; // Show the form for the clicked item |
391 | | - this.getItemInfo(url); |
392 | 421 | } |
393 | 422 | }, |
394 | 423 | /* Takes in the emitted value from ContainerNav.vue */ |
395 | 424 | async handleSelectedContainer(selectedContainer: string) { |
396 | 425 | this.displayPath = selectedContainer; |
397 | 426 | await this.getItems(this.displayPath); |
398 | 427 | }, |
| 428 | + checkUrl(url: string) { |
| 429 | + return checkUrl(url, this.currentLocation); |
| 430 | + }, |
399 | 431 | }, |
400 | 432 | mounted() { |
401 | 433 | setTimeout(() => { |
@@ -581,6 +613,42 @@ body { |
581 | 613 | .info-icon { |
582 | 614 | margin-left: auto; |
583 | 615 | } |
| 616 | +.highlightable-text { |
| 617 | + user-select: text; |
| 618 | + cursor: text; |
| 619 | +} |
| 620 | +
|
| 621 | +/* Loading spinner */ |
| 622 | +.loading-spinner-container { |
| 623 | + display: flex; |
| 624 | + flex-direction: column; |
| 625 | + align-items: center; |
| 626 | + justify-content: center; |
| 627 | + height: 100px; |
| 628 | + margin: 20px 0; |
| 629 | +} |
| 630 | +.spinner { |
| 631 | + border: 4px solid var(--border); |
| 632 | + border-top: 4px solid var(--primary); |
| 633 | + border-radius: 50%; |
| 634 | + width: 40px; |
| 635 | + height: 40px; |
| 636 | + animation: spin 1s linear infinite; |
| 637 | +} |
| 638 | +.loading-text { |
| 639 | + margin-top: 10px; |
| 640 | + font-family: "Oxanium", monospace; |
| 641 | + font-size: 14pt; |
| 642 | + color: var(--text-secondary); |
| 643 | +} |
| 644 | +@keyframes spin { |
| 645 | + 0% { |
| 646 | + transform: rotate(0deg); |
| 647 | + } |
| 648 | + 100% { |
| 649 | + transform: rotate(360deg); |
| 650 | + } |
| 651 | +} |
584 | 652 |
|
585 | 653 | .item-info-container { |
586 | 654 | background-color: var(--bg); |
@@ -610,6 +678,10 @@ body { |
610 | 678 | gap: 0.5rem; |
611 | 679 | overflow: hidden; |
612 | 680 | } |
| 681 | +.info-value-container span { |
| 682 | + color: var(--text-secondary); |
| 683 | +} |
| 684 | +
|
613 | 685 |
|
614 | 686 | .info-value { |
615 | 687 | color: var(--text-secondary); |
|
0 commit comments