From 48ae12635337f776538c8db9461da42915bf8cdc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20G=C3=B6bel?= <dgoebel@techfak.uni-bielefeld.de>
Date: Wed, 10 Aug 2022 16:08:34 +0200
Subject: [PATCH] Add a tooltip with the exact date on every relative date

#18
---
 src/components/BucketListItem.vue |  22 ++++++-
 src/components/BucketView.vue     | 103 +++++++++++++++++++-----------
 src/main.ts                       |   2 +-
 3 files changed, 86 insertions(+), 41 deletions(-)

diff --git a/src/components/BucketListItem.vue b/src/components/BucketListItem.vue
index dc5c263..a8ba47b 100644
--- a/src/components/BucketListItem.vue
+++ b/src/components/BucketListItem.vue
@@ -3,6 +3,8 @@ import type { BucketOut } from "@/client";
 import BootstrapIcon from "@/components/BootstrapIcon.vue";
 import dayjs from "dayjs";
 import fileSize from "filesize";
+import { onMounted } from "vue";
+import { Tooltip } from "bootstrap";
 
 const props = defineProps<{
   active: boolean;
@@ -10,9 +12,17 @@ const props = defineProps<{
   loading: boolean;
 }>();
 
+const tooltipID = Math.random().toString(16).substr(2, 8);
+
 const emit = defineEmits<{
   (e: "delete-bucket", bucket_name: string): void;
 }>();
+
+onMounted(() => {
+  if (!props.loading) {
+    new Tooltip("#tooltip-" + tooltipID);
+  }
+});
 </script>
 
 <template>
@@ -55,7 +65,17 @@ const emit = defineEmits<{
           <tbody>
             <tr>
               <th scope="row" class="fw-bold">Created:</th>
-              <td>{{ dayjs(bucket.created_at).fromNow() }}</td>
+              <td>
+                <span
+                  :id="'tooltip-' + tooltipID"
+                  data-bs-toggle="tooltip"
+                  :data-bs-title="
+                    dayjs(bucket.created_at).format('DD.MM.YYYY HH:mm:ss')
+                  "
+                >
+                  {{ dayjs(bucket.created_at).fromNow() }}
+                </span>
+              </td>
             </tr>
             <tr>
               <th scope="row" class="fw-bold">Objects:</th>
diff --git a/src/components/BucketView.vue b/src/components/BucketView.vue
index e6c6011..e65db0f 100644
--- a/src/components/BucketView.vue
+++ b/src/components/BucketView.vue
@@ -6,6 +6,7 @@ import { ObjectService } from "@/client";
 import BootstrapIcon from "@/components/BootstrapIcon.vue";
 import fileSize from "filesize";
 import dayjs from "dayjs";
+import { Tooltip } from "bootstrap";
 
 // Constants
 // -----------------------------------------------------------------------------
@@ -135,6 +136,44 @@ const currentSubFolders: ComputedRef<string[]> = computed(() => {
     : [];
 });
 
+const visibleObjects: ComputedRef<(S3ObjectWithFolder | S3PseudoFolder)[]> =
+  computed(() => {
+    /**
+     * Compute the visible objects based on the current sub folder
+     */
+    let currentFolder = folderStructure.value;
+    // Navigate into right sub folder
+    for (const subFolder of currentSubFolders.value) {
+      if (currentFolder.subFolders[subFolder] == null) {
+        // If sub folder doesn't exist, no object is visible
+        return [];
+      } else {
+        currentFolder = currentFolder.subFolders[subFolder];
+      }
+    }
+    // Add all objects and sub folders from the current sub folder as visible object
+    const arr = [];
+    arr.push(...currentFolder.files);
+    arr.push(
+      ...Object.keys(currentFolder.subFolders).map((subFolderName) => {
+        const folderSize = calculateFolderSize(
+          currentFolder.subFolders[subFolderName]
+        );
+        const folderLastModified = dayjs(
+          calculateFolderLastModified(currentFolder.subFolders[subFolderName])
+        ).toISOString();
+        return {
+          name: subFolderName,
+          size: folderSize,
+          key: subFolderName,
+          parentFolder: currentSubFolders.value,
+          last_modified: folderLastModified,
+        } as S3PseudoFolder;
+      })
+    );
+    return arr;
+  });
+
 const subFolderInUrl: ComputedRef<boolean> = computed(
   () => currentSubFolders.value.length > 0
 );
@@ -212,49 +251,26 @@ function updateObjects(bucketName: string) {
     });
 }
 
-const visibleObjects: ComputedRef<(S3ObjectWithFolder | S3PseudoFolder)[]> =
-  computed(() => {
-    /**
-     * Compute the visible objects based on the current sub folder
-     */
-    let currentFolder = folderStructure.value;
-    // Navigate into right sub folder
-    for (const subFolder of currentSubFolders.value) {
-      if (currentFolder.subFolders[subFolder] == null) {
-        // If sub folder doesn't exist, no object is visible
-        return [];
-      } else {
-        currentFolder = currentFolder.subFolders[subFolder];
-      }
-    }
-    // Add all objects and sub folders from the current sub folder as visible object
-    const arr = [];
-    arr.push(...currentFolder.files);
-    arr.push(
-      ...Object.keys(currentFolder.subFolders).map((subFolderName) => {
-        const folderSize = calculateFolderSize(
-          currentFolder.subFolders[subFolderName]
-        );
-        const folderLastModified = dayjs(
-          calculateFolderLastModified(currentFolder.subFolders[subFolderName])
-        ).toISOString();
-        return {
-          name: subFolderName,
-          size: folderSize,
-          key: subFolderName,
-          parentFolder: currentSubFolders.value,
-          last_modified: folderLastModified,
-        } as S3PseudoFolder;
-      })
-    );
-    return arr;
-  });
-
 function isS3Object(
   obj: S3PseudoFolder | S3ObjectWithFolder
 ): obj is S3ObjectWithFolder {
   return (obj as S3ObjectWithFolder).folder !== undefined;
 }
+
+watch(
+  visibleObjects,
+  (visObjs) => {
+    if (visObjs.length > 0) {
+      // Initialise tooltips after DOM changes
+      setTimeout(() => {
+        document
+          .querySelectorAll("span.date-tooltip")
+          .forEach((tooltipTriggerEl) => new Tooltip(tooltipTriggerEl));
+      }, 500);
+    }
+  },
+  { flush: "post" }
+);
 </script>
 
 <template>
@@ -412,7 +428,16 @@ function isS3Object(
                 </router-link>
               </div>
             </th>
-            <td>{{ dayjs(obj.last_modified).fromNow() }}</td>
+            <td>
+              <span
+                class="date-tooltip"
+                data-bs-toggle="tooltip"
+                :data-bs-title="
+                  dayjs(obj.last_modified).format('DD.MM.YYYY HH:mm:ss')
+                "
+                >{{ dayjs(obj.last_modified).fromNow() }}</span
+              >
+            </td>
             <td>{{ fileSize(obj.size) }}</td>
             <!-- Show buttons with dropdown menu if row is an object -->
             <td class="text-end">
diff --git a/src/main.ts b/src/main.ts
index b2caf37..5844f9b 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -22,4 +22,4 @@ app.use(router);
 app.mount("#app");
 
 // eslint-disable-next-line @typescript-eslint/no-unused-vars
-import { Modal, Collapse, Dropdown } from "bootstrap";
+import { Modal, Collapse, Dropdown, Tooltip } from "bootstrap";
-- 
GitLab