@agorapulse/ui-components 18.0.35 → 18.0.37

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 (28) hide show
  1. package/agorapulse-ui-components-18.0.37.tgz +0 -0
  2. package/directives/checkbox.directive.d.ts +60 -0
  3. package/directives/public_api.d.ts +1 -0
  4. package/esm2022/directives/checkbox.directive.mjs +442 -0
  5. package/esm2022/directives/public_api.mjs +2 -1
  6. package/esm2022/index.mjs +2 -2
  7. package/esm2022/nav-selector/nav-selector-category/nav-selector-category.component.mjs +19 -8
  8. package/esm2022/nav-selector/nav-selector-group/nav-selector-group.component.mjs +19 -8
  9. package/esm2022/nav-selector/nav-selector-leaf/nav-selector-leaf.component.mjs +17 -8
  10. package/esm2022/nav-selector/nav-selector-leaf-detail/nav-selector-leaf-detail.component.mjs +3 -3
  11. package/esm2022/nav-selector/nav-selector-leaf-details/nav-selector-leaf-details.component.mjs +3 -3
  12. package/esm2022/nav-selector/nav-selector.component.mjs +3 -3
  13. package/esm2022/nav-selector/utils/leaf.utils.mjs +16 -1
  14. package/esm2022/nav-selector/utils/nav-selector.filter.mjs +2 -2
  15. package/esm2022/nav-selector/utils/nav-selector.merger.mjs +2 -2
  16. package/fesm2022/agorapulse-ui-components-directives.mjs +442 -3
  17. package/fesm2022/agorapulse-ui-components-directives.mjs.map +1 -1
  18. package/fesm2022/agorapulse-ui-components-nav-selector.mjs +71 -27
  19. package/fesm2022/agorapulse-ui-components-nav-selector.mjs.map +1 -1
  20. package/fesm2022/agorapulse-ui-components.mjs +1 -1
  21. package/fesm2022/agorapulse-ui-components.mjs.map +1 -1
  22. package/index.d.ts +1 -1
  23. package/nav-selector/nav-selector-category/nav-selector-category.component.d.ts +1 -0
  24. package/nav-selector/nav-selector-group/nav-selector-group.component.d.ts +1 -0
  25. package/nav-selector/nav-selector-leaf/nav-selector-leaf.component.d.ts +1 -0
  26. package/nav-selector/utils/leaf.utils.d.ts +1 -0
  27. package/package.json +1 -1
  28. package/agorapulse-ui-components-18.0.35.tgz +0 -0
