@vuu-ui/vuu-utils 2.0.0-alpha.1 → 2.1.0-alpha.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 (27) hide show
  1. package/cjs/packages/vuu-utils/src/data-editing/DataEditingProvider.js +25 -0
  2. package/cjs/packages/vuu-utils/src/data-editing/DataEditingProvider.js.map +1 -0
  3. package/cjs/packages/vuu-utils/src/data-editing/EditTracker.js +154 -0
  4. package/cjs/packages/vuu-utils/src/data-editing/EditTracker.js.map +1 -0
  5. package/cjs/packages/vuu-utils/src/data-editing/useEditableTable.js +69 -0
  6. package/cjs/packages/vuu-utils/src/data-editing/useEditableTable.js.map +1 -0
  7. package/cjs/packages/vuu-utils/src/index.js +11 -0
  8. package/cjs/packages/vuu-utils/src/index.js.map +1 -1
  9. package/cjs/packages/vuu-utils/src/useResizeObserver.js +134 -0
  10. package/cjs/packages/vuu-utils/src/useResizeObserver.js.map +1 -0
  11. package/esm/packages/vuu-utils/src/data-editing/DataEditingProvider.js +22 -0
  12. package/esm/packages/vuu-utils/src/data-editing/DataEditingProvider.js.map +1 -0
  13. package/esm/packages/vuu-utils/src/data-editing/EditTracker.js +152 -0
  14. package/esm/packages/vuu-utils/src/data-editing/EditTracker.js.map +1 -0
  15. package/esm/packages/vuu-utils/src/data-editing/useEditableTable.js +67 -0
  16. package/esm/packages/vuu-utils/src/data-editing/useEditableTable.js.map +1 -0
  17. package/esm/packages/vuu-utils/src/index.js +4 -0
  18. package/esm/packages/vuu-utils/src/index.js.map +1 -1
  19. package/esm/packages/vuu-utils/src/useResizeObserver.js +130 -0
  20. package/esm/packages/vuu-utils/src/useResizeObserver.js.map +1 -0
  21. package/package.json +6 -6
  22. package/types/data-editing/DataEditingProvider.d.ts +7 -0
  23. package/types/data-editing/EditButtons.d.ts +7 -0
  24. package/types/data-editing/EditTracker.d.ts +24 -0
  25. package/types/data-editing/useEditableTable.d.ts +15 -0
  26. package/types/index.d.ts +4 -0
  27. package/types/useResizeObserver.d.ts +15 -0
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var React = require('react');
5
+
6
+ const DataEditingContext = React.createContext(void 0);
7
+ const DataEditingProvider = ({
8
+ children,
9
+ editTracker
10
+ }) => {
11
+ return /* @__PURE__ */ jsxRuntime.jsx(DataEditingContext.Provider, { value: editTracker, children });
12
+ };
13
+ const useEditTracker = () => {
14
+ const editTracker = React.useContext(DataEditingContext);
15
+ if (editTracker === void 0) {
16
+ console.warn(
17
+ "[useEditTracker] no DataEditingContext in scope. You need to enclose editable component(s) with DataEditingProvider"
18
+ );
19
+ }
20
+ return editTracker;
21
+ };
22
+
23
+ exports.DataEditingProvider = DataEditingProvider;
24
+ exports.useEditTracker = useEditTracker;
25
+ //# sourceMappingURL=DataEditingProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DataEditingProvider.js","sources":["../../../../../../../packages/vuu-utils/src/data-editing/DataEditingProvider.tsx"],"sourcesContent":["import { createContext, ReactNode, useContext } from \"react\";\nimport { EditTracker } from \"./EditTracker\";\n\nconst DataEditingContext = createContext<EditTracker | undefined>(undefined);\n\nexport const DataEditingProvider = ({\n children,\n editTracker,\n}: {\n children: ReactNode;\n editTracker: EditTracker;\n}) => {\n return (\n <DataEditingContext.Provider value={editTracker}>\n {children}\n </DataEditingContext.Provider>\n );\n};\n\nexport const useEditTracker = () => {\n const editTracker = useContext(DataEditingContext);\n if (editTracker === undefined) {\n console.warn(\n \"[useEditTracker] no DataEditingContext in scope. You need to enclose editable component(s) with DataEditingProvider\",\n );\n }\n return editTracker;\n};\n"],"names":["createContext","useContext"],"mappings":";;;;;AAGA,MAAM,kBAAA,GAAqBA,oBAAuC,KAAS,CAAA,CAAA;AAEpE,MAAM,sBAAsB,CAAC;AAAA,EAClC,QAAA;AAAA,EACA;AACF,CAGM,KAAA;AACJ,EAAA,sCACG,kBAAmB,CAAA,QAAA,EAAnB,EAA4B,KAAA,EAAO,aACjC,QACH,EAAA,CAAA;AAEJ;AAEO,MAAM,iBAAiB,MAAM;AAClC,EAAM,MAAA,WAAA,GAAcC,iBAAW,kBAAkB,CAAA;AACjD,EAAA,IAAI,gBAAgB,KAAW,CAAA,EAAA;AAC7B,IAAQ,OAAA,CAAA,IAAA;AAAA,MACN;AAAA,KACF;AAAA;AAEF,EAAO,OAAA,WAAA;AACT;;;;;"}
@@ -0,0 +1,154 @@
1
+ 'use strict';
2
+
3
+ var eventEmitter = require('../event-emitter.js');
4
+ var protocolMessageUtils = require('../protocol-message-utils.js');
5
+
6
+ var __typeError = (msg) => {
7
+ throw TypeError(msg);
8
+ };
9
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
10
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
11
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
12
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
13
+ var _active, _rowEdits, _editCount, _dataSource, _inEditMode;
14
+ class EditTracker extends eventEmitter.EventEmitter {
15
+ constructor() {
16
+ super(...arguments);
17
+ __privateAdd(this, _active, false);
18
+ /**
19
+ * Row key => row edits
20
+ */
21
+ __privateAdd(this, _rowEdits, /* @__PURE__ */ new Map());
22
+ __privateAdd(this, _editCount, 0);
23
+ __privateAdd(this, _dataSource);
24
+ __privateAdd(this, _inEditMode, false);
25
+ }
26
+ get active() {
27
+ return __privateGet(this, _active);
28
+ }
29
+ set active(isActive) {
30
+ __privateSet(this, _active, isActive);
31
+ }
32
+ get editCount() {
33
+ return __privateGet(this, _editCount);
34
+ }
35
+ set editCount(val) {
36
+ if (val !== __privateGet(this, _editCount)) {
37
+ const oldCount = __privateGet(this, _editCount);
38
+ __privateSet(this, _editCount, val);
39
+ if (val === 0) {
40
+ this.emit("editState", "clean");
41
+ } else if (oldCount === 0) {
42
+ this.emit("editState", "dirty");
43
+ }
44
+ }
45
+ }
46
+ set dataSource(ds) {
47
+ __privateSet(this, _dataSource, ds);
48
+ }
49
+ clear() {
50
+ __privateGet(this, _rowEdits).clear();
51
+ __privateSet(this, _editCount, 0);
52
+ }
53
+ async enterEditMode() {
54
+ __privateSet(this, _inEditMode, true);
55
+ const rpcResponse = await __privateGet(this, _dataSource)?.rpcRequest?.({
56
+ type: "RPC_REQUEST",
57
+ rpcName: "ENTER_EDIT_MODE",
58
+ params: {}
59
+ });
60
+ if (protocolMessageUtils.isRpcSuccess(rpcResponse)) {
61
+ const { table: sessionTable } = rpcResponse.data;
62
+ return sessionTable;
63
+ } else {
64
+ console.log("fail");
65
+ }
66
+ }
67
+ get inEditMode() {
68
+ return __privateGet(this, _inEditMode);
69
+ }
70
+ get editState() {
71
+ return this.editCount === 0 ? "clean" : "dirty";
72
+ }
73
+ async cancelChanges() {
74
+ const rpcResponse = await __privateGet(this, _dataSource)?.rpcRequest?.({
75
+ type: "RPC_REQUEST",
76
+ rpcName: "EXIT_EDIT_MODE",
77
+ params: {}
78
+ });
79
+ this.clear();
80
+ return rpcResponse;
81
+ }
82
+ async saveChanges() {
83
+ const rpcResponse = await __privateGet(this, _dataSource)?.rpcRequest?.({
84
+ type: "RPC_REQUEST",
85
+ rpcName: "EXIT_EDIT_MODE",
86
+ params: { save: true }
87
+ });
88
+ this.clear();
89
+ return rpcResponse;
90
+ }
91
+ // TODO how do we deal with the '_edited' pattern
92
+ edit(key, columnName, originalValue, newValue) {
93
+ const rowEditDetails = __privateGet(this, _rowEdits).get(key);
94
+ if (rowEditDetails) {
95
+ const { cellEdits } = rowEditDetails;
96
+ const cellEdit = cellEdits.get(columnName);
97
+ if (cellEdit) {
98
+ if (newValue === cellEdit.originalValue) {
99
+ if (cellEdits.size === 1) {
100
+ __privateGet(this, _rowEdits).delete(key);
101
+ } else {
102
+ cellEdits.delete(columnName);
103
+ }
104
+ this.editCount = __privateGet(this, _editCount) - 1;
105
+ } else {
106
+ cellEdit.editedValue = newValue;
107
+ }
108
+ } else {
109
+ cellEdits.set(columnName, {
110
+ originalValue,
111
+ editedValue: newValue
112
+ });
113
+ this.editCount = __privateGet(this, _editCount) + 1;
114
+ }
115
+ } else {
116
+ __privateGet(this, _rowEdits).set(key, {
117
+ cellEdits: /* @__PURE__ */ new Map([
118
+ [columnName, { originalValue, editedValue: newValue }]
119
+ ])
120
+ });
121
+ this.editCount = __privateGet(this, _editCount) + 1;
122
+ }
123
+ }
124
+ async commit(key, columnName) {
125
+ const rowEditDetails = __privateGet(this, _rowEdits).get(key);
126
+ if (rowEditDetails) {
127
+ const { cellEdits } = rowEditDetails;
128
+ const cellEditValues = cellEdits.get(columnName);
129
+ if (cellEditValues) {
130
+ const { editedValue } = cellEditValues;
131
+ const rpcResponse = await __privateGet(this, _dataSource)?.rpcRequest?.({
132
+ type: "RPC_REQUEST",
133
+ rpcName: "editCell",
134
+ params: {
135
+ column: `${columnName}`,
136
+ data: editedValue,
137
+ key
138
+ }
139
+ });
140
+ return rpcResponse;
141
+ }
142
+ } else {
143
+ throw Error(`[EditTracker] commit, key ${key} not found `);
144
+ }
145
+ }
146
+ }
147
+ _active = new WeakMap();
148
+ _rowEdits = new WeakMap();
149
+ _editCount = new WeakMap();
150
+ _dataSource = new WeakMap();
151
+ _inEditMode = new WeakMap();
152
+
153
+ exports.EditTracker = EditTracker;
154
+ //# sourceMappingURL=EditTracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EditTracker.js","sources":["../../../../../../../packages/vuu-utils/src/data-editing/EditTracker.tsx"],"sourcesContent":["import { DataSource } from \"@vuu-ui/vuu-data-types\";\nimport type { VuuRowDataItemType, VuuTable } from \"@vuu-ui/vuu-protocol-types\";\nimport { EventEmitter } from \"../event-emitter\";\nimport { isRpcSuccess } from \"../protocol-message-utils\";\n\nexport type EditState = \"clean\" | \"dirty\";\n\ntype CellEdit = {\n originalValue: VuuRowDataItemType;\n editedValue: VuuRowDataItemType;\n};\n\ntype RowEditDetails = {\n /**\n * Column name => cell edit details\n */\n cellEdits: Map<string, CellEdit>;\n};\n\ntype EditTrackerEvents = {\n editState: (editState: EditState) => void;\n};\n\nexport class EditTracker extends EventEmitter<EditTrackerEvents> {\n #active = false;\n /**\n * Row key => row edits\n */\n #rowEdits = new Map<string, RowEditDetails>();\n #editCount = 0;\n #dataSource?: DataSource;\n #inEditMode = false;\n\n get active() {\n return this.#active;\n }\n set active(isActive: boolean) {\n this.#active = isActive;\n }\n get editCount() {\n return this.#editCount;\n }\n\n set editCount(val: number) {\n if (val !== this.#editCount) {\n const oldCount = this.#editCount;\n this.#editCount = val;\n if (val === 0) {\n this.emit(\"editState\", \"clean\");\n } else if (oldCount === 0) {\n this.emit(\"editState\", \"dirty\");\n }\n }\n }\n\n set dataSource(ds: DataSource) {\n this.#dataSource = ds;\n }\n\n clear() {\n this.#rowEdits.clear();\n this.#editCount = 0;\n }\n\n async enterEditMode() {\n this.#inEditMode = true;\n\n const rpcResponse = await this.#dataSource?.rpcRequest?.({\n type: \"RPC_REQUEST\",\n rpcName: \"ENTER_EDIT_MODE\",\n params: {},\n });\n\n if (isRpcSuccess(rpcResponse)) {\n const { table: sessionTable } = rpcResponse.data as { table: VuuTable };\n return sessionTable;\n } else {\n console.log(\"fail\");\n }\n }\n\n get inEditMode() {\n return this.#inEditMode;\n }\n\n get editState(): EditState {\n return this.editCount === 0 ? \"clean\" : \"dirty\";\n }\n\n async cancelChanges() {\n const rpcResponse = await this.#dataSource?.rpcRequest?.({\n type: \"RPC_REQUEST\",\n rpcName: \"EXIT_EDIT_MODE\",\n params: {},\n });\n this.clear();\n return rpcResponse;\n }\n\n async saveChanges() {\n const rpcResponse = await this.#dataSource?.rpcRequest?.({\n type: \"RPC_REQUEST\",\n rpcName: \"EXIT_EDIT_MODE\",\n params: { save: true },\n });\n this.clear();\n return rpcResponse;\n }\n\n // TODO how do we deal with the '_edited' pattern\n edit(\n key: string,\n columnName: string,\n originalValue: VuuRowDataItemType,\n newValue: VuuRowDataItemType,\n ) {\n const rowEditDetails = this.#rowEdits.get(key);\n if (rowEditDetails) {\n const { cellEdits } = rowEditDetails;\n const cellEdit = cellEdits.get(columnName);\n if (cellEdit) {\n if (newValue === cellEdit.originalValue) {\n if (cellEdits.size === 1) {\n this.#rowEdits.delete(key);\n } else {\n // re-editing a cell had removed the edit\n cellEdits.delete(columnName);\n }\n this.editCount = this.#editCount - 1;\n } else {\n cellEdit.editedValue = newValue;\n }\n } else {\n // row has already been edited, but this column has not\n cellEdits.set(columnName, {\n originalValue,\n editedValue: newValue,\n });\n this.editCount = this.#editCount + 1;\n }\n } else {\n this.#rowEdits.set(key, {\n cellEdits: new Map([\n [columnName, { originalValue, editedValue: newValue }],\n ]),\n });\n this.editCount = this.#editCount + 1;\n }\n }\n\n async commit(key: string, columnName: string) {\n const rowEditDetails = this.#rowEdits.get(key);\n if (rowEditDetails) {\n const { cellEdits } = rowEditDetails;\n const cellEditValues = cellEdits.get(columnName);\n if (cellEditValues) {\n const { editedValue } = cellEditValues;\n const rpcResponse = await this.#dataSource?.rpcRequest?.({\n type: \"RPC_REQUEST\",\n rpcName: \"editCell\",\n params: {\n column: `${columnName}`,\n data: editedValue,\n key,\n },\n });\n\n return rpcResponse;\n }\n } else {\n throw Error(`[EditTracker] commit, key ${key} not found `);\n }\n }\n}\n"],"names":["EventEmitter","isRpcSuccess"],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var EditTracker = require('./EditTracker.js');
5
+ var DataProvider = require('../context-definitions/DataProvider.js');
6
+ var protocolMessageUtils = require('../protocol-message-utils.js');
7
+
8
+ const useEditableTable = ({ table }) => {
9
+ const { VuuDataSource } = DataProvider.useData();
10
+ const [editMode, setEditMode] = React.useState("view");
11
+ const [sessionDataSource, setSessionDataSource] = React.useState(void 0);
12
+ const dataSource = React.useMemo(() => {
13
+ return new VuuDataSource({ table });
14
+ }, [table, VuuDataSource]);
15
+ const editTracker = React.useMemo(() => new EditTracker.EditTracker(), []);
16
+ React.useMemo(() => {
17
+ if (dataSource) {
18
+ editTracker.dataSource = dataSource;
19
+ }
20
+ }, [dataSource, editTracker]);
21
+ const onCancel = React.useCallback(() => {
22
+ editTracker.dataSource = dataSource;
23
+ editTracker.cancelChanges();
24
+ setEditMode("view");
25
+ setSessionDataSource(void 0);
26
+ dataSource.resume?.();
27
+ }, [dataSource, editTracker]);
28
+ const onSave = React.useCallback(async () => {
29
+ editTracker.dataSource = dataSource;
30
+ const response = await editTracker.saveChanges();
31
+ if (protocolMessageUtils.isRpcSuccess(response)) {
32
+ setEditMode("view");
33
+ setSessionDataSource(void 0);
34
+ dataSource.resume?.();
35
+ }
36
+ }, [dataSource, editTracker]);
37
+ const onToggleEditMode = React.useCallback(
38
+ async (e) => {
39
+ const toggleButton = e.target;
40
+ const editMode2 = toggleButton.value;
41
+ setEditMode(editMode2);
42
+ if (editMode2 === "edit") {
43
+ const sessionTable = await editTracker.enterEditMode();
44
+ if (sessionTable && dataSource.tableSchema) {
45
+ dataSource.suspend?.(false);
46
+ const sessionDataSource2 = new VuuDataSource({
47
+ columns: dataSource.columns,
48
+ table: sessionTable,
49
+ viewport: sessionTable.table
50
+ });
51
+ setSessionDataSource(sessionDataSource2);
52
+ editTracker.dataSource = sessionDataSource2;
53
+ }
54
+ }
55
+ },
56
+ [VuuDataSource, dataSource, editTracker]
57
+ );
58
+ return {
59
+ dataSource: sessionDataSource ?? dataSource,
60
+ editMode,
61
+ editTracker,
62
+ onCancel,
63
+ onSave,
64
+ onToggleEditMode
65
+ };
66
+ };
67
+
68
+ exports.useEditableTable = useEditableTable;
69
+ //# sourceMappingURL=useEditableTable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useEditableTable.js","sources":["../../../../../../../packages/vuu-utils/src/data-editing/useEditableTable.ts"],"sourcesContent":["import { VuuTable } from \"@vuu-ui/vuu-protocol-types\";\nimport { SyntheticEvent, useCallback, useMemo, useState } from \"react\";\nimport { EditTracker } from \"./EditTracker\";\nimport { useData } from \"../context-definitions/DataProvider\";\nimport { DataSource } from \"@vuu-ui/vuu-data-types\";\nimport { isRpcSuccess } from \"../protocol-message-utils\";\n\nexport type EditMode = \"edit\" | \"view\";\n\nexport const useEditableTable = ({ table }: { table: VuuTable }) => {\n const { VuuDataSource } = useData();\n const [editMode, setEditMode] = useState<EditMode>(\"view\");\n const [sessionDataSource, setSessionDataSource] = useState<\n DataSource | undefined\n >(undefined);\n\n const dataSource = useMemo(() => {\n return new VuuDataSource({ table });\n }, [table, VuuDataSource]);\n\n const editTracker = useMemo(() => new EditTracker(), []);\n\n useMemo(() => {\n if (dataSource) {\n editTracker.dataSource = dataSource;\n }\n }, [dataSource, editTracker]);\n\n const onCancel = useCallback(() => {\n editTracker.dataSource = dataSource;\n editTracker.cancelChanges();\n setEditMode(\"view\");\n setSessionDataSource(undefined);\n dataSource.resume?.();\n }, [dataSource, editTracker]);\n\n const onSave = useCallback(async () => {\n editTracker.dataSource = dataSource;\n const response = await editTracker.saveChanges();\n if (isRpcSuccess(response)) {\n setEditMode(\"view\");\n setSessionDataSource(undefined);\n dataSource.resume?.();\n }\n }, [dataSource, editTracker]);\n\n const onToggleEditMode = useCallback(\n async (e: SyntheticEvent<HTMLButtonElement>) => {\n const toggleButton = e.target as HTMLButtonElement;\n const editMode = toggleButton.value as EditMode;\n setEditMode(editMode);\n if (editMode === \"edit\") {\n const sessionTable = await editTracker.enterEditMode();\n if (sessionTable && dataSource.tableSchema) {\n dataSource.suspend?.(false);\n const sessionDataSource = new VuuDataSource({\n columns: dataSource.columns,\n table: sessionTable,\n viewport: sessionTable.table,\n });\n setSessionDataSource(sessionDataSource);\n editTracker.dataSource = sessionDataSource;\n }\n }\n },\n [VuuDataSource, dataSource, editTracker],\n );\n\n return {\n dataSource: sessionDataSource ?? dataSource,\n editMode,\n editTracker,\n onCancel,\n onSave,\n onToggleEditMode,\n };\n};\n"],"names":["useData","useState","useMemo","EditTracker","useCallback","isRpcSuccess","editMode","sessionDataSource"],"mappings":";;;;;;;AASO,MAAM,gBAAmB,GAAA,CAAC,EAAE,KAAA,EAAiC,KAAA;AAClE,EAAM,MAAA,EAAE,aAAc,EAAA,GAAIA,oBAAQ,EAAA;AAClC,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAIC,eAAmB,MAAM,CAAA;AACzD,EAAA,MAAM,CAAC,iBAAA,EAAmB,oBAAoB,CAAA,GAAIA,eAEhD,KAAS,CAAA,CAAA;AAEX,EAAM,MAAA,UAAA,GAAaC,cAAQ,MAAM;AAC/B,IAAA,OAAO,IAAI,aAAA,CAAc,EAAE,KAAA,EAAO,CAAA;AAAA,GACjC,EAAA,CAAC,KAAO,EAAA,aAAa,CAAC,CAAA;AAEzB,EAAA,MAAM,cAAcA,aAAQ,CAAA,MAAM,IAAIC,uBAAY,EAAA,EAAG,EAAE,CAAA;AAEvD,EAAAD,aAAA,CAAQ,MAAM;AACZ,IAAA,IAAI,UAAY,EAAA;AACd,MAAA,WAAA,CAAY,UAAa,GAAA,UAAA;AAAA;AAC3B,GACC,EAAA,CAAC,UAAY,EAAA,WAAW,CAAC,CAAA;AAE5B,EAAM,MAAA,QAAA,GAAWE,kBAAY,MAAM;AACjC,IAAA,WAAA,CAAY,UAAa,GAAA,UAAA;AACzB,IAAA,WAAA,CAAY,aAAc,EAAA;AAC1B,IAAA,WAAA,CAAY,MAAM,CAAA;AAClB,IAAA,oBAAA,CAAqB,KAAS,CAAA,CAAA;AAC9B,IAAA,UAAA,CAAW,MAAS,IAAA;AAAA,GACnB,EAAA,CAAC,UAAY,EAAA,WAAW,CAAC,CAAA;AAE5B,EAAM,MAAA,MAAA,GAASA,kBAAY,YAAY;AACrC,IAAA,WAAA,CAAY,UAAa,GAAA,UAAA;AACzB,IAAM,MAAA,QAAA,GAAW,MAAM,WAAA,CAAY,WAAY,EAAA;AAC/C,IAAI,IAAAC,iCAAA,CAAa,QAAQ,CAAG,EAAA;AAC1B,MAAA,WAAA,CAAY,MAAM,CAAA;AAClB,MAAA,oBAAA,CAAqB,KAAS,CAAA,CAAA;AAC9B,MAAA,UAAA,CAAW,MAAS,IAAA;AAAA;AACtB,GACC,EAAA,CAAC,UAAY,EAAA,WAAW,CAAC,CAAA;AAE5B,EAAA,MAAM,gBAAmB,GAAAD,iBAAA;AAAA,IACvB,OAAO,CAAyC,KAAA;AAC9C,MAAA,MAAM,eAAe,CAAE,CAAA,MAAA;AACvB,MAAA,MAAME,YAAW,YAAa,CAAA,KAAA;AAC9B,MAAA,WAAA,CAAYA,SAAQ,CAAA;AACpB,MAAA,IAAIA,cAAa,MAAQ,EAAA;AACvB,QAAM,MAAA,YAAA,GAAe,MAAM,WAAA,CAAY,aAAc,EAAA;AACrD,QAAI,IAAA,YAAA,IAAgB,WAAW,WAAa,EAAA;AAC1C,UAAA,UAAA,CAAW,UAAU,KAAK,CAAA;AAC1B,UAAMC,MAAAA,kBAAAA,GAAoB,IAAI,aAAc,CAAA;AAAA,YAC1C,SAAS,UAAW,CAAA,OAAA;AAAA,YACpB,KAAO,EAAA,YAAA;AAAA,YACP,UAAU,YAAa,CAAA;AAAA,WACxB,CAAA;AACD,UAAA,oBAAA,CAAqBA,kBAAiB,CAAA;AACtC,UAAA,WAAA,CAAY,UAAaA,GAAAA,kBAAAA;AAAA;AAC3B;AACF,KACF;AAAA,IACA,CAAC,aAAe,EAAA,UAAA,EAAY,WAAW;AAAA,GACzC;AAEA,EAAO,OAAA;AAAA,IACL,YAAY,iBAAqB,IAAA,UAAA;AAAA,IACjC,QAAA;AAAA,IACA,WAAA;AAAA,IACA,QAAA;AAAA,IACA,MAAA;AAAA,IACA;AAAA,GACF;AACF;;;;"}
@@ -12,6 +12,9 @@ var commonTypes = require('./common-types.js');
12
12
  var componentRegistry = require('./component-registry.js');
