@mulmoclaude/collection-plugin 0.7.5 → 0.7.6
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/vue/components/CollectionKanbanView.vue.d.ts.map +1 -1
- package/dist/vue/components/CollectionRecordPanel.vue.d.ts +24 -2
- 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 +1 -1
- package/dist/vue/useCollectionRendering.d.ts.map +1 -1
- package/dist/vue/useCollectionRendering.helpers.d.ts +3 -1
- package/dist/vue/useCollectionRendering.helpers.d.ts.map +1 -1
- package/dist/vue/useCollectionRendering.renderers.d.ts +22 -0
- package/dist/vue/useCollectionRendering.renderers.d.ts.map +1 -0
- package/dist/vue/useLinkedCollectionCaches.d.ts +33 -0
- package/dist/vue/useLinkedCollectionCaches.d.ts.map +1 -0
- package/dist/vue.cjs +212 -167
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.js +212 -167
- package/dist/vue.js.map +1 -1
- package/package.json +5 -5
package/dist/vue.js
CHANGED
|
@@ -1717,7 +1717,10 @@ var CollectionKanbanView_default = /* @__PURE__ */ defineComponent({
|
|
|
1717
1717
|
const emit = __emit;
|
|
1718
1718
|
const { t } = useCollectionI18n();
|
|
1719
1719
|
/** The Uncategorized column uses the empty string as its sentinel value. */
|
|
1720
|
-
const groupSpec = computed(() =>
|
|
1720
|
+
const groupSpec = computed(() => {
|
|
1721
|
+
const spec = props.schema.fields[props.groupField];
|
|
1722
|
+
return spec?.type === "enum" ? spec : void 0;
|
|
1723
|
+
});
|
|
1721
1724
|
/** Declared enum values become columns in order, with a trailing
|
|
1722
1725
|
* Uncategorized column for empty/unknown values (also a drop target that
|
|
1723
1726
|
* clears the field). The Uncategorized column is omitted when the group
|
|
@@ -1764,7 +1767,10 @@ var CollectionKanbanView_default = /* @__PURE__ */ defineComponent({
|
|
|
1764
1767
|
const value = String(raw);
|
|
1765
1768
|
return (groupSpec.value?.values ?? []).includes(value) ? value : UNCATEGORIZED;
|
|
1766
1769
|
}
|
|
1767
|
-
const visibleItems = computed(() =>
|
|
1770
|
+
const visibleItems = computed(() => {
|
|
1771
|
+
const spec = groupSpec.value;
|
|
1772
|
+
return spec ? props.items.filter((item) => fieldVisible(spec, item)) : [];
|
|
1773
|
+
});
|
|
1768
1774
|
const itemsByColumnMap = computed(() => {
|
|
1769
1775
|
const map = /* @__PURE__ */ new Map();
|
|
1770
1776
|
for (const column of columns.value) map.set(column.value, []);
|
|
@@ -2291,7 +2297,8 @@ var CollectionRecordPanel_default = /* @__PURE__ */ defineComponent({
|
|
|
2291
2297
|
/** Required flag for an embed's per-record picker — read off the storage
|
|
2292
2298
|
* field it writes (`idField`), since the embed itself stores nothing. */
|
|
2293
2299
|
function embedPickerRequired(field) {
|
|
2294
|
-
const
|
|
2300
|
+
const idField = field.type === "embed" ? field.idField : void 0;
|
|
2301
|
+
const target = idField ? props.collection.schema.fields[idField] : void 0;
|
|
2295
2302
|
return target ? isFieldRequiredInUi(target) : false;
|
|
2296
2303
|
}
|
|
2297
2304
|
/** Tailwind fill/text/border classes tinting an enum `<select>` by its current
|
|
@@ -3043,8 +3050,11 @@ function formatCell(value, type) {
|
|
|
3043
3050
|
}
|
|
3044
3051
|
/** Resolve the ISO 4217 code for a money field: a per-record
|
|
3045
3052
|
* `currencyField` (when present and non-blank) wins over the field's
|
|
3046
|
-
* literal `currency`.
|
|
3053
|
+
* literal `currency`. Only `money` / `derived` variants carry currency
|
|
3054
|
+
* keys; any other field resolves to undefined (the formatter's USD
|
|
3055
|
+
* fallback), as before. */
|
|
3047
3056
|
function resolveCurrency(field, record) {
|
|
3057
|
+
if (field.type !== "money" && field.type !== "derived") return void 0;
|
|
3048
3058
|
if (field.currencyField && record) {
|
|
3049
3059
|
const code = record[field.currencyField];
|
|
3050
3060
|
if (typeof code === "string" && code.trim().length > 0) return code;
|
|
@@ -3149,8 +3159,60 @@ function buildEmbedOptions(schema, items) {
|
|
|
3149
3159
|
}).filter((opt) => opt.slug.length > 0).sort((left, right) => left.display.localeCompare(right.display));
|
|
3150
3160
|
}
|
|
3151
3161
|
//#endregion
|
|
3152
|
-
//#region src/vue/
|
|
3153
|
-
|
|
3162
|
+
//#region src/vue/useLinkedCollectionCaches.ts
|
|
3163
|
+
/** The de-duplicated ref + embed target slugs a schema links to. `allTargets`
|
|
3164
|
+
* is the union (each target fetched once even when both ref'd and embedded). */
|
|
3165
|
+
function linkedTargets(schema) {
|
|
3166
|
+
const refTargets = new Set(uniqueRefTargets(schema));
|
|
3167
|
+
const embedTargets = new Set(uniqueEmbedTargets(schema));
|
|
3168
|
+
return {
|
|
3169
|
+
refTargets,
|
|
3170
|
+
embedTargets,
|
|
3171
|
+
allTargets: [.../* @__PURE__ */ new Set([...refTargets, ...embedTargets])]
|
|
3172
|
+
};
|
|
3173
|
+
}
|
|
3174
|
+
/** Fan-out fetch that hydrates the linked-collection caches. Best-effort: a
|
|
3175
|
+
* target whose fetch *rejects* (vs. resolving `{ ok: false }`) is coerced to a
|
|
3176
|
+
* skip and must not abort the others. Returns null when a quicker subsequent
|
|
3177
|
+
* load has already moved on (stale-write guard) so the caller drops the write.
|
|
3178
|
+
* Pure + injectable (`fetchDetail`, `currentSlug`) so both paths are testable. */
|
|
3179
|
+
async function fetchLinkedCaches(targets, fetchDetail, currentSlug, expectedSlug) {
|
|
3180
|
+
const { refTargets, embedTargets, allTargets } = targets;
|
|
3181
|
+
const results = await Promise.all(allTargets.map(async (target) => {
|
|
3182
|
+
try {
|
|
3183
|
+
return {
|
|
3184
|
+
target,
|
|
3185
|
+
result: await fetchDetail(target)
|
|
3186
|
+
};
|
|
3187
|
+
} catch {
|
|
3188
|
+
return {
|
|
3189
|
+
target,
|
|
3190
|
+
result: { ok: false }
|
|
3191
|
+
};
|
|
3192
|
+
}
|
|
3193
|
+
}));
|
|
3194
|
+
if (currentSlug() !== expectedSlug) return null;
|
|
3195
|
+
const refCache = {};
|
|
3196
|
+
const refRecordCache = {};
|
|
3197
|
+
const embedCache = {};
|
|
3198
|
+
for (const { target, result } of results) {
|
|
3199
|
+
if (!result.ok) continue;
|
|
3200
|
+
if (refTargets.has(target)) {
|
|
3201
|
+
refCache[target] = buildRefDisplayMap(result.data);
|
|
3202
|
+
refRecordCache[target] = buildRefRecordMap(result.data);
|
|
3203
|
+
}
|
|
3204
|
+
if (embedTargets.has(target)) embedCache[target] = {
|
|
3205
|
+
schema: result.data.collection.schema,
|
|
3206
|
+
items: result.data.items
|
|
3207
|
+
};
|
|
3208
|
+
}
|
|
3209
|
+
return {
|
|
3210
|
+
refCache,
|
|
3211
|
+
refRecordCache,
|
|
3212
|
+
embedCache
|
|
3213
|
+
};
|
|
3214
|
+
}
|
|
3215
|
+
function useLinkedCollectionCaches(collection) {
|
|
3154
3216
|
const refCache = ref({});
|
|
3155
3217
|
const refRecordCache = ref({});
|
|
3156
3218
|
const embedCache = ref({});
|
|
@@ -3160,161 +3222,139 @@ function useCollectionRendering(collection, locale) {
|
|
|
3160
3222
|
embedCache.value = {};
|
|
3161
3223
|
}
|
|
3162
3224
|
async function loadLinkedCollections(schema, expectedSlug) {
|
|
3163
|
-
const
|
|
3164
|
-
|
|
3165
|
-
const allTargets = [.../* @__PURE__ */ new Set([...refTargets, ...embedTargets])];
|
|
3166
|
-
if (allTargets.length === 0) return;
|
|
3225
|
+
const targets = linkedTargets(schema);
|
|
3226
|
+
if (targets.allTargets.length === 0) return;
|
|
3167
3227
|
const binding = collectionUi();
|
|
3168
|
-
const
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
};
|
|
3174
|
-
} catch {
|
|
3175
|
-
return {
|
|
3176
|
-
target,
|
|
3177
|
-
result: { ok: false }
|
|
3178
|
-
};
|
|
3179
|
-
}
|
|
3180
|
-
}));
|
|
3181
|
-
if (collection.value?.slug !== expectedSlug) return;
|
|
3182
|
-
const nextRef = {};
|
|
3183
|
-
const nextRefRecords = {};
|
|
3184
|
-
const nextEmbed = {};
|
|
3185
|
-
for (const { target, result } of results) {
|
|
3186
|
-
if (!result.ok) continue;
|
|
3187
|
-
if (refTargets.has(target)) {
|
|
3188
|
-
nextRef[target] = buildRefDisplayMap(result.data);
|
|
3189
|
-
nextRefRecords[target] = buildRefRecordMap(result.data);
|
|
3190
|
-
}
|
|
3191
|
-
if (embedTargets.has(target)) nextEmbed[target] = {
|
|
3192
|
-
schema: result.data.collection.schema,
|
|
3193
|
-
items: result.data.items
|
|
3194
|
-
};
|
|
3195
|
-
}
|
|
3196
|
-
refCache.value = nextRef;
|
|
3197
|
-
refRecordCache.value = nextRefRecords;
|
|
3198
|
-
embedCache.value = nextEmbed;
|
|
3199
|
-
}
|
|
3200
|
-
function refDisplay(targetSlug, itemSlug) {
|
|
3201
|
-
const map = refCache.value[targetSlug];
|
|
3202
|
-
return map && map[itemSlug] || itemSlug;
|
|
3203
|
-
}
|
|
3204
|
-
function refOptions(targetSlug) {
|
|
3205
|
-
const map = refCache.value[targetSlug];
|
|
3206
|
-
return map ? sortedRefOptions(map) : [];
|
|
3207
|
-
}
|
|
3208
|
-
/** Dropdown options for an `embed` field's per-record picker (`idField`):
|
|
3209
|
-
* every record in the target collection, labelled by its name/title (or
|
|
3210
|
-
* primary key). Built from `embedCache` so it works for embed targets
|
|
3211
|
-
* that aren't also `ref` targets (the profile collection, say). */
|
|
3212
|
-
function embedOptions(targetSlug) {
|
|
3213
|
-
const data = embedCache.value[targetSlug];
|
|
3214
|
-
return data ? buildEmbedOptions(data.schema, data.items) : [];
|
|
3215
|
-
}
|
|
3216
|
-
function resolveEmbed(field, record) {
|
|
3217
|
-
if (field.type !== "embed" || !field.to) return {
|
|
3218
|
-
schema: null,
|
|
3219
|
-
item: null
|
|
3220
|
-
};
|
|
3221
|
-
const targetId = embedTargetId(field, record);
|
|
3222
|
-
const data = targetId ? embedCache.value[field.to] : void 0;
|
|
3223
|
-
if (!data) return {
|
|
3224
|
-
schema: null,
|
|
3225
|
-
item: null
|
|
3226
|
-
};
|
|
3227
|
-
const item = data.items.find((entry) => String(entry[data.schema.primaryKey] ?? "") === targetId) ?? null;
|
|
3228
|
-
return {
|
|
3229
|
-
schema: data.schema,
|
|
3230
|
-
item
|
|
3231
|
-
};
|
|
3232
|
-
}
|
|
3233
|
-
function embedValue(field, value, record) {
|
|
3234
|
-
if (field.type === "money") return formatMoney(value, resolveCurrency(field, record), locale.value);
|
|
3235
|
-
return detailText(value);
|
|
3236
|
-
}
|
|
3237
|
-
/** Build the read-only embed view-models for one record. A function of
|
|
3238
|
-
* the open record (not a bare computed) because a per-record `idField`
|
|
3239
|
-
* embed resolves a different target per row. */
|
|
3240
|
-
function embedViewsFor(record) {
|
|
3241
|
-
const out = {};
|
|
3242
|
-
if (!collection.value) return out;
|
|
3243
|
-
for (const [key, field] of Object.entries(collection.value.schema.fields)) {
|
|
3244
|
-
if (field.type !== "embed") continue;
|
|
3245
|
-
const { schema, item } = resolveEmbed(field, record);
|
|
3246
|
-
const rows = [];
|
|
3247
|
-
if (schema && item) for (const [subKey, subField] of Object.entries(schema.fields)) {
|
|
3248
|
-
const value = item[subKey];
|
|
3249
|
-
if (value === void 0 || value === null || value === "") continue;
|
|
3250
|
-
rows.push({
|
|
3251
|
-
key: subKey,
|
|
3252
|
-
label: subField.label,
|
|
3253
|
-
type: subField.type,
|
|
3254
|
-
value,
|
|
3255
|
-
display: embedValue(subField, value, item)
|
|
3256
|
-
});
|
|
3257
|
-
}
|
|
3258
|
-
out[key] = {
|
|
3259
|
-
found: Boolean(item),
|
|
3260
|
-
rows,
|
|
3261
|
-
targetSlug: field.to ?? "",
|
|
3262
|
-
recordId: embedTargetId(field, record)
|
|
3263
|
-
};
|
|
3264
|
-
}
|
|
3265
|
-
return out;
|
|
3266
|
-
}
|
|
3267
|
-
function currencySymbol(currency) {
|
|
3268
|
-
return currencySymbolForLocale(currency, locale.value);
|
|
3269
|
-
}
|
|
3270
|
-
function artifactUrl(value) {
|
|
3271
|
-
return collectionUi().fileAssetUrl(value);
|
|
3272
|
-
}
|
|
3273
|
-
function fileRoutePath(value) {
|
|
3274
|
-
return collectionUi().fileRoutePath(value);
|
|
3275
|
-
}
|
|
3276
|
-
function formatSubCell(subField, value, record) {
|
|
3277
|
-
if (subField.type === "money") return formatMoney(value, resolveCurrency(subField, record), locale.value);
|
|
3278
|
-
if (subField.type === "ref" && subField.to && typeof value === "string" && value.length > 0) return refDisplay(subField.to, value);
|
|
3279
|
-
return formatCell(value, subField.type);
|
|
3280
|
-
}
|
|
3281
|
-
const stepFor = stepForFieldType;
|
|
3282
|
-
function evaluateDerivedAgainstItem(field, fieldKey, item) {
|
|
3283
|
-
if (!field.formula || !collection.value) return null;
|
|
3284
|
-
const result = deriveAll(collection.value.schema, item, refRecordCache.value)[fieldKey];
|
|
3285
|
-
return typeof result === "number" && Number.isFinite(result) ? result : null;
|
|
3286
|
-
}
|
|
3287
|
-
function derivedDisplay(field, computedValue, record) {
|
|
3288
|
-
if (computedValue === null || computedValue === void 0) return "—";
|
|
3289
|
-
if (field.display === "money") return formatMoney(computedValue, resolveCurrency(field, record), locale.value);
|
|
3290
|
-
return formatCell(computedValue, field.display ?? "number");
|
|
3228
|
+
const snapshot = await fetchLinkedCaches(targets, (slug) => binding.fetchCollectionDetail(slug), () => collection.value?.slug, expectedSlug);
|
|
3229
|
+
if (!snapshot) return;
|
|
3230
|
+
refCache.value = snapshot.refCache;
|
|
3231
|
+
refRecordCache.value = snapshot.refRecordCache;
|
|
3232
|
+
embedCache.value = snapshot.embedCache;
|
|
3291
3233
|
}
|
|
3292
3234
|
return {
|
|
3293
3235
|
refCache,
|
|
3294
3236
|
refRecordCache,
|
|
3295
3237
|
embedCache,
|
|
3296
3238
|
resetLinkedCaches,
|
|
3297
|
-
loadLinkedCollections
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3239
|
+
loadLinkedCollections
|
|
3240
|
+
};
|
|
3241
|
+
}
|
|
3242
|
+
//#endregion
|
|
3243
|
+
//#region src/vue/useCollectionRendering.renderers.ts
|
|
3244
|
+
function lookupRefDisplay(refCache, targetSlug, itemSlug) {
|
|
3245
|
+
const map = refCache[targetSlug];
|
|
3246
|
+
return map && map[itemSlug] || itemSlug;
|
|
3247
|
+
}
|
|
3248
|
+
function refOptionsFor(refCache, targetSlug) {
|
|
3249
|
+
const map = refCache[targetSlug];
|
|
3250
|
+
return map ? sortedRefOptions(map) : [];
|
|
3251
|
+
}
|
|
3252
|
+
/** Dropdown options for an `embed` field's per-record picker (`idField`):
|
|
3253
|
+
* every record in the target collection, labelled by its name/title (or
|
|
3254
|
+
* primary key). Built from `embedCache` so it works for embed targets
|
|
3255
|
+
* that aren't also `ref` targets (the profile collection, say). */
|
|
3256
|
+
function embedOptionsFor(embedCache, targetSlug) {
|
|
3257
|
+
const data = embedCache[targetSlug];
|
|
3258
|
+
return data ? buildEmbedOptions(data.schema, data.items) : [];
|
|
3259
|
+
}
|
|
3260
|
+
function resolveEmbed(field, record, embedCache) {
|
|
3261
|
+
if (field.type !== "embed" || !field.to) return {
|
|
3262
|
+
schema: null,
|
|
3263
|
+
item: null
|
|
3264
|
+
};
|
|
3265
|
+
const targetId = embedTargetId(field, record);
|
|
3266
|
+
const data = targetId ? embedCache[field.to] : void 0;
|
|
3267
|
+
if (!data) return {
|
|
3268
|
+
schema: null,
|
|
3269
|
+
item: null
|
|
3270
|
+
};
|
|
3271
|
+
const item = data.items.find((entry) => String(entry[data.schema.primaryKey] ?? "") === targetId) ?? null;
|
|
3272
|
+
return {
|
|
3273
|
+
schema: data.schema,
|
|
3274
|
+
item
|
|
3275
|
+
};
|
|
3276
|
+
}
|
|
3277
|
+
function formatEmbedValue(field, value, record, locale) {
|
|
3278
|
+
if (field.type === "money") return formatMoney(value, resolveCurrency(field, record), locale);
|
|
3279
|
+
return detailText(value);
|
|
3280
|
+
}
|
|
3281
|
+
/** Build the read-only embed view-models for one record. A function of the
|
|
3282
|
+
* open record (not a bare computed) because a per-record `idField` embed
|
|
3283
|
+
* resolves a different target per row. `schema` is the OPEN collection's
|
|
3284
|
+
* schema (null when no collection is loaded → no views). */
|
|
3285
|
+
function buildEmbedViews(schema, embedCache, record, locale) {
|
|
3286
|
+
const out = {};
|
|
3287
|
+
if (!schema) return out;
|
|
3288
|
+
for (const [key, field] of Object.entries(schema.fields)) {
|
|
3289
|
+
if (field.type !== "embed") continue;
|
|
3290
|
+
const { schema: targetSchema, item } = resolveEmbed(field, record, embedCache);
|
|
3291
|
+
const rows = [];
|
|
3292
|
+
if (targetSchema && item) for (const [subKey, subField] of Object.entries(targetSchema.fields)) {
|
|
3293
|
+
const value = item[subKey];
|
|
3294
|
+
if (value === void 0 || value === null || value === "") continue;
|
|
3295
|
+
rows.push({
|
|
3296
|
+
key: subKey,
|
|
3297
|
+
label: subField.label,
|
|
3298
|
+
type: subField.type,
|
|
3299
|
+
value,
|
|
3300
|
+
display: formatEmbedValue(subField, value, item, locale)
|
|
3301
|
+
});
|
|
3302
|
+
}
|
|
3303
|
+
out[key] = {
|
|
3304
|
+
found: Boolean(item),
|
|
3305
|
+
rows,
|
|
3306
|
+
targetSlug: field.to ?? "",
|
|
3307
|
+
recordId: embedTargetId(field, record)
|
|
3308
|
+
};
|
|
3309
|
+
}
|
|
3310
|
+
return out;
|
|
3311
|
+
}
|
|
3312
|
+
function renderSubCell(subField, value, record, refCache, locale) {
|
|
3313
|
+
if (subField.type === "money") return formatMoney(value, resolveCurrency(subField, record), locale);
|
|
3314
|
+
if (subField.type === "ref" && subField.to && typeof value === "string" && value.length > 0) return lookupRefDisplay(refCache, subField.to, value);
|
|
3315
|
+
return formatCell(value, subField.type);
|
|
3316
|
+
}
|
|
3317
|
+
function evaluateDerived(field, fieldKey, item, schema, refRecords) {
|
|
3318
|
+
if (field.type !== "derived" || !schema) return null;
|
|
3319
|
+
const result = deriveAll(schema, item, refRecords)[fieldKey];
|
|
3320
|
+
return typeof result === "number" && Number.isFinite(result) ? result : null;
|
|
3321
|
+
}
|
|
3322
|
+
function renderDerived(field, computedValue, record, locale) {
|
|
3323
|
+
if (computedValue === null || computedValue === void 0) return "—";
|
|
3324
|
+
const display = field.type === "derived" ? field.display : void 0;
|
|
3325
|
+
if (display === "money") return formatMoney(computedValue, resolveCurrency(field, record), locale);
|
|
3326
|
+
return formatCell(computedValue, display ?? "number");
|
|
3327
|
+
}
|
|
3328
|
+
//#endregion
|
|
3329
|
+
//#region src/vue/useCollectionRendering.ts
|
|
3330
|
+
var STATELESS_RENDERERS = {
|
|
3331
|
+
resolveCurrency,
|
|
3332
|
+
formatMoney,
|
|
3333
|
+
formatCell,
|
|
3334
|
+
detailText,
|
|
3335
|
+
isExternalUrl,
|
|
3336
|
+
tableRows,
|
|
3337
|
+
hasTableRows,
|
|
3338
|
+
inputTypeFor,
|
|
3339
|
+
stepFor: stepForFieldType,
|
|
3340
|
+
deriveAll
|
|
3341
|
+
};
|
|
3342
|
+
function useCollectionRendering(collection, locale) {
|
|
3343
|
+
const caches = useLinkedCollectionCaches(collection);
|
|
3344
|
+
const { refCache, refRecordCache, embedCache } = caches;
|
|
3345
|
+
return {
|
|
3346
|
+
...caches,
|
|
3347
|
+
...STATELESS_RENDERERS,
|
|
3348
|
+
refDisplay: (targetSlug, itemSlug) => lookupRefDisplay(refCache.value, targetSlug, itemSlug),
|
|
3349
|
+
refOptions: (targetSlug) => refOptionsFor(refCache.value, targetSlug),
|
|
3350
|
+
embedOptions: (targetSlug) => embedOptionsFor(embedCache.value, targetSlug),
|
|
3351
|
+
embedViewsFor: (record) => buildEmbedViews(collection.value?.schema ?? null, embedCache.value, record, locale.value),
|
|
3352
|
+
currencySymbol: (currency) => currencySymbolForLocale(currency, locale.value),
|
|
3353
|
+
artifactUrl: (value) => collectionUi().fileAssetUrl(value),
|
|
3354
|
+
fileRoutePath: (value) => collectionUi().fileRoutePath(value),
|
|
3355
|
+
formatSubCell: (subField, value, record) => renderSubCell(subField, value, record, refCache.value, locale.value),
|
|
3356
|
+
evaluateDerivedAgainstItem: (field, fieldKey, item) => evaluateDerived(field, fieldKey, item, collection.value?.schema ?? null, refRecordCache.value),
|
|
3357
|
+
derivedDisplay: (field, computedValue, record) => renderDerived(field, computedValue, record, locale.value)
|
|
3318
3358
|
};
|
|
3319
3359
|
}
|
|
3320
3360
|
//#endregion
|
|
@@ -3805,12 +3845,12 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
3805
3845
|
return scalarSortValue(field, item[key]);
|
|
3806
3846
|
}
|
|
3807
3847
|
/** Derived rows sort by their display type: money/number → numeric,
|
|
3808
|
-
* date
|
|
3848
|
+
* date → epoch, anything else → the enriched value as a string. */
|
|
3809
3849
|
function derivedSortValue(field, key, item) {
|
|
3810
|
-
const
|
|
3850
|
+
const display = field.type === "derived" ? field.display : void 0;
|
|
3811
3851
|
if (display === void 0 || display === "number" || display === "money") return numericSortValue(evaluateDerivedAgainstItem(field, key, item));
|
|
3812
3852
|
const enriched = collection.value ? render.deriveAll(collection.value.schema, item, render.refRecordCache.value) : item;
|
|
3813
|
-
if (display === "date"
|
|
3853
|
+
if (display === "date") return dateSortValue(enriched[key]);
|
|
3814
3854
|
return stringSortValue(enriched[key]);
|
|
3815
3855
|
}
|
|
3816
3856
|
const sortedItems = computed(() => {
|
|
@@ -3988,10 +4028,12 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
3988
4028
|
function buildChatSeed(slug, message, itemId) {
|
|
3989
4029
|
const current = collection.value;
|
|
3990
4030
|
if (current?.source !== "feed") return itemId ? `/${slug} id=${itemId} ${message}` : `/${slug} ${message}`;
|
|
4031
|
+
const dataPath = current.schema.dataPath ?? `data/feeds/${slug}`;
|
|
4032
|
+
const scoped = itemId ? `(for record \`${itemId}\`) ${message}` : message;
|
|
3991
4033
|
return t("collectionsView.feedChatSeed", {
|
|
3992
4034
|
slug,
|
|
3993
|
-
dataPath
|
|
3994
|
-
message:
|
|
4035
|
+
dataPath,
|
|
4036
|
+
message: scoped
|
|
3995
4037
|
});
|
|
3996
4038
|
}
|
|
3997
4039
|
/** Start a new general-role chat seeded from the current view. */
|
|
@@ -4066,7 +4108,10 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4066
4108
|
enumOriginallyEmpty.value = snapshotEmptyEnums(result.data.collection.schema, result.data.items);
|
|
4067
4109
|
await render.loadLinkedCollections(result.data.collection.schema, slug);
|
|
4068
4110
|
if (activeSlug.value !== slug) return;
|
|
4069
|
-
if (viewing.value)
|
|
4111
|
+
if (viewing.value) {
|
|
4112
|
+
const openId = String(viewing.value[result.data.collection.schema.primaryKey] ?? "");
|
|
4113
|
+
viewing.value = findItemById(openId) ?? null;
|
|
4114
|
+
}
|
|
4070
4115
|
}
|
|
4071
4116
|
function maybeAutoRefreshFeed(slug) {
|
|
4072
4117
|
if (embedded.value) return;
|
|
@@ -4244,7 +4289,8 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4244
4289
|
}
|
|
4245
4290
|
/** Select a custom view by id (builds the `custom:<id>` mode key). */
|
|
4246
4291
|
function setCustomView(viewId) {
|
|
4247
|
-
|
|
4292
|
+
const mode = `custom:${viewId}`;
|
|
4293
|
+
view.value = mode;
|
|
4248
4294
|
}
|
|
4249
4295
|
/** A short, slug-safe id not already used by a loaded record. Collisions
|
|
4250
4296
|
* are astronomically unlikely (32 bits), but we still re-roll a few
|
|
@@ -4402,7 +4448,8 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4402
4448
|
viewing.value = null;
|
|
4403
4449
|
return;
|
|
4404
4450
|
}
|
|
4405
|
-
|
|
4451
|
+
const match = findItemById(selected) ?? null;
|
|
4452
|
+
viewing.value = match;
|
|
4406
4453
|
}
|
|
4407
4454
|
/** Title for the open-mode header: the record's primary-key value
|
|
4408
4455
|
* (e.g. `INV-2026-0001`), falling back to the collection title.
|
|
@@ -4496,19 +4543,17 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4496
4543
|
/** Whether a `toggle` field reads as checked: its projected enum field
|
|
4497
4544
|
* currently equals `onValue`. The toggle stores nothing itself. */
|
|
4498
4545
|
function toggleChecked(item, field) {
|
|
4499
|
-
return field.
|
|
4546
|
+
return field.type === "toggle" && String(item[field.field] ?? "") === field.onValue;
|
|
4500
4547
|
}
|
|
4501
4548
|
/** Flip a `toggle`: write the projected enum field to `offValue` when
|
|
4502
4549
|
* currently checked, else `onValue`. Reuses the inline-edit PUT path
|
|
4503
4550
|
* (optimistic + rollback) — the toggle has no value of its own. */
|
|
4504
4551
|
function commitToggle(item, field) {
|
|
4552
|
+
if (field.type !== "toggle" || !collection.value) return;
|
|
4505
4553
|
const targetKey = field.field;
|
|
4506
|
-
if (!targetKey || !collection.value) return;
|
|
4507
4554
|
const enumField = collection.value.schema.fields[targetKey];
|
|
4508
4555
|
if (!enumField) return;
|
|
4509
|
-
|
|
4510
|
-
if (next === void 0) return;
|
|
4511
|
-
commitInlineEdit(item, targetKey, enumField, next);
|
|
4556
|
+
commitInlineEdit(item, targetKey, enumField, toggleChecked(item, field) ? field.offValue : field.onValue);
|
|
4512
4557
|
}
|
|
4513
4558
|
async function confirmDelete(item) {
|
|
4514
4559
|
if (!collection.value) return;
|