@agnos-ui/core 0.0.1-alpha.4 → 0.0.1-alpha.5
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/{accordion.d.ts → components/accordion/accordion.d.ts} +3 -4
- package/{accordion.js → components/accordion/accordion.js} +10 -9
- package/components/alert/alert.d.ts +32 -0
- package/components/alert/alert.js +23 -0
- package/{alert.d.ts → components/alert/common.d.ts} +17 -21
- package/{alert.js → components/alert/common.js} +12 -12
- package/{modal → components/modal}/modal.d.ts +28 -21
- package/{modal → components/modal}/modal.js +39 -13
- package/{pagination.d.ts → components/pagination/pagination.d.ts} +3 -3
- package/{pagination.js → components/pagination/pagination.js} +6 -4
- package/{progressbar.d.ts → components/progressbar/progressbar.d.ts} +2 -3
- package/{progressbar.js → components/progressbar/progressbar.js} +6 -6
- package/{rating.d.ts → components/rating/rating.d.ts} +2 -3
- package/{rating.js → components/rating/rating.js} +6 -9
- package/components/select/select.d.ts +337 -0
- package/components/select/select.js +266 -0
- package/{slider.d.ts → components/slider/slider.d.ts} +60 -14
- package/components/slider/slider.js +389 -0
- package/config.d.ts +9 -9
- package/config.js +3 -3
- package/index.d.ts +23 -13
- package/index.js +29 -13
- package/package.json +30 -3
- package/services/extendWidget.d.ts +23 -0
- package/{extendWidget.js → services/extendWidget.js} +8 -1
- package/services/floatingUI.d.ts +48 -0
- package/services/floatingUI.js +97 -0
- package/services/focustrack.js +1 -1
- package/services/intersection.d.ts +1 -1
- package/services/intersection.js +2 -2
- package/services/navManager.d.ts +83 -1
- package/services/navManager.js +153 -37
- package/services/portal.js +8 -4
- package/services/siblingsInert.d.ts +2 -1
- package/services/siblingsInert.js +2 -2
- package/{transitions → services/transitions}/baseTransitions.d.ts +1 -2
- package/{transitions → services/transitions}/baseTransitions.js +7 -10
- package/services/transitions/bootstrap/collapse.d.ts +2 -0
- package/services/transitions/bootstrap/fade.d.ts +1 -0
- package/services/transitions/bootstrap.d.ts +2 -0
- package/services/transitions/bootstrap.js +2 -0
- package/{transitions → services/transitions}/collapse.js +1 -1
- package/{transitions → services/transitions}/cssTransitions.js +2 -4
- package/{transitions → services/transitions}/simpleClassTransition.js +1 -1
- package/types.d.ts +37 -4
- package/types.js +1 -0
- package/{services/directiveUtils.js → utils/directive.js} +1 -1
- package/{services → utils/internal}/checks.d.ts +12 -0
- package/{services → utils/internal}/checks.js +12 -0
- package/utils/internal/dom.d.ts +13 -0
- package/utils/internal/dom.js +49 -0
- package/utils/internal/math.d.ts +5 -0
- package/utils/internal/math.js +13 -0
- package/utils/internal/promise.d.ts +31 -0
- package/utils/internal/promise.js +113 -0
- package/{modal → utils/internal}/scrollbars.js +1 -1
- package/utils/internal/textDirection.d.ts +1 -0
- package/utils/internal/textDirection.js +1 -0
- package/utils/internal/traversal.d.ts +54 -0
- package/utils/internal/traversal.js +105 -0
- package/{services → utils}/stores.d.ts +11 -35
- package/{services → utils}/stores.js +21 -19
- package/utils/writables.d.ts +26 -0
- package/utils/writables.js +66 -0
- package/extendWidget.d.ts +0 -3
- package/select.d.ts +0 -196
- package/select.js +0 -240
- package/services/index.d.ts +0 -9
- package/services/index.js +0 -9
- package/services/writables.d.ts +0 -8
- package/services/writables.js +0 -30
- package/slider.js +0 -289
- package/transitions/bootstrap/collapse.d.ts +0 -2
- package/transitions/bootstrap/fade.d.ts +0 -1
- package/transitions/bootstrap/index.d.ts +0 -2
- package/transitions/bootstrap/index.js +0 -2
- package/transitions/index.d.ts +0 -5
- package/transitions/index.js +0 -5
- package/transitions/utils.d.ts +0 -20
- package/transitions/utils.js +0 -83
- /package/{commonProps.d.ts → components/commonProps.d.ts} +0 -0
- /package/{commonProps.js → components/commonProps.js} +0 -0
- /package/{pagination.utils.d.ts → components/pagination/bootstrap.d.ts} +0 -0
- /package/{pagination.utils.js → components/pagination/bootstrap.js} +0 -0
- /package/{transitions → services/transitions}/bootstrap/collapse.js +0 -0
- /package/{transitions → services/transitions}/bootstrap/fade.js +0 -0
- /package/{transitions → services/transitions}/collapse.d.ts +0 -0
- /package/{transitions → services/transitions}/cssTransitions.d.ts +0 -0
- /package/{transitions → services/transitions}/simpleClassTransition.d.ts +0 -0
- /package/{services/directiveUtils.d.ts → utils/directive.d.ts} +0 -0
- /package/{utils.d.ts → utils/internal/func.d.ts} +0 -0
- /package/{utils.js → utils/internal/func.js} +0 -0
- /package/{services → utils/internal}/isFocusable.d.ts +0 -0
- /package/{services → utils/internal}/isFocusable.js +0 -0
- /package/{modal → utils/internal}/scrollbars.d.ts +0 -0
- /package/{services/sortUtils.d.ts → utils/internal/sort.d.ts} +0 -0
- /package/{services/sortUtils.js → utils/internal/sort.js} +0 -0
package/package.json
CHANGED
|
@@ -21,19 +21,46 @@
|
|
|
21
21
|
".": {
|
|
22
22
|
"types": "./index.d.ts",
|
|
23
23
|
"default": "./index.js"
|
|
24
|
+
},
|
|
25
|
+
"./components/*": {
|
|
26
|
+
"types": "./components/*/*.d.ts",
|
|
27
|
+
"default": "./components/*/*.js"
|
|
28
|
+
},
|
|
29
|
+
"./config": {
|
|
30
|
+
"types": "./config.d.ts",
|
|
31
|
+
"default": "./config.js"
|
|
32
|
+
},
|
|
33
|
+
"./types": {
|
|
34
|
+
"types": "./types.d.ts",
|
|
35
|
+
"default": "./types.js"
|
|
36
|
+
},
|
|
37
|
+
"./services/*": {
|
|
38
|
+
"types": "./services/*.d.ts",
|
|
39
|
+
"default": "./services/*.js"
|
|
40
|
+
},
|
|
41
|
+
"./services/transitions/*": {
|
|
42
|
+
"types": "./services/transitions/*.d.ts",
|
|
43
|
+
"default": "./services/transitions/*.js"
|
|
44
|
+
},
|
|
45
|
+
"./utils/*": {
|
|
46
|
+
"types": "./utils/*.d.ts",
|
|
47
|
+
"default": "./utils/*.js"
|
|
24
48
|
}
|
|
25
49
|
},
|
|
26
50
|
"dependencies": {
|
|
27
|
-
"@amadeus-it-group/tansu": "0.0.
|
|
51
|
+
"@amadeus-it-group/tansu": "0.0.24"
|
|
52
|
+
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"@floating-ui/dom": "*"
|
|
28
55
|
},
|
|
29
56
|
"sideEffects": false,
|
|
30
|
-
"version": "0.0.1-alpha.
|
|
57
|
+
"version": "0.0.1-alpha.5",
|
|
31
58
|
"homepage": "https://amadeusitgroup.github.io/AgnosUI/latest/",
|
|
32
59
|
"bugs": "https://github.com/AmadeusITGroup/AgnosUI/issues",
|
|
33
60
|
"license": "MIT",
|
|
34
61
|
"repository": {
|
|
35
62
|
"type": "git",
|
|
36
63
|
"url": "https://github.com/AmadeusITGroup/AgnosUI.git",
|
|
37
|
-
"directory": "core
|
|
64
|
+
"directory": "core"
|
|
38
65
|
}
|
|
39
66
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ConfigValidator, SlotContent, Widget, WidgetFactory, WidgetProps, WidgetSlotContext, WidgetState } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Type extending the original Widget props and state with ExtraProps
|
|
4
|
+
*/
|
|
5
|
+
export type ExtendWidgetProps<W extends Widget, ExtraProps extends object> = Widget<ExtendWidgetAdaptSlotWidgetProps<WidgetProps<W>, ExtraProps>, ExtendWidgetAdaptSlotWidgetProps<WidgetState<W>, ExtraProps>, W['api'], W['actions'], W['directives']>;
|
|
6
|
+
/**
|
|
7
|
+
* Type replacing the original Props with WidgetSlotContext contaning ExtraProps
|
|
8
|
+
*/
|
|
9
|
+
export type ExtendWidgetAdaptSlotContentProps<Props extends Record<string, any>, ExtraProps extends object> = Props extends WidgetSlotContext<infer U> ? WidgetSlotContext<ExtendWidgetProps<U, ExtraProps>> & Omit<Props, keyof WidgetSlotContext<any>> : Props;
|
|
10
|
+
/**
|
|
11
|
+
* Type enriching the original widget slot Props with ExtraProps slots
|
|
12
|
+
*/
|
|
13
|
+
export type ExtendWidgetAdaptSlotWidgetProps<Props, ExtraProps extends object> = Omit<Props, `slot${string}`> & ExtraProps & {
|
|
14
|
+
[K in keyof Props & `slot${string}`]: Props[K] extends SlotContent<infer U> ? SlotContent<ExtendWidgetAdaptSlotContentProps<U, ExtraProps>> : Props[K];
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Method to extend the original widget with extra props with validator
|
|
18
|
+
* @param factory - original widget factory
|
|
19
|
+
* @param extraPropsDefaults - object containing default value for each extra prop
|
|
20
|
+
* @param extraPropsConfig - object verifying the type of each extra prop
|
|
21
|
+
* @returns widget factory with the extra props
|
|
22
|
+
*/
|
|
23
|
+
export declare const extendWidgetProps: <W extends Widget<object, object, object, object, object>, ExtraProps extends object>(factory: WidgetFactory<W>, extraPropsDefaults: ExtraProps, extraPropsConfig?: ConfigValidator<ExtraProps> | undefined) => WidgetFactory<ExtendWidgetProps<W, ExtraProps>>;
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { batch } from '@amadeus-it-group/tansu';
|
|
2
|
-
import { stateStores, writablesWithDefault } from '
|
|
2
|
+
import { stateStores, writablesWithDefault } from '../utils/stores';
|
|
3
|
+
/**
|
|
4
|
+
* Method to extend the original widget with extra props with validator
|
|
5
|
+
* @param factory - original widget factory
|
|
6
|
+
* @param extraPropsDefaults - object containing default value for each extra prop
|
|
7
|
+
* @param extraPropsConfig - object verifying the type of each extra prop
|
|
8
|
+
* @returns widget factory with the extra props
|
|
9
|
+
*/
|
|
3
10
|
export const extendWidgetProps = (factory, extraPropsDefaults, extraPropsConfig) => (propsConfig) => {
|
|
4
11
|
const extraPropsWritables = writablesWithDefault(extraPropsDefaults, propsConfig, extraPropsConfig);
|
|
5
12
|
const widget = factory(propsConfig);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ArrowOptions, AutoUpdateOptions, ComputePositionConfig, Derivable } from '@floating-ui/dom';
|
|
2
|
+
import type { PropsConfig } from '../types';
|
|
3
|
+
export interface FloatingUIProps {
|
|
4
|
+
/**
|
|
5
|
+
* Options to use when calling computePosition from Floating UI
|
|
6
|
+
*/
|
|
7
|
+
computePositionOptions: ComputePositionConfig;
|
|
8
|
+
/**
|
|
9
|
+
* Options to use when calling autoUpdate from Floating UI
|
|
10
|
+
*/
|
|
11
|
+
autoUpdateOptions: AutoUpdateOptions;
|
|
12
|
+
/**
|
|
13
|
+
* Options to use when calling the arrow middleware from Floating UI
|
|
14
|
+
*/
|
|
15
|
+
arrowOptions: Omit<ArrowOptions, 'element'> | Derivable<Omit<ArrowOptions, 'element'>>;
|
|
16
|
+
}
|
|
17
|
+
export type FloatingUI = ReturnType<typeof createFloatingUI>;
|
|
18
|
+
export declare const createFloatingUI: (propsConfig?: PropsConfig<FloatingUIProps>) => {
|
|
19
|
+
directives: {
|
|
20
|
+
/**
|
|
21
|
+
* Directive to be used on the reference element from where the floating element will be positioned
|
|
22
|
+
*/
|
|
23
|
+
referenceDirective: import("../types").Directive;
|
|
24
|
+
/**
|
|
25
|
+
* Directive to be used on the floating element
|
|
26
|
+
*/
|
|
27
|
+
floatingDirective: import("../types").Directive<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Directive to be used on the arrow element, if any
|
|
30
|
+
*/
|
|
31
|
+
arrowDirective: import("../types").Directive<void>;
|
|
32
|
+
};
|
|
33
|
+
state$: import("@amadeus-it-group/tansu").ReadableSignal<import("../utils/stores").ToState<{
|
|
34
|
+
x$: import("@amadeus-it-group/tansu").ReadableSignal<number | undefined>;
|
|
35
|
+
y$: import("@amadeus-it-group/tansu").ReadableSignal<number | undefined>;
|
|
36
|
+
strategy$: import("@amadeus-it-group/tansu").ReadableSignal<import("@floating-ui/utils").Strategy | undefined>;
|
|
37
|
+
placement$: import("@amadeus-it-group/tansu").ReadableSignal<import("@floating-ui/utils").Placement | undefined>;
|
|
38
|
+
middlewareData$: import("@amadeus-it-group/tansu").ReadableSignal<import("@floating-ui/core").MiddlewareData | undefined>;
|
|
39
|
+
}>>;
|
|
40
|
+
stores: {
|
|
41
|
+
x$: import("@amadeus-it-group/tansu").ReadableSignal<number | undefined>;
|
|
42
|
+
y$: import("@amadeus-it-group/tansu").ReadableSignal<number | undefined>;
|
|
43
|
+
strategy$: import("@amadeus-it-group/tansu").ReadableSignal<import("@floating-ui/utils").Strategy | undefined>;
|
|
44
|
+
placement$: import("@amadeus-it-group/tansu").ReadableSignal<import("@floating-ui/utils").Placement | undefined>;
|
|
45
|
+
middlewareData$: import("@amadeus-it-group/tansu").ReadableSignal<import("@floating-ui/core").MiddlewareData | undefined>;
|
|
46
|
+
};
|
|
47
|
+
patch: <U extends Partial<FloatingUIProps>>(storesValues?: void | U | undefined) => void;
|
|
48
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { computed, derived } from '@amadeus-it-group/tansu';
|
|
2
|
+
import { arrow, autoUpdate, computePosition } from '@floating-ui/dom';
|
|
3
|
+
import { createStoreDirective, directiveSubscribe, mergeDirectives } from '../utils/directive';
|
|
4
|
+
import { promiseStoreToValueStore } from '../utils/internal/promise';
|
|
5
|
+
import { stateStores, writablesForProps } from '../utils/stores';
|
|
6
|
+
const defaultConfig = {
|
|
7
|
+
computePositionOptions: {},
|
|
8
|
+
autoUpdateOptions: {},
|
|
9
|
+
arrowOptions: {},
|
|
10
|
+
};
|
|
11
|
+
export const createFloatingUI = (propsConfig) => {
|
|
12
|
+
const [{ autoUpdateOptions$, computePositionOptions$: computePositionInputOptions$, arrowOptions$: arrowInputOptions$ }, patch] = writablesForProps(defaultConfig, propsConfig);
|
|
13
|
+
const { directive: floatingDirective, element$: floatingElement$ } = createStoreDirective();
|
|
14
|
+
const { directive: referenceDirective, element$: referenceElement$ } = createStoreDirective();
|
|
15
|
+
const { directive: arrowDirective, element$: arrowElement$ } = createStoreDirective();
|
|
16
|
+
const arrowOptions$ = computed(() => {
|
|
17
|
+
const arrowElement = arrowElement$();
|
|
18
|
+
if (!arrowElement) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const arrowInputOptions = arrowInputOptions$();
|
|
22
|
+
return typeof arrowInputOptions === 'function'
|
|
23
|
+
? (state) => ({ ...arrowInputOptions(state), element: arrowElement })
|
|
24
|
+
: { ...arrowInputOptions, element: arrowElement };
|
|
25
|
+
});
|
|
26
|
+
const computePositionOptions$ = computed(() => {
|
|
27
|
+
let options = computePositionInputOptions$();
|
|
28
|
+
const arrowOptions = arrowOptions$();
|
|
29
|
+
if (arrowOptions) {
|
|
30
|
+
options = {
|
|
31
|
+
...options,
|
|
32
|
+
middleware: [...(options.middleware ?? []), arrow(arrowOptions)],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return options;
|
|
36
|
+
});
|
|
37
|
+
const promisePosition$ = derived([floatingElement$, referenceElement$, computePositionOptions$, autoUpdateOptions$], ([floatingElement, referenceElement, computePositionOptions, autoUpdateOptions], set) => {
|
|
38
|
+
if (floatingElement && referenceElement) {
|
|
39
|
+
const clean = autoUpdate(referenceElement, floatingElement, () => {
|
|
40
|
+
set(computePosition(referenceElement, floatingElement, computePositionOptions));
|
|
41
|
+
}, autoUpdateOptions);
|
|
42
|
+
return () => {
|
|
43
|
+
set(null);
|
|
44
|
+
clean();
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return undefined;
|
|
48
|
+
}, null);
|
|
49
|
+
const position$ = promiseStoreToValueStore(promisePosition$, null);
|
|
50
|
+
const placement$ = computed(() => position$()?.placement);
|
|
51
|
+
const middlewareData$ = computed(() => position$()?.middlewareData);
|
|
52
|
+
const x$ = computed(() => position$()?.x);
|
|
53
|
+
const y$ = computed(() => position$()?.y);
|
|
54
|
+
const strategy$ = computed(() => position$()?.strategy);
|
|
55
|
+
const arrowX$ = computed(() => middlewareData$()?.arrow?.x);
|
|
56
|
+
const arrowY$ = computed(() => middlewareData$()?.arrow?.y);
|
|
57
|
+
const floatingStyleApplyAction$ = computed(() => {
|
|
58
|
+
const floatingElement = floatingElement$();
|
|
59
|
+
if (floatingElement) {
|
|
60
|
+
floatingElement.style.left = `${x$() ?? 0}px`;
|
|
61
|
+
floatingElement.style.top = `${y$() ?? 0}px`;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
const arrowStyleApplyAction$ = computed(() => {
|
|
65
|
+
const arrowElement = arrowElement$();
|
|
66
|
+
if (arrowElement) {
|
|
67
|
+
const arrowX = arrowX$();
|
|
68
|
+
const arrowY = arrowY$();
|
|
69
|
+
arrowElement.style.left = arrowX != null ? `${arrowX}px` : '';
|
|
70
|
+
arrowElement.style.top = arrowY != null ? `${arrowY}px` : '';
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
patch,
|
|
75
|
+
...stateStores({
|
|
76
|
+
x$,
|
|
77
|
+
y$,
|
|
78
|
+
strategy$,
|
|
79
|
+
placement$,
|
|
80
|
+
middlewareData$,
|
|
81
|
+
}),
|
|
82
|
+
directives: {
|
|
83
|
+
/**
|
|
84
|
+
* Directive to be used on the reference element from where the floating element will be positioned
|
|
85
|
+
*/
|
|
86
|
+
referenceDirective,
|
|
87
|
+
/**
|
|
88
|
+
* Directive to be used on the floating element
|
|
89
|
+
*/
|
|
90
|
+
floatingDirective: mergeDirectives(floatingDirective, directiveSubscribe(floatingStyleApplyAction$)),
|
|
91
|
+
/**
|
|
92
|
+
* Directive to be used on the arrow element, if any
|
|
93
|
+
*/
|
|
94
|
+
arrowDirective: mergeDirectives(arrowDirective, directiveSubscribe(arrowStyleApplyAction$)),
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
};
|
package/services/focustrack.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { computed, readable } from '@amadeus-it-group/tansu';
|
|
2
|
-
import { createStoreArrayDirective } from '
|
|
2
|
+
import { createStoreArrayDirective } from '../utils/directive';
|
|
3
3
|
const evtFocusIn = 'focusin';
|
|
4
4
|
const evtFocusOut = 'focusout';
|
|
5
5
|
export const activeElement$ = readable(null, {
|
package/services/intersection.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { asReadable, derived } from '@amadeus-it-group/tansu';
|
|
2
|
-
import { noop } from '../utils';
|
|
3
|
-
import { writablesForProps } from '
|
|
2
|
+
import { noop } from '../utils/internal/func';
|
|
3
|
+
import { writablesForProps } from '../utils/stores';
|
|
4
4
|
const defaultValues = {
|
|
5
5
|
elements: [],
|
|
6
6
|
options: undefined,
|
package/services/navManager.d.ts
CHANGED
|
@@ -1,5 +1,87 @@
|
|
|
1
1
|
import type { Directive } from '../types';
|
|
2
2
|
export type NavManager = ReturnType<typeof createNavManager>;
|
|
3
|
+
/**
|
|
4
|
+
* Returns the key name given the keyboard event. The key name is built using event.key (such as ArrowLeft, PageDown...),
|
|
5
|
+
* prefixed with the modifiers. If present, modifiers are always in the same order: Meta+Ctrl+Alt+Shift+...
|
|
6
|
+
* @param event - keyboard event
|
|
7
|
+
* @returns the name of the key, including modifiers
|
|
8
|
+
*/
|
|
9
|
+
export declare const getKeyName: (event: KeyboardEvent) => string;
|
|
10
|
+
/**
|
|
11
|
+
* Returns true if the keyboard event is an ArrowLeft, ArrowRight, Home or End key press that should make the cursor move inside
|
|
12
|
+
* the input and false otherwise (i.e. the key is not ArrowLeft, ArrowRight, Home or End key, or that would not make the cursor move
|
|
13
|
+
* because it is already at one end of the input)
|
|
14
|
+
* @param event - keyboard event
|
|
15
|
+
* @returns true if the keyboard event is an ArrowLeft, ArrowRight, Home or End key press that should make the cursor move inside
|
|
16
|
+
* the input and false otherwise.
|
|
17
|
+
*/
|
|
18
|
+
export declare const isInternalInputNavigation: (event: KeyboardEvent) => boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Type of a key handler of the navigation manager.
|
|
21
|
+
* A key handler receives an object as its parameter, with the following properties:
|
|
22
|
+
* - event: key event
|
|
23
|
+
* - directiveElement: DOM element which has the navigation manager directive
|
|
24
|
+
* - navManager: navigation manager instance
|
|
25
|
+
*/
|
|
26
|
+
export type NavManagerKeyHandler = (info: {
|
|
27
|
+
directiveElement: HTMLElement;
|
|
28
|
+
event: KeyboardEvent;
|
|
29
|
+
navManager: NavManager;
|
|
30
|
+
}) => void;
|
|
31
|
+
/**
|
|
32
|
+
* Type of the parameter of the navigation manager directive.
|
|
33
|
+
*/
|
|
34
|
+
export interface NavManagerItemConfig {
|
|
35
|
+
/**
|
|
36
|
+
* Map of key handlers.
|
|
37
|
+
* The key in the map should match the result of calling {@link getKeyName} on the key event (for example "ArrowLeft" or "Ctrl+PageDown").
|
|
38
|
+
* The value in the map is the corresponding key handler.
|
|
39
|
+
*/
|
|
40
|
+
keys?: Record<string, NavManagerKeyHandler>;
|
|
41
|
+
/**
|
|
42
|
+
* Function returning DOM elements to include in the navigation manager.
|
|
43
|
+
* It receives as a parameter the DOM element on which the navigation manager directive is used.
|
|
44
|
+
* If not specified, the default selector function only returns the element on which the navigation manager directive is used.
|
|
45
|
+
*/
|
|
46
|
+
selector?: (directiveElement: HTMLElement) => Iterable<HTMLElement>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Returns a new instance of the navigation manager.
|
|
50
|
+
*
|
|
51
|
+
* The navigation manager simplifies keyboard navigation for a set of DOM elements.
|
|
52
|
+
* It provides a directive to use on some DOM elements, both to add the keydown event handler and to specify which elements should be managed
|
|
53
|
+
* (either by directly putting the directive on those elements, or by putting the directive on a parent element and
|
|
54
|
+
* specifying which child elements should be included through a selector function).
|
|
55
|
+
*
|
|
56
|
+
* It provides some utilities to move the focus between those elements (focusFirst/focusLast, focusLeft/focusRight, focusPrevious/focusNext).
|
|
57
|
+
*
|
|
58
|
+
* @returns a new instance of the navigation manager
|
|
59
|
+
*/
|
|
3
60
|
export declare const createNavManager: () => {
|
|
4
|
-
|
|
61
|
+
elementsInDomOrder$: import("@amadeus-it-group/tansu").ReadableSignal<HTMLElement[]>;
|
|
62
|
+
directive: Directive<NavManagerItemConfig>;
|
|
63
|
+
focusIndex: (index: number, moveDirection?: -1 | 0 | 1) => HTMLElement | null;
|
|
64
|
+
focusPrevious: ({ event, referenceElement, }?: {
|
|
65
|
+
event?: KeyboardEvent | undefined;
|
|
66
|
+
referenceElement?: HTMLElement | null | undefined;
|
|
67
|
+
}) => HTMLElement | null;
|
|
68
|
+
focusNext: ({ event, referenceElement, }?: {
|
|
69
|
+
event?: KeyboardEvent | undefined;
|
|
70
|
+
referenceElement?: HTMLElement | null | undefined;
|
|
71
|
+
}) => HTMLElement | null;
|
|
72
|
+
focusFirst: ({ event }?: {
|
|
73
|
+
event?: KeyboardEvent | undefined;
|
|
74
|
+
}) => HTMLElement | null;
|
|
75
|
+
focusLast: ({ event }?: {
|
|
76
|
+
event?: KeyboardEvent | undefined;
|
|
77
|
+
}) => HTMLElement | null;
|
|
78
|
+
focusLeft: (args_0?: {
|
|
79
|
+
event?: KeyboardEvent | undefined;
|
|
80
|
+
referenceElement?: HTMLElement | null | undefined;
|
|
81
|
+
} | undefined) => HTMLElement | null;
|
|
82
|
+
focusRight: (args_0?: {
|
|
83
|
+
event?: KeyboardEvent | undefined;
|
|
84
|
+
referenceElement?: HTMLElement | null | undefined;
|
|
85
|
+
} | undefined) => HTMLElement | null;
|
|
86
|
+
refreshElements: () => void;
|
|
5
87
|
};
|
package/services/navManager.js
CHANGED
|
@@ -1,52 +1,168 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { registrationArray } from '
|
|
3
|
-
import {
|
|
1
|
+
import { computed, writable } from '@amadeus-it-group/tansu';
|
|
2
|
+
import { registrationArray } from '../utils/directive';
|
|
3
|
+
import { computeCommonAncestor } from '../utils/internal/dom';
|
|
4
|
+
import { isFocusable } from '../utils/internal/isFocusable';
|
|
5
|
+
import { compareDomOrder } from '../utils/internal/sort';
|
|
6
|
+
import { getTextDirection } from '../utils/internal/textDirection';
|
|
4
7
|
// cf https://html.spec.whatwg.org/multipage/input.html#concept-input-apply
|
|
5
8
|
const textInputTypes = new Set(['text', 'search', 'url', 'tel', 'password']);
|
|
6
9
|
const isTextInput = (element) => element instanceof HTMLInputElement && textInputTypes.has(element.type);
|
|
10
|
+
/**
|
|
11
|
+
* Returns the key name given the keyboard event. The key name is built using event.key (such as ArrowLeft, PageDown...),
|
|
12
|
+
* prefixed with the modifiers. If present, modifiers are always in the same order: Meta+Ctrl+Alt+Shift+...
|
|
13
|
+
* @param event - keyboard event
|
|
14
|
+
* @returns the name of the key, including modifiers
|
|
15
|
+
*/
|
|
16
|
+
export const getKeyName = (event) => {
|
|
17
|
+
let key = event.key;
|
|
18
|
+
if (event.shiftKey) {
|
|
19
|
+
key = `Shift+${key}`;
|
|
20
|
+
}
|
|
21
|
+
if (event.altKey) {
|
|
22
|
+
key = `Alt+${key}`;
|
|
23
|
+
}
|
|
24
|
+
if (event.ctrlKey) {
|
|
25
|
+
key = `Ctrl+${key}`;
|
|
26
|
+
}
|
|
27
|
+
if (event.metaKey) {
|
|
28
|
+
key = `Meta+${key}`;
|
|
29
|
+
}
|
|
30
|
+
return key;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Returns true if the keyboard event is an ArrowLeft, ArrowRight, Home or End key press that should make the cursor move inside
|
|
34
|
+
* the input and false otherwise (i.e. the key is not ArrowLeft, ArrowRight, Home or End key, or that would not make the cursor move
|
|
35
|
+
* because it is already at one end of the input)
|
|
36
|
+
* @param event - keyboard event
|
|
37
|
+
* @returns true if the keyboard event is an ArrowLeft, ArrowRight, Home or End key press that should make the cursor move inside
|
|
38
|
+
* the input and false otherwise.
|
|
39
|
+
*/
|
|
40
|
+
export const isInternalInputNavigation = (event) => {
|
|
41
|
+
const { target, key } = event;
|
|
42
|
+
if (isTextInput(target) && (key === 'ArrowLeft' || key === 'ArrowRight' || key === 'Home' || key === 'End')) {
|
|
43
|
+
let startPosition;
|
|
44
|
+
if (key === 'ArrowLeft' || key === 'ArrowRight') {
|
|
45
|
+
const direction = getTextDirection(target);
|
|
46
|
+
startPosition = key === (direction === 'ltr' ? 'ArrowLeft' : 'ArrowRight');
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
startPosition = key === 'Home';
|
|
50
|
+
}
|
|
51
|
+
const cursorPosition = target.selectionStart === target.selectionEnd ? target.selectionStart : null;
|
|
52
|
+
if ((startPosition && cursorPosition !== 0) || (!startPosition && cursorPosition !== target.value.length)) {
|
|
53
|
+
// let the text input process the key
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
};
|
|
59
|
+
const defaultSelector = (directiveElement) => [directiveElement];
|
|
60
|
+
/**
|
|
61
|
+
* Returns a new instance of the navigation manager.
|
|
62
|
+
*
|
|
63
|
+
* The navigation manager simplifies keyboard navigation for a set of DOM elements.
|
|
64
|
+
* It provides a directive to use on some DOM elements, both to add the keydown event handler and to specify which elements should be managed
|
|
65
|
+
* (either by directly putting the directive on those elements, or by putting the directive on a parent element and
|
|
66
|
+
* specifying which child elements should be included through a selector function).
|
|
67
|
+
*
|
|
68
|
+
* It provides some utilities to move the focus between those elements (focusFirst/focusLast, focusLeft/focusRight, focusPrevious/focusNext).
|
|
69
|
+
*
|
|
70
|
+
* @returns a new instance of the navigation manager
|
|
71
|
+
*/
|
|
7
72
|
export const createNavManager = () => {
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
73
|
+
const directiveInstances$ = registrationArray();
|
|
74
|
+
const elementsRefresh$ = writable({});
|
|
75
|
+
const refreshElements = () => elementsRefresh$.set({});
|
|
76
|
+
const elements$ = computed(() => {
|
|
77
|
+
elementsRefresh$();
|
|
78
|
+
const res = [];
|
|
79
|
+
for (const item of directiveInstances$()) {
|
|
80
|
+
res.push(...item());
|
|
81
|
+
}
|
|
82
|
+
return res;
|
|
83
|
+
});
|
|
84
|
+
const commonAncestor$ = computed(() => computeCommonAncestor(elements$()), { equal: Object.is });
|
|
85
|
+
const elementsInDomOrder$ = computed(() => [...elements$()].sort(compareDomOrder));
|
|
86
|
+
const ancestorDirection = () => {
|
|
87
|
+
const commonAncestor = commonAncestor$();
|
|
88
|
+
return commonAncestor ? getTextDirection(commonAncestor) : 'ltr';
|
|
89
|
+
};
|
|
90
|
+
const preventDefaultIfRelevant = (value, event) => {
|
|
91
|
+
if (value) {
|
|
92
|
+
event?.preventDefault();
|
|
93
|
+
}
|
|
94
|
+
return value;
|
|
95
|
+
};
|
|
96
|
+
const focusIndex = (index, moveDirection = 0) => {
|
|
97
|
+
const array = elementsInDomOrder$();
|
|
98
|
+
while (index >= 0 && index < array.length) {
|
|
99
|
+
const newItem = array[index];
|
|
100
|
+
if (isFocusable(newItem)) {
|
|
101
|
+
newItem.focus();
|
|
102
|
+
if (moveDirection != 0 && isTextInput(newItem)) {
|
|
103
|
+
const changeDirection = ancestorDirection() !== getTextDirection(newItem);
|
|
104
|
+
const position = moveDirection > 0 !== changeDirection ? 0 : newItem.value.length;
|
|
105
|
+
newItem.setSelectionRange(position, position, position === 0 ? 'forward' : 'backward');
|
|
25
106
|
}
|
|
107
|
+
return newItem;
|
|
26
108
|
}
|
|
27
|
-
if (
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
109
|
+
if (moveDirection === 0) {
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
index += moveDirection;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
};
|
|
118
|
+
const createFocusNeighbour = (moveDirection) => ({ event, referenceElement = event?.target ?? document.activeElement, } = {}) => {
|
|
119
|
+
const curIndex = referenceElement ? elementsInDomOrder$().indexOf(referenceElement) : -1;
|
|
120
|
+
if (curIndex > -1) {
|
|
121
|
+
return preventDefaultIfRelevant(focusIndex(curIndex + moveDirection, moveDirection), event);
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
};
|
|
125
|
+
const directive = (directiveElement, config) => {
|
|
126
|
+
const onKeyDown = (event) => {
|
|
127
|
+
if (isInternalInputNavigation(event)) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const keyName = getKeyName(event);
|
|
131
|
+
const handler = config.keys?.[keyName];
|
|
132
|
+
if (handler) {
|
|
133
|
+
refreshElements();
|
|
134
|
+
handler({ event, directiveElement, navManager });
|
|
40
135
|
}
|
|
41
136
|
};
|
|
42
|
-
|
|
43
|
-
const unregister =
|
|
137
|
+
directiveElement.addEventListener('keydown', onKeyDown);
|
|
138
|
+
const unregister = directiveInstances$.register(() => (config?.selector ?? defaultSelector)(directiveElement));
|
|
44
139
|
return {
|
|
140
|
+
update(newConfig) {
|
|
141
|
+
config = newConfig;
|
|
142
|
+
},
|
|
45
143
|
destroy() {
|
|
46
|
-
|
|
144
|
+
directiveElement.removeEventListener('keydown', onKeyDown);
|
|
47
145
|
unregister();
|
|
48
146
|
},
|
|
49
147
|
};
|
|
50
148
|
};
|
|
51
|
-
|
|
149
|
+
const focusPrevious = createFocusNeighbour(-1);
|
|
150
|
+
const focusNext = createFocusNeighbour(1);
|
|
151
|
+
const focusFirst = ({ event } = {}) => preventDefaultIfRelevant(focusIndex(0, 1), event);
|
|
152
|
+
const focusLast = ({ event } = {}) => preventDefaultIfRelevant(focusIndex(elementsInDomOrder$().length - 1, -1), event);
|
|
153
|
+
const focusLeft = (...args) => (ancestorDirection() === 'rtl' ? focusNext : focusPrevious)(...args);
|
|
154
|
+
const focusRight = (...args) => (ancestorDirection() === 'rtl' ? focusPrevious : focusNext)(...args);
|
|
155
|
+
const navManager = {
|
|
156
|
+
elementsInDomOrder$,
|
|
157
|
+
directive,
|
|
158
|
+
focusIndex,
|
|
159
|
+
focusPrevious,
|
|
160
|
+
focusNext,
|
|
161
|
+
focusFirst,
|
|
162
|
+
focusLast,
|
|
163
|
+
focusLeft,
|
|
164
|
+
focusRight,
|
|
165
|
+
refreshElements,
|
|
166
|
+
};
|
|
167
|
+
return navManager;
|
|
52
168
|
};
|
package/services/portal.js
CHANGED
|
@@ -8,14 +8,18 @@ export const portal = (content, newArg) => {
|
|
|
8
8
|
}
|
|
9
9
|
};
|
|
10
10
|
const update = (newArg) => {
|
|
11
|
-
if (newArg !== arg) {
|
|
11
|
+
if (newArg !== arg && (newArg?.container !== arg?.container || newArg?.insertBefore !== arg?.insertBefore)) {
|
|
12
12
|
arg = newArg;
|
|
13
13
|
const container = arg?.container ?? arg?.insertBefore?.parentElement;
|
|
14
14
|
if (container) {
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
const insertBefore = arg?.insertBefore ?? null;
|
|
16
|
+
const moveNeeded = content.parentElement !== container || content.nextSibling !== insertBefore;
|
|
17
|
+
if (moveNeeded) {
|
|
18
|
+
if (!replaceComment) {
|
|
19
|
+
replaceComment = content.parentNode?.insertBefore(content.ownerDocument.createComment('portal'), content);
|
|
20
|
+
}
|
|
21
|
+
container.insertBefore(content, insertBefore);
|
|
17
22
|
}
|
|
18
|
-
container.insertBefore(content, arg?.insertBefore ?? null);
|
|
19
23
|
}
|
|
20
24
|
else {
|
|
21
25
|
removeReplaceComment();
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type { Directive } from '../types';
|
|
1
2
|
/**
|
|
2
3
|
* sliblingsInert directive
|
|
3
4
|
* When used on an element, all siblings of the element and of its ancestors will be inert with the inert attribute.
|
|
4
5
|
* In case it is used on multiple elements, only the last one has an effect (the directive keeps a stack of elements
|
|
5
6
|
* on which it is used, so when the last one disappears, the previous one in the list becomes the one in effect).
|
|
6
7
|
*/
|
|
7
|
-
export declare const sliblingsInert:
|
|
8
|
+
export declare const sliblingsInert: Directive;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { computed } from '@amadeus-it-group/tansu';
|
|
2
|
-
import { noop } from '../utils';
|
|
3
|
-
import { createStoreArrayDirective, directiveSubscribe, mergeDirectives } from '
|
|
2
|
+
import { noop } from '../utils/internal/func';
|
|
3
|
+
import { createStoreArrayDirective, directiveSubscribe, mergeDirectives } from '../utils/directive';
|
|
4
4
|
const internalSetSiblingsInert = (element) => {
|
|
5
5
|
const inertValues = new Map();
|
|
6
6
|
const recursiveHelper = (element) => {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { batch, computed, derived, writable } from '@amadeus-it-group/tansu';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
2
|
+
import { typeBoolean, typeBooleanOrNull, typeFunction } from '../../utils/writables';
|
|
3
|
+
import { promiseWithResolve } from '../../utils/internal/promise';
|
|
4
|
+
import { noop } from '../../utils/internal/func';
|
|
5
|
+
import { bindableDerived, stateStores, writablesForProps } from '../../utils/stores';
|
|
6
|
+
import { createStoreDirective, directiveSubscribe, directiveUpdate, mergeDirectives } from '../../utils/directive';
|
|
5
7
|
const neverEndingPromise = new Promise(noop);
|
|
6
8
|
export const noAnimation = async (element, direction) => {
|
|
7
9
|
element.style.display = direction === 'show' ? '' : 'none';
|
|
@@ -23,13 +25,8 @@ const configValidator = {
|
|
|
23
25
|
transition: typeFunction,
|
|
24
26
|
onShown: typeFunction,
|
|
25
27
|
onHidden: typeFunction,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
let resolve;
|
|
29
|
-
const promise = new Promise((r) => {
|
|
30
|
-
resolve = r;
|
|
31
|
-
});
|
|
32
|
-
return { promise, resolve: resolve };
|
|
28
|
+
onVisibleChange: typeFunction,
|
|
29
|
+
initDone: typeBooleanOrNull,
|
|
33
30
|
};
|
|
34
31
|
export const createTransition = (config) => {
|
|
35
32
|
const [{ animation$, initDone$, visible$: requestedVisible$, transition$, onShown$, onHidden$, onVisibleChange$, animationOnInit$ }, patch] = writablesForProps(defaultValues, config, configValidator);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const fadeTransition: import("../baseTransitions").TransitionFn;
|