@liiift-studio/sanity-font-manager 2.3.19 → 2.4.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/index.js +103 -48
- package/dist/index.mjs +103 -48
- package/package.json +85 -85
- package/src/components/BatchUploadFonts.jsx +640 -639
- 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 +673 -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/UploadScriptsComponent.jsx +537 -537
- 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 +70 -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/generateCssFile.js +205 -205
- package/src/utils/generateFontData.js +145 -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 +99 -99
- package/src/utils/parseVariableFontInstances.js +211 -211
- package/src/utils/processFontFiles.js +487 -477
- package/src/utils/regenerateFontData.js +146 -146
- 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 +115 -26
- package/src/utils/utils.js +24 -24
|
@@ -1,463 +1,463 @@
|
|
|
1
|
-
// Script-aware uploader for per-script font file variants (Latin, Arabic, Hebrew, etc.) stored in scriptFileInput
|
|
2
|
-
|
|
3
|
-
import React, { useState, useEffect, useCallback } from 'react';
|
|
4
|
-
import { Stack, Flex, Text, Button } from '@sanity/ui';
|
|
5
|
-
import { useFormValue, set, unset } from 'sanity';
|
|
6
|
-
|
|
7
|
-
// Utils
|
|
8
|
-
import generateCssFile from '../utils/generateCssFile';
|
|
9
|
-
import generateFontFile from '../utils/generateFontFile';
|
|
10
|
-
import { SCRIPTS } from '../utils/utils';
|
|
11
|
-
import { useSanityClient } from '../hooks/useSanityClient';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Component for managing font file uploads and conversions for different scripts/languages
|
|
15
|
-
* @param {Object} props - Component props
|
|
16
|
-
* @param {Object} props.elementProps - Props for the form element
|
|
17
|
-
* @param {React.Ref} props.elementProps.ref - Reference for file input
|
|
18
|
-
* @param {Function} props.onChange - Callback for handling form value changes
|
|
19
|
-
* @param {string} props.value - Current form value
|
|
20
|
-
*/
|
|
21
|
-
export const FontScriptUploaderComponent = (props) => {
|
|
22
|
-
const client = useSanityClient();
|
|
23
|
-
const {
|
|
24
|
-
elementProps: { ref },
|
|
25
|
-
onChange,
|
|
26
|
-
value = ''
|
|
27
|
-
} = props;
|
|
28
|
-
|
|
29
|
-
// State management
|
|
30
|
-
const [expanded, setExpanded] = useState(SCRIPTS.reduce((acc, language) => ({ ...acc, [language]: true }), {}));
|
|
31
|
-
const [message, setMessage] = useState({});
|
|
32
|
-
const [status, setStatus] = useState('ready');
|
|
33
|
-
const [filenames, setFilenames] = useState({});
|
|
34
|
-
|
|
35
|
-
// Form values from Sanity
|
|
36
|
-
let scriptFileInput = useFormValue(['scriptFileInput']) || [];
|
|
37
|
-
let fileInput = useFormValue(['fileInput']);
|
|
38
|
-
let doc_id = useFormValue(['_id']);
|
|
39
|
-
let doc_title = useFormValue(['title']);
|
|
40
|
-
let doc_variableFont = useFormValue(['variableFont']);
|
|
41
|
-
let doc_weight = useFormValue(['weight']);
|
|
42
|
-
let doc_style = useFormValue(['style']);
|
|
43
|
-
let doc_slug = useFormValue(['slug']);
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Updates filenames state based on scriptFileInput changes
|
|
47
|
-
*/
|
|
48
|
-
useEffect(() => {
|
|
49
|
-
if (!scriptFileInput || Object.keys(scriptFileInput).length === 0) return;
|
|
50
|
-
handleSetFilenames();
|
|
51
|
-
}, [scriptFileInput]);
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Fetches and sets filenames for all uploaded font files
|
|
55
|
-
*/
|
|
56
|
-
const handleSetFilenames = useCallback(async () => {
|
|
57
|
-
console.log('Set font names ', scriptFileInput);
|
|
58
|
-
let allIds = [];
|
|
59
|
-
|
|
60
|
-
const assetIds = SCRIPTS.reduce((acc, language) => {
|
|
61
|
-
if (scriptFileInput[language]) {
|
|
62
|
-
let newFileInput = Object.keys(scriptFileInput[language]).reduce((ftacc, filetype) => {
|
|
63
|
-
if (!scriptFileInput[language][filetype]?.asset?._ref) return ftacc;
|
|
64
|
-
allIds.push(scriptFileInput[language][filetype]?.asset?._ref);
|
|
65
|
-
return { ...ftacc, [filetype]: scriptFileInput[language][filetype]?.asset?._ref }
|
|
66
|
-
}, {});
|
|
67
|
-
acc[language] = newFileInput;
|
|
68
|
-
}
|
|
69
|
-
return acc;
|
|
70
|
-
}, {});
|
|
71
|
-
|
|
72
|
-
// Fetch all assets in a single request
|
|
73
|
-
let assetData = await client.fetch(`*[_id in $allIds] {
|
|
74
|
-
_id,
|
|
75
|
-
originalFilename
|
|
76
|
-
}`, { allIds });
|
|
77
|
-
|
|
78
|
-
assetData = assetData.reduce((acc, asset) => {
|
|
79
|
-
let ref = asset._id;
|
|
80
|
-
return { ...acc, [ref]: asset.originalFilename }
|
|
81
|
-
}, {});
|
|
82
|
-
|
|
83
|
-
let fontNames = {};
|
|
84
|
-
SCRIPTS.forEach(language => {
|
|
85
|
-
if (assetIds[language]) {
|
|
86
|
-
Object.keys(assetIds[language]).forEach(filetype => {
|
|
87
|
-
let ref = assetIds[language][filetype];
|
|
88
|
-
fontNames[language] = { ...fontNames[language], [filetype]: assetData[ref] }
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
setFilenames(fontNames);
|
|
94
|
-
}, [scriptFileInput]);
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Generates CSS file for a specific language
|
|
98
|
-
*/
|
|
99
|
-
const handleGenerateCssFile = useCallback(async (language) => {
|
|
100
|
-
setMessage({ ...message, [language]: 'Generating css: ' + doc_title + '.css' });
|
|
101
|
-
|
|
102
|
-
const woff2AssetRef = scriptFileInput[language]?.woff2?.asset?._ref;
|
|
103
|
-
// Parameterized — prevents injection via scriptFileInput asset refs
|
|
104
|
-
let [woff2Buffer] = await client.fetch(
|
|
105
|
-
`*[_id == $id]{ originalFilename, url }`,
|
|
106
|
-
{ id: woff2AssetRef }
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
let blob = await fetch(woff2Buffer.url);
|
|
110
|
-
blob = await blob.blob();
|
|
111
|
-
|
|
112
|
-
let newFileInput = await generateCssFile({
|
|
113
|
-
woff2File: blob,
|
|
114
|
-
fileInput: scriptFileInput,
|
|
115
|
-
language: language,
|
|
116
|
-
fontName: doc_title,
|
|
117
|
-
fileName: woff2Buffer.originalFilename.replace('.woff2', ''),
|
|
118
|
-
variableFont: doc_variableFont,
|
|
119
|
-
weight: doc_weight,
|
|
120
|
-
style: doc_style,
|
|
121
|
-
client: client
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
setMessage({ ...message, [language]: 'CSS generated!' });
|
|
125
|
-
setTimeout(() => { setMessage({}) }, 2000);
|
|
126
|
-
onChange(set(newFileInput));
|
|
127
|
-
}, [scriptFileInput, onChange, doc_title, doc_variableFont]);
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Generates font files in specified formats
|
|
131
|
-
*/
|
|
132
|
-
const handleGenerateFontFile = useCallback(async (code, sourceFile, language) => {
|
|
133
|
-
setMessage({ ...message, [language]: 'Generating files: ', code });
|
|
134
|
-
|
|
135
|
-
let url = `https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${sourceFile?.asset?._ref.replace("file-", "").replace("-", ".")}`;
|
|
136
|
-
console.log('Handle generate font file ', code, sourceFile, url);
|
|
137
|
-
|
|
138
|
-
if (code === 'all') {
|
|
139
|
-
await generateFontFile({
|
|
140
|
-
codes: ['otf', 'woff', 'woff2', 'eot', 'svg'],
|
|
141
|
-
language: language,
|
|
142
|
-
srcUrl: url,
|
|
143
|
-
filename: doc_slug.current + '-' + language,
|
|
144
|
-
documentId: doc_id,
|
|
145
|
-
documentTitle: doc_title,
|
|
146
|
-
documentVariableFont: doc_variableFont,
|
|
147
|
-
documentStyle: doc_style,
|
|
148
|
-
documentWeight: doc_weight,
|
|
149
|
-
fileInput: scriptFileInput,
|
|
150
|
-
});
|
|
151
|
-
} else {
|
|
152
|
-
await generateFontFile({
|
|
153
|
-
codes: [code],
|
|
154
|
-
language: language,
|
|
155
|
-
srcUrl: url,
|
|
156
|
-
filename: doc_slug.current + '-' + language,
|
|
157
|
-
documentId: doc_id,
|
|
158
|
-
documentTitle: doc_title,
|
|
159
|
-
documentVariableFont: doc_variableFont,
|
|
160
|
-
documentStyle: doc_style,
|
|
161
|
-
documentWeight: doc_weight,
|
|
162
|
-
fileInput: scriptFileInput,
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
setMessage({ ...message, [language]: 'Files generated!' });
|
|
166
|
-
setTimeout(() => { setMessage({}) }, 2000);
|
|
167
|
-
}, []);
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Handles font file upload for a specific language and format
|
|
171
|
-
*/
|
|
172
|
-
const handleUpload = useCallback(async (event, language, code) => {
|
|
173
|
-
console.log('Handle upload ', scriptFileInput, language, code);
|
|
174
|
-
|
|
175
|
-
let file = event.target.files[0];
|
|
176
|
-
let filename = doc_slug.current + '-' + language + '.' + file.name.split('.').pop();
|
|
177
|
-
|
|
178
|
-
setMessage({ ...message, [language]: 'Uploading: ' + filename });
|
|
179
|
-
|
|
180
|
-
var asset = await client.assets.upload('file', file, { filename: filename });
|
|
181
|
-
|
|
182
|
-
let langObj = scriptFileInput[language] ? { ...scriptFileInput[language] } : {};
|
|
183
|
-
let newFileInput = {
|
|
184
|
-
...scriptFileInput,
|
|
185
|
-
[language]: {
|
|
186
|
-
...langObj,
|
|
187
|
-
[code]: {
|
|
188
|
-
_type: 'file',
|
|
189
|
-
asset: {
|
|
190
|
-
_ref: asset._id,
|
|
191
|
-
_type: 'reference'
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
let id = doc_id;
|
|
198
|
-
if (id.startsWith('drafts.')) {
|
|
199
|
-
id = id.replace('drafts.', '');
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
setMessage({ ...message, [language]: filename + ' uploaded!' });
|
|
203
|
-
setTimeout(() => { setMessage({}) }, 2000);
|
|
204
|
-
|
|
205
|
-
// Generate CSS for WOFF2 files
|
|
206
|
-
if (code === 'woff2') {
|
|
207
|
-
console.log('woff2');
|
|
208
|
-
setMessage({ ...message, [language]: 'Generating Css: ' + doc_title + '.css' });
|
|
209
|
-
|
|
210
|
-
newFileInput = await generateCssFile({
|
|
211
|
-
woff2File: file,
|
|
212
|
-
fileInput: newFileInput,
|
|
213
|
-
language: language,
|
|
214
|
-
fontName: doc_title + '-' + language,
|
|
215
|
-
fileName: filename.replace('.woff2', ''),
|
|
216
|
-
variableFont: doc_variableFont,
|
|
217
|
-
weight: doc_weight,
|
|
218
|
-
style: doc_style,
|
|
219
|
-
client: client
|
|
220
|
-
});
|
|
221
|
-
setMessage({ ...message, [language]: '' + doc_title + '.css generated!' });
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
onChange(set(newFileInput));
|
|
225
|
-
}, [scriptFileInput, onChange, doc_title, doc_variableFont, doc_slug]);
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Deletes a specific font file
|
|
229
|
-
*/
|
|
230
|
-
const handleDelete = useCallback(async (code, language) => {
|
|
231
|
-
console.log('Delete : ', code, language);
|
|
232
|
-
|
|
233
|
-
setMessage({ ...message, [language]: `deleting ${language} ${code}` });
|
|
234
|
-
const asset = scriptFileInput[language][code]?.asset?._ref;
|
|
235
|
-
|
|
236
|
-
let newFileInput = { ...scriptFileInput };
|
|
237
|
-
delete newFileInput[language][code];
|
|
238
|
-
|
|
239
|
-
onChange(unset([language, code]));
|
|
240
|
-
|
|
241
|
-
await client.delete(asset)
|
|
242
|
-
.then(result => {
|
|
243
|
-
setMessage({ ...message, [language]: 'deleted asset: ', result });
|
|
244
|
-
setTimeout(() => { setMessage({}) }, 2000);
|
|
245
|
-
})
|
|
246
|
-
.catch(e => {
|
|
247
|
-
console.error('Error deleting asset: ', e.message);
|
|
248
|
-
setMessage({ ...message, [language]: 'WARNING: ' + e.message });
|
|
249
|
-
});
|
|
250
|
-
}, [doc_id, scriptFileInput, onChange]);
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Deletes all font files for a specific language
|
|
254
|
-
*/
|
|
255
|
-
const handleDeleteAll = useCallback(async (language) => {
|
|
256
|
-
setMessage({ ...message, [language]: 'deleting...' });
|
|
257
|
-
onChange(unset([language]));
|
|
258
|
-
|
|
259
|
-
console.log('Delete all : ', scriptFileInput[language]);
|
|
260
|
-
for (var i = 0; i < Object.keys(scriptFileInput[language]).length; i++) {
|
|
261
|
-
let refKey = Object.keys(scriptFileInput[language])[i];
|
|
262
|
-
if (refKey == 'documentInfo') return;
|
|
263
|
-
|
|
264
|
-
const asset = scriptFileInput[language][refKey]?.asset?._ref;
|
|
265
|
-
|
|
266
|
-
try {
|
|
267
|
-
await client.delete(asset)
|
|
268
|
-
.then(result => {
|
|
269
|
-
setMessage({ ...message, [language]: 'deleted asset: ', result });
|
|
270
|
-
setTimeout(() => { setMessage({}) }, 2000);
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
catch (e) {
|
|
274
|
-
console.error('Error deleting asset: ', e.message);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}, [scriptFileInput]);
|
|
278
|
-
|
|
279
|
-
// Render component
|
|
280
|
-
return (
|
|
281
|
-
<Stack space={4}>
|
|
282
|
-
{SCRIPTS && scriptFileInput && SCRIPTS.map((language, i) => {
|
|
283
|
-
return (
|
|
284
|
-
<Stack space={2} key={'language-' + i} style={{ borderBottom: '1px solid var(--card-border-color)', paddingBottom: 8 }}>
|
|
285
|
-
<Flex gap={2}>
|
|
286
|
-
<Text weight="semibold">{language[0]?.toUpperCase() + language.slice(1)}</Text>
|
|
287
|
-
{message && message[language] && message[language] !== '' && (
|
|
288
|
-
<Text style={{ color: 'green' }}>{message[language]}</Text>
|
|
289
|
-
)}
|
|
290
|
-
</Flex>
|
|
291
|
-
|
|
292
|
-
{expanded[language] && (
|
|
293
|
-
<Stack space={2}>
|
|
294
|
-
{/* TTF Section */}
|
|
295
|
-
<Flex justify="space-between" align="center">
|
|
296
|
-
<Text>
|
|
297
|
-
TTF: {!scriptFileInput[language]?.ttf?.asset?._ref
|
|
298
|
-
? (filenames[language]?.ttf ? <b>{filenames[language].ttf}</b> : <b>Empty</b>)
|
|
299
|
-
: <a href={`https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${scriptFileInput[language]?.ttf?.asset?._ref.replace("file-", "").replace("-", ".")}`} target="_blank">
|
|
300
|
-
{filenames[language]?.ttf ? <b>{filenames[language].ttf}</b> : <b>File</b>}
|
|
301
|
-
</a>
|
|
302
|
-
}
|
|
303
|
-
</Text>
|
|
304
|
-
{status === 'ready' && (
|
|
305
|
-
<Flex gap={1}>
|
|
306
|
-
<label>
|
|
307
|
-
<Button as="span" mode="ghost">Upload</Button>
|
|
308
|
-
<input ref={ref} type="file" style={{ display: 'none' }} onChange={(event) => handleUpload(event, language, 'ttf')} />
|
|
309
|
-
</label>
|
|
310
|
-
{value[language]?.ttf && <Button mode="ghost" tone="critical" onClick={() => handleDelete('ttf', language)}>×</Button>}
|
|
311
|
-
</Flex>
|
|
312
|
-
)}
|
|
313
|
-
</Flex>
|
|
314
|
-
|
|
315
|
-
{/* Generate All Button */}
|
|
316
|
-
{status === 'ready' && value[language]?.ttf && (
|
|
317
|
-
<Button mode="default" onClick={() => handleGenerateFontFile('all', value[language].ttf, language)}>
|
|
318
|
-
Regenerate Files from TTF
|
|
319
|
-
</Button>
|
|
320
|
-
)}
|
|
321
|
-
|
|
322
|
-
{/* OTF Section */}
|
|
323
|
-
<Flex justify="space-between" align="center">
|
|
324
|
-
<Text>
|
|
325
|
-
OTF: {!scriptFileInput[language]?.otf?.asset?._ref
|
|
326
|
-
? (filenames[language]?.otf ? <b>{filenames[language]?.otf}</b> : <b>Empty</b>)
|
|
327
|
-
: <a href={`https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${scriptFileInput[language]?.otf?.asset?._ref.replace("file-", "").replace("-", ".")}`} target="_blank">
|
|
328
|
-
{filenames[language]?.otf ? <b>{filenames[language]?.otf}</b> : <b>File</b>}
|
|
329
|
-
</a>
|
|
330
|
-
}
|
|
331
|
-
</Text>
|
|
332
|
-
{status === 'ready' && (
|
|
333
|
-
<Flex gap={1}>
|
|
334
|
-
{value[language]?.woff && <Button mode="default" onClick={() => handleGenerateFontFile('otf', value[language].woff, language)}>Build</Button>}
|
|
335
|
-
<label>
|
|
336
|
-
<Button as="span" mode="ghost">Upload</Button>
|
|
337
|
-
<input ref={ref} type="file" style={{ display: 'none' }} onChange={async (event) => handleUpload(event, language, 'otf')} />
|
|
338
|
-
</label>
|
|
339
|
-
{value[language]?.otf && <Button mode="ghost" tone="critical" onClick={() => handleDelete('otf', language)}>×</Button>}
|
|
340
|
-
</Flex>
|
|
341
|
-
)}
|
|
342
|
-
</Flex>
|
|
343
|
-
|
|
344
|
-
{/* WOFF Section */}
|
|
345
|
-
<Flex justify="space-between" align="center">
|
|
346
|
-
<Text>
|
|
347
|
-
WOFF: {!scriptFileInput[language]?.woff?.asset?._ref
|
|
348
|
-
? (filenames[language]?.woff ? <b>{filenames[language]?.woff}</b> : <b>Empty</b>)
|
|
349
|
-
: <a href={`https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${scriptFileInput[language]?.woff?.asset?._ref.replace("file-", "").replace("-", ".")}`} target="_blank">
|
|
350
|
-
{filenames[language]?.woff ? <b>{filenames[language]?.woff}</b> : <b>File</b>}
|
|
351
|
-
</a>
|
|
352
|
-
}
|
|
353
|
-
</Text>
|
|
354
|
-
{status === 'ready' && (
|
|
355
|
-
<Flex gap={1}>
|
|
356
|
-
{value[language]?.ttf && <Button mode="default" onClick={() => handleGenerateFontFile('woff', value[language].ttf, language)}>Build</Button>}
|
|
357
|
-
<label>
|
|
358
|
-
<Button as="span" mode="ghost">Upload</Button>
|
|
359
|
-
<input ref={ref} type="file" style={{ display: 'none' }} onChange={async (event) => handleUpload(event, language, 'woff')} />
|
|
360
|
-
</label>
|
|
361
|
-
{value[language]?.woff && <Button mode="ghost" tone="critical" onClick={() => handleDelete('woff', language)}>×</Button>}
|
|
362
|
-
</Flex>
|
|
363
|
-
)}
|
|
364
|
-
</Flex>
|
|
365
|
-
|
|
366
|
-
{/* WOFF2 Section */}
|
|
367
|
-
<Flex justify="space-between" align="center">
|
|
368
|
-
<Text>
|
|
369
|
-
WOFF2: {!scriptFileInput[language]?.woff2?.asset?._ref
|
|
370
|
-
? (filenames[language]?.woff2 ? <b>{filenames[language]?.woff2}</b> : <b>Empty</b>)
|
|
371
|
-
: <a href={`https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${scriptFileInput[language]?.woff2?.asset?._ref.replace("file-", "").replace("-", ".")}`} target="_blank">
|
|
372
|
-
{filenames[language]?.woff2 ? <b>{filenames[language]?.woff2}</b> : <b>File</b>}
|
|
373
|
-
</a>
|
|
374
|
-
}
|
|
375
|
-
</Text>
|
|
376
|
-
{status === 'ready' && (
|
|
377
|
-
<Flex gap={1}>
|
|
378
|
-
{value[language]?.ttf && <Button mode="default" onClick={() => handleGenerateFontFile('woff2', value[language].ttf, language)}>Build</Button>}
|
|
379
|
-
<label>
|
|
380
|
-
<Button as="span" mode="ghost">Upload</Button>
|
|
381
|
-
<input ref={ref} type="file" style={{ display: 'none' }} onChange={async (event) => handleUpload(event, language, 'woff2')} />
|
|
382
|
-
</label>
|
|
383
|
-
{value[language]?.woff2 && <Button mode="ghost" tone="critical" onClick={() => handleDelete('woff2', language)}>×</Button>}
|
|
384
|
-
</Flex>
|
|
385
|
-
)}
|
|
386
|
-
</Flex>
|
|
387
|
-
|
|
388
|
-
{/* EOT Section */}
|
|
389
|
-
<Flex justify="space-between" align="center">
|
|
390
|
-
<Text>
|
|
391
|
-
EOT: {!scriptFileInput[language]?.eot?.asset?._ref
|
|
392
|
-
? (filenames[language]?.eot ? <b>{filenames[language]?.eot}</b> : <b>Empty</b>)
|
|
393
|
-
: <a href={`https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${scriptFileInput[language]?.eot?.asset?._ref.replace("file-", "").replace("-", ".")}`} target="_blank">
|
|
394
|
-
{filenames[language]?.eot ? <b>{filenames[language]?.eot}</b> : <b>File</b>}
|
|
395
|
-
</a>
|
|
396
|
-
}
|
|
397
|
-
</Text>
|
|
398
|
-
{status === 'ready' && (
|
|
399
|
-
<Flex gap={1}>
|
|
400
|
-
{value[language]?.ttf && <Button mode="default" onClick={() => handleGenerateFontFile('eot', value[language].ttf, language)}>Build</Button>}
|
|
401
|
-
<label>
|
|
402
|
-
<Button as="span" mode="ghost">Upload</Button>
|
|
403
|
-
<input ref={ref} type="file" style={{ display: 'none' }} onChange={async (event) => handleUpload(event, language, 'eot')} />
|
|
404
|
-
</label>
|
|
405
|
-
{value[language]?.eot && <Button mode="ghost" tone="critical" onClick={() => handleDelete('eot', language)}>×</Button>}
|
|
406
|
-
</Flex>
|
|
407
|
-
)}
|
|
408
|
-
</Flex>
|
|
409
|
-
|
|
410
|
-
{/* SVG Section */}
|
|
411
|
-
<Flex justify="space-between" align="center">
|
|
412
|
-
<Text>
|
|
413
|
-
SVG: {!scriptFileInput[language]?.svg?.asset?._ref
|
|
414
|
-
? (filenames[language]?.svg ? <b>{filenames[language]?.svg}</b> : <b>Empty</b>)
|
|
415
|
-
: <a href={`https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${scriptFileInput[language]?.svg?.asset?._ref.replace("file-", "").replace("-", ".")}`} target="_blank">
|
|
416
|
-
{filenames[language]?.svg ? <b>{filenames[language]?.svg}</b> : <b>File</b>}
|
|
417
|
-
</a>
|
|
418
|
-
}
|
|
419
|
-
</Text>
|
|
420
|
-
{status === 'ready' && (
|
|
421
|
-
<Flex gap={1}>
|
|
422
|
-
{value[language]?.ttf && <Button mode="default" onClick={() => handleGenerateFontFile('svg', value[language].ttf, language)}>Build</Button>}
|
|
423
|
-
<label>
|
|
424
|
-
<Button as="span" mode="ghost">Upload</Button>
|
|
425
|
-
<input ref={ref} type="file" style={{ display: 'none' }} onChange={async (event) => handleUpload(event, language, 'svg')} />
|
|
426
|
-
</label>
|
|
427
|
-
{value[language]?.svg && <Button mode="ghost" tone="critical" onClick={() => handleDelete('svg', language)}>×</Button>}
|
|
428
|
-
</Flex>
|
|
429
|
-
)}
|
|
430
|
-
</Flex>
|
|
431
|
-
|
|
432
|
-
{/* CSS Section */}
|
|
433
|
-
<Flex justify="space-between" align="center">
|
|
434
|
-
<Text>
|
|
435
|
-
CSS: {!scriptFileInput[language]?.css?.asset?._ref
|
|
436
|
-
? (filenames[language]?.css ? <b>{filenames[language]?.css}</b> : <b>Empty</b>)
|
|
437
|
-
: <a href={`https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${scriptFileInput[language]?.css?.asset?._ref.replace("file-", "").replace("-", ".")}`} target="_blank">
|
|
438
|
-
{filenames[language]?.css ? <b>{filenames[language]?.css}</b> : <b>File</b>}
|
|
439
|
-
</a>
|
|
440
|
-
}
|
|
441
|
-
</Text>
|
|
442
|
-
{status === 'ready' && (
|
|
443
|
-
<Flex gap={1}>
|
|
444
|
-
{value[language]?.woff2 && <Button mode="default" onClick={() => handleGenerateCssFile(language)}>Build</Button>}
|
|
445
|
-
{value[language]?.css && <Button mode="ghost" tone="critical" onClick={() => handleDelete('css', language)}>×</Button>}
|
|
446
|
-
</Flex>
|
|
447
|
-
)}
|
|
448
|
-
</Flex>
|
|
449
|
-
|
|
450
|
-
{/* Delete All Button */}
|
|
451
|
-
{status === 'ready' && (value[language]?.ttf || value[language]?.otf || value[language]?.woff || value[language]?.woff2) && (
|
|
452
|
-
<Button mode="ghost" tone="critical" onClick={() => handleDeleteAll(language)} style={{ width: '100%' }}>
|
|
453
|
-
Delete All
|
|
454
|
-
</Button>
|
|
455
|
-
)}
|
|
456
|
-
</Stack>
|
|
457
|
-
)}
|
|
458
|
-
</Stack>
|
|
459
|
-
)
|
|
460
|
-
})}
|
|
461
|
-
</Stack>
|
|
462
|
-
)
|
|
463
|
-
}
|
|
1
|
+
// Script-aware uploader for per-script font file variants (Latin, Arabic, Hebrew, etc.) stored in scriptFileInput
|
|
2
|
+
|
|
3
|
+
import React, { useState, useEffect, useCallback } from 'react';
|
|
4
|
+
import { Stack, Flex, Text, Button } from '@sanity/ui';
|
|
5
|
+
import { useFormValue, set, unset } from 'sanity';
|
|
6
|
+
|
|
7
|
+
// Utils
|
|
8
|
+
import generateCssFile from '../utils/generateCssFile';
|
|
9
|
+
import generateFontFile from '../utils/generateFontFile';
|
|
10
|
+
import { SCRIPTS } from '../utils/utils';
|
|
11
|
+
import { useSanityClient } from '../hooks/useSanityClient';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Component for managing font file uploads and conversions for different scripts/languages
|
|
15
|
+
* @param {Object} props - Component props
|
|
16
|
+
* @param {Object} props.elementProps - Props for the form element
|
|
17
|
+
* @param {React.Ref} props.elementProps.ref - Reference for file input
|
|
18
|
+
* @param {Function} props.onChange - Callback for handling form value changes
|
|
19
|
+
* @param {string} props.value - Current form value
|
|
20
|
+
*/
|
|
21
|
+
export const FontScriptUploaderComponent = (props) => {
|
|
22
|
+
const client = useSanityClient();
|
|
23
|
+
const {
|
|
24
|
+
elementProps: { ref },
|
|
25
|
+
onChange,
|
|
26
|
+
value = ''
|
|
27
|
+
} = props;
|
|
28
|
+
|
|
29
|
+
// State management
|
|
30
|
+
const [expanded, setExpanded] = useState(SCRIPTS.reduce((acc, language) => ({ ...acc, [language]: true }), {}));
|
|
31
|
+
const [message, setMessage] = useState({});
|
|
32
|
+
const [status, setStatus] = useState('ready');
|
|
33
|
+
const [filenames, setFilenames] = useState({});
|
|
34
|
+
|
|
35
|
+
// Form values from Sanity
|
|
36
|
+
let scriptFileInput = useFormValue(['scriptFileInput']) || [];
|
|
37
|
+
let fileInput = useFormValue(['fileInput']);
|
|
38
|
+
let doc_id = useFormValue(['_id']);
|
|
39
|
+
let doc_title = useFormValue(['title']);
|
|
40
|
+
let doc_variableFont = useFormValue(['variableFont']);
|
|
41
|
+
let doc_weight = useFormValue(['weight']);
|
|
42
|
+
let doc_style = useFormValue(['style']);
|
|
43
|
+
let doc_slug = useFormValue(['slug']);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Updates filenames state based on scriptFileInput changes
|
|
47
|
+
*/
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
if (!scriptFileInput || Object.keys(scriptFileInput).length === 0) return;
|
|
50
|
+
handleSetFilenames();
|
|
51
|
+
}, [scriptFileInput]);
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Fetches and sets filenames for all uploaded font files
|
|
55
|
+
*/
|
|
56
|
+
const handleSetFilenames = useCallback(async () => {
|
|
57
|
+
console.log('Set font names ', scriptFileInput);
|
|
58
|
+
let allIds = [];
|
|
59
|
+
|
|
60
|
+
const assetIds = SCRIPTS.reduce((acc, language) => {
|
|
61
|
+
if (scriptFileInput[language]) {
|
|
62
|
+
let newFileInput = Object.keys(scriptFileInput[language]).reduce((ftacc, filetype) => {
|
|
63
|
+
if (!scriptFileInput[language][filetype]?.asset?._ref) return ftacc;
|
|
64
|
+
allIds.push(scriptFileInput[language][filetype]?.asset?._ref);
|
|
65
|
+
return { ...ftacc, [filetype]: scriptFileInput[language][filetype]?.asset?._ref }
|
|
66
|
+
}, {});
|
|
67
|
+
acc[language] = newFileInput;
|
|
68
|
+
}
|
|
69
|
+
return acc;
|
|
70
|
+
}, {});
|
|
71
|
+
|
|
72
|
+
// Fetch all assets in a single request
|
|
73
|
+
let assetData = await client.fetch(`*[_id in $allIds] {
|
|
74
|
+
_id,
|
|
75
|
+
originalFilename
|
|
76
|
+
}`, { allIds });
|
|
77
|
+
|
|
78
|
+
assetData = assetData.reduce((acc, asset) => {
|
|
79
|
+
let ref = asset._id;
|
|
80
|
+
return { ...acc, [ref]: asset.originalFilename }
|
|
81
|
+
}, {});
|
|
82
|
+
|
|
83
|
+
let fontNames = {};
|
|
84
|
+
SCRIPTS.forEach(language => {
|
|
85
|
+
if (assetIds[language]) {
|
|
86
|
+
Object.keys(assetIds[language]).forEach(filetype => {
|
|
87
|
+
let ref = assetIds[language][filetype];
|
|
88
|
+
fontNames[language] = { ...fontNames[language], [filetype]: assetData[ref] }
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
setFilenames(fontNames);
|
|
94
|
+
}, [scriptFileInput]);
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Generates CSS file for a specific language
|
|
98
|
+
*/
|
|
99
|
+
const handleGenerateCssFile = useCallback(async (language) => {
|
|
100
|
+
setMessage({ ...message, [language]: 'Generating css: ' + doc_title + '.css' });
|
|
101
|
+
|
|
102
|
+
const woff2AssetRef = scriptFileInput[language]?.woff2?.asset?._ref;
|
|
103
|
+
// Parameterized — prevents injection via scriptFileInput asset refs
|
|
104
|
+
let [woff2Buffer] = await client.fetch(
|
|
105
|
+
`*[_id == $id]{ originalFilename, url }`,
|
|
106
|
+
{ id: woff2AssetRef }
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
let blob = await fetch(woff2Buffer.url);
|
|
110
|
+
blob = await blob.blob();
|
|
111
|
+
|
|
112
|
+
let newFileInput = await generateCssFile({
|
|
113
|
+
woff2File: blob,
|
|
114
|
+
fileInput: scriptFileInput,
|
|
115
|
+
language: language,
|
|
116
|
+
fontName: doc_title,
|
|
117
|
+
fileName: woff2Buffer.originalFilename.replace('.woff2', ''),
|
|
118
|
+
variableFont: doc_variableFont,
|
|
119
|
+
weight: doc_weight,
|
|
120
|
+
style: doc_style,
|
|
121
|
+
client: client
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
setMessage({ ...message, [language]: 'CSS generated!' });
|
|
125
|
+
setTimeout(() => { setMessage({}) }, 2000);
|
|
126
|
+
onChange(set(newFileInput));
|
|
127
|
+
}, [scriptFileInput, onChange, doc_title, doc_variableFont]);
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Generates font files in specified formats
|
|
131
|
+
*/
|
|
132
|
+
const handleGenerateFontFile = useCallback(async (code, sourceFile, language) => {
|
|
133
|
+
setMessage({ ...message, [language]: 'Generating files: ', code });
|
|
134
|
+
|
|
135
|
+
let url = `https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${sourceFile?.asset?._ref.replace("file-", "").replace("-", ".")}`;
|
|
136
|
+
console.log('Handle generate font file ', code, sourceFile, url);
|
|
137
|
+
|
|
138
|
+
if (code === 'all') {
|
|
139
|
+
await generateFontFile({
|
|
140
|
+
codes: ['otf', 'woff', 'woff2', 'eot', 'svg'],
|
|
141
|
+
language: language,
|
|
142
|
+
srcUrl: url,
|
|
143
|
+
filename: doc_slug.current + '-' + language,
|
|
144
|
+
documentId: doc_id,
|
|
145
|
+
documentTitle: doc_title,
|
|
146
|
+
documentVariableFont: doc_variableFont,
|
|
147
|
+
documentStyle: doc_style,
|
|
148
|
+
documentWeight: doc_weight,
|
|
149
|
+
fileInput: scriptFileInput,
|
|
150
|
+
});
|
|
151
|
+
} else {
|
|
152
|
+
await generateFontFile({
|
|
153
|
+
codes: [code],
|
|
154
|
+
language: language,
|
|
155
|
+
srcUrl: url,
|
|
156
|
+
filename: doc_slug.current + '-' + language,
|
|
157
|
+
documentId: doc_id,
|
|
158
|
+
documentTitle: doc_title,
|
|
159
|
+
documentVariableFont: doc_variableFont,
|
|
160
|
+
documentStyle: doc_style,
|
|
161
|
+
documentWeight: doc_weight,
|
|
162
|
+
fileInput: scriptFileInput,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
setMessage({ ...message, [language]: 'Files generated!' });
|
|
166
|
+
setTimeout(() => { setMessage({}) }, 2000);
|
|
167
|
+
}, []);
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Handles font file upload for a specific language and format
|
|
171
|
+
*/
|
|
172
|
+
const handleUpload = useCallback(async (event, language, code) => {
|
|
173
|
+
console.log('Handle upload ', scriptFileInput, language, code);
|
|
174
|
+
|
|
175
|
+
let file = event.target.files[0];
|
|
176
|
+
let filename = doc_slug.current + '-' + language + '.' + file.name.split('.').pop();
|
|
177
|
+
|
|
178
|
+
setMessage({ ...message, [language]: 'Uploading: ' + filename });
|
|
179
|
+
|
|
180
|
+
var asset = await client.assets.upload('file', file, { filename: filename });
|
|
181
|
+
|
|
182
|
+
let langObj = scriptFileInput[language] ? { ...scriptFileInput[language] } : {};
|
|
183
|
+
let newFileInput = {
|
|
184
|
+
...scriptFileInput,
|
|
185
|
+
[language]: {
|
|
186
|
+
...langObj,
|
|
187
|
+
[code]: {
|
|
188
|
+
_type: 'file',
|
|
189
|
+
asset: {
|
|
190
|
+
_ref: asset._id,
|
|
191
|
+
_type: 'reference'
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
let id = doc_id;
|
|
198
|
+
if (id.startsWith('drafts.')) {
|
|
199
|
+
id = id.replace('drafts.', '');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
setMessage({ ...message, [language]: filename + ' uploaded!' });
|
|
203
|
+
setTimeout(() => { setMessage({}) }, 2000);
|
|
204
|
+
|
|
205
|
+
// Generate CSS for WOFF2 files
|
|
206
|
+
if (code === 'woff2') {
|
|
207
|
+
console.log('woff2');
|
|
208
|
+
setMessage({ ...message, [language]: 'Generating Css: ' + doc_title + '.css' });
|
|
209
|
+
|
|
210
|
+
newFileInput = await generateCssFile({
|
|
211
|
+
woff2File: file,
|
|
212
|
+
fileInput: newFileInput,
|
|
213
|
+
language: language,
|
|
214
|
+
fontName: doc_title + '-' + language,
|
|
215
|
+
fileName: filename.replace('.woff2', ''),
|
|
216
|
+
variableFont: doc_variableFont,
|
|
217
|
+
weight: doc_weight,
|
|
218
|
+
style: doc_style,
|
|
219
|
+
client: client
|
|
220
|
+
});
|
|
221
|
+
setMessage({ ...message, [language]: '' + doc_title + '.css generated!' });
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
onChange(set(newFileInput));
|
|
225
|
+
}, [scriptFileInput, onChange, doc_title, doc_variableFont, doc_slug]);
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Deletes a specific font file
|
|
229
|
+
*/
|
|
230
|
+
const handleDelete = useCallback(async (code, language) => {
|
|
231
|
+
console.log('Delete : ', code, language);
|
|
232
|
+
|
|
233
|
+
setMessage({ ...message, [language]: `deleting ${language} ${code}` });
|
|
234
|
+
const asset = scriptFileInput[language][code]?.asset?._ref;
|
|
235
|
+
|
|
236
|
+
let newFileInput = { ...scriptFileInput };
|
|
237
|
+
delete newFileInput[language][code];
|
|
238
|
+
|
|
239
|
+
onChange(unset([language, code]));
|
|
240
|
+
|
|
241
|
+
await client.delete(asset)
|
|
242
|
+
.then(result => {
|
|
243
|
+
setMessage({ ...message, [language]: 'deleted asset: ', result });
|
|
244
|
+
setTimeout(() => { setMessage({}) }, 2000);
|
|
245
|
+
})
|
|
246
|
+
.catch(e => {
|
|
247
|
+
console.error('Error deleting asset: ', e.message);
|
|
248
|
+
setMessage({ ...message, [language]: 'WARNING: ' + e.message });
|
|
249
|
+
});
|
|
250
|
+
}, [doc_id, scriptFileInput, onChange]);
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Deletes all font files for a specific language
|
|
254
|
+
*/
|
|
255
|
+
const handleDeleteAll = useCallback(async (language) => {
|
|
256
|
+
setMessage({ ...message, [language]: 'deleting...' });
|
|
257
|
+
onChange(unset([language]));
|
|
258
|
+
|
|
259
|
+
console.log('Delete all : ', scriptFileInput[language]);
|
|
260
|
+
for (var i = 0; i < Object.keys(scriptFileInput[language]).length; i++) {
|
|
261
|
+
let refKey = Object.keys(scriptFileInput[language])[i];
|
|
262
|
+
if (refKey == 'documentInfo') return;
|
|
263
|
+
|
|
264
|
+
const asset = scriptFileInput[language][refKey]?.asset?._ref;
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
await client.delete(asset)
|
|
268
|
+
.then(result => {
|
|
269
|
+
setMessage({ ...message, [language]: 'deleted asset: ', result });
|
|
270
|
+
setTimeout(() => { setMessage({}) }, 2000);
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
catch (e) {
|
|
274
|
+
console.error('Error deleting asset: ', e.message);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}, [scriptFileInput]);
|
|
278
|
+
|
|
279
|
+
// Render component
|
|
280
|
+
return (
|
|
281
|
+
<Stack space={4}>
|
|
282
|
+
{SCRIPTS && scriptFileInput && SCRIPTS.map((language, i) => {
|
|
283
|
+
return (
|
|
284
|
+
<Stack space={2} key={'language-' + i} style={{ borderBottom: '1px solid var(--card-border-color)', paddingBottom: 8 }}>
|
|
285
|
+
<Flex gap={2}>
|
|
286
|
+
<Text weight="semibold">{language[0]?.toUpperCase() + language.slice(1)}</Text>
|
|
287
|
+
{message && message[language] && message[language] !== '' && (
|
|
288
|
+
<Text style={{ color: 'green' }}>{message[language]}</Text>
|
|
289
|
+
)}
|
|
290
|
+
</Flex>
|
|
291
|
+
|
|
292
|
+
{expanded[language] && (
|
|
293
|
+
<Stack space={2}>
|
|
294
|
+
{/* TTF Section */}
|
|
295
|
+
<Flex justify="space-between" align="center">
|
|
296
|
+
<Text>
|
|
297
|
+
TTF: {!scriptFileInput[language]?.ttf?.asset?._ref
|
|
298
|
+
? (filenames[language]?.ttf ? <b>{filenames[language].ttf}</b> : <b>Empty</b>)
|
|
299
|
+
: <a href={`https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${scriptFileInput[language]?.ttf?.asset?._ref.replace("file-", "").replace("-", ".")}`} target="_blank">
|
|
300
|
+
{filenames[language]?.ttf ? <b>{filenames[language].ttf}</b> : <b>File</b>}
|
|
301
|
+
</a>
|
|
302
|
+
}
|
|
303
|
+
</Text>
|
|
304
|
+
{status === 'ready' && (
|
|
305
|
+
<Flex gap={1}>
|
|
306
|
+
<label>
|
|
307
|
+
<Button as="span" mode="ghost">Upload</Button>
|
|
308
|
+
<input ref={ref} type="file" style={{ display: 'none' }} onChange={(event) => handleUpload(event, language, 'ttf')} />
|
|
309
|
+
</label>
|
|
310
|
+
{value[language]?.ttf && <Button mode="ghost" tone="critical" onClick={() => handleDelete('ttf', language)}>×</Button>}
|
|
311
|
+
</Flex>
|
|
312
|
+
)}
|
|
313
|
+
</Flex>
|
|
314
|
+
|
|
315
|
+
{/* Generate All Button */}
|
|
316
|
+
{status === 'ready' && value[language]?.ttf && (
|
|
317
|
+
<Button mode="default" onClick={() => handleGenerateFontFile('all', value[language].ttf, language)}>
|
|
318
|
+
Regenerate Files from TTF
|
|
319
|
+
</Button>
|
|
320
|
+
)}
|
|
321
|
+
|
|
322
|
+
{/* OTF Section */}
|
|
323
|
+
<Flex justify="space-between" align="center">
|
|
324
|
+
<Text>
|
|
325
|
+
OTF: {!scriptFileInput[language]?.otf?.asset?._ref
|
|
326
|
+
? (filenames[language]?.otf ? <b>{filenames[language]?.otf}</b> : <b>Empty</b>)
|
|
327
|
+
: <a href={`https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${scriptFileInput[language]?.otf?.asset?._ref.replace("file-", "").replace("-", ".")}`} target="_blank">
|
|
328
|
+
{filenames[language]?.otf ? <b>{filenames[language]?.otf}</b> : <b>File</b>}
|
|
329
|
+
</a>
|
|
330
|
+
}
|
|
331
|
+
</Text>
|
|
332
|
+
{status === 'ready' && (
|
|
333
|
+
<Flex gap={1}>
|
|
334
|
+
{value[language]?.woff && <Button mode="default" onClick={() => handleGenerateFontFile('otf', value[language].woff, language)}>Build</Button>}
|
|
335
|
+
<label>
|
|
336
|
+
<Button as="span" mode="ghost">Upload</Button>
|
|
337
|
+
<input ref={ref} type="file" style={{ display: 'none' }} onChange={async (event) => handleUpload(event, language, 'otf')} />
|
|
338
|
+
</label>
|
|
339
|
+
{value[language]?.otf && <Button mode="ghost" tone="critical" onClick={() => handleDelete('otf', language)}>×</Button>}
|
|
340
|
+
</Flex>
|
|
341
|
+
)}
|
|
342
|
+
</Flex>
|
|
343
|
+
|
|
344
|
+
{/* WOFF Section */}
|
|
345
|
+
<Flex justify="space-between" align="center">
|
|
346
|
+
<Text>
|
|
347
|
+
WOFF: {!scriptFileInput[language]?.woff?.asset?._ref
|
|
348
|
+
? (filenames[language]?.woff ? <b>{filenames[language]?.woff}</b> : <b>Empty</b>)
|
|
349
|
+
: <a href={`https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${scriptFileInput[language]?.woff?.asset?._ref.replace("file-", "").replace("-", ".")}`} target="_blank">
|
|
350
|
+
{filenames[language]?.woff ? <b>{filenames[language]?.woff}</b> : <b>File</b>}
|
|
351
|
+
</a>
|
|
352
|
+
}
|
|
353
|
+
</Text>
|
|
354
|
+
{status === 'ready' && (
|
|
355
|
+
<Flex gap={1}>
|
|
356
|
+
{value[language]?.ttf && <Button mode="default" onClick={() => handleGenerateFontFile('woff', value[language].ttf, language)}>Build</Button>}
|
|
357
|
+
<label>
|
|
358
|
+
<Button as="span" mode="ghost">Upload</Button>
|
|
359
|
+
<input ref={ref} type="file" style={{ display: 'none' }} onChange={async (event) => handleUpload(event, language, 'woff')} />
|
|
360
|
+
</label>
|
|
361
|
+
{value[language]?.woff && <Button mode="ghost" tone="critical" onClick={() => handleDelete('woff', language)}>×</Button>}
|
|
362
|
+
</Flex>
|
|
363
|
+
)}
|
|
364
|
+
</Flex>
|
|
365
|
+
|
|
366
|
+
{/* WOFF2 Section */}
|
|
367
|
+
<Flex justify="space-between" align="center">
|
|
368
|
+
<Text>
|
|
369
|
+
WOFF2: {!scriptFileInput[language]?.woff2?.asset?._ref
|
|
370
|
+
? (filenames[language]?.woff2 ? <b>{filenames[language]?.woff2}</b> : <b>Empty</b>)
|
|
371
|
+
: <a href={`https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${scriptFileInput[language]?.woff2?.asset?._ref.replace("file-", "").replace("-", ".")}`} target="_blank">
|
|
372
|
+
{filenames[language]?.woff2 ? <b>{filenames[language]?.woff2}</b> : <b>File</b>}
|
|
373
|
+
</a>
|
|
374
|
+
}
|
|
375
|
+
</Text>
|
|
376
|
+
{status === 'ready' && (
|
|
377
|
+
<Flex gap={1}>
|
|
378
|
+
{value[language]?.ttf && <Button mode="default" onClick={() => handleGenerateFontFile('woff2', value[language].ttf, language)}>Build</Button>}
|
|
379
|
+
<label>
|
|
380
|
+
<Button as="span" mode="ghost">Upload</Button>
|
|
381
|
+
<input ref={ref} type="file" style={{ display: 'none' }} onChange={async (event) => handleUpload(event, language, 'woff2')} />
|
|
382
|
+
</label>
|
|
383
|
+
{value[language]?.woff2 && <Button mode="ghost" tone="critical" onClick={() => handleDelete('woff2', language)}>×</Button>}
|
|
384
|
+
</Flex>
|
|
385
|
+
)}
|
|
386
|
+
</Flex>
|
|
387
|
+
|
|
388
|
+
{/* EOT Section */}
|
|
389
|
+
<Flex justify="space-between" align="center">
|
|
390
|
+
<Text>
|
|
391
|
+
EOT: {!scriptFileInput[language]?.eot?.asset?._ref
|
|
392
|
+
? (filenames[language]?.eot ? <b>{filenames[language]?.eot}</b> : <b>Empty</b>)
|
|
393
|
+
: <a href={`https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${scriptFileInput[language]?.eot?.asset?._ref.replace("file-", "").replace("-", ".")}`} target="_blank">
|
|
394
|
+
{filenames[language]?.eot ? <b>{filenames[language]?.eot}</b> : <b>File</b>}
|
|
395
|
+
</a>
|
|
396
|
+
}
|
|
397
|
+
</Text>
|
|
398
|
+
{status === 'ready' && (
|
|
399
|
+
<Flex gap={1}>
|
|
400
|
+
{value[language]?.ttf && <Button mode="default" onClick={() => handleGenerateFontFile('eot', value[language].ttf, language)}>Build</Button>}
|
|
401
|
+
<label>
|
|
402
|
+
<Button as="span" mode="ghost">Upload</Button>
|
|
403
|
+
<input ref={ref} type="file" style={{ display: 'none' }} onChange={async (event) => handleUpload(event, language, 'eot')} />
|
|
404
|
+
</label>
|
|
405
|
+
{value[language]?.eot && <Button mode="ghost" tone="critical" onClick={() => handleDelete('eot', language)}>×</Button>}
|
|
406
|
+
</Flex>
|
|
407
|
+
)}
|
|
408
|
+
</Flex>
|
|
409
|
+
|
|
410
|
+
{/* SVG Section */}
|
|
411
|
+
<Flex justify="space-between" align="center">
|
|
412
|
+
<Text>
|
|
413
|
+
SVG: {!scriptFileInput[language]?.svg?.asset?._ref
|
|
414
|
+
? (filenames[language]?.svg ? <b>{filenames[language]?.svg}</b> : <b>Empty</b>)
|
|
415
|
+
: <a href={`https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${scriptFileInput[language]?.svg?.asset?._ref.replace("file-", "").replace("-", ".")}`} target="_blank">
|
|
416
|
+
{filenames[language]?.svg ? <b>{filenames[language]?.svg}</b> : <b>File</b>}
|
|
417
|
+
</a>
|
|
418
|
+
}
|
|
419
|
+
</Text>
|
|
420
|
+
{status === 'ready' && (
|
|
421
|
+
<Flex gap={1}>
|
|
422
|
+
{value[language]?.ttf && <Button mode="default" onClick={() => handleGenerateFontFile('svg', value[language].ttf, language)}>Build</Button>}
|
|
423
|
+
<label>
|
|
424
|
+
<Button as="span" mode="ghost">Upload</Button>
|
|
425
|
+
<input ref={ref} type="file" style={{ display: 'none' }} onChange={async (event) => handleUpload(event, language, 'svg')} />
|
|
426
|
+
</label>
|
|
427
|
+
{value[language]?.svg && <Button mode="ghost" tone="critical" onClick={() => handleDelete('svg', language)}>×</Button>}
|
|
428
|
+
</Flex>
|
|
429
|
+
)}
|
|
430
|
+
</Flex>
|
|
431
|
+
|
|
432
|
+
{/* CSS Section */}
|
|
433
|
+
<Flex justify="space-between" align="center">
|
|
434
|
+
<Text>
|
|
435
|
+
CSS: {!scriptFileInput[language]?.css?.asset?._ref
|
|
436
|
+
? (filenames[language]?.css ? <b>{filenames[language]?.css}</b> : <b>Empty</b>)
|
|
437
|
+
: <a href={`https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${scriptFileInput[language]?.css?.asset?._ref.replace("file-", "").replace("-", ".")}`} target="_blank">
|
|
438
|
+
{filenames[language]?.css ? <b>{filenames[language]?.css}</b> : <b>File</b>}
|
|
439
|
+
</a>
|
|
440
|
+
}
|
|
441
|
+
</Text>
|
|
442
|
+
{status === 'ready' && (
|
|
443
|
+
<Flex gap={1}>
|
|
444
|
+
{value[language]?.woff2 && <Button mode="default" onClick={() => handleGenerateCssFile(language)}>Build</Button>}
|
|
445
|
+
{value[language]?.css && <Button mode="ghost" tone="critical" onClick={() => handleDelete('css', language)}>×</Button>}
|
|
446
|
+
</Flex>
|
|
447
|
+
)}
|
|
448
|
+
</Flex>
|
|
449
|
+
|
|
450
|
+
{/* Delete All Button */}
|
|
451
|
+
{status === 'ready' && (value[language]?.ttf || value[language]?.otf || value[language]?.woff || value[language]?.woff2) && (
|
|
452
|
+
<Button mode="ghost" tone="critical" onClick={() => handleDeleteAll(language)} style={{ width: '100%' }}>
|
|
453
|
+
Delete All
|
|
454
|
+
</Button>
|
|
455
|
+
)}
|
|
456
|
+
</Stack>
|
|
457
|
+
)}
|
|
458
|
+
</Stack>
|
|
459
|
+
)
|
|
460
|
+
})}
|
|
461
|
+
</Stack>
|
|
462
|
+
)
|
|
463
|
+
}
|