mirror of
https://github.com/mue/mue.git
synced 2026-06-06 07:55:48 +02:00
feat: enhance accessibility and styling for form components including Checkbox, Dropdown, Radio, Slider, and Text
This commit is contained in:
@@ -33,15 +33,16 @@ const Checkbox = memo((props) => {
|
||||
EventBus.emit('refresh', props.category);
|
||||
}, [checked, props]);
|
||||
|
||||
const handleKeyDown = useCallback((e) => {
|
||||
if ((e.key === ' ' || e.key === 'Enter') && !props.disabled) {
|
||||
e.preventDefault();
|
||||
handleChange();
|
||||
}
|
||||
}, [handleChange, props.disabled]);
|
||||
|
||||
return (
|
||||
<div className={`checkbox-wrapper ${props.disabled ? 'disabled' : ''}`}>
|
||||
<span className="checkbox-label">{props.text}</span>
|
||||
<div
|
||||
className={`checkbox-box ${checked ? 'checked' : ''}`}
|
||||
onClick={props.disabled ? undefined : handleChange}
|
||||
>
|
||||
{checked && <MdCheck />}
|
||||
</div>
|
||||
<input
|
||||
type="checkbox"
|
||||
name={props.name}
|
||||
@@ -49,8 +50,12 @@ const Checkbox = memo((props) => {
|
||||
onChange={handleChange}
|
||||
disabled={props.disabled || false}
|
||||
className="checkbox-input"
|
||||
aria-hidden="true"
|
||||
aria-label={props.text}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
<div className={`checkbox-box ${checked ? 'checked' : ''}`}>
|
||||
{checked && <MdCheck />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,24 @@
|
||||
@use 'scss/variables' as *;
|
||||
@use 'scss/mixins' as *;
|
||||
|
||||
@include keyframes(checkScale) {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox-wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
@@ -13,8 +31,16 @@
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
&:hover:not(.disabled) .checkbox-label {
|
||||
@include themed {
|
||||
color: t($link);
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
flex: 1;
|
||||
transition: color 0.2s ease;
|
||||
pointer-events: none;
|
||||
|
||||
@include themed {
|
||||
color: t($color);
|
||||
@@ -28,17 +54,27 @@
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 6px;
|
||||
transition: 0.2s ease;
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
pointer-events: none;
|
||||
|
||||
@include themed {
|
||||
border: 2px solid t($modal-sidebarActive);
|
||||
background: t($modal-sidebar);
|
||||
color: t($color);
|
||||
|
||||
&:hover {
|
||||
&:hover:not(.disabled) {
|
||||
border-color: t($color);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
&:active:not(.disabled) {
|
||||
transform: scale(0.95);
|
||||
|
||||
@include themed {
|
||||
box-shadow: 0 0 0 4px rgba(255, 92, 37, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +83,10 @@
|
||||
background: t($link);
|
||||
border-color: t($link);
|
||||
}
|
||||
|
||||
svg {
|
||||
@include animation(checkScale 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55));
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
@@ -57,7 +97,22 @@
|
||||
|
||||
.checkbox-input {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
|
||||
&:focus-visible + .checkbox-box {
|
||||
@include themed {
|
||||
box-shadow: 0 0 0 3px t($link);
|
||||
}
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import variables from 'config/variables';
|
||||
import { memo, useState, useCallback, useRef, useEffect } from 'react';
|
||||
import { MdExpandMore } from 'react-icons/md';
|
||||
import { MdExpandMore, MdCheck } from 'react-icons/md';
|
||||
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
@@ -11,12 +11,15 @@ const Dropdown = memo((props) => {
|
||||
localStorage.getItem(props.name) || props.items[0]?.value,
|
||||
);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [focusedIndex, setFocusedIndex] = useState(-1);
|
||||
const containerRef = useRef(null);
|
||||
const optionsRef = useRef([]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event) => {
|
||||
if (containerRef.current && !containerRef.current.contains(event.target)) {
|
||||
setIsOpen(false);
|
||||
setFocusedIndex(-1);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -34,6 +37,7 @@ const Dropdown = memo((props) => {
|
||||
|
||||
setValue(newValue);
|
||||
setIsOpen(false);
|
||||
setFocusedIndex(-1);
|
||||
|
||||
if (!props.noSetting) {
|
||||
localStorage.setItem(props.name, newValue);
|
||||
@@ -56,27 +60,85 @@ const Dropdown = memo((props) => {
|
||||
[value, props],
|
||||
);
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(e) => {
|
||||
if (props.disabled) return;
|
||||
|
||||
switch (e.key) {
|
||||
case 'Enter':
|
||||
case ' ':
|
||||
e.preventDefault();
|
||||
setIsOpen(!isOpen);
|
||||
break;
|
||||
case 'Escape':
|
||||
setIsOpen(false);
|
||||
setFocusedIndex(-1);
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
if (!isOpen) {
|
||||
setIsOpen(true);
|
||||
} else {
|
||||
setFocusedIndex((prev) => (prev < props.items.filter((i) => i !== null).length - 1 ? prev + 1 : prev));
|
||||
}
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
if (isOpen) {
|
||||
setFocusedIndex((prev) => (prev > 0 ? prev - 1 : prev));
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
[isOpen, props.items, props.disabled],
|
||||
);
|
||||
|
||||
const handleOptionKeyDown = useCallback(
|
||||
(e, item) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
onChange(item.value);
|
||||
}
|
||||
},
|
||||
[onChange],
|
||||
);
|
||||
|
||||
const id = 'dropdown' + props.name;
|
||||
const label = props.label || '';
|
||||
const selectedItem = props.items.find((item) => item?.value === value);
|
||||
|
||||
return (
|
||||
<div className={`dropdown ${id}`} ref={containerRef}>
|
||||
<div className={`dropdown ${id} ${props.disabled ? 'disabled' : ''}`} ref={containerRef}>
|
||||
{label && <label className="dropdown-label">{label}</label>}
|
||||
<div className="dropdown-control" onClick={() => setIsOpen(!isOpen)}>
|
||||
<div
|
||||
className="dropdown-control"
|
||||
onClick={() => !props.disabled && setIsOpen(!isOpen)}
|
||||
onKeyDown={handleKeyDown}
|
||||
role="button"
|
||||
aria-haspopup="listbox"
|
||||
aria-expanded={isOpen}
|
||||
aria-label={label || props.name}
|
||||
tabIndex={props.disabled ? -1 : 0}
|
||||
>
|
||||
<span className="dropdown-value">{selectedItem?.text || value}</span>
|
||||
<MdExpandMore className={`dropdown-arrow ${isOpen ? 'open' : ''}`} />
|
||||
</div>
|
||||
{isOpen && (
|
||||
<div className="dropdown-menu">
|
||||
{props.items.map((item) =>
|
||||
<div className="dropdown-menu" role="listbox">
|
||||
{props.items.map((item, index) =>
|
||||
item !== null ? (
|
||||
<div
|
||||
key={id + item.value}
|
||||
className={`dropdown-option ${value === item.value ? 'selected' : ''}`}
|
||||
ref={(el) => (optionsRef.current[index] = el)}
|
||||
className={`dropdown-option ${value === item.value ? 'selected' : ''} ${index === focusedIndex ? 'focused' : ''}`}
|
||||
onClick={() => onChange(item.value)}
|
||||
onKeyDown={(e) => handleOptionKeyDown(e, item)}
|
||||
role="option"
|
||||
aria-selected={value === item.value}
|
||||
tabIndex={0}
|
||||
>
|
||||
{item.text}
|
||||
<span className="dropdown-option-text">{item.text}</span>
|
||||
{value === item.value && <MdCheck className="dropdown-option-check" />}
|
||||
</div>
|
||||
) : null,
|
||||
)}
|
||||
|
||||
@@ -1,10 +1,29 @@
|
||||
@use 'scss/variables' as *;
|
||||
@use 'scss/mixins' as *;
|
||||
|
||||
@include keyframes(dropdownSlideIn) {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
position: relative;
|
||||
width: 300px;
|
||||
margin-top: 10px;
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.dropdown-label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
@@ -25,7 +44,8 @@
|
||||
height: 56px;
|
||||
padding: 0 16px;
|
||||
cursor: pointer;
|
||||
transition: 0.2s ease;
|
||||
transition: all 0.2s ease;
|
||||
outline: none;
|
||||
|
||||
@include themed {
|
||||
background: t($modal-sidebar);
|
||||
@@ -37,6 +57,15 @@
|
||||
border-color: t($color);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
|
||||
@include themed {
|
||||
border-color: t($link);
|
||||
box-shadow: 0 0 0 3px t($link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-value {
|
||||
@@ -44,6 +73,7 @@
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.dropdown-arrow {
|
||||
@@ -68,6 +98,7 @@
|
||||
max-height: 250px;
|
||||
overflow-y: auto;
|
||||
z-index: 100;
|
||||
@include animation(dropdownSlideIn 0.2s ease-out);
|
||||
|
||||
@include themed {
|
||||
background: t($modal-background);
|
||||
@@ -75,24 +106,81 @@
|
||||
border-radius: t($borderRadius);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
@include themed {
|
||||
background: t($modal-sidebar);
|
||||
}
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
@include themed {
|
||||
background: t($modal-sidebarActive);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@include themed {
|
||||
background: t($color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
padding: 12px 16px;
|
||||
cursor: pointer;
|
||||
transition: 0.2s ease;
|
||||
transition: all 0.15s ease;
|
||||
outline: none;
|
||||
|
||||
@include themed {
|
||||
color: t($color);
|
||||
|
||||
&:hover {
|
||||
background: t($modal-sidebarActive);
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background: t($modal-sidebar);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&.focused {
|
||||
background: t($modal-sidebarActive);
|
||||
border-left: 2px solid t($link);
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-option-text {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dropdown-option-check {
|
||||
flex-shrink: 0;
|
||||
font-size: 14px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
@include themed {
|
||||
background: t($link);
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,21 +62,23 @@ const Radio = memo((props) => {
|
||||
{props.options.map((option) => (
|
||||
<label
|
||||
key={option.value}
|
||||
className={`radio-option ${value === option.value ? 'selected' : ''}`}
|
||||
onClick={() => handleChange(option.value)}
|
||||
className={`radio-option ${value === option.value ? 'selected' : ''} ${option.disabled || props.disabled ? 'disabled' : ''}`}
|
||||
>
|
||||
<span className="radio-label">{option.name}</span>
|
||||
<div className="radio-circle">
|
||||
{value === option.value && <div className="radio-dot" />}
|
||||
</div>
|
||||
<input
|
||||
type="radio"
|
||||
name={props.name}
|
||||
value={option.value}
|
||||
checked={value === option.value}
|
||||
onChange={() => handleChange(option.value)}
|
||||
disabled={option.disabled || props.disabled || false}
|
||||
className="radio-input"
|
||||
aria-label={option.name}
|
||||
tabIndex={0}
|
||||
/>
|
||||
<div className="radio-circle">
|
||||
{value === option.value && <div className="radio-dot" />}
|
||||
</div>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,21 @@
|
||||
@use 'scss/variables' as *;
|
||||
@use 'scss/mixins' as *;
|
||||
|
||||
@include keyframes(radioDotScale) {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.radio-group {
|
||||
width: 100%;
|
||||
@@ -33,33 +50,46 @@
|
||||
}
|
||||
|
||||
.radio-option {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
cursor: pointer;
|
||||
padding: 16px 20px;
|
||||
transition: 0.2s ease;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
@include themed {
|
||||
background: t($modal-sidebar);
|
||||
border-radius: t($borderRadius);
|
||||
box-shadow: 0 0 0 1px t($modal-sidebarActive);
|
||||
|
||||
&:hover {
|
||||
&:hover:not(.disabled) {
|
||||
background: t($modal-secondaryColour);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
}
|
||||
|
||||
&:active:not(.disabled) {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
&.selected .radio-circle {
|
||||
@include themed {
|
||||
border-color: t($link);
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.radio-label {
|
||||
flex: 1;
|
||||
font-size: 15px;
|
||||
pointer-events: none;
|
||||
|
||||
@include themed {
|
||||
color: t($color);
|
||||
@@ -73,21 +103,33 @@
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: 50%;
|
||||
transition: 0.2s ease;
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
margin-left: 20px;
|
||||
pointer-events: none;
|
||||
|
||||
@include themed {
|
||||
border: 2px solid t($modal-sidebarActive);
|
||||
background: t($modal-secondaryColour);
|
||||
}
|
||||
|
||||
&:hover:not(.disabled) {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
&:active:not(.disabled) {
|
||||
@include themed {
|
||||
box-shadow: 0 0 0 4px rgba(255, 92, 37, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.radio-dot {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
@include animation(radioDotScale 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55));
|
||||
|
||||
@include themed {
|
||||
background: t($link);
|
||||
@@ -96,7 +138,22 @@
|
||||
|
||||
.radio-input {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
|
||||
&:focus-visible + .radio-circle {
|
||||
@include themed {
|
||||
box-shadow: 0 0 0 3px t($link);
|
||||
}
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,6 +99,11 @@ const SliderComponent = memo((props) => {
|
||||
max={Number(props.max)}
|
||||
step={Number(props.step) || 1}
|
||||
style={{ '--slider-percentage': `${percentage}%` }}
|
||||
aria-label={props.title}
|
||||
aria-valuemin={Number(props.min)}
|
||||
aria-valuemax={Number(props.max)}
|
||||
aria-valuenow={Number(value)}
|
||||
disabled={props.disabled || false}
|
||||
/>
|
||||
{props.marks && props.marks.length > 0 && (
|
||||
<div className="slider-marks">
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
transition: 0.2s ease;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
|
||||
|
||||
@include themed {
|
||||
@@ -82,6 +82,7 @@
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +91,7 @@
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
transition: 0.2s ease;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
|
||||
border: none;
|
||||
|
||||
@@ -101,6 +102,52 @@
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
|
||||
&::-webkit-slider-thumb {
|
||||
@include themed {
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2), 0 0 0 3px t($link);
|
||||
}
|
||||
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
&::-moz-range-thumb {
|
||||
@include themed {
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2), 0 0 0 3px t($link);
|
||||
}
|
||||
|
||||
transform: scale(1.15);
|
||||
}
|
||||
}
|
||||
|
||||
&:active:not(:disabled) {
|
||||
&::-webkit-slider-thumb {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
&::-moz-range-thumb {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
|
||||
&::-webkit-slider-thumb {
|
||||
cursor: not-allowed;
|
||||
transform: scale(1) !important;
|
||||
}
|
||||
|
||||
&::-moz-range-thumb {
|
||||
cursor: not-allowed;
|
||||
transform: scale(1) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,15 @@ const Text = memo((props) => {
|
||||
<div className="text-field-container">
|
||||
{textarea === true ? (
|
||||
<div className={`text-field ${customcss ? 'customcss' : ''}`}>
|
||||
{title && <label className="text-field-label">{title}</label>}
|
||||
{title && (
|
||||
<div className="text-field-header">
|
||||
<label className="text-field-label">{title}</label>
|
||||
<span className="text-field-reset" onClick={resetItem}>
|
||||
<MdRefresh />
|
||||
{variables.getMessage('modals.main.settings.buttons.reset')}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<textarea
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
@@ -63,7 +71,15 @@ const Text = memo((props) => {
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-field">
|
||||
{title && <label className="text-field-label">{title}</label>}
|
||||
{title && (
|
||||
<div className="text-field-header">
|
||||
<label className="text-field-label">{title}</label>
|
||||
<span className="text-field-reset" onClick={resetItem}>
|
||||
<MdRefresh />
|
||||
{variables.getMessage('modals.main.settings.buttons.reset')}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<input
|
||||
type="text"
|
||||
value={value}
|
||||
@@ -73,10 +89,6 @@ const Text = memo((props) => {
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<span className="link" onClick={resetItem}>
|
||||
<MdRefresh />
|
||||
{variables.getMessage('modals.main.settings.buttons.reset')}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -3,17 +3,42 @@
|
||||
.text-field-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
width: 300px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.link {
|
||||
.text-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.text-field-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.text-field-label {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
|
||||
@include themed {
|
||||
color: t($subColor);
|
||||
}
|
||||
}
|
||||
|
||||
.text-field-reset {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
width: fit-content;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
|
||||
@include themed {
|
||||
color: t($link);
|
||||
@@ -24,24 +49,7 @@
|
||||
}
|
||||
|
||||
svg {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.text-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.text-field-label {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
|
||||
@include themed {
|
||||
color: t($subColor);
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,41 +1,98 @@
|
||||
@use 'variables' as *;
|
||||
@use 'mixins' as *;
|
||||
|
||||
.Toastify__toast-body {
|
||||
font-size: 16px !important;
|
||||
padding: 15px 20px !important;
|
||||
}
|
||||
|
||||
.Toastify__toast {
|
||||
border-radius: 12px !important;
|
||||
height: auto !important;
|
||||
width: auto !important;
|
||||
min-width: auto !important;
|
||||
padding: 10px !important;
|
||||
}
|
||||
|
||||
.Toastify__close-button {
|
||||
align-self: center !important;
|
||||
color: var(--modal-text) !important;
|
||||
}
|
||||
|
||||
/* .Toastify__progress-bar--default {
|
||||
height: 3px !important;
|
||||
background: var(--modal-text) !important;
|
||||
color: var(--modal-text) !important;
|
||||
} */
|
||||
|
||||
// Toast container
|
||||
.Toastify__toast-container {
|
||||
width: auto !important;
|
||||
padding: 16px !important;
|
||||
}
|
||||
|
||||
.Toastify__toast--default {
|
||||
@extend %basic;
|
||||
// Main toast styling with glassmorphism effect
|
||||
.Toastify__toast {
|
||||
@include themed() {
|
||||
background: t($background) !important;
|
||||
color: t($color) !important;
|
||||
box-shadow: t($boxShadow), 0 8px 32px rgb(0 0 0 / 20%) !important;
|
||||
}
|
||||
|
||||
border-radius: $borderRadius !important;
|
||||
-webkit-backdrop-filter: blur(15px) saturate(180%) !important;
|
||||
backdrop-filter: blur(15px) saturate(180%) !important;
|
||||
padding: 16px 20px !important;
|
||||
min-height: 64px !important;
|
||||
width: auto !important;
|
||||
min-width: 300px !important;
|
||||
max-width: 500px !important;
|
||||
font-family: inherit !important;
|
||||
box-sizing: border-box !important;
|
||||
transition: all 0.3s ease !important;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px) !important;
|
||||
box-shadow: 0 0 0 1px #484848, 0 12px 40px rgb(0 0 0 / 30%) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.Toastify__toast--info {
|
||||
@extend %basic;
|
||||
// Toast body
|
||||
.Toastify__toast-body {
|
||||
padding: 0 !important;
|
||||
font-size: 15px !important;
|
||||
line-height: 1.5 !important;
|
||||
margin: 0 !important;
|
||||
|
||||
@include themed() {
|
||||
color: t($color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.Toastify__progress-bar--info {
|
||||
background-color: gold !important;
|
||||
// Close button
|
||||
.Toastify__close-button {
|
||||
@include themed() {
|
||||
color: t($color) !important;
|
||||
opacity: 0.6 !important;
|
||||
}
|
||||
|
||||
align-self: center !important;
|
||||
transition: all 0.2s ease !important;
|
||||
|
||||
&:hover {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Progress bar base styling
|
||||
.Toastify__progress-bar {
|
||||
height: 4px !important;
|
||||
border-radius: 0 0 $borderRadius $borderRadius !important;
|
||||
|
||||
&--default,
|
||||
&--info {
|
||||
@include themed() {
|
||||
background: linear-gradient(90deg, t($link) 0%, #dd3b67 100%) !important;
|
||||
}
|
||||
}
|
||||
|
||||
&--success {
|
||||
background: linear-gradient(90deg, rgb(46 213 115) 0%, rgb(26 188 156) 100%) !important;
|
||||
}
|
||||
|
||||
&--warning {
|
||||
background: linear-gradient(90deg, #ffb032 0%, #ff9500 100%) !important;
|
||||
}
|
||||
|
||||
&--error {
|
||||
background: linear-gradient(90deg, rgb(255 71 87) 0%, #dd3b67 100%) !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Toast type variants - all use the base glassmorphism style
|
||||
.Toastify__toast--default,
|
||||
.Toastify__toast--info,
|
||||
.Toastify__toast--success,
|
||||
.Toastify__toast--warning,
|
||||
.Toastify__toast--error {
|
||||
@include themed() {
|
||||
background: t($background) !important;
|
||||
color: t($color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user