Files
HiveGrid-Wireframes/hivegrid-cloud/packages/view-models/src/index.ts
T

111 lines
3.7 KiB
TypeScript

export type VMStatus = "idle" | "loading" | "ready" | "empty" | "error";
export type VMState<T> =
| { status: "idle"; data?: undefined; error?: undefined }
| { status: "loading"; data?: T; error?: undefined }
| { status: "ready"; data: T; error?: undefined }
| { status: "empty"; data?: undefined; error?: undefined }
| { status: "error"; data?: T; error: { messageKey: string; code?: string } };
export type ActionCallbacks<T> = {
readonly onCreate?: (draft?: Partial<T>) => void | Promise<void>;
readonly onUpdate?: (item: T) => void | Promise<void>;
readonly onDelete?: (item: T) => void | Promise<void>;
readonly onRefresh?: () => void | Promise<void>;
};
export type I18nLabel = {
readonly labelKey: string;
readonly descriptionKey?: string;
};
export type RegistryGate = {
readonly permission?: string;
readonly flag?: string;
};
export type SidebarRegistryItem = I18nLabel &
RegistryGate & {
readonly id: string;
readonly href: string;
readonly icon?: string;
readonly children?: readonly SidebarRegistryItem[];
};
export type BreadcrumbRegistryItem = I18nLabel &
RegistryGate & {
readonly id: string;
readonly href?: string;
};
export type MobileRegistryItem = I18nLabel &
RegistryGate & {
readonly id: string;
readonly href: string;
readonly priority?: number;
};
export type PortalRegistryItem = I18nLabel &
RegistryGate & {
readonly id: string;
readonly slot: string;
readonly order?: number;
};
export type ShellRegistries = {
readonly sidebar?: readonly SidebarRegistryItem[];
readonly breadcrumbs?: readonly BreadcrumbRegistryItem[];
readonly mobile?: readonly MobileRegistryItem[];
readonly portals?: readonly PortalRegistryItem[];
};
// --- Subframe-Adapter contract (M015) ---
/** Swappable UI implementation: renders a serializable component model to HTML. */
export type UiRenderer<Model> = (model: Model) => string;
export type DataTableColumn = I18nLabel & { readonly key: string; readonly align?: "left" | "right" | "center" };
export type DataTableRow = { readonly id: string; readonly cells: Readonly<Record<string, string>> };
/** Domain View-Model a loader builds for a tabular list. */
export type DataTableVM = {
readonly caption?: I18nLabel;
readonly columns: readonly DataTableColumn[];
readonly rows: readonly DataTableRow[];
readonly emptyLabelKey?: string;
};
/** Normalized component prop-model the UI implementation renders. */
export type DataTableModel = {
readonly captionKey?: string;
readonly columns: readonly { readonly key: string; readonly labelKey: string; readonly align: "left" | "right" | "center" }[];
readonly rows: readonly DataTableRow[];
readonly emptyLabelKey: string;
readonly rowCount: number;
};
export function ready<T>(data: T): VMState<T> {
return { status: "ready", data };
}
export function empty<T>(): VMState<T> {
return { status: "empty" };
}
export function assertI18nLabel(label: I18nLabel): I18nLabel {
if (!/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$/.test(label.labelKey)) {
throw new Error(`Registry labels must use i18n keys: ${label.labelKey}`);
}
if (label.descriptionKey && !/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$/.test(label.descriptionKey)) {
throw new Error(`Registry descriptions must use i18n keys: ${label.descriptionKey}`);
}
return label;
}
// Domain View-Models (M013-Konvention: src/<domain>/*.vm.ts)
export * from "./walking-skeleton/walking-skeleton.vm.js";
export * from "./auth/session-list.vm.js";
export * from "./auth/select-tenant.vm.js";
export * from "./files/file-browser.vm.js";
export * from "./files/file-detail.vm.js";
export * from "./files/file-upload.vm.js";
export * from "./tasks/task-list.vm.js";
export * from "./tasks/task-detail.vm.js";