@teselagen/ove 0.7.30-beta.1 → 0.7.30

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/ove.css CHANGED
@@ -10724,6 +10724,61 @@ li.bp3-menu-divider:last-child {
10724
10724
  .veLabelText {
10725
10725
  user-select: none;
10726
10726
  }
10727
+ .veLabels {
10728
+ font-weight: lighter;
10729
+ }
10730
+
10731
+ .labelTspan:hover {
10732
+ fill: red !important;
10733
+ }
10734
+
10735
+ .labelText {
10736
+ cursor: pointer;
10737
+ }
10738
+
10739
+ .veEditor .veAnnotationHovered:not(.topLevelLabelGroup) {
10740
+ font-weight: bold;
10741
+ }
10742
+ .veEditor
10743
+ .veAnnotationHovered:not(.topLevelLabelGroup):not(
10744
+ .veCircularViewInternalLabelText
10745
+ ) {
10746
+ text-decoration: underline;
10747
+ }
10748
+
10749
+ .veEditor .veAnnotationHovered.veCutsiteLabel {
10750
+ font-weight: bold;
10751
+ text-decoration: underline;
10752
+ }
10753
+
10754
+ .bp3-dark .veLabels .veAnnotationHovered:not(.topLevelLabelGroup) {
10755
+ /* fill: white !important; */
10756
+ font-weight: normal;
10757
+ }
10758
+ .bp3-dark .veLabels .veAnnotationHovered rect {
10759
+ fill: rgb(64, 64, 92) !important;
10760
+ }
10761
+
10762
+ .partWithSelectedTag {
10763
+ font-size: 14px !important;
10764
+ font-weight: 900;
10765
+ text-decoration: underline;
10766
+ }
10767
+ .veCircularView .partWithSelectedTag.vePart .veLabelText {
10768
+ /* stroke: #ac68cc; */
10769
+ font-size: 15px !important;
10770
+ }
10771
+ .vePartLabel.partWithSelectedTag {
10772
+ font-size: large !important;
10773
+ }
10774
+
10775
+ .partWithSelectedTag path,
10776
+ path.partWithSelectedTag {
10777
+ stroke-width: 1.5 !important;
10778
+ }
10779
+ /* .veLabelLine {
10780
+ opacity: 0.1;
10781
+ } */
10727
10782
  .veRowView {
10728
10783
  overflow-x: visible;
10729
10784
  }
@@ -11197,61 +11252,6 @@ li.bp3-menu-divider:last-child {
11197
11252
  .simple-dialog .bp3-form-content {
11198
11253
  height: 40px;
11199
11254
  }
11200
- .veLabels {
11201
- font-weight: lighter;
11202
- }
11203
-
11204
- .labelTspan:hover {
11205
- fill: red !important;
11206
- }
11207
-
11208
- .labelText {
11209
- cursor: pointer;
11210
- }
11211
-
11212
- .veEditor .veAnnotationHovered:not(.topLevelLabelGroup) {
11213
- font-weight: bold;
11214
- }
11215
- .veEditor
11216
- .veAnnotationHovered:not(.topLevelLabelGroup):not(
11217
- .veCircularViewInternalLabelText
11218
- ) {
11219
- text-decoration: underline;
11220
- }
11221
-
11222
- .veEditor .veAnnotationHovered.veCutsiteLabel {
11223
- font-weight: bold;
11224
- text-decoration: underline;
11225
- }
11226
-
11227
- .bp3-dark .veLabels .veAnnotationHovered:not(.topLevelLabelGroup) {
11228
- /* fill: white !important; */
11229
- font-weight: normal;
11230
- }
11231
- .bp3-dark .veLabels .veAnnotationHovered rect {
11232
- fill: rgb(64, 64, 92) !important;
11233
- }
11234
-
11235
- .partWithSelectedTag {
11236
- font-size: 14px !important;
11237
- font-weight: 900;
11238
- text-decoration: underline;
11239
- }
11240
- .veCircularView .partWithSelectedTag.vePart .veLabelText {
11241
- /* stroke: #ac68cc; */
11242
- font-size: 15px !important;
11243
- }
11244
- .vePartLabel.partWithSelectedTag {
11245
- font-size: large !important;
11246
- }
11247
-
11248
- .partWithSelectedTag path,
11249
- path.partWithSelectedTag {
11250
- stroke-width: 1.5 !important;
11251
- }
11252
- /* .veLabelLine {
11253
- opacity: 0.1;
11254
- } */
11255
11255
  .veCircularViewMiddleOfVectorText {
11256
11256
  display: flex;
11257
11257
  height: 100%;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teselagen/ove",
3
- "version": "0.7.30-beta.1",
3
+ "version": "0.7.30",
4
4
  "main": "./src/index.js",
5
5
  "type": "module",
6
6
  "exports": {
@@ -11,16 +11,19 @@
11
11
  "./*": "./*"
12
12
  },
13
13
  "dependencies": {
14
- "@teselagen/sequence-utils": "0.3.32-beta.1",
15
- "@teselagen/range-utils": "0.3.14-beta.1",
16
- "@teselagen/ui": "0.8.6-beta.24",
17
- "@teselagen/file-utils": "0.3.20",
18
- "@teselagen/bio-parsers": "0.4.29-beta.1",
19
14
  "@blueprintjs/core": "3.54.0",
20
15
  "@hello-pangea/dnd": "16.2.0",
21
16
  "@risingstack/react-easy-state": "^6.3.0",
17
+ "@teselagen/bio-parsers": "0.4.28",
18
+ "@teselagen/file-utils": "0.3.20",
19
+ "@teselagen/range-utils": "0.3.13",
22
20
  "@teselagen/react-list": "0.8.18",
21
+ "@teselagen/sequence-utils": "0.3.31",
22
+ "@teselagen/ui": "0.9.4",
23
+ "@use-gesture/react": "10.3.0",
24
+ "biomsa": "^0.2.4",
23
25
  "classnames": "^2.3.2",
26
+ "clipboard": "^2.0.11",
24
27
  "color": "^3.2.1",
25
28
  "combokeys": "^3.0.1",
26
29
  "copy-to-clipboard": "^3.3.1",
@@ -54,15 +57,11 @@
54
57
  "redux-thunk": "2.4.1",
55
58
  "remark-gfm": "^4.0.0",
56
59
  "reselect": "^4.1.7",
60
+ "shortid": "2.2.16",
57
61
  "tg-use-local-storage-state": "^16.0.3",
58
62
  "to-regex-range": "5.0.1",
59
63
  "use-debounce": "^8.0.4",
60
- "validate.io-nonnegative-integer-array": "^1.0.1",
61
- "cypress-real-events": "^1.13.0",
62
- "biomsa": "^0.2.4",
63
- "shortid": "2.2.16",
64
- "@use-gesture/react": "10.3.0",
65
- "@playwright/test": "^1.44.1"
64
+ "validate.io-nonnegative-integer-array": "^1.0.1"
66
65
  },
67
66
  "license": "MIT"
68
67
  }
@@ -15,9 +15,6 @@ import { connect } from "react-redux";
15
15
  import {
16
16
  Button,
17
17
  Intent,
18
- Popover,
19
- Menu,
20
- MenuItem,
21
18
  Tooltip,
22
19
  Icon,
23
20
  Spinner,
@@ -108,8 +105,6 @@ export const AlignmentView = props => {
108
105
  minimapLaneSpacing,
109
106
  isInPairwiseOverviewView,
110
107
  noVisibilityOptions,
111
- updateAlignmentSortOrder,
112
- alignmentSortOrder,
113
108
  handleBackButtonClicked,
114
109
  allowTrimming,
115
110
  additionalSelectionLayerRightClickedOptions,
@@ -616,7 +611,6 @@ export const AlignmentView = props => {
616
611
  } else {
617
612
  i = _i;
618
613
  }
619
-
620
614
  const track = alignmentTracks?.[i];
621
615
  if (!track) return null;
622
616
  const {
@@ -636,7 +630,7 @@ export const AlignmentView = props => {
636
630
  });
637
631
  const linearViewWidth =
638
632
  (alignmentData || sequenceData).sequence.length * charWidth;
639
- const name = sequenceData.name || sequenceData.id;
633
+ const name = alignmentData.name || sequenceData.name || sequenceData.id;
640
634
 
641
635
  const tickSpacing = massageTickSpacing(Math.ceil(120 / charWidth));
642
636
 
@@ -1261,7 +1255,7 @@ export const AlignmentView = props => {
1261
1255
  await navigator.clipboard.writeText(seqDataToCopy);
1262
1256
  };
1263
1257
 
1264
- const getAllAlignmentsFastaText = async () => {
1258
+ const copyAllAlignmentsFastaText = async () => {
1265
1259
  await navigator.clipboard.writeText(
1266
1260
  getAllAlignmentsFastaText()
1267
1261
  );
@@ -1281,7 +1275,7 @@ export const AlignmentView = props => {
1281
1275
  "copyAllAlignmentsFastaClipboardHelper",
1282
1276
  hotkey: "cmd+c",
1283
1277
  onClick: () => {
1284
- getAllAlignmentsFastaText();
1278
+ copyAllAlignmentsFastaText();
1285
1279
  window.toastr.success("Selection Copied");
1286
1280
  }
1287
1281
  },
@@ -1289,8 +1283,8 @@ export const AlignmentView = props => {
1289
1283
  text: `Copy Selection of ${name} as Fasta`,
1290
1284
  className:
1291
1285
  "copySpecificAlignmentFastaClipboardHelper",
1292
- onClick: () => {
1293
- copySpecificAlignmentFasta();
1286
+ onClick: e => {
1287
+ copySpecificAlignmentFasta(e);
1294
1288
  window.toastr.success(
1295
1289
  "Selection Copied As Fasta"
1296
1290
  );
@@ -1581,37 +1575,6 @@ export const AlignmentView = props => {
1581
1575
  {...alignmentVisibilityToolOptions}
1582
1576
  />
1583
1577
  )}
1584
- {updateAlignmentSortOrder && !isInPairwiseOverviewView && (
1585
- <Popover
1586
- minimal
1587
- content={
1588
- <Menu>
1589
- <MenuItem
1590
- active={true || alignmentSortOrder}
1591
- onClick={() => {
1592
- updateAlignmentSortOrder("Position");
1593
- }}
1594
- text="Position"
1595
- />
1596
- <MenuItem
1597
- active={false || alignmentSortOrder}
1598
- onClick={() => {
1599
- updateAlignmentSortOrder("Alphabetical");
1600
- }}
1601
- text="Alphabetical"
1602
- />
1603
- </Menu>
1604
- }
1605
- target={
1606
- <Button
1607
- small
1608
- text="Sort Order"
1609
- rightIcon="caret-down"
1610
- icon="sort"
1611
- />
1612
- }
1613
- />
1614
- )}
1615
1578
  {additionalTopEl}
1616
1579
  {saveMessage && (
1617
1580
  <div
@@ -33,7 +33,7 @@ import {
33
33
  } from "@teselagen/ui";
34
34
  import { startCase } from "lodash-es";
35
35
  import withEditorProps from "./withEditorProps";
36
- import React, { useEffect, useState } from "react";
36
+ import { useEffect, useState } from "react";
37
37
  import { Colors, Tab, Tabs } from "@blueprintjs/core";
38
38
  import { typeField } from "./helperComponents/PropertiesDialog/typeField";
39
39
 
@@ -8,11 +8,19 @@ import { avoidOverlapWith } from "../drawAnnotations";
8
8
 
9
9
  const fontWidthToFontSize = 1.75;
10
10
 
11
- const getTextLength = text => {
12
- let len = (text || "Unlabeled").length;
11
+ export const getTextLengthWithCollapseSpace = (text, collapseWhiteSpace = true) => {
12
+ let displayText = text || "Unlabeled";
13
+ if (collapseWhiteSpace) {
14
+ displayText = displayText.replaceAll(
15
+ /\s+/g,
16
+ " "
17
+ );
18
+ }
19
+ let len = displayText.length;
13
20
  // eslint-disable-next-line no-control-regex
14
21
  const nonEnInputReg = /[^\x00-\xff]+/g;
15
- const nonEnStrings = (text || "Unlabeled").match(nonEnInputReg) || [];
22
+ const nonEnStrings =
23
+ displayText.match(nonEnInputReg) || [];
16
24
  nonEnStrings.forEach(str => (len += str.length * 0.5));
17
25
  return len;
18
26
  };
@@ -58,7 +66,7 @@ function Labels({
58
66
  _annotationCenterAngle + (rotationRadians || 0);
59
67
  return {
60
68
  ...label,
61
- width: getTextLength(label.text) * fontWidth,
69
+ width: getTextLengthWithCollapseSpace(label.text) * fontWidth,
62
70
  //three points define the label:
63
71
  innerPoint: {
64
72
  ...polarToSpecialCartesian(
@@ -214,42 +222,26 @@ const DrawLabelGroup = withHover(function ({
214
222
  }) {
215
223
  let { text = "Unlabeled" } = label;
216
224
 
217
- const textLength = getTextLength(text);
218
225
  let groupLabelXStart;
219
226
  //Add the number of unshown labels
220
227
  if (label.labelAndSublabels && label.labelAndSublabels.length > 1) {
221
- // if (label.x > 0) {
222
228
  text = "+" + (label.labelAndSublabels.length - 1) + "," + text;
223
- // } else {
224
- // text += ', +' + (label.labelAndSublabels.length - 1)
225
- // }
226
229
  }
227
230
 
228
- const labelLength = textLength * fontWidth;
229
- const maxLabelLength = labelAndSublabels.reduce(function (
230
- currentLength,
231
- { text = "Unlabeled" }
232
- ) {
233
- const _textLength = getTextLength(text);
234
- if (_textLength > currentLength) {
235
- return _textLength;
236
- }
237
- return currentLength;
238
- }, 0);
231
+ const labelLength = getTextLengthWithCollapseSpace(text) * fontWidth;
239
232
 
240
- const maxLabelWidth = maxLabelLength * fontWidth;
241
233
  const labelOnLeft = label.angle > Math.PI;
242
234
  let labelXStart = label.x - (labelOnLeft ? labelLength : 0);
235
+ const maxDistance =
236
+ (outerRadius + 90) * Math.max(1, circularViewWidthVsHeightRatio);
243
237
  if (condenseOverflowingXLabels) {
244
238
  const distancePastBoundary =
245
239
  Math.abs(label.x + (labelOnLeft ? -labelLength : labelLength)) -
246
- (outerRadius + 90) * Math.max(1, circularViewWidthVsHeightRatio);
247
- // Math.max(outerRadius (circularViewWidthVsHeightRatio / 2 + 80));
240
+ maxDistance;
241
+
248
242
  if (distancePastBoundary > 0) {
249
243
  const numberOfCharsToChop =
250
244
  Math.ceil(distancePastBoundary / fontWidth) + 2;
251
- // if (numberOfCharsToChop > text.length) numberOfCharsToChop = text.length
252
- //label overflows the boundaries!
253
245
  text = text.slice(0, -numberOfCharsToChop) + "..";
254
246
  groupLabelXStart =
255
247
  labelXStart +
@@ -261,17 +253,58 @@ const DrawLabelGroup = withHover(function ({
261
253
  const textYStart = label.y + dy / 2;
262
254
 
263
255
  //if label xStart or label xEnd don't fit within the canvas, we need to shorten the label..
264
-
265
256
  let content;
266
257
  const labelClass = ` veLabelText veLabel veCircularViewLabelText clickable ${label.color} `;
267
258
 
268
259
  if ((multipleLabels || groupLabelXStart !== undefined) && hovered) {
260
+ // Calculate the maximum label length for hovered status
261
+ const maxLabelLength = labelAndSublabels.reduce(function (
262
+ currentLength,
263
+ { text = "Unlabeled" }
264
+ ) {
265
+ const _textLength = getTextLengthWithCollapseSpace(text);
266
+ if (_textLength > currentLength) {
267
+ return _textLength;
268
+ }
269
+ return currentLength;
270
+ }, 0);
271
+ const maxLabelWidth = maxLabelLength * fontWidth;
272
+
273
+ labelXStart = label.x - (labelOnLeft ? maxLabelWidth : 0);
274
+ let distancePastBoundary = Math.abs(label.x + (labelOnLeft ? -maxLabelWidth : maxLabelWidth)) - maxDistance;
275
+ let lableRectWidth = maxLabelWidth - 14;
276
+
277
+ if (maxLabelWidth > maxDistance * 2) {
278
+ labelXStart = -maxDistance;
279
+ lableRectWidth = maxDistance * 2 - 24;
280
+ distancePastBoundary = maxLabelWidth - maxDistance * 2;
281
+ } else if (distancePastBoundary > 0) {
282
+ labelXStart += (labelOnLeft ? distancePastBoundary : -distancePastBoundary);
283
+ distancePastBoundary = 0;
284
+ }
285
+
269
286
  //HOVERED: DRAW MULTIPLE LABELS IN A RECTANGLE
270
287
  window.isLabelGroupOpen = true;
271
288
  let hoveredLabel;
272
- if (groupLabelXStart !== undefined) {
273
- labelXStart = groupLabelXStart;
289
+
290
+ let truncatedLabelAndSublabels;
291
+ if (distancePastBoundary > 0) {
292
+ truncatedLabelAndSublabels = labelAndSublabels.map(lable => {
293
+ const labelWidth = getTextLengthWithCollapseSpace(lable.text) * fontWidth;
294
+ const truncatedText =
295
+ labelWidth >= lableRectWidth
296
+ ? lable.text.slice(
297
+ 0,
298
+ -Math.ceil((labelWidth - lableRectWidth) / fontWidth) - 2
299
+ ) + ".."
300
+ : lable.text;
301
+ return {
302
+ ...lable,
303
+ text: truncatedText
304
+ };
305
+ });
274
306
  }
307
+
275
308
  labelAndSublabels.some(function (label) {
276
309
  if (label.id === hoveredId) {
277
310
  hoveredLabel = label;
@@ -317,7 +350,7 @@ const DrawLabelGroup = withHover(function ({
317
350
  // zIndex={10}
318
351
  x={labelXStart - 4}
319
352
  y={labelYStart - dy / 2}
320
- width={maxLabelWidth + 24}
353
+ width={lableRectWidth + 24}
321
354
  height={labelGroupHeight + 4}
322
355
  fill="white"
323
356
  stroke="black"
@@ -331,24 +364,26 @@ const DrawLabelGroup = withHover(function ({
331
364
  fontStyle: label.fontStyle
332
365
  }}
333
366
  >
334
- {labelAndSublabels.map(function (label, index) {
335
- return (
336
- <DrawGroupInnerLabel
337
- isSubLabel
338
- noRedux={noRedux}
339
- editorName={editorName}
340
- logHover
341
- key={"labelItem" + index}
342
- className={
343
- (label.className || "") +
344
- labelClass +
345
- " veDrawGroupInnerLabel"
346
- }
347
- id={label.id}
348
- {...{ labelXStart, label, fontWidth, index, dy }}
349
- />
350
- );
351
- })}
367
+ {(truncatedLabelAndSublabels || labelAndSublabels).map(
368
+ function (label, index) {
369
+ return (
370
+ <DrawGroupInnerLabel
371
+ isSubLabel
372
+ noRedux={noRedux}
373
+ editorName={editorName}
374
+ logHover
375
+ key={"labelItem" + index}
376
+ className={
377
+ (label.className || "") +
378
+ labelClass +
379
+ " veDrawGroupInnerLabel"
380
+ }
381
+ id={label.id}
382
+ {...{ labelXStart, label, fontWidth, index, dy }}
383
+ />
384
+ );
385
+ }
386
+ )}
352
387
  </text>
353
388
  </g>
354
389
  </PutMyParentOnTop>
@@ -364,7 +399,7 @@ const DrawLabelGroup = withHover(function ({
364
399
  data-title={label.title || label.text}
365
400
  {...avoidOverlapWith}
366
401
  x={labelXStart}
367
- textLength={getTextLength(text) * fontWidth}
402
+ textLength={getTextLengthWithCollapseSpace(text) * fontWidth}
368
403
  lengthAdjust="spacing"
369
404
  className={
370
405
  labelClass + label.className + (hovered ? " veAnnotationHovered" : "")
@@ -438,7 +473,7 @@ const DrawGroupInnerLabel = withHover(
438
473
  data-title={label.title}
439
474
  {...avoidOverlapWith}
440
475
  x={labelXStart}
441
- textLength={getTextLength(label.text) * fontWidth}
476
+ textLength={getTextLengthWithCollapseSpace(label.text) * fontWidth}
442
477
  lengthAdjust="spacing"
443
478
  onClick={label.onClick}
444
479
  onDoubleClick={e => {
@@ -1,5 +1,6 @@
1
1
  import { compose } from "redux";
2
2
  import pluralize from "pluralize";
3
+
3
4
  import { formName } from "./constants";
4
5
  import { typeField } from "./helperComponents/PropertiesDialog/typeField";
5
6
  import {
@@ -8,7 +9,7 @@ import {
8
9
  withSelectTableRecords,
9
10
  withSelectedEntities
10
11
  } from "@teselagen/ui";
11
- import React, { useEffect } from "react";
12
+ import { useEffect } from "react";
12
13
  import { hideDialog } from "./GlobalDialogUtils";
13
14
  import { startCase } from "lodash-es";
14
15
  import { tidyUpAnnotation } from "@teselagen/sequence-utils";
@@ -8,6 +8,7 @@ import { reduce, values, startCase, filter, clamp } from "lodash-es";
8
8
  import { getRangeLength } from "@teselagen/range-utils";
9
9
  import { doesLabelFitInAnnotation } from "./utils";
10
10
  import getAnnotationNameAndStartStopString from "../utils/getAnnotationNameAndStartStopString";
11
+ import { getTextLengthWithCollapseSpace } from "../CircularView/Labels";
11
12
 
12
13
  const BUFFER_WIDTH = 6; //labels shouldn't be less than 6px from eachother on the same line
13
14
 
@@ -98,11 +99,11 @@ function Labels(props) {
98
99
  annotation = annotationRange;
99
100
  }
100
101
  const annotationLength =
101
- (
102
+ getTextLengthWithCollapseSpace(
102
103
  annotation.name ||
103
- (annotation.restrictionEnzyme && annotation.restrictionEnzyme.name) ||
104
- ""
105
- ).length * textWidth;
104
+ (annotation.restrictionEnzyme && annotation.restrictionEnzyme.name) ||
105
+ ""
106
+ ) * textWidth;
106
107
  let { xStart, width } = getXStartAndWidthOfRowAnnotation(
107
108
  annotationRange,
108
109
  bpsPerRow,