Skip to content

Commit 442f06a

Browse files
author
DavidQ
committed
Fix Palette Manager hover details as absolute overlay without tile resize or layout shift - PR_26124_031-palette-hover-overlay-no-layout-shift
1 parent 09c6956 commit 442f06a

5 files changed

Lines changed: 122 additions & 218 deletions

File tree

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# PR_26124_031 Palette Hover Overlay No Layout Shift Report
2+
3+
## Scope
4+
- Updated Palette Manager V2 source swatch hover behavior only.
5+
- No schema, sample JSON, games, workspace, toolState, session, or tools/shared changes.
6+
7+
## Changes
8+
- Moved source hover details into the source tile DOM node so the tile is the fixed-size positioning anchor.
9+
- Removed the source-entry wrapper and related CSS so hover no longer introduces an extra layout surface.
10+
- Kept hover feedback to z-index and shadow on the existing tile box.
11+
- Kept hover details as an absolutely positioned popover below the tile, hidden outside normal document flow.
12+
13+
## Validation
14+
- `node --check tools/palette-manager-v2/modules/SwatchRow.js`
15+
- `git diff --check`
16+
17+
## Packaging
18+
- Delta ZIP: `tmp/PR_26124_031-palette-hover-overlay-no-layout-shift_delta.zip`
Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,3 @@
1-
# git status --short
2-
M docs/dev/reports/codex_review.diff
3-
M tools/palette-manager-v2/controls/SourcePaletteBrowserControl.js
4-
M tools/palette-manager-v2/index.html
5-
M tools/palette-manager-v2/modules/PaletteManagerApp.js
6-
M tools/palette-manager-v2/paletteManagerV2.css
7-
?? docs/dev/reports/PR_26124_030_report.md
8-
9-
# git diff --stat (excluding codex review artifacts)
10-
.../controls/SourcePaletteBrowserControl.js | 30 ++++++++++-----
11-
tools/palette-manager-v2/index.html | 8 ++--
12-
.../modules/PaletteManagerApp.js | 2 +-
13-
tools/palette-manager-v2/paletteManagerV2.css | 45 +++++++++++++++++++++-
14-
4 files changed, 69 insertions(+), 16 deletions(-)
15-
16-
# untracked file additions
17-
docs/dev/reports/PR_26124_030_report.md | 19 +
1+
docs/dev/reports/PR_26124_031_report.md
2+
tools/palette-manager-v2/modules/SwatchRow.js
3+
tools/palette-manager-v2/paletteManagerV2.css

docs/dev/reports/codex_review.diff

