@proveanything/smartlinks-utils-ui 0.3.7 → 0.3.9
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-IGFYMNKL.js +28 -0
- package/dist/chunk-IGFYMNKL.js.map +1 -0
- package/dist/components/AssetPicker/index.js +1 -1
- package/dist/components/ConditionsEditor/index.js +1 -1
- package/dist/components/FontPicker/index.js +1 -1
- package/dist/components/IconPicker/index.js +1 -1
- package/dist/components/RecordsAdmin/index.d.ts +248 -13
- package/dist/components/RecordsAdmin/index.js +987 -58
- package/dist/components/RecordsAdmin/index.js.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-WFNEZQCD.js +0 -28
- package/dist/chunk-WFNEZQCD.js.map +0 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { styleInject } from '../../chunk-
|
|
1
|
+
import { styleInject } from '../../chunk-IGFYMNKL.js';
|
|
2
2
|
import { cn } from '../../chunk-L7FQ52F5.js';
|
|
3
3
|
import { createContext, useMemo, useState, useEffect, useCallback, useRef, useContext, createElement } from 'react';
|
|
4
|
-
import { ChevronDown, Database, Lightbulb, SearchX, Inbox, LayoutGrid, Eye, MoreHorizontal, Download, Upload, Trash2, Copy, Pencil, Plus, CircleDashed, ArrowDownLeft, CheckCircle2, Globe, Tag, Boxes, Layers, Package, Rows3, Image, List, ChevronRight, Eraser, Box, AlertTriangle, Info,
|
|
4
|
+
import { ChevronDown, Database, Lightbulb, SearchX, Inbox, LayoutGrid, Eye, MoreHorizontal, Download, Upload, Trash2, Copy, Pencil, Plus, CircleDashed, ArrowDownLeft, CheckCircle2, Globe, Tag, Boxes, Layers, Package, Rows3, Image, List, ChevronRight, Eraser, ClipboardPaste, Box, X, AlertTriangle, Info, HelpCircle, Search, CornerDownLeft, Circle, AlertCircle, Undo2, Save } from 'lucide-react';
|
|
5
5
|
import { useQueryClient, useInfiniteQuery, useQuery } from '@tanstack/react-query';
|
|
6
|
+
import { createPortal } from 'react-dom';
|
|
6
7
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
8
|
|
|
8
9
|
var DEFAULT_ICONS = {
|
|
@@ -83,6 +84,7 @@ var DEFAULT_I18N = {
|
|
|
83
84
|
unsavedPromptDiscard: "Discard changes",
|
|
84
85
|
unsavedPromptCancel: "Stay here",
|
|
85
86
|
unsavedPromptSave: "Save & continue",
|
|
87
|
+
unsavedBannerBody: "Unsaved changes on {name}",
|
|
86
88
|
presentationList: "List",
|
|
87
89
|
presentationGrid: "Grid",
|
|
88
90
|
presentationGallery: "Gallery",
|
|
@@ -91,7 +93,20 @@ var DEFAULT_I18N = {
|
|
|
91
93
|
noItemsTitle: "No items yet",
|
|
92
94
|
noItemsBody: "Create your first item to get started.",
|
|
93
95
|
railEmptyTitle: "No records yet",
|
|
94
|
-
railEmptyBody: "Records you create will appear here."
|
|
96
|
+
railEmptyBody: "Records you create will appear here.",
|
|
97
|
+
copy: "Copy",
|
|
98
|
+
paste: "Paste",
|
|
99
|
+
pasteFrom: "Paste from {name}",
|
|
100
|
+
pasteReplace: "Paste (replace)",
|
|
101
|
+
clipboardEmpty: "Nothing to paste",
|
|
102
|
+
copyToast: "Copied {name}. Pick a destination to paste.",
|
|
103
|
+
pasteToast: "Pasted from {name}. Review and Save.",
|
|
104
|
+
pasteConfirmTitle: "Replace existing values?",
|
|
105
|
+
pasteConfirmBody: "{destination} already has saved values. Replace them with the copied values?",
|
|
106
|
+
pasteConfirmReplace: "Replace",
|
|
107
|
+
pasteConfirmCancel: "Cancel",
|
|
108
|
+
pasteWarnTitle: "Cross-scope paste",
|
|
109
|
+
pasteWarnContinue: "Continue"
|
|
95
110
|
};
|
|
96
111
|
|
|
97
112
|
// src/components/RecordsAdmin/types/presentation.ts
|
|
@@ -455,12 +470,24 @@ var resolveRecord = async (args) => {
|
|
|
455
470
|
const editingScope = parsedRefToScope(args.target);
|
|
456
471
|
const result = await matchRecords(args.ctx, target, { strategy: "all" }).catch(() => null);
|
|
457
472
|
const records = result?.records ?? [];
|
|
473
|
+
console.info("[RecordsAdmin/resolveRecord]", {
|
|
474
|
+
editingScope,
|
|
475
|
+
target,
|
|
476
|
+
matchCount: records.length,
|
|
477
|
+
winnerScope: records[0]?.record?.scope,
|
|
478
|
+
winnerRef: records[0]?.record?.ref
|
|
479
|
+
});
|
|
458
480
|
if (records.length === 0) {
|
|
459
481
|
return { data: null, source: "empty" };
|
|
460
482
|
}
|
|
461
483
|
const winnerEntry = records[0];
|
|
462
484
|
const winner = winnerEntry.record;
|
|
463
485
|
const winnerIsSelf = scopesEqual(winner.scope, editingScope);
|
|
486
|
+
console.info("[RecordsAdmin/resolveRecord] classification", {
|
|
487
|
+
winnerIsSelf,
|
|
488
|
+
winnerScope: winner.scope,
|
|
489
|
+
editingScope
|
|
490
|
+
});
|
|
464
491
|
if (winnerIsSelf) {
|
|
465
492
|
const parent = records[1]?.record;
|
|
466
493
|
return {
|
|
@@ -1101,6 +1128,260 @@ var useDirtyNavigation = ({
|
|
|
1101
1128
|
);
|
|
1102
1129
|
return { runWithGuard };
|
|
1103
1130
|
};
|
|
1131
|
+
var ConfirmDialog = ({
|
|
1132
|
+
open,
|
|
1133
|
+
title,
|
|
1134
|
+
body,
|
|
1135
|
+
saveLabel,
|
|
1136
|
+
discardLabel,
|
|
1137
|
+
cancelLabel,
|
|
1138
|
+
onChoice
|
|
1139
|
+
}) => {
|
|
1140
|
+
const saveBtnRef = useRef(null);
|
|
1141
|
+
useEffect(() => {
|
|
1142
|
+
if (!open) return;
|
|
1143
|
+
const prev = document.body.style.overflow;
|
|
1144
|
+
document.body.style.overflow = "hidden";
|
|
1145
|
+
const t = window.setTimeout(() => saveBtnRef.current?.focus(), 0);
|
|
1146
|
+
const onKey = (e) => {
|
|
1147
|
+
if (e.key === "Escape") {
|
|
1148
|
+
e.stopPropagation();
|
|
1149
|
+
onChoice("cancel");
|
|
1150
|
+
}
|
|
1151
|
+
};
|
|
1152
|
+
window.addEventListener("keydown", onKey, true);
|
|
1153
|
+
return () => {
|
|
1154
|
+
window.clearTimeout(t);
|
|
1155
|
+
window.removeEventListener("keydown", onKey, true);
|
|
1156
|
+
document.body.style.overflow = prev;
|
|
1157
|
+
};
|
|
1158
|
+
}, [open, onChoice]);
|
|
1159
|
+
if (!open || typeof document === "undefined") return null;
|
|
1160
|
+
return createPortal(
|
|
1161
|
+
/* @__PURE__ */ jsxs(
|
|
1162
|
+
"div",
|
|
1163
|
+
{
|
|
1164
|
+
className: "ra-shell ra-confirm-root",
|
|
1165
|
+
role: "presentation",
|
|
1166
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
1167
|
+
onClick: (e) => e.stopPropagation(),
|
|
1168
|
+
children: [
|
|
1169
|
+
/* @__PURE__ */ jsx("div", { className: "ra-confirm-backdrop", onClick: () => onChoice("cancel") }),
|
|
1170
|
+
/* @__PURE__ */ jsxs(
|
|
1171
|
+
"div",
|
|
1172
|
+
{
|
|
1173
|
+
role: "alertdialog",
|
|
1174
|
+
"aria-modal": "true",
|
|
1175
|
+
"aria-labelledby": "ra-confirm-title",
|
|
1176
|
+
"aria-describedby": "ra-confirm-body",
|
|
1177
|
+
className: "ra-confirm-card",
|
|
1178
|
+
children: [
|
|
1179
|
+
/* @__PURE__ */ jsxs("div", { className: "ra-confirm-header", children: [
|
|
1180
|
+
/* @__PURE__ */ jsx("span", { className: "ra-confirm-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(AlertTriangle, { className: "w-4 h-4" }) }),
|
|
1181
|
+
/* @__PURE__ */ jsx("h2", { id: "ra-confirm-title", className: "ra-confirm-title", children: title })
|
|
1182
|
+
] }),
|
|
1183
|
+
/* @__PURE__ */ jsx("p", { id: "ra-confirm-body", className: "ra-confirm-body", children: body }),
|
|
1184
|
+
/* @__PURE__ */ jsxs("div", { className: "ra-confirm-actions", children: [
|
|
1185
|
+
/* @__PURE__ */ jsx(
|
|
1186
|
+
"button",
|
|
1187
|
+
{
|
|
1188
|
+
type: "button",
|
|
1189
|
+
className: "ra-confirm-btn ra-confirm-btn-ghost",
|
|
1190
|
+
onClick: () => onChoice("cancel"),
|
|
1191
|
+
children: cancelLabel
|
|
1192
|
+
}
|
|
1193
|
+
),
|
|
1194
|
+
/* @__PURE__ */ jsx(
|
|
1195
|
+
"button",
|
|
1196
|
+
{
|
|
1197
|
+
type: "button",
|
|
1198
|
+
className: "ra-confirm-btn ra-confirm-btn-danger",
|
|
1199
|
+
onClick: () => onChoice("discard"),
|
|
1200
|
+
children: discardLabel
|
|
1201
|
+
}
|
|
1202
|
+
),
|
|
1203
|
+
saveLabel ? /* @__PURE__ */ jsx(
|
|
1204
|
+
"button",
|
|
1205
|
+
{
|
|
1206
|
+
type: "button",
|
|
1207
|
+
ref: saveBtnRef,
|
|
1208
|
+
className: "ra-confirm-btn ra-confirm-btn-primary",
|
|
1209
|
+
onClick: () => onChoice("save"),
|
|
1210
|
+
children: saveLabel
|
|
1211
|
+
}
|
|
1212
|
+
) : null
|
|
1213
|
+
] })
|
|
1214
|
+
]
|
|
1215
|
+
}
|
|
1216
|
+
)
|
|
1217
|
+
]
|
|
1218
|
+
}
|
|
1219
|
+
),
|
|
1220
|
+
document.body
|
|
1221
|
+
);
|
|
1222
|
+
};
|
|
1223
|
+
var DEFAULTS = {
|
|
1224
|
+
title: "Unsaved changes",
|
|
1225
|
+
body: "You have unsaved changes. Save them, discard them, or stay on this record?",
|
|
1226
|
+
saveLabel: "Save & continue",
|
|
1227
|
+
discardLabel: "Discard",
|
|
1228
|
+
cancelLabel: "Stay here"
|
|
1229
|
+
};
|
|
1230
|
+
var useConfirmDialog = () => {
|
|
1231
|
+
const [open, setOpen] = useState(false);
|
|
1232
|
+
const [state, setState] = useState(DEFAULTS);
|
|
1233
|
+
const resolverRef = useRef(null);
|
|
1234
|
+
const confirm = useCallback((i18n) => {
|
|
1235
|
+
setState({
|
|
1236
|
+
title: i18n?.title ?? DEFAULTS.title,
|
|
1237
|
+
body: i18n?.body ?? DEFAULTS.body,
|
|
1238
|
+
saveLabel: i18n?.save ?? DEFAULTS.saveLabel,
|
|
1239
|
+
discardLabel: i18n?.discard ?? DEFAULTS.discardLabel,
|
|
1240
|
+
cancelLabel: i18n?.cancel ?? DEFAULTS.cancelLabel
|
|
1241
|
+
});
|
|
1242
|
+
setOpen(true);
|
|
1243
|
+
return new Promise((resolve) => {
|
|
1244
|
+
resolverRef.current = resolve;
|
|
1245
|
+
});
|
|
1246
|
+
}, []);
|
|
1247
|
+
const handleChoice = useCallback((choice) => {
|
|
1248
|
+
setOpen(false);
|
|
1249
|
+
const r = resolverRef.current;
|
|
1250
|
+
resolverRef.current = null;
|
|
1251
|
+
r?.(choice);
|
|
1252
|
+
}, []);
|
|
1253
|
+
return {
|
|
1254
|
+
confirm,
|
|
1255
|
+
dialog: /* @__PURE__ */ jsx(
|
|
1256
|
+
ConfirmDialog,
|
|
1257
|
+
{
|
|
1258
|
+
open,
|
|
1259
|
+
title: state.title,
|
|
1260
|
+
body: state.body,
|
|
1261
|
+
saveLabel: state.saveLabel,
|
|
1262
|
+
discardLabel: state.discardLabel,
|
|
1263
|
+
cancelLabel: state.cancelLabel,
|
|
1264
|
+
onChoice: handleChoice
|
|
1265
|
+
}
|
|
1266
|
+
)
|
|
1267
|
+
};
|
|
1268
|
+
};
|
|
1269
|
+
var DEFAULTS2 = {
|
|
1270
|
+
title: "Replace existing values?",
|
|
1271
|
+
body: "This destination already has saved values. Replace them?",
|
|
1272
|
+
confirmLabel: "Replace",
|
|
1273
|
+
cancelLabel: "Cancel"
|
|
1274
|
+
};
|
|
1275
|
+
var usePasteConfirm = () => {
|
|
1276
|
+
const [open, setOpen] = useState(false);
|
|
1277
|
+
const [state, setState] = useState(DEFAULTS2);
|
|
1278
|
+
const resolverRef = useRef(null);
|
|
1279
|
+
const confirm = useCallback((i18n) => {
|
|
1280
|
+
setState({ ...DEFAULTS2, ...i18n ?? {} });
|
|
1281
|
+
setOpen(true);
|
|
1282
|
+
return new Promise((resolve) => {
|
|
1283
|
+
resolverRef.current = resolve;
|
|
1284
|
+
});
|
|
1285
|
+
}, []);
|
|
1286
|
+
const handleChoice = useCallback((choice) => {
|
|
1287
|
+
setOpen(false);
|
|
1288
|
+
const r = resolverRef.current;
|
|
1289
|
+
resolverRef.current = null;
|
|
1290
|
+
r?.(choice === "discard");
|
|
1291
|
+
}, []);
|
|
1292
|
+
return {
|
|
1293
|
+
confirm,
|
|
1294
|
+
dialog: /* @__PURE__ */ jsx(
|
|
1295
|
+
ConfirmDialog,
|
|
1296
|
+
{
|
|
1297
|
+
open,
|
|
1298
|
+
title: state.title,
|
|
1299
|
+
body: state.body,
|
|
1300
|
+
saveLabel: "",
|
|
1301
|
+
discardLabel: state.confirmLabel,
|
|
1302
|
+
cancelLabel: state.cancelLabel,
|
|
1303
|
+
onChoice: handleChoice
|
|
1304
|
+
}
|
|
1305
|
+
)
|
|
1306
|
+
};
|
|
1307
|
+
};
|
|
1308
|
+
var stores = /* @__PURE__ */ new Map();
|
|
1309
|
+
var storageKey = (key) => `ra:clipboard:${key}`;
|
|
1310
|
+
var getStore = (key) => {
|
|
1311
|
+
let s = stores.get(key);
|
|
1312
|
+
if (!s) {
|
|
1313
|
+
s = { entry: null, listeners: /* @__PURE__ */ new Set() };
|
|
1314
|
+
if (typeof window !== "undefined") {
|
|
1315
|
+
try {
|
|
1316
|
+
const raw = window.sessionStorage.getItem(storageKey(key));
|
|
1317
|
+
if (raw) s.entry = JSON.parse(raw);
|
|
1318
|
+
} catch {
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
stores.set(key, s);
|
|
1322
|
+
}
|
|
1323
|
+
return s;
|
|
1324
|
+
};
|
|
1325
|
+
var persist = (key, entry) => {
|
|
1326
|
+
if (typeof window === "undefined") return;
|
|
1327
|
+
try {
|
|
1328
|
+
if (entry) window.sessionStorage.setItem(storageKey(key), JSON.stringify(entry));
|
|
1329
|
+
else window.sessionStorage.removeItem(storageKey(key));
|
|
1330
|
+
} catch {
|
|
1331
|
+
}
|
|
1332
|
+
};
|
|
1333
|
+
var notify = (store) => {
|
|
1334
|
+
store.listeners.forEach((l) => l());
|
|
1335
|
+
};
|
|
1336
|
+
var composeKey = (appId, recordType) => `${appId}::${recordType}`;
|
|
1337
|
+
function useRecordClipboard(args) {
|
|
1338
|
+
const key = composeKey(args.appId, args.recordType);
|
|
1339
|
+
const store = getStore(key);
|
|
1340
|
+
const [entry, setEntry] = useState(
|
|
1341
|
+
store.entry
|
|
1342
|
+
);
|
|
1343
|
+
useEffect(() => {
|
|
1344
|
+
const listener = () => setEntry(store.entry);
|
|
1345
|
+
store.listeners.add(listener);
|
|
1346
|
+
listener();
|
|
1347
|
+
return () => {
|
|
1348
|
+
store.listeners.delete(listener);
|
|
1349
|
+
};
|
|
1350
|
+
}, [store]);
|
|
1351
|
+
const set = useCallback((next) => {
|
|
1352
|
+
const full = { ...next, copiedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1353
|
+
store.entry = full;
|
|
1354
|
+
persist(key, full);
|
|
1355
|
+
notify(store);
|
|
1356
|
+
}, [store, key]);
|
|
1357
|
+
const clear = useCallback(() => {
|
|
1358
|
+
store.entry = null;
|
|
1359
|
+
persist(key, null);
|
|
1360
|
+
notify(store);
|
|
1361
|
+
}, [store, key]);
|
|
1362
|
+
return { entry, hasEntry: entry != null, set, clear };
|
|
1363
|
+
}
|
|
1364
|
+
function checkPasteCompatibility(source, destination) {
|
|
1365
|
+
const from = source.kind;
|
|
1366
|
+
const to = destination.kind;
|
|
1367
|
+
if (from === to) return { status: "allowed" };
|
|
1368
|
+
if ((from === "variant" || from === "batch") && to === "product") {
|
|
1369
|
+
return { status: "warn", reason: "This will overwrite product-level values." };
|
|
1370
|
+
}
|
|
1371
|
+
return { status: "allowed" };
|
|
1372
|
+
}
|
|
1373
|
+
function cloneValue(value) {
|
|
1374
|
+
if (value == null) return value;
|
|
1375
|
+
try {
|
|
1376
|
+
return structuredClone(value);
|
|
1377
|
+
} catch {
|
|
1378
|
+
try {
|
|
1379
|
+
return JSON.parse(JSON.stringify(value));
|
|
1380
|
+
} catch {
|
|
1381
|
+
return value;
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1104
1385
|
var LABELS = {
|
|
1105
1386
|
product: "Products",
|
|
1106
1387
|
facet: "Shared",
|
|
@@ -1139,14 +1420,17 @@ var ScopeTabs = ({
|
|
|
1139
1420
|
);
|
|
1140
1421
|
}) });
|
|
1141
1422
|
};
|
|
1142
|
-
var StatusFilterPills = ({ value, onChange, counts, i18n }) => {
|
|
1423
|
+
var StatusFilterPills = ({ value, onChange, counts, i18n, hideZero }) => {
|
|
1143
1424
|
const opts = [
|
|
1144
1425
|
{ key: "all", label: i18n.filterAll, count: counts.all },
|
|
1145
1426
|
{ key: "configured", label: i18n.filterConfigured, count: counts.configured },
|
|
1146
1427
|
{ key: "partial", label: i18n.filterPartial, count: counts.partial },
|
|
1147
1428
|
{ key: "empty", label: i18n.filterEmpty, count: counts.empty }
|
|
1148
1429
|
];
|
|
1149
|
-
|
|
1430
|
+
const visible = opts.filter(
|
|
1431
|
+
(o) => o.key === value || !hideZero?.includes(o.key) || o.count > 0
|
|
1432
|
+
);
|
|
1433
|
+
return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: visible.map((o) => {
|
|
1150
1434
|
const active = value === o.key;
|
|
1151
1435
|
return /* @__PURE__ */ jsxs(
|
|
1152
1436
|
"button",
|
|
@@ -1177,8 +1461,90 @@ var StatusDot = ({ source, status, className }) => {
|
|
|
1177
1461
|
else if (source === "inherited" || status === "partial") cls = "ra-status-shared";
|
|
1178
1462
|
return /* @__PURE__ */ jsx("span", { className: cn("ra-status-dot", cls, className), "aria-hidden": "true" });
|
|
1179
1463
|
};
|
|
1464
|
+
var RowContextMenu = ({
|
|
1465
|
+
onCopy,
|
|
1466
|
+
onPaste,
|
|
1467
|
+
canPaste,
|
|
1468
|
+
pasteWillReplace,
|
|
1469
|
+
pasteSourceLabel,
|
|
1470
|
+
i18n
|
|
1471
|
+
}) => {
|
|
1472
|
+
const [open, setOpen] = useState(false);
|
|
1473
|
+
const wrapperRef = useRef(null);
|
|
1474
|
+
useEffect(() => {
|
|
1475
|
+
if (!open) return;
|
|
1476
|
+
const onDoc = (e) => {
|
|
1477
|
+
if (!wrapperRef.current?.contains(e.target)) setOpen(false);
|
|
1478
|
+
};
|
|
1479
|
+
const onKey = (e) => {
|
|
1480
|
+
if (e.key === "Escape") setOpen(false);
|
|
1481
|
+
};
|
|
1482
|
+
document.addEventListener("mousedown", onDoc);
|
|
1483
|
+
document.addEventListener("keydown", onKey);
|
|
1484
|
+
return () => {
|
|
1485
|
+
document.removeEventListener("mousedown", onDoc);
|
|
1486
|
+
document.removeEventListener("keydown", onKey);
|
|
1487
|
+
};
|
|
1488
|
+
}, [open]);
|
|
1489
|
+
if (!onCopy && !onPaste) return null;
|
|
1490
|
+
const pasteLabel = !canPaste ? i18n.clipboardEmpty : pasteSourceLabel ? i18n.pasteFrom.replace("{name}", pasteSourceLabel) : pasteWillReplace ? i18n.pasteReplace : i18n.paste;
|
|
1491
|
+
return /* @__PURE__ */ jsxs("div", { ref: wrapperRef, className: "ra-row-menu-wrap relative", children: [
|
|
1492
|
+
/* @__PURE__ */ jsx(
|
|
1493
|
+
"button",
|
|
1494
|
+
{
|
|
1495
|
+
type: "button",
|
|
1496
|
+
className: "ra-row-menu-trigger",
|
|
1497
|
+
"aria-label": "Row actions",
|
|
1498
|
+
"aria-haspopup": "menu",
|
|
1499
|
+
"aria-expanded": open,
|
|
1500
|
+
onClick: (e) => {
|
|
1501
|
+
e.stopPropagation();
|
|
1502
|
+
setOpen((v) => !v);
|
|
1503
|
+
},
|
|
1504
|
+
children: /* @__PURE__ */ jsx(MoreHorizontal, { className: "w-3.5 h-3.5", "aria-hidden": "true" })
|
|
1505
|
+
}
|
|
1506
|
+
),
|
|
1507
|
+
open && /* @__PURE__ */ jsxs("div", { role: "menu", className: "ra-row-menu", onClick: (e) => e.stopPropagation(), children: [
|
|
1508
|
+
onCopy && /* @__PURE__ */ jsxs(
|
|
1509
|
+
"button",
|
|
1510
|
+
{
|
|
1511
|
+
type: "button",
|
|
1512
|
+
role: "menuitem",
|
|
1513
|
+
className: "ra-row-menu-item",
|
|
1514
|
+
onClick: (e) => {
|
|
1515
|
+
e.stopPropagation();
|
|
1516
|
+
setOpen(false);
|
|
1517
|
+
onCopy();
|
|
1518
|
+
},
|
|
1519
|
+
children: [
|
|
1520
|
+
/* @__PURE__ */ jsx(Copy, { className: "w-3 h-3", "aria-hidden": "true" }),
|
|
1521
|
+
/* @__PURE__ */ jsx("span", { children: i18n.copy })
|
|
1522
|
+
]
|
|
1523
|
+
}
|
|
1524
|
+
),
|
|
1525
|
+
onPaste && /* @__PURE__ */ jsxs(
|
|
1526
|
+
"button",
|
|
1527
|
+
{
|
|
1528
|
+
type: "button",
|
|
1529
|
+
role: "menuitem",
|
|
1530
|
+
className: "ra-row-menu-item",
|
|
1531
|
+
disabled: !canPaste,
|
|
1532
|
+
onClick: (e) => {
|
|
1533
|
+
e.stopPropagation();
|
|
1534
|
+
setOpen(false);
|
|
1535
|
+
if (canPaste) onPaste();
|
|
1536
|
+
},
|
|
1537
|
+
children: [
|
|
1538
|
+
/* @__PURE__ */ jsx(ClipboardPaste, { className: "w-3 h-3", "aria-hidden": "true" }),
|
|
1539
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: pasteLabel })
|
|
1540
|
+
]
|
|
1541
|
+
}
|
|
1542
|
+
)
|
|
1543
|
+
] })
|
|
1544
|
+
] });
|
|
1545
|
+
};
|
|
1180
1546
|
var DefaultRecordRow = ({ record, ctx, compact = false }) => {
|
|
1181
|
-
const { selected, onSelect, isDirty } = ctx;
|
|
1547
|
+
const { selected, onSelect, isDirty, onCopy, onPaste, canPaste, pasteWillReplace, clipboardSourceLabel } = ctx;
|
|
1182
1548
|
const ScopeIcon = record.scope.kind && record.scope.kind !== "collection" ? DEFAULT_ICONS.scope[record.scope.kind] : DEFAULT_ICONS.scope.product;
|
|
1183
1549
|
return /* @__PURE__ */ jsxs(
|
|
1184
1550
|
"button",
|
|
@@ -1203,6 +1569,23 @@ var DefaultRecordRow = ({ record, ctx, compact = false }) => {
|
|
|
1203
1569
|
"aria-label": "Unsaved changes",
|
|
1204
1570
|
className: "ra-status-dot ra-status-shared shrink-0"
|
|
1205
1571
|
}
|
|
1572
|
+
),
|
|
1573
|
+
(onCopy || onPaste) && /* @__PURE__ */ jsx(
|
|
1574
|
+
RowContextMenu,
|
|
1575
|
+
{
|
|
1576
|
+
onCopy,
|
|
1577
|
+
onPaste,
|
|
1578
|
+
canPaste,
|
|
1579
|
+
pasteWillReplace,
|
|
1580
|
+
pasteSourceLabel: clipboardSourceLabel,
|
|
1581
|
+
i18n: {
|
|
1582
|
+
copy: DEFAULT_I18N.copy,
|
|
1583
|
+
paste: DEFAULT_I18N.paste,
|
|
1584
|
+
pasteFrom: DEFAULT_I18N.pasteFrom,
|
|
1585
|
+
pasteReplace: DEFAULT_I18N.pasteReplace,
|
|
1586
|
+
clipboardEmpty: DEFAULT_I18N.clipboardEmpty
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1206
1589
|
)
|
|
1207
1590
|
]
|
|
1208
1591
|
}
|
|
@@ -1210,7 +1593,7 @@ var DefaultRecordRow = ({ record, ctx, compact = false }) => {
|
|
|
1210
1593
|
};
|
|
1211
1594
|
var initials = (s) => s.split(/\s+/).filter(Boolean).slice(0, 2).map((p) => p[0]?.toUpperCase() ?? "").join("") || "?";
|
|
1212
1595
|
var DefaultRecordCard = ({ record, ctx, variant = "grid" }) => {
|
|
1213
|
-
const { selected, onSelect, isDirty } = ctx;
|
|
1596
|
+
const { selected, onSelect, isDirty, onCopy, onPaste, canPaste, pasteWillReplace, clipboardSourceLabel } = ctx;
|
|
1214
1597
|
const aspect = variant === "gallery" ? "aspect-video" : "aspect-square";
|
|
1215
1598
|
return /* @__PURE__ */ jsxs(
|
|
1216
1599
|
"button",
|
|
@@ -1262,7 +1645,26 @@ var DefaultRecordCard = ({ record, ctx, variant = "grid" }) => {
|
|
|
1262
1645
|
}
|
|
1263
1646
|
),
|
|
1264
1647
|
/* @__PURE__ */ jsxs("div", { className: "p-2.5 min-w-0", children: [
|
|
1265
|
-
/* @__PURE__ */
|
|
1648
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-1.5 min-w-0", children: [
|
|
1649
|
+
/* @__PURE__ */ jsx("div", { className: "ra-row-title flex-1 min-w-0", children: record.label }),
|
|
1650
|
+
(onCopy || onPaste) && /* @__PURE__ */ jsx(
|
|
1651
|
+
RowContextMenu,
|
|
1652
|
+
{
|
|
1653
|
+
onCopy,
|
|
1654
|
+
onPaste,
|
|
1655
|
+
canPaste,
|
|
1656
|
+
pasteWillReplace,
|
|
1657
|
+
pasteSourceLabel: clipboardSourceLabel,
|
|
1658
|
+
i18n: {
|
|
1659
|
+
copy: DEFAULT_I18N.copy,
|
|
1660
|
+
paste: DEFAULT_I18N.paste,
|
|
1661
|
+
pasteFrom: DEFAULT_I18N.pasteFrom,
|
|
1662
|
+
pasteReplace: DEFAULT_I18N.pasteReplace,
|
|
1663
|
+
clipboardEmpty: DEFAULT_I18N.clipboardEmpty
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
)
|
|
1667
|
+
] }),
|
|
1266
1668
|
variant === "gallery" && record.subtitle && /* @__PURE__ */ jsx("div", { className: "ra-row-sub", children: record.subtitle }),
|
|
1267
1669
|
record.badges && record.badges.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex gap-1 mt-1.5 flex-wrap", children: record.badges.slice(0, 3).map((b, i) => /* @__PURE__ */ jsx("span", { className: "ra-chip", "data-tone": "muted", children: b.label }, `${b.label}-${i}`)) })
|
|
1268
1670
|
] })
|
|
@@ -1278,13 +1680,18 @@ var RecordList = ({
|
|
|
1278
1680
|
presentation = "list",
|
|
1279
1681
|
renderListRow,
|
|
1280
1682
|
renderCard,
|
|
1281
|
-
groupBy
|
|
1683
|
+
groupBy,
|
|
1684
|
+
rowClipboard
|
|
1282
1685
|
}) => {
|
|
1283
|
-
const buildCtx = (item) =>
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1686
|
+
const buildCtx = (item) => {
|
|
1687
|
+
const cb = rowClipboard ? rowClipboard(item) : null;
|
|
1688
|
+
return {
|
|
1689
|
+
selected: item.ref === selectedRef,
|
|
1690
|
+
onSelect: () => onSelect(item),
|
|
1691
|
+
isDirty: !!dirtyRef && item.ref === dirtyRef,
|
|
1692
|
+
...cb ?? {}
|
|
1693
|
+
};
|
|
1694
|
+
};
|
|
1288
1695
|
const groups = useMemo(() => {
|
|
1289
1696
|
if (!groupBy) return null;
|
|
1290
1697
|
const buckets = /* @__PURE__ */ new Map();
|
|
@@ -1566,8 +1973,10 @@ var DeleteButton = ({
|
|
|
1566
1973
|
label = "Delete record",
|
|
1567
1974
|
confirmLabel = "Confirm delete",
|
|
1568
1975
|
revertMs = 3e3,
|
|
1569
|
-
disabled
|
|
1976
|
+
disabled,
|
|
1977
|
+
icon
|
|
1570
1978
|
}) => {
|
|
1979
|
+
const Icon = icon ?? Trash2;
|
|
1571
1980
|
const [armed, setArmed] = useState(false);
|
|
1572
1981
|
const [busy, setBusy] = useState(false);
|
|
1573
1982
|
const timerRef = useRef(null);
|
|
@@ -1628,7 +2037,7 @@ var DeleteButton = ({
|
|
|
1628
2037
|
color: "hsl(var(--ra-danger, var(--ra-text)))"
|
|
1629
2038
|
},
|
|
1630
2039
|
children: [
|
|
1631
|
-
/* @__PURE__ */ jsx(
|
|
2040
|
+
/* @__PURE__ */ jsx(Icon, { className: "w-3.5 h-3.5" }),
|
|
1632
2041
|
label
|
|
1633
2042
|
]
|
|
1634
2043
|
}
|
|
@@ -1641,25 +2050,58 @@ function RecordEditor({
|
|
|
1641
2050
|
preview,
|
|
1642
2051
|
bulkActions,
|
|
1643
2052
|
footerExtra,
|
|
1644
|
-
onBeforeDelete
|
|
2053
|
+
onBeforeDelete,
|
|
2054
|
+
headerLabel,
|
|
2055
|
+
headerSubtitle,
|
|
2056
|
+
headerMeta,
|
|
2057
|
+
clipboard,
|
|
2058
|
+
actionLabels,
|
|
2059
|
+
actionIcons
|
|
1645
2060
|
}) {
|
|
1646
|
-
const
|
|
2061
|
+
const saveLabel = actionLabels?.save ?? i18n.save;
|
|
2062
|
+
const discardLabel = actionLabels?.discard ?? i18n.discard;
|
|
2063
|
+
const deleteLabel = actionLabels?.delete ?? i18n.delete;
|
|
2064
|
+
const SaveIcon = actionIcons?.save;
|
|
2065
|
+
const DiscardIcon = actionIcons?.discard;
|
|
2066
|
+
const DeleteIcon = actionIcons?.delete;
|
|
2067
|
+
const sourceLabel = ctx.source === "self" ? "Customised" : ctx.source === "inherited" ? "Inherited" : null;
|
|
1647
2068
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
|
|
1648
2069
|
/* @__PURE__ */ jsxs(
|
|
1649
2070
|
"header",
|
|
1650
2071
|
{
|
|
1651
|
-
className: "sticky top-0 z-
|
|
2072
|
+
className: "sticky top-0 z-40 px-5 py-3 border-b flex items-start justify-between gap-3",
|
|
1652
2073
|
style: { borderColor: "hsl(var(--ra-border))", background: "hsl(var(--ra-surface))" },
|
|
1653
2074
|
children: [
|
|
1654
2075
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
1655
|
-
/* @__PURE__ */
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
2076
|
+
headerLabel ? /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
2077
|
+
/* @__PURE__ */ jsx(
|
|
2078
|
+
"div",
|
|
2079
|
+
{
|
|
2080
|
+
className: "text-sm font-medium truncate",
|
|
2081
|
+
style: { color: "hsl(var(--ra-text))" },
|
|
2082
|
+
title: headerLabel,
|
|
2083
|
+
children: headerLabel
|
|
2084
|
+
}
|
|
2085
|
+
),
|
|
2086
|
+
headerSubtitle && /* @__PURE__ */ jsx(
|
|
2087
|
+
"div",
|
|
2088
|
+
{
|
|
2089
|
+
className: "text-xs truncate",
|
|
2090
|
+
style: { color: "hsl(var(--ra-muted-text))" },
|
|
2091
|
+
title: headerSubtitle,
|
|
2092
|
+
children: headerSubtitle
|
|
2093
|
+
}
|
|
2094
|
+
)
|
|
2095
|
+
] }) : /* @__PURE__ */ jsx(ScopeBreadcrumb, { scope: ctx.scope }),
|
|
2096
|
+
(sourceLabel || ctx.isDirty) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 mt-1", children: [
|
|
2097
|
+
sourceLabel && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2098
|
+
/* @__PURE__ */ jsx(StatusDot, { source: ctx.source }),
|
|
2099
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] uppercase tracking-wide", style: { color: "hsl(var(--ra-muted-text))" }, children: sourceLabel })
|
|
2100
|
+
] }),
|
|
1659
2101
|
ctx.isDirty && /* @__PURE__ */ jsxs(
|
|
1660
2102
|
"span",
|
|
1661
2103
|
{
|
|
1662
|
-
className:
|
|
2104
|
+
className: `flex items-center gap-1 text-[10px] uppercase tracking-wide ${sourceLabel ? "ml-2" : ""}`,
|
|
1663
2105
|
style: { color: "hsl(var(--ra-accent))" },
|
|
1664
2106
|
children: [
|
|
1665
2107
|
/* @__PURE__ */ jsx("span", { className: "ra-status-dot ra-status-shared", "aria-hidden": "true" }),
|
|
@@ -1669,7 +2111,22 @@ function RecordEditor({
|
|
|
1669
2111
|
)
|
|
1670
2112
|
] })
|
|
1671
2113
|
] }),
|
|
1672
|
-
|
|
2114
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 shrink-0", children: [
|
|
2115
|
+
headerMeta && /* @__PURE__ */ jsx(
|
|
2116
|
+
"code",
|
|
2117
|
+
{
|
|
2118
|
+
className: "text-[10px] font-mono px-1.5 py-0.5 rounded truncate max-w-[14rem] mt-0.5",
|
|
2119
|
+
style: {
|
|
2120
|
+
color: "hsl(var(--ra-muted-text) / 0.85)",
|
|
2121
|
+
background: "hsl(var(--ra-muted) / 0.5)",
|
|
2122
|
+
border: "1px solid hsl(var(--ra-border) / 0.6)"
|
|
2123
|
+
},
|
|
2124
|
+
title: headerMeta,
|
|
2125
|
+
children: headerMeta
|
|
2126
|
+
}
|
|
2127
|
+
),
|
|
2128
|
+
bulkActions && /* @__PURE__ */ jsx(BulkActionsMenu, { ...bulkActions, i18n })
|
|
2129
|
+
] })
|
|
1673
2130
|
]
|
|
1674
2131
|
}
|
|
1675
2132
|
),
|
|
@@ -1684,15 +2141,18 @@ function RecordEditor({
|
|
|
1684
2141
|
style: { borderColor: "hsl(var(--ra-border))", background: "hsl(var(--ra-surface))" },
|
|
1685
2142
|
children: [
|
|
1686
2143
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1687
|
-
/* @__PURE__ */
|
|
2144
|
+
/* @__PURE__ */ jsxs(
|
|
1688
2145
|
"button",
|
|
1689
2146
|
{
|
|
1690
2147
|
type: "button",
|
|
1691
2148
|
onClick: ctx.reset,
|
|
1692
2149
|
disabled: !ctx.isDirty || !!ctx.isSaving,
|
|
1693
|
-
className: "text-xs px-3 py-1.5 rounded-md border transition-opacity disabled:opacity-40 hover:bg-[hsl(var(--ra-muted))]",
|
|
2150
|
+
className: "text-xs px-3 py-1.5 rounded-md border transition-opacity disabled:opacity-40 hover:bg-[hsl(var(--ra-muted))] inline-flex items-center gap-1.5",
|
|
1694
2151
|
style: { borderColor: "hsl(var(--ra-border))", color: "hsl(var(--ra-text))" },
|
|
1695
|
-
children:
|
|
2152
|
+
children: [
|
|
2153
|
+
DiscardIcon && /* @__PURE__ */ jsx(DiscardIcon, { className: "h-4 w-4" }),
|
|
2154
|
+
discardLabel
|
|
2155
|
+
]
|
|
1696
2156
|
}
|
|
1697
2157
|
),
|
|
1698
2158
|
ctx.canRemove && /* @__PURE__ */ jsx(
|
|
@@ -1700,10 +2160,45 @@ function RecordEditor({
|
|
|
1700
2160
|
{
|
|
1701
2161
|
onConfirm: () => ctx.remove(),
|
|
1702
2162
|
onBeforeDelete,
|
|
1703
|
-
label:
|
|
1704
|
-
confirmLabel: i18n.confirmDelete
|
|
2163
|
+
label: deleteLabel,
|
|
2164
|
+
confirmLabel: i18n.confirmDelete,
|
|
2165
|
+
icon: DeleteIcon
|
|
1705
2166
|
}
|
|
1706
2167
|
),
|
|
2168
|
+
clipboard && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2169
|
+
/* @__PURE__ */ jsxs(
|
|
2170
|
+
"button",
|
|
2171
|
+
{
|
|
2172
|
+
type: "button",
|
|
2173
|
+
onClick: clipboard.onCopy,
|
|
2174
|
+
disabled: !clipboard.canCopy || !!ctx.isSaving,
|
|
2175
|
+
title: i18n.copy,
|
|
2176
|
+
"aria-label": i18n.copy,
|
|
2177
|
+
className: "text-xs px-2.5 py-1.5 rounded-md border transition-opacity disabled:opacity-40 hover:bg-[hsl(var(--ra-muted))] inline-flex items-center gap-1.5",
|
|
2178
|
+
style: { borderColor: "hsl(var(--ra-border))", color: "hsl(var(--ra-text))" },
|
|
2179
|
+
children: [
|
|
2180
|
+
/* @__PURE__ */ jsx(Copy, { className: "w-3 h-3" }),
|
|
2181
|
+
i18n.copy
|
|
2182
|
+
]
|
|
2183
|
+
}
|
|
2184
|
+
),
|
|
2185
|
+
/* @__PURE__ */ jsxs(
|
|
2186
|
+
"button",
|
|
2187
|
+
{
|
|
2188
|
+
type: "button",
|
|
2189
|
+
onClick: clipboard.onPaste,
|
|
2190
|
+
disabled: !clipboard.canPaste || !!ctx.isSaving,
|
|
2191
|
+
title: !clipboard.canPaste ? i18n.clipboardEmpty : clipboard.pasteSourceLabel ? i18n.pasteFrom.replace("{name}", clipboard.pasteSourceLabel) : i18n.paste,
|
|
2192
|
+
"aria-label": i18n.paste,
|
|
2193
|
+
className: "text-xs px-2.5 py-1.5 rounded-md border transition-opacity disabled:opacity-40 hover:bg-[hsl(var(--ra-muted))] inline-flex items-center gap-1.5",
|
|
2194
|
+
style: { borderColor: "hsl(var(--ra-border))", color: "hsl(var(--ra-text))" },
|
|
2195
|
+
children: [
|
|
2196
|
+
/* @__PURE__ */ jsx(ClipboardPaste, { className: "w-3 h-3" }),
|
|
2197
|
+
clipboard.pasteWillReplace ? i18n.pasteReplace : i18n.paste
|
|
2198
|
+
]
|
|
2199
|
+
}
|
|
2200
|
+
)
|
|
2201
|
+
] }),
|
|
1707
2202
|
footerExtra,
|
|
1708
2203
|
ctx.saveError != null && !ctx.isSaving && /* @__PURE__ */ jsx(
|
|
1709
2204
|
"span",
|
|
@@ -1715,7 +2210,7 @@ function RecordEditor({
|
|
|
1715
2210
|
}
|
|
1716
2211
|
)
|
|
1717
2212
|
] }),
|
|
1718
|
-
/* @__PURE__ */
|
|
2213
|
+
/* @__PURE__ */ jsxs(
|
|
1719
2214
|
"button",
|
|
1720
2215
|
{
|
|
1721
2216
|
type: "button",
|
|
@@ -1724,9 +2219,12 @@ function RecordEditor({
|
|
|
1724
2219
|
});
|
|
1725
2220
|
},
|
|
1726
2221
|
disabled: !ctx.isDirty || !!ctx.isSaving,
|
|
1727
|
-
className: "text-xs px-3 py-1.5 rounded-md font-medium transition-opacity disabled:opacity-40",
|
|
2222
|
+
className: "text-xs px-3 py-1.5 rounded-md font-medium transition-opacity disabled:opacity-40 inline-flex items-center gap-1.5",
|
|
1728
2223
|
style: { background: "hsl(var(--ra-accent))", color: "hsl(var(--ra-surface))" },
|
|
1729
|
-
children:
|
|
2224
|
+
children: [
|
|
2225
|
+
SaveIcon && !ctx.isSaving && /* @__PURE__ */ jsx(SaveIcon, { className: "h-4 w-4" }),
|
|
2226
|
+
ctx.isSaving ? i18n.saving ?? "Saving\u2026" : saveLabel
|
|
2227
|
+
]
|
|
1730
2228
|
}
|
|
1731
2229
|
)
|
|
1732
2230
|
]
|
|
@@ -1751,16 +2249,18 @@ var ProductDrillDown = ({
|
|
|
1751
2249
|
batches,
|
|
1752
2250
|
variantsLoading,
|
|
1753
2251
|
batchesLoading,
|
|
1754
|
-
children
|
|
2252
|
+
children,
|
|
2253
|
+
hideSingleTab
|
|
1755
2254
|
}) => {
|
|
1756
2255
|
const tabs = ["product"];
|
|
1757
2256
|
if (showVariants) tabs.push("variant");
|
|
1758
2257
|
if (showBatches) tabs.push("batch");
|
|
2258
|
+
const showTabStrip = tabs.length > 1 || !hideSingleTab;
|
|
1759
2259
|
const childList = active === "variant" ? variants : active === "batch" ? batches : [];
|
|
1760
2260
|
const childLoading = active === "variant" ? variantsLoading : active === "batch" ? batchesLoading : false;
|
|
1761
2261
|
const childEmptyLabel = active === "variant" ? "No variants" : "No batches";
|
|
1762
2262
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
|
|
1763
|
-
/* @__PURE__ */ jsx(
|
|
2263
|
+
showTabStrip && /* @__PURE__ */ jsx(
|
|
1764
2264
|
"div",
|
|
1765
2265
|
{
|
|
1766
2266
|
className: "flex items-center gap-1 px-4 pt-3 border-b",
|
|
@@ -1884,13 +2384,29 @@ var InlinePreview = ({ children, scopePicker, label = "Preview" }) => /* @__PURE
|
|
|
1884
2384
|
] }),
|
|
1885
2385
|
children
|
|
1886
2386
|
] });
|
|
1887
|
-
var SidePreview = ({
|
|
2387
|
+
var SidePreview = ({
|
|
2388
|
+
children,
|
|
2389
|
+
scopePicker,
|
|
2390
|
+
label = "Preview",
|
|
2391
|
+
onClose
|
|
2392
|
+
}) => /* @__PURE__ */ jsxs("div", { className: "ra-preview-rail", children: [
|
|
1888
2393
|
/* @__PURE__ */ jsxs("header", { className: "ra-preview-rail-header", children: [
|
|
1889
2394
|
/* @__PURE__ */ jsxs("div", { className: "ra-preview-rail-title", children: [
|
|
1890
2395
|
/* @__PURE__ */ jsx(Eye, { className: "w-3 h-3" }),
|
|
1891
2396
|
label
|
|
1892
2397
|
] }),
|
|
1893
|
-
scopePicker && /* @__PURE__ */ jsx("div", { className: "ml-auto", children: scopePicker })
|
|
2398
|
+
scopePicker && /* @__PURE__ */ jsx("div", { className: "ml-auto", children: scopePicker }),
|
|
2399
|
+
onClose && /* @__PURE__ */ jsx(
|
|
2400
|
+
"button",
|
|
2401
|
+
{
|
|
2402
|
+
type: "button",
|
|
2403
|
+
onClick: onClose,
|
|
2404
|
+
"aria-label": "Close preview",
|
|
2405
|
+
className: scopePicker ? "p-1 rounded hover:bg-[hsl(var(--ra-muted))]" : "ml-auto p-1 rounded hover:bg-[hsl(var(--ra-muted))]",
|
|
2406
|
+
style: { color: "hsl(var(--ra-muted-text))" },
|
|
2407
|
+
children: /* @__PURE__ */ jsx(X, { className: "w-3.5 h-3.5" })
|
|
2408
|
+
}
|
|
2409
|
+
)
|
|
1894
2410
|
] }),
|
|
1895
2411
|
/* @__PURE__ */ jsx("div", { className: "ra-preview-rail-body", children })
|
|
1896
2412
|
] });
|
|
@@ -2004,7 +2520,13 @@ var PreviewScopePicker = ({
|
|
|
2004
2520
|
showBatches,
|
|
2005
2521
|
i18n
|
|
2006
2522
|
}) => {
|
|
2007
|
-
const
|
|
2523
|
+
const productPinned = !!editingScope.productId;
|
|
2524
|
+
const products = useProductBrowse({
|
|
2525
|
+
SL,
|
|
2526
|
+
collectionId,
|
|
2527
|
+
pageSize: 100,
|
|
2528
|
+
enabled: !productPinned
|
|
2529
|
+
});
|
|
2008
2530
|
const variants = useProductChildren({
|
|
2009
2531
|
SL,
|
|
2010
2532
|
collectionId,
|
|
@@ -2021,6 +2543,27 @@ var PreviewScopePicker = ({
|
|
|
2021
2543
|
() => value.raw === editingScope.raw,
|
|
2022
2544
|
[value.raw, editingScope.raw]
|
|
2023
2545
|
);
|
|
2546
|
+
useEffect(() => {
|
|
2547
|
+
if (productPinned) return;
|
|
2548
|
+
if (value.productId) return;
|
|
2549
|
+
const first = products.items[0];
|
|
2550
|
+
if (!first) return;
|
|
2551
|
+
onChange({
|
|
2552
|
+
...value,
|
|
2553
|
+
productId: first.id,
|
|
2554
|
+
variantId: void 0,
|
|
2555
|
+
batchId: void 0,
|
|
2556
|
+
raw: `product:${first.id}`
|
|
2557
|
+
});
|
|
2558
|
+
}, [productPinned, value.productId, products.items]);
|
|
2559
|
+
const [productPickerOpen, setProductPickerOpen] = useState(false);
|
|
2560
|
+
const showProductPicker = !productPinned && products.items.length > 1;
|
|
2561
|
+
const showVariantPicker = showVariants && !!value.productId && variants.items.length > 0;
|
|
2562
|
+
const showBatchPicker = showBatches && !!value.productId && batches.items.length > 0;
|
|
2563
|
+
if (!showProductPicker && !showVariantPicker && !showBatchPicker) {
|
|
2564
|
+
return null;
|
|
2565
|
+
}
|
|
2566
|
+
const currentProductName = products.items.find((p) => p.id === value.productId)?.name ?? value.productId ?? "";
|
|
2024
2567
|
const selectStyle = {
|
|
2025
2568
|
borderColor: "hsl(var(--ra-border))",
|
|
2026
2569
|
color: "hsl(var(--ra-text))"
|
|
@@ -2050,30 +2593,44 @@ var PreviewScopePicker = ({
|
|
|
2050
2593
|
]
|
|
2051
2594
|
}
|
|
2052
2595
|
),
|
|
2053
|
-
/* @__PURE__ */ jsxs(
|
|
2596
|
+
showProductPicker && !productPickerOpen && currentProductName && /* @__PURE__ */ jsxs(
|
|
2597
|
+
"button",
|
|
2598
|
+
{
|
|
2599
|
+
type: "button",
|
|
2600
|
+
onClick: () => setProductPickerOpen(true),
|
|
2601
|
+
className: "text-[10px] px-2 py-1 rounded-md border hover:bg-[hsl(var(--ra-muted))] inline-flex items-center gap-1 max-w-[12rem] truncate",
|
|
2602
|
+
style: selectStyle,
|
|
2603
|
+
title: `Preview as ${currentProductName} \u2014 click to change`,
|
|
2604
|
+
children: [
|
|
2605
|
+
"as ",
|
|
2606
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium truncate", children: currentProductName }),
|
|
2607
|
+
/* @__PURE__ */ jsx(ChevronDown, { className: "w-3 h-3 opacity-60" })
|
|
2608
|
+
]
|
|
2609
|
+
}
|
|
2610
|
+
),
|
|
2611
|
+
showProductPicker && productPickerOpen && /* @__PURE__ */ jsx(
|
|
2054
2612
|
"select",
|
|
2055
2613
|
{
|
|
2056
2614
|
className: SELECT_CLS,
|
|
2057
2615
|
style: selectStyle,
|
|
2616
|
+
autoFocus: true,
|
|
2617
|
+
onBlur: () => setProductPickerOpen(false),
|
|
2058
2618
|
value: value.productId ?? "",
|
|
2059
2619
|
onChange: (e) => {
|
|
2060
2620
|
const productId = e.target.value || void 0;
|
|
2061
2621
|
onChange({
|
|
2062
2622
|
...value,
|
|
2063
2623
|
productId,
|
|
2064
|
-
// Clear variant/batch when switching products.
|
|
2065
2624
|
variantId: void 0,
|
|
2066
2625
|
batchId: void 0,
|
|
2067
2626
|
raw: productId ? `product:${productId}` : ""
|
|
2068
2627
|
});
|
|
2628
|
+
setProductPickerOpen(false);
|
|
2069
2629
|
},
|
|
2070
|
-
children:
|
|
2071
|
-
/* @__PURE__ */ jsx("option", { value: "", children: "\u2014 Any product \u2014" }),
|
|
2072
|
-
products.items.map((p) => /* @__PURE__ */ jsx("option", { value: p.id, children: p.name }, p.id))
|
|
2073
|
-
]
|
|
2630
|
+
children: products.items.map((p) => /* @__PURE__ */ jsx("option", { value: p.id, children: p.name }, p.id))
|
|
2074
2631
|
}
|
|
2075
2632
|
),
|
|
2076
|
-
|
|
2633
|
+
showVariantPicker && /* @__PURE__ */ jsxs(
|
|
2077
2634
|
"select",
|
|
2078
2635
|
{
|
|
2079
2636
|
className: SELECT_CLS,
|
|
@@ -2094,7 +2651,7 @@ var PreviewScopePicker = ({
|
|
|
2094
2651
|
]
|
|
2095
2652
|
}
|
|
2096
2653
|
),
|
|
2097
|
-
|
|
2654
|
+
showBatchPicker && /* @__PURE__ */ jsxs(
|
|
2098
2655
|
"select",
|
|
2099
2656
|
{
|
|
2100
2657
|
className: SELECT_CLS,
|
|
@@ -2241,6 +2798,84 @@ function ShellHeader({
|
|
|
2241
2798
|
] })
|
|
2242
2799
|
] });
|
|
2243
2800
|
}
|
|
2801
|
+
var UnsavedBanner = ({
|
|
2802
|
+
label,
|
|
2803
|
+
context,
|
|
2804
|
+
isSaving,
|
|
2805
|
+
saveError,
|
|
2806
|
+
onSave,
|
|
2807
|
+
onDiscard,
|
|
2808
|
+
saveLabel,
|
|
2809
|
+
discardLabel,
|
|
2810
|
+
bodyTemplate,
|
|
2811
|
+
savingLabel,
|
|
2812
|
+
errorLabel,
|
|
2813
|
+
SaveIcon,
|
|
2814
|
+
DiscardIcon
|
|
2815
|
+
}) => {
|
|
2816
|
+
const SI = SaveIcon ?? Save;
|
|
2817
|
+
const DI = DiscardIcon ?? Undo2;
|
|
2818
|
+
const name = label ?? context ?? "this record";
|
|
2819
|
+
const message = bodyTemplate.replace("{name}", name);
|
|
2820
|
+
return /* @__PURE__ */ jsxs("div", { className: "ra-unsaved-banner", role: "status", "aria-live": "polite", children: [
|
|
2821
|
+
/* @__PURE__ */ jsx("span", { className: "ra-unsaved-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(AlertCircle, { className: "w-3.5 h-3.5" }) }),
|
|
2822
|
+
/* @__PURE__ */ jsxs("span", { className: "ra-unsaved-text", children: [
|
|
2823
|
+
message,
|
|
2824
|
+
context && label && /* @__PURE__ */ jsxs("span", { className: "ra-unsaved-context", children: [
|
|
2825
|
+
" \xB7 ",
|
|
2826
|
+
context
|
|
2827
|
+
] })
|
|
2828
|
+
] }),
|
|
2829
|
+
saveError != null && !isSaving && /* @__PURE__ */ jsx("span", { className: "ra-unsaved-error", role: "alert", children: errorLabel ?? "Save failed" }),
|
|
2830
|
+
/* @__PURE__ */ jsxs("div", { className: "ra-unsaved-actions", children: [
|
|
2831
|
+
/* @__PURE__ */ jsxs(
|
|
2832
|
+
"button",
|
|
2833
|
+
{
|
|
2834
|
+
type: "button",
|
|
2835
|
+
className: "ra-unsaved-btn ra-unsaved-btn-ghost",
|
|
2836
|
+
onClick: onDiscard,
|
|
2837
|
+
disabled: isSaving,
|
|
2838
|
+
children: [
|
|
2839
|
+
/* @__PURE__ */ jsx(DI, { className: "w-3 h-3" }),
|
|
2840
|
+
discardLabel
|
|
2841
|
+
]
|
|
2842
|
+
}
|
|
2843
|
+
),
|
|
2844
|
+
/* @__PURE__ */ jsxs(
|
|
2845
|
+
"button",
|
|
2846
|
+
{
|
|
2847
|
+
type: "button",
|
|
2848
|
+
className: "ra-unsaved-btn ra-unsaved-btn-primary",
|
|
2849
|
+
onClick: onSave,
|
|
2850
|
+
disabled: isSaving,
|
|
2851
|
+
children: [
|
|
2852
|
+
/* @__PURE__ */ jsx(SI, { className: "w-3 h-3" }),
|
|
2853
|
+
isSaving ? savingLabel ?? "Saving\u2026" : saveLabel
|
|
2854
|
+
]
|
|
2855
|
+
}
|
|
2856
|
+
)
|
|
2857
|
+
] })
|
|
2858
|
+
] });
|
|
2859
|
+
};
|
|
2860
|
+
var ClipboardToast = ({ message, variant = "copy", onDismiss }) => {
|
|
2861
|
+
useEffect(() => {
|
|
2862
|
+
const t = window.setTimeout(onDismiss, 2500);
|
|
2863
|
+
return () => window.clearTimeout(t);
|
|
2864
|
+
}, [message, onDismiss]);
|
|
2865
|
+
const Icon = variant === "paste" ? ClipboardPaste : Copy;
|
|
2866
|
+
return /* @__PURE__ */ jsxs(
|
|
2867
|
+
"div",
|
|
2868
|
+
{
|
|
2869
|
+
role: "status",
|
|
2870
|
+
"aria-live": "polite",
|
|
2871
|
+
className: "ra-clipboard-toast",
|
|
2872
|
+
children: [
|
|
2873
|
+
/* @__PURE__ */ jsx(Icon, { className: "w-3.5 h-3.5 shrink-0", "aria-hidden": "true" }),
|
|
2874
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: message })
|
|
2875
|
+
]
|
|
2876
|
+
}
|
|
2877
|
+
);
|
|
2878
|
+
};
|
|
2244
2879
|
|
|
2245
2880
|
// src/components/RecordsAdmin/data/csv.ts
|
|
2246
2881
|
var escapeCell = (s) => {
|
|
@@ -2350,8 +2985,8 @@ var downloadBlob = (blob, filename) => {
|
|
|
2350
2985
|
styleInject(':root {\n --ra-status-own: var(--ra-emerald, 142 71% 45%);\n --ra-status-shared: var(--ra-amber, 38 92% 50%);\n --ra-status-missing: var(--muted-foreground, 220 9% 46%);\n --ra-accent: var(--primary, 222 47% 11%);\n --ra-surface: var(--card, 0 0% 100%);\n --ra-border: var(--border, 220 13% 91%);\n --ra-text: var(--foreground, 222 47% 11%);\n --ra-muted: var(--muted, 220 14% 96%);\n --ra-muted-text: var(--muted-foreground, 220 9% 46%);\n --ra-radius: var(--radius, 0.625rem);\n --ra-dot-size: 0.5rem;\n --ra-page-bg: var(--background, 220 14% 98%);\n --ra-card-shadow: 0 1px 2px hsl(var(--ra-accent) / 0.04), 0 4px 12px hsl(var(--ra-accent) / 0.05);\n --ra-card-shadow-hover: 0 2px 4px hsl(var(--ra-accent) / 0.06), 0 8px 24px hsl(var(--ra-accent) / 0.08);\n --ra-row-hover: hsl(var(--ra-accent) / 0.05);\n --ra-row-active-bg: hsl(var(--ra-accent) / 0.10);\n --ra-row-active-bd: hsl(var(--ra-accent) / 0.45);\n --ra-focus-ring: hsl(var(--ra-accent) / 0.35);\n --ra-font-display: var(--font-display, var(--font-sans, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif));\n --ra-font-ui: var(--font-sans, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);\n --ra-title-weight: 600;\n --ra-display-weight: 700;\n --ra-info: var(--ra-blue, 214 95% 55%);\n --ra-success: var(--ra-emerald, 142 71% 45%);\n --ra-warning: var(--ra-amber, 38 92% 50%);\n --ra-danger: var(--destructive, 0 72% 51%);\n}\n.ra-status-dot {\n display: inline-block;\n width: var(--ra-dot-size);\n height: var(--ra-dot-size);\n border-radius: 9999px;\n flex-shrink: 0;\n}\n.ra-status-own {\n background: hsl(var(--ra-status-own));\n}\n.ra-status-shared {\n background: hsl(var(--ra-status-shared));\n}\n.ra-status-missing {\n background: hsl(var(--ra-status-missing) / 0.4);\n border: 1px solid hsl(var(--ra-status-missing) / 0.6);\n}\n.ra-row-active {\n background: var(--ra-row-active-bg);\n border-color: var(--ra-row-active-bd) !important;\n}\n');
|
|
2351
2986
|
|
|
2352
2987
|
// src/components/RecordsAdmin/shell/shell.css
|
|
2353
|
-
styleInject(".ra-shell {\n color: hsl(var(--ra-text));\n background: hsl(var(--ra-page-bg));\n font-family: var(--ra-font-ui);\n}\n.ra-shell *,\n.ra-shell *::before,\n.ra-shell *::after {\n box-sizing: border-box;\n}\n.ra-shell .ra-card {\n background: hsl(var(--ra-surface));\n border: 1px solid hsl(var(--ra-border));\n border-radius: var(--ra-radius);\n box-shadow: var(--ra-card-shadow);\n}\n.ra-shell .ra-card-hover {\n transition:\n box-shadow .18s ease,\n transform .18s ease,\n border-color .18s ease;\n}\n.ra-shell .ra-card-hover:hover {\n box-shadow: var(--ra-card-shadow-hover);\n}\n.ra-shell .ra-display {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-display-weight);\n letter-spacing: -0.01em;\n}\n.ra-shell .ra-title {\n font-weight: var(--ra-title-weight);\n}\n.ra-shell :where(button, [role=button], input, select, textarea, a):focus-visible {\n outline: none;\n box-shadow: 0 0 0 3px var(--ra-focus-ring);\n border-radius: calc(var(--ra-radius) * 0.6);\n}\n.ra-shell .ra-header {\n position: relative;\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.65rem 0.9rem;\n border-radius: var(--ra-radius);\n border: 1px solid hsl(var(--ra-accent) / 0.12);\n background:\n linear-gradient(\n 135deg,\n hsl(var(--ra-accent) / 0.08),\n hsl(var(--ra-accent) / 0.02) 60%,\n hsl(var(--ra-surface)) 100%);\n box-shadow: var(--ra-card-shadow);\n}\n.ra-shell .ra-header__main {\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: center;\n gap: 0.625rem;\n}\n.ra-shell .ra-header-aside {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n flex-shrink: 0;\n}\n.ra-shell .ra-header-icon {\n flex-shrink: 0;\n width: 2rem;\n height: 2rem;\n border-radius: calc(var(--ra-radius) * 0.9);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: hsl(var(--ra-accent) / 0.12);\n color: hsl(var(--ra-accent));\n border: 1px solid hsl(var(--ra-accent) / 0.18);\n}\n.ra-shell .ra-header-text {\n flex: 1;\n min-width: 0;\n}\n.ra-shell .ra-header-title {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-display-weight);\n font-size: 1rem;\n line-height: 1.2;\n color: hsl(var(--ra-text));\n letter-spacing: -0.01em;\n margin: 0;\n}\n.ra-shell .ra-header-subtitle {\n font-size: 0.75rem;\n color: hsl(var(--ra-muted-text));\n margin-top: 0.125rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ra-shell .ra-header-stats {\n display: flex;\n align-items: stretch;\n gap: 0.15rem;\n padding: 0.15rem 0.4rem;\n border-radius: calc(var(--ra-radius) * 0.75);\n background: hsl(var(--ra-surface) / 0.7);\n border: 1px solid hsl(var(--ra-border));\n}\n.ra-shell .ra-header-stats--titled {\n flex-direction: column;\n align-items: stretch;\n padding: 0.4rem 0.55rem;\n gap: 0.3rem;\n}\n.ra-shell .ra-header-stats .ra-stats-items {\n display: flex;\n align-items: stretch;\n gap: 0.15rem;\n}\n.ra-shell .ra-header-stats .ra-stats-heading {\n display: flex;\n align-items: center;\n gap: 0.35rem;\n color: hsl(var(--ra-muted-text));\n font-size: 0.65rem;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n}\n.ra-shell .ra-header-stats .ra-stats-heading-icon {\n display: inline-flex;\n align-items: center;\n color: hsl(var(--ra-text));\n opacity: 0.75;\n}\n.ra-shell .ra-header-stats .ra-stats-heading-icon > svg {\n width: 0.85rem;\n height: 0.85rem;\n}\n.ra-shell .ra-stat {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 0.15rem 0.45rem;\n min-width: 2.5rem;\n}\n.ra-shell .ra-stat-value {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-display-weight);\n font-size: 0.85rem;\n color: hsl(var(--ra-text));\n line-height: 1;\n}\n.ra-shell .ra-stat-label {\n font-size: 0.6rem;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: hsl(var(--ra-muted-text));\n margin-top: 0.15rem;\n}\n.ra-shell .ra-stat-divider {\n width: 1px;\n background: hsl(var(--ra-border));\n margin: 0.25rem 0;\n}\n.ra-shell .ra-header-actions {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n.ra-shell .ra-tabs {\n display: flex;\n gap: 0.25rem;\n padding: 0.25rem;\n background: hsl(var(--ra-muted));\n border-radius: calc(var(--ra-radius) * 0.85);\n border: 1px solid hsl(var(--ra-border));\n}\n.ra-shell .ra-tab {\n display: inline-flex;\n align-items: center;\n gap: 0.4rem;\n padding: 0.4rem 0.7rem;\n border-radius: calc(var(--ra-radius) * 0.65);\n font-size: 0.78rem;\n font-weight: 500;\n color: hsl(var(--ra-muted-text));\n background: transparent;\n border: 0;\n cursor: pointer;\n transition:\n background .15s ease,\n color .15s ease,\n transform .15s ease;\n white-space: nowrap;\n}\n.ra-shell .ra-tab:hover {\n background: hsl(var(--ra-surface));\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-tab[aria-selected=true] {\n background: hsl(var(--ra-surface));\n color: hsl(var(--ra-text));\n box-shadow: var(--ra-card-shadow);\n font-weight: var(--ra-title-weight);\n}\n.ra-shell .ra-tab[aria-selected=true] .ra-tab-icon {\n color: hsl(var(--ra-accent));\n}\n.ra-shell .ra-tab[disabled] {\n opacity: .5;\n cursor: not-allowed;\n}\n.ra-shell .ra-tab-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 1.25rem;\n padding: 0 0.35rem;\n height: 1.1rem;\n border-radius: 999px;\n background: hsl(var(--ra-accent) / 0.12);\n color: hsl(var(--ra-accent));\n font-size: 0.625rem;\n font-weight: 600;\n line-height: 1;\n}\n.ra-shell .ra-tab[aria-selected=false] .ra-tab-count {\n background: hsl(var(--ra-muted-text) / 0.15);\n color: hsl(var(--ra-muted-text));\n}\n.ra-shell[data-density=compact] .ra-row {\n padding-block: 0.4rem;\n}\n.ra-shell[data-density=compact] .ra-header {\n padding: 0.75rem 1rem;\n}\n.ra-shell[data-density=compact] .ra-header-icon {\n width: 2.25rem;\n height: 2.25rem;\n}\n.ra-shell .ra-row {\n display: flex;\n align-items: center;\n gap: 0.65rem;\n width: 100%;\n text-align: left;\n padding: 0.65rem 0.85rem;\n border-left: 3px solid transparent;\n background: transparent;\n border-bottom: 1px solid transparent;\n transition: background .12s ease, border-color .12s ease;\n cursor: pointer;\n color: hsl(var(--ra-text));\n font-family: inherit;\n}\n.ra-shell .ra-row + .ra-row {\n border-top: 1px solid hsl(var(--ra-border) / 0.6);\n}\n.ra-shell .ra-row:hover {\n background: var(--ra-row-hover);\n}\n.ra-shell .ra-row[data-selected=true] {\n background: var(--ra-row-active-bg);\n border-left-color: var(--ra-row-active-bd);\n}\n.ra-shell .ra-row-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.75rem;\n height: 1.75rem;\n border-radius: calc(var(--ra-radius) * 0.6);\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-muted-text));\n flex-shrink: 0;\n}\n.ra-shell .ra-row[data-selected=true] .ra-row-icon {\n background: hsl(var(--ra-accent) / 0.15);\n color: hsl(var(--ra-accent));\n}\n.ra-shell .ra-row-body {\n flex: 1;\n min-width: 0;\n}\n.ra-shell .ra-row-title {\n font-weight: var(--ra-title-weight);\n font-size: 0.875rem;\n line-height: 1.25;\n color: hsl(var(--ra-text));\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ra-shell .ra-row-sub {\n font-size: 0.75rem;\n color: hsl(var(--ra-muted-text));\n margin-top: 0.15rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ra-shell .ra-row-actions {\n display: inline-flex;\n align-items: center;\n gap: 0.15rem;\n margin-left: auto;\n opacity: 0;\n transition: opacity .15s ease;\n}\n.ra-shell .ra-row:hover .ra-row-actions,\n.ra-shell .ra-row:focus-within .ra-row-actions {\n opacity: 1;\n}\n.ra-shell .ra-row-action {\n width: 1.6rem;\n height: 1.6rem;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: 999px;\n background: transparent;\n color: hsl(var(--ra-muted-text));\n border: 0;\n cursor: pointer;\n transition: background .15s ease, color .15s ease;\n}\n.ra-shell .ra-row-action:hover {\n background: hsl(var(--ra-accent) / 0.10);\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-row-action[data-tone=danger]:hover {\n background: hsl(var(--ra-danger) / 0.12);\n color: hsl(var(--ra-danger));\n}\n.ra-shell .ra-chip {\n display: inline-flex;\n align-items: center;\n gap: 0.3rem;\n padding: 0.15rem 0.5rem;\n border-radius: 999px;\n font-size: 0.6875rem;\n font-weight: 500;\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-muted-text));\n border: 1px solid hsl(var(--ra-border));\n white-space: nowrap;\n max-width: 14rem;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ra-shell .ra-chip[data-tone=success] {\n background: hsl(var(--ra-success) / 0.12);\n color: hsl(var(--ra-success));\n border-color: hsl(var(--ra-success) / 0.30);\n}\n.ra-shell .ra-chip[data-tone=warning] {\n background: hsl(var(--ra-warning) / 0.14);\n color: hsl(var(--ra-warning));\n border-color: hsl(var(--ra-warning) / 0.35);\n}\n.ra-shell .ra-chip[data-tone=info] {\n background: hsl(var(--ra-info) / 0.10);\n color: hsl(var(--ra-info));\n border-color: hsl(var(--ra-info) / 0.30);\n}\n.ra-shell .ra-chip[data-tone=danger] {\n background: hsl(var(--ra-danger) / 0.10);\n color: hsl(var(--ra-danger));\n border-color: hsl(var(--ra-danger) / 0.30);\n}\n.ra-shell .ra-chip[data-tone=muted] {\n background: transparent;\n color: hsl(var(--ra-muted-text));\n border-style: dashed;\n}\n.ra-shell .ra-group {\n border-bottom: 1px solid hsl(var(--ra-border));\n}\n.ra-shell .ra-group:last-child {\n border-bottom: 0;\n}\n.ra-shell .ra-group-summary {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n width: 100%;\n padding: 0.5rem 0.85rem;\n background: hsl(var(--ra-muted) / 0.6);\n font-size: 0.7rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: hsl(var(--ra-muted-text));\n border: 0;\n cursor: pointer;\n transition: background .12s ease;\n}\n.ra-shell .ra-group-summary:hover {\n background: hsl(var(--ra-muted));\n}\n.ra-shell .ra-group-summary .ra-group-chevron {\n transition: transform .15s ease;\n}\n.ra-shell .ra-group[data-open=false] .ra-group-chevron {\n transform: rotate(-90deg);\n}\n.ra-shell .ra-group-name {\n flex: 1;\n text-align: left;\n}\n.ra-shell .ra-group-count {\n font-size: 0.65rem;\n font-weight: 600;\n color: hsl(var(--ra-muted-text));\n background: hsl(var(--ra-surface));\n border: 1px solid hsl(var(--ra-border));\n border-radius: 999px;\n padding: 0.05rem 0.4rem;\n}\n.ra-shell .ra-group[data-open=false] .ra-group-body {\n display: none;\n}\n.ra-shell .ra-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n text-align: center;\n padding: 2.5rem 1.5rem;\n gap: 0.75rem;\n}\n.ra-shell .ra-empty-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 3.25rem;\n height: 3.25rem;\n border-radius: 999px;\n background: hsl(var(--ra-accent) / 0.08);\n color: hsl(var(--ra-accent));\n margin-bottom: 0.25rem;\n}\n.ra-shell .ra-empty-title {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-display-weight);\n font-size: 1rem;\n color: hsl(var(--ra-text));\n margin: 0;\n letter-spacing: -0.01em;\n}\n.ra-shell .ra-empty-body {\n font-size: 0.8125rem;\n color: hsl(var(--ra-muted-text));\n max-width: 22rem;\n line-height: 1.45;\n}\n.ra-shell .ra-empty-actions {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-top: 0.25rem;\n flex-wrap: wrap;\n justify-content: center;\n}\n.ra-shell .ra-btn {\n display: inline-flex;\n align-items: center;\n gap: 0.4rem;\n padding: 0.45rem 0.85rem;\n border-radius: calc(var(--ra-radius) * 0.7);\n font-size: 0.8125rem;\n font-weight: 500;\n border: 1px solid hsl(var(--ra-border));\n background: hsl(var(--ra-surface));\n color: hsl(var(--ra-text));\n cursor: pointer;\n transition:\n background .15s ease,\n border-color .15s ease,\n box-shadow .15s ease,\n transform .1s ease;\n}\n.ra-shell .ra-btn:hover {\n background: hsl(var(--ra-muted));\n box-shadow: var(--ra-card-shadow);\n}\n.ra-shell .ra-btn:active {\n transform: translateY(1px);\n}\n.ra-shell .ra-btn[data-variant=primary] {\n background: hsl(var(--ra-accent));\n color: hsl(var(--ra-surface));\n border-color: hsl(var(--ra-accent));\n}\n.ra-shell .ra-btn[data-variant=primary]:hover {\n background: hsl(var(--ra-accent) / 0.92);\n}\n.ra-shell .ra-btn[data-variant=ghost] {\n background: transparent;\n border-color: transparent;\n color: hsl(var(--ra-muted-text));\n}\n.ra-shell .ra-btn[data-variant=ghost]:hover {\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-btn[data-variant=danger] {\n color: hsl(var(--ra-danger));\n}\n.ra-shell .ra-btn[data-variant=danger]:hover {\n background: hsl(var(--ra-danger) / 0.10);\n border-color: hsl(var(--ra-danger) / 0.40);\n}\n.ra-shell .ra-intro {\n position: relative;\n display: flex;\n gap: 0.85rem;\n padding: 0.9rem 1rem;\n border-radius: var(--ra-radius);\n border: 1px solid hsl(var(--ra-info) / 0.30);\n background: hsl(var(--ra-info) / 0.08);\n margin-bottom: 1rem;\n}\n.ra-shell .ra-intro[data-tone=success] {\n border-color: hsl(var(--ra-success) / 0.30);\n background: hsl(var(--ra-success) / 0.08);\n}\n.ra-shell .ra-intro[data-tone=warning] {\n border-color: hsl(var(--ra-warning) / 0.35);\n background: hsl(var(--ra-warning) / 0.10);\n}\n.ra-shell .ra-intro-icon {\n flex-shrink: 0;\n width: 2rem;\n height: 2rem;\n border-radius: 999px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: hsl(var(--ra-info) / 0.18);\n color: hsl(var(--ra-info));\n}\n.ra-shell .ra-intro[data-tone=success] .ra-intro-icon {\n background: hsl(var(--ra-success) / 0.18);\n color: hsl(var(--ra-success));\n}\n.ra-shell .ra-intro[data-tone=warning] .ra-intro-icon {\n background: hsl(var(--ra-warning) / 0.20);\n color: hsl(var(--ra-warning));\n}\n.ra-shell .ra-intro-body {\n flex: 1;\n min-width: 0;\n}\n.ra-shell .ra-intro-title {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-title-weight);\n font-size: 0.875rem;\n color: hsl(var(--ra-text));\n margin: 0 0 0.2rem 0;\n}\n.ra-shell .ra-intro-text {\n font-size: 0.8125rem;\n color: hsl(var(--ra-text) / 0.85);\n line-height: 1.45;\n}\n.ra-shell .ra-intro-dismiss {\n position: absolute;\n top: 0.5rem;\n right: 0.5rem;\n width: 1.6rem;\n height: 1.6rem;\n border-radius: 999px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 0;\n color: hsl(var(--ra-muted-text));\n cursor: pointer;\n}\n.ra-shell .ra-intro-dismiss:hover {\n background: hsl(var(--ra-text) / 0.06);\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-bulk-menu {\n min-width: 12rem;\n background: hsl(var(--ra-surface));\n border: 1px solid hsl(var(--ra-border));\n border-radius: calc(var(--ra-radius) * 0.85);\n box-shadow: var(--ra-card-shadow-hover);\n padding: 0.3rem;\n z-index: 30;\n}\n.ra-shell .ra-bulk-item {\n display: flex;\n align-items: center;\n gap: 0.55rem;\n width: 100%;\n padding: 0.45rem 0.6rem;\n border-radius: calc(var(--ra-radius) * 0.6);\n font-size: 0.8125rem;\n color: hsl(var(--ra-text));\n background: transparent;\n border: 0;\n cursor: pointer;\n text-align: left;\n transition: background .12s ease, color .12s ease;\n}\n.ra-shell .ra-bulk-item:hover {\n background: hsl(var(--ra-muted));\n}\n.ra-shell .ra-bulk-item[data-tone=danger] {\n color: hsl(var(--ra-danger));\n}\n.ra-shell .ra-bulk-item[data-tone=danger]:hover {\n background: hsl(var(--ra-danger) / 0.10);\n}\n.ra-shell .ra-bulk-divider {\n height: 1px;\n background: hsl(var(--ra-border));\n margin: 0.25rem 0;\n}\n.ra-shell .ra-preview-rail {\n background: hsl(var(--ra-surface));\n border-left: 1px solid hsl(var(--ra-border));\n box-shadow: -4px 0 16px hsl(var(--ra-accent) / 0.04);\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n}\n.ra-shell .ra-preview-rail-header {\n position: sticky;\n top: 0;\n z-index: 1;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n background:\n linear-gradient(\n 180deg,\n hsl(var(--ra-surface)) 0%,\n hsl(var(--ra-surface) / 0.92) 100%);\n border-bottom: 1px solid hsl(var(--ra-border));\n backdrop-filter: blur(6px);\n}\n.ra-shell .ra-preview-rail-title {\n display: inline-flex;\n align-items: center;\n gap: 0.4rem;\n font-size: 0.7rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: hsl(var(--ra-muted-text));\n}\n.ra-shell .ra-preview-rail-body {\n flex: 1;\n overflow-y: auto;\n padding: 1rem;\n}\n");
|
|
2354
|
-
var TOP_LEVEL_SCOPES = ["
|
|
2988
|
+
styleInject(".ra-shell {\n color: hsl(var(--ra-text));\n background: hsl(var(--ra-page-bg));\n font-family: var(--ra-font-ui);\n}\n.ra-shell *,\n.ra-shell *::before,\n.ra-shell *::after {\n box-sizing: border-box;\n}\n.ra-shell .ra-card {\n background: hsl(var(--ra-surface));\n border: 1px solid hsl(var(--ra-border));\n border-radius: var(--ra-radius);\n box-shadow: var(--ra-card-shadow);\n}\n.ra-shell .ra-card-hover {\n transition:\n box-shadow .18s ease,\n transform .18s ease,\n border-color .18s ease;\n}\n.ra-shell .ra-card-hover:hover {\n box-shadow: var(--ra-card-shadow-hover);\n}\n.ra-shell .ra-display {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-display-weight);\n letter-spacing: -0.01em;\n}\n.ra-shell .ra-title {\n font-weight: var(--ra-title-weight);\n}\n.ra-shell :where(button, [role=button], input, select, textarea, a):focus-visible {\n outline: none;\n box-shadow: 0 0 0 3px var(--ra-focus-ring);\n border-radius: calc(var(--ra-radius) * 0.6);\n}\n.ra-shell .ra-header {\n position: relative;\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.65rem 0.9rem;\n border-radius: var(--ra-radius);\n border: 1px solid hsl(var(--ra-accent) / 0.12);\n background:\n linear-gradient(\n 135deg,\n hsl(var(--ra-accent) / 0.08),\n hsl(var(--ra-accent) / 0.02) 60%,\n hsl(var(--ra-surface)) 100%);\n box-shadow: var(--ra-card-shadow);\n}\n.ra-shell .ra-header__main {\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: center;\n gap: 0.625rem;\n}\n.ra-shell .ra-header-aside {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n flex-shrink: 0;\n}\n.ra-shell .ra-header-icon {\n flex-shrink: 0;\n width: 2rem;\n height: 2rem;\n border-radius: calc(var(--ra-radius) * 0.9);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: hsl(var(--ra-accent) / 0.12);\n color: hsl(var(--ra-accent));\n border: 1px solid hsl(var(--ra-accent) / 0.18);\n}\n.ra-shell .ra-header-text {\n flex: 1;\n min-width: 0;\n}\n.ra-shell .ra-header-title {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-display-weight);\n font-size: 1rem;\n line-height: 1.2;\n color: hsl(var(--ra-text));\n letter-spacing: -0.01em;\n margin: 0;\n}\n.ra-shell .ra-header-subtitle {\n font-size: 0.75rem;\n color: hsl(var(--ra-muted-text));\n margin-top: 0.125rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ra-shell .ra-header-stats {\n display: flex;\n align-items: stretch;\n gap: 0.15rem;\n padding: 0.15rem 0.4rem;\n border-radius: calc(var(--ra-radius) * 0.75);\n background: hsl(var(--ra-surface) / 0.7);\n border: 1px solid hsl(var(--ra-border));\n}\n.ra-shell .ra-header-stats--titled {\n flex-direction: column;\n align-items: stretch;\n padding: 0.4rem 0.55rem;\n gap: 0.3rem;\n}\n.ra-shell .ra-header-stats .ra-stats-items {\n display: flex;\n align-items: stretch;\n gap: 0.15rem;\n}\n.ra-shell .ra-header-stats .ra-stats-heading {\n display: flex;\n align-items: center;\n gap: 0.35rem;\n color: hsl(var(--ra-muted-text));\n font-size: 0.65rem;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n}\n.ra-shell .ra-header-stats .ra-stats-heading-icon {\n display: inline-flex;\n align-items: center;\n color: hsl(var(--ra-text));\n opacity: 0.75;\n}\n.ra-shell .ra-header-stats .ra-stats-heading-icon > svg {\n width: 0.85rem;\n height: 0.85rem;\n}\n.ra-shell .ra-stat {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 0.15rem 0.45rem;\n min-width: 2.5rem;\n}\n.ra-shell .ra-stat-value {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-display-weight);\n font-size: 0.85rem;\n color: hsl(var(--ra-text));\n line-height: 1;\n}\n.ra-shell .ra-stat-label {\n font-size: 0.6rem;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: hsl(var(--ra-muted-text));\n margin-top: 0.15rem;\n}\n.ra-shell .ra-stat-divider {\n width: 1px;\n background: hsl(var(--ra-border));\n margin: 0.25rem 0;\n}\n.ra-shell .ra-header-actions {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n.ra-shell .ra-tabs {\n display: flex;\n gap: 0.25rem;\n padding: 0.25rem;\n background: hsl(var(--ra-muted));\n border-radius: calc(var(--ra-radius) * 0.85);\n border: 1px solid hsl(var(--ra-border));\n}\n.ra-shell .ra-tab {\n display: inline-flex;\n align-items: center;\n gap: 0.4rem;\n padding: 0.4rem 0.7rem;\n border-radius: calc(var(--ra-radius) * 0.65);\n font-size: 0.78rem;\n font-weight: 500;\n color: hsl(var(--ra-muted-text));\n background: transparent;\n border: 0;\n cursor: pointer;\n transition:\n background .15s ease,\n color .15s ease,\n transform .15s ease;\n white-space: nowrap;\n}\n.ra-shell .ra-tab:hover {\n background: hsl(var(--ra-surface));\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-tab[aria-selected=true] {\n background: hsl(var(--ra-surface));\n color: hsl(var(--ra-text));\n box-shadow: var(--ra-card-shadow);\n font-weight: var(--ra-title-weight);\n}\n.ra-shell .ra-tab[aria-selected=true] .ra-tab-icon {\n color: hsl(var(--ra-accent));\n}\n.ra-shell .ra-tab[disabled] {\n opacity: .5;\n cursor: not-allowed;\n}\n.ra-shell .ra-tab-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 1.25rem;\n padding: 0 0.35rem;\n height: 1.1rem;\n border-radius: 999px;\n background: hsl(var(--ra-accent) / 0.12);\n color: hsl(var(--ra-accent));\n font-size: 0.625rem;\n font-weight: 600;\n line-height: 1;\n}\n.ra-shell .ra-tab[aria-selected=false] .ra-tab-count {\n background: hsl(var(--ra-muted-text) / 0.15);\n color: hsl(var(--ra-muted-text));\n}\n.ra-shell[data-density=compact] .ra-row {\n padding-block: 0.4rem;\n}\n.ra-shell[data-density=compact] .ra-header {\n padding: 0.75rem 1rem;\n}\n.ra-shell[data-density=compact] .ra-header-icon {\n width: 2.25rem;\n height: 2.25rem;\n}\n.ra-shell .ra-row {\n display: flex;\n align-items: center;\n gap: 0.65rem;\n width: 100%;\n text-align: left;\n padding: 0.65rem 0.85rem;\n border-left: 3px solid transparent;\n background: transparent;\n border-bottom: 1px solid transparent;\n transition: background .12s ease, border-color .12s ease;\n cursor: pointer;\n color: hsl(var(--ra-text));\n font-family: inherit;\n}\n.ra-shell .ra-row + .ra-row {\n border-top: 1px solid hsl(var(--ra-border) / 0.6);\n}\n.ra-shell .ra-row:hover {\n background: var(--ra-row-hover);\n}\n.ra-shell .ra-row[data-selected=true] {\n background: var(--ra-row-active-bg);\n border-left-color: var(--ra-row-active-bd);\n}\n.ra-shell .ra-row-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.75rem;\n height: 1.75rem;\n border-radius: calc(var(--ra-radius) * 0.6);\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-muted-text));\n flex-shrink: 0;\n}\n.ra-shell .ra-row[data-selected=true] .ra-row-icon {\n background: hsl(var(--ra-accent) / 0.15);\n color: hsl(var(--ra-accent));\n}\n.ra-shell .ra-row-body {\n flex: 1;\n min-width: 0;\n}\n.ra-shell .ra-row-title {\n font-weight: var(--ra-title-weight);\n font-size: 0.875rem;\n line-height: 1.25;\n color: hsl(var(--ra-text));\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ra-shell .ra-row-sub {\n font-size: 0.75rem;\n color: hsl(var(--ra-muted-text));\n margin-top: 0.15rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ra-shell .ra-row-actions {\n display: inline-flex;\n align-items: center;\n gap: 0.15rem;\n margin-left: auto;\n opacity: 0;\n transition: opacity .15s ease;\n}\n.ra-shell .ra-row:hover .ra-row-actions,\n.ra-shell .ra-row:focus-within .ra-row-actions {\n opacity: 1;\n}\n.ra-shell .ra-row-action {\n width: 1.6rem;\n height: 1.6rem;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: 999px;\n background: transparent;\n color: hsl(var(--ra-muted-text));\n border: 0;\n cursor: pointer;\n transition: background .15s ease, color .15s ease;\n}\n.ra-shell .ra-row-action:hover {\n background: hsl(var(--ra-accent) / 0.10);\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-row-action[data-tone=danger]:hover {\n background: hsl(var(--ra-danger) / 0.12);\n color: hsl(var(--ra-danger));\n}\n.ra-shell .ra-chip {\n display: inline-flex;\n align-items: center;\n gap: 0.3rem;\n padding: 0.15rem 0.5rem;\n border-radius: 999px;\n font-size: 0.6875rem;\n font-weight: 500;\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-muted-text));\n border: 1px solid hsl(var(--ra-border));\n white-space: nowrap;\n max-width: 14rem;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ra-shell .ra-chip[data-tone=success] {\n background: hsl(var(--ra-success) / 0.12);\n color: hsl(var(--ra-success));\n border-color: hsl(var(--ra-success) / 0.30);\n}\n.ra-shell .ra-chip[data-tone=warning] {\n background: hsl(var(--ra-warning) / 0.14);\n color: hsl(var(--ra-warning));\n border-color: hsl(var(--ra-warning) / 0.35);\n}\n.ra-shell .ra-chip[data-tone=info] {\n background: hsl(var(--ra-info) / 0.10);\n color: hsl(var(--ra-info));\n border-color: hsl(var(--ra-info) / 0.30);\n}\n.ra-shell .ra-chip[data-tone=danger] {\n background: hsl(var(--ra-danger) / 0.10);\n color: hsl(var(--ra-danger));\n border-color: hsl(var(--ra-danger) / 0.30);\n}\n.ra-shell .ra-chip[data-tone=muted] {\n background: transparent;\n color: hsl(var(--ra-muted-text));\n border-style: dashed;\n}\n.ra-shell .ra-group {\n border-bottom: 1px solid hsl(var(--ra-border));\n}\n.ra-shell .ra-group:last-child {\n border-bottom: 0;\n}\n.ra-shell .ra-group-summary {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n width: 100%;\n padding: 0.5rem 0.85rem;\n background: hsl(var(--ra-muted) / 0.6);\n font-size: 0.7rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: hsl(var(--ra-muted-text));\n border: 0;\n cursor: pointer;\n transition: background .12s ease;\n}\n.ra-shell .ra-group-summary:hover {\n background: hsl(var(--ra-muted));\n}\n.ra-shell .ra-group-summary .ra-group-chevron {\n transition: transform .15s ease;\n}\n.ra-shell .ra-group[data-open=false] .ra-group-chevron {\n transform: rotate(-90deg);\n}\n.ra-shell .ra-group-name {\n flex: 1;\n text-align: left;\n}\n.ra-shell .ra-group-count {\n font-size: 0.65rem;\n font-weight: 600;\n color: hsl(var(--ra-muted-text));\n background: hsl(var(--ra-surface));\n border: 1px solid hsl(var(--ra-border));\n border-radius: 999px;\n padding: 0.05rem 0.4rem;\n}\n.ra-shell .ra-group[data-open=false] .ra-group-body {\n display: none;\n}\n.ra-shell .ra-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n text-align: center;\n padding: 2.5rem 1.5rem;\n gap: 0.75rem;\n}\n.ra-shell .ra-empty-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 3.25rem;\n height: 3.25rem;\n border-radius: 999px;\n background: hsl(var(--ra-accent) / 0.08);\n color: hsl(var(--ra-accent));\n margin-bottom: 0.25rem;\n}\n.ra-shell .ra-empty-title {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-display-weight);\n font-size: 1rem;\n color: hsl(var(--ra-text));\n margin: 0;\n letter-spacing: -0.01em;\n}\n.ra-shell .ra-empty-body {\n font-size: 0.8125rem;\n color: hsl(var(--ra-muted-text));\n max-width: 22rem;\n line-height: 1.45;\n}\n.ra-shell .ra-empty-actions {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-top: 0.25rem;\n flex-wrap: wrap;\n justify-content: center;\n}\n.ra-shell .ra-btn {\n display: inline-flex;\n align-items: center;\n gap: 0.4rem;\n padding: 0.45rem 0.85rem;\n border-radius: calc(var(--ra-radius) * 0.7);\n font-size: 0.8125rem;\n font-weight: 500;\n border: 1px solid hsl(var(--ra-border));\n background: hsl(var(--ra-surface));\n color: hsl(var(--ra-text));\n cursor: pointer;\n transition:\n background .15s ease,\n border-color .15s ease,\n box-shadow .15s ease,\n transform .1s ease;\n}\n.ra-shell .ra-btn:hover {\n background: hsl(var(--ra-muted));\n box-shadow: var(--ra-card-shadow);\n}\n.ra-shell .ra-btn:active {\n transform: translateY(1px);\n}\n.ra-shell .ra-btn[data-variant=primary] {\n background: hsl(var(--ra-accent));\n color: hsl(var(--ra-surface));\n border-color: hsl(var(--ra-accent));\n}\n.ra-shell .ra-btn[data-variant=primary]:hover {\n background: hsl(var(--ra-accent) / 0.92);\n}\n.ra-shell .ra-btn[data-variant=ghost] {\n background: transparent;\n border-color: transparent;\n color: hsl(var(--ra-muted-text));\n}\n.ra-shell .ra-btn[data-variant=ghost]:hover {\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-btn[data-variant=danger] {\n color: hsl(var(--ra-danger));\n}\n.ra-shell .ra-btn[data-variant=danger]:hover {\n background: hsl(var(--ra-danger) / 0.10);\n border-color: hsl(var(--ra-danger) / 0.40);\n}\n.ra-shell .ra-intro {\n position: relative;\n display: flex;\n gap: 0.85rem;\n padding: 0.9rem 1rem;\n border-radius: var(--ra-radius);\n border: 1px solid hsl(var(--ra-info) / 0.30);\n background: hsl(var(--ra-info) / 0.08);\n margin-bottom: 1rem;\n}\n.ra-shell .ra-intro[data-tone=success] {\n border-color: hsl(var(--ra-success) / 0.30);\n background: hsl(var(--ra-success) / 0.08);\n}\n.ra-shell .ra-intro[data-tone=warning] {\n border-color: hsl(var(--ra-warning) / 0.35);\n background: hsl(var(--ra-warning) / 0.10);\n}\n.ra-shell .ra-intro-icon {\n flex-shrink: 0;\n width: 2rem;\n height: 2rem;\n border-radius: 999px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: hsl(var(--ra-info) / 0.18);\n color: hsl(var(--ra-info));\n}\n.ra-shell .ra-intro[data-tone=success] .ra-intro-icon {\n background: hsl(var(--ra-success) / 0.18);\n color: hsl(var(--ra-success));\n}\n.ra-shell .ra-intro[data-tone=warning] .ra-intro-icon {\n background: hsl(var(--ra-warning) / 0.20);\n color: hsl(var(--ra-warning));\n}\n.ra-shell .ra-intro-body {\n flex: 1;\n min-width: 0;\n}\n.ra-shell .ra-intro-title {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-title-weight);\n font-size: 0.875rem;\n color: hsl(var(--ra-text));\n margin: 0 0 0.2rem 0;\n}\n.ra-shell .ra-intro-text {\n font-size: 0.8125rem;\n color: hsl(var(--ra-text) / 0.85);\n line-height: 1.45;\n}\n.ra-shell .ra-intro-dismiss {\n position: absolute;\n top: 0.5rem;\n right: 0.5rem;\n width: 1.6rem;\n height: 1.6rem;\n border-radius: 999px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 0;\n color: hsl(var(--ra-muted-text));\n cursor: pointer;\n}\n.ra-shell .ra-intro-dismiss:hover {\n background: hsl(var(--ra-text) / 0.06);\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-bulk-menu {\n min-width: 12rem;\n background: hsl(var(--ra-surface));\n border: 1px solid hsl(var(--ra-border));\n border-radius: calc(var(--ra-radius) * 0.85);\n box-shadow: var(--ra-card-shadow-hover);\n padding: 0.3rem;\n z-index: 60;\n}\n.ra-shell .ra-bulk-item {\n display: flex;\n align-items: center;\n gap: 0.55rem;\n width: 100%;\n padding: 0.45rem 0.6rem;\n border-radius: calc(var(--ra-radius) * 0.6);\n font-size: 0.8125rem;\n color: hsl(var(--ra-text));\n background: transparent;\n border: 0;\n cursor: pointer;\n text-align: left;\n transition: background .12s ease, color .12s ease;\n}\n.ra-shell .ra-bulk-item:hover {\n background: hsl(var(--ra-muted));\n}\n.ra-shell .ra-bulk-item[data-tone=danger] {\n color: hsl(var(--ra-danger));\n}\n.ra-shell .ra-bulk-item[data-tone=danger]:hover {\n background: hsl(var(--ra-danger) / 0.10);\n}\n.ra-shell .ra-bulk-divider {\n height: 1px;\n background: hsl(var(--ra-border));\n margin: 0.25rem 0;\n}\n.ra-shell .ra-preview-rail {\n background: hsl(var(--ra-surface));\n border-left: 1px solid hsl(var(--ra-border));\n box-shadow: -4px 0 16px hsl(var(--ra-accent) / 0.04);\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n}\n.ra-shell .ra-preview-rail-header {\n position: sticky;\n top: 0;\n z-index: 1;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n background:\n linear-gradient(\n 180deg,\n hsl(var(--ra-surface)) 0%,\n hsl(var(--ra-surface) / 0.92) 100%);\n border-bottom: 1px solid hsl(var(--ra-border));\n backdrop-filter: blur(6px);\n}\n.ra-shell .ra-preview-rail-title {\n display: inline-flex;\n align-items: center;\n gap: 0.4rem;\n font-size: 0.7rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: hsl(var(--ra-muted-text));\n}\n.ra-shell .ra-preview-rail-body {\n flex: 1;\n overflow-y: auto;\n padding: 1rem;\n}\n.ra-confirm-root {\n position: fixed;\n inset: 0;\n z-index: 2147483000;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 1rem;\n}\n.ra-confirm-root .ra-confirm-backdrop {\n position: absolute;\n inset: 0;\n background: hsl(0 0% 0% / 0.45);\n backdrop-filter: blur(2px);\n animation: ra-confirm-fade .12s ease-out;\n}\n.ra-confirm-root .ra-confirm-card {\n position: relative;\n width: min(440px, 100%);\n background: hsl(var(--ra-surface));\n color: hsl(var(--ra-text));\n border: 1px solid hsl(var(--ra-border));\n border-radius: var(--ra-radius);\n box-shadow: 0 1px 2px hsl(0 0% 0% / 0.08), 0 24px 48px -12px hsl(0 0% 0% / 0.32);\n padding: 1.25rem;\n animation: ra-confirm-pop .14s ease-out;\n}\n.ra-confirm-root .ra-confirm-header {\n display: flex;\n align-items: center;\n gap: 0.6rem;\n margin-bottom: 0.5rem;\n}\n.ra-confirm-root .ra-confirm-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.75rem;\n height: 1.75rem;\n border-radius: 999px;\n background: hsl(var(--ra-warning, 38 92% 50%) / 0.12);\n color: hsl(var(--ra-warning, 38 92% 50%));\n}\n.ra-confirm-root .ra-confirm-title {\n font-family: var(--ra-font-display);\n font-weight: 600;\n font-size: 1rem;\n margin: 0;\n}\n.ra-confirm-root .ra-confirm-body {\n font-size: 0.875rem;\n color: hsl(var(--ra-muted-text));\n margin: 0 0 1.1rem;\n line-height: 1.45;\n}\n.ra-confirm-root .ra-confirm-actions {\n display: flex;\n justify-content: flex-end;\n gap: 0.5rem;\n flex-wrap: wrap;\n}\n.ra-confirm-root .ra-confirm-btn {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n border: 1px solid transparent;\n border-radius: calc(var(--ra-radius) - 2px);\n padding: 0.45rem 0.85rem;\n font-size: 0.8125rem;\n font-weight: 500;\n cursor: pointer;\n transition:\n background-color .12s ease,\n border-color .12s ease,\n color .12s ease;\n}\n.ra-confirm-root .ra-confirm-btn:focus-visible {\n outline: none;\n box-shadow: 0 0 0 3px var(--ra-focus-ring);\n}\n.ra-confirm-root .ra-confirm-btn-ghost {\n background: transparent;\n color: hsl(var(--ra-muted-text));\n border-color: hsl(var(--ra-border));\n}\n.ra-confirm-root .ra-confirm-btn-ghost:hover {\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-text));\n}\n.ra-confirm-root .ra-confirm-btn-danger {\n background: transparent;\n color: hsl(var(--ra-danger, 0 72% 51%));\n border-color: hsl(var(--ra-danger, 0 72% 51%) / 0.45);\n}\n.ra-confirm-root .ra-confirm-btn-danger:hover {\n background: hsl(var(--ra-danger, 0 72% 51%) / 0.08);\n border-color: hsl(var(--ra-danger, 0 72% 51%));\n}\n.ra-confirm-root .ra-confirm-btn-primary {\n background: hsl(var(--ra-accent));\n color: hsl(var(--ra-accent-fg, 0 0% 100%));\n border-color: hsl(var(--ra-accent));\n}\n.ra-confirm-root .ra-confirm-btn-primary:hover {\n filter: brightness(0.95);\n}\n@keyframes ra-confirm-fade {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n@keyframes ra-confirm-pop {\n from {\n opacity: 0;\n transform: translateY(4px) scale(.98);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n.ra-shell .ra-unsaved-banner {\n display: flex;\n align-items: center;\n gap: 0.6rem;\n padding: 0.5rem 0.75rem;\n border: 1px solid hsl(var(--ra-warning, 38 92% 50%) / 0.35);\n background: hsl(var(--ra-warning, 38 92% 50%) / 0.08);\n border-radius: var(--ra-radius);\n font-size: 0.8125rem;\n color: hsl(var(--ra-text));\n animation: ra-unsaved-slide .14s ease-out;\n}\n.ra-shell .ra-unsaved-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: hsl(var(--ra-warning, 38 92% 50%));\n flex-shrink: 0;\n}\n.ra-shell .ra-unsaved-text {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.ra-shell .ra-unsaved-context {\n color: hsl(var(--ra-muted-text));\n font-weight: 400;\n}\n.ra-shell .ra-unsaved-error {\n color: hsl(var(--ra-danger, 0 72% 51%));\n font-size: 0.75rem;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n font-weight: 500;\n}\n.ra-shell .ra-unsaved-actions {\n display: inline-flex;\n gap: 0.4rem;\n flex-shrink: 0;\n}\n.ra-shell .ra-unsaved-btn {\n display: inline-flex;\n align-items: center;\n gap: 0.3rem;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n border: 1px solid transparent;\n border-radius: calc(var(--ra-radius) - 2px);\n padding: 0.3rem 0.6rem;\n font-size: 0.75rem;\n font-weight: 500;\n cursor: pointer;\n transition:\n background-color .12s ease,\n border-color .12s ease,\n color .12s ease,\n opacity .12s ease;\n}\n.ra-shell .ra-unsaved-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.ra-shell .ra-unsaved-btn:focus-visible {\n outline: none;\n box-shadow: 0 0 0 3px var(--ra-focus-ring);\n}\n.ra-shell .ra-unsaved-btn-ghost {\n background: transparent;\n color: hsl(var(--ra-muted-text));\n border-color: hsl(var(--ra-border));\n}\n.ra-shell .ra-unsaved-btn-ghost:hover:not(:disabled) {\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-unsaved-btn-primary {\n background: hsl(var(--ra-accent));\n color: hsl(var(--ra-accent-fg, 0 0% 100%));\n border-color: hsl(var(--ra-accent));\n}\n.ra-shell .ra-unsaved-btn-primary:hover:not(:disabled) {\n filter: brightness(0.95);\n}\n@keyframes ra-unsaved-slide {\n from {\n opacity: 0;\n transform: translateY(-3px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n.ra-shell .ra-clipboard-toast {\n position: fixed;\n bottom: 1.25rem;\n left: 50%;\n transform: translateX(-50%);\n z-index: 90;\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n max-width: min(28rem, calc(100vw - 2rem));\n padding: 0.55rem 0.85rem;\n border-radius: 999px;\n background: hsl(var(--ra-text));\n color: hsl(var(--ra-surface));\n font-size: 0.75rem;\n line-height: 1;\n box-shadow: 0 8px 24px -10px hsl(0 0% 0% / 0.45);\n animation: ra-clipboard-pop 0.18s ease-out both;\n pointer-events: none;\n}\n@keyframes ra-clipboard-pop {\n from {\n opacity: 0;\n transform: translate(-50%, 6px);\n }\n to {\n opacity: 1;\n transform: translate(-50%, 0);\n }\n}\n.ra-shell .ra-row-menu-wrap {\n display: inline-flex;\n align-items: center;\n margin-left: 0.25rem;\n}\n.ra-shell .ra-row-menu-trigger {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.5rem;\n height: 1.5rem;\n border-radius: 0.35rem;\n background: transparent;\n color: hsl(var(--ra-muted-text));\n opacity: 0;\n transition:\n opacity .15s ease,\n background .15s ease,\n color .15s ease;\n border: 1px solid transparent;\n}\n.ra-shell .ra-row:hover .ra-row-menu-trigger,\n.ra-shell .ra-card-hover:hover .ra-row-menu-trigger,\n.ra-shell .ra-row-menu-trigger:focus-visible,\n.ra-shell .ra-row-menu-trigger[aria-expanded=true] {\n opacity: 1;\n}\n.ra-shell .ra-row-menu-trigger:hover {\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-row-menu {\n position: absolute;\n right: 0;\n top: calc(100% + 4px);\n z-index: 50;\n min-width: 11rem;\n padding: 0.25rem;\n border-radius: 0.5rem;\n background: hsl(var(--ra-surface));\n border: 1px solid hsl(var(--ra-border));\n box-shadow: 0 12px 28px -10px hsl(0 0% 0% / 0.25);\n display: flex;\n flex-direction: column;\n gap: 0.125rem;\n}\n.ra-shell .ra-row-menu-item {\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.4rem 0.55rem;\n border-radius: 0.35rem;\n font-size: 0.75rem;\n color: hsl(var(--ra-text));\n background: transparent;\n border: 0;\n text-align: left;\n width: 100%;\n cursor: pointer;\n}\n.ra-shell .ra-row-menu-item:hover:not(:disabled) {\n background: hsl(var(--ra-muted));\n}\n.ra-shell .ra-row-menu-item:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n");
|
|
2989
|
+
var TOP_LEVEL_SCOPES = ["facet", "product"];
|
|
2355
2990
|
var defaultItemId = () => {
|
|
2356
2991
|
const time = Date.now().toString(36);
|
|
2357
2992
|
const rand = Math.random().toString(36).slice(2, 8);
|
|
@@ -2390,7 +3025,11 @@ function RecordsAdminShell(props) {
|
|
|
2390
3025
|
className,
|
|
2391
3026
|
previewMode = "inline",
|
|
2392
3027
|
previewScopePicker = false,
|
|
2393
|
-
|
|
3028
|
+
// Default to `keep` — admins can tab around freely while edits are
|
|
3029
|
+
// preserved per editor mount, and the inline UnsavedBanner surfaces the
|
|
3030
|
+
// dirty state at the top of the shell with Save/Discard buttons. Hosts
|
|
3031
|
+
// that want the old blocking behaviour can opt back into `'prompt'`.
|
|
3032
|
+
dirtyStrategy = "keep",
|
|
2394
3033
|
onBeforeDelete,
|
|
2395
3034
|
disableBeforeUnload = false,
|
|
2396
3035
|
presentations = ["list"],
|
|
@@ -2414,10 +3053,23 @@ function RecordsAdminShell(props) {
|
|
|
2414
3053
|
icons: iconsOverride,
|
|
2415
3054
|
groupBy,
|
|
2416
3055
|
renderEmptyState,
|
|
2417
|
-
density = "comfortable"
|
|
3056
|
+
density = "comfortable",
|
|
3057
|
+
enableClipboard = true,
|
|
3058
|
+
onCopy: onCopyOverride,
|
|
3059
|
+
onPaste: onPasteOverride,
|
|
3060
|
+
actionLabels,
|
|
3061
|
+
actionIcons,
|
|
3062
|
+
editorTabs = "off"
|
|
2418
3063
|
} = props;
|
|
2419
3064
|
const i18n = { ...DEFAULT_I18N, ...i18nOverride ?? {} };
|
|
2420
3065
|
const icons = useMemo(() => mergeIcons(iconsOverride), [iconsOverride]);
|
|
3066
|
+
const multiOpenWarnedRef = useRef(false);
|
|
3067
|
+
if (editorTabs === "multi" && !multiOpenWarnedRef.current) {
|
|
3068
|
+
multiOpenWarnedRef.current = true;
|
|
3069
|
+
console.warn(
|
|
3070
|
+
'[RecordsAdminShell] editorTabs="multi" is reserved for a future release and currently behaves like "off". Track in records-admin-shell.md.'
|
|
3071
|
+
);
|
|
3072
|
+
}
|
|
2421
3073
|
const [presentation, setPresentation] = usePresentationPref({
|
|
2422
3074
|
appId,
|
|
2423
3075
|
recordType,
|
|
@@ -2434,7 +3086,7 @@ function RecordsAdminShell(props) {
|
|
|
2434
3086
|
);
|
|
2435
3087
|
const probe = useScopeProbe({ SL, collectionId });
|
|
2436
3088
|
const topLevelScopes = useMemo(
|
|
2437
|
-
() =>
|
|
3089
|
+
() => TOP_LEVEL_SCOPES.filter((s) => requestedScopes.includes(s)),
|
|
2438
3090
|
[requestedScopes]
|
|
2439
3091
|
);
|
|
2440
3092
|
const drillVariantsAllowed = useMemo(
|
|
@@ -2448,6 +3100,7 @@ function RecordsAdminShell(props) {
|
|
|
2448
3100
|
const initialScope = useMemo(() => {
|
|
2449
3101
|
if (contextScope?.productId && topLevelScopes.includes("product")) return "product";
|
|
2450
3102
|
if (defaultScope && topLevelScopes.includes(defaultScope)) return defaultScope;
|
|
3103
|
+
if (topLevelScopes.includes("facet")) return "facet";
|
|
2451
3104
|
return topLevelScopes[0] ?? "product";
|
|
2452
3105
|
}, [contextScope?.productId, defaultScope, topLevelScopes]);
|
|
2453
3106
|
const [activeScope, setActiveScope] = useState(initialScope);
|
|
@@ -2597,13 +3250,25 @@ function RecordsAdminShell(props) {
|
|
|
2597
3250
|
useUnsavedGuard({
|
|
2598
3251
|
isDirty: editorCtx.isDirty,
|
|
2599
3252
|
label: recordType,
|
|
2600
|
-
disableBeforeUnload
|
|
3253
|
+
disableBeforeUnload,
|
|
3254
|
+
// In `keep` mode we don't want the host iframe to pop its own native
|
|
3255
|
+
// confirm — the banner is the canonical surface. Hosts on `prompt` /
|
|
3256
|
+
// `autosave` opt back into platform notifications.
|
|
3257
|
+
disableParentMessage: dirtyStrategy === "keep"
|
|
3258
|
+
});
|
|
3259
|
+
const dirtyConfirm = useConfirmDialog();
|
|
3260
|
+
const pasteConfirm = usePasteConfirm();
|
|
3261
|
+
const clipboard = useRecordClipboard({
|
|
3262
|
+
appId,
|
|
3263
|
+
recordType: recordType ?? "__default"
|
|
2601
3264
|
});
|
|
3265
|
+
const [clipboardNotice, setClipboardNotice] = useState(null);
|
|
2602
3266
|
const { runWithGuard } = useDirtyNavigation({
|
|
2603
3267
|
strategy: dirtyStrategy,
|
|
2604
3268
|
isDirty: editorCtx.isDirty,
|
|
2605
3269
|
save: editorCtx.save,
|
|
2606
3270
|
reset: editorCtx.reset,
|
|
3271
|
+
confirm: dirtyConfirm.confirm,
|
|
2607
3272
|
i18n: {
|
|
2608
3273
|
title: i18n.unsavedPromptTitle,
|
|
2609
3274
|
body: i18n.unsavedPromptBody,
|
|
@@ -2640,11 +3305,195 @@ function RecordsAdminShell(props) {
|
|
|
2640
3305
|
const csvBulk = csvSchema ? { onImportCsv: handleImport, onExportCsv: handleExport } : {};
|
|
2641
3306
|
const [previewScope, setPreviewScope] = useState(null);
|
|
2642
3307
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
3308
|
+
const [sidePreviewOpen, setSidePreviewOpen] = useState(true);
|
|
2643
3309
|
useEffect(() => {
|
|
2644
3310
|
if (!editingScope) return;
|
|
2645
3311
|
setPreviewScope((cur) => cur === null ? editingScope : cur);
|
|
2646
3312
|
}, [editingScope]);
|
|
2647
3313
|
const effectivePreviewScope = previewScope ?? editingScope;
|
|
3314
|
+
const editorHeaderLabel = useMemo(() => {
|
|
3315
|
+
if (!editingScope) return void 0;
|
|
3316
|
+
if (activeScope === "facet" && selectedFacetRef) {
|
|
3317
|
+
const hit = facetBrowse.items.find((it) => it.ref === selectedFacetRef);
|
|
3318
|
+
return hit?.label;
|
|
3319
|
+
}
|
|
3320
|
+
if (activeScope === "product" && selectedProductId) {
|
|
3321
|
+
const hit = productBrowse.items.find((it) => it.id === selectedProductId);
|
|
3322
|
+
return hit?.name ?? selectedProductId;
|
|
3323
|
+
}
|
|
3324
|
+
return void 0;
|
|
3325
|
+
}, [activeScope, editingScope, selectedFacetRef, selectedProductId, facetBrowse.items, productBrowse.items]);
|
|
3326
|
+
const editorHeaderSubtitle = useMemo(() => {
|
|
3327
|
+
if (!editingScope) return void 0;
|
|
3328
|
+
if (activeScope === "facet" && selectedFacetRef) {
|
|
3329
|
+
const hit = facetBrowse.items.find((it) => it.ref === selectedFacetRef);
|
|
3330
|
+
return hit?.subtitle;
|
|
3331
|
+
}
|
|
3332
|
+
return void 0;
|
|
3333
|
+
}, [activeScope, editingScope, selectedFacetRef, selectedProductId, facetBrowse.items, productBrowse.items]);
|
|
3334
|
+
const editorHeaderMeta = useMemo(() => {
|
|
3335
|
+
if (!editingScope) return void 0;
|
|
3336
|
+
if (activeScope === "product" && selectedProductId) {
|
|
3337
|
+
const hit = productBrowse.items.find((it) => it.id === selectedProductId);
|
|
3338
|
+
return hit?.sku ?? selectedProductId;
|
|
3339
|
+
}
|
|
3340
|
+
return void 0;
|
|
3341
|
+
}, [activeScope, editingScope, selectedProductId, productBrowse.items]);
|
|
3342
|
+
const copyCurrent = useCallback(() => {
|
|
3343
|
+
if (!enableClipboard || !editingScope) return;
|
|
3344
|
+
const sourceValue = onCopyOverride ? onCopyOverride({ value: editorCtx.value, scope: editingScope }) : cloneValue(editorCtx.value);
|
|
3345
|
+
clipboard.set({
|
|
3346
|
+
value: sourceValue,
|
|
3347
|
+
sourceScope: editingScope.kind,
|
|
3348
|
+
sourceRef: editingScope.raw,
|
|
3349
|
+
sourceLabel: editorHeaderLabel
|
|
3350
|
+
});
|
|
3351
|
+
onTelemetry?.({ type: "clipboard.copy", recordType, sourceRef: editingScope.raw });
|
|
3352
|
+
setClipboardNotice({
|
|
3353
|
+
message: i18n.copyToast.replace("{name}", editorHeaderLabel ?? editingScope.raw),
|
|
3354
|
+
variant: "copy"
|
|
3355
|
+
});
|
|
3356
|
+
}, [
|
|
3357
|
+
enableClipboard,
|
|
3358
|
+
editingScope,
|
|
3359
|
+
onCopyOverride,
|
|
3360
|
+
editorCtx.value,
|
|
3361
|
+
clipboard,
|
|
3362
|
+
editorHeaderLabel,
|
|
3363
|
+
onTelemetry,
|
|
3364
|
+
recordType,
|
|
3365
|
+
i18n.copyToast
|
|
3366
|
+
]);
|
|
3367
|
+
const pasteCurrent = useCallback(async () => {
|
|
3368
|
+
if (!enableClipboard || !editingScope || !clipboard.entry) return;
|
|
3369
|
+
const compat = checkPasteCompatibility(
|
|
3370
|
+
{ kind: clipboard.entry.sourceScope, raw: clipboard.entry.sourceRef },
|
|
3371
|
+
editingScope
|
|
3372
|
+
);
|
|
3373
|
+
if (compat.status === "denied") {
|
|
3374
|
+
setClipboardNotice({ message: compat.reason ?? "Paste not allowed here", variant: "paste" });
|
|
3375
|
+
return;
|
|
3376
|
+
}
|
|
3377
|
+
if (compat.status === "warn") {
|
|
3378
|
+
const ok = await pasteConfirm.confirm({
|
|
3379
|
+
title: i18n.pasteWarnTitle,
|
|
3380
|
+
body: compat.reason ?? "",
|
|
3381
|
+
confirmLabel: i18n.pasteWarnContinue,
|
|
3382
|
+
cancelLabel: i18n.pasteConfirmCancel
|
|
3383
|
+
});
|
|
3384
|
+
if (!ok) return;
|
|
3385
|
+
}
|
|
3386
|
+
const willReplace = resolved.source === "self";
|
|
3387
|
+
if (willReplace) {
|
|
3388
|
+
const ok = await pasteConfirm.confirm({
|
|
3389
|
+
title: i18n.pasteConfirmTitle,
|
|
3390
|
+
body: i18n.pasteConfirmBody.replace(
|
|
3391
|
+
"{destination}",
|
|
3392
|
+
editorHeaderLabel ?? editingScope.raw
|
|
3393
|
+
),
|
|
3394
|
+
confirmLabel: i18n.pasteConfirmReplace,
|
|
3395
|
+
cancelLabel: i18n.pasteConfirmCancel
|
|
3396
|
+
});
|
|
3397
|
+
if (!ok) return;
|
|
3398
|
+
}
|
|
3399
|
+
const sourceParsed = { kind: clipboard.entry.sourceScope, raw: clipboard.entry.sourceRef };
|
|
3400
|
+
const transformed = onPasteOverride ? onPasteOverride(
|
|
3401
|
+
{ value: clipboard.entry.value, sourceScope: sourceParsed },
|
|
3402
|
+
{ scope: editingScope, currentValue: editorCtx.value ?? null }
|
|
3403
|
+
) : cloneValue(clipboard.entry.value);
|
|
3404
|
+
if (transformed === null) return;
|
|
3405
|
+
editorCtx.onChange(transformed);
|
|
3406
|
+
onTelemetry?.({
|
|
3407
|
+
type: "clipboard.paste",
|
|
3408
|
+
recordType,
|
|
3409
|
+
sourceRef: clipboard.entry.sourceRef,
|
|
3410
|
+
destinationRef: editingScope.raw,
|
|
3411
|
+
replaced: willReplace
|
|
3412
|
+
});
|
|
3413
|
+
setClipboardNotice({
|
|
3414
|
+
message: i18n.pasteToast.replace("{name}", clipboard.entry.sourceLabel ?? clipboard.entry.sourceRef),
|
|
3415
|
+
variant: "paste"
|
|
3416
|
+
});
|
|
3417
|
+
}, [
|
|
3418
|
+
enableClipboard,
|
|
3419
|
+
editingScope,
|
|
3420
|
+
clipboard.entry,
|
|
3421
|
+
pasteConfirm,
|
|
3422
|
+
resolved.source,
|
|
3423
|
+
onPasteOverride,
|
|
3424
|
+
editorCtx,
|
|
3425
|
+
onTelemetry,
|
|
3426
|
+
recordType,
|
|
3427
|
+
editorHeaderLabel,
|
|
3428
|
+
i18n.pasteWarnTitle,
|
|
3429
|
+
i18n.pasteWarnContinue,
|
|
3430
|
+
i18n.pasteConfirmCancel,
|
|
3431
|
+
i18n.pasteConfirmTitle,
|
|
3432
|
+
i18n.pasteConfirmBody,
|
|
3433
|
+
i18n.pasteConfirmReplace,
|
|
3434
|
+
i18n.pasteToast
|
|
3435
|
+
]);
|
|
3436
|
+
const editorPasteCompat = useMemo(() => {
|
|
3437
|
+
if (!enableClipboard || !clipboard.entry || !editingScope) return null;
|
|
3438
|
+
if (clipboard.entry.sourceRef === editingScope.raw) return { status: "denied" };
|
|
3439
|
+
return checkPasteCompatibility(
|
|
3440
|
+
{ kind: clipboard.entry.sourceScope, raw: clipboard.entry.sourceRef },
|
|
3441
|
+
editingScope
|
|
3442
|
+
);
|
|
3443
|
+
}, [enableClipboard, clipboard.entry, editingScope]);
|
|
3444
|
+
const editorClipboard = enableClipboard && editingScope ? {
|
|
3445
|
+
onCopy: copyCurrent,
|
|
3446
|
+
onPaste: () => {
|
|
3447
|
+
void pasteCurrent();
|
|
3448
|
+
},
|
|
3449
|
+
canCopy: true,
|
|
3450
|
+
canPaste: !!clipboard.entry && editorPasteCompat?.status !== "denied",
|
|
3451
|
+
pasteSourceLabel: clipboard.entry?.sourceLabel,
|
|
3452
|
+
pasteWillReplace: resolved.source === "self" && !!clipboard.entry
|
|
3453
|
+
} : void 0;
|
|
3454
|
+
const [pendingPasteRef, setPendingPasteRef] = useState(null);
|
|
3455
|
+
useEffect(() => {
|
|
3456
|
+
if (!pendingPasteRef) return;
|
|
3457
|
+
if (!editingScope || editingScope.raw !== pendingPasteRef) return;
|
|
3458
|
+
const t = window.setTimeout(() => {
|
|
3459
|
+
setPendingPasteRef(null);
|
|
3460
|
+
void pasteCurrent();
|
|
3461
|
+
}, 0);
|
|
3462
|
+
return () => window.clearTimeout(t);
|
|
3463
|
+
}, [pendingPasteRef, editingScope, pasteCurrent]);
|
|
3464
|
+
const rowClipboard = enableClipboard ? (record) => {
|
|
3465
|
+
const summaryHasData = record.data != null;
|
|
3466
|
+
const sourceParsed = record.scope;
|
|
3467
|
+
const compat = clipboard.entry ? checkPasteCompatibility(
|
|
3468
|
+
{ kind: clipboard.entry.sourceScope, raw: clipboard.entry.sourceRef },
|
|
3469
|
+
sourceParsed
|
|
3470
|
+
) : null;
|
|
3471
|
+
const sameRef = clipboard.entry?.sourceRef === record.ref;
|
|
3472
|
+
return {
|
|
3473
|
+
onCopy: summaryHasData ? () => {
|
|
3474
|
+
const value = onCopyOverride ? onCopyOverride({ value: record.data, scope: sourceParsed }) : cloneValue(record.data);
|
|
3475
|
+
clipboard.set({
|
|
3476
|
+
value,
|
|
3477
|
+
sourceScope: sourceParsed.kind,
|
|
3478
|
+
sourceRef: record.ref,
|
|
3479
|
+
sourceLabel: record.label
|
|
3480
|
+
});
|
|
3481
|
+
onTelemetry?.({ type: "clipboard.copy", recordType, sourceRef: record.ref });
|
|
3482
|
+
setClipboardNotice({
|
|
3483
|
+
message: i18n.copyToast.replace("{name}", record.label),
|
|
3484
|
+
variant: "copy"
|
|
3485
|
+
});
|
|
3486
|
+
} : void 0,
|
|
3487
|
+
onPaste: () => {
|
|
3488
|
+
onLeftSelectRef.current?.(record);
|
|
3489
|
+
setPendingPasteRef(record.ref);
|
|
3490
|
+
},
|
|
3491
|
+
canPaste: !!clipboard.entry && !sameRef && compat?.status !== "denied",
|
|
3492
|
+
pasteWillReplace: record.status === "configured",
|
|
3493
|
+
clipboardSourceLabel: clipboard.entry?.sourceLabel
|
|
3494
|
+
};
|
|
3495
|
+
} : void 0;
|
|
3496
|
+
const onLeftSelectRef = useRef(null);
|
|
2648
3497
|
const renderEditorWithPreview = () => {
|
|
2649
3498
|
if (!editingScope) return null;
|
|
2650
3499
|
const previewBody = renderPreview && effectivePreviewScope ? renderPreview({ resolved: editorCtx.value, previewScope: effectivePreviewScope }) : null;
|
|
@@ -2670,6 +3519,12 @@ function RecordsAdminShell(props) {
|
|
|
2670
3519
|
preview: inlinePreviewBody,
|
|
2671
3520
|
footerExtra: extraFooter,
|
|
2672
3521
|
onBeforeDelete: onBeforeDelete && editingScope ? () => onBeforeDelete(editingScope) : void 0,
|
|
3522
|
+
headerLabel: editorHeaderLabel,
|
|
3523
|
+
headerSubtitle: editorHeaderSubtitle,
|
|
3524
|
+
headerMeta: editorHeaderMeta,
|
|
3525
|
+
clipboard: editorClipboard,
|
|
3526
|
+
actionLabels,
|
|
3527
|
+
actionIcons,
|
|
2673
3528
|
children: renderEditor(editorCtx)
|
|
2674
3529
|
}
|
|
2675
3530
|
);
|
|
@@ -2681,9 +3536,28 @@ function RecordsAdminShell(props) {
|
|
|
2681
3536
|
);
|
|
2682
3537
|
}
|
|
2683
3538
|
if (previewMode === "side") {
|
|
3539
|
+
if (!sidePreviewOpen) {
|
|
3540
|
+
return /* @__PURE__ */ jsx("div", { className: "relative h-full", children: baseEditor(
|
|
3541
|
+
/* @__PURE__ */ jsx(
|
|
3542
|
+
PreviewToggleButton,
|
|
3543
|
+
{
|
|
3544
|
+
onClick: () => setSidePreviewOpen(true),
|
|
3545
|
+
label: i18n.openPreview
|
|
3546
|
+
}
|
|
3547
|
+
)
|
|
3548
|
+
) });
|
|
3549
|
+
}
|
|
2684
3550
|
return /* @__PURE__ */ jsxs("div", { className: "grid h-full", style: { gridTemplateColumns: "minmax(0, 1fr) minmax(280px, 420px)" }, children: [
|
|
2685
3551
|
/* @__PURE__ */ jsx("div", { className: "overflow-hidden", children: baseEditor() }),
|
|
2686
|
-
/* @__PURE__ */ jsx(
|
|
3552
|
+
/* @__PURE__ */ jsx(
|
|
3553
|
+
SidePreview,
|
|
3554
|
+
{
|
|
3555
|
+
label: i18n.preview,
|
|
3556
|
+
scopePicker,
|
|
3557
|
+
onClose: () => setSidePreviewOpen(false),
|
|
3558
|
+
children: previewBody
|
|
3559
|
+
}
|
|
3560
|
+
)
|
|
2687
3561
|
] });
|
|
2688
3562
|
}
|
|
2689
3563
|
if (previewMode === "tab") {
|
|
@@ -2715,6 +3589,18 @@ function RecordsAdminShell(props) {
|
|
|
2715
3589
|
};
|
|
2716
3590
|
const isProductTab = activeScope === "product";
|
|
2717
3591
|
const productPinned = !!contextScope?.productId;
|
|
3592
|
+
const effectivePresentation = isProductTab ? presentation : "list";
|
|
3593
|
+
const showPresentationSwitcher = isProductTab && presentations.length > 1;
|
|
3594
|
+
const effectiveGroupBy = useMemo(() => {
|
|
3595
|
+
if (groupBy) return groupBy;
|
|
3596
|
+
if (isProductTab) return void 0;
|
|
3597
|
+
return (record) => {
|
|
3598
|
+
const key = record.scope.facetId;
|
|
3599
|
+
if (!key) return null;
|
|
3600
|
+
const label2 = record.subtitle ?? key;
|
|
3601
|
+
return { key, label: label2 };
|
|
3602
|
+
};
|
|
3603
|
+
}, [groupBy, isProductTab]);
|
|
2718
3604
|
const productListItems = useMemo(() => {
|
|
2719
3605
|
if (productPinned) {
|
|
2720
3606
|
return [{
|
|
@@ -2745,12 +3631,23 @@ function RecordsAdminShell(props) {
|
|
|
2745
3631
|
}
|
|
2746
3632
|
});
|
|
2747
3633
|
};
|
|
3634
|
+
onLeftSelectRef.current = onLeftSelect;
|
|
2748
3635
|
return /* @__PURE__ */ jsxs(
|
|
2749
3636
|
"div",
|
|
2750
3637
|
{
|
|
2751
3638
|
className: `ra-shell flex flex-col h-full ${className ?? ""}`,
|
|
2752
3639
|
"data-density": density,
|
|
2753
3640
|
children: [
|
|
3641
|
+
dirtyConfirm.dialog,
|
|
3642
|
+
pasteConfirm.dialog,
|
|
3643
|
+
clipboardNotice && /* @__PURE__ */ jsx(
|
|
3644
|
+
ClipboardToast,
|
|
3645
|
+
{
|
|
3646
|
+
message: clipboardNotice.message,
|
|
3647
|
+
variant: clipboardNotice.variant,
|
|
3648
|
+
onDismiss: () => setClipboardNotice(null)
|
|
3649
|
+
}
|
|
3650
|
+
),
|
|
2754
3651
|
/* @__PURE__ */ jsxs("div", { className: "px-4 pt-4 space-y-3", children: [
|
|
2755
3652
|
intro && !dismissed && /* @__PURE__ */ jsx(
|
|
2756
3653
|
IntroCard,
|
|
@@ -2795,6 +3692,27 @@ function RecordsAdminShell(props) {
|
|
|
2795
3692
|
introHidden: dismissed && !!intro,
|
|
2796
3693
|
onShowIntro: undismiss
|
|
2797
3694
|
}
|
|
3695
|
+
),
|
|
3696
|
+
editorCtx.isDirty && /* @__PURE__ */ jsx(
|
|
3697
|
+
UnsavedBanner,
|
|
3698
|
+
{
|
|
3699
|
+
label: editorHeaderLabel,
|
|
3700
|
+
context: editorHeaderSubtitle,
|
|
3701
|
+
isSaving: !!editorCtx.isSaving,
|
|
3702
|
+
saveError: editorCtx.saveError,
|
|
3703
|
+
onSave: () => {
|
|
3704
|
+
void editorCtx.save().catch(() => {
|
|
3705
|
+
});
|
|
3706
|
+
},
|
|
3707
|
+
onDiscard: editorCtx.reset,
|
|
3708
|
+
saveLabel: actionLabels?.save ?? i18n.save,
|
|
3709
|
+
discardLabel: actionLabels?.discard ?? i18n.discard,
|
|
3710
|
+
SaveIcon: actionIcons?.save,
|
|
3711
|
+
DiscardIcon: actionIcons?.discard,
|
|
3712
|
+
savingLabel: i18n.saving,
|
|
3713
|
+
errorLabel: i18n.saveError,
|
|
3714
|
+
bodyTemplate: i18n.unsavedBannerBody
|
|
3715
|
+
}
|
|
2798
3716
|
)
|
|
2799
3717
|
] }),
|
|
2800
3718
|
/* @__PURE__ */ jsxs(
|
|
@@ -2842,14 +3760,23 @@ function RecordsAdminShell(props) {
|
|
|
2842
3760
|
/* @__PURE__ */ jsx(
|
|
2843
3761
|
PresentationSwitcher,
|
|
2844
3762
|
{
|
|
2845
|
-
options: presentations,
|
|
3763
|
+
options: showPresentationSwitcher ? presentations : [],
|
|
2846
3764
|
value: presentation,
|
|
2847
3765
|
onChange: onPresentationChange,
|
|
2848
3766
|
i18n
|
|
2849
3767
|
}
|
|
2850
3768
|
)
|
|
2851
3769
|
] }),
|
|
2852
|
-
!isProductTab && /* @__PURE__ */ jsx(
|
|
3770
|
+
!isProductTab && /* @__PURE__ */ jsx(
|
|
3771
|
+
StatusFilterPills,
|
|
3772
|
+
{
|
|
3773
|
+
value: filter,
|
|
3774
|
+
onChange: setFilter,
|
|
3775
|
+
counts: facetBrowse.counts,
|
|
3776
|
+
hideZero: ["partial"],
|
|
3777
|
+
i18n
|
|
3778
|
+
}
|
|
3779
|
+
),
|
|
2853
3780
|
cardinality === "collection" && !isProductTab && editingScope && /* @__PURE__ */ jsxs(
|
|
2854
3781
|
"button",
|
|
2855
3782
|
{
|
|
@@ -2891,10 +3818,11 @@ function RecordsAdminShell(props) {
|
|
|
2891
3818
|
selectedRef: leftSelectedRef,
|
|
2892
3819
|
onSelect: onLeftSelect,
|
|
2893
3820
|
dirtyRef: editorCtx.isDirty ? editingScope?.raw : void 0,
|
|
2894
|
-
presentation,
|
|
3821
|
+
presentation: effectivePresentation,
|
|
2895
3822
|
renderListRow,
|
|
2896
3823
|
renderCard,
|
|
2897
|
-
groupBy
|
|
3824
|
+
groupBy: effectiveGroupBy,
|
|
3825
|
+
rowClipboard
|
|
2898
3826
|
}
|
|
2899
3827
|
),
|
|
2900
3828
|
isProductTab && !productPinned && productBrowse.hasNextPage && /* @__PURE__ */ jsx("div", { className: "p-2", children: /* @__PURE__ */ jsx(
|
|
@@ -2922,6 +3850,7 @@ function RecordsAdminShell(props) {
|
|
|
2922
3850
|
productLabel: productBrowse.items.find((p) => p.id === selectedProductId)?.name ?? selectedProductId,
|
|
2923
3851
|
showVariants: drillVariantsAllowed,
|
|
2924
3852
|
showBatches: drillBatchesAllowed,
|
|
3853
|
+
hideSingleTab: editorTabs === "off" || editorTabs === "multi",
|
|
2925
3854
|
active: drillTab,
|
|
2926
3855
|
onChange: (t) => {
|
|
2927
3856
|
void runWithGuard(() => {
|
|
@@ -3354,6 +4283,6 @@ function useMergedRecord(args) {
|
|
|
3354
4283
|
};
|
|
3355
4284
|
}
|
|
3356
4285
|
|
|
3357
|
-
export { ALL_PRESENTATIONS, BatchList, BulkActionsMenu, DEFAULT_I18N, DEFAULT_ICONS, DefaultRecordCard, DefaultRecordRow, DeleteButton, DrawerPreview, EmptyState, ErrorState, FacetList, InheritanceMarker, InheritanceProvider, InlinePreview, IntroCard, LoadingState, PresentationSwitcher, PreviewScopePicker, PreviewToggleButton, ProductDrillDown, ProductList, RecordBrowser, RecordEditor, RecordList, RecordsAdminShell, ResolvedPreview, ScopeBreadcrumb, ScopeTabs, SidePreview, StatusDot, StatusFilterPills, TabbedPreview, UtilityRow, VariantList, buildRef, bulkDelete, bulkUpsert, deleteRecord, downloadBlob, exportCsv, getRecordByRef, importCsv, listRecords, matchRecords, mergeIcons, parseRef, parsedRefToScope, parsedRefToTarget, pickHeaderIcon, resolutionChain, resolveRecord, restoreRecord, scopesEqual, upsertRecord, useCollectedRecords, useDirtyNavigation, useFacetBrowse, useIntroDismissed, useMergedRecord, usePresentationPref, useProductBrowse, useProductChildren, useRecordEditor, useRecordList, useResolveAllRecords, useResolvedRecord, useRulePreview, useScopeProbe, useUnsavedGuard };
|
|
4286
|
+
export { ALL_PRESENTATIONS, BatchList, BulkActionsMenu, DEFAULT_I18N, DEFAULT_ICONS, DefaultRecordCard, DefaultRecordRow, DeleteButton, DrawerPreview, EmptyState, ErrorState, FacetList, InheritanceMarker, InheritanceProvider, InlinePreview, IntroCard, LoadingState, PresentationSwitcher, PreviewScopePicker, PreviewToggleButton, ProductDrillDown, ProductList, RecordBrowser, RecordEditor, RecordList, RecordsAdminShell, ResolvedPreview, ScopeBreadcrumb, ScopeTabs, SidePreview, StatusDot, StatusFilterPills, TabbedPreview, UtilityRow, VariantList, buildRef, bulkDelete, bulkUpsert, checkPasteCompatibility, cloneValue, deleteRecord, downloadBlob, exportCsv, getRecordByRef, importCsv, listRecords, matchRecords, mergeIcons, parseRef, parsedRefToScope, parsedRefToTarget, pickHeaderIcon, resolutionChain, resolveRecord, restoreRecord, scopesEqual, upsertRecord, useCollectedRecords, useDirtyNavigation, useFacetBrowse, useIntroDismissed, useMergedRecord, usePresentationPref, useProductBrowse, useProductChildren, useRecordClipboard, useRecordEditor, useRecordList, useResolveAllRecords, useResolvedRecord, useRulePreview, useScopeProbe, useUnsavedGuard };
|
|
3358
4287
|
//# sourceMappingURL=index.js.map
|
|
3359
4288
|
//# sourceMappingURL=index.js.map
|