Skip to content

Commit 51a977a

Browse files
committed
wip anchoring tables
1 parent d0c849c commit 51a977a

File tree

5 files changed

+101
-38
lines changed

5 files changed

+101
-38
lines changed

src/app/dfSlice.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,11 @@ export const dataFormulatorSlice = createSlice({
335335
// separate this, so that we only delete on tier of table a time
336336
state.charts = state.charts.filter(c => !(c.intermediate && c.intermediate.resultTableId == tableId));
337337
},
338+
updateTableAnchored: (state, action: PayloadAction<{tableId: string, anchored: boolean}>) => {
339+
let tableId = action.payload.tableId;
340+
let anchored = action.payload.anchored;
341+
state.tables = state.tables.map(t => t.id == tableId ? {...t, anchored} : t);
342+
},
338343
addChallenges: (state, action: PayloadAction<{tableId: string, challenges: { text: string; difficulty: 'easy' | 'medium' | 'hard'; }[]}>) => {
339344
state.activeChallenges = [...state.activeChallenges, action.payload];
340345
},

src/data/utils.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export const loadTextDataWrapper = (title: string, text: string, fileType: strin
1818
if (fileType == "text/csv" || fileType == "text/tab-separated-values") {
1919
table = createTableFromText(tableName, text);
2020
} else if (fileType == "application/json") {
21-
table = createTableFromFromObjectArray(tableName, JSON.parse(text));
21+
table = createTableFromFromObjectArray(tableName, JSON.parse(text), true);
2222
}
2323
return table;
2424
};
@@ -52,10 +52,10 @@ export const createTableFromText = (title: string, text: string): DictTable | un
5252
return row;
5353
});
5454

55-
return createTableFromFromObjectArray(title, values);
55+
return createTableFromFromObjectArray(title, values, true);
5656
};
5757

58-
export const createTableFromFromObjectArray = (title: string, values: any[], derive?: any, anchored?: boolean): DictTable => {
58+
export const createTableFromFromObjectArray = (title: string, values: any[], anchored: boolean, derive?: any): DictTable => {
5959
const len = values.length;
6060
let names: string[] = [];
6161
let cleanNames: string[] = [];
@@ -99,7 +99,7 @@ export const createTableFromFromObjectArray = (title: string, values: any[], der
9999
types: columnTable.names().map(name => (columnTable.column(name) as Column).type),
100100
rows: columnTable.objects(),
101101
derive: derive,
102-
anchored: false
102+
anchored: anchored
103103
}
104104
};
105105

@@ -172,7 +172,7 @@ export const loadBinaryDataWrapper = (title: string, arrayBuffer: ArrayBuffer):
172172
const jsonData = XLSX.utils.sheet_to_json(worksheet);
173173

174174
// Create a table from the JSON data with sheet name included in the title
175-
const sheetTable = createTableFromFromObjectArray(`${title}-${sheetName}`, jsonData);
175+
const sheetTable = createTableFromFromObjectArray(`${title}-${sheetName}`, jsonData, true);
176176
tables.push(sheetTable);
177177
}
178178

src/views/DataThread.tsx

Lines changed: 86 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import {
1515
Tooltip,
1616
ButtonGroup,
1717
useTheme,
18-
SxProps
18+
SxProps,
19+
Button
1920
} from '@mui/material';
2021

2122
import { VegaLite } from 'react-vega'
@@ -32,6 +33,7 @@ import AddchartIcon from '@mui/icons-material/Addchart';
3233
import StarIcon from '@mui/icons-material/Star';
3334
import SouthIcon from '@mui/icons-material/South';
3435
import TableRowsIcon from '@mui/icons-material/TableRowsOutlined';
36+
import AnchorIcon from '@mui/icons-material/Anchor';
3537
import PanoramaFishEyeIcon from '@mui/icons-material/PanoramaFishEye';
3638
import InsightsIcon from '@mui/icons-material/Insights';
3739

@@ -43,7 +45,6 @@ import 'prismjs/components/prism-python' // Language
4345
import 'prismjs/components/prism-typescript' // Language
4446
import 'prismjs/themes/prism.css'; //Example style, you can use another
4547

46-
4748
import { chartAvailabilityCheck, generateChartSkeleton, getDataTable } from './VisualizationView';
4849
import { TriggerCard } from './EncodingShelfCard';
4950

