Browse Source

refactor(admin-ui): Rework internals of option-value-input.component

Michael Bromley 3 years ago
parent
commit
afa9340409

+ 2 - 2
packages/admin-ui/src/lib/catalog/src/components/option-value-input/option-value-input.component.html

@@ -1,7 +1,7 @@
 <div class="input-wrapper" [class.focus]="isFocussed" (click)="textArea.focus()">
-    <div class="chips" *ngIf="0 < options.length">
+    <div class="chips" *ngIf="0 < optionValues.length">
         <vdr-chip
-            *ngFor="let option of options; last as isLast"
+            *ngFor="let option of optionValues; last as isLast"
             [icon]="option.locked ? 'lock' : 'times'"
             [class.selected]="isLast && lastSelected"
             [class.locked]="option.locked"

+ 41 - 11
packages/admin-ui/src/lib/catalog/src/components/option-value-input/option-value-input.component.ts

@@ -3,9 +3,14 @@ import {
     ChangeDetectorRef,
     Component,
     ElementRef,
+    EventEmitter,
     forwardRef,
     Input,
+    OnChanges,
+    OnInit,
+    Output,
     Provider,
+    SimpleChanges,
     ViewChild,
 } from '@angular/core';
 import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
@@ -17,6 +22,12 @@ export const OPTION_VALUE_INPUT_VALUE_ACCESSOR: Provider = {
     multi: true,
 };
 
