@visns-studio/visns-components 6.31.0 → 6.31.2

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/README.md CHANGED
@@ -9,6 +9,19 @@ A comprehensive React component library used by the VISNS Studio team for CRM an
9
9
 
10
10
  VISNS Components is a React-based UI component library that provides a set of reusable, consistent, and customizable components for building web applications. It includes components for authentication, data grids, forms, navigation, and more, designed to work seamlessly together.
11
11
 
12
+ ## Recent Updates (v6.31.2)
13
+
14
+ ### `MergeEntity` compares the two records it is actually merging
15
+
16
+ `processTemplate` substituted `{dataId}` with the current (source) entity
17
+ before applying the caller's own values, so `loadTargetEntity(entityId)`
18
+ fetched the source a second time. Step 3 then diffed the source against
19
+ itself, always reported "no conflicts", and sent an empty `field_overrides`
20
+ map with the merge. Explicit template values now win and `{dataId}` only
21
+ falls back to the current entity when nothing was supplied. Every consumer's
22
+ merge tab (contacts, clients, …) gets a real conflict step without a config
23
+ change.
24
+
12
25
  ## Recent Updates (v6.31.0)
13
26
 
14
27
  ### The header badge self-heals too, and the pop takes its timings from the server
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.31.0",
96
+ "version": "6.31.2",
97
97
  "description": "Various packages to assist in the development of our Custom Applications.",
98
98
  "main": "src/index.js",
