@visns-studio/visns-components 6.3.11 → 6.3.13

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.11",
94
+ "version": "6.3.13",
95
95
  "description": "Various packages to assist in the development of our Custom Applications.",
96
96
  "main": "src/index.js",
97
97
  "files": [
@@ -976,6 +976,19 @@ const DataGrid = forwardRef(
976
976
  },
977
977
  }));
978
978
 
979
+ // First matching `rowColours` rule for a row, shared by getRowStyle
980
+ // (which paints it) and rowClassName (which marks the row so the
981
+ // zebra banding stands down — see .tintedRow in the module styles).
982
+ const findRowColour = useCallback(
983
+ (data) =>
984
+ ajaxSetting?.rowColours?.length > 0 && data
985
+ ? ajaxSetting.rowColours.find(
986
+ (config) => data[config.id] === config.value
987
+ )
988
+ : undefined,
989
+ [ajaxSetting]
990
+ );
991
+
979
992
  const getRowStyle = useCallback(
980
993
  (rowProps) => {
981
994
  const { data } = rowProps;
@@ -995,19 +1008,14 @@ const DataGrid = forwardRef(
995
1008
  style.fontWeight = 600;
996
1009
  }
997
1010
 
998
- if (ajaxSetting?.rowColours?.length > 0) {
999
- const colorConfig = ajaxSetting.rowColours.find(
1000
- (config) => data[config.id] === config.value
1001
- );
1002
-
1003
- if (colorConfig) {
1004
- style.backgroundColor = colorConfig.colour;
1005
- }
1011
+ const colorConfig = findRowColour(data);
1012
+ if (colorConfig) {
1013
+ style.backgroundColor = colorConfig.colour;
1006
1014
  }
1007
1015
 
1008
1016
  return Object.keys(style).length > 0 ? style : null;
1009
1017
  },
1010
- [ajaxSetting]
1018
+ [ajaxSetting, findRowColour]
1011
1019
  );
1012
1020
 
1013
1021
  /**
@@ -1104,10 +1112,14 @@ const DataGrid = forwardRef(
1104
1112
  'vs-datagrid--row',
1105
1113
  treeRowClassName(rowProps?.data),
1106
1114
  getRowClassName(rowProps),
1115
+ // A rowColours tint is an inline background on the row
1116
+ // element; the class lets the stylesheet clear the zebra
1117
+ // band that would otherwise paint over it on odd rows.
1118
+ findRowColour(rowProps?.data) ? styles.tintedRow : '',
1107
1119
  ]
1108
1120
  .filter(Boolean)
1109
1121
  .join(' '),
1110
- [getRowClassName, treeRowClassName]
1122
+ [getRowClassName, treeRowClassName, findRowColour]
1111
1123
  );
1112
1124
 
1113
1125
  const handleDownload = async (d) => {
@@ -152,6 +152,9 @@ function Field({
152
152
  const editorRef = useRef(null);
153
153
  const fileImageInputRef = useRef(null);
154
154
  const containerRef = useRef(null);
155
+ // Wrapper for the `colour` field's popup picker — used by the
156
+ // click-outside handler below to tell inside clicks from outside ones
157
+ const colorPickerRef = useRef(null);
155
158
  const [autoValue, setAutoValue] = useState('');
156
159
  const [colorPickerShow, setColorPickerShow] = useState(false);
157
160
  const [counter, setCounter] = useState(0);
@@ -163,6 +166,42 @@ function Field({
163
166
  const [emailSuggestions, setEmailSuggestions] = useState([]);
164
167
  const [showEmailSuggestions, setShowEmailSuggestions] = useState(false);
165
168
 
169
+ /**
170
+ * `colour` field popup picker: dismiss the popover when the user clicks
171
+ * anywhere outside it or presses Escape. Picking a colour inside the
172
+ * popover deliberately keeps it open (users drag around the gradient).
173
+ * Declared here — top level of the component — because the field itself
174
+ * is rendered from a switch further down.
175
+ */
176
+ useEffect(() => {
177
+ if (!colorPickerShow) {
178
+ return;
179
+ }
180
+
181
+ const onMouseDown = (event) => {
182
+ if (
183
+ colorPickerRef.current &&
184
+ !colorPickerRef.current.contains(event.target)
185
+ ) {
186
+ setColorPickerShow(false);
187
+ }
188
+ };
189
+
190
+ const onKeyDown = (event) => {
191
+ if (event.key === 'Escape') {
192
+ setColorPickerShow(false);
193
+ }
194
+ };
195
+
196
+ document.addEventListener('mousedown', onMouseDown);
197
+ document.addEventListener('keydown', onKeyDown);
198
+
199
+ return () => {
200
+ document.removeEventListener('mousedown', onMouseDown);
201
+ document.removeEventListener('keydown', onKeyDown);
202
+ };
203
+ }, [colorPickerShow]);
204
+
166
205
  /**
167
206
  * Conflict warnings (opt-in via `settings.conflict`): the field polls
168
207
  * `conflict.url` for a map of {optionId: info} describing options that
@@ -1069,46 +1108,77 @@ function Field({
1069
1108
  ))}
1070
1109
  </div>
1071
1110
  );
1072
- case 'colour':
1111
+ case 'colour': {
1112
+ const hasColour = inputValue && inputValue !== 'null';
1113
+ const toggleColorPicker = () => {
1114
+ if (isFieldReadOnly) {
1115
+ return;
1116
+ }
1117
+
1118
+ setColorPickerShow((show) => !show);
1119
+ };
1120
+
1073
1121
  return (
1074
- <div className="cpicker-inline">
1075
- <div className="cpicker-preview-container">
1122
+ <div
1123
+ className="cpicker-inline cpicker-popup"
1124
+ ref={colorPickerRef}
1125
+ >
1126
+ <div
1127
+ className="cpicker-trigger"
1128
+ role="button"
1129
+ tabIndex={isFieldReadOnly ? -1 : 0}
1130
+ aria-haspopup="dialog"
1131
+ aria-expanded={colorPickerShow}
1132
+ aria-disabled={isFieldReadOnly}
1133
+ onClick={toggleColorPicker}
1134
+ onKeyDown={(event) => {
1135
+ if (event.key === 'Enter' || event.key === ' ') {
1136
+ event.preventDefault();
1137
+ toggleColorPicker();
1138
+ }
1139
+ }}
1140
+ >
1076
1141
  <div
1077
1142
  className="cpicker__box"
1078
- data-color={inputValue && inputValue !== 'null' ? inputValue : 'No colour selected'}
1143
+ data-color={
1144
+ hasColour ? inputValue : 'No colour selected'
1145
+ }
1079
1146
  style={
1080
- inputValue && inputValue !== 'null'
1147
+ hasColour
1081
1148
  ? { backgroundColor: inputValue }
1082
1149
  : {}
1083
1150
  }
1084
- title={inputValue && inputValue !== 'null' ? `Current colour: ${inputValue}` : 'No colour selected'}
1151
+ title={
1152
+ hasColour
1153
+ ? `Current colour: ${inputValue}`
1154
+ : 'No colour selected'
1155
+ }
1085
1156
  ></div>
1086
1157
  <span className="cpicker-value">
1087
- {inputValue && inputValue !== 'null' ? inputValue : 'No colour selected'}
1158
+ {hasColour ? inputValue : 'No colour selected'}
1088
1159
  </span>
1089
1160
  </div>
1090
- <div className="cpicker-container">
1091
- <Sketch
1092
- color={
1093
- inputValue && inputValue !== 'null'
1094
- ? inputValue
1095
- : '#000000'
1096
- }
1097
- onChange={(color) => {
1098
- onChangeColour(
1099
- { target: { value: color.hex } },
1100
- { hex: color.hex },
1101
- settings.id
1102
- );
1103
- }}
1104
- style={{
1105
- boxShadow: 'none',
1106
- border: 'none'
1107
- }}
1108
- />
1109
- </div>
1161
+ {colorPickerShow && (
1162
+ <div className="cpicker-popover" role="dialog">
1163
+ <Sketch
1164
+ color={hasColour ? inputValue : '#000000'}
1165
+ onChange={(color) => {
1166
+ onChangeColour(
1167
+ { target: { value: color.hex } },
1168
+ { hex: color.hex },
1169
+ settings.id
1170
+ );
1171
+ }}
1172
+ style={{
1173
+ boxShadow: 'none',
1174
+ border: 'none'
1175
+ }}
1176
+ />
1177
+ </div>
1178
+ )}
1110
1179
  </div>
1111
1180
  );
1181
+ }
1112
1182
  case 'copytoclipboard':
1113
1183
  return (
1114
1184
  <>
@@ -83,6 +83,17 @@
83
83
  }
84
84
  }
85
85
 
86
+ // A `rowColours` tint arrives as an inline background on the ROW element,
87
+ // but the banding above paints the cell-wrap CHILD, which sits on top — so
88
+ // on odd rows the stripe swallowed the tint and only even rows showed their
89
+ // urgency colour. A tinted row (marked by rowClassName in DataGrid.jsx)
90
+ // clears its cell-wrap so the row's own colour reads on every row. Must
91
+ // stay AFTER the banding rule: same specificity, order decides. Hover is
92
+ // unaffected — its background is `!important` on the hover target.
93
+ .tintedRow :global(.InovuaReactDataGrid__row-cell-wrap) {
94
+ background: transparent;
95
+ }
96
+
86
97
  .InovuaReactDataGrid__row-hover-target:hover {
87
98
  color: var(--primary-color) !important;
88
99
  background: var(--hover-color, rgb(230, 240, 250)) !important;
@@ -807,6 +807,69 @@ form div:has(.react-toggle) input[type='checkbox'] {
807
807
  }
808
808
  }
809
809
 
810
+ /* Popup (click-to-open) variant of the colour picker — the collapsed
811
+ trigger row plus the floating Sketch popover. Additive: the inline
812
+ rules above are left as-is for any consumer still using them. */
813
+ .cpicker-popup {
814
+ position: relative;
815
+ gap: 0;
816
+ }
817
+
818
+ .cpicker-trigger {
819
+ display: flex;
820
+ align-items: center;
821
+ gap: 8px;
822
+ width: 100%;
823
+ box-sizing: border-box;
824
+ min-height: 42px;
825
+ padding: 0.4rem 0.65rem;
826
+ font-size: 0.85rem;
827
+ color: var(--paragraph-color);
828
+ background: #ffffff;
829
+ border: 1px solid rgba(var(--paragraph-color-rgb, 0, 0, 0), 0.15);
830
+ border-radius: var(--br, 6px);
831
+ cursor: pointer;
832
+ transition: border 0.15s linear;
833
+ }
834
+
835
+ .cpicker-trigger:hover,
836
+ .cpicker-trigger:focus-visible {
837
+ border: 1px solid rgba(var(--paragraph-color-rgb, 0, 0, 0), 0.85);
838
+ outline: none;
839
+ }
840
+
841
+ .cpicker-trigger[aria-disabled='true'] {
842
+ cursor: not-allowed;
843
+ opacity: 0.7;
844
+ }
845
+
846
+ /* The value chip styling only makes sense on the old inline preview —
847
+ inside the trigger it should read as plain input text */
848
+ .cpicker-trigger .cpicker-value {
849
+ background: transparent;
850
+ border: none;
851
+ padding: 0;
852
+ font-weight: 500;
853
+ }
854
+
855
+ .cpicker-popover {
856
+ position: absolute;
857
+ top: calc(100% + 4px);
858
+ left: 0;
859
+ z-index: 1100;
860
+ padding: 8px;
861
+ background: #ffffff;
862
+ border: 1px solid #dee2e6;
863
+ border-radius: 6px;
864
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
865
+ }
866
+
867
+ .cpicker-popover .w-color-sketch {
868
+ box-shadow: none !important;
869
+ border: none !important;
870
+ border-radius: 6px !important;
871
+ }
872
+
810
873
  /* Safari-specific fixes for form elements */
811
874
  @supports (-webkit-touch-callout: none) {
812
875
  /* Safari-specific styles */