@agnos-ui/core 0.0.1-alpha.0 → 0.0.1-alpha.2

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.
Files changed (73) hide show
  1. package/README.md +4 -4
  2. package/{dist/lib/accordion.d.ts → accordion.d.ts} +17 -22
  3. package/{dist/lib/accordion.js → accordion.js} +66 -68
  4. package/{dist/lib/alert.d.ts → alert.d.ts} +2 -1
  5. package/{dist/lib/alert.js → alert.js} +10 -7
  6. package/commonProps.d.ts +6 -0
  7. package/commonProps.js +1 -0
  8. package/{dist/lib/config.d.ts → config.d.ts} +5 -0
  9. package/extendWidget.d.ts +3 -0
  10. package/extendWidget.js +28 -0
  11. package/{dist/lib/index.d.ts → index.d.ts} +3 -0
  12. package/{dist/lib/index.js → index.js} +3 -0
  13. package/{dist/lib/modal → modal}/modal.d.ts +3 -9
  14. package/{dist/lib/modal → modal}/modal.js +18 -14
  15. package/package.json +14 -28
  16. package/{dist/lib/pagination.d.ts → pagination.d.ts} +4 -12
  17. package/{dist/lib/pagination.js → pagination.js} +1 -1
  18. package/progressbar.d.ts +87 -0
  19. package/progressbar.js +78 -0
  20. package/{dist/lib/rating.d.ts → rating.d.ts} +5 -11
  21. package/{dist/lib/select.d.ts → select.d.ts} +2 -5
  22. package/{dist/lib/services → services}/checks.d.ts +5 -0
  23. package/{dist/lib/services → services}/checks.js +5 -0
  24. package/{dist/lib/services → services}/index.d.ts +2 -0
  25. package/{dist/lib/services → services}/index.js +2 -0
  26. package/services/intersection.d.ts +26 -0
  27. package/services/intersection.js +47 -0
  28. package/services/navManager.d.ts +5 -0
  29. package/services/navManager.js +52 -0
  30. package/services/sortUtils.d.ts +2 -0
  31. package/services/sortUtils.js +14 -0
  32. package/{dist/lib/services → services}/stores.d.ts +60 -23
  33. package/{dist/lib/services → services}/stores.js +92 -53
  34. package/{dist/lib/services → services}/writables.d.ts +2 -1
  35. package/{dist/lib/services → services}/writables.js +15 -1
  36. package/dist/lib/tsdoc-metadata.json +0 -11
  37. /package/{dist/lib/config.js → config.js} +0 -0
  38. /package/{dist/lib/modal → modal}/scrollbars.d.ts +0 -0
  39. /package/{dist/lib/modal → modal}/scrollbars.js +0 -0
  40. /package/{dist/lib/pagination.utils.d.ts → pagination.utils.d.ts} +0 -0
  41. /package/{dist/lib/pagination.utils.js → pagination.utils.js} +0 -0
  42. /package/{dist/lib/rating.js → rating.js} +0 -0
  43. /package/{dist/lib/select.js → select.js} +0 -0
  44. /package/{dist/lib/services → services}/directiveUtils.d.ts +0 -0
  45. /package/{dist/lib/services → services}/directiveUtils.js +0 -0
  46. /package/{dist/lib/services → services}/focustrack.d.ts +0 -0
  47. /package/{dist/lib/services → services}/focustrack.js +0 -0
  48. /package/{dist/lib/services → services}/portal.d.ts +0 -0
  49. /package/{dist/lib/services → services}/portal.js +0 -0
  50. /package/{dist/lib/services → services}/siblingsInert.d.ts +0 -0
  51. /package/{dist/lib/services → services}/siblingsInert.js +0 -0
  52. /package/{dist/lib/transitions → transitions}/baseTransitions.d.ts +0 -0
  53. /package/{dist/lib/transitions → transitions}/baseTransitions.js +0 -0
  54. /package/{dist/lib/transitions → transitions}/bootstrap/collapse.d.ts +0 -0
  55. /package/{dist/lib/transitions → transitions}/bootstrap/collapse.js +0 -0
  56. /package/{dist/lib/transitions → transitions}/bootstrap/fade.d.ts +0 -0
  57. /package/{dist/lib/transitions → transitions}/bootstrap/fade.js +0 -0
  58. /package/{dist/lib/transitions → transitions}/bootstrap/index.d.ts +0 -0
  59. /package/{dist/lib/transitions → transitions}/bootstrap/index.js +0 -0
  60. /package/{dist/lib/transitions → transitions}/collapse.d.ts +0 -0
  61. /package/{dist/lib/transitions → transitions}/collapse.js +0 -0
  62. /package/{dist/lib/transitions → transitions}/cssTransitions.d.ts +0 -0
  63. /package/{dist/lib/transitions → transitions}/cssTransitions.js +0 -0
  64. /package/{dist/lib/transitions → transitions}/index.d.ts +0 -0
  65. /package/{dist/lib/transitions → transitions}/index.js +0 -0
  66. /package/{dist/lib/transitions → transitions}/simpleClassTransition.d.ts +0 -0
  67. /package/{dist/lib/transitions → transitions}/simpleClassTransition.js +0 -0
  68. /package/{dist/lib/transitions → transitions}/utils.d.ts +0 -0
  69. /package/{dist/lib/transitions → transitions}/utils.js +0 -0
  70. /package/{dist/lib/types.d.ts → types.d.ts} +0 -0
  71. /package/{dist/lib/types.js → types.js} +0 -0
  72. /package/{dist/lib/utils.d.ts → utils.d.ts} +0 -0
  73. /package/{dist/lib/utils.js → utils.js} +0 -0
