mirror of
https://github.com/Novattz/creamlinux-installer.git
synced 2026-01-24 20:32:51 -05:00
Revert "Broken animation changes but ill fix it"
This reverts commit e29f44bbd5.
This commit is contained in:
52
src/App.tsx
52
src/App.tsx
@@ -137,29 +137,31 @@ function App() {
|
|||||||
instructions?: InstructionInfo
|
instructions?: InstructionInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always update progress dialog
|
if (complete && !show_instructions) {
|
||||||
|
// Hide dialog when complete if no instructions
|
||||||
|
setTimeout(() => {
|
||||||
|
setProgressDialog((prev) => ({ ...prev, visible: false }))
|
||||||
|
|
||||||
|
// Only refresh games list if dialog is closing without instructions
|
||||||
|
if (!refreshInProgress.current) {
|
||||||
|
refreshInProgress.current = true
|
||||||
|
setTimeout(() => {
|
||||||
|
loadGames().then(() => {
|
||||||
|
refreshInProgress.current = false
|
||||||
|
})
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
} else {
|
||||||
|
// Update progress dialog
|
||||||
setProgressDialog({
|
setProgressDialog({
|
||||||
visible: true, // Always set to visible - our ProgressDialog component handles exit animation
|
visible: true,
|
||||||
title,
|
title,
|
||||||
message,
|
message,
|
||||||
progress,
|
progress,
|
||||||
showInstructions: show_instructions || false,
|
showInstructions: show_instructions || false,
|
||||||
instructions,
|
instructions,
|
||||||
})
|
})
|
||||||
|
|
||||||
// If complete and no instructions, we need to schedule a game list refresh
|
|
||||||
// The dialog will auto-close with animation, but we still need to refresh the games
|
|
||||||
if (complete && !show_instructions) {
|
|
||||||
// Schedule a refresh for after the dialog closes
|
|
||||||
if (!refreshInProgress.current) {
|
|
||||||
refreshInProgress.current = true
|
|
||||||
// Wait for dialog animation + close delay (about 1.2s total)
|
|
||||||
setTimeout(() => {
|
|
||||||
loadGames().then(() => {
|
|
||||||
refreshInProgress.current = false
|
|
||||||
})
|
|
||||||
}, 1300)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -377,7 +379,7 @@ function App() {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const handleCloseProgressDialog = () => {
|
const handleCloseProgressDialog = () => {
|
||||||
// Set dialog to not visible - animation is handled by the component
|
// Just hide the dialog without refreshing game list
|
||||||
setProgressDialog((prev) => ({ ...prev, visible: false }))
|
setProgressDialog((prev) => ({ ...prev, visible: false }))
|
||||||
|
|
||||||
// Only refresh if we need to (instructions didn't trigger update)
|
// Only refresh if we need to (instructions didn't trigger update)
|
||||||
@@ -665,14 +667,13 @@ function App() {
|
|||||||
dlcFetchController.current = null
|
dlcFetchController.current = null
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close dialog - animation is handled in DlcSelectionDialog
|
// Close dialog
|
||||||
setDlcDialog((prev) => ({ ...prev, visible: false }))
|
setDlcDialog((prev) => ({ ...prev, visible: false }))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle DLC selection confirmation
|
// Handle DLC selection confirmation
|
||||||
const handleDlcConfirm = async (selectedDlcs: DlcInfo[]) => {
|
const handleDlcConfirm = async (selectedDlcs: DlcInfo[]) => {
|
||||||
// The dialog has already started its exit animation
|
// Close the dialog first
|
||||||
// Just make sure it's marked as invisible
|
|
||||||
setDlcDialog((prev) => ({ ...prev, visible: false }))
|
setDlcDialog((prev) => ({ ...prev, visible: false }))
|
||||||
|
|
||||||
const gameId = dlcDialog.gameId
|
const gameId = dlcDialog.gameId
|
||||||
@@ -711,11 +712,9 @@ function App() {
|
|||||||
progress: 100,
|
progress: 100,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// The ProgressDialog component will now handle the exit animation
|
// Hide dialog after a delay
|
||||||
// when progress reaches 100% after a delay
|
|
||||||
|
|
||||||
// But we still need to reset installing state with a delay
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
setProgressDialog((prev) => ({ ...prev, visible: false }))
|
||||||
// Reset installing state
|
// Reset installing state
|
||||||
setGames((prevGames) =>
|
setGames((prevGames) =>
|
||||||
prevGames.map((g) => (g.id === gameId ? { ...g, installing: false } : g))
|
prevGames.map((g) => (g.id === gameId ? { ...g, installing: false } : g))
|
||||||
@@ -760,7 +759,10 @@ function App() {
|
|||||||
prevGames.map((g) => (g.id === gameId ? { ...g, installing: false } : g))
|
prevGames.map((g) => (g.id === gameId ? { ...g, installing: false } : g))
|
||||||
)
|
)
|
||||||
|
|
||||||
// ProgressDialog will handle exit animation automatically
|
// Hide dialog after a delay
|
||||||
|
setTimeout(() => {
|
||||||
|
setProgressDialog((prev) => ({ ...prev, visible: false }))
|
||||||
|
}, 3000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,13 +31,10 @@ const DlcSelectionDialog: React.FC<DlcSelectionDialogProps> = ({
|
|||||||
estimatedTimeLeft = '',
|
estimatedTimeLeft = '',
|
||||||
}) => {
|
}) => {
|
||||||
const [selectedDlcs, setSelectedDlcs] = useState<DlcInfo[]>([])
|
const [selectedDlcs, setSelectedDlcs] = useState<DlcInfo[]>([])
|
||||||
// Use a simple string for animation state - keep it simple
|
const [showContent, setShowContent] = useState(false)
|
||||||
const [animationState, setAnimationState] = useState('closed')
|
|
||||||
const [searchQuery, setSearchQuery] = useState('')
|
const [searchQuery, setSearchQuery] = useState('')
|
||||||
const [selectAll, setSelectAll] = useState(true)
|
const [selectAll, setSelectAll] = useState(true)
|
||||||
const [initialized, setInitialized] = useState(false)
|
const [initialized, setInitialized] = useState(false)
|
||||||
// Track previous visibility to detect changes
|
|
||||||
const [prevVisible, setPrevVisible] = useState(false)
|
|
||||||
|
|
||||||
// Initialize selected DLCs when DLC list changes
|
// Initialize selected DLCs when DLC list changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -53,33 +50,19 @@ const DlcSelectionDialog: React.FC<DlcSelectionDialogProps> = ({
|
|||||||
}
|
}
|
||||||
}, [visible, dlcs, initialized])
|
}, [visible, dlcs, initialized])
|
||||||
|
|
||||||
// Handle animations on visibility changes
|
// Handle visibility changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Only respond to actual changes in visibility
|
|
||||||
if (visible !== prevVisible) {
|
|
||||||
if (visible) {
|
if (visible) {
|
||||||
// Show animation
|
// Show content immediately for better UX
|
||||||
setAnimationState('visible')
|
|
||||||
} else {
|
|
||||||
// Hide animation - but only if we're currently visible
|
|
||||||
if (animationState === 'visible') {
|
|
||||||
setAnimationState('hiding')
|
|
||||||
// After animation completes, set to closed
|
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
setAnimationState('closed')
|
setShowContent(true)
|
||||||
// Also reset initialization when fully closed
|
}, 50)
|
||||||
if (!visible) {
|
|
||||||
setInitialized(false)
|
|
||||||
}
|
|
||||||
}, 200) // Match animation duration
|
|
||||||
|
|
||||||
return () => clearTimeout(timer)
|
return () => clearTimeout(timer)
|
||||||
|
} else {
|
||||||
|
setShowContent(false)
|
||||||
|
setInitialized(false) // Reset initialized state when dialog closes
|
||||||
}
|
}
|
||||||
}
|
}, [visible])
|
||||||
// Update previous visibility
|
|
||||||
setPrevVisible(visible)
|
|
||||||
}
|
|
||||||
}, [visible, prevVisible, animationState])
|
|
||||||
|
|
||||||
// Memoize filtered DLCs to avoid unnecessary recalculations
|
// Memoize filtered DLCs to avoid unnecessary recalculations
|
||||||
const filteredDlcs = useMemo(() => {
|
const filteredDlcs = useMemo(() => {
|
||||||
@@ -132,11 +115,10 @@ const DlcSelectionDialog: React.FC<DlcSelectionDialogProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleConfirm = () => {
|
const handleConfirm = () => {
|
||||||
// Just call onConfirm directly
|
|
||||||
onConfirm(selectedDlcs)
|
onConfirm(selectedDlcs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle overlay click
|
// Modified to prevent closing when loading
|
||||||
const handleOverlayClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
const handleOverlayClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
// Prevent clicks from propagating through the overlay
|
// Prevent clicks from propagating through the overlay
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
@@ -147,11 +129,6 @@ const DlcSelectionDialog: React.FC<DlcSelectionDialogProps> = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle the close button click
|
|
||||||
const handleClose = () => {
|
|
||||||
onClose()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count selected DLCs
|
// Count selected DLCs
|
||||||
const selectedCount = selectedDlcs.filter((dlc) => dlc.enabled).length
|
const selectedCount = selectedDlcs.filter((dlc) => dlc.enabled).length
|
||||||
|
|
||||||
@@ -165,19 +142,14 @@ const DlcSelectionDialog: React.FC<DlcSelectionDialogProps> = ({
|
|||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't render anything if we're in closed state
|
if (!visible) return null
|
||||||
if (animationState === 'closed') return null
|
|
||||||
|
|
||||||
// Generate appropriate classes based on animation state
|
|
||||||
const dialogClasses = `dlc-dialog-overlay ${animationState === 'hiding' ? 'exiting' : 'visible'}`
|
|
||||||
const contentClasses = `dlc-selection-dialog dialog-${animationState === 'hiding' ? 'exiting' : 'visible'}`
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={dialogClasses}
|
className={`dlc-dialog-overlay ${showContent ? 'visible' : ''}`}
|
||||||
onClick={handleOverlayClick}
|
onClick={handleOverlayClick}
|
||||||
>
|
>
|
||||||
<div className={contentClasses}>
|
<div className={`dlc-selection-dialog ${showContent ? 'dialog-visible' : ''}`}>
|
||||||
<div className="dlc-dialog-header">
|
<div className="dlc-dialog-header">
|
||||||
<h3>{isEditMode ? 'Edit DLCs' : 'Select DLCs to Enable'}</h3>
|
<h3>{isEditMode ? 'Edit DLCs' : 'Select DLCs to Enable'}</h3>
|
||||||
<div className="dlc-game-info">
|
<div className="dlc-game-info">
|
||||||
@@ -250,7 +222,7 @@ const DlcSelectionDialog: React.FC<DlcSelectionDialogProps> = ({
|
|||||||
<div className="dlc-dialog-actions">
|
<div className="dlc-dialog-actions">
|
||||||
<button
|
<button
|
||||||
className="cancel-button"
|
className="cancel-button"
|
||||||
onClick={handleClose}
|
onClick={onClose}
|
||||||
disabled={isLoading && loadingProgress < 10} // Briefly disable to prevent accidental closing at start
|
disabled={isLoading && loadingProgress < 10} // Briefly disable to prevent accidental closing at start
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
|
|||||||
@@ -27,55 +27,23 @@ const ProgressDialog: React.FC<ProgressDialogProps> = ({
|
|||||||
onClose,
|
onClose,
|
||||||
}) => {
|
}) => {
|
||||||
const [copySuccess, setCopySuccess] = useState(false)
|
const [copySuccess, setCopySuccess] = useState(false)
|
||||||
// Use a simple string for animation state - keep it simple
|
const [showContent, setShowContent] = useState(false)
|
||||||
const [animationState, setAnimationState] = useState('closed')
|
|
||||||
// Track previous visibility to detect changes
|
|
||||||
const [prevVisible, setPrevVisible] = useState(false)
|
|
||||||
|
|
||||||
// Handle animations on visibility changes
|
// Reset copy state when dialog visibility changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Only respond to actual changes in visibility
|
if (!visible) {
|
||||||
if (visible !== prevVisible) {
|
setCopySuccess(false)
|
||||||
if (visible) {
|
setShowContent(false)
|
||||||
// Show animation
|
|
||||||
setAnimationState('visible')
|
|
||||||
} else {
|
} else {
|
||||||
// Hide animation - but only if we're currently visible
|
// Add a small delay to trigger the entrance animation
|
||||||
if (animationState === 'visible') {
|
|
||||||
setAnimationState('hiding')
|
|
||||||
// After animation completes, set to closed
|
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
setAnimationState('closed')
|
setShowContent(true)
|
||||||
}, 200) // Match animation duration
|
}, 50)
|
||||||
|
|
||||||
return () => clearTimeout(timer)
|
return () => clearTimeout(timer)
|
||||||
}
|
}
|
||||||
}
|
}, [visible])
|
||||||
// Update previous visibility
|
|
||||||
setPrevVisible(visible)
|
|
||||||
}
|
|
||||||
}, [visible, prevVisible, animationState])
|
|
||||||
|
|
||||||
// Auto-close on progress completion
|
if (!visible) return null
|
||||||
useEffect(() => {
|
|
||||||
// Only auto-close if showing and progress reaches 100% and not showing instructions
|
|
||||||
if (visible && progress >= 100 && !showInstructions && animationState === 'visible') {
|
|
||||||
// Wait a moment before closing
|
|
||||||
const timer = setTimeout(() => {
|
|
||||||
// Only proceed if we're still in visible state
|
|
||||||
if (animationState === 'visible' && onClose) {
|
|
||||||
onClose() // Call the onClose function directly
|
|
||||||
}
|
|
||||||
}, 1000) // Wait 1 second after completion
|
|
||||||
|
|
||||||
return () => clearTimeout(timer)
|
|
||||||
}
|
|
||||||
}, [progress, showInstructions, animationState, visible, onClose])
|
|
||||||
|
|
||||||
// Don't render if state is closed
|
|
||||||
if (animationState === 'closed') {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleCopyCommand = () => {
|
const handleCopyCommand = () => {
|
||||||
if (instructions?.command) {
|
if (instructions?.command) {
|
||||||
@@ -90,10 +58,13 @@ const ProgressDialog: React.FC<ProgressDialogProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
// If we can close, just call onClose directly
|
setShowContent(false)
|
||||||
|
// Delay closing to allow exit animation
|
||||||
|
setTimeout(() => {
|
||||||
if (onClose) {
|
if (onClose) {
|
||||||
onClose()
|
onClose()
|
||||||
}
|
}
|
||||||
|
}, 300)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent closing when in progress
|
// Prevent closing when in progress
|
||||||
@@ -175,17 +146,13 @@ const ProgressDialog: React.FC<ProgressDialogProps> = ({
|
|||||||
// Determine if close button should be enabled
|
// Determine if close button should be enabled
|
||||||
const isCloseButtonEnabled = showInstructions || progress >= 100
|
const isCloseButtonEnabled = showInstructions || progress >= 100
|
||||||
|
|
||||||
// Generate appropriate classes based on animation state
|
|
||||||
const dialogClasses = `progress-dialog-overlay ${animationState === 'hiding' ? 'exiting' : 'visible'}`
|
|
||||||
const contentClasses = `progress-dialog ${showInstructions ? 'with-instructions' : ''} dialog-${animationState === 'hiding' ? 'exiting' : 'visible'}`
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={dialogClasses}
|
className={`progress-dialog-overlay ${showContent ? 'visible' : ''}`}
|
||||||
onClick={handleOverlayClick}
|
onClick={handleOverlayClick}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={contentClasses}
|
className={`progress-dialog ${showInstructions ? 'with-instructions' : ''} ${showContent ? 'dialog-visible' : ''}`}
|
||||||
>
|
>
|
||||||
<h3>{title}</h3>
|
<h3>{title}</h3>
|
||||||
<p>{message}</p>
|
<p>{message}</p>
|
||||||
|
|||||||
@@ -1,29 +1,6 @@
|
|||||||
@use '../variables' as *;
|
@use '../variables' as *;
|
||||||
@use '../mixins' as *;
|
@use '../mixins' as *;
|
||||||
|
|
||||||
// Shared keyframes for both enter and exit animations
|
|
||||||
@keyframes modal-appear {
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
transform: scale(0.95);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes modal-disappear {
|
|
||||||
0% {
|
|
||||||
opacity: 1;
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 0;
|
|
||||||
transform: scale(0.95);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Progress Dialog
|
// Progress Dialog
|
||||||
.progress-dialog-overlay {
|
.progress-dialog-overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -36,19 +13,22 @@
|
|||||||
@include flex-center;
|
@include flex-center;
|
||||||
z-index: var(--z-modal);
|
z-index: var(--z-modal);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
animation: modal-appear 0.2s ease-out;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
pointer-events: auto; // Ensure clicks work
|
|
||||||
|
|
||||||
// When visible, fade in
|
|
||||||
&.visible {
|
&.visible {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
animation: modal-appear 0.2s ease-out forwards;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// When exiting, fade out
|
@keyframes modal-appear {
|
||||||
&.exiting {
|
0% {
|
||||||
animation: modal-disappear 0.2s ease-out forwards;
|
opacity: 0;
|
||||||
pointer-events: none; // Prevent clicks during exit animation
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,31 +36,32 @@
|
|||||||
background-color: var(--elevated-bg);
|
background-color: var(--elevated-bg);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.3);
|
box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.3); // shadow-glow
|
||||||
width: 450px;
|
width: 450px;
|
||||||
max-width: 90vw;
|
max-width: 90vw;
|
||||||
border: 1px solid var(--border-soft);
|
border: 1px solid var(--border-soft);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: scale(0.95);
|
|
||||||
cursor: default;
|
cursor: default;
|
||||||
|
|
||||||
// When visible, show the dialog
|
|
||||||
&.dialog-visible {
|
&.dialog-visible {
|
||||||
opacity: 1;
|
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
transition: transform 0.2s var(--easing-bounce), opacity 0.2s ease-out;
|
opacity: 1;
|
||||||
}
|
|
||||||
|
|
||||||
// When exiting, hide the dialog
|
|
||||||
&.dialog-exiting {
|
|
||||||
opacity: 0;
|
|
||||||
transform: scale(0.95);
|
|
||||||
transition: transform 0.2s ease-in, opacity 0.2s ease-in;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.with-instructions {
|
&.with-instructions {
|
||||||
width: 500px;
|
width: 500px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Progress bar
|
// Progress bar
|
||||||
|
|||||||
@@ -13,18 +13,10 @@
|
|||||||
z-index: var(--z-modal);
|
z-index: var(--z-modal);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
pointer-events: auto; // Ensure clicks work
|
|
||||||
|
|
||||||
// When visible, fade in
|
|
||||||
&.visible {
|
&.visible {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
animation: modal-appear 0.2s ease-out forwards;
|
animation: modal-appear 0.2s ease-out;
|
||||||
}
|
|
||||||
|
|
||||||
// When exiting, fade out
|
|
||||||
&.exiting {
|
|
||||||
animation: modal-disappear 0.2s ease-out forwards;
|
|
||||||
pointer-events: none; // Prevent clicks during exit animation
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,18 +34,12 @@
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: scale(0.95);
|
transform: scale(0.95);
|
||||||
|
|
||||||
// When visible, show the dialog
|
|
||||||
&.dialog-visible {
|
&.dialog-visible {
|
||||||
opacity: 1;
|
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
transition: transform 0.2s var(--easing-bounce), opacity 0.2s ease-out;
|
opacity: 1;
|
||||||
}
|
transition:
|
||||||
|
transform 0.2s var(--easing-bounce),
|
||||||
// When exiting, hide the dialog
|
opacity 0.2s ease-out;
|
||||||
&.dialog-exiting {
|
|
||||||
opacity: 0;
|
|
||||||
transform: scale(0.95);
|
|
||||||
transition: transform 0.2s ease-in, opacity 0.2s ease-in;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user