piral-modals 1.0.0-pre.2296 → 1.0.1-beta.5640

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,19 +1,20 @@
1
- import { Atom, deref } from '@dbeining/react-atom';
1
+ import create from 'zustand';
2
+ import { createListener } from 'piral-base';
3
+ import { createActions } from 'piral-core';
2
4
  import { registerModal, unregisterModal, openModal, closeModal } from './actions';
3
- import { createActions, createListener } from 'piral-core';
4
5
 
5
6
  describe('Modals Actions Module', () => {
6
7
  it('registerModal and unregisterModal', () => {
7
- const state = Atom.of({
8
+ const state: any = create(() => ({
8
9
  foo: 5,
9
10
  registry: {
10
11
  foo: 5,
11
12
  modals: {},
12
13
  },
13
- });
14
+ }));
14
15
  const ctx = createActions(state, createListener({}));
15
16
  registerModal(ctx, 'foo', 10);
16
- expect(deref(state)).toEqual({
17
+ expect(state.getState()).toEqual({
17
18
  foo: 5,
18
19
  registry: {
19
20
  foo: 5,
@@ -23,7 +24,7 @@ describe('Modals Actions Module', () => {
23
24
  },
24
25
  });
25
26
  unregisterModal(ctx, 'foo');
26
- expect(deref(state)).toEqual({
27
+ expect(state.getState()).toEqual({
27
28
  foo: 5,
28
29
  registry: {
29
30
  foo: 5,
@@ -33,28 +34,28 @@ describe('Modals Actions Module', () => {
33
34
  });
34
35
 
35
36
  it('openModal adds a new modal', () => {
36
- const state = Atom.of({
37
+ const state: any = create(() => ({
37
38
  foo: 5,
38
- modals: ['b'],
39
- });
39
+ modals: [{ id: 'b' }],
40
+ }));
40
41
  const ctx = createActions(state, createListener({}));
41
- openModal(ctx, 'a');
42
- expect(deref(state)).toEqual({
42
+ openModal(ctx, { id: 'a' });
43
+ expect(state.getState()).toEqual({
43
44
  foo: 5,
44
- modals: ['a', 'b'],
45
+ modals: [{ id: 'a' }, { id: 'b' }],
45
46
  });
46
47
  });
47
48
 
48
49
  it('closeModal removes an existing modal', () => {
49
- const state = Atom.of({
50
+ const state: any = create(() => ({
50
51
  foo: 5,
51
- modals: ['a', 'b', 'c'],
52
- });
52
+ modals: [{ id: 'a' }, { id: 'b' }, { id: 'c' }],
53
+ }));
53
54
  const ctx = createActions(state, createListener({}));
54
- closeModal(ctx, 'b');
55
- expect(deref(state)).toEqual({
55
+ closeModal(ctx, { id: 'b' });
56
+ expect(state.getState()).toEqual({
56
57
  foo: 5,
57
- modals: ['a', 'c'],
58
+ modals: [{ id: 'a' }, { id: 'c' }],
58
59
  });
59
60
  });
60
61
  });
package/src/actions.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { withKey, withoutKey, prependItem, excludeItem, GlobalStateContext } from 'piral-core';
1
+ import { withKey, withoutKey, prependItem, excludeOn, GlobalStateContext } from 'piral-core';
2
2
  import { ModalRegistration, OpenModalDialog } from './types';
3
3
 
4
4
  export function openModal(ctx: GlobalStateContext, dialog: OpenModalDialog) {
@@ -11,7 +11,7 @@ export function openModal(ctx: GlobalStateContext, dialog: OpenModalDialog) {
11
11
  export function closeModal(ctx: GlobalStateContext, dialog: OpenModalDialog) {
12
12
  ctx.dispatch((state) => ({
13
13
  ...state,
14
- modals: excludeItem(state.modals, dialog),
14
+ modals: excludeOn(state.modals, (modal) => modal.id === dialog.id),
15
15
  }));
16
16
  }
17
17
 
@@ -1,6 +1,4 @@
1
- import * as React from 'react';
2
1
  import { getPiralComponent } from 'piral-core';
3
- import { ModalsHostProps, ModalsDialogProps } from './types';
4
2
 
5
- export const PiralModalsHost: React.ComponentType<ModalsHostProps> = getPiralComponent('ModalsHost');
6
- export const PiralModalsDialog: React.ComponentType<ModalsDialogProps> = getPiralComponent('ModalsDialog');
3
+ export const PiralModalsHost = getPiralComponent('ModalsHost');
4
+ export const PiralModalsDialog = getPiralComponent('ModalsDialog');
@@ -1,4 +1,4 @@
1
- import { Atom, swap } from '@dbeining/react-atom';
1
+ import create from 'zustand';
2
2
  import { createElement, FC } from 'react';
3
3
  import { createModalsApi } from './create';
4
4
 
@@ -6,7 +6,11 @@ const StubComponent: FC = (props) => createElement('div', props);
6
6
  StubComponent.displayName = 'StubComponent';
7
7
 
8
8
  function createMockContainer() {
9
- const state = Atom.of({});
9
+ const state = create(() => ({
10
+ registry: {
11
+ extensions: {},
12
+ },
13
+ }));
10
14
  return {
11
15
  context: {
12
16
  on: jest.fn(),
@@ -19,7 +23,7 @@ function createMockContainer() {
19
23
  },
20
24
  state,
21
25
  dispatch(update) {
22
- swap(state, update);
26
+ state.setState(update(state.getState()));
23
27
  },
24
28
  } as any,
25
29
  api: {} as any,
package/src/create.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import * as actions from './actions';
2
2
  import { ComponentType } from 'react';
3
- import { withApi, buildName, PiralPlugin, Dict } from 'piral-core';
3
+ import { withApi, buildName, PiralPlugin, Dict, withRootExtension, withAll, GlobalState } from 'piral-core';
4
4
  import { DefaultHost, DefaultDialog } from './default';
5
- import { PiletModalsApi, ModalRegistration, BareModalComponentProps } from './types';
5
+ import { Modals } from './Modals';
6
+ import { PiletModalsApi, ModalRegistration, BareModalComponentProps, ModalLayoutOptions } from './types';
6
7
 
7
8
  export interface InitialModalDialog {
8
9
  /**
@@ -16,7 +17,11 @@ export interface InitialModalDialog {
16
17
  /**
17
18
  * The default options for the modal dialog.
18
19
  */
19
- defaults: any;
20
+ defaults?: any;
21
+ /**
22
+ * The layout options for the modal dialog.
23
+ */
24
+ layout?: ModalLayoutOptions;
20
25
  }
21
26
 
22
27
  /**
@@ -27,69 +32,84 @@ export interface ModalsConfig {
27
32
  * The initial modal dialogs.
28
33
  */
29
34
  dialogs?: Array<InitialModalDialog>;
35
+ /**
36
+ * Defines how the next ID for the key is selected.
37
+ * By default a random number is used.
38
+ *
39
+ * @param name The name of the modal dialog.
40
+ */
41
+ selectId?(name: string): string;
30
42
  }
31
43
 
32
44
  function getModalDialogs(dialogs: Array<InitialModalDialog>) {
33
45
  const modals: Dict<ModalRegistration> = {};
34
46
 
35
- for (const { name, component, defaults } of dialogs) {
47
+ for (const { name, component, defaults, layout = {} } of dialogs) {
36
48
  modals[`global-${name}`] = {
37
49
  pilet: undefined,
38
50
  name,
39
51
  component,
40
52
  defaults,
53
+ layout,
41
54
  };
42
55
  }
43
56
 
44
57
  return modals;
45
58
  }
46
59
 
60
+ function withModals(modals: Dict<ModalRegistration>) {
61
+ return (state: GlobalState): GlobalState => ({
62
+ ...state,
63
+ components: {
64
+ ModalsHost: DefaultHost,
65
+ ModalsDialog: DefaultDialog,
66
+ ...state.components,
67
+ },
68
+ registry: {
69
+ ...state.registry,
70
+ modals,
71
+ },
72
+ modals: [],
73
+ });
74
+ }
75
+
47
76
  /**
48
77
  * Creates new Pilet API extensions for support modal dialogs.
49
78
  */
50
79
  export function createModalsApi(config: ModalsConfig = {}): PiralPlugin<PiletModalsApi> {
51
- const { dialogs = [] } = config;
80
+ const { dialogs = [], selectId = (name) => `${name}-${~~(Math.random() * 10000)}` } = config;
52
81
 
53
82
  return (context) => {
54
83
  context.defineActions(actions);
55
84
 
56
- context.dispatch((state) => ({
57
- ...state,
58
- components: {
59
- ModalsHost: DefaultHost,
60
- ModalsDialog: DefaultDialog,
61
- ...state.components,
62
- },
63
- registry: {
64
- ...state.registry,
65
- modals: getModalDialogs(dialogs),
66
- },
67
- modals: [],
68
- }));
85
+ context.dispatch(withAll(withModals(getModalDialogs(dialogs)), withRootExtension('piral-modals', Modals)));
69
86
 
70
87
  return (api, target) => {
71
88
  const pilet = target.name;
72
89
 
73
90
  return {
74
- showModal(name, options) {
91
+ showModal(simpleName, options) {
92
+ const name = buildName(pilet, simpleName);
75
93
  const dialog = {
76
- name: buildName(pilet, name),
77
- alternative: name,
94
+ id: selectId(name),
95
+ name,
96
+ alternative: simpleName,
78
97
  options,
79
98
  close() {
80
- setTimeout(() => context.closeModal(dialog), 0);
99
+ context.closeModal(dialog);
81
100
  },
82
101
  };
83
102
  context.openModal(dialog);
84
103
  return dialog.close;
85
104
  },
86
- registerModal(name, arg, defaults) {
105
+ registerModal(name, arg, defaults, layout = {}) {
87
106
  const id = buildName(pilet, name);
88
107
  context.registerModal(id, {
89
108
  pilet,
90
109
  name,
91
110
  component: withApi(context, arg, api, 'modal'),
92
111
  defaults,
112
+ layout,
93
113
  });
94
114
  return () => api.unregisterModal(name);
95
115
  },
package/src/default.tsx CHANGED
@@ -3,7 +3,7 @@ import { defaultRender } from 'piral-core';
3
3
  import { ModalsHostProps, ModalsDialogProps } from './types';
4
4
 
5
5
  export const DefaultHost: React.FC<ModalsHostProps> = (props) => (
6
- <div className="piral-modals-host" key="default_modals">
6
+ <div className="piral-modals-host">
7
7
  {props.open && <div className="piral-modals-overlay">{props.children}</div>}
8
8
  </div>
9
9
  );
package/src/types.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { ComponentType } from 'react';
2
- import {
1
+ import type { ComponentType, ReactNode } from 'react';
2
+ import type {
3
3
  Dict,
4
4
  WrappedComponent,
5
5
  BaseComponentProps,
@@ -75,9 +75,31 @@ export interface ModalsHostProps {
75
75
  * Callback to invoke closing the modal dialog.
76
76
  */
77
77
  close(): void;
78
+ /**
79
+ * The dialogs to display.
80
+ */
81
+ children?: ReactNode;
78
82
  }
79
83
 
80
- export interface ModalsDialogProps extends OpenModalDialog {}
84
+ export interface ModalsDialogProps extends OpenModalDialog {
85
+ /**
86
+ * The layout options given for the current dialog.
87
+ */
88
+ layout: ModalLayoutOptions;
89
+ /**
90
+ * The provided default options.
91
+ */
92
+ defaults: any;
93
+ /**
94
+ * The content of the dialog to display.
95
+ */
96
+ children?: ReactNode;
97
+ }
98
+
99
+ /**
100
+ * The options provided for the dialog layout.
101
+ */
102
+ export interface ModalLayoutOptions {}
81
103
 
82
104
  /**
83
105
  * The error used when a registered modal dialog crashed.
@@ -95,9 +117,17 @@ export interface ModalErrorInfoProps {
95
117
  * Callback for closing the modal programmatically.
96
118
  */
97
119
  onClose(): void;
120
+ /**
121
+ * The name of the pilet emitting the error.
122
+ */
123
+ pilet?: string;
98
124
  }
99
125
 
100
126
  export interface OpenModalDialog {
127
+ /**
128
+ * Gets the ID of the modal to open. For tracking its state.
129
+ */
130
+ id: string;
101
131
  /**
102
132
  * Specifies the fully qualified name of the dialog to show.
103
133
  */
@@ -131,6 +161,7 @@ export interface ModalRegistration extends BaseRegistration {
131
161
  name: string;
132
162
  component: WrappedComponent<ModalComponentProps<any>>;
133
163
  defaults: any;
164
+ layout: ModalLayoutOptions;
134
165
  }
135
166
 
136
167
  export interface BaseModalOptions {}
@@ -162,11 +193,13 @@ export interface PiletModalsApi {
162
193
  * @param name The name of the modal to register.
163
194
  * @param Component The component to render the page.
164
195
  * @param defaults Optionally, sets the default values for the inserted options.
196
+ * @param layout Optionally, sets the layout options for the dialog wrapper.
165
197
  */
166
198
  registerModal<T>(
167
199
  name: T extends string ? T : string,
168
200
  Component: AnyComponent<ModalComponentProps<T>>,
169
201
  defaults?: ModalOptions<T>,
202
+ layout?: ModalLayoutOptions,
170
203
  ): RegistrationDisposer;
171
204
  /**
172
205
  * Unregisters a modal by its name.