diff --git a/CHANGELOG.md b/CHANGELOG.md index 44bbcc175..3db16bbf5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,14 @@ the bottom tab bar. The rail is the first view built with Vue and Pinia, which now ship with the app; see docs/VUE-MIGRATION.md (samehkhamis). +- The Data, Authoring, Weather, Recent Imagery, Context, CCTV, Street Level + and Radio panels are one component, `UiPanel` (`src/ui/kit/`), with the + same markup plus the kit's `ui-panel` classes, their titles translated by + `t()` rather than `data-i18n`; `RailPanel` binds it to the panel chrome + store. `PanelCollapseButton` drops its `initial` and `initialTitle` props + and takes `startCollapsed`, so before startup presents a panel its button + already shows that state's glyph, words and `aria-expanded` (samehkhamis). + - The page's panels, dock, cockpit and dialogs are Vue views with the same markup as before. Hosts that do not initialize Core's locale pass `localePicker: false` to `createApplicationControls` to drop the language diff --git a/docs/VUE-MIGRATION.md b/docs/VUE-MIGRATION.md index 402010caa..1cb7fdd52 100644 --- a/docs/VUE-MIGRATION.md +++ b/docs/VUE-MIGRATION.md @@ -36,14 +36,20 @@ one-panel rule too. Share links continue to record which panels are open. ## Component kit -`src/ui/kit/` holds the generic components: `UiRail`, `UiTooltip`, `UiIcon` -and `UiButton` so far, with more (`UiPanel`, `UiSegmented`, `UiSelect` and -so on) joining as the controls they serve are converted. Names, props, +`src/ui/kit/` holds the generic components: `UiRail`, `UiTooltip`, `UiIcon`, +`UiButton` and `UiPanel` so far, with more (`UiSegmented`, `UiSelect` and +so on) joining as the controls they serve are converted. One component +serves every use of a control, with props for the differences (`UiPanel`'s +`sideways`, `glow`, `scroll`), rather than a copy per panel. Names, props, events and class names follow Halfpixel's `ui-vue` kit so the two can merge later, but the components are written for this repository and styled by its existing stylesheet and custom properties; a kit class such as `ui-button--icon` carries no Core style yet, and a converted control keeps its own classes beside it. They contain no application state. +`UiPanel` takes ui-vue's `title`, slots and `v-model:collapsed`, but its +`collapsed` drives only the button and `ui-panel--collapsed`: the panel's +owner hides the body by writing the root's `collapsed` class at once, and +there is no `closable`, since Core's panels collapse rather than close. Controls share one skin, so every panel reads the same. Every scrolling surface carries the kit's `.ui-scroll` (ui-vue's name), which draws the thin @@ -64,8 +70,10 @@ a feature supplying only its accent. when a view needs one, mirroring state an owner already publishes, so there is never a second owner of the same state. 2. **Rail.** `PanelChrome` enforces one panel per side; the panel store - mirrors each side's panels and the rail renders from it. Each panel's - collapse button (`PanelCollapseButton`), the dock tray toggles and the + mirrors each side's panels and the rail renders from it. The rail panels + are `RailPanel`s: the kit's `UiPanel` drawing Core's panel markup, bound to the `gev-panel-chrome` + store, with their titles rendered by `t()` rather than `data-i18n`. Each panel's + collapse button, the dock tray toggles and the dock pins render the open and pinned state PanelChrome presents to the `gev-panel-chrome` store, and their clicks call it there. Each panel's root draws its own state from there too (`v-panel-state`: `collapsed`, @@ -75,7 +83,8 @@ a feature supplying only its accent. watcher, not on Vue's next render, because the rails measure a panel in the same task that opens or closes it. Done. 3. **Panels.** Each template and its wiring become a component, keeping ids, - classes, `data-i18n` text and CSS. The page places each view with a + classes and CSS; text a template renders with `t()` drops its `data-i18n` + marker. The page places each view with a `.gev-view` host (`display: contents`), and `mountApplicationViews()` (`src/ui/views/index.js`) renders the view into it before the page is translated and wired. `createApplication()` calls it too, removing the diff --git a/docs/panel-surfaces.md b/docs/panel-surfaces.md index e2fa75728..30f1be88c 100644 --- a/docs/panel-surfaces.md +++ b/docs/panel-surfaces.md @@ -25,7 +25,6 @@ surface. Keep the header and any outward popup outside the scrolling body: class="panel-surface-control" target="example-panel" :name="t('Example')" - initial="−" />
@@ -90,10 +89,15 @@ Do not copy the rail example's `.panel-collapse-btn` class into the dock: existing dock rules intentionally hide that class. The generic surface skin does not replace the dock's disclosure policy. -`PanelCollapseButton` (`src/ui/rail/`) renders a panel's collapse button -(`.panel-collapse-btn`) from the open state `PanelChrome` presents to the -`gev-panel-chrome` store, and its click asks `PanelChrome` to toggle the -panel through `setPanelCollapsed`. `PanelChrome` collects the buttons' +A panel with Core's standard header (glow, title, badges, divider, actions +and collapse button) is a `RailPanel` (`src/ui/rail/`): the kit's `UiPanel` +bound to the `gev-panel-chrome` store, with its state classes drawn by +`v-panel-state` and its button's click asking `PanelChrome` to toggle it. +A panel with its own header uses `PanelCollapseButton`, which renders the +collapse button (`.panel-collapse-btn`) from the open state `PanelChrome` +presents to that store; until then, both show their `startCollapsed` state, +which should match the markup's `collapsed` class. +Either way, the click reaches `PanelChrome`'s `setPanelCollapsed`. `PanelChrome` collects the buttons' targets at initialization and binds each panel's Escape. For additions present at startup, keep those hooks and the existing chrome lifecycle. A dynamically mounted panel must own its disclosure: its own state, button and diff --git a/src/contextTabKeyboard.test.mjs b/src/contextTabKeyboard.test.mjs index 71c1b8aa6..e3b5add68 100644 --- a/src/contextTabKeyboard.test.mjs +++ b/src/contextTabKeyboard.test.mjs @@ -97,11 +97,11 @@ test('Context async action buttons remain focused while busy', () => { test('Context header holds only its title and collapse control', () => { const panel = html.match(/
([\s\S]*?)<\/div>\s*
([\s\S]*?)<\/div>\s*
- `${tag}.${cls.split(' ').filter((name) => !/^ui-(button|focus)/.test(name)).join(' ')}`); + `${tag}.${cls.split(' ').filter((name) => !/^ui-/.test(name)).join(' ')}`); assert.deepEqual(tags, ['span.panel-title', 'span.panel-divider', 'button.panel-collapse-btn']); }); diff --git a/src/i18n/locales/de.json b/src/i18n/locales/de.json index fa56aaf84..3c6d8a2d8 100644 --- a/src/i18n/locales/de.json +++ b/src/i18n/locales/de.json @@ -746,7 +746,6 @@ "Search any location...": "Ort suchen...", "WEATHER": "WETTER", "CONTEXT": "KONTEXT", - "Expand panel": "Bereich ausklappen", "Context mode": "Kontextmodus", "CONTACTS": "KONTAKTE", "Cycles the nearest contacts of whatever type you select — planes, vessels, installations. Satellites track independently.": "Wechselt zwischen den nächstgelegenen Kontakten des gewählten Typs: Flugzeuge, Schiffe, Anlagen. Satelliten werden unabhängig verfolgt.", diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json index ebabfe1b2..3693b127f 100644 --- a/src/i18n/locales/es.json +++ b/src/i18n/locales/es.json @@ -176,7 +176,6 @@ "AVAILABLE INPUTS ONLY · NOT AN ALL-CLEAR": "SOLO DATOS DISPONIBLES · NO IMPLICA TODO DESPEJADO", "Enable cockpit weather effects": "Activar los efectos climáticos de la cabina", "CONTEXT": "CONTEXTO", - "Expand panel": "Expandir panel", "Context mode": "Modo de contexto", "CONTACTS": "CONTACTOS", "Cycles the nearest contacts of whatever type you select — planes, vessels, installations. Satellites track independently.": "Recorre los contactos más cercanos del tipo que elijas — aeronaves, buques, instalaciones. Los satélites se rastrean por separado.", diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json index e768b6a89..a3e967758 100644 --- a/src/i18n/locales/fr.json +++ b/src/i18n/locales/fr.json @@ -2791,7 +2791,6 @@ "RECENT IMAGERY": "IMAGERIE RÉCENTE", "Imagery days in the box": "Jours d'imagerie dans la zone", "CONTEXT": "CONTEXTE", - "Expand panel": "Développer le panneau", "Context mode": "Mode de contexte", "CONTACTS": "CONTACTS", "Cycles the nearest contacts of whatever type you select — planes, vessels, installations. Satellites track independently.": "Parcourt les contacts les plus proches du type sélectionné — avions, navires, installations. Les satellites sont suivis séparément.", diff --git a/src/i18n/locales/it.json b/src/i18n/locales/it.json index f1bf08e73..73cfdb2c4 100644 --- a/src/i18n/locales/it.json +++ b/src/i18n/locales/it.json @@ -2791,7 +2791,6 @@ "RECENT IMAGERY": "IMMAGINI RECENTI", "Imagery days in the box": "Giorni con immagini nel riquadro", "CONTEXT": "CONTESTO", - "Expand panel": "Espandi il pannello", "Context mode": "Modalità contesto", "CONTACTS": "CONTATTI", "Cycles the nearest contacts of whatever type you select — planes, vessels, installations. Satellites track independently.": "Scorre i contatti più vicini del tipo selezionato: aerei, navi, installazioni. I satelliti vengono tracciati in modo indipendente.", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 130308457..2968f050f 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -2649,7 +2649,6 @@ "RECENT IMAGERY": "最近の衛星画像", "Imagery days in the box": "範囲内の画像のある日", "CONTEXT": "コンテキスト", - "Expand panel": "パネルを展開", "Context mode": "コンテキストモード", "CONTACTS": "コンタクト", "Cycles the nearest contacts of whatever type you select — planes, vessels, installations. Satellites track independently.": "選択した種類(航空機、船舶、施設)の最寄りのコンタクトを順に切り替えます。衛星は独立して追跡されます。", diff --git a/src/i18n/locales/pt.json b/src/i18n/locales/pt.json index 6ba74601f..82f306093 100644 --- a/src/i18n/locales/pt.json +++ b/src/i18n/locales/pt.json @@ -2793,7 +2793,6 @@ "RECENT IMAGERY": "IMAGENS RECENTES", "Imagery days in the box": "Dias com imagens na área", "CONTEXT": "CONTEXTO", - "Expand panel": "Expandir painel", "Context mode": "Modo de contexto", "CONTACTS": "CONTATOS", "Cycles the nearest contacts of whatever type you select — planes, vessels, installations. Satellites track independently.": "Percorre os contatos mais próximos do tipo que você selecionar — aviões, embarcações, instalações. Os satélites são rastreados de forma independente.", diff --git a/src/i18n/locales/ru.json b/src/i18n/locales/ru.json index 7cc27b20c..746288a94 100644 --- a/src/i18n/locales/ru.json +++ b/src/i18n/locales/ru.json @@ -707,7 +707,6 @@ "FLIR": "FLIR", "Style": "Стиль", "STATION SITE": "САЙТ СТАНЦИИ", - "Expand panel": "Развернуть панель", "CONTACTS": "КОНТАКТЫ", "Cycles the nearest contacts of whatever type you select — planes, vessels, installations. Satellites track independently.": "Перебирает ближайшие контакты выбранного типа — самолёты, суда, объекты. Спутники отслеживаются отдельно.", "SEARCH NEARBY SITES": "ПОИСК ОБЪЕКТОВ РЯДОМ", diff --git a/src/i18n/locales/uk.json b/src/i18n/locales/uk.json index 238c94fef..c27ea6332 100644 --- a/src/i18n/locales/uk.json +++ b/src/i18n/locales/uk.json @@ -2861,7 +2861,6 @@ "RECENT IMAGERY": "СВІЖІ ЗНІМКИ", "Imagery days in the box": "Дні зі знімками в області", "CONTEXT": "КОНТЕКСТ", - "Expand panel": "Розгорнути панель", "Context mode": "Режим контексту", "CONTACTS": "КОНТАКТИ", "Cycles the nearest contacts of whatever type you select — planes, vessels, installations. Satellites track independently.": "Гортає найближчі контакти вибраного вами типу — літаки, судна, обʼєкти. Супутники відстежуються окремо.", diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json index d6b46cda6..dbffef805 100644 --- a/src/i18n/locales/zh-CN.json +++ b/src/i18n/locales/zh-CN.json @@ -2545,7 +2545,6 @@ "RECENT IMAGERY": "近期影像", "Imagery days in the box": "视野内的影像天数", "CONTEXT": "态势", - "Expand panel": "展开面板", "Context mode": "态势模式", "CONTACTS": "目标", "Cycles the nearest contacts of whatever type you select — planes, vessels, installations. Satellites track independently.": "循环切换你所选类型中最近的目标,包括飞机、船舶、设施。卫星独立跟踪。", diff --git a/src/i18n/locales/zh-Hant.json b/src/i18n/locales/zh-Hant.json index caa1f5948..f3f89690e 100644 --- a/src/i18n/locales/zh-Hant.json +++ b/src/i18n/locales/zh-Hant.json @@ -2533,7 +2533,6 @@ "RECENT IMAGERY": "近期影像", "Imagery days in the box": "視野內的影像天數", "CONTEXT": "態勢", - "Expand panel": "展開面板", "Context mode": "態勢模式", "CONTACTS": "目標", "Cycles the nearest contacts of whatever type you select — planes, vessels, installations. Satellites track independently.": "循環切換你所選類型中最近的目標,包括飛機、船舶、設施。衛星獨立跟蹤。", diff --git a/src/streetLevelMarkup.test.mjs b/src/streetLevelMarkup.test.mjs index 5cf9ed0a5..7e2e32314 100644 --- a/src/streetLevelMarkup.test.mjs +++ b/src/streetLevelMarkup.test.mjs @@ -18,7 +18,7 @@ const layoutController = read('src/ui/panelLayoutController.js'); test('Street Level is a collapsible right-rail panel that starts collapsed', () => { assert.match( html, - /