@vuu-ui/vuu-datatable 2.1.19-beta.1 → 2.1.19-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.1.19-beta.1",
2
+ "version": "2.1.19-beta.2",
3
3
  "author": "heswell",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
@@ -7,16 +7,16 @@
7
7
  "@salt-ds/core": "1.54.1",
8
8
  "@salt-ds/styles": "0.2.1",
9
9
  "@salt-ds/window": "0.1.1",
10
- "@vuu-ui/vuu-data-local": "2.1.19-beta.1",
11
- "@vuu-ui/vuu-table-types": "2.1.19-beta.1",
12
- "@vuu-ui/vuu-filters": "2.1.19-beta.1",
13
- "@vuu-ui/vuu-layout": "2.1.19-beta.1",
14
- "@vuu-ui/vuu-popups": "2.1.19-beta.1",
15
- "@vuu-ui/vuu-shell": "2.1.19-beta.1",
16
- "@vuu-ui/vuu-table": "2.1.19-beta.1",
17
- "@vuu-ui/vuu-table-extras": "2.1.19-beta.1",
18
- "@vuu-ui/vuu-ui-controls": "2.1.19-beta.1",
19
- "@vuu-ui/vuu-utils": "2.1.19-beta.1"
10
+ "@vuu-ui/vuu-data-local": "2.1.19-beta.2",
11
+ "@vuu-ui/vuu-table-types": "2.1.19-beta.2",
12
+ "@vuu-ui/vuu-filters": "2.1.19-beta.2",
13
+ "@vuu-ui/vuu-layout": "2.1.19-beta.2",
14
+ "@vuu-ui/vuu-popups": "2.1.19-beta.2",
15
+ "@vuu-ui/vuu-shell": "2.1.19-beta.2",
16
+ "@vuu-ui/vuu-table": "2.1.19-beta.2",
17
+ "@vuu-ui/vuu-table-extras": "2.1.19-beta.2",
18
+ "@vuu-ui/vuu-ui-controls": "2.1.19-beta.2",
19
+ "@vuu-ui/vuu-utils": "2.1.19-beta.2"
20
20
  },
21
21
  "peerDependencies": {
22
22
  "clsx": "^2.0.0",
@@ -25,13 +25,12 @@
25
25
  },
26
26
  "files": [
27
27
  "README.md",
28
- "esm",
29
- "cjs",
28
+ "src",
30
29
  "/types"
31
30
  ],
32
31
  "exports": {
33
32
  ".": {
34
- "import": "./index.js",
33
+ "import": "./src/index.js",
35
34
  "types": "./types/index.d.ts"
36
35
  }
37
36
  },
