mirror of
https://github.com/chinchang/web-maker.git
synced 2025-07-10 00:16:18 +02:00
drag n drop in assets modal
This commit is contained in:
@ -1,37 +1,64 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import firebase from 'firebase/app';
|
import firebase from 'firebase/app';
|
||||||
import 'firebase/storage';
|
import 'firebase/storage';
|
||||||
|
import { Stack } from './Stack';
|
||||||
|
|
||||||
const Assets = () => {
|
const Assets = () => {
|
||||||
const [files, setFiles] = useState([]);
|
const [files, setFiles] = useState([]);
|
||||||
|
const [isFetchingFiles, setIsFetchingFiles] = useState(false);
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [filteredFiles, setFilteredFiles] = useState([]);
|
const [filteredFiles, setFilteredFiles] = useState([]);
|
||||||
|
const [isUploading, setIsUploading] = useState(false);
|
||||||
|
const [uploadProgress, setUploadProgress] = useState();
|
||||||
|
const [listType, setListType] = useState('grid');
|
||||||
|
|
||||||
const storageRef = firebase.storage().ref(`assets/${window.user.uid}`);
|
const storageRef = firebase.storage().ref(`assets/${window.user.uid}`);
|
||||||
|
|
||||||
// Function to handle file upload
|
const uploadFile = file => {
|
||||||
const handleFileUpload = e => {
|
|
||||||
const file = e.target.files[0];
|
|
||||||
|
|
||||||
if (file.size > 5 * 1024 * 1024) {
|
if (file.size > 5 * 1024 * 1024) {
|
||||||
// 5MB limit
|
// 5MB limit
|
||||||
alert('File size must be less than 5MB');
|
alert('File size must be less than 5MB');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setIsUploading(true);
|
||||||
const fileRef = storageRef.child(file.name);
|
const fileRef = storageRef.child(file.name);
|
||||||
fileRef
|
const task = fileRef.put(file);
|
||||||
.put(file)
|
|
||||||
.then(() => {
|
task.on(
|
||||||
alert('File uploaded successfully');
|
'state_changed',
|
||||||
fetchFiles();
|
snapshot => {
|
||||||
})
|
// Observe state change events such as progress, pause, and resume
|
||||||
.catch(error => {
|
// Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
|
||||||
|
var progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
|
||||||
|
console.log('Upload is ' + progress + '% done');
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
// Handle unsuccessful uploads
|
||||||
|
setIsUploading(false);
|
||||||
console.error('File upload error:', error);
|
console.error('File upload error:', error);
|
||||||
});
|
},
|
||||||
|
() => {
|
||||||
|
// uploadTask.snapshot.ref.getDownloadURL().then((downloadURL) => {
|
||||||
|
// console.log('File available at', downloadURL);
|
||||||
|
// });
|
||||||
|
|
||||||
|
alertsService.add('File uploaded successfully');
|
||||||
|
|
||||||
|
fetchFiles();
|
||||||
|
setIsUploading(false);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
// Function to handle file upload
|
||||||
|
const handleFileUpload = e => {
|
||||||
|
const file = e.target.files[0];
|
||||||
|
uploadFile(file);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to fetch existing files
|
// Function to fetch existing files
|
||||||
const fetchFiles = () => {
|
const fetchFiles = () => {
|
||||||
|
setIsFetchingFiles(true);
|
||||||
storageRef
|
storageRef
|
||||||
.listAll()
|
.listAll()
|
||||||
.then(result => {
|
.then(result => {
|
||||||
@ -42,9 +69,11 @@ const Assets = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Promise.all(filePromises).then(setFiles);
|
Promise.all(filePromises).then(setFiles);
|
||||||
|
setIsFetchingFiles(false);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('File fetch error:', error);
|
console.error('File fetch error:', error);
|
||||||
|
setIsFetchingFiles(false);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -73,56 +102,99 @@ const Assets = () => {
|
|||||||
const [isDropTarget, setIsDropTarget] = useState(false);
|
const [isDropTarget, setIsDropTarget] = useState(false);
|
||||||
const handleDragDropEvent = e => {
|
const handleDragDropEvent = e => {
|
||||||
console.log('drag event, ', e.type);
|
console.log('drag event, ', e.type);
|
||||||
if (e.type === 'dragleave' || e.type === 'drop') {
|
if (e.type === 'dragover') {
|
||||||
|
// required for drop to work
|
||||||
|
e.preventDefault();
|
||||||
|
} else if (e.type === 'dragleave') {
|
||||||
|
e.preventDefault();
|
||||||
setIsDropTarget(false);
|
setIsDropTarget(false);
|
||||||
}
|
} else if (e.type === 'dragenter') {
|
||||||
if (e.type === 'dragenter') {
|
|
||||||
setIsDropTarget(true);
|
setIsDropTarget(true);
|
||||||
ode;
|
|
||||||
console.log(999, e.type);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDrop = e => {
|
||||||
|
e.preventDefault();
|
||||||
|
console.log('drop');
|
||||||
|
setIsDropTarget(false);
|
||||||
|
|
||||||
|
if (e.dataTransfer.items) {
|
||||||
|
const file = e.dataTransfer.items[0].getAsFile();
|
||||||
|
uploadFile(file);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const copyFileUrl = url => {
|
||||||
|
const input = document.createElement('input');
|
||||||
|
input.value = url;
|
||||||
|
input.style.opacity = 0;
|
||||||
|
document.body.appendChild(input);
|
||||||
|
input.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
document.body.removeChild(input);
|
||||||
|
alertsService.add('File URL copied!');
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div
|
||||||
|
onDragEnter={handleDragDropEvent}
|
||||||
|
onDragLeave={handleDragDropEvent}
|
||||||
|
onDragOver={handleDragDropEvent}
|
||||||
|
onDrop={handleDrop}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="asset_manager__upload-box"
|
class="asset-manager__upload-box"
|
||||||
style={{
|
style={{
|
||||||
background: isDropTarget ? '#19a61940' : 'transparent',
|
background: isDropTarget ? '#19a61940' : 'transparent',
|
||||||
borderColor: isDropTarget ? 'limegreen' : null
|
borderColor: isDropTarget ? 'limegreen' : null
|
||||||
}}
|
}}
|
||||||
onDragEnter={handleDragDropEvent}
|
|
||||||
onDragOver={handleDragDropEvent}
|
|
||||||
onDrop={e => {
|
|
||||||
handleDragDropEvent(e);
|
|
||||||
// setFiles(e, 'a');
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<p>Drop file here to upload</p>
|
{isUploading ? <div class="asset-manager__progress-bar"></div> : null}
|
||||||
<input
|
|
||||||
type="file"
|
<div style={{ visibility: isUploading ? 'hidden' : 'visible' }}>
|
||||||
onChange={handleFileUpload}
|
<p>Drop file here to upload</p>
|
||||||
style={{ marginTop: 'auto' }}
|
<input
|
||||||
/>
|
type="file"
|
||||||
|
onChange={handleFileUpload}
|
||||||
|
style={{ marginTop: 'auto' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{isFetchingFiles && <p>Fetching your files...</p>}
|
||||||
{files.length ? (
|
{files.length ? (
|
||||||
<input
|
<Stack>
|
||||||
type="text"
|
<input
|
||||||
placeholder="Search files"
|
type="text"
|
||||||
value={searchTerm}
|
placeholder="Search files"
|
||||||
onChange={handleSearchChange}
|
value={searchTerm}
|
||||||
style={{ width: '100%' }}
|
onChange={handleSearchChange}
|
||||||
/>
|
style={{ width: '100%' }}
|
||||||
|
/>
|
||||||
|
<button onClick={() => setListType('list')}>List</button>
|
||||||
|
<button onClick={() => setListType('grid')}>Grid</button>
|
||||||
|
</Stack>
|
||||||
) : null}
|
) : null}
|
||||||
<ul>
|
<div
|
||||||
|
class={`asset-manager__file-container ${
|
||||||
|
listType === 'grid' ? 'asset-manager__file-container--grid' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
{filteredFiles.map((file, index) => (
|
{filteredFiles.map((file, index) => (
|
||||||
<li key={index}>
|
<button
|
||||||
<a href={file.url} target="_blank" rel="noopener noreferrer">
|
type="button"
|
||||||
<img src={file.url} height="20" /> {file.name}
|
key={index}
|
||||||
</a>
|
onClick={() => copyFileUrl(file.url)}
|
||||||
</li>
|
class={`asset-manager__file ${
|
||||||
|
listType === 'grid' ? 'asset-manager__file--grid' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{/* <a href={file.url} target="_blank" rel="noopener noreferrer"> */}
|
||||||
|
<img src={file.url} />{' '}
|
||||||
|
<span class="asset-manager__file-name">{file.name}</span>
|
||||||
|
{/* </a> */}
|
||||||
|
</button>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -100,6 +100,7 @@ export default class App extends Component {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.AUTO_SAVE_INTERVAL = 15000; // 15 seconds
|
this.AUTO_SAVE_INTERVAL = 15000; // 15 seconds
|
||||||
|
const savedUser = window.localStorage.getItem('user');
|
||||||
this.modalDefaultStates = {
|
this.modalDefaultStates = {
|
||||||
isModalOpen: false,
|
isModalOpen: false,
|
||||||
isAddLibraryModalOpen: false,
|
isAddLibraryModalOpen: false,
|
||||||
@ -125,7 +126,8 @@ export default class App extends Component {
|
|||||||
title: '',
|
title: '',
|
||||||
externalLibs: { js: '', css: '' }
|
externalLibs: { js: '', css: '' }
|
||||||
},
|
},
|
||||||
catalogs: {}
|
catalogs: {},
|
||||||
|
user: savedUser
|
||||||
};
|
};
|
||||||
this.defaultSettings = {
|
this.defaultSettings = {
|
||||||
preserveLastCode: true,
|
preserveLastCode: true,
|
||||||
@ -158,6 +160,10 @@ export default class App extends Component {
|
|||||||
};
|
};
|
||||||
this.prefs = {};
|
this.prefs = {};
|
||||||
|
|
||||||
|
if (savedUser) {
|
||||||
|
window.user = savedUser;
|
||||||
|
}
|
||||||
|
|
||||||
firebase.auth().onAuthStateChanged(user => {
|
firebase.auth().onAuthStateChanged(user => {
|
||||||
this.setState({ isLoginModalOpen: false });
|
this.setState({ isLoginModalOpen: false });
|
||||||
if (user) {
|
if (user) {
|
||||||
@ -165,6 +171,7 @@ export default class App extends Component {
|
|||||||
alertsService.add('You are now logged in!');
|
alertsService.add('You are now logged in!');
|
||||||
this.setState({ user });
|
this.setState({ user });
|
||||||
window.user = user;
|
window.user = user;
|
||||||
|
window.localStorage.setItem('user', user);
|
||||||
if (!window.localStorage[LocalStorageKeys.ASKED_TO_IMPORT_CREATIONS]) {
|
if (!window.localStorage[LocalStorageKeys.ASKED_TO_IMPORT_CREATIONS]) {
|
||||||
this.fetchItems(false, true).then(items => {
|
this.fetchItems(false, true).then(items => {
|
||||||
if (!items.length) {
|
if (!items.length) {
|
||||||
|
@ -85,6 +85,7 @@ p {
|
|||||||
button {
|
button {
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hide {
|
.hide {
|
||||||
@ -571,7 +572,9 @@ body:not(.light-version).overlay-visible .main-container {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
background: var(--color-bg);
|
background: var(--color-bg);
|
||||||
transition: height 0.3s ease, width 0.3s ease;
|
transition:
|
||||||
|
height 0.3s ease,
|
||||||
|
width 0.3s ease;
|
||||||
will-change: height;
|
will-change: height;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2002,13 +2005,103 @@ while the theme CSS file is loading */
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.asset_manager__upload-box {
|
.asset-manager__upload-box {
|
||||||
padding: 1rem 2rem;
|
padding: 1rem 2rem;
|
||||||
border: 3px dashed rgb(255 255 255 / 10%);
|
border: 3px dashed rgb(255 255 255 / 10%);
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
transition: 0.3s ease;
|
transition: 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes move {
|
||||||
|
0% {
|
||||||
|
background-position: 0 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: 50px 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-manager__progress-bar {
|
||||||
|
position: absolute;
|
||||||
|
inset: 1rem;
|
||||||
|
/* height: 2rem; */
|
||||||
|
background-image: linear-gradient(
|
||||||
|
-45deg,
|
||||||
|
rgba(255, 255, 255, 0.2) 25%,
|
||||||
|
transparent 25%,
|
||||||
|
transparent 50%,
|
||||||
|
rgba(255, 255, 255, 0.2) 50%,
|
||||||
|
rgba(255, 255, 255, 0.2) 75%,
|
||||||
|
transparent 75%,
|
||||||
|
transparent
|
||||||
|
);
|
||||||
|
z-index: 1;
|
||||||
|
background-size: 50px 50px;
|
||||||
|
animation: move 2s linear infinite;
|
||||||
|
border-top-right-radius: 8px;
|
||||||
|
border-bottom-right-radius: 8px;
|
||||||
|
border-top-left-radius: 20px;
|
||||||
|
border-bottom-left-radius: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.asset-manager__file {
|
||||||
|
/* list-style: none; */
|
||||||
|
/* padding: 0; */
|
||||||
|
/* margin: 0; */
|
||||||
|
display: flex;
|
||||||
|
background: rgb(255 255 255 / 4%);
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.2rem;
|
||||||
|
border-radius: 0.4rem;
|
||||||
|
color: inherit;
|
||||||
|
border: 0;
|
||||||
|
transition: 0.3s ease;
|
||||||
|
/* align-items: center; */
|
||||||
|
}
|
||||||
|
.asset-manager__file:hover {
|
||||||
|
background: rgb(255 255 255 / 7%);
|
||||||
|
}
|
||||||
|
.asset-manager__file--grid {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
/* align-items: flex-start; */
|
||||||
|
}
|
||||||
|
.asset-manager__file-container {
|
||||||
|
padding: 0;
|
||||||
|
gap: 0.3rem;
|
||||||
|
}
|
||||||
|
.asset-manager__file-container--grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-manager__file {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
.asset-manager__file-name {
|
||||||
|
max-width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.asset-manager__file img {
|
||||||
|
height: 1.5rem;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
/* border-radius: 0.5rem; */
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
.asset-manager__file--grid img {
|
||||||
|
height: 80px;
|
||||||
|
}
|
||||||
|
.stack {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.stack > * {
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 600px) {
|
@media screen and (max-width: 600px) {
|
||||||
|
Reference in New Issue
Block a user