@ship-ui/core 0.13.16 → 0.13.18

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,22 @@ 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.onAddNewFreeTextOption = output();
3037
+ this.computedFreeTextOption = computed(() => {
3038
+ const inputValue = this.inputValue();
3039
+ const valueKey = this.value();
3040
+ const newOption = valueKey ? {} : inputValue;
3041
+ if (valueKey && typeof newOption === 'object') {
3042
+ newOption[valueKey] = inputValue;
3043
+ }
3044
+ return newOption;
3045
+ });
3034
3046
  this.#previousSelectedOptions = signal(null);
3035
3047
  this.inlineTemplate = contentChild(TemplateRef);
3036
3048
  this.optionsWrapRef = viewChild.required('optionsWrap');
@@ -3166,6 +3178,8 @@ class ShipSelectComponent {
3166
3178
  this.openAbortController = new AbortController();
3167
3179
  }
3168
3180
  const input = this.inputRefEl();
3181
+ const asFreeText = this.asFreeText();
3182
+ const baseIndex = asFreeText ? -1 : 0;
3169
3183
  if (!input)
3170
3184
  return;
3171
3185
  input.setAttribute('aria-expanded', this.isOpen().toString());
@@ -3181,12 +3195,12 @@ class ShipSelectComponent {
3181
3195
  if (e.key === 'ArrowDown') {
3182
3196
  e.preventDefault();
3183
3197
  const newIndex = this.focusedOptionIndex() + 1;
3184
- this.focusedOptionIndex.set(newIndex > this.filteredOptions().length - 1 ? 0 : newIndex);
3198
+ this.focusedOptionIndex.set(newIndex > this.filteredOptions().length - 1 ? baseIndex : newIndex);
3185
3199
  }
3186
3200
  if (e.key === 'ArrowUp') {
3187
3201
  e.preventDefault();
3188
3202
  const newIndex = this.focusedOptionIndex() - 1;
3189
- this.focusedOptionIndex.set(newIndex < 0 ? this.filteredOptions().length - 1 : newIndex);
3203
+ this.focusedOptionIndex.set(newIndex < baseIndex ? this.filteredOptions().length - 1 : newIndex);
3190
3204
  }
3191
3205
  }, {
3192
3206
  signal: this.openAbortController?.signal,
@@ -3355,6 +3369,12 @@ class ShipSelectComponent {
3355
3369
  this.inputValue.set(newInputValue);
3356
3370
  this.updateInputElValue();
3357
3371
  }
3372
+ getValue(option) {
3373
+ const valueKey = this.value();
3374
+ if (!valueKey)
3375
+ return option;
3376
+ return this.#getProperty(option, valueKey);
3377
+ }
3358
3378
  getLabel(option) {
3359
3379
  const label = this.label();
3360
3380
  if (!label)
@@ -3368,8 +3388,25 @@ class ShipSelectComponent {
3368
3388
  return label.replaceAll(' ', '-');
3369
3389
  }
3370
3390
  toggleOptionByIndex(optionIndex, event) {
3371
- const option = this.filteredOptions()[optionIndex];
3372
- if ((this.asFreeText() && optionIndex === -1) || !option) {
3391
+ let option = this.filteredOptions()[optionIndex];
3392
+ if (this.asFreeText() && optionIndex === -1) {
3393
+ const newOption = this.computedFreeTextOption();
3394
+ const newOptionValue = this.getValue(newOption);
3395
+ const validateFreeTextFunc = this.validateFreeText() ?? ((val) => true);
3396
+ const isValid = validateFreeTextFunc(newOptionValue);
3397
+ if (!isValid)
3398
+ return;
3399
+ this.options.update((options) => {
3400
+ const index = options.findIndex((option) => this.getValue(option) === newOptionValue);
3401
+ if (index > -1)
3402
+ return options;
3403
+ this.onAddNewFreeTextOption.emit(newOptionValue);
3404
+ return [newOption, ...options];
3405
+ });
3406
+ optionIndex = 0;
3407
+ option = newOption;
3408
+ }
3409
+ else if (!option) {
3373
3410
  this.close();
3374
3411
  return;
3375
3412
  }
@@ -3535,9 +3572,10 @@ class ShipSelectComponent {
3535
3572
  }
3536
3573
  }
3537
3574
  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: `
3575
+ 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", onAddNewFreeTextOption: "onAddNewFreeTextOption" }, 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
3576
  @let _placeholderTemplate = placeholderTemplate();
3540
3577
  @let _optionTemplate = optionTemplate();
3578
+ @let _freeTextOptionTemplate = freeTextOptionTemplate();
3541
3579
  @let _selectedOptionTemplate = selectedOptionTemplate();
3542
3580
  @let _inlineTemplate = inlineTemplate();
3543
3581
  @let _selectedOptions = selectedOptions();
@@ -3629,6 +3667,31 @@ class ShipSelectComponent {
3629
3667
  </sh-form-field>
3630
3668
 
3631
3669
  <div class="ship-options" #optionsWrap id="optionsWrapId" role="listbox">
3670
+ @if (asFreeText()) {
3671
+ @let freeTextOption = computedFreeTextOption();
3672
+ @let freeTextOptionValue = getValue(freeTextOption);
3673
+
3674
+ @if ($any(freeTextOptionValue).length > 0) {
3675
+ <li
3676
+ (click)="toggleOptionByIndex(-1)"
3677
+ class="option"
3678
+ [id]="this.getLabelAsSlug(freeTextOption)"
3679
+ [attr.aria-selected]="isSelected(-1)"
3680
+ [class.selected]="isSelected(-1)"
3681
+ [class.focused]="-1 === focusedOptionIndex()">
3682
+ @if (_freeTextOptionTemplate) {
3683
+ <ng-container
3684
+ *ngTemplateOutlet="_freeTextOptionTemplate; context: { $implicit: { option: freeTextOption } }" />
3685
+ } @else if (_listOptionTemplate) {
3686
+ <ng-container
3687
+ *ngTemplateOutlet="_listOptionTemplate; context: { $implicit: { option: freeTextOption } }" />
3688
+ } @else {
3689
+ {{ freeTextOptionValue }}
3690
+ }
3691
+ </li>
3692
+ }
3693
+ }
3694
+
3632
3695
  @for (option of filteredOptions(); track $index) {
3633
3696
  <li
3634
3697
  (click)="toggleOptionByIndex($index)"
@@ -3668,6 +3731,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImpor
3668
3731
  template: `
3669
3732
  @let _placeholderTemplate = placeholderTemplate();
3670
3733
  @let _optionTemplate = optionTemplate();
3734
+ @let _freeTextOptionTemplate = freeTextOptionTemplate();
3671
3735
  @let _selectedOptionTemplate = selectedOptionTemplate();
3672
3736
  @let _inlineTemplate = inlineTemplate();
3673
3737
  @let _selectedOptions = selectedOptions();
@@ -3759,6 +3823,31 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImpor
3759
3823
  </sh-form-field>
3760
3824
 
3761
3825
  <div class="ship-options" #optionsWrap id="optionsWrapId" role="listbox">
3826
+ @if (asFreeText()) {
3827
+ @let freeTextOption = computedFreeTextOption();
3828
+ @let freeTextOptionValue = getValue(freeTextOption);
3829
+
3830
+ @if ($any(freeTextOptionValue).length > 0) {
3831
+ <li
3832
+ (click)="toggleOptionByIndex(-1)"
3833
+ class="option"
3834
+ [id]="this.getLabelAsSlug(freeTextOption)"
3835
+ [attr.aria-selected]="isSelected(-1)"
3836
+ [class.selected]="isSelected(-1)"
3837
+ [class.focused]="-1 === focusedOptionIndex()">
3838
+ @if (_freeTextOptionTemplate) {
3839
+ <ng-container
3840
+ *ngTemplateOutlet="_freeTextOptionTemplate; context: { $implicit: { option: freeTextOption } }" />
3841
+ } @else if (_listOptionTemplate) {
3842
+ <ng-container
3843
+ *ngTemplateOutlet="_listOptionTemplate; context: { $implicit: { option: freeTextOption } }" />
3844
+ } @else {
3845
+ {{ freeTextOptionValue }}
3846
+ }
3847
+ </li>
3848
+ }
3849
+ }
3850
+
3762
3851
  @for (option of filteredOptions(); track $index) {
3763
3852
  <li
3764
3853
  (click)="toggleOptionByIndex($index)"