13
13
  var cookieUtils = require('./cookie-utils.js');
14
14
  var cssUtils = require('./css-utils.js');
15
+ var DataEditingProvider = require('./data-editing/DataEditingProvider.js');
16
+ var EditTracker = require('./data-editing/EditTracker.js');
17
+ var useEditableTable = require('./data-editing/useEditableTable.js');
15
18
  var dataUtils = require('./data-utils.js');
16
19
  var BaseDataSource = require('./datasource/BaseDataSource.js');
17
20
  var datasourceActionUtils = require('./datasource/datasource-action-utils.js');
@@ -62,6 +65,7 @@ var typeaheadUtils = require('./typeahead-utils.js');
62
65
  var urlUtils = require('./url-utils.js');
63
66
  var useId = require('./useId.js');
64
67
  var useLayoutEffectSkipFirst = require('./useLayoutEffectSkipFirst.js');
68
+ var useResizeObserver = require('./useResizeObserver.js');
65
69
  var useStateRef = require('./useStateRef.js');
66
70
  var DataContext = require('./context-definitions/DataContext.js');
67
71
  var DataProvider = require('./context-definitions/DataProvider.js');
@@ -197,6 +201,10 @@ exports.registerConfigurationEditor = componentRegistry.registerConfigurationEdi
197
201
  exports.getCookieValue = cookieUtils.getCookieValue;
198
202
  exports.setCookieValue = cookieUtils.setCookieValue;
199
203
  exports.importCSS = cssUtils.importCSS;
204
+ exports.DataEditingProvider = DataEditingProvider.DataEditingProvider;
205
+ exports.useEditTracker = DataEditingProvider.useEditTracker;
206
+ exports.EditTracker = EditTracker.EditTracker;
207
+ exports.useEditableTable = useEditableTable.useEditableTable;
200
208
  exports.DOWN1 = dataUtils.DOWN1;
201
209
  exports.DOWN2 = dataUtils.DOWN2;
202
210
  exports.UP1 = dataUtils.UP1;
@@ -434,6 +442,9 @@ exports.getUrlParameter = urlUtils.getUrlParameter;
434
442
  exports.hasUrlParameter = urlUtils.hasUrlParameter;
435
443
  exports.useId = useId.useId;
436
444
  exports.useLayoutEffectSkipFirst = useLayoutEffectSkipFirst.useLayoutEffectSkipFirst;
445
+ exports.WidthHeight = useResizeObserver.WidthHeight;
446
+ exports.WidthOnly = useResizeObserver.WidthOnly;
447
+ exports.useResizeObserver = useResizeObserver.useResizeObserver;
437
448
  exports.useStateRef = useStateRef.useStateRef;
438
449
  exports.DataContext = DataContext.DataContext;
439
450
  exports.DataProvider = DataProvider.DataProvider;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,134 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+