@@ -78,12 +79,11 @@ let SingleThreadView: FC<{
7879
usedTableIds, // tables that have been used
7980
sx
8081
}) {
81-
let theme = useTheme();
82-
8382
let tables = useSelector((state: DataFormulatorState) => state.tables);
8483
let charts = useSelector((state: DataFormulatorState) => state.charts);
8584
let focusedChartId = useSelector((state: DataFormulatorState) => state.focusedChartId);
8685
let focusedTableId = useSelector((state: DataFormulatorState) => state.focusedTableId);
86+
const theme = useTheme();
8787

8888
let focusedChart = charts.find(c => c.id == focusedChartId);
8989

@@ -95,13 +95,18 @@ let SingleThreadView: FC<{
9595

9696
let content: any = ""
9797

98+
let originTableIdOfThread = undefined;
9899
let tableIdList = [leafTable.id]
99100
let triggerCards: any[] = []
100101
let triggers = getTriggers(leafTable, tables);
101102

102103
if (leafTable.derive) {
103104
let firstNewTableIndex = triggers.findIndex(tg => !usedTableIds.includes(tg.tableId));
104105
firstNewTableIndex = firstNewTableIndex == -1 ? triggers.length : firstNewTableIndex;
106+
107+
if (firstNewTableIndex - 1 > 0) {
108+
originTableIdOfThread = triggers[0].tableId;
109+
}
105110
triggers = firstNewTableIndex > 0 ? triggers.slice(firstNewTableIndex - 1) : triggers;
106111

107112
tableIdList = [...triggers.map((trigger) => trigger.tableId), leafTable.id];
@@ -135,8 +140,42 @@ let SingleThreadView: FC<{
135140
});
136141
}
137142

138-
// the thread is focused if the focused chart is in this table
139-
let threadIsFocused = focusedChart && tableIdList.includes(focusedChart.tableRef) && !usedTableIds.includes(focusedChart.tableRef);
143+
144+
const findTableIdsBetweenAnchors = (tableIds: string[], tables: DictTable[], focusedTableId?: string): string[] => {
145+
if (!focusedTableId) return [];
146+
147+
// Find the index of the focused table
148+
const focusedIndex = tableIds.indexOf(focusedTableId);
149+
if (focusedIndex === -1) return [];
150+
151+
// Find anchored tables or boundaries
152+
let startIndex = 0;
153+
let endIndex = tableIds.length - 1;
154+
155+
// Search backward for an anchored table or start
156+
for (let i = focusedIndex; i >= 0; i--) {
157+
const table = tables.find(t => t.id === tableIds[i]);
158+
if (table?.anchored) {
159+
startIndex = i;
160+
break;
161+
}
162+
}
163+
164+
// Search forward for an anchored table or end
165+
for (let i = focusedIndex; i < tableIds.length; i++) {
166+
const table = tables.find(t => t.id === tableIds[i]);
167+
if (table?.anchored) {
168+
endIndex = i;
169+
break;
170+
}
171+
}
172+
173+
// Return the slice of tableIds between anchors
174+
return tableIds.slice(startIndex, endIndex + 1);
175+
};
176+
177+
// Find tableIds between anchored tables that include the focused table
178+
let tableIdsBetweenAnchors = findTableIdsBetweenAnchors(originTableIdOfThread ? [originTableIdOfThread, ...tableIdList] : tableIdList, tables, focusedTableId);
140179

141180
let tableList = tableIdList.map((tableId, i) => {
142181
// filter charts relavent to this
@@ -159,17 +198,6 @@ let SingleThreadView: FC<{
159198
// only charts without dependency can be deleted
160199
let tableDeleteEnabled = !tables.some(t => t.derive?.trigger.tableId == tableId);
161200

162-
let colloapsedTableBox = <div style={{ padding: 0 }}>
163-
<Box sx={{ textTransform: 'none', padding: 0, minWidth: 0, color: 'gray' }} >
164-
<Stack direction="row" sx={{ fontSize: '12px', fontWeight: tableId == focusedTableId ? 'bold' : 'normal' }} alignItems="center" gap={"2px"}>
165-
<TableRowsIcon fontSize="inherit" sx={{ fontWeight: 'inherit' }} />
166-
<Typography sx={{ fontSize: '12px', fontWeight: 'inherit' }} >
167-
{tableId}
168-
</Typography>
169-
</Stack>
170-
</Box>
171-
</div>;
172-
173201
let regularTableBox = <div ref={relevantCharts.some(c => c.chartId == focusedChartId) ? scrollRef : null} style={{ padding: '0px' }}>
174202
<Card className={`data-thread-card ${selectedClassName}`} variant="outlined"
175203
sx={{ width: '100%', background: 'aliceblue' }}
@@ -187,12 +215,27 @@ let SingleThreadView: FC<{
187215
}
188216
}}>
189217
<Box sx={{ margin: '0px', display: 'flex' }}>
190-
<Stack direction="row" sx={{ marginLeft: 1, marginRight: 'auto', fontSize: 12 }} alignItems="center" gap={"2px"}>
191-
<TableRowsIcon sx={{ color: 'darkgray', width: '14px', height: '14px' }} />
218+
<Stack direction="row" sx={{ marginLeft: 0.5, marginRight: 'auto', fontSize: 12 }} alignItems="center" gap={"2px"}>
219+
<IconButton color="primary" sx={{
220+
minWidth: 0,
221+
padding: 0.25,
222+
'&:hover': {
223+
transform: 'scale(1.2)',
224+
transition: 'all 0.2s ease'
225+
}
226+
}}
227+
size="small"
228+
disabled={table?.derive == undefined}
229+
onClick={(event) => {
230+
event.stopPropagation();
231+
dispatch(dfActions.updateTableAnchored({tableId: tableId, anchored: !table?.anchored}));
232+
}}>
233+
{table?.anchored ? <AnchorIcon sx={{ fontSize: 18 }} /> : <TableRowsIcon sx={{ fontSize: 18 }} />}
234+
</IconButton>
192235
<Box sx={{ margin: '4px 8px 4px 2px' }}>
193236
<Typography fontSize="inherit" sx={{
194237
textAlign: 'center',
195-
color: 'rgba(0,0,0,0.7)',
238+
color: 'rgba(0,0,0,0.7)',
196239
maxWidth: '100px',
197240
wordWrap: 'break-word',
198241
whiteSpace: 'normal'
@@ -201,7 +244,10 @@ let SingleThreadView: FC<{
201244
</Stack>
202245
<ButtonGroup aria-label="Basic button group" variant="text" sx={{ textAlign: 'end', margin: "auto 2px auto auto" }}>
203246
{tableDeleteEnabled && <Tooltip title="delete table">
204-
<IconButton aria-label="share" size="small" sx={{ padding: '2px' }}>
247+
<IconButton aria-label="share" size="small" sx={{ padding: 0.25, '&:hover': {
248+
transform: 'scale(1.2)',
249+
transition: 'all 0.2s ease'
250+
} }}>
205251
<DeleteIcon fontSize="small" sx={{ fontSize: 18 }} color='warning'
206252
onClick={(event) => {
207253
event.stopPropagation();
@@ -210,7 +256,10 @@ let SingleThreadView: FC<{
210256
</IconButton>
211257
</Tooltip>}
212258
<Tooltip title="create a new chart">
213-
<IconButton aria-label="share" size="small" sx={{ padding: '2px' }}>
259+
<IconButton aria-label="share" size="small" sx={{ padding: 0.25, '&:hover': {
260+
transform: 'scale(1.2)',
261+
transition: 'all 0.2s ease'
262+
} }}>
214263
<AddchartIcon fontSize="small" sx={{ fontSize: 18 }} color='primary'
215264
onClick={(event) => {
216265
event.stopPropagation();
@@ -230,19 +279,18 @@ let SingleThreadView: FC<{
230279
<Box
231280
key={`table-${tableId}`}
232281
sx={{ display: 'flex', flexDirection: 'row' }}>
233-
<div style={{
282+
<Box sx={{
234283
minWidth: '1px', padding: '0px', width: '8px', flex: 'none', display: 'flex',
235-
marginLeft: '8px',
236-
borderLeft: '1px dashed darkgray',
284+
marginLeft: tableIdsBetweenAnchors.includes(tableId) ? '7px' : '8px',
285+
borderLeft: tableIdsBetweenAnchors.includes(tableId) ?
286+
`3px solid ${theme.palette.primary.light}` : '1px dashed darkgray',
237287
}}>
238288
<Box sx={{
239289
padding: 0, width: '1px', margin: 'auto',
240-
//borderLeft: 'thin solid lightgray',
241-
// the following for
242290
backgroundImage: 'linear-gradient(180deg, darkgray, darkgray 75%, transparent 75%, transparent 100%)',
243291
backgroundSize: '1px 6px, 3px 100%'
244292
}}></Box>
245-
</div>
293+
</Box>
246294
<Box sx={{ flex: 1, padding: '8px 0px', minHeight: '8px', ...chartElementProps }}>
247295
{releventChartElements}
248296
</Box>
@@ -271,6 +319,16 @@ let SingleThreadView: FC<{
271319
</Divider>
272320
</Box>
273321
<div style={{ padding: '2px 4px 2px 4px', marginTop: 0, marginBottom: '8px', direction: 'ltr' }}>
322+
{originTableIdOfThread && <Box sx={{ direction: 'ltr' }}>
323+
<Typography sx={{ ml: 0.25, fontSize: "10px", color: 'text.secondary', textTransform: 'none' }}>
324+
{`${originTableIdOfThread}`}
325+
</Typography>
326+
<Box sx={{
327+
height: '14px',
328+
ml: 1,
329+
borderLeft: tableIdsBetweenAnchors.includes(originTableIdOfThread) ? `3px solid ${theme.palette.primary.light}` : `1px dashed rgba(0, 0, 0, 0.3)`
330+
}}></Box>
331+
</Box>}
274332
{content}
275333
</div>
276334
</Box>

src/views/DataView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export const FreeDataViewFC: FC<FreeDataViewProps> = function DataView({ $table
4545
return tables.map(table => {
4646
// try to let table figure out all fields are derivable from the table
4747
let rows = baseTableToExtTable(table.rows, derivedFields, conceptShelfItems);
48-
let extTable = createTableFromFromObjectArray(`${table.id}`, rows, table.derive);
48+
let extTable = createTableFromFromObjectArray(`${table.id}`, rows, table.anchored, table.derive);
4949
return extTable
5050
})
5151
} else {

src/views/TableSelectionView.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ export const TableSelectionDialog: React.FC<{ buttonElement: any }> = function T
177177
.then((response) => response.json())
178178
.then((result) => {
179179
let tableChallenges : TableChallenges[] = result.map((info: any) => {
180-
let table = createTableFromFromObjectArray(info["name"], JSON.parse(info["snapshot"]))
180+
let table = createTableFromFromObjectArray(info["name"], JSON.parse(info["snapshot"]), true)
181181
return {table: table, challenges: info["challenges"], name: info["name"]}
182182
}).filter((t : TableChallenges | undefined) => t != undefined);
183183
setDatasetPreviews(tableChallenges);
@@ -221,7 +221,7 @@ export const TableSelectionDialog: React.FC<{ buttonElement: any }> = function T
221221
return response.text()
222222
})
223223
.then((text) => {
224-
let fullTable = createTableFromFromObjectArray(tableChallenges.table.id, JSON.parse(text));
224+
let fullTable = createTableFromFromObjectArray(tableChallenges.table.id, JSON.parse(text), true);
225225
if (fullTable) {
226226
dispatch(dfActions.loadTable(fullTable));
227227
dispatch(fetchFieldSemanticType(fullTable));
@@ -389,7 +389,7 @@ export const TableURLDialog: React.FC<TableURLDialogProps> = ({ buttonElement, d
389389
let table : undefined | DictTable = undefined;
390390
try {
391391
let jsonContent = JSON.parse(content);
392-
table = createTableFromFromObjectArray(tableName || 'dataset', jsonContent);
392+
table = createTableFromFromObjectArray(tableName || 'dataset', jsonContent, true);
393393
} catch (error) {
394394
table = createTableFromText(tableName || 'dataset', content);
395395
}
@@ -486,7 +486,7 @@ export const TableCopyDialogV2: React.FC<TableCopyDialogProps> = ({ buttonElemen
486486

487487
try {
488488
let content = JSON.parse(tableStr);
489-
table = createTableFromFromObjectArray(uniqueName, content);
489+
table = createTableFromFromObjectArray(uniqueName, content, true);
490490
} catch (error) {
491491
table = createTableFromText(uniqueName, tableStr);
492492
}

0 commit comments

Comments
 (0)