@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,47 @@
|
|
|
1
|
+
import {customAttribute, ILogger, INode, resolve} from "aurelia";
|
|
2
|
+
|
|
3
|
+
@customAttribute('bleet-password')
|
|
4
|
+
export class BleetPasswordCustomAttribute
|
|
5
|
+
{
|
|
6
|
+
private button?: HTMLElement;
|
|
7
|
+
private iconHidden?: HTMLElement;
|
|
8
|
+
private iconVisible?: HTMLElement;
|
|
9
|
+
private input?: HTMLInputElement;
|
|
10
|
+
|
|
11
|
+
public constructor(
|
|
12
|
+
private readonly logger: ILogger = resolve(ILogger).scopeTo('bleet-password'),
|
|
13
|
+
private readonly element: HTMLElement = resolve(INode) as HTMLElement,
|
|
14
|
+
) {
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public attaching(): void
|
|
18
|
+
{
|
|
19
|
+
this.button = this.element.querySelector('[data-password=toggle]') ?? undefined;
|
|
20
|
+
this.iconHidden = this.button?.querySelector('[data-password=icon-hidden]') ?? undefined;
|
|
21
|
+
this.iconVisible = this.button?.querySelector('[data-password=icon-visible]') ?? undefined;
|
|
22
|
+
this.input = this.element.querySelector('input') ?? undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public attached(): void
|
|
26
|
+
{
|
|
27
|
+
this.button?.addEventListener('click', this.onToggle);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public detaching(): void
|
|
31
|
+
{
|
|
32
|
+
this.button?.removeEventListener('click', this.onToggle);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private onToggle = (event: MouseEvent): void => {
|
|
36
|
+
event.preventDefault();
|
|
37
|
+
|
|
38
|
+
const isPassword = this.input?.type === 'password';
|
|
39
|
+
|
|
40
|
+
if (this.input) {
|
|
41
|
+
this.input.type = isPassword ? 'text' : 'password';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.iconHidden?.classList.toggle('hidden', isPassword);
|
|
45
|
+
this.iconVisible?.classList.toggle('hidden', !isPassword);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import {bindable, customAttribute, IEventAggregator, ILogger, INode, IPlatform, resolve} from "aurelia";
|
|
2
|
+
import {Channels, ProfileAction, ProfileStatus} from '../enums/event-aggregator';
|
|
3
|
+
import {IProfile, IProfileStatus} from '../interfaces/event-aggregator';
|
|
4
|
+
import {ITransitionService} from '../services/transition-service';
|
|
5
|
+
import {ITrapFocusService} from '../services/trap-focus-service';
|
|
6
|
+
|
|
7
|
+
@customAttribute({ name: 'bleet-profile', defaultProperty: 'id' })
|
|
8
|
+
export class BleetProfileCustomAttribute
|
|
9
|
+
{
|
|
10
|
+
|
|
11
|
+
@bindable id: string = '';
|
|
12
|
+
private toggleButton?: HTMLButtonElement;
|
|
13
|
+
private panel?: HTMLDivElement;
|
|
14
|
+
private isOpen: boolean = false;
|
|
15
|
+
|
|
16
|
+
public constructor(
|
|
17
|
+
private readonly logger: ILogger = resolve(ILogger).scopeTo('bleet-profile'),
|
|
18
|
+
private readonly ea: IEventAggregator = resolve(IEventAggregator),
|
|
19
|
+
private readonly element: HTMLElement = resolve(INode) as HTMLElement,
|
|
20
|
+
private readonly transitionService: ITransitionService = resolve(ITransitionService),
|
|
21
|
+
private readonly platform: IPlatform = resolve(IPlatform),
|
|
22
|
+
private readonly trapFocusService: ITrapFocusService = resolve(ITrapFocusService),
|
|
23
|
+
) {
|
|
24
|
+
this.logger.trace('constructor')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public attaching()
|
|
28
|
+
{
|
|
29
|
+
this.logger.trace('attaching');
|
|
30
|
+
this.toggleButton = this.element.querySelector('[data-profile=toggle]') as HTMLButtonElement;
|
|
31
|
+
this.panel = this.element.querySelector('[data-profile=panel]') as HTMLDivElement;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public attached()
|
|
35
|
+
{
|
|
36
|
+
this.logger.trace('attached');
|
|
37
|
+
this.toggleButton?.addEventListener('click', this.onClickToggle);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public detached()
|
|
41
|
+
{
|
|
42
|
+
this.logger.trace('detached');
|
|
43
|
+
this.toggleButton?.removeEventListener('click', this.onClickToggle);
|
|
44
|
+
if (this.isOpen) {
|
|
45
|
+
this.trapFocusService.stop();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private onClickToggle = (event: MouseEvent) => {
|
|
50
|
+
this.logger.trace('onClickToggle', event);
|
|
51
|
+
event.preventDefault();
|
|
52
|
+
if (this.isOpen) {
|
|
53
|
+
this.close();
|
|
54
|
+
} else {
|
|
55
|
+
this.open();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private onStopTrapFocus = () => {
|
|
60
|
+
this.logger.trace('onStopTrapFocus');
|
|
61
|
+
this.isOpen = false;
|
|
62
|
+
this.toggleButton?.setAttribute('aria-expanded', 'false');
|
|
63
|
+
this.transitionService.run(this.panel!, (element) => {
|
|
64
|
+
this.ea.publish(Channels.Profile, <IProfile>{action: ProfileAction.Close, id: this.id});
|
|
65
|
+
this.ea.publish(Channels.ProfileStatus, <IProfileStatus>{status: ProfileStatus.Closing, id: this.id});
|
|
66
|
+
this.platform.requestAnimationFrame(() => {
|
|
67
|
+
element.classList.remove('opacity-100', 'scale-100');
|
|
68
|
+
element.classList.add('opacity-0', 'scale-95', 'pointer-events-none');
|
|
69
|
+
});
|
|
70
|
+
}, (element) => {
|
|
71
|
+
element.classList.add('hidden');
|
|
72
|
+
this.ea.publish(Channels.ProfileStatus, <IProfileStatus>{status: ProfileStatus.Closed, id: this.id});
|
|
73
|
+
});
|
|
74
|
+
return Promise.resolve();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private open() {
|
|
78
|
+
this.logger.trace('open');
|
|
79
|
+
this.isOpen = true;
|
|
80
|
+
this.toggleButton?.setAttribute('aria-expanded', 'true');
|
|
81
|
+
|
|
82
|
+
// Find first focusable item in panel
|
|
83
|
+
const firstItem = this.panel?.querySelector('a, button') as HTMLElement;
|
|
84
|
+
|
|
85
|
+
this.trapFocusService.start(
|
|
86
|
+
this.toggleButton as HTMLButtonElement,
|
|
87
|
+
this.panel as HTMLElement,
|
|
88
|
+
this.element,
|
|
89
|
+
undefined,
|
|
90
|
+
this.onStopTrapFocus,
|
|
91
|
+
firstItem
|
|
92
|
+
).then(() => {
|
|
93
|
+
this.transitionService.run(this.panel!, (element) => {
|
|
94
|
+
this.ea.publish(Channels.Profile, <IProfile>{action: ProfileAction.Open, id: this.id});
|
|
95
|
+
this.ea.publish(Channels.ProfileStatus, <IProfileStatus>{status: ProfileStatus.Opening, id: this.id});
|
|
96
|
+
element.classList.remove('hidden');
|
|
97
|
+
this.platform.requestAnimationFrame(() => {
|
|
98
|
+
element.classList.add('opacity-100', 'scale-100');
|
|
99
|
+
element.classList.remove('opacity-0', 'scale-95', 'pointer-events-none');
|
|
100
|
+
});
|
|
101
|
+
}, () => {
|
|
102
|
+
this.ea.publish(Channels.ProfileStatus, <IProfileStatus>{status: ProfileStatus.Opened, id: this.id});
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private close() {
|
|
108
|
+
this.logger.trace('close');
|
|
109
|
+
this.trapFocusService.stop();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import {customAttribute, ILogger, INode, resolve} from "aurelia";
|
|
2
|
+
import {ITrapFocusService} from '../services/trap-focus-service';
|
|
3
|
+
|
|
4
|
+
// @ts-ignore
|
|
5
|
+
@customAttribute('bleet-select')
|
|
6
|
+
export class BleetSelectCustomAttribute {
|
|
7
|
+
|
|
8
|
+
private select?: HTMLSelectElement;
|
|
9
|
+
private button?: HTMLButtonElement;
|
|
10
|
+
private buttonText?: HTMLElement;
|
|
11
|
+
private optionTemplate?: HTMLTemplateElement;
|
|
12
|
+
private itemsPlace?: HTMLElement;
|
|
13
|
+
|
|
14
|
+
public constructor(
|
|
15
|
+
private readonly logger: ILogger = resolve(ILogger).scopeTo('bleet-select'),
|
|
16
|
+
private readonly element: HTMLElement = resolve(INode) as HTMLElement,
|
|
17
|
+
private readonly trapFocusService: ITrapFocusService = resolve(ITrapFocusService),
|
|
18
|
+
) {
|
|
19
|
+
this.logger.trace('constructor')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public attaching()
|
|
23
|
+
{
|
|
24
|
+
this.logger.trace('attaching');
|
|
25
|
+
this.select = this.element.querySelector('select') as HTMLSelectElement;
|
|
26
|
+
this.button = this.element.querySelector('button') as HTMLButtonElement;
|
|
27
|
+
this.buttonText = this.button.querySelector('[data-select=value]') as HTMLElement;
|
|
28
|
+
this.optionTemplate = this.element.querySelector('[data-select=item-template]') as HTMLTemplateElement;
|
|
29
|
+
this.itemsPlace = this.element.querySelector('[data-select=items]') as HTMLElement;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public attached()
|
|
33
|
+
{
|
|
34
|
+
this.logger.trace('attached');
|
|
35
|
+
if (!this.itemsPlace) {
|
|
36
|
+
throw new Error('Items place element not found');
|
|
37
|
+
}
|
|
38
|
+
if (!this.itemsPlace.id) {
|
|
39
|
+
this.itemsPlace.id = `data-select-items-${Math.random().toString(36).substring(2, 15)}`;
|
|
40
|
+
}
|
|
41
|
+
if (!this.select?.options) {
|
|
42
|
+
throw new Error('Select options not found');
|
|
43
|
+
}
|
|
44
|
+
if (!this.optionTemplate) {
|
|
45
|
+
throw new Error('Option template not found');
|
|
46
|
+
}
|
|
47
|
+
if (!this.button) {
|
|
48
|
+
throw new Error('Button element not found');
|
|
49
|
+
}
|
|
50
|
+
if (!this.buttonText) {
|
|
51
|
+
throw new Error('Button text element not found');
|
|
52
|
+
}
|
|
53
|
+
this.preparePanel();
|
|
54
|
+
|
|
55
|
+
// ARIA setup
|
|
56
|
+
this.button.ariaHasPopup = 'listbox';
|
|
57
|
+
this.button.setAttribute('aria-controls', this.itemsPlace.id);
|
|
58
|
+
this.itemsPlace.role = 'listbox';
|
|
59
|
+
|
|
60
|
+
// Event listeners
|
|
61
|
+
this.button?.addEventListener('click', this.onClickToggleMenu);
|
|
62
|
+
this.itemsPlace?.addEventListener('click', this.onClickToggleItem);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public detached()
|
|
66
|
+
{
|
|
67
|
+
this.logger.trace('detached');
|
|
68
|
+
this.button?.removeEventListener('click', this.onClickToggleMenu);
|
|
69
|
+
this.itemsPlace?.removeEventListener('click', this.onClickToggleItem);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private onClickToggleMenu = (event: MouseEvent) => {
|
|
73
|
+
this.logger.trace('onClick', event);
|
|
74
|
+
event.preventDefault();
|
|
75
|
+
if (this.select?.disabled) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
return this.toggleMenu();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private onStopTrapFocus = () => {
|
|
82
|
+
this.logger.trace('onStopTrapFocus');
|
|
83
|
+
this.itemsPlace?.classList.add('hidden');
|
|
84
|
+
return Promise.resolve();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private toggleMenu = () => {
|
|
88
|
+
const isClosed = this.itemsPlace?.classList.contains('hidden');
|
|
89
|
+
return new Promise((resolve) => {
|
|
90
|
+
if (isClosed) {
|
|
91
|
+
// Opening menu
|
|
92
|
+
(this.button as HTMLButtonElement).ariaExpanded = 'true';
|
|
93
|
+
|
|
94
|
+
// Find selected option to focus initially
|
|
95
|
+
const selectedOption = this.itemsPlace?.querySelector('[aria-selected="true"]') as HTMLElement;
|
|
96
|
+
|
|
97
|
+
return this.trapFocusService.start(
|
|
98
|
+
this.button as HTMLButtonElement,
|
|
99
|
+
this.itemsPlace as HTMLElement,
|
|
100
|
+
this.element,
|
|
101
|
+
undefined,
|
|
102
|
+
this.onStopTrapFocus,
|
|
103
|
+
selectedOption // Pass selected option as initial focus
|
|
104
|
+
)
|
|
105
|
+
.then(() => {
|
|
106
|
+
this.logger.trace('toggleMenu opened');
|
|
107
|
+
this.itemsPlace?.classList.remove('hidden');
|
|
108
|
+
resolve(void 0);
|
|
109
|
+
});
|
|
110
|
+
} else {
|
|
111
|
+
// Closing menu
|
|
112
|
+
(this.button as HTMLButtonElement).ariaExpanded = 'false';
|
|
113
|
+
return this.trapFocusService.stop()
|
|
114
|
+
.then(() => {
|
|
115
|
+
this.logger.trace('toggleMenu closed');
|
|
116
|
+
this.itemsPlace?.classList.add('hidden');
|
|
117
|
+
resolve(void 0);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private onClickToggleItem = (event: MouseEvent) => {
|
|
124
|
+
this.logger.trace('onClickItem', event);
|
|
125
|
+
event.preventDefault();
|
|
126
|
+
const element = (event.target as HTMLElement).closest('[data-value]') as HTMLElement;
|
|
127
|
+
|
|
128
|
+
// Update select options
|
|
129
|
+
Array.from((this.select as HTMLSelectElement).options).forEach((option) => {
|
|
130
|
+
option.selected = option.value == element.dataset.value;
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Dispatch change event sur le select natif pour active-form
|
|
134
|
+
this.select?.dispatchEvent(new Event('change', { bubbles: true }));
|
|
135
|
+
|
|
136
|
+
this.synchSelect();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private synchSelect() {
|
|
140
|
+
this.swapItemClasses();
|
|
141
|
+
|
|
142
|
+
// Close menu
|
|
143
|
+
return this.toggleMenu();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private swapItemClasses() {
|
|
147
|
+
Array.from((this.select as HTMLSelectElement).options).forEach((option) => {
|
|
148
|
+
const item = this.itemsPlace?.querySelector(`[data-value="${option.value}"]`) as HTMLElement;
|
|
149
|
+
if (!item) return;
|
|
150
|
+
|
|
151
|
+
const checkmark = item.querySelector('[data-select=item-check]') as HTMLElement;
|
|
152
|
+
|
|
153
|
+
// Récupérer les classes depuis les data-attributes de l'élément
|
|
154
|
+
const itemInactiveClasses = item.dataset.classInactive?.split(' ') || [];
|
|
155
|
+
const itemActiveClasses = item.dataset.classActive?.split(' ') || [];
|
|
156
|
+
const checkInactiveClasses = checkmark?.dataset.classInactive?.split(' ') || [];
|
|
157
|
+
const checkActiveClasses = checkmark?.dataset.classActive?.split(' ') || [];
|
|
158
|
+
|
|
159
|
+
if (option.selected) {
|
|
160
|
+
// Swap vers active
|
|
161
|
+
item.classList.remove(...itemInactiveClasses);
|
|
162
|
+
item.classList.add(...itemActiveClasses);
|
|
163
|
+
checkmark?.classList.remove(...checkInactiveClasses);
|
|
164
|
+
checkmark?.classList.add(...checkActiveClasses);
|
|
165
|
+
|
|
166
|
+
// Update ARIA
|
|
167
|
+
item.setAttribute('aria-selected', 'true');
|
|
168
|
+
this.button?.setAttribute('aria-activedescendant', item.id);
|
|
169
|
+
|
|
170
|
+
// Update button text
|
|
171
|
+
(this.buttonText as HTMLElement).innerHTML = option.innerHTML;
|
|
172
|
+
} else {
|
|
173
|
+
// Swap vers inactive
|
|
174
|
+
item.classList.remove(...itemActiveClasses);
|
|
175
|
+
item.classList.add(...itemInactiveClasses);
|
|
176
|
+
checkmark?.classList.remove(...checkActiveClasses);
|
|
177
|
+
checkmark?.classList.add(...checkInactiveClasses);
|
|
178
|
+
|
|
179
|
+
// Update ARIA
|
|
180
|
+
item.setAttribute('aria-selected', 'false');
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
private preparePanel() {
|
|
185
|
+
this.logger.trace('preparePanel');
|
|
186
|
+
if (!this.select) {
|
|
187
|
+
throw new Error('Select element not found');
|
|
188
|
+
}
|
|
189
|
+
// Vider le panel (sauf les templates)
|
|
190
|
+
this.itemsPlace?.querySelectorAll('button').forEach((child) => child.remove());
|
|
191
|
+
|
|
192
|
+
const options = Array.from(this.select.options);
|
|
193
|
+
options.forEach((option) => {
|
|
194
|
+
// @ts-ignore
|
|
195
|
+
const item = this.optionTemplate.content.cloneNode(true) as DocumentFragment;
|
|
196
|
+
const button = item.querySelector('button') as HTMLButtonElement;
|
|
197
|
+
|
|
198
|
+
// ARIA attributes
|
|
199
|
+
button.role = 'option';
|
|
200
|
+
button.id = `bleet-option-${option.value}-${Math.random().toString(36).substring(2, 9)}`;
|
|
201
|
+
|
|
202
|
+
// Content
|
|
203
|
+
const itemText = item.querySelector('[data-select=item-text]') as HTMLElement;
|
|
204
|
+
const itemValue = item.querySelector('[data-value]') as HTMLElement;
|
|
205
|
+
itemValue.dataset.value = option.value;
|
|
206
|
+
itemText.innerHTML = option.innerHTML;
|
|
207
|
+
|
|
208
|
+
this.itemsPlace?.append(item);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// Appliquer les classes active/inactive
|
|
212
|
+
this.swapItemClasses();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import {customAttribute, ILogger, INode, IPlatform, resolve} from "aurelia";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@customAttribute('bleet-tabs')
|
|
5
|
+
export class BleetTabsCustomAttribute {
|
|
6
|
+
|
|
7
|
+
private activeClasses: string = '';
|
|
8
|
+
private inactiveClasses: string = '';
|
|
9
|
+
private select?: HTMLSelectElement;
|
|
10
|
+
public constructor(
|
|
11
|
+
private readonly logger: ILogger = resolve(ILogger).scopeTo('bleet-tabs'),
|
|
12
|
+
private readonly element: HTMLElement = resolve(INode) as HTMLElement,
|
|
13
|
+
private readonly p: IPlatform = resolve(IPlatform),
|
|
14
|
+
) {
|
|
15
|
+
this.logger.trace('constructor')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public attaching()
|
|
19
|
+
{
|
|
20
|
+
this.logger.trace('attaching');
|
|
21
|
+
const activeButton = this.element.querySelector('[data-tabs^="tab-"][aria-selected="true"]');
|
|
22
|
+
const inactiveButton = this.element.querySelector('[data-tabs^="tab-"][aria-selected="false"]');
|
|
23
|
+
|
|
24
|
+
this.activeClasses = activeButton?.className || '';
|
|
25
|
+
this.inactiveClasses = inactiveButton?.className || '';
|
|
26
|
+
|
|
27
|
+
this.select = this.element.querySelector('select') as HTMLSelectElement;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public attached()
|
|
31
|
+
{
|
|
32
|
+
this.logger.trace('attached');
|
|
33
|
+
this.element.querySelectorAll('[data-tabs^="tab-"]').forEach((button: Element) => {
|
|
34
|
+
button.addEventListener('click', this.onClickTab);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Écouter le select mobile
|
|
38
|
+
if (this.select) {
|
|
39
|
+
this.select.addEventListener('change', this.onChangeSelect);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public detached()
|
|
44
|
+
{
|
|
45
|
+
this.logger.trace('detached');
|
|
46
|
+
this.element.querySelectorAll('[data-tabs^="tab-"]').forEach((button: Element) => {
|
|
47
|
+
button.removeEventListener('click', this.onClickTab);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (this.select) {
|
|
51
|
+
this.select.removeEventListener('change', this.onChangeSelect);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private onClickTab = (event: Event) => {
|
|
56
|
+
this.logger.trace('onClickTab', event);
|
|
57
|
+
event.preventDefault();
|
|
58
|
+
const tabIndex = (event.currentTarget as HTMLElement).getAttribute('data-tabs')?.replace('tab-', '') || '0';
|
|
59
|
+
|
|
60
|
+
// Mettre à jour les tabs
|
|
61
|
+
this.element.querySelectorAll('[data-tabs^="tab-"]').forEach((button: Element) => {
|
|
62
|
+
const buttonTabIndex = button.getAttribute('data-tabs')?.replace('tab-', '') || '0';
|
|
63
|
+
if (buttonTabIndex === tabIndex) {
|
|
64
|
+
button.setAttribute('aria-selected', 'true');
|
|
65
|
+
button.className = this.activeClasses;
|
|
66
|
+
} else {
|
|
67
|
+
button.setAttribute('aria-selected', 'false');
|
|
68
|
+
button.className = this.inactiveClasses;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Mettre à jour les panels
|
|
73
|
+
this.element.querySelectorAll('[data-tabs^="panel-"]').forEach((panel: Element) => {
|
|
74
|
+
const panelIndex = panel.getAttribute('data-tabs')?.replace('panel-', '') || '0';
|
|
75
|
+
if (panelIndex === tabIndex) {
|
|
76
|
+
panel.classList.remove('hidden');
|
|
77
|
+
panel.setAttribute('aria-hidden', 'false');
|
|
78
|
+
} else {
|
|
79
|
+
panel.classList.add('hidden');
|
|
80
|
+
panel.setAttribute('aria-hidden', 'true');
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Synchroniser le select
|
|
85
|
+
if (this.select) {
|
|
86
|
+
this.select.value = tabIndex;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private onChangeSelect = (event: Event) => {
|
|
91
|
+
this.logger.trace('onChangeSelect', event);
|
|
92
|
+
const tabIndex = this.select?.value;
|
|
93
|
+
const button = this.element.querySelector(`[data-tabs="tab-${tabIndex}"]`) as HTMLElement;
|
|
94
|
+
|
|
95
|
+
if (button) {
|
|
96
|
+
button.click();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {bindable, customAttribute, IEventAggregator, ILogger, INode, resolve} from "aurelia";
|
|
2
|
+
import {Channels, ToasterAction, UiColor, UiToastIcon} from '../enums/event-aggregator';
|
|
3
|
+
import {IToaster} from '../interfaces/event-aggregator';
|
|
4
|
+
|
|
5
|
+
@customAttribute({ name: 'bleet-toaster-trigger', defaultProperty: 'id' })
|
|
6
|
+
export class BleetToasterTriggerCustomAttribute {
|
|
7
|
+
|
|
8
|
+
@bindable id: string = '';
|
|
9
|
+
@bindable() public color: UiColor = UiColor.Info;
|
|
10
|
+
@bindable() public icon: UiToastIcon = UiToastIcon.Info;
|
|
11
|
+
@bindable() public title: string = '';
|
|
12
|
+
@bindable() public content: string = '';
|
|
13
|
+
@bindable() public duration: number = 0; // Duration in milliseconds
|
|
14
|
+
public constructor(
|
|
15
|
+
private readonly logger: ILogger = resolve(ILogger).scopeTo('bleet-toaster-trigger'),
|
|
16
|
+
private readonly element: HTMLElement = resolve(INode) as HTMLElement,
|
|
17
|
+
private readonly ea: IEventAggregator = resolve(IEventAggregator),
|
|
18
|
+
) {
|
|
19
|
+
this.logger.trace('constructor')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public attaching()
|
|
23
|
+
{
|
|
24
|
+
this.logger.trace('attaching');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public attached()
|
|
28
|
+
{
|
|
29
|
+
this.logger.trace('attached');
|
|
30
|
+
this.element.addEventListener('click', this.onClick);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public detached()
|
|
34
|
+
{
|
|
35
|
+
this.logger.trace('detached');
|
|
36
|
+
this.element.removeEventListener('click', this.onClick);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private onClick = (event: Event) => {
|
|
40
|
+
this.logger.trace('onClick', event);
|
|
41
|
+
event.preventDefault();
|
|
42
|
+
this.ea.publish(Channels.Toaster, <IToaster>{
|
|
43
|
+
action: ToasterAction.Add, toast:
|
|
44
|
+
{
|
|
45
|
+
id: this.id,
|
|
46
|
+
duration: this.duration,
|
|
47
|
+
color: this.color,
|
|
48
|
+
icon: this.icon,
|
|
49
|
+
title: this.title,
|
|
50
|
+
content: this.content
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|