fontdue-js 3.4.0 → 3.4.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,8 @@
1
+ ## 3.4.1
2
+
3
+ - Fixed the standalone type tester dropping an axis slider when its `variable-settings` starting value is negative. `variable-settings="slnt -11"` was rejected as unparseable, so no slant slider appeared and nothing in the markup explained why. Any axis with a negative range was affected. Extra spaces between an axis and its value are tolerated now too.
4
+ - Fixed the `FeatureTesters` switch knob rendering stretched in Safari 18 and earlier. The fix is in `dist/fontdue.css`.
5
+
1
6
  ## 3.4.0
2
7
 
3
8
  - Added new `openTypeFeatures.interactionStyle: 'dropdown'` for type testers. The features button opens a compact single-column overlay instead of the panel.
@@ -0,0 +1,73 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import { parseVariableSettings } from '../components/TypeTester/parseVariableSettings.js';
3
+ describe('parseVariableSettings', () => {
4
+ let warn;
5
+ beforeEach(() => {
6
+ warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
7
+ });
8
+ afterEach(() => {
9
+ warn.mockRestore();
10
+ });
11
+ it('returns null when the attribute is absent or empty', () => {
12
+ expect(parseVariableSettings(undefined)).toBeNull();
13
+ expect(parseVariableSettings(null)).toBeNull();
14
+ expect(parseVariableSettings('')).toBeNull();
15
+ });
16
+ it('parses integer and decimal values', () => {
17
+ expect(parseVariableSettings('wdth 1000, ital 0.5')).toEqual([{
18
+ axis: 'wdth',
19
+ value: 1000
20
+ }, {
21
+ axis: 'ital',
22
+ value: 0.5
23
+ }]);
24
+ expect(warn).not.toHaveBeenCalled();
25
+ });
26
+ it('parses negative values, so axes like slnt keep their slider', () => {
27
+ expect(parseVariableSettings('slnt -11')).toEqual([{
28
+ axis: 'slnt',
29
+ value: -11
30
+ }]);
31
+ expect(parseVariableSettings('slnt -11.0')).toEqual([{
32
+ axis: 'slnt',
33
+ value: -11
34
+ }]);
35
+ expect(parseVariableSettings('ital -0.5')).toEqual([{
36
+ axis: 'ital',
37
+ value: -0.5
38
+ }]);
39
+ expect(warn).not.toHaveBeenCalled();
40
+ });
41
+ it('keeps the sibling axes of a negative setting', () => {
42
+ // Regression for FD-1137: a single unparsed setting used to be dropped, and
43
+ // with it the slider for that axis, leaving adjacent testers inconsistent.
44
+ expect(parseVariableSettings('slnt -11.0, wdth 50.0, wght 300.0')).toEqual([{
45
+ axis: 'slnt',
46
+ value: -11
47
+ }, {
48
+ axis: 'wdth',
49
+ value: 50
50
+ }, {
51
+ axis: 'wght',
52
+ value: 300
53
+ }]);
54
+ expect(warn).not.toHaveBeenCalled();
55
+ });
56
+ it('tolerates whitespace around the separator and between axis and value', () => {
57
+ expect(parseVariableSettings(' wght 300 , slnt -11 ')).toEqual([{
58
+ axis: 'wght',
59
+ value: 300
60
+ }, {
61
+ axis: 'slnt',
62
+ value: -11
63
+ }]);
64
+ expect(warn).not.toHaveBeenCalled();
65
+ });
66
+ it('warns about and drops malformed settings, keeping the valid ones', () => {
67
+ expect(parseVariableSettings('wght 300, nonsense, wdth abc, slnt -')).toEqual([{
68
+ axis: 'wght',
69
+ value: 300
70
+ }]);
71
+ expect(warn).toHaveBeenCalledTimes(3);
72
+ });
73
+ });
@@ -1,7 +1,7 @@
1
1
  function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
2
2
  import React from 'react';
3
3
  import TypeTesterStandalone from './TypeTesterStandalone.js';
4
- import { notEmpty } from '../../utils.js';
4
+ import { parseVariableSettings } from './parseVariableSettings.js';
5
5
  const isAlignment = str => {
6
6
  return str === 'left' || str === 'center' || str === 'right';
7
7
  };
@@ -14,23 +14,6 @@ const parseBool = input => input === 'true' ? true : false;
14
14
  // `axes="wght, opsz"` behaves the same as `axes="wght,opsz"` and a trailing
15
15
  // comma doesn't produce a bogus empty entry.
16
16
  const splitList = input => (input === null || input === void 0 ? void 0 : input.split(',').map(token => token.trim()).filter(Boolean)) ?? [];