5
+ const WidthHeight = ["height", "width"];
6
+ const WidthOnly = ["width"];
7
+ const observedMap = /* @__PURE__ */ new Map();
8
+ const getTargetSize = (element, size, dimension) => {
9
+ switch (dimension) {
10
+ case "height":
11
+ return size.height;
12
+ case "clientHeight":
13
+ return Math.floor(element.clientHeight);
14
+ case "clientWidth":
15
+ return Math.floor(element.clientWidth);
16
+ case "contentHeight":
17
+ return size.contentHeight;
18
+ case "contentWidth":
19
+ return size.contentWidth;
20
+ case "scrollHeight":
21
+ return Math.ceil(Math.floor(element.scrollHeight));
22
+ case "scrollWidth":
23
+ return Math.ceil(Math.floor(element.scrollWidth));
24
+ case "width":
25
+ return size.width;
26
+ default:
27
+ return 0;
28
+ }
29
+ };
30
+ const resizeObserver = new ResizeObserver((entries) => {
31
+ for (const entry of entries) {
32
+ const { target, borderBoxSize, contentBoxSize } = entry;
33
+ const observedTarget = observedMap.get(target);
34
+ if (observedTarget) {
35
+ const [{ blockSize: height, inlineSize: width }] = borderBoxSize;
36
+ const [{ blockSize: contentHeight, inlineSize: contentWidth }] = contentBoxSize;
37
+ const { onResize, measurements } = observedTarget;
38
+ let sizeChanged = false;
39
+ for (const [dimension, size] of Object.entries(measurements)) {
40
+ const newSize = getTargetSize(
41
+ target,
42
+ { height, width, contentHeight, contentWidth },
43
+ dimension
44
+ );
45
+ if (newSize !== size) {
46
+ sizeChanged = true;
47
+ measurements[dimension] = newSize;
48
+ }
49
+ }
50
+ if (sizeChanged) {
51
+ onResize && onResize(measurements);
52
+ }
53
+ }
54
+ }
55
+ });
56
+ function useResizeObserver(ref, dimensions, onResize, reportInitialSize = false) {
57
+ const dimensionsRef = React.useRef(dimensions);
58
+ const measure = React.useCallback((target) => {
59
+ const { width, height } = target.getBoundingClientRect();
60
+ const { clientWidth: contentWidth, clientHeight: contentHeight } = target;
61
+ const flooredHeight = Math.floor(height);
62
+ const flooredWidth = Math.floor(width);
63
+ return dimensionsRef.current.reduce(
64
+ (map, dim) => {
65
+ map[dim] = getTargetSize(
66
+ target,
67
+ {
68
+ width: flooredWidth,
69
+ height: flooredHeight,
70
+ contentHeight,
71
+ contentWidth
72
+ },
73
+ dim
74
+ );
75
+ return map;
76
+ },
77
+ {}
78
+ );
79
+ }, []);
80
+ React.useEffect(() => {
81
+ const target = ref.current;
82
+ async function registerObserver() {
83
+ observedMap.set(target, { measurements: {} });
84
+ const observedTarget = observedMap.get(target);
85
+ if (observedTarget) {
86
+ const measurements = measure(target);
87
+ observedTarget.measurements = measurements;
88
+ resizeObserver.observe(target);
89
+ if (reportInitialSize) {
90
+ onResize(measurements);
91
+ }
92
+ } else {
93
+ console.log(
94
+ `%cuseResizeObserver an target expected to be under observation wa snot found. This warrants investigation`,
95
+ "font-weight:bold; color:red;"
96
+ );
97
+ }
98
+ }
99
+ if (target) {
100
+ if (observedMap.has(target)) {
101
+ console.log(
102
+ `useResizeObserver attemping to observe same element twice`,
103
+ {
104
+ target
105
+ }
106
+ );
107
+ }
108
+ registerObserver();
109
+ }
110
+ return () => {
111
+ if (target && observedMap.has(target)) {
112
+ resizeObserver.unobserve(target);
113
+ observedMap.delete(target);
114
+ }
115
+ };
116
+ }, [measure, ref]);
117
+ React.useEffect(() => {
118
+ const target = ref.current;
119
+ const record = observedMap.get(target);
120
+ if (record) {
121
+ if (dimensionsRef.current !== dimensions) {
122
+ dimensionsRef.current = dimensions;
123
+ const measurements = measure(target);
124
+ record.measurements = measurements;
125
+ }
126
+ record.onResize = onResize;
127
+ }
128
+ }, [dimensions, measure, ref, onResize]);
129
+ }
130
+
131
+ exports.WidthHeight = WidthHeight;
132
+ exports.WidthOnly = WidthOnly;
133
+ exports.useResizeObserver = useResizeObserver;
134
+ //# sourceMappingURL=useResizeObserver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useResizeObserver.js","sources":["../../../../../../packages/vuu-utils/src/useResizeObserver.ts"],"sourcesContent":["import { RefObject, useCallback, useEffect, useRef } from \"react\";\n\nexport const WidthHeight = [\"height\", \"width\"];\nexport const WidthOnly = [\"width\"];\n\nexport type measurements<T = string | number> = {\n height?: T;\n clientHeight?: number;\n clientWidth?: number;\n contentHeight?: number;\n contentWidth?: number;\n scrollHeight?: number;\n scrollWidth?: number;\n width?: T;\n};\ntype measuredDimension = keyof measurements<number>;\n\nexport type ResizeHandler = (measurements: measurements<number>) => void;\n\ntype observedDetails = {\n onResize?: ResizeHandler;\n measurements: measurements<number>;\n};\nconst observedMap = new Map<HTMLElement, observedDetails>();\n\nconst getTargetSize = (\n element: HTMLElement,\n size: {\n height: number;\n width: number;\n contentHeight: number;\n contentWidth: number;\n },\n dimension: measuredDimension,\n): number => {\n switch (dimension) {\n case \"height\":\n return size.height;\n case \"clientHeight\":\n return Math.floor(element.clientHeight);\n case \"clientWidth\":\n return Math.floor(element.clientWidth);\n case \"contentHeight\":\n return size.contentHeight;\n case \"contentWidth\":\n return size.contentWidth;\n case \"scrollHeight\":\n return Math.ceil(Math.floor(element.scrollHeight));\n case \"scrollWidth\":\n return Math.ceil(Math.floor(element.scrollWidth));\n case \"width\":\n return size.width;\n default:\n return 0;\n }\n};\n\n// TODO should we make this create-on-demand\nconst resizeObserver = new ResizeObserver((entries: ResizeObserverEntry[]) => {\n for (const entry of entries) {\n const { target, borderBoxSize, contentBoxSize } = entry;\n const observedTarget = observedMap.get(target as HTMLElement);\n if (observedTarget) {\n const [{ blockSize: height, inlineSize: width }] = borderBoxSize;\n const [{ blockSize: contentHeight, inlineSize: contentWidth }] =\n contentBoxSize;\n const { onResize, measurements } = observedTarget;\n let sizeChanged = false;\n for (const [dimension, size] of Object.entries(measurements)) {\n const newSize = getTargetSize(\n target as HTMLElement,\n { height, width, contentHeight, contentWidth },\n dimension as measuredDimension,\n );\n\n if (newSize !== size) {\n sizeChanged = true;\n measurements[dimension as measuredDimension] = newSize;\n }\n }\n if (sizeChanged) {\n // TODO only return measured sizes\n onResize && onResize(measurements);\n }\n }\n }\n});\n\n// TODO use an optional lag (default to false) to ask to fire onResize\n// with initial size\nexport function useResizeObserver(\n ref: RefObject<Element | HTMLElement | null>,\n dimensions: readonly string[],\n onResize: ResizeHandler,\n reportInitialSize = false,\n) {\n const dimensionsRef = useRef(dimensions);\n\n const measure = useCallback((target: HTMLElement): measurements<number> => {\n const { width, height } = target.getBoundingClientRect();\n const { clientWidth: contentWidth, clientHeight: contentHeight } = target;\n const flooredHeight = Math.floor(height);\n const flooredWidth = Math.floor(width);\n\n return dimensionsRef.current.reduce(\n (map: { [key: string]: number }, dim) => {\n map[dim] = getTargetSize(\n target,\n {\n width: flooredWidth,\n height: flooredHeight,\n contentHeight,\n contentWidth,\n },\n dim as measuredDimension,\n );\n return map;\n },\n {},\n );\n }, []);\n\n // TODO use ref to store resizeHandler here\n // resize handler registered with REsizeObserver will never change\n // use ref to store user onResize callback here\n // resizeHandler will call user callback.current\n\n // Keep this effect separate in case user inadvertently passes different\n // dimensions or callback instance each time - we only ever want to\n // initiate new observation when ref changes.\n useEffect(() => {\n const target = ref.current as HTMLElement;\n async function registerObserver() {\n // Create the map entry immediately. useEffect may fire below\n // before fonts are ready and attempt to update entry\n observedMap.set(target, { measurements: {} as measurements<number> });\n // await document.fonts.ready;\n const observedTarget = observedMap.get(target);\n if (observedTarget) {\n const measurements = measure(target);\n observedTarget.measurements = measurements;\n resizeObserver.observe(target);\n if (reportInitialSize) {\n onResize(measurements);\n }\n } else {\n console.log(\n `%cuseResizeObserver an target expected to be under observation wa snot found. This warrants investigation`,\n \"font-weight:bold; color:red;\",\n );\n }\n }\n\n if (target) {\n // TODO might we want multiple callers to attach a listener to the same element ?\n if (observedMap.has(target)) {\n console.log(\n `useResizeObserver attemping to observe same element twice`,\n {\n target,\n },\n );\n // throw Error(\n // \"useResizeObserver attemping to observe same element twice\"\n // );\n }\n // TODO set a pending entry on map\n registerObserver();\n }\n return () => {\n if (target && observedMap.has(target)) {\n resizeObserver.unobserve(target);\n observedMap.delete(target);\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [measure, ref]);\n\n useEffect(() => {\n const target = ref.current as HTMLElement;\n const record = observedMap.get(target);\n if (record) {\n if (dimensionsRef.current !== dimensions) {\n dimensionsRef.current = dimensions;\n const measurements = measure(target);\n record.measurements = measurements;\n }\n // Might not have changed, but no harm ...\n record.onResize = onResize;\n }\n }, [dimensions, measure, ref, onResize]);\n}\n"],"names":["useRef","useCallback","useEffect"],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { createContext, useContext } from 'react';
3
+
4
+ const DataEditingContext = createContext(void 0);
5
+ const DataEditingProvider = ({
6
+ children,
7
+ editTracker
8
+ }) => {
9
+ return /* @__PURE__ */ jsx(DataEditingContext.Provider, { value: editTracker, children });
10
+ };
11
+ const useEditTracker = () => {
12
+ const editTracker = useContext(DataEditingContext);
13
+ if (editTracker === void 0) {
14
+ console.warn(
15
+ "[useEditTracker] no DataEditingContext in scope. You need to enclose editable component(s) with DataEditingProvider"
16
+ );
17
+ }
18
+ return editTracker;
19
+ };
20
+
21
+ export { DataEditingProvider, useEditTracker };
22
+ //# sourceMappingURL=DataEditingProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DataEditingProvider.js","sources":["../../../../../../../packages/vuu-utils/src/data-editing/DataEditingProvider.tsx"],"sourcesContent":["import { createContext, ReactNode, useContext } from \"react\";\nimport { EditTracker } from \"./EditTracker\";\n\nconst DataEditingContext = createContext<EditTracker | undefined>(undefined);\n\nexport const DataEditingProvider = ({\n children,\n editTracker,\n}: {\n children: ReactNode;\n editTracker: EditTracker;\n}) => {\n return (\n <DataEditingContext.Provider value={editTracker}>\n {children}\n </DataEditingContext.Provider>\n );\n};\n\nexport const useEditTracker = () => {\n const editTracker = useContext(DataEditingContext);\n if (editTracker === undefined) {\n console.warn(\n \"[useEditTracker] no DataEditingContext in scope. You need to enclose editable component(s) with DataEditingProvider\",\n );\n }\n return editTracker;\n};\n"],"names":[],"mappings":";;;AAGA,MAAM,kBAAA,GAAqB,cAAuC,KAAS,CAAA,CAAA;AAEpE,MAAM,sBAAsB,CAAC;AAAA,EAClC,QAAA;AAAA,EACA;AACF,CAGM,KAAA;AACJ,EAAA,2BACG,kBAAmB,CAAA,QAAA,EAAnB,EAA4B,KAAA,EAAO,aACjC,QACH,EAAA,CAAA;AAEJ;AAEO,MAAM,iBAAiB,MAAM;AAClC,EAAM,MAAA,WAAA,GAAc,WAAW,kBAAkB,CAAA;AACjD,EAAA,IAAI,gBAAgB,KAAW,CAAA,EAAA;AAC7B,IAAQ,OAAA,CAAA,IAAA;AAAA,MACN;AAAA,KACF;AAAA;AAEF,EAAO,OAAA,WAAA;AACT;;;;"}
@@ -0,0 +1,152 @@
1
+ import { EventEmitter } from '../event-emitter.js';
2
+ import { isRpcSuccess } from '../protocol-message-utils.js';
3
+
4
+ var __typeError = (msg) => {
5
+ throw TypeError(msg);
6
+ };
7
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
8
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
9
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
10
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
11
+ var _active, _rowEdits, _editCount, _dataSource, _inEditMode;
12
+ class EditTracker extends EventEmitter {
13
+ constructor() {
14
+ super(...arguments);
15
+ __privateAdd(this, _active, false);
16
+ /**
17
+ * Row key => row edits
18
+ */
19
+ __privateAdd(this, _rowEdits, /* @__PURE__ */ new Map());
20
+ __privateAdd(this, _editCount, 0);
21
+ __privateAdd(this, _dataSource);
22
+ __privateAdd(this, _inEditMode, false);
23
+ }
24
+ get active() {
25
+ return __privateGet(this, _active);
26
+ }
27
+ set active(isActive) {
28
+ __privateSet(this, _active, isActive);
29
+ }
30
+ get editCount() {
31
+ return __privateGet(this, _editCount);
32
+ }
33
+ set editCount(val) {
34
+ if (val !== __privateGet(this, _editCount)) {
35
+ const oldCount = __privateGet(this, _editCount);
36
+ __privateSet(this, _editCount, val);
37
+ if (val === 0) {
38
+ this.emit("editState", "clean");
39
+ } else if (oldCount === 0) {
40
+ this.emit("editState", "dirty");
41
+ }
42
+ }
43
+ }
44
+ set dataSource(ds) {
45
+ __privateSet(this, _dataSource, ds);
46
+ }
47
+ clear() {
48
+ __privateGet(this, _rowEdits).clear();
49
+ __privateSet(this, _editCount, 0);
50
+ }
51
+ async enterEditMode() {
52
+ __privateSet(this, _inEditMode, true);
53
+ const rpcResponse = await __privateGet(this, _dataSource)?.rpcRequest?.({
54
+ type: "RPC_REQUEST",
55
+ rpcName: "ENTER_EDIT_MODE",
56
+ params: {}
57
+ });
58
+ if (isRpcSuccess(rpcResponse)) {
59
+ const { table: sessionTable } = rpcResponse.data;
60
+ return sessionTable;
61
+ } else {
62
+ console.log("fail");
63
+ }
64
+ }
65
+ get inEditMode() {
66
+ return __privateGet(this, _inEditMode);
67
+ }
68
+ get editState() {
69
+ return this.editCount === 0 ? "clean" : "dirty";
70
+ }
71
+ async cancelChanges() {
72
+ const rpcResponse = await __privateGet(this, _dataSource)?.rpcRequest?.({
73
+ type: "RPC_REQUEST",
74
+ rpcName: "EXIT_EDIT_MODE",
75
+ params: {}
76
+ });
77
+ this.clear();
78
+ return rpcResponse;
79
+ }
80
+ async saveChanges() {
81
+ const rpcResponse = await __privateGet(this, _dataSource)?.rpcRequest?.({
82
+ type: "RPC_REQUEST",
83
+ rpcName: "EXIT_EDIT_MODE",
84
+ params: { save: true }
85
+ });
86
+ this.clear();
87
+ return rpcResponse;
88
+ }
89
+ // TODO how do we deal with the '_edited' pattern
90
+ edit(key, columnName, originalValue, newValue) {
91
+ const rowEditDetails = __privateGet(this, _rowEdits).get(key);
92
+ if (rowEditDetails) {
93
+ const { cellEdits } = rowEditDetails;
94
+ const cellEdit = cellEdits.get(columnName);
95
+ if (cellEdit) {
96
+ if (newValue === cellEdit.originalValue) {
97
+ if (cellEdits.size === 1) {
98
+ __privateGet(this, _rowEdits).delete(key);
99
+ } else {
100
+ cellEdits.delete(columnName);
101
+ }
102
+ this.editCount = __privateGet(this, _editCount) - 1;
103
+ } else {
104
+ cellEdit.editedValue = newValue;
105
+ }
106
+ } else {
107
+ cellEdits.set(columnName, {
108
+ originalValue,
109
+ editedValue: newValue
110
+ });
111
+ this.editCount = __privateGet(this, _editCount) + 1;
112
+ }
113
+ } else {
114
+ __privateGet(this, _rowEdits).set(key, {
115
+ cellEdits: /* @__PURE__ */ new Map([
116
+ [columnName, { originalValue, editedValue: newValue }]
117
+ ])
118
+ });
119
+ this.editCount = __privateGet(this, _editCount) + 1;
120
+ }
121
+ }
122
+ async commit(key, columnName) {
123
+ const rowEditDetails = __privateGet(this, _rowEdits).get(key);
124
+ if (rowEditDetails) {
125
+ const { cellEdits } = rowEditDetails;
126
+ const cellEditValues = cellEdits.get(columnName);
127
+ if (cellEditValues) {
128
+ const { editedValue } = cellEditValues;
129
+ const rpcResponse = await __privateGet(this, _dataSource)?.rpcRequest?.({
130
+ type: "RPC_REQUEST",
131
+ rpcName: "editCell",
132
+ params: {
133
+ column: `${columnName}`,
134
+ data: editedValue,
135
+ key
136
+ }
137
+ });
138
+ return rpcResponse;
139
+ }
140
+ } else {
141
+ throw Error(`[EditTracker] commit, key ${key} not found `);
142
+ }
143
+ }
144
+ }
145
+ _active = new WeakMap();
146
+ _rowEdits = new WeakMap();
147
+ _editCount = new WeakMap();
148
+ _dataSource = new WeakMap();
149
+ _inEditMode = new WeakMap();
150
+
151
+ export { EditTracker };
152
+ //# sourceMappingURL=EditTracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EditTracker.js","sources":["../../../../../../../packages/vuu-utils/src/data-editing/EditTracker.tsx"],"sourcesContent":["import { DataSource } from \"@vuu-ui/vuu-data-types\";\nimport type { VuuRowDataItemType, VuuTable } from \"@vuu-ui/vuu-protocol-types\";\nimport { EventEmitter } from \"../event-emitter\";\nimport { isRpcSuccess } from \"../protocol-message-utils\";\n\nexport type EditState = \"clean\" | \"dirty\";\n\ntype CellEdit = {\n originalValue: VuuRowDataItemType;\n editedValue: VuuRowDataItemType;\n};\n\ntype RowEditDetails = {\n /**\n * Column name => cell edit details\n */\n cellEdits: Map<string, CellEdit>;\n};\n\ntype EditTrackerEvents = {\n editState: (editState: EditState) => void;\n};\n\nexport class EditTracker extends EventEmitter<EditTrackerEvents> {\n #active = false;\n /**\n * Row key => row edits\n */\n #rowEdits = new Map<string, RowEditDetails>();\n #editCount = 0;\n #dataSource?: DataSource;\n #inEditMode = false;\n\n get active() {\n return this.#active;\n }\n set active(isActive: boolean) {\n this.#active = isActive;\n }\n get editCount() {\n return this.#editCount;\n }\n\n set editCount(val: number) {\n if (val !== this.#editCount) {\n const oldCount = this.#editCount;\n this.#editCount = val;\n if (val === 0) {\n this.emit(\"editState\", \"clean\");\n } else if (oldCount === 0) {\n this.emit(\"editState\", \"dirty\");\n }\n }\n }\n\n set dataSource(ds: DataSource) {\n this.#dataSource = ds;\n }\n\n clear() {\n this.#rowEdits.clear();\n this.#editCount = 0;\n }\n\n async enterEditMode() {\n this.#inEditMode = true;\n\n const rpcResponse = await this.#dataSource?.rpcRequest?.({\n type: \"RPC_REQUEST\",\n rpcName: \"ENTER_EDIT_MODE\",\n params: {},\n });\n\n if (isRpcSuccess(rpcResponse)) {\n const { table: sessionTable } = rpcResponse.data as { table: VuuTable };\n return sessionTable;\n } else {\n console.log(\"fail\");\n }\n }\n\n get inEditMode() {\n return this.#inEditMode;\n }\n\n get editState(): EditState {\n return this.editCount === 0 ? \"clean\" : \"dirty\";\n }\n\n async cancelChanges() {\n const rpcResponse = await this.#dataSource?.rpcRequest?.({\n type: \"RPC_REQUEST\",\n rpcName: \"EXIT_EDIT_MODE\",\n params: {},\n });\n this.clear();\n return rpcResponse;\n }\n\n async saveChanges() {\n const rpcResponse = await this.#dataSource?.rpcRequest?.({\n type: \"RPC_REQUEST\",\n rpcName: \"EXIT_EDIT_MODE\",\n params: { save: true },\n });\n this.clear();\n return rpcResponse;\n }\n\n // TODO how do we deal with the '_edited' pattern\n edit(\n key: string,\n columnName: string,\n originalValue: VuuRowDataItemType,\n newValue: VuuRowDataItemType,\n ) {\n const rowEditDetails = this.#rowEdits.get(key);\n if (rowEditDetails) {\n const { cellEdits } = rowEditDetails;\n const cellEdit = cellEdits.get(columnName);\n if (cellEdit) {\n if (newValue === cellEdit.originalValue) {\n if (cellEdits.size === 1) {\n this.#rowEdits.delete(key);\n } else {\n // re-editing a cell had removed the edit\n cellEdits.delete(columnName);\n }\n this.editCount = this.#editCount - 1;\n } else {\n cellEdit.editedValue = newValue;\n }\n } else {\n // row has already been edited, but this column has not\n cellEdits.set(columnName, {\n originalValue,\n editedValue: newValue,\n });\n this.editCount = this.#editCount + 1;\n }\n } else {\n this.#rowEdits.set(key, {\n cellEdits: new Map([\n [columnName, { originalValue, editedValue: newValue }],\n ]),\n });\n this.editCount = this.#editCount + 1;\n }\n }\n\n async commit(key: string, columnName: string) {\n const rowEditDetails = this.#rowEdits.get(key);\n if (rowEditDetails) {\n const { cellEdits } = rowEditDetails;\n const cellEditValues = cellEdits.get(columnName);\n if (cellEditValues) {\n const { editedValue } = cellEditValues;\n const rpcResponse = await this.#dataSource?.rpcRequest?.({\n type: \"RPC_REQUEST\",\n rpcName: \"editCell\",\n params: {\n column: `${columnName}`,\n data: editedValue,\n key,\n },\n });\n\n return rpcResponse;\n }\n } else {\n throw Error(`[EditTracker] commit, key ${key} not found `);\n }\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ import { useState, useMemo, useCallback } from 'react';
2
+ import { EditTracker } from './EditTracker.js';
3
+ import { useData } from '../context-definitions/DataProvider.js';
4
+ import { isRpcSuccess } from '../protocol-message-utils.js';
5
+
6
+ const useEditableTable = ({ table }) => {
7
+ const { VuuDataSource } = useData();
8
+ const [editMode, setEditMode] = useState("view");
9
+ const [sessionDataSource, setSessionDataSource] = useState(void 0);
10
+ const dataSource = useMemo(() => {
11
+ return new VuuDataSource({ table });
12
+ }, [table, VuuDataSource]);
13
+ const editTracker = useMemo(() => new EditTracker(), []);
14
+ useMemo(() => {
15
+ if (dataSource) {
16
+ editTracker.dataSource = dataSource;
17
+ }
18
+ }, [dataSource, editTracker]);
19
+ const onCancel = useCallback(() => {
20
+ editTracker.dataSource = dataSource;
21
+ editTracker.cancelChanges();
22
+ setEditMode("view");
23
+ setSessionDataSource(void 0);
24
+ dataSource.resume?.();
25
+ }, [dataSource, editTracker]);
26
+ const onSave = useCallback(async () => {
27
+ editTracker.dataSource = dataSource;
28
+ const response = await editTracker.saveChanges();
29
+ if (isRpcSuccess(response)) {
30
+ setEditMode("view");
31
+ setSessionDataSource(void 0);
32
+ dataSource.resume?.();
33
+ }
34
+ }, [dataSource, editTracker]);
35
+ const onToggleEditMode = useCallback(
36
+ async (e) => {
37
+ const toggleButton = e.target;
38
+ const editMode2 = toggleButton.value;
39
+ setEditMode(editMode2);
40
+ if (editMode2 === "edit") {
41
+ const sessionTable = await editTracker.enterEditMode();
42
+ if (sessionTable && dataSource.tableSchema) {
43
+ dataSource.suspend?.(false);
44
+ const sessionDataSource2 = new VuuDataSource({
45
+ columns: dataSource.columns,
46
+ table: sessionTable,
47
+ viewport: sessionTable.table
48
+ });
49
+ setSessionDataSource(sessionDataSource2);
50
+ editTracker.dataSource = sessionDataSource2;
51
+ }
52
+ }
53
+ },
54
+ [VuuDataSource, dataSource, editTracker]
55
+ );
56
+ return {
57
+ dataSource: sessionDataSource ?? dataSource,
58
+ editMode,
59
+ editTracker,
60
+ onCancel,
61
+ onSave,
62
+ onToggleEditMode
63
+ };
64
+ };
65
+
66
+ export { useEditableTable };
67
+ //# sourceMappingURL=useEditableTable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useEditableTable.js","sources":["../../../../../../../packages/vuu-utils/src/data-editing/useEditableTable.ts"],"sourcesContent":["import { VuuTable } from \"@vuu-ui/vuu-protocol-types\";\nimport { SyntheticEvent, useCallback, useMemo, useState } from \"react\";\nimport { EditTracker } from \"./EditTracker\";\nimport { useData } from \"../context-definitions/DataProvider\";\nimport { DataSource } from \"@vuu-ui/vuu-data-types\";\nimport { isRpcSuccess } from \"../protocol-message-utils\";\n\nexport type EditMode = \"edit\" | \"view\";\n\nexport const useEditableTable = ({ table }: { table: VuuTable }) => {\n const { VuuDataSource } = useData();\n const [editMode, setEditMode] = useState<EditMode>(\"view\");\n const [sessionDataSource, setSessionDataSource] = useState<\n DataSource | undefined\n >(undefined);\n\n const dataSource = useMemo(() => {\n return new VuuDataSource({ table });\n }, [table, VuuDataSource]);\n\n const editTracker = useMemo(() => new EditTracker(), []);\n\n useMemo(() => {\n if (dataSource) {\n editTracker.dataSource = dataSource;\n }\n }, [dataSource, editTracker]);\n\n const onCancel = useCallback(() => {\n editTracker.dataSource = dataSource;\n editTracker.cancelChanges();\n setEditMode(\"view\");\n setSessionDataSource(undefined);\n dataSource.resume?.();\n }, [dataSource, editTracker]);\n\n const onSave = useCallback(async () => {\n editTracker.dataSource = dataSource;\n const response = await editTracker.saveChanges();\n if (isRpcSuccess(response)) {\n setEditMode(\"view\");\n setSessionDataSource(undefined);\n dataSource.resume?.();\n }\n }, [dataSource, editTracker]);\n\n const onToggleEditMode = useCallback(\n async (e: SyntheticEvent<HTMLButtonElement>) => {\n const toggleButton = e.target as HTMLButtonElement;\n const editMode = toggleButton.value as EditMode;\n setEditMode(editMode);\n if (editMode === \"edit\") {\n const sessionTable = await editTracker.enterEditMode();\n if (sessionTable && dataSource.tableSchema) {\n dataSource.suspend?.(false);\n const sessionDataSource = new VuuDataSource({\n columns: dataSource.columns,\n table: sessionTable,\n viewport: sessionTable.table,\n });\n setSessionDataSource(sessionDataSource);\n editTracker.dataSource = sessionDataSource;\n }\n }\n },\n [VuuDataSource, dataSource, editTracker],\n );\n\n return {\n dataSource: sessionDataSource ?? dataSource,\n editMode,\n editTracker,\n onCancel,\n onSave,\n onToggleEditMode,\n };\n};\n"],"names":["editMode","sessionDataSource"],"mappings":";;;;;AASO,MAAM,gBAAmB,GAAA,CAAC,EAAE,KAAA,EAAiC,KAAA;AAClE,EAAM,MAAA,EAAE,aAAc,EAAA,GAAI,OAAQ,EAAA;AAClC,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAmB,MAAM,CAAA;AACzD,EAAA,MAAM,CAAC,iBAAA,EAAmB,oBAAoB,CAAA,GAAI,SAEhD,KAAS,CAAA,CAAA;AAEX,EAAM,MAAA,UAAA,GAAa,QAAQ,MAAM;AAC/B,IAAA,OAAO,IAAI,aAAA,CAAc,EAAE,KAAA,EAAO,CAAA;AAAA,GACjC,EAAA,CAAC,KAAO,EAAA,aAAa,CAAC,CAAA;AAEzB,EAAA,MAAM,cAAc,OAAQ,CAAA,MAAM,IAAI,WAAY,EAAA,EAAG,EAAE,CAAA;AAEvD,EAAA,OAAA,CAAQ,MAAM;AACZ,IAAA,IAAI,UAAY,EAAA;AACd,MAAA,WAAA,CAAY,UAAa,GAAA,UAAA;AAAA;AAC3B,GACC,EAAA,CAAC,UAAY,EAAA,WAAW,CAAC,CAAA;AAE5B,EAAM,MAAA,QAAA,GAAW,YAAY,MAAM;AACjC,IAAA,WAAA,CAAY,UAAa,GAAA,UAAA;AACzB,IAAA,WAAA,CAAY,aAAc,EAAA;AAC1B,IAAA,WAAA,CAAY,MAAM,CAAA;AAClB,IAAA,oBAAA,CAAqB,KAAS,CAAA,CAAA;AAC9B,IAAA,UAAA,CAAW,MAAS,IAAA;AAAA,GACnB,EAAA,CAAC,UAAY,EAAA,WAAW,CAAC,CAAA;AAE5B,EAAM,MAAA,MAAA,GAAS,YAAY,YAAY;AACrC,IAAA,WAAA,CAAY,UAAa,GAAA,UAAA;AACzB,IAAM,MAAA,QAAA,GAAW,MAAM,WAAA,CAAY,WAAY,EAAA;AAC/C,IAAI,IAAA,YAAA,CAAa,QAAQ,CAAG,EAAA;AAC1B,MAAA,WAAA,CAAY,MAAM,CAAA;AAClB,MAAA,oBAAA,CAAqB,KAAS,CAAA,CAAA;AAC9B,MAAA,UAAA,CAAW,MAAS,IAAA;AAAA;AACtB,GACC,EAAA,CAAC,UAAY,EAAA,WAAW,CAAC,CAAA;AAE5B,EAAA,MAAM,gBAAmB,GAAA,WAAA;AAAA,IACvB,OAAO,CAAyC,KAAA;AAC9C,MAAA,MAAM,eAAe,CAAE,CAAA,MAAA;AACvB,MAAA,MAAMA,YAAW,YAAa,CAAA,KAAA;AAC9B,MAAA,WAAA,CAAYA,SAAQ,CAAA;AACpB,MAAA,IAAIA,cAAa,MAAQ,EAAA;AACvB,QAAM,MAAA,YAAA,GAAe,MAAM,WAAA,CAAY,aAAc,EAAA;AACrD,QAAI,IAAA,YAAA,IAAgB,WAAW,WAAa,EAAA;AAC1C,UAAA,UAAA,CAAW,UAAU,KAAK,CAAA;AAC1B,UAAMC,MAAAA,kBAAAA,GAAoB,IAAI,aAAc,CAAA;AAAA,YAC1C,SAAS,UAAW,CAAA,OAAA;AAAA,YACpB,KAAO,EAAA,YAAA;AAAA,YACP,UAAU,YAAa,CAAA;AAAA,WACxB,CAAA;AACD,UAAA,oBAAA,CAAqBA,kBAAiB,CAAA;AACtC,UAAA,WAAA,CAAY,UAAaA,GAAAA,kBAAAA;AAAA;AAC3B;AACF,KACF;AAAA,IACA,CAAC,aAAe,EAAA,UAAA,EAAY,WAAW;AAAA,GACzC;AAEA,EAAO,OAAA;AAAA,IACL,YAAY,iBAAqB,IAAA,UAAA;AAAA,IACjC,QAAA;AAAA,IACA,WAAA;AAAA,IACA,QAAA;AAAA,IACA,MAAA;AAAA,IACA;AAAA,GACF;AACF;;;;"}
@@ -10,6 +10,9 @@ export { MEASURES } from './common-types.js';
10
10
  export { getCellConfigPanelRenderer, getCellRenderer, getCellRendererOptions, getColumnHeaderContentRenderer, getColumnHeaderLabelRenderer, getConfigurationEditor, getEditRuleValidator, getLayoutComponent, getRegisteredCellRenderers, getRowClassNameGenerator, isContainer, isLayoutComponent, isView, registerComponent, registerConfigurationEditor } from './component-registry.js';
