eapp-amis-theme-editor
Version:
amis主题编辑器
527 lines (521 loc) • 17 kB
JavaScript
/**
* amis-theme-editor v2.0.11-beta.2
* Copyright 2018-2023 @fex
*/
import { __awaiter, __generator, __spreadArray, __assign } from 'tslib';
import React__default from 'react';
import { Layout, AsideNav, Icon as Icon$1, Button } from 'amis-ui';
import { classnames, parseQuery, qsstringify, render } from 'amis-core';
import { cloneDeep, defaultsDeep, isEqual } from 'lodash';
import { ParseThemeData, insertStyle, getGlobalData } from 'amis-theme-editor-helper';
import baseContent from './BaseContent.js';
import '../renderers/PaddingAndMargin.js';
import '../renderers/Border.js';
import '../renderers/Radius.js';
import '../renderers/ColorPicker.js';
import '../renderers/Size.js';
import '../renderers/Font.js';
import '../renderers/Shadow.js';
import '../renderers/ThemeWrapper.js';
import Color from '../global/Color.js';
import Font from '../global/Font.js';
import Border from '../global/Border.js';
import Size from '../global/Size.js';
import Shadow from '../global/Shadow.js';
import { getAllComponent } from '../component/index.js';
import '../icons/index.js';
import { getSchemaTpl } from '../tpl.js';
import { i18n } from 'i18n-runtime';
import { Icon } from 'amis';
/**
* 主题编辑器
*/
var CSS_NAMESPACE = ['.ThemeEditor-body-content-item-content', '.Theme-ColorSelect', '.Theme-ColorPicker', '.amis-dialog-widget'];
var navList = [{
label: '',
children: [{
icon: 'global',
label: i18n("b3652bdb239c558126e9981229c46adf"),
key: 'global'
}, {
icon: 'component',
label: i18n("16e90fff8cc77b24a91541b5d76b2c8b"),
key: 'component',
isSvg: true
}, {
icon: 'code',
label: i18n("cf209d136a55c008586b4d75832a55e0"),
key: 'custom'
}]
}];
var globalStyleTab = [{
label: '',
children: [{
label: i18n("6b36c6f7ec834692ec6c8e3816349fdd"),
key: 'color',
component: Color
}, {
label: i18n("8456bc40d48da5af833f1c63385e9879"),
key: 'font',
component: Font
}, {
label: i18n("961534b4ea37e4e88aada736b299d063"),
key: 'border',
component: Border
}, {
label: i18n("c8339fd2a85af4ba66084d28df808de4"),
key: 'space',
component: Size
}, {
label: i18n("803205e38834280d9e6a75993ac00764"),
key: 'shadow',
component: Shadow
}]
}];
var componentStyleTab = getAllComponent();
var customStyleTab = [{
label: '',
children: []
}];
var tabList = {
global: globalStyleTab,
component: componentStyleTab,
custom: customStyleTab
};
var themeConfig = {
title: i18n("b9420c6f66a59ff2d7e324dc736dcd47"),
path: 'config',
activeId: null,
body: getSchemaTpl('normal', [{
type: 'input-text',
label: i18n("2cdc6176e4ee0e11c71235932d3619ce"),
required: true,
name: 'name'
},
// {
// type: 'input-text',
// label: '标识',
// disabled: true,
// name: 'key'
// },
// {
// type: 'radios',
// label: '启用状态',
// name: 'status',
// options: [
// {
// label: '启用',
// value: true
// },
// {
// label: '停用',
// value: false
// }
// ]
// },
{
type: 'textarea',
label: i18n("3bdd08adab6ea90b9164b20a0e4151ac"),
name: 'description'
}])
};
var ThemeEditor = function (props) {
var _a = React__default.useState(),
themePanelConfig = _a[0],
setThemePanelConfig = _a[1];
var _b = React__default.useState(true),
themePanelClose = _b[0],
setThemePanelClose = _b[1];
var _c = React__default.useState(true),
themePanelInnerClose = _c[0],
setThemePanelInnerClose = _c[1];
var value = props.value,
name = props.name,
versionData = props.versionData;
var _d = React__default.useState(value),
data = _d[0],
setData = _d[1];
var _e = React__default.useState(cloneDeep(versionData)),
source = _e[0],
setSource = _e[1];
var _f = React__default.useState(''),
themeName = _f[0],
setThemeName = _f[1];
var _g = React__default.useState(),
nav = _g[0],
setNav = _g[1];
var _h = React__default.useState(),
tab = _h[0],
setTab = _h[1];
var _j = React__default.useState(),
content = _j[0],
setContent = _j[1];
var _k = React__default.useState(''),
activeToken = _k[0],
setActiveEditColor = _k[1];
var _l = React__default.useState(''),
refreshToken = _l[0],
setRefreshToken = _l[1];
function handleReFreshToken(token) {
setRefreshToken(token);
setTimeout(function () {
setRefreshToken('');
});
}
// 修改正在编辑的颜色的token
function handleSetActiveToken(token) {
if (token === activeToken) {
setActiveEditColor('');
} else {
setActiveEditColor(token);
}
}
function changeThemeData() {
return __awaiter(this, void 0, void 0, function () {
var themeData, parseThemeData;
return __generator(this, function (_a) {
themeData = value;
setThemeName(name);
setData(themeData);
parseThemeData = new ParseThemeData(themeData, CSS_NAMESPACE);
parseThemeData.generator();
insertStyle(parseThemeData.getStyle(), 'theme');
return [2 /*return*/];
});
});
}
// 切换query默认Nav下Tab
function toggleNavTab() {
var query = parseQuery(window.location);
var _a = query.nav,
nav = _a === void 0 ? 'global' : _a,
_b = query.tab,
tab = _b === void 0 ? 'color' : _b;
var currentNav = tabList[nav] || [];
var tabChildren = [];
currentNav.forEach(function (item) {
tabChildren = __spreadArray(__spreadArray([], tabChildren, true), item.children, true);
});
tabChildren === null || tabChildren === void 0 ? void 0 : tabChildren.find(function (item) {
if (item.key === tab) {
setNav(nav);
setTab(item.key);
setContent(item.component);
handleCloseRightPanel();
return true;
}
return false;
});
}
// 加参数到url上
function updateUrl(state) {
var location = window.location;
var query = parseQuery(location);
query = Object.assign({}, query, state);
var url = "".concat(origin).concat(location.pathname, "?").concat(qsstringify(query));
history.replaceState({}, document.title, url);
}
// 合并新增Tab
function mergeNavConfig() {
var _a;
// 更新组件目录
tabList.component = getAllComponent();
var moreNavConfig = props.moreNavConfig;
if (moreNavConfig) {
var globalChildren = tabList.global[0].children;
var componentChildren = tabList.component[0].children;
var customChildren = tabList.custom[0].children;
var children_1 = {
globalStyleTab: globalChildren,
componentStyleTab: componentChildren,
customStyleTab: customChildren
};
var _loop_1 = function (key) {
var k = key;
(_a = moreNavConfig[k]) === null || _a === void 0 ? void 0 : _a.forEach(function (item) {
var isExist = children_1[k].find(function (ele) {
return ele.key === item.key;
});
!isExist && children_1[k].push(item);
});
};
for (var key in moreNavConfig) {
_loop_1(key);
}
}
}
React__default.useEffect(function () {
mergeNavConfig();
// 切换query默认Tab
toggleNavTab();
}, []);
React__default.useEffect(function () {
changeThemeData();
}, [value]);
// 子组件改变触发
function onChange(data) {
var value = cloneDeep(data);
setData(value);
props.onChange && props.onChange(value);
var parseThemeData = new ParseThemeData(value, CSS_NAMESPACE);
parseThemeData.generator();
insertStyle(parseThemeData.getStyle(), 'theme');
}
// 右侧配置面板获取数据
function getFormData(value) {
if (!value || !(themePanelConfig === null || themePanelConfig === void 0 ? void 0 : themePanelConfig.path)) {
return {};
}
var path = themePanelConfig.path.split('.');
// @ts-ignore
var children = value[path[0]];
for (var i = 1; i < path.length; i++) {
children = children[path[i]];
}
return children;
}
// 过滤掉空数据
function filterNullData(data) {
var res = {};
for (var key in data) {
if (typeof data[key] === 'object' && data[key] !== null && !Array.isArray(data[key])) {
res[key] = filterNullData(data[key]);
} else if (data[key] !== null) {
res[key] = data[key];
}
}
return res;
}
// 子组件修改右侧面板配置
function handleSetConfig(newPanelConfig) {
if (newPanelConfig.path !== (themePanelConfig === null || themePanelConfig === void 0 ? void 0 : themePanelConfig.path) || newPanelConfig.activeId !== (themePanelConfig === null || themePanelConfig === void 0 ? void 0 : themePanelConfig.activeId)) {
setThemePanelInnerClose(true);
setThemePanelConfig({
title: '',
path: '',
activeId: null,
body: []
});
setTimeout(function () {
setThemePanelConfig(newPanelConfig);
setThemePanelInnerClose(false);
setThemePanelClose(false);
}, 230);
}
}
//右侧面板
var RightPanelBody = function (themePanelConfig) {
if (!themePanelConfig) {
return null;
}
var formData = Object.assign(filterNullData(getFormData(cloneDeep(data))), getGlobalData(data));
return render({
type: 'form',
// debug: true,
mode: 'horizontal',
labelAlign: 'left',
labelWidth: 65,
wrapWithPanel: false,
submitOnChange: true,
body: themePanelConfig === null || themePanelConfig === void 0 ? void 0 : themePanelConfig.body
}, {
data: formData,
onFinished: function (form) {
var value = cloneDeep(data);
var children = getFormData(value);
for (var key in form) {
if (['colorOptions', 'fontFamilyOptions', 'fontSizeOptions', 'lineHeightOptions', 'fontWeightOptions', 'borderRadiusOptions', 'borderWidthOptions', 'borderStyleOptions', 'sizesOptions', 'shadowOptions'].includes(key)) {
continue;
}
if (form.hasOwnProperty(key)) {
if (typeof form[key] === 'object') {
if (Array.isArray(form[key]) || form[key].boxShadow) {
// 数组需要直接赋值 TODO:代码优化
children[key] = cloneDeep(form[key]);
} else {
children[key] = defaultsDeep(cloneDeep(form[key]), children[key]);
}
} else {
children[key] = form[key];
}
}
}
setData(value);
props.onChange && props.onChange(value);
var parseThemeData = new ParseThemeData(value, CSS_NAMESPACE);
parseThemeData.generator();
insertStyle(parseThemeData.getStyle(), 'theme');
}
});
};
function handleExit() {
props.onBack();
}
// 关闭右侧面板,清空表单项
function handleCloseRightPanel() {
setThemePanelClose(true);
setTimeout(function () {
setThemePanelConfig({
title: '',
path: '',
activeId: null,
body: []
});
}, 500);
}
function handleSaveDraft() {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
props.onSaveDraft(data);
return [2 /*return*/];
});
});
}
function handleSave() {
return __awaiter(this, void 0, void 0, function () {
var res;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
return [4 /*yield*/, props.onSave(data)];
case 1:
res = _a.sent();
if (res) {
setSource(cloneDeep(data));
}
return [2 /*return*/];
}
});
});
}
var renderHeader = function () {
var dataIsEqual = isEqual(source, data);
return React__default.createElement("div", {
className: "Editor-header ThemeEditor-header"
}, React__default.createElement("div", {
className: "Editor-title"
}, React__default.createElement("div", {
className: "editor-back-icon",
onClick: handleExit
}, React__default.createElement(Icon$1, {
icon: "down-arrow-bold",
className: "left-arrow"
})), React__default.createElement("div", {
className: "page-title-text"
}, i18n("03b6472ec3f65ccbfb197aafa22bf437"), " ", themeName), React__default.createElement("div", {
onClick: function () {
return handleSetConfig(themeConfig);
},
className: classnames(':ThemeEditor-header-setting', (themePanelConfig === null || themePanelConfig === void 0 ? void 0 : themePanelConfig.path) === 'config' && ':ThemeEditor-header-setting--active')
}, React__default.createElement("i", {
className: "iconfont icon-setting"
}))), React__default.createElement("div", {
className: "Editor-header-actions"
}, props.actions, React__default.createElement(Button, {
onClick: handleSaveDraft,
className: "header-action-btn preview-btn margin-left-space",
level: "primary"
}, i18n("d8acae3bcfbc9ec9ade58c2dbacf65aa")), React__default.createElement(Button, {
disabled: dataIsEqual,
level: "primary",
onClick: handleSave,
className: classnames('header-action-btn', dataIsEqual && 'disabled')
}, i18n("be5fbbe34ce9979bfb6576d9eddc5612"))));
};
var renderContent = function (component) {
if (!component) {
return null;
}
return baseContent(component(__assign(__assign({}, props), {
data: data,
setConfig: handleSetConfig,
activeId: (themePanelConfig === null || themePanelConfig === void 0 ? void 0 : themePanelConfig.activeId) || (themePanelConfig === null || themePanelConfig === void 0 ? void 0 : themePanelConfig.path),
onChange: onChange,
activeToken: activeToken,
handleSetActiveToken: handleSetActiveToken,
handleCloseRightPanel: handleCloseRightPanel,
refreshToken: refreshToken,
handleReFreshToken: handleReFreshToken
})), handleSetConfig);
};
if (!data) {
return null;
}
return React__default.createElement(Layout, {
header: renderHeader(),
headerClassName: ":Editor-page-header",
bodyClassName: "Editor-page",
contentClassName: ":Editor-body",
asideFixed: true,
headerFixed: false
}, React__default.createElement("div", {
style: {
display: 'none'
}
}, JSON.stringify(data)), React__default.createElement("div", {
className: "ThemeEditor-body"
}, React__default.createElement("div", {
className: "ThemeEditor-body-leftTab"
}, React__default.createElement(AsideNav, {
className: ":ThemeEditor-nav",
navigations: navList,
renderLink: function (item) {
var link = item.link;
return React__default.createElement("div", {
onClick: function () {
setNav(link.key);
setTab(tabList[link.key][0].children[0].key);
setContent(tabList[link.key][0].children[0].component);
updateUrl({
nav: link.key,
tab: tabList[link.key][0].children[0].key
});
handleCloseRightPanel();
},
className: "ThemeEditor-nav-item",
"editor-tooltip": link.label
}, React__default.createElement(Icon, {
icon: link.icon,
className: "ThemeEditor-nav-icon"
}));
},
isActive: function (item) {
return item.key === nav;
}
}), React__default.createElement("div", {
className: "ThemeEditor-tab"
}, React__default.createElement(AsideNav, {
navigations: nav ? tabList[nav] : [],
renderLink: function (item) {
var link = item.link;
return React__default.createElement("div", {
onClick: function () {
setTab(link.key);
setContent(link.component);
updateUrl({
tab: link.key
});
handleCloseRightPanel();
},
className: "ThemeEditor-tab-item"
}, link.label);
},
isActive: function (item) {
return item.key === tab;
}
}))), renderContent(content), React__default.createElement("div", {
className: classnames(':ThemeEditor-body-rightPanel', themePanelClose && ':ThemeEditor-body-rightPanel--close')
}, React__default.createElement("div", {
className: classnames(':ThemeEditor-body-rightPanel-inner', themePanelInnerClose && ':ThemeEditor-body-rightPanelInner--close')
}, React__default.createElement("div", {
className: "ThemeEditor-body-rightPanel-title"
}, themePanelConfig === null || themePanelConfig === void 0 ? void 0 : themePanelConfig.title, React__default.createElement(Icon$1, {
icon: "close",
onClick: function () {
return handleCloseRightPanel();
}
})), React__default.createElement("div", {
className: "ThemeEditor-body-rightPanel-body"
}, RightPanelBody(themePanelConfig))))));
};
export { ThemeEditor as default, themeConfig };