react-table#actions TypeScript Examples

The following examples show how to use react-table#actions. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Grouping.tsx    From viewer-components-react with MIT License 4 votes vote down vote up
Groupings = ({ mapping, goBack }: GroupsTreeProps) => {
  const iModelConnection = useActiveIModelConnection() as IModelConnection;
  const apiContext = useContext(ApiContext);
  const iModelId = useActiveIModelConnection()?.iModelId as string;
  const [showDeleteModal, setShowDeleteModal] = useState<boolean>(false);
  const [isLoading, setIsLoading] = useState<boolean>(true);
  const [groupsView, setGroupsView] = useState<GroupsView>(GroupsView.GROUPS);
  const [selectedGroup, setSelectedGroup] = useState<GroupType | undefined>(
    undefined,
  );
  const hilitedElements = useRef<Map<string, string[]>>(new Map());
  const [selectedRows, setSelectedRows] = useState<Record<string, boolean>>({});
  const [isLoadingQuery, setLoadingQuery] = useState<boolean>(false);
  const [groups, setGroups] = useState<Group[]>([]);

  useEffect(() => {
    void fetchGroups(setGroups, iModelId, mapping.id ?? "", setIsLoading, apiContext);
  }, [apiContext, iModelId, mapping.id, setIsLoading]);

  const refresh = useCallback(async () => {
    setGroupsView(GroupsView.GROUPS);
    setSelectedGroup(undefined);
    setGroups([]);
    await fetchGroups(setGroups, iModelId, mapping.id ?? "", setIsLoading, apiContext);
  }, [apiContext, iModelId, mapping.id, setGroups]);

  const addGroup = () => {
    // TODO Retain selection in view without emphasizes. Goal is to make it so we can distinguish
    // hilited elements from regular elements without emphasis due to it blocking selection. For now clearing
    // selection.
    clearEmphasizedElements();
    setGroupsView(GroupsView.ADD);
  };

  const onModify = useCallback((value) => {
    clearEmphasizedElements();
    setSelectedGroup(value.row.original);
    setGroupsView(GroupsView.MODIFYING);
  }, []);

  const openProperties = useCallback((value) => {
    clearEmphasizedElements();
    setSelectedGroup(value.row.original);
    setGroupsView(GroupsView.PROPERTIES);
  }, []);

  const groupsColumns = useMemo(
    () => [
      {
        Header: "Table",
        columns: [
          {
            id: "groupName",
            Header: "Group",
            accessor: "groupName",
            Cell: (value: CellProps<GroupType>) => (
              <>
                {isLoadingQuery ? (
                  value.row.original.groupName
                ) : (
                  <div
                    className='iui-anchor'
                    onClick={(e) => {
                      e.stopPropagation();
                      openProperties(value);
                    }}
                  >
                    {value.row.original.groupName}
                  </div>
                )}
              </>
            ),
          },
          {
            id: "description",
            Header: "Description",
            accessor: "description",
          },
          {
            id: "dropdown",
            Header: "",
            width: 80,
            Cell: (value: CellProps<GroupType>) => {
              return (
                <div onClick={(e) => e.stopPropagation()}>
                  <DropdownMenu
                    disabled={isLoadingQuery}
                    menuItems={(close: () => void) => [
                      <MenuItem
                        key={0}
                        onClick={() => onModify(value)}
                        icon={<SvgEdit />}
                      >
                        Modify
                      </MenuItem>,
                      <MenuItem
                        key={1}
                        onClick={() => openProperties(value)}
                        icon={<SvgList />}
                      >
                        Properties
                      </MenuItem>,
                      <MenuItem
                        key={2}
                        onClick={() => {
                          setSelectedGroup(value.row.original);
                          setShowDeleteModal(true);
                          close();
                        }}
                        icon={<SvgDelete />}
                      >
                        Remove
                      </MenuItem>,
                    ]}
                  >
                    <IconButton
                      disabled={isLoadingQuery}
                      styleType='borderless'
                    >
                      <SvgMore
                        style={{
                          width: "16px",
                          height: "16px",
                        }}
                      />
                    </IconButton>
                  </DropdownMenu>
                </div>
              );
            },
          },
        ],
      },
    ],
    [isLoadingQuery, onModify, openProperties],
  );

  // Temp
  const stringToColor = function (str: string) {
    let hash = 0;
    for (let i = 0; i < str.length; i++) {
      hash = str.charCodeAt(i) + ((hash << 5) - hash);
    }
    let colour = "#";
    for (let i = 0; i < 3; i++) {
      const value = (hash >> (i * 8)) & 0xff;
      colour += (`00${value.toString(16)}`).substr(-2);
    }
    return colour;
  };

  const onSelect = useCallback(
    async (selectedData: GroupType[] | undefined) => {
      clearEmphasizedElements();
      if (selectedData && selectedData.length > 0) {
        setLoadingQuery(true);
        let allIds: string[] = [];
        for (const row of selectedData) {
          const query = row.query ?? "";
          if (hilitedElements.current.has(query)) {
            const hilitedIds = hilitedElements.current.get(query) ?? [];
            visualizeElements(hilitedIds, stringToColor(row.id ?? ""));
            allIds = allIds.concat(hilitedIds);
          } else {
            try {
              const ids: string[] = await fetchIdsFromQuery(
                query,
                iModelConnection,
              );
              if (ids.length === 0) {
                toaster.warning(`${row.groupName}'s query is valid but produced no results.`);
              }
              const hiliteIds = await visualizeElementsById(
                ids,
                stringToColor(row.id ?? ""),
                iModelConnection,
              );
              hilitedElements.current.set(query, hiliteIds);

              allIds = allIds.concat(ids);
            } catch {
              const index = groups.findIndex((group) => group.id === row.id);
              setSelectedRows((rowIds) => {
                const selectedRowIds = { ...rowIds };
                delete selectedRowIds[index];
                return selectedRowIds;
              });
              toaster.negative(`Could not load ${row.groupName}. Query could not be resolved.`);

            }
          }
        }
        await zoomToElements(allIds);
        setLoadingQuery(false);
      }
    },
    [iModelConnection, groups],
  );

  const controlledState = useCallback(
    (state) => {
      return {
        ...state,
        selectedRowIds: { ...selectedRows },
      };
    },
    [selectedRows],
  );

  const propertyMenuGoBack = useCallback(async () => {
    clearEmphasizedElements();
    setGroupsView(GroupsView.GROUPS);
    await refresh();
  }, [refresh]);

  const tableStateReducer = (
    newState: TableState,
    action: ActionType,
    _previousState: TableState,
    instance?: TableInstance,
  ): TableState => {
    switch (action.type) {
      case actions.toggleRowSelected: {
        const newSelectedRows = {
          ...selectedRows,
        };
        if (action.value) {
          newSelectedRows[action.id] = true;
        } else {
          delete newSelectedRows[action.id];
        }
        setSelectedRows(newSelectedRows);
        newState.selectedRowIds = newSelectedRows;
        break;
      }
      case actions.toggleAllRowsSelected: {
        if (!instance?.rowsById) {
          break;
        }
        const newSelectedRows = {} as Record<string, boolean>;
        if (action.value) {
          Object.keys(instance.rowsById).forEach(
            (id) => (newSelectedRows[id] = true),
          );
        }
        setSelectedRows(newSelectedRows);
        newState.selectedRowIds = newSelectedRows;
        break;
      }
      default:
        break;
    }
    return newState;
  };

  switch (groupsView) {
    case GroupsView.ADD:
      return (
        <GroupAction
          iModelId={iModelId}
          mappingId={mapping.id ?? ""}
          goBack={async () => {
            clearEmphasizedElements();
            setGroupsView(GroupsView.GROUPS);
            await refresh();
          }}
        />
      );
    case GroupsView.MODIFYING:
      return selectedGroup ? (
        <GroupAction
          iModelId={iModelId}
          mappingId={mapping.id ?? ""}
          group={selectedGroup}
          goBack={async () => {
            clearEmphasizedElements();
            setGroupsView(GroupsView.GROUPS);
            await refresh();
          }}
        />
      ) : null;
    case GroupsView.PROPERTIES:
      return selectedGroup ? (
        <PropertyMenu
          iModelId={iModelId}
          mappingId={mapping.id ?? ""}
          group={selectedGroup}
          goBack={propertyMenuGoBack}
        />
      ) : null;
    default:
      return (
        <>
          <WidgetHeader
            title={mapping.mappingName ?? ""}
            disabled={isLoading || isLoadingQuery}
            returnFn={async () => {
              clearEmphasizedElements();
              await goBack();
            }}
          />
          <div className='groups-container'>
            <Button
              startIcon={
                isLoadingQuery ? <ProgressRadial size="small" indeterminate /> : <SvgAdd />
              }
              styleType='high-visibility'
              disabled={isLoadingQuery}
              onClick={() => addGroup()}
            >
              {isLoadingQuery ? "Loading Group(s)" : "Add Group"}
            </Button>
            <Table<GroupType>
              data={groups}
              density='extra-condensed'
              columns={groupsColumns}
              emptyTableContent='No Groups available.'
              isSortable
              isSelectable
              onSelect={onSelect}
              isLoading={isLoading}
              isRowDisabled={() => isLoadingQuery}
              stateReducer={tableStateReducer}
              useControlledState={controlledState}
            />
          </div>
          <DeleteModal
            entityName={selectedGroup?.groupName ?? ""}
            show={showDeleteModal}
            setShow={setShowDeleteModal}
            onDelete={async () => {
              const reportingClientApi = new ReportingClient(apiContext.prefix);
              await reportingClientApi.deleteGroup(
                apiContext.accessToken,
                iModelId,
                mapping.id ?? "",
                selectedGroup?.id ?? "",
              );
            }}
            refresh={refresh}
          />
        </>
      );
  }
}
Example #2
Source File: ValidationTableWidget.tsx    From frontend-sample-showcase with MIT License 4 votes vote down vote up
ValidationTableWidget: React.FunctionComponent = () => {
  const iModelConnection = useActiveIModelConnection();
  const [validationResults, setValidationResults] = React.useState<ValidationResults>();
  const [ruleData, setRuleData] = React.useState<Record<string, PropertyValueValidationRule | undefined>>();
  const [selectedElement, setSelectedElement] = useState<string | undefined>();

  useEffect(() => {
    const removeDataListener = ValidationApi.onValidationDataChanged.addListener((dat) => {
      setValidationResults(dat.validationData);
      setRuleData(dat.ruleData);
    });

    const removeElementListener = ValidationApi.onMarkerClicked.addListener((elementId) => {
      setSelectedElement(elementId);
    });

    if (iModelConnection) {
      ValidationApi.getValidationData(iModelConnection.iTwinId!).catch((error) => {
        // eslint-disable-next-line no-console
        console.error(error);
      });
    }
    return () => {
      removeDataListener();
      removeElementListener();
    };
  }, [iModelConnection]);

  const columnDefinition = useMemo((): Column<TableRow>[] => [
    {
      Header: "Table",
      columns: [
        {
          Header: "Element Id",
          accessor: "elementId",
        },
        {
          Header: "Element Label",
          accessor: "elementLabel",
        },
        {
          Header: "Rule Name",
          accessor: "ruleName",
        },
        {
          Header: "Legal Range",
          accessor: "legalValues",
        },
        {
          Header: "Invalid Value",
          accessor: "badValue",
        },
      ],
    },
  ], []);

  const data = useMemo(() => {
    const rows: TableRow[] = [];

    if (validationResults !== undefined && validationResults.result !== undefined && validationResults.ruleList !== undefined && ruleData !== undefined) {
      const getLegalValue = (item: any) => {
        const currentRuleData = ruleData[validationResults.ruleList[item.ruleIndex].id];
        if (!currentRuleData)
          return "";

        if (currentRuleData.functionParameters.lowerBound) {
          if (currentRuleData.functionParameters.upperBound) {
            // Range of values
            return `[${currentRuleData.functionParameters.lowerBound},${currentRuleData.functionParameters.upperBound}]`;
          } else {
            // Value has a lower bound
            return `>${currentRuleData.functionParameters.lowerBound}`;
          }
        } else {
          // Value needs to be defined
          return "Must be Defined";
        }
      };

      validationResults.result.forEach((rowData) => {
        const row: TableRow = {
          elementId: rowData.elementId,
          elementLabel: rowData.elementLabel,
          ruleName: validationResults.ruleList[rowData.ruleIndex].displayName,
          legalValues: getLegalValue(rowData),
          badValue: rowData.badValue,
        };
        rows.push(row);
      });
    }

    return rows;
  }, [validationResults, ruleData]);

  const controlledState = useCallback(
    (state: TableState<TableRow>, meta: MetaBase<TableRow>) => {
      state.selectedRowIds = {};

      if (selectedElement) {
        const row = meta.instance.rows.find((r: { original: { elementId: string } }) => r.original.elementId === selectedElement);
        if (row) {
          state.selectedRowIds[row.id] = true;
        }
      }
      return { ...state };
    },
    [selectedElement],
  );

  const tableStateReducer = (
    newState: TableState<TableRow>,
    action: ActionType,
    _previousState: TableState<TableRow>,
    instance?: TableInstance<TableRow> | undefined,
  ): TableState<TableRow> => {
    switch (action.type) {
      case actions.toggleRowSelected: {
        newState.selectedRowIds = {};

        if (action.value)
          newState.selectedRowIds[action.id] = true;

        if (instance) {
          const elementId = instance.rowsById[action.id].original.elementId;
          ValidationApi.visualizeViolation(elementId);
          setSelectedElement(elementId);
        }
        break;
      }
      default:
        break;
    }
    return newState;
  };

  return (
    <Table<TableRow>
      useControlledState={controlledState}
      stateReducer={tableStateReducer}
      isSelectable={true}
      data={data}
      columns={columnDefinition}
      isLoading={!validationResults}
      emptyTableContent="No data"
      density="extra-condensed" />
  );
}