Publish #1
+25
-7
@@ -8,25 +8,43 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="uploadPage">
|
<div id="uploadPage">
|
||||||
<h1>Аналог DropMeFiles</h1>
|
<div id="errorPopup" class="error-popup" style="display: none;"></div>
|
||||||
<div class="upload-container">
|
<div class="page-content">
|
||||||
|
<h1>Аналог DropMeFiles</h1>
|
||||||
|
<div class="upload-container">
|
||||||
<form id="uploadForm">
|
<form id="uploadForm">
|
||||||
<label class="dropzone" id="dropzone">
|
<label class="dropzone" id="dropzone">
|
||||||
<input id="fileInput" type="file">
|
<input id="fileInput" type="file">
|
||||||
<p class="strong">Перетащите файл сюда или нажмите, чтобы выбрать</p>
|
<p class="strong">Перетащите файл сюда или нажмите, чтобы выбрать</p>
|
||||||
<p>Максимальный размер файла: <span id="maxFileSize">загрузка...</span></p>
|
<p>Максимальный размер файла: <span id="maxFileSize">загрузка...</span></p>
|
||||||
</label>
|
</label>
|
||||||
<button type="submit" id="uploadButton" disabled>Загрузить</button>
|
<div class="button-container">
|
||||||
|
<button type="submit" id="uploadButton" disabled>Загрузить</button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
<div id="successContent" class="success-content" style="display: none;">
|
||||||
|
<div class="file-info">
|
||||||
|
<h2 id="successFileName"></h2>
|
||||||
|
<p class="file-size" id="successFileSize"></p>
|
||||||
|
<div class="download-link-container">
|
||||||
|
<input type="text" id="downloadLinkInput" readonly class="download-link-input">
|
||||||
|
<button type="button" id="copyLinkButton" class="copy-link-button">Копировать</button>
|
||||||
|
</div>
|
||||||
|
<button type="button" id="uploadAnotherButton" class="upload-another-button">Загрузить другой файл</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="status" id="status"></div>
|
<div class="status" id="status"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="downloadPage" style="display: none;">
|
<div id="downloadPage" style="display: none;">
|
||||||
<h1>Скачать файл</h1>
|
<div class="page-content">
|
||||||
<div class="upload-container">
|
<h1>Скачать файл</h1>
|
||||||
<div id="downloadContent">
|
<div class="upload-container">
|
||||||
<div class="status" id="downloadStatus">Загрузка...</div>
|
<div id="downloadContent">
|
||||||
|
<div class="status" id="downloadStatus">Загрузка...</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+107
-11
@@ -34,10 +34,24 @@ async function handleDownloadPage(fileUuid) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/get_download_link/${fileUuid}`);
|
const response = await fetch(`/api/get_download_link/${fileUuid}`);
|
||||||
|
|
||||||
|
// Check for network/connection errors
|
||||||
|
if (!response.ok && (response.status === 0 || response.status >= 500)) {
|
||||||
|
downloadStatus.textContent = 'Сервис временно недоступен, приносим наши извинения.';
|
||||||
|
downloadStatus.className = 'status error';
|
||||||
|
downloadStatus.style.display = 'block';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (data.error || !data.result || !data.result.data) {
|
if (data.error || !data.result || !data.result.data) {
|
||||||
downloadStatus.textContent = data.result?.comment || data.result || 'Ошибка при получении файла';
|
// Check if it's a 404 or file not found error
|
||||||
|
if (response.status === 404 || data.result?.comment?.includes('not found') || data.result?.comment?.includes('не найден')) {
|
||||||
|
downloadStatus.textContent = 'Мы не смогли ничего найти. Перепроверьте введенный адрес.';
|
||||||
|
} else {
|
||||||
|
downloadStatus.textContent = 'Мы не смогли ничего найти. Перепроверьте введенный адрес.';
|
||||||
|
}
|
||||||
downloadStatus.className = 'status error';
|
downloadStatus.className = 'status error';
|
||||||
downloadStatus.style.display = 'block';
|
downloadStatus.style.display = 'block';
|
||||||
return;
|
return;
|
||||||
@@ -87,12 +101,25 @@ async function handleDownloadPage(fileUuid) {
|
|||||||
|
|
||||||
downloadStatus.style.display = 'none';
|
downloadStatus.style.display = 'none';
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
downloadStatus.textContent = `Ошибка: ${error.message}`;
|
// Network error or other connection issues
|
||||||
|
downloadStatus.textContent = 'Сервис временно недоступен, приносим наши извинения.';
|
||||||
downloadStatus.className = 'status error';
|
downloadStatus.className = 'status error';
|
||||||
downloadStatus.style.display = 'block';
|
downloadStatus.style.display = 'block';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Show error popup
|
||||||
|
function showErrorPopup(message) {
|
||||||
|
const popup = document.getElementById('errorPopup');
|
||||||
|
popup.textContent = message;
|
||||||
|
popup.style.display = 'block';
|
||||||
|
|
||||||
|
// Auto-hide after 5 seconds
|
||||||
|
setTimeout(() => {
|
||||||
|
popup.style.display = 'none';
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
// Upload page initialization
|
// Upload page initialization
|
||||||
function initUploadPage() {
|
function initUploadPage() {
|
||||||
const fileInput = document.getElementById('fileInput');
|
const fileInput = document.getElementById('fileInput');
|
||||||
@@ -101,6 +128,8 @@ function initUploadPage() {
|
|||||||
const uploadForm = document.getElementById('uploadForm');
|
const uploadForm = document.getElementById('uploadForm');
|
||||||
const statusDiv = document.getElementById('status');
|
const statusDiv = document.getElementById('status');
|
||||||
const maxFileSizeSpan = document.getElementById('maxFileSize');
|
const maxFileSizeSpan = document.getElementById('maxFileSize');
|
||||||
|
const successContent = document.getElementById('successContent');
|
||||||
|
const uploadFormContainer = uploadForm.parentElement;
|
||||||
|
|
||||||
let maxFileSizeBytes = 0;
|
let maxFileSizeBytes = 0;
|
||||||
|
|
||||||
@@ -133,7 +162,8 @@ function initUploadPage() {
|
|||||||
|
|
||||||
const file = fileInput.files[0];
|
const file = fileInput.files[0];
|
||||||
if (file.size > maxFileSizeBytes && maxFileSizeBytes > 0) {
|
if (file.size > maxFileSizeBytes && maxFileSizeBytes > 0) {
|
||||||
setStatus(`Файл слишком большой. Максимальный размер: ${formatBytes(maxFileSizeBytes)}`, 'error');
|
const errorMsg = `Файл слишком большой. Максимальный размер: ${formatBytes(maxFileSizeBytes)}`;
|
||||||
|
showErrorPopup(errorMsg);
|
||||||
uploadButton.disabled = true;
|
uploadButton.disabled = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -145,6 +175,52 @@ function initUploadPage() {
|
|||||||
const setStatus = (message, type = '') => {
|
const setStatus = (message, type = '') => {
|
||||||
statusDiv.textContent = message;
|
statusDiv.textContent = message;
|
||||||
statusDiv.className = type ? `status ${type}` : 'status';
|
statusDiv.className = type ? `status ${type}` : 'status';
|
||||||
|
// Hide status div when empty to avoid unnecessary spacing
|
||||||
|
if (!message || message.trim() === '') {
|
||||||
|
statusDiv.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
statusDiv.style.display = 'block';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const showSuccessState = (fileName, fileSize, downloadUrl) => {
|
||||||
|
// Hide the form
|
||||||
|
uploadForm.style.display = 'none';
|
||||||
|
statusDiv.style.display = 'none';
|
||||||
|
|
||||||
|
// Show success content
|
||||||
|
document.getElementById('successFileName').textContent = fileName;
|
||||||
|
document.getElementById('successFileSize').textContent = `Размер: ${formatBytes(fileSize)}`;
|
||||||
|
document.getElementById('downloadLinkInput').value = downloadUrl;
|
||||||
|
successContent.style.display = 'block';
|
||||||
|
|
||||||
|
// Setup copy button
|
||||||
|
const copyButton = document.getElementById('copyLinkButton');
|
||||||
|
copyButton.onclick = () => {
|
||||||
|
const input = document.getElementById('downloadLinkInput');
|
||||||
|
input.select();
|
||||||
|
input.setSelectionRange(0, 99999); // For mobile devices
|
||||||
|
document.execCommand('copy');
|
||||||
|
|
||||||
|
const originalText = copyButton.textContent;
|
||||||
|
copyButton.textContent = 'Скопировано!';
|
||||||
|
copyButton.classList.add('copied');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
copyButton.textContent = originalText;
|
||||||
|
copyButton.classList.remove('copied');
|
||||||
|
}, 2000);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Setup "upload another" button
|
||||||
|
const uploadAnotherButton = document.getElementById('uploadAnotherButton');
|
||||||
|
uploadAnotherButton.onclick = () => {
|
||||||
|
// Reset form
|
||||||
|
uploadForm.style.display = 'block';
|
||||||
|
successContent.style.display = 'none';
|
||||||
|
fileInput.value = '';
|
||||||
|
updateButtonState();
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
dropzone.addEventListener('dragover', (event) => {
|
dropzone.addEventListener('dragover', (event) => {
|
||||||
@@ -177,14 +253,14 @@ function initUploadPage() {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
if (!fileInput.files || fileInput.files.length === 0) {
|
if (!fileInput.files || fileInput.files.length === 0) {
|
||||||
setStatus('Выберите файл для загрузки.', 'error');
|
showErrorPopup('Выберите файл для загрузки.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const file = fileInput.files[0];
|
const file = fileInput.files[0];
|
||||||
|
|
||||||
if (maxFileSizeBytes > 0 && file.size > maxFileSizeBytes) {
|
if (maxFileSizeBytes > 0 && file.size > maxFileSizeBytes) {
|
||||||
setStatus(`Файл слишком большой. Максимальный размер: ${formatBytes(maxFileSizeBytes)}`, 'error');
|
showErrorPopup(`Файл слишком большой. Максимальный размер: ${formatBytes(maxFileSizeBytes)}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,13 +275,22 @@ function initUploadPage() {
|
|||||||
|
|
||||||
if (!tokenResponse.ok) {
|
if (!tokenResponse.ok) {
|
||||||
const errorData = await tokenResponse.json().catch(() => ({}));
|
const errorData = await tokenResponse.json().catch(() => ({}));
|
||||||
throw new Error(errorData.result?.comment || `Ошибка сервера: ${tokenResponse.status}`);
|
// Extract error message without status codes
|
||||||
|
let errorMessage = errorData.result || 'Ошибка при загрузке файла';
|
||||||
|
if (typeof errorMessage === 'object' && errorMessage.comment) {
|
||||||
|
errorMessage = errorMessage.comment;
|
||||||
|
}
|
||||||
|
if (typeof errorMessage === 'string' && errorMessage.includes('too large')) {
|
||||||
|
errorMessage = 'Файл слишком большой';
|
||||||
|
}
|
||||||
|
throw new Error(errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
const tokenData = await tokenResponse.json();
|
const tokenData = await tokenResponse.json();
|
||||||
|
|
||||||
if (tokenData.error || !tokenData.result || !tokenData.result.data) {
|
if (tokenData.error || !tokenData.result || !tokenData.result.data) {
|
||||||
throw new Error(tokenData.result?.comment || 'Ошибка при получении токена загрузки');
|
let errorMessage = tokenData.result?.comment || 'Ошибка при получении токена загрузки';
|
||||||
|
throw new Error(errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
const uploadData = tokenData.result.data;
|
const uploadData = tokenData.result.data;
|
||||||
@@ -230,17 +315,28 @@ function initUploadPage() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!uploadResponse.ok) {
|
if (!uploadResponse.ok) {
|
||||||
throw new Error(`Ошибка загрузки на S3: ${uploadResponse.status}`);
|
throw new Error('Ошибка при загрузке файла на сервер');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Success!
|
// Success!
|
||||||
const downloadUrl = `${window.location.origin}/get/${fileUuid}`;
|
const downloadUrl = `${window.location.origin}/get/${fileUuid}`;
|
||||||
setStatus(`Файл успешно загружен! UUID: ${fileUuid}. Ссылка для скачивания: ${downloadUrl}`, 'success');
|
showSuccessState(file.name, file.size, downloadUrl);
|
||||||
fileInput.value = '';
|
fileInput.value = '';
|
||||||
updateButtonState();
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setStatus(`Не удалось загрузить файл: ${error.message}`, 'error');
|
// Extract clean error message without status codes
|
||||||
|
let errorMessage = error.message;
|
||||||
|
if (errorMessage.includes('status') || errorMessage.match(/\d{3}/)) {
|
||||||
|
if (errorMessage.includes('too large') || errorMessage.includes('413')) {
|
||||||
|
errorMessage = 'Файл слишком большой';
|
||||||
|
} else if (errorMessage.includes('500')) {
|
||||||
|
errorMessage = 'Ошибка сервера. Попробуйте позже';
|
||||||
|
} else {
|
||||||
|
errorMessage = 'Не удалось загрузить файл';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
showErrorPopup(errorMessage);
|
||||||
|
setStatus('', '');
|
||||||
} finally {
|
} finally {
|
||||||
uploadButton.disabled = false;
|
uploadButton.disabled = false;
|
||||||
}
|
}
|
||||||
|
|||||||
+184
-3
@@ -15,10 +15,19 @@ body {
|
|||||||
padding: 24px;
|
padding: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.page-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 640px;
|
||||||
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: clamp(32px, 4vw, 48px);
|
font-size: clamp(32px, 4vw, 48px);
|
||||||
margin-bottom: 32px;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
margin: 0 0 32px 0;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.upload-container {
|
.upload-container {
|
||||||
@@ -42,6 +51,14 @@ h1 {
|
|||||||
background: rgba(99, 102, 241, 0.04);
|
background: rgba(99, 102, 241, 0.04);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropzone:focus,
|
||||||
|
.dropzone:focus-within {
|
||||||
|
outline: none;
|
||||||
|
border: 2px dashed #6366f1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropzone.dragover {
|
.dropzone.dragover {
|
||||||
@@ -52,9 +69,43 @@ h1 {
|
|||||||
|
|
||||||
.dropzone input[type="file"] {
|
.dropzone input[type="file"] {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
outline: none !important;
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
-webkit-appearance: none !important;
|
||||||
|
-moz-appearance: none !important;
|
||||||
|
appearance: none !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
background: transparent !important;
|
||||||
|
font-size: 0 !important;
|
||||||
|
line-height: 0 !important;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropzone input[type="file"]:focus,
|
||||||
|
.dropzone input[type="file"]:active,
|
||||||
|
.dropzone input[type="file"]:hover,
|
||||||
|
.dropzone input[type="file"]:focus-visible,
|
||||||
|
.dropzone input[type="file"]::-webkit-file-upload-button {
|
||||||
|
outline: none !important;
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
-webkit-appearance: none !important;
|
||||||
|
-moz-appearance: none !important;
|
||||||
|
appearance: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropzone input[type="file"]::-webkit-file-upload-button {
|
||||||
|
display: none !important;
|
||||||
|
visibility: hidden !important;
|
||||||
|
opacity: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropzone p {
|
.dropzone p {
|
||||||
@@ -92,12 +143,23 @@ button:disabled {
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
.status {
|
.status {
|
||||||
min-height: 24px;
|
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
color: #374151;
|
color: #374151;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status:empty {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status.error {
|
.status.error {
|
||||||
@@ -148,3 +210,122 @@ button:disabled {
|
|||||||
.download-button:active {
|
.download-button:active {
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Error popup */
|
||||||
|
.error-popup {
|
||||||
|
position: fixed;
|
||||||
|
top: 20px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
background: #fee2e2;
|
||||||
|
border: 2px solid #dc2626;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px 24px;
|
||||||
|
color: #991b1b;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 15px;
|
||||||
|
z-index: 1000;
|
||||||
|
box-shadow: 0 10px 25px -5px rgba(220, 38, 38, 0.3);
|
||||||
|
animation: slideDown 0.3s ease-out;
|
||||||
|
max-width: 90%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideDown {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-50%) translateY(-20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(-50%) translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Success content */
|
||||||
|
.success-content {
|
||||||
|
animation: fadeIn 0.3s ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-link-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
margin-top: 24px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-link-input {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 200px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border: 2px solid #e5e7eb;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: monospace;
|
||||||
|
background: #f9fafb;
|
||||||
|
color: #111827;
|
||||||
|
cursor: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-link-input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #6366f1;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-link-button {
|
||||||
|
padding: 12px 24px;
|
||||||
|
background: linear-gradient(135deg, #10b981, #059669);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-link-button:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 8px 16px -4px rgba(16, 185, 129, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-link-button:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-link-button.copied {
|
||||||
|
background: linear-gradient(135deg, #6366f1, #8b5cf6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-another-button {
|
||||||
|
margin-top: 24px;
|
||||||
|
padding: 12px 24px;
|
||||||
|
background: transparent;
|
||||||
|
color: #6366f1;
|
||||||
|
border: 2px solid #6366f1;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-another-button:hover {
|
||||||
|
background: #6366f1;
|
||||||
|
color: white;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 8px 16px -4px rgba(99, 102, 241, 0.4);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user