Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

aem2doc parsing now done in da-collab #127

Closed
wants to merge 13 commits into from
38 changes: 35 additions & 3 deletions blocks/browse/da-browse/da-browse.css
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,6 @@ input[type="checkbox"] {
.da-breadcrumb-list-item {
position: relative;
display: block;
text-transform: uppercase;
font-size: 16px;
font-weight: 700;
cursor: pointer;
Expand Down Expand Up @@ -508,15 +507,48 @@ li.da-actions-menu-item button:hover {

/* Empty list */
.empty-list {
border: 1px solid rgb(234, 234, 234);
background-color: rgb(248, 248, 248);
border: 1px solid rgb(234 234 234);
background-color: rgb(248 248 248);
border-radius: 6px;
display: flex;
justify-content: center;
align-items: center;
min-height: 400px;
}

/* Drag & Drop */
.da-browse-panel {
position: relative;
}

.da-browse-panel.is-dragged-over > * {
position: relative;
opacity: 0.1;
}

.da-drop-area {
display: none;
justify-content: center;
align-items: center;
border-radius: 6px;
background-color: rgb(180 255 175 / 23%);
border: 2px dotted rgb(0 194 68);
z-index: 1;
}

.da-drop-area::after {
font-size: 24px;
font-weight: 700;
content: attr(data-message);
}

.da-browse-panel.is-dragged-over > .da-drop-area {
display: flex;
opacity: 1;
position: absolute;
inset: 0;
}

@media (min-width: 900px) {
.da-breadcrumb {
margin-bottom: 12px;
Expand Down
75 changes: 71 additions & 4 deletions blocks/browse/da-browse/da-browse.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export default class DaBrowse extends LitElement {
_createName: { state: true },
_createFile: { state: true },
_fileLabel: { state: true },
_dropFiles: { state: true },
_dropMessage: { state: true },
_canPaste: {},
};

Expand All @@ -38,6 +40,8 @@ export default class DaBrowse extends LitElement {
this._createName = '';
this._createFile = '';
this._fileLabel = 'Select file';
this._dropFiles = [];
this._dropMessage = 'Drop content here';
this._tabItems = [
{ id: 'browse', label: 'Browse', selected: true },
{ id: 'search', label: 'Search', selected: false },
Expand Down Expand Up @@ -104,9 +108,12 @@ export default class DaBrowse extends LitElement {
window.location = editPath;
} else {
await saveToDa({ path });
const item = { name: this._createName, path };
if (ext) item.ext = ext;
this._listItems.unshift(item);
const hasName = this._listItems.some((item) => item.name === this._createName);
if (!hasName) {
const item = { name: this._createName, path };
if (ext) item.ext = ext;
this._listItems.unshift(item);
}
}
this.resetCreate();
this.requestUpdate();
Expand Down Expand Up @@ -273,6 +280,62 @@ export default class DaBrowse extends LitElement {
this.searchItems = e.detail.items;
}

dragenter(e) {
e.stopPropagation();
e.target.closest('.da-browse-panel').classList.add('is-dragged-over');
e.preventDefault();
}

dragleave(e) {
if (!e.target.classList.contains('da-drop-area')) return;
e.target.closest('.da-browse-panel').classList.remove('is-dragged-over');
e.preventDefault();
}

dragover(e) {
e.preventDefault();
}

setDropMessage() {
const { length } = this._dropFiles.filter((file) => !file.imported);
if (length === 0) {
this._dropMessage = 'Drop content here';
return;
}
const prefix = `Importing - ${length} `;
const suffix = length === 1 ? 'item' : 'items';
this._dropMessage = `${prefix} ${suffix}`;
}

async drop(e) {
e.preventDefault();
const { fullpath } = this.details;
const items = e.dataTransfer?.items;
if (!items) return;

const entries = [...items].map((item) => item.webkitGetAsEntry());
const makeBatches = (await import(`${getNx()}/utils/batch.js`)).default;
const { getFullEntryList, handleUpload } = await import('./helpers/drag-n-drop.js');
this._dropFiles = await getFullEntryList(entries);

this.setDropMessage();

const batches = makeBatches(this._dropFiles);
for (const batch of batches) {
await Promise.all(batch.map(async (file) => {
const item = await handleUpload(this._listItems, fullpath, file);
this.setDropMessage();
if (item) {
this._listItems.unshift(item);
this.requestUpdate();
}
}));
}
this._dropFiles = [];
this.setDropMessage();
e.target.shadowRoot.querySelector('.da-browse-panel').classList.remove('is-dragged-over');
}

renderConfig(length, crumb, idx) {
if (this.details.depth <= 2 && idx + 1 === length) {
return html`
Expand Down Expand Up @@ -423,7 +486,11 @@ export default class DaBrowse extends LitElement {
}

renderBrowse() {
return html`${this._listItems?.length > 0 ? this.listView(this._listItems, true) : this.emptyView()}`;
return html`
<div class="da-browse-panel" @dragenter=${this.dragenter} @dragleave=${this.dragleave}>
${this._listItems?.length > 0 ? this.listView(this._listItems, true) : this.emptyView()}
<div class="da-drop-area" data-message=${this._dropMessage} @dragover=${this.dragover} @drop=${this.drop}></div>
</div>`;
}

render() {
Expand Down
112 changes: 112 additions & 0 deletions blocks/browse/da-browse/helpers/drag-n-drop.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { SUPPORTED_FILES, DA_ORIGIN } from '../../../shared/constants.js';
import { daFetch } from '../../../shared/utils.js';

const MAX_DEPTH = 1000;

function traverseFolder(entry) {
const reader = entry.createReader();
// Resolved when the entire directory is traversed
return new Promise((resolveDirectory) => {
const iterationAttempts = [];
const errorHandler = () => {};
function readEntries() {
// According to the FileSystem API spec, readEntries() must be called until
// it calls the callback with an empty array.
reader.readEntries((batchEntries) => {
if (!batchEntries.length) {
// Done iterating this folder
resolveDirectory(Promise.all(iterationAttempts));
} else {
// Add a list of promises for each directory entry. If the entry is itself
// a directory, then that promise won't resolve until it is fully traversed.
iterationAttempts.push(Promise.all(batchEntries.map((batchEntry) => {
if (batchEntry.isDirectory) {
return traverseFolder(batchEntry);
}
return Promise.resolve(batchEntry);
})));
// Try calling readEntries() again for the same dir, according to spec
readEntries();
}
}, errorHandler);
}
// Initial call to recursive entry reader function
readEntries();
});
}

function packageFile(file, entry) {
const { name } = file;
let { type } = file;

// No content type fallback
const ext = (file.name || '').split('.').pop();
if (!type) type = SUPPORTED_FILES[ext];

// Check if supported type
const isSupported = Object.keys(SUPPORTED_FILES)
.some((key) => type === SUPPORTED_FILES[key]);
if (!isSupported) return null;

// Sanitize path
const path = entry.fullPath.replaceAll(' ', '-').toLowerCase();
return { data: file, name, type, ext, path };
}

function getFile(entry) {
return new Promise((resolve) => {
const callback = (file) => { resolve(packageFile(file, entry)); };
entry.file(callback);
});
}

export async function getFullEntryList(entries) {
const folderEntries = [];
const fileEntries = [];

for (const entry of entries) {
if (entry.isDirectory) {
folderEntries.push(entry);
} else {
fileEntries.push(entry);
}
}

for (const entry of folderEntries) {
const traversed = await traverseFolder(entry);
fileEntries.push(...traversed.flat(MAX_DEPTH));
}

const files = await Promise.all(fileEntries.map((entry) => getFile(entry)));
return files.filter((file) => file);
}

export async function handleUpload(list, fullpath, file) {
const { data, path } = file;
const formData = new FormData();
formData.append('data', data);
const opts = { method: 'POST', body: formData };
const postpath = `${fullpath}${path}`;

try {
await daFetch(`${DA_ORIGIN}/source${postpath}`, opts);
file.imported = true;

const [displayName] = path.split('/').slice(1);
const [filename, ...rest] = displayName.split('.');
const ext = rest.pop();
const rejoined = [filename, ...rest].join('.');

const listHasName = list.some((item) => item.name === rejoined);

if (listHasName) return null;

const item = { name: rejoined, path: `${fullpath}/${displayName}` };
if (ext) item.ext = ext;

return item;
} catch (e) {
console.log(e);
}
return null;
}
Loading