@@ -0,0 +1,40 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { FilterBar } from "@vuu-ui/vuu-filters";
3
+ import { Table } from "@vuu-ui/vuu-table";
4
+ import { useFilterTable } from "./useFilterTable.js";
5
+ import { DataSourceProvider } from "@vuu-ui/vuu-utils";
6
+ const classBase = "vuuFilterTable";
7
+ const filterTableStyles = {
8
+ "--vuuFilterBar-flex": "0 0 auto",
9
+ "--vuuMeasuredContainer-flex": "1 1 auto",
10
+ display: "flex",
11
+ flexDirection: "column"
12
+ };
13
+ const FilterTable = ({ FilterBarProps, TableProps, style: styleProps, ...htmlAttributes })=>{
14
+ const { filterBarProps } = useFilterTable({
15
+ TableProps,
16
+ FilterBarProps
17
+ });
18
+ return /*#__PURE__*/ jsx(DataSourceProvider, {
19
+ dataSource: TableProps.dataSource,
20
+ children: /*#__PURE__*/ jsxs("div", {
21
+ ...htmlAttributes,
22
+ className: classBase,
23
+ style: {
24
+ ...styleProps,
25
+ ...filterTableStyles
26
+ },
27
+ children: [
28
+ /*#__PURE__*/ jsx(FilterBar, {
29
+ ...filterBarProps
30
+ }),
31
+ /*#__PURE__*/ jsx(Table, {
32
+ ...TableProps,
33
+ height: "auto",
34
+ width: "auto"
35
+ })
36
+ ]
37
+ })
38
+ });
39
+ };
40
+ export { FilterTable };
@@ -0,0 +1,2 @@
1
+ export * from "./FilterTable.js";
2
+ export * from "./usePersistFilterState.js";
@@ -0,0 +1,23 @@
1
+ import { useCallback } from "react";
2
+ const useFilterTable = ({ FilterBarProps, TableProps: { config: { columns }, dataSource } })=>{
3
+ const handleApplyFilter = useCallback((filter)=>{
4
+ dataSource.setFilter?.(filter);
5
+ }, [
6
+ dataSource
7
+ ]);
8
+ const handleClearFilter = useCallback(()=>{
9
+ dataSource.clearFilter?.();
10
+ }, [
11
+ dataSource
12
+ ]);
13
+ const filterBarProps = {
14
+ ...FilterBarProps,
15
+ columnDescriptors: FilterBarProps?.columnDescriptors ?? columns,
16
+ onApplyFilter: handleApplyFilter,
17
+ onClearFilter: handleClearFilter
18
+ };
19
+ return {
20
+ filterBarProps
21
+ };
22
+ };
23
+ export { useFilterTable };
@@ -0,0 +1,139 @@
1
+ import { useViewContext } from "@vuu-ui/vuu-layout";
2
+ import { useWorkspace } from "@vuu-ui/vuu-shell";
3
+ import { useCallback, useMemo, useState } from "react";
4
+ const NO_CONFIG = {};
5
+ const hasFilterWithName = (filters, name)=>-1 !== filters.findIndex((f)=>f.name === name);
6
+ const usePersistFilterState = ({ tableSchema })=>{
7
+ const { load, save } = useViewContext();
8
+ const { getApplicationSettings, saveApplicationSettings } = useWorkspace();
9
+ const { "filterbar-config": filterbarConfigFromState } = useMemo(()=>load?.() ?? NO_CONFIG, [
10
+ load
11
+ ]);
12
+ const [filterState, setFilterState] = useState({
13
+ filters: filterbarConfigFromState?.filterState?.filters ?? [],
14
+ activeIndices: filterbarConfigFromState?.filterState?.activeIndices ?? []
15
+ });
16
+ const savedFilters = useMemo(()=>{
17
+ const { table: { module, table } } = tableSchema;
18
+ const savedFilters = getApplicationSettings("filters");
19
+ const key = `${module}:${table}`;
20
+ return savedFilters?.[key] ?? [];
21
+ }, [
22
+ getApplicationSettings,
23
+ tableSchema
24
+ ]);
25
+ const removeFilterFromSettings = useCallback((filter)=>{
26
+ if (!tableSchema || !filter.name) return;
27
+ const savedFilters = getApplicationSettings("filters");
28
+ if (!savedFilters) return;
29
+ const { module, table } = tableSchema.table;
30
+ const key = `${module}:${table}`;
31
+ if (hasFilterWithName(savedFilters[key], filter.name)) {
32
+ const newSavedFilters = {
33
+ ...savedFilters,
34
+ [key]: savedFilters[key].filter((f)=>f.name !== filter.name)
35
+ };
36
+ saveApplicationSettings(newSavedFilters, "filters");
37
+ }
38
+ }, [
39
+ getApplicationSettings,
40
+ saveApplicationSettings,
41
+ tableSchema
42
+ ]);
43
+ const saveFilterToSettings = useCallback((filter, name)=>{
44
+ if (tableSchema && name) {
45
+ const savedFilters = getApplicationSettings("filters");
46
+ let newFilters = savedFilters;
47
+ const { module, table } = tableSchema.table;
48
+ const key = `${module}:${table}`;
49
+ newFilters = savedFilters ? savedFilters[key] ? hasFilterWithName(savedFilters[key], name) ? {
50
+ ...savedFilters,
51
+ [key]: savedFilters[key].map((f)=>f.name === name ? {
52
+ ...filter,
53
+ name
54
+ } : f)
55
+ } : filter?.name && filter?.name !== name && hasFilterWithName(savedFilters[key], filter.name) ? {
56
+ ...savedFilters,
57
+ [key]: savedFilters[key].map((f)=>f.name === filter.name ? {
58
+ ...filter,
59
+ name
60
+ } : f)
61
+ } : {
62
+ ...savedFilters,
63
+ [key]: savedFilters[key].concat({
64
+ ...filter,
65
+ name
66
+ })
67
+ } : {
68
+ ...savedFilters,
69
+ [key]: [
70
+ {
71
+ ...filter,
72
+ name
73
+ }
74
+ ]
75
+ } : {
76
+ [key]: [
77
+ {
78
+ ...filter,
79
+ name
80
+ }
81
+ ]
82
+ };
83
+ if (newFilters !== savedFilters) saveApplicationSettings(newFilters, "filters");
84
+ }
85
+ }, [
86
+ getApplicationSettings,
87
+ saveApplicationSettings,
88
+ tableSchema
89
+ ]);
90
+ const handleFilterStateChanged = useCallback((filterState)=>{
91
+ save?.({
92
+ filterState
93
+ }, "filterbar-config");
94
+ setFilterState(filterState);
95
+ }, [
96
+ save
97
+ ]);
98
+ const handleFilterDeleted = useCallback((filter)=>{
99
+ removeFilterFromSettings(filter);
100
+ }, [
101
+ removeFilterFromSettings
102
+ ]);
103
+ const handleFilterRenamed = useCallback((filter, name)=>{
104
+ saveFilterToSettings(filter, name);
105
+ }, [
106
+ saveFilterToSettings
107
+ ]);
108
+ const buildFilterTableMenuOptions = useCallback((location)=>{
109
+ if ("filter-bar-menu" !== location) return [];
110
+ if (savedFilters.length > 0) return savedFilters.map((filter)=>({
111
+ id: "add-filter",
112
+ label: filter.name,
113
+ options: {
114
+ filter
115
+ }
116
+ }));
117
+ return [
118
+ {
119
+ label: "You have no saved filters for this table",
120
+ id: "no-action"
121
+ }
122
+ ];
123
+ }, [
124
+ savedFilters
125
+ ]);
126
+ const handleFilterTableMenuAction = useCallback((menuItemId)=>{
127
+ if ("add-filter" === menuItemId) return true;
128
+ return false;
129
+ }, []);
130
+ return {
131
+ buildFilterTableMenuOptions,
132
+ filterState,
133
+ handleFilterTableMenuAction,
134
+ onFilterStateChanged: handleFilterStateChanged,
135
+ onFilterDeleted: handleFilterDeleted,
136
+ onFilterRenamed: handleFilterRenamed
137
+ };
138
+ };
139
+ export { usePersistFilterState };
package/src/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from "./filter-table/index.js";
2
+ export * from "./json-table/index.js";
3
+ export * from "./tree-table/index.js";
4
+ export * from "./linked-table-view/LinkedTableView.js";
@@ -0,0 +1,21 @@
1
+ const css = `
2
+ .vuuJsonCell {
3
+ --group-cell-spacer-width: 20px;
4
+ cursor: pointer;
5
+ height: var(--row-height);
6
+ border-right-style: solid;
7
+ border-right-width: 1px;
8
+ align-items: center;
9
+ padding-left: 20px;
10
+ line-height: 16px;
11
+ display: inline-flex;
12
+ position: relative;
13
+
14
+ & .vuuToggleIconButton {
15
+ position: absolute;
16
+ }
17
+ }
18
+
19
+
20
+ `;
21
+ export default css;
@@ -0,0 +1,40 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { useComponentCssInjection } from "@salt-ds/styles";
3
+ import { useWindow } from "@salt-ds/window";
4
+ import { ToggleIconButton } from "@vuu-ui/vuu-ui-controls";
5
+ import JsonCell from "./JsonCell.css";
6
+ const JsonCell_JsonCell = ({ column, dataRow })=>{
7
+ const targetWindow = useWindow();
8
+ useComponentCssInjection({
9
+ testId: "vuu-array-cell",
10
+ css: JsonCell,
11
+ window: targetWindow
12
+ });
13
+ const { name } = column;
14
+ let { isExpanded, isLeaf, [name]: value } = dataRow;
15
+ const getDisplayValue = ()=>{
16
+ if (isLeaf) return value;
17
+ if ("string" == typeof value && value.endsWith("{")) {
18
+ value = value.slice(0, -1);
19
+ if (!isNaN(parseInt(value))) return `${value}: {...}`;
20
+ return "value {...}";
21
+ }
22
+ if ("string" == typeof value && value.endsWith("[")) {
23
+ value = value.slice(0, -1);
24
+ return `${value} [...]`;
25
+ }
26
+ };
27
+ const displayValue = getDisplayValue();
28
+ const isEmpty = "" === displayValue || void 0 === displayValue;
29
+ return /*#__PURE__*/ jsxs(Fragment, {
30
+ children: [
31
+ isLeaf || isEmpty ? null : /*#__PURE__*/ jsx(ToggleIconButton, {
32
+ isExpanded: isExpanded
33
+ }),
34
+ /*#__PURE__*/ jsx("span", {
35
+ children: displayValue
36
+ })
37
+ ]
38
+ });
39
+ };
40
+ export { JsonCell_JsonCell as JsonCell };
@@ -0,0 +1,38 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { registerComponent } from "@vuu-ui/vuu-utils";
3
+ import { Table } from "@vuu-ui/vuu-table";
4
+ import { JsonDataSource } from "@vuu-ui/vuu-data-local";
5
+ import { useEffect, useMemo, useRef } from "react";
6
+ import { JsonCell } from "./JsonCell.js";
7
+ registerComponent("json", JsonCell, "cell-renderer");
8
+ const JsonTable = ({ config, source: sourceProp, ...tableProps })=>{
9
+ const sourceRef = useRef(sourceProp);
10
+ const dataSourceRef = useRef(void 0);
11
+ useMemo(()=>{
12
+ dataSourceRef.current = new JsonDataSource({
13
+ data: sourceRef.current
14
+ });
15
+ }, []);
16
+ const tableConfig = useMemo(()=>({
17
+ ...config,
18
+ columns: dataSourceRef.current?.columnDescriptors ?? [],
19
+ columnSeparators: true,
20
+ rowSeparators: true
21
+ }), [
22
+ config
23
+ ]);
24
+ useEffect(()=>{
25
+ if (dataSourceRef.current) dataSourceRef.current.data = sourceProp;
26
+ }, [
27
+ sourceProp
28
+ ]);
29
+ if (void 0 === dataSourceRef.current) return null;
30
+ return /*#__PURE__*/ jsx(Table, {
31
+ ...tableProps,
32
+ config: tableConfig,
33
+ dataSource: dataSourceRef.current,
34
+ showColumnHeaderMenus: false,
35
+ selectionModel: "none"
36
+ });
37
+ };
38
+ export { JsonTable };
@@ -0,0 +1 @@
1
+ export * from "./JsonTable.js";
@@ -0,0 +1,24 @@
1
+ const css = `
2
+ .vuuLinkedTableView-header {
3
+ padding-right: var(--salt-spacing-100);
4
+ flex: 0 0 28px;
5
+ align-items: center;
6
+ display: flex;
7
+
8
+ & .vuuTabstrip {
9
+ width: fit-content;
10
+ }
11
+
12
+ & .vuuLinkedTableView-toolTray {
13
+ margin-left: auto;
14
+ }
15
+ }
16
+
17
+ .vuuLinkedTableView-view {
18
+ padding: var(--salt-spacing-100);
19
+ overflow: hidden;
20
+ }
21
+
22
+
23
+ `;
24
+ export default css;
@@ -0,0 +1,186 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Flexbox, Stack, View } from "@vuu-ui/vuu-layout";
3
+ import { Table } from "@vuu-ui/vuu-table";
4
+ import { Tab, Tabstrip } from "@vuu-ui/vuu-ui-controls";
5
+ import { useComponentCssInjection } from "@salt-ds/styles";
6
+ import { useWindow } from "@salt-ds/window";
7
+ import clsx from "clsx";
8
+ import { useLinkedTableView } from "./useLinkedTableView.js";
9
+ import LinkedTableView from "./LinkedTableView.css";
10
+ import { TableLayoutToggleButton } from "./TableLayoutToggleButton.js";
11
+ const classBase = "vuuLinkedTableView";
12
+ const LinkedTables = ({ className, linkedDataSources, ...htmlAttributes })=>{
13
+ const targetWindow = useWindow();
14
+ useComponentCssInjection({
15
+ testId: "vuu-linked-table-view",
16
+ css: LinkedTableView,
17
+ window: targetWindow
18
+ });
19
+ const { tableConfig, ...config } = useLinkedTableView({
20
+ linkedDataSources
21
+ });
22
+ const getLinkedTables = (tdsConfig, { activeTab, onChangeTabbedView, onTabChange, tabbedView, ...levelConfig })=>false === Array.isArray(tdsConfig) ? /*#__PURE__*/ jsx(View, {
23
+ ...levelConfig,
24
+ header: true,
25
+ resizeable: true,
26
+ style: {
27
+ flexBasis: 0,
28
+ flexGrow: 1,
29
+ flexShrink: 1
30
+ },
31
+ title: tdsConfig.title,
32
+ children: /*#__PURE__*/ jsx(Table, {
33
+ config: tdsConfig.config,
34
+ dataSource: tdsConfig.dataSource
35
+ })
36
+ }) : 1 === tabbedView ? /*#__PURE__*/ jsxs("div", {
37
+ style: {
38
+ display: "flex",
39
+ flexDirection: "column",
40
+ flexBasis: 0,
41
+ flexGrow: 1,
42
+ flexShrink: 1
43
+ },
44
+ children: [
45
+ /*#__PURE__*/ jsxs("div", {
46
+ className: `${classBase}-header`,
47
+ children: [
48
+ /*#__PURE__*/ jsx(Tabstrip, {
49
+ activeTabIndex: activeTab,
50
+ onActiveChange: onTabChange,
51
+ children: tdsConfig.map(({ title }, i)=>/*#__PURE__*/ jsx(Tab, {
52
+ label: title
53
+ }, i))
54
+ }),
55
+ /*#__PURE__*/ jsx("div", {
56
+ className: `${classBase}-toolTray`,
57
+ children: /*#__PURE__*/ jsx(TableLayoutToggleButton, {
58
+ onChange: onChangeTabbedView,
59
+ value: tabbedView
60
+ })
61
+ })
62
+ ]
63
+ }),
64
+ /*#__PURE__*/ jsx(Stack, {
65
+ active: activeTab,
66
+ "data-resizeable": true,
67
+ showTabs: false,
68
+ style: {
69
+ flexBasis: 0,
70
+ flexGrow: 1,
71
+ flexShrink: 1
72
+ },
73
+ children: tdsConfig.map(({ config, dataSource, title }, i)=>/*#__PURE__*/ jsx("div", {
74
+ className: `${classBase}-view`,
75
+ style: {
76
+ flexBasis: 0,
77
+ flexGrow: 1,
78
+ flexShrink: 1
79
+ },
80
+ title: title,
81
+ children: /*#__PURE__*/ jsx(Table, {
82
+ config: config,
83
+ dataSource: dataSource
84
+ })
85
+ }, i))
86
+ }, levelConfig.key)
87
+ ]
88
+ }) : /*#__PURE__*/ jsxs("div", {
89
+ style: {
90
+ display: "flex",
91
+ flexDirection: "column",
92
+ flexBasis: 0,
93
+ flexGrow: 1,
94
+ flexShrink: 1
95
+ },
96
+ children: [
97
+ /*#__PURE__*/ jsxs("div", {
98
+ className: `${classBase}-header`,
99
+ children: [
100
+ /*#__PURE__*/ jsx("div", {
101
+ className: `${classBase}-flexHeaders`,
102
+ children: tdsConfig.map(({ title }, i)=>/*#__PURE__*/ jsx("span", {
103
+ children: title
104
+ }, i))
105
+ }),
106
+ /*#__PURE__*/ jsx("div", {
107
+ className: `${classBase}-toolTray`,
108
+ children: /*#__PURE__*/ jsx(TableLayoutToggleButton, {
109
+ onChange: onChangeTabbedView,
110
+ value: tabbedView
111
+ })
112
+ })
113
+ ]
114
+ }),
115
+ /*#__PURE__*/ jsx(Flexbox, {
116
+ style: {
117
+ flexDirection: "row",
118
+ flexBasis: 0,
119
+ flexGrow: 1,
120
+ flexShrink: 1
121
+ },
122
+ children: tdsConfig.map(({ config, dataSource, title }, i)=>/*#__PURE__*/ jsx("div", {
123
+ className: `${classBase}-view`,
124
+ "data-resizeable": true,
125
+ style: {
126
+ flexBasis: 0,
127
+ flexGrow: 1,
128
+ flexShrink: 1
129
+ },
130
+ title: title,
131
+ children: /*#__PURE__*/ jsx(Table, {
132
+ config: config,
133
+ dataSource: dataSource
134
+ })
135
+ }, i))
136
+ })
137
+ ]
138
+ });
139
+ const getAllLinkedTables = (level2, level3, config)=>{
140
+ const results = [
141
+ getLinkedTables(level2, config.level2)
142
+ ];
143
+ if (level3) results.push(getLinkedTables(level3, config.level3));
144
+ return results;
145
+ };
146
+ if (!tableConfig) return null;
147
+ {
148
+ const { 1: level1, 2: level2, 3: level3 } = tableConfig;
149
+ return /*#__PURE__*/ jsx("div", {
150
+ ...htmlAttributes,
151
+ className: clsx(classBase, className),
152
+ children: /*#__PURE__*/ jsxs(Flexbox, {
153
+ style: {
154
+ flexDirection: "column",
155
+ height: "100%"
156
+ },
157
+ children: [
158
+ /*#__PURE__*/ jsx(View, {
159
+ className: `${classBase}-view`,
160
+ header: true,
161
+ resizeable: true,
162
+ style: {
163
+ flexBasis: 0,
164
+ flexGrow: 1,
165
+ flexShrink: 1
166
+ },
167
+ title: level1.title,
168
+ children: /*#__PURE__*/ jsx(Table, {
169
+ config: level1.config,
170
+ dataSource: level1.dataSource
171
+ })
172
+ }),
173
+ ...getAllLinkedTables(level2, level3, config)
174
+ ]
175
+ })
176
+ });
177
+ }
178
+ };
179
+ const LinkedTableView_LinkedTableView = ({ className, linkedDataSources, ...htmlAttributes })=>/*#__PURE__*/ jsx(View, {
180
+ ...htmlAttributes,
181
+ className: className,
182
+ children: /*#__PURE__*/ jsx(LinkedTables, {
183
+ linkedDataSources: linkedDataSources
184
+ })
185
+ });
186
+ export { LinkedTableView_LinkedTableView as LinkedTableView };
@@ -0,0 +1,27 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Icon } from "@vuu-ui/vuu-ui-controls";
3
+ import { ToggleButton, ToggleButtonGroup } from "@salt-ds/core";
4
+ const TableLayoutToggleButton = (props)=>/*#__PURE__*/ jsxs(ToggleButtonGroup, {
5
+ ...props,
6
+ children: [
7
+ /*#__PURE__*/ jsx(ToggleButton, {
8
+ className: "vuuIconToggleButton",
9
+ value: 0,
10
+ "aria-label": "Tabbed View",
11
+ children: /*#__PURE__*/ jsx(Icon, {
12
+ name: "split-v",
13
+ size: 18
14
+ })
15
+ }),
16
+ /*#__PURE__*/ jsx(ToggleButton, {
17
+ className: "vuuIconToggleButton",
18
+ value: 1,
19
+ "aria-label": "Side by side view",
20
+ children: /*#__PURE__*/ jsx(Icon, {
21
+ name: "tabs",
22
+ size: 18
23
+ })
24
+ })
25
+ ]
26
+ });
27
+ export { TableLayoutToggleButton };
@@ -0,0 +1,205 @@
1
+ import { useData } from "@vuu-ui/vuu-utils";
2
+ import { useCallback, useMemo, useState } from "react";
3
+ const getSchema = (schemas, { module, table })=>{
4
+ const schema = schemas.find(({ table: t })=>t.module === module && t.table === table);
5
+ if (schema) return schema;
6
+ throw Error(`No schema for table ${module} ${table}`);
7
+ };
8
+ const getTable = (ds)=>{
9
+ if (ds.table) return ds.table;
10
+ throw Error("No datasource table specified");
11
+ };
12
+ const getTables = (linkedDataSources)=>{
13
+ const { 1: linked1, 2: linked2, 3: linked3 } = linkedDataSources;
14
+ const tables = [];
15
+ tables.push(getTable(linked1.dataSource));
16
+ if (Array.isArray(linked2)) linked2.forEach(({ dataSource })=>{
17
+ tables.push(getTable(dataSource));
18
+ });
19
+ else tables.push(getTable(linked2.dataSource));
20
+ if (Array.isArray(linked3)) linked3.forEach(({ dataSource })=>{
21
+ tables.push(getTable(dataSource));
22
+ });
23
+ else if (linked3) tables.push(getTable(linked3.dataSource));
24
+ return tables;
25
+ };
26
+ const useLinkedTableView = ({ linkedDataSources })=>{
27
+ const { VuuDataSource, getServerAPI } = useData();
28
+ const [tableConfig, setTableConfig] = useState();
29
+ const [activeTabs, setActiveTab] = useState([
30
+ 0,
31
+ 0,
32
+ 0
33
+ ]);
34
+ const [collapsed, setCollapsed] = useState([
35
+ false,
36
+ false
37
+ ]);
38
+ const [tabbedView, setTabbedView] = useState([
39
+ 1,
40
+ 1
41
+ ]);
42
+ useMemo(async ()=>{
43
+ const tables = getTables(linkedDataSources);
44
+ const serverAPI = await getServerAPI();
45
+ const schemas = await Promise.all(tables.map(serverAPI.getTableSchema));
46
+ const isDataSource = (ds)=>typeof ds === typeof VuuDataSource;
47
+ const getDataSource = (ds)=>{
48
+ if (isDataSource(ds)) return ds;
49
+ return new VuuDataSource(ds);
50
+ };
51
+ const createVisualLink = (vuuLink, parentDs)=>{
52
+ if (parentDs && parentDs.table) {
53
+ const parentVpId = parentDs.viewport;
54
+ const toTable = vuuLink.toTable ?? parentDs.table?.table;
55
+ return {
56
+ link: {
57
+ ...vuuLink,
58
+ toTable
59
+ },
60
+ parentClientVpId: parentVpId,
61
+ parentVpId
62
+ };
63
+ }
64
+ throw Error("visual link cannot be created without parent vp id");
65
+ };
66
+ const getLinkedDataSource = (ds, vuuLink, parentDs)=>{
67
+ if (isDataSource(ds)) return ds;
68
+ return new VuuDataSource({
69
+ ...ds,
70
+ visualLink: createVisualLink(vuuLink, parentDs)
71
+ });
72
+ };
73
+ const getTableConfig = (ds)=>{
74
+ const schema = getSchema(schemas, getTable(ds));
75
+ return {
76
+ columns: schema.columns
77
+ };
78
+ };
79
+ const getRootConfig = ({ dataSource: ds, title })=>({
80
+ config: getTableConfig(ds),
81
+ dataSource: getDataSource(ds),
82
+ title
83
+ });
84
+ const getLinkedConfig = ({ dataSource: ds, vuuLink, title }, parentDataSource)=>({
85
+ config: getTableConfig(ds),
86
+ dataSource: getLinkedDataSource(ds, vuuLink, parentDataSource),
87
+ title
88
+ });
89
+ const getLinkedConfigs = (linkedTableConfig, parentDataSource)=>{
90
+ if (Array.isArray(linkedTableConfig)) return linkedTableConfig.map((config)=>getLinkedConfig(config, parentDataSource));
91
+ return getLinkedConfig(linkedTableConfig, parentDataSource);
92
+ };
93
+ const { 1: level1, 2: level2, 3: level3 } = linkedDataSources;
94
+ const configLevel1 = getRootConfig(level1);
95
+ const configLevel2 = getLinkedConfigs(level2, configLevel1.dataSource);
96
+ const dsLevel2 = Array.isArray(configLevel2) ? void 0 : configLevel2.dataSource;
97
+ const configLevel3 = level3 ? getLinkedConfigs(level3, dsLevel2) : void 0;
98
+ const results = {
99
+ 1: configLevel1,
100
+ 2: configLevel2,
101
+ 3: configLevel3
102
+ };
103
+ setTableConfig(results);
104
+ }, [
105
+ VuuDataSource,
106
+ getServerAPI,
107
+ linkedDataSources
108
+ ]);
109
+ const handleTabChangeLevel1 = useCallback((tabIndex)=>{
110
+ setActiveTab(([, v2, v3])=>[
111
+ tabIndex,
112
+ v2,
113
+ v3
114
+ ]);
115
+ }, []);
116
+ const handleTabChangeLevel2 = useCallback((tabIndex)=>{
117
+ setActiveTab(([v1, , v3])=>[
118
+ v1,
119
+ tabIndex,
120
+ v3
121
+ ]);
122
+ }, []);
123
+ const handleTabChangeLevel3 = useCallback((tabIndex)=>{
124
+ setActiveTab(([v1, v2])=>[
125
+ v1,
126
+ v2,
127
+ tabIndex
128
+ ]);
129
+ }, []);
130
+ const handleCollapseLevel2 = useCallback(()=>{
131
+ setCollapsed(([, val])=>[
132
+ true,
133
+ val
134
+ ]);
135
+ }, []);
136
+ const handleExpandLevel2 = useCallback(()=>{
137
+ setCollapsed(([, val])=>[
138
+ false,
139
+ val
140
+ ]);
141
+ }, []);
142
+ const handleCollapseLevel3 = useCallback(()=>{
143
+ setCollapsed(([val])=>[
144
+ val,
145
+ true
146
+ ]);
147
+ }, []);
148
+ const handleExpandLevel3 = useCallback(()=>{
149
+ setCollapsed(([val])=>[
150
+ val,
151
+ false
152
+ ]);
153
+ }, []);
154
+ const handleChangeTabbedView2 = useCallback((evt)=>{
155
+ const target = evt.target;
156
+ const button = target?.closest("button");
157
+ if (button) {
158
+ const value = parseInt(button.value);
159
+ setTabbedView(([, val])=>[
160
+ value,
161
+ val
162
+ ]);
163
+ }
164
+ }, []);
165
+ const handleChangeTabbedView3 = useCallback((evt)=>{
166
+ const target = evt.target;
167
+ const button = target?.closest("button");
168
+ if (button) {
169
+ const value = parseInt(button.value);
170
+ setTabbedView(([val])=>[
171
+ val,
172
+ value
173
+ ]);
174
+ }
175
+ }, []);
176
+ return {
177
+ level1: {
178
+ activeTab: activeTabs[0],
179
+ key: "level1",
180
+ onTabChange: handleTabChangeLevel1
181
+ },
182
+ level2: {
183
+ activeTab: activeTabs[1],
184
+ collapsed: collapsed[0],
185
+ key: "level2",
186
+ onChangeTabbedView: handleChangeTabbedView2,
187
+ onCollapse: handleCollapseLevel2,
188
+ onExpand: handleExpandLevel2,
189
+ onTabChange: handleTabChangeLevel2,
190
+ tabbedView: tabbedView[0]
191
+ },
192
+ level3: {
193
+ activeTab: activeTabs[2],
194
+ collapsed: collapsed[1],
195
+ key: "level3",
196
+ onChangeTabbedView: handleChangeTabbedView3,
197
+ onCollapse: handleCollapseLevel3,
198
+ onExpand: handleExpandLevel3,
199
+ onTabChange: handleTabChangeLevel3,
200
+ tabbedView: tabbedView[1]
201
+ },
202
+ tableConfig
203
+ };
204
+ };
205
+ export { useLinkedTableView };
@@ -0,0 +1,42 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { Table } from "@vuu-ui/vuu-table";
3
+ import { TreeDataSource } from "@vuu-ui/vuu-data-local";
4
+ import { useMemo, useRef } from "react";
5
+ const TreeTable = ({ config, dataSource, source, ...tableProps })=>{
6
+ const dataSourceRef = useRef(void 0);
7
+ useMemo(()=>{
8
+ if (dataSource) dataSourceRef.current = dataSource;
9
+ else if (source) dataSourceRef.current = new TreeDataSource({
10
+ data: source
11
+ });
12
+ else throw Error("TreeTable either source or dataSource must be provided");
13
+ }, [
14
+ dataSource,
15
+ source
16
+ ]);
17
+ const tableConfig = useMemo(()=>({
18
+ ...config,
19
+ columns: dataSourceRef.current?.columnDescriptors ?? [],
20
+ columnSeparators: false,
21
+ selectionBookendWidth: 0,
22
+ rowSeparators: false
23
+ }), [
24
+ config
25
+ ]);
26
+ console.log({
27
+ tableConfig,
28
+ dataSource: dataSourceRef.current
29
+ });
30
+ if (void 0 === dataSourceRef.current) return null;
31
+ return /*#__PURE__*/ jsx(Table, {
32
+ ...tableProps,
33
+ className: "vuuTreeTable",
34
+ config: tableConfig,
35
+ dataSource: dataSourceRef.current,
36
+ groupToggleTarget: "toggle-icon",
37
+ navigationStyle: "tree",
38
+ showColumnHeaderMenus: false,
39
+ selectionModel: "single"
40
+ });
41
+ };
42
+ export { TreeTable };
@@ -0,0 +1 @@
1
+ export * from "./TreeTable.js";