T
TyphonJS•2y ago
geoidesic

For my next trick, I would like to create an inventory list of items from the character

I have access to the Actor documentStore but I'm really not too sure what to do with it to make a list.
12 Replies
TyphonJS (Michael)
TyphonJS (Michael)•2y ago
You should install essential-svelte-esm and check out the reactive document demos. There is a capability w/ TJSDocument to create a "reactive embedded collection". This allows you to do dynamic filtering / reducing of an embedded collection. Behind the scene this is implemented with "dynamic reducers" which is a separate library I built brought into TRL. The goal of the next TRL release is to enable solid drag and drop support + sorting for dynamic reducers; hence this will be enabled for reactive embedded collections as well and will make creating inventory lists and any other table like views of embedded collections easy to implement. This future capability is what will enable a TRL based game system reference repo done the "right way" and is why one doesn't exist currently.
TyphonJS (Michael)
TyphonJS (Michael)•2y ago
TyphonJS-FoundryVTT
YouTube
TRL - TyphonJS Runtime Library (0.0.19) dev snapshot - Reactive Emb...
Hi folks, just a quick dev snapshot update on a powerful upgrade to the reactive document / TJSDocument API. "Reactive Embedded Collections" are now possible to create in an adhoc or structured manner that allows non-destructive filtering and sorting of Foundry embedded collections in a manner that plays quite nice w/ Svelte. Upgrade to TRL 0.0...
geoidesic
geoidesic•2y ago
Thanks for the reply. I'm trying to implement this in an ActorSheet, which already natively has drag and drop support. I notice that the EmbeddedDocAppShell implements its own. Do I need that? If so how to implement it in an actor sheet without tripping over the native drag and drop mechanism? I'm guessing that if I rely on the native drag&drop, then reactivity won't work? Or are you suggesting that I should wait till the next TRL release?
TyphonJS (Michael)
TyphonJS (Michael)•2y ago
As things go I'm not really going to be able to walk you through any sort of interim solution whether that is using the Foundry core mechanisms or a custom solution in respect to hooking things up with reactive embedded collections. Certainly feel free to explore and try things out in the meantime. It's probably best to wait until the next release. At least with this upcoming release all I'm working on is drag & drop + sorting aspects. Usually TRL has a cadence of around ~3 weeks per release. The most recent release was unusual as the scope grew quite a bit. I am working remotely for a bit of the next couple of months, so it might be longer than 3 weeks, but with a single goal to add hopefully it will be out soon.
geoidesic
geoidesic•2y ago
Cool. Just for reference. I've done this as a first attempt:
<script>
import { getContext } from "svelte";
const doc = getContext("#doc");
const items = [...$doc.items]; //- make the items iterable
</script>

<template lang="pug">
ol
+each("items as item")
li {item.name}
</template>
<script>
import { getContext } from "svelte";
const doc = getContext("#doc");
const items = [...$doc.items]; //- make the items iterable
</script>

<template lang="pug">
ol
+each("items as item")
li {item.name}
</template>
Which does render a very basic list, non-reactively (you have to close and open the Actor sheet again before any new drops will show).
TyphonJS (Michael)
TyphonJS (Michael)•2y ago
Even though you are not using reactive embedded collections this should update the list when data changes.
<script>
import { getContext } from "svelte";
const doc = getContext("#doc");
</script>

<template lang="pug">
ol
+each("[...$doc.items] as item (item.id)")
li {item.name}
</template>
<script>
import { getContext } from "svelte";
const doc = getContext("#doc");
</script>

