import type { ClickOutsideParameters } from '@svelte-put/clickoutside';
import type { MovableParameters } from '@svelte-put/movable';
import type { ComponentEvents, ComponentProps, ComponentType, createEventDispatcher, SvelteComponentTyped } from 'svelte';
import type { Writable } from 'svelte/store';
import type Modal from './Modal.svelte';
/**
 * The trigger that resolves the modal
 *
 * - `backdrop`: non-static backdrop was clicked
 *
 * - `x`: `x` button was clicked
 *
 * - `escape`: `escape` key was pressed
 *
 * - `clickoutside`: click outside of modal was detected
 *
 * - `pop`: modal was resolved from by calling the `pop` method **manually** from the modal store
 *
 * - `custom`: modal was resolved by a custom dispatch. Use this in your
 * custom modal when extending the `resolve` event with additional props.
 * See {@link ExtendedModalEvents}
 *
 *
 */
export type ResolveTrigger = 'backdrop' | 'x' | 'escape' | 'clickoutside' | 'pop' | 'custom';
/**
 * The base interface when modal is resolved
 *
 */
export type ModalComponentBaseResolved<ExtendedResolved extends Record<string, any> = {}> = {
    trigger: ResolveTrigger;
} & ExtendedResolved;
/**
 * The base events for modal
 *
 */
export type ModalComponentBaseEvents<Resolved extends ModalComponentBaseResolved = ModalComponentBaseResolved> = {
    resolve: CustomEvent<Resolved>;
};
/**
 * The resolved type for modal
 * @example
 *
 * ```typescript
 * import CustomModal from './CustomModal.svelte';
 * import type { ModalResolved } from '@svelte-put/modal';
 *
 * type CustomModalResolved = ModalResolved<CustomModal>;
 * ```
 */
export type ModalResolved<Component extends ModalComponentBase> = ComponentEvents<Component>['resolve']['detail'];
/**
 * The base slots for modal
 *
 */
export interface ModalComponentBaseSlots {
    /** content of the modal */
    default: Record<string, never>;
    /** backdrop of the modal */
    backdrop: {
        /** the resolved class name, merged from the default and the `classes` prop */
        class: string;
        /** default click handler for backdrop (dismiss with (trigger) as `backdrop`) */
        onClick: () => void;
    };
    /** modal container */
    container: {
        /** the resolved class name, merged from the default and the `classes` prop */
        class: string;
    };
    /** `x` button */
    x: {
        /** the resolved class name, merged from the default and the `classes` prop */
        class: string;
        /** default click handler for x button (dismiss with (trigger) as `x`) */
        onClick: () => void;
        /** the forwarded `xBtn` prop, see {@link ModalComponentBaseProps} */
        xBtn: boolean;
    };
    /** content of `x` button */
    'x-content': Record<string, never>;
}
/**
 * The base events for modal
 *
 */
