@teselagen/ove 0.8.35 → 0.8.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teselagen/ove",
3
- "version": "0.8.35",
3
+ "version": "0.8.37",
4
4
  "main": "./src/index.js",
5
5
  "type": "module",
6
6
  "repository": "https://github.com/TeselaGen/tg-oss",
@@ -20,9 +20,8 @@
20
20
  "@teselagen/range-utils": "0.3.20",
21
21
  "@teselagen/react-list": "0.8.18",
22
22
  "@teselagen/sequence-utils": "0.3.42",
23
- "@teselagen/ui": "0.10.19",
23
+ "@teselagen/ui": "0.10.20",
24
24
  "@use-gesture/react": "10.3.0",
25
- "biomsa": "^0.2.4",
26
25
  "classnames": "^2.3.2",
27
26
  "clipboard": "^2.0.11",
28
27
  "color": "^3.2.1",
@@ -47,7 +46,6 @@
47
46
  "react-dom": "^18.3.1",
48
47
  "react-draggable": "4.4.5",
49
48
  "react-dropzone": "^11.4.2",
50
- "react-markdown": "9.0.1",
51
49
  "react-measure": "^2.5.2",
52
50
  "react-redux": "^8.0.5",
53
51
  "react-sizeme": "^2.6.12",
@@ -56,12 +54,16 @@
56
54
  "redux-act": "^1.8.0",
57
55
  "redux-form": "^8.3.10",
58
56
  "redux-thunk": "2.4.1",
59
- "remark-gfm": "^4.0.0",
60
57
  "reselect": "^4.1.7",
61
58
  "shortid": "2.2.16",
62
59
  "tg-use-local-storage-state": "^16.0.3",
63
- "to-regex-range": "5.0.1",
64
60
  "use-debounce": "^8.0.4",
65
61
  "validate.io-nonnegative-integer-array": "^1.0.1"
62
+ },
63
+ "devDependencies": {
64
+ "biomsa": "^0.2.4",
65
+ "react-markdown": "9.0.1",
66
+ "remark-gfm": "^4.0.0",
67
+ "to-regex-range": "5.0.1"
66
68
  }
67
69
  }
@@ -1,5 +1,4 @@
1
1
  import { debounce, find, get, some, isArray } from "lodash-es";
2
- // import sizeMe from "react-sizeme";
3
2
  import { showContextMenu } from "@teselagen/ui";
