@mittwald/flow-react-components 1.5.10 → 1.5.12

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.
@@ -2,6 +2,7 @@
2
2
  /* */
3
3
  import { useDesignTokens } from "../../../../lib/theming/hooks/useDesignTokens.mjs";
4
4
  import { useCartesianChartContext } from "../../context.mjs";
5
+ import { useTickLabelWidth } from "../../hooks/useTickLabelWidth.mjs";
5
6
  import { TypedYAxis } from "./types.mjs";
6
7
  import { jsx } from "react/jsx-runtime";
7
8
  import * as Recharts from "recharts";
@@ -20,17 +21,34 @@ var YAxis = (props) => {
20
21
  const { domain, type = "auto", width, ...rest } = props;
21
22
  const tokens = useDesignTokens();
22
23
  const { layout } = useCartesianChartContext();
23
- const isCategoryAxis = type === "category" || type === "auto" && layout === "vertical";
24
+ const tickLabels = useTickLabelWidth();
25
+ const isAutoWidth = (width ?? (type === "category" || type === "auto" && layout === "vertical" ? "auto" : void 0)) === "auto";
26
+ const tickMargin = parseInt(tokens.axis.spacing.value);
27
+ const tickSize = parseInt(tokens.axis["tick-size"].value);
28
+ const tickFill = tokens.axis["color"].value;
29
+ const autoWidth = tickLabels.width === void 0 ? "auto" : tickLabels.width + tickSize + tickMargin;
30
+ const measuredTick = (tickProps) => {
31
+ const { payload, index, tickFormatter } = tickProps;
32
+ const value = `${tickFormatter ? tickFormatter(payload.value, index) : payload.value}${rest.unit ?? ""}`;
33
+ return /* @__PURE__ */ jsx(Recharts.Text, {
34
+ ...tickProps,
35
+ ref: tickLabels.labelRef,
36
+ className: "recharts-cartesian-axis-tick-value",
37
+ fill: tickFill,
38
+ width: void 0,
39
+ children: value
40
+ });
41
+ };
24
42
  return /* @__PURE__ */ jsx(Recharts.YAxis, {
25
43
  ...rest,
26
44
  allowDataOverflow: true,
27
45
  domain,
28
46
  type,
29
- width: width ?? (isCategoryAxis ? "auto" : void 0),
47
+ width: isAutoWidth ? autoWidth : width,
30
48
  fontSize: tokens.axis["font-size"].value,
31
- tick: { fill: tokens.axis["color"].value },
32
- tickMargin: parseInt(tokens.axis.spacing.value),
33
- tickSize: parseInt(tokens.axis["tick-size"].value)
49
+ tick: isAutoWidth ? measuredTick : { fill: tickFill },
50
+ tickMargin,
51
+ tickSize
34
52
  });
35
53
  };
