@snippetify/book-reader
Version:
Book reader utilities
184 lines (152 loc) • 5.39 kB
JavaScript
const Events = require('./events')
class MenuBuilder {
constructor () {
this.events = {}
this.config = {}
this.selection = null
this.rootElement = document
}
static getInstance () {
return new MenuBuilder()
}
setRootElement (element) {
this.rootElement = element
return this
}
setConfig (config) {
this.config = config
return this
}
setEvents (events) {
this.events = events
return this
}
initListeners () {
this
.onActionListener()
.onSelectionListener()
.hideContextMenuListener()
.onNextOrPreviousClickedListener()
}
onSelectionListener () {
this.events.subscribe(Events.textSelected, selection => {
this.selection = selection
this.print($(this.rootElement).find('.selection'))
})
return this
}
hideContextMenuListener () {
$(document).on('scroll', () => this.unprint())
$(this.rootElement).on('mousedown', e => {
if ($(e.target).parents('[data-type="context-menu"]').length === 0) {
this.unprint()
}
})
return this
}
onNextOrPreviousClickedListener () {
$(this.rootElement).on('click', '[data-type="arrow-left"]', e => this.slideMenu(e.target, 'left'))
$(this.rootElement).on('click', '[data-type="arrow-right"]', e => this.slideMenu(e.target, 'right'))
return this
}
onActionListener () {
$(this.rootElement).on('click', '[data-event]', e => {
this.unprint()
this.events.notify($(e.target).data('event'), this.selection)
})
return this
}
build () {
const content = $(this.config.template)
content.find('[data-type="arrow-left"]').hide()
content.find('[data-type="arrow-right"]').hide()
this.config.items.forEach(item => {
content
.find('ul')
.append(
$('<li />')
.html(
$('<button />', { 'data-event': item.event }).text(item.name)
)
)
})
return content
}
slideMenu (target, direction) {
let slideWidth = 0
let leftDisabled = false
let rightDisabled = false
const slide = $(target).parents('.menu-wrapper').find('ul')
const position = Math.abs(parseFloat(slide.css('left')))
const increment = $(target).parents('.menu-wrapper').find('.menu-inner').width()
let leftGap = direction === 'right' ? (position + increment) : (position - increment)
slide.find('li').each((_, v) => (slideWidth += $(v).width()))
if ((leftGap + increment) > slideWidth) {
rightDisabled = true
leftGap = slideWidth - increment
}
if (leftGap < 0) {
leftGap = 0
leftDisabled = true
}
$(target).parents('.menu-wrapper').find('[data-type="arrow-left"]').prop('disabled', leftDisabled)
$(target).parents('.menu-wrapper').find('[data-type="arrow-right"]').prop('disabled', rightDisabled)
slide.animate({ left: -leftGap })
}
print (anchor) {
const screenWidth = $(window).width()
const anchorwidth = $(anchor).last().width()
const anchorHeight = $(anchor).last().height()
const anchorTop = $(anchor).last().offset().top
const anchorCenter = $(anchor).last().offset().left + (anchorwidth / 2)
let innerMenuWidth = 0
let menu = $(this.rootElement).find('[data-type="context-menu"]')
// Create and Inject menu in DOM if not injected yet
if (menu.length === 0) {
$(this.rootElement).append(this.build())
menu = $(this.rootElement).find('[data-type="context-menu"]')
}
// Si le menu est plus large que l'ecran, activer le slider mode
menu.find('li').each((_, elem) => {
innerMenuWidth += $(elem).width()
if ((innerMenuWidth + 120) > $(window).width()) {
menu.find('[data-type="arrow-left"]').show()
menu.find('[data-type="arrow-right"]').show()
menu.find('.menu-inner').css({ maxWidth: innerMenuWidth })
return false
}
})
// Always reset slider
menu.find('.menu-inner').find('ul').css({ left: 0 })
menu.find('[data-type="arrow-left"]').prop('disabled', true)
const gap = 2 // decalage
const menuWidth = menu.width()
const menuHeight = menu.height()
let menuLeft = anchorCenter - (menuWidth / 2)
let menuTop = anchorTop + anchorHeight - 8
const menuTopToWindow = menuTop - $(window).scrollTop()
// Selectionner le caret à afficher
if ((menuTopToWindow + menuHeight + 30) > $(window).height()) {
menuTop = anchorTop - menuHeight + 10
menu.find('[data-type="arrow-down"]').addClass('active')
menu.find('[data-type="arrow-up"]').removeClass('active')
} else {
menu.find('[data-type="arrow-up"]').addClass('active')
menu.find('[data-type="arrow-down"]').removeClass('active')
}
menuLeft = menuLeft < 0 ? gap : menuLeft
menuLeft = (menuLeft + menuWidth) > screenWidth ? (screenWidth - menuWidth - gap) : menuLeft
// Afficher le menu, positionner le caret ainsi que le menu
menu.find('.arrow-wrapper').find('i').css({ marginLeft: anchorCenter - menuLeft - 7 })
menu.fadeIn().offset({ top: menuTop, left: menuLeft })
return this
}
unprint () {
$(this.rootElement).find('[data-type="context-menu"]').fadeOut()
}
remove () {
$(this.rootElement).find('[data-type="context-menu"]').remove()
}
}
module.exports = MenuBuilder
module.exports.default = MenuBuilder