fontdue-js 2.26.0 → 2.26.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/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 2.26.1
2
+
3
+ - Fixed a jitter in the `TypeTester` variable-font axis sliders: dragging a slider no longer shifts the handle position as the instance name and value beside it change width. The instance name now settles shortly after dragging stops, and the value reserves a fixed width for its range.
4
+
1
5
  ## 2.26.0
2
6
 
3
7
  - Added support for **OpenType color fonts** (COLRv1, COLRv0, SVG, sbix, CBDT). When a typeface ships multiple color-tech variants of the same style, fontdue-js now picks the source the current browser can actually render using `CSS.supports("font-tech(color-X)")` — fixing cases where Chrome would otherwise pin an SVG-in-OpenType file it can only render as outline. Falls back optimistically on SSR / pre-`tech()` browsers, so existing setups continue to work unchanged.
@@ -27,8 +27,10 @@ const TypeTesterVariableAxes = _ref => {
27
27
  if (!variableSettings) return null;
28
28
  if (!axes) return null;
29
29
  const handleChange = (axis, value) => {
30
- variableSettings[axis] = value;
31
- setVariableSettings(variableSettings);
30
+ setVariableSettings({
31
+ ...variableSettings,
32
+ [axis]: value
33
+ });
32
34
  };
33
35
  return /*#__PURE__*/_react.default.createElement("div", {
34
36
  className: "type-tester__variable-axes"
@@ -50,14 +52,25 @@ const TypeTesterVariableAxes = _ref => {
50
52
  } = _ref3;
51
53
  const isSmall = Math.abs(maxValue - minValue) <= 1;
52
54
  const value = variableSettings[axis];
55
+ const format = v => isSmall ? v.toFixed(2) : v.toFixed(0);
56
+
57
+ // Reserve enough room for the widest value the axis can show so the
58
+ // number changing (e.g. 99 → 100, or a decimal flipping sign) never
59
+ // reflows the slider beside it. The widest string is always at one of
60
+ // the endpoints — most digits plus any minus sign. With tabular
61
+ // figures (tnum, set in CSS) each character is a stable 1ch wide.
62
+ const valueWidth = Math.max(format(minValue).length, format(maxValue).length);
53
63
  return /*#__PURE__*/_react.default.createElement("div", {
54
64
  key: axis,
55
65
  className: "type-tester__variable-axes__axis"
56
66
  }, /*#__PURE__*/_react.default.createElement("span", {
57
67
  className: "type-tester__variable-axes__name"
58
68
  }, name), /*#__PURE__*/_react.default.createElement("span", {
59
- className: "type-tester__variable-axes__value"
60
- }, isSmall ? value.toFixed(2) : value.toFixed(0)), /*#__PURE__*/_react.default.createElement("div", {
69
+ className: "type-tester__variable-axes__value",
70
+ style: {
71
+ width: `${valueWidth}ch`
72
+ }
73
+ }, format(value)), /*#__PURE__*/_react.default.createElement("div", {
61
74
  className: "type-tester__variable-axes__slider"
62
75
  }, /*#__PURE__*/_react.default.createElement(_TypeTesterSlider.default, {
63
76
  value: value,
@@ -20,6 +20,7 @@ var _TypeTesterContent = _interopRequireDefault(require("./TypeTesterContent"));
20
20
  var _TypeTesterState = _interopRequireDefault(require("./TypeTesterState"));
21
21
  var _TypeTesterToolbar = _interopRequireDefault(require("./TypeTesterToolbar"));
22
22
  var _TypeTesterVariableAxes = _interopRequireDefault(require("./TypeTesterVariableAxes"));
23
+ var _hooks = require("../../hooks");
23
24
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
24
25
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
25
26
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
@@ -51,6 +52,12 @@ const TypeTester = _ref => {
51
52
  const props = (0, _TypeTesterState.default)({
52
53
  id
53
54
  });
55
+
56
+ // The instance name (e.g. "Regular" → "Custom") is a flex sibling of the
57
+ // inline axis slider, so updating it mid-drag changes its width and shifts
58
+ // the slider. Debounce the value used purely for the name display — the font
59
+ // preview and sliders keep reading the live `props.variableSettings`.
60
+ const nameVariableSettings = (0, _hooks.useDebouncedValue)(props.variableSettings, 200);
54
61
  (0, _react.useEffect)(() => {
55
62
  if (onFocus && props.focused) onFocus();
56
63
  if (onBlur && !props.focused) onBlur();
@@ -86,7 +93,7 @@ const TypeTester = _ref => {
86
93
  className: "type-tester__toolbar"
87
94
  }, /*#__PURE__*/_react.default.createElement(_TypeTesterStyleSelectData.default, {
88
95
  fontStyle: fontStyle,
89
- variableSettings: props.variableSettings,
96
+ variableSettings: nameVariableSettings,
90
97
  viewer: viewer,
91
98
  onSelectFontStyleValue: _ref2 => {
92
99
  let {
package/dist/fontdue.css CHANGED
@@ -708,6 +708,7 @@ div[data-component=TypeTesters] {
708
708
  margin-left: 10px;
709
709
  margin-right: 16px;
710
710
  text-align: right;
711
+ font-feature-settings: "tnum" 1;
711
712
  }
712
713
  .type-tester__variable-axes__name {
713
714
  white-space: nowrap;
package/dist/hooks.d.ts CHANGED
@@ -1 +1,2 @@
1
+ export declare function useDebouncedValue<T>(value: T, delay: number): T;
1
2
  export declare const useNewPortalElement: () => import("react").RefObject<HTMLDivElement>;
package/dist/hooks.js CHANGED
@@ -3,8 +3,22 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ exports.useDebouncedValue = useDebouncedValue;
6
7
  exports.useNewPortalElement = void 0;
7
8
  var _react = require("react");
9
+ // Returns a copy of `value` that only updates `delay` ms after `value` stops
10
+ // changing. Useful for trailing UI that shouldn't react to rapid changes mid
11
+ // interaction (e.g. the variable-font instance name while a slider is dragged).
12
+ // NOTE: detects changes by reference, so callers must update `value`
13
+ // immutably — an object mutated in place won't trigger an update.
14
+ function useDebouncedValue(value, delay) {
15
+ const [debounced, setDebounced] = (0, _react.useState)(value);
16
+ (0, _react.useEffect)(() => {
17
+ const timer = setTimeout(() => setDebounced(value), delay);
18
+ return () => clearTimeout(timer);
19
+ }, [value, delay]);
20
+ return debounced;
21
+ }
8
22
  const useNewPortalElement = () => {
9
23
  const elementRef = (0, _react.useRef)(document.createElement('div'));
10
24
  (0, _react.useEffect)(() => {
package/fontdue.css CHANGED
@@ -674,7 +674,8 @@ div[data-component='TypeTesters'] {
674
674
  .type-tester__variable-axes__value {
675
675
  margin-left: 10px;
676
676
  margin-right: 16px;
677
- width: 3em;
677
+ /* width is reserved per-axis inline (in ch) for the widest value in range,
678
+ so the number changing never reflows the slider. */
678
679
  text-align: right;
679
680
  font-feature-settings: 'tnum' 1;
680
681
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fontdue-js",
3
- "version": "2.26.0",
3
+ "version": "2.26.1",
4
4
  "scripts": {
5
5
  "build": "npm run relay && run-p build-js build-css build-ts",
6
6
  "build-js": "babel src --out-dir dist --extensions .ts,.tsx,.js,.jsx",