Skip to content

Commit cdf2537

Browse files
author
DavidQ
committed
Fix Palette Manager independent user/source sort and size controls with scrollable tile areas - PR_26124_035-palette-independent-sort-size-controls
1 parent de403bc commit cdf2537

9 files changed

Lines changed: 410 additions & 244 deletions
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# PR_26124_035 Palette Independent Sort Size Controls Report
2+
3+
## Scope
4+
- Updated Palette Manager V2 user/source sort and size controls only.
5+
- Kept controls class-based and did not touch tools/shared, schemas, sample JSON, games, workspace, toolState, or session logic.
6+
7+
## Changes
8+
- User Palette size now uses radio-style Small, Medium, and Large buttons.
9+
- Source Palette size now uses its own radio-style Small, Medium, and Large buttons.
10+
- User and source tile sizes are stored as independent in-memory UI state.
11+
- Removed shared tile size state; changing one palette size no longer changes the other palette tile grid.
12+
- User and source sort buttons retain visible active state and continue to sort cloned display rows/data without mutating palette JSON or source palette data.
13+
- User Palette tile grid remains the vertical scroll surface below fixed controls inside its accordion.
14+
15+
## Validation
16+
- `node --check tools/palette-manager-v2/controls/UserPaletteControl.js`
17+
- `node --check tools/palette-manager-v2/controls/SourcePaletteBrowserControl.js`
18+
- `node --check tools/palette-manager-v2/modules/PaletteManagerApp.js`
19+
- `git diff --check`
20+
21+
## Packaging
22+
- Delta ZIP: `tmp/PR_26124_035-palette-independent-sort-size-controls_delta.zip`
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# PR_26124_035 User Palette Scrollbar Completion Report
2+
3+
## Scope
4+
- Completed the User Palette scrollbar requirement for Palette Manager V2.
5+
- CSS/layout only; no JSON, schema, sample, games, workspace, toolState, session, or tools/shared changes.
6+
7+
## Changes
8+
- Center palette accordions now hide overflow at the accordion boundary.
9+
- Center accordion content uses fixed controls above the tile grid.
10+
- Palette tile grids now use a zero flex basis and internal overflow scrolling.
11+
- User Palette tile grid explicitly owns vertical scrolling when swatches exceed available height.
12+
13+
## Validation
14+
- `git diff --check`
15+
- `git diff --cached --check`
16+
17+
## Packaging
18+
- Delta ZIP: `tmp/PR_26124_035-user-palette-scrollbar-completion_delta.zip`

docs/dev/reports/codex_changed_files.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
docs/dev/reports/PR_26124_034_report.md
1+
docs/dev/reports/PR_26124_035_report.md
2+
docs/dev/reports/PR_26124_035_scrollbar_completion_report.md
23
tools/palette-manager-v2/controls/SourcePaletteBrowserControl.js
34
tools/palette-manager-v2/controls/UserPaletteControl.js
45
tools/palette-manager-v2/index.html

docs/dev/reports/codex_review.diff

Lines changed: 263 additions & 200 deletions
Large diffs are not rendered by default.

tools/palette-manager-v2/controls/SourcePaletteBrowserControl.js

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,14 @@ export class SourcePaletteBrowserControl {
1717
this.refs.sourceSearchInput.addEventListener("input", () => {
1818
this.app.setSourceSearch(this.refs.sourceSearchInput.value);
1919
});
20-
this.refs.sourceSwatchSizeSelect.addEventListener("change", () => {
21-
this.app.setSwatchSize(this.refs.sourceSwatchSizeSelect.value);
22-
});
2320
}
2421

