Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Row inline edits #79

Closed
zeestack opened this issue Oct 8, 2022 · 10 comments
Closed

Row inline edits #79

zeestack opened this issue Oct 8, 2022 · 10 comments
Labels
enhancement New feature or request

Comments

@zeestack
Copy link

zeestack commented Oct 8, 2022

Is your feature request related to a problem? Please describe.
The DataTable component does not support inline row edits. Inline edits are more desirable for enterprise-based applications. We can currently use the render props for each field separately. This is a length and code repetition that cannot be avoided. We always need to find an index to edit a row and update the state based on the current implementation.

Describe the solution you'd like
I would like to request a pattern where we can provide a RowComponent that implements the conditional logic if the row is being edited and renders the editable Row otherwise renders the default UI. It will be also best if we can get the row indexes as well with all event handlers so that we do not always iterate over the rows to find indexes.

Describe alternatives you've considered
Currently using render props to render editable UI vs Fixed UI.

@icflorescu icflorescu self-assigned this Oct 14, 2022
@icflorescu icflorescu added the enhancement New feature or request label Oct 14, 2022
@icflorescu
Copy link
Owner

a pattern where we can provide a RowComponent that implements the conditional logic if the row is being edited and renders the editable Row otherwise renders the default UI

I'll think about it, but I'm not sure it's feasible. The row rendering logic is quite complex.

However, this is an excellent idea:

It will be also best if we can get the row indexes as well with all event handlers so that we do not always iterate over the rows to find indexes

Thanks for the suggestion! I think adding row indexes to handlers would prove useful in many possible scenarios.

@icflorescu
Copy link
Owner

I know it's not exactly what you're asking for, but have a look at the row expansion feature.

@gfazioli
Copy link
Contributor

Hi all, I was able to implement a sort inline edit, by using a editable custom flag.
Also, the row expansion could be used and helpful in this case.
The only issue I found is the rendering of the items/columns.
Is there any way to handle the key react for each row in order to improve that?

@gfazioli
Copy link
Contributor

in short, I'm using something like