Lines changed: 84 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -1,207 +1,111 @@
1-
diff --git a/tools/palette-manager-v2/controls/SourcePaletteBrowserControl.js b/tools/palette-manager-v2/controls/SourcePaletteBrowserControl.js
2-
index a9a2b610..c8761215 100644
3-
--- a/tools/palette-manager-v2/controls/SourcePaletteBrowserControl.js
4-
+++ b/tools/palette-manager-v2/controls/SourcePaletteBrowserControl.js
5-
@@ -16,15 +16,12 @@ export class SourcePaletteBrowserControl {
6-
this.refs.sourceSearchInput.addEventListener("input", () => {
7-
this.app.setSourceSearch(this.refs.sourceSearchInput.value);
8-
});
9-
- this.refs.sourcePaletteSortSelect.addEventListener("change", () => {
10-
- this.app.setSourceSortMode(this.refs.sourcePaletteSortSelect.value);
11-
- });
1+
diff --git a/tools/palette-manager-v2/modules/SwatchRow.js b/tools/palette-manager-v2/modules/SwatchRow.js
2+
index 2670eb99..6e66e66e 100644
3+
--- a/tools/palette-manager-v2/modules/SwatchRow.js
4+
+++ b/tools/palette-manager-v2/modules/SwatchRow.js
5+
@@ -31,9 +31,6 @@ export class SwatchRow {
126
}
137

14-
render() {
15-
this.refs.sourcePaletteSelect.value = this.app.getCurrentSourcePaletteId();
16-
this.refs.sourceSearchInput.value = this.app.getSourceSearch();
17-
- this.refs.sourcePaletteSortSelect.value = this.app.getSourceSortMode();
18-
+ this.renderActiveSortButton();
19-
this.refs.sourceSwatchList.replaceChildren();
8+
static createSourceTile(documentRef, swatch, options = {}) {
9+
- const entry = documentRef.createElement("div");
10+
- entry.className = "palette-manager-v2__source-entry";
11+
-
12+
const tile = documentRef.createElement("div");
13+
tile.className = "palette-manager-v2__source-tile";
14+
tile.tabIndex = 0;
15+
@@ -68,9 +65,8 @@ export class SwatchRow {
2016

21-
const visibleSwatches = this.app.getVisibleSourceSwatches();
22-
@@ -63,12 +60,27 @@ export class SourcePaletteBrowserControl {
23-
}
17+
const details = SwatchRow.createDetailsBlock(documentRef, swatch, "palette-manager-v2__source-details");
2418

25-
renderSortOptions() {
26-
- this.refs.sourcePaletteSortSelect.replaceChildren();
27-
+ this.refs.sourcePaletteSortControls.replaceChildren();
28-
this.app.getSortModes().forEach((mode) => {
29-
- const option = this.document.createElement("option");
30-
- option.value = mode.value;
31-
- option.textContent = mode.label;
32-
- this.refs.sourcePaletteSortSelect.appendChild(option);
33-
+ const button = this.document.createElement("button");
34-
+ button.type = "button";
35-
+ button.className = "palette-manager-v2__sort-button";
36-
+ button.dataset.sortMode = mode.value;
37-
+ button.textContent = mode.value === "original" ? "Original" : mode.label;
38-
+ button.addEventListener("click", () => {
39-
+ this.app.setSourceSortMode(mode.value);
40-
+ });
41-
+ this.refs.sourcePaletteSortControls.appendChild(button);
42-
+ });
43-
+ this.renderActiveSortButton();
44-
+ }
45-
+
46-
+ renderActiveSortButton() {
47-
+ const activeMode = this.app.getSourceSortMode();
48-
+ this.refs.sourcePaletteSortControls.querySelectorAll("[data-sort-mode]").forEach((button) => {
49-
+ const isActive = button.dataset.sortMode === activeMode;
50-
+ button.classList.toggle("is-active", isActive);
51-
+ button.setAttribute("aria-pressed", String(isActive));
52-
});
19+
- tile.append(chip, tack);
20+
- entry.append(tile, details);
21+
- return entry;
22+
+ tile.append(chip, tack, details);
23+
+ return tile;
5324
}
54-
}
55-
diff --git a/tools/palette-manager-v2/index.html b/tools/palette-manager-v2/index.html
56-
index 347f6de9..907f11d8 100644
57-
--- a/tools/palette-manager-v2/index.html
58-
+++ b/tools/palette-manager-v2/index.html
59-
@@ -87,10 +87,10 @@
60-
Search source swatches
61-
<input id="sourceSearchInput" type="search" autocomplete="off" />
62-
</label>
63-
- <label class="palette-manager-v2__field">
64-
- Sort source palette
65-
- <select id="sourcePaletteSortSelect"></select>
66-
- </label>
67-
+ <div class="palette-manager-v2__field">
68-
+ <span>Sort source palette</span>
69-
+ <div id="sourcePaletteSortControls" class="palette-manager-v2__sort-buttons" role="group" aria-label="Sort source palette"></div>
70-
+ </div>
71-
<div id="sourceSwatchList" class="palette-manager-v2__source-grid" aria-label="Browse palette swatches"></div>
72-
</div>
73-
</details>
74-
diff --git a/tools/palette-manager-v2/modules/PaletteManagerApp.js b/tools/palette-manager-v2/modules/PaletteManagerApp.js
75-
index d2202881..86b164c4 100644
76-
--- a/tools/palette-manager-v2/modules/PaletteManagerApp.js
77-
+++ b/tools/palette-manager-v2/modules/PaletteManagerApp.js
78-
@@ -12,7 +12,7 @@ const REQUIRED_REF_IDS = Object.freeze([
79-
"userSwatchList",
80-
"sourcePaletteSelect",
81-
"sourceSearchInput",
82-
- "sourcePaletteSortSelect",
83-
+ "sourcePaletteSortControls",
84-
"sourceSwatchList",
85-
"editorTitle",
86-
"selectedSwatchPreview",
25+
26+
static createDetailsBlock(documentRef, swatch, className) {
8727
diff --git a/tools/palette-manager-v2/paletteManagerV2.css b/tools/palette-manager-v2/paletteManagerV2.css
88-
index 64c4c406..b2eae3b0 100644
28+
index b2eae3b0..11f536b2 100644
8929
--- a/tools/palette-manager-v2/paletteManagerV2.css
9030
+++ b/tools/palette-manager-v2/paletteManagerV2.css
91-
@@ -30,6 +30,12 @@
92-
display: flex;
93-
flex-direction: column;
94-
gap: 14px;
95-
+ overflow: visible;
96-
+}
97-
+
98-
+.palette-manager-v2__panel--center .palette-manager-v2__accordion,
99-
+.palette-manager-v2__panel--center .palette-manager-v2__accordion-content {
100-
+ overflow: visible;
101-
}
102-
103-
.palette-manager-v2__accordion {
104-
@@ -106,6 +112,33 @@
105-
gap: 8px;
106-
}
107-
108-
+.palette-manager-v2__sort-buttons {
109-
+ display: flex;
110-
+ flex-wrap: wrap;
111-
+ gap: 8px;
112-
+}
113-
+
114-
+.palette-manager-v2__sort-button {
115-
+ min-height: 34px;
116-
+ border: 1px solid var(--line, rgba(221, 214, 254, 0.26));
117-
+ border-radius: 8px;
118-
+ background: rgba(0, 0, 0, 0.18);
119-
+ color: inherit;
120-
+ cursor: pointer;
121-
+ padding: 6px 10px;
122-
+}
123-
+
124-
+.palette-manager-v2__sort-button.is-active {
125-
+ border-color: var(--accent, #ddd6fe);
126-
+ background: rgba(221, 214, 254, 0.22);
127-
+ box-shadow: inset 0 0 0 1px var(--accent, #ddd6fe);
128-
+}
129-
+
130-
+.palette-manager-v2__sort-button:focus-visible {
131-
+ outline: 2px solid var(--accent, #ddd6fe);
132-
+ outline-offset: 2px;
133-
+}
134-
+
135-
.palette-manager-v2__list {
136-
display: grid;
137-
gap: 8px;
138-
@@ -122,19 +155,22 @@
139-
min-height: 0;
140-
max-height: 620px;
141-
overflow-y: auto;
142-
+ overflow-x: visible;
143-
align-content: start;
144-
padding-right: 4px;
145-
+ padding-bottom: 150px;
31+
@@ -161,18 +161,6 @@
32+
padding-bottom: 150px;
14633
}
14734

148-
.palette-manager-v2__source-entry {
149-
+ position: relative;
150-
min-width: 0;
35+
-.palette-manager-v2__source-entry {
36+
- position: relative;
37+
- min-width: 0;
38+
- display: grid;
39+
- z-index: 1;
40+
-}
41+
-
42+
-.palette-manager-v2__source-entry:hover,
43+
-.palette-manager-v2__source-entry:focus-within {
44+
- z-index: 20;
45+
-}
46+
-
47+
.palette-manager-v2__swatch-row {
15148
display: grid;
152-
- gap: 8px;
49+
grid-template-columns: 36px minmax(0, 1fr) 42px;
50+
@@ -284,7 +272,15 @@
51+
background: var(--surface-inline, rgba(25, 12, 55, 0.74));
52+
cursor: pointer;
53+
padding: 8px;
54+
- overflow: hidden;
55+
+ overflow: visible;
15356
+ z-index: 1;
154-
}
155-
156-
.palette-manager-v2__source-entry:hover,
157-
.palette-manager-v2__source-entry:focus-within {
158-
- grid-column: 1 / -1;
57+
+}
58+
+
59+
+.palette-manager-v2__source-tile:hover,
60+
+.palette-manager-v2__source-tile:focus,
61+
+.palette-manager-v2__source-tile:focus-within {
15962
+ z-index: 20;
63+
+ box-shadow: 0 0 0 2px rgba(221, 214, 254, 0.34), 0 12px 24px rgba(0, 0, 0, 0.28);
16064
}
16165

162-
.palette-manager-v2__swatch-row {
163-
@@ -273,6 +309,10 @@
164-
165-
.palette-manager-v2__source-details {
166-
display: none;
167-
+ position: absolute;
168-
+ top: calc(100% + 8px);
169-
+ left: 0;
170-
+ z-index: 30;
66+
.palette-manager-v2__source-tile:focus-visible {
67+
@@ -314,6 +310,8 @@
68+
left: 0;
69+
z-index: 30;
17170
width: 100%;
71+
+ box-sizing: border-box;
72+
+ margin: 0;
17273
border: 1px solid rgba(255, 255, 255, 0.24);
17374
border-radius: 6px;
174-
@@ -282,6 +322,7 @@
175-
line-height: 1.35;
176-
padding: 8px;
177-
overflow-wrap: anywhere;
178-
+ box-shadow: 0 14px 28px rgba(0, 0, 0, 0.36);
75+
background: rgba(0, 0, 0, 0.88);
76+
@@ -325,8 +323,9 @@
77+
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.36);
17978
}
18079

181-
.palette-manager-v2__source-entry:hover .palette-manager-v2__source-details,
80+
-.palette-manager-v2__source-entry:hover .palette-manager-v2__source-details,
81+
-.palette-manager-v2__source-entry:focus-within .palette-manager-v2__source-details {
82+
+.palette-manager-v2__source-tile:hover .palette-manager-v2__source-details,
83+
+.palette-manager-v2__source-tile:focus .palette-manager-v2__source-details,
84+
+.palette-manager-v2__source-tile:focus-within .palette-manager-v2__source-details {
85+
display: grid;
86+
}
18287

183-
diff --git a/docs/dev/reports/PR_26124_030_report.md b/docs/dev/reports/PR_26124_030_report.md
88+
diff --git a/docs/dev/reports/PR_26124_031_report.md b/docs/dev/reports/PR_26124_031_report.md
18489
new file mode 100644
185-
index 00000000..00000000
90+
index 00000000..5bd33718
18691
--- /dev/null
187-
+++ b/docs/dev/reports/PR_26124_030_report.md
188-
@@ -0,0 +1,19 @@
189-
+# PR_26124_030 Palette Hover Popover And Sort Buttons
92+
+++ b/docs/dev/reports/PR_26124_031_report.md
93+
@@ -0,0 +1,18 @@
94+
+# PR_26124_031 Palette Hover Overlay No Layout Shift Report
95+
+
96+
+## Scope
97+
+- Updated Palette Manager V2 source swatch hover behavior only.
98+
+- No schema, sample JSON, games, workspace, toolState, session, or tools/shared changes.
19099
+
191-
+## Summary
192-
+- Converted source palette sort from dropdown to generated buttons: Original, Hue, Saturation, Brightness, Name.
193-
+- Added active source sort button styling through `.is-active` and `aria-pressed`.
194-
+- Kept User Palette sort as the existing dropdown.
195-
+- Changed source swatch details back to an absolute popover positioned below the hovered/focused tile.
196-
+- Kept the popover out of layout flow so it can overlap tiles below without shifting the grid.
197-
+- Kept source palette data sourced from `src/engine/paletteList.js` only.
100+
+## Changes
101+
+- Moved source hover details into the source tile DOM node so the tile is the fixed-size positioning anchor.
102+
+- Removed the source-entry wrapper and related CSS so hover no longer introduces an extra layout surface.
103+
+- Kept hover feedback to z-index and shadow on the existing tile box.
104+
+- Kept hover details as an absolutely positioned popover below the tile, hidden outside normal document flow.
198105
+
199106
+## Validation
200-
+- `node --check tools/palette-manager-v2/controls/SourcePaletteBrowserControl.js`
201-
+- `node --check tools/palette-manager-v2/modules/PaletteManagerApp.js`
202-
+- `node --check tools/palette-manager-v2/main.js`
203-
+- `node --check tools/common/PaletteSortService.js`
204-
+- `git diff --check` passed with line-ending warnings only.
107+
+- `node --check tools/palette-manager-v2/modules/SwatchRow.js`
108+
+- `git diff --check`
205109
+
206-
+## Package
207-
+- Delta ZIP: `tmp/PR_26124_030-palette-hover-popover-and-sort-buttons_delta.zip`
110+
+## Packaging
111+
+- Delta ZIP: `tmp/PR_26124_031-palette-hover-overlay-no-layout-shift_delta.zip`

tools/palette-manager-v2/modules/SwatchRow.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@ export class SwatchRow {
3131
}
3232

3333
static createSourceTile(documentRef, swatch, options = {}) {
34-
const entry = documentRef.createElement("div");
35-
entry.className = "palette-manager-v2__source-entry";
36-
3734
const tile = documentRef.createElement("div");
3835
tile.className = "palette-manager-v2__source-tile";
3936
tile.tabIndex = 0;
@@ -68,9 +65,8 @@ export class SwatchRow {
6865

6966
const details = SwatchRow.createDetailsBlock(documentRef, swatch, "palette-manager-v2__source-details");
7067

71-
tile.append(chip, tack);
72-
entry.append(tile, details);
73-
return entry;
68+
tile.append(chip, tack, details);
69+
return tile;
7470
}
7571

7672
static createDetailsBlock(documentRef, swatch, className) {

0 commit comments

Comments
 (0)