@visns-studio/visns-components 6.3.9 → 6.3.11

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
@@ -91,7 +91,7 @@
91
91
  "react-dom": "^17.0.0 || ^18.0.0"
92
92
  },
93
93
  "name": "@visns-studio/visns-components",
94
- "version": "6.3.9",
94
+ "version": "6.3.11",
95
95
  "description": "Various packages to assist in the development of our Custom Applications.",
96
96
  "main": "src/index.js",
97
97
  "files": [
@@ -759,21 +759,23 @@ const DataGrid = forwardRef(
759
759
  }
760
760
  );
761
761
 
762
- // Mark children for styling (getRowStyle) and
763
- // prefix their display value so nesting reads
762
+ // Mark children so the row and its display cell
763
+ // can be styled as nested.
764
+ //
765
+ // The "↳" marker is NOT written into the value
766
+ // here any more. Prepending it made the string
767
+ // itself longer, so the member's text started
768
+ // roughly a character and a half further right
769
+ // than the same column's text on every other
770
+ // row, and the indent had no fixed budget — it
771
+ // was however wide the glyph plus a space
772
+ // happened to set. It also left the row's own
773
+ // data holding a decoration, which anything
774
+ // reading that field downstream then had to
775
+ // know about. It is drawn instead by
776
+ // decorateTreeChildIndent, inside the cell.
764
777
  children.forEach((child) => {
765
778
  child.__treeChild = true;
766
-
767
- if (
768
- treeDisplayColumn &&
769
- child[treeDisplayColumn] != null &&
770
- String(
771
- child[treeDisplayColumn]
772
- ).indexOf('↳') !== 0
773
- ) {
774
- child[treeDisplayColumn] =
775
- '↳ ' + child[treeDisplayColumn];
776
- }
777
779
  });
778
780
 
779
781
  // Optional member count, e.g. "3 Headers"
