@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,171 @@
|
|
|
1
|
+
import { batch, computed, derived, writable } from '@amadeus-it-group/tansu';
|
|
2
|
+
import { bindableDerived, createStoreDirective, directiveSubscribe, directiveUpdate, mergeDirectives, stateStores, writablesForProps, } from '../services';
|
|
3
|
+
import { typeBoolean, typeFunction } from '../services/writables';
|
|
4
|
+
const noop = () => { };
|
|
5
|
+
const neverEndingPromise = new Promise(noop);
|
|
6
|
+
export const noAnimation = async (element, direction) => {
|
|
7
|
+
element.style.display = direction === 'show' ? '' : 'none';
|
|
8
|
+
};
|
|
9
|
+
const defaultValues = {
|
|
10
|
+
animation: true,
|
|
11
|
+
animationOnInit: false,
|
|
12
|
+
initDone: null,
|
|
13
|
+
visible: true,
|
|
14
|
+
transition: noAnimation,
|
|
15
|
+
onShown: noop,
|
|
16
|
+
onHidden: noop,
|
|
17
|
+
onVisibleChange: noop,
|
|
18
|
+
};
|
|
19
|
+
const configValidator = {
|
|
20
|
+
animation: typeBoolean,
|
|
21
|
+
animationOnInit: typeBoolean,
|
|
22
|
+
visible: typeBoolean,
|
|
23
|
+
transition: typeFunction,
|
|
24
|
+
onShown: typeFunction,
|
|
25
|
+
onHidden: typeFunction,
|
|
26
|
+
};
|
|
27
|
+
const promiseWithResolve = () => {
|
|
28
|
+
let resolve;
|
|
29
|
+
const promise = new Promise((r) => {
|
|
30
|
+
resolve = r;
|
|
31
|
+
});
|
|
32
|
+
return { promise, resolve: resolve };
|
|
33
|
+
};
|
|
34
|
+
export const createTransition = (config) => {
|
|
35
|
+
const [{ animation$, initDone$, visible$: requestedVisible$, transition$, onShown$, onHidden$, onVisibleChange$, animationOnInit$ }, patch] = writablesForProps(defaultValues, config, configValidator);
|
|
36
|
+
const { element$, directive: storeDirective } = createStoreDirective();
|
|
37
|
+
const elementPresent$ = computed(() => !!element$());
|
|
38
|
+
const visible$ = bindableDerived(onVisibleChange$, [requestedVisible$], ([visible]) => visible);
|
|
39
|
+
const currentTransition$ = writable(null);
|
|
40
|
+
const transitioning$ = computed(() => !!currentTransition$());
|
|
41
|
+
const stop = () => {
|
|
42
|
+
let context;
|
|
43
|
+
currentTransition$.update((currentTransition) => {
|
|
44
|
+
currentTransition?.abort.abort();
|
|
45
|
+
context = currentTransition?.context;
|
|
46
|
+
return null;
|
|
47
|
+
});
|
|
48
|
+
return context;
|
|
49
|
+
};
|
|
50
|
+
const runTransition = (visible, animation, element, transitionFn) => batch(() => {
|
|
51
|
+
const abort = new AbortController();
|
|
52
|
+
const signal = abort.signal;
|
|
53
|
+
const context = stop() ?? {};
|
|
54
|
+
const { promise, resolve } = promiseWithResolve();
|
|
55
|
+
const currentTransition = {
|
|
56
|
+
abort,
|
|
57
|
+
animation,
|
|
58
|
+
visible,
|
|
59
|
+
context,
|
|
60
|
+
element,
|
|
61
|
+
transitionFn,
|
|
62
|
+
promise,
|
|
63
|
+
};
|
|
64
|
+
currentTransition$.set(currentTransition);
|
|
65
|
+
resolve((async () => {
|
|
66
|
+
try {
|
|
67
|
+
await transitionFn(element, visible ? 'show' : 'hide', animation, signal, context);
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
if (signal.aborted) {
|
|
71
|
+
await neverEndingPromise;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
currentTransition$.set(null);
|
|
75
|
+
(visible ? onShown$ : onHidden$)()?.();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
})());
|
|
79
|
+
return currentTransition;
|
|
80
|
+
});
|
|
81
|
+
const shown$ = computed(() => !transitioning$() && visible$() && elementPresent$());
|
|
82
|
+
const hidden$ = computed(() => !transitioning$() && !visible$());
|
|
83
|
+
const effectiveAnimation$ = computed(() => (initDone$() ? animation$() : animationOnInit$()));
|
|
84
|
+
const animationFromToggle$ = writable(null);
|
|
85
|
+
let previousElement;
|
|
86
|
+
let previousVisible = requestedVisible$();
|
|
87
|
+
let pendingTransition = null;
|
|
88
|
+
const visibleAction$ = derived([visible$, element$, effectiveAnimation$, animationFromToggle$, transition$, currentTransition$], ([visible, element, animation, animationFromToggle, transition, currentTransition]) => {
|
|
89
|
+
const elementChanged = previousElement !== element;
|
|
90
|
+
previousElement = element;
|
|
91
|
+
const visibleChanged = previousVisible !== visible;
|
|
92
|
+
previousVisible = visible;
|
|
93
|
+
if (element) {
|
|
94
|
+
if (initDone$() == null) {
|
|
95
|
+
initDone$.set(true);
|
|
96
|
+
}
|
|
97
|
+
const interruptAnimation = animationFromToggle != null && currentTransition && currentTransition.animation != animationFromToggle;
|
|
98
|
+
if (elementChanged || visibleChanged || interruptAnimation) {
|
|
99
|
+
if (visibleChanged || animationFromToggle != null) {
|
|
100
|
+
pendingTransition = null;
|
|
101
|
+
}
|
|
102
|
+
const animate = animationFromToggle ?? pendingTransition?.animation ?? (elementChanged && !visible ? false : animation);
|
|
103
|
+
currentTransition = runTransition(visible, animate, element, transition);
|
|
104
|
+
pendingTransition?.resolve(currentTransition.promise);
|
|
105
|
+
pendingTransition = null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
if (elementChanged) {
|
|
110
|
+
// just removed from the DOM: stop animation if any
|
|
111
|
+
stop();
|
|
112
|
+
currentTransition = null;
|
|
113
|
+
}
|
|
114
|
+
if (visibleChanged || (visible && pendingTransition?.animation !== animationFromToggle)) {
|
|
115
|
+
pendingTransition =
|
|
116
|
+
visible && animationFromToggle != null
|
|
117
|
+
? {
|
|
118
|
+
// toggle was called to display the element, but the element is not yet in the DOM
|
|
119
|
+
// let's keep the animation setting from toggle and provide the promise for the end of toggle
|
|
120
|
+
animation: animationFromToggle,
|
|
121
|
+
...promiseWithResolve(),
|
|
122
|
+
}
|
|
123
|
+
: null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return pendingTransition?.promise ?? currentTransition?.promise;
|
|
127
|
+
});
|
|
128
|
+
let lastToggle = {};
|
|
129
|
+
const toggle = async (visible = !requestedVisible$(), animation = effectiveAnimation$()) => {
|
|
130
|
+
const currentToggle = {};
|
|
131
|
+
lastToggle = currentToggle;
|
|
132
|
+
try {
|
|
133
|
+
await batch(() => {
|
|
134
|
+
try {
|
|
135
|
+
animationFromToggle$.set(animation);
|
|
136
|
+
requestedVisible$.set(visible);
|
|
137
|
+
return visibleAction$();
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
animationFromToggle$.set(null);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
finally {
|
|
145
|
+
if (lastToggle !== currentToggle) {
|
|
146
|
+
await neverEndingPromise;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
const directive = mergeDirectives(storeDirective, directiveUpdate(patch), directiveSubscribe(visibleAction$));
|
|
151
|
+
return {
|
|
152
|
+
...stateStores({
|
|
153
|
+
visible$,
|
|
154
|
+
element$,
|
|
155
|
+
elementPresent$,
|
|
156
|
+
transitioning$,
|
|
157
|
+
shown$,
|
|
158
|
+
hidden$,
|
|
159
|
+
}),
|
|
160
|
+
patch,
|
|
161
|
+
directives: {
|
|
162
|
+
directive,
|
|
163
|
+
},
|
|
164
|
+
actions: {},
|
|
165
|
+
api: {
|
|
166
|
+
show: toggle.bind(null, true),
|
|
167
|
+
hide: toggle.bind(null, false),
|
|
168
|
+
toggle,
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createCollapseTransition } from '../collapse';
|
|
2
|
+
const verticalConfig = {
|
|
3
|
+
dimension: 'height',
|
|
4
|
+
hideClasses: ['collapse'],
|
|
5
|
+
showClasses: ['collapse', 'show'],
|
|
6
|
+
animationPendingClasses: ['collapsing'],
|
|
7
|
+
};
|
|
8
|
+
const horizontalConfig = {
|
|
9
|
+
dimension: 'width',
|
|
10
|
+
hideClasses: ['collapse', 'collapse-horizontal'],
|
|
11
|
+
showClasses: ['collapse', 'collapse-horizontal', 'show'],
|
|
12
|
+
animationPendingClasses: ['collapsing', 'collapse-horizontal'],
|
|
13
|
+
};
|
|
14
|
+
export const collapseVerticalTransition = createCollapseTransition(verticalConfig);
|
|
15
|
+
export const collapseHorizontalTransition = createCollapseTransition(horizontalConfig);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const fadeTransition: import("..").TransitionFn;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createSimpleClassTransition } from '../simpleClassTransition';
|
|
2
|
+
export const fadeTransition = createSimpleClassTransition({
|
|
3
|
+
animationPendingClasses: ['fade'],
|
|
4
|
+
animationPendingShowClasses: ['show'],
|
|
5
|
+
showClasses: ['show'],
|
|
6
|
+
hideClasses: ['d-none'],
|
|
7
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface CollapseContext {
|
|
2
|
+
/**
|
|
3
|
+
* the maximum size of the collapseable content.
|
|
4
|
+
*/
|
|
5
|
+
maxSize?: string;
|
|
6
|
+
/**
|
|
7
|
+
* the minimum size of the collapseable content
|
|
8
|
+
*/
|
|
9
|
+
minSize?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface CollapseConfig {
|
|
12
|
+
/**
|
|
13
|
+
* the direction in which the collapsing is performed
|
|
14
|
+
*/
|
|
15
|
+
dimension?: 'width' | 'height';
|
|
16
|
+
/**
|
|
17
|
+
* the list of classes to add to the collapsable element when shown
|
|
18
|
+
*/
|
|
19
|
+
showClasses?: string[];
|
|
20
|
+
/**
|
|
21
|
+
* the list of classes to add to the collapsable element when collapsed
|
|
22
|
+
*/
|
|
23
|
+
hideClasses?: string[];
|
|
24
|
+
/**
|
|
25
|
+
* the list of classes to add to the collapsable element while transitioning
|
|
26
|
+
*/
|
|
27
|
+
animationPendingClasses?: string[];
|
|
28
|
+
}
|
|
29
|
+
export declare const createCollapseTransition: ({ dimension, showClasses, hideClasses, animationPendingClasses }?: CollapseConfig) => import("./baseTransitions").TransitionFn;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createCSSTransition } from './cssTransitions';
|
|
2
|
+
import { addClasses, reflow, removeClasses } from './utils';
|
|
3
|
+
export const createCollapseTransition = ({ dimension = 'height', showClasses, hideClasses, animationPendingClasses } = {}) => createCSSTransition((element, direction, animation, context) => {
|
|
4
|
+
if (animation) {
|
|
5
|
+
let { maxSize, minSize } = context;
|
|
6
|
+
if (!maxSize) {
|
|
7
|
+
// measure the element in its show state
|
|
8
|
+
removeClasses(element, animationPendingClasses);
|
|
9
|
+
removeClasses(element, hideClasses);
|
|
10
|
+
addClasses(element, showClasses);
|
|
11
|
+
maxSize = element.getBoundingClientRect()[dimension] + 'px';
|
|
12
|
+
context.maxSize = maxSize;
|
|
13
|
+
}
|
|
14
|
+
if (!minSize) {
|
|
15
|
+
// measure the element in its hide state
|
|
16
|
+
removeClasses(element, animationPendingClasses);
|
|
17
|
+
removeClasses(element, showClasses);
|
|
18
|
+
addClasses(element, hideClasses);
|
|
19
|
+
minSize = element.getBoundingClientRect()[dimension] + 'px';
|
|
20
|
+
context.minSize = minSize;
|
|
21
|
+
}
|
|
22
|
+
removeClasses(element, showClasses);
|
|
23
|
+
removeClasses(element, hideClasses);
|
|
24
|
+
const values = direction === 'show' ? [minSize, maxSize] : [maxSize, minSize];
|
|
25
|
+
element.style[dimension] = values[0];
|
|
26
|
+
reflow(element); // explicitly applies the initial state
|
|
27
|
+
addClasses(element, animationPendingClasses);
|
|
28
|
+
reflow(element);
|
|
29
|
+
element.style[dimension] = values[1];
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
removeClasses(element, direction === 'show' ? hideClasses : showClasses);
|
|
33
|
+
}
|
|
34
|
+
return () => {
|
|
35
|
+
removeClasses(element, animationPendingClasses);
|
|
36
|
+
addClasses(element, direction === 'show' ? showClasses : hideClasses);
|
|
37
|
+
element.style[dimension] = '';
|
|
38
|
+
};
|
|
39
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { TransitionFn } from './baseTransitions';
|
|
2
|
+
/**
|
|
3
|
+
* Check if the provided html element has a transition
|
|
4
|
+
* @param element - the html element
|
|
5
|
+
* @returns true if the element has a transition
|
|
6
|
+
*/
|
|
7
|
+
export declare function hasTransition(element: HTMLElement): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Retrieve the transition duration of the provided html element in milli seconds
|
|
10
|
+
* @param element - the html element
|
|
11
|
+
* @returns transition duration in milli seconds
|
|
12
|
+
*/
|
|
13
|
+
export declare function getTransitionDurationMs(element: HTMLElement): number;
|
|
14
|
+
export type CSSTransitionFn = (element: HTMLElement, direction: 'show' | 'hide', animation: boolean, context: object) => void | (() => void);
|
|
15
|
+
export declare const createCSSTransition: (start: CSSTransitionFn) => TransitionFn;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { promiseFromEvent, promiseFromTimeout } from './utils';
|
|
2
|
+
/**
|
|
3
|
+
* Check if the provided html element has a transition
|
|
4
|
+
* @param element - the html element
|
|
5
|
+
* @returns true if the element has a transition
|
|
6
|
+
*/
|
|
7
|
+
export function hasTransition(element) {
|
|
8
|
+
return window.getComputedStyle(element).transitionProperty !== 'none';
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Retrieve the transition duration of the provided html element in milli seconds
|
|
12
|
+
* @param element - the html element
|
|
13
|
+
* @returns transition duration in milli seconds
|
|
14
|
+
*/
|
|
15
|
+
export function getTransitionDurationMs(element) {
|
|
16
|
+
const { transitionDelay, transitionDuration } = window.getComputedStyle(element);
|
|
17
|
+
const transitionDelaySec = parseFloat(transitionDelay);
|
|
18
|
+
const transitionDurationSec = parseFloat(transitionDuration);
|
|
19
|
+
return (transitionDelaySec + transitionDurationSec) * 1000;
|
|
20
|
+
}
|
|
21
|
+
const noop = () => {
|
|
22
|
+
/* do nothing */
|
|
23
|
+
};
|
|
24
|
+
export const createCSSTransition = (start) => async (element, direction, animation, signal, context) => {
|
|
25
|
+
const endFn = start(element, direction, animation, context) ?? noop;
|
|
26
|
+
if (animation && hasTransition(element)) {
|
|
27
|
+
const abort = promiseFromEvent(signal, 'abort');
|
|
28
|
+
const transitionEnd = promiseFromEvent(element, 'transitionend');
|
|
29
|
+
const timer = promiseFromTimeout(getTransitionDurationMs(element));
|
|
30
|
+
await Promise.race([abort.promise, transitionEnd.promise, timer.promise]);
|
|
31
|
+
abort.unsubscribe();
|
|
32
|
+
transitionEnd.unsubscribe();
|
|
33
|
+
timer.unsubscribe();
|
|
34
|
+
}
|
|
35
|
+
if (!signal.aborted) {
|
|
36
|
+
endFn();
|
|
37
|
+
}
|
|
38
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface SimpleClassTransitionConfig {
|
|
2
|
+
/**
|
|
3
|
+
* a list of classes to add to the collapsable element while transitioning
|
|
4
|
+
*/
|
|
5
|
+
animationPendingClasses?: string[];
|
|
6
|
+
/**
|
|
7
|
+
* a list of classes to add to the collapsable element while transitioning towards the show state
|
|
8
|
+
*/
|
|
9
|
+
animationPendingShowClasses?: string[];
|
|
10
|
+
/**
|
|
11
|
+
* a list of classes to add to the collapsable element while transitioning towards the hide state
|
|
12
|
+
*/
|
|
13
|
+
animationPendingHideClasses?: string[];
|
|
14
|
+
/**
|
|
15
|
+
* the list of classes to add to the element when shown
|
|
16
|
+
*/
|
|
17
|
+
showClasses?: string[];
|
|
18
|
+
/**
|
|
19
|
+
* the list of classes to add to the element when hidden
|
|
20
|
+
*/
|
|
21
|
+
hideClasses?: string[];
|
|
22
|
+
}
|
|
23
|
+
export interface SimpleClassTransitionContext {
|
|
24
|
+
/**
|
|
25
|
+
* `true` is the transition has started
|
|
26
|
+
*/
|
|
27
|
+
started?: boolean;
|
|
28
|
+
}
|
|
29
|
+
export declare const createSimpleClassTransition: ({ animationPendingClasses, animationPendingShowClasses, animationPendingHideClasses, showClasses, hideClasses, }: SimpleClassTransitionConfig) => import("./baseTransitions").TransitionFn;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { createCSSTransition } from './cssTransitions';
|
|
2
|
+
import { addClasses, reflow, removeClasses } from './utils';
|
|
3
|
+
export const createSimpleClassTransition = ({ animationPendingClasses, animationPendingShowClasses, animationPendingHideClasses, showClasses, hideClasses, }) => createCSSTransition((element, direction, animation, context) => {
|
|
4
|
+
removeClasses(element, showClasses);
|
|
5
|
+
removeClasses(element, hideClasses);
|
|
6
|
+
if (animation) {
|
|
7
|
+
removeClasses(element, direction === 'show' ? animationPendingHideClasses : animationPendingShowClasses);
|
|
8
|
+
if (!context.started) {
|
|
9
|
+
context.started = true;
|
|
10
|
+
// if the animation is starting, explicitly sets the initial state (reverse of the direction)
|
|
11
|
+
// so that it is not impacted by another reflow done somewhere else before we had time to put
|
|
12
|
+
// the right classes:
|
|
13
|
+
const classes = direction === 'show' ? hideClasses : showClasses;
|
|
14
|
+
addClasses(element, classes);
|
|
15
|
+
reflow(element);
|
|
16
|
+
removeClasses(element, classes);
|
|
17
|
+
}
|
|
18
|
+
addClasses(element, animationPendingClasses);
|
|
19
|
+
reflow(element);
|
|
20
|
+
addClasses(element, direction === 'show' ? animationPendingShowClasses : animationPendingHideClasses);
|
|
21
|
+
}
|
|
22
|
+
return () => {
|
|
23
|
+
removeClasses(element, animationPendingClasses);
|
|
24
|
+
removeClasses(element, animationPendingShowClasses);
|
|
25
|
+
removeClasses(element, animationPendingHideClasses);
|
|
26
|
+
addClasses(element, direction === 'show' ? showClasses : hideClasses);
|
|
27
|
+
};
|
|
28
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ReadableSignal } from '@amadeus-it-group/tansu';
|
|
2
|
+
export declare const promiseFromStore: <T>(store: ReadableSignal<T>, condition?: (value: T) => boolean) => {
|
|
3
|
+
promise: Promise<T>;
|
|
4
|
+
unsubscribe(): void;
|
|
5
|
+
};
|
|
6
|
+
export declare const promiseFromEvent: (element: EventTarget, event: string) => {
|
|
7
|
+
promise: Promise<Event>;
|
|
8
|
+
unsubscribe(): void;
|
|
9
|
+
};
|
|
10
|
+
export declare const promiseFromTimeout: (delay: number) => {
|
|
11
|
+
promise: Promise<void>;
|
|
12
|
+
unsubscribe(): void;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Launch a reflow using a call to the provided html element getBoudingClientRect
|
|
16
|
+
* @param element the html element
|
|
17
|
+
*/
|
|
18
|
+
export declare function reflow(element?: HTMLElement): void;
|
|
19
|
+
export declare const addClasses: (element: HTMLElement, classes?: string[]) => void;
|
|
20
|
+
export declare const removeClasses: (element: HTMLElement, classes?: string[]) => void;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
const noop = () => {
|
|
2
|
+
/* nothing to do*/
|
|
3
|
+
};
|
|
4
|
+
const truthyValue = (value) => !!value;
|
|
5
|
+
export const promiseFromStore = (store, condition = truthyValue) => {
|
|
6
|
+
let resolve;
|
|
7
|
+
const promise = new Promise((r) => (resolve = r));
|
|
8
|
+
let unsubscribe = () => {
|
|
9
|
+
storeUnsubscribe();
|
|
10
|
+
unsubscribe = noop;
|
|
11
|
+
};
|
|
12
|
+
let storeUnsubscribe = noop;
|
|
13
|
+
storeUnsubscribe = store.subscribe((value) => {
|
|
14
|
+
if (condition(value)) {
|
|
15
|
+
resolve(value);
|
|
16
|
+
unsubscribe();
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
if (unsubscribe === noop) {
|
|
20
|
+
storeUnsubscribe();
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
promise,
|
|
24
|
+
unsubscribe() {
|
|
25
|
+
unsubscribe();
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
export const promiseFromEvent = (element, event) => {
|
|
30
|
+
let resolve;
|
|
31
|
+
const promise = new Promise((r) => (resolve = r));
|
|
32
|
+
let unsubscribe = () => {
|
|
33
|
+
element.removeEventListener(event, eventListener);
|
|
34
|
+
unsubscribe = noop;
|
|
35
|
+
};
|
|
36
|
+
const eventListener = (event) => {
|
|
37
|
+
if (event.target === element) {
|
|
38
|
+
resolve(event);
|
|
39
|
+
unsubscribe();
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
element.addEventListener(event, eventListener);
|
|
43
|
+
return {
|
|
44
|
+
promise,
|
|
45
|
+
unsubscribe() {
|
|
46
|
+
unsubscribe();
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
export const promiseFromTimeout = (delay) => {
|
|
51
|
+
let timeout;
|
|
52
|
+
return {
|
|
53
|
+
promise: new Promise((r) => {
|
|
54
|
+
timeout = setTimeout(() => {
|
|
55
|
+
timeout = undefined;
|
|
56
|
+
r();
|
|
57
|
+
}, delay);
|
|
58
|
+
}),
|
|
59
|
+
unsubscribe() {
|
|
60
|
+
if (timeout) {
|
|
61
|
+
clearTimeout(timeout);
|
|
62
|
+
timeout = undefined;
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Launch a reflow using a call to the provided html element getBoudingClientRect
|
|
69
|
+
* @param element the html element
|
|
70
|
+
*/
|
|
71
|
+
export function reflow(element = document.body) {
|
|
72
|
+
element.getBoundingClientRect();
|
|
73
|
+
}
|
|
74
|
+
export const addClasses = (element, classes) => {
|
|
75
|
+
if (classes && classes.length > 0) {
|
|
76
|
+
element.classList.add(...classes);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
export const removeClasses = (element, classes) => {
|
|
80
|
+
if (classes && classes.length > 0) {
|
|
81
|
+
element.classList.remove(...classes);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// This file is read by tools that parse documentation comments conforming to the TSDoc standard.
|
|
2
|
+
// It should be published with your NPM package. It should not be tracked by Git.
|
|
3
|
+
{
|
|
4
|
+
"tsdocVersion": "0.12",
|
|
5
|
+
"toolPackages": [
|
|
6
|
+
{
|
|
7
|
+
"packageName": "@microsoft/api-extractor",
|
|
8
|
+
"packageVersion": "7.36.4"
|
|
9
|
+
}
|
|
10
|
+
]
|
|
11
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { ReadableSignal, SubscribableStore } from '@amadeus-it-group/tansu';
|
|
2
|
+
import type { PropsConfig } from './services';
|
|
3
|
+
export interface Widget<Props extends object = object, State extends object = object, Api extends object = object, Actions extends object = object, Directives extends object = object> {
|
|
4
|
+
/**
|
|
5
|
+
* the reactive state of the widget, combining all the values served by the stores
|
|
6
|
+
*/
|
|
7
|
+
state$: ReadableSignal<State>;
|
|
8
|
+
/**
|
|
9
|
+
* the different stores of the widget, all reactive
|
|
10
|
+
*/
|
|
11
|
+
stores: {
|
|
12
|
+
[K in keyof State as `${K & string}$`]: ReadableSignal<State[K]>;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Modify the parameter values, and recalculate the stores accordingly
|
|
16
|
+
*/
|
|
17
|
+
patch(parameters: Partial<Props>): void;
|
|
18
|
+
/**
|
|
19
|
+
* directives to be used on html elements in the template of the widget or in the slots
|
|
20
|
+
*/
|
|
21
|
+
directives: Directives;
|
|
22
|
+
/**
|
|
23
|
+
* all the handlers that should be connected to user interactions i.e. click, keyboard and touch interactions.
|
|
24
|
+
* typically, the handlers are event listeners that call api functions to affect the widget state
|
|
25
|
+
*/
|
|
26
|
+
actions: Actions;
|
|
27
|
+
/**
|
|
28
|
+
* all the api functions to interact with the widget
|
|
29
|
+
*/
|
|
30
|
+
api: Api;
|
|
31
|
+
}
|
|
32
|
+
export interface WidgetSlotContext<W extends Widget> {
|
|
33
|
+
/**
|
|
34
|
+
* the state of the widget
|
|
35
|
+
*/
|
|
36
|
+
state: WidgetState<W>;
|
|
37
|
+
/**
|
|
38
|
+
* the widget
|
|
39
|
+
*/
|
|
40
|
+
widget: Pick<W, 'actions' | 'api' | 'directives' | 'state$' | 'stores'>;
|
|
41
|
+
}
|
|
42
|
+
export declare const toSlotContextWidget: <W extends Widget<object, object, object, object, object>>(w: W) => Pick<W, "actions" | "api" | "directives" | "state$" | "stores">;
|
|
43
|
+
export type WidgetState<T extends {
|
|
44
|
+
state$: SubscribableStore<any>;
|
|
45
|
+
}> = T extends {
|
|
46
|
+
state$: SubscribableStore<infer U extends object>;
|
|
47
|
+
} ? U : never;
|
|
48
|
+
export type WidgetProps<T extends {
|
|
49
|
+
patch: (arg: any) => void;
|
|
50
|
+
}> = T extends {
|
|
51
|
+
patch: (arg: Partial<infer U extends object>) => void;
|
|
52
|
+
} ? U : never;
|
|
53
|
+
export type WidgetFactory<W extends Widget> = (props?: PropsConfig<WidgetProps<W>>) => W;
|
|
54
|
+
export type Directive<T = void> = (node: HTMLElement, args: T) => void | {
|
|
55
|
+
update?: (args: T) => void;
|
|
56
|
+
destroy?: () => void;
|
|
57
|
+
};
|
|
58
|
+
export type SlotContent<Props extends object = object> = undefined | null | string | ((props: Props) => string);
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agnos-ui/core",
|
|
3
|
+
"description": "Framework-agnostic headless widget library.",
|
|
4
|
+
"homepage": "https://amadeusitgroup.github.io/AgnosUI/latest/",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"headless",
|
|
7
|
+
"agnostic",
|
|
8
|
+
"components",
|
|
9
|
+
"widgets",
|
|
10
|
+
"alert",
|
|
11
|
+
"modal",
|
|
12
|
+
"pagination",
|
|
13
|
+
"rating"
|
|
14
|
+
],
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "./dist/lib/index.js",
|
|
17
|
+
"module": "./dist/lib/index.js",
|
|
18
|
+
"types": "./dist/lib/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/lib/index.d.ts",
|
|
22
|
+
"default": "./dist/lib/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "npm run build:lib && npm run build:tsc && npm run build:api-extractor",
|
|
27
|
+
"build:lib": "node ../scripts/rm.js dist/lib && tsc -p tsconfig.lib.json",
|
|
28
|
+
"build:tsc": "tsc",
|
|
29
|
+
"build:api-extractor": "api-extractor run",
|
|
30
|
+
"test": "vitest run",
|
|
31
|
+
"tdd": "vitest",
|
|
32
|
+
"tdd:ui": "vitest --ui",
|
|
33
|
+
"test:coverage": "vitest run --coverage"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@amadeus-it-group/tansu": "0.0.22"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist/lib"
|
|
40
|
+
],
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"bugs": "https://github.com/AmadeusITGroup/AgnosUI/issues",
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "https://github.com/AmadeusITGroup/AgnosUI.git",
|
|
46
|
+
"directory": "core"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"eslint-plugin-jsdoc": "^46.4.6"
|
|
50
|
+
},
|
|
51
|
+
"version": "0.0.1-alpha.0"
|
|
52
|
+
}
|