+interface Option {
+    id?: string;
+    name: string;
+    locked: boolean;
+}
+
 @Component({
     selector: 'vdr-option-value-input',
     templateUrl: './option-value-input.component.html',
@@ -27,14 +38,21 @@ export const OPTION_VALUE_INPUT_VALUE_ACCESSOR: Provider = {
 export class OptionValueInputComponent implements ControlValueAccessor {
     @Input() groupName = '';
     @ViewChild('textArea', { static: true }) textArea: ElementRef<HTMLTextAreaElement>;
-    options: Array<{ name: string; locked: boolean }>;
-    disabled = false;
+    @Input() options: Option[];
+    @Output() add = new EventEmitter<Option>();
+    @Output() remove = new EventEmitter<Option>();
+    @Input() disabled = false;
     input = '';
     isFocussed = false;
     lastSelected = false;
+    formValue: Option[];
     onChangeFn: (value: any) => void;
     onTouchFn: (value: any) => void;
 
+    get optionValues(): Option[] {
+        return this.formValue ?? this.options ?? [];
+    }
+
     constructor(private changeDetector: ChangeDetectorRef) {}
 
     registerOnChange(fn: any): void {
@@ -51,17 +69,21 @@ export class OptionValueInputComponent implements ControlValueAccessor {
     }
 
     writeValue(obj: any): void {
-        this.options = obj || [];
+        this.formValue = obj || [];
     }
 
     focus() {
         this.textArea.nativeElement.focus();
     }
 
-    removeOption(option: { name: string; locked: boolean }) {
+    removeOption(option: Option) {
         if (!option.locked) {
-            this.options = this.options.filter(o => o.name !== option.name);
-            this.onChangeFn(this.options);
+            if (this.formValue) {
+                this.formValue = this.formValue?.filter(o => o.name !== option.name);
+                this.onChangeFn(this.formValue);
+            } else {
+                this.remove.emit(option);
+            }
         }
     }
 
@@ -91,12 +113,19 @@ export class OptionValueInputComponent implements ControlValueAccessor {
     }
 
     private addOptionValue() {
-        this.options = unique([...this.options, ...this.parseInputIntoOptions(this.input)]);
+        const options = this.parseInputIntoOptions(this.input);
+        if (!this.formValue && this.options) {
+            for (const option of options) {
+                this.add.emit(option);
+            }
+        } else {
+            this.formValue = unique([...this.formValue, ...options]);
+            this.onChangeFn(this.formValue);
+        }
         this.input = '';
-        this.onChangeFn(this.options);
     }
 
-    private parseInputIntoOptions(input: string): Array<{ name: string; locked: boolean }> {
+    private parseInputIntoOptions(input: string): Option[] {
         return input
             .split(/[,\n]/)
             .map(s => s.trim())
@@ -105,8 +134,9 @@ export class OptionValueInputComponent implements ControlValueAccessor {
     }
 
     private removeLastOption() {
-        if (!this.options[this.options.length - 1].locked) {
-            this.options = this.options.slice(0, this.options.length - 1);
+        if (this.optionValues.length) {
+            const option = this.optionValues[this.optionValues.length - 1];
+            this.removeOption(option);
         }
     }
 }

+ 57 - 51
packages/admin-ui/src/lib/catalog/src/components/product-variants-editor/product-variants-editor.component.html

@@ -19,22 +19,20 @@
         <label>{{ 'catalog.option-values' | translate }}</label>
         <vdr-option-value-input
             #optionValueInputComponent
-            [(ngModel)]="group.values"
-            (ngModelChange)="generateVariants()"
+            [options]="group.values"
             [groupName]="group.name"
             [disabled]="group.name === ''"
+            (add)="addOption(group.id, $event.name)"
+            (remove)="removeOption(group.id, $event.id)"
         ></vdr-option-value-input>
     </div>
     <div>
-        <button *ngIf="group.isNew" class="btn btn-icon btn-danger-outline mt5" (click)="removeOption(group)">
+        <button *ngIf="group.isNew" class="btn btn-icon btn-danger-outline mt5" (click)="removeOptionGroup(group)">
             <clr-icon shape="trash"></clr-icon>
         </button>
     </div>
 </div>
-<button
-    class="btn btn-primary-outline btn-sm"
-    (click)="addOption()"
->
+<button class="btn btn-primary-outline btn-sm" (click)="addOptionGroup()">
     <clr-icon shape="plus"></clr-icon>
     {{ 'catalog.add-option' | translate }}
 </button>
@@ -43,7 +41,7 @@
     <table class="table">
         <thead>
             <tr>
-                <th>{{ 'common.create' | translate }}</th>
+                <th></th>
                 <th>{{ 'catalog.variant' | translate }}</th>
                 <th>{{ 'catalog.sku' | translate }}</th>
                 <th>{{ 'catalog.price' | translate }}</th>
@@ -52,58 +50,66 @@
             </tr>
         </thead>
         <tr *ngFor="let variant of generatedVariants" [class.disabled]="!variant.enabled || variant.existing">
-            <td>
-                <input
-                    type="checkbox"
-                    *ngIf="!variant.existing"
-                    [(ngModel)]="variant.enabled"
-                    name="enabled"
-                    clrCheckbox
-                    (ngModelChange)="formValueChanged = true"
-                />
+            <td class="left">
+                <clr-checkbox-wrapper *ngIf="!variant.existing">
+                    <input
+                        type="checkbox"
+                        [(ngModel)]="variant.enabled"
+                        name="enabled"
+                        clrCheckbox
+                        (ngModelChange)="formValueChanged = true"
+                    />
+                    <label>{{ 'common.create' | translate }}</label>
+                </clr-checkbox-wrapper>
             </td>
             <td>
                 {{ getVariantName(variant) | translate }}
             </td>
             <td>
-                <clr-input-container *ngIf="!variant.existing">
-                    <input
-                        clrInput
-                        type="text"
-                        [(ngModel)]="variant.sku"
-                        [placeholder]="'catalog.sku' | translate"
-                        name="sku"
-                        required
-                        (ngModelChange)="onFormChanged(variant)"
-                    />
-                </clr-input-container>
-                <span *ngIf="variant.existing">{{ variant.sku }}</span>
+                <div class="flex center">
+                    <clr-input-container *ngIf="!variant.existing">
+                        <input
+                            clrInput
+                            type="text"
+                            [(ngModel)]="variant.sku"
+                            [placeholder]="'catalog.sku' | translate"
+                            name="sku"
+                            required
+                            (ngModelChange)="onFormChanged(variant)"
+                        />
+                    </clr-input-container>
+                    <span *ngIf="variant.existing">{{ variant.sku }}</span>
+                </div>
             </td>
             <td>
-                <clr-input-container *ngIf="!variant.existing">
-                    <vdr-currency-input
-                        clrInput
-                        [(ngModel)]="variant.price"
-                        name="price"
-                        [currencyCode]="currencyCode"
-                        (ngModelChange)="onFormChanged(variant)"
-                    ></vdr-currency-input>
-                </clr-input-container>
-                <span *ngIf="variant.existing">{{ variant.price | localeCurrency: currencyCode }}</span>
+                <div class="flex center">
+                    <clr-input-container *ngIf="!variant.existing">
+                        <vdr-currency-input
+                            clrInput
+                            [(ngModel)]="variant.price"
+                            name="price"
+                            [currencyCode]="currencyCode"
+                            (ngModelChange)="onFormChanged(variant)"
+                        ></vdr-currency-input>
+                    </clr-input-container>
+                    <span *ngIf="variant.existing">{{ variant.price | localeCurrency: currencyCode }}</span>
+                </div>
             </td>
             <td>
-                <clr-input-container *ngIf="!variant.existing">
-                    <input
-                        clrInput
-                        type="number"
-                        [(ngModel)]="variant.stock"
-                        name="stock"
-                        min="0"
-                        step="1"
-                        (ngModelChange)="onFormChanged(variant)"
-                    />
-                </clr-input-container>
-                <span *ngIf="variant.existing">{{ variant.stock }}</span>
+                <div class="flex center">
+                    <clr-input-container *ngIf="!variant.existing">
+                        <input
+                            clrInput
+                            type="number"
+                            [(ngModel)]="variant.stock"
+                            name="stock"
+                            min="0"
+                            step="1"
+                            (ngModelChange)="onFormChanged(variant)"
+                        />
+                    </clr-input-container>
+                    <span *ngIf="variant.existing">{{ variant.stock }}</span>
+                </div>
             </td>
             <td>
                 <vdr-dropdown *ngIf="variant.productVariantId as productVariantId">

+ 19 - 3
packages/admin-ui/src/lib/catalog/src/components/product-variants-editor/product-variants-editor.component.ts

@@ -102,7 +102,7 @@ export class ProductVariantsEditorComponent implements OnInit, DeactivateAware {
             : variant.options.map(o => o.name).join(' ');
     }
 
-    addOption() {
+    addOptionGroup() {
         this.optionGroups.push({
             isNew: true,
             name: '',
@@ -110,11 +110,24 @@ export class ProductVariantsEditorComponent implements OnInit, DeactivateAware {
         });
     }
 
-    removeOption(optionGroup: OptionGroupUiModel) {
+    removeOptionGroup(optionGroup: OptionGroupUiModel) {
         this.optionGroups = this.optionGroups.filter(og => og !== optionGroup);
         this.generateVariants();
     }
 
+    addOption(groupId: string, optionName: string) {
+        this.optionGroups.find(g => g.id === groupId)?.values.push({ name: optionName, locked: false });
+        this.generateVariants();
+    }
+
+    removeOption(groupId: string, optionId: string) {
+        const optionGroup = this.optionGroups.find(g => g.id === groupId);
+        if (optionGroup) {
+            optionGroup.values = optionGroup.values.filter(v => v.id !== optionId);
+            this.generateVariants();
+        }
+    }
+
     generateVariants() {
         const groups = this.optionGroups.map(g => g.values);
         const previousVariants = this.generatedVariants;
@@ -403,7 +416,10 @@ export class ProductVariantsEditorComponent implements OnInit, DeactivateAware {
                         values: og.options.map(o => ({
                             id: o.id,
                             name: o.name,
-                            locked: true,
+                            locked: p.variants
+                                .map(v => v.options.map(option => option.id))
+                                .flat()
+                                .includes(o.id),
                         })),
                     };
                 });

+ 1 - 0
packages/admin-ui/src/lib/static/styles/global/_utilities.scss

@@ -16,6 +16,7 @@ $space-5: $space-unit * 4;
 
 .flex.center {
     align-items: center;
+    justify-content: center;
 }
 
 .flex.wrap {