(core) Show summary tables on Raw Data page

Summary:
Summary tables now have their own raw viewsection, and are shown
under Raw Data Tables on the Raw Data page.

Test Plan: Browser and Python tests.

Reviewers: jarek

Reviewed By: jarek

Differential Revision: https://phab.getgrist.com/D3495
This commit is contained in:
George Gevoian
2022-07-06 00:41:09 -07:00
parent 8bab8c18fa
commit a051830aeb
31 changed files with 452 additions and 308 deletions

View File

@@ -20,10 +20,13 @@ export class DataTables extends Disposable {
private _view: Observable<string | null>;
constructor(private _gristDoc: GristDoc) {
super();
// Remove tables that we don't have access to. ACL will remove tableId from those tables.
this._tables = Computed.create(this, use =>
use(_gristDoc.docModel.rawTables.getObservable())
.filter(t => Boolean(use(t.tableId))));
this._tables = Computed.create(this, use => {
const dataTables = use(_gristDoc.docModel.rawDataTables.getObservable());
const summaryTables = use(_gristDoc.docModel.rawSummaryTables.getObservable());
// Remove tables that we don't have access to. ACL will remove tableId from those tables.
return [...dataTables, ...summaryTables].filter(t => Boolean(use(t.tableId)));
});
// Get the user id, to remember selected layout on the next visit.
const userId = this._gristDoc.app.topAppModel.appObs.get()?.currentUser?.id ?? 0;
this._view = this.autoDispose(localStorageObs(`u=${userId}:raw:viewType`, "list"));
@@ -35,7 +38,7 @@ export class DataTables extends Disposable {
/*************** List section **********/
testId('list'),
cssBetween(
docListHeader('Raw data tables'),
docListHeader('Raw Data Tables'),
cssSwitch(
buttonSelect<any>(
this._view,
@@ -54,32 +57,13 @@ export class DataTables extends Disposable {
cssItem(
testId('table'),
cssLeft(
dom.domComputed(tableRec.tableId, (tableId) =>
cssGreenIcon(
'TypeTable',
testId(`table-id-${tableId}`)
)
),
dom.domComputed((use) => cssGreenIcon(
use(tableRec.summarySourceTable) !== 0 ? 'PivotLight' : 'TypeTable',
testId(`table-id-${use(tableRec.tableId)}`)
)),
),
cssMiddle(
css60(
testId('table-title'),
dom.domComputed(fromKo(tableRec.rawViewSectionRef), vsRef => {
if (!vsRef) {
// Some very old documents might not have rawViewSection.
return dom('span', dom.text(tableRec.tableNameDef));
} else {
return dom('div', // to disable flex grow in the widget
dom.domComputed(fromKo(tableRec.rawViewSection), vs =>
dom.update(
buildTableName(vs, testId('widget-title')),
dom.on('click', (ev) => { ev.stopPropagation(); ev.preventDefault(); }),
)
)
);
}
}),
),
css60(this._tableTitle(tableRec), testId('table-title')),
css40(
cssIdHoverWrapper(
cssUpperCase("Table id: "),
@@ -122,6 +106,30 @@ export class DataTables extends Disposable {
);
}
private _tableTitle(table: TableRec) {
return dom.domComputed((use) => {
const rawViewSectionRef = use(fromKo(table.rawViewSectionRef));
const isSummaryTable = use(table.summarySourceTable) !== 0;
if (!rawViewSectionRef || isSummaryTable) {
// Some very old documents might not have a rawViewSection, and raw summary
// tables can't currently be renamed.
const tableName = [
use(table.tableNameDef), isSummaryTable ? use(table.groupDesc) : ''
].filter(p => Boolean(p?.trim())).join(' ');
return dom('span', tableName);
} else {
return dom('div', // to disable flex grow in the widget
dom.domComputed(fromKo(table.rawViewSection), vs =>
dom.update(
buildTableName(vs, testId('widget-title')),
dom.on('click', (ev) => { ev.stopPropagation(); ev.preventDefault(); }),
)
)
);
}
});
}
private _menuItems(table: TableRec) {
const {isReadonly, docModel} = this._gristDoc;
return [
@@ -130,8 +138,8 @@ export class DataTables extends Disposable {
'Remove',
testId('menu-remove'),
dom.cls('disabled', use => use(isReadonly) || (
// Can't delete last user table, unless it is a hidden table.
use(docModel.allTables.getObservable()).length <= 1 && !use(table.isHidden)
// Can't delete last visible table, unless it is a hidden table.
use(docModel.visibleTables.getObservable()).length <= 1 && !use(table.isHidden)
))
),
dom.maybe(isReadonly, () => menuText('You do not have edit access to this document')),
@@ -141,9 +149,9 @@ export class DataTables extends Disposable {
private _removeTable(t: TableRec) {
const {docModel} = this._gristDoc;
function doRemove() {
return docModel.docData.sendAction(['RemoveTable', t.tableId.peek()]);
return docModel.docData.sendAction(['RemoveTable', t.tableId()]);
}
confirmModal(`Delete ${t.tableId()} data, and remove it from all pages?`, 'Delete', doRemove);
confirmModal(`Delete ${t.formattedTableName()} data, and remove it from all pages?`, 'Delete', doRemove);
}
}

View File

@@ -171,7 +171,7 @@ export class GristDoc extends DisposableWithEvents {
this.docInfo = this.docModel.docInfoRow;
this.hasDocTour = Computed.create(this, use =>
use(this.docModel.allTableIds.getObservable()).includes('GristDocTour'));
use(this.docModel.visibleTableIds.getObservable()).includes('GristDocTour'));
const defaultViewId = this.docInfo.newDefaultViewId;
@@ -911,7 +911,7 @@ export class GristDoc extends DisposableWithEvents {
* Renames table. Method exposed primarily for tests.
*/
public async renameTable(tableId: string, newTableName: string) {
const tableRec = this.docModel.allTables.all().find(t => t.tableId.peek() === tableId);
const tableRec = this.docModel.visibleTables.all().find(t => t.tableId.peek() === tableId);
if (!tableRec) {
throw new UserError(`No table with id ${tableId}`);
}

View File

@@ -182,7 +182,7 @@ export class Importer extends DisposableWithEvents {
private _destTables = Computed.create<Array<IOptionFull<DestId>>>(this, (use) => [
{value: NEW_TABLE, label: 'New Table'},
...(use(this._sourceInfoArray).length > 1 ? [{value: SKIP_TABLE, label: 'Skip'}] : []),
...use(this._gristDoc.docModel.allTableIds.getObservable()).map((t) => ({value: t, label: t})),
...use(this._gristDoc.docModel.visibleTableIds.getObservable()).map((id) => ({value: id, label: id})),
]);
// Source column labels for the selected import source, keyed by column id.

View File

@@ -305,7 +305,7 @@ export class GristDocAPIImpl implements GristDocAPI {
}
public async listTables(): Promise<string[]> {
// Could perhaps read tableIds from this.gristDoc.docModel.allTableIds.all()?
// Could perhaps read tableIds from this.gristDoc.docModel.visibleTableIds.all()?
const tables = await this._doc.docComm.fetchTable('_grist_Tables');
// Tables the user doesn't have access to are just blanked out.
return tables[3].tableId.filter(tableId => tableId !== '') as string[];