vault backup: 2026-07-17 14:47:12

This commit is contained in:
Marcel's Obsidian
2026-07-17 14:47:12 +02:00
parent 49bd853882
commit 031006c561
48 changed files with 2229 additions and 359 deletions
+42 -13
View File
@@ -532,7 +532,7 @@ function createColumn(value, entries, options, ctx, cb) {
if (cb.getQuickAddFolder()) {
headerEl.appendChild(cb.createAddButton(value, options.swimlaneValue ?? null));
}
if (entries.length === 0 && options.showRemoveButton !== false) {
if (ctx.globallyEmptyColumns.has(value)) {
headerEl.appendChild(createRemoveButton(ctx.doc, value, () => cb.onRemoveColumn(value, columnEl)));
}
const bodyEl = columnEl.createDiv({ cls: CSS_CLASSES.COLUMN_BODY });
@@ -550,10 +550,10 @@ function patchColumnCards(columnEl, newEntries, ctx, cb) {
const headerEl = columnEl.querySelector(`.${CSS_CLASSES.COLUMN_HEADER}`);
const columnValue = columnEl.getAttribute(DATA_ATTRIBUTES.COLUMN_VALUE);
const existingRemoveBtn = headerEl?.querySelector(`.${CSS_CLASSES.COLUMN_REMOVE_BTN}`) ?? null;
const isInSwimlane = !!columnEl.closest(`.${CSS_CLASSES.SWIMLANE}`);
if (headerEl && newEntries.length === 0 && !existingRemoveBtn && columnValue && !isInSwimlane) {
const showRemoveButton = !!columnValue && ctx.globallyEmptyColumns.has(columnValue);
if (headerEl && showRemoveButton && !existingRemoveBtn && columnValue) {
headerEl.appendChild(createRemoveButton(ctx.doc, columnValue, () => cb.onRemoveColumn(columnValue, columnEl)));
} else if (newEntries.length > 0 && existingRemoveBtn) {
} else if (!showRemoveButton && existingRemoveBtn) {
existingRemoveBtn.remove();
}
const existingAddBtn = headerEl?.querySelector(`.${CSS_CLASSES.COLUMN_ADD_BTN}`) ?? null;
@@ -661,7 +661,6 @@ function buildSwimlaneElement(laneValue, laneEntries, orderedColumnValues, ctx,
columnValue,
laneEntries.get(columnValue) ?? [],
{
showRemoveButton: false,
swimlaneValue: laneValue
},
ctx,
@@ -2988,6 +2987,10 @@ var KanbanView = class extends import_obsidian5.BasesView {
this._lastSwimlanePropertyId = void 0;
this._lastQuickAddFolder = void 0;
this._cardFingerprints = /* @__PURE__ */ new Map();
// Column values empty across the whole board (every swimlane). Recomputed each
// render() and read via _buildColumnCtx so components can show a remove button
// without the board-wide map being threaded through every render function.
this._globallyEmptyColumns = /* @__PURE__ */ new Set();
this._deferredSortableListeners = /* @__PURE__ */ new Map();
this._prefs = {
columnOrder: [],
@@ -3258,6 +3261,9 @@ var KanbanView = class extends import_obsidian5.BasesView {
const hasSwimlanes = groupedByLane !== null;
const existingIsSwimlane = existingBoard?.classList.contains(CSS_CLASSES.BOARD_WITH_SWIMLANES) ?? false;
const modeChanged = hasSwimlanes !== existingIsSwimlane;
this._globallyEmptyColumns = new Set(
orderedValues.filter((value) => (groupedEntries.get(value)?.length ?? 0) === 0)
);
if (!existingBoard || modeChanged || groupChanged || optionsChanged) {
this.fullRebuild(orderedValues, lanes, hasSwimlanes);
} else {
@@ -3526,6 +3532,11 @@ var KanbanView = class extends import_obsidian5.BasesView {
s.destroy();
this._columnSortables.delete(key);
}
const deferred = this._deferredSortableListeners.get(key);
if (deferred) {
deferred.el.removeEventListener("pointerdown", deferred.handler);
this._deferredSortableListeners.delete(key);
}
colEl.remove();
existingColumns.delete(colValue);
}
@@ -3533,8 +3544,7 @@ var KanbanView = class extends import_obsidian5.BasesView {
orderedColumnValues.forEach((colValue) => {
const entries = groupedEntries.get(colValue) ?? [];
if (!existingColumns.has(colValue)) {
const options = laneValue !== null ? { showRemoveButton: false, swimlaneValue: laneValue } : {};
const colEl = this.createColumn(colValue, entries, options);
const colEl = this.createColumn(colValue, entries, { swimlaneValue: laneValue });
containerEl.appendChild(colEl);
existingColumns.set(colValue, colEl);
const cardBody = colEl.querySelector(
@@ -3655,7 +3665,8 @@ var KanbanView = class extends import_obsidian5.BasesView {
cardCb: this._buildCardCallbacks(),
prefs: { columnColors: this._prefs.columnColors },
dragging: this._dragging,
cardFingerprints: this._cardFingerprints
cardFingerprints: this._cardFingerprints,
globallyEmptyColumns: this._globallyEmptyColumns
};
}
_buildColumnCallbacks() {
@@ -3781,12 +3792,30 @@ var KanbanView = class extends import_obsidian5.BasesView {
closeNativeNewItemPopover(this.containerEl.doc);
}
detachColumn(value, colEl) {
const sortable = this._columnSortables.get(value);
if (sortable) {
sortable.destroy();
this._columnSortables.delete(value);
const boardEl = this.containerEl.querySelector(`.${CSS_CLASSES.BOARD}`);
const columnEls = [];
if (boardEl) {
boardEl.querySelectorAll(`.${CSS_CLASSES.COLUMN}`).forEach((el) => {
if (el.getAttribute(DATA_ATTRIBUTES.COLUMN_VALUE) === value) columnEls.push(el);
});
}
colEl.remove();
const suffix = `${SWIMLANE_KEY_SEPARATOR}${value}`;
const matchesColumn = (key) => key === value || key.endsWith(suffix);
for (const key of [...this._columnSortables.keys()]) {
if (matchesColumn(key)) {
this._columnSortables.get(key)?.destroy();
this._columnSortables.delete(key);
}
}
for (const key of [...this._deferredSortableListeners.keys()]) {
if (matchesColumn(key)) {
const deferred = this._deferredSortableListeners.get(key);
deferred?.el.removeEventListener("pointerdown", deferred.handler);
this._deferredSortableListeners.delete(key);
}
}
if (columnEls.length > 0) columnEls.forEach((el) => el.remove());
else colEl.remove();
}
removeColumn(value, columnEl) {
if (!this._prefsPropertyId) return;