From 28ca784d497372abe94249ac8fcabe0779d03ec7 Mon Sep 17 00:00:00 2001 From: Sameh Khamis Date: Sun, 1 Nov 2026 12:09:38 -0800 Subject: [PATCH] feat(kit): render the chips with UiChip and UiButton UiChip is a non-interactive label chip and renders the voice card's chips. The pressable chips and pills are pressed toggles, so the rail card chips, map source chips and city and place pills render through UiButton with pressedClass, keeping their classes, attributes and the classes other code adds. The pills now report aria-pressed. --- CHANGELOG.md | 5 ++++- docs/VUE-MIGRATION.md | 5 ++++- scripts/format-scope.json | 3 +++ scripts/package-boundaries.json | 12 ++++++++---- src/data/managerPanel.test.mjs | 4 +++- src/ui/kit/UiChip.test.mjs | 28 ++++++++++++++++++++++++++++ src/ui/kit/UiChip.vue | 19 +++++++++++++++++++ src/ui/rail/RailChips.vue | 12 +++++++----- src/ui/views/CommandDockView.vue | 6 +++--- src/ui/views/panels/VoiceCard.vue | 3 ++- 10 files changed, 81 insertions(+), 16 deletions(-) create mode 100644 src/ui/kit/UiChip.test.mjs create mode 100644 src/ui/kit/UiChip.vue diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f276b151..d32717dbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,7 +65,10 @@ what the answering action returned. The Display, Radio, RTL-SDR, Cockpit, mission replay and style parameter sliders are the kit's `UiSlider`, a native range input that emits numbers, and the pressed toggles are the - kit's `UiButton`, which gains `pressedClass`. `syncScopeControls` + kit's `UiButton`, which gains `pressedClass`; so are the rail card + chips, map source chips and location pills (which now report + `aria-pressed`), and the voice card's chips + are the kit's new `UiChip`. `syncScopeControls` (`./ui/display`) and `shouldHideCollapsedRightPanels` (`./ui/layout`) are deprecated: the Display view and the rail now own what they did. `./ui/input` imports Vue (samehkhamis). diff --git a/docs/VUE-MIGRATION.md b/docs/VUE-MIGRATION.md index 0aeec0ac2..d5fbc63fa 100644 --- a/docs/VUE-MIGRATION.md +++ b/docs/VUE-MIGRATION.md @@ -61,7 +61,10 @@ adapter is gone. `active`); its pressed classes toggle through `classList`, so a class other code adds (a voice highlight) survives a press. Every pressed toggle in the views is a `UiButton` except Bhote Koshi's cinematic button and the -tilt-map button, whose owners still write their state into the DOM. +tilt-map button, whose owners still write their state into the DOM. Core's +pressable chips and pills (rail card chips, map sources, cities and places) +are `UiButton`s too; `UiChip` is ui-vue's non-interactive label chip, used by +the voice card. `UiSlider` keeps the native range input where ui-vue draws a `role="slider"` track, so Core's keyboard, form behaviour and styling stay; it takes ui-vue's `v-model`, `min`, `max`, `step` and `disabled`, emits a diff --git a/scripts/format-scope.json b/scripts/format-scope.json index d8814419c..57f479111 100644 --- a/scripts/format-scope.json +++ b/scripts/format-scope.json @@ -1132,6 +1132,9 @@ "src/ui/kit/UiSlider.test.mjs", "src/ui/kit/UiButton.vue", "src/ui/kit/UiButton.test.mjs", + "src/ui/kit/UiChip.vue", + "src/ui/kit/UiChip.test.mjs", + "src/ui/rail/RailChips.vue", "src/ui/displayPanelActions.test.mjs", "src/testSupport/vueDom.mjs", "build/vueLoader.js", diff --git a/scripts/package-boundaries.json b/scripts/package-boundaries.json index 8d5c468db..fb68740ae 100644 --- a/scripts/package-boundaries.json +++ b/scripts/package-boundaries.json @@ -134,7 +134,8 @@ "src/ui/rail/PanelCollapseButton.vue", "src/ui/rail/panelState.js", "src/sdr/gain.js", - "src/ui/kit/UiSlider.js" + "src/ui/kit/UiSlider.js", + "src/ui/kit/UiChip.vue" ], "external": ["vue", "pinia"] }, @@ -667,7 +668,8 @@ "src/ui/rail/utcClock.js", "src/ui/rail/RailTimeline.vue", "src/ui/rail/rangeValue.js", - "src/ui/weather/WeatherReadout.vue" + "src/ui/weather/WeatherReadout.vue", + "src/ui/kit/UiButton.vue" ], "external": ["vue"] }, @@ -1066,7 +1068,8 @@ "src/ui/stores/panelChrome.js", "src/ui/rail/panelState.js", "src/ui/rail/fieldValue.js", - "src/ui/kit/UiSlider.js" + "src/ui/kit/UiSlider.js", + "src/ui/kit/UiButton.vue" ], "external": ["cesium", "vue", "pinia"] }, @@ -3076,7 +3079,8 @@ "src/ui/stores/panelChrome.js", "src/ui/rail/panelState.js", "src/ui/rail/fieldValue.js", - "src/ui/kit/UiSlider.js" + "src/ui/kit/UiSlider.js", + "src/ui/kit/UiButton.vue" ], "external": [ "hls.js", diff --git a/src/data/managerPanel.test.mjs b/src/data/managerPanel.test.mjs index 51cfd3d23..d8b089255 100644 --- a/src/data/managerPanel.test.mjs +++ b/src/data/managerPanel.test.mjs @@ -898,7 +898,9 @@ test('row info and legends reconcile in place while chips retain focus and order controls.chips.push({ id: 'two', label: 'Two' }); manager._refreshTogglePanel(); assert.deepEqual( - [...block.children].map((node) => node.className), + [...block.children].map((node) => + [...node.classList].filter((name) => !name.startsWith('ui-')).join(' '), + ), [ 'data-toggle-chip chip-idle', 'data-toggle-chip chip-idle', diff --git a/src/ui/kit/UiChip.test.mjs b/src/ui/kit/UiChip.test.mjs new file mode 100644 index 000000000..68253212e --- /dev/null +++ b/src/ui/kit/UiChip.test.mjs @@ -0,0 +1,28 @@ +import { after, before, test } from 'node:test'; +import assert from 'node:assert/strict'; +import { installVueDom } from '../../testSupport/vueDom.mjs'; + +let mount; +let UiChip; +let uninstall; +before(async () => { + uninstall = installVueDom(); + ({ mount } = await import('@vue/test-utils')); + UiChip = (await import('./UiChip.vue')).default; +}); +after(() => uninstall()); + +test('a chip is a span with the kit classes and Core’s', async () => { + const wrapper = mount(UiChip, { + attrs: { class: 'gev-voice-card-chip', 'data-tone': 'ok' }, + slots: { default: 'ok' }, + }); + const chip = wrapper.get('span'); + assert.deepEqual(chip.classes(), ['ui-chip', 'gev-voice-card-chip']); + assert.equal(chip.attributes('data-tone'), 'ok'); + assert.equal(chip.text(), 'ok'); + await wrapper.setProps({ selected: true, mono: true }); + assert.ok(chip.classes().includes('ui-chip--selected')); + assert.ok(chip.classes().includes('ui-mono')); + wrapper.unmount(); +}); diff --git a/src/ui/kit/UiChip.vue b/src/ui/kit/UiChip.vue new file mode 100644 index 000000000..f2d590a7e --- /dev/null +++ b/src/ui/kit/UiChip.vue @@ -0,0 +1,19 @@ + + + diff --git a/src/ui/rail/RailChips.vue b/src/ui/rail/RailChips.vue index d912be6fa..5f04372d9 100644 --- a/src/ui/rail/RailChips.vue +++ b/src/ui/rail/RailChips.vue @@ -1,4 +1,6 @@ diff --git a/src/ui/views/CommandDockView.vue b/src/ui/views/CommandDockView.vue index c34e37286..552d0e739 100644 --- a/src/ui/views/CommandDockView.vue +++ b/src/ui/views/CommandDockView.vue @@ -117,7 +117,7 @@ function selectMapSource(chip) { MAP SOURCE {{ mapSource ? mapSource.status.text : '3D' }} -
+
{{ chip.label }}{{ chip.requirement }}
@@ -145,10 +145,10 @@ function selectMapSource(chip) {
-
+
{{ poi.key }}{{ poi.name }}
-
+
{{ city.label }}