mirror of
https://github.com/mue/mue.git
synced 2026-07-16 21:44:22 +02:00
style(hero buttons): improve micro interactions of buttons with transitions
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
import variables from 'config/variables';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState, useEffect, useMemo } from 'react';
|
||||
import EventBus from 'utils/eventbus';
|
||||
import { Button } from 'components/Elements';
|
||||
import { MdFlag, MdOutlineVisibilityOff, MdOutlineVisibility } from 'react-icons/md';
|
||||
import Slider from '../../../Form/Settings/Slider/Slider';
|
||||
import values from 'utils/data/slider_values.json';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
|
||||
const Preview = (props) => {
|
||||
return (
|
||||
@@ -19,6 +20,62 @@ const Preview = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
const VisibilityToggleButton = ({ setting, onClick }) => {
|
||||
const iconVariants = {
|
||||
initial: { opacity: 0, scale: 0.8 },
|
||||
animate: { opacity: 1, scale: 1 },
|
||||
exit: { opacity: 0, scale: 0.8 },
|
||||
};
|
||||
|
||||
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="bg-modal-content-light hover:bg-[#e5e5e5] active:bg-[#d5d5d5]
|
||||
dark:bg-modal-content-dark dark:hover:bg-[#565656] dark:active:bg-[#464646]
|
||||
p-10 rounded flex flex-col gap-2
|
||||
transition-all ease-in-out duration-700
|
||||
focus:outline-none focus:ring-2 focus:ring-offset-2
|
||||
focus:ring-blue-600 dark:focus:ring-blue-400
|
||||
focus:ring-offset-white dark:focus:ring-offset-gray-900"
|
||||
onClick={onClick}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
onClick();
|
||||
}
|
||||
}}
|
||||
aria-checked={setting}
|
||||
aria-label={setting ? 'Hide content' : 'Show content'}
|
||||
role="switch"
|
||||
>
|
||||
<AnimatePresence mode="wait" initial={false}>
|
||||
<motion.span
|
||||
key={setting ? 'hide' : 'show'}
|
||||
variants={iconVariants}
|
||||
initial={prefersReducedMotion ? 'animate' : 'initial'}
|
||||
animate="animate"
|
||||
exit={prefersReducedMotion ? 'animate' : 'exit'}
|
||||
transition={{ duration: prefersReducedMotion ? 0 : 0.2 }}
|
||||
className="text-2xl text-gray-900 dark:text-gray-100"
|
||||
aria-hidden="true"
|
||||
>
|
||||
{setting ? <MdOutlineVisibilityOff /> : <MdOutlineVisibility />}
|
||||
</motion.span>
|
||||
</AnimatePresence>
|
||||
<motion.span
|
||||
initial={prefersReducedMotion ? { opacity: 1 } : { opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: prefersReducedMotion ? 0 : 0.2 }}
|
||||
className="text-base text-gray-900 dark:text-gray-100"
|
||||
>
|
||||
{setting ? 'Hide' : 'Show'}
|
||||
</motion.span>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
const Controls = (props) => {
|
||||
const [setting, setSetting] = useState(localStorage.getItem(props.setting) === 'true');
|
||||
|
||||
@@ -45,36 +102,45 @@ const Controls = (props) => {
|
||||
EventBus.emit('refresh', props.category);
|
||||
};
|
||||
|
||||
const VisibilityToggle = () => (
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={changeSetting}
|
||||
icon={setting ? <MdOutlineVisibilityOff /> : <MdOutlineVisibility />}
|
||||
label={setting ? 'Hide' : 'Show'}
|
||||
/>
|
||||
);
|
||||
|
||||
const ReportButton = () => {
|
||||
return (
|
||||
<Button
|
||||
type="settings"
|
||||
<button
|
||||
type="button"
|
||||
className="bg-modal-content-light hover:bg-[#e5e5e5] active:bg-[#d5d5d5]
|
||||
dark:bg-modal-content-dark dark:hover:bg-[#565656] dark:active:bg-[#464646]
|
||||
p-10 rounded flex flex-col gap-2
|
||||
transition-all ease-in-out duration-700
|
||||
focus:outline-none focus:ring-2 focus:ring-offset-2
|
||||
focus:ring-blue-600 dark:focus:ring-blue-400
|
||||
focus:ring-offset-white dark:focus:ring-offset-gray-900"
|
||||
onClick={() =>
|
||||
window.open(variables.constants.BUG_REPORT + props.title.split(' ').join('+'), '_blank')
|
||||
}
|
||||
icon={<MdFlag />}
|
||||
label={variables.getMessage('settings:sections.header.report_issue')}
|
||||
/>
|
||||
aria-label={`Report an issue with ${props.title}`}
|
||||
>
|
||||
<span className="text-2xl" aria-hidden="true">
|
||||
<MdFlag />
|
||||
</span>
|
||||
<span className="text-base capitalize">
|
||||
{variables.getMessage('settings:sections.header.report_issue')}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
const memoizedToggle = useMemo(
|
||||
() => <VisibilityToggleButton setting={setting} onClick={changeSetting} />,
|
||||
[setting],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
<h1 className="py-3 uppercase tracking-tight text-neutral-300">Controls</h1>
|
||||
<div className="grid grid-cols-2 gap-5 mb-5">
|
||||
{props.visibilityToggle && memoizedToggle}
|
||||
{props.report !== false && <ReportButton />}
|
||||
</div>
|
||||
<div className="bg-modal-content-light dark:bg-modal-content-dark p-10 rounded flex flex-col gap-10 flex-grow">
|
||||
<div className="grid grid-cols-2 gap-5">
|
||||
{props.visibilityToggle && <VisibilityToggle />}
|
||||
{props.report !== false && <ReportButton />}
|
||||
</div>
|
||||
{(props.zoomSetting !== null || props.zoomSetting !== '') && (
|
||||
<Slider
|
||||
name={props.zoomSetting}
|
||||
|
||||
@@ -213,3 +213,12 @@ body {
|
||||
.modal-clamp-width {
|
||||
width: clamp(60vw, 1400px, 90vw);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user