From 5aa9dcd6501281c05ae9799d958d5a86608daba0 Mon Sep 17 00:00:00 2001 From: duckietm Date: Thu, 11 Jun 2026 10:14:27 +0200 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=86=99=20Added=20colored=20background?= =?UTF-8?q?=20to=20items=20in=20group=20furni=20&=20fix=20catalog=20price?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/layout/LayoutGridItem.tsx | 4 +- .../page/common/CatalogGridOfferView.tsx | 8 +- .../page/layout/CatalogLayoutDefaultView.tsx | 1 + .../CatalogLayoutGuildCustomFurniView.tsx | 27 ++++++- .../layout/CatalogLayoutGuildForumView.tsx | 4 +- .../widgets/CatalogItemGridWidgetView.tsx | 12 +-- src/css/catalog/CatalogClassicView.css | 81 +++++++++++-------- src/css/index.css | 6 ++ 8 files changed, 90 insertions(+), 53 deletions(-) diff --git a/src/common/layout/LayoutGridItem.tsx b/src/common/layout/LayoutGridItem.tsx index e8d66ef..54a7ad4 100644 --- a/src/common/layout/LayoutGridItem.tsx +++ b/src/common/layout/LayoutGridItem.tsx @@ -24,10 +24,10 @@ export const LayoutGridItem: FC = props => const getClassNames = useMemo(() => { - const newClassNames: string[] = [ 'layout-grid-item', 'border', 'border-2', 'border-[#c4cabf]', 'rounded-[6px]' ]; + const newClassNames: string[] = [ 'layout-grid-item' ]; - if(itemActive) newClassNames.push('bg-[#e4e7df]! border-[#aeb7aa]!'); + if(itemActive) newClassNames.push('is-grid-active', 'bg-[#e4e7df]!'); if(itemUniqueSoldout || (itemUniqueNumber > 0)) newClassNames.push('unique-item'); diff --git a/src/components/catalog/views/page/common/CatalogGridOfferView.tsx b/src/components/catalog/views/page/common/CatalogGridOfferView.tsx index 3926e42..a949177 100644 --- a/src/components/catalog/views/page/common/CatalogGridOfferView.tsx +++ b/src/components/catalog/views/page/common/CatalogGridOfferView.tsx @@ -1,5 +1,5 @@ import { MouseEventType } from '@nitrots/nitro-renderer'; -import { FC, MouseEvent, useMemo, useState } from 'react'; +import { CSSProperties, FC, MouseEvent, useMemo, useState } from 'react'; import { FaHeart } from 'react-icons/fa'; import { CatalogType, GetConfigurationValue, IPurchasableOffer, Offer, ProductTypeEnum } from '../../../../../api'; import { LayoutAvatarImageView, LayoutGridItem, LayoutGridItemProps } from '../../../../../common'; @@ -9,11 +9,12 @@ interface CatalogGridOfferViewProps extends LayoutGridItemProps { offer: IPurchasableOffer; selectOffer: (offer: IPurchasableOffer) => void; + tintColor?: string; } export const CatalogGridOfferView: FC = props => { - const { offer = null, selectOffer = null, itemActive = false, ...rest } = props; + const { offer = null, selectOffer = null, itemActive = false, tintColor = null, ...rest } = props; const [ isMouseDown, setMouseDown ] = useState(false); const { requestOfferToMover = null } = useCatalogActions(); const { currentType = CatalogType.NORMAL } = useCatalogUiState(); @@ -113,9 +114,10 @@ export const CatalogGridOfferView: FC = props => return ( = props =>
+ { LocalizeText('catalog.bundlewidget.price') }
} diff --git a/src/components/catalog/views/page/layout/CatalogLayoutGuildCustomFurniView.tsx b/src/components/catalog/views/page/layout/CatalogLayoutGuildCustomFurniView.tsx index 863b39e..6f381c7 100644 --- a/src/components/catalog/views/page/layout/CatalogLayoutGuildCustomFurniView.tsx +++ b/src/components/catalog/views/page/layout/CatalogLayoutGuildCustomFurniView.tsx @@ -1,7 +1,8 @@ -import { FC } from 'react'; +import { StringDataType } from '@nitrots/nitro-renderer'; +import { FC, useMemo } from 'react'; import { FaExchangeAlt, FaSyncAlt } from 'react-icons/fa'; import { Column } from '../../../../../common'; -import { useCatalogData, useUserGroups } from '../../../../../hooks'; +import { useCatalogData, useCatalogUiState, useUserGroups } from '../../../../../hooks'; import { CatalogFirstProductSelectorWidgetView } from '../widgets/CatalogFirstProductSelectorWidgetView'; import { CatalogGuildBadgeWidgetView } from '../widgets/CatalogGuildBadgeWidgetView'; import { CatalogGuildSelectorWidgetView } from '../widgets/CatalogGuildSelectorWidgetView'; @@ -14,9 +15,29 @@ import { CatalogLayoutProps } from './CatalogLayout.types'; export const CatalogLayouGuildCustomFurniView: FC = () => { const { currentOffer = null, roomPreviewer = null } = useCatalogData(); + const { purchaseOptions = null } = useCatalogUiState(); const { data: groups = null } = useUserGroups(); const hasGroups = !!(groups && groups.length); + const tintColor = useMemo(() => + { + const previewStuffData = purchaseOptions?.previewStuffData ?? null; + + if(!previewStuffData) return null; + + const colorA = (previewStuffData as StringDataType).getValue(3); + const colorB = (previewStuffData as StringDataType).getValue(4); + + if(!colorA || !colorA.length) return null; + + if(colorB && colorB.length && (colorB !== colorA)) + { + return `linear-gradient(90deg, #${ colorA } 0 50%, #${ colorB } 50% 100%)`; + } + + return `#${ colorA }`; + }, [ purchaseOptions ]); + return ( <> @@ -38,7 +59,7 @@ export const CatalogLayouGuildCustomFurniView: FC = () => }
- +
{ !!currentOffer &&
diff --git a/src/components/catalog/views/page/layout/CatalogLayoutGuildForumView.tsx b/src/components/catalog/views/page/layout/CatalogLayoutGuildForumView.tsx index 0145230..07ec5de 100644 --- a/src/components/catalog/views/page/layout/CatalogLayoutGuildForumView.tsx +++ b/src/components/catalog/views/page/layout/CatalogLayoutGuildForumView.tsx @@ -33,7 +33,9 @@ export const CatalogLayouGuildForumView: FC = props =>
{ hasGroups && - } +
+ +
} } diff --git a/src/components/catalog/views/page/widgets/CatalogItemGridWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogItemGridWidgetView.tsx index e34dbb8..c46e4e3 100644 --- a/src/components/catalog/views/page/widgets/CatalogItemGridWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogItemGridWidgetView.tsx @@ -7,12 +7,12 @@ import { CatalogGridOfferView } from '../common/CatalogGridOfferView'; interface CatalogItemGridWidgetViewProps extends AutoGridProps { - + tintColor?: string; } export const CatalogItemGridWidgetView: FC = props => { - const { columnCount = 5, children = null, ...rest } = props; + const { columnCount = 5, tintColor = null, children = null, ...rest } = props; const { currentOffer = null, currentPage = null } = useCatalogData(); const { selectCatalogOffer = null } = useCatalogActions(); const catalogAdmin = useCatalogAdmin(); @@ -26,13 +26,6 @@ export const CatalogItemGridWidgetView: FC = pro if(elementRef && elementRef.current) elementRef.current.scrollTop = 0; }, [ currentPage ]); - // Drag-and-drop handlers — hooks MUST run unconditionally so the - // hook order stays stable when currentPage flips from null to a - // real value (the `if(!currentPage) return null` below would - // otherwise hide these from the first render and React would flag - // "Rendered more hooks than during the previous render"). Bodies - // are safe to evaluate pre-load: currentPage? optional chaining - // already guards the only access inside handleDrop. const handleDragStart = useCallback((index: number) => { setDragIndex(index); @@ -96,6 +89,7 @@ export const CatalogItemGridWidgetView: FC = pro itemActive={ (currentOffer && (currentOffer.offerId === offer.offerId)) } offer={ offer } selectOffer={ selectOffer } + tintColor={ tintColor } /> ); diff --git a/src/css/catalog/CatalogClassicView.css b/src/css/catalog/CatalogClassicView.css index 5f602b3..b7c6293 100644 --- a/src/css/catalog/CatalogClassicView.css +++ b/src/css/catalog/CatalogClassicView.css @@ -827,40 +827,49 @@ overflow: visible !important; } -.nitro-catalog-classic-window .layout-grid-item { - width: 100% !important; - height: var(--nitro-grid-column-min-height, 70px) !important; - min-width: 0 !important; - min-height: var(--nitro-grid-column-min-height, 70px) !important; - border: 0 !important; - border-radius: 0 !important; - background-image: none !important; - box-shadow: none !important; - overflow: visible !important; -} +@layer utilities { + .nitro-catalog-classic-window .layout-grid-item { + width: 100% !important; + height: var(--nitro-grid-column-min-height, 70px) !important; + min-width: 0 !important; + min-height: var(--nitro-grid-column-min-height, 70px) !important; + border: 0 !important; + border-radius: 0 !important; + background-image: none !important; + box-shadow: none !important; + overflow: visible !important; + } -.nitro-catalog-classic-window .layout-grid-item:not(.has-highlight) { - background-color: transparent !important; -} + .nitro-catalog-classic-window .layout-grid-item:not(.has-highlight) { + background-color: #e4e7df !important; + border: 2px solid transparent !important; + border-radius: 4px !important; + box-shadow: none !important; + } -.nitro-catalog-classic-window .nitro-catalog-classic-pet-breeds .layout-grid-item { - width: 84px !important; - min-width: 84px !important; - height: 74px !important; - min-height: 74px !important; -} + .nitro-catalog-classic-window .nitro-catalog-classic-pet-breeds .layout-grid-item { + width: 84px !important; + min-width: 84px !important; + height: 74px !important; + min-height: 74px !important; + } -.nitro-catalog-classic-window .layout-grid-item:hover { - background-image: none !important; - box-shadow: inset 0 0 0 1px #a1a19b !important; -} + .nitro-catalog-classic-window .layout-grid-item:not(.has-highlight):not(.is-active):hover { + background-image: none !important; + border-color: transparent !important; + box-shadow: none !important; + } -.nitro-catalog-classic-window .layout-grid-item.is-active { - background-image: none !important; - box-shadow: - inset 0 0 0 1px #63c5e9, - inset 2px 2px 0 #ecece4, - inset -2px -2px 0 #ecece4 !important; + .nitro-catalog-classic-window .layout-grid-item.is-active { + background-color: #ffffff !important; + background-image: none !important; + border: 2px solid #62c4e8 !important; + box-shadow: none !important; + } + + .nitro-catalog-classic-window .layout-grid-item.has-guild-tint:not(.has-highlight):not(.is-active) { + background: var(--guild-tint) !important; + } } .nitro-catalog-classic-window .layout-grid-item.has-highlight { @@ -901,13 +910,15 @@ pointer-events: none; } + .nitro-catalog-classic-grid-price { display: flex; flex-direction: row; align-items: center; - justify-content: center; + justify-content: flex-end; gap: 3px; width: 100%; + padding-right: 4px; color: #000; font-size: 11px; font-weight: 700; @@ -963,7 +974,8 @@ .nitro-catalog-classic-grid-price.is-multi-price { height: auto; min-height: 0; - flex-wrap: wrap; + flex-direction: column; + align-items: flex-end; row-gap: 1px; } @@ -971,7 +983,7 @@ display: inline-flex; align-items: center; justify-content: center; - gap: 1px; + gap: 3px; height: 13px; white-space: nowrap; } @@ -1022,8 +1034,7 @@ padding-right: 2px; } -.nitro-catalog-classic-total-price-slot::before { - content: "Prezzo"; +.nitro-catalog-classic-total-price-label { color: #666; font-size: 11px; line-height: 17px; diff --git a/src/css/index.css b/src/css/index.css index 46f219d..4d5bef5 100644 --- a/src/css/index.css +++ b/src/css/index.css @@ -714,6 +714,12 @@ body { background-position: center; background-repeat: no-repeat; background-color: #cdd3d9; + border: 2px solid #c4cabf; + border-radius: 6px; +} + +.layout-grid-item.is-grid-active { + border-color: #aeb7aa; } .nitro-friends-spritesheet { From 40864cf8801912a39f4ff58a06892e9d4e006534 Mon Sep 17 00:00:00 2001 From: duckietm Date: Thu, 11 Jun 2026 10:31:24 +0200 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=86=99=20Fix=20scrollbar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/css/index.css | 139 ++++++++++++++++++++++++++-------------------- 1 file changed, 79 insertions(+), 60 deletions(-) diff --git a/src/css/index.css b/src/css/index.css index 4d5bef5..1a11ad5 100644 --- a/src/css/index.css +++ b/src/css/index.css @@ -116,97 +116,128 @@ body { } *::-webkit-scrollbar { - width: 17px !important; - height: 17px !important; + width: 18px !important; + height: 18px !important; + background-color: #bdbbb3 !important; } *::-webkit-scrollbar:horizontal { - height: 17px !important; + height: 18px !important; } *::-webkit-scrollbar:not(:horizontal) { - width: 17px !important; + width: 18px !important; } -/* App-wide Habbo scrollbar (sprites cropped from catalog_skin1.png). - Thumb sprite is 17x34 with caps + grip baked in; stretched full - height via background-size: 17px 100%. Arrow buttons are natural - 17x16 sprites. */ - *::-webkit-scrollbar-track { - background-color: #e7e5d8 !important; background-image: none !important; - box-shadow: inset 1px 0 0 #b9b6a5, inset -1px 0 0 #ffffff !important; + background-color: #bdbbb3 !important; + box-shadow: inset 1px 0 0 #000000 !important; border: 0 !important; border-radius: 0 !important; } *::-webkit-scrollbar-thumb { min-height: 24px !important; - background-color: transparent !important; - background-image: url("../assets/images/catalog/scrollbar/scroll_v_thumb.png") !important; - background-repeat: no-repeat !important; - background-position: center center !important; - background-size: 17px 100% !important; - border: 0 !important; - border-radius: 0 !important; - box-shadow: none !important; + background: + url("data:image/svg+xml;utf8,") center top / 8px 5px repeat-y, + #d9d9d9 !important; + border: 1px solid #000000 !important; + border-radius: 3px !important; + box-shadow: inset 0 0 0 2px #ffffff !important; image-rendering: pixelated !important; } -*::-webkit-scrollbar-thumb:hover, +*::-webkit-scrollbar-thumb:hover { + background: + url("data:image/svg+xml;utf8,") center top / 8px 5px repeat-y, + #e3e3e3 !important; +} + *::-webkit-scrollbar-thumb:active { - background-image: url("../assets/images/catalog/scrollbar/scroll_v_thumb_pressed.png") !important; + background: + url("data:image/svg+xml;utf8,") center top / 8px 5px repeat-y, + #bcbcbc !important; } *::-webkit-scrollbar-corner { background: transparent !important; } -*::-webkit-scrollbar-button:single-button { - display: block !important; - width: 17px !important; - height: 16px !important; - /* Cream fill so the arrow sprite's transparent rounded corners - paint over the track colour, not whatever is behind the - scrollbar (which can render black). */ - background-color: #e7e5d8 !important; - background-repeat: no-repeat !important; - background-position: center !important; - border: 0 !important; - image-rendering: pixelated !important; -} - *::-webkit-scrollbar-button:single-button:vertical:decrement { - background-image: url("../assets/images/catalog/scrollbar/scroll_v_up.png") !important; + display: block !important; + width: 18px !important; + height: 18px !important; + background: + url("data:image/svg+xml;utf8,") center center / 9px 6px no-repeat, + #d9d9d9 !important; + border: 1px solid #000000 !important; + border-radius: 3px 3px 0 0 !important; + box-shadow: + inset 0 1px 0 #ffffff, + inset 1px 0 0 rgba(255, 255, 255, 0.6) !important; } *::-webkit-scrollbar-button:single-button:vertical:decrement:active { - background-image: url("../assets/images/catalog/scrollbar/scroll_v_up_pressed.png") !important; + background: + url("data:image/svg+xml;utf8,") center center / 9px 6px no-repeat, + #bcbcbc !important; } *::-webkit-scrollbar-button:single-button:vertical:increment { - background-image: url("../assets/images/catalog/scrollbar/scroll_v_down.png") !important; + display: block !important; + width: 18px !important; + height: 18px !important; + background: + url("data:image/svg+xml;utf8,") center center / 9px 6px no-repeat, + #d9d9d9 !important; + border: 1px solid #000000 !important; + border-radius: 0 0 3px 3px !important; + box-shadow: + inset 0 1px 0 #ffffff, + inset 1px 0 0 rgba(255, 255, 255, 0.6) !important; } *::-webkit-scrollbar-button:single-button:vertical:increment:active { - background-image: url("../assets/images/catalog/scrollbar/scroll_v_down_pressed.png") !important; + background: + url("data:image/svg+xml;utf8,") center center / 9px 6px no-repeat, + #bcbcbc !important; } *::-webkit-scrollbar-button:single-button:horizontal:decrement { - width: 16px !important; - height: 17px !important; - background-image: url("../assets/images/catalog/scrollbar/scroll_h_left.png") !important; + display: block !important; + width: 18px !important; + height: 18px !important; + background: + url("data:image/svg+xml;utf8,") center center / 6px 9px no-repeat, + #d9d9d9 !important; + border: 1px solid #000000 !important; + border-radius: 3px 0 0 3px !important; + box-shadow: + inset 0 1px 0 #ffffff, + inset 1px 0 0 rgba(255, 255, 255, 0.6) !important; } *::-webkit-scrollbar-button:single-button:horizontal:decrement:active { - background-image: url("../assets/images/catalog/scrollbar/scroll_h_left_pressed.png") !important; + background: + url("data:image/svg+xml;utf8,") center center / 6px 9px no-repeat, + #bcbcbc !important; } *::-webkit-scrollbar-button:single-button:horizontal:increment { - width: 16px !important; - height: 17px !important; - background-image: url("../assets/images/catalog/scrollbar/scroll_h_right.png") !important; + display: block !important; + width: 18px !important; + height: 18px !important; + background: + url("data:image/svg+xml;utf8,") center center / 6px 9px no-repeat, + #d9d9d9 !important; + border: 1px solid #000000 !important; + border-radius: 0 3px 3px 0 !important; + box-shadow: + inset 0 1px 0 #ffffff, + inset 1px 0 0 rgba(255, 255, 255, 0.6) !important; } *::-webkit-scrollbar-button:single-button:horizontal:increment:active { - background-image: url("../assets/images/catalog/scrollbar/scroll_h_right_pressed.png") !important; + background: + url("data:image/svg+xml;utf8,") center center / 6px 9px no-repeat, + #bcbcbc !important; } @layer components { @@ -822,8 +853,6 @@ body { } } -/* Avatar editor icons are now rendered as tags via AvatarEditorIcon.tsx */ - .nitro-avatar-editor-wardrobe-figure-preview { background-color: #677181; overflow: hidden; @@ -946,10 +975,9 @@ body { } } -/* Font Size */ .fs-custom { - font-size: var(--font-size, 16px); /* Fallback to 16px if not set */ + font-size: var(--font-size, 16px); } .nitro-wired { @@ -1904,8 +1932,6 @@ body { box-shadow: none !important; } -/* ── Avatar Editor ─────────────────────────────────────────────────────── */ - .color-picker-frame { border-image-source: url('@/assets/images/avatareditor/color_frame.png'); border-image-slice: 6 6 6 6 fill; @@ -1983,8 +2009,6 @@ body { gap: 5px; } -/* ── Avatar Editor tab icons ───────────────────────────────────────────── */ - .avatar-editor-tabs { position: relative; @@ -2051,11 +2075,6 @@ body { box-shadow: none !important; } -/* ── Avatar Editor misc ─────────────────────────────────────────────────── */ - - -/* ── Pet Companion ─────────────────────────────────────────────────────── */ - .pet-equipped-bar { display: flex; align-items: center; From de383710696b25d9b2b219c06f7479a0949e85f4 Mon Sep 17 00:00:00 2001 From: duckietm Date: Thu, 11 Jun 2026 13:16:29 +0200 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=86=99=20100%=20Guild=20Furni=20Catal?= =?UTF-8?q?og=20Page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../page/common/CatalogGridOfferView.tsx | 6 +-- .../CatalogLayoutGuildCustomFurniView.tsx | 31 +++++++---- .../CatalogGuildFurniRecolorFilter.tsx | 52 +++++++++++++++++++ src/css/catalog/CatalogClassicView.css | 20 ------- 4 files changed, 77 insertions(+), 32 deletions(-) create mode 100644 src/components/catalog/views/page/widgets/CatalogGuildFurniRecolorFilter.tsx diff --git a/src/components/catalog/views/page/common/CatalogGridOfferView.tsx b/src/components/catalog/views/page/common/CatalogGridOfferView.tsx index a949177..1927a1c 100644 --- a/src/components/catalog/views/page/common/CatalogGridOfferView.tsx +++ b/src/components/catalog/views/page/common/CatalogGridOfferView.tsx @@ -1,5 +1,5 @@ import { MouseEventType } from '@nitrots/nitro-renderer'; -import { CSSProperties, FC, MouseEvent, useMemo, useState } from 'react'; +import { FC, MouseEvent, useMemo, useState } from 'react'; import { FaHeart } from 'react-icons/fa'; import { CatalogType, GetConfigurationValue, IPurchasableOffer, Offer, ProductTypeEnum } from '../../../../../api'; import { LayoutAvatarImageView, LayoutGridItem, LayoutGridItemProps } from '../../../../../common'; @@ -114,10 +114,9 @@ export const CatalogGridOfferView: FC = props => return ( = props => className="nitro-catalog-classic-grid-offer-icon" src={ iconUrl } draggable={ false } + style={ tintColor ? { filter: 'url(#guild-furni-recolor)', transform: 'translateZ(0)' } : undefined } onError={ event => { const fallbackIconUrl = product.getIconUrl(offer); diff --git a/src/components/catalog/views/page/layout/CatalogLayoutGuildCustomFurniView.tsx b/src/components/catalog/views/page/layout/CatalogLayoutGuildCustomFurniView.tsx index 6f381c7..f946726 100644 --- a/src/components/catalog/views/page/layout/CatalogLayoutGuildCustomFurniView.tsx +++ b/src/components/catalog/views/page/layout/CatalogLayoutGuildCustomFurniView.tsx @@ -1,10 +1,11 @@ import { StringDataType } from '@nitrots/nitro-renderer'; -import { FC, useMemo } from 'react'; +import { FC, useEffect, useMemo, useState } from 'react'; import { FaExchangeAlt, FaSyncAlt } from 'react-icons/fa'; import { Column } from '../../../../../common'; import { useCatalogData, useCatalogUiState, useUserGroups } from '../../../../../hooks'; import { CatalogFirstProductSelectorWidgetView } from '../widgets/CatalogFirstProductSelectorWidgetView'; import { CatalogGuildBadgeWidgetView } from '../widgets/CatalogGuildBadgeWidgetView'; +import { CatalogGuildFurniRecolorFilter } from '../widgets/CatalogGuildFurniRecolorFilter'; import { CatalogGuildSelectorWidgetView } from '../widgets/CatalogGuildSelectorWidgetView'; import { CatalogItemGridWidgetView } from '../widgets/CatalogItemGridWidgetView'; import { CatalogPurchaseWidgetView } from '../widgets/CatalogPurchaseWidgetView'; @@ -18,28 +19,40 @@ export const CatalogLayouGuildCustomFurniView: FC = () => const { purchaseOptions = null } = useCatalogUiState(); const { data: groups = null } = useUserGroups(); const hasGroups = !!(groups && groups.length); + const [ groupColors, setGroupColors ] = useState<{ colorA: string; colorB: string } | null>(null); - const tintColor = useMemo(() => + useEffect(() => { const previewStuffData = purchaseOptions?.previewStuffData ?? null; - if(!previewStuffData) return null; + if(!previewStuffData) return; const colorA = (previewStuffData as StringDataType).getValue(3); const colorB = (previewStuffData as StringDataType).getValue(4); - if(!colorA || !colorA.length) return null; + if(!colorA || !colorA.length) return; - if(colorB && colorB.length && (colorB !== colorA)) - { - return `linear-gradient(90deg, #${ colorA } 0 50%, #${ colorB } 50% 100%)`; - } + const next = { colorA, colorB: (colorB && colorB.length) ? colorB : colorA }; + + setGroupColors(prev => (prev && (prev.colorA === next.colorA) && (prev.colorB === next.colorB)) ? prev : next); + }, [ purchaseOptions ]); + + + const tintColor = useMemo(() => + { + if(!groupColors) return null; + + const { colorA, colorB } = groupColors; + + if(colorB && (colorB !== colorA)) return `linear-gradient(90deg, #${ colorA } 0 50%, #${ colorB } 50% 100%)`; return `#${ colorA }`; - }, [ purchaseOptions ]); + }, [ groupColors ]); return ( <> + { !!groupColors && + } { !!currentOffer && diff --git a/src/components/catalog/views/page/widgets/CatalogGuildFurniRecolorFilter.tsx b/src/components/catalog/views/page/widgets/CatalogGuildFurniRecolorFilter.tsx new file mode 100644 index 0000000..3de982b --- /dev/null +++ b/src/components/catalog/views/page/widgets/CatalogGuildFurniRecolorFilter.tsx @@ -0,0 +1,52 @@ +import { memo, useMemo } from 'react'; + +interface CatalogGuildFurniRecolorFilterProps +{ + colorA?: string; + colorB?: string; +} + +export const GUILD_FURNI_RECOLOR_FILTER_ID = 'guild-furni-recolor'; +const OUTLINE_LEVEL = 0.08; + +const toUnit = (hex: string, offset: number): number => +{ + const value = parseInt(hex.substr(offset, 2), 16); + + return (isNaN(value) ? 0 : value) / 255; +}; + +export const CatalogGuildFurniRecolorFilter = memo((props: CatalogGuildFurniRecolorFilterProps) => +{ + const { colorA = null, colorB = null } = props; + + const tables = useMemo(() => + { + if(!colorA || (colorA.length < 6) || !colorB || (colorB.length < 6)) return null; + + const aR = toUnit(colorA, 0), aG = toUnit(colorA, 2), aB = toUnit(colorA, 4); + const bR = toUnit(colorB, 0), bG = toUnit(colorB, 2), bB = toUnit(colorB, 4); + + return { + r: `${ OUTLINE_LEVEL } ${ bR } ${ bR } ${ aR } ${ aR } ${ aR }`, + g: `${ OUTLINE_LEVEL } ${ bG } ${ bG } ${ aG } ${ aG } ${ aG }`, + b: `${ OUTLINE_LEVEL } ${ bB } ${ bB } ${ aB } ${ aB } ${ aB }` + }; + }, [ colorA, colorB ]); + + if(!tables) return null; + + return ( + + ); +}); + +CatalogGuildFurniRecolorFilter.displayName = 'CatalogGuildFurniRecolorFilter'; diff --git a/src/css/catalog/CatalogClassicView.css b/src/css/catalog/CatalogClassicView.css index b7c6293..57c4a81 100644 --- a/src/css/catalog/CatalogClassicView.css +++ b/src/css/catalog/CatalogClassicView.css @@ -866,10 +866,6 @@ border: 2px solid #62c4e8 !important; box-shadow: none !important; } - - .nitro-catalog-classic-window .layout-grid-item.has-guild-tint:not(.has-highlight):not(.is-active) { - background: var(--guild-tint) !important; - } } .nitro-catalog-classic-window .layout-grid-item.has-highlight { @@ -1474,9 +1470,6 @@ } .nitro-catalog-classic-window *::-webkit-scrollbar-thumb { - /* Grip: a single 2px #a0a0a0 stripe in an 8px-wide centered band, - repeated every 5px (2px stripe + 3px body gap). - Outline: 1px black border, then a 2px white inset frame inside it. */ min-height: 24px !important; background: url("data:image/svg+xml;utf8,") center top / 8px 5px repeat-y, @@ -1498,9 +1491,6 @@ #bcbcbc !important; } -/* Arrow buttons: light grey cap with rounded OUTER corners (up button - rounded at the top, down button rounded at the bottom), 1px black - border, dark chevron via inline SVG. */ .nitro-catalog-classic-window *::-webkit-scrollbar-button:single-button:vertical:decrement { display: block !important; width: 18px !important; @@ -1617,13 +1607,6 @@ display: none !important; } - /* Stack the navigation above the furni/preview layout and let the - whole content area scroll. The previous grid used - `grid-template-rows: auto minmax(0, 1fr)`, but on iOS Safari the - flex height chain is indefinite, so the 1fr layout-shell row - collapsed to 0 and only the sidebar (category list) was visible. - A flex column sized to content + a scrollable content-shell is - device-robust. */ .nitro-catalog-classic-stage, .nitro-catalog-classic-stage.is-navigation-hidden { display: flex; @@ -1639,9 +1622,6 @@ max-height: 30vh; } - /* The default layout's children (preview, grid, buy bar) are - absolutely positioned against a fixed ~460px box, so give the - shell a definite height and never clip it on mobile. */ .nitro-catalog-classic-layout-shell { flex: 0 0 auto; width: 100%;