@elderbyte/ngx-starter 21.4.1 → 21.5.0-beta.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.
@@ -23572,63 +23572,52 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
23572
23572
  args: ['elderAutocomplete']
23573
23573
  }] } });
23574
23574
 
23575
+ var InteractionState;
23576
+ (function (InteractionState) {
23577
+ InteractionState["PRISTINE"] = "PRISTINE";
23578
+ InteractionState["INTERACTED"] = "INTERACTED";
23579
+ })(InteractionState || (InteractionState = {}));
23575
23580
  class ElderSelectOnTabDirective {
23576
- /***************************************************************************
23577
- * *
23578
- * Constructor *
23579
- * *
23580
- **************************************************************************/
23581
- constructor(autoTrigger, elderSelectBase) {
23582
- this.autoTrigger = autoTrigger;
23583
- this.elderSelectBase = elderSelectBase;
23581
+ constructor() {
23584
23582
  /***************************************************************************
23585
23583
  * *
23586
- * Fields *
23584
+ * Fields *
23587
23585
  * *
23588
23586
  **************************************************************************/
23589
23587
  this.logger = LoggerFactory.getLogger(this.constructor.name);
23588
+ this.autoTrigger = inject(MatAutocompleteTrigger);
23589
+ this.elderSelectBase = inject(ELDER_SELECT_BASE, {
23590
+ skipSelf: true,
23591
+ });
23592
+ this.controlValueAccessor = this.elderSelectBase;
23590
23593
  this.destroy$ = new Subject$1();
23591
- /**
23592
- * Whether the autocomplete panel was open before the event
23593
- */
23594
- this.panelOpen = false;
23595
- /**
23596
- * Whether the user selected an option.
23597
- * (We want to ignore selections if there is already a selection present and the user tabs away)
23598
- */
23599
- this.userInput = false;
23600
- this.controlValueAccessor = elderSelectBase;
23594
+ /***************************************************************************
23595
+ * *
23596
+ * State *
23597
+ * *
23598
+ **************************************************************************/
23599
+ this.isPanelOpen = false;
23600
+ this.userInteractionState = InteractionState.PRISTINE;
23601
+ this.valueSnapshot = null;
23601
23602
  }
23602
23603
  /***************************************************************************
23603
23604
  * *
23604
23605
  * Event Listener *
23605
23606
  * *
23606
23607
  **************************************************************************/
23607
- onOptionSelect() {
23608
- this.userInput = true;
23608
+ handleVerticalArrowKeyPress() {
23609
+ this.userInteractionState = InteractionState.INTERACTED;
23609
23610
  }
23610
- onBlur() {
23611
- if (!this.panelOpen) {
23611
+ handleTabKeyPress() {
23612
+ if (!this.isPanelOpen || this.shouldSkipTabSelection()) {
23612
23613
  return;
23613
23614
  }
23614
- if (!this.userInput) {
23615
- // The user did not select anything in the auto-complete
23616
- if (this.controlValueAccessor.value) {
23617
- this.logger.warn('Discarding TAB since the user did probably not intend to change the value! userInput:', this.userInput);
23618
- return;
23619
- }
23620
- if (!this.elderSelectBase.required) {
23621
- // The user did not select any option and no current value is present.
23622
- // Since the input is NOT marked as required, we assume the user did not want to select a value
23623
- return;
23624
- }
23625
- }
23626
23615
  const activeOption = this.autoTrigger.activeOption;
23627
23616
  if (activeOption) {
23628
23617
  const entity = activeOption.value;
23629
23618
  this.writeEntity(entity);
23630
23619
  }
23631
- this.reset();
23620
+ this.userInteractionState = InteractionState.PRISTINE;
23632
23621
  }
23633
23622
  /***************************************************************************
23634
23623
  * *
@@ -23636,15 +23625,25 @@ class ElderSelectOnTabDirective {
23636
23625
  * *
23637
23626
  **************************************************************************/
23638
23627
  ngAfterViewInit() {
23639
- const autocomplete = this.autoTrigger.autocomplete;
23640
- merge(autocomplete.opened.pipe(map(() => true)), autocomplete.closed.pipe(map(() => false)))
23641
- .pipe(takeUntil(this.destroy$), delay(0))
23642
- .subscribe((value) => (this.panelOpen = value));
23643
- this.autoTrigger.optionSelections
23644
- .pipe(
23645
- // TODO https://github.com/angular/components/pull/14813
23646
- takeUntil(this.destroy$), tap((opt) => this.logger.debug('[optionSelections] CHANGED ', opt)), map((opt) => opt.isUserInput))
23647
- .subscribe((isUserInput) => (this.userInput = isUserInput));
23628
+ const panelOpenAsync$ = this.buildPanelOpenAsyncObservable();
23629
+ panelOpenAsync$.subscribe((isOpen) => {
23630
+ this.isPanelOpen = isOpen;
23631
+ if (isOpen) {
23632
+ // Snapshot the value at panel-open time so Tab can compare against it.
23633
+ this.valueSnapshot = this.controlValueAccessor.value;
23634
+ // Reset userInput so any selection from a previous panel session is not carried over.
23635
+ this.userInteractionState = InteractionState.PRISTINE;
23636
+ }
23637
+ });
23638
+ const optionSelectionFromUserInput$ = this.buildOptionSelectionFromUserInputObservable();
23639
+ optionSelectionFromUserInput$.subscribe((isUserInput) => {
23640
+ if (isUserInput) {
23641
+ this.userInteractionState = InteractionState.INTERACTED;
23642
+ }
23643
+ else {
23644
+ this.userInteractionState = InteractionState.PRISTINE;
23645
+ }
23646
+ });
23648
23647
  }
23649
23648
  ngOnDestroy() {
23650
23649
  this.destroy$.next();
@@ -23655,8 +23654,35 @@ class ElderSelectOnTabDirective {
23655
23654
  * Private methods *
23656
23655
  * *
23657
23656
  **************************************************************************/
23658
- reset() {
23659
- this.userInput = false;
23657
+ isValueSnapShotNotEmpty() {
23658
+ return this.valueSnapshot !== null && this.valueSnapshot !== undefined;
23659
+ }
23660
+ buildPanelOpenAsyncObservable() {
23661
+ const autocomplete = this.autoTrigger.autocomplete;
23662
+ const panelOpen$ = merge(autocomplete.opened.pipe(map(() => true)), autocomplete.closed.pipe(map(() => false)));
23663
+ const panelOpenAsync$ = panelOpen$.pipe(takeUntil(this.destroy$), delay(0));
23664
+ return panelOpenAsync$;
23665
+ }
23666
+ buildOptionSelectionFromUserInputObservable() {
23667
+ return this.autoTrigger.optionSelections.pipe(takeUntil(this.destroy$), map((opt) => opt.isUserInput));
23668
+ }
23669
+ shouldSkipTabSelection() {
23670
+ if (this.userInteractionState === InteractionState.INTERACTED) {
23671
+ return false;
23672
+ }
23673
+ const isOptionalField = !this.elderSelectBase.required;
23674
+ const hasValueToPreserve = this.isValueSnapShotNotEmpty();
23675
+ if (isOptionalField) {
23676
+ // Since the input is NOT marked as required, we assume the user did not want to select a value
23677
+ this.logger.warn('Skipping TAB selection since the input is NOT marked as required');
23678
+ return true;
23679
+ }
23680
+ if (hasValueToPreserve) {
23681
+ // User has already selected a value
23682
+ this.logger.warn('Discarding TAB since the user did probably not intend to change the value! userInput:', this.userInteractionState);
23683
+ return true;
23684
+ }
23685
+ return false;
23660
23686
  }
23661
23687
  writeEntity(entity) {
23662
23688
  const value = this.entityToValue(entity);
@@ -23676,26 +23702,21 @@ class ElderSelectOnTabDirective {
23676
23702
  return entity;
23677
23703
  }
23678
23704
  }
23679
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ElderSelectOnTabDirective, deps: [{ token: i1$9.MatAutocompleteTrigger }, { token: ELDER_SELECT_BASE, skipSelf: true }], target: i0.ɵɵFactoryTarget.Directive }); }
23680
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.2", type: ElderSelectOnTabDirective, isStandalone: true, selector: "[elderSelectOnTab]", host: { listeners: { "keydown.arrowup": "onOptionSelect()", "keydown.arrowdown": "onOptionSelect()", "keydown.tab": "onBlur()" } }, ngImport: i0 }); }
23705
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ElderSelectOnTabDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
23706
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.2", type: ElderSelectOnTabDirective, isStandalone: true, selector: "[elderSelectOnTab]", host: { listeners: { "keydown.arrowup": "handleVerticalArrowKeyPress()", "keydown.arrowdown": "handleVerticalArrowKeyPress()", "keydown.tab": "handleTabKeyPress()" } }, ngImport: i0 }); }
23681
23707
  }
23682
23708
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ElderSelectOnTabDirective, decorators: [{
23683
23709
  type: Directive,
23684
23710
  args: [{
23685
23711
  selector: '[elderSelectOnTab]',
23686
23712
  }]
23687
- }], ctorParameters: () => [{ type: i1$9.MatAutocompleteTrigger }, { type: ElderSelectBase, decorators: [{
23688
- type: SkipSelf
23689
- }, {
23690
- type: Inject,
23691
- args: [ELDER_SELECT_BASE]
23692
- }] }], propDecorators: { onOptionSelect: [{
23713
+ }], propDecorators: { handleVerticalArrowKeyPress: [{
23693
23714
  type: HostListener,
23694
23715
  args: ['keydown.arrowup']
23695
23716
  }, {
23696
23717
  type: HostListener,
23697
23718
  args: ['keydown.arrowdown']
23698
- }], onBlur: [{
23719
+ }], handleTabKeyPress: [{
23699
23720
  type: HostListener,
23700
23721
  args: ['keydown.tab']
23701
23722
  }] } });
@@ -37971,8 +37992,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
37971
37992
  args: ['class']
37972
37993
  }] } });
37973
37994
 
37974
- const DEFAULT_MIN_WIDTH = 5;
37975
- const DEFAULT_MAX_WIDTH = 95;
37995
+ const DEFAULT_MIN_WIDTH = '5%';
37996
+ const DEFAULT_MAX_WIDTH = '95%';
37976
37997
  const DEFAULT_HANDLE_POSITION = 'end';
37977
37998
 
37978
37999
  class ElderResizeBehaviorDirective {
@@ -38001,7 +38022,7 @@ class ElderResizeBehaviorDirective {
38001
38022
  this.isInComputedWidthMode = signal(false, ...(ngDevMode ? [{ debugName: "isInComputedWidthMode" }] : []));
38002
38023
  this.widthInPercent = signal(null, ...(ngDevMode ? [{ debugName: "widthInPercent" }] : []));
38003
38024
  this._isDragging = signal(false, ...(ngDevMode ? [{ debugName: "_isDragging" }] : []));
38004
- this.containerWidth = 0;
38025
+ this.containerWidth = signal(0, ...(ngDevMode ? [{ debugName: "containerWidth" }] : []));
38005
38026
  this.dragStartMousePosX = 0;
38006
38027
  this.dragStartPaneWidthInPercent = 0;
38007
38028
  /***************************************************************************
@@ -38015,7 +38036,12 @@ class ElderResizeBehaviorDirective {
38015
38036
  if (width === null) {
38016
38037
  return null;
38017
38038
  }
38018
- return Math.max(this.minWidth(), Math.min(this.maxWidth(), width));
38039
+ const containerW = this.containerWidth();
38040
+ const minParsed = this.parseWidthConstraint(this.minWidth());
38041
+ const maxParsed = this.parseWidthConstraint(this.maxWidth());
38042
+ const minPercent = this.toPercent(minParsed, containerW);
38043
+ const maxPercent = this.toPercent(maxParsed, containerW);
38044
+ return Math.max(minPercent, Math.min(maxPercent, width));
38019
38045
  }, ...(ngDevMode ? [{ debugName: "constrainedWidth" }] : []));
38020
38046
  /***************************************************************************
38021
38047
  * *
@@ -38067,6 +38093,23 @@ class ElderResizeBehaviorDirective {
38067
38093
  * Private methods *
38068
38094
  * *
38069
38095
  **************************************************************************/
38096
+ parseWidthConstraint(input) {
38097
+ const trimmed = String(input).trim().toLowerCase();
38098
+ const floatValue = parseFloat(trimmed);
38099
+ const isValidEnding = trimmed.endsWith('px') || trimmed.endsWith('%');
38100
+ if (Number.isNaN(floatValue) || !isValidEnding) {
38101
+ this.logger.error(`Invalid width constraint: ${input}`);
38102
+ return { value: 0, unit: 'percent' };
38103
+ }
38104
+ return { value: floatValue, unit: trimmed.endsWith('px') ? 'px' : 'percent' };
38105
+ }
38106
+ toPercent(parsed, containerWidth) {
38107
+ if (parsed.unit === 'percent')
38108
+ return parsed.value;
38109
+ if (containerWidth <= 0)
38110
+ return parsed.value <= 0 ? 0 : 100;
38111
+ return (parsed.value / containerWidth) * 100;
38112
+ }
38070
38113
  removeExternalWidthStyles() {
38071
38114
  const stylesToRemove = [
38072
38115
  'flex',
@@ -38106,24 +38149,23 @@ class ElderResizeBehaviorDirective {
38106
38149
  }
38107
38150
  setWidthInPercentFromElementWidth() {
38108
38151
  this.updateContainerWidth();
38109
- if (this.containerWidth === 0) {
38152
+ if (this.containerWidth() === 0) {
38110
38153
  this.logger.error('Container width is 0. Can not calculate percent.');
38111
38154
  return;
38112
38155
  }
38113
38156
  // Calculate the current element width as a percentage of container width
38114
38157
  const paneWidth = this.elementRef.nativeElement.offsetWidth;
38115
- const percentage = (paneWidth / this.containerWidth) * 100;
38158
+ const percentage = (paneWidth / this.containerWidth()) * 100;
38116
38159
  this.widthInPercent.set(percentage);
38117
38160
  }
38118
38161
  updateContainerWidth() {
38119
38162
  const containerElement = this.elementRef.nativeElement.parentElement;
38120
38163
  if (!containerElement) {
38121
38164
  this.logger.error('Container element not found for percentage calculation');
38122
- return null;
38165
+ return;
38123
38166
  }
38124
38167
  // Use clientWidth which excludes borders and scrollbars, then subtract padding
38125
- this.containerWidth = containerElement.clientWidth - this.getContainerPadding();
38126
- return this.containerWidth;
38168
+ this.containerWidth.set(containerElement.clientWidth - this.getContainerPadding());
38127
38169
  }
38128
38170
  getContainerPadding() {
38129
38171
  const containerElement = this.elementRef.nativeElement.parentElement;
@@ -38134,14 +38176,14 @@ class ElderResizeBehaviorDirective {
38134
38176
  parseFloat(window.getComputedStyle(containerElement).paddingRight));
38135
38177
  }
38136
38178
  calcWidthInPercent(event, position) {
38137
- if (this.containerWidth === 0 ||
38179
+ if (this.containerWidth() === 0 ||
38138
38180
  this.dragStartPaneWidthInPercent === null ||
38139
38181
  isNaN(this.dragStartPaneWidthInPercent)) {
38140
38182
  return undefined;
38141
38183
  }
38142
38184
  const deltaX = event.clientX - this.dragStartMousePosX;
38143
38185
  // Convert pixel delta to percentage delta
38144
- let delta = (deltaX / this.containerWidth) * 100;
38186
+ let delta = (deltaX / this.containerWidth()) * 100;
38145
38187
  // Invert delta for left-side handles
38146
38188
  if (position === 'start') {
38147
38189
  delta = -delta;