@weng-lab/genomebrowser-ui 0.3.6-beta.0 → 0.3.6

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,7 +1,7 @@
1
1
  {
2
2
  "name": "@weng-lab/genomebrowser-ui",
3
3
  "private": false,
4
- "version": "0.3.6-beta.0",
4
+ "version": "0.3.6",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "publishConfig": {
@@ -23,7 +23,7 @@
23
23
  "@mui/x-data-grid-premium": "^8.19.0",
24
24
  "react": "^19.0.0",
25
25
  "react-dom": "^19.0.0",
26
- "@weng-lab/genomebrowser": "1.8.3-beta.0"
26
+ "@weng-lab/genomebrowser": "1.8.3"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@eslint/js": "^9.34.0",
@@ -172,6 +172,10 @@ function TooltipRow({
172
172
  function TfPeaksTooltip(rect: OverlayInteractionRect) {
173
173
  const pwm = rect.pwm;
174
174
  const label = tfDisplayName(rect.name);
175
+ const totalWidth = 340;
176
+ const pad = 8;
177
+ const lineH = 14;
178
+ const titleH = 18;
175
179
 
176
180
  // Build metadata rows (single-value rows)
177
181
  const metaRows: { label: string; value: string }[] = [];
@@ -181,33 +185,47 @@ function TfPeaksTooltip(rect: OverlayInteractionRect) {
181
185
  label: "Position",
182
186
  value: `${rect.chr ? rect.chr + ":" : ""}${rect.start.toLocaleString()}-${rect.end.toLocaleString()}`,
183
187
  });
184
- if (rect.expRatio) metaRows.push({ label: "Exps", value: rect.expRatio });
185
188
 
186
- // Multi-value rows: split comma-separated cCREs, group 3 per row
187
- const cCREItems = rect.cCREId
189
+ // Multi-value rows: split comma-separated cCREs, group 4 per row, cap at 5
190
+ const allCCREItems = rect.cCREId
188
191
  ? rect.cCREId
189
192
  .split(",")
190
193
  .map((s) => s.trim())
191
194
  .filter(Boolean)
192
195
  : [];
196
+ const maxCCREs = 5;
197
+ const cCREItems = allCCREItems.slice(0, maxCCREs);
198
+ const hiddenCCREs = Math.max(0, allCCREItems.length - maxCCREs);
193
199
  const cCRERows: string[][] = [];
194
200
  for (let i = 0; i < cCREItems.length; i += 4) {
195
201
  cCRERows.push(cCREItems.slice(i, i + 4));
196
202
  }
197
203
 
198
- // Parse expSupport JSON into flat rows
199
- const supportRows: { cellLine: string; expId: string; fileId: string }[] = [];
204
+ // Extract unique sorted biosamples from expSupport
205
+ const allBiosamples: string[] = [];
200
206
  if (rect.expSupport) {
201
- for (const [cellLine, exps] of Object.entries(rect.expSupport)) {
202
- for (const [expId, fileId] of Object.entries(exps)) {
203
- supportRows.push({ cellLine, expId, fileId });
207
+ const seen = new Set<string>();
208
+ for (const cellLine of Object.keys(rect.expSupport)) {
209
+ if (!seen.has(cellLine)) {
210
+ seen.add(cellLine);
211
+ allBiosamples.push(cellLine);
204
212
  }
205
213
  }
214
+ allBiosamples.sort((a, b) => a.localeCompare(b));
206
215
  }
207
-
208
- const pad = 8;
209
- const lineH = 14;
210
- const titleH = 18;
216
+ const maxBiosamples = 12;
217
+ const biosampleItems = allBiosamples.slice(0, maxBiosamples);
218
+ const hiddenBiosamples = Math.max(0, allBiosamples.length - maxBiosamples);
219
+ const biosampleText = biosampleItems.join(", ");
220
+ const biosampleMoreText =
221
+ hiddenBiosamples > 0 ? ` ...and ${hiddenBiosamples} more` : "";
222
+ const biosampleCharsPerLine = Math.floor((totalWidth - 2 * pad) / 5.2);
223
+ const biosampleContentLines = Math.max(
224
+ 1,
225
+ Math.ceil(
226
+ (biosampleText.length + biosampleMoreText.length) / biosampleCharsPerLine,
227
+ ),
228
+ );
211
229
 
212
230
  // Layout: compute y offsets upfront
213
231
  const hasLogo = pwm && pwm.length > 0;
@@ -216,26 +234,25 @@ function TfPeaksTooltip(rect: OverlayInteractionRect) {
216
234
  const logoSectionH = hasLogo ? logoHeight + 4 : 0;
217
235
  const metaSectionH = metaRows.length * lineH;
218
236
 
219
- // cCRE section: label row + one row per group of 3
237
+ // cCRE section: label row + one row per group of 4 + optional "+N more" row
220
238
  const cCREGap = cCRERows.length > 0 ? 8 : 0;
221
239
  const cCREHeaderH = cCRERows.length > 0 ? lineH : 0;
222
- const cCRESectionH = cCRERows.length * lineH;
240
+ const cCREMoreH = hiddenCCREs > 0 ? lineH : 0;
241
+ const cCRESectionH = cCRERows.length * lineH + cCREMoreH;
223
242
 
224
- // Support section
225
- const supportGap = supportRows.length > 0 ? 8 : 0;
226
- const supportHeaderH = supportRows.length > 0 ? lineH : 0;
227
- const supportSectionH = supportRows.length * lineH;
243
+ // Biosamples section
244
+ const biosampleGap = biosampleContentLines > 0 ? 8 : 0;
245
+ const biosampleHeaderH = biosampleContentLines > 0 ? lineH : 0;
246
+ const biosampleSectionH = biosampleContentLines * lineH;
228
247
 
229
248
  const titleY = pad;
230
249
  const logoY = titleY + titleH;
231
250
  const metaY = logoY + logoSectionH;
232
251
  const cCREY = metaY + metaSectionH + cCREGap;
233
252
  const cCREDataY = cCREY + cCREHeaderH;
234
- const supportY = cCREDataY + cCRESectionH + supportGap;
235
- const supportDataY = supportY + supportHeaderH;
236
- const totalHeight = supportDataY + supportSectionH + pad;
237
-
238
- const totalWidth = 340;
253
+ const biosampleY = cCREDataY + cCRESectionH + biosampleGap;
254
+ const biosampleDataY = biosampleY + biosampleHeaderH;
255
+ const totalHeight = biosampleDataY + biosampleSectionH + pad;
239
256
 
240
257
  return (
241
258
  <g>
@@ -306,60 +323,64 @@ function TfPeaksTooltip(rect: OverlayInteractionRect) {
306
323
  {group.join(", ")}
307
324
  </text>
308
325
  ))}
326
+ {hiddenCCREs > 0 && (
327
+ <text
328
+ x={pad}
329
+ y={cCREDataY + cCRERows.length * lineH + 2}
330
+ fontSize={9}
331
+ fill="#aaa"
332
+ dominantBaseline="hanging"
333
+ >
334
+ +{hiddenCCREs} more...
335
+ </text>
336
+ )}
309
337
  </g>
310
338
  )}
311
339
 
312
- {/* Experiments supporting this peak */}
313
- {supportRows.length > 0 && (
340
+ {/* Biosamples */}
341
+ {biosampleContentLines > 0 && (
314
342
  <g>
315
343
  <line
316
344
  x1={pad}
317
345
  x2={totalWidth - pad}
318
- y1={supportY - 4}
319
- y2={supportY - 4}
346
+ y1={biosampleY - 4}
347
+ y2={biosampleY - 4}
320
348
  stroke="#ddd"
321
349
  />
322
350
  <text
323
351
  x={pad}
324
- y={supportY + 2}
352
+ y={biosampleY + 2}
325
353
  fontSize={9}
326
354
  fontWeight="bold"
327
355
  fill="#666"
328
356
  dominantBaseline="hanging"
329
357
  >
330
- Experiments supporting this peak
358
+ Biosamples
331
359
  </text>
332
- {supportRows.map((row, i) => (
333
- <g key={i} transform={`translate(0, ${supportDataY + i * lineH})`}>
334
- <text
335
- x={8}
336
- y={0}
337
- fontSize={9}
338
- fill="#666"
339
- dominantBaseline="hanging"
340
- >
341
- {row.cellLine}
342
- </text>
343
- <text
344
- x={100}
345
- y={0}
346
- fontSize={9}
347
- fill="#666"
348
- dominantBaseline="hanging"
349
- >
350
- {row.expId}
351
- </text>
352
- <text
353
- x={210}
354
- y={0}
355
- fontSize={9}
356
- fill="#666"
357
- dominantBaseline="hanging"
358
- >
359
- {row.fileId}
360
- </text>
361
- </g>
362
- ))}
360
+ <foreignObject
361
+ x={pad}
362
+ y={biosampleDataY}
363
+ width={totalWidth - 2 * pad}
364
+ height={biosampleSectionH}
365
+ >
366
+ <div
367
+ style={{
368
+ color: "#333",
369
+ fontSize: "9px",
370
+ lineHeight: `${lineH}px`,
371
+ margin: 0,
372
+ padding: 0,
373
+ overflow: "hidden",
374
+ whiteSpace: "normal",
375
+ wordBreak: "break-word",
376
+ }}
377
+ >
378
+ {biosampleText}
379
+ {hiddenBiosamples > 0 && (
380
+ <span style={{ color: "#aaa" }}>{biosampleMoreText}</span>
381
+ )}
382
+ </div>
383
+ </foreignObject>
363
384
  </g>
364
385
  )}
365
386
  </g>
@@ -27,6 +27,6 @@ export type { OtherTrackInfo } from "./other-tracks/shared/types";
27
27
  * 2. Import and add it to the appropriate assembly array below
28
28
  */
29
29
  export const foldersByAssembly: Record<Assembly, FolderDefinition[]> = {
30
- GRCh38: [humanBiosamplesFolder, humanGenesFolder, humanOtherTracksFolder],
31
- mm10: [mouseBiosamplesFolder, mouseGenesFolder],
30
+ GRCh38: [humanGenesFolder, humanBiosamplesFolder, humanOtherTracksFolder],
31
+ mm10: [mouseGenesFolder, mouseBiosamplesFolder],
32
32
  };
package/test/main.tsx CHANGED
@@ -38,7 +38,7 @@ import type { BiosampleRowInfo } from "../src/TrackSelect/Folders/biosamples/sha
38
38
  import type { GeneRowInfo } from "../src/TrackSelect/Folders/genes/shared/types";
39
39
  import type { OtherTrackInfo } from "../src/TrackSelect/Folders/other-tracks/shared/types";
40
40
  import { Exon } from "@weng-lab/genomebrowser/dist/components/tracks/transcript/types";
41
- import { tfPeaksTrack } from "@weng-lab/genomebrowser/test/TfPeaks";
41
+ import { tfPeaksTrack } from "../src/TrackSelect/Custom/TfPeaks";
42
42
 
43
43
  interface Transcript {
44
44
  id: string;
@@ -87,7 +87,7 @@ function Main() {
87
87
  const browserStore = createBrowserStoreMemo({
88
88
  // chr7:19,695,494-19,699,803
89
89
  // chr1:11103779-11262792
90
- domain: { chromosome: "chr1", start: 11103779, end: 11262792 },
90
+ domain: { chromosome: "chr12", start: 53380108, end: 53416378 },
91
91
  marginWidth: 100,
92
92
  trackWidth: 1400,
93
93
  multiplier: 3,