@proveanything/smartlinks-utils-ui 0.7.3 → 0.7.4
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.
|
@@ -2140,9 +2140,18 @@ interface Props<T> {
|
|
|
2140
2140
|
error: Error | null;
|
|
2141
2141
|
onBack: () => void;
|
|
2142
2142
|
onSelect: (itemId: string) => void;
|
|
2143
|
+
/**
|
|
2144
|
+
* Set of keys (recordIds + scope refs) currently dirty in the shell-level
|
|
2145
|
+
* draft store. Used to paint a per-row pip on items the user has edited
|
|
2146
|
+
* but isn't currently looking at — mirrors the affordance the scope rail
|
|
2147
|
+
* gets via `RecordList.dirtyKeys`.
|
|
2148
|
+
*/
|
|
2149
|
+
dirtyKeys?: ReadonlySet<string>;
|
|
2150
|
+
/** Subset of `dirtyKeys` whose last save attempt failed. */
|
|
2151
|
+
errorKeys?: ReadonlySet<string>;
|
|
2143
2152
|
i18n: Pick<RecordsAdminI18n, 'backToScopes' | 'siblingsHeading' | 'noItemsTitle' | 'noItemsBody'>;
|
|
2144
2153
|
}
|
|
2145
|
-
declare function SiblingRail<T>({ items, selectedItemId, isLoading, error, onBack, onSelect, i18n, }: Props<T>): react_jsx_runtime.JSX.Element;
|
|
2154
|
+
declare function SiblingRail<T>({ items, selectedItemId, isLoading, error, onBack, onSelect, dirtyKeys, errorKeys, i18n, }: Props<T>): react_jsx_runtime.JSX.Element;
|
|
2146
2155
|
|
|
2147
2156
|
interface ClipboardEntry<T = unknown> {
|
|
2148
2157
|
value: T;
|
|
@@ -713,6 +713,7 @@ function useRecordEditor(args) {
|
|
|
713
713
|
const [savedFacetRule, setSavedFacetRule] = useState(
|
|
714
714
|
initialDraft ? initialDraft.baselineFacetRule : initialFacetRule
|
|
715
715
|
);
|
|
716
|
+
const [userInteracted, setUserInteracted] = useState(!!initialDraft);
|
|
716
717
|
const [optimisticSource, setOptimisticSource] = useState(null);
|
|
717
718
|
const [isSaving, setIsSaving] = useState(false);
|
|
718
719
|
const [saveError, setSaveError] = useState(null);
|
|
@@ -732,8 +733,18 @@ function useRecordEditor(args) {
|
|
|
732
733
|
setFacetRule(initialFacetRule);
|
|
733
734
|
setSavedFacetRule(initialFacetRule);
|
|
734
735
|
setOptimisticSource(null);
|
|
736
|
+
setUserInteracted(false);
|
|
735
737
|
}, [scope.raw, resolved.source, resolved.sourceRef]);
|
|
736
|
-
const
|
|
738
|
+
const valueDiff = !isEqual(value, savedSnapshot) || !isEqual(facetRule, savedFacetRule);
|
|
739
|
+
const isDirty = userInteracted && valueDiff;
|
|
740
|
+
const handleChange = useCallback((next) => {
|
|
741
|
+
setUserInteracted(true);
|
|
742
|
+
setValue(next);
|
|
743
|
+
}, []);
|
|
744
|
+
const handleFacetRuleChange = useCallback((next) => {
|
|
745
|
+
setUserInteracted(true);
|
|
746
|
+
setFacetRule(next);
|
|
747
|
+
}, []);
|
|
737
748
|
const save = useCallback(async () => {
|
|
738
749
|
const anchors = parsedRefToScope(scope);
|
|
739
750
|
const hasAnchors = !!(anchors.productId || anchors.variantId || anchors.batchId || anchors.proofId);
|
|
@@ -817,6 +828,7 @@ function useRecordEditor(args) {
|
|
|
817
828
|
const reset = useCallback(() => {
|
|
818
829
|
setValue(savedSnapshot);
|
|
819
830
|
setFacetRule(savedFacetRule);
|
|
831
|
+
setUserInteracted(false);
|
|
820
832
|
draftStore.clearDraft(draftKey);
|
|
821
833
|
}, [savedSnapshot, savedFacetRule]);
|
|
822
834
|
const remove = useCallback(async () => {
|
|
@@ -826,15 +838,42 @@ function useRecordEditor(args) {
|
|
|
826
838
|
draftStore.clearDraft(draftKey);
|
|
827
839
|
onDeleted?.();
|
|
828
840
|
}, [resolved.source, resolved.recordId]);
|
|
841
|
+
const prevDraftKeyRef = useRef(draftKey);
|
|
842
|
+
const prevScopeRawRef = useRef(scope.raw);
|
|
843
|
+
useEffect(() => {
|
|
844
|
+
const prevKey = prevDraftKeyRef.current;
|
|
845
|
+
const prevScopeRaw = prevScopeRawRef.current;
|
|
846
|
+
if (prevKey && prevKey !== draftKey && prevScopeRaw === scope.raw) {
|
|
847
|
+
const stale = draftStore.get(prevKey);
|
|
848
|
+
if (stale) draftStore.clearDraft(prevKey);
|
|
849
|
+
}
|
|
850
|
+
prevDraftKeyRef.current = draftKey;
|
|
851
|
+
prevScopeRawRef.current = scope.raw;
|
|
852
|
+
}, [draftKey, scope.raw]);
|
|
829
853
|
useEffect(() => {
|
|
830
854
|
if (!isDirty) {
|
|
831
855
|
return;
|
|
832
856
|
}
|
|
833
857
|
const anchors = parsedRefToScope(scope);
|
|
834
858
|
const saveKind = resolved.recordId && resolved.source === "self" ? "update" : createMode ? "create" : "upsert";
|
|
859
|
+
const deriveLabel = () => {
|
|
860
|
+
const v = value;
|
|
861
|
+
if (v && typeof v === "object") {
|
|
862
|
+
for (const key of ["title", "name", "label", "heading", "question"]) {
|
|
863
|
+
const raw = v[key];
|
|
864
|
+
if (typeof raw === "string" && raw.trim()) return raw.trim();
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
if (scope.raw?.startsWith("item:")) return "Untitled item";
|
|
868
|
+
if (scope.kind === "rule") return "Rule";
|
|
869
|
+
if (scope.kind && scope.kind !== "collection") {
|
|
870
|
+
return scope.kind.charAt(0).toUpperCase() + scope.kind.slice(1);
|
|
871
|
+
}
|
|
872
|
+
return "Default";
|
|
873
|
+
};
|
|
835
874
|
draftStore.upsertDraft({
|
|
836
875
|
key: draftKey,
|
|
837
|
-
label:
|
|
876
|
+
label: deriveLabel(),
|
|
838
877
|
context: scope.kind,
|
|
839
878
|
scopeRaw: scope.raw,
|
|
840
879
|
recordId: resolved.recordId,
|
|
@@ -858,7 +897,7 @@ function useRecordEditor(args) {
|
|
|
858
897
|
const cannotSaveReason = !ruleValid ? "Pick at least one value for every facet in the rule before saving." : void 0;
|
|
859
898
|
return {
|
|
860
899
|
value,
|
|
861
|
-
onChange:
|
|
900
|
+
onChange: handleChange,
|
|
862
901
|
source: effectiveSource,
|
|
863
902
|
recordId: resolved.recordId,
|
|
864
903
|
parentValue: resolved.parentValue,
|
|
@@ -873,7 +912,7 @@ function useRecordEditor(args) {
|
|
|
873
912
|
isSaving,
|
|
874
913
|
saveError,
|
|
875
914
|
facetRule,
|
|
876
|
-
onFacetRuleChange:
|
|
915
|
+
onFacetRuleChange: handleFacetRuleChange
|
|
877
916
|
};
|
|
878
917
|
}
|
|
879
918
|
var RT_KEY = (recordType) => recordType ?? "_default";
|
|
@@ -3722,6 +3761,8 @@ function SiblingRail({
|
|
|
3722
3761
|
error,
|
|
3723
3762
|
onBack,
|
|
3724
3763
|
onSelect,
|
|
3764
|
+
dirtyKeys,
|
|
3765
|
+
errorKeys,
|
|
3725
3766
|
i18n
|
|
3726
3767
|
}) {
|
|
3727
3768
|
return /* @__PURE__ */ jsxs("div", { className: "ra-sibling-rail", children: [
|
|
@@ -3745,17 +3786,36 @@ function SiblingRail({
|
|
|
3745
3786
|
!isLoading && !error && items.length > 0 && /* @__PURE__ */ jsx("ul", { className: "ra-sibling-list", children: items.map((item) => {
|
|
3746
3787
|
const id = item.itemId ?? "";
|
|
3747
3788
|
const selected = selectedItemId === id;
|
|
3748
|
-
|
|
3789
|
+
const isDirty = !!(id && dirtyKeys?.has(id) || item.ref && dirtyKeys?.has(item.ref));
|
|
3790
|
+
const hasError = !!(id && errorKeys?.has(id) || item.ref && errorKeys?.has(item.ref));
|
|
3791
|
+
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
3749
3792
|
"button",
|
|
3750
3793
|
{
|
|
3751
3794
|
type: "button",
|
|
3752
3795
|
onClick: () => onSelect(id),
|
|
3753
3796
|
className: "ra-row",
|
|
3754
3797
|
"data-selected": selected,
|
|
3755
|
-
children:
|
|
3756
|
-
/* @__PURE__ */
|
|
3757
|
-
|
|
3758
|
-
|
|
3798
|
+
children: [
|
|
3799
|
+
/* @__PURE__ */ jsxs("div", { className: "ra-row-body", children: [
|
|
3800
|
+
/* @__PURE__ */ jsx("div", { className: "ra-row-title", children: item.label }),
|
|
3801
|
+
item.subtitle && /* @__PURE__ */ jsx("div", { className: "ra-row-sub", children: item.subtitle })
|
|
3802
|
+
] }),
|
|
3803
|
+
hasError ? /* @__PURE__ */ jsx(
|
|
3804
|
+
"span",
|
|
3805
|
+
{
|
|
3806
|
+
className: "ra-error-pip",
|
|
3807
|
+
title: "Save failed",
|
|
3808
|
+
"aria-label": "Save failed"
|
|
3809
|
+
}
|
|
3810
|
+
) : isDirty ? /* @__PURE__ */ jsx(
|
|
3811
|
+
"span",
|
|
3812
|
+
{
|
|
3813
|
+
className: "ra-dirty-pip",
|
|
3814
|
+
title: "Unsaved changes",
|
|
3815
|
+
"aria-label": "Unsaved changes"
|
|
3816
|
+
}
|
|
3817
|
+
) : null
|
|
3818
|
+
]
|
|
3759
3819
|
}
|
|
3760
3820
|
) }, item.ref);
|
|
3761
3821
|
}) })
|
|
@@ -4202,8 +4262,23 @@ var UnsavedTray = ({
|
|
|
4202
4262
|
const wrapRef = useRef(null);
|
|
4203
4263
|
const SI = SaveIcon ?? Save;
|
|
4204
4264
|
const DI = DiscardIcon ?? Undo2;
|
|
4205
|
-
const
|
|
4206
|
-
|
|
4265
|
+
const uniqueDrafts = useMemo(() => {
|
|
4266
|
+
const byBucket = /* @__PURE__ */ new Map();
|
|
4267
|
+
for (const d of drafts) {
|
|
4268
|
+
const bucket = d.recordId || d.scopeRaw || d.key;
|
|
4269
|
+
const existing = byBucket.get(bucket);
|
|
4270
|
+
if (!existing) {
|
|
4271
|
+
byBucket.set(bucket, d);
|
|
4272
|
+
continue;
|
|
4273
|
+
}
|
|
4274
|
+
const existingSynthetic = existing.key.startsWith("draft:");
|
|
4275
|
+
const incomingSynthetic = d.key.startsWith("draft:");
|
|
4276
|
+
if (existingSynthetic && !incomingSynthetic) byBucket.set(bucket, d);
|
|
4277
|
+
}
|
|
4278
|
+
return Array.from(byBucket.values()).sort((a, b) => a.order - b.order);
|
|
4279
|
+
}, [drafts]);
|
|
4280
|
+
const total = uniqueDrafts.length;
|
|
4281
|
+
const errors = uniqueDrafts.filter((d) => d.status === "error").length;
|
|
4207
4282
|
const isSingle = total === 1;
|
|
4208
4283
|
useEffect(() => {
|
|
4209
4284
|
if (!open) return;
|
|
@@ -4214,7 +4289,7 @@ var UnsavedTray = ({
|
|
|
4214
4289
|
return () => window.removeEventListener("mousedown", onDoc);
|
|
4215
4290
|
}, [open]);
|
|
4216
4291
|
if (total === 0) return null;
|
|
4217
|
-
const countLabel = isSingle ?
|
|
4292
|
+
const countLabel = isSingle ? uniqueDrafts[0].label || "this record" : countTemplate.replace("{n}", String(total));
|
|
4218
4293
|
return /* @__PURE__ */ jsxs(
|
|
4219
4294
|
"div",
|
|
4220
4295
|
{
|
|
@@ -4284,7 +4359,7 @@ var UnsavedTray = ({
|
|
|
4284
4359
|
}
|
|
4285
4360
|
)
|
|
4286
4361
|
] }),
|
|
4287
|
-
open && !isSingle && /* @__PURE__ */ jsx("div", { className: "ra-unsaved-popover", role: "menu", children:
|
|
4362
|
+
open && !isSingle && /* @__PURE__ */ jsx("div", { className: "ra-unsaved-popover", role: "menu", children: uniqueDrafts.map((d) => /* @__PURE__ */ jsxs(
|
|
4288
4363
|
"button",
|
|
4289
4364
|
{
|
|
4290
4365
|
type: "button",
|
|
@@ -5833,6 +5908,8 @@ function RecordsAdminShellInner(props) {
|
|
|
5833
5908
|
error: collectionItems.error,
|
|
5834
5909
|
onBack: onItemBack,
|
|
5835
5910
|
onSelect: onItemOpen,
|
|
5911
|
+
dirtyKeys,
|
|
5912
|
+
errorKeys,
|
|
5836
5913
|
i18n
|
|
5837
5914
|
}
|
|
5838
5915
|
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|