@liiift-studio/sanity-font-manager 2.3.19 → 2.5.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/README.md +437 -437
- package/dist/UploadModal-6LIX7XOK.js +6 -0
- package/dist/UploadModal-NME2W53V.mjs +6 -0
- package/dist/chunk-646WCBRR.mjs +7276 -0
- package/dist/chunk-FH4QKHOH.js +7276 -0
- package/dist/index.js +747 -1675
- package/dist/index.mjs +400 -1237
- package/package.json +85 -85
- package/src/components/BatchUploadFonts.jsx +653 -639
- package/src/components/BulkActions.jsx +99 -0
- package/src/components/ExistingDocumentResolver.jsx +152 -0
- package/src/components/FontReviewCard.jsx +415 -0
- package/src/components/FontScriptUploaderComponent.jsx +463 -463
- package/src/components/GenerateCollectionsPairsComponent.jsx +259 -259
- package/src/components/KeyValueInput.jsx +95 -95
- package/src/components/KeyValueReferenceInput.jsx +254 -254
- package/src/components/NestedObjectArraySelector.jsx +146 -146
- package/src/components/PriceInput.jsx +26 -26
- package/src/components/PrimaryCollectionGeneratorTypeface.jsx +116 -116
- package/src/components/RegenerateSubfamiliesComponent.jsx +185 -185
- package/src/components/SetOTF.jsx +87 -87
- package/src/components/SingleUploaderTool.jsx +672 -673
- package/src/components/StatusDisplay.jsx +26 -26
- package/src/components/StyleCountInput.jsx +16 -16
- package/src/components/UpdateScriptsComponent.jsx +76 -76
- package/src/components/UploadButton.jsx +43 -43
- package/src/components/UploadModal.jsx +268 -0
- package/src/components/UploadScriptsComponent.jsx +539 -537
- package/src/components/UploadStep1Settings.jsx +272 -0
- package/src/components/UploadStep2Review.jsx +472 -0
- package/src/components/UploadStep3Execute.jsx +234 -0
- package/src/components/UploadSummary.jsx +196 -0
- package/src/components/VariableInstanceReferencesInput.jsx +190 -190
- package/src/hooks/useNestedObjects.js +92 -92
- package/src/hooks/useSanityClient.js +9 -9
- package/src/index.js +115 -70
- package/src/schema/openTypeField.js +1945 -1945
- package/src/schema/styleCountField.js +12 -12
- package/src/schema/stylesField.js +268 -268
- package/src/schema/stylisticSetField.js +301 -301
- package/src/utils/buildUploadPlan.js +325 -0
- package/src/utils/executeUploadPlan.js +437 -0
- package/src/utils/executionReducer.js +56 -0
- package/src/utils/fontHelpers.js +267 -0
- package/src/utils/generateCssFile.js +207 -205
- package/src/utils/generateFontData.js +98 -145
- package/src/utils/generateFontFile.js +38 -38
- package/src/utils/generateKeywords.js +185 -185
- package/src/utils/generateSubset.js +45 -45
- package/src/utils/getEmptyFontKit.js +101 -99
- package/src/utils/parseFont.js +55 -0
- package/src/utils/parseVariableFontInstances.js +211 -211
- package/src/utils/planReducer.js +517 -0
- package/src/utils/planTypes.js +183 -0
- package/src/utils/processFontFiles.js +529 -477
- package/src/utils/regenerateFontData.js +146 -146
- package/src/utils/resolveExistingFont.js +87 -0
- package/src/utils/sanitizeForSanityId.js +65 -65
- package/src/utils/updateFontPrices.js +94 -94
- package/src/utils/updateTypefaceDocument.js +149 -160
- package/src/utils/uploadFontFiles.js +405 -316
- package/src/utils/utils.js +24 -24
|
@@ -1,185 +1,185 @@
|
|
|
1
|
-
// Recalculates and patches the subfamily field on all fonts linked to a typeface
|
|
2
|
-
|
|
3
|
-
import React, { useState } from 'react';
|
|
4
|
-
import { nanoid } from 'nanoid';
|
|
5
|
-
import { Button, Text, Stack, Box } from '@sanity/ui';
|
|
6
|
-
import { useFormValue } from 'sanity';
|
|
7
|
-
import { useSanityClient } from '../hooks/useSanityClient';
|
|
8
|
-
import { expandAbbreviations } from '../utils/generateKeywords';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Button component that rebuilds the subfamilies array on the current typeface document
|
|
12
|
-
* by grouping all linked fonts by their `subfamily` field.
|
|
13
|
-
*
|
|
14
|
-
* NOTE: This component uses its own local updateTypefaceDocument that patches ONLY
|
|
15
|
-
* the `subfamilies` key while preserving all other styles fields (fonts, variableFont).
|
|
16
|
-
* Do NOT replace it with the shared updateTypefaceDocument from utils/, which patches
|
|
17
|
-
* the full styles object and would overwrite the fonts/variableFont arrays.
|
|
18
|
-
*/
|
|
19
|
-
export const RegenerateSubfamiliesComponent = () => {
|
|
20
|
-
const [status, setStatus] = useState('');
|
|
21
|
-
const [ready, setReady] = useState(true);
|
|
22
|
-
const [error, setError] = useState(false);
|
|
23
|
-
|
|
24
|
-
const client = useSanityClient();
|
|
25
|
-
|
|
26
|
-
const doc_id = useFormValue(['_id']);
|
|
27
|
-
const title = useFormValue(['title']);
|
|
28
|
-
const slug = useFormValue(['slug']);
|
|
29
|
-
const stylesObject = useFormValue(['styles']) || { fonts: [], variableFont: [] };
|
|
30
|
-
|
|
31
|
-
const handleClick = () => {
|
|
32
|
-
regenerateSubfamilies({ title, stylesObject, slug, doc_id, client, setStatus, setReady, setError });
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
return (
|
|
36
|
-
<>
|
|
37
|
-
{status && (
|
|
38
|
-
<Box padding={3} style={{ borderRadius: '4px', marginBottom: '10px' }}>
|
|
39
|
-
<Text size={1} style={{ color: error ? 'red' : 'green' }}>{status}</Text>
|
|
40
|
-
</Box>
|
|
41
|
-
)}
|
|
42
|
-
<Button mode="ghost" tone="primary" width="fill" padding={3} onClick={handleClick} disabled={!ready}>
|
|
43
|
-
<Stack space={2}>
|
|
44
|
-
<Text align="center">Regenerate Subfamilies</Text>
|
|
45
|
-
</Stack>
|
|
46
|
-
</Button>
|
|
47
|
-
</>
|
|
48
|
-
);
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Fetches all fonts for a typeface, groups them by subfamily, and patches the document.
|
|
53
|
-
*/
|
|
54
|
-
const regenerateSubfamilies = async ({ title, stylesObject, slug, doc_id, client, setStatus, setReady, setError }) => {
|
|
55
|
-
try {
|
|
56
|
-
setStatus('Regenerating subfamilies...');
|
|
57
|
-
setReady(false);
|
|
58
|
-
setError(false);
|
|
59
|
-
|
|
60
|
-
const allFonts = await fetchFonts(title, slug, client);
|
|
61
|
-
|
|
62
|
-
if (!allFonts || allFonts.length === 0) {
|
|
63
|
-
setStatus('No fonts found for this typeface');
|
|
64
|
-
setReady(true);
|
|
65
|
-
setError(true);
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
console.log('Found fonts:', allFonts.length);
|
|
70
|
-
setStatus(`Found ${allFonts.length} fonts. Processing...`);
|
|
71
|
-
|
|
72
|
-
const subfamilies = groupFontsBySubfamily(allFonts);
|
|
73
|
-
const newSubfamiliesArray = createSubfamiliesArray(subfamilies);
|
|
74
|
-
|
|
75
|
-
console.log('New subfamilies:', newSubfamiliesArray);
|
|
76
|
-
setStatus(`Created ${newSubfamiliesArray.length} subfamily groups`);
|
|
77
|
-
|
|
78
|
-
await updateTypefaceSubfamilies(doc_id, stylesObject, newSubfamiliesArray, client, setStatus, setError);
|
|
79
|
-
|
|
80
|
-
setStatus('Subfamilies regenerated successfully');
|
|
81
|
-
} catch (e) {
|
|
82
|
-
console.error(e.message);
|
|
83
|
-
setStatus('Error regenerating subfamilies');
|
|
84
|
-
setError(true);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
setReady(true);
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Fetches the font list for a typeface by slug.
|
|
92
|
-
* @param {string} title
|
|
93
|
-
* @param {Object} slug
|
|
94
|
-
* @param {Object} client
|
|
95
|
-
* @returns {Promise<Array>}
|
|
96
|
-
*/
|
|
97
|
-
const fetchFonts = async (title, slug, client) => {
|
|
98
|
-
const slugCurrent = slug?.current || title;
|
|
99
|
-
const query = await client.fetch(
|
|
100
|
-
`*[_type == "typeface" && slug.current == $slugCurrent][0]{
|
|
101
|
-
"fonts": styles.fonts[]->{ _id, title, subfamily, style, weight, _key }
|
|
102
|
-
}`,
|
|
103
|
-
{ slugCurrent }
|
|
104
|
-
);
|
|
105
|
-
return query?.fonts || [];
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Groups fonts by their subfamily name, excluding VF fonts from shared subfamilies.
|
|
110
|
-
* @param {Object[]} fonts
|
|
111
|
-
* @returns {Object}
|
|
112
|
-
*/
|
|
113
|
-
const groupFontsBySubfamily = (fonts) => {
|
|
114
|
-
const subfamilies = {};
|
|
115
|
-
fonts.forEach(font => {
|
|
116
|
-
if (font.title?.includes('VF')) {
|
|
117
|
-
// Variable fonts get isolated into their own unique group so they don't pollute subfamilies
|
|
118
|
-
subfamilies[`VF_${font.title}`] = [font];
|
|
119
|
-
} else {
|
|
120
|
-
const subfamilyName = font.subfamily ? expandAbbreviations(font.subfamily) : 'Regular';
|
|
121
|
-
if (!subfamilies[subfamilyName]) subfamilies[subfamilyName] = [];
|
|
122
|
-
subfamilies[subfamilyName].push(font);
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
return subfamilies;
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Converts a grouped subfamilies map into the Sanity array format.
|
|
130
|
-
* @param {Object} subfamilies
|
|
131
|
-
* @returns {Object[]}
|
|
132
|
-
*/
|
|
133
|
-
const createSubfamiliesArray = (subfamilies) => {
|
|
134
|
-
return Object.keys(subfamilies).map(subfamilyName => ({
|
|
135
|
-
title: subfamilyName,
|
|
136
|
-
_key: nanoid(),
|
|
137
|
-
_type: 'object',
|
|
138
|
-
fonts: subfamilies[subfamilyName].map(font => ({
|
|
139
|
-
_ref: font._id,
|
|
140
|
-
_key: nanoid(),
|
|
141
|
-
_type: 'reference',
|
|
142
|
-
_weak: true,
|
|
143
|
-
})),
|
|
144
|
-
}));
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Patches ONLY the subfamilies key on the typeface document, preserving fonts and variableFont.
|
|
149
|
-
* This is intentionally scoped — do NOT replace with the shared updateTypefaceDocument util.
|
|
150
|
-
* @param {string} doc_id
|
|
151
|
-
* @param {Object} stylesObject
|
|
152
|
-
* @param {Object[]} newSubfamiliesArray
|
|
153
|
-
* @param {Object} client
|
|
154
|
-
* @param {Function} setStatus
|
|
155
|
-
* @param {Function} setError
|
|
156
|
-
*/
|
|
157
|
-
const updateTypefaceSubfamilies = async (doc_id, stylesObject, newSubfamiliesArray, client, setStatus, setError) => {
|
|
158
|
-
const patch = {
|
|
159
|
-
styles: {
|
|
160
|
-
...stylesObject,
|
|
161
|
-
subfamilies: newSubfamiliesArray,
|
|
162
|
-
},
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
try {
|
|
166
|
-
await client.patch(doc_id).set(patch).commit();
|
|
167
|
-
console.log(`Updated document: ${doc_id}`);
|
|
168
|
-
|
|
169
|
-
if (doc_id.startsWith('drafts.')) {
|
|
170
|
-
const publishedId = doc_id.replace('drafts.', '');
|
|
171
|
-
// Parameterized to guard against any edge-case injection via draft IDs
|
|
172
|
-
const publishedDoc = await client.fetch(`*[_id == $publishedId]`, { publishedId }).then(res => res[0]);
|
|
173
|
-
if (publishedDoc) {
|
|
174
|
-
await client.patch(publishedId).set(patch).commit();
|
|
175
|
-
console.log(`Updated published document: ${publishedId}`);
|
|
176
|
-
} else {
|
|
177
|
-
console.log(`No published document found for ${publishedId}, skipping`);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
} catch (err) {
|
|
181
|
-
console.error('Error updating document:', err.message);
|
|
182
|
-
setStatus('Error regenerating subfamilies');
|
|
183
|
-
setError(true);
|
|
184
|
-
}
|
|
185
|
-
};
|
|
1
|
+
// Recalculates and patches the subfamily field on all fonts linked to a typeface
|
|
2
|
+
|
|
3
|
+
import React, { useState } from 'react';
|
|
4
|
+
import { nanoid } from 'nanoid';
|
|
5
|
+
import { Button, Text, Stack, Box } from '@sanity/ui';
|
|
6
|
+
import { useFormValue } from 'sanity';
|
|
7
|
+
import { useSanityClient } from '../hooks/useSanityClient';
|
|
8
|
+
import { expandAbbreviations } from '../utils/generateKeywords';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Button component that rebuilds the subfamilies array on the current typeface document
|
|
12
|
+
* by grouping all linked fonts by their `subfamily` field.
|
|
13
|
+
*
|
|
14
|
+
* NOTE: This component uses its own local updateTypefaceDocument that patches ONLY
|
|
15
|
+
* the `subfamilies` key while preserving all other styles fields (fonts, variableFont).
|
|
16
|
+
* Do NOT replace it with the shared updateTypefaceDocument from utils/, which patches
|
|
17
|
+
* the full styles object and would overwrite the fonts/variableFont arrays.
|
|
18
|
+
*/
|
|
19
|
+
export const RegenerateSubfamiliesComponent = () => {
|
|
20
|
+
const [status, setStatus] = useState('');
|
|
21
|
+
const [ready, setReady] = useState(true);
|
|
22
|
+
const [error, setError] = useState(false);
|
|
23
|
+
|
|
24
|
+
const client = useSanityClient();
|
|
25
|
+
|
|
26
|
+
const doc_id = useFormValue(['_id']);
|
|
27
|
+
const title = useFormValue(['title']);
|
|
28
|
+
const slug = useFormValue(['slug']);
|
|
29
|
+
const stylesObject = useFormValue(['styles']) || { fonts: [], variableFont: [] };
|
|
30
|
+
|
|
31
|
+
const handleClick = () => {
|
|
32
|
+
regenerateSubfamilies({ title, stylesObject, slug, doc_id, client, setStatus, setReady, setError });
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<>
|
|
37
|
+
{status && (
|
|
38
|
+
<Box padding={3} style={{ borderRadius: '4px', marginBottom: '10px' }}>
|
|
39
|
+
<Text size={1} style={{ color: error ? 'red' : 'green' }}>{status}</Text>
|
|
40
|
+
</Box>
|
|
41
|
+
)}
|
|
42
|
+
<Button mode="ghost" tone="primary" width="fill" padding={3} onClick={handleClick} disabled={!ready}>
|
|
43
|
+
<Stack space={2}>
|
|
44
|
+
<Text align="center">Regenerate Subfamilies</Text>
|
|
45
|
+
</Stack>
|
|
46
|
+
</Button>
|
|
47
|
+
</>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Fetches all fonts for a typeface, groups them by subfamily, and patches the document.
|
|
53
|
+
*/
|
|
54
|
+
const regenerateSubfamilies = async ({ title, stylesObject, slug, doc_id, client, setStatus, setReady, setError }) => {
|
|
55
|
+
try {
|
|
56
|
+
setStatus('Regenerating subfamilies...');
|
|
57
|
+
setReady(false);
|
|
58
|
+
setError(false);
|
|
59
|
+
|
|
60
|
+
const allFonts = await fetchFonts(title, slug, client);
|
|
61
|
+
|
|
62
|
+
if (!allFonts || allFonts.length === 0) {
|
|
63
|
+
setStatus('No fonts found for this typeface');
|
|
64
|
+
setReady(true);
|
|
65
|
+
setError(true);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
console.log('Found fonts:', allFonts.length);
|
|
70
|
+
setStatus(`Found ${allFonts.length} fonts. Processing...`);
|
|
71
|
+
|
|
72
|
+
const subfamilies = groupFontsBySubfamily(allFonts);
|
|
73
|
+
const newSubfamiliesArray = createSubfamiliesArray(subfamilies);
|
|
74
|
+
|
|
75
|
+
console.log('New subfamilies:', newSubfamiliesArray);
|
|
76
|
+
setStatus(`Created ${newSubfamiliesArray.length} subfamily groups`);
|
|
77
|
+
|
|
78
|
+
await updateTypefaceSubfamilies(doc_id, stylesObject, newSubfamiliesArray, client, setStatus, setError);
|
|
79
|
+
|
|
80
|
+
setStatus('Subfamilies regenerated successfully');
|
|
81
|
+
} catch (e) {
|
|
82
|
+
console.error(e.message);
|
|
83
|
+
setStatus('Error regenerating subfamilies');
|
|
84
|
+
setError(true);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
setReady(true);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Fetches the font list for a typeface by slug.
|
|
92
|
+
* @param {string} title
|
|
93
|
+
* @param {Object} slug
|
|
94
|
+
* @param {Object} client
|
|
95
|
+
* @returns {Promise<Array>}
|
|
96
|
+
*/
|
|
97
|
+
const fetchFonts = async (title, slug, client) => {
|
|
98
|
+
const slugCurrent = slug?.current || title;
|
|
99
|
+
const query = await client.fetch(
|
|
100
|
+
`*[_type == "typeface" && slug.current == $slugCurrent][0]{
|
|
101
|
+
"fonts": styles.fonts[]->{ _id, title, subfamily, style, weight, _key }
|
|
102
|
+
}`,
|
|
103
|
+
{ slugCurrent }
|
|
104
|
+
);
|
|
105
|
+
return query?.fonts || [];
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Groups fonts by their subfamily name, excluding VF fonts from shared subfamilies.
|
|
110
|
+
* @param {Object[]} fonts
|
|
111
|
+
* @returns {Object}
|
|
112
|
+
*/
|
|
113
|
+
const groupFontsBySubfamily = (fonts) => {
|
|
114
|
+
const subfamilies = {};
|
|
115
|
+
fonts.forEach(font => {
|
|
116
|
+
if (font.title?.includes('VF')) {
|
|
117
|
+
// Variable fonts get isolated into their own unique group so they don't pollute subfamilies
|
|
118
|
+
subfamilies[`VF_${font.title}`] = [font];
|
|
119
|
+
} else {
|
|
120
|
+
const subfamilyName = font.subfamily ? expandAbbreviations(font.subfamily) : 'Regular';
|
|
121
|
+
if (!subfamilies[subfamilyName]) subfamilies[subfamilyName] = [];
|
|
122
|
+
subfamilies[subfamilyName].push(font);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
return subfamilies;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Converts a grouped subfamilies map into the Sanity array format.
|
|
130
|
+
* @param {Object} subfamilies
|
|
131
|
+
* @returns {Object[]}
|
|
132
|
+
*/
|
|
133
|
+
const createSubfamiliesArray = (subfamilies) => {
|
|
134
|
+
return Object.keys(subfamilies).map(subfamilyName => ({
|
|
135
|
+
title: subfamilyName,
|
|
136
|
+
_key: nanoid(),
|
|
137
|
+
_type: 'object',
|
|
138
|
+
fonts: subfamilies[subfamilyName].map(font => ({
|
|
139
|
+
_ref: font._id,
|
|
140
|
+
_key: nanoid(),
|
|
141
|
+
_type: 'reference',
|
|
142
|
+
_weak: true,
|
|
143
|
+
})),
|
|
144
|
+
}));
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Patches ONLY the subfamilies key on the typeface document, preserving fonts and variableFont.
|
|
149
|
+
* This is intentionally scoped — do NOT replace with the shared updateTypefaceDocument util.
|
|
150
|
+
* @param {string} doc_id
|
|
151
|
+
* @param {Object} stylesObject
|
|
152
|
+
* @param {Object[]} newSubfamiliesArray
|
|
153
|
+
* @param {Object} client
|
|
154
|
+
* @param {Function} setStatus
|
|
155
|
+
* @param {Function} setError
|
|
156
|
+
*/
|
|
157
|
+
const updateTypefaceSubfamilies = async (doc_id, stylesObject, newSubfamiliesArray, client, setStatus, setError) => {
|
|
158
|
+
const patch = {
|
|
159
|
+
styles: {
|
|
160
|
+
...stylesObject,
|
|
161
|
+
subfamilies: newSubfamiliesArray,
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
await client.patch(doc_id).set(patch).commit();
|
|
167
|
+
console.log(`Updated document: ${doc_id}`);
|
|
168
|
+
|
|
169
|
+
if (doc_id.startsWith('drafts.')) {
|
|
170
|
+
const publishedId = doc_id.replace('drafts.', '');
|
|
171
|
+
// Parameterized to guard against any edge-case injection via draft IDs
|
|
172
|
+
const publishedDoc = await client.fetch(`*[_id == $publishedId]`, { publishedId }).then(res => res[0]);
|
|
173
|
+
if (publishedDoc) {
|
|
174
|
+
await client.patch(publishedId).set(patch).commit();
|
|
175
|
+
console.log(`Updated published document: ${publishedId}`);
|
|
176
|
+
} else {
|
|
177
|
+
console.log(`No published document found for ${publishedId}, skipping`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
} catch (err) {
|
|
181
|
+
console.error('Error updating document:', err.message);
|
|
182
|
+
setStatus('Error regenerating subfamilies');
|
|
183
|
+
setError(true);
|
|
184
|
+
}
|
|
185
|
+
};
|
|
@@ -1,87 +1,87 @@
|
|
|
1
|
-
// Detects and sets active OpenType features on a typeface document from the first linked font's metadata
|
|
2
|
-
|
|
3
|
-
import React, { useState } from 'react';
|
|
4
|
-
import { set, useFormValue } from 'sanity';
|
|
5
|
-
import { Stack, Button, Text } from '@sanity/ui';
|
|
6
|
-
import { useSanityClient } from '../hooks/useSanityClient';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Reads the first linked font's opentypeFeatures data and checks which configured
|
|
10
|
-
* feature keys are supported. Patches the field with the detected features array.
|
|
11
|
-
*/
|
|
12
|
-
export const SetOTF = (props) => {
|
|
13
|
-
const { onChange, value = {} } = props;
|
|
14
|
-
const client = useSanityClient();
|
|
15
|
-
const stylesObject = useFormValue(['styles']);
|
|
16
|
-
const [message, setMessage] = useState('');
|
|
17
|
-
|
|
18
|
-
/** Fetches the first font document and matches its OpenType features against the configured keys. */
|
|
19
|
-
const detect = async () => {
|
|
20
|
-
if (!stylesObject?.fonts?.length) {
|
|
21
|
-
setMessage('Error: No fonts found in styles. Please add at least one font first.');
|
|
22
|
-
setTimeout(() => setMessage(''), 5000);
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const fontRef = stylesObject.fonts[0]?._ref;
|
|
27
|
-
if (!fontRef) {
|
|
28
|
-
setMessage('Error: Invalid font reference in styles.');
|
|
29
|
-
setTimeout(() => setMessage(''), 5000);
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
const font = await client.fetch('*[_type == "font" && _id == $id][0]', { id: fontRef });
|
|
35
|
-
|
|
36
|
-
if (!font) {
|
|
37
|
-
setMessage('Error: Could not find the referenced font.');
|
|
38
|
-
setTimeout(() => setMessage(''), 5000);
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (!font.opentypeFeatures?.chars) {
|
|
43
|
-
setMessage(`Error: No OpenType feature data found in "${font.title || 'this font'}". Generate font data first.`);
|
|
44
|
-
setTimeout(() => setMessage(''), 5000);
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const features = [];
|
|
49
|
-
Object.keys(value).forEach(key => {
|
|
50
|
-
if (key !== 'features' && value[key]?.feature) {
|
|
51
|
-
const requiredFeatures = value[key].feature.split(' ');
|
|
52
|
-
const approved = requiredFeatures.every(v => font.opentypeFeatures.chars.includes(v));
|
|
53
|
-
if (approved) features.push(key);
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
onChange(set({ ...value, features }));
|
|
58
|
-
setMessage(`Features detected: ${features.length ? features.join(', ') : 'none'}.`);
|
|
59
|
-
setTimeout(() => setMessage(''), 5000);
|
|
60
|
-
} catch (err) {
|
|
61
|
-
setMessage('Error detecting features. Check the console for details.');
|
|
62
|
-
console.error('SetOTF detect error:', err);
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
return (
|
|
67
|
-
<Stack className="openType">
|
|
68
|
-
{value?.features?.length > 0 && (
|
|
69
|
-
<Text muted size={1} style={{ marginBottom: '0.5rem' }}>
|
|
70
|
-
Number of features: {value.features.length}
|
|
71
|
-
</Text>
|
|
72
|
-
)}
|
|
73
|
-
{!!stylesObject?.fonts?.length && (
|
|
74
|
-
<Button
|
|
75
|
-
text="Detect OTF"
|
|
76
|
-
mode="ghost"
|
|
77
|
-
onClick={detect}
|
|
78
|
-
style={{ borderRadius: '0 3px 0 0', marginBottom: '1rem' }}
|
|
79
|
-
/>
|
|
80
|
-
)}
|
|
81
|
-
{!!message && (
|
|
82
|
-
<Text muted size={1}><br />{message}<br /><br /></Text>
|
|
83
|
-
)}
|
|
84
|
-
{props.renderDefault(props)}
|
|
85
|
-
</Stack>
|
|
86
|
-
);
|
|
87
|
-
};
|
|
1
|
+
// Detects and sets active OpenType features on a typeface document from the first linked font's metadata
|
|
2
|
+
|
|
3
|
+
import React, { useState } from 'react';
|
|
4
|
+
import { set, useFormValue } from 'sanity';
|
|
5
|
+
import { Stack, Button, Text } from '@sanity/ui';
|
|
6
|
+
import { useSanityClient } from '../hooks/useSanityClient';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Reads the first linked font's opentypeFeatures data and checks which configured
|
|
10
|
+
* feature keys are supported. Patches the field with the detected features array.
|
|
11
|
+
*/
|
|
12
|
+
export const SetOTF = (props) => {
|
|
13
|
+
const { onChange, value = {} } = props;
|
|
14
|
+
const client = useSanityClient();
|
|
15
|
+
const stylesObject = useFormValue(['styles']);
|
|
16
|
+
const [message, setMessage] = useState('');
|
|
17
|
+
|
|
18
|
+
/** Fetches the first font document and matches its OpenType features against the configured keys. */
|
|
19
|
+
const detect = async () => {
|
|
20
|
+
if (!stylesObject?.fonts?.length) {
|
|
21
|
+
setMessage('Error: No fonts found in styles. Please add at least one font first.');
|
|
22
|
+
setTimeout(() => setMessage(''), 5000);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const fontRef = stylesObject.fonts[0]?._ref;
|
|
27
|
+
if (!fontRef) {
|
|
28
|
+
setMessage('Error: Invalid font reference in styles.');
|
|
29
|
+
setTimeout(() => setMessage(''), 5000);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const font = await client.fetch('*[_type == "font" && _id == $id][0]', { id: fontRef });
|
|
35
|
+
|
|
36
|
+
if (!font) {
|
|
37
|
+
setMessage('Error: Could not find the referenced font.');
|
|
38
|
+
setTimeout(() => setMessage(''), 5000);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!font.opentypeFeatures?.chars) {
|
|
43
|
+
setMessage(`Error: No OpenType feature data found in "${font.title || 'this font'}". Generate font data first.`);
|
|
44
|
+
setTimeout(() => setMessage(''), 5000);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const features = [];
|
|
49
|
+
Object.keys(value).forEach(key => {
|
|
50
|
+
if (key !== 'features' && value[key]?.feature) {
|
|
51
|
+
const requiredFeatures = value[key].feature.split(' ');
|
|
52
|
+
const approved = requiredFeatures.every(v => font.opentypeFeatures.chars.includes(v));
|
|
53
|
+
if (approved) features.push(key);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
onChange(set({ ...value, features }));
|
|
58
|
+
setMessage(`Features detected: ${features.length ? features.join(', ') : 'none'}.`);
|
|
59
|
+
setTimeout(() => setMessage(''), 5000);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
setMessage('Error detecting features. Check the console for details.');
|
|
62
|
+
console.error('SetOTF detect error:', err);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<Stack className="openType">
|
|
68
|
+
{value?.features?.length > 0 && (
|
|
69
|
+
<Text muted size={1} style={{ marginBottom: '0.5rem' }}>
|
|
70
|
+
Number of features: {value.features.length}
|
|
71
|
+
</Text>
|
|
72
|
+
)}
|
|
73
|
+
{!!stylesObject?.fonts?.length && (
|
|
74
|
+
<Button
|
|
75
|
+
text="Detect OTF"
|
|
76
|
+
mode="ghost"
|
|
77
|
+
onClick={detect}
|
|
78
|
+
style={{ borderRadius: '0 3px 0 0', marginBottom: '1rem' }}
|
|
79
|
+
/>
|
|
80
|
+
)}
|
|
81
|
+
{!!message && (
|
|
82
|
+
<Text muted size={1}><br />{message}<br /><br /></Text>
|
|
83
|
+
)}
|
|
84
|
+
{props.renderDefault(props)}
|
|
85
|
+
</Stack>
|
|
86
|
+
);
|
|
87
|
+
};
|