/** * Base type definitions for Kirby Panel. * * This module provides the foundational types for the Panel's * state management hierarchy: State → Feature → Modal. * * @since 4.0.0 */ // ----------------------------------------------------------------------------- // State Management // ----------------------------------------------------------------------------- /** * Base state interface for Panel state objects. * * The Panel uses a hierarchical state management pattern where all * reactive state objects inherit from this base. State objects are * created via factory functions that return Vue reactive objects. * * @typeParam TDefaults - Shape of the default state object * * @example * ```ts * // State is used by: language, menu, notification, system, translation, user, drag, theme * const notification: PanelState = panel.notification; * notification.set({ message: "Saved!" }); * ``` * * @source panel/src/panel/state.js * @source panel/src/panel/state.ts */ export interface PanelState> { /** * Returns the state key identifier. * Used by backend responses to target the correct state object. */ key: () => string; /** * Returns all default values for the state. * Used for state restoration and initialization. */ defaults: () => TDefaults; /** * Restores the default state by calling `set(defaults())`. * @returns K5 returns the restored state object; K6 narrowed the return to void. */ reset: () => TDefaults | void; /** * Sets a new state, merging with defaults. * Missing properties are filled from defaults. * * @param state - Partial state to merge * @returns The complete merged state */ set: (state: Partial) => TDefaults; /** * Returns the current state filtered to default keys only. * Properties not in defaults are excluded. */ state: () => TDefaults; /** * Validates that the state is a plain object. * * @throws Error if state is not an object * @deprecated K6 inlined the check into `set()` and removed the public method – calls on K6 will throw. */ validateState: (state: unknown) => boolean; } // ----------------------------------------------------------------------------- // Event Listeners // ----------------------------------------------------------------------------- /** * @source panel/src/panel/listeners.js * @source panel/src/panel/listeners.ts */ export type PanelEventCallback = (...args: any[]) => TReturn; /** * Map of event names to their callback functions. * @source panel/src/panel/listeners.js * @source panel/src/panel/listeners.ts */ export type PanelEventListenerMap = Partial< Record >; /** * Event listener mixin interface. * * Provides event handling capabilities for Panel features. * This is mixed into Feature and Modal classes to enable * custom event handling without a full event bus. * * @typeParam TEvents - Union of valid event names * * @example * ```ts * panel.dialog.addEventListener("submit", (value) => { * console.log("Dialog submitted:", value); * }); * * panel.dialog.emit("submit", formData); * ``` * * @source panel/src/panel/listeners.js * @source panel/src/panel/listeners.ts */ export interface PanelEventListeners { on: PanelEventListenerMap; /** * Registers a single event listener. * Only functions are registered; other values are ignored. * * @param event - Event name to listen for * @param callback - Function to call when event fires */ addEventListener: (event: TEvents, callback: PanelEventCallback) => void; /** * Registers multiple event listeners at once. * Invalid listener objects are silently ignored. * * @param listeners - Object mapping event names to callbacks */ addEventListeners: (listeners: PanelEventListenerMap) => void; /** * Emits an event, calling the registered listener if any. * * @param event - Event name to emit * @param args - Arguments to pass to the listener * @returns Listener result, or `undefined` when no listener is registered. * K5 returned a noop function in that case; K6 dropped the fallback. */ emit: (event: TEvents, ...args: any[]) => TReturn | undefined; /** * Checks if a listener is registered for an event. * * @param event - Event name to check * @returns True if a function is registered for this event */ hasEventListener: (event: TEvents) => boolean; /** Returns all registered listeners. */ listeners: () => PanelEventListenerMap; /** * Removes the listener registered for `event`. * * @param event - Event name whose listener should be removed * @since 6 */ removeEventListener: (event: TEvents) => void; /** * Clears every registered listener. Called automatically when feature * state is replaced. * @since 6 */ removeEventListeners: () => void; } // ----------------------------------------------------------------------------- // Feature // ----------------------------------------------------------------------------- /** * @source panel/src/panel/feature.js * @source panel/src/panel/feature.ts */ export interface PanelFeatureDefaults { abortController: AbortController | null; component: string | null; isLoading: boolean; on: PanelEventListenerMap; path: string | null; props: Record; query: Record; referrer: string | null; timestamp: number | null; } /** * Feature interface with loading and request capabilities. * * Features are the main building blocks of the Panel, providing * loading states, API requests, and event handling. They extend * State with HTTP request methods and the event listener mixin. * * Features include: view, dropdown. * * @typeParam TDefaults - Shape of the feature's default state * * @example * ```ts * // Load a view * await panel.view.load("/pages/home"); * * // Open a dropdown with options * await panel.dropdown.open("/dropdowns/pages/home/options"); * ``` * * @source panel/src/panel/feature.js * @source panel/src/panel/feature.ts */ export interface PanelFeature extends PanelState, PanelEventListeners { /** * AbortController for canceling pending requests. * Created on each `load()` call to enable request cancellation. */ abortController: AbortController | null; /** * Current Vue component name to render. * Set by the backend response. */ component: string | null; /** * Whether the feature is currently loading data. * Set to true during `load()`, `get()`, and `post()` calls. */ isLoading: boolean; /** * Relative path for the feature. * Used for API requests and URL building. */ path: string | null; /** * Props passed to the Vue component. * Contains all data from the backend response. */ props: Record; /** URL query parameters from the latest request. */ query: Record; /** Previous path for navigation and redirects. */ referrer: string | null; /** Timestamp from the backend for cache invalidation. */ timestamp: number | null; /** * Sends a GET request and returns the response. * Sets `isLoading` during the request. * * @param url - URL to fetch * @param options - Request options * @returns Response data or false on error */ get: ( url: string | URL, options?: PanelRequestOptions, ) => Promise; /** * Loads a feature from the server and opens it. * Creates an AbortController and routes through `panel.open()`. * * @param url - Feature URL to load * @param options - Request options or submit handler function * @returns The feature's state after loading */ load: ( url: string | URL, options?: PanelRequestOptions | PanelEventCallback, ) => Promise; /** * Opens a feature by URL or state object. * If given a URL, delegates to `load()`. Otherwise sets state directly. * * @param feature - URL string, URL object, or state object * @param options - Request options or submit handler function * @returns The feature's state after opening */ open: ( feature: string | URL | Partial, options?: PanelRequestOptions | PanelEventCallback, ) => Promise; /** * Sends a POST request to the feature's path. * Uses `props.value` if no value is provided. * * @param value - Data to send * @param options - Request options * @returns Response data or false on error * @throws Error if feature has no path */ post: (value?: any, options?: PanelRequestOptions) => Promise; /** * Reloads properties from the server to refresh state. * Only updates props if the component matches. * * @param options - Request options * @returns The feature's state after refresh */ refresh: (options?: PanelRefreshOptions) => Promise; /** * Reloads the feature by re-opening its current URL. * * @param options - Request options * @returns False if no path exists; K6 forwards the `open()` result, K5 returns void. */ reload: (options?: PanelRequestOptions) => Promise; /** Creates a full URL object for the current path and query. */ url: () => URL; } // ----------------------------------------------------------------------------- // Modal // ----------------------------------------------------------------------------- /** * Modal event types for dialogs and drawers. * @source panel/src/panel/modal.js * @source panel/src/panel/modal.ts */ export type PanelModalEvent = | "cancel" | "close" | "closed" | "input" | "open" | "submit" | "success"; /** * Bound listener functions returned by `modal.listeners()`. * @source panel/src/panel/modal.js * @source panel/src/panel/modal.ts */ export interface PanelModalListeners { cancel: () => Promise; close: (id?: string | true) => Promise; input: (value: any) => void; submit: (value?: any, options?: PanelRequestOptions) => Promise; success: (response: PanelSuccessResponse) => void; [key: string]: ((...args: any[]) => any) | undefined; } /** * Success response from modal submission. * @source panel/src/panel/modal.js * @source panel/src/panel/modal.ts */ export interface PanelSuccessResponse { message?: string; /** Events to emit (string or array of strings) */ event?: string | string[]; /** Whether to emit the global `"success"` event (default: true) */ emit?: boolean; /** URL to navigate to */ route?: string | { url: string; options?: PanelRequestOptions }; /** Alternative to route */ redirect?: string | { url: string; options?: PanelRequestOptions }; /** Whether to reload the view */ reload?: boolean | PanelRequestOptions; /** Additional properties */ [key: string]: any; } /** * Modal interface for dialogs and drawers. * * Modals extend features with overlay-specific functionality * like history navigation, form handling, and open/close states. * They manage document overflow and scroll position when open. * * Modals include: dialog, drawer * * @typeParam TDefaults - Shape of the modal's default state * * @example * ```ts * // Open a dialog * await panel.dialog.open("/dialogs/pages/create", { * on: { * submit: (value) => console.log("Created:", value) * } * }); * * // Close with history navigation * panel.drawer.goTo("previous-drawer-id"); * ``` * * @source panel/src/panel/modal.js * @source panel/src/panel/modal.ts */ export interface PanelModal< TDefaults extends object = PanelFeatureDefaults & { id: string | null }, > extends PanelFeature { /** * Unique ID for identifying nested modals. * Auto-generated via UUID if not provided. */ id: string | null; isOpen: boolean; /** * Navigation history for nested modals. * Stores state snapshots for back navigation. */ history: PanelHistory; /** * Quick access to `props.value`. * Dialogs and drawers often contain forms. */ readonly value: any; /** Cancels the modal by emitting 'cancel' and closing. */ cancel: () => Promise; /** * Closes the modal, optionally by ID. * * @param id - Specific modal ID, `true` to close all, or undefined for current * @returns Promise resolving to the previous modal's state, or void */ close: (id?: string | true) => Promise; /** * Sets focus to the first focusable input or a specific input. * * @param input - Optional input name to focus */ focus: (input?: string) => void; /** * Navigates to a specific modal in history by ID. * * @param id - Milestone ID to navigate to */ goTo: (id: string) => void; /** * Updates the form value and emits 'input' event. * Uses Vue's `set()` for reactivity. * * @param value - New form value */ input: (value: any) => void; /** * Returns bound listener functions for the Fiber component. * Includes: cancel, close, input, submit, success, plus custom listeners. */ listeners: () => PanelModalListeners; /** * Opens the modal by URL or state object. * Closes the current notification on first open and blocks document overflow. * * @param modal - URL or state object * @param options - Request options * @returns The modal's state after opening */ open: ( modal: string | URL | Partial, options?: PanelRequestOptions | PanelEventCallback, ) => Promise; /** * Reloads the modal by closing and reopening at the same URL. * * @param options - Request options * @returns False if no path exists; K6 forwards the `open()` result, K5 returns void. */ reload: (options?: PanelRequestOptions) => Promise; /** * Sets modal state, auto-generating an ID if not provided. * * @param state - State to set * @returns The complete state */ set: (state: Partial) => TDefaults; /** * Submits the modal form. * Checks for submit listener first, then sends POST if path exists. * * @param value - Form value (defaults to `props.value`) * @param options - Request options * @returns Response from listener, POST, or closes if no handler */ submit: (value?: any, options?: PanelRequestOptions) => Promise; /** * Handles success response after submission. * Shows notification, emits events, and handles redirect/reload. * * @param success - Success response object or message string * @returns The success response */ success: (success: PanelSuccessResponse | string) => PanelSuccessResponse; /** * Emits events specified in the success response. * Wraps single events in array and emits 'success' unless disabled. * * @param state - Success response with event data */ successEvents: (state: PanelSuccessResponse) => void; /** * Shows a success notification if response contains a message. * * @param state - Success response with optional message */ successNotification: (state: PanelSuccessResponse) => void; /** * Handles redirects from success response. * * @param state - Success response with route/redirect * @returns False if no redirect, otherwise navigates */ successRedirect: ( state: PanelSuccessResponse, ) => false | void | Promise; } // ----------------------------------------------------------------------------- // History // ----------------------------------------------------------------------------- /** * A history milestone representing a saved modal state. * @source panel/src/panel/history.js * @source panel/src/helpers/history.ts */ export interface PanelHistoryMilestone { id: string; /** Additional state properties */ [key: string]: any; } /** * History interface for modal navigation. * * Tracks navigation milestones within modals to enable * back/forward navigation in nested dialogs and drawers. * Each milestone stores a complete state snapshot. * * @example * ```ts * // Navigate back in drawer history * const previous = panel.drawer.history.last(); * if (previous) { * panel.drawer.goTo(previous.id); * } * ``` * * @source panel/src/panel/history.js * @source panel/src/helpers/history.ts */ export interface PanelHistory { milestones: PanelHistoryMilestone[]; /** * Adds a state to history. * The state must have an `id` property. * * @param state - State object with required `id` * @param replace - If true, replaces the last milestone instead of adding * @throws Error if state has no `id` */ add: (state: PanelHistoryMilestone, replace?: boolean) => void; /** * Gets milestone at a specific index. * Supports negative indices (-1 for last). * * @param index - Array index * @returns Milestone at index, or undefined */ at: (index: number) => PanelHistoryMilestone | undefined; /** Clears all milestones from history. */ clear: () => void; /** * Gets milestone by ID, or all milestones if no ID provided. * * @param id - Milestone ID, or null/undefined for all * @returns Single milestone, all milestones, or undefined */ get: ( id?: string | null, ) => PanelHistoryMilestone | PanelHistoryMilestone[] | undefined; /** * Navigates to a milestone, removing all items after it. * * @param id - Milestone ID to navigate to * @returns The milestone, or undefined if not found */ goto: (id: string) => PanelHistoryMilestone | undefined; /** * Checks if a milestone exists in history. * * @param id - Milestone ID to check */ has: (id: string) => boolean; /** * Returns true when more than one milestone is stored. * @since 6 */ hasPrevious: () => boolean; /** * Gets the array index of a milestone. * * @param id - Milestone ID * @returns Index, or -1 if not found */ index: (id: string) => number; /** Checks if history has no milestones. */ isEmpty: () => boolean; /** Gets the last milestone in history. */ last: () => PanelHistoryMilestone | undefined; /** * Removes a milestone by ID, or the last milestone if no ID. * * @param id - Milestone ID, or null to remove last * @returns Updated milestones array */ remove: (id?: string | null) => PanelHistoryMilestone[]; /** * Removes the last milestone from history. * * @returns Updated milestones array */ removeLast: () => PanelHistoryMilestone[]; /** * Replaces a milestone at a specific index. * Index -1 replaces the last milestone. * * @param index - Array index to replace * @param state - New state to insert */ replace: (index: number, state: PanelHistoryMilestone) => void; } // ----------------------------------------------------------------------------- // Request Options // ----------------------------------------------------------------------------- /** * Options for Panel API requests. * @source panel/src/panel/request.js * @source panel/src/panel/feature.js * @source panel/src/panel/request.ts * @source panel/src/panel/feature.ts */ export interface PanelRequestOptions { headers?: Record; /** Request body for POST/PATCH */ body?: any; query?: Record; signal?: AbortSignal; /** * If true, skips setting `isLoading` state. * Useful for background requests. */ silent?: boolean; on?: PanelEventListenerMap; /** CSRF token sent as the `x-csrf` header. */ csrf?: string | false; /** * Globals sent as the `x-panel-globals` header (was `x-fiber-globals` in K5). * Arrays are joined with commas; strings are forwarded as-is. */ globals?: string | string[]; /** Referrer path sent as the `x-panel-referrer` header (was `x-fiber-referrer` in K5). */ referrer?: string | false; } /** * Extended options for refresh requests. * @source panel/src/panel/feature.js * @source panel/src/panel/feature.ts */ export interface PanelRefreshOptions extends PanelRequestOptions { /** URL to refresh from (defaults to current URL) */ url?: string | URL; } // ----------------------------------------------------------------------------- // Context & Notification Types // ----------------------------------------------------------------------------- /** * Panel context indicating which layer is currently active. * Used to determine where notifications appear and which feature has focus. * @source panel/src/panel/panel.js * @source panel/src/panel/notification.js * @source panel/src/panel/notification.ts */ export type PanelContext = "view" | "dialog" | "drawer"; /** * Type of notification determining behavior and persistence. * Only `error` and `fatal` are written to `state.type`; `success()` and * `info()` shortcuts set `theme` (and `icon`) instead of `type`. * - `error`: Operation failed, persists until dismissed * - `fatal`: Critical error, displayed in isolated iframe * @source panel/src/panel/notification.js * @source panel/src/panel/notification.ts */ export type NotificationType = "error" | "fatal"; /** * Visual theme for notifications. * - `positive`: Green, for success * - `negative`: Red, for errors * - `info`: Blue, for information * @source panel/src/panel/notification.js * @source panel/src/panel/notification.ts */ export type NotificationTheme = "positive" | "negative" | "info";