@imjp/writenex-astro 1.3.6 → 1.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/dist/{chunk-NSW7AIVF.js → chunk-EEUN46Q2.js} +3 -3
- package/dist/chunk-HNS5YKP3.js +241 -0
- package/dist/chunk-HNS5YKP3.js.map +1 -0
- package/dist/{chunk-YBCPOLMY.js → chunk-P5KMSHFP.js} +2 -1
- package/dist/{chunk-YBCPOLMY.js.map → chunk-P5KMSHFP.js.map} +1 -1
- package/dist/{chunk-JMNCPNQX.js → chunk-XVQNYPOI.js} +65 -3
- package/dist/chunk-XVQNYPOI.js.map +1 -0
- package/dist/chunk-YRSIZLHE.js +1 -0
- package/dist/{chunk-N37EPLKG.js → chunk-ZWUGHWHD.js} +79 -10
- package/dist/chunk-ZWUGHWHD.js.map +1 -0
- package/dist/client/index.css +1 -1
- package/dist/client/index.css.map +1 -1
- package/dist/client/index.js +114 -114
- package/dist/client/index.js.map +1 -1
- package/dist/config/index.d.ts +3 -2
- package/dist/config/index.js +11 -3
- package/dist/{config-CliL0CoN.d.ts → config-B7t8CjL1.d.ts} +38 -60
- package/dist/{content-TuL3GT66.d.ts → content-CwcgR8P6.d.ts} +1 -1
- package/dist/discovery/index.d.ts +2 -2
- package/dist/discovery/index.js +2 -2
- package/dist/fields/index.d.ts +16 -0
- package/dist/fields/index.js +13 -0
- package/dist/fields-DUSm13nm.d.ts +223 -0
- package/dist/filesystem/index.d.ts +2 -2
- package/dist/filesystem/index.js +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.js +13 -5
- package/dist/index.js.map +1 -1
- package/dist/{loader-53VVP2IN.js → loader-B5WZCVBC.js} +3 -3
- package/dist/loader-B5WZCVBC.js.map +1 -0
- package/dist/{schema-DDJyoVkj.d.ts → schema-nLMfZ9-o.d.ts} +79 -53
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +3 -3
- package/package.json +5 -1
- package/src/client/components/FrontmatterForm/FrontmatterForm.css +104 -0
- package/src/client/components/FrontmatterForm/FrontmatterForm.tsx +452 -26
- package/src/config/defaults.ts +1 -0
- package/src/config/index.ts +3 -0
- package/src/config/schema.ts +114 -73
- package/src/discovery/schema.ts +120 -1
- package/src/fields/collection.ts +67 -0
- package/src/fields/fields.ts +150 -0
- package/src/fields/index.ts +42 -0
- package/src/fields/resolve.ts +149 -0
- package/src/fields/types.ts +179 -0
- package/src/index.ts +3 -0
- package/src/types/config.ts +86 -63
- package/src/types/index.ts +3 -0
- package/dist/chunk-JMNCPNQX.js.map +0 -1
- package/dist/chunk-KIKIPIFA.js +0 -1
- package/dist/chunk-N37EPLKG.js.map +0 -1
- package/dist/client/index.d.ts +0 -19
- /package/dist/{chunk-NSW7AIVF.js.map → chunk-EEUN46Q2.js.map} +0 -0
- /package/dist/{chunk-KIKIPIFA.js.map → chunk-YRSIZLHE.js.map} +0 -0
- /package/dist/{loader-53VVP2IN.js.map → fields/index.js.map} +0 -0
|
@@ -413,7 +413,6 @@ function DynamicField({
|
|
|
413
413
|
}): React.ReactElement {
|
|
414
414
|
const fieldId = `fm-${name}`;
|
|
415
415
|
const label = formatFieldLabel(name);
|
|
416
|
-
const enumOptions = parseEnumFromDescription(field.description);
|
|
417
416
|
|
|
418
417
|
switch (field.type) {
|
|
419
418
|
case "boolean":
|
|
@@ -428,6 +427,7 @@ function DynamicField({
|
|
|
428
427
|
);
|
|
429
428
|
|
|
430
429
|
case "number":
|
|
430
|
+
case "integer":
|
|
431
431
|
return (
|
|
432
432
|
<NumberField
|
|
433
433
|
id={fieldId}
|
|
@@ -436,6 +436,7 @@ function DynamicField({
|
|
|
436
436
|
onChange={onChange}
|
|
437
437
|
disabled={disabled}
|
|
438
438
|
required={field.required}
|
|
439
|
+
step={field.type === "integer" ? 1 : "any"}
|
|
439
440
|
/>
|
|
440
441
|
);
|
|
441
442
|
|
|
@@ -451,6 +452,18 @@ function DynamicField({
|
|
|
451
452
|
/>
|
|
452
453
|
);
|
|
453
454
|
|
|
455
|
+
case "datetime":
|
|
456
|
+
return (
|
|
457
|
+
<DatetimeField
|
|
458
|
+
id={fieldId}
|
|
459
|
+
label={label}
|
|
460
|
+
value={value}
|
|
461
|
+
onChange={onChange}
|
|
462
|
+
disabled={disabled}
|
|
463
|
+
required={field.required}
|
|
464
|
+
/>
|
|
465
|
+
);
|
|
466
|
+
|
|
454
467
|
case "array":
|
|
455
468
|
return (
|
|
456
469
|
<ArrayField
|
|
@@ -464,6 +477,32 @@ function DynamicField({
|
|
|
464
477
|
/>
|
|
465
478
|
);
|
|
466
479
|
|
|
480
|
+
case "multiselect":
|
|
481
|
+
return (
|
|
482
|
+
<MultiselectField
|
|
483
|
+
id={fieldId}
|
|
484
|
+
label={label}
|
|
485
|
+
value={value as string[] | undefined}
|
|
486
|
+
options={field.options || []}
|
|
487
|
+
onChange={onChange}
|
|
488
|
+
disabled={disabled}
|
|
489
|
+
required={field.required}
|
|
490
|
+
/>
|
|
491
|
+
);
|
|
492
|
+
|
|
493
|
+
case "select":
|
|
494
|
+
return (
|
|
495
|
+
<SelectField
|
|
496
|
+
id={fieldId}
|
|
497
|
+
label={label}
|
|
498
|
+
value={String(value ?? "")}
|
|
499
|
+
options={field.options || []}
|
|
500
|
+
onChange={onChange}
|
|
501
|
+
disabled={disabled}
|
|
502
|
+
required={field.required}
|
|
503
|
+
/>
|
|
504
|
+
);
|
|
505
|
+
|
|
467
506
|
case "image":
|
|
468
507
|
return (
|
|
469
508
|
<ImageField
|
|
@@ -481,24 +520,95 @@ function DynamicField({
|
|
|
481
520
|
/>
|
|
482
521
|
);
|
|
483
522
|
|
|
523
|
+
case "file":
|
|
524
|
+
return (
|
|
525
|
+
<FileField
|
|
526
|
+
id={fieldId}
|
|
527
|
+
label={label}
|
|
528
|
+
value={value as string | undefined}
|
|
529
|
+
onChange={onChange}
|
|
530
|
+
disabled={disabled}
|
|
531
|
+
required={field.required}
|
|
532
|
+
/>
|
|
533
|
+
);
|
|
534
|
+
|
|
535
|
+
case "slug":
|
|
536
|
+
return (
|
|
537
|
+
<SlugField
|
|
538
|
+
id={fieldId}
|
|
539
|
+
label={label}
|
|
540
|
+
value={String(value ?? "")}
|
|
541
|
+
onChange={onChange}
|
|
542
|
+
disabled={disabled}
|
|
543
|
+
required={field.required}
|
|
544
|
+
/>
|
|
545
|
+
);
|
|
546
|
+
|
|
547
|
+
case "url":
|
|
548
|
+
return (
|
|
549
|
+
<UrlField
|
|
550
|
+
id={fieldId}
|
|
551
|
+
label={label}
|
|
552
|
+
value={String(value ?? "")}
|
|
553
|
+
onChange={onChange}
|
|
554
|
+
disabled={disabled}
|
|
555
|
+
required={field.required}
|
|
556
|
+
/>
|
|
557
|
+
);
|
|
558
|
+
|
|
559
|
+
case "object":
|
|
560
|
+
return (
|
|
561
|
+
<ObjectField
|
|
562
|
+
id={fieldId}
|
|
563
|
+
label={label}
|
|
564
|
+
value={value as Record<string, unknown> | undefined}
|
|
565
|
+
fields={field.fields}
|
|
566
|
+
onChange={onChange}
|
|
567
|
+
disabled={disabled}
|
|
568
|
+
required={field.required}
|
|
569
|
+
/>
|
|
570
|
+
);
|
|
571
|
+
|
|
572
|
+
case "relationship":
|
|
573
|
+
return (
|
|
574
|
+
<RelationshipField
|
|
575
|
+
id={fieldId}
|
|
576
|
+
label={label}
|
|
577
|
+
value={value as string | string[] | undefined}
|
|
578
|
+
collection={field.collection}
|
|
579
|
+
onChange={onChange}
|
|
580
|
+
disabled={disabled}
|
|
581
|
+
required={field.required}
|
|
582
|
+
/>
|
|
583
|
+
);
|
|
584
|
+
|
|
585
|
+
case "markdoc":
|
|
586
|
+
case "mdx":
|
|
587
|
+
return (
|
|
588
|
+
<RichTextField
|
|
589
|
+
id={fieldId}
|
|
590
|
+
label={label}
|
|
591
|
+
value={String(value ?? "")}
|
|
592
|
+
onChange={onChange}
|
|
593
|
+
disabled={disabled}
|
|
594
|
+
required={field.required}
|
|
595
|
+
/>
|
|
596
|
+
);
|
|
597
|
+
|
|
598
|
+
case "ignored":
|
|
599
|
+
case "empty":
|
|
600
|
+
case "empty-content":
|
|
601
|
+
case "empty-document":
|
|
602
|
+
case "child":
|
|
603
|
+
return <></>;
|
|
604
|
+
|
|
484
605
|
case "string":
|
|
485
606
|
default:
|
|
486
|
-
if (enumOptions.length > 0) {
|
|
487
|
-
return (
|
|
488
|
-
<SelectField
|
|
489
|
-
id={fieldId}
|
|
490
|
-
label={label}
|
|
491
|
-
value={String(value ?? "")}
|
|
492
|
-
options={enumOptions}
|
|
493
|
-
onChange={onChange}
|
|
494
|
-
disabled={disabled}
|
|
495
|
-
required={field.required}
|
|
496
|
-
/>
|
|
497
|
-
);
|
|
498
|
-
}
|
|
499
|
-
|
|
500
607
|
const isMultiline =
|
|
501
|
-
|
|
608
|
+
field.multiline ||
|
|
609
|
+
name === "description" ||
|
|
610
|
+
name === "excerpt" ||
|
|
611
|
+
name === "summary";
|
|
502
612
|
|
|
503
613
|
return (
|
|
504
614
|
<StringField
|
|
@@ -574,9 +684,11 @@ function NumberField({
|
|
|
574
684
|
onChange,
|
|
575
685
|
disabled,
|
|
576
686
|
required,
|
|
687
|
+
step,
|
|
577
688
|
}: BaseFieldProps & {
|
|
578
689
|
value: number | undefined;
|
|
579
690
|
onChange: (value: number | undefined) => void;
|
|
691
|
+
step?: string | number;
|
|
580
692
|
}): React.ReactElement {
|
|
581
693
|
return (
|
|
582
694
|
<div className="wn-frontmatter-field">
|
|
@@ -588,6 +700,7 @@ function NumberField({
|
|
|
588
700
|
id={id}
|
|
589
701
|
type="number"
|
|
590
702
|
value={value ?? ""}
|
|
703
|
+
step={step ?? "any"}
|
|
591
704
|
onChange={(e) => {
|
|
592
705
|
const val = e.target.value;
|
|
593
706
|
onChange(val === "" ? undefined : Number(val));
|
|
@@ -869,6 +982,329 @@ function ImageField({
|
|
|
869
982
|
);
|
|
870
983
|
}
|
|
871
984
|
|
|
985
|
+
function DatetimeField({
|
|
986
|
+
id,
|
|
987
|
+
label,
|
|
988
|
+
value,
|
|
989
|
+
onChange,
|
|
990
|
+
disabled,
|
|
991
|
+
required,
|
|
992
|
+
}: BaseFieldProps & {
|
|
993
|
+
value: unknown;
|
|
994
|
+
onChange: (value: string | undefined) => void;
|
|
995
|
+
}): React.ReactElement {
|
|
996
|
+
const datetimeValue = formatDatetimeForInput(value);
|
|
997
|
+
|
|
998
|
+
return (
|
|
999
|
+
<div className="wn-frontmatter-field">
|
|
1000
|
+
<label htmlFor={id} className="wn-frontmatter-label">
|
|
1001
|
+
{label}
|
|
1002
|
+
{required && <span className="wn-frontmatter-required">*</span>}
|
|
1003
|
+
</label>
|
|
1004
|
+
<input
|
|
1005
|
+
id={id}
|
|
1006
|
+
type="datetime-local"
|
|
1007
|
+
value={datetimeValue}
|
|
1008
|
+
onChange={(e) => onChange(e.target.value || undefined)}
|
|
1009
|
+
disabled={disabled}
|
|
1010
|
+
className="wn-frontmatter-input"
|
|
1011
|
+
/>
|
|
1012
|
+
</div>
|
|
1013
|
+
);
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
function MultiselectField({
|
|
1017
|
+
id,
|
|
1018
|
+
label,
|
|
1019
|
+
value,
|
|
1020
|
+
options,
|
|
1021
|
+
onChange,
|
|
1022
|
+
disabled,
|
|
1023
|
+
required,
|
|
1024
|
+
}: BaseFieldProps & {
|
|
1025
|
+
value: string[] | undefined;
|
|
1026
|
+
options: string[];
|
|
1027
|
+
onChange: (value: string[]) => void;
|
|
1028
|
+
}): React.ReactElement {
|
|
1029
|
+
const selectedValues = value || [];
|
|
1030
|
+
|
|
1031
|
+
const handleToggle = (option: string) => {
|
|
1032
|
+
if (selectedValues.includes(option)) {
|
|
1033
|
+
onChange(selectedValues.filter((v) => v !== option));
|
|
1034
|
+
} else {
|
|
1035
|
+
onChange([...selectedValues, option]);
|
|
1036
|
+
}
|
|
1037
|
+
};
|
|
1038
|
+
|
|
1039
|
+
return (
|
|
1040
|
+
<div className="wn-frontmatter-field">
|
|
1041
|
+
<label className="wn-frontmatter-label">
|
|
1042
|
+
{label}
|
|
1043
|
+
{required && <span className="wn-frontmatter-required">*</span>}
|
|
1044
|
+
</label>
|
|
1045
|
+
<div className="wn-frontmatter-checkbox-group">
|
|
1046
|
+
{options.map((option) => (
|
|
1047
|
+
<label key={option} className="wn-frontmatter-checkbox-label">
|
|
1048
|
+
<input
|
|
1049
|
+
type="checkbox"
|
|
1050
|
+
checked={selectedValues.includes(option)}
|
|
1051
|
+
onChange={() => handleToggle(option)}
|
|
1052
|
+
disabled={disabled}
|
|
1053
|
+
className="wn-frontmatter-checkbox"
|
|
1054
|
+
/>
|
|
1055
|
+
<span>{option}</span>
|
|
1056
|
+
</label>
|
|
1057
|
+
))}
|
|
1058
|
+
</div>
|
|
1059
|
+
</div>
|
|
1060
|
+
);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
function FileField({
|
|
1064
|
+
id,
|
|
1065
|
+
label,
|
|
1066
|
+
value,
|
|
1067
|
+
onChange,
|
|
1068
|
+
disabled,
|
|
1069
|
+
required,
|
|
1070
|
+
}: BaseFieldProps & {
|
|
1071
|
+
value: string | undefined;
|
|
1072
|
+
onChange: (value: string | undefined) => void;
|
|
1073
|
+
}): React.ReactElement {
|
|
1074
|
+
return (
|
|
1075
|
+
<div className="wn-frontmatter-field">
|
|
1076
|
+
<label htmlFor={id} className="wn-frontmatter-label">
|
|
1077
|
+
{label}
|
|
1078
|
+
{required && <span className="wn-frontmatter-required">*</span>}
|
|
1079
|
+
</label>
|
|
1080
|
+
<input
|
|
1081
|
+
id={id}
|
|
1082
|
+
type="text"
|
|
1083
|
+
value={value ?? ""}
|
|
1084
|
+
onChange={(e) => onChange(e.target.value || undefined)}
|
|
1085
|
+
disabled={disabled}
|
|
1086
|
+
placeholder="./files/document.pdf"
|
|
1087
|
+
className="wn-frontmatter-input"
|
|
1088
|
+
/>
|
|
1089
|
+
</div>
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
function SlugField({
|
|
1094
|
+
id,
|
|
1095
|
+
label,
|
|
1096
|
+
value,
|
|
1097
|
+
onChange,
|
|
1098
|
+
disabled,
|
|
1099
|
+
required,
|
|
1100
|
+
}: BaseFieldProps & {
|
|
1101
|
+
value: string;
|
|
1102
|
+
onChange: (value: string) => void;
|
|
1103
|
+
}): React.ReactElement {
|
|
1104
|
+
return (
|
|
1105
|
+
<div className="wn-frontmatter-field">
|
|
1106
|
+
<label htmlFor={id} className="wn-frontmatter-label">
|
|
1107
|
+
{label}
|
|
1108
|
+
{required && <span className="wn-frontmatter-required">*</span>}
|
|
1109
|
+
</label>
|
|
1110
|
+
<input
|
|
1111
|
+
id={id}
|
|
1112
|
+
type="text"
|
|
1113
|
+
value={value}
|
|
1114
|
+
onChange={(e) => onChange(e.target.value)}
|
|
1115
|
+
disabled={disabled}
|
|
1116
|
+
placeholder="enter-slug-here"
|
|
1117
|
+
className="wn-frontmatter-input"
|
|
1118
|
+
/>
|
|
1119
|
+
</div>
|
|
1120
|
+
);
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
function UrlField({
|
|
1124
|
+
id,
|
|
1125
|
+
label,
|
|
1126
|
+
value,
|
|
1127
|
+
onChange,
|
|
1128
|
+
disabled,
|
|
1129
|
+
required,
|
|
1130
|
+
}: BaseFieldProps & {
|
|
1131
|
+
value: string;
|
|
1132
|
+
onChange: (value: string) => void;
|
|
1133
|
+
}): React.ReactElement {
|
|
1134
|
+
return (
|
|
1135
|
+
<div className="wn-frontmatter-field">
|
|
1136
|
+
<label htmlFor={id} className="wn-frontmatter-label">
|
|
1137
|
+
{label}
|
|
1138
|
+
{required && <span className="wn-frontmatter-required">*</span>}
|
|
1139
|
+
</label>
|
|
1140
|
+
<input
|
|
1141
|
+
id={id}
|
|
1142
|
+
type="url"
|
|
1143
|
+
value={value}
|
|
1144
|
+
onChange={(e) => onChange(e.target.value)}
|
|
1145
|
+
disabled={disabled}
|
|
1146
|
+
placeholder="https://example.com"
|
|
1147
|
+
className="wn-frontmatter-input"
|
|
1148
|
+
/>
|
|
1149
|
+
</div>
|
|
1150
|
+
);
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
function ObjectField({
|
|
1154
|
+
id,
|
|
1155
|
+
label,
|
|
1156
|
+
value,
|
|
1157
|
+
fields,
|
|
1158
|
+
onChange,
|
|
1159
|
+
disabled,
|
|
1160
|
+
required,
|
|
1161
|
+
}: BaseFieldProps & {
|
|
1162
|
+
value: Record<string, unknown> | undefined;
|
|
1163
|
+
fields?: Record<string, SchemaField>;
|
|
1164
|
+
onChange: (value: Record<string, unknown>) => void;
|
|
1165
|
+
}): React.ReactElement {
|
|
1166
|
+
const objectValue = value || {};
|
|
1167
|
+
const [isExpanded, setIsExpanded] = useState(true);
|
|
1168
|
+
|
|
1169
|
+
const handleFieldChange = (fieldName: string, fieldValue: unknown) => {
|
|
1170
|
+
onChange({ ...objectValue, [fieldName]: fieldValue });
|
|
1171
|
+
};
|
|
1172
|
+
|
|
1173
|
+
if (!fields || Object.keys(fields).length === 0) {
|
|
1174
|
+
return (
|
|
1175
|
+
<div className="wn-frontmatter-field">
|
|
1176
|
+
<label className="wn-frontmatter-label">
|
|
1177
|
+
{label}
|
|
1178
|
+
{required && <span className="wn-frontmatter-required">*</span>}
|
|
1179
|
+
</label>
|
|
1180
|
+
<span className="wn-frontmatter-hint">No sub-fields defined</span>
|
|
1181
|
+
</div>
|
|
1182
|
+
);
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
return (
|
|
1186
|
+
<div className="wn-frontmatter-field-group">
|
|
1187
|
+
<button
|
|
1188
|
+
type="button"
|
|
1189
|
+
className="wn-frontmatter-group-toggle"
|
|
1190
|
+
onClick={() => setIsExpanded(!isExpanded)}
|
|
1191
|
+
disabled={disabled}
|
|
1192
|
+
>
|
|
1193
|
+
<span className="wn-frontmatter-label">
|
|
1194
|
+
{label}
|
|
1195
|
+
{required && <span className="wn-frontmatter-required">*</span>}
|
|
1196
|
+
</span>
|
|
1197
|
+
<span>{isExpanded ? "[-]" : "[+]"}</span>
|
|
1198
|
+
</button>
|
|
1199
|
+
{isExpanded && (
|
|
1200
|
+
<div className="wn-frontmatter-group-content">
|
|
1201
|
+
{Object.entries(fields).map(([fieldName, fieldDef]) => (
|
|
1202
|
+
<DynamicField
|
|
1203
|
+
key={fieldName}
|
|
1204
|
+
name={fieldName}
|
|
1205
|
+
field={fieldDef}
|
|
1206
|
+
value={objectValue[fieldName]}
|
|
1207
|
+
onChange={(fieldValue) =>
|
|
1208
|
+
handleFieldChange(fieldName, fieldValue)
|
|
1209
|
+
}
|
|
1210
|
+
disabled={disabled}
|
|
1211
|
+
/>
|
|
1212
|
+
))}
|
|
1213
|
+
</div>
|
|
1214
|
+
)}
|
|
1215
|
+
</div>
|
|
1216
|
+
);
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
function RelationshipField({
|
|
1220
|
+
id,
|
|
1221
|
+
label,
|
|
1222
|
+
value,
|
|
1223
|
+
collection,
|
|
1224
|
+
onChange,
|
|
1225
|
+
disabled,
|
|
1226
|
+
required,
|
|
1227
|
+
}: BaseFieldProps & {
|
|
1228
|
+
value: string | string[] | undefined;
|
|
1229
|
+
collection?: string;
|
|
1230
|
+
onChange: (value: string | string[]) => void;
|
|
1231
|
+
}): React.ReactElement {
|
|
1232
|
+
return (
|
|
1233
|
+
<div className="wn-frontmatter-field">
|
|
1234
|
+
<label htmlFor={id} className="wn-frontmatter-label">
|
|
1235
|
+
{label}
|
|
1236
|
+
{required && <span className="wn-frontmatter-required">*</span>}
|
|
1237
|
+
</label>
|
|
1238
|
+
<input
|
|
1239
|
+
id={id}
|
|
1240
|
+
type="text"
|
|
1241
|
+
value={Array.isArray(value) ? value.join(", ") : (value ?? "")}
|
|
1242
|
+
onChange={(e) => onChange(e.target.value)}
|
|
1243
|
+
disabled={disabled}
|
|
1244
|
+
placeholder={collection ? `Reference to ${collection}` : "Reference"}
|
|
1245
|
+
className="wn-frontmatter-input"
|
|
1246
|
+
/>
|
|
1247
|
+
{collection && (
|
|
1248
|
+
<span className="wn-frontmatter-hint">References: {collection}</span>
|
|
1249
|
+
)}
|
|
1250
|
+
</div>
|
|
1251
|
+
);
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
function RichTextField({
|
|
1255
|
+
id,
|
|
1256
|
+
label,
|
|
1257
|
+
value,
|
|
1258
|
+
onChange,
|
|
1259
|
+
disabled,
|
|
1260
|
+
required,
|
|
1261
|
+
}: BaseFieldProps & {
|
|
1262
|
+
value: string;
|
|
1263
|
+
onChange: (value: string) => void;
|
|
1264
|
+
}): React.ReactElement {
|
|
1265
|
+
return (
|
|
1266
|
+
<div className="wn-frontmatter-field wn-frontmatter-field--full">
|
|
1267
|
+
<label htmlFor={id} className="wn-frontmatter-label">
|
|
1268
|
+
{label}
|
|
1269
|
+
{required && <span className="wn-frontmatter-required">*</span>}
|
|
1270
|
+
</label>
|
|
1271
|
+
<textarea
|
|
1272
|
+
id={id}
|
|
1273
|
+
value={value}
|
|
1274
|
+
onChange={(e) => onChange(e.target.value)}
|
|
1275
|
+
disabled={disabled}
|
|
1276
|
+
placeholder={`Enter ${label.toLowerCase()}`}
|
|
1277
|
+
rows={8}
|
|
1278
|
+
className="wn-frontmatter-textarea wn-frontmatter-textarea--rich"
|
|
1279
|
+
/>
|
|
1280
|
+
</div>
|
|
1281
|
+
);
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
function formatDatetimeForInput(value: unknown): string {
|
|
1285
|
+
if (!value) return "";
|
|
1286
|
+
|
|
1287
|
+
if (typeof value === "string") {
|
|
1288
|
+
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(value)) {
|
|
1289
|
+
return value.slice(0, 16);
|
|
1290
|
+
}
|
|
1291
|
+
try {
|
|
1292
|
+
const date = new Date(value);
|
|
1293
|
+
if (!isNaN(date.getTime())) {
|
|
1294
|
+
return date.toISOString().slice(0, 16) ?? "";
|
|
1295
|
+
}
|
|
1296
|
+
} catch {
|
|
1297
|
+
return "";
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
if (value instanceof Date) {
|
|
1302
|
+
return value.toISOString().slice(0, 16) ?? "";
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
return "";
|
|
1306
|
+
}
|
|
1307
|
+
|
|
872
1308
|
// Utilities
|
|
873
1309
|
|
|
874
1310
|
function formatFieldLabel(name: string): string {
|
|
@@ -902,13 +1338,3 @@ function formatDateForInput(value: unknown): string {
|
|
|
902
1338
|
|
|
903
1339
|
return "";
|
|
904
1340
|
}
|
|
905
|
-
|
|
906
|
-
function parseEnumFromDescription(description?: string): string[] {
|
|
907
|
-
if (!description) return [];
|
|
908
|
-
const match = description.match(/^Options:\s*(.+)$/i);
|
|
909
|
-
if (!match || !match[1]) return [];
|
|
910
|
-
return match[1]
|
|
911
|
-
.split(",")
|
|
912
|
-
.map((s) => s.trim())
|
|
913
|
-
.filter(Boolean);
|
|
914
|
-
}
|
package/src/config/defaults.ts
CHANGED
|
@@ -93,6 +93,7 @@ export function applyConfigDefaults(
|
|
|
93
93
|
): Required<WritenexConfig> {
|
|
94
94
|
return {
|
|
95
95
|
collections: (config.collections ?? []).map(applyCollectionDefaults),
|
|
96
|
+
singletons: (config.singletons ?? []).map(applyCollectionDefaults),
|
|
96
97
|
images: config.images
|
|
97
98
|
? { ...DEFAULT_IMAGE_CONFIG, ...config.images }
|
|
98
99
|
: DEFAULT_IMAGE_CONFIG,
|
package/src/config/index.ts
CHANGED
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
* @module @writenex/astro/config
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
export type { FieldDefinition, FieldKind, ValidationOptions } from "@/fields";
|
|
11
|
+
// Fields API
|
|
12
|
+
export { collection, fields, singleton } from "@/fields";
|
|
10
13
|
// Defaults and constants
|
|
11
14
|
export {
|
|
12
15
|
applyCollectionDefaults,
|