/**
* @package ZMLPaint\Assets\Js
*
* 古腾堡编辑器
*
* @since 1.0.0
*/
(function () {
//* 设置数据
const langs = zml_paint_editor.langs || {};
const options = zml_paint_editor.options || {};
//* 声明对象
const editor = wp.blockEditor;
const blocks = wp.blocks;
const element = wp.element;
const compose = wp.compose;
const components = wp.components;
const addFilter = wp.hooks.addFilter;
const el = element.createElement;
const Fragment = element.Fragment;
const InspectorControls = editor.InspectorControls;
const createHigherOrderComponent = compose.createHigherOrderComponent;
//* 默认语言(确保有值)
const defaultLang = options['paint-lang'] || '';
//* 注册
blocks.registerBlockType('zml-paint/code-block', {
apiVersion: 3,
title: '代码高亮',
icon: 'editor-code',
category: 'formatting',
attributes: {
className: {
type: 'string',
'default': 'zml-paint'
},
langType: {
type: 'string',
'default': defaultLang
},
langName: {
type: 'string',
'default': ''
},
mixedName: {
type: 'string',
'default': ''
},
mixedValue: {
type: 'string',
'default': 'false'
},
codeTitle: {
type: 'string',
'default': ''
},
codeFirst: {
type: 'string',
'default': ''
},
codeMark: {
type: 'string',
'default': ''
},
showGutter: {
type: 'boolean',
'default': options['paint-gutter']
},
showToolbar: {
type: 'boolean',
'default': options['paint-toolbar']
},
autoFold: {
type: 'boolean',
'default': options['paint-fold']
},
autoWrap: {
type: 'boolean',
'default': options['paint-wrap']
},
codeContent: {
type: 'string',
source: 'text',
selector: 'pre'
},
},
//* 编辑
edit: function (props) {
//* 设置对象
const attributes = props.attributes;
const elementNode = [];
const langList = [];
const mixedList = [];
//* 默认语言
if (defaultLang !== '') {
langList.push(el('option', { value: defaultLang, key: defaultLang }, langs[defaultLang] || defaultLang));
} else {
langList.push(el('option', { value: '', key: '' }, '代码语言'));
}
//* 其他语言
Object.keys(langs).forEach(function (key) {
if (key === defaultLang) {
return;
}
langList.push(
el('option', { value: key, key: key }, langs[key])
);
});
//* 混合HTML选项
mixedList.push(
el('option', { value: 'false', key: 'false' }, '无 HTML'),
el('option', { value: 'true', key: 'true' }, '混合 HTML')
);
//* 生成语言选择框
elementNode.push(
el('div', {className: 'code-option', key: 'code-option'},
el('select', {
value: attributes.langType,
onChange: function (event) {
const sel = event.target.options[event.target.selectedIndex];
props.setAttributes({
langType: sel.value,
langName: sel.text
});
event.preventDefault();
}
}, langList),
el('select', {
value: attributes.mixedValue,
onChange: function (event) {
const sel = event.target.options[event.target.selectedIndex];
props.setAttributes({
mixedValue: sel.value,
mixedName: sel.text
});
event.preventDefault();
}
}, mixedList)
)
);
//* 生成代码输入框
elementNode.push(
el('textarea', {
key: 'code-textarea',
className: 'code-textarea',
placeholder: '代码内容...',
value: attributes.codeContent,
onChange: function (event) {
props.setAttributes({ codeContent: event.target.value });
autoHeight(event.target);
event.preventDefault();
},
})
);
return el('div', { className: attributes.className }, elementNode );
},
//* 保存
save: function (props) {
const a = props.attributes;
const cls = [
'paint:' + (a.langType || ''),
a.mixedValue === 'true' ? 'mixed:true' : '',
a.codeFirst ? 'first:' + a.codeFirst : '',
a.codeMark ? 'mark:[' + a.codeMark + ']' : '',
a.showGutter != options['paint-gutter'] ? 'gutter:' + a.showGutter : '',
a.showToolbar != options['paint-toolbar'] ? 'toolbar:' + a.showToolbar : '',
a.autoFold != options['paint-fold'] ? 'fold:' + a.autoFold : '',
a.autoWrap != options['paint-wrap'] ? 'wrap:' + a.autoWrap : '',
].filter(Boolean).join(' ');
const saveProps = { className: cls };
if (a.codeTitle) {
saveProps.title = a.codeTitle;
}
return el('pre', saveProps, parseCode( a.codeContent ));
}
});
//* 选项设置
const blockSetting = createHigherOrderComponent(function (BlockEdit) {
return function (props) {
//* 设置对象、选项值。
const settingNode = [];
const attributes = props.attributes;
const codeTitle = attributes.codeTitle;
const codeFirst = attributes.codeFirst;
const codeMark = attributes.codeMark;
const showGutter = attributes.showGutter;
const showToolbar = attributes.showToolbar;
const autoFold = attributes.autoFold;
const autoWrap = attributes.autoWrap;
//* 基本设置部分
settingNode.push(
el(components.PanelBody, {
title: '基本设置',
initialOpen: true,
key: 'panel-basic'
},
el(components.TextControl, {
label: '标题',
value: codeTitle,
onChange: ( codeTitle ) => props.setAttributes({ codeTitle })
}),
el(components.TextControl, {
label: '起始行号',
value: codeFirst,
onChange: ( codeFirst ) => props.setAttributes({ codeFirst })
}),
el(components.TextControl, {
label: '高亮行',
value: codeMark,
onChange: ( codeMark ) => props.setAttributes({ codeMark })
})
)
);
//* 显示相关设置部分
settingNode.push(
el(components.PanelBody, {
title: '显示设置',
initialOpen: true,
key: 'panel-display'
},
el( components.CheckboxControl, {
label:'显示行号',
checked: !!showGutter,
onChange: ( showGutter ) => props.setAttributes( { showGutter } )
}),
el( components.CheckboxControl, {
label:'显示工具栏',
checked: !!showToolbar,
onChange: ( showToolbar ) => props.setAttributes( { showToolbar } )
}),
el( components.CheckboxControl, {
label:'自动折叠',
checked: !!autoFold,
onChange: ( autoFold ) => props.setAttributes( { autoFold } )
}),
el( components.CheckboxControl, {
label:'自动换行',
checked: !!autoWrap,
onChange: ( autoWrap ) => props.setAttributes( { autoWrap } )
})
)
);
if (validBlock(props.name) && props.isSelected) {
return el(Fragment, null,
el(BlockEdit, props),
el(InspectorControls, { group: 'settings' }, settingNode)
);
}
return el(BlockEdit, props);
}
}, 'blockSetting');
//* 生成标签(作为兜底,追加 className,不覆盖)
function blockClass(extraProps, blockType, attributes) {
if (validBlock(blockType.name)) {
const attr_lang = 'paint:' + (attributes.langType || '');
const attr_mixed = attributes.mixedValue === 'true' ? ' mixed:true' : '';
const attr_first = attributes.codeFirst === '' ? '' : ' first:' + attributes.codeFirst;
const attr_mark = attributes.codeMark === '' ? '' : ' mark:[' + attributes.codeMark + ']';
const attr_gutter = attributes.showGutter == options['paint-gutter'] ? '' : ' gutter:' + attributes.showGutter;
const attr_toolbar = attributes.showToolbar == options['paint-toolbar'] ? '' : ' toolbar:' + attributes.showToolbar;
const attr_fold = attributes.autoFold == options['paint-fold'] ? '' : ' fold:' + attributes.autoFold;
const attr_wrap = attributes.autoWrap == options['paint-wrap'] ? '' : ' wrap:' + attributes.autoWrap;
const save_class = attr_lang + attr_mixed + attr_first + attr_mark + attr_gutter + attr_toolbar + attr_fold + attr_wrap;
extraProps.className = [extraProps.className, save_class].filter(Boolean).join(' ').trim();
if (attributes.codeTitle !== '' && !extraProps.title) {
extraProps.title = attributes.codeTitle;
}
}
return extraProps;
}
//* 解析代码
function parseCode(code) {
if (code) {
return code.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
} else {
return code;
}
}
//* 自动调整高度
function autoHeight(elem) {
elem.style.height = 'auto';
elem.style.height = elem.scrollHeight + 'px';
}
//* 验证BLOCK
function validBlock(blockName) {
const blocks = ['zml-paint/code-block'];
return blocks.indexOf(blockName) !== -1;
}
//* 加载时调整高度
document.addEventListener('DOMContentLoaded', function () {
const codeBlocks = document.querySelectorAll('.code-textarea');
for (let index = 0; index < codeBlocks.length; index++) {
let element = codeBlocks[index];
autoHeight( element );
}
});
//* 过滤器
addFilter('editor.BlockEdit', 'zml-paint/block-setting', blockSetting);
addFilter('blocks.getSaveContent.extraProps', 'zml-paint/block-class', blockClass);
})(window.wp);
<?php
/**
* Package ZMLTest\Pages
*/
namespace ZMLTest\Page;
class Admin {
private $options;
public function __construct() {
$this->options = \ZMLTest\Work\get_options();
\add_action( 'admin_menu', [ $this, 'admin_menu' ] );
}
}
版权声明:文章底部版权声明
初始发布:2026年11月6日





还没有任何评论!