/ app / layouts / default.vue
default.vue
  1  <template>
  2    <div class="layout">
  3      <Navbar />
  4      <div class="fade-top"></div>
  5      <div class="content">
  6        <slot />
  7      </div>
  8      <div
  9        class="fade-bottom"
 10        :class="{ 'home-page': $route.path === '/' }"></div>
 11      <div class="bottombar" v-if="$route.path === '/'">
 12        <p class="coding-time">{{ formattedTime }}</p>
 13        <UiSelect v-model="selectedTimeRange" :items="timeRangeOptions" />
 14      </div>
 15    </div>
 16  </template>
 17  
 18  <script setup lang="ts">
 19  import * as statsLib from "~~/lib/stats";
 20  import { watch } from "vue";
 21  import { useTimeRangeOptions } from "~/composables/useTimeRangeOptions";
 22  
 23  const stats = ref(statsLib.getStats());
 24  const timeRange = ref(statsLib.getTimeRange());
 25  const { timeRangeOptions } = useTimeRangeOptions();
 26  
 27  const selectedTimeRange = computed({
 28    get: () => timeRange.value,
 29    set: (value: statsLib.TimeRange) => {
 30      statsLib.setTimeRange(value);
 31      timeRange.value = value;
 32    },
 33  });
 34  
 35  const formattedTime = computed(() => {
 36    return statsLib.formatTime(stats.value.totalSeconds || 0);
 37  });
 38  
 39  watch([() => statsLib.getStats(), () => statsLib.getTimeRange()], () => {
 40    stats.value = statsLib.getStats();
 41    timeRange.value = statsLib.getTimeRange();
 42  });
 43  </script>
 44  
 45  <style lang="scss">
 46  .layout {
 47    width: 100dvw;
 48    height: 100dvh;
 49    display: flex;
 50    flex-direction: column;
 51    box-sizing: border-box;
 52    padding: 24px;
 53  }
 54  
 55  .content {
 56    flex: 1;
 57    overflow-y: auto;
 58    height: 0;
 59    margin: 0 -24px;
 60    padding: 24px;
 61    position: relative;
 62  }
 63  
 64  .fade-top {
 65    position: absolute;
 66    top: 42px;
 67    height: 24px;
 68    width: calc(100vw - 48px);
 69    left: 24px;
 70    background: linear-gradient(
 71      to bottom,
 72      var(--background) 20%,
 73      transparent 100%
 74    );
 75    z-index: 5;
 76    pointer-events: none;
 77  }
 78  
 79  .fade-bottom {
 80    position: absolute;
 81    bottom: 18px;
 82    height: 24px;
 83    left: 24px;
 84    width: calc(100vw - 48px);
 85    background: linear-gradient(to top, var(--background) 20%, transparent 100%);
 86    z-index: 5;
 87    pointer-events: none;
 88  
 89    &.home-page {
 90      bottom: 42px;
 91    }
 92  }
 93  
 94  .bottombar {
 95    display: flex;
 96    justify-content: space-between;
 97    align-items: center;
 98    z-index: 10;
 99    height: 18px;
100    overflow: visible;
101    color: var(--text-secondary);
102  }
103  </style>