openvibers/openvibe.mediahub Commits
Drive on a phone: no blank band under the folders; a slim drop line when there are no loose files (#313)
Changes
@@@ -184,6 +184,10 @@.dr-empty:hover { border-color: color-mix(in srgb, var(--dr-acc) 60%, transparent); background: color-mix(in srgb, var(--dr-acc) 5%, transparent); }.dr-empty .dr-i { width: 54px; height: 54px; padding: 12px; border-radius: 16px; color: var(--dr-acc); background: color-mix(in srgb, var(--dr-acc) 14%, transparent); margin-bottom: 6px; }.dr-empty b { font-size: 1.15rem; color: var(--dr-text); }++/* Folders but no loose files: one dashed line under them, not the big empty card and not a blank gap. */++.dr-empty.is-slim { grid-auto-flow: column; justify-content: start; align-items: center; gap: 12px; margin-top: 18px; padding: 14px 18px; border-radius: 14px; text-align: left; }++.dr-empty.is-slim .dr-i { width: 36px; height: 36px; padding: 8px; border-radius: 11px; margin: 0; }++.dr-empty.is-slim b { font-size: 1rem; margin-right: 4px; }.dr-nomatch { padding: 24px 8px; text-align: center; color: var(--dr-muted); }.dr-links { display: grid; gap: 8px; }@@@ -291,7 +295,9 @@border-radius: 24px 24px 0 0; border-bottom: 0; box-shadow: 0 0 0 100vmax rgba(0, 0, 0, .5), var(--dr-shadow); animation: dr-sheet .24s cubic-bezier(.2, .8, .2, 1); }.dr-new-panel::before { content: ""; justify-self: center; width: 40px; height: 4px; margin: 2px 0 8px; border-radius: 99px; background: color-mix(in srgb, var(--dr-text) 25%, transparent); }.dr-pick, .dr-new-row { min-height: 60px; }-- .dr-main { order: 1; padding-bottom: 72px; }++ /* Room for the floating New at the page's end, not between the files and the places (it left a blank band). */++ .dr { padding-bottom: 72px; }++ .dr-main { order: 1; }.dr-bar-row { margin-bottom: 16px; }.dr-title { flex-basis: 100%; }.dr-tools { flex: 1 1 100%; }@@@ -393,6 +393,7 @@ ${folders.length ? html`<section class="dr-sec" aria-labelledby="h-folders"><h2<ul class="dr-folders" data-items>${folders.map((f) => ui.folderTile(f))}</ul></section>` : ''}${files.length ? html`<section class="dr-sec" aria-labelledby="h-files"><h2 id="h-files" class="dr-h">Files <span>${files.length}</span></h2><ul class="dr-files" data-items>${files.map((row) => ui.fileCard(row, { where: q ? (row.folder_id ? labels.get(row.folder_id) || 'A folder' : 'My files') : null }))}</ul></section>` : ''}++${!empty && !files.length && !q ? html`<label class="dr-empty is-slim" for="file">${ui.icon('upload')}<span><b>No loose files here</b> Drop them anywhere, or tap to choose.</span></label>` : ''}<p class="dr-nomatch" data-nomatch hidden>Nothing in this view matches. Press Enter to search every folder.</p>${showShares ? html`<section class="dr-sec" id="shared" aria-labelledby="h-shares"><h2 id="h-shares" class="dr-h">Live share links <span>${liveShares.length}</span></h2><ul class="dr-links">${liveShares.map((sh) => { const [ic, who] = whoOf(sh); return html`<li class="dr-link">${ui.icon(ic)}<span class="dr-link-main"><a href="/s/${sh.slug}">${shareNames.get(sh.slug) || `/s/${sh.slug}`}</a>@@@ -74,6 +74,13 @@ const { SAME, bytes, upload, share } = require('./helpers/drive');assert.ok(!root.text.includes('return.pdf'), 'and not in the root');assert.ok(root.text.includes('Taxes 2026'), 'where the folder is listed');assert.strictEqual((await t.get(`/files/${file.id}`, { as: kim })).status, 200);++ // Folders and no loose files: one slim drop line under the folders, never the big empty card or a blank band.++ const lea = t.network.addUser('lea');++ await t.get('/folders', { as: lea, form: { name: 'Photos' }, headers: SAME });++ const only = await t.get('/files', { as: lea });++ assert.ok(only.text.includes('Photos'), 'her folder is listed');++ assert.ok(only.text.includes('class="dr-empty is-slim"') && only.text.includes('No loose files here'), 'with the slim drop line');++ assert.ok(!only.text.includes('Your drive is empty'), 'not the empty-drive card');});await check('the file page carries one Share button, with the file page\'s own address', async () => {
@@ -184,6 +184,10 @@
@@ -184,6 +184,10 @@
.dr-empty:hover { border-color: color-mix(in srgb, var(--dr-acc) 60%, transparent); background: color-mix(in srgb, var(--dr-acc) 5%, transparent); }
.dr-empty:hover { border-color: color-mix(in srgb, var(--dr-acc) 60%, transparent); background: color-mix(in srgb, var(--dr-acc) 5%, transparent); }
.dr-empty .dr-i { width: 54px; height: 54px; padding: 12px; border-radius: 16px; color: var(--dr-acc); background: color-mix(in srgb, var(--dr-acc) 14%, transparent); margin-bottom: 6px; }
.dr-empty .dr-i { width: 54px; height: 54px; padding: 12px; border-radius: 16px; color: var(--dr-acc); background: color-mix(in srgb, var(--dr-acc) 14%, transparent); margin-bottom: 6px; }
.dr-empty b { font-size: 1.15rem; color: var(--dr-text); }
.dr-empty b { font-size: 1.15rem; color: var(--dr-text); }
+/* Folders but no loose files: one dashed line under them, not the big empty card and not a blank gap. */
+.dr-empty.is-slim { grid-auto-flow: column; justify-content: start; align-items: center; gap: 12px; margin-top: 18px; padding: 14px 18px; border-radius: 14px; text-align: left; }
+.dr-empty.is-slim .dr-i { width: 36px; height: 36px; padding: 8px; border-radius: 11px; margin: 0; }
+.dr-empty.is-slim b { font-size: 1rem; margin-right: 4px; }
.dr-nomatch { padding: 24px 8px; text-align: center; color: var(--dr-muted); }
.dr-nomatch { padding: 24px 8px; text-align: center; color: var(--dr-muted); }
.dr-links { display: grid; gap: 8px; }
.dr-links { display: grid; gap: 8px; }
@@ -291,7 +295,9 @@
@@ -291,7 +295,9 @@
border-radius: 24px 24px 0 0; border-bottom: 0; box-shadow: 0 0 0 100vmax rgba(0, 0, 0, .5), var(--dr-shadow); animation: dr-sheet .24s cubic-bezier(.2, .8, .2, 1); }
border-radius: 24px 24px 0 0; border-bottom: 0; box-shadow: 0 0 0 100vmax rgba(0, 0, 0, .5), var(--dr-shadow); animation: dr-sheet .24s cubic-bezier(.2, .8, .2, 1); }
.dr-new-panel::before { content: ""; justify-self: center; width: 40px; height: 4px; margin: 2px 0 8px; border-radius: 99px; background: color-mix(in srgb, var(--dr-text) 25%, transparent); }
.dr-new-panel::before { content: ""; justify-self: center; width: 40px; height: 4px; margin: 2px 0 8px; border-radius: 99px; background: color-mix(in srgb, var(--dr-text) 25%, transparent); }
.dr-pick, .dr-new-row { min-height: 60px; }
.dr-pick, .dr-new-row { min-height: 60px; }
- .dr-main { order: 1; padding-bottom: 72px; }
+ /* Room for the floating New at the page's end, not between the files and the places (it left a blank band). */
+ .dr { padding-bottom: 72px; }
+ .dr-main { order: 1; }
.dr-bar-row { margin-bottom: 16px; }
.dr-bar-row { margin-bottom: 16px; }
.dr-title { flex-basis: 100%; }
.dr-title { flex-basis: 100%; }
.dr-tools { flex: 1 1 100%; }
.dr-tools { flex: 1 1 100%; }
@@ -393,6 +393,7 @@ ${folders.length ? html`<section class="dr-sec" aria-labelledby="h-folders"><h2
@@ -393,6 +393,7 @@ ${folders.length ? html`<section class="dr-sec" aria-labelledby="h-folders"><h2
<ul class="dr-folders" data-items>${folders.map((f) => ui.folderTile(f))}</ul></section>` : ''}
<ul class="dr-folders" data-items>${folders.map((f) => ui.folderTile(f))}</ul></section>` : ''}
${files.length ? html`<section class="dr-sec" aria-labelledby="h-files"><h2 id="h-files" class="dr-h">Files <span>${files.length}</span></h2>
${files.length ? html`<section class="dr-sec" aria-labelledby="h-files"><h2 id="h-files" class="dr-h">Files <span>${files.length}</span></h2>
<ul class="dr-files" data-items>${files.map((row) => ui.fileCard(row, { where: q ? (row.folder_id ? labels.get(row.folder_id) || 'A folder' : 'My files') : null }))}</ul></section>` : ''}
<ul class="dr-files" data-items>${files.map((row) => ui.fileCard(row, { where: q ? (row.folder_id ? labels.get(row.folder_id) || 'A folder' : 'My files') : null }))}</ul></section>` : ''}
+${!empty && !files.length && !q ? html`<label class="dr-empty is-slim" for="file">${ui.icon('upload')}<span><b>No loose files here</b> Drop them anywhere, or tap to choose.</span></label>` : ''}
<p class="dr-nomatch" data-nomatch hidden>Nothing in this view matches. Press Enter to search every folder.</p>
<p class="dr-nomatch" data-nomatch hidden>Nothing in this view matches. Press Enter to search every folder.</p>
${showShares ? html`<section class="dr-sec" id="shared" aria-labelledby="h-shares"><h2 id="h-shares" class="dr-h">Live share links <span>${liveShares.length}</span></h2>
${showShares ? html`<section class="dr-sec" id="shared" aria-labelledby="h-shares"><h2 id="h-shares" class="dr-h">Live share links <span>${liveShares.length}</span></h2>
<ul class="dr-links">${liveShares.map((sh) => { const [ic, who] = whoOf(sh); return html`<li class="dr-link">${ui.icon(ic)}<span class="dr-link-main"><a href="/s/${sh.slug}">${shareNames.get(sh.slug) || `/s/${sh.slug}`}</a>
<ul class="dr-links">${liveShares.map((sh) => { const [ic, who] = whoOf(sh); return html`<li class="dr-link">${ui.icon(ic)}<span class="dr-link-main"><a href="/s/${sh.slug}">${shareNames.get(sh.slug) || `/s/${sh.slug}`}</a>
@@ -74,6 +74,13 @@ const { SAME, bytes, upload, share } = require('./helpers/drive');
@@ -74,6 +74,13 @@ const { SAME, bytes, upload, share } = require('./helpers/drive');
assert.ok(!root.text.includes('return.pdf'), 'and not in the root');
assert.ok(!root.text.includes('return.pdf'), 'and not in the root');
assert.ok(root.text.includes('Taxes 2026'), 'where the folder is listed');
assert.ok(root.text.includes('Taxes 2026'), 'where the folder is listed');
assert.strictEqual((await t.get(`/files/${file.id}`, { as: kim })).status, 200);
assert.strictEqual((await t.get(`/files/${file.id}`, { as: kim })).status, 200);
+ // Folders and no loose files: one slim drop line under the folders, never the big empty card or a blank band.
+ const lea = t.network.addUser('lea');
+ await t.get('/folders', { as: lea, form: { name: 'Photos' }, headers: SAME });
+ const only = await t.get('/files', { as: lea });
+ assert.ok(only.text.includes('Photos'), 'her folder is listed');
+ assert.ok(only.text.includes('class="dr-empty is-slim"') && only.text.includes('No loose files here'), 'with the slim drop line');
+ assert.ok(!only.text.includes('Your drive is empty'), 'not the empty-drive card');
});
});
await check('the file page carries one Share button, with the file page\'s own address', async () => {
await check('the file page carries one Share button, with the file page\'s own address', async () => {