...
<DataTable
	withBorder
	borderRadius="md"
	highlightOnHover
	withColumnBorders
	onRowClick={(_, rowIndex) => setSelectedRow(rowIndex)}
	columns={[
		{
			accessor: "type",
			width: 110,
			render: (item: ItemForm) => {
				const index = item.index || 0;
				return (
					<ItemTypePicker
						key={`datatable-type-${index}`}
						editable={selectedRow === index}
						label={null}
						{...form.getInputProps(`items.${index}.type`)}
					/>
				);
			},
		},
...

but it doesn't help, I mean, the key on my component.
wdyt?

@gfazioli
Copy link
Contributor

Update: currently, the only way to handle any editable input inside a cell is by using a controlled component. That's because if you update the value by using form the whole items will be re-rendering and the UI became unusable.

@icflorescu
Copy link
Owner

@gfazioli Related to your question above, I think it would be best if you could only render an <ItemTypePicker /> element when selectedRow === index and fall back to just displaying the formatted value otherwise.

@gfazioli
Copy link
Contributor

@icflorescu yep, would be a great alternative instead of using the editable props 👍

@icflorescu icflorescu removed their assignment Oct 28, 2022
@zeestack
Copy link
Author

zeestack commented Oct 28, 2022

Let me explain what I suggested, once I had implemented an editable row with React Table Hook v8. Overall UI pattern looked something like below... The benefit of doing this way, table column schema can be separated entirely from the column UI itself. Improves the separation of concerns.

export const getColumns = () => {
  const columns: Array<ColumnDef<User, unknown>> = [
    {
      id: "select",
      header: ({ table }) => (
        <div className="grid place-items-center">
          <Checkbox
            checked={table.getIsAllRowsSelected()}
            onChange={table.getToggleAllRowsSelectedHandler()}
            indeterminate={table.getIsSomeRowsSelected()}
            styles={{ input: { cursor: "pointer" } }}
          />
        </div>
      ),
    },
    {
      accessorKey: "description",
      header: "Descriptions",
      footer: () => null,
    },
    {
      accessorKey: "notes",
      header: "Notes",
      footer: () => null,
    },
    {
      accessorKey: "otp",
      header: "OTP",
      footer: () => null,
    },
    {
      accessorKey: "sportsCanada",
      header: "Sports Canada",
      footer: () => null,
    },
    {
      accessorKey: "nso",
      header: "NSO",
      footer: () => null,
    },
    {
      accessorKey: "otherSources",
      header: "Other",
      footer: () => null,
    },
    {
      accessorKey: "total",
      header: "total",
      footer: () => null,
      size: 1,
    },
    {
      header: "Action",
      size: 1,
    },
  ];
  return columns;
};
export function RowUI(props: CellContext<User, unknown>) {
  const { getValue, table, row, cell } = props;

  const initialValue = getValue() as string | number;
  const [value, setValue] = useState<string | number>(initialValue);

  useEffect(() => {
    setValue(initialValue);
  }, [initialValue]);

  const isEditting = isTableRowInEditMode(
    table.options.meta?.getState()!,
    row.original.id
  );

  const onBlur = () => {
    table.options.meta?.updateData(row.original.id, cell.column.id, value);
  };

  switch (props.cell.column.id) {
    case "description":
      return (
        <div className="grid place-items-start">
          <EditableSelect
            as={isEditting ? "editable" : "readonly"}
            searchable
            value={value.toString()}
            data={["Program1", "Program2", "Program3"]}
            onChange={(value) => setValue(value ?? "")}
            onBlur={onBlur}
          />
        </div>
      );

    case "Action":
      return (
        <div className="grid place-items-start">
          <ControlActions {...props} />
        </div>
      );

    case "select":
      return (
        <div className="grid place-items-center">
          <Checkbox
            checked={row.getIsSelected()}
            indeterminate={row.getIsSomeSelected()}
            onChange={row.getToggleSelectedHandler()}
            styles={{ input: { cursor: "pointer" } }}
          />
        </div>
      );

    default:
      return (
        <div className="grid place-items-start">
          <EditableField
            as={isEditting ? "editable" : "readonly"}
            value={value.toLocaleString()}
            onBlur={onBlur}
            type="number"
            onChange={(e) => setValue(e.currentTarget.value)}
          />
        </div>
      );
  }
}

Then I passed the RowUI to DataTable as prop.

 <DataTable
          data={data}
          columns={getColumns()}
          onRowUpdate={handleOnRowUpdate}
          onRowDelete={handleRowDelete}
          **RowUI={RowUI}**
          search={search}
          className="bottom-2 w-full border-collapse"
          bodyStyleClasses={bodyStyleClasses}
          headerStyleClasses={headerStyleClasses}
          rowsSelected={selectedRows}
          onRowsSelected={setSelectedRows}
        />

The Row UI prop was provided to defaultColumns to React Table Hook. I was thinking similar pattern for the mantine DataTable. This way you can decouple UI from Table Column Schema. When user passes the RowUI prop it renders the custom UI based on Users wish. If the ROWUI prop is absent it will be default text-only UI of the data table.

At the end, it looked something like this...

CleanShot 2022-10-28 at 17 33 32@2x

@zeestack
Copy link
Author

@icflorescu - please let me know what you think. if you like this approach, I might help with a PR.

@otobot1
Copy link
Contributor

otobot1 commented Nov 16, 2022

At a glance, it seems like a solid idea to me. More separation of concerns is always good. It seems like it could potentially break a bunch of stuff though, so any PR would have to be very thoroughly tested.

Repository owner locked and limited conversation to collaborators Jun 1, 2023
@icflorescu icflorescu converted this issue into discussion #309 Jun 1, 2023

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants