@tsed/react-formio 2.0.3 → 2.1.1

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.
Files changed (39) hide show
  1. package/coverage.json +4 -4
  2. package/dist/components/actions-table/actionsTable.component.d.ts +1 -1
  3. package/dist/components/form-access/formAccess.component.d.ts +1 -1
  4. package/dist/components/form-edit/formEdit.stories.d.ts +1 -1
  5. package/dist/components/forms-table/formsTable.component.d.ts +1 -1
  6. package/dist/components/submissions-table/submissionsTable.component.d.ts +1 -1
  7. package/dist/components/table/components/defaultCells.component.d.ts +4 -0
  8. package/dist/components/table/components/defaultRow.component.d.ts +12 -0
  9. package/dist/components/table/components/dragNDropContainer.d.ts +4 -0
  10. package/dist/components/table/hooks/useCustomTable.hook.d.ts +187 -0
  11. package/dist/components/table/hooks/useDragnDropRow.hook.d.ts +266 -0
  12. package/dist/components/table/index.d.ts +2 -1
  13. package/dist/components/table/table.component.d.ts +2 -71
  14. package/dist/components/table/table.stories.d.ts +25 -0
  15. package/dist/components/table/utils/swapElements.d.ts +1 -0
  16. package/dist/components/table/utils/swapElements.spec.d.ts +1 -0
  17. package/dist/index.js +276 -49
  18. package/dist/index.js.map +1 -1
  19. package/dist/index.modern.js +269 -49
  20. package/dist/index.modern.js.map +1 -1
  21. package/package.json +5 -3
  22. package/src/components/actions-table/actionsTable.component.tsx +2 -1
  23. package/src/components/form-access/formAccess.component.tsx +1 -1
  24. package/src/components/form-edit/formEdit.stories.tsx +2 -2
  25. package/src/components/forms-table/formsTable.component.tsx +2 -1
  26. package/src/components/forms-table/formsTable.stories.tsx +2 -2
  27. package/src/components/submissions-table/submissionsTable.component.tsx +2 -1
  28. package/src/components/table/components/defaultCells.component.tsx +22 -0
  29. package/src/components/table/components/defaultRow.component.tsx +50 -0
  30. package/src/components/table/components/dragNDropContainer.tsx +7 -0
  31. package/src/components/table/hooks/useCustomTable.hook.tsx +231 -0
  32. package/src/components/table/hooks/useDragnDropRow.hook.ts +80 -0
  33. package/src/components/table/index.ts +2 -1
  34. package/src/components/table/table.component.tsx +83 -233
  35. package/src/components/table/table.stories.tsx +56 -0
  36. package/src/components/table/utils/swapElements.spec.ts +7 -0
  37. package/src/components/table/utils/swapElements.ts +7 -0
  38. /package/dist/components/table/{utils → hooks}/useOperations.hook.d.ts +0 -0
  39. /package/src/components/table/{utils → hooks}/useOperations.hook.tsx +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tsed/react-formio",
3
- "version": "2.0.3",
3
+ "version": "2.1.1",
4
4
  "description": "Provide a react formio wrapper. Written in TypeScript.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.modern.js",
@@ -23,13 +23,15 @@
23
23
  "lodash": ">=4.17.20",
24
24
  "prop-types": ">=15.7.2",
25
25
  "react": ">=16.14.0",
26
+ "react-dnd": ">=16.0.1",
27
+ "react-dnd-html5-backend": ">=16.0.1",
26
28
  "react-dom": ">=16.14.0",
27
29
  "react-table": ">=7.6.3",
28
30
  "tooltip.js": ">=1.3.3"
29
31
  },
30
32
  "devDependencies": {
31
- "@tsed/tailwind": "2.0.3",
32
- "@tsed/tailwind-formio": "2.0.3"
33
+ "@tsed/tailwind": "2.1.1",
34
+ "@tsed/tailwind-formio": "2.1.1"
33
35
  },
34
36
  "repository": "https://github.com/TypedProject/tsed-formio",