17
-
18
- // parse a string like "wdth 1000, ital 0.5" into
19
- // [{ axis: "wdth", value: 1000 }, { axis: "ital", value: 0.5 }]
20
- function parseVariableSettings(input) {
21
- if (!input) return null;
22
- return input.split(/\s*,\s*/).map(settingString => {
23
- const m = settingString.match(/^([A-Za-z0-9]{4}) (\d+(?:\.\d+)?)$/);
24
- if (m && m.length > 2) {
25
- return {
26
- axis: m[1],
27
- value: parseFloat(m[2])
28
- };
29
- } else {
30
- console.warn(`fontdue-type-tester failed to parse variable-settings value: "${settingString}", ignoring`);
31
- }
32
- }).filter(notEmpty);
33
- }
34
17
  export const TypeTesterStandaloneElement = _ref => {
35
18
  let {
36
19
  lineHeight,
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Parse the `variable-settings` attribute of `<fontdue-type-tester>` into the
3
+ * axis/value pairs the tester expects.
4
+ *
5
+ * Whitespace around the commas and between an axis and its value is ignored, so
6
+ * `"wdth 1000,ital 0.5"` and `"wdth 1000, ital 0.5"` are equivalent, and a
7
+ * trailing comma is tolerated. Settings that don't parse are warned about and
8
+ * skipped, leaving their siblings intact.
9
+ *
10
+ * @example
11
+ * parseVariableSettings('wdth 1000, ital 0.5')
12
+ * // => [{ axis: 'wdth', value: 1000 }, { axis: 'ital', value: 0.5 }]
13
+ *
14
+ * parseVariableSettings('slnt -11.0')
15
+ * // => [{ axis: 'slnt', value: -11 }]
16
+ */
17
+ export declare function parseVariableSettings(input: string | null | undefined): {
18
+ axis: string;
19
+ value: number;
20
+ }[] | null;
21
+ export default parseVariableSettings;
@@ -0,0 +1,41 @@
1
+ import { notEmpty } from '../../utils.js';
2
+
3
+ // Value group allows a leading minus: several registered OpenType axes are
4
+ // conventionally negative (`slnt` runs about -20 to 0, `ital` can too), and a
5
+ // custom axis can have any range. A setting that fails to parse is dropped, and
6
+ // TypeTesterVariableAxes only renders a slider for an axis that has a value, so
7
+ // rejecting a sign used to make the slider vanish with nothing in the markup to
8
+ // explain it.
9
+ const SETTING = /^([A-Za-z0-9]{4})\s+(-?\d+(?:\.\d+)?)$/;
10
+
11
+ /**
12
+ * Parse the `variable-settings` attribute of `<fontdue-type-tester>` into the
13
+ * axis/value pairs the tester expects.
14
+ *
15
+ * Whitespace around the commas and between an axis and its value is ignored, so
16
+ * `"wdth 1000,ital 0.5"` and `"wdth 1000, ital 0.5"` are equivalent, and a
17
+ * trailing comma is tolerated. Settings that don't parse are warned about and
18
+ * skipped, leaving their siblings intact.
19
+ *
20
+ * @example
21
+ * parseVariableSettings('wdth 1000, ital 0.5')
22
+ * // => [{ axis: 'wdth', value: 1000 }, { axis: 'ital', value: 0.5 }]
23
+ *
24
+ * parseVariableSettings('slnt -11.0')
25
+ * // => [{ axis: 'slnt', value: -11 }]
26
+ */
27
+ export function parseVariableSettings(input) {
28
+ if (!input) return null;
29
+ return input.split(',').map(settingString => settingString.trim()).filter(Boolean).map(settingString => {
30
+ const m = settingString.match(SETTING);
31
+ if (m && m.length > 2) {
32
+ return {
33
+ axis: m[1],
34
+ value: parseFloat(m[2])
35
+ };
36
+ } else {
37
+ console.warn(`fontdue-type-tester failed to parse variable-settings value: "${settingString}", ignoring`);
38
+ }
39
+ }).filter(notEmpty);
40
+ }
41
+ export default parseVariableSettings;
package/dist/fontdue.css CHANGED
@@ -963,6 +963,7 @@ fontdue-feature-testers {
963
963
  --_sw-knob: var(--fontdue-feature-tester-switch-knob, var(--primary_background_color, canvas));
964
964
  --_sw-track: var(--fontdue-feature-tester-switch-track, transparent);
965
965
  --_sw-travel: calc(var(--_sw-w) - var(--_sw-h));
966
+ --_sw-knob-inset-right: calc(var(--_sw-w) - var(--_sw-h) + var(--_sw-p));
966
967
  position: relative;
967
968
  box-sizing: border-box;
968
969
  width: var(--_sw-w);
@@ -980,7 +981,7 @@ fontdue-feature-testers {
980
981
  top: var(--_sw-p);
981
982
  left: var(--_sw-p);
982
983
  bottom: var(--_sw-p);
983
- aspect-ratio: 1;
984
+ right: var(--_sw-knob-inset-right);
984
985
  border: var(--_sw-b) solid var(--_sw-color);
985
986
  border-radius: var(--_sw-r);
986
987
  background: transparent;
@@ -8,7 +8,7 @@ import { NODE_ACCESS_HEADER } from '../nodeAccess.js';
8
8
  // (defineVersionPlugin in .babelrc.cjs) with the literal package.json#version.
9
9
  // Exported so UI (the admin toolbar) can surface it without re-reading the
10
10
  // build-time global in a 'use client' module.
11
- export const version = "3.4.0";
11
+ export const version = "3.4.1";
12
12
  const IS_SERVER = typeof window === typeof undefined;
13
13
 
14
14
  // Opt server fetches into Next's data cache only in production; dev stays
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fontdue-js",
3
- "version": "3.4.0",
3
+ "version": "3.4.1",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "build": "npm run relay && run-p build-js build-css build-ts",