@godxjp/ui 33.25.1 → 33.26.0
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/README.md +4 -4
- package/agent/START-HERE.md +5 -5
- package/agent/components/Flex.json +28 -3
- package/agent/components/RadarChart.json +119 -0
- package/agent/components-index.json +5 -0
- package/agent/components.json +147 -3
- package/agent/index.json +6 -6
- package/agent/llms.txt +6 -6
- package/agent/tokens.json +198 -0
- package/dist/components/charts/index.d.ts +2 -0
- package/dist/components/charts/index.js +3 -1
- package/dist/components/charts/radar-chart.d.ts +5 -0
- package/dist/components/charts/radar-chart.js +273 -0
- package/dist/components/layout/affix.js +29 -16
- package/dist/components/layout/flex.js +47 -1
- package/dist/contracts/measurement.json +1 -1
- package/dist/i18n/messages/en.json +1 -0
- package/dist/i18n/messages/ja.json +1 -0
- package/dist/i18n/messages/vi.json +1 -0
- package/dist/props/components/charts.prop.d.ts +19 -0
- package/dist/props/components/layout.prop.d.ts +14 -2
- package/dist/props/registry.d.ts +57 -0
- package/dist/props/registry.js +50 -0
- package/dist/styles/layers.json +7 -1
- package/dist/styles/layout.css +172 -1
- package/dist/styles/precompiled/base.css +1 -1
- package/dist/styles/precompiled/layout.css +1 -1
- package/dist/styles.css +1 -1
- package/dist/tokens/base.css +4 -0
- package/dist/tokens/components/flex.css +25 -0
- package/dist/tokens/components/radar-chart.css +21 -0
- package/package.json +6 -2
package/agent/tokens.json
CHANGED
|
@@ -13337,6 +13337,120 @@
|
|
|
13337
13337
|
"tier": "component",
|
|
13338
13338
|
"value": "initial"
|
|
13339
13339
|
},
|
|
13340
|
+
{
|
|
13341
|
+
"description": "Lightweight row surfaces and hover actions; theme overrides remain scoped component knobs.",
|
|
13342
|
+
"name": "--flex-card-size-ratio",
|
|
13343
|
+
"tier": "component",
|
|
13344
|
+
"value": "1.4"
|
|
13345
|
+
},
|
|
13346
|
+
{
|
|
13347
|
+
"description": "Lightweight row surfaces and hover actions; theme overrides remain scoped component knobs.",
|
|
13348
|
+
"name": "--flex-card-width",
|
|
13349
|
+
"tier": "component",
|
|
13350
|
+
"value": "clamp(3.25rem, 6.6vw, 5.625rem)"
|
|
13351
|
+
},
|
|
13352
|
+
{
|
|
13353
|
+
"description": "Lightweight row surfaces and hover actions; theme overrides remain scoped component knobs.",
|
|
13354
|
+
"name": "--flex-compact-card-width",
|
|
13355
|
+
"tier": "component",
|
|
13356
|
+
"value": "clamp(1.875rem, 3.3vw, 2.8125rem)"
|
|
13357
|
+
},
|
|
13358
|
+
{
|
|
13359
|
+
"description": "Lightweight row surfaces and hover actions; theme overrides remain scoped component knobs.",
|
|
13360
|
+
"name": "--flex-orientation-duration",
|
|
13361
|
+
"tier": "component",
|
|
13362
|
+
"value": "180ms"
|
|
13363
|
+
},
|
|
13364
|
+
{
|
|
13365
|
+
"description": "Lightweight row surfaces and hover actions; theme overrides remain scoped component knobs.",
|
|
13366
|
+
"name": "--flex-felt-background",
|
|
13367
|
+
"tier": "component",
|
|
13368
|
+
"value": "initial"
|
|
13369
|
+
},
|
|
13370
|
+
{
|
|
13371
|
+
"description": "Lightweight row surfaces and hover actions; theme overrides remain scoped component knobs.",
|
|
13372
|
+
"name": "--flex-felt-foreground",
|
|
13373
|
+
"tier": "component",
|
|
13374
|
+
"value": "initial"
|
|
13375
|
+
},
|
|
13376
|
+
{
|
|
13377
|
+
"description": "Lightweight row surfaces and hover actions; theme overrides remain scoped component knobs.",
|
|
13378
|
+
"name": "--flex-felt-muted-foreground",
|
|
13379
|
+
"tier": "component",
|
|
13380
|
+
"value": "initial"
|
|
13381
|
+
},
|
|
13382
|
+
{
|
|
13383
|
+
"description": "Lightweight row surfaces and hover actions; theme overrides remain scoped component knobs.",
|
|
13384
|
+
"name": "--flex-felt-focus-color",
|
|
13385
|
+
"tier": "component",
|
|
13386
|
+
"value": "initial"
|
|
13387
|
+
},
|
|
13388
|
+
{
|
|
13389
|
+
"description": "Lightweight row surfaces and hover actions; theme overrides remain scoped component knobs.",
|
|
13390
|
+
"name": "--flex-felt-control-border-alpha",
|
|
13391
|
+
"tier": "component",
|
|
13392
|
+
"value": "0.65"
|
|
13393
|
+
},
|
|
13394
|
+
{
|
|
13395
|
+
"description": "Lightweight row surfaces and hover actions; theme overrides remain scoped component knobs.",
|
|
13396
|
+
"name": "--flex-felt-track-alpha",
|
|
13397
|
+
"tier": "component",
|
|
13398
|
+
"value": "0.8"
|
|
13399
|
+
},
|
|
13400
|
+
{
|
|
13401
|
+
"description": "Lightweight row surfaces and hover actions; theme overrides remain scoped component knobs.",
|
|
13402
|
+
"name": "--flex-felt-grain-width",
|
|
13403
|
+
"tier": "component",
|
|
13404
|
+
"value": "1px"
|
|
13405
|
+
},
|
|
13406
|
+
{
|
|
13407
|
+
"description": "Lightweight row surfaces and hover actions; theme overrides remain scoped component knobs.",
|
|
13408
|
+
"name": "--flex-felt-highlight-size",
|
|
13409
|
+
"tier": "component",
|
|
13410
|
+
"value": "72%"
|
|
13411
|
+
},
|
|
13412
|
+
{
|
|
13413
|
+
"description": "Lightweight row surfaces and hover actions; theme overrides remain scoped component knobs.",
|
|
13414
|
+
"name": "--flex-felt-grain-size",
|
|
13415
|
+
"tier": "component",
|
|
13416
|
+
"value": "3px"
|
|
13417
|
+
},
|
|
13418
|
+
{
|
|
13419
|
+
"description": "Lightweight row surfaces and hover actions; theme overrides remain scoped component knobs.",
|
|
13420
|
+
"name": "--flex-felt-grain-alpha",
|
|
13421
|
+
"tier": "component",
|
|
13422
|
+
"value": "0.025"
|
|
13423
|
+
},
|
|
13424
|
+
{
|
|
13425
|
+
"description": "Lightweight row surfaces and hover actions; theme overrides remain scoped component knobs.",
|
|
13426
|
+
"name": "--flex-felt-highlight-alpha",
|
|
13427
|
+
"tier": "component",
|
|
13428
|
+
"value": "0.045"
|
|
13429
|
+
},
|
|
13430
|
+
{
|
|
13431
|
+
"description": "Lightweight row surfaces and hover actions; theme overrides remain scoped component knobs.",
|
|
13432
|
+
"name": "--flex-etched-border-color",
|
|
13433
|
+
"tier": "component",
|
|
13434
|
+
"value": "initial"
|
|
13435
|
+
},
|
|
13436
|
+
{
|
|
13437
|
+
"description": "Lightweight row surfaces and hover actions; theme overrides remain scoped component knobs.",
|
|
13438
|
+
"name": "--flex-etched-border-alpha",
|
|
13439
|
+
"tier": "component",
|
|
13440
|
+
"value": "0.5"
|
|
13441
|
+
},
|
|
13442
|
+
{
|
|
13443
|
+
"description": "Lightweight row surfaces and hover actions; theme overrides remain scoped component knobs.",
|
|
13444
|
+
"name": "--flex-etched-border-size",
|
|
13445
|
+
"tier": "component",
|
|
13446
|
+
"value": "var(--stroke-hairline)"
|
|
13447
|
+
},
|
|
13448
|
+
{
|
|
13449
|
+
"description": "Lightweight row surfaces and hover actions; theme overrides remain scoped component knobs.",
|
|
13450
|
+
"name": "--flex-etched-radius",
|
|
13451
|
+
"tier": "component",
|
|
13452
|
+
"value": "initial"
|
|
13453
|
+
},
|
|
13340
13454
|
{
|
|
13341
13455
|
"description": "THE TWO CORNER INSETS gh#558 ASKED FOR BY NAME. Ant Design's are `marginXXL` (48px) from the bottom and `marginLG` (24px) from the inline end. Spelled here in this library's own stack/inline steps, which land on the same two numbers: --space-stack-xl = --space-10 = 40px, --space-6 = 24px. The block inset is the one a service actually has to move — a sticky action bar, a cookie strip, a mobile tab bar all live in that corner — and moving it is now retuning ONE custom property, not writing a media query at the call site (which is `no-arbitrary-spacing` and has no legal spelling).",
|
|
13342
13456
|
"name": "--float-button-offset-block-end",
|
|
@@ -15701,6 +15815,90 @@
|
|
|
15701
15815
|
"tier": "component",
|
|
15702
15816
|
"value": "initial"
|
|
15703
15817
|
},
|
|
15818
|
+
{
|
|
15819
|
+
"description": "RadarChart tokens. Radii and font sizes use intrinsic SVG coordinates; colors resolve in the local theme scope.",
|
|
15820
|
+
"name": "--radar-chart-label-inset",
|
|
15821
|
+
"tier": "component",
|
|
15822
|
+
"value": "8px"
|
|
15823
|
+
},
|
|
15824
|
+
{
|
|
15825
|
+
"description": "Unitless scale of the normalized polar geometry.",
|
|
15826
|
+
"name": "--radar-chart-plot-radius",
|
|
15827
|
+
"tier": "component",
|
|
15828
|
+
"value": "96"
|
|
15829
|
+
},
|
|
15830
|
+
{
|
|
15831
|
+
"description": "scale-exempt: polar distance in viewBox user units, not a corner radius",
|
|
15832
|
+
"name": "--radar-chart-label-radius",
|
|
15833
|
+
"tier": "component",
|
|
15834
|
+
"value": "124px"
|
|
15835
|
+
},
|
|
15836
|
+
{
|
|
15837
|
+
"description": "RadarChart tokens. Radii and font sizes use intrinsic SVG coordinates; colors resolve in the local theme scope.",
|
|
15838
|
+
"name": "--radar-chart-grid-alpha",
|
|
15839
|
+
"tier": "component",
|
|
15840
|
+
"value": "0.16"
|
|
15841
|
+
},
|
|
15842
|
+
{
|
|
15843
|
+
"description": "RadarChart tokens. Radii and font sizes use intrinsic SVG coordinates; colors resolve in the local theme scope.",
|
|
15844
|
+
"name": "--radar-chart-boundary-alpha",
|
|
15845
|
+
"tier": "component",
|
|
15846
|
+
"value": "0.32"
|
|
15847
|
+
},
|
|
15848
|
+
{
|
|
15849
|
+
"description": "RadarChart tokens. Radii and font sizes use intrinsic SVG coordinates; colors resolve in the local theme scope.",
|
|
15850
|
+
"name": "--radar-chart-axis-alpha",
|
|
15851
|
+
"tier": "component",
|
|
15852
|
+
"value": "0.2"
|
|
15853
|
+
},
|
|
15854
|
+
{
|
|
15855
|
+
"description": "scale-exempt: viewBox user units, scaled with the SVG, not CSS type",
|
|
15856
|
+
"name": "--radar-chart-label-font-size",
|
|
15857
|
+
"tier": "component",
|
|
15858
|
+
"value": "16px"
|
|
15859
|
+
},
|
|
15860
|
+
{
|
|
15861
|
+
"description": "Unitless second-line leading, multiplied by the resolved label font size.",
|
|
15862
|
+
"name": "--radar-chart-label-line-height",
|
|
15863
|
+
"tier": "component",
|
|
15864
|
+
"value": "1.25"
|
|
15865
|
+
},
|
|
15866
|
+
{
|
|
15867
|
+
"description": "RadarChart tokens. Radii and font sizes use intrinsic SVG coordinates; colors resolve in the local theme scope.",
|
|
15868
|
+
"name": "--radar-chart-tick-offset",
|
|
15869
|
+
"tier": "component",
|
|
15870
|
+
"value": "6px"
|
|
15871
|
+
},
|
|
15872
|
+
{
|
|
15873
|
+
"description": "scale-exempt: viewBox user units, scaled with the SVG, not CSS type",
|
|
15874
|
+
"name": "--radar-chart-tick-font-size",
|
|
15875
|
+
"tier": "component",
|
|
15876
|
+
"value": "10px"
|
|
15877
|
+
},
|
|
15878
|
+
{
|
|
15879
|
+
"description": "scale-exempt: SVG circle r in viewBox user units, not a corner radius",
|
|
15880
|
+
"name": "--radar-chart-point-radius",
|
|
15881
|
+
"tier": "component",
|
|
15882
|
+
"value": "3px"
|
|
15883
|
+
},
|
|
15884
|
+
{
|
|
15885
|
+
"description": "HSL channels; default muted-foreground resolves at the chart.",
|
|
15886
|
+
"name": "--radar-chart-grid-color",
|
|
15887
|
+
"tier": "component",
|
|
15888
|
+
"value": "initial"
|
|
15889
|
+
},
|
|
15890
|
+
{
|
|
15891
|
+
"description": "HSL channels; default foreground resolves at the chart.",
|
|
15892
|
+
"name": "--radar-chart-label-color",
|
|
15893
|
+
"tier": "component",
|
|
15894
|
+
"value": "initial"
|
|
15895
|
+
},
|
|
15896
|
+
{
|
|
15897
|
+
"description": "RadarChart tokens. Radii and font sizes use intrinsic SVG coordinates; colors resolve in the local theme scope.",
|
|
15898
|
+
"name": "--radar-chart-grid-stroke-width",
|
|
15899
|
+
"tier": "component",
|
|
15900
|
+
"value": "1px"
|
|
15901
|
+
},
|
|
15704
15902
|
{
|
|
15705
15903
|
"description": "Separator layout thickness; focus paint must not reserve extra space.",
|
|
15706
15904
|
"name": "--resizable-handle-size",
|
|
@@ -18,3 +18,5 @@ export { PieChart } from "./pie-chart.js";
|
|
|
18
18
|
export type { PieChartProp, PieChartProps } from "./pie-chart.js";
|
|
19
19
|
export type { ChartSeriesProp, ChartDatum, ChartDatumSelect } from "../../props/components/charts.prop.js";
|
|
20
20
|
export { CHART_COLORS } from "./chart-frame.js";
|
|
21
|
+
export { RadarChart } from "./radar-chart.js";
|
|
22
|
+
export type { RadarChartProp, RadarChartProps } from "./radar-chart.js";
|
|
@@ -5,6 +5,7 @@ import { HeatmapChart } from "./heatmap-chart.js";
|
|
|
5
5
|
import { AreaChart } from "./area-chart.js";
|
|
6
6
|
import { PieChart } from "./pie-chart.js";
|
|
7
7
|
import { CHART_COLORS } from "./chart-frame.js";
|
|
8
|
+
import { RadarChart } from "./radar-chart.js";
|
|
8
9
|
export {
|
|
9
10
|
AreaChart,
|
|
10
11
|
BarChart,
|
|
@@ -12,5 +13,6 @@ export {
|
|
|
12
13
|
CompactBarTrend,
|
|
13
14
|
HeatmapChart,
|
|
14
15
|
LineChart,
|
|
15
|
-
PieChart
|
|
16
|
+
PieChart,
|
|
17
|
+
RadarChart
|
|
16
18
|
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { RadarChartProp } from "../../props/components/charts.prop.js";
|
|
3
|
+
export type { RadarChartProp, RadarChartProp as RadarChartProps, } from "../../props/components/charts.prop.js";
|
|
4
|
+
/** Shared-scale profile. SVG geometry is intrinsic, never consumer CSS or inferred scores. */
|
|
5
|
+
export declare function RadarChart({ data, categoryKey, valueKey, valueMax, valueTicks, label, showCaption, description, size, numberFormat, emptyMessage, footer, className, id, ref, }: RadarChartProp): React.JSX.Element;
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { EmptyState } from "../data-display/empty-state.js";
|
|
5
|
+
import { useTranslation } from "../../i18n/use-translation.js";
|
|
6
|
+
import { ChartFrame, chartColor, chartHeight, useChartNumberFormat } from "./chart-frame.js";
|
|
7
|
+
function wrapLabel(text, locale, width, measure) {
|
|
8
|
+
const words = new Intl.Segmenter(locale, { granularity: "word" });
|
|
9
|
+
const graphemes = new Intl.Segmenter(locale, { granularity: "grapheme" });
|
|
10
|
+
const lines = [];
|
|
11
|
+
let line = "";
|
|
12
|
+
for (const { segment } of words.segment(text)) {
|
|
13
|
+
if (measure(line + segment) <= width) {
|
|
14
|
+
line += segment;
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
if (line.trim()) {
|
|
18
|
+
lines.push(line.trim());
|
|
19
|
+
line = "";
|
|
20
|
+
}
|
|
21
|
+
for (const { segment: char } of graphemes.segment(segment.trimStart())) {
|
|
22
|
+
if (line && measure(line + char) > width) {
|
|
23
|
+
lines.push(line);
|
|
24
|
+
line = "";
|
|
25
|
+
}
|
|
26
|
+
line += char;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (line.trim()) lines.push(line.trim());
|
|
30
|
+
if (lines.length <= 2) return lines;
|
|
31
|
+
const last = Array.from(graphemes.segment(lines[1]), (part) => part.segment);
|
|
32
|
+
while (last.length && measure(last.join("") + "\u2026") > width) last.pop();
|
|
33
|
+
return [lines[0], last.join("") + "\u2026"];
|
|
34
|
+
}
|
|
35
|
+
function RadarChart({
|
|
36
|
+
data,
|
|
37
|
+
categoryKey,
|
|
38
|
+
valueKey,
|
|
39
|
+
valueMax,
|
|
40
|
+
valueTicks,
|
|
41
|
+
label,
|
|
42
|
+
showCaption = true,
|
|
43
|
+
description,
|
|
44
|
+
size = "md",
|
|
45
|
+
numberFormat,
|
|
46
|
+
emptyMessage,
|
|
47
|
+
footer,
|
|
48
|
+
className,
|
|
49
|
+
id,
|
|
50
|
+
ref
|
|
51
|
+
}) {
|
|
52
|
+
const { t, locale } = useTranslation();
|
|
53
|
+
const fmt = useChartNumberFormat(numberFormat ?? { maximumSignificantDigits: 21 });
|
|
54
|
+
const uid = React.useId();
|
|
55
|
+
const svgRef = React.useRef(null);
|
|
56
|
+
const [measuredLabels, setMeasuredLabels] = React.useState([]);
|
|
57
|
+
React.useEffect(() => {
|
|
58
|
+
const svg = svgRef.current;
|
|
59
|
+
if (!svg || typeof svg.getBBox !== "function") return;
|
|
60
|
+
let disposed = false;
|
|
61
|
+
const context = document.createElement("canvas").getContext("2d");
|
|
62
|
+
if (!context) return;
|
|
63
|
+
const update = () => {
|
|
64
|
+
if (disposed) return;
|
|
65
|
+
const lines = Array.from(
|
|
66
|
+
svg.querySelectorAll('[data-slot="radar-label"]'),
|
|
67
|
+
(node, index) => {
|
|
68
|
+
const style = getComputedStyle(node);
|
|
69
|
+
context.font = `${style.fontWeight} ${style.fontSize} ${style.fontFamily}`;
|
|
70
|
+
const transform = new DOMMatrix(style.transform);
|
|
71
|
+
const inset = parseFloat(style.getPropertyValue("--radar-chart-label-inset")) || 0;
|
|
72
|
+
const width = Math.abs(transform.e) > 1 ? 240 - Math.abs(transform.e) - inset : 480 - inset * 2;
|
|
73
|
+
return {
|
|
74
|
+
lines: wrapLabel(
|
|
75
|
+
String(data[index][categoryKey] ?? ""),
|
|
76
|
+
locale,
|
|
77
|
+
Math.max(1, width),
|
|
78
|
+
(text) => context.measureText(text).width
|
|
79
|
+
),
|
|
80
|
+
lineHeight: parseFloat(style.getPropertyValue("--radar-chart-label-line-height")) || 1.25
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
setMeasuredLabels(
|
|
85
|
+
(previous) => JSON.stringify(previous) === JSON.stringify(lines) ? previous : lines
|
|
86
|
+
);
|
|
87
|
+
};
|
|
88
|
+
update();
|
|
89
|
+
const resize = new ResizeObserver(update);
|
|
90
|
+
resize.observe(svg);
|
|
91
|
+
const theme = new MutationObserver(update);
|
|
92
|
+
for (let node = svg; node; node = node.parentElement) {
|
|
93
|
+
theme.observe(node, {
|
|
94
|
+
attributes: true,
|
|
95
|
+
attributeFilter: ["class", "style", "data-theme", "data-tenant"]
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
void document.fonts?.ready.then(update);
|
|
99
|
+
return () => {
|
|
100
|
+
disposed = true;
|
|
101
|
+
resize.disconnect();
|
|
102
|
+
theme.disconnect();
|
|
103
|
+
};
|
|
104
|
+
}, [data, categoryKey, locale, size, valueMax]);
|
|
105
|
+
const valid = data.length >= 3 && data.length <= 6 && Number.isFinite(valueMax) && valueMax > 0 && data.every(
|
|
106
|
+
(d) => typeof d[valueKey] === "number" && Number.isFinite(d[valueKey]) && Number(d[valueKey]) >= 0 && Number(d[valueKey]) <= valueMax
|
|
107
|
+
);
|
|
108
|
+
const point = (index, fraction, radius = 1) => {
|
|
109
|
+
const angle = -Math.PI / 2 + index * Math.PI * 2 / data.length;
|
|
110
|
+
return [Math.cos(angle) * radius * fraction, Math.sin(angle) * radius * fraction];
|
|
111
|
+
};
|
|
112
|
+
const polygon = (fraction) => data.map((_, i) => point(i, fraction).join(",")).join(" ");
|
|
113
|
+
const rows = data.map(
|
|
114
|
+
(d) => `${String(d[categoryKey] ?? "")}: ${fmt.format(Number(d[valueKey]))}`
|
|
115
|
+
);
|
|
116
|
+
const summary = t("chart.summaryRadar", {
|
|
117
|
+
maximum: fmt.format(valueMax),
|
|
118
|
+
points: fmt.format(data.length)
|
|
119
|
+
});
|
|
120
|
+
const ticks = valueTicks?.filter((v) => Number.isFinite(v) && v > 0 && v <= valueMax).map((v) => v / valueMax) ?? [0.25, 0.5, 0.75, 1];
|
|
121
|
+
const stroke = "hsl(var(--radar-chart-grid-color, var(--muted-foreground)))";
|
|
122
|
+
return /* @__PURE__ */ jsx(
|
|
123
|
+
ChartFrame,
|
|
124
|
+
{
|
|
125
|
+
ref,
|
|
126
|
+
label,
|
|
127
|
+
showCaption,
|
|
128
|
+
description,
|
|
129
|
+
summaryRows: rows,
|
|
130
|
+
imgSummary: summary,
|
|
131
|
+
hasData: valid,
|
|
132
|
+
size,
|
|
133
|
+
footer,
|
|
134
|
+
className,
|
|
135
|
+
id,
|
|
136
|
+
children: valid ? /* @__PURE__ */ jsxs(
|
|
137
|
+
"svg",
|
|
138
|
+
{
|
|
139
|
+
ref: svgRef,
|
|
140
|
+
width: "100%",
|
|
141
|
+
style: { maxHeight: chartHeight(size), display: "block" },
|
|
142
|
+
viewBox: "0 0 480 320",
|
|
143
|
+
"aria-hidden": "true",
|
|
144
|
+
"data-slot": "radar-plot",
|
|
145
|
+
children: [
|
|
146
|
+
/* @__PURE__ */ jsx("title", { id: `${uid}-radar-title`, children: typeof label === "string" ? label : summary }),
|
|
147
|
+
/* @__PURE__ */ jsx("desc", { children: rows.join("; ") }),
|
|
148
|
+
/* @__PURE__ */ jsxs("g", { transform: "translate(240 160)", children: [
|
|
149
|
+
ticks.map((f) => /* @__PURE__ */ jsx(
|
|
150
|
+
"polygon",
|
|
151
|
+
{
|
|
152
|
+
points: polygon(f),
|
|
153
|
+
fill: "none",
|
|
154
|
+
style: { transform: "scale(var(--radar-chart-plot-radius))" },
|
|
155
|
+
vectorEffect: "non-scaling-stroke",
|
|
156
|
+
strokeWidth: "var(--radar-chart-grid-stroke-width)",
|
|
157
|
+
stroke,
|
|
158
|
+
opacity: f === 1 ? "var(--radar-chart-boundary-alpha)" : "var(--radar-chart-grid-alpha)"
|
|
159
|
+
},
|
|
160
|
+
f
|
|
161
|
+
)),
|
|
162
|
+
data.map((_, i) => {
|
|
163
|
+
const [x, y] = point(i, 1);
|
|
164
|
+
return /* @__PURE__ */ jsx(
|
|
165
|
+
"line",
|
|
166
|
+
{
|
|
167
|
+
strokeWidth: "var(--radar-chart-grid-stroke-width)",
|
|
168
|
+
x1: 0,
|
|
169
|
+
y1: 0,
|
|
170
|
+
x2: x,
|
|
171
|
+
y2: y,
|
|
172
|
+
stroke,
|
|
173
|
+
opacity: "var(--radar-chart-axis-alpha)",
|
|
174
|
+
vectorEffect: "non-scaling-stroke",
|
|
175
|
+
style: { transform: "scale(var(--radar-chart-plot-radius))" }
|
|
176
|
+
},
|
|
177
|
+
i
|
|
178
|
+
);
|
|
179
|
+
}),
|
|
180
|
+
ticks.map((f) => /* @__PURE__ */ jsx(
|
|
181
|
+
"text",
|
|
182
|
+
{
|
|
183
|
+
"data-slot": "radar-tick",
|
|
184
|
+
style: {
|
|
185
|
+
transform: `translate(calc(-1 * var(--radar-chart-tick-offset)), calc(-${f} * var(--radar-chart-plot-radius) * 1px))`
|
|
186
|
+
},
|
|
187
|
+
textAnchor: "end",
|
|
188
|
+
fill: stroke,
|
|
189
|
+
fontSize: "var(--radar-chart-tick-font-size)",
|
|
190
|
+
children: fmt.format(valueMax * f)
|
|
191
|
+
},
|
|
192
|
+
f
|
|
193
|
+
)),
|
|
194
|
+
/* @__PURE__ */ jsx(
|
|
195
|
+
"polygon",
|
|
196
|
+
{
|
|
197
|
+
"data-slot": "radar-area",
|
|
198
|
+
style: { transform: "scale(var(--radar-chart-plot-radius))" },
|
|
199
|
+
vectorEffect: "non-scaling-stroke",
|
|
200
|
+
points: data.map((d, i) => point(i, Number(d[valueKey]) / valueMax).join(",")).join(" "),
|
|
201
|
+
fill: chartColor(0),
|
|
202
|
+
fillOpacity: "var(--chart-area-fill-alpha)",
|
|
203
|
+
stroke: chartColor(0),
|
|
204
|
+
strokeWidth: "var(--chart-series-stroke-width)",
|
|
205
|
+
strokeLinejoin: "round"
|
|
206
|
+
}
|
|
207
|
+
),
|
|
208
|
+
data.map((d, i) => {
|
|
209
|
+
const [x, y] = point(i, Number(d[valueKey]) / valueMax);
|
|
210
|
+
const [lx, ly] = point(i, 1);
|
|
211
|
+
const fullLabel = String(d[categoryKey] ?? "");
|
|
212
|
+
const shown = measuredLabels[i]?.lines ?? wrapLabel(
|
|
213
|
+
fullLabel,
|
|
214
|
+
locale,
|
|
215
|
+
Math.abs(lx) > 0.1 ? 104 : 400,
|
|
216
|
+
(text) => Array.from(text).reduce(
|
|
217
|
+
(width, char) => width + (char.codePointAt(0) > 255 ? 16 : 9),
|
|
218
|
+
0
|
|
219
|
+
)
|
|
220
|
+
);
|
|
221
|
+
return /* @__PURE__ */ jsxs("g", { children: [
|
|
222
|
+
/* @__PURE__ */ jsx(
|
|
223
|
+
"circle",
|
|
224
|
+
{
|
|
225
|
+
r: "var(--radar-chart-point-radius)",
|
|
226
|
+
style: {
|
|
227
|
+
transform: `translate(calc(${x} * var(--radar-chart-plot-radius) * 1px), calc(${y} * var(--radar-chart-plot-radius) * 1px))`
|
|
228
|
+
},
|
|
229
|
+
fill: chartColor(0)
|
|
230
|
+
}
|
|
231
|
+
),
|
|
232
|
+
/* @__PURE__ */ jsxs(
|
|
233
|
+
"text",
|
|
234
|
+
{
|
|
235
|
+
"data-slot": "radar-label",
|
|
236
|
+
style: {
|
|
237
|
+
transform: `translate(calc(${lx} * var(--radar-chart-label-radius)), calc(${ly} * var(--radar-chart-label-radius)))`
|
|
238
|
+
},
|
|
239
|
+
textAnchor: lx > 0.1 ? "start" : lx < -0.1 ? "end" : "middle",
|
|
240
|
+
dominantBaseline: "middle",
|
|
241
|
+
fill: "hsl(var(--radar-chart-label-color, var(--foreground)))",
|
|
242
|
+
fontSize: "var(--radar-chart-label-font-size)",
|
|
243
|
+
fontFamily: "inherit",
|
|
244
|
+
children: [
|
|
245
|
+
/* @__PURE__ */ jsxs("title", { children: [
|
|
246
|
+
fullLabel,
|
|
247
|
+
": ",
|
|
248
|
+
fmt.format(Number(d[valueKey]))
|
|
249
|
+
] }),
|
|
250
|
+
shown.map((line, index) => /* @__PURE__ */ jsx(
|
|
251
|
+
"tspan",
|
|
252
|
+
{
|
|
253
|
+
x: 0,
|
|
254
|
+
dy: index ? `${measuredLabels[i]?.lineHeight ?? 1.25}em` : 0,
|
|
255
|
+
children: line
|
|
256
|
+
},
|
|
257
|
+
index
|
|
258
|
+
))
|
|
259
|
+
]
|
|
260
|
+
}
|
|
261
|
+
)
|
|
262
|
+
] }, i);
|
|
263
|
+
})
|
|
264
|
+
] })
|
|
265
|
+
]
|
|
266
|
+
}
|
|
267
|
+
) : /* @__PURE__ */ jsx(EmptyState, { title: emptyMessage ?? t("chart.empty") })
|
|
268
|
+
}
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
export {
|
|
272
|
+
RadarChart
|
|
273
|
+
};
|
|
@@ -67,7 +67,9 @@ const Affix = React.forwardRef(function Affix2({
|
|
|
67
67
|
assumeInView: true
|
|
68
68
|
});
|
|
69
69
|
const [rendered, setRendered] = React.useState(true);
|
|
70
|
-
const
|
|
70
|
+
const [sticky, setSticky] = React.useState(false);
|
|
71
|
+
const [stuck, setStuck] = React.useState(false);
|
|
72
|
+
const affixed = (sticky ? stuck : !inView) && rendered;
|
|
71
73
|
const [box, setBox] = React.useState(null);
|
|
72
74
|
const [targetInset, setTargetInset] = React.useState(0);
|
|
73
75
|
const [room, setRoom] = React.useState(null);
|
|
@@ -84,21 +86,31 @@ const Affix = React.forwardRef(function Affix2({
|
|
|
84
86
|
);
|
|
85
87
|
const rect = targetElement?.getBoundingClientRect();
|
|
86
88
|
const blockEl = fixedContainingBlock(content);
|
|
89
|
+
const sentinelRect = sentinelRef.current?.getBoundingClientRect();
|
|
90
|
+
const insetStart = sentinelRect ? rootRect.top - sentinelRect.top : 0;
|
|
91
|
+
const insetEnd = sentinelRect ? sentinelRect.bottom - rootRect.bottom : 0;
|
|
92
|
+
const visibleTop = Math.max(rect ? rect.top : 0, 0);
|
|
93
|
+
const visibleBottom = Math.min(rect ? rect.bottom : window.innerHeight, window.innerHeight);
|
|
94
|
+
const line = pinToEnd ? visibleBottom - insetEnd : (rect ? rect.top : 0) + insetStart;
|
|
95
|
+
const parent = root.parentElement;
|
|
96
|
+
const nextSticky = blockEl !== null && blockEl === targetElement && parent !== null && parent.getBoundingClientRect().height > rootRect.height + 1;
|
|
97
|
+
setSticky((previous) => previous === nextSticky ? previous : nextSticky);
|
|
98
|
+
if (nextSticky) {
|
|
99
|
+
const atLine = Math.abs((pinToEnd ? rootRect.bottom : rootRect.top) - line) < 1;
|
|
100
|
+
const nextStuck = atLine && (targetElement?.scrollTop ?? 0) > 0;
|
|
101
|
+
setStuck((previous) => previous === nextStuck ? previous : nextStuck);
|
|
102
|
+
setTargetInset((previous) => previous === 0 ? previous : 0);
|
|
103
|
+
}
|
|
87
104
|
const block = blockEl?.getBoundingClientRect();
|
|
88
105
|
const viewportInset = rect ? pinToEnd ? window.innerHeight - rect.bottom : rect.top : 0;
|
|
89
106
|
const blockOffset = block ? pinToEnd ? window.innerHeight - (block.bottom + ((blockEl?.scrollHeight ?? 0) - (blockEl?.clientHeight ?? 0) - (blockEl?.scrollTop ?? 0))) : block.top - (blockEl?.scrollTop ?? 0) : 0;
|
|
90
107
|
const next = viewportInset - blockOffset;
|
|
91
|
-
setTargetInset((previous) => previous === next ? previous : next);
|
|
92
|
-
if (!affixed) {
|
|
108
|
+
if (!nextSticky) setTargetInset((previous) => previous === next ? previous : next);
|
|
109
|
+
if (!affixed && !nextSticky) {
|
|
93
110
|
setRoom((previous) => previous === null ? previous : null);
|
|
94
111
|
return;
|
|
95
112
|
}
|
|
96
|
-
const nextRoom = Math.max(
|
|
97
|
-
0,
|
|
98
|
-
Math.floor(
|
|
99
|
-
pinToEnd ? contentRect.bottom - Math.max(rect ? rect.top : 0, 0) : Math.min(rect ? rect.bottom : window.innerHeight, window.innerHeight) - contentRect.top
|
|
100
|
-
)
|
|
101
|
-
);
|
|
113
|
+
const nextRoom = Math.max(0, Math.floor(pinToEnd ? line - visibleTop : visibleBottom - line));
|
|
102
114
|
const clamp = blockSize > nextRoom ? nextRoom : null;
|
|
103
115
|
setRoom((previous) => previous === clamp ? previous : clamp);
|
|
104
116
|
}, [affixed, pinToEnd, targetElement]);
|
|
@@ -120,7 +132,7 @@ const Affix = React.forwardRef(function Affix2({
|
|
|
120
132
|
};
|
|
121
133
|
}, [measure, targetElement]);
|
|
122
134
|
React.useEffect(() => {
|
|
123
|
-
if (!affixed || typeof window === "undefined") return void 0;
|
|
135
|
+
if (!affixed && !sticky || typeof window === "undefined") return void 0;
|
|
124
136
|
let frame = null;
|
|
125
137
|
const onScroll = () => {
|
|
126
138
|
if (frame !== null) return;
|
|
@@ -136,7 +148,7 @@ const Affix = React.forwardRef(function Affix2({
|
|
|
136
148
|
window.removeEventListener("resize", onScroll);
|
|
137
149
|
if (frame !== null) window.cancelAnimationFrame(frame);
|
|
138
150
|
};
|
|
139
|
-
}, [affixed, targetElement, measure]);
|
|
151
|
+
}, [affixed, sticky, targetElement, measure]);
|
|
140
152
|
React.useEffect(() => {
|
|
141
153
|
const content = contentRef.current;
|
|
142
154
|
if (pinToEnd || !affixed || !content || typeof document === "undefined") return void 0;
|
|
@@ -165,9 +177,9 @@ const Affix = React.forwardRef(function Affix2({
|
|
|
165
177
|
const style = {
|
|
166
178
|
...offsetBlockStart === void 0 ? void 0 : { "--affix-inset-block-start": `${offsetBlockStart}px` },
|
|
167
179
|
...offsetBlockEnd === void 0 ? void 0 : { "--affix-inset-block-end": `${offsetBlockEnd}px` },
|
|
168
|
-
...targetInset === 0 ? void 0 : { "--affix-target-inset": `${targetInset}px` },
|
|
169
|
-
|
|
170
|
-
...affixed && room !== null ? { "--affix-content-max-block-size": `${room}px` } : void 0
|
|
180
|
+
...sticky || targetInset === 0 ? void 0 : { "--affix-target-inset": `${targetInset}px` },
|
|
181
|
+
...!sticky && affixed && box ? { "--affix-content-inline-size": `${box.inline}px` } : void 0,
|
|
182
|
+
...(affixed || sticky) && room !== null ? { "--affix-content-max-block-size": `${room}px` } : void 0
|
|
171
183
|
};
|
|
172
184
|
return /* @__PURE__ */ jsxs(
|
|
173
185
|
"div",
|
|
@@ -180,6 +192,7 @@ const Affix = React.forwardRef(function Affix2({
|
|
|
180
192
|
id,
|
|
181
193
|
"data-slot": "affix",
|
|
182
194
|
"data-affixed": affixed ? "" : void 0,
|
|
195
|
+
"data-pin": sticky ? pinToEnd ? "sticky-end" : "sticky-start" : void 0,
|
|
183
196
|
className: cn("ui-affix", className),
|
|
184
197
|
style,
|
|
185
198
|
...rest,
|
|
@@ -194,7 +207,7 @@ const Affix = React.forwardRef(function Affix2({
|
|
|
194
207
|
className: "ui-affix-sentinel"
|
|
195
208
|
}
|
|
196
209
|
),
|
|
197
|
-
affixed && box ? /* @__PURE__ */ jsx(
|
|
210
|
+
affixed && box && !sticky ? /* @__PURE__ */ jsx(
|
|
198
211
|
"div",
|
|
199
212
|
{
|
|
200
213
|
"data-slot": "affix-placeholder",
|
|
@@ -209,7 +222,7 @@ const Affix = React.forwardRef(function Affix2({
|
|
|
209
222
|
ref: contentRef,
|
|
210
223
|
"data-slot": "affix-content",
|
|
211
224
|
"data-edge": pinToEnd ? "block-end" : "block-start",
|
|
212
|
-
"data-clamped": affixed && room !== null ? "" : void 0,
|
|
225
|
+
"data-clamped": (affixed || sticky) && room !== null ? "" : void 0,
|
|
213
226
|
"data-affixed": affixed ? "" : void 0,
|
|
214
227
|
className: "ui-affix-content",
|
|
215
228
|
children
|