@uniformdev/mesh-sdk-react 20.71.2-alpha.7 → 20.72.2-alpha.3

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/dist/index.d.mts CHANGED
@@ -806,7 +806,7 @@ declare function serializeVariablesEditorSerializedState(serializedEditorState:
806
806
  * If newValue is a string, it will be treated as a variable-reference-containing string, and parsed to a Lexical AST
807
807
  * If newValue is a serialized Lexical AST, it will be used as-is. If the AST is invalid, the editor will be set to an empty state.
808
808
  */
809
- declare function setVariablesEditorValue(editor: LexicalEditor, newValue: string | undefined | SerializedEditorState<SerializedLexicalNode>, tag?: string): void;
809
+ declare function setVariablesEditorValue(editor: LexicalEditor, newValue: string | undefined | null | SerializedEditorState<SerializedLexicalNode>, tag?: string): void;
810
810
 
811
811
  declare function variableDefaultTextValue(defaultValue: unknown): string;
812
812
 
@@ -1720,9 +1720,11 @@ type FilterItemProps = {
1720
1720
  initialCriteriaTitle?: string;
1721
1721
  /** How multiple criteria are joined together */
1722
1722
  criteriaGroupOperator?: 'and' | 'or';
1723
+ /** When provided, renders an interactive dropdown on the second row to switch between and/or */
1724
+ onCriteriaGroupOperatorChange?: (op: 'and' | 'or') => void;
1723
1725
  };
1724
1726
  /** A filter item component used to display filter options */
1725
- declare const FilterItem: ({ index, operatorOptions, valueOptions, onFilterOptionChange, onFilterDynamicChange, onOperatorChange, onValueChange, initialCriteriaTitle, criteriaGroupOperator, }: FilterItemProps) => _emotion_react_jsx_runtime.JSX.Element;
1727
+ declare const FilterItem: ({ index, operatorOptions, valueOptions, onFilterOptionChange, onFilterDynamicChange, onOperatorChange, onValueChange, initialCriteriaTitle, criteriaGroupOperator, onCriteriaGroupOperatorChange, }: FilterItemProps) => _emotion_react_jsx_runtime.JSX.Element;
1726
1728
 
1727
1729
  type FilterMapper = Record<string, ComponentType<any> | null>;
1728
1730
  type SearchAndFilterProviderProps = {
@@ -1869,9 +1871,9 @@ type FilterItemsProps = {
1869
1871
  additionalFiltersContainer: SearchAndFilterProps['additionalFiltersContainer'];
1870
1872
  filterTitle?: string;
1871
1873
  resetButtonText?: string;
1872
- } & Pick<FilterItemProps, 'initialCriteriaTitle' | 'criteriaGroupOperator'>;
1874
+ } & Pick<FilterItemProps, 'initialCriteriaTitle' | 'criteriaGroupOperator' | 'onCriteriaGroupOperatorChange'>;
1873
1875
  /** A filter items component used to display filter options */
1874
- declare const FilterItems: ({ addButtonText, additionalFiltersContainer, filterTitle, resetButtonText, initialCriteriaTitle, criteriaGroupOperator, }: FilterItemsProps) => _emotion_react_jsx_runtime.JSX.Element;
1876
+ declare const FilterItems: ({ addButtonText, additionalFiltersContainer, filterTitle, resetButtonText, initialCriteriaTitle, criteriaGroupOperator, onCriteriaGroupOperatorChange, }: FilterItemsProps) => _emotion_react_jsx_runtime.JSX.Element;
1875
1877
 
1876
1878
  type SearchAndFilterOptionsContainerProps = {
1877
1879
  /** The button row to be displayed at the bottom of the container */
package/dist/index.d.ts CHANGED
@@ -806,7 +806,7 @@ declare function serializeVariablesEditorSerializedState(serializedEditorState:
806
806
  * If newValue is a string, it will be treated as a variable-reference-containing string, and parsed to a Lexical AST
807
807
  * If newValue is a serialized Lexical AST, it will be used as-is. If the AST is invalid, the editor will be set to an empty state.
808
808
  */
809
- declare function setVariablesEditorValue(editor: LexicalEditor, newValue: string | undefined | SerializedEditorState<SerializedLexicalNode>, tag?: string): void;
809
+ declare function setVariablesEditorValue(editor: LexicalEditor, newValue: string | undefined | null | SerializedEditorState<SerializedLexicalNode>, tag?: string): void;
810
810
 
811
811
  declare function variableDefaultTextValue(defaultValue: unknown): string;
812
812
 
@@ -1720,9 +1720,11 @@ type FilterItemProps = {
1720
1720
  initialCriteriaTitle?: string;
1721
1721
  /** How multiple criteria are joined together */
1722
1722
  criteriaGroupOperator?: 'and' | 'or';
1723
+ /** When provided, renders an interactive dropdown on the second row to switch between and/or */
1724
+ onCriteriaGroupOperatorChange?: (op: 'and' | 'or') => void;
1723
1725
  };
1724
1726
  /** A filter item component used to display filter options */
1725
- declare const FilterItem: ({ index, operatorOptions, valueOptions, onFilterOptionChange, onFilterDynamicChange, onOperatorChange, onValueChange, initialCriteriaTitle, criteriaGroupOperator, }: FilterItemProps) => _emotion_react_jsx_runtime.JSX.Element;
1727
+ declare const FilterItem: ({ index, operatorOptions, valueOptions, onFilterOptionChange, onFilterDynamicChange, onOperatorChange, onValueChange, initialCriteriaTitle, criteriaGroupOperator, onCriteriaGroupOperatorChange, }: FilterItemProps) => _emotion_react_jsx_runtime.JSX.Element;
1726
1728
 
1727
1729
  type FilterMapper = Record<string, ComponentType<any> | null>;
1728
1730
  type SearchAndFilterProviderProps = {
@@ -1869,9 +1871,9 @@ type FilterItemsProps = {
1869
1871
  additionalFiltersContainer: SearchAndFilterProps['additionalFiltersContainer'];
1870
1872
  filterTitle?: string;
1871
1873
  resetButtonText?: string;
1872
- } & Pick<FilterItemProps, 'initialCriteriaTitle' | 'criteriaGroupOperator'>;
1874
+ } & Pick<FilterItemProps, 'initialCriteriaTitle' | 'criteriaGroupOperator' | 'onCriteriaGroupOperatorChange'>;
1873
1875
  /** A filter items component used to display filter options */
1874
- declare const FilterItems: ({ addButtonText, additionalFiltersContainer, filterTitle, resetButtonText, initialCriteriaTitle, criteriaGroupOperator, }: FilterItemsProps) => _emotion_react_jsx_runtime.JSX.Element;
1876
+ declare const FilterItems: ({ addButtonText, additionalFiltersContainer, filterTitle, resetButtonText, initialCriteriaTitle, criteriaGroupOperator, onCriteriaGroupOperatorChange, }: FilterItemsProps) => _emotion_react_jsx_runtime.JSX.Element;
1875
1877
 
1876
1878
  type SearchAndFilterOptionsContainerProps = {
1877
1879
  /** The button row to be displayed at the bottom of the container */
package/dist/index.esm.js CHANGED
@@ -503,7 +503,7 @@ function refreshVariableNodeMetadata(editor) {
503
503
 
504
504
  // src/components/Variables/util/setVariablesEditorValue.ts
505
505
  function setVariablesEditorValue(editor, newValue, tag) {
506
- if (typeof newValue === "undefined" || typeof newValue === "string") {
506
+ if (newValue === null || typeof newValue === "undefined" || typeof newValue === "string") {
507
507
  const parsedState = editor.parseEditorState(deserializeVariablesEditorState(newValue));
508
508
  editor.setEditorState(parsedState.clone(null), {
509
509
  tag
@@ -685,7 +685,8 @@ import {
685
685
  $isNodeSelection,
686
686
  $setSelection,
687
687
  COMMAND_PRIORITY_NORMAL,
688
- createCommand
688
+ createCommand,
689
+ SKIP_DOM_SELECTION_TAG
689
690
  } from "lexical";
690
691
  import { useCallback as useCallback2, useEffect as useEffect4, useMemo as useMemo6, useRef as useRef3, useState as useState4 } from "react";
691
692
  import { createPortal } from "react-dom";
@@ -1147,6 +1148,11 @@ var DISCONNECT_VARIABLE_COMMAND = createCommand(
1147
1148
  "uniform:disconnect-variable"
1148
1149
  );
1149
1150
  var INSERT_VARIABLE_COMMAND = createCommand("uniform:insert-variable");
1151
+ function isEditorFocused(editor) {
1152
+ var _a;
1153
+ const rootElement = editor.getRootElement();
1154
+ return (_a = rootElement == null ? void 0 : rootElement.contains(rootElement.ownerDocument.activeElement)) != null ? _a : false;
1155
+ }
1150
1156
  var DollarSignVariablesRegex = /(^.*)(\$\$(.{0,20}))$/;
1151
1157
  function getPossibleQueryMatch(text) {
1152
1158
  const match = DollarSignVariablesRegex.exec(text);
@@ -1441,7 +1447,7 @@ function VariablesPlugin({
1441
1447
  replaceValueOnVariableInsert,
1442
1448
  disableVariableDisplayNames
1443
1449
  ]);
1444
- const updateExistingNodeIfChanged = useCallback2(
1450
+ const computeUpdatedVariableNodeState = useCallback2(
1445
1451
  (variableNode) => {
1446
1452
  var _a, _b, _c, _d;
1447
1453
  const targetVar = variablesRef.current.variables[variableNode.reference];
@@ -1449,7 +1455,7 @@ function VariablesPlugin({
1449
1455
  const isLoadingVariables = variablesRef.current.isLoading;
1450
1456
  const currentState = variableNode.getState();
1451
1457
  if (currentState.isFresh && !targetVar && !targetUndefinedVar) {
1452
- return;
1458
+ return void 0;
1453
1459
  }
1454
1460
  const tooltip = (_d = (_c = (_b = (_a = targetVar == null ? void 0 : targetVar.tooltip) != null ? _a : targetVar == null ? void 0 : targetVar.helpText) != null ? _b : targetUndefinedVar == null ? void 0 : targetUndefinedVar.error) != null ? _c : targetUndefinedVar == null ? void 0 : targetUndefinedVar.warning) != null ? _d : targetUndefinedVar == null ? void 0 : targetUndefinedVar.info;
1455
1461
  const displayName = disableVariableDisplayNames ? prettifyBindExpression(variableNode.reference) : (targetVar == null ? void 0 : targetVar.displayName) || (targetUndefinedVar == null ? void 0 : targetUndefinedVar.displayName) || prettifyBindExpression(variableNode.reference);
@@ -1462,26 +1468,50 @@ function VariablesPlugin({
1462
1468
  tooltip,
1463
1469
  isFresh: false
1464
1470
  };
1465
- if (!dequal(currentState, newState)) {
1471
+ return dequal(currentState, newState) ? void 0 : newState;
1472
+ },
1473
+ [canEditVariable, disableVariableDisplayNames]
1474
+ );
1475
+ const updateExistingNodeIfChanged = useCallback2(
1476
+ (variableNode) => {
1477
+ const newState = computeUpdatedVariableNodeState(variableNode);
1478
+ if (newState) {
1466
1479
  variableNode.setState(newState);
1467
1480
  }
1468
1481
  },
1469
- [canEditVariable, disableVariableDisplayNames]
1482
+ [computeUpdatedVariableNodeState]
1470
1483
  );
1471
1484
  useEffect4(() => {
1472
- let selection;
1485
+ const pendingUpdates = [];
1473
1486
  editor.read(() => {
1474
- selection = $getSelection();
1487
+ $dfs().forEach(({ node }) => {
1488
+ if ($isVariableNode(node)) {
1489
+ const newState = computeUpdatedVariableNodeState(node);
1490
+ if (newState) {
1491
+ pendingUpdates.push({ key: node.getKey(), newState });
1492
+ }
1493
+ }
1494
+ });
1475
1495
  });
1496
+ if (pendingUpdates.length === 0) {
1497
+ return;
1498
+ }
1499
+ const shouldSyncDomSelection = isEditorFocused(editor);
1476
1500
  editor.update(() => {
1477
1501
  var _a;
1478
- $addUpdateTag("skip-dom-selection");
1479
- $setSelection((_a = selection == null ? void 0 : selection.clone()) != null ? _a : null);
1480
- $dfs().filter(({ node }) => $isVariableNode(node)).forEach(({ node: variableNode }) => {
1481
- updateExistingNodeIfChanged(variableNode);
1502
+ const selection = $getSelection();
1503
+ pendingUpdates.forEach(({ key, newState }) => {
1504
+ const node = $getNodeByKey(key);
1505
+ if ($isVariableNode(node)) {
1506
+ node.setState(newState);
1507
+ }
1482
1508
  });
1509
+ $setSelection((_a = selection == null ? void 0 : selection.clone()) != null ? _a : null);
1510
+ if (!shouldSyncDomSelection) {
1511
+ $addUpdateTag(SKIP_DOM_SELECTION_TAG);
1512
+ }
1483
1513
  });
1484
- }, [editor, variables, knownUndefinedValues, canEditVariable, updateExistingNodeIfChanged]);
1514
+ }, [editor, variables, knownUndefinedValues, computeUpdatedVariableNodeState]);
1485
1515
  useEffect4(() => {
1486
1516
  return editor.registerNodeTransform(VariableNode, (variableNode) => {
1487
1517
  updateExistingNodeIfChanged(variableNode);
@@ -6253,6 +6283,25 @@ var ResetConditionsBtn = css21`
6253
6283
  color: var(--gray-400);
6254
6284
  }
6255
6285
  `;
6286
+ var CriteriaGroupOperatorTrigger = css21`
6287
+ && {
6288
+ padding: 0;
6289
+ border-radius: var(--rounded-sm);
6290
+ font-size: inherit;
6291
+ line-height: inherit;
6292
+ color: inherit;
6293
+
6294
+ > div {
6295
+ gap: 2px;
6296
+ }
6297
+
6298
+ svg {
6299
+ width: 10px;
6300
+ height: 10px;
6301
+ color: var(--gray-400);
6302
+ }
6303
+ }
6304
+ `;
6256
6305
  var IconBtn = css21`
6257
6306
  align-self: center;
6258
6307
  background: transparent;
@@ -6560,7 +6609,9 @@ var FilterControls = ({
6560
6609
  const [localSearchTerm, setLocalSearchTerm] = useState21(searchTerm);
6561
6610
  useDebounce7(
6562
6611
  () => {
6563
- setSearchTerm(localSearchTerm);
6612
+ if (localSearchTerm !== searchTerm) {
6613
+ setSearchTerm(localSearchTerm);
6614
+ }
6564
6615
  },
6565
6616
  300,
6566
6617
  [localSearchTerm]
@@ -6639,7 +6690,13 @@ var FilterControls = ({
6639
6690
 
6640
6691
  // src/components/SearchAndFilter/FilterItem.tsx
6641
6692
  import { CgTrash as CgTrash2 } from "@react-icons/all-files/cg/CgTrash";
6642
- import { Icon as Icon4, InputComboBox as InputComboBox5 } from "@uniformdev/design-system";
6693
+ import {
6694
+ DropdownStyleMenuTrigger,
6695
+ Icon as Icon4,
6696
+ InputComboBox as InputComboBox5,
6697
+ Menu as Menu3,
6698
+ SelectableMenuItem
6699
+ } from "@uniformdev/design-system";
6643
6700
  import { useMemo as useMemo21 } from "react";
6644
6701
 
6645
6702
  // src/components/SearchAndFilter/util/isFilterBindable.ts
@@ -6659,7 +6716,8 @@ var FilterItem = ({
6659
6716
  onOperatorChange,
6660
6717
  onValueChange,
6661
6718
  initialCriteriaTitle = "Where",
6662
- criteriaGroupOperator = "and"
6719
+ criteriaGroupOperator = "and",
6720
+ onCriteriaGroupOperatorChange
6663
6721
  }) => {
6664
6722
  var _a, _b;
6665
6723
  const { filters, handleDeleteFilter, filterOptions } = useSearchAndFilter();
@@ -6697,8 +6755,45 @@ var FilterItem = ({
6697
6755
  } : {};
6698
6756
  const CustomLeftHandComponent = selectedFieldValue == null ? void 0 : selectedFieldValue.leftHandSideComponentWhenSelected;
6699
6757
  const isEmptyOperator = metaDataPossibleOptions === "empty";
6758
+ const joinLabel = index === 0 ? initialCriteriaTitle : index === 1 && onCriteriaGroupOperatorChange ? /* @__PURE__ */ jsxs34(
6759
+ Menu3,
6760
+ {
6761
+ menuTrigger: /* @__PURE__ */ jsx75(
6762
+ DropdownStyleMenuTrigger,
6763
+ {
6764
+ bgColor: "transparent",
6765
+ css: CriteriaGroupOperatorTrigger,
6766
+ "data-testid": "criteria-group-operator",
6767
+ "aria-label": "criteria group operator",
6768
+ children: criteriaGroupOperator
6769
+ }
6770
+ ),
6771
+ size: "small",
6772
+ placement: "bottom-start",
6773
+ children: [
6774
+ /* @__PURE__ */ jsx75(
6775
+ SelectableMenuItem,
6776
+ {
6777
+ selected: criteriaGroupOperator === "and",
6778
+ onClick: () => onCriteriaGroupOperatorChange("and"),
6779
+ "data-testid": "criteria-group-operator-and",
6780
+ children: "and"
6781
+ }
6782
+ ),
6783
+ /* @__PURE__ */ jsx75(
6784
+ SelectableMenuItem,
6785
+ {
6786
+ selected: criteriaGroupOperator === "or",
6787
+ onClick: () => onCriteriaGroupOperatorChange("or"),
6788
+ "data-testid": "criteria-group-operator-or",
6789
+ children: "or"
6790
+ }
6791
+ )
6792
+ ]
6793
+ }
6794
+ ) : criteriaGroupOperator;
6700
6795
  return /* @__PURE__ */ jsxs34("div", { css: ConditionalFilterRow, "data-testid": "filter-item", children: [
6701
- /* @__PURE__ */ jsx75("span", { children: index === 0 ? initialCriteriaTitle : criteriaGroupOperator }),
6796
+ /* @__PURE__ */ jsx75("span", { children: joinLabel }),
6702
6797
  /* @__PURE__ */ jsxs34("div", { css: [ConditionalInputRow, isEmptyOperator ? ConditionalInputRowEmpty : null], children: [
6703
6798
  CustomLeftHandComponent ? /* @__PURE__ */ jsx75(
6704
6799
  CustomLeftHandComponent,
@@ -6963,7 +7058,8 @@ var FilterItems = ({
6963
7058
  filterTitle,
6964
7059
  resetButtonText,
6965
7060
  initialCriteriaTitle,
6966
- criteriaGroupOperator
7061
+ criteriaGroupOperator,
7062
+ onCriteriaGroupOperatorChange
6967
7063
  }) => {
6968
7064
  const { filterOptions, filters, setFilters, handleAddFilter } = useSearchAndFilter();
6969
7065
  const handleUpdateFilter = (index, prop, value) => {
@@ -7033,7 +7129,8 @@ var FilterItems = ({
7033
7129
  onFilterDynamicChange: (e) => handleUpdateFilter(i, "dynamicField", e),
7034
7130
  valueOptions: possibleValueOptions,
7035
7131
  initialCriteriaTitle,
7036
- criteriaGroupOperator
7132
+ criteriaGroupOperator,
7133
+ onCriteriaGroupOperatorChange
7037
7134
  },
7038
7135
  i
7039
7136
  );
@@ -7625,7 +7722,7 @@ import {
7625
7722
  Label,
7626
7723
  LoadingIndicator as LoadingIndicator4,
7627
7724
  LoadingOverlay,
7628
- Menu as Menu3,
7725
+ Menu as Menu4,
7629
7726
  MenuItem as MenuItem2,
7630
7727
  ParameterGroup,
7631
7728
  ParameterImage,
@@ -7692,7 +7789,7 @@ export {
7692
7789
  LoadingIndicator4 as LoadingIndicator,
7693
7790
  LoadingOverlay,
7694
7791
  MULTI_SELECT_OPERATORS,
7695
- Menu3 as Menu,
7792
+ Menu4 as Menu,
7696
7793
  MenuItem2 as MenuItem,
7697
7794
  MeshApp,
7698
7795
  NUMBER_OPERATORS,
package/dist/index.js CHANGED
@@ -678,7 +678,7 @@ function refreshVariableNodeMetadata(editor) {
678
678
 
679
679
  // src/components/Variables/util/setVariablesEditorValue.ts
680
680
  function setVariablesEditorValue(editor, newValue, tag) {
681
- if (typeof newValue === "undefined" || typeof newValue === "string") {
681
+ if (newValue === null || typeof newValue === "undefined" || typeof newValue === "string") {
682
682
  const parsedState = editor.parseEditorState(deserializeVariablesEditorState(newValue));
683
683
  editor.setEditorState(parsedState.clone(null), {
684
684
  tag
@@ -1293,6 +1293,11 @@ var DISCONNECT_VARIABLE_COMMAND = (0, import_lexical4.createCommand)(
1293
1293
  "uniform:disconnect-variable"
1294
1294
  );
1295
1295
  var INSERT_VARIABLE_COMMAND = (0, import_lexical4.createCommand)("uniform:insert-variable");
1296
+ function isEditorFocused(editor) {
1297
+ var _a;
1298
+ const rootElement = editor.getRootElement();
1299
+ return (_a = rootElement == null ? void 0 : rootElement.contains(rootElement.ownerDocument.activeElement)) != null ? _a : false;
1300
+ }
1296
1301
  var DollarSignVariablesRegex = /(^.*)(\$\$(.{0,20}))$/;
1297
1302
  function getPossibleQueryMatch(text) {
1298
1303
  const match = DollarSignVariablesRegex.exec(text);
@@ -1587,7 +1592,7 @@ function VariablesPlugin({
1587
1592
  replaceValueOnVariableInsert,
1588
1593
  disableVariableDisplayNames
1589
1594
  ]);
1590
- const updateExistingNodeIfChanged = (0, import_react15.useCallback)(
1595
+ const computeUpdatedVariableNodeState = (0, import_react15.useCallback)(
1591
1596
  (variableNode) => {
1592
1597
  var _a, _b, _c, _d;
1593
1598
  const targetVar = variablesRef.current.variables[variableNode.reference];
@@ -1595,7 +1600,7 @@ function VariablesPlugin({
1595
1600
  const isLoadingVariables = variablesRef.current.isLoading;
1596
1601
  const currentState = variableNode.getState();
1597
1602
  if (currentState.isFresh && !targetVar && !targetUndefinedVar) {
1598
- return;
1603
+ return void 0;
1599
1604
  }
1600
1605
  const tooltip = (_d = (_c = (_b = (_a = targetVar == null ? void 0 : targetVar.tooltip) != null ? _a : targetVar == null ? void 0 : targetVar.helpText) != null ? _b : targetUndefinedVar == null ? void 0 : targetUndefinedVar.error) != null ? _c : targetUndefinedVar == null ? void 0 : targetUndefinedVar.warning) != null ? _d : targetUndefinedVar == null ? void 0 : targetUndefinedVar.info;
1601
1606
  const displayName = disableVariableDisplayNames ? prettifyBindExpression(variableNode.reference) : (targetVar == null ? void 0 : targetVar.displayName) || (targetUndefinedVar == null ? void 0 : targetUndefinedVar.displayName) || prettifyBindExpression(variableNode.reference);
@@ -1608,26 +1613,50 @@ function VariablesPlugin({
1608
1613
  tooltip,
1609
1614
  isFresh: false
1610
1615
  };
1611
- if (!(0, import_lite.dequal)(currentState, newState)) {
1616
+ return (0, import_lite.dequal)(currentState, newState) ? void 0 : newState;
1617
+ },
1618
+ [canEditVariable, disableVariableDisplayNames]
1619
+ );
1620
+ const updateExistingNodeIfChanged = (0, import_react15.useCallback)(
1621
+ (variableNode) => {
1622
+ const newState = computeUpdatedVariableNodeState(variableNode);
1623
+ if (newState) {
1612
1624
  variableNode.setState(newState);
1613
1625
  }
1614
1626
  },
1615
- [canEditVariable, disableVariableDisplayNames]
1627
+ [computeUpdatedVariableNodeState]
1616
1628
  );
1617
1629
  (0, import_react15.useEffect)(() => {
1618
- let selection;
1630
+ const pendingUpdates = [];
1619
1631
  editor.read(() => {
1620
- selection = (0, import_lexical4.$getSelection)();
1632
+ (0, import_utils.$dfs)().forEach(({ node }) => {
1633
+ if ($isVariableNode(node)) {
1634
+ const newState = computeUpdatedVariableNodeState(node);
1635
+ if (newState) {
1636
+ pendingUpdates.push({ key: node.getKey(), newState });
1637
+ }
1638
+ }
1639
+ });
1621
1640
  });
1641
+ if (pendingUpdates.length === 0) {
1642
+ return;
1643
+ }
1644
+ const shouldSyncDomSelection = isEditorFocused(editor);
1622
1645
  editor.update(() => {
1623
1646
  var _a;
1624
- (0, import_lexical4.$addUpdateTag)("skip-dom-selection");
1625
- (0, import_lexical4.$setSelection)((_a = selection == null ? void 0 : selection.clone()) != null ? _a : null);
1626
- (0, import_utils.$dfs)().filter(({ node }) => $isVariableNode(node)).forEach(({ node: variableNode }) => {
1627
- updateExistingNodeIfChanged(variableNode);
1647
+ const selection = (0, import_lexical4.$getSelection)();
1648
+ pendingUpdates.forEach(({ key, newState }) => {
1649
+ const node = (0, import_lexical4.$getNodeByKey)(key);
1650
+ if ($isVariableNode(node)) {
1651
+ node.setState(newState);
1652
+ }
1628
1653
  });
1654
+ (0, import_lexical4.$setSelection)((_a = selection == null ? void 0 : selection.clone()) != null ? _a : null);
1655
+ if (!shouldSyncDomSelection) {
1656
+ (0, import_lexical4.$addUpdateTag)(import_lexical4.SKIP_DOM_SELECTION_TAG);
1657
+ }
1629
1658
  });
1630
- }, [editor, variables, knownUndefinedValues, canEditVariable, updateExistingNodeIfChanged]);
1659
+ }, [editor, variables, knownUndefinedValues, computeUpdatedVariableNodeState]);
1631
1660
  (0, import_react15.useEffect)(() => {
1632
1661
  return editor.registerNodeTransform(VariableNode, (variableNode) => {
1633
1662
  updateExistingNodeIfChanged(variableNode);
@@ -6323,6 +6352,25 @@ var ResetConditionsBtn = import_react63.css`
6323
6352
  color: var(--gray-400);
6324
6353
  }
6325
6354
  `;
6355
+ var CriteriaGroupOperatorTrigger = import_react63.css`
6356
+ && {
6357
+ padding: 0;
6358
+ border-radius: var(--rounded-sm);
6359
+ font-size: inherit;
6360
+ line-height: inherit;
6361
+ color: inherit;
6362
+
6363
+ > div {
6364
+ gap: 2px;
6365
+ }
6366
+
6367
+ svg {
6368
+ width: 10px;
6369
+ height: 10px;
6370
+ color: var(--gray-400);
6371
+ }
6372
+ }
6373
+ `;
6326
6374
  var IconBtn = import_react63.css`
6327
6375
  align-self: center;
6328
6376
  background: transparent;
@@ -6622,7 +6670,9 @@ var FilterControls = ({
6622
6670
  const [localSearchTerm, setLocalSearchTerm] = (0, import_react65.useState)(searchTerm);
6623
6671
  (0, import_react_use7.useDebounce)(
6624
6672
  () => {
6625
- setSearchTerm(localSearchTerm);
6673
+ if (localSearchTerm !== searchTerm) {
6674
+ setSearchTerm(localSearchTerm);
6675
+ }
6626
6676
  },
6627
6677
  300,
6628
6678
  [localSearchTerm]
@@ -6721,7 +6771,8 @@ var FilterItem = ({
6721
6771
  onOperatorChange,
6722
6772
  onValueChange,
6723
6773
  initialCriteriaTitle = "Where",
6724
- criteriaGroupOperator = "and"
6774
+ criteriaGroupOperator = "and",
6775
+ onCriteriaGroupOperatorChange
6725
6776
  }) => {
6726
6777
  var _a, _b;
6727
6778
  const { filters, handleDeleteFilter, filterOptions } = useSearchAndFilter();
@@ -6759,8 +6810,45 @@ var FilterItem = ({
6759
6810
  } : {};
6760
6811
  const CustomLeftHandComponent = selectedFieldValue == null ? void 0 : selectedFieldValue.leftHandSideComponentWhenSelected;
6761
6812
  const isEmptyOperator = metaDataPossibleOptions === "empty";
6813
+ const joinLabel = index === 0 ? initialCriteriaTitle : index === 1 && onCriteriaGroupOperatorChange ? /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)(
6814
+ import_design_system45.Menu,
6815
+ {
6816
+ menuTrigger: /* @__PURE__ */ (0, import_jsx_runtime74.jsx)(
6817
+ import_design_system45.DropdownStyleMenuTrigger,
6818
+ {
6819
+ bgColor: "transparent",
6820
+ css: CriteriaGroupOperatorTrigger,
6821
+ "data-testid": "criteria-group-operator",
6822
+ "aria-label": "criteria group operator",
6823
+ children: criteriaGroupOperator
6824
+ }
6825
+ ),
6826
+ size: "small",
6827
+ placement: "bottom-start",
6828
+ children: [
6829
+ /* @__PURE__ */ (0, import_jsx_runtime74.jsx)(
6830
+ import_design_system45.SelectableMenuItem,
6831
+ {
6832
+ selected: criteriaGroupOperator === "and",
6833
+ onClick: () => onCriteriaGroupOperatorChange("and"),
6834
+ "data-testid": "criteria-group-operator-and",
6835
+ children: "and"
6836
+ }
6837
+ ),
6838
+ /* @__PURE__ */ (0, import_jsx_runtime74.jsx)(
6839
+ import_design_system45.SelectableMenuItem,
6840
+ {
6841
+ selected: criteriaGroupOperator === "or",
6842
+ onClick: () => onCriteriaGroupOperatorChange("or"),
6843
+ "data-testid": "criteria-group-operator-or",
6844
+ children: "or"
6845
+ }
6846
+ )
6847
+ ]
6848
+ }
6849
+ ) : criteriaGroupOperator;
6762
6850
  return /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("div", { css: ConditionalFilterRow, "data-testid": "filter-item", children: [
6763
- /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { children: index === 0 ? initialCriteriaTitle : criteriaGroupOperator }),
6851
+ /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { children: joinLabel }),
6764
6852
  /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("div", { css: [ConditionalInputRow, isEmptyOperator ? ConditionalInputRowEmpty : null], children: [
6765
6853
  CustomLeftHandComponent ? /* @__PURE__ */ (0, import_jsx_runtime74.jsx)(
6766
6854
  CustomLeftHandComponent,
@@ -7025,7 +7113,8 @@ var FilterItems = ({
7025
7113
  filterTitle,
7026
7114
  resetButtonText,
7027
7115
  initialCriteriaTitle,
7028
- criteriaGroupOperator
7116
+ criteriaGroupOperator,
7117
+ onCriteriaGroupOperatorChange
7029
7118
  }) => {
7030
7119
  const { filterOptions, filters, setFilters, handleAddFilter } = useSearchAndFilter();
7031
7120
  const handleUpdateFilter = (index, prop, value) => {
@@ -7095,7 +7184,8 @@ var FilterItems = ({
7095
7184
  onFilterDynamicChange: (e) => handleUpdateFilter(i, "dynamicField", e),
7096
7185
  valueOptions: possibleValueOptions,
7097
7186
  initialCriteriaTitle,
7098
- criteriaGroupOperator
7187
+ criteriaGroupOperator,
7188
+ onCriteriaGroupOperatorChange
7099
7189
  },
7100
7190
  i
7101
7191
  );
package/dist/index.mjs CHANGED
@@ -503,7 +503,7 @@ function refreshVariableNodeMetadata(editor) {
503
503
 
504
504
  // src/components/Variables/util/setVariablesEditorValue.ts
505
505
  function setVariablesEditorValue(editor, newValue, tag) {
506
- if (typeof newValue === "undefined" || typeof newValue === "string") {
506
+ if (newValue === null || typeof newValue === "undefined" || typeof newValue === "string") {
507
507
  const parsedState = editor.parseEditorState(deserializeVariablesEditorState(newValue));
508
508
  editor.setEditorState(parsedState.clone(null), {
509
509
  tag
@@ -685,7 +685,8 @@ import {
685
685
  $isNodeSelection,
686
686
  $setSelection,
687
687
  COMMAND_PRIORITY_NORMAL,
688
- createCommand
688
+ createCommand,
689
+ SKIP_DOM_SELECTION_TAG
689
690
  } from "lexical";
690
691
  import { useCallback as useCallback2, useEffect as useEffect4, useMemo as useMemo6, useRef as useRef3, useState as useState4 } from "react";
691
692
  import { createPortal } from "react-dom";
@@ -1147,6 +1148,11 @@ var DISCONNECT_VARIABLE_COMMAND = createCommand(
1147
1148
  "uniform:disconnect-variable"
1148
1149
  );
1149
1150
  var INSERT_VARIABLE_COMMAND = createCommand("uniform:insert-variable");
1151
+ function isEditorFocused(editor) {
1152
+ var _a;
1153
+ const rootElement = editor.getRootElement();
1154
+ return (_a = rootElement == null ? void 0 : rootElement.contains(rootElement.ownerDocument.activeElement)) != null ? _a : false;
1155
+ }
1150
1156
  var DollarSignVariablesRegex = /(^.*)(\$\$(.{0,20}))$/;
1151
1157
  function getPossibleQueryMatch(text) {
1152
1158
  const match = DollarSignVariablesRegex.exec(text);
@@ -1441,7 +1447,7 @@ function VariablesPlugin({
1441
1447
  replaceValueOnVariableInsert,
1442
1448
  disableVariableDisplayNames
1443
1449
  ]);
1444
- const updateExistingNodeIfChanged = useCallback2(
1450
+ const computeUpdatedVariableNodeState = useCallback2(
1445
1451
  (variableNode) => {
1446
1452
  var _a, _b, _c, _d;
1447
1453
  const targetVar = variablesRef.current.variables[variableNode.reference];
@@ -1449,7 +1455,7 @@ function VariablesPlugin({
1449
1455
  const isLoadingVariables = variablesRef.current.isLoading;
1450
1456
  const currentState = variableNode.getState();
1451
1457
  if (currentState.isFresh && !targetVar && !targetUndefinedVar) {
1452
- return;
1458
+ return void 0;
1453
1459
  }
1454
1460
  const tooltip = (_d = (_c = (_b = (_a = targetVar == null ? void 0 : targetVar.tooltip) != null ? _a : targetVar == null ? void 0 : targetVar.helpText) != null ? _b : targetUndefinedVar == null ? void 0 : targetUndefinedVar.error) != null ? _c : targetUndefinedVar == null ? void 0 : targetUndefinedVar.warning) != null ? _d : targetUndefinedVar == null ? void 0 : targetUndefinedVar.info;
1455
1461
  const displayName = disableVariableDisplayNames ? prettifyBindExpression(variableNode.reference) : (targetVar == null ? void 0 : targetVar.displayName) || (targetUndefinedVar == null ? void 0 : targetUndefinedVar.displayName) || prettifyBindExpression(variableNode.reference);
@@ -1462,26 +1468,50 @@ function VariablesPlugin({
1462
1468
  tooltip,
1463
1469
  isFresh: false
1464
1470
  };
1465
- if (!dequal(currentState, newState)) {
1471
+ return dequal(currentState, newState) ? void 0 : newState;
1472
+ },
1473
+ [canEditVariable, disableVariableDisplayNames]
1474
+ );
1475
+ const updateExistingNodeIfChanged = useCallback2(
1476
+ (variableNode) => {
1477
+ const newState = computeUpdatedVariableNodeState(variableNode);
1478
+ if (newState) {
1466
1479
  variableNode.setState(newState);
1467
1480
  }
1468
1481
  },
1469
- [canEditVariable, disableVariableDisplayNames]
1482
+ [computeUpdatedVariableNodeState]
1470
1483
  );
1471
1484
  useEffect4(() => {
1472
- let selection;
1485
+ const pendingUpdates = [];
1473
1486
  editor.read(() => {
1474
- selection = $getSelection();
1487
+ $dfs().forEach(({ node }) => {
1488
+ if ($isVariableNode(node)) {
1489
+ const newState = computeUpdatedVariableNodeState(node);
1490
+ if (newState) {
1491
+ pendingUpdates.push({ key: node.getKey(), newState });
1492
+ }
1493
+ }
1494
+ });
1475
1495
  });
1496
+ if (pendingUpdates.length === 0) {
1497
+ return;
1498
+ }
1499
+ const shouldSyncDomSelection = isEditorFocused(editor);
1476
1500
  editor.update(() => {
1477
1501
  var _a;
1478
- $addUpdateTag("skip-dom-selection");
1479
- $setSelection((_a = selection == null ? void 0 : selection.clone()) != null ? _a : null);
1480
- $dfs().filter(({ node }) => $isVariableNode(node)).forEach(({ node: variableNode }) => {
1481
- updateExistingNodeIfChanged(variableNode);
1502
+ const selection = $getSelection();
1503
+ pendingUpdates.forEach(({ key, newState }) => {
1504
+ const node = $getNodeByKey(key);
1505
+ if ($isVariableNode(node)) {
1506
+ node.setState(newState);
1507
+ }
1482
1508
  });
1509
+ $setSelection((_a = selection == null ? void 0 : selection.clone()) != null ? _a : null);
1510
+ if (!shouldSyncDomSelection) {
1511
+ $addUpdateTag(SKIP_DOM_SELECTION_TAG);
1512
+ }
1483
1513
  });
1484
- }, [editor, variables, knownUndefinedValues, canEditVariable, updateExistingNodeIfChanged]);
1514
+ }, [editor, variables, knownUndefinedValues, computeUpdatedVariableNodeState]);
1485
1515
  useEffect4(() => {
1486
1516
  return editor.registerNodeTransform(VariableNode, (variableNode) => {
1487
1517
  updateExistingNodeIfChanged(variableNode);
@@ -6253,6 +6283,25 @@ var ResetConditionsBtn = css21`
6253
6283
  color: var(--gray-400);
6254
6284
  }
6255
6285
  `;
6286
+ var CriteriaGroupOperatorTrigger = css21`
6287
+ && {
6288
+ padding: 0;
6289
+ border-radius: var(--rounded-sm);
6290
+ font-size: inherit;
6291
+ line-height: inherit;
6292
+ color: inherit;
6293
+
6294
+ > div {
6295
+ gap: 2px;
6296
+ }
6297
+
6298
+ svg {
6299
+ width: 10px;
6300
+ height: 10px;
6301
+ color: var(--gray-400);
6302
+ }
6303
+ }
6304
+ `;
6256
6305
  var IconBtn = css21`
6257
6306
  align-self: center;
6258
6307
  background: transparent;
@@ -6560,7 +6609,9 @@ var FilterControls = ({
6560
6609
  const [localSearchTerm, setLocalSearchTerm] = useState21(searchTerm);
6561
6610
  useDebounce7(
6562
6611
  () => {
6563
- setSearchTerm(localSearchTerm);
6612
+ if (localSearchTerm !== searchTerm) {
6613
+ setSearchTerm(localSearchTerm);
6614
+ }
6564
6615
  },
6565
6616
  300,
6566
6617
  [localSearchTerm]
@@ -6639,7 +6690,13 @@ var FilterControls = ({
6639
6690
 
6640
6691
  // src/components/SearchAndFilter/FilterItem.tsx
6641
6692
  import { CgTrash as CgTrash2 } from "@react-icons/all-files/cg/CgTrash";
6642
- import { Icon as Icon4, InputComboBox as InputComboBox5 } from "@uniformdev/design-system";
6693
+ import {
6694
+ DropdownStyleMenuTrigger,
6695
+ Icon as Icon4,
6696
+ InputComboBox as InputComboBox5,
6697
+ Menu as Menu3,
6698
+ SelectableMenuItem
6699
+ } from "@uniformdev/design-system";
6643
6700
  import { useMemo as useMemo21 } from "react";
6644
6701
 
6645
6702
  // src/components/SearchAndFilter/util/isFilterBindable.ts
@@ -6659,7 +6716,8 @@ var FilterItem = ({
6659
6716
  onOperatorChange,
6660
6717
  onValueChange,
6661
6718
  initialCriteriaTitle = "Where",
6662
- criteriaGroupOperator = "and"
6719
+ criteriaGroupOperator = "and",
6720
+ onCriteriaGroupOperatorChange
6663
6721
  }) => {
6664
6722
  var _a, _b;
6665
6723
  const { filters, handleDeleteFilter, filterOptions } = useSearchAndFilter();
@@ -6697,8 +6755,45 @@ var FilterItem = ({
6697
6755
  } : {};
6698
6756
  const CustomLeftHandComponent = selectedFieldValue == null ? void 0 : selectedFieldValue.leftHandSideComponentWhenSelected;
6699
6757
  const isEmptyOperator = metaDataPossibleOptions === "empty";
6758
+ const joinLabel = index === 0 ? initialCriteriaTitle : index === 1 && onCriteriaGroupOperatorChange ? /* @__PURE__ */ jsxs34(
6759
+ Menu3,
6760
+ {
6761
+ menuTrigger: /* @__PURE__ */ jsx75(
6762
+ DropdownStyleMenuTrigger,
6763
+ {
6764
+ bgColor: "transparent",
6765
+ css: CriteriaGroupOperatorTrigger,
6766
+ "data-testid": "criteria-group-operator",
6767
+ "aria-label": "criteria group operator",
6768
+ children: criteriaGroupOperator
6769
+ }
6770
+ ),
6771
+ size: "small",
6772
+ placement: "bottom-start",
6773
+ children: [
6774
+ /* @__PURE__ */ jsx75(
6775
+ SelectableMenuItem,
6776
+ {
6777
+ selected: criteriaGroupOperator === "and",
6778
+ onClick: () => onCriteriaGroupOperatorChange("and"),
6779
+ "data-testid": "criteria-group-operator-and",
6780
+ children: "and"
6781
+ }
6782
+ ),
6783
+ /* @__PURE__ */ jsx75(
6784
+ SelectableMenuItem,
6785
+ {
6786
+ selected: criteriaGroupOperator === "or",
6787
+ onClick: () => onCriteriaGroupOperatorChange("or"),
6788
+ "data-testid": "criteria-group-operator-or",
6789
+ children: "or"
6790
+ }
6791
+ )
6792
+ ]
6793
+ }
6794
+ ) : criteriaGroupOperator;
6700
6795
  return /* @__PURE__ */ jsxs34("div", { css: ConditionalFilterRow, "data-testid": "filter-item", children: [
6701
- /* @__PURE__ */ jsx75("span", { children: index === 0 ? initialCriteriaTitle : criteriaGroupOperator }),
6796
+ /* @__PURE__ */ jsx75("span", { children: joinLabel }),
6702
6797
  /* @__PURE__ */ jsxs34("div", { css: [ConditionalInputRow, isEmptyOperator ? ConditionalInputRowEmpty : null], children: [
6703
6798
  CustomLeftHandComponent ? /* @__PURE__ */ jsx75(
6704
6799
  CustomLeftHandComponent,
@@ -6963,7 +7058,8 @@ var FilterItems = ({
6963
7058
  filterTitle,
6964
7059
  resetButtonText,
6965
7060
  initialCriteriaTitle,
6966
- criteriaGroupOperator
7061
+ criteriaGroupOperator,
7062
+ onCriteriaGroupOperatorChange
6967
7063
  }) => {
6968
7064
  const { filterOptions, filters, setFilters, handleAddFilter } = useSearchAndFilter();
6969
7065
  const handleUpdateFilter = (index, prop, value) => {
@@ -7033,7 +7129,8 @@ var FilterItems = ({
7033
7129
  onFilterDynamicChange: (e) => handleUpdateFilter(i, "dynamicField", e),
7034
7130
  valueOptions: possibleValueOptions,
7035
7131
  initialCriteriaTitle,
7036
- criteriaGroupOperator
7132
+ criteriaGroupOperator,
7133
+ onCriteriaGroupOperatorChange
7037
7134
  },
7038
7135
  i
7039
7136
  );
@@ -7625,7 +7722,7 @@ import {
7625
7722
  Label,
7626
7723
  LoadingIndicator as LoadingIndicator4,
7627
7724
  LoadingOverlay,
7628
- Menu as Menu3,
7725
+ Menu as Menu4,
7629
7726
  MenuItem as MenuItem2,
7630
7727
  ParameterGroup,
7631
7728
  ParameterImage,
@@ -7692,7 +7789,7 @@ export {
7692
7789
  LoadingIndicator4 as LoadingIndicator,
7693
7790
  LoadingOverlay,
7694
7791
  MULTI_SELECT_OPERATORS,
7695
- Menu3 as Menu,
7792
+ Menu4 as Menu,
7696
7793
  MenuItem2 as MenuItem,
7697
7794
  MeshApp,
7698
7795
  NUMBER_OPERATORS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniformdev/mesh-sdk-react",
3
- "version": "20.71.2-alpha.7+2ec20a4ae2",
3
+ "version": "20.72.2-alpha.3+a1f072e7b4",
4
4
  "description": "Uniform Mesh Framework SDK for React",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "main": "./dist/index.js",
@@ -50,10 +50,10 @@
50
50
  "@lexical/selection": "0.45.0",
51
51
  "@lexical/utils": "0.45.0",
52
52
  "@react-icons/all-files": "https://github.com/react-icons/react-icons/releases/download/v5.5.0/react-icons-all-files-5.5.0.tgz",
53
- "@uniformdev/canvas": "20.71.2-alpha.7+2ec20a4ae2",
54
- "@uniformdev/design-system": "20.71.2-alpha.7+2ec20a4ae2",
55
- "@uniformdev/mesh-sdk": "20.71.2-alpha.7+2ec20a4ae2",
56
- "@uniformdev/richtext": "20.71.2-alpha.7+2ec20a4ae2",
53
+ "@uniformdev/canvas": "20.72.2-alpha.3+a1f072e7b4",
54
+ "@uniformdev/design-system": "20.72.2-alpha.3+a1f072e7b4",
55
+ "@uniformdev/mesh-sdk": "20.72.2-alpha.3+a1f072e7b4",
56
+ "@uniformdev/richtext": "20.72.2-alpha.3+a1f072e7b4",
57
57
  "dequal": "^2.0.3",
58
58
  "lexical": "0.45.0",
59
59
  "mitt": "3.0.1",
@@ -85,5 +85,5 @@
85
85
  "publishConfig": {
86
86
  "access": "public"
87
87
  },
88
- "gitHead": "2ec20a4ae2d167b6e4e6ad47463b90fb16b73c47"
88
+ "gitHead": "a1f072e7b42ac4b3c694ebdd2e75216efd74a9c2"
89
89
  }