@visns-studio/visns-components 6.3.6 → 6.3.7
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.
|
|
94
|
+
"version": "6.3.7",
|
|
95
95
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
96
96
|
"main": "src/index.js",
|
|
97
97
|
"files": [
|
|
@@ -78,6 +78,16 @@ import {
|
|
|
78
78
|
} from 'lucide-react';
|
|
79
79
|
import styles from './styles/DataGrid.module.scss';
|
|
80
80
|
|
|
81
|
+
/**
|
|
82
|
+
* `ajaxSetting.rowStyles[].style` tokens → the module class that draws them.
|
|
83
|
+
* Kept as a map rather than `styles['row' + capitalise(token)]` so an unknown
|
|
84
|
+
* token in a config is simply ignored instead of resolving to `undefined` and
|
|
85
|
+
* putting the literal string "undefined" in the row's class list.
|
|
86
|
+
*/
|
|
87
|
+
const ROW_STYLE_CLASSES = {
|
|
88
|
+
complete: styles.rowComplete,
|
|
89
|
+
};
|
|
90
|
+
|
|
81
91
|
import '@visns-studio/visns-datagrid-enterprise/index.css';
|
|
82
92
|
|
|
83
93
|
import CustomFetch from './Fetch';
|
|
@@ -959,6 +969,80 @@ const DataGrid = forwardRef(
|
|
|
959
969
|
[ajaxSetting]
|
|
960
970
|
);
|
|
961
971
|
|
|
972
|
+
/**
|
|
973
|
+
* Opt-in non-colour row states (`ajaxSetting.rowStyles`).
|
|
974
|
+
*
|
|
975
|
+
* rowStyles: [
|
|
976
|
+
* { id: 'jigging_status', value: 1, style: 'complete' }
|
|
977
|
+
* ]
|
|
978
|
+
*
|
|
979
|
+
* Each rule names a FIELD STATE, not a look: `style` is a token the
|
|
980
|
+
* package maps to a class in DataGrid.module.scss, so a project says
|
|
981
|
+
* what a row means and the library decides how that reads. Today the
|
|
982
|
+
* only token is `complete` (line-through plus a muted ink).
|
|
983
|
+
*
|
|
984
|
+
* Deliberately different from `rowColours` in two ways:
|
|
985
|
+
*
|
|
986
|
+
* - EVERY matching rule applies, where rowColours takes the first
|
|
987
|
+
* match only. A row is frequently more than one thing at once (a
|
|
988
|
+
* finished item that is also on a T-Bar), and with first-match the
|
|
989
|
+
* completion state would be swallowed by whichever tint happened to
|
|
990
|
+
* be listed above it.
|
|
991
|
+
* - It carries no colour. That is the point: a pale tint is the first
|
|
992
|
+
* thing to disappear on a plant-floor tablet in daylight, so a state
|
|
993
|
+
* that matters has to be legible without one. Colour, where a rule
|
|
994
|
+
* also sets one, stays the redundant second channel.
|
|
995
|
+
*
|
|
996
|
+
* `operator` follows the same small vocabulary as `readOnlyWhen`:
|
|
997
|
+
* eq (default), neq, notEmpty.
|
|
998
|
+
*/
|
|
999
|
+
const getRowClassName = useCallback(
|
|
1000
|
+
({ data }) => {
|
|
1001
|
+
const rules = ajaxSetting?.rowStyles;
|
|
1002
|
+
|
|
1003
|
+
if (!Array.isArray(rules) || rules.length === 0 || !data) {
|
|
1004
|
+
return '';
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
const matches = (rule) => {
|
|
1008
|
+
const value = data[rule.id];
|
|
1009
|
+
|
|
1010
|
+
switch (rule.operator) {
|
|
1011
|
+
case 'neq':
|
|
1012
|
+
return value != rule.value;
|
|
1013
|
+
case 'notEmpty':
|
|
1014
|
+
return Array.isArray(value)
|
|
1015
|
+
? value.length > 0
|
|
1016
|
+
: value != null && value !== '';
|
|
1017
|
+
case 'eq':
|
|
1018
|
+
default:
|
|
1019
|
+
return value == rule.value;
|
|
1020
|
+
}
|
|
1021
|
+
};
|
|
1022
|
+
|
|
1023
|
+
return rules
|
|
1024
|
+
.filter(matches)
|
|
1025
|
+
.map((rule) => ROW_STYLE_CLASSES[rule.style])
|
|
1026
|
+
.filter(Boolean)
|
|
1027
|
+
.join(' ');
|
|
1028
|
+
},
|
|
1029
|
+
[ajaxSetting]
|
|
1030
|
+
);
|
|
1031
|
+
|
|
1032
|
+
/**
|
|
1033
|
+
* The grid's own row class plus whatever `rowStyles` adds. Passed as a
|
|
1034
|
+
* function rather than the bare string the grid used to get: the grid
|
|
1035
|
+
* accepts either, and `vs-datagrid--row` (the hover rule) has to stay
|
|
1036
|
+
* on every row regardless.
|
|
1037
|
+
*/
|
|
1038
|
+
const rowClassName = useCallback(
|
|
1039
|
+
(rowProps) =>
|
|
1040
|
+
['vs-datagrid--row', getRowClassName(rowProps)]
|
|
1041
|
+
.filter(Boolean)
|
|
1042
|
+
.join(' '),
|
|
1043
|
+
[getRowClassName]
|
|
1044
|
+
);
|
|
1045
|
+
|
|
962
1046
|
const handleDownload = async (d) => {
|
|
963
1047
|
const toastId = toast.loading('Starting download please wait...');
|
|
964
1048
|
|
|
@@ -5205,7 +5289,7 @@ const DataGrid = forwardRef(
|
|
|
5205
5289
|
pagination
|
|
5206
5290
|
renderRowContextMenu={renderRowContextMenu}
|
|
5207
5291
|
renderColumnContextMenu={renderColumnContextMenu}
|
|
5208
|
-
rowClassName=
|
|
5292
|
+
rowClassName={rowClassName}
|
|
5209
5293
|
rowHeight={isTabletMode ? 52 : null}
|
|
5210
5294
|
rowStyle={getRowStyle}
|
|
5211
5295
|
selected={selected}
|
|
@@ -40,6 +40,55 @@ const buildToggleUrl = (urlTemplate, stageItem, rowData) => {
|
|
|
40
40
|
return url;
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Evaluate an opt-in stageConfig rule ({key, operator, value}) against one
|
|
45
|
+
* entry of a stage list. Absent rule -> false, so every setting that uses this
|
|
46
|
+
* is inert until a config asks for it.
|
|
47
|
+
*
|
|
48
|
+
* Operators: eq (default), neq, gte, gt, lte, lt, in. The comparison operators
|
|
49
|
+
* coerce to Number, because the values these rules match on arrive as strings
|
|
50
|
+
* from some payloads and as numbers from others, and a stage ordinal compared
|
|
51
|
+
* as a string sorts "10" below "2".
|
|
52
|
+
*/
|
|
53
|
+
const matchesStageRule = (rule, stageItem) => {
|
|
54
|
+
if (!rule || !rule.key || !stageItem) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const value = stageItem[rule.key];
|
|
59
|
+
|
|
60
|
+
if (value === null || value === undefined) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (rule.operator === 'in') {
|
|
65
|
+
return (
|
|
66
|
+
Array.isArray(rule.value) &&
|
|
67
|
+
rule.value.some((candidate) => candidate == value)
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const left = Number(value);
|
|
72
|
+
const right = Number(rule.value);
|
|
73
|
+
const comparable = !Number.isNaN(left) && !Number.isNaN(right);
|
|
74
|
+
|
|
75
|
+
switch (rule.operator) {
|
|
76
|
+
case 'neq':
|
|
77
|
+
return value != rule.value;
|
|
78
|
+
case 'gte':
|
|
79
|
+
return comparable && left >= right;
|
|
80
|
+
case 'gt':
|
|
81
|
+
return comparable && left > right;
|
|
82
|
+
case 'lte':
|
|
83
|
+
return comparable && left <= right;
|
|
84
|
+
case 'lt':
|
|
85
|
+
return comparable && left < right;
|
|
86
|
+
case 'eq':
|
|
87
|
+
default:
|
|
88
|
+
return value == rule.value;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
43
92
|
const getStageStatus = (stageItem, toggleConfig) => {
|
|
44
93
|
if (!toggleConfig || !toggleConfig.statusField) return null;
|
|
45
94
|
|
|
@@ -2895,6 +2944,30 @@ export const renderStageCounterColumn = ({ column, commonProps, navigate, onUpda
|
|
|
2895
2944
|
});
|
|
2896
2945
|
}
|
|
2897
2946
|
|
|
2947
|
+
// `completeWhen` (opt-in): the rule that says this
|
|
2948
|
+
// entry is DONE as far as the grid showing it is
|
|
2949
|
+
// concerned, drawn as a rule through the label plus a
|
|
2950
|
+
// step back in weight.
|
|
2951
|
+
//
|
|
2952
|
+
// It lives in the view config rather than in the data
|
|
2953
|
+
// because "complete" is a statement about the VIEWER,
|
|
2954
|
+
// not about the entry: the same stage list is served
|
|
2955
|
+
// unchanged to every grid, and a member sitting at
|
|
2956
|
+
// Despatch is finished business to the Despatch list
|
|
2957
|
+
// while still being work in progress to Pickling. The
|
|
2958
|
+
// grid is the only party that knows which it is.
|
|
2959
|
+
//
|
|
2960
|
+
// Not a colour, on purpose: the pill's fill already
|
|
2961
|
+
// encodes the stage, and a second hue on a tablet in
|
|
2962
|
+
// daylight is the signal least likely to survive the
|
|
2963
|
+
// reading conditions. The rule through the label is
|
|
2964
|
+
// legible in glare and in greyscale; the fill stays as
|
|
2965
|
+
// it was, so nothing is lost by adding this.
|
|
2966
|
+
const isStageComplete = matchesStageRule(
|
|
2967
|
+
column.stageConfig.completeWhen,
|
|
2968
|
+
stageItem
|
|
2969
|
+
);
|
|
2970
|
+
|
|
2898
2971
|
// Apply visual feedback for inactive stages
|
|
2899
2972
|
const stageStyle = {
|
|
2900
2973
|
width: useFullLabel ? 'auto' : '20px',
|
|
@@ -2911,7 +2984,21 @@ export const renderStageCounterColumn = ({ column, commonProps, navigate, onUpda
|
|
|
2911
2984
|
fontSize: '11px',
|
|
2912
2985
|
fontWeight: 'bold',
|
|
2913
2986
|
cursor: isToggleable ? 'pointer' : 'default',
|
|
2914
|
-
|
|
2987
|
+
...(isStageComplete
|
|
2988
|
+
? {
|
|
2989
|
+
textDecoration: 'line-through',
|
|
2990
|
+
textDecorationThickness:
|
|
2991
|
+
'var(--grid-stage-complete-thickness, 2px)',
|
|
2992
|
+
}
|
|
2993
|
+
: {}),
|
|
2994
|
+
// An inactive toggle already mutes to 0.5; a
|
|
2995
|
+
// complete entry steps back a little less, so the
|
|
2996
|
+
// two states stay tellable apart when both apply
|
|
2997
|
+
opacity: !isStageActive
|
|
2998
|
+
? 0.5
|
|
2999
|
+
: isStageComplete
|
|
3000
|
+
? 'var(--grid-stage-complete-opacity, 0.65)'
|
|
3001
|
+
: 1,
|
|
2915
3002
|
transition: 'opacity 0.2s ease, transform 0.1s ease',
|
|
2916
3003
|
border: isToggleable ? '1px solid rgba(255,255,255,0.3)' : 'none',
|
|
2917
3004
|
};
|
|
@@ -1354,3 +1354,51 @@
|
|
|
1354
1354
|
}
|
|
1355
1355
|
|
|
1356
1356
|
|
|
1357
|
+
|
|
1358
|
+
/* =========================================================================
|
|
1359
|
+
Row states (`ajaxSetting.rowStyles`)
|
|
1360
|
+
|
|
1361
|
+
A row tint is the first thing to go on a plant-floor tablet: pale fills
|
|
1362
|
+
are chosen to sit under black text, which is exactly what makes them
|
|
1363
|
+
vanish under glare or a low-angle view. The rows that carry these states
|
|
1364
|
+
are the ones a crew scans a list FOR ("which of these headers is already
|
|
1365
|
+
done?"), so the state has to survive the conditions the list is read in.
|
|
1366
|
+
|
|
1367
|
+
The treatment is therefore a rule through the text plus a step back in
|
|
1368
|
+
ink weight — two signals, neither of them hue. Any `rowColours` tint on
|
|
1369
|
+
the same row is untouched and becomes the redundant third.
|
|
1370
|
+
|
|
1371
|
+
Strike and ink are applied to the CELLS, not the row: the grid lays its
|
|
1372
|
+
cells out absolutely, so `text-decoration` set on the row element has no
|
|
1373
|
+
in-flow descendants to inherit it and would draw nothing.
|
|
1374
|
+
|
|
1375
|
+
Density hooks, per the `--nav-*` / `--grid-legend-*` convention above —
|
|
1376
|
+
every declaration is `var(--hook, <the value it always had>)`, so an unset
|
|
1377
|
+
hook is a no-op and a project retunes from `:root`:
|
|
1378
|
+
|
|
1379
|
+
--grid-row-complete-ink struck-through text colour.
|
|
1380
|
+
default: var(--muted-color, #6b7688)
|
|
1381
|
+
--grid-row-complete-thickness weight of the rule through the text.
|
|
1382
|
+
default: 2px — heavier than the browser
|
|
1383
|
+
default, which is hairline at this size.
|
|
1384
|
+
--grid-row-complete-opacity applies to non-text cell content (status
|
|
1385
|
+
pills, icons), which a rule cannot cross.
|
|
1386
|
+
default: 0.7
|
|
1387
|
+
========================================================================= */
|
|
1388
|
+
|
|
1389
|
+
.rowComplete {
|
|
1390
|
+
:global(.InovuaReactDataGrid__cell) {
|
|
1391
|
+
color: var(--grid-row-complete-ink, var(--muted-color, #6b7688));
|
|
1392
|
+
text-decoration: line-through;
|
|
1393
|
+
text-decoration-thickness: var(--grid-row-complete-thickness, 2px);
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
// Cell content that is drawn rather than set — the action buttons, the
|
|
1397
|
+
// stage pills, the boolean ticks — takes the muting alone, since a rule
|
|
1398
|
+
// through the text cannot cross it.
|
|
1399
|
+
:global(.InovuaReactDataGrid__cell) svg,
|
|
1400
|
+
:global(.InovuaReactDataGrid__cell) img,
|
|
1401
|
+
:global(.InovuaReactDataGrid__cell) button {
|
|
1402
|
+
opacity: var(--grid-row-complete-opacity, 0.7);
|
|
1403
|
+
}
|
|
1404
|
+
}
|