nehan-speech-border
Version:
nehan plugin to display speech border
123 lines (112 loc) • 4.26 kB
text/typescript
import { CssStyleSheet, LogicalPos, LogicalEdgeMap, LogicalEdgeDirection, DomCallbackContext, DynamicStyleContext, LogicalCssEvaluator, WritingMode } from "nehan";
export interface StyleArgs {
selector?: string;
direction: LogicalEdgeDirection;
bgColor?: string;
borderSize?: number;
borderColor?: string;
borderRadius?: number;
triangleOffset?: number;
};
const defaultStyle = {
selector: ".speech-border",
direction: "start",
bgColor: "white",
borderColor: "#bcbcbc",
borderSize: 2,
borderRadius: 4,
triangleOffset: 12,
};
const TriangleClipPath = {
"top": "polygon(50% 0, 100% 100%, 0 100%)",
"right": "polygon(0 0, 100% 50%, 0 100%)",
"bottom": "polygon(0 0, 100% 0, 50% 100%)",
"left": "polygon(0 50%, 100% 0, 100% 100%)",
};
function getTrianglePos(direction: LogicalEdgeDirection, triangleOffset: number) {
switch (direction) {
case "before": return {
containerPos: { before: "0px", start: "0px" },
outerPos: new LogicalPos({ start: triangleOffset, after: 0 }),
innerPos: new LogicalPos({ start: triangleOffset + 2, after: 0 })
};
case "after": return {
containerPos: { after: "0px", start: "0px" },
outerPos: new LogicalPos({ start: triangleOffset, before: 0 }),
innerPos: new LogicalPos({ start: triangleOffset + 2, before: 0 }),
};
case "start": return {
containerPos: { before: "0px", start: "0px" },
outerPos: new LogicalPos({ start: -16, before: triangleOffset }),
innerPos: new LogicalPos({ start: -12, before: triangleOffset + 2 }),
};
case "end": return {
containerPos: { before: "0px", end: "0px" },
outerPos: new LogicalPos({ end: -16, before: triangleOffset }),
innerPos: new LogicalPos({ end: -12, before: triangleOffset + 2 }),
};
}
};
function getClipPath(writingMode: WritingMode, direction: LogicalEdgeDirection) {
const logicalMap = LogicalEdgeMap.select(writingMode);
const clipDir = logicalMap.get(direction);
return TriangleClipPath[clipDir];
}
/**
@param {Object} args
@param {Nehan.LogicalEdgeDirection} args.direction
@param {String} args.bgColor
@param {String} args.borderColor
@return {Nehan.CssStyleSheet}
*/
export const create = (args: StyleArgs): CssStyleSheet => {
const style = { ...defaultStyle, ...args };
const trianglePos = getTrianglePos(style.direction, style.triangleOffset);
return new CssStyleSheet({
[`${style.selector}`]: {
marginAfter: "1em",
},
[`${style.selector}.${style.direction}>.content`]: {
[`margin-${style.direction}`]: "1em",
"padding": "0.5em",
"background": style.bgColor,
"border": `${style.borderSize}px solid ${style.borderColor}`,
"borderRadius": `${style.borderRadius}px`,
},
[`${style.selector}.${style.direction}>.content::before`]: {
"display": "block",
"position": "absolute",
"content": " ",
"measure": "0",
"extent": "0",
"!logical-pos": (ctx: DynamicStyleContext) => {
return trianglePos.containerPos;
},
"@oncreate": (ctx: DomCallbackContext) => {
const writingMode = ctx.box.env.writingMode;
const evaluator = new LogicalCssEvaluator(writingMode);
const clipPath = getClipPath(writingMode, style.direction);
const $outer = document.createElement("div");
$outer.style.position = "absolute";
$outer.style.display = "block";
$outer.style.content = " ";
$outer.style.width = "16px";
$outer.style.height = "16px";
$outer.style.backgroundColor = style.borderColor;
$outer.style.clipPath = clipPath;
const $inner = document.createElement("div");
$inner.style.position = "absolute";
$inner.style.display = "block";
$inner.style.content = " ";
$inner.style.width = "12px";
$inner.style.height = "12px";
$inner.style.backgroundColor = style.bgColor;
$inner.style.clipPath = clipPath;
trianglePos.outerPos.acceptCssEvaluator(evaluator).applyTo($outer.style);
trianglePos.innerPos.acceptCssEvaluator(evaluator).applyTo($inner.style);
ctx.dom.appendChild($outer);
ctx.dom.appendChild($inner);
}
}
});
};