11
11
  export { getCookieValue, setCookieValue } from './cookie-utils.js';
12
12
  export { importCSS } from './css-utils.js';
13
+ export { DataEditingProvider, useEditTracker } from './data-editing/DataEditingProvider.js';
14
+ export { EditTracker } from './data-editing/EditTracker.js';
15
+ export { useEditableTable } from './data-editing/useEditableTable.js';
13
16
  export { DOWN1, DOWN2, UP1, UP2, asInteger, getMovingValueDirection, isValidNumber, numericTypeOfStringValue, shallowEquals, stringIsValidDecimal, stringIsValidInt, stringIsValidNumber } from './data-utils.js';
14
17
  export { BaseDataSource, defaultSuspenseProps } from './datasource/BaseDataSource.js';
15
18
  export { isViewportMenusAction, isVisualLinkCreatedAction, isVisualLinkRemovedAction, isVisualLinksAction, isVuuFeatureAction } from './datasource/datasource-action-utils.js';
@@ -60,6 +63,7 @@ export { NO_DATA_MATCH } from './typeahead-utils.js';
60
63
  export { getUrlParameter, hasUrlParameter } from './url-utils.js';
61
64
  export { useId } from './useId.js';
62
65
  export { useLayoutEffectSkipFirst } from './useLayoutEffectSkipFirst.js';