@@ -1,6 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
- import { HostListener, Directive, inject, ElementRef, ChangeDetectorRef, Input, EventEmitter, Output, forwardRef, Attribute } from '@angular/core';
3
- import { NG_VALIDATORS } from '@angular/forms';
2
+ import { HostListener, Directive, inject, ElementRef, ChangeDetectorRef, forwardRef, Renderer2, ViewContainerRef, EventEmitter, booleanAttribute, Output, Input, Attribute } from '@angular/core';
3
+ import { SymbolRegistry, apCheck, SymbolComponent } from '@agorapulse/ui-symbol';
4
+ import { NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
4
5
  import DOMPurify from 'dompurify';
5
6
 
6
7
  /**
@@ -91,6 +92,444 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImpor
91
92
  }]
92
93
  }] });
93
94
 
95
+ const AP_CHECKBOX_DIRECTIVE_CONTROL_VALUE_ACCESSOR = {
96
+ provide: NG_VALUE_ACCESSOR,
97
+ useExisting: forwardRef(() => CheckboxDirective),
98
+ multi: true,
99
+ };
100
+ /*
101
+ Host bindings is important here:
102
+ - This provides encapsulation and reactivity regardless of how the directive is used in templates.
103
+ - It guarantees that properties like checked, disabled, required, and ARIA attributes always reflect
104
+ only the directive's logic, not just consumer bindings.
105
+ - So we need to have host bindings for all the properties that we want to control.
106
+ - We could also use @HostListener.
107
+ */
108
+ class CheckboxDirective {
109
+ symbolRegistry = inject(SymbolRegistry);
110
+ elementRef = inject((ElementRef));
111
+ renderer = inject(Renderer2);
112
+ viewContainer = inject(ViewContainerRef);
113
+ ariaLabel;
114
+ ariaLabelledby;
115
+ ariaDescribedby;
116
+ disabled = false;
117
+ set indeterminate(indeterminate) {
118
+ if (indeterminate) {
119
+ this._checked = false;
120
+ }
121
+ this._indeterminate = indeterminate;
122
+ const input = this.elementRef.nativeElement;
123
+ if (input) {
124
+ input.indeterminate = indeterminate;
125
+ }
126
+ // Only update visibility if containers are ready
127
+ if (this.checkmarkContainer && this.indeterminateContainer) {
128
+ this.updateCheckmarkVisibility();
129
+ }
130
+ }
131
+ get indeterminate() {
132
+ return this._indeterminate;
133
+ }
134
+ set checked(checked) {
135
+ this._checked = checked;
136
+ // Only update visibility if containers are ready
137
+ if (this.checkmarkContainer && this.indeterminateContainer) {
138
+ this.updateCheckmarkVisibility();
139
+ }
140
+ }
141
+ get checked() {
142
+ return this._checked;
143
+ }
144
+ required = false;
145
+ set name(name) {
146
+ this._name = name;
147
+ }
148
+ get name() {
149
+ return this._name;
150
+ }
151
+ // eslint-disable-next-line @angular-eslint/no-output-native
152
+ change = new EventEmitter();
153
+ _checked = false;
154
+ _indeterminate = false;
155
+ _name = '';
156
+ _controlValueAccessorChangeFn = () => { };
157
+ _onTouched = () => { };
158
+ listeners = [];
159
+ styleElement;
160
+ wrapper;
161
+ checkmarkContainer;
162
+ indeterminateContainer;
163
+ symbolComponent;
164
+ ngOnInit() {
165
+ this.symbolRegistry.registerSymbols([apCheck]);
166
+ this.createCheckboxStructure();
167
+ this.setupEventListeners();
168
+ }
169
+ ngOnDestroy() {
170
+ this.listeners.forEach(unlisten => unlisten());
171
+ if (this.styleElement && this.styleElement.parentNode) {
172
+ this.styleElement.parentNode.removeChild(this.styleElement);
173
+ }
174
+ if (this.symbolComponent) {
175
+ this.symbolComponent.destroy();
176
+ }
177
+ if (this.checkmarkContainer && this.checkmarkContainer.parentNode) {
178
+ this.checkmarkContainer.parentNode.removeChild(this.checkmarkContainer);
179
+ }
180
+ if (this.indeterminateContainer && this.indeterminateContainer.parentNode) {
181
+ this.indeterminateContainer.parentNode.removeChild(this.indeterminateContainer);
182
+ }
183
+ // Move input back out of wrapper and remove wrapper
184
+ if (this.wrapper && this.wrapper.parentNode) {
185
+ const input = this.elementRef.nativeElement;
186
+ this.renderer.insertBefore(this.wrapper.parentNode, input, this.wrapper);
187
+ this.wrapper.parentNode.removeChild(this.wrapper);
188
+ }
189
+ }
190
+ createCheckboxStructure() {
191
+ const input = this.elementRef.nativeElement;
192
+ const parent = input.parentElement;
193
+ // Hot take - Input doesn't need positioning since it will be wrapped
194
+ // Make input's parent use inline-flex for alignment
195
+ if (parent) {
196
+ this.renderer.setStyle(parent, 'display', 'inline-flex');
197
+ this.renderer.setStyle(parent, 'align-items', 'center');
198
+ this.renderer.setStyle(parent, 'gap', 'var(--ref-spacing-xxs)');
199
+ }
200
+ this.renderer.addClass(input, 'ap-checkbox-styled');
201
+ this.checkmarkContainer = this.createOverlayContainer('ap-checkbox-checkmark');
202
+ this.wrapper = this.renderer.createElement('span');
203
+ this.renderer.setStyle(this.wrapper, 'position', 'relative');
204
+ this.renderer.setStyle(this.wrapper, 'display', 'inline-flex');
205
+ this.renderer.setStyle(this.wrapper, 'align-items', 'center');
206
+ this.renderer.setStyle(this.wrapper, 'vertical-align', 'middle');
207
+ this.renderer.insertBefore(parent, this.wrapper, input);
208
+ this.renderer.appendChild(this.wrapper, input);
209
+ this.renderer.appendChild(this.wrapper, this.checkmarkContainer);
210
+ this.indeterminateContainer = this.createOverlayContainer('ap-checkbox-indeterminate');
211
+ this.renderer.appendChild(this.wrapper, this.indeterminateContainer);
212
+ this.createCheckmarkSymbol();
213
+ this.createIndeterminateBar();
214
+ this.applyCheckboxStyles();
215
+ this.updateCheckmarkVisibility();
216
+ }
217
+ createOverlayContainer(className) {
218
+ const container = this.renderer.createElement('span');
219
+ this.renderer.addClass(container, className);
220
+ this.renderer.setStyle(container, 'position', 'absolute');
221
+ this.renderer.setStyle(container, 'top', '50%');
222
+ this.renderer.setStyle(container, 'left', '50%');
223
+ this.renderer.setStyle(container, 'transform', 'translate(-50%, -50%)');
224
+ this.renderer.setStyle(container, 'width', 'auto');
225
+ this.renderer.setStyle(container, 'height', 'auto');
226
+ this.renderer.setStyle(container, 'display', 'flex');
227
+ this.renderer.setStyle(container, 'justify-content', 'center');
228
+ this.renderer.setStyle(container, 'align-items', 'center');
229
+ this.renderer.setStyle(container, 'pointer-events', 'none');
230
+ this.renderer.setStyle(container, 'opacity', '0');
231
+ this.renderer.setStyle(container, 'z-index', '1');
232
+ return container;
233
+ }
234
+ createCheckmarkSymbol() {
235
+ // Create the symbol component
236
+ this.symbolComponent = this.viewContainer.createComponent(SymbolComponent);
237
+ // Set inputs using setInput method for signal-based inputs
238
+ this.symbolComponent.setInput('symbolId', 'check');
239
+ this.symbolComponent.setInput('color', 'white');
240
+ this.symbolComponent.setInput('size', 10);
241
+ // Append the symbol to our checkmark container
242
+ this.renderer.appendChild(this.checkmarkContainer, this.symbolComponent.location.nativeElement);
243
+ }
244
+ createIndeterminateBar() {
245
+ // Create the indeterminate bar element exactly like the component
246
+ const bar = this.renderer.createElement('span');
247
+ this.renderer.setStyle(bar, 'height', '1.5px');
248
+ this.renderer.setStyle(bar, 'background', 'var(--ref-color-white)');
249
+ this.renderer.setStyle(bar, 'width', '8px');
250
+ // Append the bar to the indeterminate container
251
+ this.renderer.appendChild(this.indeterminateContainer, bar);
252
+ }
253
+ updateCheckmarkVisibility() {
254
+ if (this.checkmarkContainer) {
255
+ const opacity = this._checked && !this._indeterminate ? '1' : '0';
256
+ this.renderer.setStyle(this.checkmarkContainer, 'opacity', opacity);
257
+ }
258
+ if (this.indeterminateContainer) {
259
+ const opacity = this._indeterminate && !this._checked ? '1' : '0';
260
+ this.renderer.setStyle(this.indeterminateContainer, 'opacity', opacity);
261
+ }
262
+ }
263
+ setupEventListeners() {
264
+ const input = this.elementRef.nativeElement;
265
+ // Input change listener (native checkbox change)
266
+ const changeListener = this.renderer.listen(input, 'change', () => {
267
+ this.onValueChange();
268
+ });
269
+ this.listeners.push(changeListener);
270
+ // Listen for label clicks to ensure focus is maintained
271
+ const parent = input.parentElement;
272
+ if (parent) {
273
+ const labelClickListener = this.renderer.listen(parent, 'click', (event) => {
274
+ const target = event.target;
275
+ // If clicking on a label that targets our input, ensure focus
276
+ if (target.tagName === 'LABEL' && target.getAttribute('for') === this.name) {
277
+ // Small delay to ensure the label click is processed first
278
+ setTimeout(() => {
279
+ input.focus();
280
+ }, 0);
281
+ }
282
+ });
283
+ this.listeners.push(labelClickListener);
284
+ }
285
+ }
286
+ applyCheckboxStyles() {
287
+ // Style the input directly without moving DOM elements
288
+ const styles = `
289
+ /* Complete reset and custom styling for our checkbox */
290
+ input[type="checkbox"].ap-checkbox-styled {
291
+ /* Targeted reset of problematic properties */
292
+ appearance: none !important;
293
+ -webkit-appearance: none !important;
294
+ -moz-appearance: none !important;
295
+
296
+ /* Our custom styling - use !important to override any global styles */
297
+ display: inline-block !important;
298
+ width: 16px !important;
299
+ height: 16px !important;
300
+ min-width: 16px !important;
301
+ min-height: 16px !important;
302
+ margin: 0 !important;
303
+ padding: 0 !important;
304
+ border: 1px solid var(--ref-color-grey-60);
305
+ border-radius: var(--sys-border-radius-sm);
306
+ background: var(--ref-color-white);
307
+ background-color: var(--ref-color-white);
308
+ box-sizing: border-box;
309
+ position: relative;
310
+ cursor: pointer;
311
+ vertical-align: middle;
312
+
313
+ /* Ensure no other styles can interfere */
314
+ box-shadow: none;
315
+ text-decoration: none;
316
+ font: inherit;
317
+ color: inherit;
318
+ letter-spacing: normal;
319
+ word-spacing: normal;
320
+ text-transform: none;
321
+ text-indent: 0;
322
+ text-shadow: none;
323
+ text-align: start;
324
+ }
325
+
326
+ /* Hover state */
327
+ input[type="checkbox"].ap-checkbox-styled:hover {
328
+ border-color: var(--ref-color-grey-80);
329
+ }
330
+
331
+ /* Active/pressed state */
332
+ input[type="checkbox"].ap-checkbox-styled:active {
333
+ border-color: var(--ref-color-grey-100);
334
+ }
335
+
336
+ /* Checked state */
337
+ input[type="checkbox"].ap-checkbox-styled:checked {
338
+ background: var(--ref-color-electric-blue-100);
339
+ border-color: var(--ref-color-electric-blue-100);
340
+ }
341
+
342
+ /* Checked hover state */
343
+ input[type="checkbox"].ap-checkbox-styled:checked:hover {
344
+ background: var(--ref-color-electric-blue-80);
345
+ border-color: var(--ref-color-electric-blue-100);
346
+ }
347
+
348
+ /* Checked active/pressed state */
349
+ input[type="checkbox"].ap-checkbox-styled:checked:active {
350
+ background: var(--ref-color-electric-blue-60);
351
+ border-color: var(--ref-color-electric-blue-100);
352
+ }
353
+
354
+ /* Indeterminate state */
355
+ input[type="checkbox"].ap-checkbox-styled:indeterminate {
356
+ background: var(--ref-color-electric-blue-100);
357
+ border-color: var(--ref-color-electric-blue-100);
358
+ }
359
+
360
+ /* Disabled state */
361
+ input[type="checkbox"].ap-checkbox-styled:disabled {
362
+ border-color: var(--ref-color-grey-20);
363
+ background: var(--ref-color-grey-10);
364
+ cursor: default;
365
+ }
366
+
367
+ input[type="checkbox"].ap-checkbox-styled:disabled:checked {
368
+ background: var(--ref-color-grey-20);
369
+ }
370
+
371
+ /* Focus state - only show on hover-capable devices like the component */
372
+ @media (hover: hover) {
373
+ input[type="checkbox"].ap-checkbox-styled:focus:not(.disabled) {
374
+ outline: 3px solid var(--ref-color-electric-blue-100) !important;
375
+ outline-offset: 1px !important;
376
+ }
377
+ }
378
+
379
+ /* Label styling - targets labels that follow our wrapper containing styled checkboxes */
380
+ span:has(input[type="checkbox"].ap-checkbox-styled) + label {
381
+ display: flex;
382
+ align-items: center;
383
+ font-family: var(--comp-forms-label-font-family);
384
+ font-size: var(--comp-forms-label-size);
385
+ font-weight: var(--comp-forms-label-font-weight);
386
+ line-height: var(--comp-forms-label-line-height);
387
+ color: var(--comp-forms-label-text-color);
388
+ }
389
+
390
+ /* Empty label styling */
391
+ span:has(input[type="checkbox"].ap-checkbox-styled) + label:empty {
392
+ display: none;
393
+ }
394
+
395
+ /* Disabled label styling */
396
+ span:has(input[type="checkbox"].ap-checkbox-styled:disabled) + label {
397
+ color: var(--ref-color-grey-60);
398
+ }
399
+
400
+ /* Label hover styling */
401
+ span:has(input[type="checkbox"].ap-checkbox-styled) + label:hover:not(.disabled) {
402
+ cursor: pointer;
403
+ }
404
+
405
+ /* Disabled label hover */
406
+ span:has(input[type="checkbox"].ap-checkbox-styled:disabled) + label:hover {
407
+ cursor: default;
408
+ }
409
+ `;
410
+ // Create and append style element
411
+ this.styleElement = this.renderer.createElement('style');
412
+ this.renderer.appendChild(this.styleElement, this.renderer.createText(styles));
413
+ this.renderer.appendChild(document.head, this.styleElement);
414
+ }
415
+ onBlur() {
416
+ this._onTouched();
417
+ }
418
+ onValueChange() {
419
+ if (!this.disabled) {
420
+ const input = this.elementRef.nativeElement;
421
+ if (this._indeterminate) {
422
+ // When indeterminate, clicking should go to checked state
423
+ this._checked = true;
424
+ this._indeterminate = false;
425
+ input.checked = true;
426
+ input.indeterminate = false;
427
+ }
428
+ else {
429
+ // Normal toggle behavior
430
+ this._checked = input.checked;
431
+ }
432
+ this.updateCheckmarkVisibility();
433
+ this.change.emit(this._checked);
434
+ this._controlValueAccessorChangeFn(this._checked);
435
+ }
436
+ }
437
+ // ControlValueAccessor implementation
438
+ writeValue(value) {
439
+ this._checked = value;
440
+ const input = this.elementRef.nativeElement;
441
+ input.checked = value;
442
+ // Ensure indeterminate state is properly set
443
+ input.indeterminate = this._indeterminate;
444
+ this.updateCheckmarkVisibility();
445
+ }
446
+ registerOnChange(fn) {
447
+ this._controlValueAccessorChangeFn = fn;
448
+ }
449
+ registerOnTouched(fn) {
450
+ this._onTouched = fn;
451
+ }
452
+ setDisabledState(isDisabled) {
453
+ this.disabled = isDisabled;
454
+ const input = this.elementRef.nativeElement;
455
+ input.disabled = isDisabled;
456
+ }
457
+ validate() {
458
+ const isNotValid = !this._checked && this.required;
459
+ return (isNotValid && {
460
+ invalid: true,
461
+ });
462
+ }
463
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: CheckboxDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
464
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "18.2.9", type: CheckboxDirective, isStandalone: true, selector: "input[type=\"checkbox\"][apCheckbox]", inputs: { ariaLabel: ["aria-label", "ariaLabel"], ariaLabelledby: ["aria-labelledby", "ariaLabelledby"], ariaDescribedby: ["aria-describedby", "ariaDescribedby"], disabled: ["disabled", "disabled", booleanAttribute], indeterminate: ["indeterminate", "indeterminate", booleanAttribute], checked: "checked", required: ["required", "required", booleanAttribute], name: "name" }, outputs: { change: "change" }, host: { listeners: { "blur": "onBlur()" }, properties: { "disabled": "disabled", "required": "required", "checked": "checked", "attr.aria-label": "ariaLabel", "attr.aria-labelledby": "ariaLabelledby", "attr.aria-describedby": "ariaDescribedby", "attr.data-test": "name", "id": "name" } }, providers: [
465
+ AP_CHECKBOX_DIRECTIVE_CONTROL_VALUE_ACCESSOR,
466
+ {
467
+ provide: NG_VALIDATORS,
468
+ useExisting: CheckboxDirective,
469
+ multi: true,
470
+ },
471
+ ], ngImport: i0 });
472
+ }
473
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: CheckboxDirective, decorators: [{
474
+ type: Directive,
475
+ args: [{
476
+ selector: 'input[type="checkbox"][apCheckbox]',
477
+ standalone: true,
478
+ providers: [
479
+ AP_CHECKBOX_DIRECTIVE_CONTROL_VALUE_ACCESSOR,
480
+ {
481
+ provide: NG_VALIDATORS,
482
+ useExisting: CheckboxDirective,
483
+ multi: true,
484
+ },
485
+ ],
486
+ host: {
487
+ '(blur)': 'onBlur()',
488
+ '[disabled]': 'disabled',
489
+ '[required]': 'required',
490
+ '[checked]': 'checked',
491
+ '[attr.aria-label]': 'ariaLabel',
492
+ '[attr.aria-labelledby]': 'ariaLabelledby',
493
+ '[attr.aria-describedby]': 'ariaDescribedby',
494
+ '[attr.data-test]': 'name',
495
+ '[id]': 'name',
496
+ },
497
+ }]
498
+ }], propDecorators: { ariaLabel: [{
499
+ type: Input,
500
+ args: ['aria-label']
501
+ }], ariaLabelledby: [{
502
+ type: Input,
503
+ args: ['aria-labelledby']
504
+ }], ariaDescribedby: [{
505
+ type: Input,
506
+ args: ['aria-describedby']
507
+ }], disabled: [{
508
+ type: Input,
509
+ args: [{
510
+ transform: booleanAttribute,
511
+ }]
512
+ }], indeterminate: [{
513
+ type: Input,
514
+ args: [{
515
+ transform: booleanAttribute,
516
+ }]
517
+ }], checked: [{
518
+ type: Input
519
+ }], required: [{
520
+ type: Input,
521
+ args: [{
522
+ transform: booleanAttribute,
523
+ }]
524
+ }], name: [{
525
+ type: Input,
526
+ args: [{
527
+ required: true,
528
+ }]
529
+ }], change: [{
530
+ type: Output
531
+ }] } });
532
+
94
533
  class DefaultImageDirective {
95
534
  src = '';
96
535
  default = '';
@@ -469,5 +908,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImpor
469
908
  * Generated bundle index. Do not edit.
470
909
  */
471
910
 
472
- export { AutosizeTextareaDirective, BaseButtonDirective, DefaultImageDirective, EllipsisDirective, EqualValidatorDirective, FrozenGifDirective, GifService, MultiStyleTextDirective };
911
+ export { AP_CHECKBOX_DIRECTIVE_CONTROL_VALUE_ACCESSOR, AutosizeTextareaDirective, BaseButtonDirective, CheckboxDirective, DefaultImageDirective, EllipsisDirective, EqualValidatorDirective, FrozenGifDirective, GifService, MultiStyleTextDirective };
473
912
  //# sourceMappingURL=agorapulse-ui-components-directives.mjs.map