fontdue-js 3.8.0 → 3.10.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/CHANGELOG.md +11 -0
- package/README.md +3 -1
- package/dist/__generated__/StoreModalIndexItem_fontCollection.graphql.d.ts +10 -1
- package/dist/__generated__/StoreModalIndexItem_fontCollection.graphql.js +45 -35
- package/dist/__generated__/StoreModalIndexPaginationQuery.graphql.d.ts +1 -1
- package/dist/__generated__/StoreModalIndexPaginationQuery.graphql.js +101 -93
- package/dist/__generated__/StoreModalIndexQuery.graphql.d.ts +3 -1
- package/dist/__generated__/StoreModalIndexQuery.graphql.js +24 -17
- package/dist/__generated__/TestFontsForm_Query.graphql.d.ts +14 -2
- package/dist/__generated__/TestFontsForm_Query.graphql.js +76 -18
- package/dist/__tests__/storeModalIndexUnlisted.test.js +71 -0
- package/dist/__tests__/testFontsFamilyChoice.test.js +150 -0
- package/dist/__tests__/typeTesterTruncate.test.js +71 -1
- package/dist/components/ConfigContext.d.ts +11 -0
- package/dist/components/ConfigContext.js +7 -1
- package/dist/components/StoreModal/StoreModalIndex.js +8 -2
- package/dist/components/StoreModal/StoreModalIndexItem.d.ts +1 -1
- package/dist/components/StoreModal/StoreModalIndexItem.js +26 -8
- package/dist/components/TestFontsForm/TestFontsFormElement.d.ts +4 -1
- package/dist/components/TestFontsForm/TestFontsFormElement.js +11 -3
- package/dist/components/TestFontsForm/index.d.ts +6 -3
- package/dist/components/TestFontsForm/index.js +82 -13
- package/dist/components/TestFontsForm/index.server.js +2 -1
- package/dist/components/TestFontsForm/variables.d.ts +8 -0
- package/dist/components/TestFontsForm/variables.js +22 -0
- package/dist/components/TypeTester/truncate.d.ts +10 -0
- package/dist/components/TypeTester/truncate.js +31 -0
- package/dist/components/TypeTester/useTypeTesterStyler.d.ts +1 -6
- package/dist/components/TypeTester/useTypeTesterStyler.js +22 -15
- package/dist/fontdue.css +22 -10
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { TestFontsForm_Query } from '../../__generated__/TestFontsForm_Query.graphql.js';
|
|
3
3
|
import { SerializablePreloadedQuery, type LoadQueryOptions } from '../../relay/loadSerializableQuery.js';
|
|
4
|
-
import { Config } from '../ConfigContext.js';
|
|
5
|
-
|
|
4
|
+
import { Config, TestFontsFamilySelection } from '../ConfigContext.js';
|
|
5
|
+
import { TestFontsFormCollectionProps } from './variables.js';
|
|
6
|
+
export interface TestFontsForm_props extends TestFontsFormCollectionProps {
|
|
6
7
|
agreementLabel?: string;
|
|
7
8
|
downloadLabel?: string;
|
|
8
9
|
newsletterCheckboxChecked?: boolean;
|
|
10
|
+
familySelection?: TestFontsFamilySelection;
|
|
9
11
|
}
|
|
10
12
|
export type TestFontsFormPreloadedQuery = SerializablePreloadedQuery<TestFontsForm_Query>;
|
|
11
|
-
export
|
|
13
|
+
export type LoadTestFontsFormQueryVariables = TestFontsFormCollectionProps;
|
|
14
|
+
export declare function loadTestFontsFormQuery(variables?: LoadTestFontsFormQueryVariables | LoadQueryOptions, options?: LoadQueryOptions): Promise<TestFontsFormPreloadedQuery>;
|
|
12
15
|
export declare function TestFontsFormPreloadedQueryRenderer({ preloadedQuery, ...rest }: TestFontsForm_props & {
|
|
13
16
|
preloadedQuery: TestFontsFormPreloadedQuery;
|
|
14
17
|
}): React.JSX.Element;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
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); }
|
|
4
4
|
import _TestFontsForm_Query from "../../__generated__/TestFontsForm_Query.graphql.js";
|
|
5
5
|
import _TestFontsFormUpdateCustomerMutation from "../../__generated__/TestFontsFormUpdateCustomerMutation.graphql.js";
|
|
6
|
-
import React, { useState, useRef, useCallback } from 'react';
|
|
6
|
+
import React, { useState, useRef, useCallback, useContext, useId } from 'react';
|
|
7
7
|
import { commitMutation, graphql, useRelayEnvironment, useLazyLoadQuery, usePreloadedQuery } from 'react-relay';
|
|
8
8
|
// The script-injecting reCAPTCHA wrapper (loads Google's api.js). Importing the
|
|
9
9
|
// package's barebone named export instead was the FD-917 root cause; see
|
|
@@ -16,8 +16,18 @@ import Checkbox from '../Checkbox/index.js';
|
|
|
16
16
|
import loadSerializableQuery from '../../relay/loadSerializableQuery.js';
|
|
17
17
|
import useSerializablePreloadedQuery from '../../relay/useSerializablePreloadedQuery.js';
|
|
18
18
|
import { EnsureFontdueContext, LazyQueryBoundary } from '../FontdueContextProvider/index.js';
|
|
19
|
+
import ConfigContext from '../ConfigContext.js';
|
|
20
|
+
import { testFontsFormQueryVariables } from './variables.js';
|
|
19
21
|
import { useRecaptchaTimeout, isRecaptchaScriptLoaded, RECAPTCHA_UNAVAILABLE_MESSAGE } from '../../hooks/useRecaptchaTimeout.js';
|
|
20
22
|
const updateCustomerMutation = (_TestFontsFormUpdateCustomerMutation.hash && _TestFontsFormUpdateCustomerMutation.hash !== "ba56958399f0893bd667ff02c33a6975" && console.error("The definition of 'TestFontsFormUpdateCustomerMutation' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _TestFontsFormUpdateCustomerMutation);
|
|
23
|
+
// The archive endpoint's family filter: `collection_ids[]` narrows the ZIP to
|
|
24
|
+
// those families; none means every family.
|
|
25
|
+
const COLLECTION_IDS_PARAM = 'collection_ids[]';
|
|
26
|
+
const withCollectionIds = (archiveUrl, collectionIds) => {
|
|
27
|
+
if (collectionIds.length === 0) return archiveUrl;
|
|
28
|
+
const query = new URLSearchParams(collectionIds.map(id => [COLLECTION_IDS_PARAM, id]));
|
|
29
|
+
return `${archiveUrl}${archiveUrl.includes('?') ? '&' : '?'}${query}`;
|
|
30
|
+
};
|
|
21
31
|
const TestFontsDownloading = _ref => {
|
|
22
32
|
let {
|
|
23
33
|
archiveUrl
|
|
@@ -31,12 +41,15 @@ const TestFontsDownloading = _ref => {
|
|
|
31
41
|
}, "here"), ".")));
|
|
32
42
|
};
|
|
33
43
|
const TestFontsFormComponent = _ref2 => {
|
|
34
|
-
var _data$viewer$settings, _data$viewer$settings2, _data$viewer$
|
|
44
|
+
var _data$viewer$testFont, _data$viewer$fontdueC, _data$viewer$settings, _data$viewer$settings2, _data$viewer$testFont2;
|
|
35
45
|
let {
|
|
36
46
|
data,
|
|
37
47
|
agreementLabel,
|
|
38
48
|
downloadLabel = 'Download test fonts',
|
|
39
|
-
newsletterCheckboxChecked = false
|
|
49
|
+
newsletterCheckboxChecked = false,
|
|
50
|
+
collectionId,
|
|
51
|
+
collectionSlug,
|
|
52
|
+
familySelection: familySelectionProp
|
|
40
53
|
} = _ref2;
|
|
41
54
|
const [email, setEmail] = useState('');
|
|
42
55
|
const [name, setName] = useState('');
|
|
@@ -52,7 +65,28 @@ const TestFontsFormComponent = _ref2 => {
|
|
|
52
65
|
const recaptchaRef = useRef(null);
|
|
53
66
|
const recaptchaTimeout = useRecaptchaTimeout();
|
|
54
67
|
const environment = useRelayEnvironment();
|
|
68
|
+
const config = useContext(ConfigContext);
|
|
69
|
+
const idPrefix = useId();
|
|
70
|
+
const [chosenIds, setChosenIds] = useState([]);
|
|
55
71
|
if (!data.viewer) return null;
|
|
72
|
+
const families = ((_data$viewer$testFont = data.viewer.testFonts) === null || _data$viewer$testFont === void 0 ? void 0 : _data$viewer$testFont.families) ?? [];
|
|
73
|
+
const {
|
|
74
|
+
collectionIds,
|
|
75
|
+
collectionSlugs
|
|
76
|
+
} = testFontsFormQueryVariables({
|
|
77
|
+
collectionId,
|
|
78
|
+
collectionSlug
|
|
79
|
+
});
|
|
80
|
+
// A form for particular collections downloads exactly those families.
|
|
81
|
+
const limited = collectionIds != null || collectionSlugs != null;
|
|
82
|
+
// This form's prop, then code config (which, on the script-tag path, already
|
|
83
|
+
// has the dashboard setting merged beneath it), then the dashboard setting.
|
|
84
|
+
const familySelection = familySelectionProp ?? config.testFonts.familySelection ?? ((_data$viewer$fontdueC = data.viewer.fontdueConfig) === null || _data$viewer$fontdueC === void 0 ? void 0 : _data$viewer$fontdueC.testFonts.familySelection) ?? 'all';
|
|
85
|
+
// One family leaves nothing to choose.
|
|
86
|
+
const choosing = !limited && familySelection === 'choice' && families.length > 1;
|
|
87
|
+
const downloadIds = limited ? families.map(family => family.id) : choosing ? families.map(family => family.id).filter(id => chosenIds.includes(id)) : [];
|
|
88
|
+
const toggleFamily = (id, checked) => setChosenIds(ids => checked ? [...ids, id] : ids.filter(other => other !== id));
|
|
89
|
+
const allChosen = families.every(family => chosenIds.includes(family.id));
|
|
56
90
|
const recaptchaEnabled = ((_data$viewer$settings = data.viewer.settings) === null || _data$viewer$settings === void 0 ? void 0 : _data$viewer$settings.recaptchaEnabled) ?? false;
|
|
57
91
|
const recaptchaSiteKey = (_data$viewer$settings2 = data.viewer.settings) === null || _data$viewer$settings2 === void 0 ? void 0 : _data$viewer$settings2.recaptchaSiteKey;
|
|
58
92
|
const submitForm = useCallback(token => {
|
|
@@ -151,23 +185,48 @@ const TestFontsFormComponent = _ref2 => {
|
|
|
151
185
|
}
|
|
152
186
|
submitForm(recaptchaToken);
|
|
153
187
|
};
|
|
154
|
-
|
|
155
|
-
// Button is disabled only while actively submitting or waiting for reCAPTCHA
|
|
156
|
-
const disabled = !eulaAgreed || !email || !name;
|
|
188
|
+
const disabled = !eulaAgreed || !email || !name || choosing && downloadIds.length === 0;
|
|
157
189
|
const settings = data.viewer.settings;
|
|
158
190
|
const newsletterOptInLabel = settings === null || settings === void 0 ? void 0 : settings.newsletterOptInLabel;
|
|
159
191
|
const eulaAgreementLabel = settings === null || settings === void 0 ? void 0 : settings.eulaAgreementLabel;
|
|
160
|
-
const archiveUrl = (_data$viewer$
|
|
192
|
+
const archiveUrl = (_data$viewer$testFont2 = data.viewer.testFonts) === null || _data$viewer$testFont2 === void 0 ? void 0 : _data$viewer$testFont2.archiveUrl;
|
|
193
|
+
if (limited && families.length === 0) {
|
|
194
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
195
|
+
className: "test-fonts test-fonts--unavailable"
|
|
196
|
+
}, /*#__PURE__*/React.createElement("p", null, "Test fonts aren\u2019t available for this family."));
|
|
197
|
+
}
|
|
161
198
|
return /*#__PURE__*/React.createElement("div", {
|
|
162
199
|
className: "test-fonts"
|
|
163
200
|
}, error && /*#__PURE__*/React.createElement("div", {
|
|
164
201
|
className: "test-fonts__errors"
|
|
165
202
|
}, error), submitted ? /*#__PURE__*/React.createElement(TestFontsDownloading, {
|
|
166
|
-
archiveUrl: archiveUrl
|
|
203
|
+
archiveUrl: withCollectionIds(archiveUrl, downloadIds)
|
|
167
204
|
}) : /*#__PURE__*/React.createElement("form", {
|
|
168
205
|
className: "test-fonts__form",
|
|
169
206
|
onSubmit: handleSubmit
|
|
207
|
+
}, choosing && /*#__PURE__*/React.createElement("div", {
|
|
208
|
+
className: "test-fonts__section test-fonts__families",
|
|
209
|
+
role: "group",
|
|
210
|
+
"aria-label": "Families"
|
|
170
211
|
}, /*#__PURE__*/React.createElement("div", {
|
|
212
|
+
className: "test-fonts__checkbox-field test-fonts__families__all"
|
|
213
|
+
}, /*#__PURE__*/React.createElement(Checkbox, {
|
|
214
|
+
id: `${idPrefix}-families-all`,
|
|
215
|
+
checked: allChosen,
|
|
216
|
+
onChange: event => setChosenIds(event.target.checked ? families.map(family => family.id) : [])
|
|
217
|
+
}), /*#__PURE__*/React.createElement("label", {
|
|
218
|
+
htmlFor: `${idPrefix}-families-all`
|
|
219
|
+
}, "Select all")), families.map((family, index) => /*#__PURE__*/React.createElement("div", {
|
|
220
|
+
className: "test-fonts__checkbox-field test-fonts__family",
|
|
221
|
+
key: family.id
|
|
222
|
+
}, /*#__PURE__*/React.createElement(Checkbox, {
|
|
223
|
+
id: `${idPrefix}-family-${index}`,
|
|
224
|
+
value: family.id,
|
|
225
|
+
checked: chosenIds.includes(family.id),
|
|
226
|
+
onChange: event => toggleFamily(family.id, event.target.checked)
|
|
227
|
+
}), /*#__PURE__*/React.createElement("label", {
|
|
228
|
+
htmlFor: `${idPrefix}-family-${index}`
|
|
229
|
+
}, family.name)))), /*#__PURE__*/React.createElement("div", {
|
|
171
230
|
className: "test-fonts__section"
|
|
172
231
|
}, /*#__PURE__*/React.createElement(TextField, {
|
|
173
232
|
label: "Name *",
|
|
@@ -223,11 +282,21 @@ const TestFontsFormComponent = _ref2 => {
|
|
|
223
282
|
}, " \u2192")))), /*#__PURE__*/React.createElement("form", {
|
|
224
283
|
action: archiveUrl ?? undefined,
|
|
225
284
|
ref: downloadForm
|
|
226
|
-
}
|
|
285
|
+
}, downloadIds.map(id => /*#__PURE__*/React.createElement("input", {
|
|
286
|
+
key: id,
|
|
287
|
+
type: "hidden",
|
|
288
|
+
name: COLLECTION_IDS_PARAM,
|
|
289
|
+
value: id
|
|
290
|
+
}))));
|
|
227
291
|
};
|
|
228
|
-
const query = (_TestFontsForm_Query.hash && _TestFontsForm_Query.hash !== "
|
|
229
|
-
|
|
230
|
-
|
|
292
|
+
const query = (_TestFontsForm_Query.hash && _TestFontsForm_Query.hash !== "da75e312a602ac0ee2dfb79271299c77" && console.error("The definition of 'TestFontsForm_Query' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _TestFontsForm_Query);
|
|
293
|
+
// Pass the same `collectionId` / `collectionSlug` as the form's props.
|
|
294
|
+
export async function loadTestFontsFormQuery(variables, options) {
|
|
295
|
+
// Before the collection variables existed this took `options` alone.
|
|
296
|
+
if (variables && ('url' in variables || 'headers' in variables)) {
|
|
297
|
+
return loadTestFontsFormQuery({}, variables);
|
|
298
|
+
}
|
|
299
|
+
return loadSerializableQuery(TestFontsFormQueryNode, testFontsFormQueryVariables(variables ?? {}), options);
|
|
231
300
|
}
|
|
232
301
|
export function TestFontsFormPreloadedQueryRenderer(_ref4) {
|
|
233
302
|
let {
|
|
@@ -244,7 +313,7 @@ export function TestFontsFormPreloadedQueryRenderer(_ref4) {
|
|
|
244
313
|
}, rest));
|
|
245
314
|
}
|
|
246
315
|
function TestFontsFormLazyQueryRenderer(props) {
|
|
247
|
-
const data = useLazyLoadQuery(query,
|
|
316
|
+
const data = useLazyLoadQuery(query, testFontsFormQueryVariables(props));
|
|
248
317
|
return /*#__PURE__*/React.createElement(TestFontsFormComponent, _extends({
|
|
249
318
|
data: data
|
|
250
319
|
}, props));
|
|
@@ -2,6 +2,7 @@ function _extends() { return _extends = Object.assign ? Object.assign.bind() : f
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import loadSerializableQuery from '../../relay/loadSerializableQuery.js';
|
|
4
4
|
import { TestFontsFormPreloadedQueryRenderer } from './index.js';
|
|
5
|
+
import { testFontsFormQueryVariables } from './variables.js';
|
|
5
6
|
import TestFontsFormQueryNode from '../../__generated__/TestFontsForm_Query.graphql.js';
|
|
6
7
|
// Stub for the RSC export condition. See FontdueProvider/index.server.tsx
|
|
7
8
|
// for the rationale.
|
|
@@ -14,7 +15,7 @@ export function loadTestFontsFormQuery() {
|
|
|
14
15
|
// `<TestFontsForm />` in an RSC without manually invoking
|
|
15
16
|
// `loadTestFontsFormQuery` + threading `preloadedQuery` through props.
|
|
16
17
|
export default async function TestFontsForm(props) {
|
|
17
|
-
const preloadedQuery = await loadSerializableQuery(TestFontsFormQueryNode,
|
|
18
|
+
const preloadedQuery = await loadSerializableQuery(TestFontsFormQueryNode, testFontsFormQueryVariables(props));
|
|
18
19
|
return /*#__PURE__*/React.createElement(TestFontsFormPreloadedQueryRenderer, _extends({
|
|
19
20
|
preloadedQuery: preloadedQuery
|
|
20
21
|
}, props));
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface TestFontsFormCollectionProps {
|
|
2
|
+
collectionId?: string | readonly string[];
|
|
3
|
+
collectionSlug?: string | readonly string[];
|
|
4
|
+
}
|
|
5
|
+
export declare const testFontsFormQueryVariables: ({ collectionId, collectionSlug, }: TestFontsFormCollectionProps) => {
|
|
6
|
+
collectionIds: string[] | null;
|
|
7
|
+
collectionSlugs: string[] | null;
|
|
8
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Shared by the client component and the RSC entry (index.server.tsx), so it
|
|
2
|
+
// lives outside the 'use client' module: a server component can't call a
|
|
3
|
+
// function imported from one.
|
|
4
|
+
|
|
5
|
+
const toList = value => {
|
|
6
|
+
if (value == null) return null;
|
|
7
|
+
const list = (typeof value === 'string' ? value.split(',') : [...value]).map(item => item.trim()).filter(Boolean);
|
|
8
|
+
return list.length > 0 ? list : null;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// The query's variables for a form's collection props: null (no narrowing)
|
|
12
|
+
// unless the form is limited to particular collections.
|
|
13
|
+
export const testFontsFormQueryVariables = _ref => {
|
|
14
|
+
let {
|
|
15
|
+
collectionId,
|
|
16
|
+
collectionSlug
|
|
17
|
+
} = _ref;
|
|
18
|
+
return {
|
|
19
|
+
collectionIds: toList(collectionId),
|
|
20
|
+
collectionSlugs: toList(collectionSlug)
|
|
21
|
+
};
|
|
22
|
+
};
|
|
@@ -10,3 +10,13 @@ export interface ResolvedTruncate {
|
|
|
10
10
|
export declare const normalizeTruncate: (value?: TruncateOption) => ResolvedTruncate;
|
|
11
11
|
export declare const shouldTruncate: ({ lines, expandOnFocus }: ResolvedTruncate, focused: boolean) => boolean;
|
|
12
12
|
export declare const parseTruncateAttribute: (input: string | undefined, elementName: string) => TruncateOption | undefined;
|
|
13
|
+
export type ColumnsConfig = false | {
|
|
14
|
+
count: 'auto' | number;
|
|
15
|
+
width: string;
|
|
16
|
+
gap: string;
|
|
17
|
+
};
|
|
18
|
+
export declare const truncateColumnStyle: (columns: ColumnsConfig) => {
|
|
19
|
+
columnCount: "auto" | number;
|
|
20
|
+
columnWidth?: string;
|
|
21
|
+
columnGap?: string;
|
|
22
|
+
};
|
|
@@ -92,4 +92,35 @@ export const parseTruncateAttribute = (input, elementName) => {
|
|
|
92
92
|
lines,
|
|
93
93
|
expandOnFocus
|
|
94
94
|
};
|
|
95
|
+
};
|
|
96
|
+
// The column declarations of a capped paragraph. Truncation lays the paragraph
|
|
97
|
+
// out as a single column whose overflow flows sideways, into clipped columns.
|
|
98
|
+
// With columns off that is a bare `column-count: 1`; WebKit before Safari 26.1
|
|
99
|
+
// builds no column for it, so the stylesheet adds the column-width those builds
|
|
100
|
+
// need (`[data-truncated]` in _type-tester.scss, FD-191). A configured single
|
|
101
|
+
// column with an auto width is written without its width for the same reason,
|
|
102
|
+
// so the stylesheet can reach it; the count is read loosely because a
|
|
103
|
+
// script-tag config arrives untyped. Everything else is written as configured,
|
|
104
|
+
// and an auto count keeps its auto width: 1px there would mean one column per
|
|
105
|
+
// pixel.
|
|
106
|
+
export const truncateColumnStyle = columns => {
|
|
107
|
+
if (!columns) return {
|
|
108
|
+
columnCount: 1
|
|
109
|
+
};
|
|
110
|
+
const {
|
|
111
|
+
count,
|
|
112
|
+
width,
|
|
113
|
+
gap
|
|
114
|
+
} = columns;
|
|
115
|
+
if (Number(count) === 1 && (!width || width === 'auto')) {
|
|
116
|
+
return {
|
|
117
|
+
columnCount: 1,
|
|
118
|
+
columnGap: gap
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
columnCount: count,
|
|
123
|
+
columnWidth: width,
|
|
124
|
+
columnGap: gap
|
|
125
|
+
};
|
|
95
126
|
};
|
|
@@ -2,12 +2,7 @@ import { EditorState } from 'draft-js';
|
|
|
2
2
|
import { Alignment } from './types.js';
|
|
3
3
|
import { VariableSettings } from '../../utils.js';
|
|
4
4
|
import { VerticalMetrics } from '../useFontStyle.js';
|
|
5
|
-
import { TruncateOption } from './truncate.js';
|
|
6
|
-
export type ColumnsConfig = false | {
|
|
7
|
-
count: 'auto' | number;
|
|
8
|
-
width: string;
|
|
9
|
-
gap: string;
|
|
10
|
-
};
|
|
5
|
+
import { ColumnsConfig, TruncateOption } from './truncate.js';
|
|
11
6
|
export interface UseTypeTesterStylerProps {
|
|
12
7
|
autofit: boolean;
|
|
13
8
|
autofitOnChange: boolean;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "../../global-shim.js";
|
|
2
2
|
import { useLayoutEffect } from 'react';
|
|
3
3
|
import useAutofit from '../../hooks/useAutofit.js';
|
|
4
|
-
import { normalizeTruncate, shouldTruncate as resolveShouldTruncate } from './truncate.js';
|
|
4
|
+
import { normalizeTruncate, shouldTruncate as resolveShouldTruncate, truncateColumnStyle } from './truncate.js';
|
|
5
5
|
const useTypeTesterStyler = _ref => {
|
|
6
6
|
let {
|
|
7
7
|
size,
|
|
@@ -86,12 +86,9 @@ const useTypeTesterStyler = _ref => {
|
|
|
86
86
|
// is what lets the metric padding reveal descenders/ascenders cleanly, and it
|
|
87
87
|
// means a multi-column tester (e.g. a 3-column specimen) truncates to
|
|
88
88
|
// `truncateLines` lines *per column*. When truncating with columns disabled we
|
|
89
|
-
// still establish a single column so the clipped overflow behaves the same way
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
columnWidth: columns.width,
|
|
93
|
-
columnGap: columns.gap
|
|
94
|
-
} : undefined;
|
|
89
|
+
// still establish a single column so the clipped overflow behaves the same way
|
|
90
|
+
// (truncateColumnStyle; WebKit before Safari 26.1 needs a stylesheet nudge to
|
|
91
|
+
// build that column, see `[data-truncated]` in _type-tester.scss, FD-191).
|
|
95
92
|
|
|
96
93
|
// Outside truncation the columns apply to Draft's `[data-contents]` block
|
|
97
94
|
// (via _type-tester.scss), which shrink-wraps — on the full-width container,
|
|
@@ -126,23 +123,35 @@ const useTypeTesterStyler = _ref => {
|
|
|
126
123
|
// out to N lines of whitespace. Columns send the clipped overflow
|
|
127
124
|
// sideways (into off-screen columns) rather than below, so the last
|
|
128
125
|
// visible line of every column is never partially covered by the next
|
|
129
|
-
// one. That lets
|
|
130
|
-
//
|
|
131
|
-
// line-height; the negative margins cancel
|
|
126
|
+
// one. That lets a `lineOverflowEm` allowance above the first line and
|
|
127
|
+
// below the last fully reveal ascenders/accents and descenders at any
|
|
128
|
+
// line-height; the negative margins cancel both in layout so a
|
|
132
129
|
// collapsed paragraph still occupies exactly its line count.
|
|
133
130
|
//
|
|
131
|
+
// Where the two allowances go depends on the engine, so the stylesheet
|
|
132
|
+
// places them from the custom property below (`[data-truncated]` in
|
|
133
|
+
// _type-tester.scss); nothing here writes a padding inline, so those
|
|
134
|
+
// rules win. Chromium and Firefox fit a line into a column by its line
|
|
135
|
+
// box, so the column must span exactly N line boxes and the allowances
|
|
136
|
+
// are this box's own top and bottom padding, outside the column.
|
|
137
|
+
// WebKit fits a line by its inline box extent (the font's ascent plus
|
|
138
|
+
// descent): the Nth line would overhang such a column and move to the
|
|
139
|
+
// hidden one, and a line pushed into a later column gets a strut that
|
|
140
|
+
// starts it one allowance down. So for WebKit both allowances are
|
|
141
|
+
// padding on Draft's content block, inside the column, and every
|
|
142
|
+
// column holds the same N lines (FD-191).
|
|
143
|
+
//
|
|
134
144
|
// Everything is in `em` so it tracks the rendered font-size (including
|
|
135
145
|
// the mobile `--type-tester--adjustment`); the `+ 2px` is slack that
|
|
136
146
|
// keeps sub-pixel rounding from dropping the Nth line (the extra never
|
|
137
147
|
// reveals line N+1 because that line is clipped out to the side).
|
|
138
148
|
boxSizing: 'border-box',
|
|
139
149
|
maxHeight: `calc(${truncateLines * lineHeight + 2 * lineOverflowEm}em + 2px)`,
|
|
140
|
-
paddingTop: `${lineOverflowEm}em`,
|
|
141
|
-
paddingBottom: `${lineOverflowEm}em`,
|
|
142
150
|
paddingLeft: sideBearing,
|
|
143
151
|
paddingRight: sideBearing,
|
|
144
152
|
marginTop: `${-lineOverflowEm}em`,
|
|
145
153
|
marginBottom: `${-lineOverflowEm}em`,
|
|
154
|
+
'--type-tester--line-overflow': `${lineOverflowEm}em`,
|
|
146
155
|
// `clip`, not `hidden`, once the cap outlives focus. A hidden box is
|
|
147
156
|
// still a scroll container, so the browser scrolls it to chase a
|
|
148
157
|
// caret past line N, and the offset is unrecoverable because there is
|
|
@@ -167,9 +176,7 @@ const useTypeTesterStyler = _ref => {
|
|
|
167
176
|
columnFill: 'auto',
|
|
168
177
|
widows: 1,
|
|
169
178
|
orphans: 1,
|
|
170
|
-
...(
|
|
171
|
-
columnCount: 1
|
|
172
|
-
})
|
|
179
|
+
...truncateColumnStyle(columns)
|
|
173
180
|
} : {
|
|
174
181
|
height: 'auto',
|
|
175
182
|
overflow: 'visible',
|
package/dist/fontdue.css
CHANGED
|
@@ -182,6 +182,9 @@
|
|
|
182
182
|
.test-fonts__checkbox-field label p {
|
|
183
183
|
margin: 0;
|
|
184
184
|
}
|
|
185
|
+
.test-fonts__families .test-fonts__checkbox-field {
|
|
186
|
+
margin-bottom: 10px;
|
|
187
|
+
}
|
|
185
188
|
.test-fonts__agreement[data-highlighted=true] {
|
|
186
189
|
color: red;
|
|
187
190
|
}
|
|
@@ -721,8 +724,6 @@ fontdue-type-tester {
|
|
|
721
724
|
}
|
|
722
725
|
.type-tester__features__button {
|
|
723
726
|
display: flex;
|
|
724
|
-
--checkbox-size: 20px;
|
|
725
|
-
--checkbox-margin: 10px;
|
|
726
727
|
}
|
|
727
728
|
.type-tester__features[data-selection-style=checkbox] .type-tester__features__button {
|
|
728
729
|
margin-bottom: 5px;
|
|
@@ -889,6 +890,19 @@ fontdue-type-tester {
|
|
|
889
890
|
}
|
|
890
891
|
.type-tester__text__container[data-truncated] {
|
|
891
892
|
overflow: hidden;
|
|
893
|
+
padding-top: var(--type-tester--line-overflow, 0);
|
|
894
|
+
padding-bottom: var(--type-tester--line-overflow, 0);
|
|
895
|
+
}
|
|
896
|
+
@supports (font: -apple-system-body) or (-webkit-hyphenate-limit-lines: 1) {
|
|
897
|
+
.type-tester__text__container[data-truncated] {
|
|
898
|
+
column-width: 1px;
|
|
899
|
+
padding-top: 0;
|
|
900
|
+
padding-bottom: 0;
|
|
901
|
+
}
|
|
902
|
+
.type-tester__text__container[data-truncated] [data-contents=true] {
|
|
903
|
+
padding-top: var(--type-tester--line-overflow, 0);
|
|
904
|
+
padding-bottom: var(--type-tester--line-overflow, 0);
|
|
905
|
+
}
|
|
892
906
|
}
|
|
893
907
|
.type-tester__text__container [data-contents=true] {
|
|
894
908
|
column-count: var(--type-tester--column-count, auto);
|
|
@@ -1293,8 +1307,6 @@ fontdue-feature-testers {
|
|
|
1293
1307
|
grid-template-columns: max-content auto;
|
|
1294
1308
|
grid-column-gap: 20px;
|
|
1295
1309
|
line-height: 1.6;
|
|
1296
|
-
--checkbox-size: 20px;
|
|
1297
|
-
--checkbox-margin: 10px;
|
|
1298
1310
|
}
|
|
1299
1311
|
.character-viewer__monitor__details > span {
|
|
1300
1312
|
white-space: nowrap;
|
|
@@ -1375,10 +1387,10 @@ fontdue-feature-testers {
|
|
|
1375
1387
|
.checkbox {
|
|
1376
1388
|
position: relative;
|
|
1377
1389
|
flex-shrink: 0;
|
|
1378
|
-
margin-right: var(--checkbox-margin,
|
|
1379
|
-
width: var(--checkbox-size,
|
|
1380
|
-
height: var(--checkbox-size,
|
|
1381
|
-
font-size: calc(var(--checkbox-size,
|
|
1390
|
+
margin-right: var(--checkbox-margin, 10px);
|
|
1391
|
+
width: var(--checkbox-size, 20px);
|
|
1392
|
+
height: var(--checkbox-size, 20px);
|
|
1393
|
+
font-size: calc(var(--checkbox-size, 20px) * 0.8);
|
|
1382
1394
|
}
|
|
1383
1395
|
.checkbox input {
|
|
1384
1396
|
background: none;
|
|
@@ -1402,8 +1414,8 @@ fontdue-feature-testers {
|
|
|
1402
1414
|
.checkbox input {
|
|
1403
1415
|
-webkit-appearance: none;
|
|
1404
1416
|
vertical-align: top;
|
|
1405
|
-
width: var(--checkbox-size,
|
|
1406
|
-
height: var(--checkbox-size,
|
|
1417
|
+
width: var(--checkbox-size, 20px);
|
|
1418
|
+
height: var(--checkbox-size, 20px);
|
|
1407
1419
|
border: 1px solid var(--checkbox-color, currentcolor);
|
|
1408
1420
|
}
|
|
1409
1421
|
.checkbox__icon {
|
package/dist/version.js
CHANGED
|
@@ -8,4 +8,4 @@
|
|
|
8
8
|
// (Fontage.ClientVersions) to spot sites that need to upgrade before a
|
|
9
9
|
// feature that depends on a newer client can work for them.
|
|
10
10
|
export const CLIENT_VERSION_HEADER = 'fontdue-client-version';
|
|
11
|
-
export const version = "3.
|
|
11
|
+
export const version = "3.10.0";
|