mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-05 19:28:39 +00:00
Change buildCommon.makeVList params to struct for better type-safety. (#940)
* Change buildCommon.makeVList params to struct for better type-safety. This is towards #939. The expected structure of the makeVList params depends on the `positionType` parameter. To allow this to be strictly-typed using flow, this PR combines the co-related parameters into a single struct. * Add type for makeVList param in comments for documentation.
This commit is contained in:
committed by
Kevin Barabash
parent
b4c5dfaf20
commit
305a35c3a5
@@ -247,46 +247,47 @@ const makeFragment = function(children) {
|
||||
return fragment;
|
||||
};
|
||||
|
||||
|
||||
// TODO(#939): Uncomment and use VListParam as the type of makeVList's first param.
|
||||
/*
|
||||
type VListElem =
|
||||
{type: "elem", elem: DomChildNode, marginLeft?: string, marginRight?: string};
|
||||
type VListKern = {type: "kern", size: number};
|
||||
|
||||
// A list of child or kern nodes to be stacked on top of each other (i.e. the
|
||||
// first element will be at the bottom, and the last at the top).
|
||||
type VListChild = VListElem | VListKern;
|
||||
|
||||
type VListParam = {|
|
||||
// Each child contains how much it should be shifted downward.
|
||||
positionType: "individualShift",
|
||||
children: (VListElem & {shift: number})[],
|
||||
|} | {|
|
||||
// "top": The positionData specifies the topmost point of the vlist (note this
|
||||
// is expected to be a height, so positive values move up).
|
||||
// "bottom": The positionData specifies the bottommost point of the vlist (note
|
||||
// this is expected to be a depth, so positive values move down).
|
||||
// "shift": The vlist will be positioned such that its baseline is positionData
|
||||
// away from the baseline of the first child. Positive values move
|
||||
// downwards.
|
||||
positionType: "top" | "bottom" | "shift",
|
||||
positionData: number,
|
||||
children: VListChild[],
|
||||
|} | {|
|
||||
// The vlist is positioned so that its baseline is aligned with the baseline
|
||||
// of the first child. This is equivalent to "shift" with positionData=0.
|
||||
positionType: "firstBaseline",
|
||||
children: VListChild[],
|
||||
|};
|
||||
*/
|
||||
|
||||
/**
|
||||
* Makes a vertical list by stacking elements and kerns on top of each other.
|
||||
* Allows for many different ways of specifying the positioning method.
|
||||
*
|
||||
* Arguments:
|
||||
* - children: A list of child or kern nodes to be stacked on top of each other
|
||||
* (i.e. the first element will be at the bottom, and the last at
|
||||
* the top). Element nodes are specified as
|
||||
* {type: "elem", elem: node}
|
||||
* while kern nodes are specified as
|
||||
* {type: "kern", size: size}
|
||||
* - positionType: The method by which the vlist should be positioned. Valid
|
||||
* values are:
|
||||
* - "individualShift": The children list only contains elem
|
||||
* nodes, and each node contains an extra
|
||||
* "shift" value of how much it should be
|
||||
* shifted (note that shifting is always
|
||||
* moving downwards). positionData is
|
||||
* ignored.
|
||||
* - "top": The positionData specifies the topmost point of
|
||||
* the vlist (note this is expected to be a height,
|
||||
* so positive values move up)
|
||||
* - "bottom": The positionData specifies the bottommost point
|
||||
* of the vlist (note this is expected to be a
|
||||
* depth, so positive values move down
|
||||
* - "shift": The vlist will be positioned such that its
|
||||
* baseline is positionData away from the baseline
|
||||
* of the first child. Positive values move
|
||||
* downwards.
|
||||
* - "firstBaseline": The vlist will be positioned such that
|
||||
* its baseline is aligned with the
|
||||
* baseline of the first child.
|
||||
* positionData is ignored. (this is
|
||||
* equivalent to "shift" with
|
||||
* positionData=0)
|
||||
* - positionData: Data used in different ways depending on positionType
|
||||
* - options: An Options object
|
||||
*
|
||||
* See parameter documentation on the type documentation above.
|
||||
*/
|
||||
const makeVList = function(children, positionType, positionData, options) {
|
||||
const makeVList = function({positionType, positionData, children}, options) {
|
||||
let depth;
|
||||
let currPos;
|
||||
let i;
|
||||
|
184
src/buildHTML.js
184
src/buildHTML.js
@@ -397,15 +397,21 @@ groupTypes.supsub = function(group, options) {
|
||||
vlistElem[0].marginLeft = -base.italic + "em";
|
||||
}
|
||||
|
||||
supsub = buildCommon.makeVList(vlistElem, "shift", subShift, options);
|
||||
supsub = buildCommon.makeVList({
|
||||
positionType: "shift",
|
||||
positionData: subShift,
|
||||
children: vlistElem,
|
||||
}, options);
|
||||
} else if (!group.value.sub) {
|
||||
// Rule 18c, d
|
||||
supShift = Math.max(supShift, minSupShift,
|
||||
supm.depth + 0.25 * metrics.xHeight);
|
||||
|
||||
supsub = buildCommon.makeVList([
|
||||
{type: "elem", elem: supm, marginRight: scriptspace},
|
||||
], "shift", -supShift, options);
|
||||
supsub = buildCommon.makeVList({
|
||||
positionType: "shift",
|
||||
positionData: -supShift,
|
||||
children: [{type: "elem", elem: supm, marginRight: scriptspace}],
|
||||
}, options);
|
||||
} else {
|
||||
supShift = Math.max(
|
||||
supShift, minSupShift, supm.depth + 0.25 * metrics.xHeight);
|
||||
@@ -433,7 +439,10 @@ groupTypes.supsub = function(group, options) {
|
||||
vlistElem[0].marginLeft = -base.italic + "em";
|
||||
}
|
||||
|
||||
supsub = buildCommon.makeVList(vlistElem, "individualShift", null, options);
|
||||
supsub = buildCommon.makeVList({
|
||||
positionType: "individualShift",
|
||||
children: vlistElem,
|
||||
}, options);
|
||||
}
|
||||
|
||||
// We ensure to wrap the supsub vlist in a span.msupsub to reset text-align
|
||||
@@ -510,10 +519,13 @@ groupTypes.genfrac = function(group, options) {
|
||||
denomShift += 0.5 * (clearance - candidateClearance);
|
||||
}
|
||||
|
||||
frac = buildCommon.makeVList([
|
||||
frac = buildCommon.makeVList({
|
||||
positionType: "individualShift",
|
||||
children: [
|
||||
{type: "elem", elem: denomm, shift: denomShift},
|
||||
{type: "elem", elem: numerm, shift: -numShift},
|
||||
], "individualShift", null, options);
|
||||
],
|
||||
}, options);
|
||||
} else {
|
||||
// Rule 15d
|
||||
const axisHeight = options.fontMetrics().axisHeight;
|
||||
@@ -534,11 +546,14 @@ groupTypes.genfrac = function(group, options) {
|
||||
|
||||
const midShift = -(axisHeight - 0.5 * ruleWidth);
|
||||
|
||||
frac = buildCommon.makeVList([
|
||||
frac = buildCommon.makeVList({
|
||||
positionType: "individualShift",
|
||||
children: [
|
||||
{type: "elem", elem: denomm, shift: denomShift},
|
||||
{type: "elem", elem: rule, shift: midShift},
|
||||
{type: "elem", elem: numerm, shift: -numShift},
|
||||
], "individualShift", null, options);
|
||||
],
|
||||
}, options);
|
||||
}
|
||||
|
||||
// Since we manually change the style sometimes (with \dfrac or \tfrac),
|
||||
@@ -648,9 +663,10 @@ groupTypes.smash = function(group, options) {
|
||||
// makeVList applies "display: table-cell", which prevents the browser
|
||||
// from acting on that line height. So we'll call makeVList now.
|
||||
|
||||
return buildCommon.makeVList([
|
||||
{type: "elem", elem: node},
|
||||
], "firstBaseline", null, options);
|
||||
return buildCommon.makeVList({
|
||||
positionType: "firstBaseline",
|
||||
children: [{type: "elem", elem: node}],
|
||||
}, options);
|
||||
};
|
||||
|
||||
groupTypes.op = function(group, options) {
|
||||
@@ -770,21 +786,29 @@ groupTypes.op = function(group, options) {
|
||||
// that we are supposed to shift the limits by 1/2 of the slant,
|
||||
// but since we are centering the limits adding a full slant of
|
||||
// margin will shift by 1/2 that.
|
||||
finalGroup = buildCommon.makeVList([
|
||||
finalGroup = buildCommon.makeVList({
|
||||
positionType: "top",
|
||||
positionData: top,
|
||||
children: [
|
||||
{type: "kern", size: options.fontMetrics().bigOpSpacing5},
|
||||
{type: "elem", elem: subm, marginLeft: -slant + "em"},
|
||||
{type: "kern", size: subKern},
|
||||
{type: "elem", elem: base},
|
||||
], "top", top, options);
|
||||
],
|
||||
}, options);
|
||||
} else if (!subGroup) {
|
||||
bottom = base.depth + baseShift;
|
||||
|
||||
finalGroup = buildCommon.makeVList([
|
||||
finalGroup = buildCommon.makeVList({
|
||||
positionType: "bottom",
|
||||
positionData: bottom,
|
||||
children: [
|
||||
{type: "elem", elem: base},
|
||||
{type: "kern", size: supKern},
|
||||
{type: "elem", elem: supm, marginLeft: slant + "em"},
|
||||
{type: "kern", size: options.fontMetrics().bigOpSpacing5},
|
||||
], "bottom", bottom, options);
|
||||
],
|
||||
}, options);
|
||||
} else if (!supGroup && !subGroup) {
|
||||
// This case probably shouldn't occur (this would mean the
|
||||
// supsub was sending us a group with no superscript or
|
||||
@@ -796,7 +820,10 @@ groupTypes.op = function(group, options) {
|
||||
subKern +
|
||||
base.depth + baseShift;
|
||||
|
||||
finalGroup = buildCommon.makeVList([
|
||||
finalGroup = buildCommon.makeVList({
|
||||
positionType: "bottom",
|
||||
positionData: bottom,
|
||||
children: [
|
||||
{type: "kern", size: options.fontMetrics().bigOpSpacing5},
|
||||
{type: "elem", elem: subm, marginLeft: -slant + "em"},
|
||||
{type: "kern", size: subKern},
|
||||
@@ -804,7 +831,8 @@ groupTypes.op = function(group, options) {
|
||||
{type: "kern", size: supKern},
|
||||
{type: "elem", elem: supm, marginLeft: slant + "em"},
|
||||
{type: "kern", size: options.fontMetrics().bigOpSpacing5},
|
||||
], "bottom", bottom, options);
|
||||
],
|
||||
}, options);
|
||||
}
|
||||
|
||||
return makeSpan(["mop", "op-limits"], [finalGroup], options);
|
||||
@@ -917,12 +945,15 @@ groupTypes.overline = function(group, options) {
|
||||
const line = makeLineSpan("overline-line", options);
|
||||
|
||||
// Generate the vlist, with the appropriate kerns
|
||||
const vlist = buildCommon.makeVList([
|
||||
const vlist = buildCommon.makeVList({
|
||||
positionType: "firstBaseline",
|
||||
children: [
|
||||
{type: "elem", elem: innerGroup},
|
||||
{type: "kern", size: 3 * line.height},
|
||||
{type: "elem", elem: line},
|
||||
{type: "kern", size: line.height},
|
||||
], "firstBaseline", null, options);
|
||||
],
|
||||
}, options);
|
||||
|
||||
return makeSpan(["mord", "overline"], [vlist], options);
|
||||
};
|
||||
@@ -936,12 +967,16 @@ groupTypes.underline = function(group, options) {
|
||||
const line = makeLineSpan("underline-line", options);
|
||||
|
||||
// Generate the vlist, with the appropriate kerns
|
||||
const vlist = buildCommon.makeVList([
|
||||
const vlist = buildCommon.makeVList({
|
||||
positionType: "top",
|
||||
positionData: innerGroup.height,
|
||||
children: [
|
||||
{type: "kern", size: line.height},
|
||||
{type: "elem", elem: line},
|
||||
{type: "kern", size: 3 * line.height},
|
||||
{type: "elem", elem: innerGroup},
|
||||
], "top", innerGroup.height, options);
|
||||
],
|
||||
}, options);
|
||||
|
||||
return makeSpan(["mord", "underline"], [vlist], options);
|
||||
};
|
||||
@@ -1003,12 +1038,15 @@ groupTypes.sqrt = function(group, options) {
|
||||
inner.style.paddingLeft = img.advanceWidth + "em";
|
||||
|
||||
// Overlay the image and the argument.
|
||||
const body = buildCommon.makeVList([
|
||||
const body = buildCommon.makeVList({
|
||||
positionType: "firstBaseline",
|
||||
children: [
|
||||
{type: "elem", elem: inner},
|
||||
{type: "kern", size: -(inner.height + imgShift)},
|
||||
{type: "elem", elem: img},
|
||||
{type: "kern", size: ruleWidth},
|
||||
], "firstBaseline", null, options);
|
||||
],
|
||||
}, options);
|
||||
body.children[0].children[0].classes.push("svg-align");
|
||||
|
||||
if (!group.value.index) {
|
||||
@@ -1025,9 +1063,11 @@ groupTypes.sqrt = function(group, options) {
|
||||
const toShift = 0.6 * (body.height - body.depth);
|
||||
|
||||
// Build a VList with the superscript shifted up correctly
|
||||
const rootVList = buildCommon.makeVList(
|
||||
[{type: "elem", elem: rootm}],
|
||||
"shift", -toShift, options);
|
||||
const rootVList = buildCommon.makeVList({
|
||||
positionType: "shift",
|
||||
positionData: -toShift,
|
||||
children: [{type: "elem", elem: rootm}],
|
||||
}, options);
|
||||
// Add a class surrounding it so we can add on the appropriate
|
||||
// kerning
|
||||
const rootVListWrap = makeSpan(["root"], [rootVList]);
|
||||
@@ -1246,19 +1286,25 @@ groupTypes.accent = function(group, options) {
|
||||
// we shift it to the right by 1*skew.
|
||||
accentBody.style.marginLeft = 2 * skew + "em";
|
||||
|
||||
accentBody = buildCommon.makeVList([
|
||||
accentBody = buildCommon.makeVList({
|
||||
positionType: "firstBaseline",
|
||||
children: [
|
||||
{type: "elem", elem: body},
|
||||
{type: "kern", size: -clearance},
|
||||
{type: "elem", elem: accentBody},
|
||||
], "firstBaseline", null, options);
|
||||
],
|
||||
}, options);
|
||||
|
||||
} else {
|
||||
accentBody = stretchy.svgSpan(group, options);
|
||||
|
||||
accentBody = buildCommon.makeVList([
|
||||
accentBody = buildCommon.makeVList({
|
||||
positionType: "firstBaseline",
|
||||
children: [
|
||||
{type: "elem", elem: body},
|
||||
{type: "elem", elem: accentBody},
|
||||
], "firstBaseline", null, options);
|
||||
],
|
||||
}, options);
|
||||
|
||||
const styleSpan = accentBody.children[0].children[0].children[1];
|
||||
styleSpan.classes.push("svg-align"); // text-align: left;
|
||||
@@ -1320,18 +1366,25 @@ groupTypes.horizBrace = function(group, options) {
|
||||
// This first vlist contains the subject matter and the brace: equation
|
||||
let vlist;
|
||||
if (group.value.isOver) {
|
||||
vlist = buildCommon.makeVList([
|
||||
vlist = buildCommon.makeVList({
|
||||
positionType: "firstBaseline",
|
||||
children: [
|
||||
{type: "elem", elem: body},
|
||||
{type: "kern", size: 0.1},
|
||||
{type: "elem", elem: braceBody},
|
||||
], "firstBaseline", null, options);
|
||||
],
|
||||
}, options);
|
||||
vlist.children[0].children[0].children[1].classes.push("svg-align");
|
||||
} else {
|
||||
vlist = buildCommon.makeVList([
|
||||
vlist = buildCommon.makeVList({
|
||||
positionType: "bottom",
|
||||
positionData: body.depth + 0.1 + braceBody.height,
|
||||
children: [
|
||||
{type: "elem", elem: braceBody},
|
||||
{type: "kern", size: 0.1},
|
||||
{type: "elem", elem: body},
|
||||
], "bottom", body.depth + 0.1 + braceBody.height, options);
|
||||
],
|
||||
}, options);
|
||||
vlist.children[0].children[0].children[0].classes.push("svg-align");
|
||||
}
|
||||
|
||||
@@ -1349,18 +1402,24 @@ groupTypes.horizBrace = function(group, options) {
|
||||
[vlist], options);
|
||||
|
||||
if (group.value.isOver) {
|
||||
vlist = buildCommon.makeVList([
|
||||
vlist = buildCommon.makeVList({
|
||||
positionType: "firstBaseline",
|
||||
children: [
|
||||
{type: "elem", elem: vSpan},
|
||||
{type: "kern", size: 0.2},
|
||||
{type: "elem", elem: supSubGroup},
|
||||
], "firstBaseline", null, options);
|
||||
],
|
||||
}, options);
|
||||
} else {
|
||||
vlist = buildCommon.makeVList([
|
||||
vlist = buildCommon.makeVList({
|
||||
positionType: "bottom",
|
||||
positionData: vSpan.depth + 0.2 + supSubGroup.height,
|
||||
children: [
|
||||
{type: "elem", elem: supSubGroup},
|
||||
{type: "kern", size: 0.2},
|
||||
{type: "elem", elem: vSpan},
|
||||
], "bottom", vSpan.depth + 0.2 + supSubGroup.height,
|
||||
options);
|
||||
],
|
||||
}, options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1376,11 +1435,15 @@ groupTypes.accentUnder = function(group, options) {
|
||||
const kern = (/tilde/.test(group.value.label) ? 0.12 : 0);
|
||||
|
||||
// Generate the vlist, with the appropriate kerns
|
||||
const vlist = buildCommon.makeVList([
|
||||
const vlist = buildCommon.makeVList({
|
||||
positionType: "bottom",
|
||||
positionData: accentBody.height + kern,
|
||||
children: [
|
||||
{type: "elem", elem: accentBody},
|
||||
{type: "kern", size: kern},
|
||||
{type: "elem", elem: innerGroup},
|
||||
], "bottom", accentBody.height + kern, options);
|
||||
],
|
||||
}, options);
|
||||
|
||||
vlist.children[0].children[0].children[0].classes.push("svg-align");
|
||||
|
||||
@@ -1429,17 +1492,23 @@ groupTypes.enclose = function(group, options) {
|
||||
|
||||
let vlist;
|
||||
if (isColorbox) {
|
||||
vlist = buildCommon.makeVList([
|
||||
vlist = buildCommon.makeVList({
|
||||
positionType: "individualShift",
|
||||
children: [
|
||||
// Put the color background behind inner;
|
||||
{type: "elem", elem: img, shift: imgShift},
|
||||
{type: "elem", elem: inner, shift: 0},
|
||||
], "individualShift", null, options);
|
||||
],
|
||||
}, options);
|
||||
} else {
|
||||
vlist = buildCommon.makeVList([
|
||||
vlist = buildCommon.makeVList({
|
||||
positionType: "individualShift",
|
||||
children: [
|
||||
// Write the \cancel stroke on top of inner.
|
||||
{type: "elem", elem: inner, shift: 0},
|
||||
{type: "elem", elem: img, shift: imgShift},
|
||||
], "individualShift", null, options);
|
||||
],
|
||||
}, options);
|
||||
}
|
||||
|
||||
if (/cancel/.test(label)) {
|
||||
@@ -1487,16 +1556,22 @@ groupTypes.xArrow = function(group, options) {
|
||||
const lowerShift = -options.fontMetrics().axisHeight
|
||||
+ lowerGroup.height + 0.5 * arrowBody.height
|
||||
+ 0.111;
|
||||
vlist = buildCommon.makeVList([
|
||||
vlist = buildCommon.makeVList({
|
||||
positionType: "individualShift",
|
||||
children: [
|
||||
{type: "elem", elem: upperGroup, shift: upperShift},
|
||||
{type: "elem", elem: arrowBody, shift: arrowShift},
|
||||
{type: "elem", elem: lowerGroup, shift: lowerShift},
|
||||
], "individualShift", null, options);
|
||||
],
|
||||
}, options);
|
||||
} else {
|
||||
vlist = buildCommon.makeVList([
|
||||
vlist = buildCommon.makeVList({
|
||||
positionType: "individualShift",
|
||||
children: [
|
||||
{type: "elem", elem: upperGroup, shift: upperShift},
|
||||
{type: "elem", elem: arrowBody, shift: arrowShift},
|
||||
], "individualShift", null, options);
|
||||
],
|
||||
}, options);
|
||||
}
|
||||
|
||||
vlist.children[0].children[0].children[1].classes.push("svg-align");
|
||||
@@ -1522,10 +1597,11 @@ groupTypes.raisebox = function(group, options) {
|
||||
size: 6, // simulate \normalsize
|
||||
}}, options);
|
||||
const dy = calculateSize(group.value.dy.value, options);
|
||||
return buildCommon.makeVList([{
|
||||
type: "elem",
|
||||
elem: body,
|
||||
}], "shift", -dy, options);
|
||||
return buildCommon.makeVList({
|
||||
positionType: "shift",
|
||||
positionData: -dy,
|
||||
children: [{type: "elem", elem: body}],
|
||||
}, options);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -307,7 +307,11 @@ const makeStackedDelim = function(delim, heightTotal, center, options, mode,
|
||||
|
||||
// Finally, build the vlist
|
||||
const newOptions = options.havingBaseStyle(Style.TEXT);
|
||||
const inner = buildCommon.makeVList(inners, "bottom", depth, newOptions);
|
||||
const inner = buildCommon.makeVList({
|
||||
positionType: "bottom",
|
||||
positionData: depth,
|
||||
children: inners,
|
||||
}, newOptions);
|
||||
|
||||
return styleWrap(
|
||||
buildCommon.makeSpan(["delimsizing", "mult"], [inner], newOptions),
|
||||
|
@@ -243,7 +243,10 @@ const htmlBuilder = function(group, options) {
|
||||
col.push({type: "elem", elem: elem, shift: shift});
|
||||
}
|
||||
|
||||
col = buildCommon.makeVList(col, "individualShift", null, options);
|
||||
col = buildCommon.makeVList({
|
||||
positionType: "individualShift",
|
||||
children: col,
|
||||
}, options);
|
||||
col = buildCommon.makeSpan(
|
||||
["col-align-" + (colDescr.align || "c")],
|
||||
[col]);
|
||||
|
@@ -63,9 +63,10 @@ defineFunction({
|
||||
}
|
||||
|
||||
// See smash for comment re: use of makeVList
|
||||
node = buildCommon.makeVList([
|
||||
{type: "elem", elem: node},
|
||||
], "firstBaseline", null, options);
|
||||
node = buildCommon.makeVList({
|
||||
positionType: "firstBaseline",
|
||||
children: [{type: "elem", elem: node}],
|
||||
}, options);
|
||||
|
||||
return node;
|
||||
},
|
||||
|
Reference in New Issue
Block a user