remove fluid-dnd drag and drop

This commit is contained in:
Justin Edmund 2025-09-15 21:26:01 -07:00
parent e26b5c2e20
commit ef30e57eba
5 changed files with 278 additions and 231 deletions

View file

@ -60,6 +60,7 @@
"dependencies": { "dependencies": {
"@tanstack/svelte-query": "^5.87.1", "@tanstack/svelte-query": "^5.87.1",
"bits-ui": "^2.9.6", "bits-ui": "^2.9.6",
"fluid-dnd": "^2.6.2",
"modern-normalize": "^3.0.1", "modern-normalize": "^3.0.1",
"zod": "^4.1.5" "zod": "^4.1.5"
} }

View file

@ -14,6 +14,9 @@ importers:
bits-ui: bits-ui:
specifier: ^2.9.6 specifier: ^2.9.6
version: 2.9.6(@internationalized/date@3.9.0)(svelte@5.38.7) version: 2.9.6(@internationalized/date@3.9.0)(svelte@5.38.7)
fluid-dnd:
specifier: ^2.6.2
version: 2.6.2
modern-normalize: modern-normalize:
specifier: ^3.0.1 specifier: ^3.0.1
version: 3.0.1 version: 3.0.1
@ -1338,6 +1341,9 @@ packages:
flatted@3.3.3: flatted@3.3.3:
resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
fluid-dnd@2.6.2:
resolution: {integrity: sha512-NB11wa6QmKkelLi/PrV95nfXM0oA3lrynQD91VrLbhk2BEfH7PLTUMjqlSkavu+fqPlPVCL8HFhYsfv9GVqd7Q==}
fsevents@2.3.2: fsevents@2.3.2:
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@ -3332,6 +3338,8 @@ snapshots:
flatted@3.3.3: {} flatted@3.3.3: {}
fluid-dnd@2.6.2: {}
fsevents@2.3.2: fsevents@2.3.2:
optional: true optional: true

View file

@ -1,61 +1,75 @@
<svelte:options runes={true} /> <svelte:options runes={true} />
<script lang="ts"> <script lang="ts">
import type { GridCharacter } from '$lib/types/api/party' import type { GridCharacter } from '$lib/types/api/party'
import { getContext } from 'svelte'
import type { PartyContext } from '$lib/services/party.service'
interface Props { interface Props {
characters?: GridCharacter[] characters?: GridCharacter[]
mainWeaponElement?: number | null | undefined mainWeaponElement?: number | null | undefined
partyElement?: number | null | undefined partyElement?: number | null | undefined
} }
let { let { characters = [], mainWeaponElement = undefined, partyElement = undefined }: Props = $props()
characters = [],
mainWeaponElement = undefined,
partyElement = undefined
}: Props = $props()
import CharacterUnit from '$lib/components/units/CharacterUnit.svelte' import CharacterUnit from '$lib/components/units/CharacterUnit.svelte'
let grid = $derived(Array.from({ length: 5 }, (_, i) => characters.find((c) => c.position === i))) const ctx = getContext<PartyContext>('party')
</script> </script>
<div class="wrapper"> <div class="wrapper">
<ul class="characters" aria-label="Character Grid"> <ul
{#each grid as c, i} class="characters"
<li aria-label={`Character slot ${i}`}> aria-label="Character Grid"
<CharacterUnit item={c} position={i} {mainWeaponElement} {partyElement} /> >
</li> {#each Array(5) as _, i}
{/each} {@const character = characters.find((c) => c.position === i)}
</ul> <li
aria-label={`Character slot ${i}`}
class:main-character={i === 0}
class:Empty={!character}
>
<CharacterUnit item={character} position={i} {mainWeaponElement} {partyElement} />
</li>
{/each}
</ul>
</div> </div>
<style lang="scss"> <style lang="scss">
@use '$src/themes/colors' as *; @use '$src/themes/colors' as *;
@use '$src/themes/typography' as *; @use '$src/themes/typography' as *;
@use '$src/themes/spacing' as *; @use '$src/themes/spacing' as *;
.characters { .characters {
display: grid; display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr)); grid-template-columns: repeat(5, minmax(0, 1fr));
gap: $unit-3x; gap: $unit-3x;
& > li { list-style: none; } & > li {
} list-style: none;
}
}
.unit { .unit {
width: 100%; width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
gap: $unit; gap: $unit;
} }
.image { width: 100%; height: auto; border: 1px solid $grey-75; border-radius: 8px; display: block; } .image {
width: 100%;
height: auto;
border: 1px solid $grey-75;
border-radius: 8px;
display: block;
}
.name { .name {
font-size: $font-small; font-size: $font-small;
text-align: center; text-align: center;
color: $grey-50; color: $grey-50;
} }
</style> </style>

View file