2522
render() {
2623
this.refs.sourcePaletteSelect.value = this.app.getCurrentSourcePaletteId();
2724
this.refs.sourceSearchInput.value = this.app.getSourceSearch();
28-
this.refs.sourceSwatchSizeSelect.value = this.app.getSwatchSize();
29-
this.refs.sourceSwatchList.dataset.swatchSize = this.app.getSwatchSize();
25+
this.refs.sourceSwatchList.dataset.swatchSize = this.app.getSourceSwatchSize();
3026
this.renderActiveSortButton();
27+
this.renderActiveSizeButton();
3128
this.refs.sourceSwatchList.replaceChildren();
3229

3330
const visibleSwatches = this.app.getVisibleSourceSwatches();
@@ -83,13 +80,20 @@ export class SourcePaletteBrowserControl {
8380
}
8481

8582
renderSizeOptions() {
86-
this.refs.sourceSwatchSizeSelect.replaceChildren();
83+
this.refs.sourceSwatchSizeControls.replaceChildren();
8784
this.app.getSwatchSizeOptions().forEach((size) => {
88-
const option = this.document.createElement("option");
89-
option.value = size.value;
90-
option.textContent = size.label;
91-
this.refs.sourceSwatchSizeSelect.appendChild(option);
85+
const button = this.document.createElement("button");
86+
button.type = "button";
87+
button.className = "palette-manager-v2__sort-button palette-manager-v2__size-button";
88+
button.dataset.swatchSize = size.value;
89+
button.setAttribute("role", "radio");
90+
button.textContent = size.label;
91+
button.addEventListener("click", () => {
92+
this.app.setSourceSwatchSize(size.value);
93+
});
94+
this.refs.sourceSwatchSizeControls.appendChild(button);
9295
});
96+
this.renderActiveSizeButton();
9397
}
9498

9599
renderActiveSortButton() {
@@ -100,4 +104,13 @@ export class SourcePaletteBrowserControl {
100104
button.setAttribute("aria-checked", String(isActive));
101105
});
102106
}
107+
108+
renderActiveSizeButton() {
109+
const activeSize = this.app.getSourceSwatchSize();
110+
this.refs.sourceSwatchSizeControls.querySelectorAll("[data-swatch-size]").forEach((button) => {
111+
const isActive = button.dataset.swatchSize === activeSize;
112+
button.classList.toggle("is-active", isActive);
113+
button.setAttribute("aria-checked", String(isActive));
114+
});
115+
}
103116
}

