Skip to content

Commit 79cbe3e

Browse files
try and maintain focus when re-rendering cart
1 parent 92ea654 commit 79cbe3e

File tree

2 files changed

+47
-1
lines changed

2 files changed

+47
-1
lines changed

src/Umbraco.Commerce.Cart/Client/src/components/ucc-cart-modal.element.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { UCC_CART_CONTEXT } from "../contexts/ucc.context.ts";
22
import { CartConfig, CartItem } from "../types.ts";
33
import { UccModalElement } from "./ucc-modal.element.ts";
4-
import { debounce, delegate } from "../utils.ts";
4+
import {debounce, delegate, getUniqueSelector} from "../utils.ts";
55
import { UccCartRepository } from "../repositories/cart.respository.ts";
66
import { UccEvent } from "../events/ucc.event.ts";
77

@@ -65,6 +65,9 @@ export class UccCartModalElement extends UccModalElement
6565

6666
const cart = this._context.cart.get();
6767
if (cart && cart.items.length > 0) {
68+
const activeEl = this._host.ownerDocument.activeElement as HTMLElement;
69+
const activeElSelector = activeEl ? getUniqueSelector(activeEl) : undefined;
70+
const activeElCaretPos = activeEl instanceof HTMLInputElement ? activeEl.selectionStart : undefined;
6871
this._host.querySelector('.ucc-cart-items')!.innerHTML = cart.items.map((item) => {
6972
const propsToDisplay = Object.keys(item.properties ?? {}).filter((x) => (config.properties ?? []).map(y => y.toLowerCase()).includes(x.toLowerCase()));
7073
return `
@@ -136,6 +139,15 @@ export class UccCartModalElement extends UccModalElement
136139
this._host.querySelector<HTMLElement>('.ucc-cart-total--taxes .ucc-cart-total-value')!.textContent = cart.subtotal.tax;
137140
this._host.querySelector<HTMLElement>('.ucc-cart-total--total .ucc-cart-total-value')!.textContent = cart.subtotal.withTax;
138141
this._host.querySelector<HTMLElement>('.ucc-cart-checkout')!.classList.remove('ucc-cart-checkout--disabled');
142+
if (activeElSelector) {
143+
const activeEl = this._host.querySelector<HTMLElement>(activeElSelector);
144+
if (activeEl) {
145+
activeEl.focus();
146+
if (activeEl instanceof HTMLInputElement && activeElCaretPos) {
147+
activeEl.setSelectionRange(activeElCaretPos!, activeElCaretPos!);
148+
}
149+
}
150+
}
139151
} else {
140152
this._host.querySelector<HTMLElement>('.ucc-cart-items')!.innerHTML = `
141153
<div class="ucc-cart-empty">

src/Umbraco.Commerce.Cart/Client/src/utils.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,40 @@ export function trapFocus(e: KeyboardEvent, selector:string)
4949
}
5050
}
5151

52+
export const getUniqueSelector = (element:Element) => {
53+
if (!(element instanceof Element)) return null;
54+
55+
let selector = [];
56+
57+
while (element.parentElement) {
58+
let tag = element.tagName.toLowerCase();
59+
60+
// Use ID if available (most specific)
61+
if (element.id) {
62+
selector.unshift(`#${element.id}`);
63+
break;
64+
}
65+
66+
// Use class names if available
67+
let classSelector = element.className
68+
.split(/\s+/)
69+
.filter(Boolean)
70+
.map(cls => `.${cls}`)
71+
.join('');
72+
73+
// Get index among siblings of the same type
74+
let siblings = Array.from(element.parentElement.children).filter(
75+
el => el.tagName === element.tagName
76+
);
77+
let index = siblings.length > 1 ? `:nth-of-type(${siblings.indexOf(element) + 1})` : '';
78+
79+
selector.unshift(tag + classSelector + index);
80+
element = element.parentElement;
81+
}
82+
83+
return selector.join(' > ');
84+
}
85+
5286
export type ReactiveValue<T> = {
5387
hasValue: () => boolean;
5488
get: () => T | null;

0 commit comments

Comments
 (0)