@liiift-studio/sanity-font-manager 2.2.0 → 2.3.1
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 +73 -0
- package/dist/index.js +393 -1
- package/dist/index.mjs +390 -1
- package/package.json +1 -1
- package/src/components/KeyValueInput.jsx +95 -0
- package/src/components/KeyValueReferenceInput.jsx +254 -0
- package/src/components/SingleUploaderTool.jsx +1 -1
- package/src/components/VariableInstanceReferencesInput.jsx +190 -0
- package/src/index.js +3 -0
package/dist/index.mjs
CHANGED
|
@@ -2835,7 +2835,7 @@ var SingleUploaderTool = (props) => {
|
|
|
2835
2835
|
}
|
|
2836
2836
|
)
|
|
2837
2837
|
}
|
|
2838
|
-
), renderFontSection("ttf"), status === "ready" && (fileInput == null ? void 0 : fileInput.ttf) && /* @__PURE__ */ React7.createElement(Grid3, { columns: [2], gap: 2 }, /* @__PURE__ */ React7.createElement(
|
|
2838
|
+
), renderFontSection("ttf"), status === "ready" && (fileInput == null ? void 0 : fileInput.ttf) && /* @__PURE__ */ React7.createElement(Grid3, { columns: [1, 2], gap: 2 }, /* @__PURE__ */ React7.createElement(
|
|
2839
2839
|
Button5,
|
|
2840
2840
|
{
|
|
2841
2841
|
mode: "ghost",
|
|
@@ -3499,6 +3499,392 @@ var UploadButton = forwardRef(({ handleUpload }, ref) => {
|
|
|
3499
3499
|
UploadButton.displayName = "UploadButton";
|
|
3500
3500
|
var UploadButton_default = UploadButton;
|
|
3501
3501
|
|
|
3502
|
+
// src/components/KeyValueInput.jsx
|
|
3503
|
+
import React12, { useState as useState8, useCallback as useCallback7 } from "react";
|
|
3504
|
+
import { Button as Button9, Grid as Grid5, Stack as Stack8, TextInput as TextInput2 } from "@sanity/ui";
|
|
3505
|
+
import { AddIcon, ArrowDownIcon, ArrowUpIcon, TrashIcon as TrashIcon3 } from "@sanity/icons";
|
|
3506
|
+
import { set as set4 } from "sanity";
|
|
3507
|
+
function KeyValueInput({ value = [], onChange }) {
|
|
3508
|
+
const [pairs, setPairs] = useState8(value);
|
|
3509
|
+
const handlePairChange = useCallback7((index, field, fieldValue) => {
|
|
3510
|
+
const updatedPairs = pairs.map((pair, idx) => idx === index ? { ...pair, [field]: fieldValue } : pair);
|
|
3511
|
+
setPairs(updatedPairs);
|
|
3512
|
+
onChange(set4(updatedPairs));
|
|
3513
|
+
}, [pairs, onChange]);
|
|
3514
|
+
const handleAddPair = useCallback7(() => {
|
|
3515
|
+
const newPair = { key: "", value: "", _key: Math.random().toString(36).substr(2, 9) };
|
|
3516
|
+
const updatedPairs = [...pairs, newPair];
|
|
3517
|
+
setPairs(updatedPairs);
|
|
3518
|
+
onChange(set4(updatedPairs));
|
|
3519
|
+
}, [pairs, onChange]);
|
|
3520
|
+
const handleRemovePair = useCallback7((index) => {
|
|
3521
|
+
const updatedPairs = pairs.filter((_, idx) => idx !== index);
|
|
3522
|
+
setPairs(updatedPairs);
|
|
3523
|
+
onChange(set4(updatedPairs));
|
|
3524
|
+
}, [pairs, onChange]);
|
|
3525
|
+
const handleMoveUp = useCallback7((index) => {
|
|
3526
|
+
if (index === 0) return;
|
|
3527
|
+
const updatedPairs = [...pairs];
|
|
3528
|
+
[updatedPairs[index], updatedPairs[index - 1]] = [updatedPairs[index - 1], updatedPairs[index]];
|
|
3529
|
+
setPairs(updatedPairs);
|
|
3530
|
+
onChange(set4(updatedPairs));
|
|
3531
|
+
}, [pairs, onChange]);
|
|
3532
|
+
const handleMoveDown = useCallback7((index) => {
|
|
3533
|
+
if (index === pairs.length - 1) return;
|
|
3534
|
+
const updatedPairs = [...pairs];
|
|
3535
|
+
[updatedPairs[index], updatedPairs[index + 1]] = [updatedPairs[index + 1], updatedPairs[index]];
|
|
3536
|
+
setPairs(updatedPairs);
|
|
3537
|
+
onChange(set4(updatedPairs));
|
|
3538
|
+
}, [pairs, onChange]);
|
|
3539
|
+
return /* @__PURE__ */ React12.createElement(Stack8, { space: 3 }, pairs.map((pair, index) => /* @__PURE__ */ React12.createElement(Grid5, { className: "manualButtonWrap", columns: [2], key: index, gap: 0, style: { position: "relative" } }, /* @__PURE__ */ React12.createElement("div", { style: { position: "absolute", height: "100%", top: "0", left: "-10px", width: "min-content", transform: "translate(-100%, 0%)" } }, /* @__PURE__ */ React12.createElement("button", { className: "manualButton manualButtonUp", style: { fontSize: "15px", height: "50%" }, onClick: () => handleMoveUp(index) }, /* @__PURE__ */ React12.createElement(ArrowUpIcon, null)), /* @__PURE__ */ React12.createElement("button", { className: "manualButton manualButtonDown", style: { fontSize: "15px", height: "50%" }, onClick: () => handleMoveDown(index) }, /* @__PURE__ */ React12.createElement(ArrowDownIcon, null))), /* @__PURE__ */ React12.createElement(
|
|
3540
|
+
TextInput2,
|
|
3541
|
+
{
|
|
3542
|
+
value: pair.key,
|
|
3543
|
+
onChange: (e) => handlePairChange(index, "key", e.target.value),
|
|
3544
|
+
placeholder: "Key"
|
|
3545
|
+
}
|
|
3546
|
+
), /* @__PURE__ */ React12.createElement("div", { style: { marginLeft: "-1px" } }, /* @__PURE__ */ React12.createElement(
|
|
3547
|
+
TextInput2,
|
|
3548
|
+
{
|
|
3549
|
+
value: pair.value,
|
|
3550
|
+
onChange: (e) => handlePairChange(index, "value", e.target.value),
|
|
3551
|
+
placeholder: "Value"
|
|
3552
|
+
}
|
|
3553
|
+
)), /* @__PURE__ */ React12.createElement(
|
|
3554
|
+
"button",
|
|
3555
|
+
{
|
|
3556
|
+
className: "manualButton",
|
|
3557
|
+
onClick: () => handleRemovePair(index),
|
|
3558
|
+
style: { position: "absolute", top: "0", right: "-10px", transform: "translate(100%, 0%)" }
|
|
3559
|
+
},
|
|
3560
|
+
/* @__PURE__ */ React12.createElement(TrashIcon3, null)
|
|
3561
|
+
))), /* @__PURE__ */ React12.createElement(Button9, { tone: "primary", onClick: handleAddPair, icon: AddIcon, text: "Add Row" }));
|
|
3562
|
+
}
|
|
3563
|
+
|
|
3564
|
+
// src/components/KeyValueReferenceInput.jsx
|
|
3565
|
+
import React13, { useState as useState9, useCallback as useCallback8, useEffect as useEffect6 } from "react";
|
|
3566
|
+
import { Button as Button10, Stack as Stack9, TextInput as TextInput3, Box as Box4, Card as Card4, Flex as Flex8, Text as Text11, Dialog, Menu as Menu2, MenuButton as MenuButton2, MenuItem as MenuItem2, Autocomplete } from "@sanity/ui";
|
|
3567
|
+
import { AddIcon as AddIcon2, ArrowDownIcon as ArrowDownIcon2, ArrowUpIcon as ArrowUpIcon2, TrashIcon as TrashIcon4, SyncIcon, EllipsisHorizontalIcon } from "@sanity/icons";
|
|
3568
|
+
import { set as set5, useFormValue as useFormValue8 } from "sanity";
|
|
3569
|
+
import { nanoid as nanoid8 } from "nanoid";
|
|
3570
|
+
function KeyValueReferenceInput(props) {
|
|
3571
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
3572
|
+
const { value = [], onChange, schemaType, referenceType, fetchReferences, topActions } = props;
|
|
3573
|
+
const [pairs, setPairs] = useState9(value);
|
|
3574
|
+
const [referenceData, setReferenceData] = useState9({});
|
|
3575
|
+
const [isDialogOpen, setIsDialogOpen] = useState9(false);
|
|
3576
|
+
const [editingIndex, setEditingIndex] = useState9(null);
|
|
3577
|
+
const [availableReferences, setAvailableReferences] = useState9([]);
|
|
3578
|
+
const sanityClient = useSanityClient();
|
|
3579
|
+
const formDocument = useFormValue8([]);
|
|
3580
|
+
useEffect6(() => {
|
|
3581
|
+
const refIds = pairs.filter((p) => {
|
|
3582
|
+
var _a2;
|
|
3583
|
+
return (_a2 = p.value) == null ? void 0 : _a2._ref;
|
|
3584
|
+
}).map((p) => p.value._ref);
|
|
3585
|
+
if (refIds.length === 0) return;
|
|
3586
|
+
if (!sanityClient) {
|
|
3587
|
+
const fallback = {};
|
|
3588
|
+
refIds.forEach((id) => {
|
|
3589
|
+
fallback[id] = `Reference (${id.substring(0, 6)}...)`;
|
|
3590
|
+
});
|
|
3591
|
+
setReferenceData(fallback);
|
|
3592
|
+
return;
|
|
3593
|
+
}
|
|
3594
|
+
sanityClient.fetch(`*[_id in $ids]{_id, title}`, { ids: refIds }).then((result) => {
|
|
3595
|
+
const map = {};
|
|
3596
|
+
result.forEach((item) => {
|
|
3597
|
+
map[item._id] = item.title;
|
|
3598
|
+
});
|
|
3599
|
+
setReferenceData(map);
|
|
3600
|
+
}).catch((err) => {
|
|
3601
|
+
console.error("Error fetching reference data:", err);
|
|
3602
|
+
const fallback = {};
|
|
3603
|
+
refIds.forEach((id) => {
|
|
3604
|
+
fallback[id] = `Reference (${id.substring(0, 6)}...)`;
|
|
3605
|
+
});
|
|
3606
|
+
setReferenceData(fallback);
|
|
3607
|
+
});
|
|
3608
|
+
}, [pairs, sanityClient]);
|
|
3609
|
+
const handlePairChange = useCallback8((index, field, fieldValue) => {
|
|
3610
|
+
const updatedPairs = pairs.map((pair, idx) => idx === index ? { ...pair, [field]: fieldValue } : pair);
|
|
3611
|
+
setPairs(updatedPairs);
|
|
3612
|
+
onChange(set5(updatedPairs));
|
|
3613
|
+
}, [pairs, onChange]);
|
|
3614
|
+
const handleAddPair = useCallback8(() => {
|
|
3615
|
+
const updatedPairs = [...pairs, { key: "", value: null, _key: nanoid8() }];
|
|
3616
|
+
setPairs(updatedPairs);
|
|
3617
|
+
onChange(set5(updatedPairs));
|
|
3618
|
+
}, [pairs, onChange]);
|
|
3619
|
+
const handleRemovePair = useCallback8((index) => {
|
|
3620
|
+
const updatedPairs = pairs.filter((_, idx) => idx !== index);
|
|
3621
|
+
setPairs(updatedPairs);
|
|
3622
|
+
onChange(set5(updatedPairs));
|
|
3623
|
+
}, [pairs, onChange]);
|
|
3624
|
+
const handleMoveUp = useCallback8((index) => {
|
|
3625
|
+
if (index === 0) return;
|
|
3626
|
+
const updatedPairs = [...pairs];
|
|
3627
|
+
[updatedPairs[index], updatedPairs[index - 1]] = [updatedPairs[index - 1], updatedPairs[index]];
|
|
3628
|
+
setPairs(updatedPairs);
|
|
3629
|
+
onChange(set5(updatedPairs));
|
|
3630
|
+
}, [pairs, onChange]);
|
|
3631
|
+
const handleMoveDown = useCallback8((index) => {
|
|
3632
|
+
if (index === pairs.length - 1) return;
|
|
3633
|
+
const updatedPairs = [...pairs];
|
|
3634
|
+
[updatedPairs[index], updatedPairs[index + 1]] = [updatedPairs[index + 1], updatedPairs[index]];
|
|
3635
|
+
setPairs(updatedPairs);
|
|
3636
|
+
onChange(set5(updatedPairs));
|
|
3637
|
+
}, [pairs, onChange]);
|
|
3638
|
+
const openReferenceSelector = useCallback8(async (index) => {
|
|
3639
|
+
setEditingIndex(index);
|
|
3640
|
+
if (!sanityClient) {
|
|
3641
|
+
console.error("KeyValueReferenceInput: Sanity client not available");
|
|
3642
|
+
return;
|
|
3643
|
+
}
|
|
3644
|
+
try {
|
|
3645
|
+
let refs;
|
|
3646
|
+
if (fetchReferences) {
|
|
3647
|
+
refs = await fetchReferences(sanityClient, formDocument);
|
|
3648
|
+
} else if (referenceType) {
|
|
3649
|
+
refs = await sanityClient.fetch(`*[_type == $type]{_id, title}`, { type: referenceType });
|
|
3650
|
+
} else {
|
|
3651
|
+
console.warn("KeyValueReferenceInput: provide a fetchReferences prop or referenceType");
|
|
3652
|
+
refs = [];
|
|
3653
|
+
}
|
|
3654
|
+
setAvailableReferences(refs);
|
|
3655
|
+
setIsDialogOpen(true);
|
|
3656
|
+
} catch (err) {
|
|
3657
|
+
console.error("Error fetching available references:", err);
|
|
3658
|
+
}
|
|
3659
|
+
}, [sanityClient, fetchReferences, referenceType, formDocument]);
|
|
3660
|
+
const closeDialog = useCallback8(() => {
|
|
3661
|
+
setIsDialogOpen(false);
|
|
3662
|
+
setEditingIndex(null);
|
|
3663
|
+
}, []);
|
|
3664
|
+
const handleReferenceSelect = useCallback8((reference) => {
|
|
3665
|
+
if (editingIndex === null) return;
|
|
3666
|
+
handlePairChange(editingIndex, "value", { _type: "reference", _ref: reference._id, _weak: true });
|
|
3667
|
+
closeDialog();
|
|
3668
|
+
}, [editingIndex, handlePairChange, closeDialog]);
|
|
3669
|
+
const referenceOptions = availableReferences.map((ref) => ({ value: ref._id, title: ref.title }));
|
|
3670
|
+
const keyField = (_d = (_c = (_b = (_a = schemaType == null ? void 0 : schemaType.options) == null ? void 0 : _a.of) == null ? void 0 : _b[0]) == null ? void 0 : _c.fields) == null ? void 0 : _d.find((f) => f.name === "key");
|
|
3671
|
+
const valueField = (_h = (_g = (_f = (_e = schemaType == null ? void 0 : schemaType.options) == null ? void 0 : _e.of) == null ? void 0 : _f[0]) == null ? void 0 : _g.fields) == null ? void 0 : _h.find((f) => f.name === "value");
|
|
3672
|
+
const keyTitle = (keyField == null ? void 0 : keyField.title) || "Key";
|
|
3673
|
+
const valueTitle = (valueField == null ? void 0 : valueField.title) || "Value";
|
|
3674
|
+
const keyPlaceholder = (keyField == null ? void 0 : keyField.placeholder) || "Enter key";
|
|
3675
|
+
const pickerLabel = referenceType || valueTitle.toLowerCase();
|
|
3676
|
+
return /* @__PURE__ */ React13.createElement(Stack9, { space: 3 }, topActions && /* @__PURE__ */ React13.createElement(Box4, { paddingBottom: 2 }, topActions), /* @__PURE__ */ React13.createElement(Box4, null, /* @__PURE__ */ React13.createElement(Stack9, { space: 2 }, pairs.map((pair, index) => {
|
|
3677
|
+
var _a2;
|
|
3678
|
+
return /* @__PURE__ */ React13.createElement(Box4, { key: index, style: { position: "relative" } }, /* @__PURE__ */ React13.createElement("div", { style: { position: "absolute", height: "100%", top: "0", left: "-5px", width: "min-content", transform: "translate(-100%, 0%)" } }, /* @__PURE__ */ React13.createElement("button", { className: "manualButton manualButtonUp", style: { fontSize: "15px", height: "50%" }, onClick: () => handleMoveUp(index) }, /* @__PURE__ */ React13.createElement(ArrowUpIcon2, null)), /* @__PURE__ */ React13.createElement("button", { className: "manualButton manualButtonDown", style: { fontSize: "15px", height: "50%" }, onClick: () => handleMoveDown(index) }, /* @__PURE__ */ React13.createElement(ArrowDownIcon2, null))), /* @__PURE__ */ React13.createElement(Flex8, { gap: 2, align: "flex-start" }, /* @__PURE__ */ React13.createElement(Box4, { flex: 1 }, /* @__PURE__ */ React13.createElement(
|
|
3679
|
+
TextInput3,
|
|
3680
|
+
{
|
|
3681
|
+
value: pair.key,
|
|
3682
|
+
onChange: (e) => handlePairChange(index, "key", e.target.value),
|
|
3683
|
+
placeholder: keyPlaceholder
|
|
3684
|
+
}
|
|
3685
|
+
)), /* @__PURE__ */ React13.createElement(Box4, { flex: 1, style: { minHeight: "100%" } }, ((_a2 = pair.value) == null ? void 0 : _a2._ref) ? /* @__PURE__ */ React13.createElement(Card4, { className: "referenceCard", radius: 2, tone: "primary", style: { paddingLeft: "1rem", height: "fit-content" } }, /* @__PURE__ */ React13.createElement(Flex8, { align: "center", justify: "space-between" }, /* @__PURE__ */ React13.createElement(
|
|
3686
|
+
Text11,
|
|
3687
|
+
{
|
|
3688
|
+
size: 2,
|
|
3689
|
+
style: { whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", maxWidth: "90%" }
|
|
3690
|
+
},
|
|
3691
|
+
referenceData[pair.value._ref] || "Loading..."
|
|
3692
|
+
), /* @__PURE__ */ React13.createElement(
|
|
3693
|
+
MenuButton2,
|
|
3694
|
+
{
|
|
3695
|
+
button: /* @__PURE__ */ React13.createElement(Button10, { icon: EllipsisHorizontalIcon, mode: "bleed", title: "Options" }),
|
|
3696
|
+
id: `ref-options-${index}`,
|
|
3697
|
+
menu: /* @__PURE__ */ React13.createElement(Menu2, null, /* @__PURE__ */ React13.createElement(MenuItem2, { tone: "critical", icon: TrashIcon4, text: "Remove", onClick: () => handlePairChange(index, "value", null) }), /* @__PURE__ */ React13.createElement(MenuItem2, { icon: SyncIcon, text: "Replace", onClick: () => openReferenceSelector(index) })),
|
|
3698
|
+
popover: { portal: true, tone: "default", placement: "left" }
|
|
3699
|
+
}
|
|
3700
|
+
))) : /* @__PURE__ */ React13.createElement(
|
|
3701
|
+
Box4,
|
|
3702
|
+
{
|
|
3703
|
+
padding: 2,
|
|
3704
|
+
style: { minHeight: "100%", border: "1px dashed #ccc", borderRadius: "4px", display: "flex", alignItems: "center", justifyContent: "center", cursor: "pointer" },
|
|
3705
|
+
onClick: () => openReferenceSelector(index)
|
|
3706
|
+
},
|
|
3707
|
+
/* @__PURE__ */ React13.createElement(Text11, { muted: true, size: 2 }, "Click to select a ", pickerLabel)
|
|
3708
|
+
))), /* @__PURE__ */ React13.createElement(
|
|
3709
|
+
"button",
|
|
3710
|
+
{
|
|
3711
|
+
className: "manualButton",
|
|
3712
|
+
onClick: () => handleRemovePair(index),
|
|
3713
|
+
style: { position: "absolute", top: "0", right: "-7px", transform: "translate(100%, 0%)" }
|
|
3714
|
+
},
|
|
3715
|
+
/* @__PURE__ */ React13.createElement(TrashIcon4, null)
|
|
3716
|
+
));
|
|
3717
|
+
}))), /* @__PURE__ */ React13.createElement(Button10, { tone: "primary", mode: "ghost", onClick: handleAddPair, icon: AddIcon2, text: `Add ${keyTitle}` }), isDialogOpen && /* @__PURE__ */ React13.createElement(
|
|
3718
|
+
Dialog,
|
|
3719
|
+
{
|
|
3720
|
+
header: `Select a ${pickerLabel}`,
|
|
3721
|
+
id: "reference-selector-dialog",
|
|
3722
|
+
onClose: closeDialog,
|
|
3723
|
+
width: 1
|
|
3724
|
+
},
|
|
3725
|
+
/* @__PURE__ */ React13.createElement(Box4, { padding: 4 }, /* @__PURE__ */ React13.createElement(
|
|
3726
|
+
Autocomplete,
|
|
3727
|
+
{
|
|
3728
|
+
id: "reference-autocomplete",
|
|
3729
|
+
options: referenceOptions,
|
|
3730
|
+
placeholder: `Search ${pickerLabel}s...`,
|
|
3731
|
+
renderOption: (option) => /* @__PURE__ */ React13.createElement(Card4, { key: option.value, padding: 3, radius: 2, tone: "default", style: { cursor: "pointer" } }, /* @__PURE__ */ React13.createElement(Text11, { size: 2 }, option.title)),
|
|
3732
|
+
renderValue: (val) => {
|
|
3733
|
+
var _a2;
|
|
3734
|
+
return ((_a2 = referenceOptions.find((o) => o.value === val)) == null ? void 0 : _a2.title) || "";
|
|
3735
|
+
},
|
|
3736
|
+
onChange: (newValue) => {
|
|
3737
|
+
const selected = availableReferences.find((r) => r._id === newValue);
|
|
3738
|
+
if (selected) handleReferenceSelect(selected);
|
|
3739
|
+
},
|
|
3740
|
+
openButton: true,
|
|
3741
|
+
fontSize: 2
|
|
3742
|
+
}
|
|
3743
|
+
))
|
|
3744
|
+
));
|
|
3745
|
+
}
|
|
3746
|
+
|
|
3747
|
+
// src/components/VariableInstanceReferencesInput.jsx
|
|
3748
|
+
import React14, { useState as useState10, useCallback as useCallback9 } from "react";
|
|
3749
|
+
import { Button as Button11, Flex as Flex9, Dialog as Dialog2, Box as Box5, Stack as Stack10, Text as Text12 } from "@sanity/ui";
|
|
3750
|
+
import { SyncIcon as SyncIcon2, DocumentTextIcon } from "@sanity/icons";
|
|
3751
|
+
import { set as set6, useFormValue as useFormValue9 } from "sanity";
|
|
3752
|
+
import { nanoid as nanoid9 } from "nanoid";
|
|
3753
|
+
function VariableInstanceReferencesInput(props) {
|
|
3754
|
+
const { value = [], onChange } = props;
|
|
3755
|
+
const [isAutofilling, setIsAutofilling] = useState10(false);
|
|
3756
|
+
const [showConfirmDialog, setShowConfirmDialog] = useState10(false);
|
|
3757
|
+
const [pendingAction, setPendingAction] = useState10(null);
|
|
3758
|
+
const formDocument = useFormValue9([]);
|
|
3759
|
+
const sanityClient = useSanityClient();
|
|
3760
|
+
const fetchReferences = useCallback9(async (client, doc) => {
|
|
3761
|
+
const typefaceName = doc == null ? void 0 : doc.typefaceName;
|
|
3762
|
+
if (!typefaceName) {
|
|
3763
|
+
return client.fetch(`*[_type == 'font' && variableFont != true]{_id, title}`, {});
|
|
3764
|
+
}
|
|
3765
|
+
return client.fetch(
|
|
3766
|
+
`*[_type == 'font' && typefaceName == $typefaceName && variableFont != true]{_id, title}`,
|
|
3767
|
+
{ typefaceName }
|
|
3768
|
+
);
|
|
3769
|
+
}, []);
|
|
3770
|
+
const performAutofillWithMatching = useCallback9(async (mode) => {
|
|
3771
|
+
setIsAutofilling(true);
|
|
3772
|
+
try {
|
|
3773
|
+
if (!(formDocument == null ? void 0 : formDocument.variableInstances)) {
|
|
3774
|
+
console.warn("Cannot autofill: no variableInstances data on this document");
|
|
3775
|
+
return;
|
|
3776
|
+
}
|
|
3777
|
+
const mappings = await parseVariableFontInstances(formDocument, sanityClient);
|
|
3778
|
+
if (mappings.length === 0) {
|
|
3779
|
+
console.warn("No variable instances could be parsed from this font");
|
|
3780
|
+
return;
|
|
3781
|
+
}
|
|
3782
|
+
const updatedPairs = mode === "replace" ? mappings : [...value, ...mappings.filter((m) => !value.some((p) => p.key === m.key))];
|
|
3783
|
+
onChange(set6(updatedPairs));
|
|
3784
|
+
} catch (err) {
|
|
3785
|
+
console.error("Error during autofill with matching:", err);
|
|
3786
|
+
} finally {
|
|
3787
|
+
setIsAutofilling(false);
|
|
3788
|
+
}
|
|
3789
|
+
}, [formDocument, sanityClient, value, onChange]);
|
|
3790
|
+
const performAutofillKeysOnly = useCallback9(async (mode) => {
|
|
3791
|
+
setIsAutofilling(true);
|
|
3792
|
+
try {
|
|
3793
|
+
if (!(formDocument == null ? void 0 : formDocument.variableInstances)) {
|
|
3794
|
+
console.warn("Cannot autofill: no variableInstances data on this document");
|
|
3795
|
+
return;
|
|
3796
|
+
}
|
|
3797
|
+
let instances;
|
|
3798
|
+
try {
|
|
3799
|
+
instances = JSON.parse(formDocument.variableInstances);
|
|
3800
|
+
} catch {
|
|
3801
|
+
console.error("Invalid variableInstances JSON on this document");
|
|
3802
|
+
return;
|
|
3803
|
+
}
|
|
3804
|
+
const keys = Object.keys(instances);
|
|
3805
|
+
if (keys.length === 0) {
|
|
3806
|
+
console.warn("No variable instances found in JSON");
|
|
3807
|
+
return;
|
|
3808
|
+
}
|
|
3809
|
+
const keyOnlyPairs = keys.map((key) => ({ key, value: null, _key: nanoid9() }));
|
|
3810
|
+
const updatedPairs = mode === "replace" ? keyOnlyPairs : [...value, ...keyOnlyPairs.filter((p) => !value.some((existing) => existing.key === p.key))];
|
|
3811
|
+
onChange(set6(updatedPairs));
|
|
3812
|
+
} catch (err) {
|
|
3813
|
+
console.error("Error during keys-only autofill:", err);
|
|
3814
|
+
} finally {
|
|
3815
|
+
setIsAutofilling(false);
|
|
3816
|
+
}
|
|
3817
|
+
}, [formDocument, value, onChange]);
|
|
3818
|
+
const handleAutofillWithMatching = useCallback9(() => {
|
|
3819
|
+
if (value.length > 0) {
|
|
3820
|
+
setPendingAction("matching");
|
|
3821
|
+
setShowConfirmDialog(true);
|
|
3822
|
+
return;
|
|
3823
|
+
}
|
|
3824
|
+
performAutofillWithMatching("replace");
|
|
3825
|
+
}, [value, performAutofillWithMatching]);
|
|
3826
|
+
const handleAutofillKeysOnly = useCallback9(() => {
|
|
3827
|
+
if (value.length > 0) {
|
|
3828
|
+
setPendingAction("keysOnly");
|
|
3829
|
+
setShowConfirmDialog(true);
|
|
3830
|
+
return;
|
|
3831
|
+
}
|
|
3832
|
+
performAutofillKeysOnly("replace");
|
|
3833
|
+
}, [value, performAutofillKeysOnly]);
|
|
3834
|
+
const handleConfirmChoice = useCallback9(async (choice) => {
|
|
3835
|
+
setShowConfirmDialog(false);
|
|
3836
|
+
if (pendingAction === "matching") await performAutofillWithMatching(choice);
|
|
3837
|
+
else if (pendingAction === "keysOnly") await performAutofillKeysOnly(choice);
|
|
3838
|
+
setPendingAction(null);
|
|
3839
|
+
}, [pendingAction, performAutofillWithMatching, performAutofillKeysOnly]);
|
|
3840
|
+
const handleConfirmCancel = useCallback9(() => {
|
|
3841
|
+
setShowConfirmDialog(false);
|
|
3842
|
+
setPendingAction(null);
|
|
3843
|
+
}, []);
|
|
3844
|
+
const showAutofill = !!((formDocument == null ? void 0 : formDocument.variableFont) && (formDocument == null ? void 0 : formDocument.variableInstances));
|
|
3845
|
+
const topActions = showAutofill ? /* @__PURE__ */ React14.createElement(Flex9, { gap: 2 }, /* @__PURE__ */ React14.createElement(
|
|
3846
|
+
Button11,
|
|
3847
|
+
{
|
|
3848
|
+
tone: "primary",
|
|
3849
|
+
mode: "ghost",
|
|
3850
|
+
onClick: handleAutofillWithMatching,
|
|
3851
|
+
icon: SyncIcon2,
|
|
3852
|
+
text: "Autofill with Matching",
|
|
3853
|
+
disabled: isAutofilling,
|
|
3854
|
+
loading: isAutofilling
|
|
3855
|
+
}
|
|
3856
|
+
), /* @__PURE__ */ React14.createElement(
|
|
3857
|
+
Button11,
|
|
3858
|
+
{
|
|
3859
|
+
tone: "default",
|
|
3860
|
+
mode: "ghost",
|
|
3861
|
+
onClick: handleAutofillKeysOnly,
|
|
3862
|
+
icon: DocumentTextIcon,
|
|
3863
|
+
text: "Autofill Keys Only",
|
|
3864
|
+
disabled: isAutofilling,
|
|
3865
|
+
loading: isAutofilling
|
|
3866
|
+
}
|
|
3867
|
+
)) : null;
|
|
3868
|
+
return /* @__PURE__ */ React14.createElement(React14.Fragment, null, /* @__PURE__ */ React14.createElement(
|
|
3869
|
+
KeyValueReferenceInput,
|
|
3870
|
+
{
|
|
3871
|
+
...props,
|
|
3872
|
+
referenceType: "font",
|
|
3873
|
+
fetchReferences,
|
|
3874
|
+
topActions
|
|
3875
|
+
}
|
|
3876
|
+
), showConfirmDialog && /* @__PURE__ */ React14.createElement(
|
|
3877
|
+
Dialog2,
|
|
3878
|
+
{
|
|
3879
|
+
header: "Existing entries found",
|
|
3880
|
+
id: "autofill-confirm-dialog",
|
|
3881
|
+
onClose: handleConfirmCancel,
|
|
3882
|
+
width: 1
|
|
3883
|
+
},
|
|
3884
|
+
/* @__PURE__ */ React14.createElement(Box5, { padding: 4 }, /* @__PURE__ */ React14.createElement(Stack10, { space: 4 }, /* @__PURE__ */ React14.createElement(Text12, null, "You already have ", value.length, " ", value.length === 1 ? "entry" : "entries", ". How would you like to proceed?"), /* @__PURE__ */ React14.createElement(Flex9, { gap: 2, justify: "flex-end" }, /* @__PURE__ */ React14.createElement(Button11, { text: "Cancel", mode: "ghost", onClick: handleConfirmCancel }), /* @__PURE__ */ React14.createElement(Button11, { text: "Merge (Add New)", tone: "primary", mode: "ghost", onClick: () => handleConfirmChoice("merge") }), /* @__PURE__ */ React14.createElement(Button11, { text: "Replace All", tone: "critical", onClick: () => handleConfirmChoice("replace") }))))
|
|
3885
|
+
));
|
|
3886
|
+
}
|
|
3887
|
+
|
|
3502
3888
|
// src/utils/getEmptyFontKit.js
|
|
3503
3889
|
import * as fontkit7 from "fontkit";
|
|
3504
3890
|
import slugify4 from "slugify";
|
|
@@ -3581,6 +3967,8 @@ export {
|
|
|
3581
3967
|
FontScriptUploaderComponent,
|
|
3582
3968
|
GenerateCollectionsPairsComponent,
|
|
3583
3969
|
HtmlDescription,
|
|
3970
|
+
KeyValueInput,
|
|
3971
|
+
KeyValueReferenceInput,
|
|
3584
3972
|
PriceInput_default as PriceInput,
|
|
3585
3973
|
RegenerateSubfamiliesComponent,
|
|
3586
3974
|
SCRIPTS,
|
|
@@ -3590,6 +3978,7 @@ export {
|
|
|
3590
3978
|
UpdateScriptsComponent,
|
|
3591
3979
|
UploadButton_default as UploadButton,
|
|
3592
3980
|
UploadScriptsComponent,
|
|
3981
|
+
VariableInstanceReferencesInput,
|
|
3593
3982
|
addItalicToFontTitle,
|
|
3594
3983
|
createFontObject,
|
|
3595
3984
|
determineWeight,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liiift-studio/sanity-font-manager",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.1",
|
|
4
4
|
"description": "Sanity Studio plugin — full font management suite with batch upload, format conversion, metadata extraction, CSS generation, collection/pair generation, and script variant support. Supports Sanity v3, v4, and v5.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Liiift Studio",
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// Ordered key-value string pair editor for Sanity Studio — add, remove, and reorder rows
|
|
2
|
+
|
|
3
|
+
import React, { useState, useCallback } from 'react';
|
|
4
|
+
import { Button, Grid, Stack, TextInput } from '@sanity/ui';
|
|
5
|
+
import { AddIcon, ArrowDownIcon, ArrowUpIcon, TrashIcon } from '@sanity/icons';
|
|
6
|
+
import { set } from 'sanity';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Ordered key-value string pair editor with add, remove, and reorder controls.
|
|
10
|
+
* Writes an array of { _key, key, value } objects to Sanity.
|
|
11
|
+
* @param {Array} value - Current array of { _key, key, value } pairs
|
|
12
|
+
* @param {Function} onChange - Sanity onChange callback
|
|
13
|
+
*/
|
|
14
|
+
export function KeyValueInput({ value = [], onChange }) {
|
|
15
|
+
const [pairs, setPairs] = useState(value);
|
|
16
|
+
|
|
17
|
+
/** Updates a specific field for a pair at the given index */
|
|
18
|
+
const handlePairChange = useCallback((index, field, fieldValue) => {
|
|
19
|
+
const updatedPairs = pairs.map((pair, idx) => idx === index ? { ...pair, [field]: fieldValue } : pair);
|
|
20
|
+
setPairs(updatedPairs);
|
|
21
|
+
onChange(set(updatedPairs));
|
|
22
|
+
}, [pairs, onChange]);
|
|
23
|
+
|
|
24
|
+
/** Appends a new empty pair */
|
|
25
|
+
const handleAddPair = useCallback(() => {
|
|
26
|
+
const newPair = { key: '', value: '', _key: Math.random().toString(36).substr(2, 9) };
|
|
27
|
+
const updatedPairs = [...pairs, newPair];
|
|
28
|
+
setPairs(updatedPairs);
|
|
29
|
+
onChange(set(updatedPairs));
|
|
30
|
+
}, [pairs, onChange]);
|
|
31
|
+
|
|
32
|
+
/** Removes the pair at the given index */
|
|
33
|
+
const handleRemovePair = useCallback((index) => {
|
|
34
|
+
const updatedPairs = pairs.filter((_, idx) => idx !== index);
|
|
35
|
+
setPairs(updatedPairs);
|
|
36
|
+
onChange(set(updatedPairs));
|
|
37
|
+
}, [pairs, onChange]);
|
|
38
|
+
|
|
39
|
+
/** Swaps a pair with the one above it */
|
|
40
|
+
const handleMoveUp = useCallback((index) => {
|
|
41
|
+
if (index === 0) return;
|
|
42
|
+
const updatedPairs = [...pairs];
|
|
43
|
+
[updatedPairs[index], updatedPairs[index - 1]] = [updatedPairs[index - 1], updatedPairs[index]];
|
|
44
|
+
setPairs(updatedPairs);
|
|
45
|
+
onChange(set(updatedPairs));
|
|
46
|
+
}, [pairs, onChange]);
|
|
47
|
+
|
|
48
|
+
/** Swaps a pair with the one below it */
|
|
49
|
+
const handleMoveDown = useCallback((index) => {
|
|
50
|
+
if (index === pairs.length - 1) return;
|
|
51
|
+
const updatedPairs = [...pairs];
|
|
52
|
+
[updatedPairs[index], updatedPairs[index + 1]] = [updatedPairs[index + 1], updatedPairs[index]];
|
|
53
|
+
setPairs(updatedPairs);
|
|
54
|
+
onChange(set(updatedPairs));
|
|
55
|
+
}, [pairs, onChange]);
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<Stack space={3}>
|
|
59
|
+
{pairs.map((pair, index) => (
|
|
60
|
+
<Grid className="manualButtonWrap" columns={[2]} key={index} gap={0} style={{ position: 'relative' }}>
|
|
61
|
+
<div style={{ position: 'absolute', height: '100%', top: '0', left: '-10px', width: 'min-content', transform: 'translate(-100%, 0%)' }}>
|
|
62
|
+
<button className="manualButton manualButtonUp" style={{ fontSize: '15px', height: '50%' }} onClick={() => handleMoveUp(index)}>
|
|
63
|
+
<ArrowUpIcon />
|
|
64
|
+
</button>
|
|
65
|
+
<button className="manualButton manualButtonDown" style={{ fontSize: '15px', height: '50%' }} onClick={() => handleMoveDown(index)}>
|
|
66
|
+
<ArrowDownIcon />
|
|
67
|
+
</button>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<TextInput
|
|
71
|
+
value={pair.key}
|
|
72
|
+
onChange={(e) => handlePairChange(index, 'key', e.target.value)}
|
|
73
|
+
placeholder="Key"
|
|
74
|
+
/>
|
|
75
|
+
<div style={{ marginLeft: '-1px' }}>
|
|
76
|
+
<TextInput
|
|
77
|
+
value={pair.value}
|
|
78
|
+
onChange={(e) => handlePairChange(index, 'value', e.target.value)}
|
|
79
|
+
placeholder="Value"
|
|
80
|
+
/>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<button
|
|
84
|
+
className="manualButton"
|
|
85
|
+
onClick={() => handleRemovePair(index)}
|
|
86
|
+
style={{ position: 'absolute', top: '0', right: '-10px', transform: 'translate(100%, 0%)' }}
|
|
87
|
+
>
|
|
88
|
+
<TrashIcon />
|
|
89
|
+
</button>
|
|
90
|
+
</Grid>
|
|
91
|
+
))}
|
|
92
|
+
<Button tone="primary" onClick={handleAddPair} icon={AddIcon} text="Add Row" />
|
|
93
|
+
</Stack>
|
|
94
|
+
);
|
|
95
|
+
}
|