mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
feat(fires): caption the confidence switch and name the slider "Fire intensity (FRP)"
The switch had no visible word saying what ALL / NOMINAL+ / HIGH refer to, and "Min FRP" assumed the reader knows the acronym. The segmented row descriptor gains an optional visible `label` (a caption above the group); the fires row uses "Confidence" and "Fire intensity (FRP)", and the slider value reads "≥ N MW" so the threshold is still clear. Locales updated. Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
1 parent
22a87500c6
commit
1dad6b658a
9 files changed
+70
-18
No files matched your search
@@ -3008,9 +3008,10 @@
|
||||
"Every detection — many of the strongest fires are rated low confidence": "Todas las detecciones — muchos de los incendios más intensos tienen confianza baja",
|
||||
"Nominal and high confidence detections": "Detecciones de confianza nominal y alta",
|
||||
"High confidence detections only": "Solo detecciones de confianza alta",
|
||||
"Min FRP": "FRP mín.",
|
||||
"Fire intensity (FRP)": "Intensidad (FRP)",
|
||||
"≥ {value} MW": "≥ {value} MW",
|
||||
"Confidence": "Confianza",
|
||||
"Minimum fire radiative power, megawatts": "Potencia radiativa mínima del fuego, en megavatios",
|
||||
"{value} MW": "{value} MW",
|
||||
"fire intensity filter|All": "Todo",
|
||||
"Detection confidence": "Confianza de la detección",
|
||||
"{shown} of {total} shown": "{shown} de {total} mostrados"
|
||||
|
||||
@@ -3077,9 +3077,10 @@
|
||||
"Every detection — many of the strongest fires are rated low confidence": "Все детекции — у многих сильнейших пожаров достоверность низкая",
|
||||
"Nominal and high confidence detections": "Детекции средней и высокой достоверности",
|
||||
"High confidence detections only": "Только детекции высокой достоверности",
|
||||
"Min FRP": "Мин. FRP",
|
||||
"Fire intensity (FRP)": "Интенсивность (FRP)",
|
||||
"≥ {value} MW": "≥ {value} МВт",
|
||||
"Confidence": "Достоверность",
|
||||
"Minimum fire radiative power, megawatts": "Минимальная мощность излучения пожара, мегаватты",
|
||||
"{value} MW": "{value} МВт",
|
||||
"fire intensity filter|All": "Все",
|
||||
"Detection confidence": "Достоверность детекции",
|
||||
"{shown} of {total} shown": "показано {shown} из {total}"
|
||||
|
||||
@@ -2870,9 +2870,10 @@
|
||||
"Every detection — many of the strongest fires are rated low confidence": "所有检测——许多最强的火点置信度为低",
|
||||
"Nominal and high confidence detections": "标称和高置信度的检测",
|
||||
"High confidence detections only": "仅高置信度的检测",
|
||||
"Min FRP": "最小 FRP",
|
||||
"Fire intensity (FRP)": "火势强度 (FRP)",
|
||||
"≥ {value} MW": "≥ {value} MW",
|
||||
"Confidence": "置信度",
|
||||
"Minimum fire radiative power, megawatts": "最小火辐射功率(兆瓦)",
|
||||
"{value} MW": "{value} MW",
|
||||
"fire intensity filter|All": "全部",
|
||||
"Detection confidence": "检测置信度",
|
||||
"{shown} of {total} shown": "显示 {shown} / {total}"
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
"{value} KM",
|
||||
"{value} KM/H",
|
||||
"{value} KM/S",
|
||||
"{value} MW",
|
||||
"≥ {value} MW",
|
||||
"ΔE",
|
||||
"ΔN",
|
||||
"▲ {frp} MW"
|
||||
@@ -197,7 +197,7 @@
|
||||
"{type} SPD: {speed} HDG: {heading}",
|
||||
"{value} KM",
|
||||
"{value} KM/S",
|
||||
"{value} MW",
|
||||
"≥ {value} MW",
|
||||
"ΔE",
|
||||
"ΔN",
|
||||
"▲ {frp} MW"
|
||||
|
||||
@@ -135,11 +135,11 @@ export function createQueries({
|
||||
return {
|
||||
slider: {
|
||||
id: 'min-frp',
|
||||
label: t('Min FRP'),
|
||||
label: t('Fire intensity (FRP)'),
|
||||
ariaLabel: t('Minimum fire radiative power, megawatts'),
|
||||
valueText:
|
||||
minFrp > 0
|
||||
? t('{value} MW', { value: frpLabel(minFrp) })
|
||||
? t('≥ {value} MW', { value: frpLabel(minFrp) })
|
||||
: t('All', { context: 'fire intensity filter' }),
|
||||
min: 0,
|
||||
max: SLIDER_STEPS,
|
||||
@@ -156,6 +156,7 @@ export function createQueries({
|
||||
},
|
||||
segmented: {
|
||||
id: 'confidence',
|
||||
label: t('Confidence'),
|
||||
ariaLabel: t('Detection confidence'),
|
||||
options: FIRMS_CONFIDENCE_MODES.map((mode) => ({
|
||||
id: `confidence-${mode}`,
|
||||
|
||||
@@ -104,8 +104,9 @@ export function syncSliderBlock(container, slider, { before = null } = {}) {
|
||||
* Buttons carry `data-chip-id` like chips so the row's delegated click handler
|
||||
* can resolve them; the caller matches `.data-toggle-segment`.
|
||||
*
|
||||
* Descriptor: `{ id, ariaLabel, options: [{ id, label, title, active,
|
||||
* disabled, params }] }`.
|
||||
* Descriptor: `{ id, label, ariaLabel, options: [{ id, label, title, active,
|
||||
* disabled, params }] }` — `label` is an optional visible caption above the
|
||||
* buttons (the group itself is a flex item, so the caption is a sibling).
|
||||
* @param {HTMLElement} container The row's `.data-toggle-controls` node.
|
||||
* @param {?object} segmented Descriptor, or null to remove the group.
|
||||
* @param {{before?: ?Node}} [options] Insertion anchor for a new group.
|
||||
@@ -121,19 +122,38 @@ export function syncSegmentedGroup(
|
||||
const existing = [...container.children].find(
|
||||
(node) => node.dataset?.segmentedId,
|
||||
);
|
||||
const existingCaption = [...container.children].find(
|
||||
(node) => node.dataset?.segmentedCaptionFor,
|
||||
);
|
||||
if (!segmented) {
|
||||
existing?.remove();
|
||||
existingCaption?.remove();
|
||||
return null;
|
||||
}
|
||||
let group = existing;
|
||||
if (!group || group.dataset.segmentedId !== segmented.id) {
|
||||
existing?.remove();
|
||||
existingCaption?.remove();
|
||||
group = document.createElement('div');
|
||||
group.className = 'gev-segmented data-toggle-segmented';
|
||||
group.dataset.segmentedId = segmented.id;
|
||||
group.setAttribute('role', 'radiogroup');
|
||||
container.insertBefore(group, before);
|
||||
}
|
||||
let caption = [...container.children].find(
|
||||
(node) => node.dataset?.segmentedCaptionFor === segmented.id,
|
||||
);
|
||||
if (segmented.label) {
|
||||
if (!caption) {
|
||||
caption = document.createElement('span');
|
||||
caption.className = 'data-toggle-segmented-label';
|
||||
caption.dataset.segmentedCaptionFor = segmented.id;
|
||||
container.insertBefore(caption, group);
|
||||
}
|
||||
set(caption, 'textContent', segmented.label);
|
||||
} else {
|
||||
caption?.remove();
|
||||
}
|
||||
if (segmented.ariaLabel) attribute(group, 'aria-label', segmented.ariaLabel);
|
||||
const stale = new Map();
|
||||
for (const node of [...group.children])
|
||||
|
||||
@@ -24,6 +24,7 @@ const sliderDescriptor = (overrides = {}) => ({
|
||||
|
||||
const segmentedDescriptor = (active = 'all') => ({
|
||||
id: 'confidence',
|
||||
label: 'Confidence',
|
||||
ariaLabel: 'Detection confidence',
|
||||
options: ['all', 'nominal', 'high'].map((mode) => ({
|
||||
id: `confidence-${mode}`,
|
||||
@@ -96,6 +97,9 @@ test('the segmented group is a radiogroup whose active option reads checked, rec
|
||||
assert.equal(group.getAttribute('role'), 'radiogroup');
|
||||
assert.equal(group.getAttribute('aria-label'), 'Detection confidence');
|
||||
assert.ok(group.className.split(' ').includes('gev-segmented'));
|
||||
const caption = container.children[container.children.indexOf(group) - 1];
|
||||
assert.equal(caption.className, 'data-toggle-segmented-label');
|
||||
assert.equal(caption.textContent, 'Confidence');
|
||||
const buttons = group.children;
|
||||
assert.deepEqual(
|
||||
buttons.map((b) => [
|
||||
@@ -118,10 +122,18 @@ test('the segmented group is a radiogroup whose active option reads checked, rec
|
||||
assert.equal(buttons[0].getAttribute('aria-checked'), 'false');
|
||||
assert.equal(buttons[2].getAttribute('aria-checked'), 'true');
|
||||
assert.ok(buttons[2].classList.contains('active'));
|
||||
assert.equal(container.children.length, 2, 'caption and group');
|
||||
|
||||
syncSegmentedGroup(container, { ...segmentedDescriptor('high'), label: '' });
|
||||
assert.equal(
|
||||
container.querySelector('.data-toggle-segmented-label'),
|
||||
null,
|
||||
'an empty label removes the caption',
|
||||
);
|
||||
assert.equal(container.children.length, 1);
|
||||
|
||||
syncSegmentedGroup(container, null);
|
||||
assert.equal(container.children.length, 0);
|
||||
assert.equal(container.children.length, 0, 'null removes group and caption');
|
||||
});
|
||||
|
||||
test('a layer row dispatches slider input and switch clicks as user params', async () => {
|
||||
@@ -163,10 +175,15 @@ test('a layer row dispatches slider input and switch clicks as user params', asy
|
||||
row.querySelector('.data-toggle-controls-info').textContent,
|
||||
'12 of 40 shown',
|
||||
);
|
||||
// Order: switch, slider, info.
|
||||
// Order: switch caption, switch, slider, info.
|
||||
assert.deepEqual(
|
||||
container.children.map((node) => node.className.split(' ')[0]),
|
||||
['gev-segmented', 'data-toggle-slider', 'data-toggle-controls-info'],
|
||||
[
|
||||
'data-toggle-segmented-label',
|
||||
'gev-segmented',
|
||||
'data-toggle-slider',
|
||||
'data-toggle-controls-info',
|
||||
],
|
||||
);
|
||||
|
||||
const input = row.querySelector('.data-toggle-slider-input');
|
||||
|
||||
@@ -1043,6 +1043,16 @@ html:not([lang='en']) .data-row-list-text {
|
||||
max-width: 220px;
|
||||
}
|
||||
|
||||
.data-toggle-segmented-label {
|
||||
flex-basis: 100%;
|
||||
padding-top: 2px;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 8px;
|
||||
letter-spacing: 1.2px;
|
||||
color: var(--text-dim);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.data-toggle-controls .data-toggle-segment {
|
||||
min-height: 20px;
|
||||
padding: 3px 6px;
|
||||
|
||||
Reference in new issue
Block a user