@12-apps/ui 6.86.0 → 6.86.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.
- package/dist/_internal/components/data-display/Alert/Alert.js +2 -2
- package/dist/_internal/components/data-display/CardKit/card-actions-context.js +3 -3
- package/dist/_internal/components/data-display/CardKit/use-remove-confirm.js +4 -4
- package/dist/_internal/components/data-display/Carousel/Carousel.js +2 -2
- package/dist/_internal/components/data-display/DataGrid/DataGrid.js +2 -2
- package/dist/_internal/components/data-display/DataViews/DataViewsGrid.js +20 -20
- package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +24 -24
- package/dist/_internal/components/data-display/DataViews/base-list-card-geometry.js +1 -1
- package/dist/_internal/components/data-display/DataViews/base-list-card-layout.js +1 -1
- package/dist/_internal/components/data-display/DataViews/base-list-card.js +2 -2
- package/dist/_internal/components/data-display/DataViews/data-views-grid-bodies.js +4 -4
- package/dist/_internal/components/data-display/DataViews/data-views-grid-parts.js +13 -13
- package/dist/_internal/components/data-display/DataViews/data-views-scope-tabs.js +2 -2
- package/dist/_internal/components/data-display/DataViews/data-views-shell-toolbar.js +4 -4
- package/dist/_internal/components/data-display/DataViews/data-views-table-parts.js +3 -3
- package/dist/_internal/components/data-display/DataViews/data-views-toolbar.js +3 -3
- package/dist/_internal/components/data-display/DataViews/use-data-views-state.js +2 -2
- package/dist/_internal/components/data-display/FleetMap/FleetBody.js +13 -13
- package/dist/_internal/components/data-display/FleetMap/FleetCanvas.js +12 -12
- package/dist/_internal/components/data-display/FleetMap/FleetMap.js +14 -14
- package/dist/_internal/components/data-display/Lightbox/Lightbox.js +4 -4
- package/dist/_internal/components/data-display/MapPreview/MapDecorations.js +2 -2
- package/dist/_internal/components/data-display/MapPreview/MapLayers.js +2 -2
- package/dist/_internal/components/data-display/MapPreview/MapMarkers.js +2 -2
- package/dist/_internal/components/data-display/MapPreview/MapPreview.hooks.js +2 -2
- package/dist/_internal/components/data-display/MapPreview/MapPreview.js +11 -11
- package/dist/_internal/components/data-display/MapPreview/MapTiles.js +2 -2
- package/dist/_internal/components/data-display/MapPreview/StaticMapView.js +4 -4
- package/dist/_internal/components/data-display/MapPreview/useMapPreview.js +3 -3
- package/dist/_internal/components/data-display/RegionMap/RegionMap.js +3 -2
- package/dist/_internal/components/data-display/RegionMap/RegionMap.labels.js +12 -0
- package/dist/_internal/components/data-display/RegionMap/RegionMap.labels.js.map +1 -0
- package/dist/_internal/components/data-display/RegionMap/RegionMapDrawing.js +2 -1
- package/dist/_internal/components/data-display/index.js +27 -26
- package/dist/_internal/components/feedback/NotificationStatusNotice/NotificationStatusNotice.js +3 -3
- package/dist/_internal/components/feedback/SequenceResolver/SequenceResolver.js +4 -4
- package/dist/_internal/components/feedback/SequenceResolver/SequenceResolver.parts.js +3 -3
- package/dist/_internal/components/feedback/index.js +5 -5
- package/dist/_internal/components/index.js +30 -29
- package/dist/{chunk-T6QVM5Q5.js → chunk-4LUWZ6UE.js} +2 -2
- package/dist/{chunk-JKIKHWD2.js → chunk-5NT2M2JI.js} +4 -4
- package/dist/{chunk-I7JUAHUO.js → chunk-65KJLPF5.js} +3 -3
- package/dist/{chunk-C74PDNTF.js → chunk-67ADHGA2.js} +2 -2
- package/dist/{chunk-ZLVRCQPG.js → chunk-A5CKUJP2.js} +5 -5
- package/dist/{chunk-A63WU4UZ.js → chunk-AKE6KVQG.js} +70 -41
- package/dist/chunk-AKE6KVQG.js.map +1 -0
- package/dist/{chunk-XMWTVDQN.js → chunk-B32554CQ.js} +12 -12
- package/dist/{chunk-OA6T2ME3.js → chunk-BLZDSCIZ.js} +4 -4
- package/dist/{chunk-OZYUE2BI.js → chunk-CJUK32UH.js} +4 -4
- package/dist/{chunk-HWPQKHN2.js → chunk-COKPPU42.js} +7 -7
- package/dist/{chunk-U4WAAZXJ.js → chunk-DVX4HDNP.js} +2 -2
- package/dist/{chunk-CESGZRVQ.js → chunk-EMSMJRP3.js} +4 -4
- package/dist/{chunk-IFX5KP65.js → chunk-EXVCVXFX.js} +4 -4
- package/dist/{chunk-U3HGQHEO.js → chunk-H7CZRKN7.js} +4 -4
- package/dist/{chunk-5ZC7CNU4.js → chunk-I2LMLQGQ.js} +2 -2
- package/dist/chunk-J3AWGTJY.js +29 -0
- package/dist/chunk-J3AWGTJY.js.map +1 -0
- package/dist/{chunk-K6VEN5VN.js → chunk-K3PMMW23.js} +4 -4
- package/dist/{chunk-YHPSPFU5.js → chunk-LIN4A2BW.js} +4 -4
- package/dist/{chunk-LXMFHD7M.js → chunk-LTSVYILY.js} +4 -4
- package/dist/{chunk-IEAPXS57.js → chunk-MW7P2C2C.js} +2 -2
- package/dist/{chunk-JVTZ7FWR.js → chunk-NCI74EOS.js} +4 -4
- package/dist/{chunk-IMCSY2SI.js → chunk-OKRITX4S.js} +7 -7
- package/dist/{chunk-LRQT4MQO.js → chunk-PJNIJIE6.js} +2 -2
- package/dist/{chunk-2WVHZIWP.js → chunk-QSRZOXQI.js} +2 -2
- package/dist/{chunk-IF5UY3CT.js → chunk-QVDEGBME.js} +4 -4
- package/dist/{chunk-XNMU32XC.js → chunk-TMUGBHHG.js} +2 -2
- package/dist/{chunk-AWUFS2ZC.js → chunk-U2DRE36S.js} +4 -4
- package/dist/{chunk-NEYZV6V5.js → chunk-UAOE3ZPC.js} +6 -6
- package/dist/{chunk-VHGHF537.js → chunk-V4CWJ7Q6.js} +2 -2
- package/dist/{chunk-3OL2TSEJ.js → chunk-WNNA6YYE.js} +2 -2
- package/dist/{chunk-T6YTDEY4.js → chunk-WXZCNHBX.js} +2 -2
- package/dist/{chunk-5XTPEA3O.js → chunk-ZHYLOB7Q.js} +2 -2
- package/dist/{chunk-ARMKSYEQ.js → chunk-ZO2GZRZS.js} +2 -2
- package/dist/data-display/Alert.js +2 -2
- package/dist/data-display/CardKit.js +7 -7
- package/dist/data-display/Carousel.js +2 -2
- package/dist/data-display/DataGrid.js +2 -2
- package/dist/data-display/DataViews.js +31 -31
- package/dist/data-display/FleetMap.js +14 -14
- package/dist/data-display/Lightbox.js +4 -4
- package/dist/data-display/MapPreview.js +11 -11
- package/dist/data-display/RegionMap.js +3 -2
- package/dist/feedback/NotificationStatusNotice.js +3 -3
- package/dist/feedback/SequenceResolver.js +4 -4
- package/dist/types/components/data-display/RegionMap/RegionMap.labels.d.ts +42 -0
- package/dist/types/components/data-display/RegionMap/RegionMap.labels.d.ts.map +1 -0
- package/dist/types/components/data-display/RegionMap/RegionMapDrawing.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/data-display/RegionMap/RegionMap.labels.ts +60 -0
- package/src/components/data-display/RegionMap/RegionMapDrawing.tsx +87 -45
- package/dist/chunk-A63WU4UZ.js.map +0 -1
- /package/dist/{chunk-T6QVM5Q5.js.map → chunk-4LUWZ6UE.js.map} +0 -0
- /package/dist/{chunk-JKIKHWD2.js.map → chunk-5NT2M2JI.js.map} +0 -0
- /package/dist/{chunk-I7JUAHUO.js.map → chunk-65KJLPF5.js.map} +0 -0
- /package/dist/{chunk-C74PDNTF.js.map → chunk-67ADHGA2.js.map} +0 -0
- /package/dist/{chunk-ZLVRCQPG.js.map → chunk-A5CKUJP2.js.map} +0 -0
- /package/dist/{chunk-XMWTVDQN.js.map → chunk-B32554CQ.js.map} +0 -0
- /package/dist/{chunk-OA6T2ME3.js.map → chunk-BLZDSCIZ.js.map} +0 -0
- /package/dist/{chunk-OZYUE2BI.js.map → chunk-CJUK32UH.js.map} +0 -0
- /package/dist/{chunk-HWPQKHN2.js.map → chunk-COKPPU42.js.map} +0 -0
- /package/dist/{chunk-U4WAAZXJ.js.map → chunk-DVX4HDNP.js.map} +0 -0
- /package/dist/{chunk-CESGZRVQ.js.map → chunk-EMSMJRP3.js.map} +0 -0
- /package/dist/{chunk-IFX5KP65.js.map → chunk-EXVCVXFX.js.map} +0 -0
- /package/dist/{chunk-U3HGQHEO.js.map → chunk-H7CZRKN7.js.map} +0 -0
- /package/dist/{chunk-5ZC7CNU4.js.map → chunk-I2LMLQGQ.js.map} +0 -0
- /package/dist/{chunk-K6VEN5VN.js.map → chunk-K3PMMW23.js.map} +0 -0
- /package/dist/{chunk-YHPSPFU5.js.map → chunk-LIN4A2BW.js.map} +0 -0
- /package/dist/{chunk-LXMFHD7M.js.map → chunk-LTSVYILY.js.map} +0 -0
- /package/dist/{chunk-IEAPXS57.js.map → chunk-MW7P2C2C.js.map} +0 -0
- /package/dist/{chunk-JVTZ7FWR.js.map → chunk-NCI74EOS.js.map} +0 -0
- /package/dist/{chunk-IMCSY2SI.js.map → chunk-OKRITX4S.js.map} +0 -0
- /package/dist/{chunk-LRQT4MQO.js.map → chunk-PJNIJIE6.js.map} +0 -0
- /package/dist/{chunk-2WVHZIWP.js.map → chunk-QSRZOXQI.js.map} +0 -0
- /package/dist/{chunk-IF5UY3CT.js.map → chunk-QVDEGBME.js.map} +0 -0
- /package/dist/{chunk-XNMU32XC.js.map → chunk-TMUGBHHG.js.map} +0 -0
- /package/dist/{chunk-AWUFS2ZC.js.map → chunk-U2DRE36S.js.map} +0 -0
- /package/dist/{chunk-NEYZV6V5.js.map → chunk-UAOE3ZPC.js.map} +0 -0
- /package/dist/{chunk-VHGHF537.js.map → chunk-V4CWJ7Q6.js.map} +0 -0
- /package/dist/{chunk-3OL2TSEJ.js.map → chunk-WNNA6YYE.js.map} +0 -0
- /package/dist/{chunk-T6YTDEY4.js.map → chunk-WXZCNHBX.js.map} +0 -0
- /package/dist/{chunk-5XTPEA3O.js.map → chunk-ZHYLOB7Q.js.map} +0 -0
- /package/dist/{chunk-ARMKSYEQ.js.map → chunk-ZO2GZRZS.js.map} +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** Where a label's centre may go along one axis so the whole label stays in view. */
|
|
2
|
+
export declare function clampCentre(wanted: number, half: number, span: number, gap: number): number;
|
|
3
|
+
/** An area that may carry a label, as {@link labelOrder} ranks it. */
|
|
4
|
+
interface Labelable {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
excluded?: boolean;
|
|
8
|
+
geometry: {
|
|
9
|
+
area: number;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The order labels claim their place in: highlighted first (what the person is
|
|
14
|
+
* looking for), then excluded, then the larger area, then by name. When two
|
|
15
|
+
* labels would overlap, the earlier one is drawn and the later one hidden.
|
|
16
|
+
*/
|
|
17
|
+
export declare function labelOrder<T extends Labelable>(areas: readonly T[], highlighted: ReadonlySet<string>): T[];
|
|
18
|
+
/** A label measured in the drawing: where it wants to be (fractions), and its size in px. */
|
|
19
|
+
export interface LabelBox {
|
|
20
|
+
fractionX: number;
|
|
21
|
+
fractionY: number;
|
|
22
|
+
spanX: number;
|
|
23
|
+
spanY: number;
|
|
24
|
+
}
|
|
25
|
+
/** Where each label goes, in px, and whether it is drawn at all. */
|
|
26
|
+
export interface PlacedLabel {
|
|
27
|
+
x: number;
|
|
28
|
+
y: number;
|
|
29
|
+
visible: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Lay labels out in priority order ({@link labelOrder}): each is pulled inside
|
|
33
|
+
* the drawing by its own size, and one whose box would overlap a label already
|
|
34
|
+
* drawn is hidden — the area keeps its name in its `<title>` and in the list,
|
|
35
|
+
* so hiding costs nothing but the clutter two names over each other made.
|
|
36
|
+
*/
|
|
37
|
+
export declare function placeLabels(labels: readonly LabelBox[], box: {
|
|
38
|
+
spanX: number;
|
|
39
|
+
spanY: number;
|
|
40
|
+
}, gap: number): PlacedLabel[];
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=RegionMap.labels.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RegionMap.labels.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/RegionMap/RegionMap.labels.ts"],"names":[],"mappings":"AAEA,qFAAqF;AACrF,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAG3F;AAED,sEAAsE;AACtE,UAAU,SAAS;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,SAAS,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAK1G;AAED,6FAA6F;AAC7F,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,oEAAoE;AACpE,MAAM,WAAW,WAAW;IAC1B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,EAAE,GAAG,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW,EAAE,CAU1H"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RegionMapDrawing.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/RegionMap/RegionMapDrawing.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAS,KAAK,KAAK,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,EAAsD,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACxG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"RegionMapDrawing.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/RegionMap/RegionMapDrawing.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAS,KAAK,KAAK,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,EAAsD,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACxG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGxD,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,cAAc,GAAG;QAAE,IAAI,EAAE,QAAQ,CAAA;KAAE,CAAC;IAC3C,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC;CACd;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,UAAU,EACV,cAAc,EACd,SAAS,EACT,KAAK,EACL,QAAQ,EACR,MAAM,EACN,KAAK,GACN,EAAE,qBAAqB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CA4D3C"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// How the labels over a RegionMap drawing are ranked and laid out.
|
|
2
|
+
|
|
3
|
+
/** Where a label's centre may go along one axis so the whole label stays in view. */
|
|
4
|
+
export function clampCentre(wanted: number, half: number, span: number, gap: number): number {
|
|
5
|
+
if (span <= 2 * (half + gap)) return span / 2;
|
|
6
|
+
return Math.min(span - half - gap, Math.max(half + gap, wanted));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** An area that may carry a label, as {@link labelOrder} ranks it. */
|
|
10
|
+
interface Labelable {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
excluded?: boolean;
|
|
14
|
+
geometry: { area: number };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The order labels claim their place in: highlighted first (what the person is
|
|
19
|
+
* looking for), then excluded, then the larger area, then by name. When two
|
|
20
|
+
* labels would overlap, the earlier one is drawn and the later one hidden.
|
|
21
|
+
*/
|
|
22
|
+
export function labelOrder<T extends Labelable>(areas: readonly T[], highlighted: ReadonlySet<string>): T[] {
|
|
23
|
+
const rank = (area: T): number => (highlighted.has(area.id) ? 0 : area.excluded ? 1 : 2);
|
|
24
|
+
return [...areas].sort(
|
|
25
|
+
(a, b) => rank(a) - rank(b) || b.geometry.area - a.geometry.area || a.name.localeCompare(b.name),
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** A label measured in the drawing: where it wants to be (fractions), and its size in px. */
|
|
30
|
+
export interface LabelBox {
|
|
31
|
+
fractionX: number;
|
|
32
|
+
fractionY: number;
|
|
33
|
+
spanX: number;
|
|
34
|
+
spanY: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Where each label goes, in px, and whether it is drawn at all. */
|
|
38
|
+
export interface PlacedLabel {
|
|
39
|
+
x: number;
|
|
40
|
+
y: number;
|
|
41
|
+
visible: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Lay labels out in priority order ({@link labelOrder}): each is pulled inside
|
|
46
|
+
* the drawing by its own size, and one whose box would overlap a label already
|
|
47
|
+
* drawn is hidden — the area keeps its name in its `<title>` and in the list,
|
|
48
|
+
* so hiding costs nothing but the clutter two names over each other made.
|
|
49
|
+
*/
|
|
50
|
+
export function placeLabels(labels: readonly LabelBox[], box: { spanX: number; spanY: number }, gap: number): PlacedLabel[] {
|
|
51
|
+
const kept: { x0: number; y0: number; x1: number; y1: number }[] = [];
|
|
52
|
+
return labels.map((label) => {
|
|
53
|
+
const x = clampCentre(label.fractionX * box.spanX, label.spanX / 2, box.spanX, gap);
|
|
54
|
+
const y = clampCentre(label.fractionY * box.spanY, label.spanY / 2, box.spanY, gap);
|
|
55
|
+
const rect = { x0: x - label.spanX / 2, y0: y - label.spanY / 2, x1: x + label.spanX / 2, y1: y + label.spanY / 2 };
|
|
56
|
+
const visible = !kept.some((o) => rect.x0 < o.x1 && o.x0 < rect.x1 && rect.y0 < o.y1 && o.y0 < rect.y1);
|
|
57
|
+
if (visible) kept.push(rect);
|
|
58
|
+
return { x, y, visible };
|
|
59
|
+
});
|
|
60
|
+
}
|
|
@@ -7,6 +7,7 @@ import { rem, remPx } from '../../../tokens/relative';
|
|
|
7
7
|
|
|
8
8
|
import { labelPosition, type AreaGeometry, type DrawnRegion, type PlaneBox } from './RegionMap.helpers';
|
|
9
9
|
import type { RegionMapModel } from './RegionMap.hooks';
|
|
10
|
+
import { labelOrder, placeLabels } from './RegionMap.labels';
|
|
10
11
|
|
|
11
12
|
export interface RegionMapDrawingProps {
|
|
12
13
|
model: RegionMapModel & { view: PlaneBox };
|
|
@@ -43,7 +44,11 @@ export function RegionMapDrawing({
|
|
|
43
44
|
const { view, drawn } = model;
|
|
44
45
|
const wanted = new Set(highlightedIds);
|
|
45
46
|
const highlighted = drawn.filter((d) => wanted.has(d.id));
|
|
46
|
-
|
|
47
|
+
// Highlighted, then excluded, then larger, then by name: the order labels
|
|
48
|
+
// claim their place in, so a collision hides the less important one.
|
|
49
|
+
const labelled = labelOrder(drawn.filter((d) => d.excluded || wanted.has(d.id)), wanted);
|
|
50
|
+
const boxRef = React.useRef<HTMLDivElement>(null);
|
|
51
|
+
useLabelPlacement(boxRef, labelled.map((d) => d.id).join('|'), remPx(theme, 4));
|
|
47
52
|
const base = drawn.map((d) => ({ id: d.id, name: d.name, geometry: d.geometry, outside: d.outside === true }));
|
|
48
53
|
return (
|
|
49
54
|
<Box
|
|
@@ -60,6 +65,7 @@ export function RegionMapDrawing({
|
|
|
60
65
|
bgcolor: 'background.paper',
|
|
61
66
|
}}
|
|
62
67
|
data-testid={`${testId}-canvas`}
|
|
68
|
+
ref={boxRef}
|
|
63
69
|
>
|
|
64
70
|
<svg
|
|
65
71
|
viewBox={`${view.minX} ${view.minY} ${view.spanX} ${view.spanY}`}
|
|
@@ -73,19 +79,7 @@ export function RegionMapDrawing({
|
|
|
73
79
|
<BaseAreas areas={base} testId={testId} theme={theme} />
|
|
74
80
|
<ExcludedAreas drawn={drawn} hatchId={hatchId} testId={testId} theme={theme} />
|
|
75
81
|
<Circles radii={model.radii} theme={theme} />
|
|
76
|
-
{highlighted
|
|
77
|
-
<path
|
|
78
|
-
key={`h-${d.id}`}
|
|
79
|
-
d={d.geometry.path}
|
|
80
|
-
fillRule="evenodd"
|
|
81
|
-
vectorEffect="non-scaling-stroke"
|
|
82
|
-
fill={alpha(theme.palette.primary.main, 0.3)}
|
|
83
|
-
stroke={theme.palette.primary.main}
|
|
84
|
-
strokeWidth={2.5}
|
|
85
|
-
pointerEvents="none"
|
|
86
|
-
data-testid={`${testId}-highlight-${d.id}`}
|
|
87
|
-
/>
|
|
88
|
-
))}
|
|
82
|
+
<Highlights areas={highlighted} testId={testId} theme={theme} />
|
|
89
83
|
{showCenter && (
|
|
90
84
|
<circle
|
|
91
85
|
cx={0}
|
|
@@ -107,6 +101,35 @@ export function RegionMapDrawing({
|
|
|
107
101
|
);
|
|
108
102
|
}
|
|
109
103
|
|
|
104
|
+
/** The highlighted areas, drawn over everything but the centre. */
|
|
105
|
+
function Highlights({
|
|
106
|
+
areas,
|
|
107
|
+
testId,
|
|
108
|
+
theme,
|
|
109
|
+
}: {
|
|
110
|
+
areas: readonly DrawnRegion[];
|
|
111
|
+
testId: string;
|
|
112
|
+
theme: Theme;
|
|
113
|
+
}): React.JSX.Element {
|
|
114
|
+
return (
|
|
115
|
+
<>
|
|
116
|
+
{areas.map((d) => (
|
|
117
|
+
<path
|
|
118
|
+
key={`h-${d.id}`}
|
|
119
|
+
d={d.geometry.path}
|
|
120
|
+
fillRule="evenodd"
|
|
121
|
+
vectorEffect="non-scaling-stroke"
|
|
122
|
+
fill={alpha(theme.palette.primary.main, 0.3)}
|
|
123
|
+
stroke={theme.palette.primary.main}
|
|
124
|
+
strokeWidth={2.5}
|
|
125
|
+
pointerEvents="none"
|
|
126
|
+
data-testid={`${testId}-highlight-${d.id}`}
|
|
127
|
+
/>
|
|
128
|
+
))}
|
|
129
|
+
</>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
110
133
|
/** The hatch an excluded area is filled with: lines, not a colour. */
|
|
111
134
|
function Hatch({ id, pitch, theme }: { id: string; pitch: number; theme: Theme }): React.JSX.Element {
|
|
112
135
|
return (
|
|
@@ -225,16 +248,52 @@ function Circles({ radii, theme }: { radii: readonly number[]; theme: Theme }):
|
|
|
225
248
|
);
|
|
226
249
|
}
|
|
227
250
|
|
|
228
|
-
/**
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
251
|
+
/**
|
|
252
|
+
* Places every label the drawing carries, in their DOM order (the priority
|
|
253
|
+
* order), and hides one that would overlap a label already drawn
|
|
254
|
+
* (`placeLabels`). Measured by a ResizeObserver, never in a layout effect: its
|
|
255
|
+
* first call comes after the browser's own layout, so mounting a label on a
|
|
256
|
+
* tap forces no synchronous reflow of the drawing.
|
|
257
|
+
*/
|
|
258
|
+
function useLabelPlacement(boxRef: React.RefObject<HTMLDivElement | null>, key: string, gap: number): void {
|
|
259
|
+
React.useEffect(() => {
|
|
260
|
+
const box = boxRef.current;
|
|
261
|
+
if (!box || typeof ResizeObserver === 'undefined') return undefined;
|
|
262
|
+
const labels = Array.from(box.querySelectorAll<HTMLElement>('[data-region-label]'));
|
|
263
|
+
const place = (): void => {
|
|
264
|
+
const spanX = box.clientWidth;
|
|
265
|
+
const spanY = box.clientHeight;
|
|
266
|
+
if (spanX === 0 || spanY === 0) return;
|
|
267
|
+
const placed = placeLabels(
|
|
268
|
+
labels.map((label) => ({
|
|
269
|
+
fractionX: Number(label.dataset.fractionX),
|
|
270
|
+
fractionY: Number(label.dataset.fractionY),
|
|
271
|
+
spanX: label.offsetWidth,
|
|
272
|
+
spanY: label.offsetHeight,
|
|
273
|
+
})),
|
|
274
|
+
{ spanX, spanY },
|
|
275
|
+
gap,
|
|
276
|
+
);
|
|
277
|
+
placed.forEach(({ x, y, visible }, index) => {
|
|
278
|
+
const label = labels[index]!;
|
|
279
|
+
label.style.left = `${(x / spanX) * 100}%`;
|
|
280
|
+
label.style.top = `${(y / spanY) * 100}%`;
|
|
281
|
+
label.style.visibility = visible ? 'visible' : 'hidden';
|
|
282
|
+
label.dataset.hidden = visible ? 'false' : 'true';
|
|
283
|
+
});
|
|
284
|
+
};
|
|
285
|
+
const observer = new ResizeObserver(place);
|
|
286
|
+
observer.observe(box);
|
|
287
|
+
for (const label of labels) observer.observe(label);
|
|
288
|
+
return () => observer.disconnect();
|
|
289
|
+
}, [boxRef, key, gap]);
|
|
232
290
|
}
|
|
233
291
|
|
|
234
292
|
/**
|
|
235
293
|
* An area's name over the drawing, at the theme's type size whatever the
|
|
236
|
-
* width — and
|
|
237
|
-
* the
|
|
294
|
+
* width. Placed — and hidden on a collision — by {@link useLabelPlacement}.
|
|
295
|
+
* Decorative (`aria-hidden`): the area's name is its `<title>` and its row in
|
|
296
|
+
* the list.
|
|
238
297
|
*/
|
|
239
298
|
function AreaLabel({
|
|
240
299
|
area,
|
|
@@ -248,42 +307,25 @@ function AreaLabel({
|
|
|
248
307
|
theme: Theme;
|
|
249
308
|
}): React.JSX.Element {
|
|
250
309
|
const at = labelPosition(area.geometry.rings, view);
|
|
251
|
-
const ref = React.useRef<HTMLSpanElement>(null);
|
|
252
|
-
const gap = remPx(theme, 4);
|
|
253
|
-
// Measured by a ResizeObserver, never in a layout effect: the observer's
|
|
254
|
-
// first call comes after the browser's own layout, so mounting a label on a
|
|
255
|
-
// tap forces no synchronous reflow of the drawing.
|
|
256
|
-
React.useEffect(() => {
|
|
257
|
-
const label = ref.current;
|
|
258
|
-
const box = label?.parentElement;
|
|
259
|
-
if (!label || !box || typeof ResizeObserver === 'undefined') return undefined;
|
|
260
|
-
const observer = new ResizeObserver(() => {
|
|
261
|
-
const width = box.clientWidth;
|
|
262
|
-
const height = box.clientHeight;
|
|
263
|
-
if (width === 0 || height === 0) return;
|
|
264
|
-
const x = clampCentre(at.fractionX * width, label.offsetWidth / 2, width, gap);
|
|
265
|
-
const y = clampCentre(at.fractionY * height, label.offsetHeight / 2, height, gap);
|
|
266
|
-
label.style.left = `${(x / width) * 100}%`;
|
|
267
|
-
label.style.top = `${(y / height) * 100}%`;
|
|
268
|
-
});
|
|
269
|
-
observer.observe(box);
|
|
270
|
-
observer.observe(label);
|
|
271
|
-
return () => observer.disconnect();
|
|
272
|
-
}, [at.fractionX, at.fractionY, gap]);
|
|
273
310
|
const halo = `0 0 ${rem(theme, 3)} ${theme.palette.background.paper}`;
|
|
274
311
|
return (
|
|
275
312
|
<Typography
|
|
276
|
-
ref={ref}
|
|
277
313
|
component="span"
|
|
278
314
|
aria-hidden
|
|
279
315
|
data-testid={`${testId}-label-${area.id}`}
|
|
316
|
+
data-region-label=""
|
|
317
|
+
data-fraction-x={at.fractionX}
|
|
318
|
+
data-fraction-y={at.fractionY}
|
|
280
319
|
// Position as an inline style, not `sx`: a per-label class would insert
|
|
281
320
|
// a new CSS rule (and a style recalculation) on every tap.
|
|
282
|
-
style={{
|
|
321
|
+
style={{
|
|
322
|
+
left: `${(at.fractionX * 100).toFixed(2)}%`,
|
|
323
|
+
top: `${(at.fractionY * 100).toFixed(2)}%`,
|
|
324
|
+
}}
|
|
283
325
|
sx={{
|
|
284
326
|
position: 'absolute',
|
|
285
327
|
transform: 'translate(-50%, -50%)',
|
|
286
|
-
fontSize: rem(theme,
|
|
328
|
+
fontSize: rem(theme, 12),
|
|
287
329
|
fontWeight: 700,
|
|
288
330
|
lineHeight: 1.1,
|
|
289
331
|
textAlign: 'center',
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/data-display/RegionMap/RegionMapDrawing.tsx"],"sourcesContent":["import Box from '@mui/material/Box/index.js';\nimport Typography from '@mui/material/Typography/index.js';\nimport { alpha, type Theme } from '@mui/material/styles/index.js';\nimport React from 'react';\n\nimport { rem, remPx } from '../../../tokens/relative';\n\nimport { labelPosition, type AreaGeometry, type DrawnRegion, type PlaneBox } from './RegionMap.helpers';\nimport type { RegionMapModel } from './RegionMap.hooks';\n\nexport interface RegionMapDrawingProps {\n model: RegionMapModel & { view: PlaneBox };\n showCenter: boolean;\n highlightedIds: readonly string[];\n maxHeight: string | undefined;\n label: string;\n tappable: boolean;\n testId: string;\n theme: Theme;\n}\n\n/**\n * The drawing: one `role=\"img\"`, square, with HTML labels laid over it.\n *\n * Strokes are `non-scaling-stroke` and labels are HTML at theme sizes, so the\n * picture reads the same at 320 px and 1600 px; `maxHeight` caps it on a wide\n * screen. Where it sits in a wider box is the host's call.\n *\n * The base areas are a memoised layer: a toggle changes `excluded`, not the\n * geometry, so only the hatch, highlight and label layers re-render.\n */\nexport function RegionMapDrawing({\n model,\n showCenter,\n highlightedIds,\n maxHeight,\n label,\n tappable,\n testId,\n theme,\n}: RegionMapDrawingProps): React.JSX.Element {\n const hatchId = `${React.useId().replace(/:/g, '')}-hatch`;\n const { view, drawn } = model;\n const wanted = new Set(highlightedIds);\n const highlighted = drawn.filter((d) => wanted.has(d.id));\n const labelled = [...drawn.filter((d) => d.excluded), ...highlighted.filter((d) => !d.excluded)];\n const base = drawn.map((d) => ({ id: d.id, name: d.name, geometry: d.geometry, outside: d.outside === true }));\n return (\n <Box\n sx={{\n position: 'relative',\n width: '100%',\n maxWidth: maxHeight,\n maxHeight,\n aspectRatio: '1 / 1',\n border: 1,\n borderColor: 'divider',\n borderRadius: 1,\n overflow: 'hidden',\n bgcolor: 'background.paper',\n }}\n data-testid={`${testId}-canvas`}\n >\n <svg\n viewBox={`${view.minX} ${view.minY} ${view.spanX} ${view.spanY}`}\n role=\"img\"\n aria-label={label}\n onPointerDown={model.onPointerDown}\n onClick={model.onClick}\n style={{ display: 'block', width: '100%', height: '100%', cursor: tappable ? 'pointer' : 'default' }}\n >\n <Hatch id={hatchId} pitch={view.spanX / 70} theme={theme} />\n <BaseAreas areas={base} testId={testId} theme={theme} />\n <ExcludedAreas drawn={drawn} hatchId={hatchId} testId={testId} theme={theme} />\n <Circles radii={model.radii} theme={theme} />\n {highlighted.map((d) => (\n <path\n key={`h-${d.id}`}\n d={d.geometry.path}\n fillRule=\"evenodd\"\n vectorEffect=\"non-scaling-stroke\"\n fill={alpha(theme.palette.primary.main, 0.3)}\n stroke={theme.palette.primary.main}\n strokeWidth={2.5}\n pointerEvents=\"none\"\n data-testid={`${testId}-highlight-${d.id}`}\n />\n ))}\n {showCenter && (\n <circle\n cx={0}\n cy={0}\n r={view.spanX / 90}\n fill={theme.palette.primary.main}\n stroke={theme.palette.background.paper}\n strokeWidth={2}\n vectorEffect=\"non-scaling-stroke\"\n pointerEvents=\"none\"\n data-testid={`${testId}-center`}\n />\n )}\n </svg>\n {labelled.map((d) => (\n <AreaLabel key={`l-${d.id}`} area={d} view={view} testId={testId} theme={theme} />\n ))}\n </Box>\n );\n}\n\n/** The hatch an excluded area is filled with: lines, not a colour. */\nfunction Hatch({ id, pitch, theme }: { id: string; pitch: number; theme: Theme }): React.JSX.Element {\n return (\n <defs>\n <pattern id={id} width={pitch} height={pitch} patternUnits=\"userSpaceOnUse\" patternTransform=\"rotate(45)\">\n <rect width={pitch} height={pitch} fill={alpha(theme.palette.text.primary, 0.1)} />\n <rect width={pitch / 3} height={pitch} fill={theme.palette.text.secondary} />\n </pattern>\n </defs>\n );\n}\n\ninterface BaseArea {\n id: string;\n name: string;\n geometry: AreaGeometry;\n outside: boolean;\n}\n\ninterface BaseAreasProps {\n areas: readonly BaseArea[];\n testId: string;\n theme: Theme;\n}\n\n/** Same areas, same geometry objects, same look: nothing to re-render. */\nfunction sameBase(a: BaseAreasProps, b: BaseAreasProps): boolean {\n if (a.theme !== b.theme || a.testId !== b.testId || a.areas.length !== b.areas.length) return false;\n return a.areas.every((area, i) => {\n const other = b.areas[i]!;\n return (\n area.id === other.id &&\n area.geometry === other.geometry &&\n area.outside === other.outside &&\n area.name === other.name\n );\n });\n}\n\n/** Every area's fill and border — the tap targets. Memoised: see `sameBase`. */\nconst BaseAreas = React.memo(function BaseAreas({ areas, testId, theme }: BaseAreasProps): React.JSX.Element {\n const { palette } = theme;\n return (\n <>\n {areas.map((d) => (\n <path\n key={d.id}\n d={d.geometry.path}\n fillRule=\"evenodd\"\n vectorEffect=\"non-scaling-stroke\"\n fill={d.outside ? palette.background.paper : palette.action.hover}\n stroke={palette.divider}\n strokeWidth={1}\n data-region-id={d.id}\n data-testid={`${testId}-area-${d.id}`}\n >\n <title>{d.name}</title>\n </path>\n ))}\n </>\n );\n}, sameBase);\n\n/** The hatch over every excluded area. */\nfunction ExcludedAreas({\n drawn,\n hatchId,\n testId,\n theme,\n}: {\n drawn: readonly DrawnRegion[];\n hatchId: string;\n testId: string;\n theme: Theme;\n}): React.JSX.Element {\n return (\n <>\n {drawn\n .filter((d) => d.excluded)\n .map((d) => (\n <path\n key={`x-${d.id}`}\n d={d.geometry.path}\n fillRule=\"evenodd\"\n vectorEffect=\"non-scaling-stroke\"\n fill={`url(#${hatchId})`}\n stroke={theme.palette.text.secondary}\n strokeWidth={1.5}\n pointerEvents=\"none\"\n data-testid={`${testId}-excluded-${d.id}`}\n />\n ))}\n </>\n );\n}\n\n/** The dashed circles around the centre. */\nfunction Circles({ radii, theme }: { radii: readonly number[]; theme: Theme }): React.JSX.Element {\n return (\n <>\n {radii.map((radius) => (\n <circle\n key={radius}\n cx={0}\n cy={0}\n r={radius}\n fill=\"none\"\n stroke={theme.palette.info.main}\n strokeWidth={1.75}\n strokeDasharray=\"7 5\"\n vectorEffect=\"non-scaling-stroke\"\n pointerEvents=\"none\"\n />\n ))}\n </>\n );\n}\n\n/** Where a label's centre may go along one axis so the whole label stays in view. */\nfunction clampCentre(wanted: number, half: number, span: number, gap: number): number {\n if (span <= 2 * (half + gap)) return span / 2;\n return Math.min(span - half - gap, Math.max(half + gap, wanted));\n}\n\n/**\n * An area's name over the drawing, at the theme's type size whatever the\n * width — and moved in by its own measured size, so it is never clipped at\n * the drawing's edge.\n */\nfunction AreaLabel({\n area,\n view,\n testId,\n theme,\n}: {\n area: DrawnRegion;\n view: PlaneBox;\n testId: string;\n theme: Theme;\n}): React.JSX.Element {\n const at = labelPosition(area.geometry.rings, view);\n const ref = React.useRef<HTMLSpanElement>(null);\n const gap = remPx(theme, 4);\n // Measured by a ResizeObserver, never in a layout effect: the observer's\n // first call comes after the browser's own layout, so mounting a label on a\n // tap forces no synchronous reflow of the drawing.\n React.useEffect(() => {\n const label = ref.current;\n const box = label?.parentElement;\n if (!label || !box || typeof ResizeObserver === 'undefined') return undefined;\n const observer = new ResizeObserver(() => {\n const width = box.clientWidth;\n const height = box.clientHeight;\n if (width === 0 || height === 0) return;\n const x = clampCentre(at.fractionX * width, label.offsetWidth / 2, width, gap);\n const y = clampCentre(at.fractionY * height, label.offsetHeight / 2, height, gap);\n label.style.left = `${(x / width) * 100}%`;\n label.style.top = `${(y / height) * 100}%`;\n });\n observer.observe(box);\n observer.observe(label);\n return () => observer.disconnect();\n }, [at.fractionX, at.fractionY, gap]);\n const halo = `0 0 ${rem(theme, 3)} ${theme.palette.background.paper}`;\n return (\n <Typography\n ref={ref}\n component=\"span\"\n aria-hidden\n data-testid={`${testId}-label-${area.id}`}\n // Position as an inline style, not `sx`: a per-label class would insert\n // a new CSS rule (and a style recalculation) on every tap.\n style={{ left: `${(at.fractionX * 100).toFixed(2)}%`, top: `${(at.fractionY * 100).toFixed(2)}%` }}\n sx={{\n position: 'absolute',\n transform: 'translate(-50%, -50%)',\n fontSize: rem(theme, 11),\n fontWeight: 700,\n lineHeight: 1.1,\n textAlign: 'center',\n maxWidth: '45%',\n color: 'text.primary',\n pointerEvents: 'none',\n textShadow: `${halo}, ${halo}`,\n }}\n >\n {area.name}\n </Typography>\n );\n}\n"],"mappings":"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","names":["BaseAreas"]}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|