99
99
  "files": [
@@ -174,13 +174,16 @@ const MergeEntity = ({ endpoints, config, dataId, onUpdate = () => {} }) => {
174
174
  const [showAdvanced, setShowAdvanced] = useState(false);
175
175
 
176
176
  // Replace placeholders in URLs
177
+ // Explicit values win: `{dataId}` only falls back to the current entity
178
+ // when the caller did not supply one (loadTargetEntity passes the target's
179
+ // id, and must not be given the source again).
177
180
  const processTemplate = (template, data = {}) => {
178
181
  let processed = template;
179
- processed = processed.replace(/\{dataId\}/g, dataId);
180
182
  Object.keys(data).forEach((key) => {
181
183
  const placeholder = new RegExp(`\\{${key}\\}`, 'g');
182
184
  processed = processed.replace(placeholder, data[key]);
183
185
  });
186
+ processed = processed.replace(/\{dataId\}/g, dataId);
184
187
  return processed;
185
188
  };
186
189
 
@@ -766,6 +766,8 @@ body.tablet-mode .InovuaReactDataGrid__cell.cell-word-wrap > div {
766
766
 
767
767
  /* -- column filter row ---------------------------------------------------- */
768
768
 
769
+ /* Native controls in the filter row (the <select> filters, and any bare
770
+ <input> that is not one of the toolkit's composite fields below). */
769
771
  .InovuaReactDataGrid__column-header__filter input,
770
772
  .InovuaReactDataGrid__column-header__filter select {
771
773
  height: 28px !important;
@@ -778,18 +780,130 @@ body.tablet-mode .InovuaReactDataGrid__cell.cell-word-wrap > div {
778
780
  font-size: 0.75rem !important;
779
781
  }
780
782
 
781
- /* The toolkit WRAPPER around that input drew its own box too: a 1px #cdcecd
782
- border at 1px radius, with the styled 6px-radius input sitting inside it.
783
- Two nested borders a pixel apart, which is what made the filter row read as
784
- one enormous raw text box spanning the column rather than as a filter.
785
-
786
- Only the input keeps an edge. The wrapper is a layout box. */
783
+ /* TEXT and NUMERIC filters are composite fields: the toolkit renders a
784
+ wrapper holding the <input> AND, once there is a value, a clear cross (and
785
+ for numbers a pair of spin arrows) as siblings of it. An earlier version of
786
+ this file put the edge on the <input> and stripped the wrapper, which
787
+ looked right only while the field was empty — the moment a value went in,
788
+ the cross appeared OUTSIDE the bordered box, floating beside a shrunken
789
+ input.
790
+
791
+ So they are built the same way as the combo box below: the wrapper is the
792
+ 28px bordered field, and everything inside it — input, cross, arrows — is
793
+ borderless furniture laid out along its 26px content box. */
787
794
  .InovuaReactDataGrid__column-header__filter.inovua-react-toolkit-text-input,
788
795
  .InovuaReactDataGrid__column-header__filter.inovua-react-toolkit-numeric-input {
796
+ display: inline-flex !important;
797
+ align-items: center !important;
798
+ height: 28px !important;
799
+ min-height: 28px !important;
800
+ max-height: 28px !important;
801
+ padding: 0 !important;
802
+ border: 1px solid var(--dgx-line) !important;
803
+ border-radius: 6px !important;
804
+ background: var(--dgx-surface) !important;
805
+ box-shadow: none !important;
806
+ overflow: hidden !important;
807
+ transition:
808
+ border-color 0.15s ease,
809
+ box-shadow 0.15s ease !important;
810
+ }
811
+
812
+ /* Same focus treatment as the native date filter beside it. */
813
+ .InovuaReactDataGrid__column-header__filter.inovua-react-toolkit-text-input--focused,
814
+ .InovuaReactDataGrid__column-header__filter.inovua-react-toolkit-numeric-input--focused {
815
+ border-color: #7986cb !important;
816
+ box-shadow: 0 0 0 2px rgba(121, 134, 203, 0.4) !important;
817
+ }
818
+
819
+ /* The inner <input> gives up the edge the generic rule above hands every
820
+ filter input: it is the typing area of the field, not a field of its own. */
821
+ .InovuaReactDataGrid__column-header__filter.inovua-react-toolkit-text-input
822
+ .inovua-react-toolkit-text-input__input,
823
+ .InovuaReactDataGrid__column-header__filter.inovua-react-toolkit-numeric-input
824
+ .inovua-react-toolkit-numeric-input__input {
825
+ flex: 1 1 auto !important;
826
+ min-width: 0 !important;
827
+ height: 100% !important;
828
+ min-height: 0 !important;
829
+ margin: 0 !important;
830
+ padding: 0 0.5rem !important;
789
831
  border: 0 !important;
790
832
  border-radius: 0 !important;
791
833
  background: none !important;
792
834
  box-shadow: none !important;
835
+ outline: none !important;
836
+ }
837
+
838
+ /* The numeric wrapper wraps its input in one more box; let it fill. */
839
+ .InovuaReactDataGrid__column-header__filter.inovua-react-toolkit-numeric-input
840
+ .inovua-react-toolkit-numeric-input__number-input {
841
+ flex: 1 1 auto !important;
842
+ min-width: 0 !important;
843
+ height: 100% !important;
844
+ }
845
+
846
+ /* The clear cross: a fixed-width tool at the trailing edge, inside the box.
847
+ The vendored `flex: 1` would have let it grow to half the field. */
848
+ .InovuaReactDataGrid__column-header__filter.inovua-react-toolkit-text-input
849
+ .inovua-react-toolkit-text-input__clear-button-wrapper,
850
+ .InovuaReactDataGrid__column-header__filter.inovua-react-toolkit-numeric-input
851
+ .inovua-react-toolkit-numeric-input__clear-button-wrapper {
852
+ flex: 0 0 auto !important;
853
+ display: flex !important;
854
+ align-items: center !important;
855
+ height: 100% !important;
856
+ margin: 0 !important;
857
+ }
858
+
859
+ .InovuaReactDataGrid__column-header__filter.inovua-react-toolkit-text-input
860
+ .inovua-react-toolkit-text-input__clear-button,
861
+ .InovuaReactDataGrid__column-header__filter.inovua-react-toolkit-numeric-input
862
+ .inovua-react-toolkit-numeric-input__clear-button {
863
+ flex: 0 0 auto !important;
864
+ width: 22px !important;
865
+ height: 100% !important;
866
+ margin: 0 2px 0 0 !important;
867
+ padding: 0 !important;
868
+ stroke: var(--dgx-muted) !important;
869
+ border-radius: 4px !important;
870
+ }
871
+
872
+ .InovuaReactDataGrid__column-header__filter.inovua-react-toolkit-text-input
873
+ .inovua-react-toolkit-text-input__clear-button:hover,
874
+ .InovuaReactDataGrid__column-header__filter.inovua-react-toolkit-numeric-input
875
+ .inovua-react-toolkit-numeric-input__clear-button:hover {
876
+ stroke: var(--dgx-ink) !important;
877
+ background: var(--dgx-hover) !important;
878
+ }
879
+
880
+ /* The number spinner: the vendored 30px column overflowed the 26px content
881
+ box and its arrows were painted outside the field. Pin it to the field's
882
+ height, at the trailing edge, and let the hidden variant take no room. */
883
+ .InovuaReactDataGrid__column-header__filter.inovua-react-toolkit-numeric-input
884
+ .inovua-react-toolkit-numeric-input__spinner-wrapper {
885
+ flex: 0 0 auto !important;
886
+ width: 18px !important;
887
+ height: 100% !important;
888
+ min-height: 0 !important;
889
+ margin: 0 !important;
890
+ fill: var(--dgx-muted) !important;
891
+ }
892
+
893
+ .InovuaReactDataGrid__column-header__filter.inovua-react-toolkit-numeric-input
894
+ .inovua-react-toolkit-numeric-input__spinner-wrapper-hidden {
895
+ display: none !important;
896
+ }
897
+
898
+ .InovuaReactDataGrid__column-header__filter.inovua-react-toolkit-numeric-input
899
+ .inovua-react-toolkit-numeric-input__spinner-arrow {
900
+ padding: 0 3px !important;
901
+ }
902
+
903
+ .InovuaReactDataGrid__column-header__filter.inovua-react-toolkit-numeric-input
904
+ .inovua-react-toolkit-numeric-input__spinner-arrow:hover {
905
+ fill: var(--dgx-ink) !important;
906
+ background: var(--dgx-hover) !important;
793
907
  }
794
908
 
795
909
  /* A COMBO BOX is built the other way round, and it matters.
@@ -308,10 +308,33 @@ input[type]:not([type='search']):not([type='url']):not([type='hidden']):not(
308
308
  width: 100%;
309
309
  line-height: 1;
310
310
  font-size: 0.85rem;
311
+ }
311
312
 
312
- &:focus {
313
- border: 1px solid var(--primary-color);
314
- }
313
+ /* The focus border, as its own rule and NOT nested under the list above.
314
+
315
+ Nested `&:focus` is `:is(<the whole list>):focus`, and `:is()` takes the
316
+ specificity of its MOST specific argument — the twenty-clause `input` chain.
317
+ So the focus border on a plain `<select>` carried a specificity no consumer
318
+ stylesheet could reach without `!important`: a select styled as a borderless
319
+ segment of a custom control grew a full navy box the moment it was focused.
320
+ Stated per element, `select:focus` is (0,1,1) again and a consumer's own
321
+ rule wins as it should. The input chain keeps the specificity it always had. */
322
+ textarea:focus,
323
+ select:focus,
324
+ input[type]:not([type='search']):not([type='url']):not([type='hidden']):not(
325
+ [type='checkbox']
326
+ ):not([type='radio']):not(.inovua-react-toolkit-numeric-input__input):not(
327
+ .inovua-react-toolkit-combo-box__input
328
+ ):not(.inovua-react-toolkit-date-input__input):not(
329
+ .inovua-react-toolkit-text-input__input
330
+ ):not(.inovua-react-toolkit-numeric-input__input):not(
331
+ .variable-inserter-search-input
332
+ ):not(.react-datepicker__month-select):not(
333
+ .react-datepicker__year-select
334
+ ):not(.visns-native-date-filter__input):not([type='date']):not(
335
+ [type='datetime-local']
336
+ ):not([type='time']):not([class*='searchInput']):not([class*='filterInput']):not([class*='rowActionPickerSearch']):focus {
337
+ border: 1px solid var(--primary-color);
315
338
  }
316
339
 
317
340
  /* THE DROPDOWN ARROW, restored. The rule further up gives `select` its