mindnet_obsidian/src/interview/loopState.ts
Lars 684217bdf4
Some checks are pending
Node.js build / build (20.x) (push) Waiting to run
Node.js build / build (22.x) (push) Waiting to run
Pane 2 mit Edit, Delete, sort, etc.
2026-01-16 19:25:55 +01:00

234 lines
5.3 KiB
TypeScript

/**
* Loop runtime state management.
* Pure functions for managing loop item state (draft, committed items, editing).
*/
export interface LoopRuntimeState {
items: unknown[]; // Committed items
draft: Record<string, unknown>; // Current draft being edited
editIndex: number | null; // Index of item being edited, or null for new item
}
/**
* Create initial loop state.
*/
export function createLoopState(): LoopRuntimeState {
return {
items: [],
draft: {},
editIndex: null,
};
}
/**
* Set a field value in the draft.
*/
export function setDraftField(
state: LoopRuntimeState,
fieldId: string,
value: unknown
): LoopRuntimeState {
return {
...state,
draft: {
...state.draft,
[fieldId]: value,
},
};
}
/**
* Check if draft has any non-empty data (is dirty).
* Returns true if draft contains any non-empty string, true, non-zero number, non-empty array, or non-empty object.
*/
export function isDraftDirty(draft: Record<string, unknown>): boolean {
for (const [key, value] of Object.entries(draft)) {
if (value === null || value === undefined) {
continue;
}
if (typeof value === "string" && value.trim() !== "") {
return true;
}
if (typeof value === "boolean" && value === true) {
return true;
}
if (typeof value === "number" && value !== 0) {
return true;
}
if (Array.isArray(value) && value.length > 0) {
return true;
}
if (typeof value === "object" && Object.keys(value).length > 0) {
return true;
}
}
return false;
}
/**
* Commit draft to items (add new or update existing).
*/
export function commitDraft(state: LoopRuntimeState): LoopRuntimeState {
// If draft is not dirty, return state unchanged
if (!isDraftDirty(state.draft)) {
return state;
}
const newItems = [...state.items];
if (state.editIndex !== null && state.editIndex >= 0 && state.editIndex < newItems.length) {
// Update existing item
newItems[state.editIndex] = { ...state.draft };
} else {
// Add new item
newItems.push({ ...state.draft });
}
return {
items: newItems,
draft: {},
editIndex: null,
};
}
/**
* Start editing an existing item (load into draft).
*/
export function startEdit(
state: LoopRuntimeState,
index: number
): LoopRuntimeState {
if (index < 0 || index >= state.items.length) {
return state; // Invalid index, return unchanged
}
const item = state.items[index];
if (!item || typeof item !== "object") {
return state;
}
return {
...state,
draft: { ...(item as Record<string, unknown>) },
editIndex: index,
};
}
/**
* Delete an item at the given index.
*/
export function deleteItem(
state: LoopRuntimeState,
index: number
): LoopRuntimeState {
if (index < 0 || index >= state.items.length) {
return state; // Invalid index, return unchanged
}
const newItems = [...state.items];
newItems.splice(index, 1);
// If we were editing the deleted item or an item after it, adjust editIndex
let newEditIndex = state.editIndex;
if (state.editIndex !== null) {
if (state.editIndex === index) {
// Deleted the item we were editing
newEditIndex = null;
} else if (state.editIndex > index) {
// Deleted an item before the one we're editing, adjust index
newEditIndex = state.editIndex - 1;
}
}
return {
...state,
items: newItems,
editIndex: newEditIndex,
// Clear draft if we were editing the deleted item
draft: newEditIndex === null ? {} : state.draft,
};
}
/**
* Move an item up (swap with previous item).
*/
export function moveItemUp(
state: LoopRuntimeState,
index: number
): LoopRuntimeState {
if (index <= 0 || index >= state.items.length) {
return state; // Cannot move first item up or invalid index
}
const newItems = [...state.items];
const temp = newItems[index];
newItems[index] = newItems[index - 1];
newItems[index - 1] = temp;
// Adjust editIndex if we're editing one of the moved items
let newEditIndex = state.editIndex;
if (state.editIndex !== null) {
if (state.editIndex === index) {
newEditIndex = index - 1;
} else if (state.editIndex === index - 1) {
newEditIndex = index;
}
}
return {
...state,
items: newItems,
editIndex: newEditIndex,
};
}
/**
* Move an item down (swap with next item).
*/
export function moveItemDown(
state: LoopRuntimeState,
index: number
): LoopRuntimeState {
if (index < 0 || index >= state.items.length - 1) {
return state; // Cannot move last item down or invalid index
}
const newItems = [...state.items];
const temp = newItems[index];
newItems[index] = newItems[index + 1];
newItems[index + 1] = temp;
// Adjust editIndex if we're editing one of the moved items
let newEditIndex = state.editIndex;
if (state.editIndex !== null) {
if (state.editIndex === index) {
newEditIndex = index + 1;
} else if (state.editIndex === index + 1) {
newEditIndex = index;
}
}
return {
...state,
items: newItems,
editIndex: newEditIndex,
};
}
/**
* Clear draft and exit edit mode.
*/
export function clearDraft(state: LoopRuntimeState): LoopRuntimeState {
return {
...state,
draft: {},
editIndex: null,
};
}