tools/palette-manager-v2/controls/UserPaletteControl.js

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,14 @@ export class UserPaletteControl {
1010
bind() {
1111
this.renderSortOptions();
1212
this.renderSizeOptions();
13-
this.refs.userSwatchSizeSelect.addEventListener("change", () => {
14-
this.app.setSwatchSize(this.refs.userSwatchSizeSelect.value);
15-
});
1613
}
1714

1815
render() {
1916
const swatches = this.app.getUserSwatches();
2017
this.refs.userPaletteCount.textContent = `${swatches.length} user swatches`;
21-
this.refs.userSwatchSizeSelect.value = this.app.getSwatchSize();
22-
this.refs.userSwatchList.dataset.swatchSize = this.app.getSwatchSize();
18+
this.refs.userSwatchList.dataset.swatchSize = this.app.getUserSwatchSize();
2319
this.renderActiveSortButton();
20+
this.renderActiveSizeButton();
2421
this.refs.userSwatchList.replaceChildren();
2522

2623
if (swatches.length === 0) {
@@ -59,13 +56,20 @@ export class UserPaletteControl {
5956
}
6057

6158
renderSizeOptions() {
62-
this.refs.userSwatchSizeSelect.replaceChildren();
59+
this.refs.userSwatchSizeControls.replaceChildren();
6360
this.app.getSwatchSizeOptions().forEach((size) => {
64-
const option = this.document.createElement("option");
65-
option.value = size.value;
66-
option.textContent = size.label;
67-
this.refs.userSwatchSizeSelect.appendChild(option);
61+
const button = this.document.createElement("button");
62+
button.type = "button";
63+
button.className = "palette-manager-v2__sort-button palette-manager-v2__size-button";
64+
button.dataset.swatchSize = size.value;
65+
button.setAttribute("role", "radio");
66+
button.textContent = size.label;
67+
button.addEventListener("click", () => {
68+
this.app.setUserSwatchSize(size.value);
69+
});
70+
this.refs.userSwatchSizeControls.appendChild(button);
6871
});
72+
this.renderActiveSizeButton();
6973
}
7074

7175
renderActiveSortButton() {
@@ -76,4 +80,13 @@ export class UserPaletteControl {
7680
button.setAttribute("aria-checked", String(isActive));
7781
});
7882
}
83+
84+
renderActiveSizeButton() {
85+
const activeSize = this.app.getUserSwatchSize();
86+
this.refs.userSwatchSizeControls.querySelectorAll("[data-swatch-size]").forEach((button) => {
87+
const isActive = button.dataset.swatchSize === activeSize;
88+
button.classList.toggle("is-active", isActive);
89+
button.setAttribute("aria-checked", String(isActive));
90+
});
91+
}
7992
}

tools/palette-manager-v2/index.html

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,10 @@ <h2 id="editorTitle">Selected Swatch</h2>
6969
<span>Sort user palette</span>
7070
<div id="userPaletteSortControls" class="palette-manager-v2__sort-buttons" role="radiogroup" aria-label="Sort user palette"></div>
7171
</div>
72-
<label class="palette-manager-v2__field palette-manager-v2__size-field">
73-
Size
74-
<select id="userSwatchSizeSelect"></select>
75-
</label>
72+
<div class="palette-manager-v2__field palette-manager-v2__size-field">
73+
<span>Size</span>
74+
<div id="userSwatchSizeControls" class="palette-manager-v2__size-buttons" role="radiogroup" aria-label="User palette tile size"></div>
75+
</div>
7676
</div>
7777
<div id="userPaletteCount" class="palette-manager-v2__meta">0 user swatches</div>
7878
<div id="userSwatchList" class="palette-manager-v2__tile-grid palette-manager-v2__tile-grid--user" aria-label="User palette swatches"></div>
@@ -98,10 +98,10 @@ <h2 id="editorTitle">Selected Swatch</h2>
9898
<span>Sort source palette</span>
9999
<div id="sourcePaletteSortControls" class="palette-manager-v2__sort-buttons" role="radiogroup" aria-label="Sort source palette"></div>
100100
</div>
101-
<label class="palette-manager-v2__field palette-manager-v2__size-field">
102-
Size
103-
<select id="sourceSwatchSizeSelect"></select>
104-
</label>
101+
<div class="palette-manager-v2__field palette-manager-v2__size-field">
102+
<span>Size</span>
103+
<div id="sourceSwatchSizeControls" class="palette-manager-v2__size-buttons" role="radiogroup" aria-label="Source palette tile size"></div>
104+
</div>
105105
</div>
106106
<div id="sourceSwatchList" class="palette-manager-v2__tile-grid palette-manager-v2__tile-grid--source" aria-label="Browse palette swatches"></div>
107107
</div>

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

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ import { cloneSwatch, normalizeHex, sanitizeText, swatchKey } from "./paletteUti
99
const REQUIRED_REF_IDS = Object.freeze([
1010
"userPaletteCount",
1111
"userPaletteSortControls",
12-
"userSwatchSizeSelect",
12+
"userSwatchSizeControls",
1313
"userSwatchList",
1414
"sourcePaletteSelect",
1515
"sourceSearchInput",
1616
"sourcePaletteSortControls",
17-
"sourceSwatchSizeSelect",
17+
"sourceSwatchSizeControls",
1818
"sourceSwatchList",
1919
"editorTitle",
2020
"selectedSwatchPreview",
@@ -36,11 +36,15 @@ const REQUIRED_REF_IDS = Object.freeze([
3636
]);
3737

3838
const SWATCH_SIZE_OPTIONS = Object.freeze([
39-
Object.freeze({ value: "small", label: "Sm" }),
40-
Object.freeze({ value: "medium", label: "Me" }),
41-
Object.freeze({ value: "large", label: "Lg" })
39+
Object.freeze({ value: "small", label: "Small" }),
40+
Object.freeze({ value: "medium", label: "Medium" }),
41+
Object.freeze({ value: "large", label: "Large" })
4242
]);
4343

44+
function isValidSwatchSize(swatchSize) {
45+
return SWATCH_SIZE_OPTIONS.some((size) => size.value === swatchSize);
46+
}
47+
4448
function collectRefs(documentRef) {
4549
return REQUIRED_REF_IDS.reduce((refs, id) => {
4650
const element = documentRef.getElementById(id);
@@ -75,7 +79,8 @@ export class PaletteManagerApp {
7579
sourceSearch: "",
7680
userSortMode: "hue",
7781
sourceSortMode: "hue",
78-
swatchSize: "large",
82+
userSwatchSize: "large",
83+
sourceSwatchSize: "large",
7984
errors: [],
8085
status: "Ready."
8186
};
@@ -144,8 +149,12 @@ export class PaletteManagerApp {
144149
return this.state.sourceSortMode;
145150
}
146151

147-
getSwatchSize() {
148-
return this.state.swatchSize;
152+
getUserSwatchSize() {
153+
return this.state.userSwatchSize;
154+
}
155+
156+
getSourceSwatchSize() {
157+
return this.state.sourceSwatchSize;
149158
}
150159

151160
getSourcePaletteIds() {
@@ -219,11 +228,19 @@ export class PaletteManagerApp {
219228
this.render();
220229
}
221230

222-
setSwatchSize(swatchSize) {
223-
if (!SWATCH_SIZE_OPTIONS.some((size) => size.value === swatchSize)) {
231+
setUserSwatchSize(swatchSize) {
232+
if (!isValidSwatchSize(swatchSize)) {
233+
return;
234+
}
235+
this.state.userSwatchSize = swatchSize;
236+
this.render();
237+
}
238+
239+
setSourceSwatchSize(swatchSize) {
240+
if (!isValidSwatchSize(swatchSize)) {
224241
return;
225242
}
226-
this.state.swatchSize = swatchSize;
243+
this.state.sourceSwatchSize = swatchSize;
227244
this.render();
228245
}
229246

tools/palette-manager-v2/paletteManagerV2.css

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,21 @@
4646

4747
.palette-manager-v2__panel--center .palette-manager-v2__accordion[open] {
4848
flex: 1 1 0;
49+
overflow: hidden;
4950
}
5051

5152
.palette-manager-v2__panel--center .palette-manager-v2__accordion-content {
5253
display: flex;
53-
flex: 1 1 auto;
54+
flex: 1 1 0;
5455
flex-direction: column;
5556
min-height: 0;
5657
overflow: hidden;
5758
}
5859

60+
.palette-manager-v2__panel--center .palette-manager-v2__accordion-content > :not(.palette-manager-v2__tile-grid) {
61+
flex: 0 0 auto;
62+
}
63+
5964
.palette-manager-v2__accordion {
6065
border: 1px solid var(--line, rgba(221, 214, 254, 0.26));
6166
border-radius: 8px;
@@ -143,15 +148,26 @@
143148
flex-wrap: wrap;
144149
}
145150

146-
.palette-manager-v2__sort-row > .palette-manager-v2__field {
151+
.palette-manager-v2__sort-row > .palette-manager-v2__field:not(.palette-manager-v2__size-field) {
147152
flex: 1 1 320px;
148153
}
149154

150155
.palette-manager-v2__size-field {
151-
flex: 0 0 84px;
156+
flex: 0 0 auto;
152157
margin-left: auto;
153158
}
154159

160+
.palette-manager-v2__size-buttons {
161+
display: flex;
162+
justify-content: flex-end;
163+
gap: 8px;
164+
flex-wrap: wrap;
165+
}
166+
167+
.palette-manager-v2__size-button {
168+
min-width: 70px;
169+
}
170+
155171
.palette-manager-v2__sort-button {
156172
min-height: 34px;
157173
border: 1px solid var(--line, rgba(221, 214, 254, 0.26));
@@ -190,11 +206,13 @@
190206
display: grid;
191207
grid-template-columns: repeat(auto-fill, minmax(var(--swatch-tile-width), 1fr));
192208
gap: 10px;
193-
flex: 1 1 auto;
209+
flex: 1 1 0;
210+
height: 0;
194211
min-height: 0;
195212
overflow-y: auto;
196213
overflow-x: visible;
197214
align-content: start;
215+
overscroll-behavior: contain;
198216
padding-right: 4px;
199217
padding-bottom: 150px;
200218
}
@@ -222,6 +240,7 @@
222240

223241
.palette-manager-v2__tile-grid--user {
224242
max-height: none;
243+
overflow-y: auto;
225244
}
226245

227246
.palette-manager-v2__tile-grid--source {

0 commit comments

Comments
 (0)