mirror of
https://github.com/mue/mue.git
synced 2026-07-18 22:44:08 +02:00
style(marketplace): potential marketplace page redesign
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
import { memo } from 'react';
|
||||
import { MdClose } from 'react-icons/md';
|
||||
import './installButton.scss';
|
||||
|
||||
const MueLogo = () => (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 500 500"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="mueLogo"
|
||||
>
|
||||
<circle cx="250" cy="250" r="250" fill="url(#paint0_linear_1870_2)" />
|
||||
<path
|
||||
d="M291.02 132.002V164.879H362.13V235.422H395.302V132.002H291.02Z"
|
||||
fill="url(#paint1_linear_1870_2)"
|
||||
/>
|
||||
<path
|
||||
d="M314.592 241.186H285.201V270.542H258.081V241.186H228.669V214.119H258.081V184.784H285.201V214.119H314.592V241.186ZM354.25 171.261H283.307V146.651H164.332V308.676H378.929V242.089H354.25V171.261Z"
|
||||
fill="url(#paint2_linear_1870_2)"
|
||||
/>
|
||||
<path
|
||||
d="M156.949 176.811H134.164V338.836H348.761V316.031H156.949V176.811Z"
|
||||
fill="url(#paint3_linear_1870_2)"
|
||||
/>
|
||||
<path
|
||||
d="M126.785 206.975H104V369H318.597V346.195H126.785V206.975Z"
|
||||
fill="url(#paint4_linear_1870_2)"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="paint0_linear_1870_2"
|
||||
x1="462"
|
||||
y1="120"
|
||||
x2="29.5"
|
||||
y2="383"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#FF5C25" />
|
||||
<stop offset="0.484375" stopColor="#D21A11" />
|
||||
<stop offset="1" stopColor="#FF456E" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint1_linear_1870_2"
|
||||
x1="343.161"
|
||||
y1="235.422"
|
||||
x2="343.161"
|
||||
y2="132.002"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#F18D91" />
|
||||
<stop offset="1" stopColor="#FBD3C6" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint2_linear_1870_2"
|
||||
x1="271.631"
|
||||
y1="308.676"
|
||||
x2="271.631"
|
||||
y2="146.651"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#F18D91" />
|
||||
<stop offset="1" stopColor="#FBD3C6" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint3_linear_1870_2"
|
||||
x1="241.463"
|
||||
y1="338.836"
|
||||
x2="241.463"
|
||||
y2="176.811"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#F18D91" />
|
||||
<stop offset="1" stopColor="#FBD3C6" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint4_linear_1870_2"
|
||||
x1="211.298"
|
||||
y1="369"
|
||||
x2="211.298"
|
||||
y2="206.975"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#F18D91" />
|
||||
<stop offset="1" stopColor="#FBD3C6" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const InstallButton = ({ onClick, isInstalled, label }) => {
|
||||
return (
|
||||
<button
|
||||
className={`installButton ${isInstalled ? 'installed' : 'notInstalled'}`}
|
||||
onClick={onClick}
|
||||
>
|
||||
<span className="buttonContent">
|
||||
<span className="labelText">{label}</span>
|
||||
<span className="iconWrapper">
|
||||
<span className={`icon installIcon ${isInstalled ? 'hide' : 'show'}`}>
|
||||
<MueLogo />
|
||||
</span>
|
||||
<span className={`icon removeIcon ${isInstalled ? 'show' : 'hide'}`}>
|
||||
<MdClose />
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(InstallButton);
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './InstallButton';
|
||||
@@ -0,0 +1,166 @@
|
||||
.installButton {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
padding: 12px 24px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
min-width: 180px;
|
||||
|
||||
.buttonContent {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.iconWrapper {
|
||||
position: relative;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.icon {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
&.show {
|
||||
opacity: 1;
|
||||
transform: rotate(0deg) scale(1);
|
||||
}
|
||||
|
||||
&.hide {
|
||||
opacity: 0;
|
||||
transform: rotate(90deg) scale(0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.mueLogo {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
|
||||
}
|
||||
|
||||
.removeIcon {
|
||||
svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.labelText {
|
||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
// Install state (not installed yet)
|
||||
&.notInstalled {
|
||||
background: linear-gradient(135deg, #ff5c25 0%, #d21a11 48%, #ff456e 100%);
|
||||
color: white;
|
||||
// box-shadow: 0 4px 12px rgba(255, 92, 37, 0.3);
|
||||
|
||||
&:hover {
|
||||
// box-shadow: 0 6px 16px rgba(255, 92, 37, 0.4);
|
||||
|
||||
.mueLogo {
|
||||
animation: logoFloat 0.6s ease-in-out;
|
||||
}
|
||||
|
||||
// .labelText {
|
||||
// transform: translateX(2px);
|
||||
// }
|
||||
}
|
||||
|
||||
&:active {
|
||||
box-shadow: 0 2px 8px rgba(255, 92, 37, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
// Installed state (already installed)
|
||||
&.installed {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: white;
|
||||
// border: 2px solid rgba(255, 255, 255, 0.2);
|
||||
// backdrop-filter: blur(10px);
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 92, 37, 0.2);
|
||||
border-color: rgba(255, 92, 37, 0.4);
|
||||
// transform: translateY(-2px);
|
||||
box-shadow: 0 6px 16px rgba(255, 92, 37, 0.2);
|
||||
|
||||
.removeIcon {
|
||||
animation: rotateIcon 0.4s ease-in-out;
|
||||
}
|
||||
|
||||
// .labelText {
|
||||
// transform: translateX(2px);
|
||||
// }
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Loading state (optional - for future use)
|
||||
&:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
transform: none !important;
|
||||
|
||||
&:hover {
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes logoFloat {
|
||||
0%,
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotateIcon {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
// Smooth state transition animation
|
||||
.installButton.notInstalled.transitioning,
|
||||
.installButton.installed.transitioning {
|
||||
animation: buttonPulse 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes buttonPulse {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import Items from '../components/Items/Items';
|
||||
import { Dropdown, FileUpload } from 'components/Form/Settings';
|
||||
import { Header, CustomActions } from 'components/Layout/Settings';
|
||||
import { Button } from 'components/Elements';
|
||||
import InstallButton from '../components/Elements/InstallButton';
|
||||
|
||||
import { install, uninstall, urlParser } from 'utils/marketplace';
|
||||
import { updateHash } from 'utils/deepLinking';
|
||||
@@ -173,9 +174,9 @@ const Added = memo(() => {
|
||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const button = item.display_name ? (
|
||||
<Button
|
||||
type="settings"
|
||||
<InstallButton
|
||||
onClick={uninstallItem}
|
||||
isInstalled={true}
|
||||
label={variables.getMessage('modals.main.marketplace.product.buttons.remove')}
|
||||
/>
|
||||
) : '';
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
import ItemPage from './ItemPage';
|
||||
import Items from '../components/Items/Items';
|
||||
import { Button } from 'components/Elements';
|
||||
import InstallButton from '../components/Elements/InstallButton';
|
||||
|
||||
import { install, urlParser, uninstall } from 'utils/marketplace';
|
||||
import { updateHash } from 'utils/deepLinking';
|
||||
@@ -34,18 +35,16 @@ class Marketplace extends PureComponent {
|
||||
};
|
||||
this.buttons = {
|
||||
uninstall: (
|
||||
<Button
|
||||
type="settings"
|
||||
<InstallButton
|
||||
onClick={() => this.manage('uninstall')}
|
||||
icon={<MdClose />}
|
||||
isInstalled={true}
|
||||
label={variables.getMessage('modals.main.marketplace.product.buttons.remove')}
|
||||
/>
|
||||
),
|
||||
install: (
|
||||
<Button
|
||||
type="settings"
|
||||
<InstallButton
|
||||
onClick={() => this.manage('install')}
|
||||
icon={<MdLibraryAdd />}
|
||||
isInstalled={false}
|
||||
label={variables.getMessage('modals.main.marketplace.product.buttons.addtomue')}
|
||||
/>
|
||||
),
|
||||
|
||||
@@ -97,6 +97,95 @@ class ItemPage extends PureComponent {
|
||||
.sort(() => 0.5 - Math.random())
|
||||
.slice(0, 3);
|
||||
|
||||
// Extract colour from data (British spelling as used in API)
|
||||
const mainColor = this.props.data.data.colour;
|
||||
|
||||
// Helper function to determine if a color is light or dark
|
||||
const isLightColor = (hexColor) => {
|
||||
if (!hexColor) return false;
|
||||
// Remove # if present
|
||||
const hex = hexColor.replace('#', '');
|
||||
// Convert to RGB
|
||||
const r = parseInt(hex.substr(0, 2), 16);
|
||||
const g = parseInt(hex.substr(2, 2), 16);
|
||||
const b = parseInt(hex.substr(4, 2), 16);
|
||||
// Calculate relative luminance (perceived brightness)
|
||||
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
||||
return luminance > 0.6; // If > 0.6, it's a light color
|
||||
};
|
||||
|
||||
const isLight = isLightColor(mainColor);
|
||||
const textColor = isLight ? '#000000' : '#ffffff';
|
||||
|
||||
// Create dynamic styles for theming with the main color
|
||||
const themedStyles = mainColor ? (
|
||||
<style>{`
|
||||
/* Icon buttons styling */
|
||||
.iconButtons .btn-icon {
|
||||
background: ${mainColor} !important;
|
||||
background-image: none !important;
|
||||
border-color: ${mainColor} !important;
|
||||
box-shadow: 0 0 0 1px ${mainColor}, 0 4px 12px ${mainColor}40 !important;
|
||||
color: ${textColor} !important;
|
||||
}
|
||||
.iconButtons .btn-icon:hover {
|
||||
background: ${mainColor} !important;
|
||||
filter: brightness(${isLight ? '0.95' : '1.15'});
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 0 0 1px ${mainColor}, 0 6px 20px ${mainColor}60 !important;
|
||||
}
|
||||
|
||||
/* ItemInfo background gradient */
|
||||
.itemInfo {
|
||||
background: linear-gradient(135deg, ${mainColor}ee 0%, ${mainColor}aa 50%, ${mainColor}66 100%) !important;
|
||||
box-shadow: 0 8px 32px ${mainColor}40 !important;
|
||||
}
|
||||
|
||||
/* Icon shadow */
|
||||
.itemInfo .icon {
|
||||
box-shadow: 0 8px 32px ${mainColor}80, 0 0 0 1px ${mainColor}40 !important;
|
||||
}
|
||||
|
||||
/* Install button styling */
|
||||
.itemInfo .installButton {
|
||||
background: ${mainColor} !important;
|
||||
background-image: linear-gradient(135deg, ${mainColor} 0%, ${mainColor}dd 100%) !important;
|
||||
box-shadow: 0 4px 16px ${mainColor}60 !important;
|
||||
}
|
||||
|
||||
.itemInfo .installButton:hover {
|
||||
background: ${mainColor} !important;
|
||||
filter: brightness(${isLight ? '0.95' : '1.15'});
|
||||
box-shadow: 0 6px 24px ${mainColor}80 !important;
|
||||
}
|
||||
|
||||
/* Install button text and icon color */
|
||||
.itemInfo .installButton span,
|
||||
.itemInfo .installButton svg {
|
||||
color: ${textColor} !important;
|
||||
}
|
||||
|
||||
/* Mue logo - circle matches text color, paths match button color */
|
||||
.itemInfo .installButton .mueLogo circle {
|
||||
fill: ${textColor} !important;
|
||||
}
|
||||
|
||||
.itemInfo .installButton .mueLogo path {
|
||||
fill: ${mainColor} !important;
|
||||
}
|
||||
|
||||
/* Remove the default gradient animation when themed */
|
||||
.itemInfo .installButton.installed {
|
||||
background: ${mainColor}aa !important;
|
||||
background-image: none !important;
|
||||
}
|
||||
|
||||
.itemInfo .installButton.installed:hover {
|
||||
background: ${mainColor}99 !important;
|
||||
}
|
||||
`}</style>
|
||||
) : null;
|
||||
|
||||
if (!this.props.data.display_name) {
|
||||
return null;
|
||||
}
|
||||
@@ -330,12 +419,7 @@ class ItemPage extends PureComponent {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="itemInfo"
|
||||
style={{
|
||||
backgroundImage: `url("${this.props.data.data.icon_url}")`,
|
||||
}}
|
||||
>
|
||||
<div className="itemInfo">
|
||||
<div className="front">
|
||||
<img
|
||||
className="icon"
|
||||
@@ -357,30 +441,33 @@ class ItemPage extends PureComponent {
|
||||
</p>
|
||||
)}
|
||||
{this.props.data.data.sideload !== true && (
|
||||
<div className="iconButtons">
|
||||
<Button
|
||||
type="icon"
|
||||
onClick={() => this.setState({ shareModal: true })}
|
||||
icon={<MdIosShare />}
|
||||
tooltipTitle={variables.getMessage('widgets.quote.share')}
|
||||
tooltipKey="share"
|
||||
/>
|
||||
<Button
|
||||
type="icon"
|
||||
onClick={() =>
|
||||
window.open(
|
||||
variables.constants.REPORT_ITEM +
|
||||
this.props.data.data.display_name.split(' ').join('+'),
|
||||
'_blank',
|
||||
)
|
||||
}
|
||||
icon={<MdFlag />}
|
||||
tooltipTitle={variables.getMessage(
|
||||
'modals.main.marketplace.product.buttons.report',
|
||||
)}
|
||||
tooltipKey="report"
|
||||
/>
|
||||
</div>
|
||||
<>
|
||||
{themedStyles}
|
||||
<div className="iconButtons">
|
||||
<Button
|
||||
type="icon"
|
||||
onClick={() => this.setState({ shareModal: true })}
|
||||
icon={<MdIosShare />}
|
||||
tooltipTitle={variables.getMessage('widgets.quote.share')}
|
||||
tooltipKey="share"
|
||||
/>
|
||||
<Button
|
||||
type="icon"
|
||||
onClick={() =>
|
||||
window.open(
|
||||
variables.constants.REPORT_ITEM +
|
||||
this.props.data.data.display_name.split(' ').join('+'),
|
||||
'_blank',
|
||||
)
|
||||
}
|
||||
icon={<MdFlag />}
|
||||
tooltipTitle={variables.getMessage(
|
||||
'modals.main.marketplace.product.buttons.report',
|
||||
)}
|
||||
tooltipKey="report"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{this.props.data.data.in_collections?.length > 0 && (
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user