@masterkeymaterial/ui 0.0.2 → 0.0.4

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 (140) hide show
  1. package/fesm2022/masterkeymaterial-ui.mjs +6457 -0
  2. package/fesm2022/masterkeymaterial-ui.mjs.map +1 -0
  3. package/package.json +14 -5
  4. package/types/masterkeymaterial-ui.d.ts +928 -0
  5. package/ng-package.json +0 -10
  6. package/src/elements/ui-button/ui-button.css +0 -229
  7. package/src/elements/ui-button/ui-button.html +0 -12
  8. package/src/elements/ui-button/ui-button.ts +0 -133
  9. package/src/elements/ui-check-box/ui-check-box.css +0 -66
  10. package/src/elements/ui-check-box/ui-check-box.html +0 -5
  11. package/src/elements/ui-check-box/ui-check-box.ts +0 -65
  12. package/src/elements/ui-chip/ui-chip.css +0 -24
  13. package/src/elements/ui-chip/ui-chip.html +0 -3
  14. package/src/elements/ui-chip/ui-chip.ts +0 -25
  15. package/src/elements/ui-drop-zone/ui-drop-zone.css +0 -91
  16. package/src/elements/ui-drop-zone/ui-drop-zone.html +0 -8
  17. package/src/elements/ui-drop-zone/ui-drop-zone.ts +0 -153
  18. package/src/elements/ui-file-list/ui-file-list.css +0 -43
  19. package/src/elements/ui-file-list/ui-file-list.html +0 -17
  20. package/src/elements/ui-file-list/ui-file-list.ts +0 -22
  21. package/src/elements/ui-list-errors/ui-list-errors.css +0 -30
  22. package/src/elements/ui-list-errors/ui-list-errors.html +0 -10
  23. package/src/elements/ui-list-errors/ui-list-errors.ts +0 -14
  24. package/src/elements/ui-loading/ui-loading.css +0 -13
  25. package/src/elements/ui-loading/ui-loading.html +0 -1
  26. package/src/elements/ui-loading/ui-loading.ts +0 -10
  27. package/src/elements/ui-menu/ui-menu.css +0 -69
  28. package/src/elements/ui-menu/ui-menu.html +0 -20
  29. package/src/elements/ui-menu/ui-menu.ts +0 -267
  30. package/src/elements/ui-procurar/ui-procurar.css +0 -48
  31. package/src/elements/ui-procurar/ui-procurar.html +0 -14
  32. package/src/elements/ui-procurar/ui-procurar.ts +0 -82
  33. package/src/elements/ui-progress/ui-progress.css +0 -0
  34. package/src/elements/ui-progress/ui-progress.html +0 -1
  35. package/src/elements/ui-progress/ui-progress.ts +0 -15
  36. package/src/elements/ui-select/ui-select.css +0 -211
  37. package/src/elements/ui-select/ui-select.html +0 -46
  38. package/src/elements/ui-select/ui-select.ts +0 -482
  39. package/src/elements/ui-slide/ui-slide.css +0 -75
  40. package/src/elements/ui-slide/ui-slide.html +0 -7
  41. package/src/elements/ui-slide/ui-slide.ts +0 -61
  42. package/src/fields/Base/BaseFieldsForm/BaseFieldsForm.component.ts +0 -113
  43. package/src/fields/Base/BaseFieldsValues/BaseFieldsValues.ts +0 -112
  44. package/src/fields/Formulario/Formulario.ts +0 -1056
  45. package/src/fields/Formulario/form-action/form-action.css +0 -98
  46. package/src/fields/Formulario/form-action/form-action.html +0 -75
  47. package/src/fields/Formulario/form-action/form-action.ts +0 -187
  48. package/src/fields/Formulario/form-controls/form-controls.css +0 -108
  49. package/src/fields/Formulario/form-controls/form-controls.html +0 -105
  50. package/src/fields/Formulario/form-controls/form-controls.ts +0 -122
  51. package/src/fields/Formulario/form-fase/form-fase.css +0 -84
  52. package/src/fields/Formulario/form-fase/form-fase.html +0 -24
  53. package/src/fields/Formulario/form-fase/form-fase.ts +0 -157
  54. package/src/fields/Formulario/form-filter/form-filter.css +0 -50
  55. package/src/fields/Formulario/form-filter/form-filter.html +0 -25
  56. package/src/fields/Formulario/form-filter/form-filter.ts +0 -53
  57. package/src/fields/Formulario/form-no-action/form-no-action.css +0 -32
  58. package/src/fields/Formulario/form-no-action/form-no-action.html +0 -12
  59. package/src/fields/Formulario/form-no-action/form-no-action.ts +0 -21
  60. package/src/fields/Formulario/formated-values/formated-values.css +0 -104
  61. package/src/fields/Formulario/formated-values/formated-values.html +0 -15
  62. package/src/fields/Formulario/formated-values/formated-values.ts +0 -186
  63. package/src/fields/Formulario/single-values/single-values.css +0 -88
  64. package/src/fields/Formulario/single-values/single-values.html +0 -11
  65. package/src/fields/Formulario/single-values/single-values.ts +0 -65
  66. package/src/fields/autocomplete/autocomplete.css +0 -94
  67. package/src/fields/autocomplete/autocomplete.html +0 -38
  68. package/src/fields/autocomplete/autocomplete.ts +0 -294
  69. package/src/fields/button/button.css +0 -7
  70. package/src/fields/button/button.html +0 -19
  71. package/src/fields/button/button.ts +0 -38
  72. package/src/fields/checkbox/checkbox.css +0 -27
  73. package/src/fields/checkbox/checkbox.html +0 -20
  74. package/src/fields/checkbox/checkbox.ts +0 -44
  75. package/src/fields/color/CirculoColorido/circulocolorido.css +0 -50
  76. package/src/fields/color/CirculoColorido/circulocolorido.html +0 -8
  77. package/src/fields/color/CirculoColorido/circulocolorido.ts +0 -24
  78. package/src/fields/color/color.css +0 -15
  79. package/src/fields/color/color.html +0 -24
  80. package/src/fields/color/color.ts +0 -47
  81. package/src/fields/date/date.html +0 -19
  82. package/src/fields/date/date.ts +0 -29
  83. package/src/fields/datetime/datetime.html +0 -19
  84. package/src/fields/datetime/datetime.ts +0 -29
  85. package/src/fields/display/display.css +0 -7
  86. package/src/fields/display/display.html +0 -20
  87. package/src/fields/display/display.ts +0 -43
  88. package/src/fields/editor/editor.css +0 -51
  89. package/src/fields/editor/editor.html +0 -37
  90. package/src/fields/editor/editor.ts +0 -218
  91. package/src/fields/email/email.html +0 -19
  92. package/src/fields/email/email.ts +0 -29
  93. package/src/fields/fields.css +0 -180
  94. package/src/fields/generic/generic.html +0 -3
  95. package/src/fields/generic/generic.ts +0 -91
  96. package/src/fields/hidden/hidden.html +0 -3
  97. package/src/fields/hidden/hidden.ts +0 -20
  98. package/src/fields/icon/icon.css +0 -19
  99. package/src/fields/icon/icon.html +0 -27
  100. package/src/fields/icon/icon.ts +0 -31
  101. package/src/fields/multifactor/multifactor.css +0 -21
  102. package/src/fields/multifactor/multifactor.html +0 -39
  103. package/src/fields/multifactor/multifactor.ts +0 -106
  104. package/src/fields/multikv/multikv.css +0 -43
  105. package/src/fields/multikv/multikv.html +0 -47
  106. package/src/fields/multikv/multikv.ts +0 -88
  107. package/src/fields/multitext/multitext.css +0 -36
  108. package/src/fields/multitext/multitext.html +0 -38
  109. package/src/fields/multitext/multitext.ts +0 -75
  110. package/src/fields/number/number.html +0 -20
  111. package/src/fields/number/number.ts +0 -35
  112. package/src/fields/password/password.html +0 -23
  113. package/src/fields/password/password.ts +0 -37
  114. package/src/fields/search/search.css +0 -0
  115. package/src/fields/search/search.html +0 -19
  116. package/src/fields/search/search.ts +0 -54
  117. package/src/fields/select/select.css +0 -15
  118. package/src/fields/select/select.html +0 -18
  119. package/src/fields/select/select.ts +0 -52
  120. package/src/fields/slide/slide.css +0 -27
  121. package/src/fields/slide/slide.html +0 -20
  122. package/src/fields/slide/slide.ts +0 -45
  123. package/src/fields/text/text.html +0 -19
  124. package/src/fields/text/text.ts +0 -30
  125. package/src/fields/textarea/textarea.css +0 -4
  126. package/src/fields/textarea/textarea.html +0 -20
  127. package/src/fields/textarea/textarea.ts +0 -31
  128. package/src/fields/time/time.html +0 -19
  129. package/src/fields/time/time.ts +0 -29
  130. package/src/fields/upload/upload.css +0 -24
  131. package/src/fields/upload/upload.html +0 -41
  132. package/src/fields/upload/upload.ts +0 -137
  133. package/src/interfaces/interfaces.ts +0 -61
  134. package/src/public-api.ts +0 -38
  135. package/src/util/ClassOf.pipe.ts +0 -11
  136. package/src/util/JsonColorido.pipe.ts +0 -11
  137. package/src/util/util.ts +0 -2151
  138. package/tsconfig.lib.json +0 -16
  139. package/tsconfig.lib.prod.json +0 -9
  140. package/tsconfig.spec.json +0 -13