36
54
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"YAxis.mjs","names":[],"sources":["../../../../../../../../../src/components/CartesianChart/components/YAxis/YAxis.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport * as Recharts from \"recharts\";\nimport type {\n ChartDataValue,\n DataKey,\n DataKeyValue,\n} from \"@/components/CartesianChart/types\";\nimport { useCartesianChartContext } from \"@/components/CartesianChart/context\";\nimport { useDesignTokens } from \"@/lib/theming\";\n\nexport type YAxisProps<\n TData extends ChartDataValue = ChartDataValue,\n TDataKey extends DataKey<TData> = DataKey<TData>,\n TDataMatch = DataKeyValue<TData, TDataKey>,\n> = Pick<\n Recharts.YAxisProps,\n | \"className\"\n | \"orientation\"\n | \"allowDecimals\"\n | \"interval\"\n | \"minTickGap\"\n | \"scale\"\n | \"type\"\n | \"domain\"\n | \"hide\"\n | \"unit\"\n> & {\n dataKey?: TDataKey;\n tickFormatter?: (value: TDataMatch, index: number) => string;\n /**\n * The width of the axis in pixels. `\"auto\"` sizes the axis to its widest tick\n * label. Defaults to `\"auto\"` for a category axis (e.g. with `layout` set to\n * `\"vertical\"`) and to `60` otherwise. Pin a number to align the plot areas\n * of synchronized charts.\n */\n width?: number | \"auto\";\n};\n\n/**\n * `tickFormatter` is deliberately not part of the remote surface. A function\n * property crosses the boundary as a thread proxy whose call returns a Promise,\n * and recharts concatenates the formatter's result straight into the tick label\n * — so remotely every tick rendered `[object Promise]`. Dropping it from the\n * remote surface leaves the raw value there; the prop keeps working locally.\n *\n * @flr-generate all\n * @flr-ignore-props tickFormatter\n */\nexport const YAxis: FC<YAxisProps> = (props) => {\n const { domain, type = \"auto\", width, ...rest } = props;\n\n const tokens = useDesignTokens();\n const { layout } = useCartesianChartContext();\n\n const isCategoryAxis =\n type === \"category\" || (type === \"auto\" && layout === \"vertical\");\n\n return (\n <Recharts.YAxis\n {...rest}\n allowDataOverflow\n domain={domain}\n type={type}\n width={width ?? (isCategoryAxis ? \"auto\" : undefined)}\n fontSize={tokens.axis[\"font-size\"].value}\n tick={{\n fill: tokens.axis[\"color\"].value,\n }}\n tickMargin={parseInt(tokens.axis.spacing.value)}\n tickSize={parseInt(tokens.axis[\"tick-size\"].value)}\n />\n );\n};\n\nexport { TypedYAxis } from \"./types\";\n\nexport default YAxis;\n"],"mappings":";;;;;;;;;;;;;;;;;;AAgDA,IAAa,SAAyB,UAAU;CAC9C,MAAM,EAAE,QAAQ,OAAO,QAAQ,OAAO,GAAG,SAAS;CAElD,MAAM,SAAS,gBAAgB;CAC/B,MAAM,EAAE,WAAW,yBAAyB;CAE5C,MAAM,iBACJ,SAAS,cAAe,SAAS,UAAU,WAAW;CAExD,OACE,oBAAC,SAAS,OAAV;EACE,GAAI;EACJ,mBAAA;EACQ;EACF;EACN,OAAO,UAAU,iBAAiB,SAAS,KAAA;EAC3C,UAAU,OAAO,KAAK,YAAY,CAAC;EACnC,MAAM,EACJ,MAAM,OAAO,KAAK,QAAQ,CAAC,MAC7B;EACA,YAAY,SAAS,OAAO,KAAK,QAAQ,KAAK;EAC9C,UAAU,SAAS,OAAO,KAAK,YAAY,CAAC,KAAK;CAClD,CAAA;AAEL"}
1
+ {"version":3,"file":"YAxis.mjs","names":[],"sources":["../../../../../../../../../src/components/CartesianChart/components/YAxis/YAxis.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport * as Recharts from \"recharts\";\nimport type {\n ChartDataValue,\n DataKey,\n DataKeyValue,\n} from \"@/components/CartesianChart/types\";\nimport { useCartesianChartContext } from \"@/components/CartesianChart/context\";\nimport { useDesignTokens } from \"@/lib/theming\";\nimport { useTickLabelWidth } from \"@/components/CartesianChart/hooks/useTickLabelWidth\";\n\nexport type YAxisProps<\n TData extends ChartDataValue = ChartDataValue,\n TDataKey extends DataKey<TData> = DataKey<TData>,\n TDataMatch = DataKeyValue<TData, TDataKey>,\n> = Pick<\n Recharts.YAxisProps,\n | \"className\"\n | \"orientation\"\n | \"allowDecimals\"\n | \"interval\"\n | \"minTickGap\"\n | \"scale\"\n | \"type\"\n | \"domain\"\n | \"hide\"\n | \"unit\"\n> & {\n dataKey?: TDataKey;\n tickFormatter?: (value: TDataMatch, index: number) => string;\n /**\n * The width of the axis in pixels. `\"auto\"` sizes the axis to its widest tick\n * label. Defaults to `\"auto\"` for a category axis (e.g. with `layout` set to\n * `\"vertical\"`) and to `60` otherwise. Pin a number to align the plot areas\n * of synchronized charts.\n */\n width?: number | \"auto\";\n};\n\n/**\n * `tickFormatter` is deliberately not part of the remote surface. A function\n * property crosses the boundary as a thread proxy whose call returns a Promise,\n * and recharts concatenates the formatter's result straight into the tick label\n * — so remotely every tick rendered `[object Promise]`. Dropping it from the\n * remote surface leaves the raw value there; the prop keeps working locally.\n *\n * @flr-generate all\n * @flr-ignore-props tickFormatter\n */\nexport const YAxis: FC<YAxisProps> = (props) => {\n const { domain, type = \"auto\", width, ...rest } = props;\n\n const tokens = useDesignTokens();\n const { layout } = useCartesianChartContext();\n const tickLabels = useTickLabelWidth();\n\n const isCategoryAxis =\n type === \"category\" || (type === \"auto\" && layout === \"vertical\");\n const isAutoWidth =\n (width ?? (isCategoryAxis ? \"auto\" : undefined)) === \"auto\";\n\n const tickMargin = parseInt(tokens.axis.spacing.value);\n const tickSize = parseInt(tokens.axis[\"tick-size\"].value);\n const tickFill = tokens.axis[\"color\"].value;\n\n /*\n * Recharts' own `\"auto\"` stays in charge until the labels are measured, so\n * the first frame is close already.\n */\n const autoWidth =\n tickLabels.width === undefined\n ? \"auto\"\n : tickLabels.width + tickSize + tickMargin;\n\n /*\n * Recharts' default tick label, plus the measuring ref. Recharts passes a\n * numeric axis width on to the label, which wraps once the text is wider —\n * so the label drops it, as it does with `\"auto\"`.\n */\n const measuredTick = (tickProps: Recharts.YAxisTickContentProps) => {\n const { payload, index, tickFormatter } = tickProps;\n const value = `${tickFormatter ? tickFormatter(payload.value, index) : payload.value}${rest.unit ?? \"\"}`;\n return (\n <Recharts.Text\n {...tickProps}\n ref={tickLabels.labelRef}\n className=\"recharts-cartesian-axis-tick-value\"\n fill={tickFill}\n width={undefined}\n >\n {value}\n </Recharts.Text>\n );\n };\n\n return (\n <Recharts.YAxis\n {...rest}\n allowDataOverflow\n domain={domain}\n type={type}\n width={isAutoWidth ? autoWidth : width}\n fontSize={tokens.axis[\"font-size\"].value}\n tick={isAutoWidth ? measuredTick : { fill: tickFill }}\n tickMargin={tickMargin}\n tickSize={tickSize}\n />\n );\n};\n\nexport { TypedYAxis } from \"./types\";\n\nexport default YAxis;\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAiDA,IAAa,SAAyB,UAAU;CAC9C,MAAM,EAAE,QAAQ,OAAO,QAAQ,OAAO,GAAG,SAAS;CAElD,MAAM,SAAS,gBAAgB;CAC/B,MAAM,EAAE,WAAW,yBAAyB;CAC5C,MAAM,aAAa,kBAAkB;CAIrC,MAAM,eACH,UAFD,SAAS,cAAe,SAAS,UAAU,WAAW,aAE1B,SAAS,KAAA,QAAgB;CAEvD,MAAM,aAAa,SAAS,OAAO,KAAK,QAAQ,KAAK;CACrD,MAAM,WAAW,SAAS,OAAO,KAAK,YAAY,CAAC,KAAK;CACxD,MAAM,WAAW,OAAO,KAAK,QAAQ,CAAC;CAMtC,MAAM,YACJ,WAAW,UAAU,KAAA,IACjB,SACA,WAAW,QAAQ,WAAW;CAOpC,MAAM,gBAAgB,cAA8C;EAClE,MAAM,EAAE,SAAS,OAAO,kBAAkB;EAC1C,MAAM,QAAQ,GAAG,gBAAgB,cAAc,QAAQ,OAAO,KAAK,IAAI,QAAQ,QAAQ,KAAK,QAAQ;EACpG,OACE,oBAAC,SAAS,MAAV;GACE,GAAI;GACJ,KAAK,WAAW;GAChB,WAAU;GACV,MAAM;GACN,OAAO,KAAA;GAEN,UAAA;EACY,CAAA;CAEnB;CAEA,OACE,oBAAC,SAAS,OAAV;EACE,GAAI;EACJ,mBAAA;EACQ;EACF;EACN,OAAO,cAAc,YAAY;EACjC,UAAU,OAAO,KAAK,YAAY,CAAC;EACnC,MAAM,cAAc,eAAe,EAAE,MAAM,SAAS;EACxC;EACF;CACX,CAAA;AAEL"}
@@ -0,0 +1,67 @@
1
+ "use client"
2
+ /* */
3
+ import { createFrameResizeObserver } from "../../../lib/dom/createFrameResizeObserver.mjs";
4
+ import { useCallback, useEffect, useRef, useState } from "react";
5
+ //#region src/components/CartesianChart/hooks/useTickLabelWidth.ts
6
+ var measureLabel = (label) => {
7
+ try {
8
+ return label.getBBox().width;
9
+ } catch {
10
+ return 0;
11
+ }
12
+ };
13
+ /**
14
+ * Measures the widest tick label of an axis, for an axis that sizes itself to
15
+ * its labels.
16
+ *
17
+ * Replaces recharts' own `width="auto"` measurement, which undersizes the axis
18
+ * in two ways. It reads `getBoundingClientRect()` — screen pixels, which a CSS
19
+ * `zoom` or `transform` on an ancestor scales — and uses the result as SVG user
20
+ * units. And it measures once, so a web font that replaces its fallback after
21
+ * mount widens the labels past the axis. `getBBox()` measures in user units,
22
+ * and a `ResizeObserver` — which tracks an SVG element's bbox — measures again
23
+ * whenever a label changes size: a font swap, a new text, a hidden chart that
24
+ * is shown.
25
+ *
26
+ * Attach the returned ref to every tick label. The width is `undefined` until a
27
+ * label has been measured.
28
+ *
29
+ * @internal
30
+ */
31
+ var useTickLabelWidth = () => {
32
+ const labels = useRef(/* @__PURE__ */ new Set());
33
+ const observer = useRef(null);
34
+ const [width, setWidth] = useState();
35
+ const measure = useCallback(() => {
36
+ const widest = Math.max(0, ...[...labels.current].map(measureLabel));
37
+ setWidth(widest > 0 ? Math.ceil(widest) : void 0);
38
+ }, []);
39
+ useEffect(() => {
40
+ if (typeof ResizeObserver === "undefined") return;
41
+ const frameObserver = createFrameResizeObserver(measure);
42
+ frameObserver.observe(labels.current);
43
+ observer.current = frameObserver;
44
+ return () => {
45
+ frameObserver.disconnect();
46
+ observer.current = null;
47
+ };
48
+ }, [measure]);
49
+ return {
50
+ width,
51
+ labelRef: useCallback((label) => {
52
+ if (!label) return;
53
+ labels.current.add(label);
54
+ observer.current?.observe(labels.current);
55
+ measure();
56
+ return () => {
57
+ labels.current.delete(label);
58
+ observer.current?.observe(labels.current);
59
+ measure();
60
+ };
61
+ }, [measure])
62
+ };
63
+ };
64
+ //#endregion
65
+ export { useTickLabelWidth };
66
+
67
+ //# sourceMappingURL=useTickLabelWidth.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTickLabelWidth.mjs","names":[],"sources":["../../../../../../../../src/components/CartesianChart/hooks/useTickLabelWidth.ts"],"sourcesContent":["import {\n type RefCallback,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport {\n createFrameResizeObserver,\n type FrameResizeObserver,\n} from \"@/lib/dom/createFrameResizeObserver\";\n\nconst measureLabel = (label: SVGTextElement): number => {\n try {\n return label.getBBox().width;\n } catch {\n // Firefox throws for an element that is not rendered, and jsdom has no\n // `getBBox` at all.\n return 0;\n }\n};\n\n/**\n * Measures the widest tick label of an axis, for an axis that sizes itself to\n * its labels.\n *\n * Replaces recharts' own `width=\"auto\"` measurement, which undersizes the axis\n * in two ways. It reads `getBoundingClientRect()` — screen pixels, which a CSS\n * `zoom` or `transform` on an ancestor scales — and uses the result as SVG user\n * units. And it measures once, so a web font that replaces its fallback after\n * mount widens the labels past the axis. `getBBox()` measures in user units,\n * and a `ResizeObserver` — which tracks an SVG element's bbox — measures again\n * whenever a label changes size: a font swap, a new text, a hidden chart that\n * is shown.\n *\n * Attach the returned ref to every tick label. The width is `undefined` until a\n * label has been measured.\n *\n * @internal\n */\nexport const useTickLabelWidth = () => {\n const labels = useRef(new Set<SVGTextElement>());\n const observer = useRef<FrameResizeObserver>(null);\n const [width, setWidth] = useState<number>();\n\n const measure = useCallback(() => {\n const widest = Math.max(0, ...[...labels.current].map(measureLabel));\n setWidth(widest > 0 ? Math.ceil(widest) : undefined);\n }, []);\n\n useEffect(() => {\n // jsdom has no `ResizeObserver`.\n if (typeof ResizeObserver === \"undefined\") {\n return;\n }\n const frameObserver = createFrameResizeObserver(measure);\n frameObserver.observe(labels.current);\n observer.current = frameObserver;\n return () => {\n frameObserver.disconnect();\n observer.current = null;\n };\n }, [measure]);\n\n const labelRef: RefCallback<SVGTextElement> = useCallback(\n (label) => {\n if (!label) {\n return;\n }\n labels.current.add(label);\n observer.current?.observe(labels.current);\n measure();\n return () => {\n labels.current.delete(label);\n observer.current?.observe(labels.current);\n measure();\n };\n },\n [measure],\n );\n\n return { width, labelRef };\n};\n"],"mappings":";;;;;AAYA,IAAM,gBAAgB,UAAkC;CACtD,IAAI;EACF,OAAO,MAAM,QAAQ,CAAC,CAAC;CACzB,QAAQ;EAGN,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;;;AAoBA,IAAa,0BAA0B;CACrC,MAAM,SAAS,uBAAO,IAAI,IAAoB,CAAC;CAC/C,MAAM,WAAW,OAA4B,IAAI;CACjD,MAAM,CAAC,OAAO,YAAY,SAAiB;CAE3C,MAAM,UAAU,kBAAkB;EAChC,MAAM,SAAS,KAAK,IAAI,GAAG,GAAG,CAAC,GAAG,OAAO,OAAO,CAAC,CAAC,IAAI,YAAY,CAAC;EACnE,SAAS,SAAS,IAAI,KAAK,KAAK,MAAM,IAAI,KAAA,CAAS;CACrD,GAAG,CAAC,CAAC;CAEL,gBAAgB;EAEd,IAAI,OAAO,mBAAmB,aAC5B;EAEF,MAAM,gBAAgB,0BAA0B,OAAO;EACvD,cAAc,QAAQ,OAAO,OAAO;EACpC,SAAS,UAAU;EACnB,aAAa;GACX,cAAc,WAAW;GACzB,SAAS,UAAU;EACrB;CACF,GAAG,CAAC,OAAO,CAAC;CAmBZ,OAAO;EAAE;EAAO,UAjB8B,aAC3C,UAAU;GACT,IAAI,CAAC,OACH;GAEF,OAAO,QAAQ,IAAI,KAAK;GACxB,SAAS,SAAS,QAAQ,OAAO,OAAO;GACxC,QAAQ;GACR,aAAa;IACX,OAAO,QAAQ,OAAO,KAAK;IAC3B,SAAS,SAAS,QAAQ,OAAO,OAAO;IACxC,QAAQ;GACV;EACF,GACA,CAAC,OAAO,CAGM;CAAS;AAC3B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mittwald/flow-react-components",
3
- "version": "1.5.10",
3
+ "version": "1.5.12",
4
4
  "type": "module",
5
5
  "description": "A React implementation of Flow, mittwald’s design system",
6
6
  "homepage": "https://flow.mittwald.de",
@@ -84,9 +84,9 @@
84
84
  "@internationalized/string-compiler": "^3.4.1",
85
85
  "@lezer/common": "^1.5.2",
86
86
  "@lezer/highlight": "^1.2.3",
87
- "@mittwald/flow-icons": "^1.5.10",
87
+ "@mittwald/flow-icons": "^1.5.12",
88
88
  "@mittwald/password-tools-js": "^3.0.0",
89
- "@mittwald/react-tunnel": "^1.5.10",
89
+ "@mittwald/react-tunnel": "^1.5.12",
90
90
  "@mittwald/react-use-promise": "^4.2.2",
91
91
  "@react-aria/form": "^3.2.1",
92
92
  "@react-aria/i18n": "^3.13.1",
@@ -139,13 +139,13 @@
139
139
  "@internationalized/date": "^3.12.3",
140
140
  "@lezer/generator": "^1.8.0",
141
141
  "@lezer/lr": "^1.4.10",
142
- "@mittwald/flow-components-base": "1.5.10",
143
- "@mittwald/flow-core": "1.5.10",
144
- "@mittwald/flow-design-tokens": "1.5.10",
145
- "@mittwald/flow-icons-base": "1.5.10",
142
+ "@mittwald/flow-components-base": "1.5.12",
143
+ "@mittwald/flow-core": "1.5.12",
144
+ "@mittwald/flow-design-tokens": "1.5.12",
145
+ "@mittwald/flow-icons-base": "1.5.12",
146
146
  "@mittwald/react-use-promise": "^4.2.2",
147
147
  "@mittwald/remote-dom-react": "1.2.2-mittwald.10",
148
- "@mittwald/typescript-config": "1.5.10",
148
+ "@mittwald/typescript-config": "1.5.12",
149
149
  "@nx/storybook": "^22.7.6",
150
150
  "@storybook/addon-a11y": "^10.5.10",
151
151
  "@storybook/addon-actions": "^9.0.8",
@@ -199,7 +199,7 @@
199
199
  },
200
200
  "peerDependencies": {
201
201
  "@internationalized/date": "^3.12.2",
202
- "@mittwald/flow-icons-pro": "1.5.10",
202
+ "@mittwald/flow-icons-pro": "1.5.12",
203
203
  "@mittwald/react-use-promise": "^4.2.2",
204
204
  "next": "^16.2.3",
205
205
  "react": "^19.2.0",
@@ -223,5 +223,5 @@
223
223
  "browserslist": [
224
224
  "baseline widely available"
225
225
  ],
226
- "gitHead": "04d86e653c22db90eb059b03aa0088e871bd591e"
226
+ "gitHead": "51db540dfe71c4ccf5a495961edf6280ab68e4db"
227
227
  }