@mulmoclaude/collection-plugin 0.7.6 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/style.css +14 -0
- package/dist/vue/components/CollectionBacklinksView.vue.d.ts +9 -0
- package/dist/vue/components/CollectionBacklinksView.vue.d.ts.map +1 -0
- package/dist/vue/components/CollectionRecordPanel.vue.d.ts.map +1 -1
- package/dist/vue/components/CollectionView.vue.d.ts.map +1 -1
- package/dist/vue/useCollectionRendering.d.ts +2 -1
- package/dist/vue/useCollectionRendering.d.ts.map +1 -1
- package/dist/vue/useCollectionRendering.helpers.d.ts +11 -0
- package/dist/vue/useCollectionRendering.helpers.d.ts.map +1 -1
- package/dist/vue/useCollectionRendering.renderers.d.ts +11 -1
- package/dist/vue/useCollectionRendering.renderers.d.ts.map +1 -1
- package/dist/vue/useLinkedCollectionCaches.d.ts +5 -1
- package/dist/vue/useLinkedCollectionCaches.d.ts.map +1 -1
- package/dist/vue.cjs +187 -51
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.js +188 -52
- package/dist/vue.js.map +1 -1
- package/package.json +3 -3
package/dist/vue.cjs
CHANGED
|
@@ -1234,18 +1234,18 @@ var CollectionRecordModal_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
1234
1234
|
});
|
|
1235
1235
|
//#endregion
|
|
1236
1236
|
//#region src/vue/components/CollectionCalendarView.vue?vue&type=script&setup=true&lang.ts
|
|
1237
|
-
var _hoisted_1$
|
|
1237
|
+
var _hoisted_1$15 = {
|
|
1238
1238
|
class: "flex flex-col gap-3",
|
|
1239
1239
|
"data-testid": "collection-calendar"
|
|
1240
1240
|
};
|
|
1241
|
-
var _hoisted_2$
|
|
1242
|
-
var _hoisted_3$
|
|
1243
|
-
var _hoisted_4$
|
|
1244
|
-
var _hoisted_5$
|
|
1241
|
+
var _hoisted_2$14 = { class: "flex items-center gap-2" };
|
|
1242
|
+
var _hoisted_3$14 = ["aria-label"];
|
|
1243
|
+
var _hoisted_4$14 = ["aria-label"];
|
|
1244
|
+
var _hoisted_5$13 = {
|
|
1245
1245
|
class: "text-sm font-bold text-slate-800 flex-1",
|
|
1246
1246
|
"data-testid": "collection-calendar-month"
|
|
1247
1247
|
};
|
|
1248
|
-
var _hoisted_6$
|
|
1248
|
+
var _hoisted_6$12 = { class: "grid grid-cols-7 gap-1 text-[10px] font-bold text-slate-400 uppercase tracking-wider select-none" };
|
|
1249
1249
|
var _hoisted_7$10 = { class: "grid grid-cols-7 gap-1" };
|
|
1250
1250
|
var _hoisted_8$10 = [
|
|
1251
1251
|
"aria-label",
|
|
@@ -1375,23 +1375,23 @@ var CollectionCalendarView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
1375
1375
|
viewMonth.value = now.getMonth() + 1;
|
|
1376
1376
|
}
|
|
1377
1377
|
return (_ctx, _cache) => {
|
|
1378
|
-
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$
|
|
1379
|
-
(0, vue.createElementVNode)("div", _hoisted_2$
|
|
1378
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$15, [
|
|
1379
|
+
(0, vue.createElementVNode)("div", _hoisted_2$14, [
|
|
1380
1380
|
(0, vue.createElementVNode)("button", {
|
|
1381
1381
|
type: "button",
|
|
1382
1382
|
class: "h-8 w-8 flex items-center justify-center rounded text-slate-500 hover:bg-slate-100 transition-colors",
|
|
1383
1383
|
"aria-label": (0, vue.unref)(t)("collectionsView.calendarPrevMonth"),
|
|
1384
1384
|
"data-testid": "collection-calendar-prev",
|
|
1385
1385
|
onClick: _cache[0] || (_cache[0] = ($event) => stepMonth(-1))
|
|
1386
|
-
}, [..._cache[2] || (_cache[2] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "chevron_left", -1)])], 8, _hoisted_3$
|
|
1386
|
+
}, [..._cache[2] || (_cache[2] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "chevron_left", -1)])], 8, _hoisted_3$14),
|
|
1387
1387
|
(0, vue.createElementVNode)("button", {
|
|
1388
1388
|
type: "button",
|
|
1389
1389
|
class: "h-8 w-8 flex items-center justify-center rounded text-slate-500 hover:bg-slate-100 transition-colors",
|
|
1390
1390
|
"aria-label": (0, vue.unref)(t)("collectionsView.calendarNextMonth"),
|
|
1391
1391
|
"data-testid": "collection-calendar-next",
|
|
1392
1392
|
onClick: _cache[1] || (_cache[1] = ($event) => stepMonth(1))
|
|
1393
|
-
}, [..._cache[3] || (_cache[3] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "chevron_right", -1)])], 8, _hoisted_4$
|
|
1394
|
-
(0, vue.createElementVNode)("h3", _hoisted_5$
|
|
1393
|
+
}, [..._cache[3] || (_cache[3] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "chevron_right", -1)])], 8, _hoisted_4$14),
|
|
1394
|
+
(0, vue.createElementVNode)("h3", _hoisted_5$13, (0, vue.toDisplayString)(monthLabel.value), 1),
|
|
1395
1395
|
(0, vue.createElementVNode)("button", {
|
|
1396
1396
|
type: "button",
|
|
1397
1397
|
class: "h-8 px-2.5 flex items-center gap-1 rounded border border-slate-200 bg-white text-slate-600 hover:bg-slate-50 text-xs font-bold transition-colors",
|
|
@@ -1399,7 +1399,7 @@ var CollectionCalendarView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
1399
1399
|
onClick: goToday
|
|
1400
1400
|
}, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.calendarToday")), 1)
|
|
1401
1401
|
]),
|
|
1402
|
-
(0, vue.createElementVNode)("div", _hoisted_6$
|
|
1402
|
+
(0, vue.createElementVNode)("div", _hoisted_6$12, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(weekdayLabels.value, (label, idx) => {
|
|
1403
1403
|
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
1404
1404
|
key: idx,
|
|
1405
1405
|
class: "px-1 py-1 text-center"
|
|
@@ -1440,18 +1440,18 @@ var CollectionCalendarView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
1440
1440
|
});
|
|
1441
1441
|
//#endregion
|
|
1442
1442
|
//#region src/vue/components/CollectionDayView.vue?vue&type=script&setup=true&lang.ts
|
|
1443
|
-
var _hoisted_1$
|
|
1444
|
-
var _hoisted_2$
|
|
1443
|
+
var _hoisted_1$14 = { class: "flex items-center gap-2 border-b border-slate-200 px-4 py-3" };
|
|
1444
|
+
var _hoisted_2$13 = {
|
|
1445
1445
|
class: "flex-1 text-sm font-bold text-slate-800",
|
|
1446
1446
|
"data-testid": "collection-day-view-title"
|
|
1447
1447
|
};
|
|
1448
|
-
var _hoisted_3$
|
|
1449
|
-
var _hoisted_4$
|
|
1450
|
-
var _hoisted_5$
|
|
1448
|
+
var _hoisted_3$13 = ["aria-label"];
|
|
1449
|
+
var _hoisted_4$13 = ["aria-label"];
|
|
1450
|
+
var _hoisted_5$12 = {
|
|
1451
1451
|
key: 0,
|
|
1452
1452
|
class: "px-4 py-10 text-center text-sm text-slate-400"
|
|
1453
1453
|
};
|
|
1454
|
-
var _hoisted_6$
|
|
1454
|
+
var _hoisted_6$11 = { class: "absolute -top-2 left-0 w-10 pr-1 text-right text-[10px] tabular-nums text-slate-400" };
|
|
1455
1455
|
var _hoisted_7$9 = {
|
|
1456
1456
|
class: "absolute inset-y-0 right-0",
|
|
1457
1457
|
style: { "left": "2.75rem" }
|
|
@@ -1537,6 +1537,7 @@ var CollectionDayView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
1537
1537
|
"datetime",
|
|
1538
1538
|
"table",
|
|
1539
1539
|
"embed",
|
|
1540
|
+
"backlinks",
|
|
1540
1541
|
"image",
|
|
1541
1542
|
"markdown"
|
|
1542
1543
|
]);
|
|
@@ -1632,8 +1633,8 @@ var CollectionDayView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
1632
1633
|
role: "dialog",
|
|
1633
1634
|
"aria-modal": "true"
|
|
1634
1635
|
}, [(0, vue.createElementVNode)("div", { class: (0, vue.normalizeClass)(["flex min-h-0 flex-col", __props.showDetail ? "w-80 shrink-0 border-r border-slate-200" : "w-full"]) }, [
|
|
1635
|
-
(0, vue.createElementVNode)("div", _hoisted_1$
|
|
1636
|
-
(0, vue.createElementVNode)("h3", _hoisted_2$
|
|
1636
|
+
(0, vue.createElementVNode)("div", _hoisted_1$14, [
|
|
1637
|
+
(0, vue.createElementVNode)("h3", _hoisted_2$13, (0, vue.toDisplayString)(dayLabel.value), 1),
|
|
1637
1638
|
__props.canCreate ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1638
1639
|
key: 0,
|
|
1639
1640
|
type: "button",
|
|
@@ -1641,16 +1642,16 @@ var CollectionDayView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
1641
1642
|
"aria-label": (0, vue.unref)(t)("collectionsView.calendarCreateOn", { date: dayKey.value }),
|
|
1642
1643
|
"data-testid": "collection-day-view-create",
|
|
1643
1644
|
onClick: onCreate
|
|
1644
|
-
}, [..._cache[3] || (_cache[3] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "add", -1)])], 8, _hoisted_3$
|
|
1645
|
+
}, [..._cache[3] || (_cache[3] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "add", -1)])], 8, _hoisted_3$13)) : (0, vue.createCommentVNode)("", true),
|
|
1645
1646
|
(0, vue.createElementVNode)("button", {
|
|
1646
1647
|
type: "button",
|
|
1647
1648
|
class: "h-8 w-8 flex items-center justify-center rounded text-slate-500 hover:bg-slate-100 transition-colors",
|
|
1648
1649
|
"aria-label": (0, vue.unref)(t)("collectionsView.dayViewClose"),
|
|
1649
1650
|
"data-testid": "collection-day-view-close",
|
|
1650
1651
|
onClick: _cache[0] || (_cache[0] = ($event) => emit("close"))
|
|
1651
|
-
}, [..._cache[4] || (_cache[4] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "close", -1)])], 8, _hoisted_4$
|
|
1652
|
+
}, [..._cache[4] || (_cache[4] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "close", -1)])], 8, _hoisted_4$13)
|
|
1652
1653
|
]),
|
|
1653
|
-
timedEntries.value.length === 0 && allDayEntries.value.length === 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_5$
|
|
1654
|
+
timedEntries.value.length === 0 && allDayEntries.value.length === 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_5$12, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.dayViewEmpty")), 1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
1654
1655
|
key: 1,
|
|
1655
1656
|
ref_key: "scrollEl",
|
|
1656
1657
|
ref: scrollEl,
|
|
@@ -1664,7 +1665,7 @@ var CollectionDayView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
1664
1665
|
key: hour,
|
|
1665
1666
|
class: "absolute left-0 right-0 border-t border-slate-100",
|
|
1666
1667
|
style: (0, vue.normalizeStyle)({ top: `${(hour - 1) * HOUR_PX}px` })
|
|
1667
|
-
}, [(0, vue.createElementVNode)("span", _hoisted_6$
|
|
1668
|
+
}, [(0, vue.createElementVNode)("span", _hoisted_6$11, (0, vue.toDisplayString)(hourLabel(hour - 1)), 1)], 4);
|
|
1668
1669
|
}), 64)), (0, vue.createElementVNode)("div", _hoisted_7$9, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(timedEntries.value, (entry) => {
|
|
1669
1670
|
return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1670
1671
|
key: entry.id,
|
|
@@ -1699,15 +1700,15 @@ var CollectionDayView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
1699
1700
|
});
|
|
1700
1701
|
//#endregion
|
|
1701
1702
|
//#region src/vue/components/CollectionKanbanView.vue?vue&type=script&setup=true&lang.ts
|
|
1702
|
-
var _hoisted_1$
|
|
1703
|
+
var _hoisted_1$13 = {
|
|
1703
1704
|
class: "h-full overflow-x-auto overflow-y-hidden",
|
|
1704
1705
|
"data-testid": "collection-kanban"
|
|
1705
1706
|
};
|
|
1706
|
-
var _hoisted_2$
|
|
1707
|
-
var _hoisted_3$
|
|
1708
|
-
var _hoisted_4$
|
|
1709
|
-
var _hoisted_5$
|
|
1710
|
-
var _hoisted_6$
|
|
1707
|
+
var _hoisted_2$12 = { class: "flex gap-3 h-full p-1 min-w-max" };
|
|
1708
|
+
var _hoisted_3$12 = ["data-testid"];
|
|
1709
|
+
var _hoisted_4$12 = { class: "flex items-center justify-between px-3 py-2 border-b border-slate-200" };
|
|
1710
|
+
var _hoisted_5$11 = { class: "flex items-center gap-2 min-w-0" };
|
|
1711
|
+
var _hoisted_6$10 = ["title"];
|
|
1711
1712
|
var _hoisted_7$8 = { class: "text-[11px] text-slate-400 shrink-0" };
|
|
1712
1713
|
var _hoisted_8$8 = [
|
|
1713
1714
|
"data-testid",
|
|
@@ -1826,15 +1827,15 @@ var CollectionKanbanView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
1826
1827
|
emit("move", itemId(item), next);
|
|
1827
1828
|
}
|
|
1828
1829
|
return (_ctx, _cache) => {
|
|
1829
|
-
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$
|
|
1830
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$13, [(0, vue.createElementVNode)("div", _hoisted_2$12, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(columns.value, (column) => {
|
|
1830
1831
|
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
1831
1832
|
key: column.value,
|
|
1832
1833
|
"data-testid": `collection-kanban-column-${column.value || "uncategorized"}`,
|
|
1833
1834
|
class: "w-72 shrink-0 flex flex-col bg-slate-100 rounded-lg"
|
|
1834
|
-
}, [(0, vue.createElementVNode)("div", _hoisted_4$
|
|
1835
|
+
}, [(0, vue.createElementVNode)("div", _hoisted_4$12, [(0, vue.createElementVNode)("div", _hoisted_5$11, [(0, vue.createElementVNode)("span", { class: (0, vue.normalizeClass)(["w-2 h-2 rounded-full shrink-0", (0, vue.unref)(_mulmoclaude_core_collection.resolveEnumColor)(__props.schema, __props.groupField, column.value).dot]) }, null, 2), (0, vue.createElementVNode)("span", {
|
|
1835
1836
|
class: "font-semibold text-xs text-slate-600 truncate",
|
|
1836
1837
|
title: column.label
|
|
1837
|
-
}, (0, vue.toDisplayString)(column.label), 9, _hoisted_6$
|
|
1838
|
+
}, (0, vue.toDisplayString)(column.label), 9, _hoisted_6$10)]), (0, vue.createElementVNode)("span", _hoisted_7$8, (0, vue.toDisplayString)(itemsByColumn(column.value).length), 1)]), (0, vue.createVNode)((0, vue.unref)(vuedraggable.default), {
|
|
1838
1839
|
"model-value": itemsByColumn(column.value),
|
|
1839
1840
|
"item-key": __props.schema.primaryKey,
|
|
1840
1841
|
group: "collection-kanban-cards",
|
|
@@ -1865,7 +1866,7 @@ var CollectionKanbanView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
1865
1866
|
"model-value",
|
|
1866
1867
|
"item-key",
|
|
1867
1868
|
"onChange"
|
|
1868
|
-
])], 8, _hoisted_3$
|
|
1869
|
+
])], 8, _hoisted_3$12);
|
|
1869
1870
|
}), 128))])]);
|
|
1870
1871
|
};
|
|
1871
1872
|
}
|
|
@@ -1894,6 +1895,61 @@ function activatePathLink(event, path, stop = false) {
|
|
|
1894
1895
|
nav(path);
|
|
1895
1896
|
}
|
|
1896
1897
|
//#endregion
|
|
1898
|
+
//#region src/vue/components/CollectionBacklinksView.vue?vue&type=script&setup=true&lang.ts
|
|
1899
|
+
var _hoisted_1$12 = ["data-testid"];
|
|
1900
|
+
var _hoisted_2$11 = { class: "w-full text-[11px] text-slate-600 bg-white" };
|
|
1901
|
+
var _hoisted_3$11 = { class: "bg-slate-50 border-b border-slate-200 text-slate-500 font-bold uppercase tracking-wider" };
|
|
1902
|
+
var _hoisted_4$11 = { class: "divide-y divide-slate-100" };
|
|
1903
|
+
var _hoisted_5$10 = [
|
|
1904
|
+
"data-testid",
|
|
1905
|
+
"onClick",
|
|
1906
|
+
"onKeydown"
|
|
1907
|
+
];
|
|
1908
|
+
var _hoisted_6$9 = ["data-testid"];
|
|
1909
|
+
//#endregion
|
|
1910
|
+
//#region src/vue/components/CollectionBacklinksView.vue
|
|
1911
|
+
var CollectionBacklinksView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
1912
|
+
__name: "CollectionBacklinksView",
|
|
1913
|
+
props: {
|
|
1914
|
+
view: {},
|
|
1915
|
+
fieldKey: {}
|
|
1916
|
+
},
|
|
1917
|
+
setup(__props) {
|
|
1918
|
+
const { t } = useCollectionI18n();
|
|
1919
|
+
return (_ctx, _cache) => {
|
|
1920
|
+
return __props.view.rows.length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
1921
|
+
key: 0,
|
|
1922
|
+
class: "border border-slate-200/80 rounded-xl overflow-hidden shadow-sm mt-1",
|
|
1923
|
+
"data-testid": `collections-backlinks-${__props.fieldKey}`
|
|
1924
|
+
}, [(0, vue.createElementVNode)("table", _hoisted_2$11, [(0, vue.createElementVNode)("thead", _hoisted_3$11, [(0, vue.createElementVNode)("tr", null, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.view.columns, (column) => {
|
|
1925
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("th", {
|
|
1926
|
+
key: column.key,
|
|
1927
|
+
class: "text-left px-4 py-2 font-bold"
|
|
1928
|
+
}, (0, vue.toDisplayString)(column.label), 1);
|
|
1929
|
+
}), 128))])]), (0, vue.createElementVNode)("tbody", _hoisted_4$11, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.view.rows, (row) => {
|
|
1930
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("tr", {
|
|
1931
|
+
key: row.id,
|
|
1932
|
+
class: "group hover:bg-indigo-50/30 cursor-pointer transition-colors",
|
|
1933
|
+
role: "link",
|
|
1934
|
+
tabindex: "0",
|
|
1935
|
+
"data-testid": `collections-backlinks-${__props.fieldKey}-${row.id}`,
|
|
1936
|
+
onClick: ($event) => (0, vue.unref)(activateRefLink)($event, __props.view.fromSlug, row.id),
|
|
1937
|
+
onKeydown: [(0, vue.withKeys)(($event) => (0, vue.unref)(activateRefLink)($event, __props.view.fromSlug, row.id), ["enter"]), (0, vue.withKeys)(($event) => (0, vue.unref)(activateRefLink)($event, __props.view.fromSlug, row.id), ["space"])]
|
|
1938
|
+
}, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(row.cells, (cell, cellIdx) => {
|
|
1939
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("td", {
|
|
1940
|
+
key: __props.view.columns[cellIdx]?.key ?? cellIdx,
|
|
1941
|
+
class: "px-4 py-2 align-middle font-medium"
|
|
1942
|
+
}, [(0, vue.createElementVNode)("span", { class: (0, vue.normalizeClass)(cellIdx === 0 ? "text-indigo-600 group-hover:text-indigo-800 font-bold" : "") }, (0, vue.toDisplayString)(cell), 3)]);
|
|
1943
|
+
}), 128))], 40, _hoisted_5$10);
|
|
1944
|
+
}), 128))])])], 8, _hoisted_1$12)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
|
|
1945
|
+
key: 1,
|
|
1946
|
+
class: "text-slate-400 italic",
|
|
1947
|
+
"data-testid": `collections-backlinks-${__props.fieldKey}`
|
|
1948
|
+
}, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.noRows")), 9, _hoisted_6$9));
|
|
1949
|
+
};
|
|
1950
|
+
}
|
|
1951
|
+
});
|
|
1952
|
+
//#endregion
|
|
1897
1953
|
//#region src/vue/components/CollectionEmbedView.vue?vue&type=script&setup=true&lang.ts
|
|
1898
1954
|
var _hoisted_1$11 = [
|
|
1899
1955
|
"href",
|
|
@@ -2194,7 +2250,7 @@ var _hoisted_71$1 = [
|
|
|
2194
2250
|
"onClick"
|
|
2195
2251
|
];
|
|
2196
2252
|
var _hoisted_72$1 = {
|
|
2197
|
-
key:
|
|
2253
|
+
key: 14,
|
|
2198
2254
|
class: "text-slate-800 font-semibold"
|
|
2199
2255
|
};
|
|
2200
2256
|
var _hoisted_73$1 = {
|
|
@@ -2267,6 +2323,7 @@ var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
2267
2323
|
* safe in the template. */
|
|
2268
2324
|
const detailRecord = (0, vue.computed)(() => editing.value ? props.liveDerived ?? props.liveRecord ?? {} : props.viewing ?? {});
|
|
2269
2325
|
const embedViews = (0, vue.computed)(() => props.render.embedViewsFor(detailRecord.value));
|
|
2326
|
+
const backlinksViews = (0, vue.computed)(() => props.render.backlinksViewsFor(detailRecord.value));
|
|
2270
2327
|
const embedOwnerByKey = (0, vue.computed)(() => {
|
|
2271
2328
|
const map = /* @__PURE__ */ new Map();
|
|
2272
2329
|
for (const field of Object.values(props.collection.schema.fields)) if (field.type === "embed" && field.idField) map.set(field.idField, field);
|
|
@@ -2283,11 +2340,12 @@ var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
2283
2340
|
return editing.value.mode === "create" ? "collections-create" : "collections-edit";
|
|
2284
2341
|
});
|
|
2285
2342
|
/** Whether a field gets an editable control in edit mode. Toggle (a
|
|
2286
|
-
* projection of an enum that has its own input), derived (computed),
|
|
2287
|
-
* embed (a foreign record)
|
|
2288
|
-
*
|
|
2343
|
+
* projection of an enum that has its own input), derived (computed),
|
|
2344
|
+
* embed (a foreign record), and backlinks (reverse refs owned by OTHER
|
|
2345
|
+
* records) stay read-only in both modes, so the cell geometry never
|
|
2346
|
+
* changes on the view↔edit toggle. */
|
|
2289
2347
|
function isEditableType(type) {
|
|
2290
|
-
return type !== "toggle" && type !== "derived" && type !== "embed";
|
|
2348
|
+
return type !== "toggle" && type !== "derived" && type !== "embed" && type !== "backlinks";
|
|
2291
2349
|
}
|
|
2292
2350
|
/** Wide field types span the full grid width in BOTH modes — keeping
|
|
2293
2351
|
* `image` full-width here (not just when viewing) is what stops a field
|
|
@@ -2297,6 +2355,7 @@ var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
2297
2355
|
"table",
|
|
2298
2356
|
"markdown",
|
|
2299
2357
|
"embed",
|
|
2358
|
+
"backlinks",
|
|
2300
2359
|
"image"
|
|
2301
2360
|
].includes(field.type) ? "col-span-full" : "col-span-1";
|
|
2302
2361
|
}
|
|
@@ -2599,28 +2658,32 @@ var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
2599
2658
|
key: 8,
|
|
2600
2659
|
view: embedViews.value[key],
|
|
2601
2660
|
"field-key": String(key)
|
|
2602
|
-
}, null, 8, ["view", "field-key"])) : field.type === "
|
|
2661
|
+
}, null, 8, ["view", "field-key"])) : field.type === "backlinks" && backlinksViews.value[key] ? ((0, vue.openBlock)(), (0, vue.createBlock)(CollectionBacklinksView_default, {
|
|
2603
2662
|
key: 9,
|
|
2663
|
+
view: backlinksViews.value[key],
|
|
2664
|
+
"field-key": String(key)
|
|
2665
|
+
}, null, 8, ["view", "field-key"])) : field.type === "image" && typeof detailRecord.value[key] === "string" && detailRecord.value[key] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("img", {
|
|
2666
|
+
key: 10,
|
|
2604
2667
|
src: (0, vue.unref)(resolveImageSrc)(String(detailRecord.value[key])),
|
|
2605
2668
|
alt: field.label,
|
|
2606
2669
|
class: "max-h-64 max-w-full object-contain rounded-lg border border-slate-200 bg-slate-50",
|
|
2607
2670
|
"data-testid": `collections-detail-image-${key}`
|
|
2608
2671
|
}, null, 8, _hoisted_68$1)) : field.type !== "file" && __props.render.isExternalUrl(detailRecord.value[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
|
|
2609
|
-
key:
|
|
2672
|
+
key: 11,
|
|
2610
2673
|
href: String(detailRecord.value[key]),
|
|
2611
2674
|
target: "_blank",
|
|
2612
2675
|
rel: "noopener noreferrer",
|
|
2613
2676
|
class: "text-blue-600 hover:text-blue-800 font-semibold hover:underline break-all",
|
|
2614
2677
|
"data-testid": `collections-detail-url-${key}`
|
|
2615
2678
|
}, (0, vue.toDisplayString)(String(detailRecord.value[key])), 9, _hoisted_69$1)) : field.type === "file" && __props.render.artifactUrl(detailRecord.value[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
|
|
2616
|
-
key:
|
|
2679
|
+
key: 12,
|
|
2617
2680
|
href: __props.render.artifactUrl(detailRecord.value[key]) ?? void 0,
|
|
2618
2681
|
target: "_blank",
|
|
2619
2682
|
rel: "noopener noreferrer",
|
|
2620
2683
|
class: "text-blue-600 hover:text-blue-800 font-semibold hover:underline break-all",
|
|
2621
2684
|
"data-testid": `collections-detail-file-${key}`
|
|
2622
2685
|
}, (0, vue.toDisplayString)(String(detailRecord.value[key])), 9, _hoisted_70$1)) : field.type === "file" && __props.render.fileRoutePath(detailRecord.value[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
|
|
2623
|
-
key:
|
|
2686
|
+
key: 13,
|
|
2624
2687
|
href: __props.render.fileRoutePath(detailRecord.value[key]) ?? void 0,
|
|
2625
2688
|
class: "text-blue-600 hover:text-blue-800 font-semibold hover:underline break-all",
|
|
2626
2689
|
"data-testid": `collections-detail-file-${key}`,
|
|
@@ -3140,6 +3203,14 @@ function uniqueEmbedTargets(schema) {
|
|
|
3140
3203
|
for (const field of Object.values(schema.fields)) if (field.type === "embed" && typeof field.to === "string" && field.to.length > 0) targets.add(field.to);
|
|
3141
3204
|
return [...targets];
|
|
3142
3205
|
}
|
|
3206
|
+
/** Slugs of every SOURCE collection a `backlinks` field reverses over.
|
|
3207
|
+
* Top-level only, like `embed` (the schema rejects `backlinks` inside a
|
|
3208
|
+
* table's `of`). Mirrors the server's `uniqueBacklinkSources`. */
|
|
3209
|
+
function uniqueBacklinkSources(schema) {
|
|
3210
|
+
const sources = /* @__PURE__ */ new Set();
|
|
3211
|
+
for (const field of Object.values(schema.fields)) if (field.type === "backlinks" && field.from.length > 0) sources.add(field.from);
|
|
3212
|
+
return [...sources];
|
|
3213
|
+
}
|
|
3143
3214
|
function buildRefDisplayMap(detail) {
|
|
3144
3215
|
const { fields, primaryKey } = detail.collection.schema;
|
|
3145
3216
|
const displayField = displayFieldFor(fields, primaryKey);
|
|
@@ -3152,15 +3223,23 @@ function buildRefDisplayMap(detail) {
|
|
|
3152
3223
|
}
|
|
3153
3224
|
return map;
|
|
3154
3225
|
}
|
|
3155
|
-
|
|
3156
|
-
|
|
3226
|
+
/** Index DERIVED records by primary key — the client mirror of the
|
|
3227
|
+
* server's `loadTarget` indexing: string non-empty ids only, one record
|
|
3228
|
+
* per id (a duplicate keeps the LAST record in its FIRST position —
|
|
3229
|
+
* plain-object key semantics). Shared by the ref-record cache and the
|
|
3230
|
+
* backlinks view so every client consumer agrees with server enrichment
|
|
3231
|
+
* on which source records exist. */
|
|
3232
|
+
function derivedRecordsById(schema, items) {
|
|
3157
3233
|
const map = {};
|
|
3158
|
-
for (const item of
|
|
3234
|
+
for (const item of items) {
|
|
3159
3235
|
const slugRaw = item[schema.primaryKey];
|
|
3160
3236
|
if (typeof slugRaw === "string" && slugRaw.length > 0) map[slugRaw] = (0, _mulmoclaude_core_collection.deriveAll)(schema, item, {});
|
|
3161
3237
|
}
|
|
3162
3238
|
return map;
|
|
3163
3239
|
}
|
|
3240
|
+
function buildRefRecordMap(detail) {
|
|
3241
|
+
return derivedRecordsById(detail.collection.schema, detail.items);
|
|
3242
|
+
}
|
|
3164
3243
|
function sortedRefOptions(map) {
|
|
3165
3244
|
return Object.entries(map).map(([slug, display]) => ({
|
|
3166
3245
|
slug,
|
|
@@ -3185,10 +3264,14 @@ function buildEmbedOptions(schema, items) {
|
|
|
3185
3264
|
//#endregion
|
|
3186
3265
|
//#region src/vue/useLinkedCollectionCaches.ts
|
|
3187
3266
|
/** The de-duplicated ref + embed target slugs a schema links to. `allTargets`
|
|
3188
|
-
* is the union (each target fetched once even when both ref'd and embedded).
|
|
3267
|
+
* is the union (each target fetched once even when both ref'd and embedded).
|
|
3268
|
+
* `backlinks` SOURCE collections ride in `embedTargets`: a backlink needs
|
|
3269
|
+
* exactly what an embed target caches (the source's schema + items in
|
|
3270
|
+
* `embedCache`), so reverse sources reuse that cache rather than adding a
|
|
3271
|
+
* parallel one. */
|
|
3189
3272
|
function linkedTargets(schema) {
|
|
3190
3273
|
const refTargets = new Set(uniqueRefTargets(schema));
|
|
3191
|
-
const embedTargets = new Set(uniqueEmbedTargets(schema));
|
|
3274
|
+
const embedTargets = /* @__PURE__ */ new Set([...uniqueEmbedTargets(schema), ...uniqueBacklinkSources(schema)]);
|
|
3192
3275
|
return {
|
|
3193
3276
|
refTargets,
|
|
3194
3277
|
embedTargets,
|
|
@@ -3333,6 +3416,58 @@ function buildEmbedViews(schema, embedCache, record, locale) {
|
|
|
3333
3416
|
}
|
|
3334
3417
|
return out;
|
|
3335
3418
|
}
|
|
3419
|
+
/** One backlinks table CELL: money formatted with its currency, anything
|
|
3420
|
+
* else through the same `formatCell` list tables use — so a markdown
|
|
3421
|
+
* source column (a worklog `notes`) shows the one-line 80-char preview,
|
|
3422
|
+
* not the whole text. Unknown `display` key ⇒ plain text (fail-soft). */
|
|
3423
|
+
function formatBacklinkCell(sourceField, value, row, locale) {
|
|
3424
|
+
if (!sourceField) return detailText(value);
|
|
3425
|
+
if (sourceField.type === "money") return formatMoney(value, resolveCurrency(sourceField, row), locale);
|
|
3426
|
+
return formatCell(value, sourceField.type);
|
|
3427
|
+
}
|
|
3428
|
+
/** Build the read-only backlinks view-models for one record: for each
|
|
3429
|
+
* `backlinks` field, the rows of `from` whose `via` points at the open
|
|
3430
|
+
* record (matched via the SHARED `backlinkRows`, on source records
|
|
3431
|
+
* derived exactly like the server's enrichment — so a `display`/`filter`
|
|
3432
|
+
* on a derived column such as an invoice `total` agrees on both sides).
|
|
3433
|
+
* Source data comes out of `embedCache` (reverse sources ride the embed
|
|
3434
|
+
* fan-out — see `linkedTargets`). Fail-soft: an unloadable source or an
|
|
3435
|
+
* unknown `display` key degrades to `found: false` / a raw-key column,
|
|
3436
|
+
* never a throw. */
|
|
3437
|
+
function buildBacklinksViews(schema, embedCache, record, locale) {
|
|
3438
|
+
const out = {};
|
|
3439
|
+
if (!schema) return out;
|
|
3440
|
+
for (const [key, field] of Object.entries(schema.fields)) {
|
|
3441
|
+
if (field.type !== "backlinks") continue;
|
|
3442
|
+
const columns = field.display.map((col) => ({
|
|
3443
|
+
key: col,
|
|
3444
|
+
label: col
|
|
3445
|
+
}));
|
|
3446
|
+
const data = embedCache[field.from];
|
|
3447
|
+
if (!data) {
|
|
3448
|
+
out[key] = {
|
|
3449
|
+
found: false,
|
|
3450
|
+
columns,
|
|
3451
|
+
rows: [],
|
|
3452
|
+
fromSlug: field.from
|
|
3453
|
+
};
|
|
3454
|
+
continue;
|
|
3455
|
+
}
|
|
3456
|
+
for (const column of columns) column.label = data.schema.fields[column.key]?.label ?? column.key;
|
|
3457
|
+
const selfId = String(record?.[schema.primaryKey] ?? "");
|
|
3458
|
+
const sourceById = derivedRecordsById(data.schema, data.items);
|
|
3459
|
+
out[key] = {
|
|
3460
|
+
found: true,
|
|
3461
|
+
columns,
|
|
3462
|
+
rows: (0, _mulmoclaude_core_collection.backlinkRows)(field, selfId, Object.values(sourceById)).map((row) => ({
|
|
3463
|
+
id: String(row[data.schema.primaryKey] ?? ""),
|
|
3464
|
+
cells: columns.map((column) => formatBacklinkCell(data.schema.fields[column.key], row[column.key], row, locale))
|
|
3465
|
+
})),
|
|
3466
|
+
fromSlug: field.from
|
|
3467
|
+
};
|
|
3468
|
+
}
|
|
3469
|
+
return out;
|
|
3470
|
+
}
|
|
3336
3471
|
function renderSubCell(subField, value, record, refCache, locale) {
|
|
3337
3472
|
if (subField.type === "money") return formatMoney(value, resolveCurrency(subField, record), locale);
|
|
3338
3473
|
if (subField.type === "ref" && subField.to && typeof value === "string" && value.length > 0) return lookupRefDisplay(refCache, subField.to, value);
|
|
@@ -3373,6 +3508,7 @@ function useCollectionRendering(collection, locale) {
|
|
|
3373
3508
|
refOptions: (targetSlug) => refOptionsFor(refCache.value, targetSlug),
|
|
3374
3509
|
embedOptions: (targetSlug) => embedOptionsFor(embedCache.value, targetSlug),
|
|
3375
3510
|
embedViewsFor: (record) => buildEmbedViews(collection.value?.schema ?? null, embedCache.value, record, locale.value),
|
|
3511
|
+
backlinksViewsFor: (record) => buildBacklinksViews(collection.value?.schema ?? null, embedCache.value, record, locale.value),
|
|
3376
3512
|
currencySymbol: (currency) => currencySymbolForLocale(currency, locale.value),
|
|
3377
3513
|
artifactUrl: (value) => collectionUi().fileAssetUrl(value),
|
|
3378
3514
|
fileRoutePath: (value) => collectionUi().fileRoutePath(value),
|
|
@@ -4149,7 +4285,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4149
4285
|
* list table only (a whole embedded record doesn't fit a table cell,
|
|
4150
4286
|
* and it'd be identical in every row). The detail modal and the edit
|
|
4151
4287
|
* form iterate the full `schema.fields` so embeds render there too. */
|
|
4152
|
-
const listColumnFields = (0, vue.computed)(() => collection.value ? Object.entries(collection.value.schema.fields).filter(([key, field]) => field.type !== "embed" && field.type !== "image" && key !== collection.value?.schema.primaryKey) : []);
|
|
4288
|
+
const listColumnFields = (0, vue.computed)(() => collection.value ? Object.entries(collection.value.schema.fields).filter(([key, field]) => field.type !== "embed" && field.type !== "backlinks" && field.type !== "image" && key !== collection.value?.schema.primaryKey) : []);
|
|
4153
4289
|
/** True when the current collection declares `schema.singleton` —
|
|
4154
4290
|
* exactly one record, its primary key fixed to the declared value. */
|
|
4155
4291
|
const isSingleton = (0, vue.computed)(() => Boolean(collection.value?.schema.singleton));
|
|
@@ -4338,7 +4474,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4338
4474
|
boolOriginallyPresent[key] = false;
|
|
4339
4475
|
boolTouched[key] = false;
|
|
4340
4476
|
} else if (field.type === "table") table[key] = [];
|
|
4341
|
-
else if (field.type !== "derived" && field.type !== "embed" && field.type !== "toggle") text[key] = "";
|
|
4477
|
+
else if (field.type !== "derived" && field.type !== "embed" && field.type !== "backlinks" && field.type !== "toggle") text[key] = "";
|
|
4342
4478
|
const { singleton, primaryKey } = collection.value.schema;
|
|
4343
4479
|
if (singleton) text[primaryKey] = singleton;
|
|
4344
4480
|
else if (primaryKey in text) text[primaryKey] = generateUniqueItemId(primaryKey);
|
|
@@ -4370,7 +4506,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4370
4506
|
} else if (field.type === "table" && field.of) {
|
|
4371
4507
|
const sub = field.of;
|
|
4372
4508
|
table[key] = (Array.isArray(raw) ? raw : []).filter((row) => Boolean(row) && typeof row === "object" && !Array.isArray(row)).map((row) => (0, _mulmoclaude_core_collection.rowFromItem)(row, sub));
|
|
4373
|
-
} else if (field.type !== "derived" && field.type !== "embed" && field.type !== "toggle") text[key] = raw === void 0 || raw === null ? "" : String(raw);
|
|
4509
|
+
} else if (field.type !== "derived" && field.type !== "embed" && field.type !== "backlinks" && field.type !== "toggle") text[key] = raw === void 0 || raw === null ? "" : String(raw);
|
|
4374
4510
|
}
|
|
4375
4511
|
const primaryRaw = item[collection.value.schema.primaryKey];
|
|
4376
4512
|
const originalId = typeof primaryRaw === "string" ? primaryRaw : String(primaryRaw ?? "");
|