package/package.json CHANGED
@@ -1,52 +1,38 @@
1
1
  {
2
2
  "name": "@agnos-ui/core",
3
3
  "description": "Framework-agnostic headless widget library.",
4
- "homepage": "https://amadeusitgroup.github.io/AgnosUI/latest/",
5
4
  "keywords": [
6
5
  "headless",
7
6
  "agnostic",
8
7
  "components",
9
8
  "widgets",
9
+ "accordion",
10
10
  "alert",
11
11
  "modal",
12
12
  "pagination",
13
13
  "rating"
14
14
  ],
15
15
  "type": "module",
16
- "main": "./dist/lib/index.js",
17
- "module": "./dist/lib/index.js",
18
- "types": "./dist/lib/index.d.ts",
16
+ "main": "./index.js",
17
+ "module": "./index.js",
18
+ "types": "./index.d.ts",
19
19
  "exports": {
20
20
  ".": {
21
- "types": "./dist/lib/index.d.ts",
22
- "default": "./dist/lib/index.js"
21
+ "types": "./index.d.ts",
22
+ "default": "./index.js"
23
23
  }
24
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
25
  "dependencies": {
36
- "@amadeus-it-group/tansu": "0.0.22"
26
+ "@amadeus-it-group/tansu": "0.0.23"
37
27
  },
38
- "files": [
39
- "dist/lib"
40
- ],
41
- "license": "MIT",
28
+ "sideEffects": false,
29
+ "version": "v0.0.1-alpha.2",
30
+ "homepage": "https://amadeusitgroup.github.io/AgnosUI/latest/",
42
31
  "bugs": "https://github.com/AmadeusITGroup/AgnosUI/issues",
32
+ "license": "MIT",
43
33
  "repository": {
44
34
  "type": "git",
45
35
  "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
- }
36
+ "directory": "core/lib"
37
+ }
38
+ }
@@ -1,5 +1,6 @@
1
1
  import type { PropsConfig } from './services';
2
2
  import type { Widget, SlotContent, WidgetSlotContext } from './types';
3
+ import type { WidgetsCommonPropsAndState } from './commonProps';
3
4
  /**
4
5
  * A type for the slot context of the pagination widget
5
6
  */
