From 3f104f0091b1cd4d77e7924a3e1be183749348a9 Mon Sep 17 00:00:00 2001 From: Cory Rylan Date: Fri, 31 Jul 2026 15:40:34 -0500 Subject: [PATCH 1/2] fix(core): size height calculation - only set explicit computed height when size is applied, prevents specificity and rounding errors on standard single selects Signed-off-by: Cory Rylan --- projects/core/.visual/pagination.dark.png | 4 ++-- projects/core/.visual/pagination.png | 4 ++-- projects/core/.visual/select.dark.png | 4 ++-- projects/core/.visual/select.png | 4 ++-- projects/core/src/select/select.css | 8 ++++++-- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/projects/core/.visual/pagination.dark.png b/projects/core/.visual/pagination.dark.png index 1358893f9..a14fb4933 100644 --- a/projects/core/.visual/pagination.dark.png +++ b/projects/core/.visual/pagination.dark.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:250a084f548e8deb75819f8be3c4c82b8969c41c1949d3ab643a20f2569813b0 -size 15903 +oid sha256:640e83f556161ce3881611f5d145befc3cec50d5d64f00227f403e04aaa69b9c +size 15770 diff --git a/projects/core/.visual/pagination.png b/projects/core/.visual/pagination.png index 06768f636..66a346660 100644 --- a/projects/core/.visual/pagination.png +++ b/projects/core/.visual/pagination.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:087ae06f0a3d0ff4f794542a6c070028b7101944b27c9e99728853952717005e -size 15337 +oid sha256:1fa7669b7ccb636b048c54a0fea7e375e39716c47de9bbb9c05de25cac019d41 +size 15208 diff --git a/projects/core/.visual/select.dark.png b/projects/core/.visual/select.dark.png index b8a19c51c..08f7b9086 100644 --- a/projects/core/.visual/select.dark.png +++ b/projects/core/.visual/select.dark.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f416092d0c6cc6b78e0f45a5a41744fe25b00f895b6c1aae9903810bb09a7bbd -size 54914 +oid sha256:2f7d61859b1fe6bfdf88f7d9372430076047ecf1acf88340ffff6f6e06e1db38 +size 54656 diff --git a/projects/core/.visual/select.png b/projects/core/.visual/select.png index 254bc5002..0a7fd3bdd 100644 --- a/projects/core/.visual/select.png +++ b/projects/core/.visual/select.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:60bb957f7720434f776cc5089733a2818d657c69cf46d12572e8f51e841ecdd6 -size 51555 +oid sha256:9b5a086676af009876702a974509cb59b6455df89dd9bf133db978a63fdfb66c +size 51212 diff --git a/projects/core/src/select/select.css b/projects/core/src/select/select.css index cc2aa282e..b19941094 100644 --- a/projects/core/src/select/select.css +++ b/projects/core/src/select/select.css @@ -23,8 +23,7 @@ --_icon-color: var(--color); --_gap: var(--gap); --size: 1; - --_row-height: calc(var(--height) * var(--size) + var(--gap) * (var(--size) - 1)); - --_height: calc(var(--_row-height) + var(--nve-ref-border-width-md) * 2); + --_height: var(--height); contain: initial; width: var(--width); max-width: var(--max-width); @@ -139,6 +138,11 @@ opacity: 0 !important; } +:host(:state(size)) { + --_row-height: calc(var(--height) * var(--size) + var(--gap) * (var(--size) - 1)); + --_height: calc(var(--_row-height) + var(--nve-ref-border-width-md) * 2); +} + :host(:state(size)) [input] { overflow: auto; display: block; From 5bc62fdff7bd5c88ce33f9bb75823dc745103bb9 Mon Sep 17 00:00:00 2001 From: Cory Rylan Date: Tue, 4 Aug 2026 18:55:39 -0500 Subject: [PATCH 2/2] fix(core): update fit-text input to native field sizing Signed-off-by: Cory Rylan --- projects/core/.visual/toolbar.dark.png | 4 +- projects/core/.visual/toolbar.png | 4 +- projects/core/src/forms/control/control.css | 10 +++ .../core/src/forms/control/control.test.ts | 70 ++++++------------- projects/core/src/forms/control/control.ts | 30 -------- projects/core/src/input/input.css | 4 -- 6 files changed, 36 insertions(+), 86 deletions(-) diff --git a/projects/core/.visual/toolbar.dark.png b/projects/core/.visual/toolbar.dark.png index c83ab70cd..62503cbdb 100644 --- a/projects/core/.visual/toolbar.dark.png +++ b/projects/core/.visual/toolbar.dark.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d8d3894884767aa56de8fccb96089e7430b8651a754a69c3b1ea9da659c64c5e -size 23851 +oid sha256:5f3be506275015df28c5de0a76ebf19878b300812d36f8786a3989f42b5ef9cf +size 24215 diff --git a/projects/core/.visual/toolbar.png b/projects/core/.visual/toolbar.png index 5e211858c..312e4df5c 100644 --- a/projects/core/.visual/toolbar.png +++ b/projects/core/.visual/toolbar.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:895dc2cf10be6f6312cd43e52c26fed8441c3e1f4ce32fe204e15f57b9b32d16 -size 24445 +oid sha256:af679e6ee80b83aeb1cb346146bc68840833a9f9257f6df701456968994f01b8 +size 24352 diff --git a/projects/core/src/forms/control/control.css b/projects/core/src/forms/control/control.css index 98161c28b..424a753a1 100644 --- a/projects/core/src/forms/control/control.css +++ b/projects/core/src/forms/control/control.css @@ -53,6 +53,16 @@ --width: fit-content; } +:host([fit-text]) { + --max-width: fit-content; + --width: fit-content; + + ::slotted(input), + ::slotted(select) { + field-sizing: content; + } +} + :host([nve-control]) ::slotted(input:focus), :host([nve-control]) ::slotted(select:focus), :host([nve-control]) ::slotted(textarea:focus), diff --git a/projects/core/src/forms/control/control.test.ts b/projects/core/src/forms/control/control.test.ts index b1510a2a8..f61e09b0c 100644 --- a/projects/core/src/forms/control/control.test.ts +++ b/projects/core/src/forms/control/control.test.ts @@ -269,27 +269,40 @@ describe(`${Control.metadata.tag}: fit-text input`, () => { removeFixture(fixture); }); - it('should set control width to input text character width', async () => { + it('should set input width to text content width', async () => { + await new Promise(resolve => requestAnimationFrame(resolve)); + const fitTextWidth = input.getBoundingClientRect().width; + + element.fitText = false; await elementIsStable(element); - expect(element.style.getPropertyValue('--control-width')).toBe(`4ch`); + await new Promise(resolve => requestAnimationFrame(resolve)); + + expect(fitTextWidth).toBeLessThan(input.getBoundingClientRect().width); }); - it('should update control width to input text character width', async () => { - await elementIsStable(element); + it('should update input width to text content width', async () => { + await new Promise(resolve => requestAnimationFrame(resolve)); + const initialWidth = input.getBoundingClientRect().width; + input.value = '123456789012345678901234567890'; input.dispatchEvent(new Event('input')); await elementIsStable(element); - expect(element.style.getPropertyValue('--control-width')).toBe(`30ch`); + await new Promise(resolve => requestAnimationFrame(resolve)); + + expect(input.getBoundingClientRect().width).toBeGreaterThan(initialWidth); }); - it('should update control width to input text character width with icon offset', async () => { - await elementIsStable(element); + it('should update input width to native date content width', async () => { + await new Promise(resolve => requestAnimationFrame(resolve)); + const textWidth = input.getBoundingClientRect().width; + input.type = 'date'; input.value = ''; input.dispatchEvent(new Event('input')); await elementIsStable(element); - expect(element.style.getPropertyValue('--control-width')).toBe(`4ch`); - expect(input.style.maxWidth).toBe(`2ch`); + await new Promise(resolve => requestAnimationFrame(resolve)); + + expect(input.getBoundingClientRect().width).toBeGreaterThan(textWidth); }); }); @@ -321,42 +334,3 @@ describe(`${Control.metadata.tag}: fit-content input`, () => { expect(Math.floor(input.getBoundingClientRect().width) < 250).toBe(true); }); }); - -describe(`${Control.metadata.tag}: fit-text select`, () => { - let fixture: HTMLElement; - let element: Control; - let input: HTMLSelectElement; - - beforeEach(async () => { - fixture = await createFixture(html` - - - - message - - `); - element = fixture.querySelector(Control.metadata.tag); - input = fixture.querySelector('select'); - await elementIsStable(element); - }); - - afterEach(() => { - removeFixture(fixture); - }); - - it('should set control width to input text character width', async () => { - await elementIsStable(element); - expect(element.style.getPropertyValue('--control-width')).toBe(`12ch`); - }); - - it('should update control width to input text character width', async () => { - await elementIsStable(element); - input.value = '2'; - input.dispatchEvent(new Event('change')); - await elementIsStable(element); - expect(element.style.getPropertyValue('--control-width')).toBe(`19ch`); - }); -}); diff --git a/projects/core/src/forms/control/control.ts b/projects/core/src/forms/control/control.ts index 82e4d3688..673a90bb7 100644 --- a/projects/core/src/forms/control/control.ts +++ b/projects/core/src/forms/control/control.ts @@ -171,10 +171,6 @@ export class Control extends LitElement { super.disconnectedCallback(); this.shadowRoot!.removeEventListener('slotchange', this.#onRootSlotchange); this.shadowRoot!.removeEventListener('slotchange', this.#onInputSlotchange); - if (this.fitText && this.input) { - this.input.removeEventListener('input', this.#onFitTextUpdate); - this.input.removeEventListener('change', this.#onFitTextUpdate); - } this.#observers.forEach(observer => observer.disconnect()); this.#observers.length = 0; } @@ -184,7 +180,6 @@ export class Control extends LitElement { if (this.input && this.#observers.length === 0) { this.#setupInput(); - this.#setupFitText(); } }; @@ -242,31 +237,6 @@ export class Control extends LitElement { this.#updateAssociations(); }; - #setupFitText() { - if (this.fitText) { - this.#getCharacterWidth(); - this.input.addEventListener('input', this.#onFitTextUpdate); - this.input.addEventListener('change', this.#onFitTextUpdate); - } - } - - #onFitTextUpdate = () => { - this.#getCharacterWidth(); - }; - - #getCharacterWidth() { - if (this.input.tagName === 'INPUT') { - const offset = this.input.type !== 'text' ? 4 : 0; - this.style.setProperty('--control-width', `${this.input.value.length + offset}ch`); - this.input.style.setProperty('max-width', `${this.input.value.length + 2}ch`, 'important'); - } else if (this.input.tagName === 'SELECT') { - this.style.setProperty( - '--control-width', - `${(this.input as unknown as HTMLSelectElement).options[(this.input as unknown as HTMLSelectElement).selectedIndex]!.textContent!.length + 4}ch` - ); - } - } - #polyfillShowPicker() { if (!this.input.showPicker) { this.input.showPicker = () => this.input.focus(); diff --git a/projects/core/src/input/input.css b/projects/core/src/input/input.css index 723194cab..f48e846aa 100644 --- a/projects/core/src/input/input.css +++ b/projects/core/src/input/input.css @@ -22,10 +22,6 @@ min-width: var(--min-width); } -:host([fit-text]) { - --max-width: fit-content; -} - [input] { height: var(--height); background: var(--background);