From ff7199fbbb1bd6f380f666a12865204d6f7eed68 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sun, 30 Nov 2025 02:31:58 -0800 Subject: [PATCH] fix: add is-active state to weapon and summon units --- src/lib/components/units/SummonUnit.svelte | 27 +++++++++++++++++++++ src/lib/components/units/WeaponUnit.svelte | 28 ++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/lib/components/units/SummonUnit.svelte b/src/lib/components/units/SummonUnit.svelte index 846d5700..884844c9 100644 --- a/src/lib/components/units/SummonUnit.svelte +++ b/src/lib/components/units/SummonUnit.svelte @@ -9,6 +9,7 @@ import { getSummonImage } from '$lib/features/database/detail/image' import { openDetailsSidebar } from '$lib/features/details/openDetailsSidebar.svelte' import { sidebar } from '$lib/stores/sidebar.svelte' + import { GridType } from '$lib/types/enums' import * as m from '$lib/paraglide/messages' interface Props { @@ -23,6 +24,8 @@ updateParty: (p: Party) => void canEdit: () => boolean getEditKey: () => string | null + getSelectedSlot?: () => number + getActiveTab?: () => GridType services: { gridService: any; partyService: any } openPicker?: (opts: { type: 'weapon' | 'summon' | 'character'; position: number; item?: any }) => void } @@ -48,6 +51,13 @@ // Check if this item is currently active in the sidebar let isActive = $derived(item?.id && sidebar.activeItemId === String(item.id)) + // Check if this empty slot is currently selected for adding an item + let isEmptySelected = $derived( + !item && + ctx?.getSelectedSlot?.() === position && + ctx?.getActiveTab?.() === GridType.Summon + ) + // Determine element class for focus ring let elementClass = $derived.by(() => { const element = item?.summon?.element @@ -106,6 +116,7 @@ class:friend={item?.friend || position === 6} class:cell={!((item?.main || position === -1) || (item?.friend || position === 6))} class:editable={ctx?.canEdit()} + class:is-active={isActive} onclick={() => viewDetails()} > ctx?.canEdit() && ctx?.openPicker && ctx.openPicker({ type: 'summon', position, item })} > void canEdit: () => boolean getEditKey: () => string | null + getSelectedSlot?: () => number + getActiveTab?: () => GridType services: { gridService: any; partyService: any } openPicker?: (opts: { type: 'weapon' | 'summon' | 'character'; position: number; item?: any }) => void } @@ -69,6 +72,13 @@ // Check if this item is currently active in the sidebar let isActive = $derived(item?.id && sidebar.activeItemId === String(item.id)) + // Check if this empty slot is currently selected for adding an item + let isEmptySelected = $derived( + !item && + ctx?.getSelectedSlot?.() === position && + ctx?.getActiveTab?.() === GridType.Weapon + ) + // Determine element class for focus ring let elementClass = $derived.by(() => { // For weapons with null element that have an instance element, use it @@ -148,6 +158,7 @@ class:cell={!(item?.mainhand || position === -1)} class:extra={position >= 9} class:editable={ctx?.canEdit()} + class:is-active={isActive} onclick={() => viewDetails()} >
@@ -212,6 +223,7 @@ class:cell={position !== -1} class:extra={position >= 9} class:editable={ctx?.canEdit()} + class:is-selected={isEmptySelected} onclick={() => ctx?.canEdit() && ctx?.openPicker && ctx.openPicker({ type: 'weapon', position, item })} > @@ -360,6 +372,22 @@ opacity: 0.95; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } + + // Slot selection - subtle dark pulsing glow (works for both empty and filled) + &.is-selected, + &.is-active { + animation: pulse-slot-shadow 2s ease-in-out infinite; + } + } + + @keyframes pulse-slot-shadow { + 0%, + 100% { + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.12), 0 0 4px 2px rgba(0, 0, 0, 0.06); + } + 50% { + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.24), 0 0 8px 4px rgba(0, 0, 0, 0.12); + } } .frame.weapon.main {