主要功能: - 修改RequirementModal支持12个订单班选择 - 添加OrderClassIconMap图标映射组件 - Store中添加selectedOrderClass状态管理 - WorkflowPage支持传递orderClass参数 - web_result添加URL参数切换功能 - 创建order-class-handler.js动态处理页面主题 技术改进: - 创建软链接关联订单班数据目录 - 生成wenlu.json和food.json数据结构 - 删除重复的web_result目录 - 添加测试页面test-order-class.html 影响范围: - 展会策划系统现支持12个订单班 - 结果展示页面自动适配不同订单班主题 - 用户可选择不同行业生成对应方案 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
437 lines
12 KiB
JavaScript
437 lines
12 KiB
JavaScript
import React from 'react';
|
|
import createElement from './create-element';
|
|
import checkForListedLanguage from './checkForListedLanguage';
|
|
|
|
const newLineRegex = /\n/g;
|
|
function getNewLines(str) {
|
|
return str.match(newLineRegex);
|
|
}
|
|
|
|
function getAllLineNumbers({ lines, startingLineNumber, style }) {
|
|
return lines.map((_, i) => {
|
|
const number = i + startingLineNumber;
|
|
return (
|
|
<span
|
|
key={`line-${i}`}
|
|
className="react-syntax-highlighter-line-number"
|
|
style={typeof style === 'function' ? style(number) : style}
|
|
>
|
|
{`${number}\n`}
|
|
</span>
|
|
);
|
|
});
|
|
}
|
|
|
|
function AllLineNumbers({
|
|
codeString,
|
|
codeStyle,
|
|
containerStyle = { float: 'left', paddingRight: '10px' },
|
|
numberStyle = {},
|
|
startingLineNumber
|
|
}) {
|
|
return (
|
|
<code style={Object.assign({}, codeStyle, containerStyle)}>
|
|
{getAllLineNumbers({
|
|
lines: codeString.replace(/\n$/, '').split('\n'),
|
|
style: numberStyle,
|
|
startingLineNumber
|
|
})}
|
|
</code>
|
|
);
|
|
}
|
|
|
|
function getEmWidthOfNumber(num) {
|
|
return `${num.toString().length}.25em`;
|
|
}
|
|
|
|
function getInlineLineNumber(lineNumber, inlineLineNumberStyle) {
|
|
return {
|
|
type: 'element',
|
|
tagName: 'span',
|
|
properties: {
|
|
key: `line-number--${lineNumber}`,
|
|
className: [
|
|
'comment',
|
|
'linenumber',
|
|
'react-syntax-highlighter-line-number'
|
|
],
|
|
style: inlineLineNumberStyle
|
|
},
|
|
children: [
|
|
{
|
|
type: 'text',
|
|
value: lineNumber
|
|
}
|
|
]
|
|
};
|
|
}
|
|
|
|
function assembleLineNumberStyles(
|
|
lineNumberStyle,
|
|
lineNumber,
|
|
largestLineNumber
|
|
) {
|
|
// minimally necessary styling for line numbers
|
|
const defaultLineNumberStyle = {
|
|
display: 'inline-block',
|
|
minWidth: getEmWidthOfNumber(largestLineNumber),
|
|
paddingRight: '1em',
|
|
textAlign: 'right',
|
|
userSelect: 'none'
|
|
};
|
|
// prep custom styling
|
|
const customLineNumberStyle =
|
|
typeof lineNumberStyle === 'function'
|
|
? lineNumberStyle(lineNumber)
|
|
: lineNumberStyle;
|
|
// combine
|
|
const assembledStyle = {
|
|
...defaultLineNumberStyle,
|
|
...customLineNumberStyle
|
|
};
|
|
return assembledStyle;
|
|
}
|
|
|
|
function createLineElement({
|
|
children,
|
|
lineNumber,
|
|
lineNumberStyle,
|
|
largestLineNumber,
|
|
showInlineLineNumbers,
|
|
lineProps = {},
|
|
className = [],
|
|
showLineNumbers,
|
|
wrapLongLines,
|
|
wrapLines = false
|
|
}) {
|
|
const properties = wrapLines
|
|
? {
|
|
...(typeof lineProps === 'function' ? lineProps(lineNumber) : lineProps)
|
|
}
|
|
: {};
|
|
|
|
properties['className'] = properties['className']
|
|
? [...properties['className'].trim().split(/\s+/), ...className]
|
|
: className;
|
|
|
|
if (lineNumber && showInlineLineNumbers) {
|
|
const inlineLineNumberStyle = assembleLineNumberStyles(
|
|
lineNumberStyle,
|
|
lineNumber,
|
|
largestLineNumber
|
|
);
|
|
children.unshift(getInlineLineNumber(lineNumber, inlineLineNumberStyle));
|
|
}
|
|
|
|
if (wrapLongLines & showLineNumbers) {
|
|
properties.style = { display: 'flex', ...properties.style };
|
|
}
|
|
|
|
return {
|
|
type: 'element',
|
|
tagName: 'span',
|
|
properties,
|
|
children
|
|
};
|
|
}
|
|
|
|
function flattenCodeTree(tree, className = [], newTree = []) {
|
|
for (let i = 0; i < tree.length; i++) {
|
|
const node = tree[i];
|
|
if (node.type === 'text') {
|
|
newTree.push(
|
|
createLineElement({
|
|
children: [node],
|
|
className: [...new Set(className)]
|
|
})
|
|
);
|
|
} else if (node.children) {
|
|
const classNames = className.concat(node.properties.className);
|
|
flattenCodeTree(node.children, classNames).forEach(i => newTree.push(i));
|
|
}
|
|
}
|
|
return newTree;
|
|
}
|
|
|
|
function processLines(
|
|
codeTree,
|
|
wrapLines,
|
|
lineProps,
|
|
showLineNumbers,
|
|
showInlineLineNumbers,
|
|
startingLineNumber,
|
|
largestLineNumber,
|
|
lineNumberStyle,
|
|
wrapLongLines
|
|
) {
|
|
const tree = flattenCodeTree(codeTree.value);
|
|
const newTree = [];
|
|
let lastLineBreakIndex = -1;
|
|
let index = 0;
|
|
|
|
function createWrappedLine(children, lineNumber, className = []) {
|
|
return createLineElement({
|
|
children,
|
|
lineNumber,
|
|
lineNumberStyle,
|
|
largestLineNumber,
|
|
showInlineLineNumbers,
|
|
lineProps,
|
|
className,
|
|
showLineNumbers,
|
|
wrapLongLines,
|
|
wrapLines
|
|
});
|
|
}
|
|
|
|
function createUnwrappedLine(children, lineNumber) {
|
|
if (showLineNumbers && lineNumber && showInlineLineNumbers) {
|
|
const inlineLineNumberStyle = assembleLineNumberStyles(
|
|
lineNumberStyle,
|
|
lineNumber,
|
|
largestLineNumber
|
|
);
|
|
children.unshift(getInlineLineNumber(lineNumber, inlineLineNumberStyle));
|
|
}
|
|
return children;
|
|
}
|
|
|
|
function createLine(children, lineNumber, className = []) {
|
|
return wrapLines || className.length > 0
|
|
? createWrappedLine(children, lineNumber, className)
|
|
: createUnwrappedLine(children, lineNumber);
|
|
}
|
|
|
|
while (index < tree.length) {
|
|
const node = tree[index];
|
|
const value = node.children[0].value;
|
|
const newLines = getNewLines(value);
|
|
|
|
if (newLines) {
|
|
const splitValue = value.split('\n');
|
|
splitValue.forEach((text, i) => {
|
|
const lineNumber =
|
|
showLineNumbers && newTree.length + startingLineNumber;
|
|
const newChild = { type: 'text', value: `${text}\n` };
|
|
|
|
// if it's the first line
|
|
if (i === 0) {
|
|
const children = tree.slice(lastLineBreakIndex + 1, index).concat(
|
|
createLineElement({
|
|
children: [newChild],
|
|
className: node.properties.className
|
|
})
|
|
);
|
|
|
|
const line = createLine(children, lineNumber);
|
|
newTree.push(line);
|
|
|
|
// if it's the last line
|
|
} else if (i === splitValue.length - 1) {
|
|
const stringChild =
|
|
tree[index + 1] &&
|
|
tree[index + 1].children &&
|
|
tree[index + 1].children[0];
|
|
const lastLineInPreviousSpan = { type: 'text', value: `${text}` };
|
|
if (stringChild) {
|
|
const newElem = createLineElement({
|
|
children: [lastLineInPreviousSpan],
|
|
className: node.properties.className
|
|
});
|
|
tree.splice(index + 1, 0, newElem);
|
|
} else {
|
|
const children = [lastLineInPreviousSpan];
|
|
const line = createLine(
|
|
children,
|
|
lineNumber,
|
|
node.properties.className
|
|
);
|
|
newTree.push(line);
|
|
}
|
|
|
|
// if it's neither the first nor the last line
|
|
} else {
|
|
const children = [newChild];
|
|
const line = createLine(
|
|
children,
|
|
lineNumber,
|
|
node.properties.className
|
|
);
|
|
newTree.push(line);
|
|
}
|
|
});
|
|
lastLineBreakIndex = index;
|
|
}
|
|
index++;
|
|
}
|
|
|
|
if (lastLineBreakIndex !== tree.length - 1) {
|
|
const children = tree.slice(lastLineBreakIndex + 1, tree.length);
|
|
if (children && children.length) {
|
|
const lineNumber = showLineNumbers && newTree.length + startingLineNumber;
|
|
const line = createLine(children, lineNumber);
|
|
newTree.push(line);
|
|
}
|
|
}
|
|
|
|
return wrapLines ? newTree : [].concat(...newTree);
|
|
}
|
|
|
|
function defaultRenderer({ rows, stylesheet, useInlineStyles }) {
|
|
return rows.map((node, i) =>
|
|
createElement({
|
|
node,
|
|
stylesheet,
|
|
useInlineStyles,
|
|
key: `code-segment-${i}`
|
|
})
|
|
);
|
|
}
|
|
|
|
// only highlight.js has the highlightAuto method
|
|
function isHighlightJs(astGenerator) {
|
|
return astGenerator && typeof astGenerator.highlightAuto !== 'undefined';
|
|
}
|
|
|
|
function getCodeTree({ astGenerator, language, code, defaultCodeValue }) {
|
|
// figure out whether we're using lowlight/highlight or refractor/prism
|
|
// then attempt highlighting accordingly
|
|
|
|
// lowlight/highlight?
|
|
if (isHighlightJs(astGenerator)) {
|
|
const hasLanguage = checkForListedLanguage(astGenerator, language);
|
|
if (language === 'text') {
|
|
return { value: defaultCodeValue, language: 'text' };
|
|
} else if (hasLanguage) {
|
|
return astGenerator.highlight(language, code);
|
|
} else {
|
|
return astGenerator.highlightAuto(code);
|
|
}
|
|
}
|
|
|
|
// must be refractor/prism, then
|
|
try {
|
|
return language && language !== 'text'
|
|
? { value: astGenerator.highlight(code, language) }
|
|
: { value: defaultCodeValue };
|
|
} catch (e) {
|
|
return { value: defaultCodeValue };
|
|
}
|
|
}
|
|
|
|
export default function(defaultAstGenerator, defaultStyle) {
|
|
return function SyntaxHighlighter({
|
|
language,
|
|
children,
|
|
style = defaultStyle,
|
|
customStyle = {},
|
|
codeTagProps = {
|
|
className: language ? `language-${language}` : undefined,
|
|
style: {
|
|
...style['code[class*="language-"]'],
|
|
...style[`code[class*="language-${language}"]`]
|
|
}
|
|
},
|
|
useInlineStyles = true,
|
|
showLineNumbers = false,
|
|
showInlineLineNumbers = true,
|
|
startingLineNumber = 1,
|
|
lineNumberContainerStyle,
|
|
lineNumberStyle = {},
|
|
wrapLines,
|
|
wrapLongLines = false,
|
|
lineProps = {},
|
|
renderer,
|
|
PreTag = 'pre',
|
|
CodeTag = 'code',
|
|
code = (Array.isArray(children) ? children[0] : children) || '',
|
|
astGenerator,
|
|
...rest
|
|
}) {
|
|
astGenerator = astGenerator || defaultAstGenerator;
|
|
|
|
const allLineNumbers = showLineNumbers ? (
|
|
<AllLineNumbers
|
|
containerStyle={lineNumberContainerStyle}
|
|
codeStyle={codeTagProps.style || {}}
|
|
numberStyle={lineNumberStyle}
|
|
startingLineNumber={startingLineNumber}
|
|
codeString={code}
|
|
/>
|
|
) : null;
|
|
|
|
const defaultPreStyle = style.hljs ||
|
|
style['pre[class*="language-"]'] || { backgroundColor: '#fff' };
|
|
const generatorClassName = isHighlightJs(astGenerator) ? 'hljs' : 'prismjs';
|
|
const preProps = useInlineStyles
|
|
? Object.assign({}, rest, {
|
|
style: Object.assign({}, defaultPreStyle, customStyle)
|
|
})
|
|
: Object.assign({}, rest, {
|
|
className: rest.className
|
|
? `${generatorClassName} ${rest.className}`
|
|
: generatorClassName,
|
|
style: Object.assign({}, customStyle)
|
|
});
|
|
|
|
if (wrapLongLines) {
|
|
codeTagProps.style = { whiteSpace: 'pre-wrap', ...codeTagProps.style };
|
|
} else {
|
|
codeTagProps.style = { whiteSpace: 'pre', ...codeTagProps.style };
|
|
}
|
|
|
|
if (!astGenerator) {
|
|
return (
|
|
<PreTag {...preProps}>
|
|
{allLineNumbers}
|
|
<CodeTag {...codeTagProps}>{code}</CodeTag>
|
|
</PreTag>
|
|
);
|
|
}
|
|
|
|
/*
|
|
* Some custom renderers rely on individual row elements so we need to turn wrapLines on
|
|
* if renderer is provided and wrapLines is undefined.
|
|
*/
|
|
if ((wrapLines === undefined && renderer) || wrapLongLines)
|
|
wrapLines = true;
|
|
|
|
renderer = renderer || defaultRenderer;
|
|
const defaultCodeValue = [{ type: 'text', value: code }];
|
|
const codeTree = getCodeTree({
|
|
astGenerator,
|
|
language,
|
|
code,
|
|
defaultCodeValue
|
|
});
|
|
if (codeTree.language === null) {
|
|
codeTree.value = defaultCodeValue;
|
|
}
|
|
|
|
// pre-determine largest line number so that we can force minWidth on all linenumber elements
|
|
const lineBreakCount = code.match(/\n/g)?.length ?? 0;
|
|
const largestLineNumber = startingLineNumber + lineBreakCount;
|
|
|
|
const rows = processLines(
|
|
codeTree,
|
|
wrapLines,
|
|
lineProps,
|
|
showLineNumbers,
|
|
showInlineLineNumbers,
|
|
startingLineNumber,
|
|
largestLineNumber,
|
|
lineNumberStyle,
|
|
wrapLongLines
|
|
);
|
|
|
|
return (
|
|
<PreTag {...preProps}>
|
|
<CodeTag {...codeTagProps}>
|
|
{!showInlineLineNumbers && allLineNumbers}
|
|
{renderer({ rows, stylesheet: style, useInlineStyles })}
|
|
</CodeTag>
|
|
</PreTag>
|
|
);
|
|
};
|
|
}
|