Kaynağa Gözat

Adds components for showing Docker stats and markdown docs

Alicia Sykes 2 yıl önce
ebeveyn
işleme
eb3e5e5ade
2 değiştirilmiş dosya ile 162 ekleme ve 0 silme
  1. 77 0
      src/lib/DockerStats.svelte
  2. 85 0
      src/lib/MdContent.svelte

+ 77 - 0
src/lib/DockerStats.svelte

@@ -0,0 +1,77 @@
+
+<script lang="ts">
+  import type { DockerHubResponse } from '$src/Types';
+
+  export let info: DockerHubResponse;
+
+  const formatBigNumber = (num: number): string => {
+    if (!num) return '';
+    const units = ['k', 'M', 'B'];
+    let unitIndex = 0;
+    let value = num;
+    while (value >= 1000 && unitIndex < units.length) {
+      value /= 1000;
+      unitIndex++;
+    }
+    const decimalPlaces = num < 10000 || (num >= 100000 && num < 1000000) ? 0 : 1;
+    return num < 1000 ? num.toString() : value.toFixed(decimalPlaces) + units[unitIndex - 1];
+  };
+
+  const formatDate = (dateTime: string): string => {
+    if (!dateTime) return '';
+    const date = new Date(dateTime);
+    return new Intl.DateTimeFormat('en-US', {
+      year: 'numeric',
+      month: 'short',
+      day: '2-digit',
+    }).format(date);
+  };
+
+  const timeAgo = (dateTime: string): string => {
+    if (!dateTime) return '';
+    const elapsed = Date.now() - new Date(dateTime).getTime();
+    const msPer = [60000, 3600000, 86400000, 2592000000, 31536000000];
+    const units = ['minute', 'hour', 'day', 'month', 'year'];
+
+    for (let i = 0; i < msPer.length; i++) {
+      if (elapsed < msPer[i]) {
+        const value = Math.floor(elapsed / (i > 0 ? msPer[i - 1] : 1));
+        return value === 0 ? 'just now' : `${value} ${units[i - 1] || 'minute'}${value > 1 ? 's' : ''} ago`;
+      }
+    }
+    return `${Math.floor(elapsed / msPer[4])} years ago`;
+  };
+
+  const makeRenderData = () => {
+    const results = [
+      { label: 'Pulls', value: formatBigNumber(info.pull_count) },
+      { label: 'Stars', value: formatBigNumber(info.star_count) },
+      { label: 'User', value: info.hub_user },
+      { label: 'Created', value: formatDate(info.date_registered) },
+      { label: 'Updated', value: timeAgo(info.last_updated) },
+      { label: 'Status', value: info.status_description }
+    ];
+    return results;
+  };
+</script>
+
+<div class="stats">
+  {#each makeRenderData() as stat}
+    <div class="row">
+      <span class="lbl">{stat.label}: </span>
+      <span>{stat.value}</span>
+    </div>
+  {/each}
+</div>
+
+<style lang="scss">
+  .stats {
+    background: var(--card-2);
+    padding: 1rem;
+    border-radius: 6px;
+    .lbl {
+      font-weight: 500;
+      margin-right: 0.5rem;
+    }
+  }
+</style>

+ 85 - 0
src/lib/MdContent.svelte

@@ -0,0 +1,85 @@
+<script lang="ts">
+import { slide } from 'svelte/transition';
+import snarkdown from 'snarkdown';
+export let content: string;
+export let multiContent: { name: string, content: string, description: string, visible: false }[];
+
+let showDocs = false;
+
+const toggleDocs = () => {
+  showDocs = !showDocs;
+};
+
+</script>
+
+<section class="docker-docs">
+  <h2>Container Documentation</h2>
+  {#if content}
+    <button on:click={toggleDocs}>{ showDocs ? 'Hide' : 'Expand' } Content</button>
+    {#if showDocs}
+      <p transition:slide>{@html snarkdown(content)}</p>
+    {/if}
+
+  {:else if multiContent && multiContent.length > 0}
+    {#each multiContent as { name, description, content, visible }}
+      <h3>{name} Documentation</h3>
+      <p class="desc">{description || ''}</p>
+      <button on:click={() => visible = !visible}>{ visible ? 'Hide' : 'Expand' } {name}</button>
+      {#if visible}
+        <p transition:slide>{@html snarkdown(content)}</p>
+      {/if}
+    {/each}
+  {/if}
+</section>
+
+<style lang="scss">
+  .docker-docs {
+    background: var(--card);
+    padding: 1rem;
+    border-radius: 6px;
+    margin: 1rem auto;
+    max-width: 1000px;
+    transition: all 0.2s ease-in-out;
+    button {
+      background: var(--background);
+      padding: 0.25rem 0.5rem;
+      border-radius: 6px;
+      border: none;
+      color: var(--foreground);
+      font-family: Kanit;
+      font-size: 1.2rem;
+      cursor: pointer;
+      transition: all 0.2s ease-in-out;
+      &:hover {
+        background: var(--gradient);
+        transform: scale(1.1) rotate(-1deg);
+      }
+    }
+    h2 {
+      font-size: 2rem;
+      margin: 0;
+    }
+    h3 {
+      margin: 0.5rem 0;
+      text-transform: capitalize;
+    }
+    .desc {
+      opacity: 0.7;
+      margin: 0.5rem 0;
+      font-style: italic;
+    }
+    :global(img) {
+      max-width: 100%;
+    }
+    :global(a) {
+      color: var(--accent);
+      text-decoration: none;
+    }
+    :global(pre) {
+      background: var(--card-2);
+      padding: 1rem;
+      border-radius: 6px;
+      overflow: auto;
+    }
+  }
+</style>