aboutsummaryrefslogtreecommitdiff
path: root/source/library/components/HistoryPanel.svelte
diff options
context:
space:
mode:
Diffstat (limited to 'source/library/components/HistoryPanel.svelte')
-rw-r--r--source/library/components/HistoryPanel.svelte225
1 files changed, 122 insertions, 103 deletions
diff --git a/source/library/components/HistoryPanel.svelte b/source/library/components/HistoryPanel.svelte
index 01f397a..e224e1e 100644
--- a/source/library/components/HistoryPanel.svelte
+++ b/source/library/components/HistoryPanel.svelte
@@ -1,4 +1,5 @@
<script lang="ts">
+ /*** UTILITY ------------------------------------------ ***/
import type { HistoryEntry } from "../state/history.svelte.ts";
type Props = {
@@ -34,6 +35,7 @@
return b.timestamp - a.timestamp;
}));
+ /*** HELPER ------------------------------------------- ***/
function formatTimestamp(ms: number): string {
const d = new Date(ms);
return d.toLocaleString();
@@ -50,10 +52,6 @@
}
}
- function onImportClick() {
- fileInput?.click();
- }
-
function onFileChange(event: Event) {
const input = event.currentTarget as HTMLInputElement;
const file = input.files?.[0];
@@ -63,22 +61,29 @@
input.value = "";
}
+
+ function onImportClick() {
+ fileInput?.click();
+ }
</script>
<style lang="scss">
.panel {
- background: var(--graphiql-panel, #252526);
- border-right: 1px solid var(--graphiql-border, #333);
+ background-color: var(--uchu-yin-9);
+ color: var(--uchu-yin-1);
display: grid;
+ font-size: 0.8rem;
grid-template-rows: auto 1fr;
- height: 100%;
min-height: 0;
+ height: 100%;
overflow: hidden;
+ z-index: 2;
}
- .header {
+ .header,
+ .notice {
align-items: center;
- border-bottom: 1px solid var(--graphiql-border, #333);
+ border-bottom: 1px solid var(--uchu-yin-8);
display: flex;
gap: 0.5rem;
justify-content: space-between;
@@ -86,26 +91,32 @@
}
.title {
- font-size: 0.8125rem;
font-weight: 600;
}
.actions {
display: flex;
gap: 0.5rem;
+
+ .action {
+ background: none;
+ border: none;
+ cursor: pointer;
+ font-family: inherit;
+ font-size: 0.75rem;
+ padding: 0;
+ }
}
- .action {
- background: none;
- border: none;
- color: var(--graphiql-muted, #858585);
- cursor: pointer;
- font-family: inherit;
- font-size: 0.75rem;
- padding: 0;
+ .action,
+ .notice-dismiss,
+ .remove {
+ &:not(:hover) {
+ color: var(--uchu-yin-5);
+ }
&:hover {
- color: var(--graphiql-fg, #d4d4d4);
+ color: var(--uchu-yin-1);
}
}
@@ -113,38 +124,20 @@
display: none;
}
- .notice {
- align-items: center;
- background: var(--graphiql-bg, #1e1e1e);
- border-bottom: 1px solid var(--graphiql-border, #333);
- color: var(--graphiql-fg, #d4d4d4);
- display: flex;
- font-size: 0.75rem;
- gap: 0.5rem;
- justify-content: space-between;
- padding: 0.375rem 0.75rem;
- }
-
- .notice-dismiss {
+ .notice-dismiss,
+ .remove {
background: none;
border: none;
- color: var(--graphiql-muted, #858585);
cursor: pointer;
- font-size: 0.875rem;
+ font-size: 1rem;
line-height: 1;
padding: 0 0.25rem;
-
- &:hover {
- color: var(--graphiql-fg, #d4d4d4);
- }
}
.list {
display: grid;
- gap: 0.125rem;
min-height: 0;
overflow-y: auto;
- padding: 0.375rem 0;
}
.entry {
@@ -154,22 +147,56 @@
gap: 0.125rem;
grid-template-columns: auto 1fr auto;
padding: 0.375rem 0.75rem;
+ transition: opacity 0.1s;
+
+ &:not(:last-of-type) {
+ border-bottom: 1px solid var(--uchu-yin);
+ }
&:hover {
- background: var(--graphiql-bg, #1e1e1e);
+ background-color: var(--uchu-yin);
+ }
+
+ .list:has(.entry:hover) &:not(:hover) {
+ opacity: 0.4;
+ }
+
+ &-title {
+ font-size: 0.8rem;
+ margin-bottom: 0.1rem;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ &-time {
+ color: var(--uchu-yin-5);
+ font-size: 0.7rem;
}
}
.star {
+ align-self: center;
background: none;
border: none;
- color: var(--graphiql-muted, #858585);
cursor: pointer;
- font-size: 0.875rem;
+ font-size: 0.8rem;
padding: 0 0.375rem 0 0;
+ &:not(.active) {
+ color: var(--uchu-yin-5);
+ }
+
&.active {
- color: var(--graphiql-accent, #e3b341);
+ color: var(--uchu-orange-4);
+
+ svg {
+ fill: currentColor;
+ }
+ }
+
+ svg {
+ width: 1rem;
}
}
@@ -178,75 +205,55 @@
min-width: 0;
}
- .entry-title {
- font-size: 0.8125rem;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
-
- .entry-time {
- color: var(--graphiql-muted, #858585);
- font-size: 0.7rem;
- }
-
.remove {
align-self: center;
- background: none;
- border: none;
- color: var(--graphiql-muted, #858585);
- cursor: pointer;
- font-size: 1rem;
- line-height: 1;
- padding: 0 0.25rem;
-
- &:hover {
- color: var(--graphiql-fg, #d4d4d4);
- }
}
.empty {
- color: var(--graphiql-muted, #858585);
- font-size: 0.8125rem;
+ color: var(--uchu-yin-5);
+ font-size: 0.8rem;
padding: 0.75rem;
}
</style>
<div class="panel">
- <div>
- <div class="header">
- <span class="title">History</span>
- <div class="actions">
- {#if onExport}
- <button class="action" onclick={onExport} type="button">Export</button>
- {/if}
- {#if onImport}
- <button class="action" onclick={onImportClick} type="button">Import</button>
- <input
- accept="application/json"
- bind:this={fileInput}
- class="hidden-input"
- onchange={onFileChange}
- type="file"/>
- {/if}
- {#if entries.length > 0}
- <button class="action" onclick={onClear} type="button">Clear</button>
- {/if}
- </div>
+ <div class="header">
+ <span class="title">History</span>
+
+ <div class="actions">
+ {#if onExport}
+ <button class="action" onclick={onExport} type="button">Export</button>
+ {/if}
+
+ {#if onImport}
+ <button class="action" onclick={onImportClick} type="button">Import</button>
+ <input
+ accept="application/json"
+ bind:this={fileInput}
+ class="hidden-input"
+ onchange={onFileChange}
+ type="file"/>
+ {/if}
+
+ {#if entries.length > 0}
+ <button class="action" onclick={onClear} type="button">Clear</button>
+ {/if}
</div>
- {#if notice}
- <div class="notice">
- <span>{notice}</span>
- {#if onDismissNotice}
- <button
- aria-label="Dismiss notice"
- class="notice-dismiss"
- onclick={onDismissNotice}
- type="button">×</button>
- {/if}
- </div>
- {/if}
</div>
+
+ {#if notice}
+ <div class="notice">
+ <span>{notice}</span>
+ {#if onDismissNotice}
+ <button
+ aria-label="Dismiss notice"
+ class="notice-dismiss"
+ onclick={onDismissNotice}
+ type="button">&times;</button>
+ {/if}
+ </div>
+ {/if}
+
<div class="list">
{#if sorted.length === 0}
<div class="empty">No history yet.</div>
@@ -264,16 +271,28 @@
class="star"
class:active={entry.favorite}
onclick={(e) => { e.stopPropagation(); onFavorite(entry.id); }}
- type="button">{entry.favorite ? "★" : "☆"}</button>
+ type="button">
+ {#if entry.favorite}
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <path d="M12 2L14.9389 7.95492L21.5106 8.90983L16.7553 13.5451L17.8779 20.0902L12 17L6.12215 20.0902L7.24472 13.5451L2.48944 8.90983L9.06108 7.95492L12 2Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
+ </svg>
+ {:else}
+ <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
+ <path d="M12 2L14.9389 7.95492L21.5106 8.90983L16.7553 13.5451L17.8779 20.0902L12 17L6.12215 20.0902L7.24472 13.5451L2.48944 8.90983L9.06108 7.95492L12 2Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
+ </svg>
+ {/if}
+ </button>
+
<div class="meta">
<div class="entry-title">{entry.title}</div>
<div class="entry-time">{formatTimestamp(entry.timestamp)}</div>
</div>
+
<button
aria-label="Remove entry"
class="remove"
onclick={(e) => { e.stopPropagation(); onRemove(entry.id); }}
- type="button">×</button>
+ type="button">&times;</button>
</div>
{/each}
{/if}