@@ -1,153 +0,0 @@
1
- import { Component, ElementRef, input, model, output, viewChild } from '@angular/core';
2
- import { IDropOutput, IDropZoneEvent, IFileMetadata } from '../../interfaces/interfaces';
3
- import { LibUtil, LOG } from '../../util/util';
4
-
5
- @Component({
6
- selector: 'ui-drop-zone',
7
- templateUrl: './ui-drop-zone.html',
8
- styleUrl: './ui-drop-zone.css',
9
- })
10
- export class UiDropZone {
11
-
12
- value = model<IDropOutput>();
13
- disabled = input<boolean>();
14
- sobrepor = input<boolean>();
15
- clicked = output<Event>();
16
- changed = output<IDropOutput>();
17
-
18
- rippleEffect = viewChild('rippleEffect', { read: ElementRef });
19
- ripple = viewChild('ripple', { read: ElementRef });
20
- // dropzonearea = viewChild('dropzonearea', { read: ElementRef })
21
- isDragging: boolean = false;
22
-
23
- async onDropZone(tipo: IDropZoneEvent, ev: Event) {
24
- ev.preventDefault();
25
- ev.stopPropagation();
26
- if (this.disabled()) return;
27
-
28
- let target = ((ev as Event).target as HTMLDivElement);
29
- if (tipo == "dragenter" && !this.disabled()) this.isDragging = true;
30
- if (tipo == "dragleave" && !this.disabled()) this.isDragging = false;
31
- if (tipo == "click" && target && target.classList?.contains("dropzone")) {
32
- this.clicked.emit(ev);
33
- }
34
- }
35
-
36
- async onDrop(ev: DragEvent) {
37
- ev.preventDefault();
38
- ev.stopPropagation();
39
- this.isDragging = false;
40
- if (this.disabled()) return;
41
-
42
- const dataTransfer = ev.dataTransfer;
43
- if (!dataTransfer) {
44
- this.createRipple(ev as DragEvent, "red");
45
- return;
46
- }
47
- this.createRipple(ev as DragEvent);
48
-
49
- const output: IDropOutput = {
50
- timestamp: new Date().toISOString(),
51
- types: (dataTransfer.types as any),
52
- tipo: "unknown",
53
- };
54
-
55
- // 1. Verificar se há arquivos
56
- if (dataTransfer.files && dataTransfer.files.length > 0) {
57
-
58
- output.data = ([] as IFileMetadata[]);
59
- output.tipo = "file";
60
-
61
- for await (let file of dataTransfer.files) {
62
- let lastIndexOf = file.name.lastIndexOf(".");
63
- if (lastIndexOf < 0) {
64
- LOG(1, `❌ O arquivo ${file.name} está sem uma extensão.`);
65
- continue;
66
- }
67
- let base64: string | null = await LibUtil.fileReader(file);
68
- if (!base64) {
69
- LOG(1, `❌ Ocorreu um erro com o arquivo ${file.name}.`);
70
- continue;
71
- }
72
-
73
- (output.data as IFileMetadata[]).push({
74
- base64: base64,
75
- name: file.name,
76
- size: file.size,
77
- extension: file.name.slice(lastIndexOf), // inclui o "."
78
- type: file.type,
79
- lastModifiedIso: new Date(file.lastModified).toISOString(),
80
- });
81
- }
82
-
83
- }
84
-
85
- // 2. Verificar se há texto
86
- const text = dataTransfer.getData('text/plain');
87
- if (text) {
88
- output.data = text;
89
- output.tipo = "text";
90
- }
91
-
92
- // 3. Verificar se há HTML
93
- const html = dataTransfer.getData('text/html');
94
- if (html) {
95
- output.data = html;
96
- output.tipo = "html";
97
- }
98
-
99
- // 4. Verificar se há URL
100
- const url = dataTransfer.getData('text/uri-list');
101
- if (url) {
102
- output.data = url;
103
- output.tipo = "url";
104
- }
105
-
106
- // 5. Verificar se há dados JSON customizados
107
- try {
108
- const json = dataTransfer.getData('application/json');
109
- if (json) {
110
- output.data = JSON.parse(json);
111
- output.tipo = "json";
112
- }
113
- } catch (e) {
114
- // JSON inválido, ignorar
115
- }
116
-
117
- // 6. Capturar todos os outros tipos de dados disponíveis
118
- dataTransfer.types.forEach(type => {
119
- if (!['text/plain', 'text/html', 'text/uri-list', 'application/json', 'Files'].includes(type)) {
120
- const data = dataTransfer.getData(type);
121
- if (data) {
122
- output.data = data;
123
- output.tipo = "unknown";
124
- }
125
- }
126
- });
127
-
128
- this.value.set(output);
129
- this.changed.emit(output);
130
- }
131
-
132
- createRipple(ev: DragEvent, classColor: string = "") {
133
- const rippleEffect = ev.currentTarget ?? this.rippleEffect()?.nativeElement;
134
- const ripple = this.ripple()?.nativeElement.cloneNode(true) as HTMLElement;
135
- const rect = rippleEffect.getBoundingClientRect();
136
- // Calcula a posição do clique relativa ao botão
137
- const x = ev.clientX - rect.left;
138
- const y = ev.clientY - rect.top;
139
- // Define o tamanho do ripple (maior dimensão do botão * 2)
140
- const size = Math.max(rect.width, rect.height) * 2;
141
- ripple.style.width = ripple.style.height = size + 'px';
142
- ripple.style.left = (x - size / 2) + 'px';
143
- ripple.style.top = (y - size / 2) + 'px';
144
- ripple.classList.add('ripple');
145
- if (classColor) {
146
- ripple.classList.add(classColor);
147
- }
148
- rippleEffect.appendChild(ripple);
149
- ripple.addEventListener('animationend', () => {
150
- ripple.remove();
151
- });
152
- }
153
- }
@@ -1,43 +0,0 @@
1
- :host {
2
- width: 100%;
3
- }
4
-
5
- .e-filelist-area {
6
- display: flex;
7
- flex-direction: column;
8
- gap: 12px;
9
- margin: 6px 0;
10
- width: 100%;
11
- }
12
-
13
- .e-filelist-item {
14
- display: grid;
15
- grid-template-columns: 1fr auto 1fr;
16
- gap: 5px;
17
- }
18
-
19
- .e-filelist-item-icon {
20
- display: flex;
21
- flex-direction: column;
22
- justify-content: center;
23
- align-items: center;
24
- height: 100%;
25
- aspect-ratio: 1 / 1;
26
- border-radius: 6px;
27
- font-size: 1.2rem;
28
- }
29
-
30
- .e-filelist-item-icon-ext {
31
- display: flex;
32
- justify-content: center;
33
- align-items: center;
34
- font-size: 0.7rem;
35
- }
36
-
37
- .e-filelist-item-text {
38
- align-items: center;
39
- align-content: center;
40
- overflow: hidden;
41
- text-overflow: ellipsis;
42
- white-space: nowrap;
43
- }
@@ -1,17 +0,0 @@
1
- <div class="e-filelist-area">
2
- @for (file of filesList(); track $index) {
3
- <div class="e-filelist-item">
4
- <div class="e-filelist-item-icon">
5
- <i class="bi bi-file-earmark-fill"></i>
6
- <div class="e-filelist-item-icon-ext">{{ file.extension?.slice(1) }}</div>
7
- </div>
8
- <div class="e-filelist-item-text">{{ file.name }}</div>
9
- <div class="e-filelist-action">
10
- @if(!disabled()){
11
- <ui-button tipo="icon" tema="error" [disabled]="disabled()" (click)="onDelete(file)" icone="bi bi-x-lg">
12
- </ui-button>
13
- }
14
- </div>
15
- </div>
16
- }
17
- </div>
@@ -1,22 +0,0 @@
1
- import { Component, input, output } from '@angular/core';
2
- import { UiButton } from "../ui-button/ui-button";
3
- import { IFileMetadata } from '../../interfaces/interfaces';
4
-
5
- @Component({
6
- selector: 'ui-file-list',
7
- templateUrl: './ui-file-list.html',
8
- styleUrl: './ui-file-list.css',
9
- imports: [UiButton]
10
- })
11
- export class UiFileList {
12
-
13
- disabled = input<boolean>();
14
- filesList = input<IFileMetadata[]>();
15
- remove = output<IFileMetadata>();
16
-
17
- onDelete(file: IFileMetadata) {
18
- if (this.disabled()) return;
19
- this.remove.emit(file);
20
- }
21
-
22
- }
@@ -1,30 +0,0 @@
1
- :host {
2
- display: contents;
3
- }
4
-
5
- .erroArea {
6
- background-color: transparent;
7
- border-radius: var(--MasterRadius);
8
- font-size: 12px;
9
- padding: 0px 8px 2px 8px;
10
- display: flex;
11
- flex-wrap: wrap;
12
- justify-content: flex-end;
13
- }
14
-
15
- .erroArea .erroText {
16
- color: var(--sys-error);
17
- }
18
-
19
- .erroArea .dicaText {
20
- color: var(--sys-info);
21
- }
22
-
23
- .erroArea.hide {
24
- display: none;
25
- }
26
-
27
- span {
28
- display: block;
29
- margin-left: 3px;
30
- }
@@ -1,10 +0,0 @@
1
- @if(show()){
2
- <div class="erroArea" [class.hide]="(!errors() || errors()?.length === 0) && (!dica())">
3
- @for (erro of errors(); track $index) {
4
- <span class="erroText">{{ erro ?? 'Erro desconhecido' }}</span>
5
- }
6
- @if(dica()){
7
- <span class="dicaText">{{ dica() ?? '' }}</span>
8
- }
9
- </div>
10
- }
@@ -1,14 +0,0 @@
1
- import { Component, input } from '@angular/core';
2
-
3
- @Component({
4
- selector: 'ui-list-errors',
5
- templateUrl: './ui-list-errors.html',
6
- styleUrl: './ui-list-errors.css',
7
- })
8
- export class UiListErrors {
9
-
10
- show = input.required<boolean | undefined>();
11
-
12
- errors = input.required<(string | undefined)[] | undefined>();
13
- dica = input.required<string | undefined>();
14
- }
@@ -1,13 +0,0 @@
1
- i {
2
- animation: spin 1s linear infinite;
3
- }
4
-
5
- @keyframes spin {
6
- 0% {
7
- transform: rotate(0deg);
8
- }
9
-
10
- 100% {
11
- transform: rotate(360deg);
12
- }
13
- }
@@ -1 +0,0 @@
1
- <i class="fa fa-spinner"></i>
@@ -1,10 +0,0 @@
1
- import { Component } from '@angular/core';
2
-
3
- @Component({
4
- selector: 'ui-loading',
5
- templateUrl: './ui-loading.html',
6
- styleUrl: './ui-loading.css',
7
- })
8
- export class UiLoading {
9
-
10
- }
@@ -1,69 +0,0 @@
1
- .e-menu-area {
2
- background-color: var(--sys-card);
3
- border-radius: 4px;
4
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
5
- padding: 8px 0;
6
- display: flex;
7
- flex-direction: column;
8
- gap: var(--ui-gap, 4px);
9
- user-select: none;
10
- }
11
-
12
- .e-menu-trigger {
13
- flex: 1;
14
- display: block;
15
- }
16
-
17
- .e-menu-overlay {
18
- position: fixed;
19
- top: 0;
20
- left: 0;
21
- width: 100%;
22
- height: 100vh;
23
- z-index: 1001;
24
- background-color: transparent;
25
- pointer-events: none;
26
- transition: background-color 0.2s ease-in-out;
27
- }
28
-
29
- .e-menu-overlay.open {
30
- background-color: rgba(0, 0, 0, 0.25);
31
- pointer-events: auto;
32
- }
33
-
34
- .e-menu-container {
35
- width: max-content;
36
- position: fixed;
37
- z-index: 2002;
38
- display: flex;
39
- flex-direction: column;
40
- overflow: hidden;
41
- border-radius: 10px;
42
- transform: translateZ(0);
43
- will-change: transform;
44
-
45
- transform-origin: top left;
46
- transform: scale(0.8) translate(0, -10%) rotateX(90deg) skew(15deg);
47
-
48
- opacity: 0;
49
- visibility: hidden;
50
- /* transition: opacity 0.2s ease-in-out, visibility 0.2s ease-in-out, transform 0.2s ease-in-out; */
51
- transition: all 0.15s ease-in-out;
52
- }
53
-
54
- .e-menu-overlay.open .e-menu-container {
55
- color: var(--sys-on-card);
56
- background: var(--sys-card);
57
- outline: 1px solid var(--sys-outline);
58
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
59
- visibility: visible;
60
- opacity: 1;
61
- transform: scale(1);
62
- }
63
-
64
- .e-menu-overlay.open .e-menu-container.smallScreen {
65
- width: 90vw;
66
- left: 5vw !important;
67
- /* 170 == 300 List + 40 Search / 2 */
68
- top: calc(50vh - 170px) !important;
69
- }
@@ -1,20 +0,0 @@
1
- <div #menuTrigger class="e-menu-trigger" (click)="onClickSlotBotao($event)" (keyup.Enter)="onOpenOrCloseOptions()"
2
- (keydown.ArrowDown)="onArrowDown($event)" (keyup.ArrowUp)="onArrowUp($event)" (keyup.Escape)="closeOverlay()"
3
- [id]="'tr'+id()" [class.open]="isOpened()">
4
- <ng-content select="[slot='botao']">
5
- <ui-button tipo="icon" tema="primary" [icone]="icone()"></ui-button>
6
- </ng-content>
7
- </div>
8
-
9
- <div #menuOverlay class="e-menu-overlay" [class.open]="isOpened()" (click)="onClickOverlay()"
10
- (keydown.ArrowDown)="onArrowDown($event)" (keyup.ArrowUp)="onArrowUp($event)" [style.zIndex]="zIndex()"
11
- [id]="'ov'+id()">
12
-
13
- <div #menuContainer class="e-menu-container" [class.smallScreen]="isSmallScreen()" [style.left]="menuPosition().left"
14
- [style.top]="menuPosition().top" (click)="onClickContainer($event)">
15
- <div [style.minWidth.px]="optionMinWidth()" class="e-menu-area">
16
- <ng-content></ng-content>
17
- </div>
18
- </div>
19
-
20
- </div>
@@ -1,267 +0,0 @@
1
- import { AfterViewInit, Component, effect, ElementRef, HostListener, inject, input, model, OnDestroy, OnInit, signal, viewChild } from '@angular/core';
2
- import { autoUpdate, computePosition, flip, offset, shift } from '@floating-ui/dom';
3
- import { UiButton } from "../ui-button/ui-button";
4
- import { LibUtil, LOG } from '../../util/util';
5
- import { IPosicoesFloating } from '../../interfaces/interfaces';
6
-
7
- @Component({
8
- selector: 'ui-menu',
9
- templateUrl: './ui-menu.html',
10
- styleUrl: './ui-menu.css',
11
- imports: [UiButton],
12
- host: { '[class.opened]': 'isOpened()' }
13
- })
14
- export class UiMenu implements OnDestroy, OnInit, AfterViewInit {
15
- // UTILIZE:
16
- // 'slot:botao' para colocar o botão que irá abrir o menu, ou utilize o botão padrão (três pontos verticais)
17
- // O demais elementos dentro do <ui-menu> serão considerados opções do menu, e serão exibidos dentro do overlay quando o menu for aberto
18
-
19
- elementRef: ElementRef = inject(ElementRef);
20
-
21
- readonly disabled = input<boolean>();
22
- readonly icone = input<string | undefined | null>("bi bi-three-dots-vertical");
23
- autoCloseInCascade = model<boolean>(true);
24
-
25
- posicao = model<IPosicoesFloating & string>('right-start');
26
- isOpened = model(false);
27
- id = signal(LibUtil.uuid().split('-')[0]);
28
- internalId = signal<{ id: string | undefined; changed: boolean } | undefined>({ id: undefined, changed: false });
29
- isSmallScreen = signal(window.innerWidth < 500);
30
- optionMinWidth = signal<number>(0);
31
- menuPosition = signal<{ top: string; left: string }>({ top: '0px', left: '0px' });
32
- zIndex = signal<string>('999');
33
- closeOverlayObserver = new MutationObserver((mutations) => {
34
- let hasOpenClass = false;
35
- for (const mutation of mutations) {
36
- if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
37
- const target = mutation.target as HTMLElement;
38
- if (target.classList.contains('open')) {
39
- hasOpenClass = true;
40
- break;
41
- }
42
- }
43
- }
44
- if (!hasOpenClass) {
45
- // LOG(1, "%cObserver detectou overlay fechado:", 'color: lightcoral;', mutations?.[0]);
46
- this.closeOverlay(true); // Callback para fechar o overlay quando a mutação indicar que perdeu a classe 'open'
47
- }
48
- });
49
-
50
-
51
- readonly menuTrigger = viewChild<ElementRef>('menuTrigger');
52
- readonly menuOverlay = viewChild<ElementRef>('menuOverlay');
53
- readonly menuContainer = viewChild<ElementRef>('menuContainer');
54
-
55
- constructor() {
56
- effect(() => {
57
-
58
- const eTrigger = this.menuTrigger()?.nativeElement as HTMLElement;
59
- if (!eTrigger) return;
60
-
61
- if (!this.isOpened()) return;
62
-
63
- const cleanupAutoUpdate = autoUpdate(
64
- eTrigger,
65
- this.menuContainer()?.nativeElement,
66
- () => this.updatePosition()
67
- );
68
- return () => {
69
- cleanupAutoUpdate();
70
- };
71
-
72
- });
73
-
74
- // Atualiza posição quando o menu abre
75
- effect(() => {
76
- if (this.isOpened()) {
77
- setTimeout(() => {
78
- this.updatePosition();
79
- }, 1);
80
- }
81
- });
82
- effect(() => {
83
- let internalId = this.internalId();
84
- if (internalId?.id === undefined && internalId?.changed === true) {
85
- // LOG(1, "%cAlterado Internal:", 'color: lightcoral;', internalId, " - Menu ID:", this.id());
86
- this.closeOverlay();
87
- };
88
- });
89
- }
90
- ngOnInit() {
91
- // Inicializa o zIndex com base no valor armazenado no sessionStorage
92
- this.zIndex.set(sessionStorage.getItem(`ui-menu-index`) ? (Number(sessionStorage.getItem(`ui-menu-index`)) + 1).toString() : '1001');
93
- sessionStorage.setItem(`ui-menu-index`, this.zIndex());
94
- }
95
- ngOnDestroy() {
96
- if (this.isOpened() == true) this.closeOverlay();
97
- this.closeOverlayObserver.disconnect();
98
- // this.cleanupScrollListeners();
99
- }
100
- ngAfterViewInit() {
101
- this.checkScreenSize();
102
- this.setOverlaySizeToSizeViewport();
103
-
104
- this.updatePosition();
105
- }
106
-
107
- // Abre ou fecha o overlay de opções
108
- onOpenOrCloseOptions() {
109
- if (!this.disabled()) {
110
- if (this.isOpened()) {
111
- this.closeOverlay();
112
- } else {
113
- this.openOverlay();
114
- }
115
- this.menuTrigger()?.nativeElement.focus();
116
- }
117
- }
118
- // Abre ou fecha o overlay de opções
119
- onClickSlotBotao(event: Event) {
120
- if (!this.disabled()) {
121
- this.openOverlay();
122
- this.menuTrigger()?.nativeElement.focus();
123
- }
124
- // LOG(1, "%cAbriu ID:", 'color: lightgreen;', this.id());
125
- }
126
-
127
- onClickOverlay() {
128
- // LOG(1, "%cFECHOU OVERLAY ID:", 'color: lightblue;', this.id());
129
- this.closeOverlay();
130
- }
131
-
132
-
133
- onClickContainer(event: Event) {
134
- event.stopPropagation();
135
- let closestOverlay = (event.target as HTMLElement).closest('.e-menu-overlay');
136
- let overlayId = closestOverlay?.id?.replaceAll('ov', '');
137
- let closestTrigger = (event.target as HTMLElement).closest('.e-menu-trigger');
138
- let triggerId = closestTrigger?.id?.replaceAll('tr', '');
139
- this.internalId.set({ id: triggerId, changed: true });
140
-
141
- // LOG(1, 'Triger ID:', triggerId, ' - Overlay ID:', overlayId);
142
-
143
- if (overlayId == this.id() && triggerId != null && triggerId != this.id()) {
144
- // Um menu interno foi clicado, não fecha o overlay
145
- // O atual nível de menu irá observar o elemento closestTrigger e só irá fechar o overlay quando o elemento trigger fechar.
146
- this.closeOverlayObserver.observe(closestTrigger as HTMLElement, { attributes: true, attributeFilter: ['class'] });
147
- } else {
148
- // Clicou no container, fecha o overlay
149
- this.closeOverlay();
150
- }
151
-
152
- }
153
-
154
- // Fecha o overlay de opções
155
- protected closeOverlay(fromCascade: boolean = false) {
156
- if (fromCascade && !this.autoCloseInCascade()) return;
157
-
158
- this.closeOverlayObserver.disconnect();
159
- setTimeout(() => {
160
- this.isOpened.set(false);
161
- }, 2)
162
- }
163
-
164
- // Abre o overlay de opções
165
- protected openOverlay() {
166
- this.updatePosition();
167
- setTimeout(() => {
168
- this.isOpened.set(true);
169
- }, 2)
170
- }
171
-
172
- // Atualiza a posição do menu de opções
173
- private async updatePosition() {
174
- if (this.isSmallScreen()) return;
175
-
176
- const trigger = this.menuTrigger()?.nativeElement;
177
- const menuContainer = this.menuContainer()?.nativeElement;
178
- if (!trigger || !menuContainer) return;
179
-
180
- const { x, y } = await computePosition(trigger, menuContainer, {
181
- placement: this.posicao(),
182
- middleware: [
183
- offset(2),
184
- flip({
185
- fallbackPlacements: ['left-start', 'bottom-start', 'top-start', 'bottom-end', 'top-end'],
186
- }),
187
- shift({ padding: 10 }),
188
- ],
189
- });
190
- this.menuPosition.set({
191
- left: `${x}px`,
192
- top: `${y}px`
193
- });
194
- }
195
-
196
- setOverlaySizeToSizeViewport() {
197
- // if (!this.isOpened()) return;
198
-
199
- // Obtem o tamanho da largura da viewport
200
- const viewportWidth = window.innerWidth;
201
- const parcialViewport = viewportWidth * 0.92;
202
-
203
- const overlay = this.menuOverlay()?.nativeElement as HTMLElement;
204
- if (overlay) {
205
- let currentElement = overlay.parentElement as HTMLElement | null;
206
- while (currentElement) {
207
- let larguraOverlay = overlay.clientWidth;
208
-
209
- // Se chegar no body, para (para evitar mover o overlay para fora do body)
210
- if (currentElement.tagName.toLowerCase() === "body") break;
211
-
212
- // Move o overlay para o próximo nível acima
213
- currentElement.appendChild(overlay);
214
-
215
- if (larguraOverlay >= parcialViewport) {
216
- currentElement = null;
217
- break;
218
- }
219
-
220
- currentElement = currentElement.parentElement as HTMLElement;
221
- }
222
-
223
- }
224
- }
225
-
226
- onChangeMediaQuery(e: MediaQueryListEvent | MediaQueryList) {
227
- this.isSmallScreen.set(e.matches);
228
- const eTrigger = this.menuTrigger()?.nativeElement as HTMLElement;
229
- if (eTrigger) {
230
- if (this.isSmallScreen()) {
231
- this.optionMinWidth.set(0);
232
- } else {
233
- this.optionMinWidth.set(eTrigger.offsetWidth - 4);
234
- }
235
- }
236
- }
237
-
238
- checkScreenSize() {
239
- const mediaQuery = window.matchMedia('(max-width: 500px)');
240
- this.onChangeMediaQuery(mediaQuery);
241
- mediaQuery.addEventListener('change', this.onChangeMediaQuery.bind(this));
242
- }
243
-
244
- onArrowUp(event: Event) {
245
- event.preventDefault();
246
- // this.moveFocoPara('cima', event);
247
- }
248
-
249
- onArrowDown(event: Event) {
250
- event.preventDefault();
251
- // this.moveFocoPara('baixo', event);
252
- }
253
-
254
- // moveFocoPara(direcao: 'cima' | 'baixo', event: Event) {
255
- // this.openOverlay();
256
- // LOG(1, `Moverá para ${direcao} - Event:`, event);
257
- // const container = this.menuContainer()?.nativeElement as HTMLElement;
258
- // if (!container) return;
259
-
260
- // const focusableElements = Array.from(container.querySelectorAll('.e-menu-area>*')).filter(el => el instanceof HTMLElement && !el.hasAttribute('disabled')) as HTMLElement[];
261
- // if (focusableElements.length === 0) return;
262
-
263
- // //
264
-
265
- // }
266
-
267
- }