@@ -13,7 +14,7 @@ export interface PaginationNumberContext extends PaginationContext {
13
14
  */
14
15
  displayedPage: number;
15
16
  }
16
- export interface PaginationCommonPropsAndState {
17
+ export interface PaginationCommonPropsAndState extends WidgetsCommonPropsAndState {
17
18
  /**
18
19
  * The current page.
19
20
  *
@@ -86,11 +87,6 @@ export interface PaginationCommonPropsAndState {
86
87
  * @defaultValue false
87
88
  */
88
89
  boundaryLinks: boolean;
89
- /**
90
- * An input to add a custom class to the UL
91
- * @defaultValue ''
92
- */
93
- className: string;
94
90
  /**
95
91
  * The template to use for the ellipsis slot
96
92
  * for I18n, we suggest to use the global configuration
@@ -398,11 +394,6 @@ export declare function getPaginationDefaultConfig(): {
398
394
  * @defaultValue false
399
395
  */
400
396
  boundaryLinks: boolean;
401
- /**
402
- * An input to add a custom class to the UL
403
- * @defaultValue ''
404
- */
405
- className: string;
406
397
  /**
407
398
  * The template to use for the ellipsis slot
408
399
  * for I18n, we suggest to use the global configuration
@@ -455,10 +446,11 @@ export declare function getPaginationDefaultConfig(): {
455
446
  * @param displayedPage - The current page number
456
447
  */
457
448
  slotNumberLabel: SlotContent<PaginationNumberContext>;
449
+ className: string;
458
450
  };
459
451
  /**
460
452
  * Create a PaginationWidget with given config props
461
- * @param config - an optional alert config
453
+ * @param config - an optional pagination config
462
454
  * @returns a PaginationWidget
463
455
  */
464
456
  export declare function createPagination(config?: PropsConfig<PaginationProps>): PaginationWidget;
@@ -64,7 +64,7 @@ const configValidator = {
64
64
  };
65
65
  /**
66
66
  * Create a PaginationWidget with given config props
67
- * @param config - an optional alert config
67
+ * @param config - an optional pagination config
68
68
  * @returns a PaginationWidget
69
69
  */
70
70
  export function createPagination(config) {
@@ -0,0 +1,87 @@
1
+ import type { PropsConfig } from './services';
2
+ import type { SlotContent, Widget, WidgetSlotContext } from './types';
3
+ import type { WidgetsCommonPropsAndState } from './commonProps';
4
+ export type ProgressbarContext = WidgetSlotContext<ProgressbarWidget>;
5
+ export interface ProgressbarCommonPropsAndState extends WidgetsCommonPropsAndState {
6
+ /**
7
+ * The minimum value.
8
+ * @defaultValue 0
9
+ */
10
+ min: number;
11
+ /**
12
+ * The maximum value.
13
+ * @defaultValue 100
14
+ */
15
+ max: number;
16
+ /**
17
+ * The current value.
18
+ * @defaultValue 0
19
+ */
20
+ value: number;
21
+ /**
22
+ * The aria label.
23
+ */
24
+ ariaLabel: string;
25
+ /**
26
+ * Global template for the Progressbar content.
27
+ */
28
+ slotContent: SlotContent<ProgressbarContext>;
29
+ /**
30
+ * Label of the progress.
31
+ */
32
+ slotDefault: SlotContent<ProgressbarContext>;
33
+ /**
34
+ * Height of the progressbar, can be any valid css height value.
35
+ */
36
+ height: string;
37
+ /**
38
+ * If `true`, shows a striped progressbar.
39
+ */
40
+ striped: boolean;
41
+ /**
42
+ * If `true`, animates a striped progressbar.
43
+ * Takes effect only for browsers supporting CSS3 animations, and if `striped` is `true`.
44
+ */
45
+ animated: boolean;
46
+ }
47
+ export interface ProgressbarState extends ProgressbarCommonPropsAndState {
48
+ /**
49
+ * Percentage of completion.
50
+ */
51
+ percentage: number;
52
+ /**
53
+ * `true` if the value is above its minimum value.
54
+ */
55
+ started: boolean;
56
+ /**
57
+ * `true` if the value has reached its maximum value.
58
+ */
59
+ finished: boolean;
60
+ /**
61
+ * The aria value text.
62
+ */
63
+ ariaValueText: string | undefined;
64
+ }
65
+ export interface ProgressbarProps extends ProgressbarCommonPropsAndState {
66
+ /**
67
+ * Return the value for the 'aria-valuetext' attribute.
68
+ * @param value - current value
69
+ * @param minimum - minimum value
70
+ * @param maximum - maximum value
71
+ */
72
+ ariaValueTextFn: (value: number, minimum: number, maximum: number) => string | undefined;
73
+ }
74
+ export interface ProgressbarApi {
75
+ }
76
+ export type ProgressbarWidget = Widget<ProgressbarProps, ProgressbarState, ProgressbarApi, object, object>;
77
+ /**
78
+ * Retrieve a shallow copy of the default Progressbar config
79
+ * @returns the default Progressbar config
80
+ */
81
+ export declare function getProgressbarDefaultConfig(): ProgressbarProps;
82
+ /**
83
+ * Create an ProgressbarWidget with given config props
84
+ * @param config - an optional progress bar config
85
+ * @returns an ProgressbarWidget
86
+ */
87
+ export declare function createProgressbar(config?: PropsConfig<ProgressbarProps>): ProgressbarWidget;
package/progressbar.js ADDED
@@ -0,0 +1,78 @@
1
+ import { clamp } from './services/checks';
2
+ import { bindableDerived, stateStores, typeBoolean, typeFunction, typeNumber, typeString, writablesForProps } from './services';
3
+ import { computed, readable } from '@amadeus-it-group/tansu';
4
+ import { noop } from './utils';
5
+ const defaultConfig = {
6
+ min: 0,
7
+ max: 100,
8
+ value: 0,
9
+ ariaLabel: 'Progressbar',
10
+ className: '',
11
+ slotContent: undefined,
12
+ slotDefault: undefined,
13
+ height: '',
14
+ striped: false,
15
+ animated: false,
16
+ ariaValueTextFn: () => undefined,
17
+ };
18
+ /**
19
+ * Retrieve a shallow copy of the default Progressbar config
20
+ * @returns the default Progressbar config
21
+ */
22
+ export function getProgressbarDefaultConfig() {
23
+ return { ...defaultConfig };
24
+ }
25
+ const configValidator = {
26
+ min: typeNumber,
27
+ max: typeNumber,
28
+ value: typeNumber,
29
+ ariaLabel: typeString,
30
+ className: typeString,
31
+ height: typeString,
32
+ striped: typeBoolean,
33
+ animated: typeBoolean,
34
+ ariaValueTextFn: typeFunction,
35
+ };
36
+ /**
37
+ * Create an ProgressbarWidget with given config props
38
+ * @param config - an optional progress bar config
39
+ * @returns an ProgressbarWidget
40
+ */
41
+ export function createProgressbar(config) {
42
+ const [{
43
+ // dirty inputs that need adjustment:
44
+ max$: _dirtyMaximum$, value$: _dirtyValue$,
45
+ // clean inputs
46
+ min$, ariaValueTextFn$, ...stateProps }, patch,] = writablesForProps(defaultConfig, config, configValidator);
47
+ const max$ = bindableDerived(readable(noop), [_dirtyMaximum$, min$], ([dirtyMaximum, minimum]) => Math.max(minimum, dirtyMaximum));
48
+ const value$ = bindableDerived(readable(noop), [_dirtyValue$, min$, max$], ([dirtyValue, min, max]) => clamp(dirtyValue, max, min));
49
+ const percentage$ = computed(() => {
50
+ const max = max$();
51
+ const min = min$();
52
+ if (max > min) {
53
+ return clamp(((value$() - min) * 100) / (max - min), 100, 0);
54
+ }
55
+ else {
56
+ return 0;
57
+ }
58
+ });
59
+ const started$ = computed(() => value$() > min$());
60
+ const finished$ = computed(() => value$() === max$());
61
+ const ariaValueText$ = computed(() => ariaValueTextFn$()(value$(), min$(), max$()));
62
+ return {
63
+ ...stateStores({
64
+ min$,
65
+ max$,
66
+ value$,
67
+ percentage$,
68
+ started$,
69
+ finished$,
70
+ ariaValueText$,
71
+ ...stateProps,
72
+ }),
73
+ patch,
74
+ api: {},
75
+ directives: {},
76
+ actions: {},
77
+ };
78
+ }
@@ -1,5 +1,6 @@
1
1
  import type { PropsConfig } from './services';
2
2
  import type { SlotContent, Widget } from './types';
3
+ import type { WidgetsCommonPropsAndState } from './commonProps';
3
4
  export interface StarContext {
4
5
  /**
5
6
  * indicates how much the current star is filled, from 0 to 100
@@ -10,7 +11,7 @@ export interface StarContext {
10
11
  */
11
12
  index: number;
12
13
  }
13
- export interface RatingCommonPropsAndState {
14
+ export interface RatingCommonPropsAndState extends WidgetsCommonPropsAndState {
14
15
  /**
15
16
  * The current rating. Could be a decimal value like `3.75`.
16
17
  */
@@ -38,10 +39,6 @@ export interface RatingCommonPropsAndState {
38
39
  * If the component is disabled, `tabindex` will still be set to `-1`.
39
40
  */
40
41
  tabindex: number;
41
- /**
42
- * Classname to be applied on the rating container
43
- */
44
- className: string;
45
42
  /**
46
43
  * The template to override the way each star is displayed.
47
44
  */
@@ -57,7 +54,7 @@ export interface RatingCommonPropsAndState {
57
54
  }
58
55
  export interface RatingProps extends RatingCommonPropsAndState {
59
56
  /**
60
- * Return the value for the 'aria-value' attribute.
57
+ * Return the value for the 'aria-valuetext' attribute.
61
58
  * @param rating - Current rating value.
62
59
  * @param maxRating - maxRating value.
63
60
  */
@@ -134,7 +131,7 @@ export type RatingWidget = Widget<RatingProps, RatingState, object, RatingAction
134
131
  */
135
132
  export declare function getRatingDefaultConfig(): {
136
133
  /**
137
- * Return the value for the 'aria-value' attribute.
134
+ * Return the value for the 'aria-valuetext' attribute.
138
135
  * @param rating - Current rating value.
139
136
  * @param maxRating - maxRating value.
140
137
  */
@@ -184,10 +181,6 @@ export declare function getRatingDefaultConfig(): {
184
181
  * If the component is disabled, `tabindex` will still be set to `-1`.
185
182
  */
186
183
  tabindex: number;
187
- /**
188
- * Classname to be applied on the rating container
189
- */
190
- className: string;
191
184
  /**
192
185
  * The template to override the way each star is displayed.
193
186
  */
@@ -200,6 +193,7 @@ export declare function getRatingDefaultConfig(): {
200
193
  * The aria labelled by
201
194
  */
202
195
  ariaLabelledBy: string;
196
+ className: string;
203
197
  };
204
198
  /**
205
199
  * Create a RatingWidget with given config props
@@ -1,11 +1,8 @@
1
1
  import type { HasFocus } from './services/focustrack';
2
2
  import type { PropsConfig } from './services/stores';
3
3
  import type { Widget } from './types';
4
- export interface SelectCommonPropsAndState<Item> {
5
- /**
6
- * the class to attach to the select DOM element
7
- */
8
- className: string;
4
+ import type { WidgetsCommonPropsAndState } from './commonProps';
5
+ export interface SelectCommonPropsAndState<Item> extends WidgetsCommonPropsAndState {
9
6
  /**
10
7
  * List of selected items
11
8
  */
@@ -22,6 +22,11 @@ export declare function isFunction(value: any): value is (...args: any[]) => any
22
22
  * @returns true if the value is a string
23
23
  */
24
24
  export declare function isString(value: any): value is string;
25
+ /**
26
+ * an array type guard
27
+ * @returns true if the value is an array
28
+ */
29
+ export declare const isArray: (arg: any) => arg is any[];
25
30
  /**
26
31
  * Clamp the value based on a maximum and optional minimum
27
32
  * @param value the value to check
@@ -30,6 +30,11 @@ export function isFunction(value) {
30
30
  export function isString(value) {
31
31
  return typeof value === 'string';
32
32
  }
33
+ /**
34
+ * an array type guard
35
+ * @returns true if the value is an array
36
+ */
37
+ export const isArray = Array.isArray;
33
38
  // TODO should we check that max > min?
34
39
  /**
35
40
  * Clamp the value based on a maximum and optional minimum
@@ -1,6 +1,8 @@
1
1
  export * from './siblingsInert';
2
2
  export * from './directiveUtils';
3
3
  export * from './focustrack';
4
+ export * from './intersection';
4
5
  export * from './portal';
5
6
  export * from './stores';
6
7
  export * from './writables';
8
+ export * from './navManager';
@@ -1,6 +1,8 @@
1
1
  export * from './siblingsInert';
2
2
  export * from './directiveUtils';
3
3
  export * from './focustrack';
4
+ export * from './intersection';
4
5
  export * from './portal';
5
6
  export * from './stores';
6
7
  export * from './writables';
8
+ export * from './navManager';
@@ -0,0 +1,26 @@
1
+ import type { PropsConfig } from './stores';
2
+ export interface IntersectionProps {
3
+ /**
4
+ * elements to observe
5
+ */
6
+ elements: HTMLElement[];
7
+ /**
8
+ * IntersectionObserverInit used in the IntersectionObserver
9
+ *
10
+ * See the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver#options)
11
+ */
12
+ options: Partial<IntersectionObserverInit> | undefined;
13
+ }
14
+ export declare const createIntersection: (config?: PropsConfig<IntersectionProps>) => {
15
+ /**
16
+ * Readable of observed elements
17
+ */
18
+ elements$: import("@amadeus-it-group/tansu").ReadableSignal<HTMLElement[]>;
19
+ /**
20
+ * Store of map that contains the visible elements (for the key) and the corresponding entries
21
+ *
22
+ * See the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry)
23
+ */
24
+ visibleElements$: import("@amadeus-it-group/tansu").ReadableSignal<Map<Element, IntersectionObserverEntry>>;
25
+ patch: <U extends Partial<IntersectionProps>>(storesValues?: void | U | undefined) => void;
26
+ };
@@ -0,0 +1,47 @@
1
+ import { asReadable, derived } from '@amadeus-it-group/tansu';
2
+ import { noop } from '../utils';
3
+ import { writablesForProps } from './stores';
4
+ const defaultValues = {
5
+ elements: [],
6
+ options: undefined,
7
+ };
8
+ export const createIntersection = (config) => {
9
+ const [{ elements$, options$ }, patch] = writablesForProps(defaultValues, config);
10
+ const visibleElements$ = derived([elements$, options$], ([elements, options], set) => {
11
+ if (elements.length) {
12
+ const visibleElements = new Map();
13
+ const observer = new IntersectionObserver((entries) => {
14
+ for (const entry of entries) {
15
+ const { target, isIntersecting } = entry;
16
+ if (isIntersecting) {
17
+ visibleElements.set(target, entry);
18
+ }
19
+ else {
20
+ visibleElements.delete(target);
21
+ }
22
+ }
23
+ set(visibleElements);
24
+ }, options);
25
+ for (const element of elements) {
26
+ observer.observe(element);
27
+ }
28
+ return () => {
29
+ observer.disconnect();
30
+ };
31
+ }
32
+ return noop;
33
+ }, new Map());
34
+ return {
35
+ /**
36
+ * Readable of observed elements
37
+ */
38
+ elements$: asReadable(elements$),
39
+ /**
40
+ * Store of map that contains the visible elements (for the key) and the corresponding entries
41
+ *
42
+ * See the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry)
43
+ */
44
+ visibleElements$: asReadable(visibleElements$),
45
+ patch,
46
+ };
47
+ };
@@ -0,0 +1,5 @@
1
+ import type { Directive } from '../types';
2
+ export type NavManager = ReturnType<typeof createNavManager>;
3
+ export declare const createNavManager: () => {
4
+ directive: Directive;
5
+ };
@@ -0,0 +1,52 @@
1
+ import { compareDomOrder } from './sortUtils';
2
+ import { registrationArray } from './directiveUtils';
3
+ import { computed } from '@amadeus-it-group/tansu';
4
+ // cf https://html.spec.whatwg.org/multipage/input.html#concept-input-apply
5
+ const textInputTypes = new Set(['text', 'search', 'url', 'tel', 'password']);
6
+ const isTextInput = (element) => element instanceof HTMLInputElement && textInputTypes.has(element.type);
7
+ export const createNavManager = () => {
8
+ const array$ = registrationArray();
9
+ const sortedArray$ = computed(() => [...array$()].sort(compareDomOrder));
10
+ const directive = (element) => {
11
+ const onKeyDown = (event) => {
12
+ let move = 0;
13
+ switch (event.key) {
14
+ case 'ArrowLeft':
15
+ move = -1;
16
+ break;
17
+ case 'ArrowRight':
18
+ move = 1;
19
+ break;
20
+ }
21
+ if (isTextInput(event.target)) {
22
+ const cursorPosition = event.target.selectionStart === event.target.selectionEnd ? event.target.selectionStart : null;
23
+ if ((cursorPosition !== 0 && move < 0) || (cursorPosition !== event.target.value.length && move > 0)) {
24
+ move = 0;
25
+ }
26
+ }
27
+ if (move != 0) {
28
+ const array = sortedArray$();
29
+ const currentIndex = array.indexOf(element);
30
+ const newIndex = currentIndex + move;
31
+ if (newIndex < array.length && newIndex >= 0) {
32
+ const newItem = array[newIndex];
33
+ event.preventDefault();
34
+ newItem.focus();
35
+ if (isTextInput(newItem)) {
36
+ const position = move < 0 ? newItem.value.length : 0;
37
+ newItem.setSelectionRange(position, position);
38
+ }
39
+ }
40
+ }
41
+ };
42
+ element.addEventListener('keydown', onKeyDown);
43
+ const unregister = array$.register(element);
44
+ return {
45
+ destroy() {
46
+ element.removeEventListener('keydown', onKeyDown);
47
+ unregister();
48
+ },
49
+ };
50
+ };
51
+ return { directive };
52
+ };
@@ -0,0 +1,2 @@
1
+ export declare const compareDefault: (a: any, b: any) => 0 | 1 | -1;
2
+ export declare const compareDomOrder: (element1: Node, element2: Node) => 0 | 1 | -1;
@@ -0,0 +1,14 @@
1
+ export const compareDefault = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
2
+ export const compareDomOrder = (element1, element2) => {
3
+ if (element1 === element2) {
4
+ return 0;
5
+ }
6
+ const result = element1.compareDocumentPosition(element2);
7
+ if (result & Node.DOCUMENT_POSITION_FOLLOWING) {
8
+ return -1;
9
+ }
10
+ else if (result & Node.DOCUMENT_POSITION_PRECEDING) {
11
+ return 1;
12
+ }
13
+ throw new Error('failed to compare elements');
14
+ };