@agorapulse/ui-components 18.0.42 → 18.0.44

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.
@@ -1,7 +1,6 @@
1
1
  import * as i0 from '@angular/core';
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';
2
+ import { HostListener, Directive, inject, ElementRef, ChangeDetectorRef, Renderer2, signal, effect, booleanAttribute, Input, EventEmitter, Output, forwardRef, Attribute } from '@angular/core';
3
+ import { NG_VALIDATORS } from '@angular/forms';
5
4
  import DOMPurify from 'dompurify';
6
5
 
7
6
  /**
@@ -92,77 +91,54 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImpor
92
91
  }]
93
92
  }] });
94
93
 
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
94
  class CheckboxDirective {
109
- symbolRegistry = inject(SymbolRegistry);
110
95
  elementRef = inject((ElementRef));
111
96
  renderer = inject(Renderer2);
112
- viewContainer = inject(ViewContainerRef);
113
- ariaLabel;
114
- ariaLabelledby;
115
- ariaDescribedby;
116
- disabled = false;
117
- set indeterminate(indeterminate) {
97
+ // State signals
98
+ _checked = signal(false);
99
+ _indeterminate = signal(false);
100
+ // Input signal accessors that work with host bindings
101
+ checked = this._checked.asReadonly();
102
+ indeterminate = this._indeterminate.asReadonly();
103
+ // Input setters with proper aliases for template binding
104
+ // Couldn't be migrated to yet
105
+ set checkedInput(checked) {
106
+ this._checked.set(checked);
107
+ this.updateNativeElementSync();
108
+ }
109
+ set indeterminateInput(indeterminate) {
118
110
  if (indeterminate) {
119
- this._checked = false;
111
+ this._checked.set(false);
120
112
  }
121
- this._indeterminate = indeterminate;
113
+ this._indeterminate.set(indeterminate);
114
+ this.updateNativeElementSync();
115
+ }
116
+ updateNativeElementSync() {
122
117
  const input = this.elementRef.nativeElement;
123
118
  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();
119
+ input.checked = this.checked();
120
+ input.indeterminate = this.indeterminate();
139
121
  }
140
122
  }
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 = () => { };
123
+ // Private properties
158
124
  listeners = [];
159
125
  styleElement;
160
- wrapper;
161
- checkmarkContainer;
162
- indeterminateContainer;
163
- symbolComponent;
126
+ checkmarkSvg;
127
+ indeterminateBar;
128
+ constructor() {
129
+ effect(() => {
130
+ if (this.checkmarkSvg && this.indeterminateBar) {
131
+ this.updateCheckmarkVisibility();
132
+ }
133
+ });
134
+ }
135
+ // Custom SVG content
136
+ checkSvgContent = `
137
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="10" height="10" fill="white">
138
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M14.238 3.337a.92.92 0 0 1 .025 1.301l-7.7 8a.92.92 0 0 1-1.326 0l-3.5-3.636a.92.92 0 1 1 1.326-1.276L5.9 10.674l7.037-7.312a.92.92 0 0 1 1.301-.025Z" />
139
+ </svg>
140
+ `;
164
141
  ngOnInit() {
165
- this.symbolRegistry.registerSymbols([apCheck]);
166
142
  this.createCheckboxStructure();
167
143
  this.setupEventListeners();
168
144
  }
@@ -171,93 +147,65 @@ class CheckboxDirective {
171
147
  if (this.styleElement && this.styleElement.parentNode) {
172
148
  this.styleElement.parentNode.removeChild(this.styleElement);
173
149
  }
174
- if (this.symbolComponent) {
175
- this.symbolComponent.destroy();
176
- }
177
- if (this.checkmarkContainer && this.checkmarkContainer.parentNode) {
178
- this.checkmarkContainer.parentNode.removeChild(this.checkmarkContainer);
150
+ if (this.checkmarkSvg && this.checkmarkSvg.parentNode) {
151
+ this.checkmarkSvg.parentNode.removeChild(this.checkmarkSvg);
179
152
  }
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);
153
+ if (this.indeterminateBar && this.indeterminateBar.parentNode) {
154
+ this.indeterminateBar.parentNode.removeChild(this.indeterminateBar);
188
155
  }
189
156
  }
190
157
  createCheckboxStructure() {
191
158
  const input = this.elementRef.nativeElement;
192
159
  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
160
+ // Make input's parent use inline-flex for alignment and position relative
195
161
  if (parent) {
196
162
  this.renderer.setStyle(parent, 'display', 'inline-flex');
197
163
  this.renderer.setStyle(parent, 'align-items', 'center');
198
164
  this.renderer.setStyle(parent, 'gap', 'var(--ref-spacing-xxs)');
165
+ this.renderer.setStyle(parent, 'position', 'relative');
199
166
  }
200
167
  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();
168
+ this.createCheckmarkSvg();
213
169
  this.createIndeterminateBar();
214
170
  this.applyCheckboxStyles();
215
171
  this.updateCheckmarkVisibility();
216
172
  }
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);
173
+ createOverlayElement(element) {
174
+ const parent = this.elementRef.nativeElement.parentElement;
175
+ // Apply common positioning styles
176
+ this.renderer.setStyle(element, 'position', 'absolute');
177
+ this.renderer.setStyle(element, 'top', '50%');
178
+ this.renderer.setStyle(element, 'left', '8px'); // Center over 16px checkbox
179
+ this.renderer.setStyle(element, 'transform', 'translate(-50%, -50%)');
180
+ this.renderer.setStyle(element, 'pointer-events', 'none');
181
+ this.renderer.setStyle(element, 'opacity', '0');
182
+ this.renderer.setStyle(element, 'z-index', '1');
183
+ // Append to parent
184
+ this.renderer.appendChild(parent, element);
185
+ }
186
+ createCheckmarkSvg() {
187
+ // Create SVG element from string
188
+ const tempDiv = this.renderer.createElement('div');
189
+ this.renderer.setProperty(tempDiv, 'innerHTML', this.checkSvgContent);
190
+ this.checkmarkSvg = tempDiv.firstElementChild;
191
+ this.createOverlayElement(this.checkmarkSvg);
243
192
  }
244
193
  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);
194
+ // Create the indeterminate bar element
195
+ this.indeterminateBar = this.renderer.createElement('span');
196
+ this.renderer.setStyle(this.indeterminateBar, 'height', '1.5px');
197
+ this.renderer.setStyle(this.indeterminateBar, 'background', 'var(--ref-color-white)');
198
+ this.renderer.setStyle(this.indeterminateBar, 'width', '8px');
199
+ this.createOverlayElement(this.indeterminateBar);
252
200
  }
253
201
  updateCheckmarkVisibility() {
254
- if (this.checkmarkContainer) {
255
- const opacity = this._checked && !this._indeterminate ? '1' : '0';
256
- this.renderer.setStyle(this.checkmarkContainer, 'opacity', opacity);
202
+ if (this.checkmarkSvg) {
203
+ const opacity = this.checked() && !this.indeterminate() ? '1' : '0';
204
+ this.renderer.setStyle(this.checkmarkSvg, 'opacity', opacity);
257
205
  }
258
- if (this.indeterminateContainer) {
259
- const opacity = this._indeterminate && !this._checked ? '1' : '0';
260
- this.renderer.setStyle(this.indeterminateContainer, 'opacity', opacity);
206
+ if (this.indeterminateBar) {
207
+ const opacity = this.indeterminate() && !this.checked() ? '1' : '0';
208
+ this.renderer.setStyle(this.indeterminateBar, 'opacity', opacity);
261
209
  }
262
210
  }
263
211
  setupEventListeners() {
@@ -273,7 +221,7 @@ class CheckboxDirective {
273
221
  const labelClickListener = this.renderer.listen(parent, 'click', (event) => {
274
222
  const target = event.target;
275
223
  // If clicking on a label that targets our input, ensure focus
276
- if (target.tagName === 'LABEL' && target.getAttribute('for') === this.name) {
224
+ if (target.tagName === 'LABEL' && target.getAttribute('for') === input.id) {
277
225
  // Small delay to ensure the label click is processed first
278
226
  setTimeout(() => {
279
227
  input.focus();
@@ -284,16 +232,13 @@ class CheckboxDirective {
284
232
  }
285
233
  }
286
234
  applyCheckboxStyles() {
287
- // Style the input directly without moving DOM elements
288
235
  const styles = `
289
- /* Complete reset and custom styling for our checkbox */
236
+ /* Minimal checkbox styling */
290
237
  input[type="checkbox"].ap-checkbox-styled {
291
- /* Targeted reset of problematic properties */
292
238
  appearance: none !important;
293
239
  -webkit-appearance: none !important;
294
240
  -moz-appearance: none !important;
295
241
 
296
- /* Our custom styling - use !important to override any global styles */
297
242
  display: inline-block !important;
298
243
  width: 16px !important;
299
244
  height: 16px !important;
@@ -304,23 +249,9 @@ class CheckboxDirective {
304
249
  border: 1px solid var(--ref-color-grey-60);
305
250
  border-radius: var(--sys-border-radius-sm);
306
251
  background: var(--ref-color-white);
307
- background-color: var(--ref-color-white);
308
252
  box-sizing: border-box;
309
- position: relative;
310
253
  cursor: pointer;
311
254
  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
255
  }
325
256
 
326
257
  /* Hover state */
@@ -368,7 +299,7 @@ class CheckboxDirective {
368
299
  background: var(--ref-color-grey-20);
369
300
  }
370
301
 
371
- /* Focus state - only show on hover-capable devices like the component */
302
+ /* Focus state */
372
303
  @media (hover: hover) {
373
304
  input[type="checkbox"].ap-checkbox-styled:focus:not(.disabled) {
374
305
  outline: 3px solid var(--ref-color-electric-blue-100) !important;
@@ -376,8 +307,8 @@ class CheckboxDirective {
376
307
  }
377
308
  }
378
309
 
379
- /* Label styling - targets labels that follow our wrapper containing styled checkboxes */
380
- span:has(input[type="checkbox"].ap-checkbox-styled) + label {
310
+ /* Label styling */
311
+ input[type="checkbox"].ap-checkbox-styled + label {
381
312
  display: flex;
382
313
  align-items: center;
383
314
  font-family: var(--comp-forms-label-font-family);
@@ -385,25 +316,17 @@ class CheckboxDirective {
385
316
  font-weight: var(--comp-forms-label-font-weight);
386
317
  line-height: var(--comp-forms-label-line-height);
387
318
  color: var(--comp-forms-label-text-color);
319
+ cursor: pointer;
388
320
  }
389
321
 
390
322
  /* Empty label styling */
391
- span:has(input[type="checkbox"].ap-checkbox-styled) + label:empty {
323
+ input[type="checkbox"].ap-checkbox-styled + label:empty {
392
324
  display: none;
393
325
  }
394
326
 
395
327
  /* Disabled label styling */
396
- span:has(input[type="checkbox"].ap-checkbox-styled:disabled) + label {
328
+ input[type="checkbox"].ap-checkbox-styled:disabled + label {
397
329
  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
330
  cursor: default;
408
331
  }
409
332
  `;
@@ -412,122 +335,41 @@ class CheckboxDirective {
412
335
  this.renderer.appendChild(this.styleElement, this.renderer.createText(styles));
413
336
  this.renderer.appendChild(document.head, this.styleElement);
414
337
  }
415
- onBlur() {
416
- this._onTouched();
417
- }
418
338
  onValueChange() {
419
- if (!this.disabled) {
339
+ if (!this.elementRef.nativeElement.disabled) {
420
340
  const input = this.elementRef.nativeElement;
421
- if (this._indeterminate) {
341
+ if (this.indeterminate()) {
422
342
  // When indeterminate, clicking should go to checked state
423
- this._checked = true;
424
- this._indeterminate = false;
343
+ this._checked.set(true);
344
+ this._indeterminate.set(false);
425
345
  input.checked = true;
426
346
  input.indeterminate = false;
427
347
  }
428
348
  else {
429
349
  // Normal toggle behavior
430
- this._checked = input.checked;
350
+ this._checked.set(input.checked);
431
351
  }
432
- this.updateCheckmarkVisibility();
433
- this.change.emit(this._checked);
434
- this._controlValueAccessorChangeFn(this._checked);
435
352
  }
436
353
  }
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
354
  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 });
355
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "18.2.9", type: CheckboxDirective, isStandalone: true, selector: "input[type=\"checkbox\"][apCheckbox]", inputs: { checkedInput: ["checked", "checkedInput"], indeterminateInput: ["indeterminate", "indeterminateInput", booleanAttribute] }, providers: [], ngImport: i0 });
472
356
  }
473
357
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: CheckboxDirective, decorators: [{
474
358
  type: Directive,
475
359
  args: [{
476
360
  selector: 'input[type="checkbox"][apCheckbox]',
477
361
  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
- },
362
+ providers: [],
497
363
  }]
498
- }], propDecorators: { ariaLabel: [{
499
- type: Input,
500
- args: ['aria-label']
501
- }], ariaLabelledby: [{
364
+ }], ctorParameters: () => [], propDecorators: { checkedInput: [{
502
365
  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: [{
366
+ args: ['checked']
367
+ }], indeterminateInput: [{
513
368
  type: Input,
514
369
  args: [{
515
370
  transform: booleanAttribute,
371
+ alias: 'indeterminate',
516
372
  }]
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
373
  }] } });
532
374
 
533
375
  class DefaultImageDirective {
@@ -908,5 +750,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImpor
908
750
  * Generated bundle index. Do not edit.
909
751
  */
910
752
 
911
- export { AP_CHECKBOX_DIRECTIVE_CONTROL_VALUE_ACCESSOR, AutosizeTextareaDirective, BaseButtonDirective, CheckboxDirective, DefaultImageDirective, EllipsisDirective, EqualValidatorDirective, FrozenGifDirective, GifService, MultiStyleTextDirective };
753
+ export { AutosizeTextareaDirective, BaseButtonDirective, CheckboxDirective, DefaultImageDirective, EllipsisDirective, EqualValidatorDirective, FrozenGifDirective, GifService, MultiStyleTextDirective };
912
754
  //# sourceMappingURL=agorapulse-ui-components-directives.mjs.map