Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 38 additions & 4 deletions docs/guide/store.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,28 @@ this is done using the configuration function:
```ts
import {config} from "jenesius-vue-modal";
import ModalConfirm from "./ModalConfirm";
import ModalAlert from "./ModalAlert"

config({
store: {
confirm: ModalConfirm
}
store: {
confirm: ModalConfirm,
alert: {
component: "ModalAlert"
}
}
})
```
In this example, we have added one modal window to the store. Now it can be opened by name by passing the key there:
In this example, we have added two modal windows to the repository:
- by directly transferring components
- in the second case, we specified an object with the `component` property set, this is necessary to set some
properties specifically for this component.

Now you can open it by name by passing the key there:
```ts
openModal('confirm');
openModal('alert');
```

Of course, *pushModal* and *promptModal* methods also support this functionality.

## Checking for a Modal Window
Expand All @@ -36,3 +46,27 @@ getComponentFromStore('alert') // undefined
getComponentFromStore('confirm') // Component
```
**Returns** the VueComponent if it was previously initialized in the store, *undefined* otherwise.

## Extended component transfer

In this article, we will consider the case when we, together with components, pass an entire object with the described `component` property.
In this case, we can also specify the following properties:

- `backgroundClose`
- `draggable`
- `beforeEach`

All these properties correspond to the parameter from [configuration](./config).

```ts
config({
confirm: {
component: ModalConfirm,
draggable: true,
backgroundClose: false,
beforeEach() {
Logger.write('Attempting to open a confirmation window.')
}
}
})
```
38 changes: 36 additions & 2 deletions docs/ru/guide/store.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,26 @@
```ts
import {config} from "jenesius-vue-modal";
import ModalConfirm from "./ModalConfirm";
import ModalAlert from "./ModalAlert"

config({
store: {
confirm: ModalConfirm
confirm: ModalConfirm,
alert: {
component: "ModalAlert"
}
}
})
```
В этом примере мы добавили в магазин одно модальное окно. Теперь его можно открыть по имени, передав туда ключ:
В этом примере мы добавили в хранилище два модальных окна:
- передав напрямую компоненты
- во втором случае, мы указали объект с установленным свойством `component`, это необходимо для установления некоторых
свойств конкретно для данной компоненты.

Теперь его можно открыть по имени, передав туда ключ:
```ts
openModal('confirm');
openModal('alert');
```
Конечно, методы *pushModal* и *promptModal* также поддерживают эту функциональность.

Expand All @@ -34,3 +44,27 @@ getComponentFromStore('alert') // undefined
getComponentFromStore('confirm') // Component
```
**Возвращает** компонент VueComponent, если он был ранее инициализирован в хранилище, в противном случае *undefined*.

## Расширенная передача компоненты

В данной артикле рассмотрим случай, когда мы вместе компоненты передаём целый объект с описанным свойством `component`.
В таком случае мы также можем указать следующие свойства:

- `backgroundClose`
- `draggable`
- `beforeEach`

Все эти свойства соответствуют параметром из [конфигурации](./config).

