@agnos-ui/core 0.0.1-alpha.0
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.
- package/README.md +15 -0
- package/dist/lib/accordion.d.ts +318 -0
- package/dist/lib/accordion.js +263 -0
- package/dist/lib/alert.d.ts +100 -0
- package/dist/lib/alert.js +66 -0
- package/dist/lib/config.d.ts +71 -0
- package/dist/lib/config.js +53 -0
- package/dist/lib/index.d.ts +11 -0
- package/dist/lib/index.js +11 -0
- package/dist/lib/modal/modal.d.ts +318 -0
- package/dist/lib/modal/modal.js +156 -0
- package/dist/lib/modal/scrollbars.d.ts +2 -0
- package/dist/lib/modal/scrollbars.js +27 -0
- package/dist/lib/pagination.d.ts +464 -0
- package/dist/lib/pagination.js +148 -0
- package/dist/lib/pagination.utils.d.ts +8 -0
- package/dist/lib/pagination.utils.js +110 -0
- package/dist/lib/rating.d.ts +209 -0
- package/dist/lib/rating.js +141 -0
- package/dist/lib/select.d.ts +199 -0
- package/dist/lib/select.js +240 -0
- package/dist/lib/services/checks.d.ts +32 -0
- package/dist/lib/services/checks.js +43 -0
- package/dist/lib/services/directiveUtils.d.ts +95 -0
- package/dist/lib/services/directiveUtils.js +190 -0
- package/dist/lib/services/focustrack.d.ts +19 -0
- package/dist/lib/services/focustrack.js +46 -0
- package/dist/lib/services/index.d.ts +6 -0
- package/dist/lib/services/index.js +6 -0
- package/dist/lib/services/portal.d.ts +6 -0
- package/dist/lib/services/portal.js +33 -0
- package/dist/lib/services/siblingsInert.d.ts +7 -0
- package/dist/lib/services/siblingsInert.js +40 -0
- package/dist/lib/services/stores.d.ts +140 -0
- package/dist/lib/services/stores.js +219 -0
- package/dist/lib/services/writables.d.ts +7 -0
- package/dist/lib/services/writables.js +16 -0
- package/dist/lib/transitions/baseTransitions.d.ts +136 -0
- package/dist/lib/transitions/baseTransitions.js +171 -0
- package/dist/lib/transitions/bootstrap/collapse.d.ts +2 -0
- package/dist/lib/transitions/bootstrap/collapse.js +15 -0
- package/dist/lib/transitions/bootstrap/fade.d.ts +1 -0
- package/dist/lib/transitions/bootstrap/fade.js +7 -0
- package/dist/lib/transitions/bootstrap/index.d.ts +2 -0
- package/dist/lib/transitions/bootstrap/index.js +2 -0
- package/dist/lib/transitions/collapse.d.ts +29 -0
- package/dist/lib/transitions/collapse.js +39 -0
- package/dist/lib/transitions/cssTransitions.d.ts +15 -0
- package/dist/lib/transitions/cssTransitions.js +38 -0
- package/dist/lib/transitions/index.d.ts +5 -0
- package/dist/lib/transitions/index.js +5 -0
- package/dist/lib/transitions/simpleClassTransition.d.ts +29 -0
- package/dist/lib/transitions/simpleClassTransition.js +28 -0
- package/dist/lib/transitions/utils.d.ts +20 -0
- package/dist/lib/transitions/utils.js +83 -0
- package/dist/lib/tsdoc-metadata.json +11 -0
- package/dist/lib/types.d.ts +58 -0
- package/dist/lib/types.js +7 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.js +2 -0
- package/package.json +52 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { bindDirectiveNoArg, stateStores, typeBoolean, typeString, writablesForProps } from './services';
|
|
2
|
+
import { createTransition } from './transitions';
|
|
3
|
+
import { fadeTransition } from './transitions/bootstrap';
|
|
4
|
+
import { noop } from './utils';
|
|
5
|
+
const defaultConfig = {
|
|
6
|
+
visible: true,
|
|
7
|
+
dismissible: true,
|
|
8
|
+
type: 'primary',
|
|
9
|
+
ariaCloseButtonLabel: 'Close',
|
|
10
|
+
onVisibleChange: noop,
|
|
11
|
+
onShown: noop,
|
|
12
|
+
onHidden: noop,
|
|
13
|
+
slotStructure: undefined,
|
|
14
|
+
slotDefault: undefined,
|
|
15
|
+
animation: true,
|
|
16
|
+
animationOnInit: false,
|
|
17
|
+
transition: fadeTransition,
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Retrieve a shallow copy of the default alert config
|
|
21
|
+
* @returns the default alert config
|
|
22
|
+
*/
|
|
23
|
+
export function getAlertDefaultConfig() {
|
|
24
|
+
return { ...defaultConfig };
|
|
25
|
+
}
|
|
26
|
+
const configValidator = {
|
|
27
|
+
dismissible: typeBoolean,
|
|
28
|
+
type: typeString,
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Create an AlertWidget with given config props
|
|
32
|
+
* @param config - an optional alert config
|
|
33
|
+
* @returns an AlertWidget
|
|
34
|
+
*/
|
|
35
|
+
export function createAlert(config) {
|
|
36
|
+
const [{ transition$, animationOnInit$, animation$, visible$: requestedVisible$, onVisibleChange$, onHidden$, onShown$, ...stateProps }, patch,] = writablesForProps(defaultConfig, config, configValidator);
|
|
37
|
+
const transition = createTransition({
|
|
38
|
+
transition: transition$,
|
|
39
|
+
visible: requestedVisible$,
|
|
40
|
+
animation: animation$,
|
|
41
|
+
animationOnInit: animationOnInit$,
|
|
42
|
+
onVisibleChange: onVisibleChange$,
|
|
43
|
+
onHidden: onHidden$,
|
|
44
|
+
onShown: onShown$,
|
|
45
|
+
});
|
|
46
|
+
const close = () => {
|
|
47
|
+
patch({ visible: false });
|
|
48
|
+
};
|
|
49
|
+
const open = () => {
|
|
50
|
+
patch({ visible: true });
|
|
51
|
+
};
|
|
52
|
+
const visible$ = transition.stores.visible$;
|
|
53
|
+
const hidden$ = transition.stores.hidden$;
|
|
54
|
+
return {
|
|
55
|
+
...stateStores({ ...stateProps, visible$, hidden$ }),
|
|
56
|
+
patch,
|
|
57
|
+
api: {
|
|
58
|
+
open,
|
|
59
|
+
close,
|
|
60
|
+
},
|
|
61
|
+
directives: {
|
|
62
|
+
transitionDirective: bindDirectiveNoArg(transition.directives.directive),
|
|
63
|
+
},
|
|
64
|
+
actions: {},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { ReadableSignal, WritableSignal } from '@amadeus-it-group/tansu';
|
|
2
|
+
import type { ModalProps } from './modal/modal';
|
|
3
|
+
import type { AlertProps } from './alert';
|
|
4
|
+
import type { PaginationProps } from './pagination';
|
|
5
|
+
import type { RatingProps } from './rating';
|
|
6
|
+
import type { SelectProps } from './select';
|
|
7
|
+
import type { AccordionProps } from './accordion';
|
|
8
|
+
export type Partial2Levels<T> = Partial<{
|
|
9
|
+
[Level1 in keyof T]: Partial<T[Level1]>;
|
|
10
|
+
}>;
|
|
11
|
+
export type WidgetsConfigStore<T> = WritableSignal<Partial2Levels<T>> & {
|
|
12
|
+
own$: WritableSignal<Partial2Levels<T>>;
|
|
13
|
+
parent$: undefined | WritableSignal<Partial2Levels<T>>;
|
|
14
|
+
adaptedParent$: undefined | ReadableSignal<Partial2Levels<T>>;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Merges source object into destination object, up to the provided number of levels.
|
|
18
|
+
* @param destination - destination object
|
|
19
|
+
* @param source - source object
|
|
20
|
+
* @param levels - number of levels to merge
|
|
21
|
+
* @returns the destination object in most cases, or the source in some cases (if the source is not undefined and either levels is smaller than 1
|
|
22
|
+
* or the source is not an object)
|
|
23
|
+
*/
|
|
24
|
+
export declare const mergeInto: <T>(destination: T, source: T | undefined, levels?: number) => T;
|
|
25
|
+
/**
|
|
26
|
+
* Creates a new widgets default configuration store, optionally inheriting from a parent store, and containing
|
|
27
|
+
* its own set of widgets configuration properties that override the same properties form the parent configuration.
|
|
28
|
+
*
|
|
29
|
+
* @remarks
|
|
30
|
+
* The resulting store has a value computed from the parent store in two steps:
|
|
31
|
+
* - first step: the parent configuration is transformed by the adaptParentConfig function (if specified).
|
|
32
|
+
* If adaptParentConfig is not specified, this step is skipped.
|
|
33
|
+
* - second step: the configuration from step 1 is merged (2-levels deep) with the own$ store. The own$ store initially contains
|
|
34
|
+
* an empty object (i.e. no property from the parent is overridden). It can be changed by calling set on the store returned by this function.
|
|
35
|
+
*
|
|
36
|
+
* @param parent$ - optional parent widgets default configuration store.
|
|
37
|
+
* @param adaptParentConfig - optional function that receives a 2-levels copy of the widgets default configuration
|
|
38
|
+
* from parent$ (or an empty object if parent$ is not specified) and returns the widgets default configuration to be used.
|
|
39
|
+
* It is called only if the configuration is needed, and was not yet computed for the current value of the parent configuration.
|
|
40
|
+
* It is called in a tansu reactive context, so it can use any tansu store and will be called again if those stores change.
|
|
41
|
+
* @returns the resulting widgets default configuration store, which contains 3 additional properties that are stores:
|
|
42
|
+
* parent$, adaptedParent$ (containing the value computed after the first step), and own$ (that contains only overridding properties).
|
|
43
|
+
* The resulting store is writable, its set function is actually the set function of the own$ store.
|
|
44
|
+
*/
|
|
45
|
+
export declare const createWidgetsConfig: <T>(parent$?: WidgetsConfigStore<T> | undefined, adaptParentConfig?: (config: Partial<{ [Level1 in keyof T]: Partial<T[Level1]>; }>) => Partial<{ [Level1 in keyof T]: Partial<T[Level1]>; }>) => WidgetsConfigStore<T>;
|
|
46
|
+
export interface WidgetsConfig {
|
|
47
|
+
/**
|
|
48
|
+
* the pagination widget config
|
|
49
|
+
*/
|
|
50
|
+
pagination: PaginationProps;
|
|
51
|
+
/**
|
|
52
|
+
* the rating widget config
|
|
53
|
+
*/
|
|
54
|
+
rating: RatingProps;
|
|
55
|
+
/**
|
|
56
|
+
* the select widget config
|
|
57
|
+
*/
|
|
58
|
+
select: SelectProps<any>;
|
|
59
|
+
/**
|
|
60
|
+
* the modal widget config
|
|
61
|
+
*/
|
|
62
|
+
modal: ModalProps;
|
|
63
|
+
/**
|
|
64
|
+
* the alert widget config
|
|
65
|
+
*/
|
|
66
|
+
alert: AlertProps;
|
|
67
|
+
/**
|
|
68
|
+
* the accordion widget config
|
|
69
|
+
*/
|
|
70
|
+
accordion: AccordionProps;
|
|
71
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { asReadable, computed, writable } from '@amadeus-it-group/tansu';
|
|
2
|
+
import { identity } from './utils';
|
|
3
|
+
/**
|
|
4
|
+
* Merges source object into destination object, up to the provided number of levels.
|
|
5
|
+
* @param destination - destination object
|
|
6
|
+
* @param source - source object
|
|
7
|
+
* @param levels - number of levels to merge
|
|
8
|
+
* @returns the destination object in most cases, or the source in some cases (if the source is not undefined and either levels is smaller than 1
|
|
9
|
+
* or the source is not an object)
|
|
10
|
+
*/
|
|
11
|
+
export const mergeInto = (destination, source, levels = Infinity) => {
|
|
12
|
+
if (source !== undefined) {
|
|
13
|
+
if (typeof source === 'object' && source && levels >= 1) {
|
|
14
|
+
for (const key of Object.keys(source)) {
|
|
15
|
+
destination[key] = mergeInto(destination[key] ?? {}, source[key], levels - 1);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
return source;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return destination;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Creates a new widgets default configuration store, optionally inheriting from a parent store, and containing
|
|
26
|
+
* its own set of widgets configuration properties that override the same properties form the parent configuration.
|
|
27
|
+
*
|
|
28
|
+
* @remarks
|
|
29
|
+
* The resulting store has a value computed from the parent store in two steps:
|
|
30
|
+
* - first step: the parent configuration is transformed by the adaptParentConfig function (if specified).
|
|
31
|
+
* If adaptParentConfig is not specified, this step is skipped.
|
|
32
|
+
* - second step: the configuration from step 1 is merged (2-levels deep) with the own$ store. The own$ store initially contains
|
|
33
|
+
* an empty object (i.e. no property from the parent is overridden). It can be changed by calling set on the store returned by this function.
|
|
34
|
+
*
|
|
35
|
+
* @param parent$ - optional parent widgets default configuration store.
|
|
36
|
+
* @param adaptParentConfig - optional function that receives a 2-levels copy of the widgets default configuration
|
|
37
|
+
* from parent$ (or an empty object if parent$ is not specified) and returns the widgets default configuration to be used.
|
|
38
|
+
* It is called only if the configuration is needed, and was not yet computed for the current value of the parent configuration.
|
|
39
|
+
* It is called in a tansu reactive context, so it can use any tansu store and will be called again if those stores change.
|
|
40
|
+
* @returns the resulting widgets default configuration store, which contains 3 additional properties that are stores:
|
|
41
|
+
* parent$, adaptedParent$ (containing the value computed after the first step), and own$ (that contains only overridding properties).
|
|
42
|
+
* The resulting store is writable, its set function is actually the set function of the own$ store.
|
|
43
|
+
*/
|
|
44
|
+
export const createWidgetsConfig = (parent$, adaptParentConfig = identity) => {
|
|
45
|
+
const own$ = writable({});
|
|
46
|
+
const adaptedParent$ = adaptParentConfig === identity ? parent$ : computed(() => adaptParentConfig(mergeInto({}, parent$?.(), 2)));
|
|
47
|
+
return asReadable(computed(() => mergeInto(mergeInto({}, adaptedParent$?.(), 2), own$(), 2)), {
|
|
48
|
+
...own$,
|
|
49
|
+
own$,
|
|
50
|
+
adaptedParent$,
|
|
51
|
+
parent$,
|
|
52
|
+
});
|
|
53
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './types';
|
|
2
|
+
export * from './select';
|
|
3
|
+
export * from './services';
|
|
4
|
+
export * from './transitions';
|
|
5
|
+
export * from './rating';
|
|
6
|
+
export * from './pagination';
|
|
7
|
+
export * from './pagination.utils';
|
|
8
|
+
export * from './config';
|
|
9
|
+
export * from './modal/modal';
|
|
10
|
+
export * from './alert';
|
|
11
|
+
export * from './accordion';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './types';
|
|
2
|
+
export * from './select';
|
|
3
|
+
export * from './services';
|
|
4
|
+
export * from './transitions';
|
|
5
|
+
export * from './rating';
|
|
6
|
+
export * from './pagination';
|
|
7
|
+
export * from './pagination.utils';
|
|
8
|
+
export * from './config';
|
|
9
|
+
export * from './modal/modal';
|
|
10
|
+
export * from './alert';
|
|
11
|
+
export * from './accordion';
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import type { PropsConfig } from '../services';
|
|
2
|
+
import type { TransitionFn } from '../transitions';
|
|
3
|
+
import type { Widget, Directive, SlotContent, WidgetSlotContext } from '../types';
|
|
4
|
+
/**
|
|
5
|
+
* Value present in the {@link ModalBeforeCloseEvent.result|result} property of the {@link ModalProps.onBeforeClose|onBeforeClose} event
|
|
6
|
+
* and returned by the {@link ModalApi.open|open} method, when the modal is closed by a click inside the viewport but outside the modal.
|
|
7
|
+
*/
|
|
8
|
+
export declare const modalOutsideClick: unique symbol;
|
|
9
|
+
/**
|
|
10
|
+
* Value present in the {@link ModalBeforeCloseEvent.result|result} property of the {@link ModalProps.onBeforeClose|onBeforeClose} event
|
|
11
|
+
* and returned by the {@link ModalApi.open|open} method, when the modal is closed by a click on the close button.
|
|
12
|
+
*/
|
|
13
|
+
export declare const modalCloseButtonClick: unique symbol;
|
|
14
|
+
/**
|
|
15
|
+
* Context of the modal slots.
|
|
16
|
+
*/
|
|
17
|
+
export type ModalContext = WidgetSlotContext<ModalWidget>;
|
|
18
|
+
/**
|
|
19
|
+
* Properties of the modal widget that are also in the state of the modal.
|
|
20
|
+
*/
|
|
21
|
+
export interface ModalCommonPropsAndState {
|
|
22
|
+
/**
|
|
23
|
+
* Value of the aria-label attribute to put on the close button.
|
|
24
|
+
*/
|
|
25
|
+
ariaCloseButtonLabel: string;
|
|
26
|
+
/**
|
|
27
|
+
* Classes to add on the backdrop DOM element.
|
|
28
|
+
*/
|
|
29
|
+
backdropClass: string;
|
|
30
|
+
/**
|
|
31
|
+
* Whether to display the close button.
|
|
32
|
+
*/
|
|
33
|
+
closeButton: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Which element should contain the modal and backdrop DOM elements.
|
|
36
|
+
* If it is not null, the modal and backdrop DOM elements are moved to the specified container.
|
|
37
|
+
* Otherwise, they stay where the widget is located.
|
|
38
|
+
*/
|
|
39
|
+
container: HTMLElement | null;
|
|
40
|
+
/**
|
|
41
|
+
* Classes to add on the modal DOM element.
|
|
42
|
+
*/
|
|
43
|
+
modalClass: string;
|
|
44
|
+
/**
|
|
45
|
+
* Body of the modal.
|
|
46
|
+
*/
|
|
47
|
+
slotDefault: SlotContent<ModalContext>;
|
|
48
|
+
/**
|
|
49
|
+
* Footer of the modal.
|
|
50
|
+
*/
|
|
51
|
+
slotFooter: SlotContent<ModalContext>;
|
|
52
|
+
/**
|
|
53
|
+
* Header of the modal. The default header includes {@link ModalCommonPropsAndState.slotTitle|slotTitle}.
|
|
54
|
+
*/
|
|
55
|
+
slotHeader: SlotContent<ModalContext>;
|
|
56
|
+
/**
|
|
57
|
+
* Structure of the modal.
|
|
58
|
+
* The default structure uses {@link ModalCommonPropsAndState.slotHeader|slotHeader}, {@link ModalCommonPropsAndState.slotDefault|slotDefault} and {@link ModalCommonPropsAndState.slotFooter|slotFooter}.
|
|
59
|
+
*/
|
|
60
|
+
slotStructure: SlotContent<ModalContext>;
|
|
61
|
+
/**
|
|
62
|
+
* Title of the modal.
|
|
63
|
+
*/
|
|
64
|
+
slotTitle: SlotContent<ModalContext>;
|
|
65
|
+
/**
|
|
66
|
+
* Whether the modal should be visible when the transition is completed.
|
|
67
|
+
*/
|
|
68
|
+
visible: boolean;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Type of the parameter of {@link ModalProps.onBeforeClose|onBeforeClose}.
|
|
72
|
+
*/
|
|
73
|
+
export interface ModalBeforeCloseEvent {
|
|
74
|
+
/**
|
|
75
|
+
* Result of the modal, which is the value passed to the {@link ModalApi.close|close} method
|
|
76
|
+
* and later resolved by the promise returned by the {@link ModalApi.open|open} method.
|
|
77
|
+
* If needed, it can be changed from the {@link ModalProps.onBeforeClose|onBeforeClose} event handler.
|
|
78
|
+
*/
|
|
79
|
+
result: any;
|
|
80
|
+
/**
|
|
81
|
+
* Whether to cancel the close of the modal.
|
|
82
|
+
* It can be changed from the {@link ModalProps.onBeforeClose|onBeforeClose} event handler.
|
|
83
|
+
*/
|
|
84
|
+
cancel: boolean;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Properties of the modal widget.
|
|
88
|
+
*/
|
|
89
|
+
export interface ModalProps extends ModalCommonPropsAndState {
|
|
90
|
+
/**
|
|
91
|
+
* Whether the modal and its backdrop (if present) should be animated when shown or hidden.
|
|
92
|
+
*/
|
|
93
|
+
animation: boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Whether a backdrop should be created behind the modal.
|
|
96
|
+
*/
|
|
97
|
+
backdrop: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* The transition to use for the backdrop behind the modal (if present).
|
|
100
|
+
*/
|
|
101
|
+
backdropTransition: TransitionFn;
|
|
102
|
+
/**
|
|
103
|
+
* Whether the modal should be closed when clicking on the viewport outside the modal.
|
|
104
|
+
*/
|
|
105
|
+
closeOnOutsideClick: boolean;
|
|
106
|
+
/**
|
|
107
|
+
* The transition to use for the modal.
|
|
108
|
+
*/
|
|
109
|
+
modalTransition: TransitionFn;
|
|
110
|
+
/**
|
|
111
|
+
* Event to be triggered when the modal is about to be closed (i.e. the {@link ModalApi.close|close} method was called).
|
|
112
|
+
*
|
|
113
|
+
* @param event - event giving access to the argument given to the {@link ModalApi.close|close} method and allowing
|
|
114
|
+
* to cancel the close process.
|
|
115
|
+
*/
|
|
116
|
+
onBeforeClose: (event: ModalBeforeCloseEvent) => void;
|
|
117
|
+
/**
|
|
118
|
+
* Event to be triggered when the visible property changes.
|
|
119
|
+
*
|
|
120
|
+
* @param visible - new value of the visible propery
|
|
121
|
+
*/
|
|
122
|
+
onVisibleChange: (visible: boolean) => void;
|
|
123
|
+
/**
|
|
124
|
+
* Event to be triggered when the transition is completed and the modal is not visible.
|
|
125
|
+
*/
|
|
126
|
+
onHidden: () => void;
|
|
127
|
+
/**
|
|
128
|
+
* Event to be triggered when the transition is completed and the modal is visible.
|
|
129
|
+
*/
|
|
130
|
+
onShown: () => void;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* State of the modal widget.
|
|
134
|
+
*/
|
|
135
|
+
export interface ModalState extends ModalCommonPropsAndState {
|
|
136
|
+
/**
|
|
137
|
+
* Whether the backdrop is fully hidden. This can be true either because {@link ModalProps.backdrop|backdrop} is false or
|
|
138
|
+
* because {@link ModalCommonPropsAndState.visible|visible} is false and there is no current transition.
|
|
139
|
+
*/
|
|
140
|
+
backdropHidden: boolean;
|
|
141
|
+
/**
|
|
142
|
+
* Whether the modal is fully hidden.
|
|
143
|
+
*/
|
|
144
|
+
hidden: boolean;
|
|
145
|
+
/**
|
|
146
|
+
* Whether there is an active transition to either display or hide the modal.
|
|
147
|
+
*/
|
|
148
|
+
transitioning: boolean;
|
|
149
|
+
/**
|
|
150
|
+
* DOM element of the modal.
|
|
151
|
+
*/
|
|
152
|
+
modalElement: HTMLElement | null;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* API of the modal widget.
|
|
156
|
+
*/
|
|
157
|
+
export interface ModalApi {
|
|
158
|
+
/**
|
|
159
|
+
* Closes the modal with the given result.
|
|
160
|
+
*
|
|
161
|
+
* @param result - result of the modal, as passed in the {@link ModalBeforeCloseEvent.result|result} property of the event passed to the
|
|
162
|
+
* {@link ModalProps.onBeforeClose|onBeforeClose} event handler (and possibly changed by it) and resolved by the promise returned by the {@link ModalApi.open|open} method.
|
|
163
|
+
*/
|
|
164
|
+
close(result?: any): void;
|
|
165
|
+
/**
|
|
166
|
+
* Opens the modal and returns a promise that is resolved when the modal is closed.
|
|
167
|
+
* The resolved value is the result passed to the {@link ModalApi.close|close} method and possibly changed by the
|
|
168
|
+
* {@link ModalProps.onBeforeClose|onBeforeClose} event handler
|
|
169
|
+
*/
|
|
170
|
+
open(): Promise<any>;
|
|
171
|
+
/**
|
|
172
|
+
* Method to change some modal properties.
|
|
173
|
+
*/
|
|
174
|
+
patch: ModalWidget['patch'];
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Actions of the modal widget.
|
|
178
|
+
*/
|
|
179
|
+
export interface ModalActions {
|
|
180
|
+
/**
|
|
181
|
+
* Action to be called when the user clicks on the close button. It closes the modal with the {@link modalCloseButtonClick} result.
|
|
182
|
+
* @param event - mouse event
|
|
183
|
+
*/
|
|
184
|
+
closeButtonClick(event: Pick<MouseEvent, never>): void;
|
|
185
|
+
/**
|
|
186
|
+
* Action to be called when the user clicks on the modal DOM element (which is supposed to have the size of the full viewport).
|
|
187
|
+
* If the click is not done on a descendant of the modal DOM element, it is considered to be done outside the modal
|
|
188
|
+
* and, depending on the value of the {@link ModalProps.closeOnOutsideClick|closeOnOutsideClick} prop, the modal is or isn't closed
|
|
189
|
+
* (with the {@link modalOutsideClick} result).
|
|
190
|
+
* @param event - mouse event
|
|
191
|
+
*/
|
|
192
|
+
modalClick(event: Pick<MouseEvent, 'target' | 'currentTarget'>): void;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Directives of the modal widget.
|
|
196
|
+
*/
|
|
197
|
+
export interface ModalDirectives {
|
|
198
|
+
/**
|
|
199
|
+
* Directive to put on the modal DOM element.
|
|
200
|
+
*/
|
|
201
|
+
modalDirective: Directive;
|
|
202
|
+
/**
|
|
203
|
+
* Directive to put on the backdrop DOM element.
|
|
204
|
+
*/
|
|
205
|
+
backdropDirective: Directive;
|
|
206
|
+
/**
|
|
207
|
+
* Portal directive to put on the modal DOM element.
|
|
208
|
+
*/
|
|
209
|
+
modalPortalDirective: Directive;
|
|
210
|
+
/**
|
|
211
|
+
* Portal directive to put on the backdrop DOM element.
|
|
212
|
+
*/
|
|
213
|
+
backdropPortalDirective: Directive;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Modal widget.
|
|
217
|
+
*/
|
|
218
|
+
export type ModalWidget = Widget<ModalProps, ModalState, ModalApi, ModalActions, ModalDirectives>;
|
|
219
|
+
/**
|
|
220
|
+
* Returns a copy of the default modal config.
|
|
221
|
+
* @returns a copy of the default modal config
|
|
222
|
+
*/
|
|
223
|
+
export declare function getModalDefaultConfig(): {
|
|
224
|
+
/**
|
|
225
|
+
* Whether the modal and its backdrop (if present) should be animated when shown or hidden.
|
|
226
|
+
*/
|
|
227
|
+
animation: boolean;
|
|
228
|
+
/**
|
|
229
|
+
* Whether a backdrop should be created behind the modal.
|
|
230
|
+
*/
|
|
231
|
+
backdrop: boolean;
|
|
232
|
+
/**
|
|
233
|
+
* The transition to use for the backdrop behind the modal (if present).
|
|
234
|
+
*/
|
|
235
|
+
backdropTransition: TransitionFn;
|
|
236
|
+
/**
|
|
237
|
+
* Whether the modal should be closed when clicking on the viewport outside the modal.
|
|
238
|
+
*/
|
|
239
|
+
closeOnOutsideClick: boolean;
|
|
240
|
+
/**
|
|
241
|
+
* The transition to use for the modal.
|
|
242
|
+
*/
|
|
243
|
+
modalTransition: TransitionFn;
|
|
244
|
+
/**
|
|
245
|
+
* Event to be triggered when the modal is about to be closed (i.e. the {@link ModalApi.close|close} method was called).
|
|
246
|
+
*
|
|
247
|
+
* @param event - event giving access to the argument given to the {@link ModalApi.close|close} method and allowing
|
|
248
|
+
* to cancel the close process.
|
|
249
|
+
*/
|
|
250
|
+
onBeforeClose: (event: ModalBeforeCloseEvent) => void;
|
|
251
|
+
/**
|
|
252
|
+
* Event to be triggered when the visible property changes.
|
|
253
|
+
*
|
|
254
|
+
* @param visible - new value of the visible propery
|
|
255
|
+
*/
|
|
256
|
+
onVisibleChange: (visible: boolean) => void;
|
|
257
|
+
/**
|
|
258
|
+
* Event to be triggered when the transition is completed and the modal is not visible.
|
|
259
|
+
*/
|
|
260
|
+
onHidden: () => void;
|
|
261
|
+
/**
|
|
262
|
+
* Event to be triggered when the transition is completed and the modal is visible.
|
|
263
|
+
*/
|
|
264
|
+
onShown: () => void;
|
|
265
|
+
/**
|
|
266
|
+
* Value of the aria-label attribute to put on the close button.
|
|
267
|
+
*/
|
|
268
|
+
ariaCloseButtonLabel: string;
|
|
269
|
+
/**
|
|
270
|
+
* Classes to add on the backdrop DOM element.
|
|
271
|
+
*/
|
|
272
|
+
backdropClass: string;
|
|
273
|
+
/**
|
|
274
|
+
* Whether to display the close button.
|
|
275
|
+
*/
|
|
276
|
+
closeButton: boolean;
|
|
277
|
+
/**
|
|
278
|
+
* Which element should contain the modal and backdrop DOM elements.
|
|
279
|
+
* If it is not null, the modal and backdrop DOM elements are moved to the specified container.
|
|
280
|
+
* Otherwise, they stay where the widget is located.
|
|
281
|
+
*/
|
|
282
|
+
container: HTMLElement | null;
|
|
283
|
+
/**
|
|
284
|
+
* Classes to add on the modal DOM element.
|
|
285
|
+
*/
|
|
286
|
+
modalClass: string;
|
|
287
|
+
/**
|
|
288
|
+
* Body of the modal.
|
|
289
|
+
*/
|
|
290
|
+
slotDefault: SlotContent<ModalContext>;
|
|
291
|
+
/**
|
|
292
|
+
* Footer of the modal.
|
|
293
|
+
*/
|
|
294
|
+
slotFooter: SlotContent<ModalContext>;
|
|
295
|
+
/**
|
|
296
|
+
* Header of the modal. The default header includes {@link ModalCommonPropsAndState.slotTitle|slotTitle}.
|
|
297
|
+
*/
|
|
298
|
+
slotHeader: SlotContent<ModalContext>;
|
|
299
|
+
/**
|
|
300
|
+
* Structure of the modal.
|
|
301
|
+
* The default structure uses {@link ModalCommonPropsAndState.slotHeader|slotHeader}, {@link ModalCommonPropsAndState.slotDefault|slotDefault} and {@link ModalCommonPropsAndState.slotFooter|slotFooter}.
|
|
302
|
+
*/
|
|
303
|
+
slotStructure: SlotContent<ModalContext>;
|
|
304
|
+
/**
|
|
305
|
+
* Title of the modal.
|
|
306
|
+
*/
|
|
307
|
+
slotTitle: SlotContent<ModalContext>;
|
|
308
|
+
/**
|
|
309
|
+
* Whether the modal should be visible when the transition is completed.
|
|
310
|
+
*/
|
|
311
|
+
visible: boolean;
|
|
312
|
+
};
|
|
313
|
+
/**
|
|
314
|
+
* Creates a new modal widget instance.
|
|
315
|
+
* @param config$ - config of the modal, either as a store or as an object containing values or stores.
|
|
316
|
+
* @returns a new modal widget instance
|
|
317
|
+
*/
|
|
318
|
+
export declare const createModal: (config$?: PropsConfig<ModalProps>) => ModalWidget;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { computed, readable } from '@amadeus-it-group/tansu';
|
|
2
|
+
import { sliblingsInert, bindDirective, bindDirectiveNoArg, directiveSubscribe, mergeDirectives, portal, registrationArray, stateStores, writablesForProps, } from '../services';
|
|
3
|
+
import { createTransition } from '../transitions';
|
|
4
|
+
import { fadeTransition } from '../transitions/bootstrap/fade';
|
|
5
|
+
import { promiseFromStore } from '../transitions/utils';
|
|
6
|
+
import { noop } from '../utils';
|
|
7
|
+
import { removeScrollbars, revertScrollbars } from './scrollbars';
|
|
8
|
+
/**
|
|
9
|
+
* Value present in the {@link ModalBeforeCloseEvent.result|result} property of the {@link ModalProps.onBeforeClose|onBeforeClose} event
|
|
10
|
+
* and returned by the {@link ModalApi.open|open} method, when the modal is closed by a click inside the viewport but outside the modal.
|
|
11
|
+
*/
|
|
12
|
+
export const modalOutsideClick = Symbol();
|
|
13
|
+
/**
|
|
14
|
+
* Value present in the {@link ModalBeforeCloseEvent.result|result} property of the {@link ModalProps.onBeforeClose|onBeforeClose} event
|
|
15
|
+
* and returned by the {@link ModalApi.open|open} method, when the modal is closed by a click on the close button.
|
|
16
|
+
*/
|
|
17
|
+
export const modalCloseButtonClick = Symbol();
|
|
18
|
+
const defaultConfig = {
|
|
19
|
+
animation: true,
|
|
20
|
+
ariaCloseButtonLabel: 'Close',
|
|
21
|
+
backdrop: true,
|
|
22
|
+
backdropClass: '',
|
|
23
|
+
backdropTransition: fadeTransition,
|
|
24
|
+
closeButton: true,
|
|
25
|
+
closeOnOutsideClick: true,
|
|
26
|
+
container: typeof window !== 'undefined' ? document.body : null,
|
|
27
|
+
modalClass: '',
|
|
28
|
+
modalTransition: fadeTransition,
|
|
29
|
+
onBeforeClose: noop,
|
|
30
|
+
onVisibleChange: noop,
|
|
31
|
+
onHidden: noop,
|
|
32
|
+
onShown: noop,
|
|
33
|
+
slotDefault: undefined,
|
|
34
|
+
slotFooter: undefined,
|
|
35
|
+
slotHeader: undefined,
|
|
36
|
+
slotStructure: undefined,
|
|
37
|
+
slotTitle: undefined,
|
|
38
|
+
visible: false,
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Returns a copy of the default modal config.
|
|
42
|
+
* @returns a copy of the default modal config
|
|
43
|
+
*/
|
|
44
|
+
export function getModalDefaultConfig() {
|
|
45
|
+
return { ...defaultConfig };
|
|
46
|
+
}
|
|
47
|
+
const modals$ = registrationArray();
|
|
48
|
+
const hasModals$ = computed(() => modals$().length > 0);
|
|
49
|
+
const scrollbarsAction$ = computed(() => {
|
|
50
|
+
if (hasModals$()) {
|
|
51
|
+
removeScrollbars();
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
revertScrollbars();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
const modalsAction$ = computed(() => {
|
|
58
|
+
scrollbarsAction$();
|
|
59
|
+
});
|
|
60
|
+
/**
|
|
61
|
+
* Creates a new modal widget instance.
|
|
62
|
+
* @param config$ - config of the modal, either as a store or as an object containing values or stores.
|
|
63
|
+
* @returns a new modal widget instance
|
|
64
|
+
*/
|
|
65
|
+
export const createModal = (config$) => {
|
|
66
|
+
const [{ animation$, backdrop$, backdropTransition$, closeOnOutsideClick$, container$, modalTransition$, onBeforeClose$, onVisibleChange$, onHidden$, onShown$, visible$: requestedVisible$, ...stateProps }, patch,] = writablesForProps(defaultConfig, config$);
|
|
67
|
+
const modalTransition = createTransition({
|
|
68
|
+
transition: modalTransition$,
|
|
69
|
+
visible: requestedVisible$,
|
|
70
|
+
animation: animation$,
|
|
71
|
+
animationOnInit: animation$,
|
|
72
|
+
onVisibleChange: onVisibleChange$,
|
|
73
|
+
// TODO: for onHidden and onShown, should we combine with information from the backdrop transition?
|
|
74
|
+
// (especially in case one of the two transitions takes more time than the other)
|
|
75
|
+
onHidden: onHidden$,
|
|
76
|
+
onShown: onShown$,
|
|
77
|
+
});
|
|
78
|
+
const visible$ = modalTransition.stores.visible$;
|
|
79
|
+
const backdropTransition = createTransition({
|
|
80
|
+
transition: backdropTransition$,
|
|
81
|
+
visible: requestedVisible$,
|
|
82
|
+
animation: animation$,
|
|
83
|
+
animationOnInit: animation$,
|
|
84
|
+
});
|
|
85
|
+
const transitioning$ = computed(() => modalTransition.stores.transitioning$() || (backdrop$() && backdropTransition.stores.transitioning$()));
|
|
86
|
+
const hidden$ = computed(() => !transitioning$() && !visible$());
|
|
87
|
+
const backdropHidden$ = computed(() => !backdrop$() || hidden$());
|
|
88
|
+
let hideResult;
|
|
89
|
+
const close = (result) => {
|
|
90
|
+
hideResult = result;
|
|
91
|
+
const beforeCloseEvent = {
|
|
92
|
+
get result() {
|
|
93
|
+
return hideResult;
|
|
94
|
+
},
|
|
95
|
+
set result(value) {
|
|
96
|
+
hideResult = value;
|
|
97
|
+
},
|
|
98
|
+
cancel: false,
|
|
99
|
+
};
|
|
100
|
+
onBeforeClose$()(beforeCloseEvent);
|
|
101
|
+
if (beforeCloseEvent.cancel) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
patch({ visible: false });
|
|
105
|
+
};
|
|
106
|
+
const modalPortalDirective = bindDirective(portal, computed(() => ({ container: container$() })));
|
|
107
|
+
const backdropPortalDirective = bindDirective(portal, computed(() => ({
|
|
108
|
+
container: container$(),
|
|
109
|
+
insertBefore: container$() && modalTransition.stores.element$()?.parentElement === container$() ? modalTransition.stores.element$() : undefined,
|
|
110
|
+
})));
|
|
111
|
+
const registerModalAction$ = readable(undefined, () => modals$.register(res));
|
|
112
|
+
const action$ = computed(() => {
|
|
113
|
+
if (modalTransition.stores.elementPresent$() && !hidden$()) {
|
|
114
|
+
registerModalAction$();
|
|
115
|
+
}
|
|
116
|
+
modalsAction$();
|
|
117
|
+
});
|
|
118
|
+
const res = {
|
|
119
|
+
...stateStores({
|
|
120
|
+
backdropHidden$,
|
|
121
|
+
container$,
|
|
122
|
+
hidden$,
|
|
123
|
+
transitioning$,
|
|
124
|
+
visible$,
|
|
125
|
+
modalElement$: modalTransition.stores.element$,
|
|
126
|
+
...stateProps,
|
|
127
|
+
}),
|
|
128
|
+
directives: {
|
|
129
|
+
modalPortalDirective,
|
|
130
|
+
backdropPortalDirective,
|
|
131
|
+
backdropDirective: bindDirectiveNoArg(backdropTransition.directives.directive),
|
|
132
|
+
modalDirective: mergeDirectives(bindDirectiveNoArg(modalTransition.directives.directive), sliblingsInert, directiveSubscribe(action$)),
|
|
133
|
+
},
|
|
134
|
+
patch,
|
|
135
|
+
api: {
|
|
136
|
+
close,
|
|
137
|
+
async open() {
|
|
138
|
+
patch({ visible: true });
|
|
139
|
+
await promiseFromStore(hidden$).promise;
|
|
140
|
+
return hideResult;
|
|
141
|
+
},
|
|
142
|
+
patch,
|
|
143
|
+
},
|
|
144
|
+
actions: {
|
|
145
|
+
modalClick(event) {
|
|
146
|
+
if (event.currentTarget === event.target && closeOnOutsideClick$()) {
|
|
147
|
+
close(modalOutsideClick);
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
closeButtonClick(event) {
|
|
151
|
+
close(modalCloseButtonClick);
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
return res;
|
|
156
|
+
};
|