diff options
| author | netop://ウィビ <paul@webb.page> | 2026-04-24 16:37:33 -0700 |
|---|---|---|
| committer | netop://ウィビ <paul@webb.page> | 2026-04-24 16:37:33 -0700 |
| commit | 510fd8cbe53abb39cba2c7cbaaefcf2783dc0066 (patch) | |
| tree | 8f753a33c475b285f2a297785d34cda3b0a8faed /source/library/components/HistoryPanel.svelte | |
| parent | 261f3bdb77799009344aab4a60686b7186ebd3b0 (diff) | |
| download | graphiql-510fd8cbe53abb39cba2c7cbaaefcf2783dc0066.tar.gz graphiql-510fd8cbe53abb39cba2c7cbaaefcf2783dc0066.zip | |
Implement v0.6-1.0: shortcuts, format, export/import, splitter, timing, APQ
- v0.6: matchShortcut + format(); Cmd+Shift+Enter/W/F + Cmd+Alt+arrows
- v0.7: SessionStore.exportAll/importTabs with version-1 validator
- v0.8: Splitter component + four resize handles persisted under layout.*
- v0.10: createApqFetcher (HTTP-only) wrapping shared http-body helpers
- Drop .svelte re-exports from index.ts for multi-entry JSR/npm publishing
Diffstat (limited to 'source/library/components/HistoryPanel.svelte')
| -rw-r--r-- | source/library/components/HistoryPanel.svelte | 106 |
1 files changed, 100 insertions, 6 deletions
diff --git a/source/library/components/HistoryPanel.svelte b/source/library/components/HistoryPanel.svelte index b7f5c4c..01f397a 100644 --- a/source/library/components/HistoryPanel.svelte +++ b/source/library/components/HistoryPanel.svelte @@ -3,13 +3,29 @@ type Props = { entries: HistoryEntry[]; + notice?: string | null; onClear: () => void; + onDismissNotice?: () => void; + onExport?: () => void; onFavorite: (id: string) => void; + onImport?: (file: File) => void; onLoad: (id: string, inNewTab: boolean) => void; onRemove: (id: string) => void; }; - let { entries, onClear, onFavorite, onLoad, onRemove }: Props = $props(); + let { + entries, + notice = null, + onClear, + onDismissNotice, + onExport, + onFavorite, + onImport, + onLoad, + onRemove + }: Props = $props(); + + let fileInput = $state<HTMLInputElement | null>(null); const sorted = $derived([...entries].sort((a, b) => { if (a.favorite !== b.favorite) @@ -33,6 +49,20 @@ onLoad(id, event.shiftKey); } } + + function onImportClick() { + fileInput?.click(); + } + + function onFileChange(event: Event) { + const input = event.currentTarget as HTMLInputElement; + const file = input.files?.[0]; + + if (file) + onImport?.(file); + + input.value = ""; + } </script> <style lang="scss"> @@ -50,6 +80,7 @@ align-items: center; border-bottom: 1px solid var(--graphiql-border, #333); display: flex; + gap: 0.5rem; justify-content: space-between; padding: 0.5rem 0.75rem; } @@ -59,7 +90,12 @@ font-weight: 600; } - .clear { + .actions { + display: flex; + gap: 0.5rem; + } + + .action { background: none; border: none; color: var(--graphiql-muted, #858585); @@ -73,6 +109,36 @@ } } + .hidden-input { + 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 { + background: none; + border: none; + color: var(--graphiql-muted, #858585); + cursor: pointer; + font-size: 0.875rem; + line-height: 1; + padding: 0 0.25rem; + + &:hover { + color: var(--graphiql-fg, #d4d4d4); + } + } + .list { display: grid; gap: 0.125rem; @@ -147,10 +213,38 @@ </style> <div class="panel"> - <div class="header"> - <span class="title">History</span> - {#if entries.length > 0} - <button class="clear" onclick={onClear} type="button">Clear</button> + <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> + {#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> <div class="list"> |