@aiaiai-pt/design-system 0.30.0 → 0.31.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -449,6 +449,12 @@
|
|
|
449
449
|
"required" on the field — not just the disconnected visual hint below.
|
|
450
450
|
Passed through the DS field components' `{...rest}` onto the input. -->
|
|
451
451
|
{@const ariaRequired = parameter.required ? "true" : undefined}
|
|
452
|
+
<!-- #252 — a parameter with `editable: false` (visibility.editable === false)
|
|
453
|
+
renders READ-ONLY: its value (e.g. a logged-in citizen's prefilled
|
|
454
|
+
identity — name/email) is shown but not editable. The value still rides
|
|
455
|
+
the payload; the field just can't be changed. Default (undefined/true) is
|
|
456
|
+
editable, so this is purely additive. -->
|
|
457
|
+
{@const editable = parameter.editable !== false}
|
|
452
458
|
{#if type === "enum" || type === "select" || enumOptions(parameter).length}
|
|
453
459
|
<Select
|
|
454
460
|
label={String(parameter.label ?? key)}
|
|
@@ -465,6 +471,7 @@
|
|
|
465
471
|
name={key}
|
|
466
472
|
type="number"
|
|
467
473
|
value={String(values[key] ?? "")}
|
|
474
|
+
readonly={!editable}
|
|
468
475
|
oninput={(event: Event) => {
|
|
469
476
|
const value = (event.target as HTMLInputElement).value;
|
|
470
477
|
setValue(key, value === "" ? "" : Number(value));
|
|
@@ -554,7 +561,8 @@
|
|
|
554
561
|
<Input
|
|
555
562
|
label={String(parameter.label ?? key)}
|
|
556
563
|
name={key}
|
|
557
|
-
value={String(values[key] ?? "")}
|
|
564
|
+
value={String(values[key] ?? initialValue(parameter) ?? "")}
|
|
565
|
+
readonly={!editable}
|
|
558
566
|
oninput={(event: Event) => setValue(key, (event.target as HTMLInputElement).value)}
|
|
559
567
|
aria-required={ariaRequired}
|
|
560
568
|
/>
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
<script>
|
|
16
16
|
import { fromLonLat } from 'ol/proj.js';
|
|
17
17
|
import { boundingExtent } from 'ol/extent.js';
|
|
18
|
-
import { createTileLayer, createMapStyles, createOverlayLayers, watchTheme, renderMapError } from './map-utils.js';
|
|
18
|
+
import { createTileLayer, createMapStyles, createOverlayLayers, clusterTooltipLabel, watchTheme, renderMapError } from './map-utils.js';
|
|
19
19
|
|
|
20
20
|
let {
|
|
21
21
|
/** @type {{ id: string, lon: number, lat: number, label?: string, [key: string]: any }[]} */
|
|
@@ -286,21 +286,21 @@
|
|
|
286
286
|
return;
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
-
const clustered = feature.get('features');
|
|
290
289
|
if (container) container.style.cursor = 'pointer';
|
|
291
290
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
tooltipEl.style.display = 'block';
|
|
297
|
-
tooltipOverlay.setPosition(feature.getGeometry()?.getCoordinates());
|
|
298
|
-
}
|
|
299
|
-
} else {
|
|
300
|
-
tooltipEl.textContent = `${clustered?.length ?? 0} items`;
|
|
301
|
-
tooltipEl.style.display = 'block';
|
|
302
|
-
tooltipOverlay.setPosition(feature.getGeometry()?.getCoordinates());
|
|
291
|
+
const label = clusterTooltipLabel(feature);
|
|
292
|
+
if (label == null) {
|
|
293
|
+
tooltipEl.style.display = 'none';
|
|
294
|
+
return;
|
|
303
295
|
}
|
|
296
|
+
tooltipEl.textContent = label;
|
|
297
|
+
tooltipEl.style.display = 'block';
|
|
298
|
+
// Cluster features carry a Point geometry; raw overlay (file/WFS)
|
|
299
|
+
// features can be polygons/lines, so anchor those at the pointer.
|
|
300
|
+
const coords = feature.get('features') === undefined
|
|
301
|
+
? evt.coordinate
|
|
302
|
+
: feature.getGeometry()?.getCoordinates();
|
|
303
|
+
tooltipOverlay.setPosition(coords);
|
|
304
304
|
});
|
|
305
305
|
|
|
306
306
|
// `popup` opens the anchored ficha-resumo overlay; otherwise `onclick`
|
package/components/map-utils.js
CHANGED
|
@@ -374,7 +374,8 @@ export function getHeatmapGradient(el) {
|
|
|
374
374
|
*/
|
|
375
375
|
export function geoStylerToFlat(style) {
|
|
376
376
|
if (!style || typeof style !== "object") return {};
|
|
377
|
-
if (!Array.isArray(style.rules))
|
|
377
|
+
if (!Array.isArray(style.rules))
|
|
378
|
+
return /** @type {OverlayLayerStyle} */ (style);
|
|
378
379
|
const sym = style.rules?.[0]?.symbolizers?.[0];
|
|
379
380
|
if (!sym || typeof sym !== "object") return {};
|
|
380
381
|
/** @type {OverlayLayerStyle} */
|
|
@@ -389,8 +390,10 @@ export function geoStylerToFlat(style) {
|
|
|
389
390
|
if (typeof sym.width === "number") flat.strokeWidth = sym.width;
|
|
390
391
|
} else if (sym.kind === "Fill") {
|
|
391
392
|
if (typeof sym.color === "string") flat.fillColor = sym.color;
|
|
392
|
-
if (typeof sym.outlineColor === "string")
|
|
393
|
-
|
|
393
|
+
if (typeof sym.outlineColor === "string")
|
|
394
|
+
flat.strokeColor = sym.outlineColor;
|
|
395
|
+
if (typeof sym.outlineWidth === "number")
|
|
396
|
+
flat.strokeWidth = sym.outlineWidth;
|
|
394
397
|
}
|
|
395
398
|
return flat;
|
|
396
399
|
}
|
|
@@ -449,7 +452,9 @@ export async function createOverlayLayers(defs, styles) {
|
|
|
449
452
|
const point = new Style({
|
|
450
453
|
image: new CircleStyle({
|
|
451
454
|
radius: flat.pointRadius ?? 6,
|
|
452
|
-
fill: new Fill({
|
|
455
|
+
fill: new Fill({
|
|
456
|
+
color: flat.pointColor ?? flat.strokeColor ?? "#3366cc",
|
|
457
|
+
}),
|
|
453
458
|
stroke,
|
|
454
459
|
}),
|
|
455
460
|
});
|
|
@@ -484,6 +489,39 @@ export async function createOverlayLayers(defs, styles) {
|
|
|
484
489
|
});
|
|
485
490
|
}
|
|
486
491
|
|
|
492
|
+
// ─── Hover tooltip label ─────────────────────────────────────────
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Decide the hover-tooltip text for a feature under the pointer.
|
|
496
|
+
*
|
|
497
|
+
* Markers passed via the `markers` prop go through an OL Cluster source, so
|
|
498
|
+
* each rendered feature carries a `features` collection: one entry → that
|
|
499
|
+
* marker's label; many → "N items". Features served from an overlay `layers`
|
|
500
|
+
* def (a file/WFS source) are added to a plain VectorSource and have NO
|
|
501
|
+
* `features` collection (`undefined`) — for those, fall back to the feature's
|
|
502
|
+
* own descriptive props rather than rendering the misleading "0 items".
|
|
503
|
+
*
|
|
504
|
+
* @param {{ get: (key: string) => any }} feature — an OL Feature (or any
|
|
505
|
+
* object exposing `.get(key)`).
|
|
506
|
+
* @returns {string | null} the label to show, or null to hide the tooltip.
|
|
507
|
+
*/
|
|
508
|
+
export function clusterTooltipLabel(feature) {
|
|
509
|
+
const clustered = feature.get("features");
|
|
510
|
+
if (clustered === undefined) {
|
|
511
|
+
const label =
|
|
512
|
+
feature.get("name") ??
|
|
513
|
+
feature.get("title") ??
|
|
514
|
+
feature.get("label") ??
|
|
515
|
+
feature.get("markerData")?.label;
|
|
516
|
+
return label != null && String(label) !== "" ? String(label) : null;
|
|
517
|
+
}
|
|
518
|
+
if (clustered.length === 1) {
|
|
519
|
+
const data = clustered[0].get("markerData");
|
|
520
|
+
return data?.label ?? null;
|
|
521
|
+
}
|
|
522
|
+
return `${clustered.length} items`;
|
|
523
|
+
}
|
|
524
|
+
|
|
487
525
|
// ─── Boundary (#187 D-e prep) ────────────────────────────────────
|
|
488
526
|
|
|
489
527
|
/**
|