CarlosFdez / global-progress-clocks

FoundryVTT module for Blades in the Dark style progress clocks that show on the sidebar
MIT License
7 stars 7 forks source link

Render clocks dynamically instead of using images. #2

Closed Lunar-Dawn closed 1 year ago

Lunar-Dawn commented 1 year ago

This changes the rendering of the clocks from a simple image to a dynamic one using a CSS background and dynamic spokes. This has several advantages over the image-based approach:

I am quite unfamiliar with both Foundy module development and handlebars, so I might have done something more subtle wrong as far as those are concerned. Apologies if so.

I have not tested your deployment pipeline since I don't quite know how it works, but the image directory export will probably need to be removed. Size restrictions are also only on the front end so database.js might want to enforce them too, but I wanted to make sure you were interested in the PR before I go beyond proof-of-concept.

The current look in Foundry: image

CarlosFdez commented 1 year ago

Rendering dynamically is indeed something I've been wanting to do and only haven't done in the interest in time. I'll take a look soon, once I get more progress on stuff I'm working on.

CarlosFdez commented 1 year ago

So I started digging into it. Here are my general thoughts. 1) Avoid registering new handlebars helpers, this may interfere with certain system implementations. 2) Avoid let if at all possible (prefer const). Try an IIFE if need be. 3) Are there systems that go past 16 clock? The most I've ever seen was 12, but I can imagine someone doing a 14 or 16 clock.

To handle the lack of a times helper, you could consider adding spokes to the render data. spokes: Array(count).fill(0) will handle that. If you wanna attach any additional data to each spoke render pass you could even do a map().

Past that, I have some more vague concerns about using a number input instead of a select, since a select is generally quicker in the heat of play. I can see select not being tenable when there are many options though. Do people actually make 11 clocks? Its easy to convince me of this though.

Lunar-Dawn commented 1 year ago

597bd92289776160ca04505809b713263ffe2912 Should resolve your first and second issues.

I agree that clocks bigger than 16 are excessive. I do not specifically know of any system that uses clocks that big or bigger, but I didn't want to limit users unnecessarily. The choice of a max size of 32 was based purely on the fact that it was the last power of 2 that was readable, so it was rather arbitrarily set.

I don't think a number input will be much slower than a select, scrolling a few times shouldn't be much slower than finding the right value in a dropdown (especially as you note if there are a lot of values). I have certainly used clocks of size 9 as 3*3, albeit rarely.

A solution to the selection could be a hybrid approach with a number field and a row of buttons for the common options that set the field. Although I am unsure how dynamic Foundry dialogues can/should be or how intuitive that would be for users.

CarlosFdez commented 1 year ago

The reason for limiting to 16 was that we could maybe keep it as a select. If its a number input there is no reason to limit it that severely.

My current thought right now for a hybrid approach is actually a dropdown autocomplete with common sizes that shows when you click on it (regardless of what you typed). I'll look into that in the coming days after I get some work done, but if you wanna take a peek beforehand you can. I know there are libs that can do that, but ideally I'd prefer not to use a lib if its easy.

Thanks for this, btw.

Lunar-Dawn commented 1 year ago

That does sound like a nice design with the dropdown. Creating the dropdown itself should be easy, but I suspect that getting it to nicely break out of the dialogue window might be difficult. Still, I'll take a shot at it.

No problem, I've wanted to get into Foundry module development for a while and this was a fun opportunity to try it.

CarlosFdez commented 1 year ago

Sounds good then. As for the dropdown, this might work if you can find a way to get the autocomplete to always show even if you just click it: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist

ContextMenu unfortunately does have the problem you mentioned where it cannot exit the dialog. Any proper custom implemented auto-complete needs to add to the document body and be absolutely positioned. This means the only other possible solution is a library if datalist is too cumbersome.

Lunar-Dawn commented 1 year ago

I tried datalists first, but you cannot disable the filtering by current input, and the "show on focus" functionality is a bit testy and browser-dependent, so I wrote a custom solution instead.

The breakout from overflow: hidden is pretty clean, but it does mean that the dropdown size can't be defined in percentages in CSS, so it is currently just 100px. It could be easily set with JS in the rendering hook but it does feel a bit dirty since none of the other rendering uses JS.

image

Lunar-Dawn commented 1 year ago

I tried it and there needs to be some limit on the clock sizes. 541adcd50879467881e0eb7d80169d4926a5786b sets a limit of 256 in the database, which I hope is the correct way to do it. It currently just rejects the clock on the backend and shows an error, if there is a way to make that check when submitting the form that would be better, but I don't know how to in Foundry. (a custom onSubmit maybe?)

Otherwise the browser might try to render a few thousand spokes and hang (which is one of the reasons I considered writing the dynamic clock as canvases instead before doing it with HTML/CSS).

CarlosFdez commented 1 year ago

I don't know how I didn't see these notifications. I'll check it out soon and start pushing to it in between other stuff.

CarlosFdez commented 1 year ago

I moved the dialog to a new file. For my own personal curiousity, why is the dropdown not being clipped by parents having overflow:hidden? Normally even position absolute things have to deal with that.

Lunar-Dawn commented 1 year ago

As far as I understand it (although admittedly I haven't checked the specs for it), it is because absolute elements get rendered as if they were children of the closest non-position: static ancestor but get their default position from their "real" position in the tree.

The dropdown thus gets positioned relative to the wrapper but is rendered as if it were a child of the dialogue, which is outside the overflow: hidden. This can be seen if you set the top property or the dropdown width in percent, as those will be relative to the entire dialogue.

CarlosFdez commented 1 year ago

I see. top/left/right/bottom places it relative to the nearest relative, but without those it does try to put it in its current location and you can use margin to fake it. I guess that's the trick, as awkward as it is.