visbug-lib
Version:
<p align="center"> <img src="./assets/visbug.png" width="300" height="300" alt="visbug"> <br> <a href="https://www.npmjs.org/package/visbug"><img src="https://img.shields.io/npm/v/visbug.svg?style=flat" alt="npm latest version number"></a> <a href
265 lines (216 loc) • 6.87 kB
JavaScript
import $ from 'blingblingjs'
import hotkeys from 'hotkeys-js'
import {
Handles, Label, Overlay, Gridlines, Corners,
Hotkeys, Metatip, Ally, Distance, BoxModel, Grip
} from '../'
import {
Selectable, Moveable, Padding, Margin, EditText, Font,
Flex, Search, ColorPicker, BoxShadow, HueShift, MetaTip,
Guides, Screenshot, Position, Accessibility, draggable
} from '../../features/'
import { VisBugStyles } from '../styles.store'
import { VisBugModel } from './model'
import * as Icons from './vis-bug.icons'
import { provideSelectorEngine } from '../../features/search'
import { metaKey } from '../../utilities/'
import { PluginRegistry } from '../../plugins/_registry'
const modemap = {
'hex': 'toHexString',
'hsla': 'toHslString',
'rgba': 'toRgbString',
}
export default class VisBug extends HTMLElement {
constructor() {
super()
this.toolbar_model = VisBugModel
this._tutsBaseURL = 'tuts' // can be set by content script
this.$shadow = this.attachShadow({mode: 'closed'})
}
connectedCallback() {
this.$shadow.adoptedStyleSheets = [VisBugStyles]
if (!this.$shadow.innerHTML)
this.setup()
this.selectorEngine = Selectable(this)
this.colorPicker = ColorPicker(this.$shadow, this.selectorEngine)
provideSelectorEngine(this.selectorEngine)
this.toolSelected($('[data-tool="guides"]', this.$shadow)[0])
}
disconnectedCallback() {
this.deactivate_feature()
this.cleanup()
this.selectorEngine.disconnect()
hotkeys.unbind(
Object.keys(this.toolbar_model).reduce((events, key) =>
events += ',' + key, ''))
hotkeys.unbind(`${metaKey}+/`)
}
setup() {
this.$shadow.innerHTML = this.render()
this._colormode = modemap['hsla']
$('li[data-tool]', this.$shadow).on('click', e =>
this.toolSelected(e.currentTarget) && e.stopPropagation())
draggable({
el:this,
surface: this.$shadow.querySelector('ol:not([colors])'),
cursor: 'grab',
})
Object.entries(this.toolbar_model).forEach(([key, value]) =>
hotkeys(key, e => {
e.preventDefault()
this.toolSelected(
$(`[data-tool="${value.tool}"]`, this.$shadow)[0]
)
})
)
hotkeys(`${metaKey}+/,${metaKey}+.`, e =>
this.$shadow.host.style.display =
this.$shadow.host.style.display === 'none'
? 'block'
: 'none')
}
cleanup() {
const bye = [
...document.getElementsByTagName('visbug-hover'),
...document.getElementsByTagName('visbug-handles'),
...document.getElementsByTagName('visbug-label'),
...document.getElementsByTagName('visbug-gridlines'),
].forEach(el => el.remove())
this.teardown();
document.querySelectorAll('[data-pseudo-select=true]')
.forEach(el =>
el.removeAttribute('data-pseudo-select'))
}
toolSelected(el) {
if (typeof el === 'string')
el = $(`[data-tool="${el}"]`, this.$shadow)[0]
if (this.active_tool && this.active_tool.dataset.tool === el.dataset.tool) return
if (this.active_tool) {
this.active_tool.attr('data-active', null)
this.deactivate_feature()
}
el.attr('data-active', true)
this.active_tool = el
this[el.dataset.tool]()
}
render() {
return `
<visbug-hotkeys></visbug-hotkeys>
<ol>
${Object.entries(this.toolbar_model).reduce((list, [key, tool]) => `
${list}
<li aria-label="${tool.label} Tool" aria-description="${tool.description}" aria-hotkey="${key}" data-tool="${tool.tool}" data-active="${key == 'g'}">
${tool.icon}
${this.demoTip({key, ...tool})}
</li>
`,'')}
</ol>
<ol colors>
<li class="color" id="foreground" aria-label="Text" aria-description="Change the text color">
<input type="color" value="">
${Icons.color_text}
</li>
<li class="color" id="background" aria-label="Background or Fill" aria-description="Change the background color or fill of svg">
<input type="color" value="">
${Icons.color_background}
</li>
<li class="color" id="border" aria-label="Border or Stroke" aria-description="Change the border color or stroke of svg">
<input type="color" value="">
${Icons.color_border}
</li>
</ol>
`
}
demoTip({key, tool, label, description, instruction}) {
return `
<aside ${tool}>
<figure>
<img src="${this._tutsBaseURL}/${tool}.gif" alt="${description}" />
<figcaption>
<h2>
${label}
<span hotkey>${key}</span>
</h2>
<p>${description}</p>
${instruction}
</figcaption>
</figure>
</aside>
`
}
move() {
this.deactivate_feature = Moveable(this.selectorEngine)
}
margin() {
this.deactivate_feature = Margin(this.selectorEngine)
}
padding() {
this.deactivate_feature = Padding(this.selectorEngine)
}
font() {
this.deactivate_feature = Font(this.selectorEngine)
}
text() {
this.selectorEngine.onSelectedUpdate(EditText)
this.deactivate_feature = () =>
this.selectorEngine.removeSelectedCallback(EditText)
}
align() {
this.deactivate_feature = Flex(this.selectorEngine)
}
search() {
this.deactivate_feature = Search($('[data-tool="search"]', this.$shadow))
}
boxshadow() {
this.deactivate_feature = BoxShadow(this.selectorEngine)
}
hueshift() {
this.deactivate_feature = HueShift({
Color: this.colorPicker,
Visbug: this.selectorEngine,
})
}
inspector() {
this.deactivate_feature = MetaTip(this.selectorEngine)
}
accessibility() {
this.deactivate_feature = Accessibility()
}
guides() {
this.deactivate_feature = Guides(this.selectorEngine)
}
screenshot() {
this.deactivate_feature = Screenshot()
}
position() {
let feature = Position()
this.selectorEngine.onSelectedUpdate(feature.onNodesSelected)
this.deactivate_feature = () => {
this.selectorEngine.removeSelectedCallback(feature.onNodesSelected)
feature.disconnect()
}
}
execCommand(command) {
const query = `/${command}`
if (PluginRegistry.has(query))
return PluginRegistry.get(query)({
selected: this.selectorEngine.selection(),
query
})
return Promise.resolve(new Error("Query not found"))
}
get activeTool() {
return this.active_tool.dataset.tool
}
set tutsBaseURL(url) {
this._tutsBaseURL = url
this.setup()
}
set colorMode(mode) {
this._colormode = modemap[mode]
}
get colorMode() {
return this._colormode
}
}
customElements.define('vis-bug', VisBug)