@visns-studio/visns-components 5.26.1 → 5.26.3
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 +1 -1
- package/src/components/DataGrid.jsx +232 -6
- package/src/components/Form.jsx +5 -5
- package/src/components/cells/CellWithTooltip.jsx +14 -7
- package/src/components/columns/ColumnRenderers.jsx +16 -2
- package/src/components/generic/GenericDashboard.jsx +17 -5
- package/src/components/generic/GenericIndex.jsx +5 -0
- package/src/components/styles/global-datagrid.css +26 -0
package/package.json
CHANGED
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"react-dom": "^17.0.0 || ^18.0.0"
|
|
95
95
|
},
|
|
96
96
|
"name": "@visns-studio/visns-components",
|
|
97
|
-
"version": "5.26.
|
|
97
|
+
"version": "5.26.3",
|
|
98
98
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
99
99
|
"main": "src/index.js",
|
|
100
100
|
"files": [
|
|
@@ -242,6 +242,7 @@ const DataGrid = forwardRef(
|
|
|
242
242
|
form,
|
|
243
243
|
gridHeight,
|
|
244
244
|
hoverColor,
|
|
245
|
+
legend,
|
|
245
246
|
mapbox,
|
|
246
247
|
pageData,
|
|
247
248
|
pageSetting,
|
|
@@ -473,6 +474,36 @@ const DataGrid = forwardRef(
|
|
|
473
474
|
setColumnsMetadata(res.metadata);
|
|
474
475
|
}
|
|
475
476
|
|
|
477
|
+
// Opt-in (ajaxSetting.suppressRepeated {key, columns}):
|
|
478
|
+
// when consecutive rows share the same non-empty `key`
|
|
479
|
+
// value, blank the listed fields on every row after the
|
|
480
|
+
// first, so the first row of the run reads as the group
|
|
481
|
+
// row and the rest show only what differs. Rows must
|
|
482
|
+
// already be sorted so related rows are adjacent.
|
|
483
|
+
if (
|
|
484
|
+
ajaxSetting?.suppressRepeated?.key &&
|
|
485
|
+
Array.isArray(ajaxSetting.suppressRepeated.columns) &&
|
|
486
|
+
Array.isArray(res.data)
|
|
487
|
+
) {
|
|
488
|
+
const { key, columns: repeatedFields } =
|
|
489
|
+
ajaxSetting.suppressRepeated;
|
|
490
|
+
|
|
491
|
+
for (let i = 1; i < res.data.length; i++) {
|
|
492
|
+
const current = res.data[i]?.[key];
|
|
493
|
+
const previous = res.data[i - 1]?.[key];
|
|
494
|
+
|
|
495
|
+
if (
|
|
496
|
+
current != null &&
|
|
497
|
+
current !== '' &&
|
|
498
|
+
current === previous
|
|
499
|
+
) {
|
|
500
|
+
repeatedFields.forEach((field) => {
|
|
501
|
+
res.data[i][field] = null;
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
476
507
|
// Group rows together while preserving the backend's sortBy order:
|
|
477
508
|
// first appearance of each group key determines group order, so when
|
|
478
509
|
// the backend sorts by e.g. pickling_completed_at desc, groups appear
|
|
@@ -524,6 +555,103 @@ const DataGrid = forwardRef(
|
|
|
524
555
|
}
|
|
525
556
|
dataRef.current = res.data;
|
|
526
557
|
|
|
558
|
+
// Opt-in grouped display (ajaxSetting.treeGroupBy {key,
|
|
559
|
+
// column, parentFields, countLabel}): consecutive rows
|
|
560
|
+
// sharing a non-empty `key` value get a synthetic summary
|
|
561
|
+
// parent row inserted above them, always visible — no
|
|
562
|
+
// expand/collapse. `parentFields` are lifted from the
|
|
563
|
+
// first row onto the parent and blanked on the children,
|
|
564
|
+
// so shared details display once; children get an "↳"
|
|
565
|
+
// prefix in the display column. Single rows stay plain.
|
|
566
|
+
// Runs after dataRef is set so selection/group helpers
|
|
567
|
+
// keep operating on the real rows only.
|
|
568
|
+
if (
|
|
569
|
+
ajaxSetting?.treeGroupBy?.key &&
|
|
570
|
+
Array.isArray(res.data)
|
|
571
|
+
) {
|
|
572
|
+
const {
|
|
573
|
+
key: treeKey,
|
|
574
|
+
column: treeDisplayColumn,
|
|
575
|
+
parentFields = [],
|
|
576
|
+
countLabel,
|
|
577
|
+
} = ajaxSetting.treeGroupBy;
|
|
578
|
+
const rows = res.data;
|
|
579
|
+
const grouped = [];
|
|
580
|
+
let i = 0;
|
|
581
|
+
|
|
582
|
+
while (i < rows.length) {
|
|
583
|
+
const row = rows[i];
|
|
584
|
+
const value = row?.[treeKey];
|
|
585
|
+
let j = i + 1;
|
|
586
|
+
|
|
587
|
+
if (value != null && value !== '') {
|
|
588
|
+
while (
|
|
589
|
+
j < rows.length &&
|
|
590
|
+
rows[j]?.[treeKey] === value
|
|
591
|
+
) {
|
|
592
|
+
j++;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
if (j - i > 1) {
|
|
597
|
+
const children = rows.slice(i, j);
|
|
598
|
+
const parent = {
|
|
599
|
+
__treeParent: true,
|
|
600
|
+
id: `tree-group-${value}`,
|
|
601
|
+
[treeKey]: value,
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
// Show the group value in the display column
|
|
605
|
+
if (treeDisplayColumn) {
|
|
606
|
+
parent[treeDisplayColumn] = value;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// Lift shared fields onto the parent, blank
|
|
610
|
+
// them on every child
|
|
611
|
+
parentFields.forEach((field) => {
|
|
612
|
+
parent[field] = children[0]?.[field];
|
|
613
|
+
children.forEach((child) => {
|
|
614
|
+
child[field] = null;
|
|
615
|
+
});
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
// Mark children for styling (getRowStyle) and
|
|
619
|
+
// prefix their display value so nesting reads
|
|
620
|
+
children.forEach((child) => {
|
|
621
|
+
child.__treeChild = true;
|
|
622
|
+
|
|
623
|
+
if (
|
|
624
|
+
treeDisplayColumn &&
|
|
625
|
+
child[treeDisplayColumn] != null &&
|
|
626
|
+
String(
|
|
627
|
+
child[treeDisplayColumn]
|
|
628
|
+
).indexOf('↳') !== 0
|
|
629
|
+
) {
|
|
630
|
+
child[treeDisplayColumn] =
|
|
631
|
+
'↳ ' + child[treeDisplayColumn];
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
// Optional member count, e.g. "3 Headers"
|
|
636
|
+
if (countLabel?.field && countLabel?.template) {
|
|
637
|
+
parent[countLabel.field] =
|
|
638
|
+
countLabel.template.replace(
|
|
639
|
+
'{count}',
|
|
640
|
+
String(children.length)
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
grouped.push(parent, ...children);
|
|
645
|
+
} else {
|
|
646
|
+
grouped.push(row);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
i = j;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
res.data = grouped;
|
|
653
|
+
}
|
|
654
|
+
|
|
527
655
|
// Force grid to recalculate row heights after data changes
|
|
528
656
|
// This helps with multi-line content timing without interfering with core mechanisms
|
|
529
657
|
setTimeout(() => {
|
|
@@ -687,6 +815,19 @@ const DataGrid = forwardRef(
|
|
|
687
815
|
const getRowStyle = useCallback(
|
|
688
816
|
(rowProps) => {
|
|
689
817
|
const { data } = rowProps;
|
|
818
|
+
const style = {};
|
|
819
|
+
|
|
820
|
+
// Tree mode: mark the summary parent and its nested
|
|
821
|
+
// children with a left accent bar so the block reads as
|
|
822
|
+
// one unit and children are visibly subordinate
|
|
823
|
+
if (ajaxSetting?.treeGroupBy?.key) {
|
|
824
|
+
if (data?.__treeParent) {
|
|
825
|
+
style.borderLeft = '4px solid #2e7dbd';
|
|
826
|
+
style.fontWeight = 600;
|
|
827
|
+
} else if (data?.__treeChild) {
|
|
828
|
+
style.borderLeft = '4px solid #93c5fd';
|
|
829
|
+
}
|
|
830
|
+
}
|
|
690
831
|
|
|
691
832
|
if (ajaxSetting?.rowColours?.length > 0) {
|
|
692
833
|
const colorConfig = ajaxSetting.rowColours.find(
|
|
@@ -694,13 +835,11 @@ const DataGrid = forwardRef(
|
|
|
694
835
|
);
|
|
695
836
|
|
|
696
837
|
if (colorConfig) {
|
|
697
|
-
|
|
698
|
-
backgroundColor: colorConfig.colour,
|
|
699
|
-
};
|
|
838
|
+
style.backgroundColor = colorConfig.colour;
|
|
700
839
|
}
|
|
701
840
|
}
|
|
702
841
|
|
|
703
|
-
return null;
|
|
842
|
+
return Object.keys(style).length > 0 ? style : null;
|
|
704
843
|
},
|
|
705
844
|
[ajaxSetting]
|
|
706
845
|
);
|
|
@@ -821,12 +960,14 @@ const DataGrid = forwardRef(
|
|
|
821
960
|
const cleanSelection = {};
|
|
822
961
|
Object.keys(param.selected).forEach((key) => {
|
|
823
962
|
const item = param.selected[key];
|
|
824
|
-
// Only keep items that have valid IDs and are not
|
|
963
|
+
// Only keep items that have valid IDs and are not
|
|
964
|
+
// group objects or synthetic tree parent rows
|
|
825
965
|
if (
|
|
826
966
|
key !== 'undefined' &&
|
|
827
967
|
key !== 'null' &&
|
|
828
968
|
item &&
|
|
829
969
|
!item.__group &&
|
|
970
|
+
!item.__treeParent &&
|
|
830
971
|
item.id !== undefined
|
|
831
972
|
) {
|
|
832
973
|
cleanSelection[key] = item;
|
|
@@ -3373,7 +3514,43 @@ const DataGrid = forwardRef(
|
|
|
3373
3514
|
}
|
|
3374
3515
|
};
|
|
3375
3516
|
|
|
3376
|
-
|
|
3517
|
+
// Interactive cells (inline dropdowns, timers, stage
|
|
3518
|
+
// toggles) must not render on synthetic tree parent
|
|
3519
|
+
// rows — they'd write against the fake id. Display-only
|
|
3520
|
+
// renders (text, relation, gallery, stageCounter) are
|
|
3521
|
+
// safe and show the lifted shared values.
|
|
3522
|
+
const TREE_PARENT_BLOCKED_TYPES = [
|
|
3523
|
+
'dropdown',
|
|
3524
|
+
'multiDropdown',
|
|
3525
|
+
'timer',
|
|
3526
|
+
'stage',
|
|
3527
|
+
'input',
|
|
3528
|
+
];
|
|
3529
|
+
const guardTreeParentRender = (gridColumn, columnDef) => {
|
|
3530
|
+
if (
|
|
3531
|
+
!ajaxSetting?.treeGroupBy?.key ||
|
|
3532
|
+
!gridColumn?.render ||
|
|
3533
|
+
!TREE_PARENT_BLOCKED_TYPES.includes(
|
|
3534
|
+
columnDef?.type
|
|
3535
|
+
)
|
|
3536
|
+
) {
|
|
3537
|
+
return gridColumn;
|
|
3538
|
+
}
|
|
3539
|
+
|
|
3540
|
+
const originalRender = gridColumn.render;
|
|
3541
|
+
|
|
3542
|
+
return {
|
|
3543
|
+
...gridColumn,
|
|
3544
|
+
render: (arg) =>
|
|
3545
|
+
arg?.data?.__treeParent
|
|
3546
|
+
? null
|
|
3547
|
+
: originalRender(arg),
|
|
3548
|
+
};
|
|
3549
|
+
};
|
|
3550
|
+
|
|
3551
|
+
const newColumns = columns.map((column) =>
|
|
3552
|
+
guardTreeParentRender(renderColumn(column), column)
|
|
3553
|
+
);
|
|
3377
3554
|
|
|
3378
3555
|
// Count settings that could render in the trailing Action
|
|
3379
3556
|
// column: role-permitted AND not claimed by an 'action' column.
|
|
@@ -3417,6 +3594,12 @@ const DataGrid = forwardRef(
|
|
|
3417
3594
|
? style.text_vertical_align
|
|
3418
3595
|
: 'center',
|
|
3419
3596
|
render: ({ data }) => {
|
|
3597
|
+
// Synthetic tree parent rows are summaries —
|
|
3598
|
+
// no row actions
|
|
3599
|
+
if (data?.__treeParent) {
|
|
3600
|
+
return null;
|
|
3601
|
+
}
|
|
3602
|
+
|
|
3420
3603
|
return (
|
|
3421
3604
|
<div className={styles.tdactions}>
|
|
3422
3605
|
{memoizedSettings
|
|
@@ -3774,6 +3957,49 @@ const DataGrid = forwardRef(
|
|
|
3774
3957
|
</div>
|
|
3775
3958
|
)}
|
|
3776
3959
|
|
|
3960
|
+
{/* Opt-in colour legend (config key `legend`: array of
|
|
3961
|
+
{colour, label}); entries without a colour render as
|
|
3962
|
+
plain lead-in text */}
|
|
3963
|
+
{Array.isArray(legend) && legend.length > 0 && (
|
|
3964
|
+
<div
|
|
3965
|
+
style={{
|
|
3966
|
+
display: 'flex',
|
|
3967
|
+
flexWrap: 'wrap',
|
|
3968
|
+
alignItems: 'center',
|
|
3969
|
+
gap: '14px',
|
|
3970
|
+
padding: '6px 4px 8px',
|
|
3971
|
+
fontSize: '0.8rem',
|
|
3972
|
+
color: '#555',
|
|
3973
|
+
}}
|
|
3974
|
+
>
|
|
3975
|
+
{legend.map((item, index) => (
|
|
3976
|
+
<span
|
|
3977
|
+
key={index}
|
|
3978
|
+
style={{
|
|
3979
|
+
display: 'inline-flex',
|
|
3980
|
+
alignItems: 'center',
|
|
3981
|
+
gap: '6px',
|
|
3982
|
+
}}
|
|
3983
|
+
>
|
|
3984
|
+
{(item.colour || item.color) && (
|
|
3985
|
+
<span
|
|
3986
|
+
style={{
|
|
3987
|
+
width: '14px',
|
|
3988
|
+
height: '14px',
|
|
3989
|
+
borderRadius: '3px',
|
|
3990
|
+
backgroundColor:
|
|
3991
|
+
item.colour || item.color,
|
|
3992
|
+
display: 'inline-block',
|
|
3993
|
+
flexShrink: 0,
|
|
3994
|
+
}}
|
|
3995
|
+
/>
|
|
3996
|
+
)}
|
|
3997
|
+
{item.label}
|
|
3998
|
+
</span>
|
|
3999
|
+
))}
|
|
4000
|
+
</div>
|
|
4001
|
+
)}
|
|
4002
|
+
|
|
3777
4003
|
{/* DataGrid container with its own border */}
|
|
3778
4004
|
<div className={styles.dataGridContainer}>
|
|
3779
4005
|
|
package/src/components/Form.jsx
CHANGED
|
@@ -2686,11 +2686,11 @@ function Form({
|
|
|
2686
2686
|
<div
|
|
2687
2687
|
className={`${styles.formItem} ${styles.fwItem}`}
|
|
2688
2688
|
style={{
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2689
|
+
// Never exceed the parent panel (sizing
|
|
2690
|
+
// against 100vw bled past narrower panels);
|
|
2691
|
+
// the grid keeps its configured px width
|
|
2692
|
+
// inside and scrolls horizontally here
|
|
2693
|
+
width: '100%',
|
|
2694
2694
|
maxWidth: formSettings?.save?.return?.width
|
|
2695
2695
|
? `${parseInt(
|
|
2696
2696
|
formSettings.save.return.width
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { createPortal } from 'react-dom';
|
|
2
3
|
import { Tooltip } from 'react-tooltip';
|
|
3
4
|
import moment from 'moment';
|
|
4
5
|
import numeral from 'numeral';
|
|
@@ -138,9 +139,14 @@ const CellWithTooltip = ({
|
|
|
138
139
|
width: '100%',
|
|
139
140
|
};
|
|
140
141
|
|
|
141
|
-
// Render tooltip
|
|
142
|
-
|
|
143
|
-
|
|
142
|
+
// Render the tooltip into document.body: grid rows are transformed
|
|
143
|
+
// (virtualization), which traps a fixed-position tooltip inside the
|
|
144
|
+
// row's stacking context — the next row paints over it no matter how
|
|
145
|
+
// high its z-index. Portaling out of the row escapes that entirely.
|
|
146
|
+
const tooltipPortal =
|
|
147
|
+
shouldShowTooltip &&
|
|
148
|
+
createPortal(
|
|
149
|
+
<Tooltip
|
|
144
150
|
id={tooltipId}
|
|
145
151
|
style={{
|
|
146
152
|
backgroundColor: 'rgba(0, 0, 0, 0.9)',
|
|
@@ -162,10 +168,11 @@ const CellWithTooltip = ({
|
|
|
162
168
|
noArrow={false}
|
|
163
169
|
variant="dark"
|
|
164
170
|
className="cell-tooltip"
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
171
|
+
positionStrategy="fixed" // Use fixed positioning strategy
|
|
172
|
+
fallbackPlacements={['bottom', 'right', 'left']} // Fallback positions if top doesn't fit
|
|
173
|
+
/>,
|
|
174
|
+
document.body
|
|
175
|
+
);
|
|
169
176
|
|
|
170
177
|
return (
|
|
171
178
|
<>
|
|
@@ -2498,6 +2498,13 @@ export const renderGalleryColumn = ({
|
|
|
2498
2498
|
!Array.isArray(imagesArray) ||
|
|
2499
2499
|
imagesArray.length === 0
|
|
2500
2500
|
) {
|
|
2501
|
+
// Nested child rows have their shared fields lifted onto
|
|
2502
|
+
// the summary parent — "No images" there would wrongly
|
|
2503
|
+
// suggest the item has none, so stay blank
|
|
2504
|
+
if (data?.__treeChild) {
|
|
2505
|
+
return null;
|
|
2506
|
+
}
|
|
2507
|
+
|
|
2501
2508
|
return (
|
|
2502
2509
|
<div
|
|
2503
2510
|
style={{
|
|
@@ -2639,8 +2646,15 @@ export const renderStageCounterColumn = ({ column, commonProps, navigate, onUpda
|
|
|
2639
2646
|
sortable: false,
|
|
2640
2647
|
render: ({ data }) => {
|
|
2641
2648
|
if (data && column.stageConfig && column.stageConfig.key) {
|
|
2642
|
-
// Check if the data has the specified key and it's an array
|
|
2643
|
-
|
|
2649
|
+
// Check if the data has the specified key and it's an array;
|
|
2650
|
+
// fallbackKey (opt-in) covers rows whose primary key field
|
|
2651
|
+
// was lifted elsewhere (e.g. nested child rows showing their
|
|
2652
|
+
// own progress while the parent shows the whole group's)
|
|
2653
|
+
const stageData =
|
|
2654
|
+
data[column.stageConfig.key] ??
|
|
2655
|
+
(column.stageConfig.fallbackKey
|
|
2656
|
+
? data[column.stageConfig.fallbackKey]
|
|
2657
|
+
: null);
|
|
2644
2658
|
|
|
2645
2659
|
if (stageData && Array.isArray(stageData)) {
|
|
2646
2660
|
const stageCount = stageData.length;
|
|
@@ -662,19 +662,31 @@ function GenericDashboard({ setting, userProfile, dynamicDashboard = false }) {
|
|
|
662
662
|
return null;
|
|
663
663
|
});
|
|
664
664
|
|
|
665
|
-
|
|
665
|
+
// allSettled: one failed widget request must not blank the whole
|
|
666
|
+
// dashboard — failed widgets stay empty, the rest still render.
|
|
667
|
+
const results = await Promise.allSettled(promises);
|
|
666
668
|
|
|
667
669
|
const fetchedData = dashboardSetting.widgets.reduce(
|
|
668
670
|
(acc, widget, index) => {
|
|
669
|
-
|
|
671
|
+
const result = results[index];
|
|
672
|
+
|
|
673
|
+
if (result.status === 'rejected') {
|
|
674
|
+
console.error(
|
|
675
|
+
`Error fetching data for widget ${widget.id}:`,
|
|
676
|
+
result.reason
|
|
677
|
+
);
|
|
678
|
+
return acc;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
if (result.value) {
|
|
670
682
|
// Check if the response indicates an error
|
|
671
|
-
if (
|
|
683
|
+
if (result.value?.data?.value) {
|
|
672
684
|
console.error(
|
|
673
685
|
`API error for widget ${widget.id}:`,
|
|
674
|
-
|
|
686
|
+
result.value.data.value
|
|
675
687
|
);
|
|
676
688
|
} else {
|
|
677
|
-
acc[widget.id] =
|
|
689
|
+
acc[widget.id] = result.value?.data;
|
|
678
690
|
}
|
|
679
691
|
}
|
|
680
692
|
return acc;
|
|
@@ -109,6 +109,8 @@ function useConfig(setting, tabs, filters, setRowsSelected, tableSetting) {
|
|
|
109
109
|
type: setting.type ? setting.type : 'table',
|
|
110
110
|
// Make sure to include functions if they exist in the setting
|
|
111
111
|
functions: setting.functions || {},
|
|
112
|
+
// Optional colour legend rendered above the grid
|
|
113
|
+
legend: setting.legend,
|
|
112
114
|
};
|
|
113
115
|
|
|
114
116
|
setConfig(newConfig);
|
|
@@ -222,6 +224,8 @@ function useConfig(setting, tabs, filters, setRowsSelected, tableSetting) {
|
|
|
222
224
|
// Preserve functions from the tab config, falling back to the original config functions
|
|
223
225
|
functions:
|
|
224
226
|
activeTabConfig.functions || config.functions || {},
|
|
227
|
+
// Tab-specific legend, falling back to the base one
|
|
228
|
+
legend: activeTabConfig.legend ?? setting.legend,
|
|
225
229
|
};
|
|
226
230
|
|
|
227
231
|
setConfig(updatedConfig);
|
|
@@ -248,6 +252,7 @@ function useConfig(setting, tabs, filters, setRowsSelected, tableSetting) {
|
|
|
248
252
|
type: setting.type ? setting.type : 'table',
|
|
249
253
|
// Preserve functions from the original config or setting
|
|
250
254
|
functions: config.functions || setting.functions || {},
|
|
255
|
+
legend: setting.legend,
|
|
251
256
|
};
|
|
252
257
|
|
|
253
258
|
// Add the filter for the active child
|
|
@@ -70,6 +70,32 @@ div[role="tooltip"],
|
|
|
70
70
|
align-items: flex-start !important; /* Align content to top */
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/* Tablet mode uses a fixed row height (natural row measurement is
|
|
74
|
+
disabled there), so word-wrap cells must clamp to the row instead of
|
|
75
|
+
spilling their overflow into the row below. The cell itself must FILL
|
|
76
|
+
the row (height:auto would shrink it and paint its borders as a
|
|
77
|
+
floating box inside the row); the innermost wrapper gets a two-line
|
|
78
|
+
clamp — what fits a 52px row — for a clean ellipsis. */
|
|
79
|
+
body.tablet-mode .InovuaReactDataGrid__cell.cell-word-wrap,
|
|
80
|
+
body.tablet-mode .InovuaReactDataGrid__cell.cell-word-wrap > *,
|
|
81
|
+
body.tablet-mode .InovuaReactDataGrid__cell.cell-word-wrap div,
|
|
82
|
+
body.tablet-mode .InovuaReactDataGrid__cell.cell-word-wrap span {
|
|
83
|
+
overflow: hidden !important;
|
|
84
|
+
max-height: 100% !important;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
body.tablet-mode .InovuaReactDataGrid__cell.cell-word-wrap {
|
|
88
|
+
height: 100% !important;
|
|
89
|
+
align-items: center !important;
|
|
90
|
+
padding: 6px 8px !important;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
body.tablet-mode .InovuaReactDataGrid__cell.cell-word-wrap > div {
|
|
94
|
+
display: -webkit-box !important;
|
|
95
|
+
-webkit-box-orient: vertical !important;
|
|
96
|
+
-webkit-line-clamp: 2 !important;
|
|
97
|
+
}
|
|
98
|
+
|
|
73
99
|
/* Default cell behavior - no word wrap */
|
|
74
100
|
.InovuaReactDataGrid__cell:not(.cell-word-wrap) {
|
|
75
101
|
white-space: nowrap !important;
|