@ -1,120 +1,133 @@
<svelte:options runes={true} /> <svelte:options runes={true} />
<script lang="ts"> <script lang="ts">
import type { GridSummon } from '$lib/types/api/party' import type { GridSummon } from '$lib/types/api/party'
import { getContext } from 'svelte'
import type { PartyContext } from '$lib/services/party.service'
interface Props { interface Props {
summons?: GridSummon[] summons?: GridSummon[]
} }
let { summons = [] }: Props = $props() let { summons = [] }: Props = $props()
import SummonUnit from '$lib/components/units/SummonUnit.svelte' import SummonUnit from '$lib/components/units/SummonUnit.svelte'
import ExtraSummons from '$lib/components/extra/ExtraSummonsGrid.svelte' import ExtraSummons from '$lib/components/extra/ExtraSummonsGrid.svelte'
let main = $derived(summons.find((s) => s.main || s.position === -1)) const ctx = getContext<PartyContext>('party')
let friend = $derived(summons.find((s) => s.friend || s.position === 6))
let grid = $derived(Array.from({ length: 4 }, (_, i) => summons.find((s) => s.position === i))) let main = $derived(summons.find((s) => s.main || s.position === -1))
let friend = $derived(summons.find((s) => s.friend || s.position === 6))
</script> </script>
<div class="wrapper"> <div class="wrapper">
<div class="grid"> <div class="grid">
<div class="LabeledUnit"> <div class="LabeledUnit">
<div class="label">Main</div> <div class="label">Main</div>
<SummonUnit item={main} position={-1} /> <SummonUnit item={main} position={-1} />
</div> </div>
<section> <section>
<div class="label">Summons</div> <div class="label">Summons</div>
<ul class="summons"> <ul class="summons">
{#each grid as s, i} {#each Array(4) as _, i}
<li aria-label={`Summon slot ${i}`}> {@const summon = summons.find((s) => s.position === i)}
<SummonUnit item={s} position={i} /> <li
</li> aria-label={`Summon slot ${i}`}
{/each} class:Empty={!summon}
</ul> >
</section> <SummonUnit item={summon} position={i} />
</li>
{/each}
</ul>
</section>
<div class="LabeledUnit"> <div class="LabeledUnit">
<div class="label friend">Friend</div> <div class="label friend">Friend</div>
<SummonUnit item={friend} position={6} /> <SummonUnit item={friend} position={6} />
</div> </div>
</div> </div>
<ExtraSummons summons={summons} offset={4} /> <ExtraSummons {summons} offset={4} />
</div> </div>
<style lang="scss"> <style lang="scss">
@use '$src/themes/colors' as *; @use '$src/themes/colors' as *;
@use '$src/themes/typography' as *; @use '$src/themes/typography' as *;
@use '$src/themes/spacing' as *; @use '$src/themes/spacing' as *;
@use '$src/themes/mixins' as *; @use '$src/themes/mixins' as *;
.grid { .grid {
display: grid; display: grid;
grid-template-columns: 1.17fr 2fr 1.17fr; grid-template-columns: 1.17fr 2fr 1.17fr;
gap: $unit-3x; gap: $unit-3x;
justify-content: center; justify-content: center;
margin: 0 auto; margin: 0 auto;
max-width: $grid-width; max-width: $grid-width;
@include breakpoint(tablet) { @include breakpoint(tablet) {
gap: $unit-2x; gap: $unit-2x;
} }
@include breakpoint(phone) { @include breakpoint(phone) {
gap: $unit; gap: $unit;
} }
& .label { & .label {
color: $grey-55; color: $grey-55;
font-size: $font-tiny; font-size: $font-tiny;
font-weight: $medium; font-weight: $medium;
margin-bottom: $unit; margin-bottom: $unit;
text-align: center; text-align: center;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
@include breakpoint(phone) { @include breakpoint(phone) {
&.friend { &.friend {
max-width: 78px; max-width: 78px;
} }
} }
} }
.summons { .summons {
display: grid; display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
grid-template-rows: repeat(2, minmax(0, 1fr)); grid-template-rows: repeat(2, minmax(0, 1fr));
gap: $unit-3x; gap: $unit-3x;
@include breakpoint(tablet) { @include breakpoint(tablet) {
gap: $unit-2x; gap: $unit-2x;
} }
@include breakpoint(phone) { @include breakpoint(phone) {
gap: $unit; gap: $unit;
} }
& > li { & > li {
list-style: none; list-style: none;
} }
} }
} }
.unit { .unit {
width: 100%; width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
gap: $unit; gap: $unit;
} }
.image { width: 100%; height: auto; border: 1px solid $grey-75; border-radius: 8px; display: block; } .image {
width: 100%;
height: auto;
border: 1px solid $grey-75;
border-radius: 8px;
display: block;
}
.name { .name {
font-size: $font-small; font-size: $font-small;
text-align: center; text-align: center;
color: $grey-50; color: $grey-50;
} }
</style> </style>

View file

@ -1,124 +1,135 @@
<svelte:options runes={true} /> <svelte:options runes={true} />
<script lang="ts"> <script lang="ts">
import type { GridWeapon } from '$lib/types/api/party' import type { GridWeapon } from '$lib/types/api/party'
import { getContext } from 'svelte'
import type { PartyContext } from '$lib/services/party.service'
interface Props { interface Props {
weapons?: GridWeapon[] weapons?: GridWeapon[]
raidExtra?: boolean raidExtra?: boolean
showGuidebooks?: boolean showGuidebooks?: boolean
guidebooks?: Record<string, any> guidebooks?: Record<string, any>
} }
let { let {
weapons = [], weapons = [],
raidExtra = undefined, raidExtra = undefined,
showGuidebooks = undefined, showGuidebooks = undefined,
guidebooks = undefined guidebooks = undefined
}: Props = $props() }: Props = $props()
import WeaponUnit from '$lib/components/units/WeaponUnit.svelte' import WeaponUnit from '$lib/components/units/WeaponUnit.svelte'
import ExtraWeapons from '$lib/components/extra/ExtraWeaponsGrid.svelte' import ExtraWeapons from '$lib/components/extra/ExtraWeaponsGrid.svelte'
import Guidebooks from '$lib/components/extra/GuidebooksGrid.svelte' import Guidebooks from '$lib/components/extra/GuidebooksGrid.svelte'
let mainhand = $derived(weapons.find((w) => (w as any).mainhand || w.position === -1)) const ctx = getContext<PartyContext>('party')
let grid = $derived(Array.from({ length: 9 }, (_, i) => weapons.find((w) => w.position === i)))
let mainhand = $derived(weapons.find((w) => (w as any).mainhand || w.position === -1))
</script> </script>
<div class="wrapper"> <div class="wrapper">
<div class="grid"> <div class="grid">
<div aria-label="Mainhand Weapon"> <div aria-label="Mainhand Weapon">
<WeaponUnit item={mainhand} position={-1} /> <WeaponUnit item={mainhand} position={-1} />
</div> </div>
<ul class="weapons" aria-label="Weapon Grid"> <ul class="weapons" aria-label="Weapon Grid">
{#each grid as w, i} {#each Array(9) as _, i}
<li class:Empty={!w} aria-label={`Weapon slot ${i}`}> {@const weapon = weapons.find((w) => w.position === i)}
<WeaponUnit item={w} {i} position={i} /> <li
</li> aria-label={weapon ? `Weapon ${i}` : `Empty slot ${i}`}
{/each} data-index={i}
</ul> class={weapon ? '' : 'Empty'}
</div> >
{#if raidExtra} <WeaponUnit item={weapon} position={i} />
<ExtraWeapons weapons={weapons} offset={9} /> </li>
{/if} {/each}
{#if showGuidebooks} </ul>
<Guidebooks {guidebooks} /> </div>
{/if} {#if raidExtra}
<ExtraWeapons {weapons} offset={9} />
{/if}
{#if showGuidebooks}
<Guidebooks {guidebooks} />
{/if}
</div> </div>
<style lang="scss"> <style lang="scss">
@use '$src/themes/colors' as *; @use '$src/themes/colors' as *;
@use '$src/themes/typography' as *; @use '$src/themes/typography' as *;
@use '$src/themes/spacing' as *; @use '$src/themes/spacing' as *;
@use '$src/themes/mixins' as *; @use '$src/themes/mixins' as *;
.wrapper { .wrapper {
align-items: center; align-items: center;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
@include breakpoint(phone) { @include breakpoint(phone) {
margin: 0 2px; margin: 0 2px;
} }
.grid { .grid {
display: grid; display: grid;
gap: $unit-3x; gap: $unit-3x;
grid-template-columns: 1.278fr 3fr; grid-template-columns: 1.278fr 3fr;
justify-items: center; justify-items: center;
grid-template-areas: 'mainhand grid'; grid-template-areas: 'mainhand grid';
max-width: $grid-width; max-width: $grid-width;
@include breakpoint(tablet) { @include breakpoint(tablet) {
gap: $unit-2x; gap: $unit-2x;
} }
@include breakpoint(phone) { @include breakpoint(phone) {
gap: $unit; gap: $unit;
} }
.weapons { .weapons {
display: grid; /* make the right-images container a grid */ display: grid; /* make the right-images container a grid */
grid-template-columns: repeat( grid-template-columns: repeat(
3, 3,
minmax(0, 1fr) minmax(0, 1fr)
); /* create 3 columns, each taking up 1 fraction */ ); /* create 3 columns, each taking up 1 fraction */
grid-template-rows: repeat( grid-template-rows: repeat(3, 1fr); /* create 3 rows, each taking up 1 fraction */
3, gap: $unit-3x;
1fr
); /* create 3 rows, each taking up 1 fraction */
gap: $unit-3x;
@include breakpoint(tablet) { @include breakpoint(tablet) {
gap: $unit-2x; gap: $unit-2x;
} }
@include breakpoint(phone) { @include breakpoint(phone) {
gap: $unit; gap: $unit;
} }
& > li { & > li {
list-style: none; list-style: none;
} }
} }
} }
} }
.unit { .unit {
width: 100%; width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
gap: $unit; gap: $unit;
} }
.image { width: 100%; height: auto; border: 1px solid $grey-75; border-radius: 8px; display: block; } .image {
width: 100%;
height: auto;
border: 1px solid $grey-75;
border-radius: 8px;
display: block;
}
.name { .name {
font-size: $font-small; font-size: $font-small;
text-align: center; text-align: center;
color: $grey-50; color: $grey-50;
} }
</style> </style>