35
37
  "bugs": {
@@ -5,7 +5,8 @@ import React, { useState } from "react";
5
5
  import { ActionSchema } from "../../interfaces";
6
6
  import { iconClass } from "../../utils/iconClass";
7
7
  import { Select } from "../select/select.component";
8
- import { Table, TableProps } from "../table/table.component";
8
+ import { TableProps } from "../table/hooks/useCustomTable.hook";
9
+ import { Table } from "../table/table.component";
9
10
 
10
11
  export type ActionsTableProps = Omit<TableProps<ActionSchema>, "columns"> & {
11
12
  onAddAction?: (actionName: string) => void;
@@ -1,7 +1,7 @@
1
1
  import PropTypes from "prop-types";
2
2
  import React, { PropsWithChildren, ReactElement, useCallback, useEffect, useMemo, useState } from "react";
3
3
 
4
- import { FormOptions, FormSchema, Submission } from "../../interfaces";
4
+ import type { FormOptions, FormSchema, Submission } from "../../interfaces";
5
5
  import { Card } from "../card/card.component";
6
6
  import { Form } from "../form/form.component";
7
7
  import { ChangedSubmission } from "../form/useForm.hook";
@@ -1,8 +1,8 @@
1
1
  import React from "react";
2
2
 
3
- import { FormEdit } from "../../index";
3
+ import { FormEdit } from "./formEdit.component";
4
4
  import { defaultDisplayChoices } from "./formParameters.component";
5
-
5
+ console.log("===", FormEdit);
6
6
  export default {
7
7
  title: "ReactFormio/FormEdit",
8
8
  component: FormEdit,
@@ -3,7 +3,8 @@ import React from "react";
3
3
  import { FormSchema } from "../../interfaces";
4
4
  import { DefaultColumnFilter } from "../table/filters/defaultColumnFilter.component";
5
5
  import { SelectColumnFilter } from "../table/filters/selectColumnFilter.component";
6
- import { Table, TableProps } from "../table/table.component";
6
+ import { TableProps } from "../table/hooks/useCustomTable.hook";
7
+ import { Table } from "../table/table.component";
7
8
  import { FormsCell as DefaultFormCell } from "./components/formCell.component";
8
9
 
9
10
  export type FormsTableProps = Omit<TableProps<FormSchema>, "columns"> & {
@@ -1,4 +1,4 @@
1
- import { ICONS } from "@tsed/tailwind-formio/lib/templates/tailwind/iconClass";
1
+ import tailwind from "@tsed/tailwind-formio";
2
2
  import React from "react";
3
3
 
4
4
  import { FormsTable } from "./formsTable.component";
@@ -10,7 +10,7 @@ export default {
10
10
  icon: {
11
11
  control: {
12
12
  type: "select",
13
- options: Object.keys(ICONS)
13
+ options: Object.keys(tailwind.templates.tailwind.ICONS)
14
14
  }
15
15
  },
16
16
  data: {
@@ -1,7 +1,8 @@
1
1
  import React from "react";
2
2
 
3
3
  import { FormSchema, Submission } from "../../interfaces";
4
- import { Table, TableProps } from "../table/table.component";
4
+ import { TableProps } from "../table/hooks/useCustomTable.hook";
5
+ import { Table } from "../table/table.component";
5
6
  import { mapFormToColumns } from "../table/utils/mapFormToColumns";
6
7
 
7
8
  export type SubmissionsTableProps = Omit<TableProps<Submission>, "columns"> & {
@@ -0,0 +1,22 @@
1
+ import React from "react";
2
+ import { Row } from "react-table";
3
+
4
+ export function DefaultCells<Data extends object = {}>({ row }: { row: Row<Data> }) {
5
+ return (
6
+ <>
7
+ {row.cells.map((cell, i) => {
8
+ const { hidden, colspan } = cell.column as any;
9
+
10
+ if (hidden) {
11
+ return null;
12
+ }
13
+
14
+ return (
15
+ <td colSpan={colspan} {...cell.getCellProps()} key={`tableInstance.page.cells.${cell.value || "value"}.${i}`}>
16
+ {cell.render("Cell")}
17
+ </td>
18
+ );
19
+ })}
20
+ </>
21
+ );
22
+ }
@@ -0,0 +1,50 @@
1
+ import classnames from "classnames";
2
+ import React, { DetailedHTMLProps, HTMLAttributes } from "react";
3
+ import { Row } from "react-table";
4
+
5
+ import { iconClass } from "../../../utils/iconClass";
6
+ import { useDndRow } from "../hooks/useDragnDropRow.hook";
7
+ import { DefaultCells } from "./defaultCells.component";
8
+
9
+ export interface DefaultRowProps<Data extends object = {}>
10
+ extends Omit<DetailedHTMLProps<HTMLAttributes<HTMLTableRowElement>, HTMLTableRowElement>, "onClick" | "onDrag" | "onDrop"> {
11
+ onClick: (data: any, action: string) => void;
12
+ row: Row<Data>;
13
+ index: number;
14
+ onDrop: (item: Data) => void;
15
+ onDrag: (index: number, hoverIndex: number) => void;
16
+ enableDragNDrop?: boolean;
17
+ }
18
+
19
+ export function DefaultDndRow<Data extends object = {}>(props: DefaultRowProps<Data>) {
20
+ const { isDragging, dragRef, dropRef, opacity } = useDndRow(props);
21
+
22
+ return (
23
+ <tr ref={dropRef} style={{ opacity }}>
24
+ <td ref={dragRef} role='cell' style={{ cursor: isDragging ? "grabbing" : "grab" }} className='align-middle'>
25
+ <div className='flex items-center justify-center'>
26
+ <i className={classnames(iconClass(undefined, "dots-vertical-rounded"))} />
27
+ </div>
28
+ </td>
29
+ <DefaultCells<Data> {...props} />
30
+ </tr>
31
+ );
32
+ }
33
+
34
+ export function DefaultRow<Data extends object = {}>({ onClick, row, enableDragNDrop, onDrop, onDrag, ...props }: DefaultRowProps<Data>) {
35
+ const opts = {
36
+ ...props,
37
+ onClick: () => onClick(row.original, "row"),
38
+ ...row.getRowProps()
39
+ };
40
+
41
+ if (enableDragNDrop) {
42
+ return <DefaultDndRow<Data> {...opts} row={row} onDrag={onDrag} onDrop={onDrop} />;
43
+ }
44
+
45
+ return (
46
+ <tr {...opts}>
47
+ <DefaultCells<Data> row={row} />
48
+ </tr>
49
+ );
50
+ }
@@ -0,0 +1,7 @@
1
+ import React, { PropsWithChildren } from "react";
2
+ import { DndProvider } from "react-dnd";
3
+ import { HTML5Backend } from "react-dnd-html5-backend";
4
+
5
+ export function DrapNDropContainer({ enableDragNDrop, children }: PropsWithChildren<{ enableDragNDrop?: boolean }>) {
6
+ return enableDragNDrop ? <DndProvider backend={HTML5Backend}>{children}</DndProvider> : <>{children}</>;
7
+ }
@@ -0,0 +1,231 @@
1
+ import noop from "lodash/noop";
2
+ import React, { PropsWithChildren, useState } from "react";
3
+ import { CellProps, FilterProps, Renderer, TableOptions, useFilters, useGroupBy, usePagination, useSortBy, useTable } from "react-table";
4
+
5
+ import { OnClickOperation, Operation, QueryOptions } from "../../../interfaces";
6
+ import { Pagination as DefaultPagination } from "../../pagination/pagination.component";
7
+ import { DefaultArrowSort } from "../components/defaultArrowSort.component";
8
+ import { DefaultCellHeader, DefaultCellHeaderProps } from "../components/defaultCellHeader.component";
9
+ import { DefaultRow, DefaultRowProps } from "../components/defaultRow.component";
10
+ import { DefaultColumnFilter } from "../filters/defaultColumnFilter.component";
11
+ import { swapElements } from "../utils/swapElements";
12
+ import { useOperations } from "./useOperations.hook";
13
+
14
+ export interface TableProps<Data extends object = any> extends TableOptions<Data>, Partial<QueryOptions> {
15
+ className?: string;
16
+ /**
17
+ * Call the listener when a filter / pagination / sort change.
18
+ */
19
+ onChange?: (query: QueryOptions) => void;
20
+ /**
21
+ * Call the listener when a line is clicked.
22
+ */
23
+ onClick?: OnClickOperation<Data>;
24
+ /**
25
+ * Pagination steps list
26
+ */
27
+ pageSizes?: number[];
28
+ /**
29
+ *
30
+ */
31
+ isLoading?: boolean;
32
+ /**
33
+ * Custom EmptyData displayed when there is no data
34
+ */
35
+ EmptyData?: React.ComponentType;
36
+ /**
37
+ * Custom ArrowSort
38
+ */
39
+ ArrowSort?: React.ComponentType;
40
+ /**
41
+ * Custom default ColumnFilter
42
+ */
43
+ ColumnFilter?: Renderer<FilterProps<Data>>;
44
+ /**
45
+ * Custom cell
46
+ */
47
+ Cell?: React.ComponentType<CellProps<Data>>;
48
+ /**
49
+ * Custom Row
50
+ */
51
+ Row?: React.ComponentType<DefaultRowProps<Data>>;
52
+ /**
53
+ *
54
+ */
55
+ CellHeader?: React.ComponentType<DefaultCellHeaderProps<Data>>;
56
+ /**
57
+ *
58
+ */
59
+ CellOperations?: React.ComponentType;
60
+ /**
61
+ * Custom Loader
62
+ */
63
+ Loader?: React.ComponentType;
64
+ /**
65
+ * Custom Loader
66
+ */
67
+ Pagination?: React.ComponentType;
68
+ /**
69
+ * Disable filters
70
+ */
71
+ disableFilters?: boolean;
72
+ /**
73
+ * Disable pagination
74
+ */
75
+ disablePagination?: boolean;
76
+ manualPagination?: boolean;
77
+ manualSortBy?: boolean;
78
+ manualFilters?: boolean;
79
+ /**
80
+ * Configuration operation for each line.
81
+ */
82
+ operations?: Operation[];
83
+
84
+ i18n?: (f: string) => string;
85
+
86
+ /**
87
+ * Enable drag and drop rows
88
+ */
89
+ enableDragNDrop?: boolean;
90
+ onDrag?: (data: Data[]) => void;
91
+ onDrop?: (item: Data) => void;
92
+ }
93
+
94
+ export function getOperationCallback(operations: Operation[], onClick: OnClickOperation) {
95
+ return (data: any, action: string) => {
96
+ const operation = operations.find((operation) => operation.action === action || operation.alias === action);
97
+ if (operation) {
98
+ onClick(data, operation);
99
+ }
100
+ };
101
+ }
102
+
103
+ function DefaultLoader() {
104
+ return <div className={"text-center p-2 pb-4 font-bold"}>Loading in progress</div>;
105
+ }
106
+
107
+ function DefaultEmptyData() {
108
+ return <div className='text-center p-2 pb-4 font-bold'>No data found</div>;
109
+ }
110
+
111
+ const hooks = [useFilters, useGroupBy, useSortBy, usePagination];
112
+
113
+ export function useCustomTable<Data extends object = {}>(props: PropsWithChildren<TableProps<Data>>) {
114
+ const {
115
+ children,
116
+ className = "",
117
+ columns,
118
+ data,
119
+ onChange = noop,
120
+ onClick = noop,
121
+ onDrag = noop,
122
+ onDrop = noop,
123
+ operations = [],
124
+ pageSizes = [10, 25, 50, 100],
125
+ filters: controlledFilters,
126
+ filterId: controlledFilterId,
127
+ pageSize: controlledPageSize,
128
+ pageIndex: controlledPageIndex,
129
+ sortBy: controlledSortBy,
130
+ isLoading,
131
+ disableFilters,
132
+ disablePagination,
133
+ ArrowSort = DefaultArrowSort,
134
+ ColumnFilter = DefaultColumnFilter,
135
+ EmptyData = DefaultEmptyData,
136
+ Loader = DefaultLoader,
137
+ Pagination = DefaultPagination,
138
+ Row = DefaultRow,
139
+ CellHeader = DefaultCellHeader as any,
140
+ CellOperations,
141
+ i18n = (f: string) => f,
142
+ ...ctx
143
+ } = props;
144
+
145
+ const _onClick = getOperationCallback(operations, onClick);
146
+
147
+ const defaultColumn = React.useMemo(
148
+ () => ({
149
+ // Let's set up our default Filter UI
150
+ Filter: ColumnFilter,
151
+ ArrowSort
152
+ }),
153
+ [ColumnFilter, ArrowSort]
154
+ ) as any;
155
+
156
+ const [filterId, setFilterId] = React.useState(controlledFilterId);
157
+
158
+ // DND
159
+ const [records, setRecords] = useState<Data[]>(data);
160
+ const _onDrag = (dragIndex: number, hoverIndex: number) => {
161
+ const newRecords = swapElements([...records], dragIndex, hoverIndex);
162
+
163
+ setRecords(newRecords);
164
+
165
+ onDrag(newRecords);
166
+ };
167
+
168
+ const tableInstance = useTable<Data>(
169
+ {
170
+ ...props,
171
+ columns,
172
+ data,
173
+ ctx,
174
+ defaultColumn,
175
+ // getRowId,
176
+ initialState: {
177
+ ...(props.initialState || {}),
178
+ filters: controlledFilters || [],
179
+ pageIndex: controlledPageIndex || 0,
180
+ pageSize: controlledPageSize || 10,
181
+ sortBy: controlledSortBy || []
182
+ } as any,
183
+ manualPagination: props.manualPagination === undefined ? true : props.manualPagination,
184
+ manualSortBy: props.manualSortBy === undefined ? true : props.manualPagination,
185
+ manualFilters: props.manualFilters === undefined ? true : props.manualFilters,
186
+ disableFilters,
187
+ filterId,
188
+ setFilterId
189
+ } as any,
190
+ ...hooks,
191
+ useOperations({ operations, CellOperations, onClick: _onClick, ctx, i18n })
192
+ );
193
+
194
+ const {
195
+ setPageSize,
196
+ state: { pageIndex, pageSize, sortBy, filters }
197
+ } = tableInstance;
198
+
199
+ React.useEffect(() => {
200
+ onChange({
201
+ pageIndex,
202
+ pageSize,
203
+ sortBy,
204
+ filters,
205
+ filterId
206
+ });
207
+ }, [onChange, pageIndex, pageSize, sortBy, filters, filterId]);
208
+
209
+ return {
210
+ ...props,
211
+ className,
212
+ tableInstance,
213
+ CellHeader,
214
+ isLoading,
215
+ onClick: _onClick,
216
+ Loader,
217
+ EmptyData,
218
+ Row,
219
+ data,
220
+ disablePagination,
221
+ Pagination,
222
+ pageIndex,
223
+ pageSize,
224
+ pageSizes,
225
+ setPageSize,
226
+ i18n,
227
+ children,
228
+ onDrag: _onDrag,
229
+ onDrop: onDrop
230
+ };
231
+ }
@@ -0,0 +1,80 @@
1
+ import { useRef } from "react";
2
+ import { useDrag, useDrop } from "react-dnd";
3
+
4
+ import { DefaultRowProps } from "../components/defaultRow.component";
5
+
6
+ const DND_ITEM_TYPE = "row";
7
+
8
+ export function useDndRow<Data extends object = {}>({ onDrag, onDrop, index, ...props }: DefaultRowProps<Data>) {
9
+ const dropRef = useRef<HTMLTableRowElement>(null);
10
+ const dragRef = useRef<HTMLTableDataCellElement>(null);
11
+
12
+ const [, drop] = useDrop<Data>({
13
+ accept: DND_ITEM_TYPE,
14
+ drop(item) {
15
+ onDrop(item);
16
+ },
17
+ hover(item: any, monitor) {
18
+ if (!dropRef.current) {
19
+ return;
20
+ }
21
+ const dragIndex = item.index;
22
+ const hoverIndex = index;
23
+ // Don't replace items with themselves
24
+ if (dragIndex === hoverIndex) {
25
+ return;
26
+ }
27
+ // Determine rectangle on screen
28
+ const hoverBoundingRect = dropRef.current!.getBoundingClientRect();
29
+ // Get vertical middle
30
+ const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
31
+ // Determine mouse position
32
+ const clientOffset = monitor.getClientOffset();
33
+ if (!clientOffset) {
34
+ return;
35
+ }
36
+ // Get pixels to the top
37
+ const hoverClientY = clientOffset.y - hoverBoundingRect.top;
38
+ // Only perform the move when the mouse has crossed half of the items height
39
+ // When dragging downwards, only move when the cursor is below 50%
40
+ // When dragging upwards, only move when the cursor is above 50%
41
+ // Dragging downwards
42
+ if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
43
+ return;
44
+ }
45
+ // Dragging upwards
46
+ if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
47
+ return;
48
+ }
49
+
50
+ // Time to actually perform the action
51
+ onDrag(dragIndex, hoverIndex);
52
+ // Note: we're mutating the monitor item here!
53
+ // Generally it's better to avoid mutations,
54
+ // but it's good here for the sake of performance
55
+ // to avoid expensive index searches.
56
+ item.index = hoverIndex;
57
+ }
58
+ });
59
+
60
+ const [{ isDragging }, drag, preview] = useDrag(() => ({
61
+ type: DND_ITEM_TYPE,
62
+ item: { index },
63
+ collect: (monitor) => ({
64
+ isDragging: monitor.isDragging()
65
+ })
66
+ }));
67
+
68
+ const opacity = isDragging ? 0 : 1;
69
+
70
+ preview(drop(dropRef));
71
+ drag(dragRef);
72
+
73
+ return {
74
+ ...props,
75
+ opacity,
76
+ isDragging,
77
+ dropRef,
78
+ dragRef
79
+ };
80
+ }
@@ -7,5 +7,6 @@ export * from "./components/defaultOperationButton.component";
7
7
  export * from "./filters/defaultColumnFilter.component";
8
8
  export * from "./filters/selectColumnFilter.component";
9
9
  export * from "./filters/sliderColumnFilter.component";
10
+ export * from "./hooks/useCustomTable.hook";
11
+ export * from "./hooks/useOperations.hook";
10
12
  export * from "./table.component";
11
- export * from "./utils/useOperations.hook";