export interface ModalComponentBaseProps {
    /**
     * whether the modal is at topmost position (last pushed),
     * this prop is handled by the `ModalPortal` component and
     * you don't have to touch this
     */
    topmost?: boolean;
    /**
     * how the backdrop should render or behave. Defaults to `true`
     *
     * - `false` - no backdrop
     *
     * - `true` - backdrop that when clicked on will dismiss modal
     *
     * - `static` - backdrop that does not dismiss modal
     */
    backdrop?: 'static' | boolean;
    /**
     * whether to render the `x` button (for dismissing modal). Defaults to `true`
     *
     * if you are overriding the `x` slot, this prop is forwarded to the slot template.
     * See {@link ModalComponentBaseSlots}
     */
    xBtn?: boolean;
    /**
     * whether to dismiss modal when `esc` key is pressed. Defaults to `true`
     */
    escape?: boolean;
    /**
     * whether to dismiss modal when clicking outside. Most useful when
     * you want to not have a backdrop but still dismiss modal when clicking
     * outside the modal. Defaults to `false`.
     *
     * Can be provided as boolean or the parameter object
     */
    clickoutside?: boolean | ClickOutsideParameters;
    /**
     * whether to make the modal "movable" (drag around).
     * Can be provided as boolean or the parameter object.
     * Defaults to `false`.
     */
    movable?: boolean | MovableParameters;
    /**
     * custom class names for the modal elements.
     *
     * - If property is provided as `string`, it'll be added to the default class name of that element.
     *
     * - If property is provided as `{ override: string }`, it'll override the default class name of that element.
     *
     *
     *
     * As Svelte style is component-scoped. You will need to use either a global
     * styling system like Tailwind or the `:global()` selector (example below).
     *
     * When overriding slots, the classes provided here will be merged with the default
     * class names and passed to the slot template. See {@link ModalComponentBaseSlots}
     * @example
     *
     * ```html
     * <script lang="ts">
     *   import Modal from '@svelte-put/modal/Modal.svelte';
     *   import type { ExtendedModalProps } from '@svelte-put/modal';
     *
     *   type $$Props = ExtendedModalProps;
     * </script>
     *
     * <Modal
     *   classes={{
     *     backdrop: 'bg-gray-500 bg-opacity-50',
     *     container: { override: 'custom-modal-container' },
     *     x: 'a-global-class',
     *   }}
     *   {...$$props}
     *   on:resolve
     * >
     *  <svelte:fragment slot="x" let:class={className} let:xBtn let:onClick>
     *     {#if xBtn}
     *       <button type="button" class={className} on:click={onClick}>
     *         some custom svg here perhaps
     *       </button>
     *     {/if}
     *   </svelte:fragment>
     * </Modal>
     *
     * <style>
     *   :global(.custom-modal-container) {
     *     background-color: #fff;
     *     with: 80%;
     *   }
     * </style>
     * ```
     */
    classes?: Partial<Record<Exclude<keyof ModalComponentBaseSlots, 'default' | 'x-content'>, string | {
        override: string;
    }>>;
    /**
     * Some accessibility attributes passed to the modal container element as discussed
     * in {@link https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/dialog_role | MDN Accessibility - Dialog}
     *
     *
     *
     * Since the base `Modal` does not know beforehand its content (passed by slots),
     * responsibility for accessibility is delegated to you.
     *
     * By default, the `role` attribute is set to `dialog` without any aria attribute
     * @example
     *
     * ```html
     * <script lang="ts">
     *   import Modal from '@svelte-put/modal/Modal.svelte';
     *   import type { ExtendedModalProps } from '@svelte-put/modal';
     *
     *   $$Props = ExtendedModalProps;
     *   $$Events = ExtendedModalEvents<{
     *      confirmed: boolean;
     *   }>
     *
     *   // create type-safe dispatch function
     *   const dispatch = createModalEventDispatcher<$$Events>();
     *   function resolve() {
     *     dispatch('resolve', {
     *       trigger: 'custom',
     *       confirmed: true,
     *     });
     *   }
     * </script
     *
     * <Modal
     *    {...$$props}
     *    accessibility={{
     *      role: 'dialog',
     *      labelledBy: 'confirmation-dialog-title',
     *      describedBy: 'confirmation-dialog-description',
     *    }}
     *    on:resolve
     * >
     *    <h2 id="confirmation-dialog-title">Submission Confirmation</h2>
     *    <p id="confirmation-dialog-description">Are you sure you want to submit?</p>
     *    <button type="button" on:click={() => resolve(true)}>Yes</button>
     *    <button type="button" on:click={() => resolve(false)}>No</button>
     * </Modal>
     * ```
     */
    accessibility?: {
        role: 'dialog' | 'alertdialog';
        /** id of the element for `aria-labelledby` */
        labelledBy?: string;
        /** id of the element for `aria-describedby` */
        describedBy?: string;
    };
    /**
     * svelte event dispatcher. Should only pass this prop if extending the events.
     * See {@link ExtendedModalEvents} for more details an dexamples
     */
    dispatch?: ReturnType<typeof createEventDispatcher>;
}
/**
 * The base component for building modal. Modals extending this component needs to
 * meet these specified constraints
 *
 */
export type ModalComponentBase = SvelteComponentTyped<{}, ModalComponentBaseEvents<ModalComponentBaseResolved>, {}>;
/**
 * Either the Svelte modal component or an option object that specifies how
 * to push the modal onto the stack
 *
 */
export type ModalPushInput<Component extends ModalComponentBase> = ComponentType<Component> | {
    /**
     * id to track this modal
     * @default (crypto && crypto.randomUUID && crypto.randomUUID()) ?? `modal-indexed-$\{modals.length\}`
     */
    id?: string;
    /**
     * the component to push
     */
    component: ComponentType<Component>;
    /**
     * props passed to pushed component
     */
    props?: ComponentProps<Component>;
};
/**
 * The return of the `push` method of modal store
 *
 */
