ImageGrid/ui (gen4)/features/ui-preact-render.js
2018-11-08 21:10:58 +03:00

439 lines
9.4 KiB
JavaScript
Executable File

/**********************************************************************
*
*
*
**********************************************************************/
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
(function(require){ var module={} // make module AMD/node compatible...
/*********************************************************************/
// XXX should we guard against loading in node???
if(typeof(window) == 'undefined'){
return
}
// NOTE: this needs preact.js to be loaded by index.html
if(typeof(preact) == 'undefined'){
console.error('Preact.js required but not present.')
}
var h = preact.h
var object = require('lib/object')
var actions = require('lib/actions')
var features = require('lib/features')
var core = require('features/core')
var ribbons = require('imagegrid/ribbons')
/*********************************************************************/
// Preact Components...
//
// XXX at this point this is an experiment...
// XXX Questions:
// - do we do align in the Preact render or outside?
// - do we update preview in Preact render or outside?
//
class IGRibbonSet extends preact.Component {
render(props, state){
var data = props.data
var ribbons = data.ribbon_order.map(function(gid){
return h(IGRibbon, {
gid: gid,
current: data.current,
base: data.base,
data: data
}) })
var s = props.scale || 1
return h('div',
{
className: 'ribbon-set',
style: {
transform: 'scale('+ s +', '+ s +')',
},
}, [
h('div', {className: 'current-marker'}),
h('div', {className: 'ribbon-locator'}, ribbons),
])
}
}
// render:
// - ribbon
// - images
// - image marks
//
// XXX needs horizontal align...
class IGRibbon extends preact.Component {
render(props, state){
var data = props.data
var ribbon = props.gid
var images = data.ribbons[ribbon]
.map(function(gid){
var marks = data.tags.marked.indexOf(gid) >= 0 ?
h(IGImageMark, {
gid: gid,
type: 'selected',
data: data,
})
: []
return [
h(IGImage, {
gid: gid,
data: data,
})].concat(marks)
})
.reduce(function(a, b){ return a.concat(b) })
.filter(function(a){ return !!a })
var base = data.base == ribbon ? ['base'] : []
return h('div',
{
classList: ['ribbon'].concat(base).join(' '),
gid: props.gid,
style: {
// XXX offset...
},
}, images)
}
}
// render:
// - image
class IGImage extends preact.Component {
render(props, state){
var data = props.data || {}
var gid = props.gid
return h('div',
{
classList: ['image']
.concat(data.current == gid ? ['current'] : [])
.join(' '),
gid: gid || '',
style: {
// XXX background-image...
},
// XXX handle clicks???
})
}
}
// render:
// - image mark
class IGImageMark extends preact.Component {
render(props, state){
var gid = props.gid
var type = props.type
var data = props.data
return h('div',
{
classList: ['mark'].concat([type]).join(' '),
gid: gid,
})
}
}
//---------------------------------------------------------------------
var RibbonsClassPrototype = {
// XXX this is almost exclusively needed for determining scale...
createImage: function(){
return preact.render(h(IGImage)) },
}
RibbonsClassPrototype.__proto__ = ribbons.BaseRibbons.prototype.__proto__
var RibbonsPrototype = {
viewer: null,
dom: null,
createImage: RibbonsClassPrototype.createImage,
update: function(data, full){
if(!data){
return
}
full
&& this.clear()
this.dom = preact.render(
h(IGRibbonSet, {
data: data,
images: this.images || {},
scale: this.scale() || 1,
}),
this.viewer[0],
this.dom)
return this
},
clear: function(){
if(this.dom){
this.dom.remove()
delete this.dom
}
return this
},
}
RibbonsPrototype.__proto__ = ribbons.BaseRibbons.prototype
var Ribbons =
module.Ribbons =
object.makeConstructor('Ribbons',
RibbonsClassPrototype,
RibbonsPrototype)
/*********************************************************************/
// Checklist:
// - full ribbons:
// - build the initial DOM - DONE
// - centering - DONE
// - scaling - DONE (save/restore does not work)
// - ribbon up/down navigation - XXX
// XXX BUG: up does not hit limit on top ribbon,
// similar thing sometimes happened on down...
// XXX see if we need to put .focusRibbon(..) in ui???
// - shifting images - XXX
// - preview setting -
// - marks -
// - partial ribbons:
// - XXX
//
//
var RenderActions = actions.Actions({
get dom(){
return this.ribbons ? this.ribbons.viewer : undefined },
load: [
function(data){
return function(){
// XXX setup .ribbons
var viewer = data.viewer
viewer = viewer == null && this.ribbons != null
? this.dom
: viewer
if(this.ribbons == null){
this.ribbons = Ribbons(viewer, this.images)
// XXX is this correct???
//this.ribbons.__image_updaters = [this.updateImage.bind(this)]
this.dom.trigger('ig.attached')
} else {
//this.ribbons.clear()
this.ribbons.images = this.images
}
this.reload()
}
}],
// XXX do a full reload...
reload: [
function(force){
// full reload...
if(force == 'full'){
//this.stop()
/*
killAllWorkers()
.done(function(){
reload()
})
*/
return location.reload()
}
// XXX is this correct here???
this.ribbons.preventTransitions()
if(!this.ribbons){
return
}
// XXX need to get the data...
// XXX
this.ribbons.update(this.data, force)
this.ribbons.restoreTransitions()
}],
// XXX refresh the previews...
refresh: [
function(){
if(!this.ribbons){
return
}
// XXX need to get the data...
// XXX
this.ribbons.update(this.data)
}],
clear: [
function(){ this.ribbons && this.ribbons.clear() }],
resizing: [
core.Event(function(unit, size, overflow){
// This is a resizing protocol root function.
//
// This will never be used directly, but will wrap protocol user
// functions.
//
// As an example see: .viewScale(..)
// XXX stop current animation...
// XXX
// XXX call .resizingDone(..) when animations done...
// XXX
})],
viewScale: ['- Zoom/',
function(scale){
if(!this.ribbons){
return
}
if(scale == null || scale == '?'){
return this.ribbons.scale()
}
this.resizing.chainCall(this, function(){
this.ribbons.scale(scale)
}, 'scale', scale)
}],
fitImage: ['Zoom/Fit image',
function(count, overflow){
if(!this.ribbons){
return
}
if(count == '?'){
return this.ribbons.getScreenWidthImages()
}
this.resizing.chainCall(this, function(){
if(count != null){
overflow = overflow == false ? 0 : overflow
var o = overflow != null ? overflow
: count % 2 != 1 ? 0
: (this.config['fit-overflow'] || 0)
count += o
}
// set the scale...
this.ribbons.fitImage(count)
// XXX refresh image previews...
}, 'screenwidth', count, overflow)
}],
fitRibbon: ['Zoom/Fit ribbon vertically',
function(count, whole){
if(!this.ribbons){
return
}
if(count == '?'){
return this.ribbons.getScreenHeightRibbons()
}
this.resizing.chainCall(this, function(){
this.ribbons.fitRibbon(count, whole)
// XXX refresh image previews...
}, 'screenheight', count, whole)
}],
// XXX do we need updateImage here???
centerImage: ['- Interface/Center an image in ribbon horizontally',
function(target, align, offset, scale){
this.ribbons && this.ribbons.centerImage(target, align, offset, scale) }],
centerRibbon: ['- Interface/Center a ribbon vertically',
function(target){
this.ribbons && this.ribbons.centerRibbon(target) }],
ribbonRotation: ['- Interface|Ribbon/',
function(angle){
// XXX
}],
// XXX should these be here, in ui or in ribbons???
// XXX these are identical to features/ui-ribbons.js
focusRibbon: [
function(target, mode){
mode = mode || this.config['ribbon-focus-mode']
var c = this.data.getRibbonOrder()
var i = this.data.getRibbonOrder(target)
// NOTE: we are not changing the direction here based on
// this.direction as swap will confuse the user...
var direction = c < i ? 'before' : 'after'
if(mode == 'visual'){
var ribbons = this.ribbons
var r = this.data.getRibbon(target)
var t = ribbons.getImageByPosition('current', r)
if(t.length > 1){
t = t.eq(direction == 'before' ? 0 : 1)
}
t = ribbons.elemGID(t)
this.focusImage(t, r)
}
}],
})
var Render =
module.Render = core.ImageGridFeatures.Feature({
title: '',
doc: '',
tag: 'ui-preact-render',
exclusive: ['ui-render'],
depends: [
// XXX
],
actions: RenderActions,
handlers: [
[[
'shiftImageTo',
'shiftImageUp',
'shiftImageDown',
'shiftImageLeft',
'shiftImageRight',
], function(){
// XXX stub...
this.reload()
}],
],
})
/**********************************************************************
* vim:set ts=4 sw=4 : */ return module })