66
+ export { WidthHeight, WidthOnly, useResizeObserver } from './useResizeObserver.js';
63
67
  export { useStateRef } from './useStateRef.js';
64
68
  export { DataContext } from './context-definitions/DataContext.js';
65
69
  export { DataProvider, useData } from './context-definitions/DataProvider.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,130 @@
1
+ import { useRef, useCallback, useEffect } from 'react';
2
+
3
+ const WidthHeight = ["height", "width"];
4
+ const WidthOnly = ["width"];
5
+ const observedMap = /* @__PURE__ */ new Map();
6
+ const getTargetSize = (element, size, dimension) => {
7
+ switch (dimension) {
8
+ case "height":
9
+ return size.height;
10
+ case "clientHeight":
11
+ return Math.floor(element.clientHeight);
12
+ case "clientWidth":
13
+ return Math.floor(element.clientWidth);
14
+ case "contentHeight":
15
+ return size.contentHeight;
16
+ case "contentWidth":
17
+ return size.contentWidth;
18
+ case "scrollHeight":
19
+ return Math.ceil(Math.floor(element.scrollHeight));
20
+ case "scrollWidth":
21
+ return Math.ceil(Math.floor(element.scrollWidth));
22
+ case "width":
23
+ return size.width;
24
+ default:
25
+ return 0;
26
+ }
27
+ };
28
+ const resizeObserver = new ResizeObserver((entries) => {
29
+ for (const entry of entries) {
30
+ const { target, borderBoxSize, contentBoxSize } = entry;
31
+ const observedTarget = observedMap.get(target);
32
+ if (observedTarget) {
33
+ const [{ blockSize: height, inlineSize: width }] = borderBoxSize;
34
+ const [{ blockSize: contentHeight, inlineSize: contentWidth }] = contentBoxSize;
35
+ const { onResize, measurements } = observedTarget;
36
+ let sizeChanged = false;
37
+ for (const [dimension, size] of Object.entries(measurements)) {
38
+ const newSize = getTargetSize(
39
+ target,
40
+ { height, width, contentHeight, contentWidth },
41
+ dimension
42
+ );
43
+ if (newSize !== size) {
44
+ sizeChanged = true;
45
+ measurements[dimension] = newSize;
46
+ }
47
+ }
48
+ if (sizeChanged) {
49
+ onResize && onResize(measurements);
50
+ }
51
+ }
52
+ }
53
+ });
54
+ function useResizeObserver(ref, dimensions, onResize, reportInitialSize = false) {
55
+ const dimensionsRef = useRef(dimensions);
56
+ const measure = useCallback((target) => {
57
+ const { width, height } = target.getBoundingClientRect();
58
+ const { clientWidth: contentWidth, clientHeight: contentHeight } = target;
59
+ const flooredHeight = Math.floor(height);
60
+ const flooredWidth = Math.floor(width);
61
+ return dimensionsRef.current.reduce(
62
+ (map, dim) => {
63
+ map[dim] = getTargetSize(
64
+ target,
65
+ {
66
+ width: flooredWidth,
67
+ height: flooredHeight,
68
+ contentHeight,
69
+ contentWidth
70
+ },
71
+ dim
72
+ );
73
+ return map;
74
+ },
75
+ {}
76
+ );
77
+ }, []);
78
+ useEffect(() => {
79
+ const target = ref.current;
80
+ async function registerObserver() {
81
+ observedMap.set(target, { measurements: {} });
82
+ const observedTarget = observedMap.get(target);
83
+ if (observedTarget) {
84
+ const measurements = measure(target);
85
+ observedTarget.measurements = measurements;
86
+ resizeObserver.observe(target);
87
+ if (reportInitialSize) {
88
+ onResize(measurements);
89
+ }
90
+ } else {
91
+ console.log(
92
+ `%cuseResizeObserver an target expected to be under observation wa snot found. This warrants investigation`,
93
+ "font-weight:bold; color:red;"
94
+ );
95
+ }
96
+ }
97
+ if (target) {
98
+ if (observedMap.has(target)) {
99
+ console.log(
100
+ `useResizeObserver attemping to observe same element twice`,
101
+ {
102
+ target
103
+ }
104
+ );
105
+ }
106
+ registerObserver();
107
+ }
108
+ return () => {
109
+ if (target && observedMap.has(target)) {
110
+ resizeObserver.unobserve(target);
111
+ observedMap.delete(target);
112
+ }
113
+ };
114
+ }, [measure, ref]);
115
+ useEffect(() => {
116
+ const target = ref.current;
117
+ const record = observedMap.get(target);
118
+ if (record) {
119
+ if (dimensionsRef.current !== dimensions) {
120
+ dimensionsRef.current = dimensions;
121
+ const measurements = measure(target);
122
+ record.measurements = measurements;
123
+ }
124
+ record.onResize = onResize;
125
+ }
126
+ }, [dimensions, measure, ref, onResize]);
127
+ }
128
+
129
+ export { WidthHeight, WidthOnly, useResizeObserver };
130
+ //# sourceMappingURL=useResizeObserver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useResizeObserver.js","sources":["../../../../../../packages/vuu-utils/src/useResizeObserver.ts"],"sourcesContent":["import { RefObject, useCallback, useEffect, useRef } from \"react\";\n\nexport const WidthHeight = [\"height\", \"width\"];\nexport const WidthOnly = [\"width\"];\n\nexport type measurements<T = string | number> = {\n height?: T;\n clientHeight?: number;\n clientWidth?: number;\n contentHeight?: number;\n contentWidth?: number;\n scrollHeight?: number;\n scrollWidth?: number;\n width?: T;\n};\ntype measuredDimension = keyof measurements<number>;\n\nexport type ResizeHandler = (measurements: measurements<number>) => void;\n\ntype observedDetails = {\n onResize?: ResizeHandler;\n measurements: measurements<number>;\n};\nconst observedMap = new Map<HTMLElement, observedDetails>();\n\nconst getTargetSize = (\n element: HTMLElement,\n size: {\n height: number;\n width: number;\n contentHeight: number;\n contentWidth: number;\n },\n dimension: measuredDimension,\n): number => {\n switch (dimension) {\n case \"height\":\n return size.height;\n case \"clientHeight\":\n return Math.floor(element.clientHeight);\n case \"clientWidth\":\n return Math.floor(element.clientWidth);\n case \"contentHeight\":\n return size.contentHeight;\n case \"contentWidth\":\n return size.contentWidth;\n case \"scrollHeight\":\n return Math.ceil(Math.floor(element.scrollHeight));\n case \"scrollWidth\":\n return Math.ceil(Math.floor(element.scrollWidth));\n case \"width\":\n return size.width;\n default:\n return 0;\n }\n};\n\n// TODO should we make this create-on-demand\nconst resizeObserver = new ResizeObserver((entries: ResizeObserverEntry[]) => {\n for (const entry of entries) {\n const { target, borderBoxSize, contentBoxSize } = entry;\n const observedTarget = observedMap.get(target as HTMLElement);\n if (observedTarget) {\n const [{ blockSize: height, inlineSize: width }] = borderBoxSize;\n const [{ blockSize: contentHeight, inlineSize: contentWidth }] =\n contentBoxSize;\n const { onResize, measurements } = observedTarget;\n let sizeChanged = false;\n for (const [dimension, size] of Object.entries(measurements)) {\n const newSize = getTargetSize(\n target as HTMLElement,\n { height, width, contentHeight, contentWidth },\n dimension as measuredDimension,\n );\n\n if (newSize !== size) {\n sizeChanged = true;\n measurements[dimension as measuredDimension] = newSize;\n }\n }\n if (sizeChanged) {\n // TODO only return measured sizes\n onResize && onResize(measurements);\n }\n }\n }\n});\n\n// TODO use an optional lag (default to false) to ask to fire onResize\n// with initial size\nexport function useResizeObserver(\n ref: RefObject<Element | HTMLElement | null>,\n dimensions: readonly string[],\n onResize: ResizeHandler,\n reportInitialSize = false,\n) {\n const dimensionsRef = useRef(dimensions);\n\n const measure = useCallback((target: HTMLElement): measurements<number> => {\n const { width, height } = target.getBoundingClientRect();\n const { clientWidth: contentWidth, clientHeight: contentHeight } = target;\n const flooredHeight = Math.floor(height);\n const flooredWidth = Math.floor(width);\n\n return dimensionsRef.current.reduce(\n (map: { [key: string]: number }, dim) => {\n map[dim] = getTargetSize(\n target,\n {\n width: flooredWidth,\n height: flooredHeight,\n contentHeight,\n contentWidth,\n },\n dim as measuredDimension,\n );\n return map;\n },\n {},\n );\n }, []);\n\n // TODO use ref to store resizeHandler here\n // resize handler registered with REsizeObserver will never change\n // use ref to store user onResize callback here\n // resizeHandler will call user callback.current\n\n // Keep this effect separate in case user inadvertently passes different\n // dimensions or callback instance each time - we only ever want to\n // initiate new observation when ref changes.\n useEffect(() => {\n const target = ref.current as HTMLElement;\n async function registerObserver() {\n // Create the map entry immediately. useEffect may fire below\n // before fonts are ready and attempt to update entry\n observedMap.set(target, { measurements: {} as measurements<number> });\n // await document.fonts.ready;\n const observedTarget = observedMap.get(target);\n if (observedTarget) {\n const measurements = measure(target);\n observedTarget.measurements = measurements;\n resizeObserver.observe(target);\n if (reportInitialSize) {\n onResize(measurements);\n }\n } else {\n console.log(\n `%cuseResizeObserver an target expected to be under observation wa snot found. This warrants investigation`,\n \"font-weight:bold; color:red;\",\n );\n }\n }\n\n if (target) {\n // TODO might we want multiple callers to attach a listener to the same element ?\n if (observedMap.has(target)) {\n console.log(\n `useResizeObserver attemping to observe same element twice`,\n {\n target,\n },\n );\n // throw Error(\n // \"useResizeObserver attemping to observe same element twice\"\n // );\n }\n // TODO set a pending entry on map\n registerObserver();\n }\n return () => {\n if (target && observedMap.has(target)) {\n resizeObserver.unobserve(target);\n observedMap.delete(target);\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [measure, ref]);\n\n useEffect(() => {\n const target = ref.current as HTMLElement;\n const record = observedMap.get(target);\n if (record) {\n if (dimensionsRef.current !== dimensions) {\n dimensionsRef.current = dimensions;\n const measurements = measure(target);\n record.measurements = measurements;\n }\n // Might not have changed, but no harm ...\n record.onResize = onResize;\n }\n }, [dimensions, measure, ref, onResize]);\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
- "version": "2.0.0-alpha.1",
2
+ "version": "2.1.0-alpha.1",
3
3
  "author": "heswell",
