mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-08 12:38:39 +00:00
Move "xArrow" into functions/arrow.js and add flow types. (#1327)
* Move "xArrow" into functions/arrow.js. * Add flow types to functions/arrow.js. * Address review comments.
This commit is contained in:
committed by
Kevin Barabash
parent
35d6181a95
commit
28dfa91fb5
@@ -509,66 +509,6 @@ export const groupTypes = {
|
|||||||
return makeSpan(["mord", (group.value.isOver ? "mover" : "munder")],
|
return makeSpan(["mord", (group.value.isOver ? "mover" : "munder")],
|
||||||
[vlist], options);
|
[vlist], options);
|
||||||
},
|
},
|
||||||
|
|
||||||
xArrow(group, options) {
|
|
||||||
const style = options.style;
|
|
||||||
|
|
||||||
// Build the argument groups in the appropriate style.
|
|
||||||
// Ref: amsmath.dtx: \hbox{$\scriptstyle\mkern#3mu{#6}\mkern#4mu$}%
|
|
||||||
|
|
||||||
let newOptions = options.havingStyle(style.sup());
|
|
||||||
const upperGroup = buildGroup(group.value.body, newOptions, options);
|
|
||||||
upperGroup.classes.push("x-arrow-pad");
|
|
||||||
|
|
||||||
let lowerGroup;
|
|
||||||
if (group.value.below) {
|
|
||||||
// Build the lower group
|
|
||||||
newOptions = options.havingStyle(style.sub());
|
|
||||||
lowerGroup = buildGroup(group.value.below, newOptions, options);
|
|
||||||
lowerGroup.classes.push("x-arrow-pad");
|
|
||||||
}
|
|
||||||
|
|
||||||
const arrowBody = stretchy.svgSpan(group, options);
|
|
||||||
|
|
||||||
// Re shift: Note that stretchy.svgSpan returned arrowBody.depth = 0.
|
|
||||||
// The point we want on the math axis is at 0.5 * arrowBody.height.
|
|
||||||
const arrowShift = -options.fontMetrics().axisHeight +
|
|
||||||
0.5 * arrowBody.height;
|
|
||||||
// 2 mu kern. Ref: amsmath.dtx: #7\if0#2\else\mkern#2mu\fi
|
|
||||||
let upperShift = -options.fontMetrics().axisHeight
|
|
||||||
- 0.5 * arrowBody.height - 0.111; // 0.111 em = 2 mu
|
|
||||||
if (upperGroup.depth > 0.25 || group.value.label === "\\xleftequilibrium") {
|
|
||||||
upperShift -= upperGroup.depth; // shift up if depth encroaches
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate the vlist
|
|
||||||
let vlist;
|
|
||||||
if (group.value.below) {
|
|
||||||
const lowerShift = -options.fontMetrics().axisHeight
|
|
||||||
+ lowerGroup.height + 0.5 * arrowBody.height
|
|
||||||
+ 0.111;
|
|
||||||
vlist = buildCommon.makeVList({
|
|
||||||
positionType: "individualShift",
|
|
||||||
children: [
|
|
||||||
{type: "elem", elem: upperGroup, shift: upperShift},
|
|
||||||
{type: "elem", elem: arrowBody, shift: arrowShift},
|
|
||||||
{type: "elem", elem: lowerGroup, shift: lowerShift},
|
|
||||||
],
|
|
||||||
}, options);
|
|
||||||
} else {
|
|
||||||
vlist = buildCommon.makeVList({
|
|
||||||
positionType: "individualShift",
|
|
||||||
children: [
|
|
||||||
{type: "elem", elem: upperGroup, shift: upperShift},
|
|
||||||
{type: "elem", elem: arrowBody, shift: arrowShift},
|
|
||||||
],
|
|
||||||
}, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
vlist.children[0].children[0].children[1].classes.push("svg-align");
|
|
||||||
|
|
||||||
return makeSpan(["mrel", "x-arrow"], [vlist], options);
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -280,30 +280,6 @@ groupTypes.horizBrace = function(group, options) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.xArrow = function(group, options) {
|
|
||||||
const arrowNode = stretchy.mathMLnode(group.value.label);
|
|
||||||
let node;
|
|
||||||
let lowerNode;
|
|
||||||
|
|
||||||
if (group.value.body) {
|
|
||||||
const upperNode = buildGroup(group.value.body, options);
|
|
||||||
if (group.value.below) {
|
|
||||||
lowerNode = buildGroup(group.value.below, options);
|
|
||||||
node = new mathMLTree.MathNode(
|
|
||||||
"munderover", [arrowNode, lowerNode, upperNode]
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
node = new mathMLTree.MathNode("mover", [arrowNode, upperNode]);
|
|
||||||
}
|
|
||||||
} else if (group.value.below) {
|
|
||||||
lowerNode = buildGroup(group.value.below, options);
|
|
||||||
node = new mathMLTree.MathNode("munder", [arrowNode, lowerNode]);
|
|
||||||
} else {
|
|
||||||
node = new mathMLTree.MathNode("mover", [arrowNode]);
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
};
|
|
||||||
|
|
||||||
groupTypes.tag = function(group, options) {
|
groupTypes.tag = function(group, options) {
|
||||||
const table = new mathMLTree.MathNode("mtable", [
|
const table = new mathMLTree.MathNode("mtable", [
|
||||||
new mathMLTree.MathNode("mlabeledtr", [
|
new mathMLTree.MathNode("mlabeledtr", [
|
||||||
|
@@ -90,31 +90,8 @@ defineFunction("horizBrace", [
|
|||||||
// Stretchy accents under the body
|
// Stretchy accents under the body
|
||||||
import "./functions/accentunder";
|
import "./functions/accentunder";
|
||||||
|
|
||||||
// Stretchy arrows with an optional argument
|
// Stretch arrows
|
||||||
defineFunction("xArrow", [
|
import "./functions/arrow";
|
||||||
"\\xleftarrow", "\\xrightarrow", "\\xLeftarrow", "\\xRightarrow",
|
|
||||||
"\\xleftrightarrow", "\\xLeftrightarrow", "\\xhookleftarrow",
|
|
||||||
"\\xhookrightarrow", "\\xmapsto", "\\xrightharpoondown",
|
|
||||||
"\\xrightharpoonup", "\\xleftharpoondown", "\\xleftharpoonup",
|
|
||||||
"\\xrightleftharpoons", "\\xleftrightharpoons", "\\xlongequal",
|
|
||||||
"\\xtwoheadrightarrow", "\\xtwoheadleftarrow", "\\xtofrom",
|
|
||||||
// The next 3 functions are here to support the mhchem extension.
|
|
||||||
// Direct use of these functions is discouraged and may break someday.
|
|
||||||
"\\xrightleftarrows", "\\xrightequilibrium",
|
|
||||||
"\\xleftequilibrium",
|
|
||||||
], {
|
|
||||||
numArgs: 1,
|
|
||||||
numOptionalArgs: 1,
|
|
||||||
}, function(context, args, optArgs) {
|
|
||||||
const below = optArgs[0];
|
|
||||||
const body = args[0];
|
|
||||||
return {
|
|
||||||
type: "xArrow", // x for extensible
|
|
||||||
label: context.funcName,
|
|
||||||
body: body,
|
|
||||||
below: below,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// Row and line breaks
|
// Row and line breaks
|
||||||
import "./functions/cr";
|
import "./functions/cr";
|
||||||
|
124
src/functions/arrow.js
Normal file
124
src/functions/arrow.js
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
// @flow
|
||||||
|
import defineFunction from "../defineFunction";
|
||||||
|
import buildCommon from "../buildCommon";
|
||||||
|
import mathMLTree from "../mathMLTree";
|
||||||
|
import stretchy from "../stretchy";
|
||||||
|
|
||||||
|
import * as html from "../buildHTML";
|
||||||
|
import * as mml from "../buildMathML";
|
||||||
|
|
||||||
|
import type ParseNode from "../ParseNode.js";
|
||||||
|
|
||||||
|
// Stretchy arrows with an optional argument
|
||||||
|
defineFunction({
|
||||||
|
type: "xArrow",
|
||||||
|
names: [
|
||||||
|
"\\xleftarrow", "\\xrightarrow", "\\xLeftarrow", "\\xRightarrow",
|
||||||
|
"\\xleftrightarrow", "\\xLeftrightarrow", "\\xhookleftarrow",
|
||||||
|
"\\xhookrightarrow", "\\xmapsto", "\\xrightharpoondown",
|
||||||
|
"\\xrightharpoonup", "\\xleftharpoondown", "\\xleftharpoonup",
|
||||||
|
"\\xrightleftharpoons", "\\xleftrightharpoons", "\\xlongequal",
|
||||||
|
"\\xtwoheadrightarrow", "\\xtwoheadleftarrow", "\\xtofrom",
|
||||||
|
// The next 3 functions are here to support the mhchem extension.
|
||||||
|
// Direct use of these functions is discouraged and may break someday.
|
||||||
|
"\\xrightleftarrows", "\\xrightequilibrium", "\\xleftequilibrium",
|
||||||
|
],
|
||||||
|
props: {
|
||||||
|
numArgs: 1,
|
||||||
|
numOptionalArgs: 1,
|
||||||
|
},
|
||||||
|
handler(context, args, optArgs) {
|
||||||
|
return {
|
||||||
|
type: "xArrow", // x for extensible
|
||||||
|
label: context.funcName,
|
||||||
|
body: args[0],
|
||||||
|
below: optArgs[0],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
// Flow is unable to correctly infer the type of `group`, even though it's
|
||||||
|
// unamibiguously determined from the passed-in `type` above.
|
||||||
|
htmlBuilder(group: ParseNode<"xArrow">, options) {
|
||||||
|
const style = options.style;
|
||||||
|
|
||||||
|
// Build the argument groups in the appropriate style.
|
||||||
|
// Ref: amsmath.dtx: \hbox{$\scriptstyle\mkern#3mu{#6}\mkern#4mu$}%
|
||||||
|
|
||||||
|
let newOptions = options.havingStyle(style.sup());
|
||||||
|
const upperGroup = html.buildGroup(group.value.body, newOptions, options);
|
||||||
|
upperGroup.classes.push("x-arrow-pad");
|
||||||
|
|
||||||
|
let lowerGroup;
|
||||||
|
if (group.value.below) {
|
||||||
|
// Build the lower group
|
||||||
|
newOptions = options.havingStyle(style.sub());
|
||||||
|
lowerGroup = html.buildGroup(group.value.below, newOptions, options);
|
||||||
|
lowerGroup.classes.push("x-arrow-pad");
|
||||||
|
}
|
||||||
|
|
||||||
|
const arrowBody = stretchy.svgSpan(group, options);
|
||||||
|
|
||||||
|
// Re shift: Note that stretchy.svgSpan returned arrowBody.depth = 0.
|
||||||
|
// The point we want on the math axis is at 0.5 * arrowBody.height.
|
||||||
|
const arrowShift = -options.fontMetrics().axisHeight +
|
||||||
|
0.5 * arrowBody.height;
|
||||||
|
// 2 mu kern. Ref: amsmath.dtx: #7\if0#2\else\mkern#2mu\fi
|
||||||
|
let upperShift = -options.fontMetrics().axisHeight
|
||||||
|
- 0.5 * arrowBody.height - 0.111; // 0.111 em = 2 mu
|
||||||
|
if (upperGroup.depth > 0.25 || group.value.label === "\\xleftequilibrium") {
|
||||||
|
upperShift -= upperGroup.depth; // shift up if depth encroaches
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the vlist
|
||||||
|
let vlist;
|
||||||
|
if (lowerGroup) {
|
||||||
|
const lowerShift = -options.fontMetrics().axisHeight
|
||||||
|
+ lowerGroup.height + 0.5 * arrowBody.height
|
||||||
|
+ 0.111;
|
||||||
|
vlist = buildCommon.makeVList({
|
||||||
|
positionType: "individualShift",
|
||||||
|
children: [
|
||||||
|
{type: "elem", elem: upperGroup, shift: upperShift},
|
||||||
|
{type: "elem", elem: arrowBody, shift: arrowShift},
|
||||||
|
{type: "elem", elem: lowerGroup, shift: lowerShift},
|
||||||
|
],
|
||||||
|
}, options);
|
||||||
|
} else {
|
||||||
|
vlist = buildCommon.makeVList({
|
||||||
|
positionType: "individualShift",
|
||||||
|
children: [
|
||||||
|
{type: "elem", elem: upperGroup, shift: upperShift},
|
||||||
|
{type: "elem", elem: arrowBody, shift: arrowShift},
|
||||||
|
],
|
||||||
|
}, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// $FlowFixMe: Replace this with passing "svg-align" into makeVList.
|
||||||
|
vlist.children[0].children[0].children[1].classes.push("svg-align");
|
||||||
|
|
||||||
|
return buildCommon.makeSpan(["mrel", "x-arrow"], [vlist], options);
|
||||||
|
},
|
||||||
|
mathmlBuilder(group, options) {
|
||||||
|
const arrowNode = stretchy.mathMLnode(group.value.label);
|
||||||
|
let node;
|
||||||
|
let lowerNode;
|
||||||
|
|
||||||
|
if (group.value.body) {
|
||||||
|
const upperNode = mml.buildGroup(group.value.body, options);
|
||||||
|
if (group.value.below) {
|
||||||
|
lowerNode = mml.buildGroup(group.value.below, options);
|
||||||
|
node = new mathMLTree.MathNode(
|
||||||
|
"munderover", [arrowNode, lowerNode, upperNode]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
node = new mathMLTree.MathNode("mover", [arrowNode, upperNode]);
|
||||||
|
}
|
||||||
|
} else if (group.value.below) {
|
||||||
|
lowerNode = mml.buildGroup(group.value.below, options);
|
||||||
|
node = new mathMLTree.MathNode("munder", [arrowNode, lowerNode]);
|
||||||
|
} else {
|
||||||
|
node = new mathMLTree.MathNode("mover", [arrowNode]);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@@ -168,7 +168,7 @@ const groupLength = function(arg: ParseNode<*>): number {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const svgSpan = function(
|
const svgSpan = function(
|
||||||
group: ParseNode<"accent"> | ParseNode<"accentUnder">,
|
group: ParseNode<"accent"> | ParseNode<"accentUnder"> | ParseNode<"xArrow">,
|
||||||
options: Options,
|
options: Options,
|
||||||
): DomSpan | SvgSpan {
|
): DomSpan | SvgSpan {
|
||||||
// Create a span with inline SVG for the element.
|
// Create a span with inline SVG for the element.
|
||||||
@@ -180,9 +180,13 @@ const svgSpan = function(
|
|||||||
let viewBoxWidth = 400000; // default
|
let viewBoxWidth = 400000; // default
|
||||||
const label = group.value.label.substr(1);
|
const label = group.value.label.substr(1);
|
||||||
if (utils.contains(["widehat", "widetilde", "utilde"], label)) {
|
if (utils.contains(["widehat", "widetilde", "utilde"], label)) {
|
||||||
|
// Each type in the `if` statement corresponds to one of the ParseNode
|
||||||
|
// types below. This narrowing is required to access `grp.value.base`.
|
||||||
|
// $FlowFixMe
|
||||||
|
const grp: ParseNode<"accent"> | ParseNode<"accentUnder"> = group;
|
||||||
// There are four SVG images available for each function.
|
// There are four SVG images available for each function.
|
||||||
// Choose a taller image when there are more characters.
|
// Choose a taller image when there are more characters.
|
||||||
const numChars = groupLength(group.value.base);
|
const numChars = groupLength(grp.value.base);
|
||||||
let viewBoxHeight;
|
let viewBoxHeight;
|
||||||
let pathName;
|
let pathName;
|
||||||
let height;
|
let height;
|
||||||
|
Reference in New Issue
Block a user