export interface ModalPushOutput<Component extends ModalComponentBase, Resolved extends ModalComponentBaseResolved = ModalResolved<Component>> {
    /**
     * id of the pushed modal, pass to `pop` to dismiss modal
     * @example
     *
     * ```typescript
     * import { createModalStore } from '@svelte-put/modal';
     * const store = createModalStore();
     * const pushed = store.push({ component: SomeComponent, props: { someProp: 'value' } });
     * store.pop(pushed);
     * ```
     */
    id: string;
    /** component to render in modal layout */
    component: ComponentType<Component>;
    /** props passed to modal component */
    props: ComponentProps<Component>;
    /**
     * wait for the modal to resolve, and return the promise wrapping the resolved value
     * @example
     *
     * ```typescript
     * import { createModalStore } from '@svelte-put/modal';
     * const store = createModalStore();
     * const pushed = store.push({ component: ConfirmationModal });
     * // assuming ConfirmationModal resolves with { confirmed: boolean }
     * const { confirmed } = await pushed.resolve();
     * ```
     */
    resolve: () => Promise<Resolved>;
    /**
     * whether the modal has been resolved
     */
    resolved: boolean;
}
/**
 * Utility type for building custom props type from the base Modal component
 * @example
 *
 * ```html
 * <script lang="ts">
 *   import Modal from '@svelte-put/modal/Modal.svelte';
 *   import type { ExtendedModalProps } from '@svelte-put/modal';
 *
 *   $$Props = ExtendedModalProps<{ anotherProp: string }>;
 *
 *   export let anotherProp: string;
 * </script>
 *
 * <Modal {...$$restProps} on:resolve>
 *   <p>{anotherProp}</p>
 * </Modal>
 * ```
 */
export type ExtendedModalProps<ExtendedProps extends Record<string, any> = {}> = ComponentProps<Modal> & ExtendedProps;
/**
 * Utility type for building custom events type from the base Modal component.
 * Use in conjunction with {@link createModalEventDispatcher}
 *
 *
 *
 *
 * When extending events, we need to create a new `dispatch` function.
 * Remember to pass this new `dispatch` as prop to the `Modal` component.
 *
 * When dispatching your extended `resolve` event, you will also need to pass
 * the `trigger` prop to the event detail. Any {@link ResolveTrigger} is valid,
 * but in this context `custom` is recommended (see example below).
 *
 * For simple no-action modal, just forward the resolve event.
 *
 * ```html
 * <script lang="ts">
 * import Modal from '@svelte-put/modal/Modal.svelte';
 * import type { ExtendedModalProps } from '@svelte-put/modal';
 *
 * $$Props = ExtendedModalProps;
 * </script
 *
 * <Modal {...$$props} on:resolve />
 * ```
 * @example
 *
 * Custom the base `resolve` event.
 *
 * ```html
 * <script lang="ts">
 *   import Modal from '@svelte-put/modal/Modal.svelte';
 *   import { createModalEventDispatcher } from '@svelte-put/modal;
 *   import type { ExtendedModalEvents, ExtendedModalProps } from '@svelte-put/modal';
 *
 *   $$Props = ExtendedModalProps;
 *   $$Events = ExtendedModalEvents<{
 *      confirmed: boolean;
 *   }>
 *
 *   // create type-safe dispatch function
 *   const dispatch = createModalEventDispatcher<$$Events>();
 *
 *   function resolve() {
 *     dispatch('resolve', {
 *       trigger: 'custom',
 *       confirmed: true,
 *     });
 *   }
 * </script>
 *
 * <Modal {...$$props} {dispatch}>
 *   <button type="button" on:click={() => resolve(true)}>Confirm</button>
 *   <button type="button" on:click={() => resolve(false)}>Cancel</button>
 * </Modal>
 * ```
 * @example
 *
 * Add other events.
 *
 * ```html
 * <script lang="ts">
 *   import Modal from '@svelte-put/modal/Modal.svelte';
 *   import { createModalEventDispatcher } from '@svelte-put/modal;
 *   import type { ExtendedModalEvents, ExtendedModalProps } from '@svelte-put/modal';
 *
 *   $$Props = ExtendedModalProps;
 *   $$Events = ExtendedModalEvents<{}, {
 *     anotherEvent: string;
 *   }>
 *
 *   // create type-safe dispatch function
 *   const dispatch = createModalEventDispatcher<$$Events>();
 *
 *   function handleClick() {
 *     dispatch('anotherEvent', 'detail');
 *   }
 * </script>
 *
 * <Modal {...$$props} {dispatch}>
 *   <button type="button" on:click={handleClick}>Another Event</button>
 * </Modal>
 * ```
 */