4
3
  import {
5
4
  Button,
@@ -1,16 +1,48 @@
1
1
  import { ANNOTATION_LABEL_FONT_WIDTH } from "./constants";
2
2
  import { getWidth } from "./getXStartAndWidthOfRowAnnotation";
3
3
 
4
- // Cache canvas context for text measurement
4
+ // Cache canvas context and computed font size for text measurement
5
5
  let measureCanvas;
6
- export function getAnnotationTextWidth(
7
- text,
8
- fontSize = ANNOTATION_LABEL_FONT_WIDTH,
9
- fontFamily = "monospace"
10
- ) {
6
+ let cachedFontSize = null;
7
+
8
+ /**
9
+ * Get the computed font size for the ve-monospace-font class
10
+ */
11
+ function getVeMonospaceFontSize() {
12
+ if (cachedFontSize !== null) {
13
+ return cachedFontSize;
14
+ }
15
+
16
+ // Create a temporary element to measure the computed font size
17
+ const tempElement = document.createElement("div");
18
+ tempElement.className = "ve-monospace-font";
19
+ tempElement.style.position = "absolute";
20
+ tempElement.style.visibility = "hidden";
21
+ tempElement.style.pointerEvents = "none";
22
+ document.body.appendChild(tempElement);
23
+
24
+ try {
25
+ const computedStyle = window.getComputedStyle(tempElement);
26
+ const fontSize = parseFloat(computedStyle.fontSize);
27
+ cachedFontSize = fontSize || ANNOTATION_LABEL_FONT_WIDTH; // fallback to 10px if parsing fails
28
+ return cachedFontSize;
29
+ } catch (error) {
30
+ console.warn(
31
+ "Failed to compute ve-monospace-font size, using fallback",
32
+ error
33
+ );
34
+ cachedFontSize = ANNOTATION_LABEL_FONT_WIDTH; // fallback
35
+ return cachedFontSize;
36
+ } finally {
37
+ document.body.removeChild(tempElement);
38
+ }
39
+ }
40
+
41
+ export function getAnnotationTextWidth(text, fontFamily = "monospace") {
11
42
  if (!measureCanvas) {
12
43
  measureCanvas = document.createElement("canvas");
13
44
  }
45
+ const fontSize = getVeMonospaceFontSize();
14
46
  const ctx = measureCanvas.getContext("2d");
15
47
  ctx.font = `${fontSize}px ${fontFamily}`;
16
48
  return ctx.measureText(text).width;
@@ -22,9 +54,10 @@ export const doesLabelFitInAnnotation = (
22
54
  charWidth
23
55
  ) => {
24
56
  const textLength = getAnnotationTextWidth(text);
57
+ const fontSize = getVeMonospaceFontSize();
25
58
  const widthMinusOne = range
26
- ? getWidth(range, charWidth, 0) - ANNOTATION_LABEL_FONT_WIDTH * 2
27
- : width - ANNOTATION_LABEL_FONT_WIDTH * 2;
59
+ ? getWidth(range, charWidth, 0) - fontSize * 2
60
+ : width - fontSize * 2;
28
61
  return widthMinusOne > textLength;
29
62
  };
30
63
 
@@ -227,7 +227,7 @@ export const EditAvailabilityItem = connectToEditor(
227
227
  ) : null;
228
228
  });
229
229
 
230
- export function StatusBar({
230
+ function StatusBar({
231
231
  disableSetReadOnly,
232
232
  disableBpEditing,
233
233
  onSave,
@@ -11,6 +11,7 @@ import {
11
11
  removeDuplicatesIcon,
12
12
  useMemoDeepEqual
13
13
  } from "@teselagen/ui";
14
+ import { convertDnaCaretPositionOrRangeToAA } from "@teselagen/sequence-utils";
14
15
  import { map, upperFirst, pick, startCase, isFunction } from "lodash-es";
15
16
  import {
16
17
  AnchorButton,
@@ -86,7 +87,10 @@ const genericAnnotationProperties = ({
86
87
 
87
88
  const annotationsToUse = React.useMemo(
88
89
  () =>
89
- map(annotations, annotation => {
90
+ map(annotations, _annotation => {
91
+ const annotation = isProtein
92
+ ? convertDnaCaretPositionOrRangeToAA(_annotation)
93
+ : _annotation;
90
94
  return {
91
95
  ...annotation,
92
96
  ...(annotation.strand === undefined && {
@@ -95,7 +99,7 @@ const genericAnnotationProperties = ({
95
99
  size: getRangeLength(annotation, sequenceLength)
96
100
  };
97
101
  }),
98
- [annotations, sequenceLength]
102
+ [annotations, sequenceLength, isProtein]
99
103
  );
100
104
 
101
105
  const keyedPartTags = getKeyedTagsAndTagOptions(allPartTags) ?? {};
@@ -180,7 +184,7 @@ const genericAnnotationProperties = ({
180
184
  }
181
185
  }
182
186
  ]),
183
- sizeSchema(isProtein),
187
+ sizeSchema(),
184
188
  ...(withTags && allPartTags
185
189
  ? [
186
190
  {
@@ -79,7 +79,7 @@ class OrfProperties extends React.Component {
79
79
  displayName: "Size (aa)",
80
80
  type: "number"
81
81
  },
82
- sizeSchema(this.props.isProtein),
82
+ sizeSchema(),
83
83
  { path: "frame", type: "number" },
84
84
  { path: "strand", type: "number" }
85
85
  ]
@@ -1,22 +1,18 @@
1
1
  import React from "react";
2
2
  import { isEqual } from "lodash-es";
3
- import { convertDnaCaretPositionOrRangeToAA } from "@teselagen/sequence-utils";
4
3
  import { convertRangeTo1Based } from "@teselagen/range-utils";
5
4
  import selectors from "../../selectors";
6
5
 
7
- export const sizeSchema = isProtein => ({
6
+ export const sizeSchema = () => ({
8
7
  path: "size",
9
8
  type: "number",
10
- render: (val, _record) => {
11
- const record = isProtein
12
- ? convertDnaCaretPositionOrRangeToAA(_record)
13
- : _record;
9
+ render: (val, record) => {
14
10
  const base1Range = convertRangeTo1Based(record);
15
11
  const hasJoinedLocations = record.locations && record.locations.length > 1;
16
12
 
17
13
  return (
18
14
  <span>
19
- {isProtein ? Math.floor(val / 3) : val}{" "}
15
+ {val}{" "}
20
16
  <span style={{ fontSize: 10 }}>
21
17
  {hasJoinedLocations ? (
22
18
  record.locations.map((loc, i) => {
@@ -41,12 +37,9 @@ export const sizeSchema = isProtein => ({
41
37
  export const getMemoOrfs = (() => {
42
38
  let lastDeps;
43
39
  let lastResult;
44
- return (editorState) => {
45
- const {
46
- sequenceData,
47
- minimumOrfSize,
48
- useAdditionalOrfStartCodons
49
- } = editorState;
40
+ return editorState => {
41
+ const { sequenceData, minimumOrfSize, useAdditionalOrfStartCodons } =
42
+ editorState;
50
43
 
51
44
  const { sequence, circular } = sequenceData;
52
45
 
@@ -14,6 +14,7 @@ import { forEach, camelCase, startCase } from "lodash-es";
14
14
  import { sizeSchema } from "../PropertiesDialog/utils";
15
15
  import { getRangeLength } from "@teselagen/range-utils";
16
16
  import { useFormValue } from "../../utils/useFormValue";
17
+ import { convertDnaCaretPositionOrRangeToAA } from "@teselagen/sequence-utils";
17
18
 
18
19
  const dialogFormName = "RemoveDuplicatesDialog";
19
20
  const dataTableFormName = "duplicatesToRemove";
@@ -33,6 +34,7 @@ const RemoveDuplicatesDialog = props => {
33
34
  const ignoreName = useFormValue(dialogFormName, "ignoreName");
34
35
  const ignoreStartAndEnd = useFormValue(dialogFormName, "ignoreStartAndEnd");
35
36
  const ignoreStrand = useFormValue(dialogFormName, "ignoreStrand");
37
+ const isProteinSeq = isProtein || sequenceData.isProtein;
36
38
 
37
39
  const recomputeDups = useCallback(
38
40
  values => {
@@ -42,19 +44,25 @@ const RemoveDuplicatesDialog = props => {
42
44
  const annotations = sequenceData[type];
43
45
  const newDups = [];
44
46
  const seqsHashByStartEndStrandName = {};
45
- forEach(annotations, a => {
46
- const hash = `${ignoreStartAndEnd ? "" : a.start}&${
47
- ignoreStartAndEnd ? "" : a.end
48
- }&${ignoreStrand ? "" : a.strand}&${ignoreName ? "" : a.name}`;
47
+ forEach(annotations, _annotation => {
48
+ const annotation = isProteinSeq
49
+ ? convertDnaCaretPositionOrRangeToAA(_annotation)
50
+ : _annotation;
51
+ const hash = `${ignoreStartAndEnd ? "" : annotation.start}&${
52
+ ignoreStartAndEnd ? "" : annotation.end
53
+ }&${ignoreStrand ? "" : annotation.strand}&${ignoreName ? "" : annotation.name}`;
49
54
  if (seqsHashByStartEndStrandName[hash]) {
50
- newDups.push({ ...a, size: getRangeLength(a, sequenceLength) });
55
+ newDups.push({
56
+ ...annotation,
57
+ size: getRangeLength(annotation, sequenceLength)
58
+ });
51
59
  } else {
52
60
  seqsHashByStartEndStrandName[hash] = true;
53
61
  }
54
62
  });
55
63
  return newDups;
56
64
  },
57
- [sequenceData, sequenceLength, type]
65
+ [sequenceData, sequenceLength, type, isProteinSeq]
58
66
  );
59
67
 
60
68
  const [dups, setDups] = useState(recomputeDups);
@@ -79,11 +87,11 @@ const RemoveDuplicatesDialog = props => {
79
87
  fields: [
80
88
  { path: "name", type: "string" },
81
89
  // ...(noType ? [] : [{ path: "type", type: "string" }]),
82
- sizeSchema(isProtein),
90
+ sizeSchema(),
83
91
  { path: "strand", type: "string" }
84
92
  ]
85
93
  }),
86
- [isProtein]
94
+ []
87
95
  );
88
96
 
89
97
  return (
package/src/index.js CHANGED
@@ -28,10 +28,7 @@ export {
28
28
  default as LinearView,
29
29
  LinearView as LinearViewUnconnected
30
30
  } from "./LinearView";
31
- export {
32
- default as StatusBar,
33
- StatusBar as StatusBarUnconnected
34
- } from "./StatusBar";
31
+ export { default as StatusBar } from "./StatusBar";
35
32
  export {
36
33
  default as DigestTool,
37
34
  DigestTool as DigestToolUnconnected