fontdue-js 3.0.6 → 3.2.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 +17 -0
- package/dist/__generated__/FeatureTesterCard_fontStyle.graphql.d.ts +23 -0
- package/dist/__generated__/FeatureTesterCard_fontStyle.graphql.js +62 -0
- package/dist/__generated__/FeatureTesterStandaloneQuery.graphql.d.ts +25 -0
- package/dist/__generated__/FeatureTesterStandaloneQuery.graphql.js +240 -0
- package/dist/__generated__/FeatureTestersIdQuery.graphql.d.ts +21 -0
- package/dist/__generated__/FeatureTestersIdQuery.graphql.js +344 -0
- package/dist/__generated__/FeatureTestersSlugQuery.graphql.d.ts +25 -0
- package/dist/__generated__/FeatureTestersSlugQuery.graphql.js +364 -0
- package/dist/__generated__/FeatureTesters_collection.graphql.d.ts +39 -0
- package/dist/__generated__/FeatureTesters_collection.graphql.js +145 -0
- package/dist/__generated__/NodePasswordFormAccessNodeMutation.graphql.d.ts +2 -1
- package/dist/__generated__/NodePasswordFormAccessNodeMutation.graphql.js +10 -4
- package/dist/__generated__/NodePasswordFormElementIDQuery.graphql.d.ts +21 -0
- package/dist/__generated__/NodePasswordFormElementIDQuery.graphql.js +86 -0
- package/dist/__generated__/NodePasswordFormElementSlugQuery.graphql.d.ts +25 -0
- package/dist/__generated__/NodePasswordFormElementSlugQuery.graphql.js +108 -0
- package/dist/__tests__/createFontdueFetch.test.js +128 -4
- package/dist/__tests__/highlight.test.js +141 -0
- package/dist/__tests__/middleware.test.js +132 -0
- package/dist/__tests__/nextAdapter.test.js +115 -1
- package/dist/components/FeatureTester/FeatureTesterCard.d.ts +30 -0
- package/dist/components/FeatureTester/FeatureTesterCard.js +167 -0
- package/dist/components/FeatureTester/FeatureTesterElement.d.ts +19 -0
- package/dist/components/FeatureTester/FeatureTesterElement.js +42 -0
- package/dist/components/FeatureTester/FeatureTesters.d.ts +43 -0
- package/dist/components/FeatureTester/FeatureTesters.js +156 -0
- package/dist/components/FeatureTester/FeatureTesters.server.d.ts +9 -0
- package/dist/components/FeatureTester/FeatureTesters.server.js +40 -0
- package/dist/components/FeatureTester/FeatureTestersElement.d.ts +10 -0
- package/dist/components/FeatureTester/FeatureTestersElement.js +27 -0
- package/dist/components/FeatureTester/highlight.d.ts +19 -0
- package/dist/components/FeatureTester/highlight.js +71 -0
- package/dist/components/FeatureTester/index.d.ts +26 -0
- package/dist/components/FeatureTester/index.js +66 -0
- package/dist/components/FeatureTester/index.server.d.ts +7 -0
- package/dist/components/FeatureTester/index.server.js +30 -0
- package/dist/components/NodePasswordForm/NodePasswordFormElement.d.ts +3 -0
- package/dist/components/NodePasswordForm/NodePasswordFormElement.js +65 -0
- package/dist/components/NodePasswordForm/index.d.ts +27 -1
- package/dist/components/NodePasswordForm/index.js +54 -60
- package/dist/components/Root/index.js +2 -1
- package/dist/fontdue.css +145 -0
- package/dist/hooks/useFeatureTesterAutofit.d.ts +17 -0
- package/dist/hooks/useFeatureTesterAutofit.js +106 -0
- package/dist/next/index.d.ts +2 -0
- package/dist/next/index.js +19 -3
- package/dist/next/registerSingleTenantResolver.js +13 -1
- package/dist/next/tenant.d.ts +1 -0
- package/dist/next/tenant.js +58 -2
- package/dist/next/unlock.d.ts +3 -0
- package/dist/next/unlock.js +49 -0
- package/dist/nodeAccess.d.ts +18 -0
- package/dist/nodeAccess.js +84 -0
- package/dist/preview/server.d.ts +1 -20
- package/dist/preview/server.js +16 -85
- package/dist/relay/environment.js +8 -2
- package/dist/relay/serverConfig.d.ts +3 -0
- package/dist/relay/serverConfig.js +22 -1
- package/dist/server/index.d.ts +8 -0
- package/dist/server/index.js +90 -7
- package/dist/server/middleware.d.ts +37 -0
- package/dist/server/middleware.js +144 -0
- package/package.json +9 -2
- package/types/next-headers.d.ts +5 -1
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
import React from 'react';
|
|
3
|
+
import FeatureTesters from './FeatureTesters.js';
|
|
4
|
+
const parseBool = (input, fallback) => input === undefined || input === '' ? fallback : input === 'true';
|
|
5
|
+
export const FeatureTestersElement = _ref => {
|
|
6
|
+
let {
|
|
7
|
+
collectionId,
|
|
8
|
+
collectionSlug,
|
|
9
|
+
highlightColor,
|
|
10
|
+
toggle,
|
|
11
|
+
autofit
|
|
12
|
+
} = _ref;
|
|
13
|
+
const shared = {
|
|
14
|
+
highlightColor: highlightColor || undefined,
|
|
15
|
+
// Only override the per-card dashboard settings when the attribute is
|
|
16
|
+
// actually present on the element; otherwise leave undefined so each card
|
|
17
|
+
// uses its own.
|
|
18
|
+
toggle: toggle === undefined ? undefined : toggle === 'hover' ? 'hover' : 'button',
|
|
19
|
+
autofit: autofit === undefined ? undefined : parseBool(autofit, false)
|
|
20
|
+
};
|
|
21
|
+
return collectionId ? /*#__PURE__*/React.createElement(FeatureTesters, _extends({
|
|
22
|
+
collectionId: collectionId
|
|
23
|
+
}, shared)) : /*#__PURE__*/React.createElement(FeatureTesters, _extends({
|
|
24
|
+
collectionSlug: collectionSlug || ''
|
|
25
|
+
}, shared));
|
|
26
|
+
};
|
|
27
|
+
export default FeatureTestersElement;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface GlyphNameEntry {
|
|
2
|
+
readonly characters: string;
|
|
3
|
+
readonly features?: readonly string[] | null;
|
|
4
|
+
}
|
|
5
|
+
export interface Segment {
|
|
6
|
+
text: string;
|
|
7
|
+
sensitive: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface Mark {
|
|
10
|
+
start: number;
|
|
11
|
+
end: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function sensitiveStringsForFeature(glyphNames: readonly GlyphNameEntry[] | null | undefined, feature: string): string[];
|
|
14
|
+
export interface SensitivityOptions {
|
|
15
|
+
autoStrings?: readonly string[];
|
|
16
|
+
addMarks?: readonly Mark[];
|
|
17
|
+
}
|
|
18
|
+
export declare function computeSensitivityFlags(content: string, opts: SensitivityOptions): boolean[];
|
|
19
|
+
export declare function segmentByFlags(content: string, flags: readonly boolean[]): Segment[];
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Decide which characters in a sample string are "sensitive" to an OpenType
|
|
2
|
+
// feature.
|
|
3
|
+
//
|
|
4
|
+
// Auto-detection: `FontStyle.glyphNames` lists `{ characters, features }` where
|
|
5
|
+
// `characters` is the INPUT string a feature substitutes (a single character
|
|
6
|
+
// for single substitutions like ss03/smcp, or a sequence for ligatures like
|
|
7
|
+
// dlig). "Which characters does feature X touch" is the set of `characters`
|
|
8
|
+
// whose `features` includes X. That static map only covers single + ligature
|
|
9
|
+
// substitutions; the exact set for any feature comes from the server's shaped
|
|
10
|
+
// diff (`changedRanges`), which FeatureTesterCard passes in as marks.
|
|
11
|
+
//
|
|
12
|
+
// Everything is computed as a boolean-per-codepoint array so the sources
|
|
13
|
+
// compose cleanly. A contiguous run of `true` becomes one highlight span, which
|
|
14
|
+
// keeps ligature clusters whole (we never split a cluster across spans).
|
|
15
|
+
|
|
16
|
+
// An inclusive code-point index range. FeatureTesterCard builds these from the
|
|
17
|
+
// server-materialized `changedRanges`.
|
|
18
|
+
|
|
19
|
+
// The de-duplicated set of input strings the feature substitutes.
|
|
20
|
+
export function sensitiveStringsForFeature(glyphNames, feature) {
|
|
21
|
+
if (!glyphNames) return [];
|
|
22
|
+
const set = new Set();
|
|
23
|
+
for (const g of glyphNames) {
|
|
24
|
+
if (g.characters && g.features && g.features.includes(feature)) {
|
|
25
|
+
set.add(g.characters);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return Array.from(set);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Set flags[i] = true for every occurrence of `value` (matched by code point).
|
|
32
|
+
function applyText(chars, value, flags) {
|
|
33
|
+
const seq = Array.from(value);
|
|
34
|
+
if (seq.length === 0) return;
|
|
35
|
+
for (let i = 0; i + seq.length <= chars.length; i++) {
|
|
36
|
+
if (seq.every((c, k) => chars[i + k] === c)) {
|
|
37
|
+
for (let k = 0; k < seq.length; k++) flags[i + k] = true;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function computeSensitivityFlags(content, opts) {
|
|
42
|
+
const chars = Array.from(content);
|
|
43
|
+
const flags = new Array(chars.length).fill(false);
|
|
44
|
+
if (opts.autoStrings) {
|
|
45
|
+
for (const value of opts.autoStrings) applyText(chars, value, flags);
|
|
46
|
+
}
|
|
47
|
+
if (opts.addMarks) {
|
|
48
|
+
for (const mark of opts.addMarks) {
|
|
49
|
+
for (let i = Math.max(0, mark.start); i <= mark.end && i < chars.length; i++) {
|
|
50
|
+
flags[i] = true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return flags;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Collapse a per-codepoint flag array into consecutive sensitive/non-sensitive
|
|
58
|
+
// runs (one highlight span each).
|
|
59
|
+
export function segmentByFlags(content, flags) {
|
|
60
|
+
const chars = Array.from(content);
|
|
61
|
+
const segments = [];
|
|
62
|
+
for (let i = 0; i < chars.length; i++) {
|
|
63
|
+
const sensitive = flags[i] ?? false;
|
|
64
|
+
const last = segments[segments.length - 1];
|
|
65
|
+
if (last && last.sensitive === sensitive) last.text += chars[i];else segments.push({
|
|
66
|
+
text: chars[i],
|
|
67
|
+
sensitive
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return segments;
|
|
71
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { FeatureTesterCardOptions } from './FeatureTesterCard.js';
|
|
3
|
+
import { Config } from '../ConfigContext.js';
|
|
4
|
+
import { SerializablePreloadedQuery, type LoadQueryOptions } from '../../relay/loadSerializableQuery.js';
|
|
5
|
+
import { FeatureTesterStandaloneQuery } from '../../__generated__/FeatureTesterStandaloneQuery.graphql.js';
|
|
6
|
+
export type FeatureTesterPreloadedQuery = SerializablePreloadedQuery<FeatureTesterStandaloneQuery>;
|
|
7
|
+
export interface LoadFeatureTesterQueryVariables {
|
|
8
|
+
familyName: string;
|
|
9
|
+
styleName: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function loadFeatureTesterQuery(variables: LoadFeatureTesterQueryVariables, options?: LoadQueryOptions): Promise<FeatureTesterPreloadedQuery>;
|
|
12
|
+
export declare function FeatureTesterPreloadedRenderer({ preloadedQuery, ...rest }: FeatureTesterCardOptions & {
|
|
13
|
+
preloadedQuery: FeatureTesterPreloadedQuery;
|
|
14
|
+
}): React.JSX.Element;
|
|
15
|
+
export type FeatureTesterProps = FeatureTesterCardOptions & {
|
|
16
|
+
config?: Config;
|
|
17
|
+
} & ({
|
|
18
|
+
preloadedQuery: FeatureTesterPreloadedQuery;
|
|
19
|
+
familyName?: never;
|
|
20
|
+
styleName?: never;
|
|
21
|
+
} | {
|
|
22
|
+
preloadedQuery?: never;
|
|
23
|
+
familyName: string;
|
|
24
|
+
styleName: string;
|
|
25
|
+
});
|
|
26
|
+
export default function FeatureTester(props: FeatureTesterProps): React.JSX.Element;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
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
|
+
import _FeatureTesterStandaloneQuery from "../../__generated__/FeatureTesterStandaloneQuery.graphql.js";
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { graphql, useLazyLoadQuery, usePreloadedQuery } from 'react-relay';
|
|
7
|
+
import FeatureTesterCard from './FeatureTesterCard.js';
|
|
8
|
+
import { EnsureFontdueContext } from '../FontdueContextProvider/index.js';
|
|
9
|
+
import loadSerializableQuery from '../../relay/loadSerializableQuery.js';
|
|
10
|
+
import useSerializablePreloadedQuery from '../../relay/useSerializablePreloadedQuery.js';
|
|
11
|
+
import FeatureTesterStandaloneQueryNode from '../../__generated__/FeatureTesterStandaloneQuery.graphql.js';
|
|
12
|
+
const query = (_FeatureTesterStandaloneQuery.hash && _FeatureTesterStandaloneQuery.hash !== "eddba300b2fd5e283ab1f8f787edc518" && console.error("The definition of 'FeatureTesterStandaloneQuery' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _FeatureTesterStandaloneQuery);
|
|
13
|
+
function FeatureTesterRender(_ref) {
|
|
14
|
+
var _data$viewer;
|
|
15
|
+
let {
|
|
16
|
+
data,
|
|
17
|
+
...rest
|
|
18
|
+
} = _ref;
|
|
19
|
+
const fontStyle = (_data$viewer = data.viewer) === null || _data$viewer === void 0 ? void 0 : _data$viewer.fontStyle;
|
|
20
|
+
if (!fontStyle) {
|
|
21
|
+
console.log('[FeatureTester]: No FontStyle found');
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return /*#__PURE__*/React.createElement(FeatureTesterCard, _extends({
|
|
25
|
+
fontStyle: fontStyle
|
|
26
|
+
}, rest));
|
|
27
|
+
}
|
|
28
|
+
export async function loadFeatureTesterQuery(variables, options) {
|
|
29
|
+
return loadSerializableQuery(FeatureTesterStandaloneQueryNode, {
|
|
30
|
+
familyName: variables.familyName,
|
|
31
|
+
styleName: variables.styleName
|
|
32
|
+
}, options);
|
|
33
|
+
}
|
|
34
|
+
export function FeatureTesterPreloadedRenderer(_ref2) {
|
|
35
|
+
let {
|
|
36
|
+
preloadedQuery,
|
|
37
|
+
...rest
|
|
38
|
+
} = _ref2;
|
|
39
|
+
const queryRef = useSerializablePreloadedQuery(preloadedQuery, 'store-or-network', query);
|
|
40
|
+
const data = usePreloadedQuery(query, queryRef);
|
|
41
|
+
return /*#__PURE__*/React.createElement(FeatureTesterRender, _extends({
|
|
42
|
+
data: data
|
|
43
|
+
}, rest));
|
|
44
|
+
}
|
|
45
|
+
function FeatureTesterLazyRenderer(_ref3) {
|
|
46
|
+
let {
|
|
47
|
+
familyName,
|
|
48
|
+
styleName,
|
|
49
|
+
...rest
|
|
50
|
+
} = _ref3;
|
|
51
|
+
const data = useLazyLoadQuery(query, {
|
|
52
|
+
familyName,
|
|
53
|
+
styleName
|
|
54
|
+
});
|
|
55
|
+
return /*#__PURE__*/React.createElement(FeatureTesterRender, _extends({
|
|
56
|
+
data: data
|
|
57
|
+
}, rest));
|
|
58
|
+
}
|
|
59
|
+
// Unified FeatureTester entry point. Accepts either a preloaded query
|
|
60
|
+
// (server-side preload path) or `{familyName, styleName}` (lazy path, used by
|
|
61
|
+
// the CDN custom element and inside an existing `<FontdueProvider>` tree).
|
|
62
|
+
export default function FeatureTester(props) {
|
|
63
|
+
return /*#__PURE__*/React.createElement(EnsureFontdueContext, {
|
|
64
|
+
config: props.config
|
|
65
|
+
}, 'preloadedQuery' in props && props.preloadedQuery ? /*#__PURE__*/React.createElement(FeatureTesterPreloadedRenderer, props) : /*#__PURE__*/React.createElement(FeatureTesterLazyRenderer, props));
|
|
66
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { FeatureTesterProps } from './index.js';
|
|
3
|
+
export type { FeatureTesterPreloadedQuery } from './index.js';
|
|
4
|
+
export declare function loadFeatureTesterQuery(): never;
|
|
5
|
+
export default function FeatureTester(props: Extract<FeatureTesterProps, {
|
|
6
|
+
familyName: string;
|
|
7
|
+
}>): Promise<React.JSX.Element>;
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
import React from 'react';
|
|
3
|
+
import loadSerializableQuery from '../../relay/loadSerializableQuery.js';
|
|
4
|
+
import { FeatureTesterPreloadedRenderer } from './index.js';
|
|
5
|
+
import FeatureTesterStandaloneQueryNode from '../../__generated__/FeatureTesterStandaloneQuery.graphql.js';
|
|
6
|
+
// Stub for the RSC export condition. See FontdueProvider/index.server.tsx
|
|
7
|
+
// for the rationale.
|
|
8
|
+
export function loadFeatureTesterQuery() {
|
|
9
|
+
throw new Error("loadFeatureTesterQuery isn't needed in React Server Components — " + 'the <FeatureTester> server entrypoint awaits its query automatically. ' + 'Drop the manual call.');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Next RSC variant: awaits the query on the server and hands the response to
|
|
13
|
+
// the client `FeatureTesterPreloadedRenderer`. Lets pages just write
|
|
14
|
+
// `<FeatureTester familyName="..." styleName="..." feature="..." content="..." />`
|
|
15
|
+
// in an RSC without manually invoking `loadFeatureTesterQuery` + threading
|
|
16
|
+
// `preloadedQuery` through props.
|
|
17
|
+
export default async function FeatureTester(props) {
|
|
18
|
+
const {
|
|
19
|
+
familyName,
|
|
20
|
+
styleName,
|
|
21
|
+
...rest
|
|
22
|
+
} = props;
|
|
23
|
+
const preloadedQuery = await loadSerializableQuery(FeatureTesterStandaloneQueryNode, {
|
|
24
|
+
familyName,
|
|
25
|
+
styleName
|
|
26
|
+
});
|
|
27
|
+
return /*#__PURE__*/React.createElement(FeatureTesterPreloadedRenderer, _extends({
|
|
28
|
+
preloadedQuery: preloadedQuery
|
|
29
|
+
}, rest));
|
|
30
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
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
|
+
import _NodePasswordFormElementSlugQuery from "../../__generated__/NodePasswordFormElementSlugQuery.graphql.js";
|
|
5
|
+
import _NodePasswordFormElementIDQuery from "../../__generated__/NodePasswordFormElementIDQuery.graphql.js";
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { graphql, useLazyLoadQuery } from 'react-relay';
|
|
8
|
+
import NodePasswordForm from './index.js';
|
|
9
|
+
// Auto-detecting wrapper behind the <fontdue-node-password-form> custom element.
|
|
10
|
+
// Unlike the presentational React component (the default export of ./index, used
|
|
11
|
+
// when the server has already detected the lock), this queries the collection
|
|
12
|
+
// itself and renders the form only when access is denied — so it works dropped
|
|
13
|
+
// onto any page, hiding itself for a visitor who already has access.
|
|
14
|
+
|
|
15
|
+
const idQuery = (_NodePasswordFormElementIDQuery.hash && _NodePasswordFormElementIDQuery.hash !== "213a61ae079e4f57b7b197b0d2f5256a" && console.error("The definition of 'NodePasswordFormElementIDQuery' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _NodePasswordFormElementIDQuery);
|
|
16
|
+
function NodePasswordFormIDQueryRenderer(_ref) {
|
|
17
|
+
let {
|
|
18
|
+
collectionId,
|
|
19
|
+
...rest
|
|
20
|
+
} = _ref;
|
|
21
|
+
const data = useLazyLoadQuery(idQuery, {
|
|
22
|
+
collectionId
|
|
23
|
+
});
|
|
24
|
+
// The @catch directive surfaces the password-protection error as ok:false; the
|
|
25
|
+
// node is only locked then, so only then do we render the form (we don't want
|
|
26
|
+
// to show it to a visitor who already has access).
|
|
27
|
+
if (data.collection.ok === false) return /*#__PURE__*/React.createElement(NodePasswordForm, _extends({}, rest, {
|
|
28
|
+
collectionId: collectionId
|
|
29
|
+
}));
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const slugQuery = (_NodePasswordFormElementSlugQuery.hash && _NodePasswordFormElementSlugQuery.hash !== "200a33ca6a1f9e4e7e95b7100bbdd261" && console.error("The definition of 'NodePasswordFormElementSlugQuery' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _NodePasswordFormElementSlugQuery);
|
|
33
|
+
function NodePasswordFormSlugQueryRenderer(_ref2) {
|
|
34
|
+
var _data$viewer$slug;
|
|
35
|
+
let {
|
|
36
|
+
collectionSlug,
|
|
37
|
+
...rest
|
|
38
|
+
} = _ref2;
|
|
39
|
+
const data = useLazyLoadQuery(slugQuery, {
|
|
40
|
+
collectionSlug
|
|
41
|
+
});
|
|
42
|
+
// see note in NodePasswordFormIDQueryRenderer
|
|
43
|
+
if (((_data$viewer$slug = data.viewer.slug) === null || _data$viewer$slug === void 0 ? void 0 : _data$viewer$slug.collection.ok) === false) return /*#__PURE__*/React.createElement(NodePasswordForm, _extends({}, rest, {
|
|
44
|
+
collectionSlug: collectionSlug
|
|
45
|
+
}));
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
export default function NodePasswordFormElement(_ref3) {
|
|
49
|
+
let {
|
|
50
|
+
collectionId,
|
|
51
|
+
collectionSlug,
|
|
52
|
+
...rest
|
|
53
|
+
} = _ref3;
|
|
54
|
+
if (collectionId) {
|
|
55
|
+
return /*#__PURE__*/React.createElement(NodePasswordFormIDQueryRenderer, _extends({
|
|
56
|
+
collectionId: collectionId
|
|
57
|
+
}, rest));
|
|
58
|
+
}
|
|
59
|
+
if (collectionSlug) {
|
|
60
|
+
return /*#__PURE__*/React.createElement(NodePasswordFormSlugQueryRenderer, _extends({
|
|
61
|
+
collectionSlug: collectionSlug
|
|
62
|
+
}, rest));
|
|
63
|
+
}
|
|
64
|
+
throw new Error('NodePasswordForm expected either a collectionId or collectionSlug prop');
|
|
65
|
+
}
|
|
@@ -2,5 +2,31 @@ import React from 'react';
|
|
|
2
2
|
export interface NodePasswordForm_props {
|
|
3
3
|
collectionId?: string | null;
|
|
4
4
|
collectionSlug?: string | null;
|
|
5
|
+
/**
|
|
6
|
+
* Path of the storefront's unlock route (see fontdue-js/next/unlock), e.g.
|
|
7
|
+
* "/api/unlock". On a successful unlock the form POSTs `{ token }` there
|
|
8
|
+
* before reloading. Required on statically-rendered Next storefronts: the
|
|
9
|
+
* route enables the draft-mode bypass that takes this visitor off the
|
|
10
|
+
* full-route cache, so the reload can actually render the unlocked
|
|
11
|
+
* collection instead of re-serving the cached password form. Omit on
|
|
12
|
+
* frameworks that render server responses per request (Astro, React Router,
|
|
13
|
+
* TanStack Start) — there the cookie alone is enough.
|
|
14
|
+
*/
|
|
15
|
+
unlockEndpoint?: string | null;
|
|
5
16
|
}
|
|
6
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Password form for a collection you already know is locked. Render it when a
|
|
19
|
+
* collection comes back password-protected (see the per-framework guides): it
|
|
20
|
+
* commits the `accessNode` mutation, remembers the returned access token, and
|
|
21
|
+
* reloads so the now-unlocked collection renders.
|
|
22
|
+
*
|
|
23
|
+
* Unlike the `<fontdue-node-password-form>` custom element (which detects the
|
|
24
|
+
* locked state itself), this does no data fetching of its own, so it renders
|
|
25
|
+
* immediately on the server with no Suspense flash — the server already detected
|
|
26
|
+
* the lock.
|
|
27
|
+
*
|
|
28
|
+
* Self-wraps in `EnsureFontdueContext` so it works as a standalone island (e.g.
|
|
29
|
+
* an Astro `client:load`) without its own provider, and no-ops inside an outer
|
|
30
|
+
* `<FontdueProvider>` (Next/RR7/TanStack).
|
|
31
|
+
*/
|
|
32
|
+
export default function NodePasswordForm(props: NodePasswordForm_props): React.JSX.Element;
|
|
@@ -1,14 +1,54 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
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
|
-
import _NodePasswordFormSlugQuery from "../../__generated__/NodePasswordFormSlugQuery.graphql.js";
|
|
5
|
-
import _NodePasswordFormIDQuery from "../../__generated__/NodePasswordFormIDQuery.graphql.js";
|
|
6
3
|
import _NodePasswordFormAccessNodeMutation from "../../__generated__/NodePasswordFormAccessNodeMutation.graphql.js";
|
|
7
4
|
import React, { useState } from 'react';
|
|
8
|
-
import { graphql,
|
|
5
|
+
import { graphql, useMutation } from 'react-relay';
|
|
9
6
|
import TextField from '../TextField/index.js';
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
import { EnsureFontdueContext } from '../FontdueContextProvider/index.js';
|
|
8
|
+
import { rememberNodeAccessToken } from '../../nodeAccess.js';
|
|
9
|
+
const accessNodeMutation = (_NodePasswordFormAccessNodeMutation.hash && _NodePasswordFormAccessNodeMutation.hash !== "eb0d3828d65a52c727ef60caa67709a1" && console.error("The definition of 'NodePasswordFormAccessNodeMutation' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _NodePasswordFormAccessNodeMutation);
|
|
10
|
+
/**
|
|
11
|
+
* Password form for a collection you already know is locked. Render it when a
|
|
12
|
+
* collection comes back password-protected (see the per-framework guides): it
|
|
13
|
+
* commits the `accessNode` mutation, remembers the returned access token, and
|
|
14
|
+
* reloads so the now-unlocked collection renders.
|
|
15
|
+
*
|
|
16
|
+
* Unlike the `<fontdue-node-password-form>` custom element (which detects the
|
|
17
|
+
* locked state itself), this does no data fetching of its own, so it renders
|
|
18
|
+
* immediately on the server with no Suspense flash — the server already detected
|
|
19
|
+
* the lock.
|
|
20
|
+
*
|
|
21
|
+
* Self-wraps in `EnsureFontdueContext` so it works as a standalone island (e.g.
|
|
22
|
+
* an Astro `client:load`) without its own provider, and no-ops inside an outer
|
|
23
|
+
* `<FontdueProvider>` (Next/RR7/TanStack).
|
|
24
|
+
*/
|
|
25
|
+
export default function NodePasswordForm(props) {
|
|
26
|
+
return /*#__PURE__*/React.createElement(EnsureFontdueContext, null, /*#__PURE__*/React.createElement(NodePasswordFormFields, props));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Tell the storefront's unlock route (fontdue-js/next/unlock) about a
|
|
30
|
+
// successful unlock so it can enable Next's draft-mode bypass — without it, a
|
|
31
|
+
// statically-cached font page would keep serving this visitor the password
|
|
32
|
+
// form after they unlocked (the full-route cache is visitor-blind). Always
|
|
33
|
+
// resolves: the reload must happen even if the endpoint is absent or errors,
|
|
34
|
+
// because on per-request-rendered frameworks the cookie alone unlocks.
|
|
35
|
+
async function notifyUnlockEndpoint(endpoint, token) {
|
|
36
|
+
if (!endpoint || !token) return;
|
|
37
|
+
try {
|
|
38
|
+
await fetch(endpoint, {
|
|
39
|
+
method: 'POST',
|
|
40
|
+
headers: {
|
|
41
|
+
'content-type': 'application/json'
|
|
42
|
+
},
|
|
43
|
+
body: JSON.stringify({
|
|
44
|
+
token
|
|
45
|
+
})
|
|
46
|
+
});
|
|
47
|
+
} catch {
|
|
48
|
+
// Ignore: reload regardless, the cookie may still be enough.
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function NodePasswordFormFields(props) {
|
|
12
52
|
const [password, setPassword] = useState('');
|
|
13
53
|
const [error, setError] = useState(null);
|
|
14
54
|
const [commitMutation, submitting] = useMutation(accessNodeMutation);
|
|
@@ -27,7 +67,14 @@ function NodePasswordFormComponent(props) {
|
|
|
27
67
|
onCompleted: res => {
|
|
28
68
|
var _res$accessNode;
|
|
29
69
|
if ((_res$accessNode = res.accessNode) !== null && _res$accessNode !== void 0 && _res$accessNode.success) {
|
|
30
|
-
|
|
70
|
+
// Persist the token so the next server render unlocks the collection:
|
|
71
|
+
// the Fontdue session cookie that does this for script-tag sites can't
|
|
72
|
+
// ride a proxied or cross-origin server fetch, so we forward this
|
|
73
|
+
// token as a header instead (see fontdue-js nodeAccess).
|
|
74
|
+
rememberNodeAccessToken(res.accessNode.token);
|
|
75
|
+
notifyUnlockEndpoint(props.unlockEndpoint, res.accessNode.token).then(() => location.reload());
|
|
76
|
+
} else {
|
|
77
|
+
setError('Incorrect password');
|
|
31
78
|
}
|
|
32
79
|
},
|
|
33
80
|
onError: error => {
|
|
@@ -55,57 +102,4 @@ function NodePasswordFormComponent(props) {
|
|
|
55
102
|
disabled: disabled,
|
|
56
103
|
className: "submit-button"
|
|
57
104
|
}, "Submit")));
|
|
58
|
-
}
|
|
59
|
-
const idQuery = (_NodePasswordFormIDQuery.hash && _NodePasswordFormIDQuery.hash !== "2af86f8f6df3d2e6061ea1c9dd777a26" && console.error("The definition of 'NodePasswordFormIDQuery' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _NodePasswordFormIDQuery);
|
|
60
|
-
function NodePasswordFormIDQueryRenderer(_ref) {
|
|
61
|
-
let {
|
|
62
|
-
collectionId,
|
|
63
|
-
...rest
|
|
64
|
-
} = _ref;
|
|
65
|
-
const data = useLazyLoadQuery(idQuery, {
|
|
66
|
-
collectionId
|
|
67
|
-
});
|
|
68
|
-
// this is counterintuitive, but we've added the @catch directive on the collection,
|
|
69
|
-
// and we're expecting it to respond with an error if the node is inaccessible due to being
|
|
70
|
-
// password-protected. so, when we encounter the error we're safe to render the password form.
|
|
71
|
-
// (we don't want to show the form if the user has already gained access)
|
|
72
|
-
if (data.collection.ok === false) return /*#__PURE__*/React.createElement(NodePasswordFormComponent, _extends({}, rest, {
|
|
73
|
-
collectionId: collectionId
|
|
74
|
-
}));
|
|
75
|
-
}
|
|
76
|
-
const slugQuery = (_NodePasswordFormSlugQuery.hash && _NodePasswordFormSlugQuery.hash !== "c40faf067007027fa9ba6673350c7202" && console.error("The definition of 'NodePasswordFormSlugQuery' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _NodePasswordFormSlugQuery);
|
|
77
|
-
function NodePasswordFormSlugQueryRenderer(_ref2) {
|
|
78
|
-
var _data$viewer$slug;
|
|
79
|
-
let {
|
|
80
|
-
collectionSlug,
|
|
81
|
-
...rest
|
|
82
|
-
} = _ref2;
|
|
83
|
-
const data = useLazyLoadQuery(slugQuery, {
|
|
84
|
-
collectionSlug
|
|
85
|
-
});
|
|
86
|
-
// see note in NodePasswordFormIDQueryRenderer
|
|
87
|
-
if (((_data$viewer$slug = data.viewer.slug) === null || _data$viewer$slug === void 0 ? void 0 : _data$viewer$slug.collection.ok) === false) return /*#__PURE__*/React.createElement(NodePasswordFormComponent, _extends({}, rest, {
|
|
88
|
-
collectionSlug: collectionSlug
|
|
89
|
-
}));
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// default client export
|
|
94
|
-
export default function NodePasswordForm(_ref3) {
|
|
95
|
-
let {
|
|
96
|
-
collectionId,
|
|
97
|
-
collectionSlug,
|
|
98
|
-
...rest
|
|
99
|
-
} = _ref3;
|
|
100
|
-
if (collectionId) {
|
|
101
|
-
return /*#__PURE__*/React.createElement(NodePasswordFormIDQueryRenderer, _extends({
|
|
102
|
-
collectionId: collectionId
|
|
103
|
-
}, rest));
|
|
104
|
-
}
|
|
105
|
-
if (collectionSlug) {
|
|
106
|
-
return /*#__PURE__*/React.createElement(NodePasswordFormSlugQueryRenderer, _extends({
|
|
107
|
-
collectionSlug: collectionSlug
|
|
108
|
-
}, rest));
|
|
109
|
-
}
|
|
110
|
-
throw new Error('NodePasswordForm expected either a collectionId or collectionSlug prop');
|
|
111
105
|
}
|
|
@@ -16,6 +16,7 @@ const customElementMap = {
|
|
|
16
16
|
'fontdue-cart': /*#__PURE__*/lazy(() => retryImport(() => import('../Cart/index.js'))),
|
|
17
17
|
'fontdue-cart-button': /*#__PURE__*/lazy(() => retryImport(() => import('../CartButton/index.js'))),
|
|
18
18
|
'fontdue-character-viewer': /*#__PURE__*/lazy(() => retryImport(() => import('../CharacterViewer/index.js'))),
|
|
19
|
+
'fontdue-feature-testers': /*#__PURE__*/lazy(() => retryImport(() => import('../FeatureTester/FeatureTestersElement.js'))),
|
|
19
20
|
// @ts-ignore
|
|
20
21
|
'fontdue-collection-aa': /*#__PURE__*/lazy(() => retryImport(() => import('../CollectionAa/index.js'))),
|
|
21
22
|
// @ts-ignore
|
|
@@ -23,7 +24,7 @@ const customElementMap = {
|
|
|
23
24
|
'fontdue-font-families': /*#__PURE__*/lazy(() => retryImport(() => import('../FontFamilies/index.js'))),
|
|
24
25
|
'fontdue-customer-login-form': /*#__PURE__*/lazy(() => retryImport(() => import('../CustomerLoginForm/index.js'))),
|
|
25
26
|
'fontdue-newsletter-signup': /*#__PURE__*/lazy(() => retryImport(() => import('../NewsletterSignup/NewsletterSignupElement.js'))),
|
|
26
|
-
'fontdue-node-password-form': /*#__PURE__*/lazy(() => retryImport(() => import('../NodePasswordForm/
|
|
27
|
+
'fontdue-node-password-form': /*#__PURE__*/lazy(() => retryImport(() => import('../NodePasswordForm/NodePasswordFormElement.js'))),
|
|
27
28
|
// @ts-ignore
|
|
28
29
|
'fontdue-precart': /*#__PURE__*/lazy(() => retryImport(() => import('../Precart/index.js'))),
|
|
29
30
|
// @ts-ignore
|
package/dist/fontdue.css
CHANGED
|
@@ -807,6 +807,151 @@ fontdue-type-tester {
|
|
|
807
807
|
font-weight: inherit;
|
|
808
808
|
}
|
|
809
809
|
|
|
810
|
+
fontdue-feature-tester,
|
|
811
|
+
fontdue-feature-testers {
|
|
812
|
+
display: block;
|
|
813
|
+
max-width: 100%;
|
|
814
|
+
min-width: 0;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
.fontdue-feature-tester {
|
|
818
|
+
--_ft-text: var(--fontdue-feature-tester-text, var(--primary_text_color, currentColor));
|
|
819
|
+
--_ft-muted: var(
|
|
820
|
+
--fontdue-feature-tester-muted,
|
|
821
|
+
color-mix(in srgb, var(--_ft-text) 50%, transparent)
|
|
822
|
+
);
|
|
823
|
+
--_ft-highlight: var(--fontdue-feature-tester-highlight, var(--_ft-text));
|
|
824
|
+
--_ft-bg: var(--fontdue-feature-tester-bg, transparent);
|
|
825
|
+
--_ft-border: var(
|
|
826
|
+
--fontdue-feature-tester-border,
|
|
827
|
+
1px solid var(--horizontal_rule_color, color-mix(in srgb, currentColor 15%, transparent))
|
|
828
|
+
);
|
|
829
|
+
--_ft-radius: var(--fontdue-feature-tester-radius, 0);
|
|
830
|
+
--_ft-padding: var(--fontdue-feature-tester-padding, 18px 0 30px);
|
|
831
|
+
--_ft-align: var(--fontdue-feature-tester-align, left);
|
|
832
|
+
font-size: 1rem;
|
|
833
|
+
color: var(--_ft-text);
|
|
834
|
+
background: var(--_ft-bg);
|
|
835
|
+
border-top: var(--_ft-border);
|
|
836
|
+
border-radius: var(--_ft-radius);
|
|
837
|
+
padding: var(--_ft-padding);
|
|
838
|
+
min-width: 0;
|
|
839
|
+
max-width: 100%;
|
|
840
|
+
}
|
|
841
|
+
.fontdue-feature-tester__bar {
|
|
842
|
+
display: flex;
|
|
843
|
+
align-items: center;
|
|
844
|
+
gap: 14px;
|
|
845
|
+
margin-bottom: 16px;
|
|
846
|
+
}
|
|
847
|
+
.fontdue-feature-tester__toggle {
|
|
848
|
+
background: none;
|
|
849
|
+
color: inherit;
|
|
850
|
+
font: inherit;
|
|
851
|
+
text-decoration: inherit;
|
|
852
|
+
text-align: inherit;
|
|
853
|
+
border: 0;
|
|
854
|
+
border-radius: 0;
|
|
855
|
+
padding: 0;
|
|
856
|
+
margin: 0;
|
|
857
|
+
appearance: none;
|
|
858
|
+
text-transform: inherit;
|
|
859
|
+
}
|
|
860
|
+
.fontdue-feature-tester__toggle:active, .fontdue-feature-tester__toggle:focus {
|
|
861
|
+
outline: none;
|
|
862
|
+
}
|
|
863
|
+
.fontdue-feature-tester__toggle:not(:disabled) {
|
|
864
|
+
cursor: pointer;
|
|
865
|
+
}
|
|
866
|
+
.fontdue-feature-tester__toggle {
|
|
867
|
+
display: inline-flex;
|
|
868
|
+
align-items: center;
|
|
869
|
+
flex: none;
|
|
870
|
+
color: inherit;
|
|
871
|
+
font: inherit;
|
|
872
|
+
}
|
|
873
|
+
.fontdue-feature-tester__toggle:focus-visible .fontdue-feature-tester__switch {
|
|
874
|
+
outline: 1px solid currentcolor;
|
|
875
|
+
outline-offset: 2px;
|
|
876
|
+
}
|
|
877
|
+
.fontdue-feature-tester__switch {
|
|
878
|
+
--_sw-w: var(--fontdue-feature-tester-switch-width, 40px);
|
|
879
|
+
--_sw-h: var(--fontdue-feature-tester-switch-height, 20px);
|
|
880
|
+
--_sw-r: var(--fontdue-feature-tester-switch-radius, 0);
|
|
881
|
+
--_sw-p: var(--fontdue-feature-tester-switch-padding, 2px);
|
|
882
|
+
--_sw-b: var(--fontdue-feature-tester-switch-border-width, 1px);
|
|
883
|
+
--_sw-color: var(--fontdue-feature-tester-switch-color, currentColor);
|
|
884
|
+
--_sw-knob: var(--fontdue-feature-tester-switch-knob, var(--primary_background_color, canvas));
|
|
885
|
+
--_sw-track: var(--fontdue-feature-tester-switch-track, transparent);
|
|
886
|
+
--_sw-travel: calc(var(--_sw-w) - var(--_sw-h));
|
|
887
|
+
position: relative;
|
|
888
|
+
box-sizing: border-box;
|
|
889
|
+
width: var(--_sw-w);
|
|
890
|
+
height: var(--_sw-h);
|
|
891
|
+
flex: none;
|
|
892
|
+
border: var(--_sw-b) solid var(--_sw-color);
|
|
893
|
+
border-radius: var(--_sw-r);
|
|
894
|
+
background: var(--_sw-track);
|
|
895
|
+
transition: background 120ms ease-out;
|
|
896
|
+
}
|
|
897
|
+
.fontdue-feature-tester__switch::after {
|
|
898
|
+
content: "";
|
|
899
|
+
position: absolute;
|
|
900
|
+
box-sizing: border-box;
|
|
901
|
+
top: var(--_sw-p);
|
|
902
|
+
left: var(--_sw-p);
|
|
903
|
+
bottom: var(--_sw-p);
|
|
904
|
+
aspect-ratio: 1;
|
|
905
|
+
border: var(--_sw-b) solid var(--_sw-color);
|
|
906
|
+
border-radius: var(--_sw-r);
|
|
907
|
+
background: transparent;
|
|
908
|
+
transition: transform 120ms ease-out, background 120ms ease-out;
|
|
909
|
+
}
|
|
910
|
+
@media (hover: hover) {
|
|
911
|
+
.fontdue-feature-tester[data-on=false] .fontdue-feature-tester__toggle:hover .fontdue-feature-tester__switch::after {
|
|
912
|
+
background: var(--_sw-color);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
.fontdue-feature-tester__label {
|
|
916
|
+
white-space: nowrap;
|
|
917
|
+
}
|
|
918
|
+
.fontdue-feature-tester[data-toggle=button] .fontdue-feature-tester__label {
|
|
919
|
+
cursor: pointer;
|
|
920
|
+
}
|
|
921
|
+
.fontdue-feature-tester__tag {
|
|
922
|
+
margin-left: auto;
|
|
923
|
+
color: inherit;
|
|
924
|
+
}
|
|
925
|
+
.fontdue-feature-tester[data-on=true] .fontdue-feature-tester__switch {
|
|
926
|
+
background: var(--_sw-color);
|
|
927
|
+
}
|
|
928
|
+
.fontdue-feature-tester[data-on=true] .fontdue-feature-tester__switch::after {
|
|
929
|
+
transform: translateX(var(--_sw-travel));
|
|
930
|
+
border-color: transparent;
|
|
931
|
+
background: var(--_sw-knob);
|
|
932
|
+
}
|
|
933
|
+
.fontdue-feature-tester[data-toggle=hover] .fontdue-feature-tester__switch,
|
|
934
|
+
.fontdue-feature-tester[data-toggle=hover] .fontdue-feature-tester__toggle {
|
|
935
|
+
display: none;
|
|
936
|
+
}
|
|
937
|
+
@media (hover: none) {
|
|
938
|
+
.fontdue-feature-tester[data-toggle=hover] .fontdue-feature-tester__switch,
|
|
939
|
+
.fontdue-feature-tester[data-toggle=hover] .fontdue-feature-tester__toggle {
|
|
940
|
+
display: inline-flex;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
.fontdue-feature-tester__sample {
|
|
944
|
+
white-space: pre-wrap;
|
|
945
|
+
text-align: var(--_ft-align);
|
|
946
|
+
color: var(--_ft-text);
|
|
947
|
+
}
|
|
948
|
+
.fontdue-feature-tester[data-highlighted=true] .fontdue-feature-tester__sample {
|
|
949
|
+
color: var(--_ft-muted);
|
|
950
|
+
}
|
|
951
|
+
.fontdue-feature-tester__hit {
|
|
952
|
+
color: var(--_ft-highlight);
|
|
953
|
+
}
|
|
954
|
+
|
|
810
955
|
.select-button {
|
|
811
956
|
background: none;
|
|
812
957
|
color: inherit;
|