diff --git a/packages/gallery/src/app.js b/packages/gallery/src/app.js index 7644666..1f496ee 100644 --- a/packages/gallery/src/app.js +++ b/packages/gallery/src/app.js @@ -10,7 +10,7 @@ import { EventEmitter } from 'events'; EventEmitter.defaultMaxListeners = 1000; // https://github.com/pouchdb/pouchdb/issues/6123 // Attach our root view to the DOM -createView(GalleryView, {}).mount(document.querySelector('#app')); +createView(GalleryView, {}).mount(document.body); // Start the router router.start('home'); diff --git a/packages/gallery/src/index.html b/packages/gallery/src/index.html index 6eb08d3..2d741a8 100644 --- a/packages/gallery/src/index.html +++ b/packages/gallery/src/index.html @@ -5,7 +5,6 @@ -
diff --git a/packages/gallery/src/interface/dropzone.js b/packages/gallery/src/interface/dropzone.js new file mode 100644 index 0000000..212cb62 --- /dev/null +++ b/packages/gallery/src/interface/dropzone.js @@ -0,0 +1,75 @@ +import { prop, computed } from 'frptools'; +import { injectStyle, el } from '../services/style'; + +const CSS_DROPZONE = { + width: '200px', + height: '200px', + border: '2px #666 dashed', + borderRadius: '5px' +}; + +const CSS_DROPZONE_ACTIVE = { + borderStyle: 'solid', + backgroundColor: '#eee' +}; + +export function Dropzone(vm, model) { + const { ondrop, ondragenter, ondragleave } = model; + + const enterCounter = prop(0); + enterCounter.subscribe(() => vm.redraw()); + + function onDragOver(evt) { + // allows the browser to accept drops. + evt.preventDefault(); + } + + function onDragEnter() { + enterCounter(enterCounter() + 1); + + if (ondragenter) { + ondragenter(); + } + } + + function onDragLeave() { + enterCounter(enterCounter() - 1); + + if (ondragleave) { + ondragleave(); + } + } + + function onDrop(evt) { + evt.preventDefault(); + enterCounter(0); + + if (ondrop) { + ondrop(evt.dataTransfer.files); + } + } + + return function render(vm, model) { + const { className, activeClassName, class: _class, children } = model; + + const class_ = Object.assign( + { + [className || injectStyle(CSS_DROPZONE)]: true, + [activeClassName || injectStyle(CSS_DROPZONE_ACTIVE)]: enterCounter() > 0 + }, + _class || {} + ); + + return el( + 'div', + { + class: class_, + ondragenter: onDragEnter, + ondragover: onDragOver, + ondragleave: onDragLeave, + ondrop: onDrop + }, + ...children + ); + }; +} diff --git a/packages/gallery/src/interface/gallery.js b/packages/gallery/src/interface/gallery.js index ee64367..c80dac8 100644 --- a/packages/gallery/src/interface/gallery.js +++ b/packages/gallery/src/interface/gallery.js @@ -3,8 +3,9 @@ import { ImageType } from '../data/image.js'; import { AlbumType } from '../data/album.js'; import { ThumbnailView } from './thumbnail.js'; import { AlbumView } from './album.js'; +import { Dropzone } from './dropzone.js'; import { router, routeChanged } from '../services/router.js'; -import { styled, el } from '../services/style.js'; +import { injectStyle, styled, el } from '../services/style.js'; export function GalleryView(vm, model) { const { db } = model; @@ -28,8 +29,8 @@ export function GalleryView(vm, model) { let laCleanup = null; let title = ''; - function uploadImages(evt) { - Array.from(evt.currentTarget.files).forEach(ImageType.upload); + function uploadImages(files) { + Array.from(files).forEach(ImageType.upload); } function deleteImage(i) { @@ -64,41 +65,60 @@ export function GalleryView(vm, model) { }); }); - return function(vm, model, key, opts) { - return el('.gallery', [ + function renderDropzone() { + return [ + el('a', { href: router.href('images') }, 'Images'), + el('a', { href: router.href('albums') }, 'Albums'), + el('h1', title), + ...(title === 'Images' + ? data().map(i => { + return vw( + ThumbnailView, + { + doc: i, + showTags: true, + remove: deleteImage + }, + i._hash() + ); + }) + : data().map(a => { + return vw(AlbumView, a, a._hash()); + })) + ]; + } + + return function render(vm, params, key, opts) { + if (!data || !data.ready()) { + return el('h1', 'Loading...'); + } + + return el( + '.gallery', + { class: slate }, header([ el('div', { css: { fontSize: '20pt' } }, 'Gallery'), - el('button', { onclick: addAlbum }, 'Add Album'), - el('input#fInput', { - type: 'file', - multiple: true, - accept: 'image/jpeg', - onchange: uploadImages - }) + headerRight( + { + css: { visibility: /* selectMode */ true ? 'visible' : 'hidden' } + }, + [el('button', { onclick: addAlbum }, 'Add Album')] + ) ]), - ...(!data || !data.ready() - ? [el('h1', 'Loading...')] - : [ - el('a', { href: router.href('images') }, 'Images'), - el('a', { href: router.href('albums') }, 'Albums'), - el('h1', title), - ...(title === 'Images' - ? data().map(i => { - return vw( - ThumbnailView, - { - doc: i, - showTags: true, - remove: deleteImage - }, - i._id + i._rev - ); - }) - : data().map(a => { - return vw(AlbumView, a, a._id + a._rev); - })) - ]) - ]); + vw( + Dropzone, + { + className: slate, + activeClassName: 'dropHover', + ondrop: uploadImages, + type: 'file', + multiple: true, // FIXME - these don't carry through to the input tag + accept: 'image/jpeg', + children: renderDropzone() + }, + 'dz' + ) + ); }; } @@ -109,3 +129,15 @@ const header = styled({ display: 'flex', alignItems: 'center' }); + +const headerRight = styled({ + display: 'flex', + alignItems: 'center' +}); + +const slate = injectStyle({ + display: 'flex', + flex: 1, + flexDirection: 'column' + // overflow: 'hidden', +}); diff --git a/packages/gallery/src/services/db.js b/packages/gallery/src/services/db.js index b4ba77f..b295541 100644 --- a/packages/gallery/src/services/db.js +++ b/packages/gallery/src/services/db.js @@ -7,7 +7,7 @@ import find from 'pouchdb-find'; import { log, warn } from './console.js'; import { isObject } from '../utils/comparators.js'; import { LiveArray } from '../utils/livearray.js'; -import { deepAssign } from '../utils/conversion.js'; +import { deepAssign, pouchDocHash } from '../utils/conversion.js'; export const PouchDB = core .plugin(idb) @@ -43,6 +43,10 @@ export class TypeSpec { return doc; } + _hash() { + return pouchDocHash(this); + } + async delete() { return await this.update({ _deleted: true }); }