1 changed files with 189 additions and 0 deletions
@ -0,0 +1,189 @@
@@ -0,0 +1,189 @@
|
||||
<script lang="ts"> |
||||
import { visualizationConfig } from '$lib/stores/visualizationConfig'; |
||||
import { Button, Badge } from 'flowbite-svelte'; |
||||
import { CloseCircleOutline } from 'flowbite-svelte-icons'; |
||||
|
||||
let { |
||||
onReload = () => {} |
||||
} = $props<{ |
||||
onReload?: () => void; |
||||
}>(); |
||||
|
||||
let newKind = $state(''); |
||||
let showAddInput = $state(false); |
||||
let inputError = $state(''); |
||||
|
||||
function validateKind(value: string): number | null { |
||||
const kind = parseInt(value.trim()); |
||||
if (isNaN(kind)) { |
||||
inputError = 'Must be a number'; |
||||
return null; |
||||
} |
||||
if (kind < 0) { |
||||
inputError = 'Must be positive'; |
||||
return null; |
||||
} |
||||
if ($visualizationConfig.allowedKinds.includes(kind)) { |
||||
inputError = 'Already added'; |
||||
return null; |
||||
} |
||||
inputError = ''; |
||||
return kind; |
||||
} |
||||
|
||||
function handleAddKind() { |
||||
const kind = validateKind(newKind); |
||||
if (kind !== null) { |
||||
visualizationConfig.addKind(kind); |
||||
newKind = ''; |
||||
showAddInput = false; |
||||
inputError = ''; |
||||
} |
||||
} |
||||
|
||||
function handleKeydown(e: KeyboardEvent) { |
||||
if (e.key === 'Enter') { |
||||
handleAddKind(); |
||||
} else if (e.key === 'Escape') { |
||||
showAddInput = false; |
||||
newKind = ''; |
||||
inputError = ''; |
||||
} |
||||
} |
||||
|
||||
function removeKind(kind: number) { |
||||
visualizationConfig.removeKind(kind); |
||||
} |
||||
|
||||
function toggleKind(kind: number) { |
||||
visualizationConfig.toggleKind(kind); |
||||
} |
||||
|
||||
// Get kind name for display |
||||
function getKindName(kind: number): string { |
||||
switch (kind) { |
||||
case 30040: return 'Publication Index'; |
||||
case 30041: return 'Publication Content'; |
||||
case 30818: return 'Wiki'; |
||||
case 1: return 'Text Note'; |
||||
case 0: return 'Metadata'; |
||||
default: return `Kind ${kind}`; |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<div class="space-y-3"> |
||||
<div class="flex flex-wrap gap-2 items-center"> |
||||
{#each $visualizationConfig.allowedKinds as kind} |
||||
{@const isDisabled = $visualizationConfig.disabledKinds.includes(kind)} |
||||
<button |
||||
class="badge-container {isDisabled ? 'disabled' : ''}" |
||||
onclick={() => toggleKind(kind)} |
||||
title={isDisabled ? `Click to enable ${getKindName(kind)}` : `Click to disable ${getKindName(kind)}`} |
||||
> |
||||
<Badge |
||||
color="dark" |
||||
class="flex items-center gap-1 px-2 py-1 {isDisabled ? 'opacity-40' : ''}" |
||||
> |
||||
<span class="text-xs">{kind}</span> |
||||
<button |
||||
onclick={(e) => { |
||||
e.stopPropagation(); |
||||
removeKind(kind); |
||||
}} |
||||
class="ml-1 text-red-500 hover:text-red-700 transition-colors" |
||||
title="Remove {getKindName(kind)}" |
||||
> |
||||
<CloseCircleOutline class="w-3 h-3" /> |
||||
</button> |
||||
</Badge> |
||||
</button> |
||||
{/each} |
||||
|
||||
{#if showAddInput} |
||||
<div class="flex items-center gap-1"> |
||||
<div class="relative"> |
||||
<input |
||||
bind:value={newKind} |
||||
type="number" |
||||
placeholder="Kind" |
||||
class="w-20 px-2 py-1 text-xs border rounded dark:bg-gray-700 dark:border-gray-600" |
||||
onkeydown={handleKeydown} |
||||
oninput={() => validateKind(newKind)} |
||||
/> |
||||
{#if inputError} |
||||
<div class="absolute top-full left-0 mt-1 text-xs text-red-500 whitespace-nowrap"> |
||||
{inputError} |
||||
</div> |
||||
{/if} |
||||
</div> |
||||
<Button size="xs" onclick={handleAddKind} disabled={!!inputError}> |
||||
Add |
||||
</Button> |
||||
<Button |
||||
size="xs" |
||||
color="light" |
||||
onclick={() => { |
||||
showAddInput = false; |
||||
newKind = ''; |
||||
inputError = ''; |
||||
}} |
||||
> |
||||
× |
||||
</Button> |
||||
</div> |
||||
{:else} |
||||
<Button |
||||
size="xs" |
||||
color="light" |
||||
onclick={() => showAddInput = true} |
||||
class="gap-1" |
||||
> |
||||
<span>+</span> |
||||
<span>Add Kind</span> |
||||
</Button> |
||||
{/if} |
||||
|
||||
<Button |
||||
size="xs" |
||||
color="blue" |
||||
onclick={onReload} |
||||
class="gap-1" |
||||
title="Reload graph with current event type filters" |
||||
> |
||||
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> |
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path> |
||||
</svg> |
||||
<span>Reload</span> |
||||
</Button> |
||||
</div> |
||||
|
||||
<label class="flex items-center gap-2 text-xs"> |
||||
<input |
||||
type="checkbox" |
||||
checked={$visualizationConfig.allowFreeEvents} |
||||
onchange={() => visualizationConfig.toggleFreeEvents()} |
||||
class="rounded dark:bg-gray-700 dark:border-gray-600" |
||||
/> |
||||
<span>Allow free events <span class="text-orange-500 font-normal">(not tested)</span></span> |
||||
<span class="text-gray-500 dark:text-gray-400">(not connected to 30040)</span> |
||||
</label> |
||||
</div> |
||||
|
||||
<style> |
||||
.badge-container { |
||||
background: none; |
||||
border: none; |
||||
padding: 0; |
||||
cursor: pointer; |
||||
transition: transform 0.2s ease; |
||||
} |
||||
|
||||
.badge-container:hover:not(.disabled) { |
||||
transform: scale(1.05); |
||||
} |
||||
|
||||
.badge-container.disabled { |
||||
cursor: pointer; |
||||
} |
||||
</style> |
||||
Loading…
Reference in new issue