diff --git a/README.md b/README.md index 7377fc72a..0307bee11 100644 --- a/README.md +++ b/README.md @@ -153,6 +153,29 @@ annotations = [ In the example above, the "Strong promoter" would span the first to twenty-second base pair. +Optional Annotation Parameters: + - Annotation border styling; user can customize border style and border color: + ```js +annotations = [ + { start: 0, end: 22, name: "Strong promoter", direction: 1, border: {style: "dashed", borderColor: 'purple'}, +]; +``` + - Annotation font styling; user can change font family, font weight, or font color: + ```js +annotations = [ + { start: 0, end: 22, name: "Strong promoter", direction: 1, border: "dashed", font: {fontFamily: "Times New Roman", fontWeight: 800, fontSize: 17, fontColor: 'blue'}}, +]; +``` + - Adding an svg icon to annotation: WIP + + - Annotation background color gradient. User can enter a starting color and stopping color: +```js +annotations = [ + { start: 0, end: 22, name: "Strong promoter", direction: 1, border: "dashed", font: {fontFamily: "Times New Roman", fontWeight: 800, fontSize: 17, fontColor: 'blue'}}, + svg: 'promoter', gradient: {start: 'lightblue', stop: 'green'} +]; +``` + #### `primers (=[])` An array of `Primer`s to render. Each `Primer` requires 0-based start (inclusive) and end (exclusive) indexes. `name`s are rendered on top of the primers. Set the primer's direction to `1` for forward primer and `-1` for reverse primer. diff --git a/demo/public/annotations-example.png b/demo/public/annotations-example.png new file mode 100644 index 000000000..4a2534782 Binary files /dev/null and b/demo/public/annotations-example.png differ diff --git a/src/Linear/Annotations.tsx b/src/Linear/Annotations.tsx index 6e3607eb6..6b0f00e14 100644 --- a/src/Linear/Annotations.tsx +++ b/src/Linear/Annotations.tsx @@ -182,7 +182,15 @@ const SingleNamedElement = (props: { } // 0.591 is our best approximation of Roboto Mono's aspect ratio (width / height). - const fontSize = 12; + let fontSize = 12; + if (element.font?.fontSize) { + // 19 is a subjective limit to fontSize that will fit inside bounds of annotation. If larger than 19, cap it. + if (element.font.fontSize > 19) { + fontSize = 19; + } else { + fontSize = element.font.fontSize; + } + } const annotationCharacterWidth = 0.591 * fontSize; const availableCharacters = Math.floor((width - 40) / annotationCharacterWidth); @@ -198,10 +206,53 @@ const SingleNamedElement = (props: { } } + let strokeVal: string | null = null; + let strokeWidth: string | null = null; + + if (element.border?.style) { + switch (element.border.style) { + case "dashed": + strokeVal = "5, 5"; + break; + case "dotted": + strokeVal = "1, 5"; + break; + case "bold": + strokeWidth = "2"; + break; + } + } + let borderColor: string | null = null; + if (element.border?.borderColor) { + borderColor = element.border.borderColor; + } + + let fontFamily: string | undefined = undefined; + let fontWeight: number = 400; + let fontColor: string = "rgb(42, 42, 42)"; + + if (element.font?.fontFamily) { + fontFamily = element.font.fontFamily; + } + if (element.font?.fontWeight) { + fontWeight = element.font.fontWeight; + } + if (element.font?.fontColor) { + fontColor = element.font.fontColor; + } + return ( {/* provides a hover tooltip on most browsers */} <title>{name} + {element.gradient && ( + + + + + + + )} { // do nothing }} @@ -233,7 +286,7 @@ const SingleNamedElement = (props: { dominantBaseline="middle" fontSize={fontSize} id={element.id} - style={annotationLabel} + style={{ ...annotationLabel, fontFamily, fontWeight, fill: fontColor }} textAnchor="middle" x={width / 2} y={height / 2 + 1} diff --git a/src/elements.ts b/src/elements.ts index c81b327e9..5e3b07744 100644 --- a/src/elements.ts +++ b/src/elements.ts @@ -10,11 +10,34 @@ export interface NameRange extends Range { color?: string; id: string; name: string; + border?: Border; + font?: Font; + gradient?: Gradient; +} + +interface Font { + fontFamily?: string; + fontSize?: number; + fontWeight?: number; + fontColor?: string; +} + +interface Border { + style?: "dashed" | "dotted" | "bold"; + borderColor?: string; +} + +interface Gradient { + start: string; + stop: string; } /** AnnotationProp is an annotation provided to SeqViz via the annotations prop. */ export interface AnnotationProp { color?: string; + border?: Border; + font?: Font; + gradient?: Gradient; direction?: number | string; end: number; name: string; diff --git a/src/style.ts b/src/style.ts index 797dd2fe1..90d0e80c5 100644 --- a/src/style.ts +++ b/src/style.ts @@ -24,7 +24,6 @@ export const svgText: CSS.Properties = { MozUserSelect: "none", WebkitUserSelect: "none", background: "none", - fill: "rgb(42, 42, 42)", fontFamily: "Roboto Mono, Monaco, monospace", msUserSelect: "none", userSelect: "none", @@ -98,8 +97,6 @@ export const annotation: CSS.Properties = { export const annotationLabel: CSS.Properties = { ...svgText, - color: "rgb(42, 42, 42)", - fontWeight: 400, shapeRendering: "geometricPrecision", strokeLinejoin: "round", textRendering: "optimizeLegibility", @@ -151,3 +148,9 @@ export const seqBlock: CSS.Properties = { padding: 0, width: "100%", }; + +export const iconStyle: CSS.Properties = { + width: "25px", + height: "25px", + zIndex: 100, +}; diff --git a/webpack.config.js b/webpack.config.js index 87e4b36d6..2feafa870 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -14,7 +14,7 @@ const cdnBuild = { module: { rules: [ { test: /\.(t|j)sx?$/, loader: "ts-loader", exclude: /node_modules/ }, - { test: /\.js$/, enforce: "pre", loader: "source-map-loader", exclude: /node_modules/ }, + { test: /\.js$/, enforce: "pre", loader: "source-map-loader", exclude: /node_modules/ } ], }, optimization: {