@blackcube/aurelia2-bleet 1.0.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/blackcube-aurelia2-bleet-1.0.0.tgz +0 -0
- package/dist/index.es.js +4514 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.js +4549 -0
- package/dist/index.js.map +1 -0
- package/dist/types/attributes/ajaxify-trigger.d.ts +36 -0
- package/dist/types/attributes/ajaxify-trigger.d.ts.map +1 -0
- package/dist/types/attributes/alert.d.ts +15 -0
- package/dist/types/attributes/alert.d.ts.map +1 -0
- package/dist/types/attributes/badge.d.ts +13 -0
- package/dist/types/attributes/badge.d.ts.map +1 -0
- package/dist/types/attributes/burger.d.ts +11 -0
- package/dist/types/attributes/burger.d.ts.map +1 -0
- package/dist/types/attributes/drawer-trigger.d.ts +16 -0
- package/dist/types/attributes/drawer-trigger.d.ts.map +1 -0
- package/dist/types/attributes/dropdown.d.ts +38 -0
- package/dist/types/attributes/dropdown.d.ts.map +1 -0
- package/dist/types/attributes/index.d.ts +16 -0
- package/dist/types/attributes/index.d.ts.map +1 -0
- package/dist/types/attributes/menu.d.ts +32 -0
- package/dist/types/attributes/menu.d.ts.map +1 -0
- package/dist/types/attributes/modal-trigger.d.ts +16 -0
- package/dist/types/attributes/modal-trigger.d.ts.map +1 -0
- package/dist/types/attributes/pager.d.ts +13 -0
- package/dist/types/attributes/pager.d.ts.map +1 -0
- package/dist/types/attributes/password.d.ts +15 -0
- package/dist/types/attributes/password.d.ts.map +1 -0
- package/dist/types/attributes/profile.d.ts +24 -0
- package/dist/types/attributes/profile.d.ts.map +1 -0
- package/dist/types/attributes/select.d.ts +24 -0
- package/dist/types/attributes/select.d.ts.map +1 -0
- package/dist/types/attributes/tabs.d.ts +16 -0
- package/dist/types/attributes/tabs.d.ts.map +1 -0
- package/dist/types/attributes/toaster-trigger.d.ts +19 -0
- package/dist/types/attributes/toaster-trigger.d.ts.map +1 -0
- package/dist/types/attributes/upload.d.ts +57 -0
- package/dist/types/attributes/upload.d.ts.map +1 -0
- package/dist/types/codecs/ajaxify-codec.d.ts +5 -0
- package/dist/types/codecs/ajaxify-codec.d.ts.map +1 -0
- package/dist/types/codecs/csrf-codec.d.ts +7 -0
- package/dist/types/codecs/csrf-codec.d.ts.map +1 -0
- package/dist/types/codecs/request-codec.d.ts +5 -0
- package/dist/types/codecs/request-codec.d.ts.map +1 -0
- package/dist/types/components/bleet-ajaxify.d.ts +17 -0
- package/dist/types/components/bleet-ajaxify.d.ts.map +1 -0
- package/dist/types/components/bleet-ajaxify.html.d.ts +3 -0
- package/dist/types/components/bleet-ajaxify.html.d.ts.map +1 -0
- package/dist/types/components/bleet-drawer.d.ts +40 -0
- package/dist/types/components/bleet-drawer.d.ts.map +1 -0
- package/dist/types/components/bleet-drawer.html.d.ts +3 -0
- package/dist/types/components/bleet-drawer.html.d.ts.map +1 -0
- package/dist/types/components/bleet-modal.d.ts +46 -0
- package/dist/types/components/bleet-modal.d.ts.map +1 -0
- package/dist/types/components/bleet-modal.html.d.ts +3 -0
- package/dist/types/components/bleet-modal.html.d.ts.map +1 -0
- package/dist/types/components/bleet-overlay.d.ts +21 -0
- package/dist/types/components/bleet-overlay.d.ts.map +1 -0
- package/dist/types/components/bleet-quilljs.d.ts +19 -0
- package/dist/types/components/bleet-quilljs.d.ts.map +1 -0
- package/dist/types/components/bleet-quilljs.html.d.ts +3 -0
- package/dist/types/components/bleet-quilljs.html.d.ts.map +1 -0
- package/dist/types/components/bleet-toast.d.ts +26 -0
- package/dist/types/components/bleet-toast.d.ts.map +1 -0
- package/dist/types/components/bleet-toast.html.d.ts +3 -0
- package/dist/types/components/bleet-toast.html.d.ts.map +1 -0
- package/dist/types/components/bleet-toaster-trigger.d.ts +20 -0
- package/dist/types/components/bleet-toaster-trigger.d.ts.map +1 -0
- package/dist/types/components/bleet-toaster.d.ts +15 -0
- package/dist/types/components/bleet-toaster.d.ts.map +1 -0
- package/dist/types/components/bleet-toaster.html.d.ts +3 -0
- package/dist/types/components/bleet-toaster.html.d.ts.map +1 -0
- package/dist/types/components/index.d.ts +9 -0
- package/dist/types/components/index.d.ts.map +1 -0
- package/dist/types/configure.d.ts +35 -0
- package/dist/types/configure.d.ts.map +1 -0
- package/dist/types/enums/api.d.ts +11 -0
- package/dist/types/enums/api.d.ts.map +1 -0
- package/dist/types/enums/event-aggregator.d.ts +123 -0
- package/dist/types/enums/event-aggregator.d.ts.map +1 -0
- package/dist/types/index.d.ts +26 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/interfaces/api.d.ts +56 -0
- package/dist/types/interfaces/api.d.ts.map +1 -0
- package/dist/types/interfaces/dialog.d.ts +18 -0
- package/dist/types/interfaces/dialog.d.ts.map +1 -0
- package/dist/types/interfaces/event-aggregator.d.ts +75 -0
- package/dist/types/interfaces/event-aggregator.d.ts.map +1 -0
- package/dist/types/services/api-service.d.ts +64 -0
- package/dist/types/services/api-service.d.ts.map +1 -0
- package/dist/types/services/http-service.d.ts +22 -0
- package/dist/types/services/http-service.d.ts.map +1 -0
- package/dist/types/services/socketio-service.d.ts +23 -0
- package/dist/types/services/socketio-service.d.ts.map +1 -0
- package/dist/types/services/storage-service.d.ts +13 -0
- package/dist/types/services/storage-service.d.ts.map +1 -0
- package/dist/types/services/svg-service.d.ts +17 -0
- package/dist/types/services/svg-service.d.ts.map +1 -0
- package/dist/types/services/transition-service.d.ts +13 -0
- package/dist/types/services/transition-service.d.ts.map +1 -0
- package/dist/types/services/trap-focus-service.d.ts +28 -0
- package/dist/types/services/trap-focus-service.d.ts.map +1 -0
- package/doc/bleet-api-reference.md +1333 -0
- package/doc/bleet-model-api-reference.md +379 -0
- package/doc/bleet-typescript-api-reference.md +1037 -0
- package/package.json +43 -0
- package/resource.d.ts +22 -0
- package/src/attributes/ajaxify-trigger.ts +218 -0
- package/src/attributes/alert.ts +55 -0
- package/src/attributes/badge.ts +39 -0
- package/src/attributes/burger.ts +36 -0
- package/src/attributes/drawer-trigger.ts +53 -0
- package/src/attributes/dropdown.ts +377 -0
- package/src/attributes/index.ts +15 -0
- package/src/attributes/menu.ts +179 -0
- package/src/attributes/modal-trigger.ts +53 -0
- package/src/attributes/pager.ts +43 -0
- package/src/attributes/password.ts +47 -0
- package/src/attributes/profile.ts +112 -0
- package/src/attributes/select.ts +214 -0
- package/src/attributes/tabs.ts +99 -0
- package/src/attributes/toaster-trigger.ts +54 -0
- package/src/attributes/upload.ts +380 -0
- package/src/codecs/ajaxify-codec.ts +16 -0
- package/src/codecs/csrf-codec.ts +41 -0
- package/src/codecs/request-codec.ts +16 -0
- package/src/components/bleet-ajaxify.html.ts +4 -0
- package/src/components/bleet-ajaxify.ts +62 -0
- package/src/components/bleet-drawer.html.ts +36 -0
- package/src/components/bleet-drawer.ts +236 -0
- package/src/components/bleet-modal.html.ts +30 -0
- package/src/components/bleet-modal.ts +274 -0
- package/src/components/bleet-overlay.ts +111 -0
- package/src/components/bleet-quilljs.html.ts +4 -0
- package/src/components/bleet-quilljs.ts +73 -0
- package/src/components/bleet-toast.html.ts +44 -0
- package/src/components/bleet-toast.ts +133 -0
- package/src/components/bleet-toaster-trigger.ts +66 -0
- package/src/components/bleet-toaster.html.ts +11 -0
- package/src/components/bleet-toaster.ts +72 -0
- package/src/components/index.ts +8 -0
- package/src/configure.ts +121 -0
- package/src/enums/api.ts +12 -0
- package/src/enums/event-aggregator.ts +131 -0
- package/src/index.ts +220 -0
- package/src/interfaces/api.ts +64 -0
- package/src/interfaces/dialog.ts +25 -0
- package/src/interfaces/event-aggregator.ts +88 -0
- package/src/services/api-service.ts +387 -0
- package/src/services/http-service.ts +166 -0
- package/src/services/socketio-service.ts +138 -0
- package/src/services/storage-service.ts +36 -0
- package/src/services/svg-service.ts +35 -0
- package/src/services/transition-service.ts +39 -0
- package/src/services/trap-focus-service.ts +213 -0
- package/src/types/css.d.ts +4 -0
- package/src/types/html.d.ts +12 -0
- package/src/types/svg.d.ts +4 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {DI, ILogger, IPlatform, resolve} from "aurelia";
|
|
2
|
+
|
|
3
|
+
export interface IStorageService extends StorageService { }
|
|
4
|
+
export const IStorageService = /*@__PURE__*/DI.createInterface<IStorageService>('IStorageService', (x) => x.singleton(StorageService));
|
|
5
|
+
|
|
6
|
+
export class StorageService
|
|
7
|
+
{
|
|
8
|
+
public constructor(
|
|
9
|
+
private readonly logger: ILogger = resolve(ILogger).scopeTo('StorageService'),
|
|
10
|
+
private readonly platform:IPlatform = resolve(IPlatform),
|
|
11
|
+
) {
|
|
12
|
+
this.logger.trace('constructor')
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public load(key: string, def: any = null): any
|
|
16
|
+
{
|
|
17
|
+
this.logger.trace('load', key);
|
|
18
|
+
const value = localStorage.getItem(key);
|
|
19
|
+
if (value === null) {
|
|
20
|
+
return def;
|
|
21
|
+
}
|
|
22
|
+
return JSON.parse(value);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public save(key: string, value: any): void
|
|
26
|
+
{
|
|
27
|
+
this.logger.trace('save', key, value);
|
|
28
|
+
localStorage.setItem(key, JSON.stringify(value));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public remove(key: string): void
|
|
32
|
+
{
|
|
33
|
+
this.logger.trace('remove', key);
|
|
34
|
+
localStorage.removeItem(key);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { DI, ILogger, resolve } from "aurelia";
|
|
2
|
+
|
|
3
|
+
export interface ISvgService extends SvgService { }
|
|
4
|
+
export const ISvgService = /*@__PURE__*/DI.createInterface<ISvgService>('ISvgService', (x) => x.singleton(SvgService));
|
|
5
|
+
|
|
6
|
+
export class SvgService {
|
|
7
|
+
private static readonly ICONS: Record<string, string> = {
|
|
8
|
+
'information-circle': `<svg class="size-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path fill-rule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm8.706-1.442c1.146-.573 2.437.463 2.126 1.706l-.709 2.836.042-.02a.75.75 0 0 1 .67 1.34l-.04.022c-1.147.573-2.438-.463-2.127-1.706l.71-2.836-.042.02a.75.75 0 1 1-.671-1.34l.041-.022ZM12 9a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z" clip-rule="evenodd"/></svg>`,
|
|
9
|
+
'check-circle': `<svg class="size-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path fill-rule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm13.36-1.814a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z" clip-rule="evenodd"/></svg>`,
|
|
10
|
+
'exclamation-triangle': `<svg class="size-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path fill-rule="evenodd" d="M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003ZM12 8.25a.75.75 0 0 1 .75.75v3.75a.75.75 0 0 1-1.5 0V9a.75.75 0 0 1 .75-.75Zm0 8.25a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z" clip-rule="evenodd"/></svg>`,
|
|
11
|
+
'x-circle': `<svg class="size-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path fill-rule="evenodd" d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25Zm-1.72 6.97a.75.75 0 1 0-1.06 1.06L10.94 12l-1.72 1.72a.75.75 0 1 0 1.06 1.06L12 13.06l1.72 1.72a.75.75 0 1 0 1.06-1.06L13.06 12l1.72-1.72a.75.75 0 1 0-1.06-1.06L12 10.94l-1.72-1.72Z" clip-rule="evenodd"/></svg>`,
|
|
12
|
+
'x-mark': `<svg class="size-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path fill-rule="evenodd" d="M5.47 5.47a.75.75 0 0 1 1.06 0L12 10.94l5.47-5.47a.75.75 0 1 1 1.06 1.06L13.06 12l5.47 5.47a.75.75 0 1 1-1.06 1.06L12 13.06l-5.47 5.47a.75.75 0 0 1-1.06-1.06L10.94 12 5.47 6.53a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd"/></svg>`,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
public constructor(
|
|
16
|
+
private readonly logger: ILogger = resolve(ILogger).scopeTo('SvgService'),
|
|
17
|
+
) {
|
|
18
|
+
this.logger.trace('constructor');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Retourne le SVG pour une icône
|
|
23
|
+
* @param icon Clé heroicon (ex: 'check-circle') ou SVG inline custom
|
|
24
|
+
* @returns Le SVG du map si clé connue, sinon retourne icon tel quel (SVG custom)
|
|
25
|
+
*/
|
|
26
|
+
public get(icon: string): string | null {
|
|
27
|
+
this.logger.trace('get', icon);
|
|
28
|
+
if (!icon) return null;
|
|
29
|
+
return SvgService.ICONS[icon] ?? icon;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public has(key: string): boolean {
|
|
33
|
+
return key in SvgService.ICONS;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {DI, IEventAggregator, ILogger, INode, IPlatform, resolve} from "aurelia";
|
|
2
|
+
|
|
3
|
+
export interface ITransitionService extends TransitionService { }
|
|
4
|
+
export const ITransitionService = /*@__PURE__*/DI.createInterface<ITransitionService>('ITransitionService', (x) => x.singleton(TransitionService));
|
|
5
|
+
|
|
6
|
+
export type TransitionCallback = (e: HTMLElement|HTMLDialogElement) => void
|
|
7
|
+
export class TransitionService
|
|
8
|
+
{
|
|
9
|
+
public securityTimeout: number = 2000;
|
|
10
|
+
public constructor(
|
|
11
|
+
private readonly logger: ILogger = resolve(ILogger).scopeTo('TransitionService'),
|
|
12
|
+
private readonly platform:IPlatform = resolve(IPlatform),
|
|
13
|
+
) {
|
|
14
|
+
this.logger.trace('constructor')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public run(element: HTMLElement, before: TransitionCallback, after?: TransitionCallback) {
|
|
18
|
+
let securityTimeout: any = undefined;
|
|
19
|
+
const endTransition = (evt: TransitionEvent) => {
|
|
20
|
+
if (securityTimeout !== undefined) {
|
|
21
|
+
this.platform.clearTimeout(securityTimeout);
|
|
22
|
+
securityTimeout = undefined;
|
|
23
|
+
}
|
|
24
|
+
element.removeEventListener('transitionend', endTransition);
|
|
25
|
+
if (after) {
|
|
26
|
+
this.logger.trace('after()');
|
|
27
|
+
after(element);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (before) {
|
|
31
|
+
securityTimeout = this.platform.setTimeout(endTransition, this.securityTimeout);
|
|
32
|
+
element.addEventListener('transitionend', endTransition);
|
|
33
|
+
this.platform.requestAnimationFrame(() => {
|
|
34
|
+
this.logger.trace('before()');
|
|
35
|
+
before(element);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import {DI, ILogger, IPlatform, resolve} from "aurelia";
|
|
2
|
+
|
|
3
|
+
export interface ITrapFocusService extends TrapFocusService { }
|
|
4
|
+
export const ITrapFocusService = /*@__PURE__*/DI.createInterface<ITrapFocusService>('ITrapFocusService', (x) => x.transient(TrapFocusService));
|
|
5
|
+
|
|
6
|
+
export class TrapFocusService
|
|
7
|
+
{
|
|
8
|
+
|
|
9
|
+
public focusableElementsQuerySelector = '[href], button, input, select, textarea, [tabindex]:not([tabindex="-1"]), [accesskey], summary, canvas, audio, video, details, iframe, [contenteditable]';
|
|
10
|
+
|
|
11
|
+
private opener: HTMLElement|null = null;
|
|
12
|
+
private target: HTMLElement|null = null;
|
|
13
|
+
private globalElement: HTMLElement|null = null;
|
|
14
|
+
private startCallback: Function|null = null;
|
|
15
|
+
private stopCallback: Function|null = null;
|
|
16
|
+
private focusableElements: HTMLElement[] = [];
|
|
17
|
+
private lastFocusedElement: HTMLElement|null = null;
|
|
18
|
+
private started: boolean = false;
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
public constructor(
|
|
22
|
+
private readonly logger: ILogger = resolve(ILogger).scopeTo('TrapFocusService'),
|
|
23
|
+
private readonly platform:IPlatform = resolve(IPlatform),
|
|
24
|
+
) {
|
|
25
|
+
this.logger.trace('constructor')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private buildFocusableElements() {
|
|
29
|
+
this.focusableElements = [];
|
|
30
|
+
const focusableElements = this.target?.querySelectorAll(this.focusableElementsQuerySelector);
|
|
31
|
+
focusableElements?.forEach((element: Element) => {
|
|
32
|
+
const isDisabled = element.hasAttribute('disabled');
|
|
33
|
+
const isAriaHidden = (element.getAttribute('aria-hidden') === 'true');
|
|
34
|
+
const isNotTabbable = (element.getAttribute('tabindex') === '-1');
|
|
35
|
+
if (isDisabled === false && isAriaHidden === false && isNotTabbable === false) {
|
|
36
|
+
this.focusableElements.push(element as HTMLElement);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public start(opener: HTMLElement, target: HTMLElement, globalElement: HTMLElement, startCallback?: Function, stopCallback?: Function, initialFocusElement?: HTMLElement) {
|
|
42
|
+
this.logger.trace('start', opener, target);
|
|
43
|
+
this.startCallback = startCallback ?? null;
|
|
44
|
+
this.stopCallback = stopCallback ?? null;
|
|
45
|
+
this.opener = opener;
|
|
46
|
+
this.target = target;
|
|
47
|
+
this.globalElement = globalElement;
|
|
48
|
+
this.buildFocusableElements();
|
|
49
|
+
|
|
50
|
+
// Use provided initialFocusElement if valid, otherwise default to first focusable
|
|
51
|
+
if (initialFocusElement && this.focusableElements.includes(initialFocusElement)) {
|
|
52
|
+
this.lastFocusedElement = initialFocusElement;
|
|
53
|
+
} else {
|
|
54
|
+
this.lastFocusedElement = this.focusableElements[0] || undefined;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this.logger.trace('start: add keydown listener');
|
|
58
|
+
this.platform.requestAnimationFrame(() => {
|
|
59
|
+
this.logger.trace('start: focus initial element', this.lastFocusedElement);
|
|
60
|
+
this.lastFocusedElement?.focus();
|
|
61
|
+
|
|
62
|
+
});
|
|
63
|
+
this.target.addEventListener('keydown', this.onKeyDown);
|
|
64
|
+
this.platform.document.addEventListener('click', this.onClickOutside);
|
|
65
|
+
this.started = true;
|
|
66
|
+
if(this.startCallback) {
|
|
67
|
+
const promise = this.startCallback();
|
|
68
|
+
if (promise && promise instanceof Promise) {
|
|
69
|
+
return promise;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return Promise.resolve();
|
|
73
|
+
|
|
74
|
+
}
|
|
75
|
+
public stop() {
|
|
76
|
+
this.logger.trace('stop');
|
|
77
|
+
return new Promise((resolve, reject) => {
|
|
78
|
+
|
|
79
|
+
if (this.started) {
|
|
80
|
+
this.logger.trace('stop: remove keydown listener');
|
|
81
|
+
this.target?.removeEventListener('keydown', this.onKeyDown);
|
|
82
|
+
this.platform.document.removeEventListener('click', this.onClickOutside);
|
|
83
|
+
|
|
84
|
+
if(this.stopCallback) {
|
|
85
|
+
const promise = this.stopCallback();
|
|
86
|
+
if (promise && promise instanceof Promise) {
|
|
87
|
+
this.platform.requestAnimationFrame(() => {
|
|
88
|
+
this.opener?.focus()
|
|
89
|
+
this.cleanup();
|
|
90
|
+
promise.then((res) => {
|
|
91
|
+
return resolve(res);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
return resolve(void 0);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
this.platform.requestAnimationFrame(() => {
|
|
100
|
+
this.opener?.focus()
|
|
101
|
+
this.cleanup();
|
|
102
|
+
return resolve(void 0);
|
|
103
|
+
});
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return reject('TrapFocusService: not started');
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
private cleanup() {
|
|
111
|
+
this.logger.trace('cleanup');
|
|
112
|
+
if (this.started) {
|
|
113
|
+
this.opener = null;
|
|
114
|
+
this.startCallback = null;
|
|
115
|
+
this.stopCallback = null;
|
|
116
|
+
this.target = null;
|
|
117
|
+
this.lastFocusedElement = null;
|
|
118
|
+
this.focusableElements = [];
|
|
119
|
+
this.started = false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
private focusedElementIndex() : number {
|
|
125
|
+
let index = -1;
|
|
126
|
+
if (this.lastFocusedElement) {
|
|
127
|
+
index = this.focusableElements.indexOf(this.lastFocusedElement);
|
|
128
|
+
}
|
|
129
|
+
if (index === -1 && this.lastFocusedElement !== undefined) {
|
|
130
|
+
this.lastFocusedElement = null;
|
|
131
|
+
}
|
|
132
|
+
return index;
|
|
133
|
+
}
|
|
134
|
+
private focusPreviousElement(loop = true): HTMLElement | null {
|
|
135
|
+
|
|
136
|
+
const currentIndex = this.focusedElementIndex();
|
|
137
|
+
if (currentIndex === -1) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
let changed = false;
|
|
141
|
+
if (currentIndex === 0 && loop === true) {
|
|
142
|
+
this.lastFocusedElement = this.focusableElements[this.focusableElements.length - 1];
|
|
143
|
+
changed = true;
|
|
144
|
+
} else if (currentIndex > 0) {
|
|
145
|
+
this.lastFocusedElement = this.focusableElements[currentIndex - 1];
|
|
146
|
+
changed = true;
|
|
147
|
+
}
|
|
148
|
+
if (changed === true) {
|
|
149
|
+
this.platform.requestAnimationFrame(() => {
|
|
150
|
+
this.logger.trace('focusPreviousElement: focusing', this.lastFocusedElement);
|
|
151
|
+
this.lastFocusedElement?.focus();
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return this.lastFocusedElement;
|
|
155
|
+
}
|
|
156
|
+
private focusNextElement(loop = true): HTMLElement | null {
|
|
157
|
+
const currentIndex = this.focusedElementIndex();
|
|
158
|
+
if (currentIndex === -1) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
let changed = false;
|
|
162
|
+
if (currentIndex === this.focusableElements.length - 1 && loop === true) {
|
|
163
|
+
this.lastFocusedElement = this.focusableElements[0];
|
|
164
|
+
changed = true;
|
|
165
|
+
} else if (currentIndex < this.focusableElements.length - 1) {
|
|
166
|
+
this.lastFocusedElement = this.focusableElements[currentIndex + 1];
|
|
167
|
+
changed = true;
|
|
168
|
+
}
|
|
169
|
+
if (changed === true) {
|
|
170
|
+
this.platform.requestAnimationFrame(() => {
|
|
171
|
+
this.logger.trace('focusNextElement: focusing', this.lastFocusedElement);
|
|
172
|
+
this.lastFocusedElement?.focus();
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
return this.lastFocusedElement;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private onKeyDown = (event: KeyboardEvent) => {
|
|
179
|
+
let changeFocus = false;
|
|
180
|
+
if (event.key === 'Tab') {
|
|
181
|
+
if (event.shiftKey) {
|
|
182
|
+
// shift + tab loop backwards
|
|
183
|
+
event.preventDefault();
|
|
184
|
+
this.lastFocusedElement = this.focusPreviousElement(true);
|
|
185
|
+
} else {
|
|
186
|
+
// tab loop forwards
|
|
187
|
+
event.preventDefault();
|
|
188
|
+
this.lastFocusedElement = this.focusNextElement(true);
|
|
189
|
+
}
|
|
190
|
+
} else if (event.key === 'ArrowUp') {
|
|
191
|
+
// up arrow, no loop
|
|
192
|
+
event.preventDefault();
|
|
193
|
+
this.lastFocusedElement = this.focusPreviousElement(false);
|
|
194
|
+
} else if (event.key === 'ArrowDown') {
|
|
195
|
+
// down arrow, no loop
|
|
196
|
+
event.preventDefault();
|
|
197
|
+
this.lastFocusedElement = this.focusNextElement(false);
|
|
198
|
+
} else if (event.key === 'Escape') {
|
|
199
|
+
// stop trap focus
|
|
200
|
+
event.preventDefault();
|
|
201
|
+
this.stop();
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
private onClickOutside = (event: MouseEvent) => {
|
|
205
|
+
this.logger.trace('onClickOutside', event);
|
|
206
|
+
if (this.started && this.globalElement && event.target) {
|
|
207
|
+
this.logger.trace('onClickOutside: checking if click is outside globalElement', event.target, this.globalElement);
|
|
208
|
+
if (!this.globalElement.contains(event.target as Node)) {
|
|
209
|
+
this.stop();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
declare module '*.html' {
|
|
2
|
+
import { IContainer } from '@aurelia/kernel';
|
|
3
|
+
import { BindableDefinition } from '@aurelia/runtime';
|
|
4
|
+
export const name: string;
|
|
5
|
+
export const template: string;
|
|
6
|
+
export default template;
|
|
7
|
+
export const dependencies: string[];
|
|
8
|
+
export const containerless: boolean | undefined;
|
|
9
|
+
export const bindables: Record<string, BindableDefinition>;
|
|
10
|
+
export const shadowOptions: { mode: 'open' | 'closed'} | undefined;
|
|
11
|
+
export function register(container: IContainer);
|
|
12
|
+
}
|