diff --git a/libs/domain/product/variant-list/index.ts b/libs/domain/product/variant-list/index.ts index 786ddcf5..987bb52d 100644 --- a/libs/domain/product/variant-list/index.ts +++ b/libs/domain/product/variant-list/index.ts @@ -1 +1,2 @@ export * from './variant-list.component'; +export * from './variant-list.styles'; diff --git a/libs/domain/product/variant-list/variant-list.component.ts b/libs/domain/product/variant-list/variant-list.component.ts index c45a71a8..a33cde45 100644 --- a/libs/domain/product/variant-list/variant-list.component.ts +++ b/libs/domain/product/variant-list/variant-list.component.ts @@ -6,33 +6,108 @@ import { ProductListService, ProductMixin, } from '@oryx-frontend/product'; -import { hydrate } from '@oryx-frontend/utilities'; +import { LinkService, RouteType, RouterService } from '@oryx-frontend/router'; +import { computed, hydrate, signalAware } from '@oryx-frontend/utilities'; +import { + createSignal, + effect, +} from '@oryx-frontend/utilities/src/signals/core'; import { LitElement, TemplateResult, html } from 'lit'; import { repeat } from 'lit/directives/repeat.js'; +import { variantListStyle } from './variant-list.styles'; @hydrate({ context: PRODUCT }) +@signalAware() export class ProductVariantListComponent extends ProductMixin( LayoutMixin(LitElement) ) { + static styles = variantListStyle; + protected productListService = resolve(ProductListService); protected productListPageService = resolve(ProductListPageService); - protected override render(): TemplateResult { - return html` - ${this.renderLayout({ - template: this.renderList(), - })} - `; + protected routerService = resolve(RouterService); + protected linkService = resolve(LinkService); + + protected $variant = createSignal(undefined); + + protected $link = computed(() => { + const variantSku = this.$variant(); + if (!variantSku) return; + return this.linkService.get({ + type: RouteType.Product, + qualifier: { sku: variantSku }, + }); + }); + + protected route = effect(() => { + const link = this.$link(); + if (link) { + this.routerService.navigate(link as any as string); + } + }); + + protected render(): TemplateResult | void { + const variants = this.$product()?.variants; + + if (!variants || Object.keys(variants).length < 2) return; + + return this.renderAttributeSelectors(); } - protected renderList(): TemplateResult { - console.log('variant list', this.$product()?.variants); - return html` + protected renderAttributeSelectors() { + const { variantDefinition, attributeNames } = this.$product() ?? {}; + if (!variantDefinition) return; + const keys = Object.keys(variantDefinition); + + return html`
${repeat( - this.$product()?.variants ?? [], - (p) => p.sku, - (p) => html`` + keys, + (key) => + html`

${attributeNames?.[key]}

+ ${repeat( + variantDefinition[key], + (value) => html` + + + ${value} + + ` + )}
` )} - `; + `; + } + + protected isChecked(key: string, value: string) { + const product = this.$product(); + return product?.attributes?.[key] === value; + } + + protected handleVariantChange(e: Event) { + const form = e.currentTarget as HTMLFormElement; + const formData = new FormData(form); + + const selectedAttributes: Record = {}; + formData.forEach((value, key) => { + selectedAttributes[key] = value as string; + }); + + const { variants } = this.$product() ?? {}; + + if (!variants) return; + const matchingVariant = Object.keys(variants).find((sku) => { + const values = variants[sku]; + + return Object.keys(selectedAttributes).every( + (attrKey) => values![attrKey] === selectedAttributes[attrKey] + ); + }); + if (matchingVariant) this.$variant.set(matchingVariant); } } diff --git a/libs/domain/product/variant-list/variant-list.styles.ts b/libs/domain/product/variant-list/variant-list.styles.ts new file mode 100644 index 00000000..12a0a3e4 --- /dev/null +++ b/libs/domain/product/variant-list/variant-list.styles.ts @@ -0,0 +1,8 @@ +import { css } from 'lit'; + +export const variantListStyle = css` + :host { + display: grid; + gap: 8px; + } +`;