IO-3624 Finalize admin config UX and validation polish
This commit is contained in:
@@ -29,20 +29,19 @@ export default function ShopInfoSectionNavigator({ tabsRef, activeTabKey }) {
|
||||
const nextTargetMap = new Map();
|
||||
const nextOptions = Array.from(activePane.querySelectorAll(".imex-form-row"))
|
||||
.filter((card) => {
|
||||
const titleNode = getOwnCardTitleNode(card);
|
||||
if (!titleNode?.textContent?.trim()) return false;
|
||||
|
||||
const ancestorCard = card.parentElement?.closest(".imex-form-row");
|
||||
return !ancestorCard || !activePane.contains(ancestorCard);
|
||||
return shouldIncludeCardInNavigator(card, activePane);
|
||||
})
|
||||
.map((card, index) => {
|
||||
const label = getOwnCardTitleNode(card)?.textContent?.trim();
|
||||
const { title, depth, searchLabel } = getCardNavigatorInfo(card, activePane);
|
||||
const value = `${activeTabKey}-shop-info-section-${index}`;
|
||||
|
||||
nextTargetMap.set(value, card);
|
||||
|
||||
return {
|
||||
label,
|
||||
label: renderNavigatorOptionLabel(title, depth),
|
||||
labelText: title,
|
||||
searchLabel,
|
||||
depth,
|
||||
value
|
||||
};
|
||||
});
|
||||
@@ -103,12 +102,13 @@ export default function ShopInfoSectionNavigator({ tabsRef, activeTabKey }) {
|
||||
<div className="shop-info-section-navigator">
|
||||
<Select
|
||||
allowClear
|
||||
showSearch={{ optionFilterProp: "label" }}
|
||||
showSearch
|
||||
value={selectedSection}
|
||||
placeholder={t("bodyshop.labels.jump_to_section")}
|
||||
options={options}
|
||||
popupMatchSelectWidth={false}
|
||||
disabled={options.length === 0}
|
||||
filterOption={(input, option) => option?.searchLabel?.toLowerCase().includes(input.toLowerCase())}
|
||||
onChange={handleSectionChange}
|
||||
/>
|
||||
</div>
|
||||
@@ -120,6 +120,77 @@ function getOwnCardTitleNode(card) {
|
||||
return headNode?.querySelector(".ant-card-head-title");
|
||||
}
|
||||
|
||||
function getOwnCardTitle(card) {
|
||||
return getOwnCardTitleNode(card)?.textContent?.trim();
|
||||
}
|
||||
|
||||
function getAncestorCards(card, activePane) {
|
||||
const ancestors = [];
|
||||
let currentCard = card.parentElement?.closest(".imex-form-row");
|
||||
|
||||
while (currentCard && activePane.contains(currentCard)) {
|
||||
ancestors.push(currentCard);
|
||||
currentCard = currentCard.parentElement?.closest(".imex-form-row");
|
||||
}
|
||||
|
||||
return ancestors.reverse();
|
||||
}
|
||||
|
||||
function getCardDepth(card, activePane) {
|
||||
return getAncestorCards(card, activePane).length;
|
||||
}
|
||||
|
||||
function isVisibleCard(card) {
|
||||
return card.offsetParent !== null;
|
||||
}
|
||||
|
||||
function isNavigatorEligibleSubsection(card) {
|
||||
return (
|
||||
!card.classList.contains("imex-form-row--compact") &&
|
||||
!card.classList.contains("imex-form-row--title-only") &&
|
||||
!card.querySelector(":scope > .ant-card-actions")
|
||||
);
|
||||
}
|
||||
|
||||
function shouldIncludeCardInNavigator(card, activePane) {
|
||||
const title = getOwnCardTitle(card);
|
||||
if (!title || !isVisibleCard(card)) return false;
|
||||
|
||||
const depth = getCardDepth(card, activePane);
|
||||
if (depth === 0) return true;
|
||||
if (depth === 1) return isNavigatorEligibleSubsection(card);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getCardNavigatorInfo(card, activePane) {
|
||||
const title = getOwnCardTitle(card);
|
||||
const ancestors = getAncestorCards(card, activePane);
|
||||
const depth = ancestors.length;
|
||||
const parentTitle = depth === 1 ? getOwnCardTitle(ancestors[0]) : null;
|
||||
|
||||
return {
|
||||
title,
|
||||
depth,
|
||||
searchLabel: parentTitle ? `${parentTitle} ${title}` : title
|
||||
};
|
||||
}
|
||||
|
||||
function renderNavigatorOptionLabel(title, depth) {
|
||||
return (
|
||||
<span
|
||||
className={[
|
||||
"shop-info-section-navigator__option",
|
||||
depth > 0 ? "shop-info-section-navigator__option--subsection" : null
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" ")}
|
||||
>
|
||||
<span className="shop-info-section-navigator__option-label">{title}</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function clearHighlightedTarget(highlightedTargetRef) {
|
||||
if (highlightedTargetRef.current) {
|
||||
highlightedTargetRef.current.classList.remove(HIGHLIGHT_CLASS);
|
||||
@@ -132,6 +203,11 @@ function areOptionsEqual(currentOptions, nextOptions) {
|
||||
|
||||
return currentOptions.every((option, index) => {
|
||||
const nextOption = nextOptions[index];
|
||||
return option.label === nextOption.label && option.value === nextOption.value;
|
||||
return (
|
||||
option.labelText === nextOption.labelText &&
|
||||
option.searchLabel === nextOption.searchLabel &&
|
||||
option.depth === nextOption.depth &&
|
||||
option.value === nextOption.value
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user