T

TyphonJS

typhonjs-runtime

Join Server

Question: Modifying class list of SvelteApplication

MMLeahy4/11/2023
A question from @Dorako:
so, my module hooks into renderApplication and modifies the html[0].classList to add a .dorako-ui class to most Applications, but this strategy does not work for Svelte Applications. I had suspected this would be the case, but I'm not sure why or how to fix it.

Would I need to submit some sort of update to the SvelteApplication to push my dorako-ui class to the options.classes?
DDorako4/11/2023
My logging suggests that hook is triggering my code, but manipulating the DOM isn't working. If I manually add the .dorako-ui class to the application frame, the results are as expected (broad styling changes, replacing the background image etc.)
MMLeahy4/11/2023
And you can't add the class in the defaultOptions classes entry?

Also there is a onSvelteMount callback that passes back the the element in an object.

onSvelteMount({ element })
{
   element.classList.add('dorako-ui');
}


This occurs after the render callbacks from Foundry, but not sure why you can't add a class to the ApplicationShell component per se.

---

I suppose this brings up a possibility of making the optional classes entry data reactive.

So you can add / remove optional classes and have the default application shells automatically update.
DDorako4/11/2023
Would this not need to be in the Svelte-based module itself? In this specific case I'm trying to affect the Item Piles module which uses TJS, but Dorako UI itself is just ol' boring JS and CSS
MMLeahy4/11/2023
Gotcha, so you are trying to provide a global theming module.

One of the catches with this approach is that you have to keep in mind in general that there is an option in the core Application to limit the callbacks that are generated: baseApplication. So renderApplication may not always trigger for every app.

Is it perhaps CSS specificity that is causing a problem? Can you see the dorako-ui class added when you look at the DOM in browser dev tools?
DDorako4/11/2023
Yeah, lots of the pf2e system sheets don't trigger renderApplication which is a pain, so I have a list of those specific application names.

It's not a question of CSS specificity, the class isn't in the DOM
DDorako4/11/2023
If I add the class manually by editing the DOM in the inspector I see the expected results (my "base theme" styling applying)
MMLeahy4/11/2023
I'll just take a minute to add a renderApplication hook in a demo and see what happens.
DDorako4/11/2023
Awesome, thanks
MMLeahy4/11/2023
So what is happening in this case is that the html JQuery reference that is passed back in that hook is the default Foundry outer application shell that is not used when mounting a Svelte component which controls the outer application shell.

If you add the class in onSvelteMount it works fine.

A workaround that may or may not be considered acceptable is using setTimeout in renderApplication like this:

Hooks.on('renderApplication, (app) =>
{
   setTimeout(() => app.element[0].classList.add('doraku-ui'), 0);
});
MMLeahy4/11/2023
Actually I just verified that you don't need the timeout.

This works fine:
Hooks.on('renderApplication, (app) =>
{
   app.element[0].classList.add('doraku-ui');
});


It also appears to work fine for normal Foundry applications.
DDorako4/11/2023
well now! Are there any potential implications of affecting the app's classlist through the element list, rather than through the jquery reference?
MMLeahy4/11/2023
It should be the same element / value actually except in the SvelteApplication loading case where html is the unused outer application shell. By that time element is the mounted Svelte component.
DDorako4/11/2023
Hooks.on("renderSvelteApplication", (app) => {
  app.element[0].classList.add("dorako-ui");
});

Works great and shouldn't interfere with my existing stuff
MMLeahy4/11/2023
I'd say again you have to be careful in this approach as it won't universally work for any apps that define baseApplication. There shouldn't be any problems with the code w/ renderApplication and using app.element[0]. IE no need to add custom code for TRL handling.
DDorako4/11/2023
I'll probably use a strict list like I do for the system applications, so I'll only affect the stuff I know to be compatible
MMLeahy4/11/2023
If this is for particular systems to like PF2E then you have a bit more freedom in specific targeting vs a broad UI overhaul module.
DDorako4/11/2023
I primarily support PF2e, but SWADE is also supported
MMLeahy4/11/2023
Yeah in that case you can check game.system.id I believe and generate hooks dynamically for game system specific cases where baseApplication is defined.

// Unique app names that use `baseApplication` option.
const systemAppHooks = {
   pf2e: ['PF2EActor', 'SomethingUnique'],
   swade: [...]
}

Hooks.once('init', () =>
{
   Hooks.on('renderApplication', (app) => app.element[0].classList.add('doraku-ui'));

   const customApps = systemAppHooks[game.system.id] ?? [];
   for (appName of customApps)
   {
      Hooks.on(`render${appName}`, (app) => app.element[0].classList.add('doraku-ui'));
   }
});
DDorako4/11/2023
unrelated question, but to what extent can I assume that the specificity-raising selectors like in .item.active.underscore.svelte-14iev2w will remain the same?
MMLeahy4/11/2023
I wouldn't rely on it. The package author can modify the hash svelte-14iev2w, but it is also auto generated based on styles of the component, so it can change if the styles embedded in a component changes.
DDorako4/11/2023
Perfect, I can apply the styling how I want it now
MMLeahy4/11/2023
You could use a regex though for styles. Quite likely the svelte- part won't change unless the package author completely generates a custom hash which is not as likely.

So... .item .active .underscore [class^="svelte-"] if you had to override specific styles defined by the component.

Err.. You might have to play around with the regex. Not sure how that would work with chaining previous classes.
MMLeahy4/11/2023
It looks like you got things going though! 😄
DDorako4/11/2023
Yeah I just opted for minimal touches, and I avoided the specificity class by just using other classes instead.
Thanks for the help!
If I ever do up creating a module that contains applications I'll be sure to use TJS. Just moving the window around is a world of difference as always.