mirror of
https://github.com/mue/mue.git
synced 2026-07-15 13:03:55 +02:00
feat(QuoteOptions): refactor custom quote UI and styles for improved layout and usability
This commit is contained in:
@@ -25,8 +25,12 @@ export default function Quote() {
|
||||
const zoomQuote = localStorage.getItem('zoomQuote');
|
||||
return `${1.2 * Number((zoomQuote || 100) / 100)}em`;
|
||||
});
|
||||
const [authorDetails, setAuthorDetails] = useState(localStorage.getItem('authorDetails') === 'true');
|
||||
const [isLegacyStyle, setIsLegacyStyle] = useState(localStorage.getItem('widgetStyle') === 'legacy');
|
||||
const [authorDetails, setAuthorDetails] = useState(
|
||||
localStorage.getItem('authorDetails') === 'true',
|
||||
);
|
||||
const [isLegacyStyle, setIsLegacyStyle] = useState(
|
||||
localStorage.getItem('widgetStyle') === 'legacy',
|
||||
);
|
||||
|
||||
// Compute if current quote is favorited
|
||||
const isFavourited = useMemo(() => {
|
||||
@@ -62,8 +66,9 @@ export default function Quote() {
|
||||
}
|
||||
};
|
||||
|
||||
const shouldRefresh = localStorage.getItem('quotechange') === 'refresh' ||
|
||||
localStorage.getItem('quotechange') === null;
|
||||
const shouldRefresh =
|
||||
localStorage.getItem('quotechange') === 'refresh' ||
|
||||
localStorage.getItem('quotechange') === null;
|
||||
|
||||
if (shouldRefresh) {
|
||||
getQuote();
|
||||
@@ -100,8 +105,9 @@ export default function Quote() {
|
||||
{quoteData.quote}
|
||||
</span>
|
||||
|
||||
{authorDetails && (
|
||||
isLegacyStyle ? (
|
||||
{authorDetails &&
|
||||
quoteData.author &&
|
||||
(isLegacyStyle ? (
|
||||
<AuthorInfoLegacy
|
||||
author={quoteData.author}
|
||||
authorlink={quoteData.authorlink}
|
||||
@@ -122,8 +128,7 @@ export default function Quote() {
|
||||
onShare={() => toggleShareModal(true)}
|
||||
isFavourited={isFavourited}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
import { Checkbox, Dropdown, Textarea } from 'components/Form/Settings';
|
||||
import { Button } from 'components/Elements';
|
||||
import { FREQUENCY_OPTIONS } from 'utils/frequencyManager';
|
||||
import './QuoteOptions.scss';
|
||||
|
||||
const QuoteOptions = ({ currentSubSection, onSubSectionChange, sectionName }) => {
|
||||
const getCustom = () => {
|
||||
@@ -143,9 +144,11 @@ const QuoteOptions = ({ currentSubSection, onSubSectionChange, sectionName }) =>
|
||||
localStorage.removeItem('quoteQueue');
|
||||
}
|
||||
// Notify the frequency interval hook that the frequency changed
|
||||
window.dispatchEvent(new CustomEvent('frequencyChanged', {
|
||||
detail: { type: 'quote' }
|
||||
}));
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('frequencyChanged', {
|
||||
detail: { type: 'quote' },
|
||||
}),
|
||||
);
|
||||
}}
|
||||
items={FREQUENCY_OPTIONS.map((opt) => ({
|
||||
value: opt.value,
|
||||
@@ -209,35 +212,48 @@ const QuoteOptions = ({ currentSubSection, onSubSectionChange, sectionName }) =>
|
||||
</Row>
|
||||
|
||||
{customQuote.length !== 0 ? (
|
||||
<div className="messagesContainer">
|
||||
{customQuote.map((_url, index) => (
|
||||
<div className="messageMap" key={index}>
|
||||
<div className="icon">
|
||||
<MdOutlineFormatQuote />
|
||||
</div>
|
||||
<div className="messageText">
|
||||
<Textarea
|
||||
value={customQuote[index].quote}
|
||||
placeholder={variables.getMessage('modals.main.settings.sections.quote.title')}
|
||||
onChange={(e) => handleCustomQuote(e, true, index, 'quote')}
|
||||
style={{ fontSize: '22px', fontWeight: 'bold' }}
|
||||
minRows={1}
|
||||
/>
|
||||
<Textarea
|
||||
value={customQuote[index].author}
|
||||
placeholder={variables.getMessage('modals.main.settings.sections.quote.author')}
|
||||
className="subtitle"
|
||||
onChange={(e) => handleCustomQuote(e, true, index, 'author')}
|
||||
minRows={1}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div className="messageAction">
|
||||
<Button
|
||||
type="settings"
|
||||
<div className="customQuoteContainer">
|
||||
{customQuote.map((quote, index) => (
|
||||
<div className="customQuoteItem" key={index}>
|
||||
<div className="quoteContent">
|
||||
<div className="quoteHeader">
|
||||
<span className="quoteLabel">
|
||||
<MdOutlineFormatQuote className="quoteLabelIcon" />
|
||||
{variables.getMessage('modals.main.settings.sections.quote.title')}
|
||||
</span>
|
||||
<button
|
||||
className="quoteRemoveBtn"
|
||||
onClick={() => modifyCustomQuote('remove', index)}
|
||||
icon={<MdCancel />}
|
||||
label={variables.getMessage('modals.main.marketplace.product.buttons.remove')}
|
||||
aria-label={variables.getMessage(
|
||||
'modals.main.marketplace.product.buttons.remove',
|
||||
)}
|
||||
>
|
||||
<MdCancel />
|
||||
</button>
|
||||
</div>
|
||||
<div className="quoteInputGroup">
|
||||
<Textarea
|
||||
value={quote.quote}
|
||||
placeholder={variables.getMessage(
|
||||
'modals.main.settings.sections.quote.title',
|
||||
)}
|
||||
onChange={(e) => handleCustomQuote(e, true, index, 'quote')}
|
||||
minRows={3}
|
||||
className="quoteTextarea"
|
||||
/>
|
||||
</div>
|
||||
<div className="quoteInputGroup">
|
||||
<label className="quoteLabel">
|
||||
{variables.getMessage('modals.main.settings.sections.quote.author')}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="authorInput"
|
||||
value={quote.author}
|
||||
placeholder={variables.getMessage(
|
||||
'modals.main.settings.sections.quote.author',
|
||||
)}
|
||||
onChange={(e) => handleCustomQuote(e, true, index, 'author')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -245,20 +261,18 @@ const QuoteOptions = ({ currentSubSection, onSubSectionChange, sectionName }) =>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="photosEmpty">
|
||||
<div className="emptyNewMessage">
|
||||
<MdOutlineFormatQuote />
|
||||
<span className="title">{variables.getMessage(`${QUOTE_SECTION}.no_quotes`)}</span>
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('modals.main.settings.sections.message.add_some')}
|
||||
</span>
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => modifyCustomQuote('add')}
|
||||
icon={<MdAdd />}
|
||||
label={variables.getMessage(`${QUOTE_SECTION}.add`)}
|
||||
/>
|
||||
</div>
|
||||
<div className="customQuoteEmpty">
|
||||
<MdOutlineFormatQuote className="emptyIcon" />
|
||||
<span className="emptyTitle">{variables.getMessage(`${QUOTE_SECTION}.no_quotes`)}</span>
|
||||
<span className="emptySubtitle">
|
||||
{variables.getMessage('modals.main.settings.sections.message.add_some')}
|
||||
</span>
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => modifyCustomQuote('add')}
|
||||
icon={<MdAdd />}
|
||||
label={variables.getMessage(`${QUOTE_SECTION}.add`)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
||||
198
src/features/quote/options/QuoteOptions.scss
Normal file
198
src/features/quote/options/QuoteOptions.scss
Normal file
@@ -0,0 +1,198 @@
|
||||
@use 'scss/variables' as *;
|
||||
|
||||
.customQuoteContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.customQuoteItem {
|
||||
position: relative;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
@include themed {
|
||||
background: t($modal-secondaryColour);
|
||||
border: 1px solid t($modal-border);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.quoteRemoveBtn {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.quoteHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.quoteLabel {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.8px;
|
||||
|
||||
@include themed {
|
||||
color: t($color);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.quoteLabelIcon {
|
||||
font-size: 1rem;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.quoteRemoveBtn {
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.2rem;
|
||||
transition: all 0.2s ease;
|
||||
opacity: 0.7;
|
||||
|
||||
@include themed {
|
||||
background: t($modal-sidebarActive);
|
||||
border: 1px solid t($modal-border);
|
||||
color: t($color);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 100, 100, 0.15);
|
||||
border-color: rgba(255, 100, 100, 0.3);
|
||||
color: rgba(255, 100, 100, 0.9);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
|
||||
.quoteContent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
padding-right: 50px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.quoteInputGroup {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
|
||||
.quoteTextarea {
|
||||
border-radius: 8px;
|
||||
padding: 12px 14px;
|
||||
font-size: 1rem;
|
||||
line-height: 1.6;
|
||||
resize: vertical;
|
||||
transition: all 0.2s ease;
|
||||
min-height: 80px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
@include themed {
|
||||
background: t($modal-background);
|
||||
border: 1px solid t($modal-border);
|
||||
color: t($color);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
|
||||
@include themed {
|
||||
border-color: t($link);
|
||||
}
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
@include themed {
|
||||
color: t($subColor);
|
||||
}
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.authorInput {
|
||||
border-radius: 8px;
|
||||
padding: 10px 14px;
|
||||
font-size: 0.95rem;
|
||||
transition: all 0.2s ease;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
@include themed {
|
||||
background: t($modal-background);
|
||||
border: 1px solid t($modal-border);
|
||||
color: t($color);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
|
||||
@include themed {
|
||||
border-color: t($link);
|
||||
}
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
@include themed {
|
||||
color: t($subColor);
|
||||
}
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.customQuoteEmpty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 60px 20px;
|
||||
text-align: center;
|
||||
gap: 16px;
|
||||
|
||||
.emptyIcon {
|
||||
font-size: 4rem;
|
||||
margin-bottom: 8px;
|
||||
|
||||
@include themed {
|
||||
color: t($subColor);
|
||||
}
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.emptyTitle {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
|
||||
@include themed {
|
||||
color: t($color);
|
||||
}
|
||||
}
|
||||
|
||||
.emptySubtitle {
|
||||
font-size: 0.95rem;
|
||||
max-width: 400px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
@include themed {
|
||||
color: t($subColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user