@@ -979,16 +981,18 @@ const DataGrid = forwardRef(
979
981
  const { data } = rowProps;
980
982
  const style = {};
981
983
 
982
- // Tree mode: mark the summary parent and its nested
983
- // children with a left accent bar so the block reads as
984
- // one unit and children are visibly subordinate
985
- if (ajaxSetting?.treeGroupBy?.key) {
986
- if (data?.__treeParent) {
987
- style.borderLeft = '4px solid #2e7dbd';
988
- style.fontWeight = 600;
989
- } else if (data?.__treeChild) {
990
- style.borderLeft = '4px solid #93c5fd';
991
- }
984
+ // Tree mode: the parent's heavier weight. The left accent bar
985
+ // that used to be set here as `borderLeft` is now drawn by
986
+ // `.treeParentRow` / `.treeChildRow` — see rowClassName below
987
+ // and the notes in DataGrid.module.scss.
988
+ //
989
+ // A border belongs to the row's box, so a 4px one shifted the
990
+ // whole cell strip 4px right: every column edge on a tree row
991
+ // sat 4px off the same edge on an ordinary row and off the
992
+ // column header. The bar is now an inset overlay that occupies
993
+ // no space in the box model, so the columns line up exactly.
994
+ if (ajaxSetting?.treeGroupBy?.key && data?.__treeParent) {
995
+ style.fontWeight = 600;
992
996
  }
993
997
 
994
998
  if (ajaxSetting?.rowColours?.length > 0) {
@@ -1066,6 +1070,28 @@ const DataGrid = forwardRef(
1066
1070
  [ajaxSetting]
1067
1071
  );
1068
1072
 
1073
+ /**
1074
+ * The tree row's accent bar, as a class rather than a border.
1075
+ *
1076
+ * Kept out of `rowStyles` (which is a project-facing config) because
1077
+ * this is the library's own tree chrome, not something a view asks
1078
+ * for.
1079
+ */
1080
+ const treeRowClassName = useCallback(
1081
+ (data) => {
1082
+ if (!ajaxSetting?.treeGroupBy?.key || !data) {
1083
+ return '';
1084
+ }
1085
+
1086
+ if (data.__treeParent) {
1087
+ return styles.treeParentRow;
1088
+ }
1089
+
1090
+ return data.__treeChild ? styles.treeChildRow : '';
1091
+ },
1092
+ [ajaxSetting]
1093
+ );
1094
+
1069
1095
  /**
1070
1096
  * The grid's own row class plus whatever `rowStyles` adds. Passed as a
1071
1097
  * function rather than the bare string the grid used to get: the grid
@@ -1074,10 +1100,14 @@ const DataGrid = forwardRef(
1074
1100
  */
1075
1101
  const rowClassName = useCallback(
1076
1102
  (rowProps) =>
1077
- ['vs-datagrid--row', getRowClassName(rowProps)]
1103
+ [
1104
+ 'vs-datagrid--row',
1105
+ treeRowClassName(rowProps?.data),
1106
+ getRowClassName(rowProps),
1107
+ ]
1078
1108
  .filter(Boolean)
1079
1109
  .join(' '),
1080
- [getRowClassName]
1110
+ [getRowClassName, treeRowClassName]
1081
1111
  );
1082
1112
 
1083
1113
  const handleDownload = async (d) => {
@@ -3749,9 +3779,29 @@ const DataGrid = forwardRef(
3749
3779
  entries.filter(isRowActionConfig);
3750
3780
  // Per-row conditions may hide row actions at
3751
3781
  // runtime, but the column must fit the maximum
3752
- const width = actionColumnWidthFor(
3753
- actionSettings.length + rowActions.length
3754
- );
3782
+ const actionCount =
3783
+ actionSettings.length + rowActions.length;
3784
+ const computedWidth =
3785
+ actionColumnWidthFor(actionCount);
3786
+ // An explicit config maxWidth may narrow the
3787
+ // column below the 100px floor (a single-icon
3788
+ // column doesn't need it), but never below
3789
+ // what the icons themselves need — tap targets
3790
+ // stay intact in tablet mode.
3791
+ const iconsNeed =
3792
+ actionCount *
3793
+ (isTabletMode ? 44 : 34) +
3794
+ 16;
3795
+ const width =
3796
+ column.maxWidth != null
3797
+ ? Math.max(
3798
+ iconsNeed,
3799
+ Math.min(
3800
+ column.maxWidth,
3801
+ computedWidth
3802
+ )
3803
+ )
3804
+ : computedWidth;
3755
3805
  return {
3756
3806
  ...commonProps,
3757
3807
  name: Array.isArray(column.id)
@@ -4151,6 +4201,68 @@ const DataGrid = forwardRef(
4151
4201
  };
4152
4202
  };
4153
4203
 
4204
+ /**
4205
+ * The nesting marker on a tree member's display cell.
4206
+ *
4207
+ * Drawn here rather than prepended to the value, so the
4208
+ * indent is a FIXED budget spent inside the cell's content
4209
+ * box instead of however much advance width the glyph and
4210
+ * a space happen to take. The marker is a flex item of a
4211
+ * known width and the value takes the rest with
4212
+ * `min-width: 0`, so a long value ellipsizes at the column
4213
+ * edge rather than running past it — the column's left and
4214
+ * right edges are then identical on ordinary, parent and
4215
+ * member rows.
4216
+ *
4217
+ * Applied UNDER decorateTreeParentActions, which delegates
4218
+ * to this for every non-parent row.
4219
+ */
4220
+ const decorateTreeChildIndent = (gridColumn, columnDef) => {
4221
+ const tree = ajaxSetting?.treeGroupBy;
4222
+ const columnId = Array.isArray(columnDef?.id)
4223
+ ? columnDef.id.join('-')
4224
+ : columnDef?.id;
4225
+
4226
+ if (
4227
+ !tree?.key ||
4228
+ !tree.column ||
4229
+ tree.column !== columnId
4230
+ ) {
4231
+ return gridColumn;
4232
+ }
4233
+
4234
+ const originalRender = gridColumn.render;
4235
+
4236
+ return {
4237
+ ...gridColumn,
4238
+ render: (arg) => {
4239
+ const content = originalRender
4240
+ ? originalRender(arg)
4241
+ : arg?.value ?? null;
4242
+
4243
+ if (!arg?.data?.__treeChild) {
4244
+ return content;
4245
+ }
4246
+
4247
+ return (
4248
+ <span className={styles.treeChildCell}>
4249
+ <span
4250
+ className={styles.treeChildMarker}
4251
+ aria-hidden="true"
4252
+ >
4253
+ ↳
4254
+ </span>
4255
+ <span
4256
+ className={styles.treeChildValue}
4257
+ >
4258
+ {content}
4259
+ </span>
4260
+ </span>
4261
+ );
4262
+ },
4263
+ };
4264
+ };
4265
+
4154
4266
  /**
4155
4267
  * The group's collapse control.
4156
4268
  *
@@ -4429,11 +4541,18 @@ const DataGrid = forwardRef(
4429
4541
  };
4430
4542
  };
4431
4543
 
4544
+ // decorateTreeChildIndent sits INSIDE
4545
+ // decorateTreeParentActions: the latter draws parent rows
4546
+ // itself and delegates everything else downward, so the
4547
+ // member indent has to be the render it delegates to.
4432
4548
  const newColumns = columns.map((column) =>
4433
4549
  decorateColumnRowActions(
4434
4550
  decorateTreeParentActions(
4435
- guardTreeParentRender(
4436
- renderColumn(column),
4551
+ decorateTreeChildIndent(
4552
+ guardTreeParentRender(
4553
+ renderColumn(column),
4554
+ column
4555
+ ),
4437
4556
  column
4438
4557
  ),
4439
4558
  column
@@ -1579,3 +1579,96 @@
1579
1579
  );
1580
1580
  }
1581
1581
  }
1582
+
1583
+ /* =========================================================================
1584
+ treeGroupBy nesting: accent bar and member indent
1585
+
1586
+ Both of these used to be drawn in ways that took up space in the layout,
1587
+ which is what pushed tree rows out of line with ordinary ones:
1588
+
1589
+ - the accent bar was `border-left: 4px` set on the ROW. A border is part
1590
+ of the row's box, so the whole cell strip started 4px to the right and
1591
+ every column edge on a tree row sat 4px off the same edge on an
1592
+ ordinary row (and off the column header).
1593
+
1594
+ - the "↳" was PREPENDED TO THE VALUE, so it was ordinary text inside the
1595
+ cell's single text run. The member's own text therefore began after the
1596
+ glyph plus a space — about 11px at 14px Barlow — while the parent's
1597
+ label and every ordinary row's text began at the cell's padding edge.
1598
+ The indent was whatever the font made it, it varied with the type size,
1599
+ and a long value had that much less room before it ran out of cell.
1600
+
1601
+ Both are now drawn without occupying layout: the bar is an inset overlay,
1602
+ and the marker is a fixed-width flex item spent from INSIDE the cell's
1603
+ existing content box.
1604
+
1605
+ Density hooks, per the convention above:
1606
+
1607
+ --grid-tree-accent-width the nesting bar. default: 4px
1608
+ --grid-tree-indent member indent budget, marker included.
1609
+ default: 18px
1610
+ ========================================================================= */
1611
+
1612
+ /* The bar is painted by an overlay pinned to the row's left edge rather than
1613
+ by a border, so it costs nothing in the box model. It has to be an overlay
1614
+ rather than an inset box-shadow because `.InovuaReactDataGrid__row-cell-wrap`
1615
+ carries `background: inherit` — it repaints the row's own background across
1616
+ the full width and would cover any shadow the row painted underneath. */
1617
+ .treeParentRow,
1618
+ .treeChildRow {
1619
+ position: relative;
1620
+
1621
+ &::after {
1622
+ content: '';
1623
+ position: absolute;
1624
+ top: 0;
1625
+ bottom: 0;
1626
+ left: 0;
1627
+ width: var(--grid-tree-accent-width, 4px);
1628
+ /* Above the cell strip, which paints an opaque background of its own */
1629
+ z-index: 1;
1630
+ pointer-events: none;
1631
+ }
1632
+ }
1633
+
1634
+ .treeParentRow::after {
1635
+ background: var(--grid-tree-accent-parent, #2e7dbd);
1636
+ }
1637
+
1638
+ .treeChildRow::after {
1639
+ background: var(--grid-tree-accent-child, #93c5fd);
1640
+ }
1641
+
1642
+ /* The member's display cell: marker and value on one line, inside the cell.
1643
+ `min-width: 0` on both the wrapper and the value is what lets the value
1644
+ ellipsize at the column edge instead of overflowing past it — a flex item
1645
+ defaults to `min-width: auto`, which refuses to shrink below its content. */
1646
+ .treeChildCell {
1647
+ display: flex;
1648
+ align-items: center;
1649
+ width: 100%;
1650
+ min-width: 0;
1651
+ overflow: hidden;
1652
+ }
1653
+
1654
+ .treeChildMarker {
1655
+ /* Fixed basis, never shrinks: this IS the indent budget, so the value
1656
+ starts at the same offset on every member row whatever the glyph, the
1657
+ type size or the value's length. */
1658
+ flex: 0 0 var(--grid-tree-indent, 18px);
1659
+ width: var(--grid-tree-indent, 18px);
1660
+ display: inline-flex;
1661
+ align-items: center;
1662
+ justify-content: flex-start;
1663
+ color: var(--muted-color, #6b7688);
1664
+ line-height: 1;
1665
+ user-select: none;
1666
+ }
1667
+
1668
+ .treeChildValue {
1669
+ flex: 1 1 auto;
1670
+ min-width: 0;
1671
+ overflow: hidden;
1672
+ text-overflow: ellipsis;
1673
+ white-space: nowrap;
1674
+ }