4
4
  "license": "Apache-2.0",
5
5
  "types": "types/index.d.ts",
6
6
  "devDependencies": {
7
- "@vuu-ui/vuu-data-types": "2.0.0-alpha.1",
8
- "@vuu-ui/vuu-table-types": "2.0.0-alpha.1",
9
- "@vuu-ui/vuu-filter-types": "2.0.0-alpha.1",
10
- "@vuu-ui/vuu-protocol-types": "2.0.0-alpha.1"
7
+ "@vuu-ui/vuu-data-types": "2.1.0-alpha.1",
8
+ "@vuu-ui/vuu-table-types": "2.1.0-alpha.1",
9
+ "@vuu-ui/vuu-filter-types": "2.1.0-alpha.1",
10
+ "@vuu-ui/vuu-protocol-types": "2.1.0-alpha.1"
11
11
  },
12
12
  "peerDependencies": {
13
13
  "@internationalized/date": "^3.0.0",
14
- "@vuu-ui/vuu-filter-parser": "2.0.0-alpha.1",
14
+ "@vuu-ui/vuu-filter-parser": "2.1.0-alpha.1",
15
15
  "clsx": "^2.0.0",
16
16
  "react": "^19.2.3",
17
17
  "react-dom": "^19.2.3"
@@ -0,0 +1,7 @@
1
+ import { ReactNode } from "react";
2
+ import { EditTracker } from "./EditTracker";
3
+ export declare const DataEditingProvider: ({ children, editTracker, }: {
4
+ children: ReactNode;
5
+ editTracker: EditTracker;
6
+ }) => import("react/jsx-runtime").JSX.Element;
7
+ export declare const useEditTracker: () => EditTracker | undefined;
@@ -0,0 +1,7 @@
1
+ import { EditTracker } from "./EditTracker";
2
+ export interface EditButtonProps {
3
+ editTracker?: EditTracker;
4
+ onCancel: () => void;
5
+ onSave: () => void;
6
+ }
7
+ export declare const EditButtons: ({ editTracker, onCancel, onSave, }: EditButtonProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,24 @@
1
+ import { DataSource } from "@vuu-ui/vuu-data-types";
2
+ import type { VuuRowDataItemType, VuuTable } from "@vuu-ui/vuu-protocol-types";
3
+ import { EventEmitter } from "../event-emitter";
4
+ export type EditState = "clean" | "dirty";
5
+ type EditTrackerEvents = {
6
+ editState: (editState: EditState) => void;
7
+ };
8
+ export declare class EditTracker extends EventEmitter<EditTrackerEvents> {
9
+ #private;
10
+ get active(): boolean;
11
+ set active(isActive: boolean);
12
+ get editCount(): number;
13
+ set editCount(val: number);
14
+ set dataSource(ds: DataSource);
15
+ clear(): void;
16
+ enterEditMode(): Promise<VuuTable | undefined>;
17
+ get inEditMode(): boolean;
18
+ get editState(): EditState;
19
+ cancelChanges(): Promise<import("@vuu-ui/vuu-protocol-types").RpcResultSuccess | import("@vuu-ui/vuu-protocol-types").RpcResultError | undefined>;
20
+ saveChanges(): Promise<import("@vuu-ui/vuu-protocol-types").RpcResultSuccess | import("@vuu-ui/vuu-protocol-types").RpcResultError | undefined>;
21
+ edit(key: string, columnName: string, originalValue: VuuRowDataItemType, newValue: VuuRowDataItemType): void;
22
+ commit(key: string, columnName: string): Promise<import("@vuu-ui/vuu-protocol-types").RpcResultSuccess | import("@vuu-ui/vuu-protocol-types").RpcResultError | undefined>;
23
+ }
24
+ export {};
@@ -0,0 +1,15 @@
1
+ import { VuuTable } from "@vuu-ui/vuu-protocol-types";
2
+ import { SyntheticEvent } from "react";
3
+ import { EditTracker } from "./EditTracker";
4
+ import { DataSource } from "@vuu-ui/vuu-data-types";
5
+ export type EditMode = "edit" | "view";
6
+ export declare const useEditableTable: ({ table }: {
7
+ table: VuuTable;
8
+ }) => {
9
+ dataSource: DataSource;
10
+ editMode: EditMode;
11
+ editTracker: EditTracker;
12
+ onCancel: () => void;
13
+ onSave: () => Promise<void>;
14
+ onToggleEditMode: (e: SyntheticEvent<HTMLButtonElement>) => Promise<void>;
15
+ };
package/types/index.d.ts CHANGED
@@ -12,6 +12,9 @@ export * from "./common-types";
12
12
  export * from "./component-registry";
13
13
  export * from "./cookie-utils";
14
14
  export * from "./css-utils";
15
+ export { DataEditingProvider, useEditTracker, } from "./data-editing/DataEditingProvider";
16
+ export { EditTracker, type EditState } from "./data-editing/EditTracker";
17
+ export { useEditableTable } from "./data-editing/useEditableTable";
15
18
  export * from "./data-utils";
16
19
  export * from "./datasource/BaseDataSource";
17
20
  export * from "./datasource/datasource-action-utils";
@@ -62,6 +65,7 @@ export * from "./url-utils";
62
65
  export * from "./useId";
63
66
  export * from "./useLayoutEffectSkipFirst";
64
67
  export * from "./user-types";
68
+ export { useResizeObserver, WidthHeight, WidthOnly, type measurements, type ResizeHandler, } from "./useResizeObserver";
65
69
  export * from "./useStateRef";
66
70
  /** Context declarations hosted in utils to minimize intra package dependencies */
67
71
  export { DataContext } from "./context-definitions/DataContext";
@@ -0,0 +1,15 @@
1
+ import { RefObject } from "react";
2
+ export declare const WidthHeight: string[];
3
+ export declare const WidthOnly: string[];
4
+ export type measurements<T = string | number> = {
5
+ height?: T;
6
+ clientHeight?: number;
7
+ clientWidth?: number;
8
+ contentHeight?: number;
9
+ contentWidth?: number;
10
+ scrollHeight?: number;
11
+ scrollWidth?: number;
12
+ width?: T;
13
+ };
14
+ export type ResizeHandler = (measurements: measurements<number>) => void;
15
+ export declare function useResizeObserver(ref: RefObject<Element | HTMLElement | null>, dimensions: readonly string[], onResize: ResizeHandler, reportInitialSize?: boolean): void;