Retile Mode#213
Conversation
|
Currently also contains #194 ; will remove once that's merged. |
|
You will probably hate me but why not a drag and drop :D ? The good part is that this allows full resizability. And now with the beauty of container queries in CSS we can set the font size based on the tile size (most confess unfortunately the base idea is from claude opus... :) )!
I uploaded the code to a Test repo (I like clean commit history so I rebase and amend a lot during development so this is likely to be changed but the drag and drop is rather stable. Also when I designed the DnD architecture (while it relies on some Angular concept though) I tried it to be as standalone as possible as I have/had the intention to port it to ORM at some point. Code is in the https://github.com/Abasz/Test/tree/main/src/app/settings-dialog/tile-layout-editor folder It has the wrapper, tile-layout-editor.component. The DnD library (but I would be carefully to call it that) for the dragging part uses native browser events but not the dragging as that is basically imitated via transform (this is necessary due to the difference for touch screens). https://github.com/Abasz/Test I like the idea of grid "density" setting freely so I might "steal" that :) Currently I used a portrait and landscape mode that generates a 3x4 or a 4x3 grid. |
I had thought of that early on 😊. But over the past month week I have been losing my mind with buggy drag and drop with some tooling I use for work, and then again for personal tools. Then I recall all the bug reports from drag and drop at my old Frontend position. Leaning maybe later! Plus the logic in place here makes incremental steps easier.
I do like that — nice going Claude. |
fd9d9e7 to
d9dc018
Compare
|
This was actually highly inspired by some of the asks you've made across the repo — it's slated for the next release, which aligns with my availability (or lack thereof… 2026 has been no joke so far), but I'd like to know your thoughts :) |
|
i see. this all seems very nice and a big upgrade to the current UI features. cant wait to test it out! |
|
@DXCanas Would you mind rebasing this on the current latest dev branch? I have started to work on the TS and Vite migration but since this have substantial changes that would be affected I would like to use this as base to avoid creating extra work. |
Add forceCurveDivisionMode and gridConfig (landscape/portrait) to default GUI configuration. This provides the foundation for dynamic grid layout with configurable rows and columns per orientation. Default configuration: - Landscape: 4 columns × 2 rows (8 tiles) - Portrait: 2 columns × 4 rows (8 tiles) AI-assisted-by: Claude (Anthropic)
Refactor all dashboard metric components to support slotted content for retile mode controls: - Add slotContent parameter to all metric templates in dashboardMetrics.js - Refactor DashboardMetric to use CSS classes instead of inline styles - Add 'with-icon' class for font-size differentiation - Move battery icon positioning from DashboardMetric slot styles to BatteryIcon component itself - Add slot support to DashboardForceCurve title area - Add grid-column: span 2 styling to DashboardForceCurve host - Add forceCurveDivisionMode property to force curve configuration This prepares metric components to accept inline controls during retile mode while maintaining clean separation of concerns. AI-assisted-by: Claude (Anthropic)
Add retile mode functionality to dashboard toolbar: - Add Retile/Submit button that toggles retile mode state - Show Reset button when in retile mode - Add 'active' styling for retile button when mode is enabled - Dispatch retile-mode-changed event with active state - Dispatch reset-layout-to-default event for reset functionality - Delegate settings dialog opening to parent component via open-settings event - Remove direct SettingsDialog import (now handled by parent) The toolbar now provides UI controls for entering/exiting retile mode and resetting the dashboard layout to defaults. AI-assisted-by: Claude (Anthropic)
Add interactive dashboard customization with retile mode: - Local metrics state with add/remove/replace controls - Dynamic grid configuration via CSS variables - Computed grid state in willUpdate() with fine-grained change detection - Automatic metric trimming when exceeding grid slots - Add-tile interface and reset to defaults - Settings dialog integration AI-assisted-by: Claude (Anthropic)
|
Did a substantial interactive rebase and stripped out #194 to make it a little more readable. No testing yet :) |
|
Is this even worth bringing in, though, given #200? And the lack of enthusiasm from folks about this PR, more generally, haha I meant to start looking at the drag and drop stuff last week (I think using that for reorder exclusively would be a pretty clean/debuggable implementation that already uses some of the methods here) but... Life has been having its way with me lately. |
|
The reason this is stalled a bit and the reason I have not been testing is because it creates massive conflict with the TS, vite/vitest migration as well as this is a massive change on the UI with a lot of possible issues on responsiveness on different screen layout. Besides I like the idea of resizing of the tiles which this lacks (I mean you can resize the grid, but I meant that the tiles can have different sizes). I like the idea of the retile button that starts the retiling (instead of duplicating the grid in the settings) but I am not sure how much extra work that would be vs porting the DnD. I have asked copilot what is the difficulty to migrate the ESP Rowing Monitor DnD tile system and it said medium which I agree with in general. But this also means that porting that has not a clear benefit (though I always like to avoid reinventing the wheel). May be something in between: new tile addition is done as now (i.e. via the radio picker) and then reorder/resize is done with drag and drop after clicking the retile. What do you think? Just FYI, I refreshed the code in the test repo as I have made changes to the DnD (but not really material) I also had to finish things on my other project that is 95% ready so I am getting back to this. Thanks for your patience. |
This is basically what I had in mind, I think, without accounting for tile resize
So what I was trying to say was: radio for new tiles, dropdown for removal/replacement, and DnD for reorder. I think limiting changes for each "medium" makes it easier to test, etc. I'm not sure how tile resize would fit into this, though. Personally, I'd argue tile resize is a big enough change that it should happen later — The utility is obviously helpful and clever. But, IMO, it's introduces a lot of new modalities to the current mental model, and introduces more complexity as far as the responsiveness goes. The work here is still within the current design paradigm of laying out/arranging consistent "tiles" within a given grid. Giving the ability to change the size of an existing tile doesn't really have the same analog (I guess you can have smaller tiles and larger tiles in a mosaic. But you can't really "stretch" a current tile to be larger than it already is. It's just a different tile altogether), and I think we'd have to be careful about how to design it in a way that's intuitive. I was even iffy about variable grid size tbh, but brought it in giving some of the comments I've seen in discussions around vertical display -- I tucked that into the dialog to let retile maintain its sort of analogue mental model.
One of the reasons I wanted to use native form dropdowns is that most mobile browsers (or any browser) have a nice, responsive version "for free". Buttons can be resized using media queries as is being done elsewhere. Radio buttons I'll have to test a bit. |
Yes I agree that resize can be a separate implementation after we have the basic drag for reorder. The reason I was raising this is that because resize with a drag requires drag and "knowledge" of the grid context and those are related to some extent. But the resize is a self contained change indeed. Of course the concept of the resize is that we increase the container but it needs to "snap" (adhere) the grid. The variable grid size is indeed an issue as that may trigger reorder, this is something I have not though about actually. Any way lets can the resize for now, but keep in mind when deciding on ground work if we can choose something that would help (or at least not hinder) that feature :D
So this is a bit tricky now that I have thought about. If we have removal and replace, technically we do not need drag reorder as I can just replace and remove. Would that from a UX perspective bad if we had the radio addition and just a remove and no replace (meaning that replace would require actual removal and re-addition)? Reason for asking is that if we have the replace and removal via a button drag reorder seems a bit redundant (though a nice UX feature most certainly) |
Ah, yeah -- that's sort of what I was trying to say earlier. I'd thought about it, but didn't want to introduce the complexity of drag and drop; both for interface and testing complexity. So I designed around it, trying to stay within the current "bounds" of interaction types (and making it easy to use just a keyboard, in case that's a use case for some).
Yeah, after you mentioned yesterday, I started thinking about how that might potentially be implemented; I think it'd be a state thing. There could be some sort of x/y size associated with each metric with default values; those would get fed into the grid calculations as well as the "available metrics" that go into the radio button add, to determine what fits or not. Thinking about that actually made me consider that, for affordance's sake, we should probably also include Re: triggering reorder — we may be able to cut this down by switching from a straight grid to
So in my mind, drag-to-reorder would not be a replacement for "replace" -- you drag it "in between" 2 tiles to move a tile there vs where it was before. Only in between. None of the other tiles dissapear, they just shift "forward". I have seen the design pattern where one drags on top of a tile to replace it, but I personally kind of hate it -- it always makes reorder finicky (I have to aim precisely at the space in between 2 items), particularly with small touch screens. idk the quality of the pi screens that people have hooked up, but I also really hate drag and drop on anything but perfectly glossy/slippery displays. Any stick from plastic drives me freaking nuts. I used to have replacement set up as a dropdown, and removal as a button. I got rid of it while I was quick-hacking, just because the styles were getting finicky and I wanted a prototype to start testing. So. to answer your question: I personally think trying to replace "replace" with just drag and drop would be worse UX than having an explicit "replace", just because it would be less clear as well as potentially frustrating. But if you mean to just remove "replace" altogether, and a "remove" + "reorder" instead, I'd say that UX is OK, but it's more work (number of taps, plus a "drag", which is pretty "expensive" in my mind) than just having a "replace" feature. |
Every tile gets added with this default (force curve for instance row: 1 column 2) and then that could be changed.
Correct. The way I did DnD is that I track the space available and I check the current size. This is relevant in case of reorder as well because reorder is essentially a swap between two tiles. But if one is bigger than its a swap between 3 tiles. This also means that you cannot swap a 1 space tile with a 2 space tile (well I have thought about shifting and so on but it was extremely messy and its not worth the effort)
Never heard of it (though this does not mean much :D:D) but looking at the docs browser support is ... well ... not extensive (to say the least) :D I will have a look. But in reality it would still require the full math as we need to provide start coordinate and spans and we need to calculate space. What I did is I created drag strategies (move, drag, and resize) that calculate things differently internally but have the same api. Becuase you have drop (moving to a new place) and drag over (when you are trying to interact with an already exisiting tile). But for resize you need to handle it differently than for move (latter swaps former block). the selection is made at runtime based on what kind of drag and drop session we are in (move, palce, or resize) and then these actually return the final tile shape on release (if blocked operation the same, if allowed the changed).
Technically I understood you but essentially your replace is a remove + place I was trying to make the following comment (after re-reading admittedly poorly): I would think that remove + place is easy enough to use than compared having three different operations place, remove and also replace (where the latter would be just an alias command for the first two in sequence). One additional problem I have with replace (without remove) is that once the grid is full one will end up with an always full grid (unless full remove or something). And since we should provide a default layout (i.e. a full one) generally users will need to use replace only. Unless they clear the full screen. I mean not that I think many people want to see less metrics but I know from the kayak sessions that I had with professional athletes that sometimes a couch does not want to show too many things to the athlete as it is distracting (especially to one that is not used to see so many of those metrics. Actually this is why I have resize in ESPRM. I was told that they want to see stroke rate and power, nothing else when they train for strength. Or when they practice technique, like stroke consistency only, drive length and stroke times. And I wanted to fill the screen so it is bigger.
I agree that if you try to shift and then reorder is bad. I have experimented with it and it was horrible UX. I ended up pure swap. If you go above a tile with another those two gets swapped that is it. Now on the touch screen you are making a very important point that I have not though of. Namely that some of these touchscreens that ORM gets used with are almost capacitive like... on those a drag and drop may be extremely horrible experience. Never tried though but this is something to keep in mind. This starts to justify a purely "click" based approach actually
I think I was not phrasing things properly, sorry about that. So we have technically 3 main types of operations:
While the first and second operation can be 2 different sub type: a swap or place to an empty place, from the first operation I would restrict it to adding to empty space. If the grid is full one needs to remove (this will also be necessary if you want resize because replace swapping or shifting there will not work as you will need the empty space - shifting is as I said doable but messy I think with the boundaries). While the second you can move to an empty space or if you drag over to one that is already occupied its a swap. Again apart from the resize, a move is essentially a "remove one or two tiles and add the same tile(s) but at different place(s)". Strictly speaking they do not need drag and drop. While resize (that is also doable without) is much better experience with dragging. Now my statement is that:
|
|
Sorry about that one. I pulled the rug underneath this one by removing the 0.9.7 branch after merging it into main. My bad. |
|
@JaapvanEkris should I just open a new one? |
Yeah, against 0.9.8 |
|
@Abasz I had a very long message drafted up, and it was wiped by an intercepted kb shortcut in a new browser. I am defeated. So I will tackle comments later. Not trying to delay further, though. I'll get to the rest of the points in a few days but my basic summary:
I think resize, as you've described your implementation, comes with a lot of complex 2-dimensional math that isn't very well suited to the current state of the code in this branch. BUT I think we might be able to come up with a single-dimension alternative that doesn't require that math and instead relies on the re-gridding logic here?
The general against argument "replace", as I understand it, is a usability one: affordance. If there's no I initially didn't even want an "add" tile, but needed it because of the variable length of the tiles (2 for force curve vs 1), and then saw the need 🫠 But I was mistaken, I think, for the reason you're laying out. To be fair, replace is 3 operations: remove, add, then move. Which does save some time. But I think just "Add" and "Remove" being sufficient. And simpler. And, combined with regrid + a 1-dimensional resize (just length) could end up with many of the layouts your grid-based resize has? Overall, I am liking the idea of "remove", "add", and "drag-to-reorder". Combined with variable grid/regrid, I think it's pretty feature complete and doesn't require drag and drop for folks less-than-ideal displays. I had a lot of other thoughts, spurred by your earlier comment, that I'd been turning around for days. But. I'll rewrite them later 🙃 |
|
Hey guys. It's been a helluva time for me lately, so dev here has been limited mostly to weekends. I've also been injured, so have not gotten to spend much time on my rowing machine 😢 I saw the converation over in #233; should I expect some trouble rebasing on top of that, then submitting a new PR @Abasz? |
I alluded to a workaround earlier, but wasn't clear. In your example: stroke ratio, drive length, stroke times. Lets say that's all you want to see, and you want it to be big. Regrid to 1x3, and set those as your metrics. Not quite as flexible (you can't have 1 large, 3 small, etc) but it does cover the use case and cut down on logic significantly. On the other hand, given my inconsistency, I would totally understand if you want to to just port your ESPRM logic over here :) |
Sorry to hear that. Hope life will start treating you better soon! |
Indeed you can do that! Have not thought about that.
I expect yes. But dont worry I can do the rebase np. Especially since now we are under TS probably that is more efficient. The only thing I dont know if I do the rebase, and I do a force push whether you will still be able to start the PR but we are about to find this out :D:D What do want to ask you is that you add tests (it can be done by Claude with a quick brows through I think, probably not the most efficient or best tests but definitely better than nothing and on a usefulness/time spent scale they are excellent :D) |
I can attest to that. I am migrating the backend to TS and Vitest, and after some tweaks it actually works well. Will take some time, as I want to make sure the whole "tree" of unit and integration tests functions again when putting it in a PR. A practical one: would it be wise to create Vitest projects for all individual directories under the /opt/openrowingmonitor/app dir? This way we can only test the client (i..e. frontend), the engine (backend), etc..? I saw that @Abasz took a shot at it by calling a test test:client, but as soon as I added backend stuff it was included. Might save you lot a lot of time (a lot of backend tests take a lot of time due to the use of recorded data). |
I had a look at the rebase/merge quickly. And actually I will create a new branch and migrate the changes "manually" and create new commits, create a new PR and add @DXCanas as editor right. This is the simplest way to do so so the commit history remains clean but I do not need to fight 4 commits of merge conflicts. Hope this is ok with you
Never used those so I have no idea. But we would need to test the front-end separately that is important so if this is the solution I am all in. Backend runs ages and actually because of rounding sometimes they fail on the machine I work on. I will need to see if we can add folder selection with globing, but I will also have a look at this project things as that would be cleaner of course (allowing separate config for instance). If we go down this road, then I will "compartmentalise the TS config and so on so we can have those separately as well.
Yes I was not expecting such a quick move there on the backend :D so the npm command was not designed for selectivity |
I'm OK with it, but can I ask that you add the
Sure! Can't promise great results because I'm not experienced enough in FE tests to prompt as specifically as I do for the edits themselves, but it would be good practice for me 😄 |
It will take some additional time. I want/need to use the typescript migration to take a thorough look at some behaviour of some objects. In many cases we respond with '0' when we actually mean 'undefined', so that is something to investigate. Another thing is that many tests do not cover all methods provided by a module, and that some specific concerns need to be adressed as well. For example: the accumulators have issues with rounding errors (some tests downstream actually fail on that). So some work ahead. |
Absolutely! I was not aware of such thing in github but of course I will do that! |
|
Gents, I have made a stupid mistake, namely that I pushed directly to the develbranch the changes wtr. the retile. Accidentally I have set the remote tracking wrong for my local branch... so instead of actually creating a new remote branch (and supposed to be pushed) was pushed to devel. Anyway I force pushed to clean this out... I am really sorry about this... If I need to fix things let me know I will do it. |


Alright; this is something I've been noodling on while using ORM.
Seeing the discussion in #165, I was emboldened to use my Sunday morning to try and make it happen.
Then it became my sunday afternoon.
And part of the night.
It was a bigger change than I anticipated 😅, as the diff will show
But I have a prototype going! Basically: Making a distinction between settings and "retiling"; The styles aren't done yet, but the basic functionality is mostly there.
If some of the settings/dialog changes seem confusing, it's because I initially envisioned all the "available metric" options to live in a single modal/dialog, activated when hitting a "replace" button or an "add" "tile. But after starting down that path, I realized it's actually more work than the current settings-dialog implementation. So I thought about a "quick edit" mode that abandoned the dialog altogether. I still like the idea of keeping all dialogs at the "dashboard" level, though.
Forgive the long commit history; it started with a well laid out plan to execute and quickly devolved into fast patches done by hand and by AI. AI used heavily for commit messaging and the like. Testing has been a pain, dealing with edge cases, etc.
But here are some demo shots:









