@ship-ui/core 0.13.16 → 0.13.17

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.
@@ -3015,6 +3015,7 @@ class ShipSelectComponent {
3015
3015
  this.value = input();
3016
3016
  this.label = input();
3017
3017
  this.asFreeText = input(false);
3018
+ this.validateFreeText = input();
3018
3019
  this.placeholder = input();
3019
3020
  this.readonly = model(false);
3020
3021
  this.disabled = model(false);
@@ -3026,11 +3027,21 @@ class ShipSelectComponent {
3026
3027
  this.optionTemplate = input(null);
3027
3028
  this.selectedOptionTemplate = input(null);
3028
3029
  this.placeholderTemplate = input(null);
3030
+ this.freeTextOptionTemplate = input(null);
3029
3031
  this.isOpen = model(false);
3030
3032
  this.isLoading = model(false);
3031
3033
  this.options = model([]);
3032
3034
  this.selectedOptions = model([]);
3033
3035
  this.cleared = output();
3036
+ this.computedFreeTextOption = computed(() => {
3037
+ const inputValue = this.inputValue();
3038
+ const valueKey = this.value();
3039
+ const newOption = valueKey ? {} : inputValue;
3040
+ if (valueKey && typeof newOption === 'object') {
3041
+ newOption[valueKey] = inputValue;
3042
+ }
3043
+ return newOption;
3044
+ });
3034
3045
  this.#previousSelectedOptions = signal(null);
3035
3046
  this.inlineTemplate = contentChild(TemplateRef);
3036
3047
  this.optionsWrapRef = viewChild.required('optionsWrap');
@@ -3166,6 +3177,8 @@ class ShipSelectComponent {
3166
3177
  this.openAbortController = new AbortController();
3167
3178
  }
3168
3179
  const input = this.inputRefEl();
3180
+ const asFreeText = this.asFreeText();
3181
+ const baseIndex = asFreeText ? -1 : 0;
3169
3182
  if (!input)
3170
3183
  return;
3171
3184
  input.setAttribute('aria-expanded', this.isOpen().toString());
@@ -3181,12 +3194,12 @@ class ShipSelectComponent {
3181
3194
  if (e.key === 'ArrowDown') {
3182
3195
  e.preventDefault();
3183
3196
  const newIndex = this.focusedOptionIndex() + 1;
3184
- this.focusedOptionIndex.set(newIndex > this.filteredOptions().length - 1 ? 0 : newIndex);
3197
+ this.focusedOptionIndex.set(newIndex > this.filteredOptions().length - 1 ? baseIndex : newIndex);
3185
3198
  }
3186
3199
  if (e.key === 'ArrowUp') {
3187
3200
  e.preventDefault();
3188
3201
  const newIndex = this.focusedOptionIndex() - 1;
3189
- this.focusedOptionIndex.set(newIndex < 0 ? this.filteredOptions().length - 1 : newIndex);
3202
+ this.focusedOptionIndex.set(newIndex < baseIndex ? this.filteredOptions().length - 1 : newIndex);
3190
3203
  }
3191
3204
  }, {
3192
3205
  signal: this.openAbortController?.signal,
@@ -3355,6 +3368,12 @@ class ShipSelectComponent {
3355
3368
  this.inputValue.set(newInputValue);
3356
3369
  this.updateInputElValue();
3357
3370
  }
3371
+ getValue(option) {
3372
+ const valueKey = this.value();
3373
+ if (!valueKey)
3374
+ return option;
3375
+ return this.#getProperty(option, valueKey);
3376
+ }
3358
3377
  getLabel(option) {
3359
3378
  const label = this.label();
3360
3379
  if (!label)
@@ -3368,8 +3387,24 @@ class ShipSelectComponent {
3368
3387
  return label.replaceAll(' ', '-');
3369
3388
  }
3370
3389
  toggleOptionByIndex(optionIndex, event) {
3371
- const option = this.filteredOptions()[optionIndex];
3372
- if ((this.asFreeText() && optionIndex === -1) || !option) {
3390
+ let option = this.filteredOptions()[optionIndex];
3391
+ if (this.asFreeText() && optionIndex === -1) {
3392
+ const newOption = this.computedFreeTextOption();
3393
+ const newOptionValue = this.getValue(newOption);
3394
+ const validateFreeTextFunc = this.validateFreeText() ?? ((val) => true);
3395
+ const isValid = validateFreeTextFunc(newOptionValue);
3396
+ if (!isValid)
3397
+ return;
3398
+ this.options.update((options) => {
3399
+ const index = options.findIndex((option) => this.getValue(option) === newOptionValue);
3400
+ if (index > -1)
3401
+ return options;
3402
+ return [newOption, ...options];
3403
+ });
3404
+ optionIndex = 0;
3405
+ option = newOption;
3406
+ }
3407
+ else if (!option) {
3373
3408
  this.close();
3374
3409
  return;
3375
3410
  }
@@ -3535,9 +3570,10 @@ class ShipSelectComponent {
3535
3570
  }
3536
3571
  }
3537
3572
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: ShipSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3538
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: ShipSelectComponent, isStandalone: true, selector: "sh-select", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, asFreeText: { classPropertyName: "asFreeText", publicName: "asFreeText", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, lazySearch: { classPropertyName: "lazySearch", publicName: "lazySearch", isSignal: true, isRequired: false, transformFunction: null }, inlineSearch: { classPropertyName: "inlineSearch", publicName: "inlineSearch", isSignal: true, isRequired: false, transformFunction: null }, asText: { classPropertyName: "asText", publicName: "asText", isSignal: true, isRequired: false, transformFunction: null }, isClearable: { classPropertyName: "isClearable", publicName: "isClearable", isSignal: true, isRequired: false, transformFunction: null }, selectMultiple: { classPropertyName: "selectMultiple", publicName: "selectMultiple", isSignal: true, isRequired: false, transformFunction: null }, optionTemplate: { classPropertyName: "optionTemplate", publicName: "optionTemplate", isSignal: true, isRequired: false, transformFunction: null }, selectedOptionTemplate: { classPropertyName: "selectedOptionTemplate", publicName: "selectedOptionTemplate", isSignal: true, isRequired: false, transformFunction: null }, placeholderTemplate: { classPropertyName: "placeholderTemplate", publicName: "placeholderTemplate", isSignal: true, isRequired: false, transformFunction: null }, isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, selectedOptions: { classPropertyName: "selectedOptions", publicName: "selectedOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { readonly: "readonlyChange", disabled: "disabledChange", isOpen: "isOpenChange", isLoading: "isLoadingChange", options: "optionsChange", selectedOptions: "selectedOptionsChange", cleared: "cleared" }, host: { properties: { "class.multiple": "selectMultiple()" } }, queries: [{ propertyName: "inlineTemplate", first: true, predicate: TemplateRef, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "optionsWrapRef", first: true, predicate: ["optionsWrap"], descendants: true, isSignal: true }], ngImport: i0, template: `
3573
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: ShipSelectComponent, isStandalone: true, selector: "sh-select", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, asFreeText: { classPropertyName: "asFreeText", publicName: "asFreeText", isSignal: true, isRequired: false, transformFunction: null }, validateFreeText: { classPropertyName: "validateFreeText", publicName: "validateFreeText", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, lazySearch: { classPropertyName: "lazySearch", publicName: "lazySearch", isSignal: true, isRequired: false, transformFunction: null }, inlineSearch: { classPropertyName: "inlineSearch", publicName: "inlineSearch", isSignal: true, isRequired: false, transformFunction: null }, asText: { classPropertyName: "asText", publicName: "asText", isSignal: true, isRequired: false, transformFunction: null }, isClearable: { classPropertyName: "isClearable", publicName: "isClearable", isSignal: true, isRequired: false, transformFunction: null }, selectMultiple: { classPropertyName: "selectMultiple", publicName: "selectMultiple", isSignal: true, isRequired: false, transformFunction: null }, optionTemplate: { classPropertyName: "optionTemplate", publicName: "optionTemplate", isSignal: true, isRequired: false, transformFunction: null }, selectedOptionTemplate: { classPropertyName: "selectedOptionTemplate", publicName: "selectedOptionTemplate", isSignal: true, isRequired: false, transformFunction: null }, placeholderTemplate: { classPropertyName: "placeholderTemplate", publicName: "placeholderTemplate", isSignal: true, isRequired: false, transformFunction: null }, freeTextOptionTemplate: { classPropertyName: "freeTextOptionTemplate", publicName: "freeTextOptionTemplate", isSignal: true, isRequired: false, transformFunction: null }, isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, selectedOptions: { classPropertyName: "selectedOptions", publicName: "selectedOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { readonly: "readonlyChange", disabled: "disabledChange", isOpen: "isOpenChange", isLoading: "isLoadingChange", options: "optionsChange", selectedOptions: "selectedOptionsChange", cleared: "cleared" }, host: { properties: { "class.multiple": "selectMultiple()" } }, queries: [{ propertyName: "inlineTemplate", first: true, predicate: TemplateRef, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "optionsWrapRef", first: true, predicate: ["optionsWrap"], descendants: true, isSignal: true }], ngImport: i0, template: `
3539
3574
  @let _placeholderTemplate = placeholderTemplate();
3540
3575
  @let _optionTemplate = optionTemplate();
3576
+ @let _freeTextOptionTemplate = freeTextOptionTemplate();
3541
3577
  @let _selectedOptionTemplate = selectedOptionTemplate();
3542
3578
  @let _inlineTemplate = inlineTemplate();
3543
3579
  @let _selectedOptions = selectedOptions();
@@ -3629,6 +3665,31 @@ class ShipSelectComponent {
3629
3665
  </sh-form-field>
3630
3666
 
3631
3667
  <div class="ship-options" #optionsWrap id="optionsWrapId" role="listbox">
3668
+ @if (asFreeText()) {
3669
+ @let freeTextOption = computedFreeTextOption();
3670
+ @let freeTextOptionValue = getValue(freeTextOption);
3671
+
3672
+ @if ($any(freeTextOptionValue).length > 0) {
3673
+ <li
3674
+ (click)="toggleOptionByIndex(-1)"
3675
+ class="option"
3676
+ [id]="this.getLabelAsSlug(freeTextOption)"
3677
+ [attr.aria-selected]="isSelected(-1)"
3678
+ [class.selected]="isSelected(-1)"
3679
+ [class.focused]="-1 === focusedOptionIndex()">
3680
+ @if (_freeTextOptionTemplate) {
3681
+ <ng-container
3682
+ *ngTemplateOutlet="_freeTextOptionTemplate; context: { $implicit: { option: freeTextOption } }" />
3683
+ } @else if (_listOptionTemplate) {
3684
+ <ng-container
3685
+ *ngTemplateOutlet="_listOptionTemplate; context: { $implicit: { option: freeTextOption } }" />
3686
+ } @else {
3687
+ {{ freeTextOptionValue }}
3688
+ }
3689
+ </li>
3690
+ }
3691
+ }
3692
+
3632
3693
  @for (option of filteredOptions(); track $index) {
3633
3694
  <li
3634
3695
  (click)="toggleOptionByIndex($index)"
@@ -3668,6 +3729,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImpor
3668
3729
  template: `
3669
3730
  @let _placeholderTemplate = placeholderTemplate();
3670
3731
  @let _optionTemplate = optionTemplate();
3732
+ @let _freeTextOptionTemplate = freeTextOptionTemplate();
3671
3733
  @let _selectedOptionTemplate = selectedOptionTemplate();
3672
3734
  @let _inlineTemplate = inlineTemplate();
3673
3735
  @let _selectedOptions = selectedOptions();
@@ -3759,6 +3821,31 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImpor
3759
3821
  </sh-form-field>
3760
3822
 
3761
3823
  <div class="ship-options" #optionsWrap id="optionsWrapId" role="listbox">
3824
+ @if (asFreeText()) {
3825
+ @let freeTextOption = computedFreeTextOption();
3826
+ @let freeTextOptionValue = getValue(freeTextOption);
3827
+
3828
+ @if ($any(freeTextOptionValue).length > 0) {
3829
+ <li
3830
+ (click)="toggleOptionByIndex(-1)"
3831
+ class="option"
3832
+ [id]="this.getLabelAsSlug(freeTextOption)"
3833
+ [attr.aria-selected]="isSelected(-1)"
3834
+ [class.selected]="isSelected(-1)"
3835
+ [class.focused]="-1 === focusedOptionIndex()">
3836
+ @if (_freeTextOptionTemplate) {
3837
+ <ng-container
3838
+ *ngTemplateOutlet="_freeTextOptionTemplate; context: { $implicit: { option: freeTextOption } }" />
3839
+ } @else if (_listOptionTemplate) {
3840
+ <ng-container
3841
+ *ngTemplateOutlet="_listOptionTemplate; context: { $implicit: { option: freeTextOption } }" />
3842
+ } @else {
3843
+ {{ freeTextOptionValue }}
3844
+ }
3845
+ </li>
3846
+ }
3847
+ }
3848
+
3762
3849
  @for (option of filteredOptions(); track $index) {
3763
3850
  <li
3764
3851
  (click)="toggleOptionByIndex($index)"