```ts
config({
confirm: {
component: ModalConfirm,
draggable: true,
backgroundClose: false,
beforeEach() {
Logger.write('Попытка открыть окно подтверждения.')
}
}
})
```
16 changes: 12 additions & 4 deletions src/methods/addModal.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Modal, {ModalOptions} from "../utils/Modal";
import {configuration, getNamespace} from "../utils/state";
import {configuration, getNamespace, isStoreComponentConfiguration} from "../utils/state";
import ModalError from "../utils/ModalError";
import {Component, markRaw} from "vue";
import {getComponentFromStore} from "../index";
Expand Down Expand Up @@ -30,9 +30,17 @@ export default async function _addModal(component: string | Component, params: a

// If component is string. In this case we get the component from store.
if (typeof component === "string") {
const refComponent = getComponentFromStore(component);
if (!refComponent) throw ModalError.ModalNotExistsInStore(component);
component = refComponent;
const storeElement = getComponentFromStore(component);
if (!storeElement) throw ModalError.ModalNotExistsInStore(component);

if (isStoreComponentConfiguration(storeElement)) {
component = storeElement.component;
Object.assign(options, storeElement);

if (storeElement.beforeEach && (await storeElement.beforeEach()) === false)
throw ModalError.RejectedByBeforeEach();

} else component = storeElement;
}
if (!component) throw ModalError.ModalComponentNotProvided();

Expand Down
3 changes: 2 additions & 1 deletion src/methods/get-component-from-store.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {configuration} from "../utils/state";
import {Component} from "vue";
import {IStoreElement} from "../utils/types";

/**
* @description Method using for checking exist modal in store.
* */
export default function getComponentFromStore(modalName: string): Component | undefined {
export default function getComponentFromStore(modalName: string): IStoreElement | undefined {
return configuration.store[modalName] || undefined
}

18 changes: 14 additions & 4 deletions src/utils/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
*
* */

import {Component, watch} from "vue";
import {watch} from "vue";
import NamespaceStore, {INamespaceKey} from "./NamespaceStore";
import {IBeforeEachCallback, IStoreComponentConfiguration, IStoreElement} from "./types";

const modalState = (function () {

Expand Down Expand Up @@ -55,7 +56,7 @@ export function getNamespace(name?: INamespaceKey) {

export const configuration = modalState.configuration;

export interface ConfigInterface{
export interface ConfigInterface {
/**
* @description Disable scrolling in time when modal is open.
* */
Expand Down Expand Up @@ -89,12 +90,21 @@ export interface ConfigInterface{
* @description A hook that will fire before opening each modal window. In this hook, you can cancel closing by
* passing the value false.
*/
beforeEach: () => any,
beforeEach: IBeforeEachCallback,
/**
* @description If the value is set to true, then only one modal window will be shown. If several are opened
* (using pushModal), only the last one will be shown, and the rest will be hidden using `v-show`.
*/
singleShow: boolean,
store: Record<string, Component>
store: Record<string, IStoreElement>
}

/**
* @description The function determines whether the transferred data is an extended configuration of a storage element
*/
export function isStoreComponentConfiguration(element: unknown): element is IStoreComponentConfiguration {
return !!element && typeof element === 'object' && !!(element as IStoreComponentConfiguration).component;
}



11 changes: 11 additions & 0 deletions src/utils/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {IEventClose} from "./dto";
import {Component, ComputedRef, ExtractPropTypes, Ref, UnwrapRef} from "vue";
import {INamespaceKey} from "./NamespaceStore";
import {ConfigInterface} from "./state";

export type GuardFunction = (e: IEventClose) => void | boolean | Promise<boolean>
export type GuardFunctionPromisify = () => Promise<void>
Expand All @@ -14,3 +15,13 @@ export type WrapComponent<T = {}> = Component & {
}
export type WrapComponentProps<P extends WrapComponent = WrapComponent> = P["props"] | UnwrapRef<P["props"]> | Ref<any> | ComputedRef<P["props"]> | ExtractPropTypes<P["props"]>

export type IStoreElement = IStoreComponentConfiguration | Component

export interface IStoreComponentConfiguration extends Partial<Pick<ConfigInterface, 'beforeEach' | 'backgroundClose' | 'draggable'>>{
component: Component,
}

/**
* @description Callback, which will be called before each attempt to open the modal window.
*/
export type IBeforeEachCallback = () => any;
77 changes: 77 additions & 0 deletions tests/configuration-store-element.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import {container, config, pushModal} from "../src/index";
import ModalTitle from "./components/modal-title.vue";
import NamespaceStore from "../src/utils/NamespaceStore";
import {mount} from "@vue/test-utils";


beforeEach(async () => {
NamespaceStore.instance.forceClean()
})

afterEach(() => {
config({
store: {}
})
})

describe("Configuration using Extend Store Element", () => {
test("Default opening modal", async () => {
const wrapper = await mount(container);
config({
store: {
Foo: {
component: ModalTitle
}
}
})
await pushModal('Foo');
await pushModal('Foo');

expect(NamespaceStore.instance.getByName().queue.length).toBe(2)

})

test("Opening modal by name should add options from configuration", async () => {
const wrapper = await mount(container);
config({
store: {
Foo: {
component: ModalTitle,
backgroundClose: false,
draggable: true,
}
}
})
const modal = await pushModal('Foo');
const modal_2 = await pushModal(ModalTitle);

expect(modal.backgroundClose).toBe(false);
expect(modal.draggable).toBe(true);

expect(modal_2.backgroundClose).toBe(true);
expect(modal_2.draggable).toBe(false);
})

test("Configuration with beforeEach", async () => {
await mount(container);
let count = 3;
config({
store: {
Foo: {
component: ModalTitle,
beforeEach() {
if (count-- > 0) return false;
}
}
}
})
await pushModal('Foo').catch(() => {});
await pushModal('Foo').catch(() => {});
await pushModal('Foo').catch(() => {});
await pushModal('Foo');
await pushModal('Foo');

expect(NamespaceStore.instance.getByName().queue.length).toBe(2)

})
})
Loading