clqms-shadcn-v1/src/lib/components/composable/use-master-detail.svelte.js

225 lines
5.2 KiB
JavaScript

import { useResponsive } from "./use-responsive.svelte.js";
import { useForm } from "./use-form.svelte.js";
import { tick } from "svelte";
export function useMasterDetail(options = {}) {
const { onSelect = null, formConfig = null, } = options;
let selectedItem = $state(null);
let mode = $state("view");
let isLoadingDetail = $state(false);
let formSnapshot = $state(null);
let extraSnapshots = $state({});
let extraRegistry = $state.raw([]);
let showExitConfirm = $state(false);
const formState = useForm(formConfig);
const { isMobile } = useResponsive();
const isFormMode = $derived(mode === "create" || mode === "edit");
const showMaster = $derived(!isMobile || (mode === "view" && !selectedItem));
const showDetail = $derived(!isMobile || selectedItem || isFormMode);
const layout = $derived({
masterWidth: isMobile ? "w-full" : isFormMode ? "w-[3%]" : "w-[35%]",
detailWidth: isMobile ? "w-full" : isFormMode ? "w-[97%]" : "w-[65%]",
});
// const isDirty = $derived(
// JSON.stringify(formState.form) !== JSON.stringify(formSnapshot)
// );
// const isDirty = $derived(
// JSON.stringify(formState.form) !== JSON.stringify(formSnapshot) ||
// (extraRegistry.length > 0 && extraRegistry.some(({ key, get }) =>
// JSON.stringify(get()) !== JSON.stringify(extraSnapshots[key])
// ))
// );
const isDirty = $derived.by(() => {
if (JSON.stringify(formState.form) !== JSON.stringify(formSnapshot)) {
return true;
}
return extraRegistry.some((item) => {
const current = item.get();
const snapshot = extraSnapshots[item.key];
return JSON.stringify(current) !== JSON.stringify(snapshot);
});
});
async function select(item) {
mode = "view";
if (onSelect) {
isLoadingDetail = true;
try {
const detailData = await onSelect(item);
selectedItem = detailData;
} catch (error) {
console.error("Failed to fetch detail:", error);
selectedItem = null;
} finally {
isLoadingDetail = false;
}
} else {
selectedItem = item;
}
}
async function enterCreate(initialData = null) {
mode = "create";
selectedItem = null;
formState.reset();
for (const item of extraRegistry) {
item.reset?.();
}
if (initialData) {
formState.setForm(initialData);
}
await tick();
formSnapshot = $state.snapshot(formState.form);
// extraSnapshots = extraRegistry.length > 0 ? Object.fromEntries(
// extraRegistry.map(({ key, get }) => [key, $state.snapshot(get())])
// ) : {};
extraSnapshots = extraRegistry.length > 0
? Object.fromEntries(extraRegistry.map(item => [item.key, $state.snapshot(item.get())]))
: {};
}
async function enterEdit(param) {
if (!selectedItem) return;
mode = "edit";
const raw = param
? selectedItem[param]
: selectedItem;
if (!raw) return;
const base = $state.snapshot(raw);
const entity = formConfig.mapToForm
? formConfig.mapToForm(base)
: base;
formState.reset();
formState.setForm(entity);
Object.keys(formState.errors).forEach(key => {
formState.errors[key] = null;
});
formSnapshot = $state.snapshot(formState.form);
await tick();
if (extraRegistry.length > 0) {
extraSnapshots = Object.fromEntries(
// extraRegistry.map(({ key, get }) => [key, $state.snapshot(get())])
extraRegistry.map((item) => {
const key = item.key;
const get = item.get;
return [key, $state.snapshot(get())];
})
);
}
}
function exitForm(force = false) {
if (!force && isDirty) {
showExitConfirm = true;
return;
}
// Direct exit
mode = "view";
selectedItem = null;
formSnapshot = null;
}
function confirmExit() {
mode = "view";
selectedItem = null;
formSnapshot = null;
extraSnapshots = {};
}
function backToList() {
selectedItem = null;
mode = "view";
formSnapshot = null;
extraSnapshots = {}
}
function saveForm() {
formSnapshot = { ...form };
}
function registerExtraState({ key, get, reset }) {
const exists = extraRegistry.findIndex(e => e.key === key);
if (exists !== -1) {
extraRegistry[exists] = { key, get, reset };
} else {
extraRegistry = [...extraRegistry, { key, get, reset }]
}
}
return {
get selectedItem() {
return selectedItem;
},
get mode() {
return mode;
},
get isFormMode() {
return isFormMode;
},
get isMobile() {
return isMobile;
},
get showMaster() {
return showMaster;
},
get showDetail() {
return showDetail;
},
get layout() {
return layout;
},
get formSnapshot() {
return formSnapshot;
},
// get form() {
// return form;
// },
get isDirty() {
return isDirty;
},
get isLoadingDetail() {
return isLoadingDetail;
},
get formState() {
return formState;
},
get showExitConfirm() { return showExitConfirm; },
set showExitConfirm(value) { showExitConfirm = value; },
select,
enterCreate,
enterEdit,
exitForm,
confirmExit,
backToList,
saveForm,
registerExtraState,
};
}