mirror of
https://github.com/faiztyanirh/clqms-shadcn-v1.git
synced 2026-04-25 10:32:06 +07:00
225 lines
5.2 KiB
JavaScript
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,
|
|
};
|
|
} |