<template lang="pug">
ol
+each("[...$doc.items] as item (item.id)")
li {item.name}
</template>
This is kind of where referring you back to the Svelte tutorials is probably relevant. 😄 Granted that is not a particularly good solution above as it will render again whenever anything in the document changes.
geoidesic
geoidesic•2y ago
Hmm... you say I'm not using reactive embedded collections... but doc is a TJSDocument and $doc.items is an EmbeddedCollection. Isn't that a reactive embedded collection?
TyphonJS (Michael)
TyphonJS (Michael)•2y ago
Watch the video above and install essential-svelte-esm to play around with the demo.
geoidesic
geoidesic•2y ago
I have 🙂 still confused though 🙂 I'll keep trying
No description
geoidesic
geoidesic•2y ago
Cool I got it using Svelte reactivity: $: items = [...$doc.items];
TyphonJS (Michael)
TyphonJS (Michael)•2y ago
You can certainly do that... What I listed above is just a shortcut of sorts. Svelte templates are reactive. Whenever you use a store IE $doc in a reactive statement IE $: or in a template it will update reactively. "reactive embedded collections" refers to the API of TJSDocument under the embedded accessor. This is definitely an area where there could be better documentation in the actual source code. As things go pointing you to the TRL source code won't help too much sans thorough documentation. A fair amount of details are also in a separate library that can also use a bit more documentation. All of this does delve into more advanced territory in general. The API is rock solid and the separate library has 100% test coverage, so it all works, but I think it might be a bit troublesome in general to point you to to it understand things by just reading the code. If I recall v10 was about to drop when I was working on that and had to switch over to maintaining FQL for that window of time. I will be buffing up all the documentation and provide more examples in the next TRL release. In the meantime learning more about the Foundry API and how embedded collections work with the core API won't hurt.
geoidesic
geoidesic•2y ago
Sounds good. I had a look at the EmbeddedDocAppShell. The one thing I'm not grokking about it is where the array of items gets passed into the embedded store? I see this "EmbeddedDocAppShell.svelte":
const filterSearch = createFilterQuery('type');

const input = {
store: filterSearch,
efx: rippleFocus(),
placeholder: 'wildcard',
type: 'search'
}

const doc = new TJSDocument();

/** @type {import('@typhonjs-fvtt/runtime/svelte/store').DynMapReducer<string, Item>} */
const wildcard = doc.embedded.create('Item', {
name: 'wildcard',
filters: [filterSearch],
sort: (a, b) => a.name.localeCompare(b.name)
});
const filterSearch = createFilterQuery('type');

const input = {
store: filterSearch,
efx: rippleFocus(),
placeholder: 'wildcard',
type: 'search'
}

const doc = new TJSDocument();

/** @type {import('@typhonjs-fvtt/runtime/svelte/store').DynMapReducer<string, Item>} */
const wildcard = doc.embedded.create('Item', {
name: 'wildcard',
filters: [filterSearch],
sort: (a, b) => a.name.localeCompare(b.name)
});
Which seems to be doing everything except actually passing in the items? Or does it search through the documentStore's collections looking for one that matches the name "Item" as class name or something like that? I assume so. Anyway I've managed to get a list working using the EmbeddedDoc thingy. However, one problem persists. I can' figure out how to make a reactive SUM from the weight property of each item in the inventory. I tried this:
$documentStore.system.inventoryWeight = [...$documentStore.items].reduce((sum, item) => {
sum += parseFloat(item.system.weight) * parseInt(item.system.quantity);
return sum;
}, 0);
$documentStore.system.inventoryWeight = [...$documentStore.items].reduce((sum, item) => {
sum += parseFloat(item.system.weight) * parseInt(item.system.quantity);
return sum;
}, 0);
But of course that's not reactive because of the spread operator. I'm gathering that I have to somehow register that as a reducer using doc.embedded.create but it's not clear how I might do that. Based on reading your svelte/store code, my expectation is that I would do something like this:
const sum = doc.embedded.create("Item", {
name: "inventoryWeight",
derived: function (d, k, t) {
t.reduce((d, k) => {
d += parseFloat(k.system.weight) * parseInt(k.system.quantity);
return d;
}, 0);
},
});
const sum = doc.embedded.create("Item", {
name: "inventoryWeight",
derived: function (d, k, t) {
t.reduce((d, k) => {
d += parseFloat(k.system.weight) * parseInt(k.system.quantity);
return d;
}, 0);
},
});
But that doesn't work – so I'm not quite sure how to refactor my reducer as something that can be passed to the derived property: I understand I'm getting ahead of the docs here and you're busy progressing the library 🙂 so, no worries if you feel you can't get into it, but any help or pointers would be appreciated. I managed to do this using svelte '$: reactivity. no need for all the above.