Gift Modal: Fix UI (#6642)
This commit is contained in:
parent
49013cdf7b
commit
b40fe83338
@ -133,7 +133,6 @@
|
|||||||
|
|
||||||
.resaleHeaderContentContainer {
|
.resaleHeaderContentContainer {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-items: center;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,6 +165,12 @@
|
|||||||
.resaleHeaderText {
|
.resaleHeaderText {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-bottom: 0.0625rem !important;
|
margin-bottom: 0.0625rem !important;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.3125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resaleHeaderTextBlock {
|
||||||
|
margin-left: 4.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.resaleHeaderDescription {
|
.resaleHeaderDescription {
|
||||||
|
|||||||
@ -40,7 +40,6 @@ import Button from '../../ui/Button';
|
|||||||
import InfiniteScroll from '../../ui/InfiniteScroll';
|
import InfiniteScroll from '../../ui/InfiniteScroll';
|
||||||
import Modal from '../../ui/Modal';
|
import Modal from '../../ui/Modal';
|
||||||
import Transition from '../../ui/Transition';
|
import Transition from '../../ui/Transition';
|
||||||
import BalanceBlock from '../stars/BalanceBlock';
|
|
||||||
import GiftComposer from './GiftComposer';
|
import GiftComposer from './GiftComposer';
|
||||||
import GiftItemPremium from './GiftItemPremium';
|
import GiftItemPremium from './GiftItemPremium';
|
||||||
import GiftItemStar from './GiftItemStar';
|
import GiftItemStar from './GiftItemStar';
|
||||||
@ -518,23 +517,25 @@ const GiftModal: FC<OwnProps & StateProps> = ({
|
|||||||
const isFirstLoading = areResaleGiftsLoading && !resaleGiftsCount;
|
const isFirstLoading = areResaleGiftsLoading && !resaleGiftsCount;
|
||||||
return (
|
return (
|
||||||
<div className={styles.resaleHeaderContentContainer}>
|
<div className={styles.resaleHeaderContentContainer}>
|
||||||
<h2 className={styles.resaleHeaderText}>
|
<div className={styles.resaleHeaderTextBlock}>
|
||||||
{selectedResaleGift.title}
|
<h2 className={styles.resaleHeaderText}>
|
||||||
</h2>
|
{selectedResaleGift.title}
|
||||||
{isFirstLoading
|
</h2>
|
||||||
&& (
|
{isFirstLoading
|
||||||
<div className={styles.resaleHeaderDescription}>
|
&& (
|
||||||
{lang('Loading')}
|
<div className={styles.resaleHeaderDescription}>
|
||||||
</div>
|
{lang('Loading')}
|
||||||
)}
|
</div>
|
||||||
{!isFirstLoading && resaleGiftsCount !== undefined
|
)}
|
||||||
&& (
|
{!isFirstLoading && resaleGiftsCount !== undefined
|
||||||
<div className={styles.resaleHeaderDescription}>
|
&& (
|
||||||
{lang('HeaderDescriptionResaleGifts', {
|
<div className={styles.resaleHeaderDescription}>
|
||||||
count: resaleGiftsCount,
|
{lang('HeaderDescriptionResaleGifts', {
|
||||||
}, { withNodes: true, withMarkdown: true, pluralValue: resaleGiftsCount })}
|
count: resaleGiftsCount,
|
||||||
</div>
|
}, { withNodes: true, withMarkdown: true, pluralValue: resaleGiftsCount })}
|
||||||
)}
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<GiftResaleFilters dialogRef={dialogRef} />
|
<GiftResaleFilters dialogRef={dialogRef} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -555,6 +556,7 @@ const GiftModal: FC<OwnProps & StateProps> = ({
|
|||||||
contentClassName={styles.content}
|
contentClassName={styles.content}
|
||||||
className={buildClassName(styles.modalDialog, styles.root)}
|
className={buildClassName(styles.modalDialog, styles.root)}
|
||||||
isLowStackPriority
|
isLowStackPriority
|
||||||
|
withBalanceBar
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
className={styles.closeButton}
|
className={styles.closeButton}
|
||||||
@ -566,12 +568,11 @@ const GiftModal: FC<OwnProps & StateProps> = ({
|
|||||||
>
|
>
|
||||||
<div className={buttonClassName} />
|
<div className={buttonClassName} />
|
||||||
</Button>
|
</Button>
|
||||||
<BalanceBlock className={styles.balance} balance={starBalance} withAddButton />
|
|
||||||
<div className={buildClassName(
|
<div className={buildClassName(
|
||||||
styles.header,
|
styles.header,
|
||||||
isResaleScreen && styles.resaleHeader,
|
isResaleScreen && styles.resaleHeader,
|
||||||
!shouldShowHeader && styles.hiddenHeader,
|
!shouldShowHeader && styles.hiddenHeader,
|
||||||
isCategoryListPinned && styles.noBorder)}
|
isCategoryListPinned && !isResaleScreen && styles.noBorder)}
|
||||||
>
|
>
|
||||||
<Transition
|
<Transition
|
||||||
name="slideVerticalFade"
|
name="slideVerticalFade"
|
||||||
|
|||||||
@ -15,14 +15,14 @@
|
|||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding-right: 0.5rem;
|
|
||||||
padding-left: 0.5rem;
|
|
||||||
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
@include mixins.gradient-border-horizontal(0.5rem, 0.5rem);
|
@include mixins.gradient-border-horizontal(0.25rem, 0.25rem);
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
height: 0;
|
height: 0;
|
||||||
@ -33,10 +33,69 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.itemIcon {
|
.dropdownArrows {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
width: 0.625rem;
|
||||||
|
height: 1rem;
|
||||||
margin-inline-start: 0.25rem;
|
margin-inline-start: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.arrowLine {
|
||||||
|
/* stylelint-disable-next-line plugin/whole-pixel */
|
||||||
|
--x: 1.5px;
|
||||||
|
--y: 0.25rem;
|
||||||
|
--a: 45deg;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
/* stylelint-disable-next-line plugin/whole-pixel */
|
||||||
|
width: 1.5px;
|
||||||
|
height: 0.3125rem;
|
||||||
|
border-radius: 1px;
|
||||||
|
|
||||||
|
background-color: currentcolor;
|
||||||
|
|
||||||
|
transition: transform 0.2s ease-in-out;
|
||||||
|
|
||||||
|
&.topLeft {
|
||||||
|
transform: translate(calc(-1 * var(--x)), calc(-1 * var(--y))) rotate(var(--a));
|
||||||
|
}
|
||||||
|
|
||||||
|
&.topRight {
|
||||||
|
transform: translate(var(--x), calc(-1 * var(--y))) rotate(calc(-1 * var(--a)));
|
||||||
|
}
|
||||||
|
|
||||||
|
&.bottomLeft {
|
||||||
|
transform: translate(calc(-1 * var(--x)), var(--y)) rotate(calc(-1 * var(--a)));
|
||||||
|
}
|
||||||
|
|
||||||
|
&.bottomRight {
|
||||||
|
transform: translate(var(--x), var(--y)) rotate(var(--a));
|
||||||
|
}
|
||||||
|
|
||||||
|
&.open.topLeft {
|
||||||
|
transform: translate(calc(-1 * var(--x)), calc(-1 * var(--y))) rotate(calc(-1 * var(--a)));
|
||||||
|
}
|
||||||
|
|
||||||
|
&.open.topRight {
|
||||||
|
transform: translate(var(--x), calc(-1 * var(--y))) rotate(var(--a));
|
||||||
|
}
|
||||||
|
|
||||||
|
&.open.bottomLeft {
|
||||||
|
transform: translate(calc(-1 * var(--x)), var(--y)) rotate(var(--a));
|
||||||
|
}
|
||||||
|
|
||||||
|
&.open.bottomRight {
|
||||||
|
transform: translate(var(--x), var(--y)) rotate(calc(-1 * var(--a)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.sticker {
|
.sticker {
|
||||||
margin-inline-start: 0.375rem;
|
margin-inline-start: 0.375rem;
|
||||||
}
|
}
|
||||||
@ -55,6 +114,11 @@
|
|||||||
margin-inline-start: 3rem;
|
margin-inline-start: 3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menuItemCount {
|
||||||
|
margin-inline-start: 0.5rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
.backdrop {
|
.backdrop {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0.625rem;
|
left: 0.625rem;
|
||||||
@ -69,6 +133,11 @@
|
|||||||
margin-inline-start: 0.5 !important;
|
margin-inline-start: 0.5 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.itemIcon {
|
||||||
|
margin-inline-end: 0.125rem;
|
||||||
|
font-size: 1.125rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -76,7 +145,7 @@
|
|||||||
|
|
||||||
width: auto;
|
width: auto;
|
||||||
margin-inline: 0.25rem;
|
margin-inline: 0.25rem;
|
||||||
padding: 0.125rem 0.75rem;
|
padding: 0.25rem 0.75rem;
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
|
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
|
|||||||
@ -126,6 +126,27 @@ const GiftResaleFilters: FC<StateProps & OwnProps> = ({
|
|||||||
};
|
};
|
||||||
}, [filteredAttributes, searchModelQuery, searchBackdropQuery, searchPatternQuery]);
|
}, [filteredAttributes, searchModelQuery, searchBackdropQuery, searchPatternQuery]);
|
||||||
|
|
||||||
|
const countersMap = useMemo(() => {
|
||||||
|
const map = {
|
||||||
|
model: new Map<string, number>(),
|
||||||
|
pattern: new Map<string, number>(),
|
||||||
|
backdrop: new Map<number, number>(),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const counter of counters ?? []) {
|
||||||
|
const { attribute, count } = counter;
|
||||||
|
if (attribute.type === 'model') {
|
||||||
|
map.model.set(attribute.documentId, count);
|
||||||
|
} else if (attribute.type === 'pattern') {
|
||||||
|
map.pattern.set(attribute.documentId, count);
|
||||||
|
} else if (attribute.type === 'backdrop') {
|
||||||
|
map.backdrop.set(attribute.backdropId, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}, [counters]);
|
||||||
|
|
||||||
// Sort Menu
|
// Sort Menu
|
||||||
const sortMenuRef = useRef<HTMLDivElement>();
|
const sortMenuRef = useRef<HTMLDivElement>();
|
||||||
const {
|
const {
|
||||||
@ -175,18 +196,21 @@ const GiftResaleFilters: FC<StateProps & OwnProps> = ({
|
|||||||
const SortMenuButton: FC<{ onTrigger: (e: ReactMouseEvent<HTMLDivElement, MouseEvent>) => void; isOpen?: boolean }>
|
const SortMenuButton: FC<{ onTrigger: (e: ReactMouseEvent<HTMLDivElement, MouseEvent>) => void; isOpen?: boolean }>
|
||||||
= useMemo(() => {
|
= useMemo(() => {
|
||||||
const sortType = filter.sortType;
|
const sortType = filter.sortType;
|
||||||
|
const iconName = sortType === 'byDate' ? 'sort-by-date'
|
||||||
|
: sortType === 'byNumber' ? 'sort-by-number'
|
||||||
|
: 'sort-by-price';
|
||||||
return ({ onTrigger, isOpen: isMenuOpen }) => (
|
return ({ onTrigger, isOpen: isMenuOpen }) => (
|
||||||
<div
|
<div
|
||||||
className={styles.item}
|
className={styles.item}
|
||||||
onClick={onTrigger}
|
onClick={onTrigger}
|
||||||
>
|
>
|
||||||
|
<Icon
|
||||||
|
name={iconName}
|
||||||
|
className={styles.itemIcon}
|
||||||
|
/>
|
||||||
{sortType === 'byDate' && lang('ValueGiftSortByDate')}
|
{sortType === 'byDate' && lang('ValueGiftSortByDate')}
|
||||||
{sortType === 'byNumber' && lang('ValueGiftSortByNumber')}
|
{sortType === 'byNumber' && lang('ValueGiftSortByNumber')}
|
||||||
{sortType === 'byPrice' && lang('ValueGiftSortByPrice')}
|
{sortType === 'byPrice' && lang('ValueGiftSortByPrice')}
|
||||||
<Icon
|
|
||||||
name="dropdown-arrows"
|
|
||||||
className={styles.itemIcon}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}, [lang, filter]);
|
}, [lang, filter]);
|
||||||
@ -203,10 +227,7 @@ const GiftResaleFilters: FC<StateProps & OwnProps> = ({
|
|||||||
{attributesCount === 0 && lang('GiftAttributeModel')}
|
{attributesCount === 0 && lang('GiftAttributeModel')}
|
||||||
{attributesCount > 0
|
{attributesCount > 0
|
||||||
&& lang('GiftAttributeModelPlural', { count: attributesCount }, { pluralValue: attributesCount })}
|
&& lang('GiftAttributeModelPlural', { count: attributesCount }, { pluralValue: attributesCount })}
|
||||||
<Icon
|
{renderDropdownArrows(isMenuOpen)}
|
||||||
name="dropdown-arrows"
|
|
||||||
className={styles.itemIcon}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}, [lang, filter]);
|
}, [lang, filter]);
|
||||||
@ -222,10 +243,7 @@ const GiftResaleFilters: FC<StateProps & OwnProps> = ({
|
|||||||
{attributesCount === 0 && lang('GiftAttributeBackdrop')}
|
{attributesCount === 0 && lang('GiftAttributeBackdrop')}
|
||||||
{attributesCount > 0
|
{attributesCount > 0
|
||||||
&& lang('GiftAttributeBackdropPlural', { count: attributesCount }, { pluralValue: attributesCount })}
|
&& lang('GiftAttributeBackdropPlural', { count: attributesCount }, { pluralValue: attributesCount })}
|
||||||
<Icon
|
{renderDropdownArrows(isMenuOpen)}
|
||||||
name="dropdown-arrows"
|
|
||||||
className={styles.itemIcon}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}, [lang, filter]);
|
}, [lang, filter]);
|
||||||
@ -240,10 +258,7 @@ const GiftResaleFilters: FC<StateProps & OwnProps> = ({
|
|||||||
{attributesCount === 0 && lang('GiftAttributeSymbol')}
|
{attributesCount === 0 && lang('GiftAttributeSymbol')}
|
||||||
{attributesCount > 0
|
{attributesCount > 0
|
||||||
&& lang('GiftAttributeSymbolPlural', { count: attributesCount }, { pluralValue: attributesCount })}
|
&& lang('GiftAttributeSymbolPlural', { count: attributesCount }, { pluralValue: attributesCount })}
|
||||||
<Icon
|
{renderDropdownArrows(isMenuOpen)}
|
||||||
name="dropdown-arrows"
|
|
||||||
className={styles.itemIcon}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}, [lang, filter]);
|
}, [lang, filter]);
|
||||||
@ -334,6 +349,17 @@ const GiftResaleFilters: FC<StateProps & OwnProps> = ({
|
|||||||
} });
|
} });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function renderDropdownArrows(isOpen?: boolean) {
|
||||||
|
return (
|
||||||
|
<div className={styles.dropdownArrows}>
|
||||||
|
<div className={buildClassName(styles.arrowLine, styles.topLeft, isOpen && styles.open)} />
|
||||||
|
<div className={buildClassName(styles.arrowLine, styles.topRight, isOpen && styles.open)} />
|
||||||
|
<div className={buildClassName(styles.arrowLine, styles.bottomLeft, isOpen && styles.open)} />
|
||||||
|
<div className={buildClassName(styles.arrowLine, styles.bottomRight, isOpen && styles.open)} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function renderSortMenuItems() {
|
function renderSortMenuItems() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -425,7 +451,7 @@ const GiftResaleFilters: FC<StateProps & OwnProps> = ({
|
|||||||
onReset={handleSearchModelInputReset}
|
onReset={handleSearchModelInputReset}
|
||||||
placeholder={lang('Search')}
|
placeholder={lang('Search')}
|
||||||
/>
|
/>
|
||||||
<MenuItem icon="select" onClick={handleSelectedAllModelsClick} disabled={isSelectedAll}>
|
<MenuItem icon="select" onClick={handleSelectedAllModelsClick}>
|
||||||
{lang('ContextMenuItemSelectAll')}
|
{lang('ContextMenuItemSelectAll')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
{models.map((model) => {
|
{models.map((model) => {
|
||||||
@ -447,6 +473,9 @@ const GiftResaleFilters: FC<StateProps & OwnProps> = ({
|
|||||||
/>
|
/>
|
||||||
<div className={styles.menuItemStickerText}>
|
<div className={styles.menuItemStickerText}>
|
||||||
{model.name}
|
{model.name}
|
||||||
|
<span className={styles.menuItemCount}>
|
||||||
|
{lang.number(countersMap.model.get(model.sticker.id) || 0)}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<Icon
|
<Icon
|
||||||
className={styles.menuItemIcon}
|
className={styles.menuItemIcon}
|
||||||
@ -495,7 +524,7 @@ const GiftResaleFilters: FC<StateProps & OwnProps> = ({
|
|||||||
onReset={handleSearchBackdropInputReset}
|
onReset={handleSearchBackdropInputReset}
|
||||||
placeholder={lang('Search')}
|
placeholder={lang('Search')}
|
||||||
/>
|
/>
|
||||||
<MenuItem icon="select" onClick={handleSelectedAllBackdropsClick} disabled={isSelectedAll}>
|
<MenuItem icon="select" onClick={handleSelectedAllBackdropsClick}>
|
||||||
{lang('ContextMenuItemSelectAll')}
|
{lang('ContextMenuItemSelectAll')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
{backdrops.map((backdrop) => {
|
{backdrops.map((backdrop) => {
|
||||||
@ -515,6 +544,9 @@ const GiftResaleFilters: FC<StateProps & OwnProps> = ({
|
|||||||
/>
|
/>
|
||||||
<div className={styles.backdropAttributeMenuItemText}>
|
<div className={styles.backdropAttributeMenuItemText}>
|
||||||
{backdrop.name}
|
{backdrop.name}
|
||||||
|
<span className={styles.menuItemCount}>
|
||||||
|
{lang.number(countersMap.backdrop.get(backdrop.backdropId) || 0)}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<Icon
|
<Icon
|
||||||
className={styles.menuItemIcon}
|
className={styles.menuItemIcon}
|
||||||
@ -560,7 +592,7 @@ const GiftResaleFilters: FC<StateProps & OwnProps> = ({
|
|||||||
onReset={handleSearchPatternInputReset}
|
onReset={handleSearchPatternInputReset}
|
||||||
placeholder={lang('Search')}
|
placeholder={lang('Search')}
|
||||||
/>
|
/>
|
||||||
<MenuItem icon="select" onClick={handleSelectedAllPatternsClick} disabled={isSelectedAll}>
|
<MenuItem icon="select" onClick={handleSelectedAllPatternsClick}>
|
||||||
{lang('ContextMenuItemSelectAll')}
|
{lang('ContextMenuItemSelectAll')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
{patterns.map((pattern) => {
|
{patterns.map((pattern) => {
|
||||||
@ -583,6 +615,9 @@ const GiftResaleFilters: FC<StateProps & OwnProps> = ({
|
|||||||
|
|
||||||
<div className={styles.menuItemStickerText}>
|
<div className={styles.menuItemStickerText}>
|
||||||
{pattern.name}
|
{pattern.name}
|
||||||
|
<span className={styles.menuItemCount}>
|
||||||
|
{lang.number(countersMap.pattern.get(pattern.sticker.id) || 0)}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<Icon
|
<Icon
|
||||||
className={styles.menuItemIcon}
|
className={styles.menuItemIcon}
|
||||||
|
|||||||
@ -58,10 +58,10 @@
|
|||||||
|
|
||||||
&.with-balance-bar {
|
&.with-balance-bar {
|
||||||
.modal-container {
|
.modal-container {
|
||||||
top: 5.5rem;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-dialog {
|
.modal-dialog {
|
||||||
max-height: calc(100vh - 7.5rem);
|
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -208,14 +208,14 @@ const Modal: FC<OwnProps> = ({
|
|||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
role="dialog"
|
role="dialog"
|
||||||
>
|
>
|
||||||
{withBalanceBar && (
|
|
||||||
<ModalStarBalanceBar
|
|
||||||
isModalOpen={isOpen}
|
|
||||||
currency={currencyInBalanceBar}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<div className="modal-container">
|
<div className="modal-container">
|
||||||
<div className="modal-backdrop" onClick={!noBackdropClose ? onClose : undefined} />
|
<div className="modal-backdrop" onClick={!noBackdropClose ? onClose : undefined} />
|
||||||
|
{withBalanceBar && (
|
||||||
|
<ModalStarBalanceBar
|
||||||
|
isModalOpen={isOpen}
|
||||||
|
currency={currencyInBalanceBar}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<div className={modalDialogClassName} ref={actualDialogRef} style={dialogStyle}>
|
<div className={modalDialogClassName} ref={actualDialogRef} style={dialogStyle}>
|
||||||
{renderHeader()}
|
{renderHeader()}
|
||||||
{Boolean(moreMenuItems) && (
|
{Boolean(moreMenuItems) && (
|
||||||
|
|||||||
@ -1,14 +1,12 @@
|
|||||||
.root {
|
.root {
|
||||||
position: absolute;
|
|
||||||
z-index: var(--z-modal);
|
z-index: var(--z-modal);
|
||||||
top: 0rem;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -1rem);
|
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
margin-top: 2rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
padding: 0.5rem 1.25rem;
|
padding: 0.5rem 1.25rem;
|
||||||
border-radius: 2rem;
|
border-radius: 2rem;
|
||||||
|
|
||||||
@ -20,6 +18,12 @@
|
|||||||
|
|
||||||
transition: transform 0.2s ease, opacity 0.2s ease;
|
transition: transform 0.2s ease, opacity 0.2s ease;
|
||||||
|
|
||||||
|
&.hidden {
|
||||||
|
pointer-events: none;
|
||||||
|
transform: scale(0);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
:global(.confirm) & {
|
:global(.confirm) & {
|
||||||
z-index: var(--z-modal-confirm);
|
z-index: var(--z-modal-confirm);
|
||||||
}
|
}
|
||||||
@ -32,14 +36,6 @@
|
|||||||
transform: none !important;
|
transform: none !important;
|
||||||
transition: none;
|
transition: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(:global(.open)) {
|
|
||||||
transform: translate(-50%, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(:global(.closing)) {
|
|
||||||
transform: translate(-50%, 1rem);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.starIcon {
|
.starIcon {
|
||||||
@ -51,3 +47,8 @@
|
|||||||
.tonInUsdDescription {
|
.tonInUsdDescription {
|
||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tonInUsdDescription,
|
||||||
|
.getMoreStarsLink {
|
||||||
|
font-weight: var(--font-weight-semibold);
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
import {
|
import { memo } from '../../lib/teact/teact';
|
||||||
memo,
|
|
||||||
} from '../../lib/teact/teact';
|
|
||||||
import { getActions, withGlobal } from '../../global';
|
import { getActions, withGlobal } from '../../global';
|
||||||
|
|
||||||
import type { ApiStarsAmount, ApiTonAmount } from '../../api/types';
|
import type { ApiStarsAmount, ApiTonAmount } from '../../api/types';
|
||||||
@ -10,6 +8,7 @@ import buildClassName from '../../util/buildClassName';
|
|||||||
import { convertTonFromNanos, convertTonToUsd, formatCurrencyAsString } from '../../util/formatCurrency';
|
import { convertTonFromNanos, convertTonToUsd, formatCurrencyAsString } from '../../util/formatCurrency';
|
||||||
import { formatStarsAsIcon, formatTonAsIcon } from '../../util/localization/format';
|
import { formatStarsAsIcon, formatTonAsIcon } from '../../util/localization/format';
|
||||||
|
|
||||||
|
import useIsTopmostBalanceBarModal from '../../hooks/element/useIsTopmostBalanceBarModal';
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
import useLastCallback from '../../hooks/useLastCallback';
|
import useLastCallback from '../../hooks/useLastCallback';
|
||||||
import useShowTransition from '../../hooks/useShowTransition';
|
import useShowTransition from '../../hooks/useShowTransition';
|
||||||
@ -56,6 +55,8 @@ function ModalStarBalanceBar({
|
|||||||
withShouldRender: true,
|
withShouldRender: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isTopmost = useIsTopmostBalanceBarModal(ref, Boolean(shouldRender && currentBalance));
|
||||||
|
|
||||||
const handleGetMoreStars = useLastCallback(() => {
|
const handleGetMoreStars = useLastCallback(() => {
|
||||||
openStarsBalanceModal(isTonMode ? { currency: 'TON' } : {});
|
openStarsBalanceModal(isTonMode ? { currency: 'TON' } : {});
|
||||||
});
|
});
|
||||||
@ -66,7 +67,7 @@ function ModalStarBalanceBar({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={buildClassName(styles.root)}
|
className={buildClassName(styles.root, !isTopmost && styles.hidden)}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
@ -101,7 +102,7 @@ function ModalStarBalanceBar({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!isTonMode && (
|
{!isTonMode && (
|
||||||
<Link isPrimary onClick={handleGetMoreStars}>
|
<Link className={styles.getMoreStarsLink} isPrimary onClick={handleGetMoreStars}>
|
||||||
{lang('GetMoreStarsLinkText')}
|
{lang('GetMoreStarsLinkText')}
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
|||||||
59
src/hooks/element/useIsTopmostBalanceBarModal.ts
Normal file
59
src/hooks/element/useIsTopmostBalanceBarModal.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { useEffect, useState } from '../../lib/teact/teact';
|
||||||
|
|
||||||
|
import { createCallbackManager } from '../../util/callbacks';
|
||||||
|
|
||||||
|
const BALANCE_BAR_MODAL_SELECTOR = '.Modal.with-balance-bar';
|
||||||
|
|
||||||
|
const balanceBarCallbacks = createCallbackManager();
|
||||||
|
|
||||||
|
export default function useIsTopmostBalanceBarModal(
|
||||||
|
ref: { current?: HTMLElement },
|
||||||
|
isActive?: boolean,
|
||||||
|
) {
|
||||||
|
const [isTopmost, setIsTopmost] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isActive) return undefined;
|
||||||
|
|
||||||
|
const updateIsTopmost = () => {
|
||||||
|
setIsTopmost(checkIsTopmostBalanceBarModal(ref.current));
|
||||||
|
};
|
||||||
|
|
||||||
|
updateIsTopmost();
|
||||||
|
|
||||||
|
const unsubscribe = balanceBarCallbacks.addCallback(updateIsTopmost);
|
||||||
|
balanceBarCallbacks.runCallbacks();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unsubscribe();
|
||||||
|
balanceBarCallbacks.runCallbacks();
|
||||||
|
};
|
||||||
|
}, [isActive, ref]);
|
||||||
|
|
||||||
|
return isTopmost;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkIsTopmostBalanceBarModal(element?: HTMLElement) {
|
||||||
|
if (!element) return true;
|
||||||
|
|
||||||
|
const parentModal = element.closest(BALANCE_BAR_MODAL_SELECTOR);
|
||||||
|
if (!parentModal) return true;
|
||||||
|
|
||||||
|
const allBalanceBarModals = document.querySelectorAll(BALANCE_BAR_MODAL_SELECTOR);
|
||||||
|
if (allBalanceBarModals.length <= 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let topmostModal: Element | undefined;
|
||||||
|
let highestZIndex = -Infinity;
|
||||||
|
|
||||||
|
allBalanceBarModals.forEach((modal) => {
|
||||||
|
const zIndex = parseInt(getComputedStyle(modal).zIndex, 10) || 0;
|
||||||
|
if (zIndex >= highestZIndex) {
|
||||||
|
highestZIndex = zIndex;
|
||||||
|
topmostModal = modal;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return parentModal === topmostModal;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user