mirror of
https://github.com/mue/mue.git
synced 2026-07-18 06:24:17 +02:00
7.x Structural Changes (#637)
* refactor(files): Initial commit on experimental file structure * refactor(structure): New components system * refactor(structure): Tidy settings' components * Refactor(structure): Component exports and imports * refactor(settings): Use new component imports * feat: unified background.js script * fix(build): Partially, distributions still not ready * feat: critical error on noscript, light theme support for it * fix(background): Critical issue of code making every background #000 * refactor(welcome): Partition into different files + shared components - This took too long and destroyed my sanity --------- Co-authored-by: alexsparkes <turbomarshmello@gmail.com> Co-authored-by: alexsparkes <alexsparkes@gmail.com>
This commit is contained in:
516
src/features/widgets/quote/Quote.jsx
Normal file
516
src/features/widgets/quote/Quote.jsx
Normal file
@@ -0,0 +1,516 @@
|
||||
import variables from 'config/variables';
|
||||
import { PureComponent, createRef } from 'react';
|
||||
import {
|
||||
MdContentCopy,
|
||||
MdStarBorder,
|
||||
MdStar,
|
||||
MdPerson,
|
||||
MdOpenInNew,
|
||||
MdIosShare,
|
||||
} from 'react-icons/md';
|
||||
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import { Tooltip } from 'components/Elements';
|
||||
|
||||
import Modal from 'react-modal';
|
||||
import { ShareModal } from 'components/Elements';
|
||||
|
||||
import offline_quotes from './offline_quotes.json';
|
||||
|
||||
import EventBus from 'modules/helpers/eventbus';
|
||||
|
||||
import './quote.scss';
|
||||
|
||||
export default class Quote extends PureComponent {
|
||||
buttons = {
|
||||
share: (
|
||||
<Tooltip title={variables.getMessage('widgets.quote.share')}>
|
||||
<button
|
||||
onClick={() => this.setState({ shareModal: true })}
|
||||
aria-label={variables.getMessage('widgets.quote.share')}
|
||||
>
|
||||
<MdIosShare className="copyButton" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
),
|
||||
copy: (
|
||||
<Tooltip title={variables.getMessage('widgets.quote.copy')}>
|
||||
<button
|
||||
onClick={() => this.copyQuote()}
|
||||
aria-label={variables.getMessage('widgets.quote.copy')}
|
||||
>
|
||||
<MdContentCopy className="copyButton" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
),
|
||||
unfavourited: (
|
||||
<Tooltip title={variables.getMessage('widgets.quote.favourite')}>
|
||||
<button
|
||||
onClick={() => this.favourite()}
|
||||
aria-label={variables.getMessage('widgets.quote.favourite')}
|
||||
>
|
||||
<MdStarBorder className="copyButton" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
),
|
||||
favourited: (
|
||||
<Tooltip title={variables.getMessage('widgets.quote.unfavourite')}>
|
||||
<button
|
||||
onClick={() => this.favourite()}
|
||||
aria-label={variables.getMessage('widgets.quote.unfavourite')}
|
||||
>
|
||||
<MdStar className="copyButton" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
),
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
quote: '',
|
||||
author: '',
|
||||
authorOccupation: '',
|
||||
favourited: this.useFavourite(),
|
||||
share: localStorage.getItem('quoteShareButton') === 'false' ? null : this.buttons.share,
|
||||
copy: localStorage.getItem('copyButton') === 'false' ? null : this.buttons.copy,
|
||||
quoteLanguage: '',
|
||||
type: localStorage.getItem('quoteType') || 'api',
|
||||
shareModal: false,
|
||||
};
|
||||
this.quote = createRef();
|
||||
this.quotediv = createRef();
|
||||
this.quoteauthor = createRef();
|
||||
}
|
||||
|
||||
useFavourite() {
|
||||
if (localStorage.getItem('favouriteQuoteEnabled') === 'true') {
|
||||
return localStorage.getItem('favouriteQuote')
|
||||
? this.buttons.favourited
|
||||
: this.buttons.unfavourited;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
doOffline() {
|
||||
// Get a random quote from our local JSON
|
||||
const quote = offline_quotes[Math.floor(Math.random() * offline_quotes.length)];
|
||||
|
||||
this.setState({
|
||||
quote: '"' + quote.quote + '"',
|
||||
author: quote.author,
|
||||
authorlink: this.getAuthorLink(quote.author),
|
||||
authorimg: '',
|
||||
});
|
||||
}
|
||||
|
||||
getAuthorLink(author) {
|
||||
return localStorage.getItem('authorLink') === 'false' || author === 'Unknown'
|
||||
? null
|
||||
: `https://${variables.languagecode.split('_')[0]}.wikipedia.org/wiki/${author
|
||||
.split(' ')
|
||||
.join('_')}`;
|
||||
}
|
||||
|
||||
stripHTML(html) {
|
||||
const tmpdoc = new DOMParser().parseFromString(html, 'text/html');
|
||||
return tmpdoc.body.textContent || '';
|
||||
}
|
||||
|
||||
async getAuthorImg(author) {
|
||||
if (localStorage.getItem('authorImg') === 'false') {
|
||||
return {
|
||||
authorimg: null,
|
||||
authorimglicense: null,
|
||||
};
|
||||
}
|
||||
|
||||
const authorimgdata = await (
|
||||
await fetch(
|
||||
`https://${
|
||||
variables.languagecode.split('_')[0]
|
||||
}.wikipedia.org/w/api.php?action=query&titles=${author}&origin=*&prop=pageimages&format=json&pithumbsize=100`,
|
||||
)
|
||||
).json();
|
||||
|
||||
let authorimg, authorimglicense;
|
||||
try {
|
||||
authorimg =
|
||||
authorimgdata.query.pages[Object.keys(authorimgdata.query.pages)[0]].thumbnail.source;
|
||||
|
||||
const authorimglicensedata = await (
|
||||
await fetch(
|
||||
`https://${
|
||||
variables.languagecode.split('_')[0]
|
||||
}.wikipedia.org/w/api.php?action=query&prop=imageinfo&iiprop=extmetadata&titles=File:${
|
||||
authorimgdata.query.pages[Object.keys(authorimgdata.query.pages)[0]].pageimage
|
||||
}&origin=*&format=json`,
|
||||
)
|
||||
).json();
|
||||
|
||||
const metadata =
|
||||
authorimglicensedata.query.pages[Object.keys(authorimglicensedata.query.pages)[0]]
|
||||
.imageinfo[0].extmetadata;
|
||||
const license = metadata.LicenseShortName;
|
||||
const photographer =
|
||||
metadata.Attribution.value ||
|
||||
this.stripHTML(metadata.Artist?.value || '').replace(/ \(talk\)/, '') || // talk page link (if applicable) is only removed for English
|
||||
'Unknown';
|
||||
authorimglicense = `© ${photographer}. ${license.value}`;
|
||||
authorimglicense = authorimglicense.replace(/copyright\s/i, '').replace(/©\s©\s/, '© ');
|
||||
|
||||
if (license.value === 'Public domain') {
|
||||
authorimglicense = null;
|
||||
} else if (photographer.value === 'Unknown' || !photographer) {
|
||||
authorimglicense = null;
|
||||
authorimg = null;
|
||||
}
|
||||
} catch (e) {
|
||||
authorimg = null;
|
||||
authorimglicense = null;
|
||||
}
|
||||
|
||||
if (author === 'Unknown') {
|
||||
authorimg = null;
|
||||
authorimglicense = null;
|
||||
}
|
||||
|
||||
return {
|
||||
authorimg,
|
||||
authorimglicense,
|
||||
};
|
||||
}
|
||||
|
||||
async getQuote() {
|
||||
const offline = localStorage.getItem('offlineMode') === 'true';
|
||||
|
||||
const favouriteQuote = localStorage.getItem('favouriteQuote');
|
||||
if (favouriteQuote) {
|
||||
let author = favouriteQuote.split(' - ')[1];
|
||||
const authorimgdata = await this.getAuthorImg(author);
|
||||
return this.setState({
|
||||
quote: favouriteQuote.split(' - ')[0],
|
||||
author,
|
||||
authorlink: this.getAuthorLink(author),
|
||||
authorimg: authorimgdata.authorimg,
|
||||
authorimglicense: authorimgdata.authorimglicense,
|
||||
});
|
||||
}
|
||||
|
||||
switch (this.state.type) {
|
||||
case 'custom':
|
||||
let customQuote;
|
||||
try {
|
||||
customQuote = JSON.parse(localStorage.getItem('customQuote'));
|
||||
} catch (e) {
|
||||
// move to new format
|
||||
customQuote = [
|
||||
{
|
||||
quote: localStorage.getItem('customQuote'),
|
||||
author: localStorage.getItem('customQuoteAuthor'),
|
||||
},
|
||||
];
|
||||
localStorage.setItem('customQuote', JSON.stringify(customQuote));
|
||||
}
|
||||
|
||||
// pick random
|
||||
customQuote = customQuote
|
||||
? customQuote[Math.floor(Math.random() * customQuote.length)]
|
||||
: null;
|
||||
|
||||
if (customQuote !== undefined) {
|
||||
return this.setState({
|
||||
quote: '"' + customQuote.quote + '"',
|
||||
author: customQuote.author,
|
||||
authorlink: this.getAuthorLink(customQuote.author),
|
||||
authorimg: await this.getAuthorImg(customQuote.author),
|
||||
noQuote: false,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
noQuote: true,
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'quote_pack':
|
||||
if (offline) {
|
||||
return this.doOffline();
|
||||
}
|
||||
|
||||
const quotePack = [];
|
||||
const installed = JSON.parse(localStorage.getItem('installed'));
|
||||
installed.forEach((item) => {
|
||||
if (item.type === 'quotes') {
|
||||
const quotes = item.quotes.map((quote) => ({
|
||||
...quote,
|
||||
fallbackauthorimg: item.icon_url,
|
||||
}));
|
||||
quotePack.push(...quotes);
|
||||
}
|
||||
});
|
||||
|
||||
if (quotePack) {
|
||||
const data = quotePack[Math.floor(Math.random() * quotePack.length)];
|
||||
return this.setState({
|
||||
quote: '"' + data.quote + '"',
|
||||
author: data.author,
|
||||
authorlink: this.getAuthorLink(data.author),
|
||||
authorimg: data.fallbackauthorimg,
|
||||
});
|
||||
} else {
|
||||
return this.doOffline();
|
||||
}
|
||||
case 'api':
|
||||
if (offline) {
|
||||
return this.doOffline();
|
||||
}
|
||||
|
||||
const getAPIQuoteData = async () => {
|
||||
const quoteLanguage = localStorage.getItem('quoteLanguage');
|
||||
const data = await (
|
||||
await fetch(variables.constants.API_URL + '/quotes/random?language=' + quoteLanguage)
|
||||
).json();
|
||||
// If we hit the ratelimit, we fall back to local quotes
|
||||
if (data.statusCode === 429) {
|
||||
return null;
|
||||
}
|
||||
const authorimgdata = await this.getAuthorImg(data.author);
|
||||
return {
|
||||
quote: '"' + data.quote.replace(/\s+$/g, '') + '"',
|
||||
author: data.author,
|
||||
authorlink: this.getAuthorLink(data.author),
|
||||
authorimg: authorimgdata.authorimg,
|
||||
authorimglicense: authorimgdata.authorimglicense,
|
||||
quoteLanguage: quoteLanguage,
|
||||
authorOccupation: data.author_occupation,
|
||||
};
|
||||
};
|
||||
|
||||
// First we try and get a quote from the API...
|
||||
try {
|
||||
let data = JSON.parse(localStorage.getItem('nextQuote')) || (await getAPIQuoteData());
|
||||
localStorage.setItem('nextQuote', null);
|
||||
if (data) {
|
||||
this.setState(data);
|
||||
localStorage.setItem('currentQuote', JSON.stringify(data));
|
||||
localStorage.setItem('nextQuote', JSON.stringify(await getAPIQuoteData())); // pre-fetch data about the next quote
|
||||
} else {
|
||||
this.doOffline();
|
||||
}
|
||||
} catch (e) {
|
||||
// ...and if that fails we load one locally
|
||||
this.doOffline();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
copyQuote() {
|
||||
variables.stats.postEvent('feature', 'Quote copied');
|
||||
navigator.clipboard.writeText(`${this.state.quote} - ${this.state.author}`);
|
||||
toast(variables.getMessage('toasts.quote'));
|
||||
}
|
||||
|
||||
favourite() {
|
||||
if (localStorage.getItem('favouriteQuote')) {
|
||||
localStorage.removeItem('favouriteQuote');
|
||||
this.setState({
|
||||
favourited: this.buttons.unfavourited,
|
||||
});
|
||||
} else {
|
||||
localStorage.setItem('favouriteQuote', this.state.quote + ' - ' + this.state.author);
|
||||
this.setState({
|
||||
favourited: this.buttons.favourited,
|
||||
});
|
||||
}
|
||||
|
||||
variables.stats.postEvent('feature', 'Quote favourite');
|
||||
}
|
||||
|
||||
init() {
|
||||
this.setZoom();
|
||||
|
||||
const quoteType = localStorage.getItem('quoteType');
|
||||
|
||||
if (
|
||||
this.state.type !== quoteType ||
|
||||
localStorage.getItem('quoteLanguage') !== this.state.quoteLanguage ||
|
||||
(quoteType === 'custom' && this.state.quote !== localStorage.getItem('customQuote')) ||
|
||||
(quoteType === 'custom' && this.state.author !== localStorage.getItem('customQuoteAuthor'))
|
||||
) {
|
||||
this.getQuote();
|
||||
}
|
||||
}
|
||||
|
||||
setZoom() {
|
||||
const zoomQuote = Number((localStorage.getItem('zoomQuote') || 100) / 100);
|
||||
this.quote.current.style.fontSize = `${0.8 * zoomQuote}em`;
|
||||
this.quoteauthor.current.style.fontSize = `${0.9 * zoomQuote}em`;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// const test = localStorage.getItem('quotechange');
|
||||
|
||||
// this.interval = setInterval(() => {
|
||||
// if (test !== null) {
|
||||
// const targetTime = Number(
|
||||
// Number(localStorage.getItem('quoteStartTime')) +
|
||||
// Number(localStorage.getItem('quotechange')),
|
||||
// );
|
||||
// const currentTime = Number(Date.now());
|
||||
// if (currentTime >= targetTime) {
|
||||
// this.setZoom();
|
||||
// this.getQuote();
|
||||
// localStorage.setItem('quoteStartTime', Date.now());
|
||||
// } else {
|
||||
// try {
|
||||
// this.setState(JSON.parse(localStorage.getItem('currentQuote')));
|
||||
// } catch (e) {
|
||||
// this.setZoom();
|
||||
// this.getQuote();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
this.setZoom();
|
||||
|
||||
EventBus.on('refresh', (data) => {
|
||||
if (data === 'quote') {
|
||||
if (localStorage.getItem('quote') === 'false') {
|
||||
return (this.quotediv.current.style.display = 'none');
|
||||
}
|
||||
|
||||
this.quotediv.current.style.display = 'block';
|
||||
this.init();
|
||||
|
||||
// buttons hot reload
|
||||
this.setState({
|
||||
favourited: this.useFavourite(),
|
||||
share: localStorage.getItem('quoteShareButton') === 'false' ? null : this.buttons.share,
|
||||
copy: localStorage.getItem('copyButton') === 'false' ? null : this.buttons.copy,
|
||||
});
|
||||
}
|
||||
|
||||
// uninstall quote pack reverts the quote to what you had previously
|
||||
if (data === 'marketplacequoteuninstall') {
|
||||
this.init();
|
||||
}
|
||||
|
||||
if (data === 'quoterefresh') {
|
||||
this.getQuote();
|
||||
}
|
||||
});
|
||||
|
||||
if (
|
||||
localStorage.getItem('quotechange') === 'refresh' ||
|
||||
localStorage.getItem('quotechange') === null
|
||||
) {
|
||||
this.setZoom();
|
||||
this.getQuote();
|
||||
localStorage.setItem('quoteStartTime', Date.now());
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
EventBus.off('refresh');
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.noQuote === true) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="quotediv" ref={this.quotediv}>
|
||||
<Modal
|
||||
closeTimeoutMS={300}
|
||||
isOpen={this.state.shareModal}
|
||||
className="Modal mainModal"
|
||||
overlayClassName="Overlay"
|
||||
ariaHideApp={false}
|
||||
onRequestClose={() => this.setState({ shareModal: false })}
|
||||
>
|
||||
<ShareModal
|
||||
data={`${this.state.quote} - ${this.state.author}`}
|
||||
modalClose={() => this.setState({ shareModal: false })}
|
||||
/>
|
||||
</Modal>
|
||||
<span className="quote" ref={this.quote}>
|
||||
{this.state.quote}
|
||||
</span>
|
||||
|
||||
{localStorage.getItem('widgetStyle') === 'legacy' ? (
|
||||
<>
|
||||
<div>
|
||||
<h1 className="quoteauthor" ref={this.quoteauthor}>
|
||||
<a
|
||||
href={this.state.authorlink}
|
||||
className="quoteAuthorLink"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="Learn about the author of the quote."
|
||||
>
|
||||
{this.state.author}
|
||||
</a>
|
||||
</h1>
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'center', gap: '20px' }}>
|
||||
{this.state.copy} {this.state.share} {this.state.favourited}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="author-holder">
|
||||
<div className="author">
|
||||
<div
|
||||
className="author-img"
|
||||
style={{ backgroundImage: `url(${this.state.authorimg})` }}
|
||||
>
|
||||
{this.state.authorimg === undefined || this.state.authorimg ? '' : <MdPerson />}
|
||||
</div>
|
||||
{this.state.author !== '' ? (
|
||||
<div className="author-content" ref={this.quoteauthor}>
|
||||
<span className="title">{this.state.author}</span>
|
||||
{this.state.authorOccupation !== 'Unknown' && (
|
||||
<span className="subtitle">{this.state.authorOccupation}</span>
|
||||
)}
|
||||
<span className="author-license">
|
||||
{this.state.authorimglicense &&
|
||||
this.state.authorimglicense.replace(' undefined. ', ' ')}
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="author-content whileLoading" ref={this.quoteauthor}>
|
||||
{/* these are placeholders for skeleton and as such don't need translating */}
|
||||
<span className="title">loading</span>
|
||||
<span className="subtitle">loading</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="quote-buttons">
|
||||
{this.state.authorOccupation !== 'Unknown' && this.state.authorlink !== '' ? (
|
||||
<Tooltip title={variables.getMessage('widgets.quote.link_tooltip')}>
|
||||
<a
|
||||
href={this.state.authorlink}
|
||||
className="quoteAuthorLink"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="Learn about the author of the quote."
|
||||
>
|
||||
<MdOpenInNew />
|
||||
</a>{' '}
|
||||
</Tooltip>
|
||||
) : null}
|
||||
{this.state.copy} {this.state.share} {this.state.favourited}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
102
src/features/widgets/quote/offline_quotes.json
Normal file
102
src/features/widgets/quote/offline_quotes.json
Normal file
@@ -0,0 +1,102 @@
|
||||
[
|
||||
{
|
||||
"author": "Robert De Niro",
|
||||
"quote": "Time goes on. So whatever you’re going to do, do it. Do it now. Don’t wait."
|
||||
},
|
||||
{
|
||||
"author": "Walt Disney",
|
||||
"quote": "All our dreams can come true, if we have the courage to pursue them."
|
||||
},
|
||||
{
|
||||
"author": "Confucius",
|
||||
"quote": "It does not matter how slowly you go as long as you do not stop."
|
||||
},
|
||||
{
|
||||
"author": "Roy T. Bennett",
|
||||
"quote": "Believe in yourself. You are braver than you think, more talented than you know, and capable of more than you imagine."
|
||||
},
|
||||
{
|
||||
"author": "Wayne Dyer",
|
||||
"quote": "If you believe it will work out, you’ll see opportunities. If you believe it won’t, you will see obstacles."
|
||||
},
|
||||
{
|
||||
"author": "George Addair",
|
||||
"quote": "Everything you’ve ever wanted is on the other side of fear."
|
||||
},
|
||||
{
|
||||
"author": "Winston Churchill",
|
||||
"quote": "Success is not final, failure is not fatal: it is the courage to continue that counts."
|
||||
},
|
||||
{
|
||||
"author": "Paulo Coelho",
|
||||
"quote": "There is only one thing that makes a dream impossible to achieve: the fear of failure"
|
||||
},
|
||||
{
|
||||
"author": "Brian Tracy",
|
||||
"quote": "Your true success in life begins only when you make the commitment to become excellent at what you do."
|
||||
},
|
||||
{
|
||||
"author": "Chantal Sutherland",
|
||||
"quote": "Believe in yourself, take on your challenges, dig deep within yourself to conquer fears. Never let anyone bring you down. You got to keep going."
|
||||
},
|
||||
{
|
||||
"author": "Les Brown",
|
||||
"quote": "Too many of us are not living our dreams because we are living our fears."
|
||||
},
|
||||
{
|
||||
"author": "Bob Riley",
|
||||
"quote": "Hard times don’t create heroes. It is during the hard times when the ‘hero’ within us is revealed."
|
||||
},
|
||||
{
|
||||
"author": "Jack Canfield",
|
||||
"quote": "If you can tune into your purpose and really align with it, setting goals so that your vision is an expression of that purpose, then life flows much more easily."
|
||||
},
|
||||
{
|
||||
"author": "Napolean Hill",
|
||||
"quote": "Whatever the mind can conceive and believe, it can achieve."
|
||||
},
|
||||
{
|
||||
"author": "Jim Rohn",
|
||||
"quote": "Don’t wish it were easier. Wish you were better."
|
||||
},
|
||||
{
|
||||
"author": "Serena Williams",
|
||||
"quote": "A champion is defined not by their wins but by how they can recover when they fall."
|
||||
},
|
||||
{
|
||||
"author": "Sheryl Sandberg",
|
||||
"quote": "Motivation comes from working on things we care about."
|
||||
},
|
||||
{
|
||||
"author": "Reese Witherspoon",
|
||||
"quote": "With the right kind of coaching and determination you can accomplish anything."
|
||||
},
|
||||
{
|
||||
"author": "Hazrat Inayat Khan",
|
||||
"quote": "Some people look for a beautiful place. Others make a place beautiful."
|
||||
},
|
||||
{
|
||||
"author": "Albert Einstein",
|
||||
"quote": "Life is like riding a bicycle. To keep your balance, you must keep moving."
|
||||
},
|
||||
{
|
||||
"author": "Walt Disney",
|
||||
"quote": "The way to get started is to quit talking and begin doing."
|
||||
},
|
||||
{
|
||||
"author": "Winston Churchill",
|
||||
"quote": "A pessimist sees the difficulty in every opportunity; an optimist sees the opportunity in every difficulty."
|
||||
},
|
||||
{
|
||||
"author": "Will Rogers",
|
||||
"quote": "Don't let yesterday take up too much of today."
|
||||
},
|
||||
{
|
||||
"author": "Vince Lombardi",
|
||||
"quote": "It's not whether you get knocked down, it's whether you get up."
|
||||
},
|
||||
{
|
||||
"author": "Steve Jobs",
|
||||
"quote": "If you are working on something that you really care about, you don’t have to be pushed. The vision pulls you."
|
||||
}
|
||||
]
|
||||
168
src/features/widgets/quote/quote.scss
Normal file
168
src/features/widgets/quote/quote.scss
Normal file
@@ -0,0 +1,168 @@
|
||||
@import 'scss/variables';
|
||||
|
||||
.quote {
|
||||
font-size: 0.8em;
|
||||
text-shadow: 0 0 10px rgb(0 0 0 / 30%);
|
||||
cursor: initial;
|
||||
user-select: none;
|
||||
|
||||
--shadow-shift: 0.125rem;
|
||||
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
width: 40vw;
|
||||
}
|
||||
|
||||
.quoteauthor {
|
||||
font-size: 0.9em;
|
||||
letter-spacing: 0.5px;
|
||||
user-select: none;
|
||||
|
||||
--shadow-shift: 0.125rem;
|
||||
|
||||
svg {
|
||||
margin-left: 10px;
|
||||
filter: drop-shadow(0 0 6px rgb(0 0 0 / 30%));
|
||||
}
|
||||
}
|
||||
|
||||
i.material-icons,
|
||||
h1.quoteauthor {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.quoteAuthorLink {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.author {
|
||||
@extend %basic;
|
||||
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
height: 70px;
|
||||
font-weight: 300;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.author-name {
|
||||
font-size: clamp(15px, 2.5vw, 0.6em);
|
||||
}
|
||||
|
||||
.author-knownfor {
|
||||
font-size: clamp(13px, 2.5vw, 0.4em);
|
||||
|
||||
@include themed {
|
||||
color: t($subColor);
|
||||
}
|
||||
}
|
||||
|
||||
.author-license {
|
||||
font-size: clamp(8px, 2.5vw, 0.1em);
|
||||
|
||||
@include themed {
|
||||
color: t($subColor);
|
||||
}
|
||||
}
|
||||
|
||||
.author img {
|
||||
height: 100%;
|
||||
width: auto;
|
||||
border-radius: 12px 0 0 12px;
|
||||
}
|
||||
|
||||
.author-img {
|
||||
@extend %basic;
|
||||
|
||||
height: 100%;
|
||||
width: 70px;
|
||||
border-radius: 12px 0 0 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-size: cover !important;
|
||||
background-repeat: no-repeat !important;
|
||||
background-position: initial;
|
||||
}
|
||||
|
||||
.author-content {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
padding: 20px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.author-holder {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-flow: column;
|
||||
animation: fadeIn 1s;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.quote-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 20px 20px 20px 0;
|
||||
|
||||
a {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
a {
|
||||
@include basicIconButton(11px, 1.3rem, ui);
|
||||
}
|
||||
}
|
||||
|
||||
.quotediv {
|
||||
animation: fadeIn 1s;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
|
||||
button {
|
||||
@include basicIconButton(11px, 1.3rem, ui);
|
||||
}
|
||||
}
|
||||
|
||||
.deleteButton {
|
||||
height: auto !important;
|
||||
|
||||
@include basicIconButton(11px, 1.3rem, modal);
|
||||
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
.author-content.whileLoading {
|
||||
@include themed {
|
||||
gap: 5px;
|
||||
|
||||
.title {
|
||||
color: transparent;
|
||||
width: 100px;
|
||||
background: t($modal-sidebar);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: transparent;
|
||||
width: 50px;
|
||||
background: t($modal-sidebarActive);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user