@visns-studio/visns-components 6.23.1 → 6.24.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.
package/package.json CHANGED
@@ -93,7 +93,7 @@
93
93
  "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
94
94
  },
95
95
  "name": "@visns-studio/visns-components",
96
- "version": "6.23.1",
96
+ "version": "6.24.0",
97
97
  "description": "Various packages to assist in the development of our Custom Applications.",
98
98
  "main": "src/index.js",
99
99
  "files": [
@@ -43,6 +43,7 @@ import NativeDateFilterEditor from './columns/NativeDateFilterEditor';
43
43
  import SelectFilterEditor from './columns/SelectFilterEditor';
44
44
  import { toast } from 'react-toastify';
45
45
  import fetchUtil from '../utils/fetchUtil';
46
+ import { resolveFormVariant } from '../utils/formLayout';
46
47
  import { confirmDialog } from './utils/ConfirmDialog';
47
48
  import {
48
49
  useDensity,
@@ -1431,7 +1432,14 @@ const DataGrid = forwardRef(
1431
1432
  // A rowColours tint is an inline background on the row
1432
1433
  // element; the class lets the stylesheet clear the zebra
1433
1434
  // band that would otherwise paint over it on odd rows.
1434
- findRowColour(rowProps?.data) ? styles.tintedRow : '',
1435
+ // The literal twin exists for global-datagrid.css, which
1436
+ // cannot see the hashed module class: the 2026-pass row
1437
+ // surface there is `!important` (it has to outrank the
1438
+ // vendored theme) and would beat the inline tint, so that
1439
+ // rule stands down on rows carrying this class.
1440
+ findRowColour(rowProps?.data)
1441
+ ? `${styles.tintedRow} vs-datagrid--row-tinted`
1442
+ : '',
1435
1443
  ]
1436
1444
  .filter(Boolean)
1437
1445
  .join(' '),
@@ -6774,10 +6782,14 @@ const DataGrid = forwardRef(
6774
6782
  /* A record form is the case a side sheet is for: the list
6775
6783
  stays visible behind it, a long form scrolls naturally
6776
6784
  instead of stretching the dialog, and the width is fixed
6777
- so the fields keep one measure on every monitor. A view
6778
- that genuinely wants the centred dialog sets
6779
- `form.layout: "modal"`. */
6780
- variant={formData.layout === 'modal' ? 'modal' : 'sheet'}
6785
+ so the fields keep one measure on every monitor. That is
6786
+ the library default, so forms land in the sheet unless
6787
+ something says otherwise. A whole project can flip to the
6788
+ centred dialog with `setDefaultFormLayout('modal')` at
6789
+ bootstrap, and a single view that wants the other
6790
+ presentation overrides it with `form.layout: "modal"` or
6791
+ `form.layout: "sheet"`. */
6792
+ variant={resolveFormVariant(formData.layout)}
6781
6793
  size={formData.modalSize || 'medium'}
6782
6794
  >
6783
6795
  <Form
@@ -47,6 +47,7 @@ import { saveAs } from 'file-saver';
47
47
  import { toast } from 'react-toastify';
48
48
  import imageCompression from 'browser-image-compression';
49
49
  import fetchUtil from '../../utils/fetchUtil';
50
+ import { resolveFormVariant } from '../../utils/formLayout';
50
51
  import {
51
52
  CheckCircle as CircleCheck,
52
53
  Copy,
@@ -4267,7 +4268,7 @@ function GenericDetail({
4267
4268
  title={formData.title || 'Edit Details'}
4268
4269
  closeOnDocumentClick={false}
4269
4270
  verticalAlign={formData.verticalAlign || 'center'}
4270
- variant={formData.layout === 'modal' ? 'modal' : 'sheet'}
4271
+ variant={resolveFormVariant(formData.layout)}
4271
4272
  size={formData.modalSize || 'medium'}
4272
4273
  >
4273
4274
  <Form
@@ -647,10 +647,23 @@ body.tablet-mode .InovuaReactDataGrid__cell.cell-word-wrap > div {
647
647
  .InovuaReactDataGrid__row {
648
648
  min-height: 44px !important;
649
649
  border: 0 !important;
650
- background: var(--dgx-surface) !important;
651
650
  transition: background 140ms cubic-bezier(0.4, 0, 0.2, 1) !important;
652
651
  }
653
652
 
653
+ /* The surface is on its own rule so it can stand down for a `rowColours`
654
+ tint. That tint arrives as an INLINE background set by rowStyle in
655
+ DataGrid.jsx, and inline loses to any `!important` — so painting the
656
+ surface `!important` on every row silently killed the priority/urgency
657
+ colours projects configure (tasks grids and the like). Rows with a tint
658
+ are marked `vs-datagrid--row-tinted` (the literal twin of the module's
659
+ hashed `.tintedRow`); on those rows nothing is declared here and the
660
+ inline tint wins over the vendored theme's non-important zebra.
661
+ Hover and selected below stay `!important` on every row by design —
662
+ they have always outranked the tint. */
663
+ .InovuaReactDataGrid__row:not(.vs-datagrid--row-tinted) {
664
+ background: var(--dgx-surface) !important;
665
+ }
666
+
654
667
  .InovuaReactDataGrid__row:hover,
655
668
  .InovuaReactDataGrid__row--hover-target:hover {
656
669
  background: var(--dgx-hover) !important;
package/src/index.js CHANGED
@@ -14,6 +14,14 @@ import {
14
14
  SECONDARY_GRID_HEIGHT,
15
15
  } from './components/utils/useDensity';
16
16
  import { readBuildEnv, buildEnv } from './components/utils/buildEnv';
17
+ // Project-wide default for how record CRUD forms are presented — the side
18
+ // sheet unless an app calls setDefaultFormLayout('modal') at bootstrap. A
19
+ // form config's own `layout` still wins.
20
+ import {
21
+ setDefaultFormLayout,
22
+ getDefaultFormLayout,
23
+ resolveFormVariant,
24
+ } from './utils/formLayout';
17
25
 
18
26
  /** CRM Components */
19
27
  import AsyncSelect from './components/AsyncSelect';
@@ -478,6 +486,7 @@ export {
478
486
  GenericQuote,
479
487
  GenericReport,
480
488
  GenericSort,
489
+ getDefaultFormLayout,
481
490
  GroupedReportRenderer,
482
491
  hasChallengeState,
483
492
  ImpersonateGate,
@@ -523,6 +532,7 @@ export {
523
532
  resolveAuthEndpoints,
524
533
  resolveClientPaths,
525
534
  resolveClientProtocol,
535
+ resolveFormVariant,
526
536
  // Passkeys (WebAuthn). Exported so a consuming app can build its own
527
537
  // enrolment screen against the same marshalling the login screen uses.
528
538
  base64UrlToBytes,
@@ -536,6 +546,7 @@ export {
536
546
  SectionTypeSelector,
537
547
  SelectList,
538
548
  Select,
549
+ setDefaultFormLayout,
539
550
  showConfirmDialog,
540
551
  SortableList,
541
552
  StagePopupModal,
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Project-wide default for how record CRUD forms are presented.
3
+ *
4
+ * StandardModal can draw a form either as a centred dialog (`variant="modal"`)
5
+ * or as a right-side slide-in sheet (`variant="sheet"`). The library ships with
6
+ * the sheet as its default so existing projects keep the presentation they
7
+ * already have. A consuming app that prefers the classic centred dialog calls
8
+ * `setDefaultFormLayout('modal')` once at bootstrap (before the first render)
9
+ * and every DataGrid / GenericDetail form follows.
10
+ *
11
+ * An individual form config still wins in both directions: set
12
+ * `layout: 'modal'` or `layout: 'sheet'` on the form and that form ignores the
13
+ * project default.
14
+ */
15
+
16
+ /** The two presentations StandardModal understands. */
17
+ const VALID_LAYOUTS = ['modal', 'sheet'];
18
+
19
+ /** Library default — the side sheet, unless a project opts out. */
20
+ let defaultFormLayout = 'sheet';
21
+
22
+ /** Warn at most once so a bad bootstrap call is not a console flood. */
23
+ let warnedInvalidLayout = false;
24
+
25
+ /**
26
+ * Set the project-wide default presentation for record forms.
27
+ * @param {'modal'|'sheet'} layout - Presentation to use when a form config does
28
+ * not specify its own `layout`.
29
+ * @returns {void} Anything other than 'modal' or 'sheet' is ignored.
30
+ */
31
+ export const setDefaultFormLayout = (layout) => {
32
+ if (!VALID_LAYOUTS.includes(layout)) {
33
+ if (!warnedInvalidLayout) {
34
+ warnedInvalidLayout = true;
35
+ // eslint-disable-next-line no-console
36
+ console.warn(
37
+ `setDefaultFormLayout: expected 'modal' or 'sheet', received ${JSON.stringify(layout)}. Keeping '${defaultFormLayout}'.`
38
+ );
39
+ }
40
+ return;
41
+ }
42
+
43
+ defaultFormLayout = layout;
44
+ };
45
+
46
+ /**
47
+ * Read the current project-wide default.
48
+ * @returns {'modal'|'sheet'} The default presentation for record forms.
49
+ */
50
+ export const getDefaultFormLayout = () => defaultFormLayout;
51
+
52
+ /**
53
+ * Resolve the StandardModal variant for one form.
54
+ * @param {string} [layout] - The form config's own `layout`, if it has one.
55
+ * @returns {'modal'|'sheet'} The form's own layout when it is valid, otherwise
56
+ * the project-wide default.
57
+ */
58
+ export const resolveFormVariant = (layout) =>
59
+ (VALID_LAYOUTS.includes(layout) ? layout : defaultFormLayout);