@carto/ps-react-ui 4.17.1 → 4.17.3
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/components.js +1 -1
- package/dist/legend/stores.js +1 -1
- package/dist/{legend-store-registry-Bo8U_qWM.js → legend-store-registry-p1tSwJXH.js} +151 -147
- package/dist/legend-store-registry-p1tSwJXH.js.map +1 -0
- package/dist/legend.js +670 -562
- package/dist/legend.js.map +1 -1
- package/dist/smart-tooltip-DFV6GD8R.js +40 -0
- package/dist/smart-tooltip-DFV6GD8R.js.map +1 -0
- package/dist/types/legend/components/legend-actions/legend-actions.d.ts +2 -2
- package/dist/types/legend/components/legend-group/styles.d.ts +44 -16
- package/dist/types/legend/components/legend-row/styles.d.ts +37 -0
- package/dist/types/legend/components/legend-sortable/styles.d.ts +4 -3
- package/dist/widgets/wrapper.js +1 -1
- package/dist/widgets-v2.js +1 -1
- package/package.json +3 -3
- package/src/components/smart-tooltip/smart-tooltip.tsx +25 -10
- package/src/legend/components/legend-actions/legend-actions.tsx +2 -2
- package/src/legend/components/legend-group/legend-group.tsx +3 -1
- package/src/legend/components/legend-group/styles.ts +77 -32
- package/src/legend/components/legend-row/legend-row.tsx +13 -8
- package/src/legend/components/legend-row/styles.ts +59 -4
- package/src/legend/components/legend-sortable/styles.ts +13 -4
- package/src/legend/stores/legend-store-registry.ts +28 -4
- package/src/legend/stores/legend-store.test.ts +23 -0
- package/dist/legend-store-registry-Bo8U_qWM.js.map +0 -1
- package/dist/smart-tooltip-D4vwQpFf.js +0 -37
- package/dist/smart-tooltip-D4vwQpFf.js.map +0 -1
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsx as y } from "react/jsx-runtime";
|
|
2
|
+
import { c as z } from "react/compiler-runtime";
|
|
3
|
+
import { useRef as I, useState as N, useLayoutEffect as P } from "react";
|
|
4
|
+
import { T as R } from "./tooltip-BDnrRKrp.js";
|
|
5
|
+
const x = [];
|
|
6
|
+
function j(E) {
|
|
7
|
+
const e = z(13), {
|
|
8
|
+
title: g,
|
|
9
|
+
dependencies: a,
|
|
10
|
+
timeout: h,
|
|
11
|
+
TooltipProps: c,
|
|
12
|
+
children: f
|
|
13
|
+
} = E, u = a === void 0 ? x : a, t = h === void 0 ? 500 : h, d = I(null), [S, b] = N(!1);
|
|
14
|
+
let s;
|
|
15
|
+
e[0] !== u ? (s = JSON.stringify(u), e[0] = u, e[1] = s) : s = e[1];
|
|
16
|
+
const m = s;
|
|
17
|
+
let n;
|
|
18
|
+
e[2] !== t ? (n = () => {
|
|
19
|
+
const O = () => {
|
|
20
|
+
const i = d.current;
|
|
21
|
+
i && b(i.scrollWidth > i.clientWidth || i.scrollHeight > i.clientHeight);
|
|
22
|
+
}, w = setTimeout(O, t), v = d.current, T = v && typeof ResizeObserver < "u" ? new ResizeObserver(O) : null;
|
|
23
|
+
return v && T?.observe(v), () => {
|
|
24
|
+
clearTimeout(w), T?.disconnect();
|
|
25
|
+
};
|
|
26
|
+
}, e[2] = t, e[3] = n) : n = e[3];
|
|
27
|
+
let r;
|
|
28
|
+
e[4] !== m || e[5] !== t ? (r = [m, t], e[4] = m, e[5] = t, e[6] = r) : r = e[6], P(n, r);
|
|
29
|
+
const p = S && g;
|
|
30
|
+
let o;
|
|
31
|
+
e[7] !== f ? (o = f({
|
|
32
|
+
ref: d
|
|
33
|
+
}), e[7] = f, e[8] = o) : o = e[8];
|
|
34
|
+
let l;
|
|
35
|
+
return e[9] !== c || e[10] !== p || e[11] !== o ? (l = /* @__PURE__ */ y(R, { title: p, ...c, children: o }), e[9] = c, e[10] = p, e[11] = o, e[12] = l) : l = e[12], l;
|
|
36
|
+
}
|
|
37
|
+
export {
|
|
38
|
+
j as S
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=smart-tooltip-DFV6GD8R.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smart-tooltip-DFV6GD8R.js","sources":["../src/components/smart-tooltip/smart-tooltip.tsx"],"sourcesContent":["import type { TooltipProps } from '@mui/material'\nimport { useLayoutEffect, useRef, useState, type JSX, type Ref } from 'react'\nimport { Tooltip } from '../tooltip/tooltip'\n\nconst EMPTY_DEPENDENCIES: unknown[] = []\n\n/**\n * An intelligent tooltip wrapper that automatically detects text overflow and displays a tooltip only when content is truncated.\n *\n * @remarks\n * Uses a render prop pattern. The child function receives a `ref` that must be attached to the element being monitored for overflow. Use the `dependencies` array to trigger re-evaluation when content or container size changes.\n *\n * @example\n * ```tsx\n * <SmartTooltip title=\"This is a long text that might get truncated\">\n * {({ ref }) => (\n * <Typography ref={ref} noWrap sx={{ overflow: 'hidden', textOverflow: 'ellipsis' }}>\n * This is a long text that might get truncated\n * </Typography>\n * )}\n * </SmartTooltip>\n * ```\n */\nexport function SmartTooltip<T extends HTMLElement>({\n title,\n dependencies = EMPTY_DEPENDENCIES,\n timeout = 500,\n TooltipProps,\n children,\n}: {\n title: string | undefined\n dependencies?: unknown[]\n timeout?: number\n children: (props: { ref: Ref<T> }) => JSX.Element\n TooltipProps?: Partial<TooltipProps>\n}) {\n const ref = useRef<T>(null)\n const [isOverflowing, setIsOverflowing] = useState(false)\n // Call sites pass a fresh `dependencies={[…]}` literal each render; key by\n // value so the effect does not churn ResizeObserver on referential churn.\n const dependenciesKey = JSON.stringify(dependencies)\n\n useLayoutEffect(() => {\n const checkOverflow = () => {\n const el = ref.current\n if (!el) return\n setIsOverflowing(\n el.scrollWidth > el.clientWidth || el.scrollHeight > el.clientHeight,\n )\n }\n\n // Initial measure after layout settles (fonts, flex collapse, …).\n const timerId = setTimeout(checkOverflow, timeout)\n // Re-measure when the element is resized (e.g. legend title reflows as\n // hover-faded actions collapse/expand).\n const el = ref.current\n const resizeObserver =\n el && typeof ResizeObserver !== 'undefined'\n ? new ResizeObserver(checkOverflow)\n : null\n if (el) resizeObserver?.observe(el)\n\n return () => {\n clearTimeout(timerId)\n resizeObserver?.disconnect()\n }\n }, [dependenciesKey, timeout])\n\n return (\n <Tooltip title={isOverflowing && title} {...TooltipProps}>\n {children({ ref })}\n </Tooltip>\n )\n}\n"],"names":["EMPTY_DEPENDENCIES","SmartTooltip","t0","$","_c","title","dependencies","t1","timeout","t2","TooltipProps","children","undefined","ref","useRef","isOverflowing","setIsOverflowing","useState","t3","JSON","stringify","dependenciesKey","t4","checkOverflow","el","current","scrollWidth","clientWidth","scrollHeight","clientHeight","timerId","setTimeout","el_0","resizeObserver","ResizeObserver","observe","clearTimeout","disconnect","t5","useLayoutEffect","t6","t7","t8","Tooltip"],"mappings":";;;;AAIA,MAAMA,IAAgC,CAAA;AAmB/B,SAAAC,EAAAC,GAAA;AAAA,QAAAC,IAAAC,EAAA,EAAA,GAA6C;AAAA,IAAAC,OAAAA;AAAAA,IAAAC,cAAAC;AAAAA,IAAAC,SAAAC;AAAAA,IAAAC,cAAAA;AAAAA,IAAAC,UAAAA;AAAAA,EAAAA,IAAAT,GAElDI,IAAAC,MAAAK,SAAAZ,IAAAO,GACAC,IAAAC,MAAAG,SAAA,MAAAH,GAUAI,IAAYC,EAAU,IAAI,GAC1B,CAAAC,GAAAC,CAAA,IAA0CC,EAAS,EAAK;AAAC,MAAAC;AAAA,EAAAf,SAAAG,KAGjCY,IAAAC,KAAIC,UAAWd,CAAY,GAACH,OAAAG,GAAAH,OAAAe,KAAAA,IAAAf,EAAA,CAAA;AAApD,QAAAkB,IAAwBH;AAA4B,MAAAI;AAAA,EAAAnB,SAAAK,KAEpCc,IAAAA,MAAA;AACd,UAAAC,IAAsBA,MAAA;AACpB,YAAAC,IAAWX,EAAGY;AACd,MAAKD,KACLR,EACEQ,EAAEE,cAAeF,EAAEG,eAAgBH,EAAEI,eAAgBJ,EAAEK,YACzD;AAAA,IAAC,GAIHC,IAAgBC,WAAWR,GAAef,CAAO,GAGjDwB,IAAWnB,EAAGY,SACdQ,IACED,KAAM,OAAOE,iBAAmB,MAAhC,IACQA,eAAeX,CAChB,IAFP;AAGF,WAAIC,KAAIS,GAAcE,QAAUX,CAAE,GAE3B,MAAA;AACLY,mBAAaN,CAAO,GACpBG,GAAcI,WAAAA;AAAAA,IAAc;AAAA,EAC7B,GACFlC,OAAAK,GAAAL,OAAAmB,KAAAA,IAAAnB,EAAA,CAAA;AAAA,MAAAmC;AAAA,EAAAnC,EAAA,CAAA,MAAAkB,KAAAlB,SAAAK,KAAE8B,IAAA,CAACjB,GAAiBb,CAAO,GAACL,OAAAkB,GAAAlB,OAAAK,GAAAL,OAAAmC,KAAAA,IAAAnC,EAAA,CAAA,GAxB7BoC,EAAgBjB,GAwBbgB,CAA0B;AAGX,QAAAE,IAAAzB,KAAAV;AAAsB,MAAAoC;AAAA,EAAAtC,SAAAQ,KACnC8B,IAAA9B,EAAS;AAAA,IAAAE,KAAAA;AAAAA,EAAAA,CAAO,GAACV,OAAAQ,GAAAR,OAAAsC,KAAAA,IAAAtC,EAAA,CAAA;AAAA,MAAAuC;AAAA,SAAAvC,EAAA,CAAA,MAAAO,KAAAP,UAAAqC,KAAArC,EAAA,EAAA,MAAAsC,KADpBC,sBAACC,GAAA,EAAe,OAAAH,GAAsB,GAAM9B,GACzC+B,UAAAA,GACH,GAAUtC,OAAAO,GAAAP,QAAAqC,GAAArC,QAAAsC,GAAAtC,QAAAuC,KAAAA,IAAAvC,EAAA,EAAA,GAFVuC;AAEU;"}
|
|
@@ -7,8 +7,8 @@ export interface LegendActionsProps {
|
|
|
7
7
|
* parity): the wrapper partitions its children by component identity and
|
|
8
8
|
* renders this slot inside the header, wrapped in the hover-fade group
|
|
9
9
|
* (`PsLegend-rowFade` in rows, `PsLegend-groupFade` in group headers —
|
|
10
|
-
* resolved from the nearest context). Compose the
|
|
11
|
-
* rightmost, per design.
|
|
10
|
+
* resolved from the nearest context). Compose the visibility toggle last so
|
|
11
|
+
* it sits rightmost, per design.
|
|
12
12
|
*
|
|
13
13
|
* @experimental This API is new and may change in a future release.
|
|
14
14
|
*/
|
|
@@ -13,6 +13,22 @@ export declare const styles: {
|
|
|
13
13
|
borderTop: string;
|
|
14
14
|
borderColor: "divider";
|
|
15
15
|
};
|
|
16
|
+
};
|
|
17
|
+
groupDragging: {
|
|
18
|
+
opacity: number;
|
|
19
|
+
};
|
|
20
|
+
header: {
|
|
21
|
+
display: "flex";
|
|
22
|
+
alignItems: "center";
|
|
23
|
+
gap: number;
|
|
24
|
+
padding: ({ spacing }: Theme) => string;
|
|
25
|
+
minHeight: number;
|
|
26
|
+
borderBottom: string;
|
|
27
|
+
borderColor: "divider";
|
|
28
|
+
position: "sticky";
|
|
29
|
+
top: number;
|
|
30
|
+
zIndex: number;
|
|
31
|
+
backgroundColor: "background.default";
|
|
16
32
|
'& .PsLegend-groupFade > *': {
|
|
17
33
|
opacity: number;
|
|
18
34
|
transition: ({ transitions }: Theme) => string;
|
|
@@ -23,8 +39,36 @@ export declare const styles: {
|
|
|
23
39
|
opacity: number;
|
|
24
40
|
};
|
|
25
41
|
};
|
|
42
|
+
'@media (hover: hover)': {
|
|
43
|
+
'& .PsLegend-groupFade > *:not(.active)': {
|
|
44
|
+
width: number;
|
|
45
|
+
minWidth: number;
|
|
46
|
+
padding: number;
|
|
47
|
+
overflow: "hidden";
|
|
48
|
+
pointerEvents: "none";
|
|
49
|
+
};
|
|
50
|
+
'& .PsLegend-groupActions:not(:has(.active))': {
|
|
51
|
+
width: number;
|
|
52
|
+
minWidth: number;
|
|
53
|
+
overflow: "hidden";
|
|
54
|
+
marginInlineStart: ({ spacing }: Theme) => string;
|
|
55
|
+
pointerEvents: "none";
|
|
56
|
+
};
|
|
57
|
+
};
|
|
26
58
|
'&:hover .PsLegend-groupFade > *, &:focus-within .PsLegend-groupFade > *': {
|
|
27
59
|
opacity: number;
|
|
60
|
+
width: string;
|
|
61
|
+
minWidth: number;
|
|
62
|
+
padding: string;
|
|
63
|
+
overflow: "visible";
|
|
64
|
+
pointerEvents: "auto";
|
|
65
|
+
};
|
|
66
|
+
'&:hover .PsLegend-groupActions, &:focus-within .PsLegend-groupActions': {
|
|
67
|
+
width: string;
|
|
68
|
+
minWidth: number;
|
|
69
|
+
overflow: "visible";
|
|
70
|
+
marginInlineStart: number;
|
|
71
|
+
pointerEvents: "auto";
|
|
28
72
|
};
|
|
29
73
|
'& .PsLegend-dragHandle-group > *': {
|
|
30
74
|
opacity: number;
|
|
@@ -37,22 +81,6 @@ export declare const styles: {
|
|
|
37
81
|
opacity: number;
|
|
38
82
|
};
|
|
39
83
|
};
|
|
40
|
-
groupDragging: {
|
|
41
|
-
opacity: number;
|
|
42
|
-
};
|
|
43
|
-
header: {
|
|
44
|
-
display: "flex";
|
|
45
|
-
alignItems: "center";
|
|
46
|
-
gap: number;
|
|
47
|
-
padding: ({ spacing }: Theme) => string;
|
|
48
|
-
minHeight: number;
|
|
49
|
-
borderBottom: string;
|
|
50
|
-
borderColor: "divider";
|
|
51
|
-
position: "sticky";
|
|
52
|
-
top: number;
|
|
53
|
-
zIndex: number;
|
|
54
|
-
backgroundColor: "background.default";
|
|
55
|
-
};
|
|
56
84
|
titleArea: {
|
|
57
85
|
flex: number;
|
|
58
86
|
minWidth: number;
|
|
@@ -18,8 +18,36 @@ export declare const styles: {
|
|
|
18
18
|
opacity: number;
|
|
19
19
|
};
|
|
20
20
|
};
|
|
21
|
+
'@media (hover: hover)': {
|
|
22
|
+
'& .PsLegend-rowFade > *:not(.active)': {
|
|
23
|
+
width: number;
|
|
24
|
+
minWidth: number;
|
|
25
|
+
padding: number;
|
|
26
|
+
overflow: "hidden";
|
|
27
|
+
pointerEvents: "none";
|
|
28
|
+
};
|
|
29
|
+
'& .PsLegend-rowActions:not(:has(.active))': {
|
|
30
|
+
width: number;
|
|
31
|
+
minWidth: number;
|
|
32
|
+
overflow: "hidden";
|
|
33
|
+
marginInlineStart: ({ spacing }: Theme) => string;
|
|
34
|
+
pointerEvents: "none";
|
|
35
|
+
};
|
|
36
|
+
};
|
|
21
37
|
'&:hover .PsLegend-rowFade > *, &:focus-within .PsLegend-rowFade > *': {
|
|
22
38
|
opacity: number;
|
|
39
|
+
width: string;
|
|
40
|
+
minWidth: number;
|
|
41
|
+
padding: string;
|
|
42
|
+
overflow: "visible";
|
|
43
|
+
pointerEvents: "auto";
|
|
44
|
+
};
|
|
45
|
+
'&:hover .PsLegend-rowActions, &:focus-within .PsLegend-rowActions': {
|
|
46
|
+
width: string;
|
|
47
|
+
minWidth: number;
|
|
48
|
+
overflow: "visible";
|
|
49
|
+
marginInlineStart: number;
|
|
50
|
+
pointerEvents: "auto";
|
|
23
51
|
};
|
|
24
52
|
'&:hover .PsLegend-value, &:focus-within .PsLegend-value': {
|
|
25
53
|
opacity: number;
|
|
@@ -48,6 +76,9 @@ export declare const styles: {
|
|
|
48
76
|
zIndex: number;
|
|
49
77
|
backgroundColor: "background.paper";
|
|
50
78
|
};
|
|
79
|
+
headerGrouped: {
|
|
80
|
+
paddingLeft: number;
|
|
81
|
+
};
|
|
51
82
|
titleBox: {
|
|
52
83
|
flex: number;
|
|
53
84
|
minWidth: number;
|
|
@@ -103,9 +134,15 @@ export declare const styles: {
|
|
|
103
134
|
gap: number;
|
|
104
135
|
padding: ({ spacing }: Theme) => string;
|
|
105
136
|
};
|
|
137
|
+
contentGrouped: {
|
|
138
|
+
paddingLeft: number;
|
|
139
|
+
};
|
|
106
140
|
footer: {
|
|
107
141
|
padding: ({ spacing }: Theme) => string;
|
|
108
142
|
};
|
|
143
|
+
footerGrouped: {
|
|
144
|
+
paddingLeft: number;
|
|
145
|
+
};
|
|
109
146
|
dimmed: {
|
|
110
147
|
opacity: number;
|
|
111
148
|
};
|
|
@@ -6,13 +6,14 @@ export declare const styles: {
|
|
|
6
6
|
display: "flex";
|
|
7
7
|
alignItems: "center";
|
|
8
8
|
justifyContent: "center";
|
|
9
|
-
|
|
9
|
+
left: number;
|
|
10
|
+
top: number;
|
|
10
11
|
};
|
|
11
12
|
handleSlotLayer: {
|
|
12
|
-
|
|
13
|
+
height: number;
|
|
13
14
|
};
|
|
14
15
|
handleSlotGroup: {
|
|
15
|
-
|
|
16
|
+
height: number;
|
|
16
17
|
};
|
|
17
18
|
handle: {
|
|
18
19
|
padding: string;
|
package/dist/widgets/wrapper.js
CHANGED
|
@@ -5,7 +5,7 @@ import { MoreVert as D } from "@mui/icons-material";
|
|
|
5
5
|
import { useState as H, useLayoutEffect as G } from "react";
|
|
6
6
|
import "../lasso-tool-YLFRgb-S.js";
|
|
7
7
|
import "../cjs-D4KH3azB.js";
|
|
8
|
-
import { S as U } from "../smart-tooltip-
|
|
8
|
+
import { S as U } from "../smart-tooltip-DFV6GD8R.js";
|
|
9
9
|
import "@carto/ps-utils";
|
|
10
10
|
import { u as V } from "../use-widget-selector-DFl2hW0R.js";
|
|
11
11
|
import { w as M } from "../widget-store-Bw5zRUGg.js";
|
package/dist/widgets-v2.js
CHANGED
|
@@ -6,7 +6,7 @@ import { j as we, i as Ee, e as Ce, n as Te, r as ke, q as _e, v as Ie, u as Le,
|
|
|
6
6
|
import "zustand";
|
|
7
7
|
import { Box as W, LinearProgress as We, Typography as Z, AccordionSummary as $e, AccordionDetails as De, Accordion as Fe, AlertTitle as Be, Alert as Oe, Button as Re, IconButton as Me, Divider as Pe, Popper as Ue, Grow as Ne, Paper as je, Link as pe } from "@mui/material";
|
|
8
8
|
import { ExpandMore as ze, Close as He } from "@mui/icons-material";
|
|
9
|
-
import { S as Ve } from "./smart-tooltip-
|
|
9
|
+
import { S as Ve } from "./smart-tooltip-DFV6GD8R.js";
|
|
10
10
|
import { createPortal as Ge } from "react-dom";
|
|
11
11
|
import { WidgetOptions as qe } from "@carto/meridian-ds/custom-icons";
|
|
12
12
|
import "./lasso-tool-YLFRgb-S.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carto/ps-react-ui",
|
|
3
|
-
"version": "4.17.
|
|
3
|
+
"version": "4.17.3",
|
|
4
4
|
"description": "CARTO's Professional Service React Material library",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"devDependencies": {
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"react-dom": "19.2.4",
|
|
18
18
|
"react-markdown": "10.1.0",
|
|
19
19
|
"zustand": "5.0.12",
|
|
20
|
-
"@carto/ps-
|
|
21
|
-
"@carto/ps-
|
|
20
|
+
"@carto/ps-common-types": "1.0.0",
|
|
21
|
+
"@carto/ps-utils": "2.1.0"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
24
|
"@carto/meridian-ds": "^2.0.0",
|
|
@@ -36,20 +36,35 @@ export function SmartTooltip<T extends HTMLElement>({
|
|
|
36
36
|
}) {
|
|
37
37
|
const ref = useRef<T>(null)
|
|
38
38
|
const [isOverflowing, setIsOverflowing] = useState(false)
|
|
39
|
+
// Call sites pass a fresh `dependencies={[…]}` literal each render; key by
|
|
40
|
+
// value so the effect does not churn ResizeObserver on referential churn.
|
|
41
|
+
const dependenciesKey = JSON.stringify(dependencies)
|
|
39
42
|
|
|
40
43
|
useLayoutEffect(() => {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
const checkOverflow = () => {
|
|
45
|
+
const el = ref.current
|
|
46
|
+
if (!el) return
|
|
47
|
+
setIsOverflowing(
|
|
48
|
+
el.scrollWidth > el.clientWidth || el.scrollHeight > el.clientHeight,
|
|
49
|
+
)
|
|
50
|
+
}
|
|
46
51
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
// Initial measure after layout settles (fonts, flex collapse, …).
|
|
53
|
+
const timerId = setTimeout(checkOverflow, timeout)
|
|
54
|
+
// Re-measure when the element is resized (e.g. legend title reflows as
|
|
55
|
+
// hover-faded actions collapse/expand).
|
|
56
|
+
const el = ref.current
|
|
57
|
+
const resizeObserver =
|
|
58
|
+
el && typeof ResizeObserver !== 'undefined'
|
|
59
|
+
? new ResizeObserver(checkOverflow)
|
|
60
|
+
: null
|
|
61
|
+
if (el) resizeObserver?.observe(el)
|
|
50
62
|
|
|
51
|
-
return () =>
|
|
52
|
-
|
|
63
|
+
return () => {
|
|
64
|
+
clearTimeout(timerId)
|
|
65
|
+
resizeObserver?.disconnect()
|
|
66
|
+
}
|
|
67
|
+
}, [dependenciesKey, timeout])
|
|
53
68
|
|
|
54
69
|
return (
|
|
55
70
|
<Tooltip title={isOverflowing && title} {...TooltipProps}>
|
|
@@ -17,8 +17,8 @@ export interface LegendActionsProps {
|
|
|
17
17
|
* parity): the wrapper partitions its children by component identity and
|
|
18
18
|
* renders this slot inside the header, wrapped in the hover-fade group
|
|
19
19
|
* (`PsLegend-rowFade` in rows, `PsLegend-groupFade` in group headers —
|
|
20
|
-
* resolved from the nearest context). Compose the
|
|
21
|
-
* rightmost, per design.
|
|
20
|
+
* resolved from the nearest context). Compose the visibility toggle last so
|
|
21
|
+
* it sits rightmost, per design.
|
|
22
22
|
*
|
|
23
23
|
* @experimental This API is new and may change in a future release.
|
|
24
24
|
*/
|
|
@@ -97,7 +97,9 @@ export function LegendGroup({ groupId, children }: LegendGroupProps) {
|
|
|
97
97
|
/>
|
|
98
98
|
</Box>
|
|
99
99
|
|
|
100
|
-
<Box sx={styles.actions}>
|
|
100
|
+
<Box className='PsLegend-groupActions' sx={styles.actions}>
|
|
101
|
+
{actions}
|
|
102
|
+
</Box>
|
|
101
103
|
</Box>
|
|
102
104
|
|
|
103
105
|
<Collapse in={!group.collapsed}>
|
|
@@ -15,16 +15,46 @@ export const styles = {
|
|
|
15
15
|
borderTop: '1px solid',
|
|
16
16
|
borderColor: 'divider',
|
|
17
17
|
},
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
},
|
|
19
|
+
// The original stays in place, dimmed, while its overlay clone is dragged.
|
|
20
|
+
groupDragging: {
|
|
21
|
+
opacity: 0.4,
|
|
22
|
+
},
|
|
23
|
+
// Figma: 48px tall, 16px left inset (matches the row's own left inset),
|
|
24
|
+
// 8px gap to the label, actions right-aligned with ~8px edge inset (the
|
|
25
|
+
// small icon-button padding brings the glyphs to the design's 16px optical
|
|
26
|
+
// inset), hairline divider below. Sticky: pins to the top of the panel's
|
|
27
|
+
// scroll container.
|
|
28
|
+
header: {
|
|
29
|
+
display: 'flex',
|
|
30
|
+
alignItems: 'center',
|
|
31
|
+
gap: 1,
|
|
32
|
+
padding: ({ spacing }) => spacing(1.5, 2, 1.5, 2),
|
|
33
|
+
minHeight: LEGEND_GROUP_HEADER_HEIGHT,
|
|
34
|
+
borderBottom: '1px solid',
|
|
35
|
+
borderColor: 'divider',
|
|
36
|
+
position: 'sticky',
|
|
37
|
+
top: 0,
|
|
38
|
+
// Above the sticky row headers (2), which in turn sit above MUI's
|
|
39
|
+
// z-index-1 form internals in row content.
|
|
40
|
+
zIndex: 3,
|
|
41
|
+
// Distinct from the row header / panel body (both `background.paper`) so
|
|
42
|
+
// the group header visually separates from the rest.
|
|
43
|
+
backgroundColor: 'background.default',
|
|
44
|
+
// Hover-fade group actions (widgets-v2 Wrapper parity). Scoped to the
|
|
45
|
+
// header itself (not the group root) so hovering a member row's body
|
|
46
|
+
// below never lights up the group's own actions/handle — only hovering
|
|
47
|
+
// the header (title/handle/actions) does. A control carrying `.active`
|
|
48
|
+
// (hidden group) stays visible. Touch devices always show them. The
|
|
49
|
+
// collapse chevron sits outside the fade group. Each non-active child
|
|
50
|
+
// also collapses to zero width on its own (not just the whole actions
|
|
51
|
+
// slot) so a sibling's `.active` pin (e.g. the eye on a mixed-visibility
|
|
52
|
+
// group) doesn't leave a same-sized invisible hole where the still-faded
|
|
53
|
+
// ⋮ menu would've been.
|
|
24
54
|
'& .PsLegend-groupFade > *': {
|
|
25
55
|
opacity: 1,
|
|
26
56
|
transition: ({ transitions }: Theme) =>
|
|
27
|
-
transitions.create('opacity', {
|
|
57
|
+
transitions.create(['opacity', 'width'], {
|
|
28
58
|
duration: transitions.duration.standard,
|
|
29
59
|
easing: transitions.easing.easeInOut,
|
|
30
60
|
}),
|
|
@@ -35,8 +65,44 @@ export const styles = {
|
|
|
35
65
|
opacity: 1,
|
|
36
66
|
},
|
|
37
67
|
},
|
|
68
|
+
// Faded actions don't reserve flex space on fine-pointer devices — the
|
|
69
|
+
// label grows to the trailing edge. Hover/focus-within restores the slot
|
|
70
|
+
// so the label reflows with the buttons; a dirty `.active` child keeps
|
|
71
|
+
// the slot open even outside hover.
|
|
72
|
+
'@media (hover: hover)': {
|
|
73
|
+
'& .PsLegend-groupFade > *:not(.active)': {
|
|
74
|
+
width: 0,
|
|
75
|
+
minWidth: 0,
|
|
76
|
+
// border-box IconButtons keep their padding inside `width` — zero it
|
|
77
|
+
// too, otherwise the fixed 2px+2px padding survives as a tiny but
|
|
78
|
+
// needless residual gap.
|
|
79
|
+
padding: 0,
|
|
80
|
+
overflow: 'hidden',
|
|
81
|
+
pointerEvents: 'none',
|
|
82
|
+
},
|
|
83
|
+
'& .PsLegend-groupActions:not(:has(.active))': {
|
|
84
|
+
width: 0,
|
|
85
|
+
minWidth: 0,
|
|
86
|
+
overflow: 'hidden',
|
|
87
|
+
// Cancel the header gap so nothing is left behind the label.
|
|
88
|
+
marginInlineStart: ({ spacing }: Theme) => `calc(-1 * ${spacing(1)})`,
|
|
89
|
+
pointerEvents: 'none',
|
|
90
|
+
},
|
|
91
|
+
},
|
|
38
92
|
'&:hover .PsLegend-groupFade > *, &:focus-within .PsLegend-groupFade > *': {
|
|
39
93
|
opacity: 1,
|
|
94
|
+
width: 'auto',
|
|
95
|
+
minWidth: 0,
|
|
96
|
+
padding: '2px',
|
|
97
|
+
overflow: 'visible',
|
|
98
|
+
pointerEvents: 'auto',
|
|
99
|
+
},
|
|
100
|
+
'&:hover .PsLegend-groupActions, &:focus-within .PsLegend-groupActions': {
|
|
101
|
+
width: 'auto',
|
|
102
|
+
minWidth: 0,
|
|
103
|
+
overflow: 'visible',
|
|
104
|
+
marginInlineStart: 0,
|
|
105
|
+
pointerEvents: 'auto',
|
|
40
106
|
},
|
|
41
107
|
// Drag handle (Legend.Sortable): absolutely positioned (no reserved flex
|
|
42
108
|
// slot, so it never shifts the title/icon); the button inside fades in
|
|
@@ -57,31 +123,6 @@ export const styles = {
|
|
|
57
123
|
opacity: 1,
|
|
58
124
|
},
|
|
59
125
|
},
|
|
60
|
-
// The original stays in place, dimmed, while its overlay clone is dragged.
|
|
61
|
-
groupDragging: {
|
|
62
|
-
opacity: 0.4,
|
|
63
|
-
},
|
|
64
|
-
// Figma: 48px tall, icon at 12px from the left, 8px gap to the label,
|
|
65
|
-
// actions right-aligned with ~8px edge inset (the small icon-button padding
|
|
66
|
-
// brings the glyphs to the design's 16px optical inset), hairline divider
|
|
67
|
-
// below. Sticky: pins to the top of the panel's scroll container.
|
|
68
|
-
header: {
|
|
69
|
-
display: 'flex',
|
|
70
|
-
alignItems: 'center',
|
|
71
|
-
gap: 1,
|
|
72
|
-
padding: ({ spacing }) => spacing(1.5, 2, 1.5, 1),
|
|
73
|
-
minHeight: LEGEND_GROUP_HEADER_HEIGHT,
|
|
74
|
-
borderBottom: '1px solid',
|
|
75
|
-
borderColor: 'divider',
|
|
76
|
-
position: 'sticky',
|
|
77
|
-
top: 0,
|
|
78
|
-
// Above the sticky row headers (2), which in turn sit above MUI's
|
|
79
|
-
// z-index-1 form internals in row content.
|
|
80
|
-
zIndex: 3,
|
|
81
|
-
// Distinct from the row header / panel body (both `background.paper`) so
|
|
82
|
-
// the group header visually separates from the rest.
|
|
83
|
-
backgroundColor: 'background.default',
|
|
84
|
-
},
|
|
85
126
|
// Accordion click target: everything in the header but the actions box.
|
|
86
127
|
// Non-native button (role='button'), so it carries its own focus ring.
|
|
87
128
|
titleArea: {
|
|
@@ -102,6 +143,10 @@ export const styles = {
|
|
|
102
143
|
display: 'flex',
|
|
103
144
|
color: 'text.secondary',
|
|
104
145
|
},
|
|
146
|
+
// No `flex:1` here — the label sizes to its own text (shrinking to
|
|
147
|
+
// ellipsis when too long) so the chevron sits right after it, instead of
|
|
148
|
+
// being dragged to the trailing edge of `titleArea` (which keeps its own
|
|
149
|
+
// `flex:1` to fill the header up to the actions).
|
|
105
150
|
label: {
|
|
106
151
|
minWidth: 0,
|
|
107
152
|
overflow: 'hidden',
|
|
@@ -74,7 +74,10 @@ export function LegendRow({ layerId, children }: LegendRowProps) {
|
|
|
74
74
|
sortableProps?.dragging === true && styles.rowDragging,
|
|
75
75
|
]}
|
|
76
76
|
>
|
|
77
|
-
<Box
|
|
77
|
+
<Box
|
|
78
|
+
sx={[styles.header, inGroup && styles.headerGrouped]}
|
|
79
|
+
style={{ top: stickyTop }}
|
|
80
|
+
>
|
|
78
81
|
{sortableProps?.handle}
|
|
79
82
|
<Box
|
|
80
83
|
sx={[styles.titleBox, canToggle && styles.titleBoxInteractive]}
|
|
@@ -115,21 +118,23 @@ export function LegendRow({ layerId, children }: LegendRowProps) {
|
|
|
115
118
|
)}
|
|
116
119
|
</Box>
|
|
117
120
|
|
|
118
|
-
<Box sx={styles.actions}>
|
|
121
|
+
<Box className='PsLegend-rowActions' sx={styles.actions}>
|
|
122
|
+
{actions}
|
|
123
|
+
</Box>
|
|
119
124
|
</Box>
|
|
120
125
|
|
|
121
126
|
<Collapse in={!effectivelyCollapsed}>
|
|
122
127
|
<Box
|
|
123
|
-
sx={
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
+
sx={[
|
|
129
|
+
styles.content,
|
|
130
|
+
inGroup && styles.contentGrouped,
|
|
131
|
+
!layer.visible && styles.dimmed,
|
|
132
|
+
]}
|
|
128
133
|
>
|
|
129
134
|
{body}
|
|
130
135
|
</Box>
|
|
131
136
|
{layer.helperText && (
|
|
132
|
-
<Box sx={styles.footer}>
|
|
137
|
+
<Box sx={[styles.footer, inGroup && styles.footerGrouped]}>
|
|
133
138
|
{typeof layer.helperText === 'string' ? (
|
|
134
139
|
<Typography variant='caption' color='text.secondary'>
|
|
135
140
|
{layer.helperText}
|
|
@@ -16,12 +16,16 @@ export const styles = {
|
|
|
16
16
|
// `.PsLegend-rowFade` are hidden on fine-pointer devices and fade in when
|
|
17
17
|
// the row is hovered or holds focus; a control carrying `.active` (dirty
|
|
18
18
|
// state — hidden layer, open menu, opacity ≠ 1) stays visible. Coarse-
|
|
19
|
-
// pointer (touch) devices always show them.
|
|
20
|
-
//
|
|
19
|
+
// pointer (touch) devices always show them. Each non-active child also
|
|
20
|
+
// collapses to zero width on its own (not just the whole actions slot —
|
|
21
|
+
// see `PsLegend-rowActions` below) so a sibling's `.active` pin (e.g. the
|
|
22
|
+
// eye while hidden) doesn't leave a same-sized invisible hole where the
|
|
23
|
+
// still-faded ⋮ menu would've been; the title grows into that space. The
|
|
24
|
+
// collapse chevron sits outside the fade group and never fades.
|
|
21
25
|
'& .PsLegend-rowFade > *': {
|
|
22
26
|
opacity: 1,
|
|
23
27
|
transition: ({ transitions }: Theme) =>
|
|
24
|
-
transitions.create('opacity', {
|
|
28
|
+
transitions.create(['opacity', 'width'], {
|
|
25
29
|
duration: transitions.duration.standard,
|
|
26
30
|
easing: transitions.easing.easeInOut,
|
|
27
31
|
}),
|
|
@@ -32,8 +36,44 @@ export const styles = {
|
|
|
32
36
|
opacity: 1,
|
|
33
37
|
},
|
|
34
38
|
},
|
|
39
|
+
// Faded actions don't reserve flex space on fine-pointer devices — the
|
|
40
|
+
// title grows to the trailing edge. Hover/focus-within restores the slot
|
|
41
|
+
// so the title reflows with the buttons; a dirty `.active` child keeps
|
|
42
|
+
// the slot open even outside hover.
|
|
43
|
+
'@media (hover: hover)': {
|
|
44
|
+
'& .PsLegend-rowFade > *:not(.active)': {
|
|
45
|
+
width: 0,
|
|
46
|
+
minWidth: 0,
|
|
47
|
+
// border-box IconButtons keep their padding inside `width` — zero it
|
|
48
|
+
// too, otherwise the fixed 2px+2px padding survives as a tiny but
|
|
49
|
+
// needless residual gap.
|
|
50
|
+
padding: 0,
|
|
51
|
+
overflow: 'hidden',
|
|
52
|
+
pointerEvents: 'none',
|
|
53
|
+
},
|
|
54
|
+
'& .PsLegend-rowActions:not(:has(.active))': {
|
|
55
|
+
width: 0,
|
|
56
|
+
minWidth: 0,
|
|
57
|
+
overflow: 'hidden',
|
|
58
|
+
// Cancel the header gap so nothing is left behind the title.
|
|
59
|
+
marginInlineStart: ({ spacing }: Theme) => `calc(-1 * ${spacing(0.5)})`,
|
|
60
|
+
pointerEvents: 'none',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
35
63
|
'&:hover .PsLegend-rowFade > *, &:focus-within .PsLegend-rowFade > *': {
|
|
36
64
|
opacity: 1,
|
|
65
|
+
width: 'auto',
|
|
66
|
+
minWidth: 0,
|
|
67
|
+
padding: '2px',
|
|
68
|
+
overflow: 'visible',
|
|
69
|
+
pointerEvents: 'auto',
|
|
70
|
+
},
|
|
71
|
+
'&:hover .PsLegend-rowActions, &:focus-within .PsLegend-rowActions': {
|
|
72
|
+
width: 'auto',
|
|
73
|
+
minWidth: 0,
|
|
74
|
+
overflow: 'visible',
|
|
75
|
+
marginInlineStart: 0,
|
|
76
|
+
pointerEvents: 'auto',
|
|
37
77
|
},
|
|
38
78
|
// Hovering anywhere in the row also reveals the category/icon values
|
|
39
79
|
// (the renderers keep their own list-scoped rule for standalone usage).
|
|
@@ -68,7 +108,9 @@ export const styles = {
|
|
|
68
108
|
// band (subtitle flows below them) — hence `flex-start` with the title
|
|
69
109
|
// optically centered against the 30px icon-button row. Sticky: pins inside
|
|
70
110
|
// the panel's scroll container; `top` is applied inline per row (0, or the
|
|
71
|
-
// group-header height for grouped rows).
|
|
111
|
+
// group-header height for grouped rows). Grouped rows get an extra 20px
|
|
112
|
+
// left inset (36px total) so member layers read as nested under the group
|
|
113
|
+
// and line up with the group's own icon+label text start.
|
|
72
114
|
header: {
|
|
73
115
|
display: 'flex',
|
|
74
116
|
alignItems: 'flex-start',
|
|
@@ -81,6 +123,9 @@ export const styles = {
|
|
|
81
123
|
zIndex: 2,
|
|
82
124
|
backgroundColor: 'background.paper',
|
|
83
125
|
},
|
|
126
|
+
headerGrouped: {
|
|
127
|
+
paddingLeft: 4.5,
|
|
128
|
+
},
|
|
84
129
|
titleBox: {
|
|
85
130
|
flex: 1,
|
|
86
131
|
minWidth: 0,
|
|
@@ -107,6 +152,10 @@ export const styles = {
|
|
|
107
152
|
alignItems: 'center',
|
|
108
153
|
gap: 0.5,
|
|
109
154
|
},
|
|
155
|
+
// No `flex:1` here — the title sizes to its own text (shrinking to
|
|
156
|
+
// ellipsis when too long) so the chevron sits right after it, instead of
|
|
157
|
+
// being dragged to the trailing edge of `titleLine` (which spans the full
|
|
158
|
+
// width of `titleBox`, itself `flex:1` to fill the header up to actions).
|
|
110
159
|
title: {
|
|
111
160
|
minWidth: 0,
|
|
112
161
|
overflow: 'hidden',
|
|
@@ -154,10 +203,16 @@ export const styles = {
|
|
|
154
203
|
gap: 1,
|
|
155
204
|
padding: ({ spacing }) => spacing(0.5, 2, 1, 2),
|
|
156
205
|
},
|
|
206
|
+
contentGrouped: {
|
|
207
|
+
paddingLeft: 4.5,
|
|
208
|
+
},
|
|
157
209
|
// ~16px above the note (content's 8px bottom inset + 8px here), per design.
|
|
158
210
|
footer: {
|
|
159
211
|
padding: ({ spacing }) => spacing(1, 2, 1, 2),
|
|
160
212
|
},
|
|
213
|
+
footerGrouped: {
|
|
214
|
+
paddingLeft: 4.5,
|
|
215
|
+
},
|
|
161
216
|
dimmed: {
|
|
162
217
|
opacity: 0.5,
|
|
163
218
|
},
|