sidebar: wire up edit sidebars in openDetailsSidebar feature
This commit is contained in:
parent
8ac9dea2d3
commit
47885b1429
2 changed files with 193 additions and 3 deletions
|
|
@ -1,12 +1,43 @@
|
|||
import { sidebar } from '$lib/stores/sidebar.svelte'
|
||||
import { partyStore } from '$lib/stores/partyStore.svelte'
|
||||
import DetailsSidebar from '$lib/components/sidebar/DetailsSidebar.svelte'
|
||||
import EditWeaponSidebar from '$lib/components/sidebar/EditWeaponSidebar.svelte'
|
||||
import EditCharacterSidebar from '$lib/components/sidebar/EditCharacterSidebar.svelte'
|
||||
import type { GridCharacter, GridWeapon, GridSummon } from '$lib/types/api/party'
|
||||
import { canWeaponBeModified, canCharacterBeModified } from '$lib/utils/modificationDetector'
|
||||
|
||||
interface DetailsSidebarOptions {
|
||||
type: 'weapon' | 'character' | 'summon'
|
||||
item: GridCharacter | GridWeapon | GridSummon
|
||||
}
|
||||
|
||||
type ElementName = 'wind' | 'fire' | 'water' | 'earth' | 'dark' | 'light'
|
||||
|
||||
const ELEMENT_MAP: Record<number, ElementName> = {
|
||||
1: 'wind',
|
||||
2: 'fire',
|
||||
3: 'water',
|
||||
4: 'earth',
|
||||
5: 'dark',
|
||||
6: 'light'
|
||||
}
|
||||
|
||||
function getItemElement(type: 'weapon' | 'character' | 'summon', item: GridCharacter | GridWeapon | GridSummon): ElementName | undefined {
|
||||
let elementId: number | undefined
|
||||
|
||||
if (type === 'character') {
|
||||
elementId = (item as GridCharacter).character?.element
|
||||
} else if (type === 'weapon') {
|
||||
const weapon = item as GridWeapon
|
||||
// Use grid weapon element if set, otherwise canonical weapon element
|
||||
elementId = weapon.element || weapon.weapon?.element
|
||||
} else if (type === 'summon') {
|
||||
elementId = (item as GridSummon).summon?.element
|
||||
}
|
||||
|
||||
return elementId ? ELEMENT_MAP[elementId] : undefined
|
||||
}
|
||||
|
||||
export function openDetailsSidebar(options: DetailsSidebarOptions) {
|
||||
const { type, item } = options
|
||||
|
||||
|
|
@ -23,11 +54,126 @@ export function openDetailsSidebar(options: DetailsSidebarOptions) {
|
|||
itemName = getName(summon)
|
||||
}
|
||||
|
||||
// Check if this item can be edited
|
||||
const canEditWeapon = type === 'weapon' && canWeaponBeModified(item as GridWeapon)
|
||||
const canEditCharacter = type === 'character' && canCharacterBeModified(item as GridCharacter)
|
||||
const canEdit = canEditWeapon || canEditCharacter
|
||||
|
||||
// Create edit handler for editable items
|
||||
const onsave = canEdit
|
||||
? () => {
|
||||
if (canEditWeapon) {
|
||||
openWeaponEditSidebar(item as GridWeapon)
|
||||
} else if (canEditCharacter) {
|
||||
openCharacterEditSidebar(item as GridCharacter)
|
||||
}
|
||||
}
|
||||
: undefined
|
||||
|
||||
// Get the element for styling
|
||||
const element = getItemElement(type, item)
|
||||
|
||||
// Open the sidebar with the details component
|
||||
const title = itemName !== 'Details' ? itemName : `${type.charAt(0).toUpperCase() + type.slice(1)} Details`
|
||||
sidebar.openWithComponent(title, DetailsSidebar, {
|
||||
type,
|
||||
item
|
||||
}, {
|
||||
onsave,
|
||||
saveLabel: 'Edit',
|
||||
element
|
||||
})
|
||||
}
|
||||
|
||||
export function openWeaponEditSidebar(weapon: GridWeapon) {
|
||||
const weaponName = getName(weapon.weapon)
|
||||
const title = weaponName !== 'Details' ? weaponName : 'Edit Weapon'
|
||||
|
||||
// Get element for styling
|
||||
const element = getItemElement('weapon', weapon)
|
||||
|
||||
// Keep track of the current weapon state for going back to details
|
||||
let currentWeapon = weapon
|
||||
|
||||
// Handler to go back to details view
|
||||
const goBackToDetails = () => {
|
||||
// Get the updated weapon from the store if available
|
||||
const updated = partyStore.getWeapon(weapon.id)
|
||||
openDetailsSidebar({ type: 'weapon', item: updated ?? currentWeapon })
|
||||
}
|
||||
|
||||
// Handler for save button - saves updates via partyStore
|
||||
const handleSave = async (updates: Partial<GridWeapon>) => {
|
||||
if (!weapon.id) {
|
||||
console.error('Cannot save weapon without ID')
|
||||
goBackToDetails()
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const updated = await partyStore.updateWeapon(String(weapon.id), updates)
|
||||
currentWeapon = updated
|
||||
goBackToDetails()
|
||||
} catch (error) {
|
||||
console.error('Failed to save weapon:', error)
|
||||
// Still go back on error - the optimistic update will be visible
|
||||
goBackToDetails()
|
||||
}
|
||||
}
|
||||
|
||||
sidebar.openWithComponent(title, EditWeaponSidebar, {
|
||||
weapon,
|
||||
onSave: handleSave,
|
||||
onCancel: goBackToDetails
|
||||
}, {
|
||||
element,
|
||||
onback: goBackToDetails
|
||||
})
|
||||
}
|
||||
|
||||
export function openCharacterEditSidebar(character: GridCharacter) {
|
||||
const characterName = getName(character.character)
|
||||
const title = characterName !== 'Details' ? characterName : 'Edit Character'
|
||||
|
||||
// Get element for styling
|
||||
const element = getItemElement('character', character)
|
||||
|
||||
// Keep track of the current character state for going back to details
|
||||
let currentCharacter = character
|
||||
|
||||
// Handler to go back to details view
|
||||
const goBackToDetails = () => {
|
||||
// Get the updated character from the store if available
|
||||
const updated = partyStore.getCharacter(character.id)
|
||||
openDetailsSidebar({ type: 'character', item: updated ?? currentCharacter })
|
||||
}
|
||||
|
||||
// Handler for save button - saves updates via partyStore
|
||||
const handleSave = async (updates: Partial<GridCharacter>) => {
|
||||
if (!character.id) {
|
||||
console.error('Cannot save character without ID')
|
||||
goBackToDetails()
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const updated = await partyStore.updateCharacter(String(character.id), updates)
|
||||
currentCharacter = updated
|
||||
goBackToDetails()
|
||||
} catch (error) {
|
||||
console.error('Failed to save character:', error)
|
||||
// Still go back on error - the optimistic update will be visible
|
||||
goBackToDetails()
|
||||
}
|
||||
}
|
||||
|
||||
sidebar.openWithComponent(title, EditCharacterSidebar, {
|
||||
character,
|
||||
onSave: handleSave,
|
||||
onCancel: goBackToDetails
|
||||
}, {
|
||||
element,
|
||||
onback: goBackToDetails
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,18 @@ interface SidebarState {
|
|||
componentProps: Record<string, any> | undefined
|
||||
scrollable: boolean
|
||||
activeItemId: string | undefined
|
||||
onsave: (() => void) | undefined
|
||||
saveLabel: string | undefined
|
||||
element: 'wind' | 'fire' | 'water' | 'earth' | 'dark' | 'light' | undefined
|
||||
onback: (() => void) | undefined
|
||||
}
|
||||
|
||||
interface OpenWithComponentOptions {
|
||||
scrollable?: boolean
|
||||
onsave?: () => void
|
||||
saveLabel?: string
|
||||
element?: 'wind' | 'fire' | 'water' | 'earth' | 'dark' | 'light'
|
||||
onback?: () => void
|
||||
}
|
||||
|
||||
class SidebarStore {
|
||||
|
|
@ -21,7 +33,11 @@ class SidebarStore {
|
|||
component: undefined,
|
||||
componentProps: undefined,
|
||||
scrollable: true,
|
||||
activeItemId: undefined
|
||||
activeItemId: undefined,
|
||||
onsave: undefined,
|
||||
saveLabel: undefined,
|
||||
element: undefined,
|
||||
onback: undefined
|
||||
})
|
||||
|
||||
open(title?: string, content?: Snippet, scrollable = true) {
|
||||
|
|
@ -37,14 +53,22 @@ class SidebarStore {
|
|||
title: string,
|
||||
component: Component<any, any, any>,
|
||||
props?: Record<string, any>,
|
||||
scrollable = true
|
||||
options?: OpenWithComponentOptions | boolean
|
||||
) {
|
||||
// Handle backward compatibility where 4th param was scrollable boolean
|
||||
const opts: OpenWithComponentOptions =
|
||||
typeof options === 'boolean' ? { scrollable: options } : options ?? {}
|
||||
|
||||
this.state.open = true
|
||||
this.state.title = title
|
||||
this.state.component = component
|
||||
this.state.componentProps = props
|
||||
this.state.content = undefined
|
||||
this.state.scrollable = scrollable
|
||||
this.state.scrollable = opts.scrollable ?? true
|
||||
this.state.onsave = opts.onsave
|
||||
this.state.saveLabel = opts.saveLabel
|
||||
this.state.element = opts.element
|
||||
this.state.onback = opts.onback
|
||||
// Extract and store the item ID if it's a details sidebar
|
||||
if (props?.item?.id) {
|
||||
this.state.activeItemId = String(props.item.id)
|
||||
|
|
@ -60,6 +84,10 @@ class SidebarStore {
|
|||
this.state.content = undefined
|
||||
this.state.component = undefined
|
||||
this.state.componentProps = undefined
|
||||
this.state.onsave = undefined
|
||||
this.state.saveLabel = undefined
|
||||
this.state.element = undefined
|
||||
this.state.onback = undefined
|
||||
}, 300)
|
||||
}
|
||||
|
||||
|
|
@ -98,6 +126,22 @@ class SidebarStore {
|
|||
get activeItemId() {
|
||||
return this.state.activeItemId
|
||||
}
|
||||
|
||||
get onsave() {
|
||||
return this.state.onsave
|
||||
}
|
||||
|
||||
get saveLabel() {
|
||||
return this.state.saveLabel
|
||||
}
|
||||
|
||||
get element() {
|
||||
return this.state.element
|
||||
}
|
||||
|
||||
get onback() {
|
||||
return this.state.onback
|
||||
}
|
||||
}
|
||||
|
||||
export const sidebar = new SidebarStore()
|
||||
|
|
|
|||
Loading…
Reference in a new issue