@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,537 +1,539 @@
|
|
|
1
|
-
// Batch uploader for script-specific font variants across multiple fonts at once
|
|
2
|
-
|
|
3
|
-
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
4
|
-
import { Button, Flex, Grid, Stack, Text, TextInput, MenuButton, Menu, MenuItem, Select } from '@sanity/ui';
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
*
|
|
17
|
-
* @param {Object} props
|
|
18
|
-
* @
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const [
|
|
29
|
-
const [
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
*
|
|
47
|
-
* @
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
reader.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
*
|
|
66
|
-
* @param {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
let
|
|
83
|
-
let
|
|
84
|
-
let
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
weightName =
|
|
106
|
-
weightName = weightName
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
let
|
|
111
|
-
let
|
|
112
|
-
let
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
//
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
.
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
if(
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
console.log('
|
|
179
|
-
console.log('
|
|
180
|
-
console.log('
|
|
181
|
-
console.log('
|
|
182
|
-
console.log('
|
|
183
|
-
console.log('
|
|
184
|
-
console.log('
|
|
185
|
-
console.log('
|
|
186
|
-
console.log('
|
|
187
|
-
console.log('
|
|
188
|
-
|
|
189
|
-
console.log('
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
/
|
|
210
|
-
/
|
|
211
|
-
/
|
|
212
|
-
/
|
|
213
|
-
/
|
|
214
|
-
/
|
|
215
|
-
/
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
//
|
|
234
|
-
//
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
let
|
|
240
|
-
let
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
else if ( file.name.endsWith('.
|
|
260
|
-
else if ( file.name.endsWith('.
|
|
261
|
-
else if ( file.name.endsWith('.
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
{ ...stylesObject, fonts : [...fontRefs] }
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
console.log('
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
console.log('
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
setStatus('fonts uploaded
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
1
|
+
// Batch uploader for script-specific font variants across multiple fonts at once
|
|
2
|
+
|
|
3
|
+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
4
|
+
import { Button, Flex, Grid, Stack, Text, TextInput, MenuButton, Menu, MenuItem, Select } from '@sanity/ui';
|
|
5
|
+
import { parseFont } from '../utils/parseFont';
|
|
6
|
+
import { getNameString, getVariationAxes, getItalicAngle, getWeightClass } from '../utils/fontHelpers';
|
|
7
|
+
import slugify from 'slugify';
|
|
8
|
+
import { useSanityClient } from '../hooks/useSanityClient';
|
|
9
|
+
import { useFormValue } from 'sanity';
|
|
10
|
+
import { nanoid } from 'nanoid';
|
|
11
|
+
import generateCssFile from '../utils/generateCssFile';
|
|
12
|
+
import { generateStyleKeywords, reverseSpellingLookup } from '../utils/generateKeywords';
|
|
13
|
+
import { SCRIPTS } from '../utils/utils';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Component for uploading and managing script variants of fonts
|
|
17
|
+
* @param {Object} props - Component props
|
|
18
|
+
* @param {Object} props.elementProps - Element properties including ref
|
|
19
|
+
* @returns {JSX.Element} Upload interface for script variants
|
|
20
|
+
*/
|
|
21
|
+
export const UploadScriptsComponent = (props) => {
|
|
22
|
+
|
|
23
|
+
// Props and client initialization
|
|
24
|
+
const {elementProps: {ref}} = props;
|
|
25
|
+
const client = useSanityClient();
|
|
26
|
+
|
|
27
|
+
// Component state
|
|
28
|
+
const [selectedScript, setSelectedScript] = useState(""); // Currently selected script
|
|
29
|
+
const [status, setStatus] = React.useState(''); // Upload status message
|
|
30
|
+
const [ready, setReady] = React.useState(true); // Component ready state
|
|
31
|
+
|
|
32
|
+
// Form values from Sanity
|
|
33
|
+
let doc_id = useFormValue(['_id']); // Document ID
|
|
34
|
+
const title = useFormValue(['title']); // Typeface title
|
|
35
|
+
const slug = useFormValue(['slug']); // URL slug
|
|
36
|
+
const scripts = useFormValue(['scripts']) || []; // Supported scripts
|
|
37
|
+
const stylesObject = useFormValue(['styles']); // Font styles data
|
|
38
|
+
let subfamiliesArray = stylesObject?.subfamilies || []; // Font subfamilies
|
|
39
|
+
|
|
40
|
+
// Memoized style keywords for font processing
|
|
41
|
+
const {weightKeywordList, italicKeywordList} = useMemo(() =>
|
|
42
|
+
generateStyleKeywords()
|
|
43
|
+
, []);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Reads a font file and returns its content as a Uint8Array
|
|
47
|
+
* @param {File} file - The font file to read
|
|
48
|
+
* @returns {Promise<Uint8Array>} Font file content
|
|
49
|
+
*/
|
|
50
|
+
const readFontFile = (file) => {
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
const reader = new FileReader();
|
|
53
|
+
|
|
54
|
+
reader.onload = (event) => {
|
|
55
|
+
resolve(new Uint8Array(event.target.result));
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
reader.onerror = (error) => { reject(error); };
|
|
59
|
+
reader.readAsArrayBuffer(file);
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Handles the upload and processing of font files for a specific script
|
|
66
|
+
* @param {Event} event - The file input change event
|
|
67
|
+
* @param {string} script - The selected script variant (e.g., 'cyrillic', 'greek')
|
|
68
|
+
*/
|
|
69
|
+
const handleUpload = useCallback(async(event, script) => {
|
|
70
|
+
setReady(false);
|
|
71
|
+
try{
|
|
72
|
+
let failedFiles = [];
|
|
73
|
+
|
|
74
|
+
console.log('handle upload ', title, script );
|
|
75
|
+
setStatus('uploading fonts files.. ');
|
|
76
|
+
|
|
77
|
+
if(!title) {
|
|
78
|
+
console.error('typeface needs title');
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let fontRefs = [];
|
|
83
|
+
let variableRefs = [];
|
|
84
|
+
let subfamilies = {};
|
|
85
|
+
let fontsObjects = {};
|
|
86
|
+
|
|
87
|
+
// read font files ,
|
|
88
|
+
// create if doesnt exist - create sanity fontObjects template
|
|
89
|
+
// add font file to sanity font
|
|
90
|
+
// create subfamily list
|
|
91
|
+
for(var i = 0 ; i < event.target.files.length ; i++ ){
|
|
92
|
+
|
|
93
|
+
const file = event.target.files[i];
|
|
94
|
+
const fontBuffer = await readFontFile(file);
|
|
95
|
+
const font = await parseFont(fontBuffer, file.name);
|
|
96
|
+
|
|
97
|
+
const fullName = getNameString(font, 4);
|
|
98
|
+
const familyName = getNameString(font, 1);
|
|
99
|
+
console.log('Reading font:', fullName, file.name);
|
|
100
|
+
|
|
101
|
+
let weightName = getNameString(font, 17) || getNameString(font, 2) || '';
|
|
102
|
+
weightName = weightName.replace("Italic", "").replace("It", "").trim();
|
|
103
|
+
|
|
104
|
+
if ((weightName == '' || weightName.toLowerCase() == 'roman') && fullName) {
|
|
105
|
+
weightName = fullName.replace(title + " ", "").replace(title, "").trim();
|
|
106
|
+
weightName = weightName.replace("Italic", "").replace("It", "").trim();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const axes = getVariationAxes(font);
|
|
110
|
+
let variableFont = axes !== null;
|
|
111
|
+
let subfamilyName = familyName.toLowerCase().trim().replace(title.toLowerCase().trim(),'').trim();
|
|
112
|
+
let fontTitle = fullName;
|
|
113
|
+
const italicAngle = getItalicAngle(font);
|
|
114
|
+
let style = (italicAngle !== 0 || fullName.toLowerCase().includes('italic')) ? 'Italic' : 'Regular';
|
|
115
|
+
|
|
116
|
+
if(fontTitle.toLowerCase().trim().includes(script)){
|
|
117
|
+
fontTitle = fontTitle.toLowerCase().trim().replace(script, '').trim();
|
|
118
|
+
fontTitle = fontTitle.split(' ').map( word => {
|
|
119
|
+
if( word == '') return
|
|
120
|
+
return word;
|
|
121
|
+
})
|
|
122
|
+
.filter( word => word != undefined)
|
|
123
|
+
.join(' ');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// remove weight and italic keywords from subfamily name
|
|
127
|
+
weightKeywordList.forEach( keyword => {
|
|
128
|
+
const kw = keyword.trim();
|
|
129
|
+
if(subfamilyName.includes(kw)) subfamilyName = subfamilyName.replace(kw, '').trim();
|
|
130
|
+
|
|
131
|
+
// if(fontTitle.includes(kw)){
|
|
132
|
+
// fontTitle = fontTitle.replace(kw, '');
|
|
133
|
+
// }
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
let italicKW = [];
|
|
137
|
+
italicKeywordList.forEach( keyword => {
|
|
138
|
+
const kw = keyword.toLowerCase().trim();
|
|
139
|
+
if(subfamilyName.includes(kw)){
|
|
140
|
+
subfamilyName = subfamilyName.replace(kw, '');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if(fontTitle.includes(kw)){
|
|
144
|
+
fontTitle = fontTitle.replace(kw, '');
|
|
145
|
+
italicKW.push(kw.charAt(0).toUpperCase() + kw.slice(1));
|
|
146
|
+
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
fontTitle = fontTitle.replace(/-/g, ' ');
|
|
151
|
+
fontTitle = fontTitle.trim().split(' ').map( word => word[0].toUpperCase() + word.slice(1)).join(' ');
|
|
152
|
+
|
|
153
|
+
if(subfamilyName.trim().includes(script)){
|
|
154
|
+
subfamilyName = subfamilyName.trim().replace(script, '').trim();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
subfamilyName = subfamilyName.trim();
|
|
158
|
+
subfamilyName = (subfamilyName == '' ) ? 'Regular' : subfamilyName.split(' ').map( word => word[0].toUpperCase() + word.slice(1)).join(' ');
|
|
159
|
+
|
|
160
|
+
// remove subfamily from weight name
|
|
161
|
+
if (subfamilyName !== '' ) {
|
|
162
|
+
weightName = weightName
|
|
163
|
+
.replace(`${subfamilyName} `, '')
|
|
164
|
+
.replace(` ${subfamilyName}`, '')
|
|
165
|
+
.trim();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if(variableFont && !fontTitle.toLowerCase().trim().endsWith(' vf')) fontTitle = fontTitle + ' VF';
|
|
169
|
+
|
|
170
|
+
if(italicKW.length > 0){
|
|
171
|
+
italicKW = italicKW.map( item => reverseSpellingLookup(item)); // replace each item in the italicKW list with the value in reverseSpellingLookup
|
|
172
|
+
fontTitle = fontTitle + italicKW.join(' ');
|
|
173
|
+
style = 'Italic';
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
let id = slugify(fontTitle.toLowerCase().trim());
|
|
177
|
+
|
|
178
|
+
console.log('=== Font Info ====');
|
|
179
|
+
console.log(' ')
|
|
180
|
+
console.log('font id : ', id);
|
|
181
|
+
console.log('font title : ', fontTitle);
|
|
182
|
+
console.log('Full name:', fullName);
|
|
183
|
+
console.log('Family name:', familyName);
|
|
184
|
+
console.log('file name : ', file.name);
|
|
185
|
+
console.log('subfamily : ', subfamilyName);
|
|
186
|
+
console.log('style : ', style);
|
|
187
|
+
console.log('weight : ', weightName);
|
|
188
|
+
console.log('variable : ', variableFont);
|
|
189
|
+
console.log('italicKW ', italicKW);
|
|
190
|
+
console.log(' ')
|
|
191
|
+
console.log('=======');
|
|
192
|
+
|
|
193
|
+
subfamilies[id] = subfamilyName; // add subfamily to list
|
|
194
|
+
|
|
195
|
+
if( fontsObjects[id]){
|
|
196
|
+
fontsObjects[id].files = [...fontsObjects[id].files, file];
|
|
197
|
+
} else {
|
|
198
|
+
let fontObject = {
|
|
199
|
+
_key: nanoid(),
|
|
200
|
+
_id: id,
|
|
201
|
+
title: fontTitle,
|
|
202
|
+
slug: {_type:'slug', current:id},
|
|
203
|
+
typefaceName: title, // Change to match Typeface Document
|
|
204
|
+
style: style,
|
|
205
|
+
variableFont: variableFont,
|
|
206
|
+
weightName: weightName,
|
|
207
|
+
normalWeight:true,
|
|
208
|
+
weight: getWeightClass(font) || (
|
|
209
|
+
/hairline|extra thin|extrathin/.test(weightName?.toLowerCase()) ? 100 :
|
|
210
|
+
/thin|extra light|extralight/.test(weightName?.toLowerCase()) ? 200 :
|
|
211
|
+
/light|book/.test(weightName?.toLowerCase()) ? 300 :
|
|
212
|
+
/regular|normal/.test(weightName?.toLowerCase()) ? 400 :
|
|
213
|
+
/medium/.test(weightName?.toLowerCase()) ? 500 :
|
|
214
|
+
/semi bold|semibold/.test(weightName?.toLowerCase()) ? 600 :
|
|
215
|
+
/bold/.test(weightName?.toLowerCase()) ? 700 :
|
|
216
|
+
/extra bold|extrabold/.test(weightName?.toLowerCase()) ? 800 :
|
|
217
|
+
/black|ultra/.test(weightName?.toLowerCase()) ? 900 :
|
|
218
|
+
400),
|
|
219
|
+
files : [file],
|
|
220
|
+
fontKit: font,
|
|
221
|
+
scriptFileInput: {[script]:{}},
|
|
222
|
+
};
|
|
223
|
+
fontsObjects[id] = fontObject;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Extract unique subfamily names and prepare for processing
|
|
228
|
+
let uniqueSubfamiles = [...new Set(Object.values(subfamilies))];
|
|
229
|
+
|
|
230
|
+
console.log('Subfamilies : ', subfamilies, uniqueSubfamiles, uniqueSubfamiles.length);
|
|
231
|
+
console.log('fontsObjects : ', fontsObjects);
|
|
232
|
+
|
|
233
|
+
// Process each font object:
|
|
234
|
+
// 1. Upload font files as Sanity assets
|
|
235
|
+
// 2. Create file references linking fonts to assets
|
|
236
|
+
// 3. Generate CSS for web fonts
|
|
237
|
+
for(var i = 0 ; i < Object.keys(fontsObjects).length ; i++ ){
|
|
238
|
+
|
|
239
|
+
let id = Object.keys(fontsObjects)[i];
|
|
240
|
+
let fontObject = fontsObjects[id];
|
|
241
|
+
let files = fontObject.files;
|
|
242
|
+
let newFileInput = fontObject.scriptFileInput[script];
|
|
243
|
+
|
|
244
|
+
console.log(fontObject.title , ' : subfamily : ', subfamilies[id]);
|
|
245
|
+
|
|
246
|
+
// add subfamily to font object if more than one exists
|
|
247
|
+
if(uniqueSubfamiles.length > 1) fontObject.subfamily = subfamilies[id];
|
|
248
|
+
else fontObject.subfamily = '';
|
|
249
|
+
|
|
250
|
+
// add price to font object - set sell = true if there is a price > 0
|
|
251
|
+
fontObject.price = process.env.SANITY_STUDIO_DEFAULT_STYLE_PRICE || 40;
|
|
252
|
+
if(fontObject.price > 0) fontObject.sell = true;
|
|
253
|
+
|
|
254
|
+
// upload files
|
|
255
|
+
for(var j = 0 ; j < files.length ; j++ ){
|
|
256
|
+
let file = files[j];
|
|
257
|
+
let fileType = "";
|
|
258
|
+
if ( file.name.endsWith('.otf') ) fileType = "otf"
|
|
259
|
+
else if ( file.name.endsWith('.ttf') ) fileType = "ttf"
|
|
260
|
+
else if ( file.name.endsWith('.woff') ) fileType = "woff"
|
|
261
|
+
else if ( file.name.endsWith('.woff2') ) fileType = "woff2"
|
|
262
|
+
else if ( file.name.endsWith('.eot') ) fileType = "eot"
|
|
263
|
+
else if ( file.name.endsWith('.svg') ) fileType = "svg"
|
|
264
|
+
|
|
265
|
+
console.log('uploading font file : ', fontObject._id+'.'+fileType);
|
|
266
|
+
const filename = fontObject._id+'-'+script;
|
|
267
|
+
let fontTitle = fontObject.title+' '+script;
|
|
268
|
+
fontTitle = fontTitle.split(' ').map( word => word[0].toUpperCase() + word.slice(1)).join(' ');
|
|
269
|
+
|
|
270
|
+
let baseAsset = await client.assets.upload('file', file, { filename: filename+'.'+fileType })
|
|
271
|
+
.catch( err => {
|
|
272
|
+
console.error('error uploading font: ', fontObject.title);
|
|
273
|
+
setStatus('error uploading font ' + err.message);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// create file ref from font
|
|
277
|
+
newFileInput[fileType] = {
|
|
278
|
+
_type: 'file',
|
|
279
|
+
asset: {
|
|
280
|
+
_ref: baseAsset._id,
|
|
281
|
+
_type: 'reference'
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
console.log('newFileInput', newFileInput);
|
|
286
|
+
|
|
287
|
+
// generate css
|
|
288
|
+
if(file.name.endsWith('.woff2')){
|
|
289
|
+
console.log('generating css file for: ', fontObject.title);
|
|
290
|
+
setStatus('generating css file for: ' + fontObject.title);
|
|
291
|
+
newFileInput = await generateCssFile({
|
|
292
|
+
woff2File: file,
|
|
293
|
+
fileInput: newFileInput,
|
|
294
|
+
// script: script,
|
|
295
|
+
fontName: fontTitle,
|
|
296
|
+
fileName: filename,
|
|
297
|
+
variableFont: fontObject.variableFont,
|
|
298
|
+
weight: fontObject.weight,
|
|
299
|
+
client: client,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
fontObject.scriptFileInput[script] = newFileInput;
|
|
304
|
+
fontsObjects[id] = fontObject;
|
|
305
|
+
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
console.log('creating sanity fonts', fontsObjects);
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
// create (with existing data if exists ) fonts and refs (for typeface)
|
|
313
|
+
for(var i = 0 ; i < Object.keys(fontsObjects).length ; i++ ){
|
|
314
|
+
let fontId = Object.keys(fontsObjects)[i];
|
|
315
|
+
let font = fontsObjects[fontId];
|
|
316
|
+
|
|
317
|
+
// add existing file refs to new file input
|
|
318
|
+
let existingFont = await client.fetch(
|
|
319
|
+
`*[_type == 'font' && _id == $fontId]{
|
|
320
|
+
fileInput,
|
|
321
|
+
description,
|
|
322
|
+
metaData,
|
|
323
|
+
metrics,
|
|
324
|
+
opentypeFeatures,
|
|
325
|
+
characterSet,
|
|
326
|
+
subfamily,
|
|
327
|
+
scriptFileInput,
|
|
328
|
+
}`,
|
|
329
|
+
{ fontId: font._id }
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
existingFont = existingFont[0];
|
|
333
|
+
|
|
334
|
+
let fontResponse;
|
|
335
|
+
let files = font.files;
|
|
336
|
+
let fontKit = font.fontKit;
|
|
337
|
+
delete font.files;
|
|
338
|
+
delete font.fontKit;
|
|
339
|
+
|
|
340
|
+
console.log('creating font : ', font);
|
|
341
|
+
|
|
342
|
+
try{
|
|
343
|
+
if(existingFont && existingFont != null){
|
|
344
|
+
|
|
345
|
+
if(existingFont.scriptFileInput && existingFont.scriptFileInput != null){
|
|
346
|
+
let newFileInput = {...font.scriptFileInput};
|
|
347
|
+
|
|
348
|
+
Object.keys(existingFont.scriptFileInput).forEach( key => {
|
|
349
|
+
if(!newFileInput[key]){
|
|
350
|
+
newFileInput[key] = existingFont.scriptFileInput[key];
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
font.scriptFileInput = newFileInput;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
fontResponse = await client.patch(font._id).set({ scriptFileInput: font.scriptFileInput }).commit()
|
|
357
|
+
|
|
358
|
+
} else{
|
|
359
|
+
fontResponse = await client.createOrReplace({
|
|
360
|
+
_key: nanoid(),
|
|
361
|
+
_id: font._id,
|
|
362
|
+
_type: 'font',
|
|
363
|
+
...font,
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
catch(e){
|
|
368
|
+
console.error('error creating font: ', font.title, font.subfamily);
|
|
369
|
+
failedFiles = [...failedFiles, ...(files.map(file=>{return{name:file.name, fk: fontKit}}))];
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
// Create font refs for typeface
|
|
375
|
+
// add to fontRef array or variableRef array
|
|
376
|
+
|
|
377
|
+
const fontRef = {_key: nanoid(), _type:'reference', _ref: fontResponse._id, _weak: true };
|
|
378
|
+
|
|
379
|
+
console.log('font response : ', fontResponse);
|
|
380
|
+
console.log('existing styles object : ', stylesObject);
|
|
381
|
+
|
|
382
|
+
// add new font refs for typeface
|
|
383
|
+
if(!font.variableFont){
|
|
384
|
+
if(stylesObject.fonts && stylesObject.fonts.length > 0){
|
|
385
|
+
let fontExists = stylesObject.fonts.findIndex( font => font._ref == fontResponse._id);
|
|
386
|
+
let inFontRefs = fontRefs.findIndex( font => font._ref == fontResponse._id);
|
|
387
|
+
if(fontExists == -1 && inFontRefs == -1){
|
|
388
|
+
fontRefs.push(fontRef);
|
|
389
|
+
}
|
|
390
|
+
} else {
|
|
391
|
+
fontRefs.push(fontRef);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// add new font refs for typeface (variable)
|
|
396
|
+
if(font.variableFont){
|
|
397
|
+
if(stylesObject.variableFont && stylesObject.variableFont.length > 0){
|
|
398
|
+
let vfExists = stylesObject.variableFont.findIndex( font => font._ref == fontResponse._id);
|
|
399
|
+
let inVariableRefs = variableRefs.findIndex( font => font._ref == fontResponse._id);
|
|
400
|
+
if( vfExists == -1 && inVariableRefs == -1 && font.variableFont){
|
|
401
|
+
variableRefs.push(fontRef);
|
|
402
|
+
}
|
|
403
|
+
} else {
|
|
404
|
+
variableRefs.push(fontRef);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
console.log(fontResponse._id, ' created!');
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Update Sanity typeface document with new font references
|
|
412
|
+
console.log('updating styles refs (fonts, variable fonts, subfamilies) ', fontRefs, variableRefs, subfamilies, uniqueSubfamiles)
|
|
413
|
+
setStatus('Updating font references...');
|
|
414
|
+
|
|
415
|
+
let newStylesObject = stylesObject.fonts ?
|
|
416
|
+
{ ...stylesObject, fonts : [...stylesObject.fonts, ...fontRefs] }
|
|
417
|
+
:
|
|
418
|
+
{ ...stylesObject, fonts : [...fontRefs] };
|
|
419
|
+
|
|
420
|
+
if(uniqueSubfamiles.length > 1){
|
|
421
|
+
newStylesObject.subfamilies = uniqueSubfamiles;
|
|
422
|
+
}
|
|
423
|
+
else{
|
|
424
|
+
newStylesObject.subfamilies = [];
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
newStylesObject.variableFont = stylesObject?.variableFont ? [...stylesObject?.variableFont, ...variableRefs] : [...variableRefs];
|
|
428
|
+
|
|
429
|
+
let patch = {styles:newStylesObject};
|
|
430
|
+
|
|
431
|
+
subfamiliesArray = subfamiliesArray ? subfamiliesArray : [];
|
|
432
|
+
|
|
433
|
+
console.log('new styles obj : ', newStylesObject);
|
|
434
|
+
console.log('existing subfamily list : ', subfamiliesArray);
|
|
435
|
+
console.log('unique subfamilies ', uniqueSubfamiles);
|
|
436
|
+
|
|
437
|
+
subfamiliesArray = [...subfamiliesArray, ...uniqueSubfamiles].filter((sf, index, self) => {
|
|
438
|
+
return self.indexOf(sf) === index;
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
patch.styles.subfamilies = subfamiliesArray;
|
|
442
|
+
|
|
443
|
+
console.log('doc_id : ',doc_id);
|
|
444
|
+
console.log('typeface patch : ',patch);
|
|
445
|
+
|
|
446
|
+
let includedScripts = [ script, ...scripts].filter((lang, index, self) => {
|
|
447
|
+
return self.indexOf(lang) === index;
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
patch.scripts = includedScripts;
|
|
451
|
+
|
|
452
|
+
console.log('included scripts : ', includedScripts);
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
if( doc_id.startsWith('drafts.')){
|
|
456
|
+
await client.patch(doc_id).set(patch).commit()
|
|
457
|
+
.catch(err => {
|
|
458
|
+
console.error('error patching styles: ', err.message);
|
|
459
|
+
setStatus('error patching styles '+ err.message);
|
|
460
|
+
});
|
|
461
|
+
doc_id = doc_id.replace('drafts.','');
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
await client.patch(doc_id).set(patch).commit()
|
|
465
|
+
.catch(err => {
|
|
466
|
+
console.error('error patching styles: ', err.message);
|
|
467
|
+
setStatus('error patching styles');
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
console.log('success');
|
|
471
|
+
|
|
472
|
+
if(failedFiles.length > 0){
|
|
473
|
+
console.log('failed files : ', failedFiles);
|
|
474
|
+
const names = failedFiles.map( file => file.name);
|
|
475
|
+
console.log('names : ', failedFiles.map( file => file?.fk?.name?.records));
|
|
476
|
+
setStatus('fonts uploaded with errors. Failed files : '+ names.join(', '));
|
|
477
|
+
} else {
|
|
478
|
+
setStatus('fonts uploaded!');
|
|
479
|
+
}
|
|
480
|
+
setStatus('fonts uploaded!');
|
|
481
|
+
} catch(e){
|
|
482
|
+
console.error(e);
|
|
483
|
+
setStatus('error uploading font '+e.message);
|
|
484
|
+
}
|
|
485
|
+
setReady(true);
|
|
486
|
+
|
|
487
|
+
},[title, slug, doc_id]);
|
|
488
|
+
|
|
489
|
+
// Render component UI
|
|
490
|
+
return (
|
|
491
|
+
<Stack>
|
|
492
|
+
{/* Display status message when processing */}
|
|
493
|
+
{!ready &&
|
|
494
|
+
<Text><br/>{status}<br/><br/></Text>
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
{/* Display upload interface when ready */}
|
|
498
|
+
{ready &&
|
|
499
|
+
<Stack>
|
|
500
|
+
<Grid columns={!!(selectedScript && selectedScript !== "") ? 2 : 1} gap={2}>
|
|
501
|
+
{/* Script selection dropdown */}
|
|
502
|
+
<Select
|
|
503
|
+
id="menu-button-example"
|
|
504
|
+
onChange={(e)=>setSelectedScript(e.target.value)}
|
|
505
|
+
>
|
|
506
|
+
<option key={'script-none'} value={""}> </option>
|
|
507
|
+
{SCRIPTS.map((script,i) =>
|
|
508
|
+
<option key={'script-'+i} value={script}>
|
|
509
|
+
{script[0]?.toUpperCase()+script.slice(1)}
|
|
510
|
+
</option>
|
|
511
|
+
)}
|
|
512
|
+
</Select>
|
|
513
|
+
|
|
514
|
+
{/* File upload button - only shown when script is selected */}
|
|
515
|
+
{!!(selectedScript && selectedScript !== "") &&
|
|
516
|
+
<>
|
|
517
|
+
<label htmlFor="upload-scripts-file">
|
|
518
|
+
<Button
|
|
519
|
+
style={{pointerEvents: "none"}}
|
|
520
|
+
text="Upload (ttf/otf/woff/woff2/etc..)"
|
|
521
|
+
/>
|
|
522
|
+
</label>
|
|
523
|
+
<input
|
|
524
|
+
ref={ref}
|
|
525
|
+
name="upload-scripts-file"
|
|
526
|
+
id="upload-scripts-file"
|
|
527
|
+
type="file"
|
|
528
|
+
multiple
|
|
529
|
+
hidden
|
|
530
|
+
onChange={(event) => handleUpload(event, selectedScript)}
|
|
531
|
+
/>
|
|
532
|
+
</>
|
|
533
|
+
}
|
|
534
|
+
</Grid>
|
|
535
|
+
</Stack>
|
|
536
|
+
}
|
|
537
|
+
</Stack>
|
|
538
|
+
)
|
|
539
|
+
};
|