267 lines
8.3 KiB
JavaScript
267 lines
8.3 KiB
JavaScript
import { util } from '../jsmind/jsmind.util.js'
|
|
import { getRelativePosition } from './custom.util.js'
|
|
import { PALETTE_COLORS } from './custom.config.js'
|
|
|
|
const TOOLBAR_ID = 'jsmind-toolbar'
|
|
|
|
/**
|
|
* jsMind 工具列管理
|
|
* jsMind Toolbar Manager
|
|
*/
|
|
export class JsmindToolbar {
|
|
/**
|
|
* 建構工具列
|
|
* Constructor for toolbar
|
|
* @param {Object} jm - jsMind 實例 (jsMind instance)
|
|
* @param {Object} options - jsMind 實例 (options)
|
|
*/
|
|
constructor(jm, options) {
|
|
this.jm = jm
|
|
this.container = document.getElementById(jm.options.container)
|
|
this.toolbarNodeId = null
|
|
this.toolbar = null
|
|
this.bgColorPalette = null
|
|
this.strokeColorPalette = null
|
|
this.textColorPalette = null
|
|
this.options = options
|
|
this.init()
|
|
}
|
|
|
|
/**
|
|
* 初始化工具列事件
|
|
* Initialize toolbar events
|
|
*/
|
|
init() {
|
|
// 監聽節點選取事件
|
|
// Listen for node selection events
|
|
this.jm.add_event_listener((e, f, g) => {
|
|
// 忽略非選擇節點事件
|
|
// Ignore non-selection events
|
|
if (e !== 4) return
|
|
|
|
const node = this.jm.get_selected_node()
|
|
if (!node || node.id === this.toolbarNodeId) return
|
|
|
|
this.toolbarNodeId = node.id
|
|
if (!this.toolbar) {
|
|
this.createToolbar()
|
|
}
|
|
this.moveToolbar(node)
|
|
})
|
|
|
|
// 確保不會重複綁定點擊事件
|
|
// Ensure click event is not bound multiple times
|
|
this.container.removeEventListener('click', this.onClickOutside)
|
|
this.container.addEventListener('click', this.onClickOutside.bind(this))
|
|
}
|
|
|
|
/**
|
|
* 處理點擊事件來隱藏工具列
|
|
* Handle click event to hide toolbar
|
|
* @param {Event} e - 事件對象 (Event object)
|
|
*/
|
|
onClickOutside(e) {
|
|
const clickedNode = e.target.tagName === 'JMNODE'
|
|
const clickedToolbar = e.target.closest(`#${TOOLBAR_ID}`)
|
|
if (!clickedNode && !clickedToolbar && this.toolbar) {
|
|
this.hideToolbar()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 建立工具列 UI
|
|
* Create toolbar UI
|
|
*/
|
|
createToolbar() {
|
|
this.toolbar = document.createElement('div')
|
|
this.toolbar.id = TOOLBAR_ID
|
|
|
|
// 建立工具列按鈕
|
|
// Create toolbar buttons
|
|
const buttons = [
|
|
{ id: 'toolbar-add-child-btn', text: this.options.text.addNode, onClick: this.handleAddChild.bind(this) },
|
|
{ id: 'toolbar-delete-btn', text: this.options.text.deleteNode, onClick: this.handleDelete.bind(this) },
|
|
{
|
|
id: 'toolbar-stroke-color-btn',
|
|
text: this.options.text.strokeColor,
|
|
onClick: this.handleStrokeColor.bind(this),
|
|
},
|
|
{
|
|
id: 'toolbar-bg-color-btn',
|
|
text: this.options.text.bgColor,
|
|
onClick: this.handleBgColor.bind(this),
|
|
},
|
|
{
|
|
id: 'toolbar-text-color-btn',
|
|
text: this.options.text.textColor,
|
|
onClick: this.handleTextColor.bind(this),
|
|
},
|
|
]
|
|
buttons.forEach((button) => {
|
|
const btn = document.createElement('button')
|
|
btn.id = button.id
|
|
btn.innerText = button.text
|
|
btn.onclick = button.onClick
|
|
this.toolbar.appendChild(btn)
|
|
// 附加顏色選單
|
|
// Append color palettes to corresponding buttons
|
|
if (button.id === 'toolbar-bg-color-btn') {
|
|
this.bgColorPalette = this.createColorPalette(
|
|
(color) => this.setNodeStyle('background-color', color),
|
|
btn
|
|
)
|
|
}
|
|
if (button.id === 'toolbar-stroke-color-btn') {
|
|
this.strokeColorPalette = this.createColorPalette(
|
|
(color) => this.setNodeStyle('leading-line-color', color),
|
|
btn
|
|
)
|
|
}
|
|
if (button.id === 'toolbar-text-color-btn') {
|
|
this.textColorPalette = this.createColorPalette(
|
|
(color) => this.setNodeStyle('foreground-color', color),
|
|
btn
|
|
)
|
|
}
|
|
})
|
|
|
|
this.container.appendChild(this.toolbar)
|
|
}
|
|
|
|
/**
|
|
* 移動工具列至選中節點
|
|
* Move the toolbar to the selected node
|
|
*/
|
|
moveToolbar(node) {
|
|
const nodeElement = node._data.view.element
|
|
if (!nodeElement) return
|
|
|
|
const { left, top } = getRelativePosition(nodeElement, this.container)
|
|
this.toolbar.style.left = `${left}px`
|
|
this.toolbar.style.top = `${top - 40}px`
|
|
this.toolbar.style.display = 'block'
|
|
|
|
// 根節點則隱藏刪除與線條顏色按鈕
|
|
// Hide delete & stroke color buttons if the node is root
|
|
const deleteBtn = this.toolbar.querySelector('#toolbar-delete-btn')
|
|
const strokeColorBtn = this.toolbar.querySelector('#toolbar-stroke-color-btn')
|
|
|
|
if (node.id === 'root') {
|
|
deleteBtn.style.display = 'none'
|
|
strokeColorBtn.style.display = 'none'
|
|
} else {
|
|
deleteBtn.style.display = 'inline-block'
|
|
strokeColorBtn.style.display = 'inline-block'
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 隱藏工具列
|
|
* Hide the toolbar
|
|
*/
|
|
hideToolbar() {
|
|
this.toolbar.style.display = 'none'
|
|
this.bgColorPalette.style.display = 'none'
|
|
this.strokeColorPalette.style.display = 'none'
|
|
this.textColorPalette.style.display = 'none'
|
|
this.toolbarNodeId = null
|
|
}
|
|
|
|
/**
|
|
* 建立顏色選單
|
|
* Create color palette
|
|
*/
|
|
createColorPalette(onSelect, button) {
|
|
const colorPalette = document.createElement('div')
|
|
colorPalette.classList.add('toolbar-color-palette')
|
|
colorPalette.style.display = 'none'
|
|
|
|
PALETTE_COLORS.forEach((color) => {
|
|
const colorBox = document.createElement('div')
|
|
colorBox.classList.add('toolbar-color-palette-box')
|
|
colorBox.style.backgroundColor = color
|
|
colorBox.onclick = () => {
|
|
onSelect(color)
|
|
colorPalette.style.display = 'none'
|
|
}
|
|
colorPalette.appendChild(colorBox)
|
|
})
|
|
|
|
button.appendChild(colorPalette)
|
|
return colorPalette
|
|
}
|
|
|
|
/**
|
|
* 設定節點樣式
|
|
* Set node style
|
|
*/
|
|
setNodeStyle(style, color) {
|
|
if (!this.toolbarNodeId) return
|
|
const node = this.jm.get_node(this.toolbarNodeId)
|
|
if (!node) return
|
|
node.data[style] = color
|
|
if (style === 'leading-line-color') this.jm.view.show_lines()
|
|
else this.jm.view.restore_selected_node_custom_style(node)
|
|
}
|
|
|
|
/**
|
|
* 處理新增節點事件
|
|
* Handle add child node event
|
|
*/
|
|
handleAddChild(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation()
|
|
if (!this.toolbarNodeId) return
|
|
const node = this.jm.get_node(this.toolbarNodeId)
|
|
if (!node) return
|
|
|
|
const newNode = this.jm.add_node(node, util.uuid.newid(), 'NewNode')
|
|
this.jm.select_node(newNode)
|
|
}
|
|
|
|
/**
|
|
* 處理刪除節點事件
|
|
* Handle delete node event
|
|
*/
|
|
handleDelete(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation()
|
|
if (!this.toolbarNodeId) return
|
|
const node = this.jm.get_node(this.toolbarNodeId)
|
|
if (!node) return
|
|
this.jm.remove_node(node)
|
|
this.hideToolbar()
|
|
}
|
|
|
|
/**
|
|
* 處理其他樣式設定事件
|
|
* Handle style setting event
|
|
*/
|
|
handleStrokeColor(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation()
|
|
this.toggleColorPalette(this.strokeColorPalette)
|
|
}
|
|
handleBgColor(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation()
|
|
this.toggleColorPalette(this.bgColorPalette)
|
|
}
|
|
handleTextColor(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation()
|
|
this.toggleColorPalette(this.textColorPalette)
|
|
}
|
|
|
|
/**
|
|
* 顯示或隱藏顏色選單
|
|
* Toggle color palette display
|
|
*/
|
|
toggleColorPalette(palette) {
|
|
;[this.bgColorPalette, this.strokeColorPalette, this.textColorPalette].forEach((p) => {
|
|
if (p !== palette) p.style.display = 'none'
|
|
})
|
|
palette.style.display = palette.style.display === 'block' ? 'none' : 'block'
|
|
}
|
|
}
|