zds-pickers 4.2.0 → 4.2.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/index.cjs.js +3 -3
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +145 -120
- package/dist/index.es.js.map +1 -1
- package/dist/other/SvgText.js +82 -19
- package/dist/types/other/SvgText.d.ts +4 -1
- package/package.json +1 -1
package/dist/other/SvgText.js
CHANGED
|
@@ -1,6 +1,35 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
const MEASUREMENT_ELEMENT_ID = '__react_svg_text_measurement_id';
|
|
4
|
+
/**
|
|
5
|
+
* The only computed properties that affect measured text geometry. Copying
|
|
6
|
+
* the full computed style (~300 properties) onto the measurement node per
|
|
7
|
+
* instance forced a style flush that dominated initial render in hosts
|
|
8
|
+
* with many SvgText instances.
|
|
9
|
+
*/
|
|
10
|
+
const FONT_PROPERTIES = [
|
|
11
|
+
'font-family',
|
|
12
|
+
'font-feature-settings',
|
|
13
|
+
'font-kerning',
|
|
14
|
+
'font-size',
|
|
15
|
+
'font-stretch',
|
|
16
|
+
'font-style',
|
|
17
|
+
'font-variant',
|
|
18
|
+
'font-weight',
|
|
19
|
+
'letter-spacing',
|
|
20
|
+
'text-transform',
|
|
21
|
+
'word-spacing',
|
|
22
|
+
];
|
|
23
|
+
const fontSignature = (style) => FONT_PROPERTIES.map(key => style.getPropertyValue(key)).join('|');
|
|
24
|
+
/**
|
|
25
|
+
* Word widths only depend on the word and the font it renders in, so they
|
|
26
|
+
* are cached module-wide by font signature. Text that repeats across
|
|
27
|
+
* instances (labels, numbers) measures once per font, ever.
|
|
28
|
+
*/
|
|
29
|
+
const fontMetricsCache = new Map();
|
|
30
|
+
const __resetFontMetricsCacheForTests = () => {
|
|
31
|
+
fontMetricsCache.clear();
|
|
32
|
+
};
|
|
4
33
|
const calculateWordsByLines = (text, wordWidths, maxWidth, maxHeight) => {
|
|
5
34
|
const { lineHeight, spaceWidth, wordsWithComputedWidth } = wordWidths;
|
|
6
35
|
return text.split(/\s+/).reduce((result, word) => {
|
|
@@ -27,19 +56,39 @@ const calculateWordsByLines = (text, wordWidths, maxWidth, maxHeight) => {
|
|
|
27
56
|
};
|
|
28
57
|
const calculateWordWidths = (style, textNode, text) => {
|
|
29
58
|
if (style && textNode) {
|
|
30
|
-
|
|
31
|
-
|
|
59
|
+
const signature = fontSignature(style);
|
|
60
|
+
let metrics = fontMetricsCache.get(signature);
|
|
32
61
|
const wordArray = [...new Set(String(text).split(/\s+/))];
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
62
|
+
const knownWidths = metrics?.widths;
|
|
63
|
+
const missingWords = knownWidths
|
|
64
|
+
? wordArray.filter(word => !knownWidths.has(word))
|
|
65
|
+
: wordArray;
|
|
66
|
+
if (!metrics || missingWords.length) {
|
|
67
|
+
for (const key of FONT_PROPERTIES) {
|
|
68
|
+
textNode.style.setProperty(key, style.getPropertyValue(key), style.getPropertyPriority(key));
|
|
69
|
+
}
|
|
70
|
+
if (!metrics) {
|
|
71
|
+
textNode.textContent = '\u00A0';
|
|
72
|
+
const spaceWidth = textNode?.getComputedTextLength?.() || 8;
|
|
73
|
+
const lineHeight = textNode.getBBox().height;
|
|
74
|
+
metrics = { widths: new Map(), spaceWidth, lineHeight };
|
|
75
|
+
fontMetricsCache.set(signature, metrics);
|
|
76
|
+
}
|
|
77
|
+
for (const word of missingWords) {
|
|
78
|
+
textNode.textContent = word;
|
|
79
|
+
metrics.widths.set(word, textNode.getBBox().width);
|
|
80
|
+
}
|
|
81
|
+
textNode.setAttribute('style', '');
|
|
82
|
+
}
|
|
83
|
+
const wordsWithComputedWidth = {};
|
|
84
|
+
for (const word of wordArray) {
|
|
85
|
+
wordsWithComputedWidth[word] = metrics.widths.get(word) ?? 0;
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
wordsWithComputedWidth,
|
|
89
|
+
spaceWidth: metrics.spaceWidth,
|
|
90
|
+
lineHeight: metrics.lineHeight,
|
|
91
|
+
};
|
|
43
92
|
}
|
|
44
93
|
return undefined;
|
|
45
94
|
};
|
|
@@ -67,14 +116,28 @@ const SvgText = (props) => {
|
|
|
67
116
|
if (el === null) {
|
|
68
117
|
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
69
118
|
svg.setAttribute('id', MEASUREMENT_ELEMENT_ID);
|
|
119
|
+
svg.setAttribute('aria-hidden', 'true');
|
|
120
|
+
// Rendered but invisible: getBBox returns zeros under display:none,
|
|
121
|
+
// so park it offscreen instead. Being outside any host SVG also
|
|
122
|
+
// keeps per-word getBBox from forcing layout of live content.
|
|
123
|
+
svg.style.position = 'fixed';
|
|
124
|
+
svg.style.left = '-9999px';
|
|
125
|
+
svg.style.top = '0';
|
|
126
|
+
const textEl = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
|
127
|
+
svg.appendChild(textEl);
|
|
70
128
|
document.body.appendChild(svg);
|
|
71
|
-
svg
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
129
|
+
// Measure against the <text> child — pointing at the <svg> container
|
|
130
|
+
// made every measurement return the svg's own box.
|
|
131
|
+
measureRef.current = textEl;
|
|
132
|
+
// Deliberately no cleanup: the element is a shared singleton and
|
|
133
|
+
// other mounted instances may still hold it as their measure target.
|
|
134
|
+
return;
|
|
76
135
|
}
|
|
77
|
-
|
|
136
|
+
// Hosts may provide either the <text> itself or a container around one.
|
|
137
|
+
measureRef.current =
|
|
138
|
+
el.tagName.toLowerCase() === 'text'
|
|
139
|
+
? el
|
|
140
|
+
: (el.querySelector('text') ?? el);
|
|
78
141
|
}, []);
|
|
79
142
|
useEffect(() => {
|
|
80
143
|
const wordWithWidths = calculateWordWidths(style, measureRef.current, text);
|
|
@@ -105,4 +168,4 @@ const SvgText = (props) => {
|
|
|
105
168
|
// textAnchor={textAnchor}
|
|
106
169
|
transform: transform, ...rest, children: _jsxs(_Fragment, { children: [displayedLines.map((line, idx) => (_jsx("tspan", { dx: x + dx, dy: idx === 0 ? startDy + dy : wordWidths?.lineHeight || 0, x: 0, children: line.words.join(' ') }, idx))), textLines.length && displayedLines.length !== textLines.length && (_jsxs(_Fragment, { children: [_jsx("tspan", { children: "..." }), _jsx("title", { children: text })] }))] }) }));
|
|
107
170
|
};
|
|
108
|
-
export {
|
|
171
|
+
export { __resetFontMetricsCacheForTests, calculateWordsByLines, calculateWordWidths, MEASUREMENT_ELEMENT_ID, SvgText, };
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
declare const MEASUREMENT_ELEMENT_ID = "__react_svg_text_measurement_id";
|
|
2
|
+
declare const __resetFontMetricsCacheForTests: () => void;
|
|
2
3
|
type WordsByLine = {
|
|
3
4
|
words: string[];
|
|
4
5
|
width: number;
|
|
5
6
|
showLine: boolean;
|
|
6
7
|
};
|
|
8
|
+
declare const calculateWordsByLines: (text: string, wordWidths: WordWidths, maxWidth: number, maxHeight: number) => WordsByLine[];
|
|
7
9
|
type WordWidths = {
|
|
8
10
|
wordsWithComputedWidth: Record<string, number>;
|
|
9
11
|
spaceWidth: number;
|
|
10
12
|
lineHeight: number;
|
|
11
13
|
};
|
|
14
|
+
declare const calculateWordWidths: (style?: CSSStyleDeclaration, textNode?: SVGGraphicsElement | null, text?: string) => WordWidths | undefined;
|
|
12
15
|
type TextProps = Partial<HTMLOrSVGElement> & {
|
|
13
16
|
className?: string;
|
|
14
17
|
dx?: number;
|
|
@@ -23,5 +26,5 @@ type TextProps = Partial<HTMLOrSVGElement> & {
|
|
|
23
26
|
y?: number;
|
|
24
27
|
};
|
|
25
28
|
declare const SvgText: (props: TextProps) => import("react/jsx-runtime").JSX.Element;
|
|
26
|
-
export {
|
|
29
|
+
export { __resetFontMetricsCacheForTests, calculateWordsByLines, calculateWordWidths, MEASUREMENT_ELEMENT_ID, SvgText, };
|
|
27
30
|
export type { TextProps, WordWidths, WordsByLine };
|