export type ExtendedModalEvents<ExtendedResolved extends Record<string, any> = {}, ExtendedEvents extends Record<string, CustomEvent<any>> = {}> = {
    resolve: CustomEvent<ModalComponentBaseResolved & Partial<ExtendedResolved>>;
} & ExtendedEvents;
/**
 * callback as one passed to ModalStore `onPop`
 *
 */
export type ModalResolveCallback<Component extends ModalComponentBase = ModalComponentBase> = (resolved: ModalResolved<Component>) => void;
/**
 *
 *Push a new modal to the stack
 * @param input - {@link ModalPushInput}
 * @returns the {@link ModalPushOutput}
 */
export type ModalStorePush = <Component extends ModalComponentBase>(input: ModalPushInput<Component>) => ModalPushOutput<Component>;
/**
 *
 *Pop the modal with given id.
 *If `id` is not provided, pop the topmost modal
 *
 *
 *
 *When calling this manually (rather than being called from the `ModalPortal` component),
 *the trigger is expected to be `pop`;
 * @param pushed - the returned {@link ModalPushOutput} output from `push`
 * @param resolved - custom resolved value, if any
 * @returns the popped {@link ModalPushOutput} or `undefined` in the
 * case no modal was found that matches the specified input
 */
export type ModalStorePop = <Pushed extends ModalPushOutput<Component, Resolved>, Component extends ModalComponentBase, Resolved extends ModalResolved<Component>>(pushed?: Pushed, resolved?: Resolved) => Pushed | undefined;
/**
 *
 *callback for when a modal is popped. Can be called multiple times
 *to registered multiple callbacks
 *
 *
 *
 *This should be called before the modal is pushed.
 *
 *If the same callback is registered multiple times, it will only be called once
 *(be aware that inline arrow function will be a different function each time)
 *
 *After the modal is popped, the callback list will be cleared. Meaning
 *next time the same modal is pushed, callback must be registered again.
 *
 *See example for typescript support.
 * @example
 *
 * ```typescript
 *  import CustomModal from './CustomModal.svelte';
 *  import { createModalStore } from '@svelte-put/modal';
 *  import type { ModalResolveCallback, ModalResolved } from '@svelte-put/modal';
 *
 *  const store = createModalStore();
 *  const pushed = store.push(CustomModal);
 *
 *  let unsubscribe = store.onPop<CustomModal>(pushed.id, (resolved) => {});
 *  unsubscribe(); // to unregister the callback
 *
 *  // or
 *
 *  function onPop(resolved: ModalResolved<CustomModal>) {};
 *  unsubscribe = store.onPop<CustomModal>(pushed.id, onPop);
 *  unsubscribe(); // to unregister the callback
 *
 *  // or
 *
 *  const sideEffect: ModalResolveCallback<CustomModal> = (resolved) => {};
 *  unsubscribe = store.onPop(pushed.id, sideEffect);
 *  unsubscribe(); // to unregister the callback
 * ```
 * @param modalId - the id returned from push operation
 * @param callback - {@link ModalResolveCallback}
 * @returns the unsubscribe function, when call will remove the callback
 */
export type ModalStoreOnPop = <Component extends ModalComponentBase = ModalComponentBase>(modalId: string, callback: ModalResolveCallback<Component>) => () => void;
/**
 *
 */
export type ModalStoreValue = ModalPushOutput<ModalComponentBase, ModalComponentBaseResolved>[];
/**
 *
 */
export type ModalStoreSubscribe = Writable<ModalStoreValue>['subscribe'];
/**
 *
 */
export interface ModalStore {
    subscribe: ModalStoreSubscribe;
    /** see {@link ModalStorePush} */
    push: ModalStorePush;
    /** see {@link ModalStorePop} */
    pop: ModalStorePop;
    /** see {@link ModalStoreOnPop} */
    onPop: ModalStoreOnPop;
}
//# sourceMappingURL=modal.types.d.ts.map