/** * Internal floating toolbar manager. * * Renders schema-declared toolbar chrome against one active editor host * contract so text and non-text hosts share the same floating UI behavior. * * Exposes: SFE.ToolbarManager */ (function() { 'use strict'; window.MWP = window.MWP || {}; window.MWP.SFE = window.MWP.SFE || {}; const SFE = window.MWP.SFE; SFE.ManagerData = SFE.ManagerData || {}; const TOOLBAR_FORMAT_ICONS = { undo: '', redo: '', bold: '', italic: '', strikethrough: '', link: '', alignNone: '', alignWide: '', alignFull: '', alignLeft: '', alignCenter: '', alignRight: '', textAlignLeft: '', textAlignCenter: '', textAlignRight: '', orderedList: '', unorderedList: '', indent: '', outdent: '', textAlignmentDropdown: '', replaceMedia: 'Replace', }; /** * Create one runtime toolbar button definition. * * @param {Object} config Button configuration. * @returns {Object} Runtime toolbar button definition. */ function createToolbarButton(config = {}) { return { icon: config.icon || '', title: config.title || '', action: typeof config.action === 'function' ? config.action : () => {}, className: config.className || '', formatKey: config.formatKey || '', formatType: config.formatType || '', value: Object.prototype.hasOwnProperty.call(config, 'value') ? config.value : undefined, tag: config.tag || '', activeTags: Array.isArray(config.activeTags) ? config.activeTags : [], }; } /** * Create one runtime toolbar dropdown definition. * * @param {Object} config Dropdown configuration. * @returns {Object} Runtime toolbar dropdown definition. */ function createToolbarDropdown(config = {}) { return { type: 'dropdown', title: config.title || '', defaultIcon: config.defaultIcon || '', options: Array.isArray(config.options) ? config.options.filter(Boolean) : [], formatKey: config.formatKey || '', }; } /** * Execute one schema block-attribute operation from a toolbar action. * * @param {Object|null} editor Editor host. * @param {string} operationId Schema operation id. * @param {*} value Requested operation value. * @returns {void} */ function executeSchemaBlockAttributeOperation(editor, operationId, value) { const operationExecutor = SFE.SchemaOperationExecutor || null; if (!operationExecutor || typeof operationExecutor.executeBlockAttributeOperation !== 'function') { return; } operationExecutor.executeBlockAttributeOperation({ editorHost: editor, operationId, value, saveHistory: true, }); } /** * Execute one schema list operation from a toolbar action. * * @param {Object|null} editor Editor host. * @param {string} operationId Schema or primitive operation id. * @returns {void} */ function executeSchemaListOperation(editor, operationId) { if (typeof editor?.executeListStructureOperation !== 'function') { return; } editor.executeListStructureOperation({ kind: operationId, }); } /** * Execute one list type switch from a toolbar action. * * @param {Object|null} editor Editor host. * @param {string} listType List type token. * @returns {void} */ function executeListTypeOperation(editor, listType) { const createTag = listType === 'ordered' ? 'ol' : 'ul'; const oppositeTag = listType === 'ordered' ? 'UL' : 'OL'; const operationExecutor = SFE.SchemaOperationExecutor || null; if (editor?._linkUIActive && typeof editor.closeLinkUI === 'function') { editor.closeLinkUI(); } const selection = window.getSelection(); if (selection?.rangeCount > 0) { const range = selection.getRangeAt(0); let startLi = range.startContainer; let endLi = range.endContainer; while (startLi && startLi !== editor?.element && startLi.tagName !== 'LI') { startLi = startLi.parentNode; } while (endLi && endLi !== editor?.element && endLi.tagName !== 'LI') { endLi = endLi.parentNode; } if (startLi && endLi && startLi !== endLi) { return; } } if ( (editor?.element?.tagName === 'UL' || editor?.element?.tagName === 'OL') && operationExecutor && typeof operationExecutor.executeCurrentListTypeChange === 'function' ) { const result = operationExecutor.executeCurrentListTypeChange({ editorHost: editor, value: listType, }); if (result) { return; } } const listItem = typeof editor?.getCurrentListItem === 'function' ? editor.getCurrentListItem() : null; if (!listItem) { if (typeof editor?.insertListManual === 'function') { editor.insertListManual(createTag); setTimeout(() => { const list = typeof editor?.getParentList === 'function' ? editor.getParentList() : null; if (list) { list.classList.add('wp-block-list'); } }, 10); } return; } const currentList = listItem.parentNode; if ( currentList && currentList.tagName === oppositeTag && typeof editor?.changeListType === 'function' ) { editor.changeListType(currentList, createTag); } } /** * Build one toolbar button for a schema token that toggles inline formatting. * * @param {string} token Token name. * @param {string} title Button title. * @param {string} icon Icon markup. * @param {string} tagName Inline tag name. * @param {string[]} activeTags Active-tag list for toolbar state. * @returns {Object} Runtime toolbar button definition. */ function createInlineFormatButton(token, title, icon, tagName, activeTags) { return createToolbarButton({ formatKey: token, title, icon, activeTags, action: (editor) => { if (typeof editor?.toggleInlineFormat !== 'function') { return; } editor.executeAction(() => { editor.toggleInlineFormat(tagName); }, { saveHistory: false }); }, }); } /** * Build one toolbar button for a schema token that opens link editing. * * @param {string} token Token name. * @param {string} title Button title. * @returns {Object} Runtime toolbar button definition. */ function createLinkButton(token, title) { return createToolbarButton({ formatKey: token, title, icon: TOOLBAR_FORMAT_ICONS.link, action: (editor) => { if (typeof editor?.showLinkUI !== 'function') { return; } const usesElementScopedLinkEditing = typeof editor.supportsElementLinkEditing === 'function' ? editor.supportsElementLinkEditing() : false; const existingLink = typeof editor.getParentElement === 'function' ? editor.getParentElement('a') : null; editor.showLinkUI(usesElementScopedLinkEditing ? null : existingLink); }, }); } /** * Determine whether schema declares element-scoped link editing for the * active text component. * * @param {HTMLElement|null} element Active editable element. * @param {Object} editorOptions Normalized component editor options. * @returns {boolean} True when the component edits its root anchor directly. */ function supportsElementScopedLinkToken(element, editorOptions = {}) { if (!element || element.tagName !== 'A') { return false; } const inlineFormatCapabilities = ( editorOptions?.inlineFormatCapabilities && typeof editorOptions.inlineFormatCapabilities === 'object' ) ? editorOptions.inlineFormatCapabilities : null; const attributeCapabilities = ( editorOptions?.attributeCapabilities && typeof editorOptions.attributeCapabilities === 'object' ) ? editorOptions.attributeCapabilities : null; const buttonLinkCapability = inlineFormatCapabilities?.buttonLink; const buttonLinkTag = typeof buttonLinkCapability?.tag === 'string' ? buttonLinkCapability.tag.trim().toLowerCase() : ''; if (buttonLinkTag !== 'a') { return false; } const attributes = Array.isArray(attributeCapabilities?.buttonLink?.attributes) ? attributeCapabilities.buttonLink.attributes .map(value => (typeof value === 'string' ? value.trim() : '')) .filter(Boolean) : []; return attributes.includes('url'); } /** * Build one toolbar button for a schema list indentation action. * * @param {string} token Token name. * @param {string} title Button title. * @param {string} icon Icon markup. * @param {string} operationId Schema list operation id. * @returns {Object} Runtime toolbar button definition. */ function createListIndentButton(token, title, icon, operationId) { return createToolbarButton({ formatKey: token, title, icon, action: (editor) => { executeSchemaListOperation(editor, operationId); }, }); } /** * Build one toolbar option for schema-backed block alignment. * * @param {string} optionKey Icon/format lookup key. * @param {string} title Option title. * @param {string} value Alignment value. * @returns {Object} Runtime toolbar option definition. */ function createBlockAlignOption(optionKey, title, value) { return createToolbarButton({ formatKey: optionKey, formatType: 'blockAlign', title, icon: TOOLBAR_FORMAT_ICONS[optionKey], value, action: (editor) => { executeSchemaBlockAttributeOperation(editor, 'set_align', value); }, }); } /** * Build one toolbar option for schema-backed text alignment. * * @param {string} optionKey Icon/format lookup key. * @param {string} title Option title. * @param {string} value Alignment value. * @returns {Object} Runtime toolbar option definition. */ function createTextAlignmentOption(optionKey, title, value) { return createToolbarButton({ formatKey: optionKey, formatType: 'textAlignment', title, icon: TOOLBAR_FORMAT_ICONS[optionKey], value, action: (editor) => { executeSchemaBlockAttributeOperation(editor, 'set_text_align', value); }, }); } /** * Build one toolbar option for schema-backed heading level changes. * * @param {string|number} level Heading level or element-tag value. * @returns {Object} Runtime toolbar option definition. */ function createHeadingLevelOption(level) { const value = typeof level === 'string' ? level.trim().toLowerCase() : level; const tag = typeof value === 'number' ? `h${value}` : value; const levelNumber = Number.parseInt(String(tag).replace(/^h/i, ''), 10); const title = tag === 'p' ? 'Paragraph' : tag === 'div' ? 'Div' : `Heading ${levelNumber}`; return createToolbarButton({ formatKey: String(tag), title, icon: title, tag, value, action: (editor) => { executeSchemaBlockAttributeOperation(editor, 'set_heading_level', value); }, }); } /** * Return the canonical built-in block-align toolbar option map. * * @returns {Map} Built-in block-align options keyed by value. */ function getBlockAlignOptionMap() { return new Map([ ['none', createBlockAlignOption('alignNone', 'None', 'none')], ['wide', createBlockAlignOption('alignWide', 'Wide Width', 'wide')], ['full', createBlockAlignOption('alignFull', 'Full Width', 'full')], ['left', createBlockAlignOption('alignLeft', 'Align Left', 'left')], ['center', createBlockAlignOption('alignCenter', 'Align Center', 'center')], ['right', createBlockAlignOption('alignRight', 'Align Right', 'right')], ]); } /** * Create the schema-backed text-alignment dropdown. * * @returns {Object} Runtime toolbar dropdown definition. */ function createTextAlignmentDropdown() { return createToolbarDropdown({ formatKey: 'textAlignment', title: 'Text Alignment', defaultIcon: TOOLBAR_FORMAT_ICONS.textAlignmentDropdown, options: [ createTextAlignmentOption('textAlignLeft', 'Align Text Left', 'left'), createTextAlignmentOption('textAlignCenter', 'Align Text Center', 'center'), createTextAlignmentOption('textAlignRight', 'Align Text Right', 'right'), ], }); } function getSchemaBlockAlignValues(editorOptions = {}) { const operations = Array.isArray(editorOptions?.operations) ? editorOptions.operations : []; const operation = operations.find(candidate => ( String(candidate?.id || '').trim() === 'set_align' && String(candidate?.kind || '').trim() === 'block_attribute_change' )) || null; const operationValues = Array.isArray(operation?.values) ? operation.values : null; const capabilityValues = Array.isArray(editorOptions?.attributeCapabilities?.align?.values) ? editorOptions.attributeCapabilities.align.values : null; const values = operationValues && operationValues.length ? operationValues : capabilityValues; return Array.isArray(values) ? values.map(value => String(value || '').trim().toLowerCase()).filter(Boolean) : []; } function createBlockAlignDropdown(editorOptions = {}) { const allowedValues = getSchemaBlockAlignValues(editorOptions); const optionMap = getBlockAlignOptionMap(); const options = allowedValues.map(value => optionMap.get(value)).filter(Boolean); if (!options.length) { return null; } return createToolbarDropdown({ formatKey: 'align', title: 'Align', defaultIcon: options[0].icon, options }); } /** * Create the schema-backed heading-level dropdown. * * @returns {Object} Runtime toolbar dropdown definition. */ function createHeadingDropdown(editorOptions = {}) { const operation = (editorOptions.operations || []).find(candidate => ( String(candidate?.id || '').trim() === 'set_heading_level' )) || null; const values = Array.isArray(operation?.values) ? operation.values : (editorOptions.attributeCapabilities?.headingLevels?.values || []); return createToolbarDropdown({ formatKey: 'headingLevels', title: 'Heading Level', defaultIcon: 'H', options: values.map(createHeadingLevelOption), }); } /** * Create the shared media-replace toolbar button. * * @returns {Object} Runtime toolbar button definition. */ function createReplaceMediaButton() { return createToolbarButton({ formatKey: 'replaceMedia', icon: TOOLBAR_FORMAT_ICONS.replaceMedia, title: 'Replace Media', className: 'mwp-sfe-editor-btn-text', action: (editor) => { if (typeof editor?.showMediaReplaceUI === 'function') { editor.showMediaReplaceUI(); } } }); } /** * Resolve one built-in schema toolbar token to a runtime format definition. * * @param {string} token Built-in schema toolbar token. * @param {HTMLElement|null} element Active editable element. * @param {Object} editorOptions Normalized editor options. * @returns {Object|null} Runtime toolbar definition or null. */ function resolveSchemaFormatToken(token, element, editorOptions = {}) { const normalizedToken = typeof token === 'string' ? token.trim() : ''; if (!normalizedToken) return null; switch (normalizedToken) { case 'undo': return createToolbarButton({ formatKey: 'undo', title: 'Undo', icon: TOOLBAR_FORMAT_ICONS.undo, action: (editor) => editor?.undo?.(), }); case 'redo': return createToolbarButton({ formatKey: 'redo', title: 'Redo', icon: TOOLBAR_FORMAT_ICONS.redo, action: (editor) => editor?.redo?.(), }); case 'bold': return createInlineFormatButton('bold', 'Bold', TOOLBAR_FORMAT_ICONS.bold, 'strong', ['strong', 'b']); case 'italic': return createInlineFormatButton('italic', 'Italic', TOOLBAR_FORMAT_ICONS.italic, 'em', ['em', 'i']); case 'strikethrough': return createInlineFormatButton('strikethrough', 'Strikethrough', TOOLBAR_FORMAT_ICONS.strikethrough, 's', ['s', 'strike']); case 'link': return createLinkButton('link', 'Link'); case 'buttonLink': return !supportsElementScopedLinkToken(element, editorOptions) ? null : createLinkButton('buttonLink', 'Button Link'); case 'textAlignment': return createTextAlignmentDropdown(); case 'align': return createBlockAlignDropdown(editorOptions); case 'headingLevels': return createHeadingDropdown(editorOptions); case 'orderedList': return createToolbarButton({ formatKey: 'orderedList', title: 'Ordered List', icon: TOOLBAR_FORMAT_ICONS.orderedList, action: (editor) => executeListTypeOperation(editor, 'ordered'), }); case 'unorderedList': return createToolbarButton({ formatKey: 'unorderedList', title: 'Unordered List', icon: TOOLBAR_FORMAT_ICONS.unorderedList, action: (editor) => executeListTypeOperation(editor, 'unordered'), }); case 'indent': return createListIndentButton('indent', 'Indent', TOOLBAR_FORMAT_ICONS.indent, 'indent_list_item'); case 'outdent': return createListIndentButton('outdent', 'Outdent', TOOLBAR_FORMAT_ICONS.outdent, 'outdent_list_item'); case 'replaceMedia': return createReplaceMediaButton(); default: return null; } } /** * Convert one nested schema format token spec into concrete toolbar configs. * * @param {Array} formatsSpec Nested schema token spec. * @param {HTMLElement|null} element Active editable element. * @param {Object} editorOptions Normalized editor options. * @param {number} depth Current recursion depth. * @returns {Array|null} Concrete toolbar config tree. */ function buildFormatsFromSchemaSpec(formatsSpec, element, editorOptions = {}, depth = 0) { if (depth > 3 || !Array.isArray(formatsSpec)) return null; const resolved = []; formatsSpec.forEach(item => { if (typeof item === 'string') { const format = resolveSchemaFormatToken(item, element, editorOptions); if (format) { resolved.push(format); } return; } if (Array.isArray(item)) { const group = buildFormatsFromSchemaSpec(item, element, editorOptions, depth + 1); if (Array.isArray(group) && group.length) { resolved.push(group); } } }); return resolved.length ? resolved : null; } /** * Flatten one nested toolbar definition tree into a single format list. * * @param {Array} items Nested toolbar definition tree. * @returns {Object[]} Flat format list. */ function flattenFormats(items = []) { const flattened = []; (items || []).forEach((item) => { if (Array.isArray(item)) { flattened.push(...flattenFormats(item)); return; } if (!item || typeof item !== 'object') { return; } flattened.push(item); if (item.type === 'dropdown' && Array.isArray(item.options)) { flattened.push(...flattenFormats(item.options)); } }); return flattened; } class ToolbarManager { constructor(host, options = {}) { this.host = host || null; this.options = options || {}; this.toolbar = null; this._toolbarPointerdownHandler = null; this._activeToolbarDropdownWrapper = null; if (this.host && typeof this.host.attachToolbarManager === 'function') { this.host.attachToolbarManager(this); } } getFormats() { return Array.isArray(this.host?.formats) ? this.host.formats : []; } /** * Return one flat runtime toolbar format list. * * @returns {Object[]} Flat toolbar format list. */ getFlatFormats() { return flattenFormats(this.getFormats()); } createToolbar() { if (this.toolbar && this.toolbar.parentNode) this.toolbar.remove(); this.toolbar = document.createElement('div'); this.toolbar.className = 'mwp-sfe-editor-toolbar'; const selectorSvg = ''; const createButton = (format) => { const btn = document.createElement('button'); btn.type = 'button'; btn.className = ['mwp-sfe-editor-btn', format.className || ''].filter(Boolean).join(' '); btn.innerHTML = format.icon; btn.title = format.title; btn.dataset.format = format.title; if (format.title === 'Undo') btn.dataset.action = 'undo'; if (format.title === 'Redo') btn.dataset.action = 'redo'; btn.addEventListener('mousedown', (event) => event.preventDefault()); btn.addEventListener('click', (event) => { event.preventDefault(); format.action(this.host); }); return btn; }; const buildItems = (items, container) => { items.forEach(item => { if (Array.isArray(item)) { const group = document.createElement('div'); group.className = 'mwp-sfe-btn-group'; buildItems(item, group); container.appendChild(group); } else if (item.type === 'dropdown') { const wrapper = document.createElement('div'); wrapper.className = 'mwp-sfe-dropdown'; const toggle = document.createElement('button'); toggle.className = 'mwp-sfe-editor-btn mwp-sfe-dropdown-toggle'; toggle.title = item.title; if (item.formatKey === 'headingLevels') { wrapper.classList.add('mwp-sfe-dropdown-heading-level'); toggle.innerHTML = `${item.defaultIcon} ${selectorSvg}`; } else { toggle.innerHTML = `${item.defaultIcon}`; } const content = document.createElement('div'); content.className = 'mwp-sfe-dropdown-content'; item.options.forEach(opt => { const optBtn = createButton(opt); optBtn.addEventListener('click', () => { if (item.formatKey === 'headingLevels') { toggle.innerHTML = `${opt.icon} ${selectorSvg}`; } else { toggle.innerHTML = `${opt.icon}`; } content.classList.remove('mwp-sfe-show'); }); content.appendChild(optBtn); }); toggle.addEventListener('mousedown', (event) => event.preventDefault()); toggle.addEventListener('click', (event) => { event.preventDefault(); this.toolbar.querySelectorAll('.mwp-sfe-dropdown-content.mwp-sfe-show').forEach(el => { if (el !== content) el.classList.remove('mwp-sfe-show'); }); content.classList.toggle('mwp-sfe-show'); this._activeToolbarDropdownWrapper = content.classList.contains('mwp-sfe-show') ? wrapper : null; }); wrapper.appendChild(toggle); wrapper.appendChild(content); if (container.className !== 'mwp-sfe-btn-group') { const group = document.createElement('div'); group.className = 'mwp-sfe-btn-group'; group.appendChild(wrapper); container.appendChild(group); } else { container.appendChild(wrapper); } } else { const btn = createButton(item); if (container.className !== 'mwp-sfe-btn-group') { const group = document.createElement('div'); group.className = 'mwp-sfe-btn-group'; group.appendChild(btn); container.appendChild(group); } else { container.appendChild(btn); } } }); }; buildItems(this.getFormats(), this.toolbar); this.attachToolbarDropdownCloseHandler(); const toolbarContainer = this.host?.options?.toolbarContainer || null; if (toolbarContainer) { toolbarContainer.innerHTML = ''; toolbarContainer.appendChild(this.toolbar); } else if (this.host?.element?.parentNode) { this.host.element.parentNode.insertBefore(this.toolbar, this.host.element); } this.updateUndoRedoButtons(); } closeToolbarDropdowns() { if (!this.toolbar) { return; } this.toolbar.querySelectorAll('.mwp-sfe-dropdown-content.mwp-sfe-show').forEach((element) => { element.classList.remove('mwp-sfe-show'); }); this._activeToolbarDropdownWrapper = null; } attachToolbarDropdownCloseHandler() { if (!this.toolbar) { return; } if (this._toolbarPointerdownHandler) { document.removeEventListener('pointerdown', this._toolbarPointerdownHandler, true); } this._toolbarPointerdownHandler = (event) => { if ( !this.toolbar || ( this._activeToolbarDropdownWrapper && this._activeToolbarDropdownWrapper.contains(event.target) ) ) { return; } this.closeToolbarDropdowns(); }; document.addEventListener('pointerdown', this._toolbarPointerdownHandler, true); } updateUndoRedoButtons() { if (!this.toolbar) return; const undoBtn = this.toolbar.querySelector('[data-action="undo"]'); const redoBtn = this.toolbar.querySelector('[data-action="redo"]'); const canUndo = typeof this.host?.canUndo === 'function' ? this.host.canUndo() : ( typeof this.host?.historyIndex === 'number' && this.host.historyIndex > 0 ); const canRedo = typeof this.host?.canRedo === 'function' ? this.host.canRedo() : ( typeof this.host?.historyIndex === 'number' && Array.isArray(this.host?.history) && this.host.historyIndex < this.host.history.length - 1 ); if (undoBtn) { undoBtn.disabled = !canUndo; } if (redoBtn) { redoBtn.disabled = !canRedo; } } setToolbarButtonDisabled(title, isDisabled) { if (!this.toolbar || !title) { return; } const button = this.toolbar.querySelector(`button[title="${title}"]`); if (button) { button.disabled = !!isDisabled; } } updateToolbarState() { if (!this.toolbar || !this.host) return; const selection = window.getSelection(); const hasSelectionInEditor = ( selection && selection.rangeCount && typeof this.host.isSelectionInEditor === 'function' && this.host.isSelectionInEditor() ); const parents = []; if (this.host.element?.tagName) { parents.push(this.host.element.tagName.toLowerCase()); } if (hasSelectionInEditor) { const range = selection.getRangeAt(0); let node = range.commonAncestorContainer; while (node && node !== this.host.element) { if (node.nodeType === 1) parents.push(node.tagName.toLowerCase()); node = node.parentNode; } } this.toolbar.querySelectorAll('.mwp-sfe-editor-btn').forEach(btn => { btn.classList.remove('mwp-sfe-editor-btn-active'); }); const checkActive = (format, tags) => { if (!format || !Array.isArray(tags) || !tags.some(tag => parents.includes(tag))) { return false; } const btn = this.toolbar.querySelector(`button[title="${format.title}"]`); if (btn) btn.classList.add('mwp-sfe-editor-btn-active'); return true; }; if (hasSelectionInEditor) { this.getFlatFormats().forEach((format) => { if (!Array.isArray(format?.activeTags) || !format.activeTags.length) { return; } checkActive(format, format.activeTags); }); } const currentAlign = typeof this.host.getBlockAlignState === 'function' ? this.host.getBlockAlignState() : 'none'; const currentTextAlignment = typeof this.host.getTextAlignmentState === 'function' ? this.host.getTextAlignmentState() : 'left'; const currentHeadingLevel = typeof this.host.getHeadingLevelState === 'function' ? this.host.getHeadingLevelState() : null; const getDropdownFormats = (items) => { const dropdowns = []; (items || []).forEach(item => { if (Array.isArray(item)) { dropdowns.push(...getDropdownFormats(item)); return; } if (item && item.type === 'dropdown') { dropdowns.push(item); } }); return dropdowns; }; getDropdownFormats(this.getFormats()).forEach(item => { const toggle = this.toolbar.querySelector(`button[title="${item.title}"]`); if (!toggle) return; const activeOption = item.options.find(opt => { const headingLevel = typeof this.host.getHeadingLevelValueForOption === 'function' ? this.host.getHeadingLevelValueForOption(opt) : null; if ( item.formatKey === 'headingLevels' && headingLevel === currentHeadingLevel ) { return true; } if ( typeof this.host.isTextAlignmentOptionActive === 'function' && this.host.isTextAlignmentOptionActive(opt, currentTextAlignment) ) { return true; } if ( typeof this.host.isBlockAlignOptionActive === 'function' && this.host.isBlockAlignOptionActive(opt, currentAlign) ) { return true; } return false; }); const selectorSvg = ''; if (activeOption) { toggle.innerHTML = item.formatKey === 'headingLevels' ? `${activeOption.icon} ${selectorSvg}` : `${activeOption.icon}`; } else { toggle.innerHTML = item.formatKey === 'headingLevels' ? `${item.defaultIcon} ${selectorSvg}` : `${item.defaultIcon}`; } item.options.forEach(opt => { const optionButton = this.toolbar.querySelector(`button[title="${opt.title}"]`); if (!optionButton) return; let isDisabled = false; if (item.formatKey === 'headingLevels') { const optionLevel = typeof this.host.getHeadingLevelValueForOption === 'function' ? this.host.getHeadingLevelValueForOption(opt) : null; isDisabled = optionLevel === currentHeadingLevel; } else if ( typeof this.host.getTextAlignmentValueForOption === 'function' && this.host.getTextAlignmentValueForOption(opt) ) { isDisabled = typeof this.host.isTextAlignmentOptionActive === 'function' ? this.host.isTextAlignmentOptionActive(opt, currentTextAlignment) : false; } else if ( typeof this.host.getBlockAlignValueForOption === 'function' && this.host.getBlockAlignValueForOption(opt) ) { isDisabled = typeof this.host.isBlockAlignOptionActive === 'function' ? this.host.isBlockAlignOptionActive(opt, currentAlign) : false; } optionButton.disabled = isDisabled; }); }); const operationExecutor = SFE.SchemaOperationExecutor || null; const currentListItem = typeof this.host.getCurrentListItem === 'function' ? this.host.getCurrentListItem() : null; const currentList = ( operationExecutor && typeof operationExecutor.getCurrentListElement === 'function' ) ? operationExecutor.getCurrentListElement(this.host) : ( typeof this.host.getParentList === 'function' ? this.host.getParentList() : null ); const canIndent = typeof this.host.canIndentListItem === 'function' ? this.host.canIndentListItem(currentListItem) : false; const canOutdent = typeof this.host.canOutdentListItem === 'function' ? this.host.canOutdentListItem(currentListItem) : false; this.setToolbarButtonDisabled('Ordered List', !currentList || currentList.tagName === 'OL'); this.setToolbarButtonDisabled('Unordered List', !currentList || currentList.tagName === 'UL'); this.setToolbarButtonDisabled('Indent', !currentListItem || !canIndent); this.setToolbarButtonDisabled('Outdent', !currentListItem || !canOutdent); this.updateUndoRedoButtons(); } destroy(options = {}) { const removeToolbar = options.removeToolbar !== false; if (this._toolbarPointerdownHandler) { document.removeEventListener('pointerdown', this._toolbarPointerdownHandler, true); this._toolbarPointerdownHandler = null; } this._activeToolbarDropdownWrapper = null; if (removeToolbar && this.toolbar && this.toolbar.parentNode) { this.toolbar.remove(); } if (this.host && typeof this.host.detachToolbarManager === 'function') { this.host.detachToolbarManager(this); } this.toolbar = removeToolbar ? null : this.toolbar; this.host = null; } static resolveFormats(editorOptions = {}, element = null) { const schemaFormats = buildFormatsFromSchemaSpec(editorOptions?.formats, element, editorOptions); return Array.isArray(schemaFormats) && schemaFormats.length ? schemaFormats : []; } } SFE.ToolbarManager = ToolbarManager; if (typeof module !== 'undefined' && module.exports) { module.exports = { ToolbarManager }; } })(); Hald do Brasil Home Catálogo Blog Nossa empresa Menu Home Catálogo Blog Nossa empresa LINHA COMPLETA DE ACESSÓRIOS PARA SUA OBRAOferecemos uma ampla variedade de acessórios projetados para atender a todas as exigências do seu projeto, com soluções que garantem eficiência, durabilidade e precisão em cada etapa. Ideal para todas as necessidades nossa linha de produtos é pensada para entregar o melhor desempenho independentemente da complexidade da tarefa.Clique AquiPrecisão e desempenho para cortes precisos em madeiraNossos Discos de Corte para Madeira, disponíveis em 24 e 36 dentes, são projetados para proporcionar cortes rápidos e precisos em madeiras de diversos tipos. Com excelente performance e acabamento suave, esses discos são ideais para trabalhos que exigem um corte limpo e sem estilhaços, garantindo qualidade superior em cada aplicação.Clique AquiPotência e precisão para sua obraOs Ponteiros e Talhadeiras Hald foram projetadas para proporcionar desempenho excepcional em demolições e modelagens. Com alta resistência e durabilidade, garantem cortes rápidos e precisos em concreto, alvenaria e outros materiais duros. Ideais para as necessidades mais exigentes, nossos produtos entregam eficiência e confiança em cada aplicação.Clique AquiCorte Imbatível para os Materiais Mais ResistentesDesenvolvidos para cortar os materiais mais resistentes, os Discos Diamantados Hald oferecem alta durabilidade e precisão, mesmo nas condições mais exigentes. Ideais para concreto, cerâmica, granito e outros materiais duros, nossos discos garantem cortes rápidos, limpos e sem comprometer a qualidade.Clique AquiDesbaste com excelência, confiabilidade e resultados superiores Com durabilidade e resistência excepcionais, eles garantem um desbaste rápido e preciso, mesmo nas condições mais desafiadoras. Perfeitos para aço, concreto, cerâmica e outros materiais, nossos discos são a escolha ideal para otimizar o tempo e a qualidade da sua obra.Clique AquiCortes Precisos e eficientes em Cada AplicaçãoPara cortes rápidos e precisos em aço inox, com alta resistência e durabilidade. Ideais para aplicações exigentes, nossos discos garantem um acabamento de alta qualidade, maximizando a produtividade e reduzindo o desgaste da ferramenta.Clique AquiDesempenho superior para os desafios da sua obraCom alta resistência e durabilidade, garantem impactos poderosos e cortes precisos em concreto, alvenaria e outros materiais pesados. Ideais para garantir eficiência e rapidez em suas obras.Clique AquiPerfuração com Precisão e Alta PerformanceAs Brocas Hald são a escolha certa para quem busca qualidade e durabilidade em cada perfuração. Desenvolvidas para atender às exigências de diversos materiais, como concreto, metal, madeira e alvenaria, nossas brocas garantem precisão e resistência, mesmo nas tarefas mais desafiadoras.Clique AquiCortadora de PisoCortadora de piso Cortadora de piso Cortadora de piso Cortadora de piso Cortadora de piso Cortadora de piso Cortadora de piso Cortadora de pisoClique Aqui LINHA COMPLETA DE ACESSÓRIOS PARA SUA OBRAOferecemos uma ampla variedade de acessórios projetados para atender a todas as exigências do seu projeto, com soluções que garantem eficiência, durabilidade e precisão em cada etapa. Ideal para todas as necessidades nossa linha de produtos é pensada para entregar o melhor desempenho independentemente da complexidade da tarefa.Clique AquiPrecisão e desempenho para cortes precisos em madeiraNossos Discos de Corte para Madeira, disponíveis em 24 e 36 dentes, são projetados para proporcionar cortes rápidos e precisos em madeiras de diversos tipos. Com excelente performance e acabamento suave, esses discos são ideais para trabalhos que exigem um corte limpo e sem estilhaços, garantindo qualidade superior em cada aplicação.Clique AquiPotência e precisão para sua obraOs Ponteiros e Talhadeiras Hald foram projetadas para proporcionar desempenho excepcional em demolições e modelagens. Com alta resistência e durabilidade, garantem cortes rápidos e precisos em concreto, alvenaria e outros materiais duros. Ideais para as necessidades mais exigentes, nossos produtos entregam eficiência e confiança em cada aplicação.Clique AquiCorte Imbatível para os Materiais Mais ResistentesDesenvolvidos para cortar os materiais mais resistentes, os Discos Diamantados Hald oferecem alta durabilidade e precisão, mesmo nas condições mais exigentes. Ideais para concreto, cerâmica, granito e outros materiais duros, nossos discos garantem cortes rápidos, limpos e sem comprometer a qualidade.Clique AquiDesbaste com excelência, confiabilidade e resultados superiores Com durabilidade e resistência excepcionais, eles garantem um desbaste rápido e preciso, mesmo nas condições mais desafiadoras. Perfeitos para aço, concreto, cerâmica e outros materiais, nossos discos são a escolha ideal para otimizar o tempo e a qualidade da sua obra.Clique AquiCortes Precisos e eficientes em Cada AplicaçãoPara cortes rápidos e precisos em aço inox, com alta resistência e durabilidade. Ideais para aplicações exigentes, nossos discos garantem um acabamento de alta qualidade, maximizando a produtividade e reduzindo o desgaste da ferramenta.Clique AquiDesempenho superior para os desafios da sua obraCom alta resistência e durabilidade, garantem impactos poderosos e cortes precisos em concreto, alvenaria e outros materiais pesados. Ideais para garantir eficiência e rapidez em suas obras.Clique AquiPerfuração com Precisão e Alta PerformanceAs Brocas Hald são a escolha certa para quem busca qualidade e durabilidade em cada perfuração. Desenvolvidas para atender às exigências de diversos materiais, como concreto, metal, madeira e alvenaria, nossas brocas garantem precisão e resistência, mesmo nas tarefas mais desafiadoras.Clique AquiCortadora de PisoCortadora de piso Cortadora de piso Cortadora de piso Cortadora de piso Cortadora de piso Cortadora de piso Cortadora de piso Cortadora de pisoClique Aqui LINHA COMPLETA DE ACESSÓRIOS PARA SUA OBRAOferecemos uma ampla variedade de acessórios projetados para atender a todas as exigências do seu projeto, com soluções que garantem eficiência, durabilidade e precisão em cada etapa. Ideal para todas as necessidades nossa linha de produtos é pensada para entregar o melhor desempenho independentemente da complexidade da tarefa.Clique AquiPrecisão e desempenho para cortes precisos em madeiraNossos Discos de Corte para Madeira, disponíveis em 24 e 36 dentes, são projetados para proporcionar cortes rápidos e precisos em madeiras de diversos tipos. Com excelente performance e acabamento suave, esses discos são ideais para trabalhos que exigem um corte limpo e sem estilhaços, garantindo qualidade superior em cada aplicação.Clique AquiPotência e precisão para sua obraOs Ponteiros e Talhadeiras Hald foram projetadas para proporcionar desempenho excepcional em demolições e modelagens. Com alta resistência e durabilidade, garantem cortes rápidos e precisos em concreto, alvenaria e outros materiais duros. Ideais para as necessidades mais exigentes, nossos produtos entregam eficiência e confiança em cada aplicação.Clique AquiCorte Imbatível para os Materiais Mais ResistentesDesenvolvidos para cortar os materiais mais resistentes, os Discos Diamantados Hald oferecem alta durabilidade e precisão, mesmo nas condições mais exigentes. Ideais para concreto, cerâmica, granito e outros materiais duros, nossos discos garantem cortes rápidos, limpos e sem comprometer a qualidade.Clique AquiDesbaste com excelência, confiabilidade e resultados superiores Com durabilidade e resistência excepcionais, eles garantem um desbaste rápido e preciso, mesmo nas condições mais desafiadoras. Perfeitos para aço, concreto, cerâmica e outros materiais, nossos discos são a escolha ideal para otimizar o tempo e a qualidade da sua obra.Clique AquiCortes Precisos e eficientes em Cada AplicaçãoPara cortes rápidos e precisos em aço inox, com alta resistência e durabilidade. Ideais para aplicações exigentes, nossos discos garantem um acabamento de alta qualidade, maximizando a produtividade e reduzindo o desgaste da ferramenta.Clique AquiDesempenho superior para os desafios da sua obraCom alta resistência e durabilidade, garantem impactos poderosos e cortes precisos em concreto, alvenaria e outros materiais pesados. Ideais para garantir eficiência e rapidez em suas obras.Clique AquiPerfuração com Precisão e Alta PerformanceAs Brocas Hald são a escolha certa para quem busca qualidade e durabilidade em cada perfuração. Desenvolvidas para atender às exigências de diversos materiais, como concreto, metal, madeira e alvenaria, nossas brocas garantem precisão e resistência, mesmo nas tarefas mais desafiadoras.Clique AquiCortadora de PisoCortadora de piso Cortadora de piso Cortadora de piso Cortadora de piso Cortadora de piso Cortadora de piso Cortadora de piso Cortadora de pisoClique Aqui produtos mais procurados Conheça os produtos mais produtos em nosso site Disco Diamantado Turbo 4" x 110mm Utilizado para cortes em materiais duros como concreto, mármore, granito e cerâmica. Ideal para trabalhos... Saiba mais Disco Diamantado Segmentado 4,5" x 110mm Utilizado para cortes em materiais como azulejos, mármores, cerâmicas, entre outros. Ideal para trabalhos... Saiba mais Disco Diamantado Segmentado 355 x 22.2mm Utilizado para cortes em materiais duros como concreto e asfalto. Ideal para trabalhos em construção... Saiba mais Broca SDS Plus 14 x 260mm Utilizada para perfurações em concreto, alvenaria e tijolos. Ideal para aplicações em montagem de estruturas... Saiba mais Broca SDS Plus 12 x 260mm Utilizada para perfurações em concreto, alvenaria e tijolos. Ideal para aplicações em montagem de estruturas... Saiba mais Broca SDS Plus 12 x 210mm Utilizada para perfurações em concreto, alvenaria e tijolos. Ideal para aplicações em montagem de estruturas... Saiba mais quem somos A Hald do Brasil nasceu com o propósito de oferecer máquinas, equipamentos e acessórios que atendam às necessidades dos profissionais da construção civil com excelência e inovação.Com um DNA jovem e dinâmico, disponibilizamos uma linha completa produtos modernos, tecnológicos e sustentáveis, que prima exclusivamente pela segurança. Nosso compromisso é transformar desafios em soluções, combinando eficiência, durabilidade e precisão em tudo o que fazemos. Precisão e desempenho para cortes perfeitosNossos Discos de Corte para Madeira, disponíveis em 24 e 36 dentes, são projetados para proporcionar cortes rápidos e precisos em madeiras de diversos tipos. Com excelente performance e acabamento suave, esses discos são ideais para trabalhos que exigem um corte limpo e sem estilhaços, garantindo qualidade superior em cada aplicação.Saiba maisDesempenho superior para os desafios da sua obraCom alta resistência e durabilidade, garantem impactos poderosos e cortes precisos em concreto, alvenaria e outros materiais pesados. Ideais para garantir eficiência e rapidez em suas obras.Saiba maisLinha completa de acessórios para sua obraOferecemos uma ampla variedade de acessórios projetados para atender a todas as exigências do seu projeto, com soluções que garantem eficiência, durabilidade e precisão em cada etapa. Ideal para todas as necessidades, nossa linha de produtos é pensada para entregar o melhor desempenho independentemente da complexidade da tarefaSaiba maisPerfuração com precisão e alta performanceAs Brocas Hald são a escolha certa para quem busca qualidade e durabilidade em cada perfuração. Desenvolvidas para atender às exigências de diversos materiais, como concreto, metal, madeira e alvenaria, nossas brocas garantem precisão e resistência, mesma nas tarefas mais desafiadorasSaiba maisPrecisão e desempenho para cortes precisos em madeiraNossos Discos de Corte para Madeira, disponíveis em 24 e 36 dentes, são projetados para proporcionar cortes rápidos e precisos em madeiras de diversos tipos. Com excelente performance e acabamento suave, esses discos são ideais para trabalhos que exigem um corte limpo e sem estilhaços, garantindo qualidade superior em cada aplicação.Saiba maisPotência e precisão para sua obraOs Ponteiros e Talhadeiras Hald foram projetas para proporcionar desempenho excepcional em demolições e modelagens. Com alta resistência e durabilidade, garantem cortes rápidos e precisos em concreto, alvenaria e outros materiais duros. Ideais para as necessidades mais exigentes, nossos produtos entregam eficiência e confiança em cada aplicação.Saiba maisCortes precisos e eficientes em cada aplicaçãoPara cortes rápidos e precisos em aço inox, com alta resistência e durabilidade. Ideais para aplicações exigentes, nossos discos garantem um acabamento de alta qualidade, maximizando a produtividade e reduzindo o desgaste de ferramentaSaiba mais Fique por dentro das novidades. Receba ofertas exclusivas, lançamentos de produtos e dicas para turbinar seus projetos diretamente no seu e-mail. Inscreva-se Atuamos em todo território nacional A Hald do Brasil Atua em todo território Nacional, oferecendo máquinas, equipamentos e acessórios de alta qualidade para a construção civil. Nosso compromisso é atender profissionais em todos os cantos do país, com produtos que aliam eficiência, durabilidade e precisão.Com uma ampla rede de distribuição e suporte, garantimos que nossos clientes tenham acesso rápido e confiável às melhores soluções do mercado. Não importa onde você esteja, a Hald do Brasil está ao seu lado, entregando inovação e desempenho para os desafios do dia a dia no setor da construção civil.Conte conosco para transformar suas ideias em realidade, com produtos desenvolvidos para quem preza por excelência e resultados em cada projeto. Conheça nosso blog Fique sempre informado com o nosso Blog Vai Montar Móveis? Conheça 4 Ferramentas que Podem te Ajudar! #3 Introdução Montar móveis pode ser uma tarefa desafiadora, especialmente se você não estiver preparado... Saiba mais Vai Montar Móveis? Conheça 4 Ferramentas que Podem te Ajudar! #2 Introdução Montar móveis pode ser uma tarefa desafiadora, especialmente se você não estiver preparado... Saiba mais Vai Montar Móveis? Conheça 4 Ferramentas que Podem te Ajudar! #3 Introdução Montar móveis pode ser uma tarefa desafiadora, especialmente se você não estiver preparado... Saiba mais Todos os direitos reservados - Hald do Brasil 2026
Conheça os produtos mais produtos em nosso site
A Hald do Brasil nasceu com o propósito de oferecer máquinas, equipamentos e acessórios que atendam às necessidades dos profissionais da construção civil com excelência e inovação.
Com um DNA jovem e dinâmico, disponibilizamos uma linha completa produtos modernos, tecnológicos e sustentáveis, que prima exclusivamente pela segurança. Nosso compromisso é transformar desafios em soluções, combinando eficiência, durabilidade e precisão em tudo o que fazemos.
A Hald do Brasil Atua em todo território Nacional, oferecendo máquinas, equipamentos e acessórios de alta qualidade para a construção civil. Nosso compromisso é atender profissionais em todos os cantos do país, com produtos que aliam eficiência, durabilidade e precisão.
Com uma ampla rede de distribuição e suporte, garantimos que nossos clientes tenham acesso rápido e confiável às melhores soluções do mercado. Não importa onde você esteja, a Hald do Brasil está ao seu lado, entregando inovação e desempenho para os desafios do dia a dia no setor da construção civil.
Conte conosco para transformar suas ideias em realidade, com produtos desenvolvidos para quem preza por excelência e resultados em cada projeto.
Fique sempre informado com o nosso Blog
Todos os direitos reservados - Hald do Brasil 2026