@abi-software/map-side-bar 2.20.0 → 2.20.1-demo.0

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.
@@ -0,0 +1,992 @@
1
+ <template>
2
+ <div ref="cardElementRef" class="card" :class="{ active: isActive }" :style="cardStyleVars">
3
+ <div class="card-header" @click="openCard">
4
+ <div class="title-content">
5
+ <div class="card-title">
6
+ {{ cellType.preferredLabel }}
7
+ </div>
8
+ <div class="card-keywords">
9
+ <span>{{ cellType.species }}</span>
10
+ <div class="card-chips">
11
+ <span
12
+ v-for="somaLocation in cellType.somaLocations"
13
+ class="card-chip"
14
+ :key="somaLocation"
15
+ >
16
+ {{ somaLocation }}
17
+ </span>
18
+ </div>
19
+ <span class="source-publication-chip" v-if="cellType.sourceNomenclature">
20
+ {{ cellType.sourceNomenclatureLabel }}
21
+ </span>
22
+ </div>
23
+ <el-button
24
+ v-if="isActive && (cellType.alertNotes?.length || cellType.curatorNotes?.length)"
25
+ round
26
+ size="small"
27
+ class="alert-chip"
28
+ @click.stop="showAlertMessage"
29
+ >
30
+ <el-icon class="alert"><el-icon-warn-triangle-filled /></el-icon>
31
+ Notes
32
+ </el-button>
33
+ </div>
34
+ <div class="title-buttons" @click.stop>
35
+ <CopyToClipboard @copied="onCopied" :content="updatedCopyContent" />
36
+ <el-popover width="auto" trigger="hover" :teleported="false" popper-class="popover-map-pin">
37
+ <template #reference>
38
+ <el-button class="button-circle" circle @click="closeCard">
39
+ <el-icon color="white">
40
+ <el-icon-close />
41
+ </el-icon>
42
+ </el-button>
43
+ </template>
44
+ <span>Close</span>
45
+ </el-popover>
46
+ </div>
47
+ </div>
48
+ <div class="card-details">
49
+ <div class="card-details-inner">
50
+ <div v-if="cellType.entity" class="card-section">
51
+ <div class="card-section-title">Entity</div>
52
+ <span>{{ cellType.entity }}</span>
53
+ </div>
54
+ <div v-if="cellType.species" class="card-section">
55
+ <div class="card-section-title">Species</div>
56
+ <span>{{ cellType.species }}</span>
57
+ </div>
58
+ <div v-if="cellType.somaLocations?.length" class="card-section">
59
+ <div class="card-section-title">Soma Location</div>
60
+ <div class="card-list-items">
61
+ <div
62
+ v-for="location in somaLocations"
63
+ class="card-list-item"
64
+ :key="location.id"
65
+ @mouseenter="showSomaLocation(location.name)"
66
+ @mouseleave="showSomaLocation()"
67
+ >
68
+ <span>{{ location.name }}</span>
69
+ <div class="card-list-search">
70
+ <el-popover
71
+ width="180"
72
+ trigger="hover"
73
+ :teleported="true"
74
+ :append-to="cardElement"
75
+ popper-class="popover-origin-help popover-search-actions"
76
+ @show="onSearchPopoverShow(location.id)"
77
+ @hide="onSearchPopoverHide(location.id)"
78
+ >
79
+ <template #reference>
80
+ <el-icon
81
+ class="status-search-icon"
82
+ :class="{ 'is-visible': activeSearchPopoverLocationId === location.id }"
83
+ >
84
+ <el-icon-search />
85
+ </el-icon>
86
+ </template>
87
+ <div class="search-action-list">
88
+ <button
89
+ type="button"
90
+ class="search-action-button"
91
+ @click="openDatasetSearch(location.name)"
92
+ >
93
+ Search dataset
94
+ </button>
95
+ <button
96
+ type="button"
97
+ class="search-action-button"
98
+ @click="openConnectivitySearch(location.name)"
99
+ >
100
+ Search connectivity
101
+ </button>
102
+ </div>
103
+ </el-popover>
104
+ </div>
105
+ </div>
106
+ </div>
107
+ </div>
108
+ <div v-if="cellType.circuitRole" class="card-section">
109
+ <div class="card-section-title">Circuit Role</div>
110
+ <span>{{ cellType.circuitRole }}</span>
111
+ </div>
112
+ <div v-if="cellType.creLine" class="card-section">
113
+ <div class="card-section-title">Cre Line</div>
114
+ <span>{{ cellType.creLine }}</span>
115
+ </div>
116
+ <div v-if="formattedMarkerGenes.length" class="card-section">
117
+ <div class="card-section-title">Marker Genes</div>
118
+ <div class="marker-genes-list">
119
+ <a
120
+ v-for="markerGene in formattedMarkerGenes"
121
+ :key="markerGene.key"
122
+ class="marker-gene-link"
123
+ :href="markerGene.uri"
124
+ target="_blank"
125
+ rel="noopener noreferrer"
126
+ >
127
+ <span>{{ markerGene.name }}</span>
128
+ <sup v-if="markerGene.expression">{{ markerGene.expression }}</sup>
129
+ <IconOpenExternal class="external-link-icon" />
130
+ </a>
131
+ </div>
132
+ </div>
133
+ <div v-if="cellType.fiberTypeString" class="card-section">
134
+ <div class="card-section-title">Axon Phenotype</div>
135
+ <div class="card-section-content">{{ cellType.fiberTypeString }}</div>
136
+ </div>
137
+ <div v-if="cellType.physiologyString" class="card-section">
138
+ <div class="card-section-title">Physiology</div>
139
+ <div class="card-section-content success">{{ cellType.physiologyString }}</div>
140
+ </div>
141
+ <div v-if="cellType.relatedCells?.length" class="card-section">
142
+ <div class="card-section-title">Related Species Variants</div>
143
+ <ul class="related-cell-list">
144
+ <li v-for="relatedCell in cellType.relatedCells" :key="relatedCell">
145
+ <button
146
+ class="related-cell-item"
147
+ @click.stop="$emit('related-cell-click', relatedCell)"
148
+ title="Click to view this cell type"
149
+ >
150
+ {{ relatedCell.label }}
151
+ </button>
152
+ </li>
153
+ </ul>
154
+ </div>
155
+ <div v-if="cellType.sourceNomenclature" class="card-section source-publication-section">
156
+ <div class="card-section-title">Source Publication</div>
157
+ <p>
158
+ <a
159
+ class="source-publication-link"
160
+ :href="cellType.sourceNomenclature"
161
+ target="_blank"
162
+ rel="noopener noreferrer"
163
+ >
164
+ {{ cellType.sourceNomenclatureLabel }}
165
+ <IconOpenExternal class="external-link-icon" />
166
+ </a>
167
+ </p>
168
+ </div>
169
+ <div
170
+ class="card-section"
171
+ v-if="cellType.alertNotes?.length || cellType.curatorNotes?.length"
172
+ ref="alertElement"
173
+ >
174
+ <div class="card-section-title">Notes</div>
175
+ <div class="alert-block">
176
+ <div class="alert-block-section" v-if="cellType.alertNotes?.length">
177
+ <div class="alert-block-title">Alert Notes:</div>
178
+ <div
179
+ v-for="(note, index) in cellType.alertNotes"
180
+ :key="`alert-${index}`"
181
+ v-html="formatAlertText(note)"
182
+ class="alert-block-note"
183
+ ></div>
184
+ </div>
185
+ <div class="alert-block-section" v-if="cellType.curatorNotes?.length">
186
+ <div class="alert-block-title">Curator Notes:</div>
187
+ <div
188
+ v-for="(note, index) in cellType.curatorNotes"
189
+ :key="`curator-${index}`"
190
+ v-html="formatAlertText(note)"
191
+ class="alert-block-note"
192
+ ></div>
193
+ </div>
194
+ </div>
195
+ </div>
196
+ </div>
197
+ </div>
198
+ </div>
199
+ </template>
200
+
201
+ <script>
202
+ import { CopyToClipboard } from '@abi-software/map-utilities';
203
+ import '@abi-software/map-utilities/dist/style.css';
204
+ import EventBus from './EventBus.js';
205
+ import {
206
+ capitalise,
207
+ formatAlertText as formatAlertTextUtil,
208
+ scrollToRef,
209
+ } from '../utils/common.js';
210
+ import IconOpenExternal from './icons/IconOpenExternal.vue';
211
+
212
+ const LOCATION_ID_MAP = {
213
+ soma_tg: 'trigeminal ganglion',
214
+ soma_drg: 'dorsal root ganglion',
215
+ };
216
+
217
+ export default {
218
+ name: 'CellCard',
219
+ components: {
220
+ CopyToClipboard,
221
+ IconOpenExternal,
222
+ },
223
+ props: {
224
+ cellType: {
225
+ type: Object,
226
+ required: true,
227
+ },
228
+ isActive: {
229
+ type: Boolean,
230
+ default: false,
231
+ },
232
+ },
233
+ emits: [
234
+ 'open',
235
+ 'close',
236
+ 'soma-location-hovered',
237
+ 'dataset-search',
238
+ 'connectivity-search',
239
+ 'related-cell-click',
240
+ ],
241
+ data() {
242
+ return {
243
+ cardElement: null,
244
+ activeSearchPopoverLocationId: null,
245
+ };
246
+ },
247
+ mounted: function () {
248
+ this.cardElement = this.$refs.cardElementRef;
249
+ },
250
+ computed: {
251
+ cardStyleVars: function () {
252
+ return {
253
+ '--cell-card-source-color': this.cellType?.sourceColor || this.cellType?.color || '#8300bf',
254
+ };
255
+ },
256
+ formattedMarkerGenes: function () {
257
+ return (this.cellType?.markerGenes || [])
258
+ .map((markerGene, index) => {
259
+ const name = String(markerGene?.name || '').trim();
260
+ const expression = String(markerGene?.expression || '').trim();
261
+ const uri = this.sanitizeMarkerGeneUri(markerGene?.uri, expression);
262
+
263
+ if (!name || !uri) {
264
+ return null;
265
+ }
266
+
267
+ return {
268
+ key: `${name}-${expression || 'none'}-${index}`,
269
+ name,
270
+ expression,
271
+ uri,
272
+ };
273
+ })
274
+ .filter(Boolean);
275
+ },
276
+ updatedCopyContent: function () {
277
+ const {
278
+ preferredLabel,
279
+ entity,
280
+ species,
281
+ somaLocations,
282
+ circuitRole,
283
+ creLine,
284
+ geneExpressionString,
285
+ fiberTypeString,
286
+ physiologyString,
287
+ relatedCells,
288
+ alertNotes,
289
+ curatorNotes,
290
+ sourceNomenclature,
291
+ sourceNomenclatureLabel,
292
+ } = this.cellType;
293
+
294
+ const contentArray = [];
295
+ if (preferredLabel) {
296
+ contentArray.push(`<div><strong>Cell Type:</strong> ${preferredLabel}</div>`);
297
+ }
298
+ if (entity) {
299
+ contentArray.push(`<div><strong>Entity:</strong> ${entity}</div>`);
300
+ }
301
+ if (species) {
302
+ contentArray.push(`<div><strong>Species:</strong> ${species}</div>`);
303
+ }
304
+ if (somaLocations?.length) {
305
+ const items = somaLocations.map((location) => `<li>${location}</li>`).join('');
306
+ contentArray.push(`<div><strong>Soma Locations:</strong><ul>${items}</ul></div>`);
307
+ }
308
+ if (circuitRole) {
309
+ contentArray.push(`<div><strong>Circuit Role:</strong> ${circuitRole}</div>`);
310
+ }
311
+ if (creLine) {
312
+ contentArray.push(`<div><strong>Cre Line:</strong> ${creLine}</div>`);
313
+ }
314
+ if (geneExpressionString) {
315
+ contentArray.push(`<div><strong>Marker Genes:</strong> ${geneExpressionString}</div>`);
316
+ }
317
+ if (fiberTypeString) {
318
+ contentArray.push(`<div><strong>Axon Phenotype:</strong> ${fiberTypeString}</div>`);
319
+ }
320
+ if (physiologyString) {
321
+ contentArray.push(`<div><strong>Physiology:</strong> ${physiologyString}</div>`);
322
+ }
323
+ if (relatedCells?.length) {
324
+ const relatedItems = relatedCells
325
+ .map((cell) => cell?.label)
326
+ .filter(Boolean)
327
+ .map((label) => `<li>${label}</li>`)
328
+ .join('');
329
+ if (relatedItems) {
330
+ contentArray.push(
331
+ `<div><strong>Related Species Variants:</strong><ul>${relatedItems}</ul></div>`,
332
+ );
333
+ }
334
+ }
335
+ if (sourceNomenclatureLabel) {
336
+ const sourceLabel = sourceNomenclature
337
+ ? `${sourceNomenclatureLabel} (<a href="${sourceNomenclature}">${sourceNomenclature}</a>)`
338
+ : sourceNomenclatureLabel;
339
+ contentArray.push(`<div><strong>Source Publication:</strong> ${sourceLabel}</div>`);
340
+ }
341
+
342
+ if (alertNotes?.length) {
343
+ const alertContent = alertNotes.map((note) => formatAlertTextUtil(note)).join('\n');
344
+ contentArray.push(`<div><strong>Alert Notes:</strong></div>\n${alertContent}`);
345
+ }
346
+
347
+ if (curatorNotes?.length) {
348
+ const curatorContent = curatorNotes.map((note) => formatAlertTextUtil(note)).join('\n');
349
+ contentArray.push(`<div><strong>Curator Notes:</strong></div>\n${curatorContent}`);
350
+ }
351
+
352
+ return contentArray.join('\n');
353
+ },
354
+ somaLocations: function () {
355
+ const mappedLocations = this.cellType.somaLocations.map((location) => {
356
+ const locationKey =
357
+ Object.keys(LOCATION_ID_MAP).find((key) => LOCATION_ID_MAP[key] === location) || '';
358
+ const isClustered = this.cellType.clusterAttributes?.[locationKey];
359
+ if (isClustered) {
360
+ return {
361
+ name: capitalise(location),
362
+ id: locationKey,
363
+ };
364
+ }
365
+ return false;
366
+ });
367
+ return mappedLocations;
368
+ },
369
+ },
370
+ methods: {
371
+ sanitizeMarkerGeneUri: function (uri, expression = '') {
372
+ let normalizedUri = String(uri || '').trim();
373
+ const normalizedExpression = String(expression || '').trim();
374
+
375
+ if (!normalizedUri) return '';
376
+
377
+ if (normalizedExpression) {
378
+ const escapedExpression = normalizedExpression.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
379
+ normalizedUri = normalizedUri.replace(new RegExp(`\\s+${escapedExpression}$`, 'i'), '');
380
+ }
381
+
382
+ return normalizedUri.trim();
383
+ },
384
+ transformString: function (str) {
385
+ if (!str) return '';
386
+ // replace the string with ^ with <sup> and the next word with </sup>
387
+ return str.replace(/\^(\w+)/g, '<sup>$1</sup>');
388
+ },
389
+ onCopied: function () {
390
+ EventBus.emit('trackEvent', {
391
+ event_name: `portal_maps_cell_card_copy`,
392
+ category: this.cellType.preferredLabel || '',
393
+ location: 'map_sidebar_cell_card',
394
+ });
395
+ },
396
+ openCard: function () {
397
+ this.$emit('open', this.cellType.id);
398
+
399
+ EventBus.emit('trackEvent', {
400
+ event_name: `portal_maps_cell_card_open`,
401
+ category: this.cellType.id || '',
402
+ location: 'map_sidebar_cell_card',
403
+ });
404
+ },
405
+ closeCard: function () {
406
+ this.$emit('close');
407
+
408
+ EventBus.emit('trackEvent', {
409
+ event_name: `portal_maps_cell_card_close`,
410
+ category: this.cellType.id || '',
411
+ location: 'map_sidebar_cell_card',
412
+ });
413
+ },
414
+ showSomaLocation: function (name) {
415
+ this.$emit('soma-location-hovered', name);
416
+ },
417
+ openDatasetSearch: function (query) {
418
+ this.$emit('dataset-search', {
419
+ species: capitalise(this.cellType?.species),
420
+ location: query,
421
+ });
422
+ },
423
+ openConnectivitySearch: function (query) {
424
+ // Find the UBERON term from query value (label)
425
+ const availableDataRaw = localStorage.getItem('available-name-curie-mapping');
426
+ const availableData = availableDataRaw ? JSON.parse(availableDataRaw) : {};
427
+ const locationCurie = Object.keys(availableData).find(
428
+ (curie) => availableData[curie].toLowerCase() === query.toLowerCase(),
429
+ );
430
+ const facets = locationCurie
431
+ ? [
432
+ {
433
+ facet: `["${locationCurie}",[]]`,
434
+ facetPropPath: 'flatmap.connectivity.source.all',
435
+ tagLabel: query,
436
+ term: 'All',
437
+ },
438
+ ]
439
+ : [];
440
+ this.$emit('connectivity-search', {
441
+ facets: facets,
442
+ query: '',
443
+ });
444
+ },
445
+ onSearchPopoverShow: function (locationId) {
446
+ this.activeSearchPopoverLocationId = locationId;
447
+ },
448
+ onSearchPopoverHide: function (locationId) {
449
+ if (this.activeSearchPopoverLocationId === locationId) {
450
+ this.activeSearchPopoverLocationId = null;
451
+ }
452
+ },
453
+ showAlertMessage: function () {
454
+ scrollToRef(this, 'alertElement');
455
+ },
456
+ formatAlertText: function (text) {
457
+ return formatAlertTextUtil(text);
458
+ },
459
+ },
460
+ };
461
+ </script>
462
+
463
+ <style lang="scss" scoped>
464
+ .card {
465
+ position: relative;
466
+ font-family: Asap;
467
+ font-size: 14px;
468
+ box-sizing: border-box;
469
+
470
+ &::before {
471
+ content: '';
472
+ position: absolute;
473
+ top: 5px;
474
+ left: 5px;
475
+ width: calc(100% - 15px);
476
+ height: calc(100% - 10px);
477
+ border: 3px solid transparent;
478
+ border-radius: 5px;
479
+ box-sizing: border-box;
480
+ z-index: 1;
481
+ }
482
+
483
+ &.active::before,
484
+ &:hover::before {
485
+ border-color: $app-primary-color;
486
+ }
487
+
488
+ &:not(:first-child) {
489
+ margin-top: 5px;
490
+
491
+ &::after {
492
+ content: '';
493
+ position: absolute;
494
+ top: 0px;
495
+ left: 10px;
496
+ width: 455px; // Same as other cards in the sidebar
497
+ height: 2px; // Same as other cards in the sidebar
498
+ background-color: #e4e7ed;
499
+ z-index: 0;
500
+ }
501
+ }
502
+
503
+ .card-details {
504
+ display: none;
505
+ }
506
+
507
+ &.active {
508
+ background-color: #f7faff;
509
+
510
+ &::before {
511
+ top: 0;
512
+ left: 0;
513
+ width: 100%;
514
+ height: 100%;
515
+ }
516
+
517
+ .card-header {
518
+ cursor: default;
519
+
520
+ &::before {
521
+ opacity: 1;
522
+ }
523
+
524
+ .card-title {
525
+ margin: 0;
526
+ line-height: 1.3em !important;
527
+ font-size: 18px;
528
+ font-weight: bold;
529
+ color: $app-primary-color;
530
+ }
531
+
532
+ .card-keywords {
533
+ display: none;
534
+ }
535
+ }
536
+
537
+ .card-details {
538
+ display: block;
539
+ }
540
+ }
541
+ }
542
+
543
+ .card-header {
544
+ display: flex;
545
+ justify-content: space-between;
546
+ align-items: flex-start;
547
+ cursor: pointer;
548
+ position: relative;
549
+
550
+ &::before {
551
+ content: '';
552
+ position: absolute;
553
+ bottom: 7px;
554
+ left: 5px;
555
+ width: calc(100% - 10px);
556
+ height: 1px;
557
+ opacity: 0;
558
+ transition: opacity 0.25s ease;
559
+ }
560
+ }
561
+
562
+ .card-header,
563
+ .card-details {
564
+ position: relative;
565
+ z-index: 2;
566
+ }
567
+
568
+ .card-header,
569
+ .card-details-inner {
570
+ padding: 1rem;
571
+ }
572
+
573
+ .title-content {
574
+ display: flex;
575
+ flex-direction: column;
576
+ align-items: flex-start;
577
+ }
578
+
579
+ .card-title {
580
+ margin-bottom: 0.75rem;
581
+ font-weight: bold;
582
+ line-height: 1.5;
583
+ letter-spacing: 1.05px;
584
+ display: -webkit-box;
585
+ -webkit-line-clamp: 5;
586
+ line-clamp: 5;
587
+ -webkit-box-orient: vertical;
588
+ overflow: hidden;
589
+ }
590
+
591
+ .card-details-inner {
592
+ display: flex;
593
+ flex-direction: column;
594
+ gap: 1.75rem;
595
+ padding-top: 0;
596
+ }
597
+
598
+ .card-keywords,
599
+ .card-chips {
600
+ display: flex;
601
+ align-items: center;
602
+ gap: 0.5rem;
603
+
604
+ span {
605
+ color: #606266;
606
+ }
607
+ }
608
+
609
+ .card-chip {
610
+ display: inline-block;
611
+ padding: 2px 8px;
612
+ background-color: #f0f0f0;
613
+ border-radius: 12px;
614
+ transition: background-color 0.2s ease;
615
+
616
+ .card-details & {
617
+ padding: 4px 8px;
618
+ border: 1px solid #dcdcdc;
619
+ }
620
+ }
621
+
622
+ .card-list-items {
623
+ display: flex;
624
+ flex-direction: column;
625
+ gap: 0.25rem;
626
+ box-sizing: border-box;
627
+ position: relative;
628
+ }
629
+
630
+ .card-list-item {
631
+ width: 100%;
632
+ display: flex;
633
+ align-items: center;
634
+ justify-content: space-between;
635
+ gap: 0.5rem;
636
+ padding: 0.5rem;
637
+ position: relative;
638
+ box-sizing: border-box;
639
+
640
+ &::before {
641
+ content: '';
642
+ position: absolute;
643
+ inset: 0;
644
+ transition:
645
+ background-color 0.2s ease,
646
+ border-color 0.2s ease;
647
+ background-color: rgba($app-primary-color, 0.04);
648
+ border-left: 4px solid rgba($app-primary-color, 0.16);
649
+ }
650
+
651
+ &:hover::before {
652
+ background-color: rgba($app-primary-color, 0.07);
653
+ border-left-color: rgba($app-primary-color, 0.24);
654
+ }
655
+
656
+ > span {
657
+ line-height: 1.5em;
658
+ font-weight: 500;
659
+ }
660
+ }
661
+
662
+ .card-list-search {
663
+ display: flex;
664
+ gap: 0.25rem;
665
+ }
666
+
667
+ .status-search-icon {
668
+ font-size: 16px;
669
+ color: $app-primary-color;
670
+ cursor: pointer;
671
+ opacity: 0;
672
+ transition: opacity 0.2s ease;
673
+
674
+ &.is-visible {
675
+ opacity: 1;
676
+ }
677
+
678
+ &:hover {
679
+ color: #ac76c5;
680
+ }
681
+ }
682
+
683
+ .card-list-item:hover .status-search-icon {
684
+ opacity: 1;
685
+ }
686
+
687
+ :deep(.popover-origin-help.el-popover) {
688
+ font-family: 'Asap', sans-serif;
689
+ background: #f3ecf6 !important;
690
+ border: 1px solid $app-primary-color !important;
691
+ border-radius: 4px !important;
692
+ color: #303133 !important;
693
+ text-transform: none !important; // need to overide the tooltip text transform
694
+ font-weight: 400;
695
+
696
+ .el-popper__arrow {
697
+ &:before {
698
+ background: #f3ecf6 !important;
699
+ border-color: $app-primary-color;
700
+ background-color: #ffffff;
701
+ }
702
+ }
703
+ }
704
+
705
+ :deep(.popover-search-actions.el-popover) {
706
+ padding: 6px;
707
+ }
708
+
709
+ .search-action-list {
710
+ display: flex;
711
+ flex-direction: column;
712
+ gap: 4px;
713
+ }
714
+
715
+ .search-action-button {
716
+ border: 0;
717
+ width: 100%;
718
+ padding: 6px 8px;
719
+ text-align: left;
720
+ border-radius: 4px;
721
+ font-family: inherit;
722
+ font-size: 12px;
723
+ color: #303133;
724
+ background: transparent;
725
+ cursor: pointer;
726
+
727
+ &:hover {
728
+ background-color: rgba($app-primary-color, 0.12);
729
+ color: $app-primary-color;
730
+ }
731
+ }
732
+
733
+ .card-section {
734
+ .card-section-title {
735
+ font-weight: 600;
736
+ }
737
+
738
+ p {
739
+ margin: 0;
740
+ color: #606266;
741
+ }
742
+
743
+ ul {
744
+ margin: 0;
745
+ padding-left: 1rem;
746
+
747
+ &.related-cell-list {
748
+ list-style-type: none;
749
+ padding-left: 0;
750
+ }
751
+
752
+ li {
753
+ color: #606266;
754
+ }
755
+ }
756
+ }
757
+
758
+ .card-section {
759
+ .card-section-title {
760
+ display: block;
761
+ margin-bottom: 0.75rem;
762
+ font-size: 16px;
763
+ font-weight: 600;
764
+ text-transform: uppercase;
765
+ }
766
+ }
767
+
768
+ .card-section-content {
769
+ padding: 0.5rem;
770
+ background-color: #f0f0f0;
771
+ border-radius: 4px;
772
+ border: 1px solid #dcdcdc;
773
+
774
+ &.success {
775
+ border-color: #b3e5b3;
776
+ background-color: #f0f9eb;
777
+ }
778
+ }
779
+
780
+ .marker-genes-list {
781
+ display: flex;
782
+ flex-wrap: wrap;
783
+ gap: 0.5rem;
784
+ }
785
+
786
+ .marker-gene-link {
787
+ display: inline-flex;
788
+ align-items: flex-start;
789
+ gap: 0.1rem;
790
+ padding: 0.25rem 0.5rem;
791
+ font-size: 12px;
792
+ border: 1px solid $app-primary-color;
793
+ border-radius: 4px;
794
+ color: $app-primary-color;
795
+ background-color: #f9f2fc;
796
+ text-decoration: none;
797
+ line-height: 1.2;
798
+
799
+ &:hover {
800
+ background-image: linear-gradient(rgba(0, 0, 0, 0.05), rgba(0, 0, 0, 0.05));
801
+ }
802
+
803
+ sup {
804
+ font-size: 0.75em;
805
+ line-height: 1;
806
+ }
807
+ }
808
+
809
+ .external-link-icon {
810
+ display: inline-flex;
811
+ align-items: center;
812
+ width: 16px;
813
+ height: 16px;
814
+ margin-left: 2px;
815
+ color: currentColor;
816
+ opacity: 0.7;
817
+ vertical-align: middle;
818
+ }
819
+
820
+ .source-publication-chip,
821
+ .source-publication-link {
822
+ display: inline-block;
823
+ border-radius: 4px;
824
+ color: white !important;
825
+ text-decoration: none;
826
+ background-color: var(--cell-card-source-color);
827
+ }
828
+
829
+ .source-publication-chip {
830
+ padding: 2px 8px;
831
+ font-size: 13px;
832
+ }
833
+
834
+ .alert-chip {
835
+ margin-top: 5px;
836
+ background-color: $app-primary-color;
837
+ border-color: $app-primary-color;
838
+ color: #fff;
839
+
840
+ &:hover {
841
+ color: #fff !important;
842
+ background-color: #ac76c5 !important;
843
+ border: 1px solid #ac76c5 !important;
844
+ }
845
+
846
+ :deep(> span) {
847
+ gap: 2px;
848
+ }
849
+
850
+ .alert {
851
+ width: 1rem;
852
+ height: 1rem;
853
+ color: inherit;
854
+ margin: 0;
855
+ }
856
+ }
857
+
858
+ .source-publication-link {
859
+ padding: 4px 8px;
860
+
861
+ &:hover {
862
+ background-image: linear-gradient(rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0.12));
863
+ }
864
+ }
865
+
866
+ :deep(.el-popper.popover-map-pin) {
867
+ padding: 4px 10px;
868
+ min-width: max-content;
869
+ font-family: Asap;
870
+ font-size: 12px;
871
+ line-height: inherit;
872
+ color: inherit;
873
+ background: #f3ecf6 !important;
874
+ border: 1px solid $app-primary-color;
875
+
876
+ & .el-popper__arrow::before {
877
+ border: 1px solid;
878
+ border-color: $app-primary-color;
879
+ background: #f3ecf6;
880
+ }
881
+ }
882
+
883
+ .title-buttons {
884
+ flex: 1 0 0%;
885
+ max-width: 20%;
886
+ flex-direction: row;
887
+ justify-content: end;
888
+ gap: 0.5rem;
889
+ display: none;
890
+
891
+ .card.active & {
892
+ display: flex;
893
+ }
894
+
895
+ :deep(.copy-clipboard-button) {
896
+ &,
897
+ &:hover,
898
+ &:focus {
899
+ border-color: $app-primary-color !important;
900
+ border-radius: 50%;
901
+ }
902
+
903
+ &.is-disabled {
904
+ border-color: #dab3ec !important;
905
+ }
906
+ }
907
+
908
+ .el-button + .el-button {
909
+ margin-left: 0 !important;
910
+ }
911
+ }
912
+
913
+ .button-circle {
914
+ margin: 0;
915
+ width: 24px !important;
916
+ height: 24px !important;
917
+ border: 1px solid $app-primary-color !important;
918
+
919
+ &,
920
+ &:hover,
921
+ &:focus,
922
+ &:active {
923
+ background-color: $app-primary-color;
924
+ border-color: $app-primary-color !important;
925
+ }
926
+
927
+ &.secondary {
928
+ background-color: white !important;
929
+ }
930
+ }
931
+
932
+ .alert-block {
933
+ background-color: var(--el-color-warning-light-9);
934
+ border: 1px dashed var(--el-color-warning);
935
+ padding: 0.75rem;
936
+ border-radius: 4px;
937
+ display: flex;
938
+ flex-direction: column;
939
+ gap: 1rem;
940
+
941
+ :deep(a) {
942
+ color: $app-primary-color;
943
+ word-break: break-all;
944
+
945
+ &:hover {
946
+ opacity: 0.8;
947
+ }
948
+ }
949
+ }
950
+
951
+ .alert-block-section {
952
+ .alert-block-title {
953
+ font-weight: 600;
954
+ margin-bottom: 0.5rem;
955
+ }
956
+
957
+ .alert-block-note {
958
+ color: #606266;
959
+ padding-left: 0.5rem;
960
+
961
+ + .alert-block-note {
962
+ margin-top: 0.5rem;
963
+ }
964
+ }
965
+ }
966
+
967
+ .related-cell-list {
968
+ li + li {
969
+ margin-top: 0.5rem;
970
+ }
971
+ }
972
+
973
+ .related-cell-item {
974
+ cursor: pointer;
975
+ color: $app-primary-color;
976
+ background: none;
977
+ border: 1px solid $app-primary-color;
978
+ border-radius: 4px;
979
+ padding: 2px 4px;
980
+ font-family: inherit;
981
+ font-size: inherit;
982
+ line-height: inherit;
983
+ transition:
984
+ background-color 0.2s ease,
985
+ border-color 0.2s ease,
986
+ text-decoration-color 0.2s ease;
987
+
988
+ &:hover {
989
+ background-color: rgba($app-primary-color, 0.08);
990
+ }
991
+ }
992
+ </style>