mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Adding UI for reverse columns
Summary: - Adding an UI for two-way reference column. - Reusing table name as label for the reverse column Test Plan: Updated Reviewers: georgegevoian Reviewed By: georgegevoian Differential Revision: https://phab.getgrist.com/D4344
This commit is contained in:
@@ -32,6 +32,9 @@ const {COMMENTS} = require('app/client/models/features');
|
||||
const {DismissedPopup} = require('app/common/Prefs');
|
||||
const {markAsSeen} = require('app/client/models/UserPrefs');
|
||||
const {buildConfirmDelete, reportUndo} = require('app/client/components/modals');
|
||||
const {buildReassignModal} = require('app/client/ui/buildReassignModal');
|
||||
const {MutedError} = require('app/client/models/errors');
|
||||
|
||||
|
||||
/**
|
||||
* BaseView forms the basis for ViewSection classes.
|
||||
@@ -648,7 +651,17 @@ BaseView.prototype.sendPasteActions = function(cutCallback, actions) {
|
||||
// If the cut occurs on an edit restricted cell, there may be no cut action.
|
||||
if (cutAction) { actions.unshift(cutAction); }
|
||||
}
|
||||
return this.gristDoc.docData.sendActions(actions);
|
||||
return this.gristDoc.docData.sendActions(actions).catch(ex => {
|
||||
if (ex.code === 'UNIQUE_REFERENCE_VIOLATION') {
|
||||
buildReassignModal({
|
||||
docModel: this.gristDoc.docModel,
|
||||
actions,
|
||||
}).catch(reportError);
|
||||
throw new MutedError();
|
||||
} else {
|
||||
throw ex;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
BaseView.prototype.buildDom = function() {
|
||||
|
||||
@@ -343,6 +343,9 @@ export class Comm extends dispose.Disposable implements GristServerAPI, DocListA
|
||||
if (message.details) {
|
||||
err.details = message.details;
|
||||
}
|
||||
if (message.error?.startsWith('[Sandbox] UniqueReferenceError')) {
|
||||
err.code = 'UNIQUE_REFERENCE_VIOLATION';
|
||||
}
|
||||
err.shouldFork = message.shouldFork;
|
||||
log.warn(`Comm response #${reqId} ${r.methodName} ERROR:${code} ${message.error}`
|
||||
+ (message.shouldFork ? ` (should fork)` : ''));
|
||||
|
||||
@@ -55,7 +55,8 @@ const {NEW_FILTER_JSON} = require('app/client/models/ColumnFilter');
|
||||
const {CombinedStyle} = require("app/client/models/Styles");
|
||||
const {buildRenameColumn} = require('app/client/ui/ColumnTitle');
|
||||
const {makeT} = require('app/client/lib/localization');
|
||||
const { isList } = require('app/common/gristTypes');
|
||||
const {isList} = require('app/common/gristTypes');
|
||||
|
||||
|
||||
const t = makeT('GridView');
|
||||
|
||||
@@ -628,27 +629,25 @@ GridView.prototype.paste = async function(data, cutCallback) {
|
||||
|
||||
if (actions.length > 0) {
|
||||
let cursorPos = this.cursor.getCursorPos();
|
||||
return this.sendPasteActions(cutCallback, actions)
|
||||
.then(results => {
|
||||
// If rows were added, get their rowIds from the action results.
|
||||
let addRowIds = (actions[0][0] === 'BulkAddRecord' ? results[0] : []);
|
||||
console.assert(addRowIds.length <= updateRowIds.length,
|
||||
`Unexpected number of added rows: ${addRowIds.length} of ${updateRowIds.length}`);
|
||||
let newRowIds = updateRowIds.slice(0, updateRowIds.length - addRowIds.length)
|
||||
.concat(addRowIds);
|
||||
const results = await this.sendPasteActions(cutCallback, actions);
|
||||
// If rows were added, get their rowIds from the action results.
|
||||
let addRowIds = (actions[0][0] === 'BulkAddRecord' ? results[0] : []);
|
||||
console.assert(addRowIds.length <= updateRowIds.length,
|
||||
`Unexpected number of added rows: ${addRowIds.length} of ${updateRowIds.length}`);
|
||||
let newRowIds = updateRowIds.slice(0, updateRowIds.length - addRowIds.length)
|
||||
.concat(addRowIds);
|
||||
|
||||
// Restore the cursor to the right rowId, even if it jumped.
|
||||
this.cursor.setCursorPos({rowId: cursorPos.rowId === 'new' ? addRowIds[0] : cursorPos.rowId});
|
||||
// Restore the cursor to the right rowId, even if it jumped.
|
||||
this.cursor.setCursorPos({rowId: cursorPos.rowId === 'new' ? addRowIds[0] : cursorPos.rowId});
|
||||
|
||||
// Restore the selection if it would select the correct rows.
|
||||
let topRowIndex = this.viewData.getRowIndex(newRowIds[0]);
|
||||
if (newRowIds.every((r, i) => r === this.viewData.getRowId(topRowIndex + i))) {
|
||||
this.cellSelector.selectArea(topRowIndex, leftIndex,
|
||||
topRowIndex + outputHeight - 1, leftIndex + outputWidth - 1);
|
||||
}
|
||||
// Restore the selection if it would select the correct rows.
|
||||
let topRowIndex = this.viewData.getRowIndex(newRowIds[0]);
|
||||
if (newRowIds.every((r, i) => r === this.viewData.getRowId(topRowIndex + i))) {
|
||||
this.cellSelector.selectArea(topRowIndex, leftIndex,
|
||||
topRowIndex + outputHeight - 1, leftIndex + outputWidth - 1);
|
||||
}
|
||||
|
||||
commands.allCommands.clearCopySelection.run();
|
||||
});
|
||||
await commands.allCommands.clearCopySelection.run();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -207,6 +207,16 @@ export class GristDoc extends DisposableWithEvents {
|
||||
|
||||
public isTimingOn = Observable.create(this, false);
|
||||
|
||||
/**
|
||||
* Checks if it is ok to show raw data popup for currently selected section.
|
||||
* We can't show raw data if:
|
||||
* - we already have full screen section (which looks the same)
|
||||
* - we are already showing raw data
|
||||
*
|
||||
* Extracted to single computed as it is used here and in menus.
|
||||
*/
|
||||
public canShowRawData: Computed<boolean>;
|
||||
|
||||
private _actionLog: ActionLog;
|
||||
private _undoStack: UndoStack;
|
||||
private _lastOwnActionGroup: ActionGroupWithCursorPos | null = null;
|
||||
@@ -498,7 +508,21 @@ export class GristDoc extends DisposableWithEvents {
|
||||
reloadPlugins() {
|
||||
void this.docComm.reloadPlugins().then(() => G.window.location.reload(false));
|
||||
},
|
||||
|
||||
async showRawData(sectionId: number = 0) {
|
||||
if (!this.canShowRawData.get()) {
|
||||
return;
|
||||
}
|
||||
if (!sectionId) {
|
||||
const viewSection = this.viewModel.activeSection();
|
||||
if (viewSection?.isDisposed()) { return; }
|
||||
if (viewSection.isRaw.peek()) {
|
||||
return;
|
||||
}
|
||||
sectionId = viewSection.id.peek();
|
||||
}
|
||||
const anchorUrlState = { hash: { sectionId, popup: true } };
|
||||
await urlState().pushUrl(anchorUrlState, { replace: true });
|
||||
},
|
||||
// Command to be manually triggered on cell selection. Moves the cursor to the selected cell.
|
||||
// This is overridden by the formula editor to insert "$col" variables when clicking cells.
|
||||
setCursor: this.onSetCursorPos.bind(this),
|
||||
@@ -603,6 +627,14 @@ export class GristDoc extends DisposableWithEvents {
|
||||
this._prevSectionId = null;
|
||||
}
|
||||
}));
|
||||
|
||||
this.canShowRawData = Computed.create(this, (use) => {
|
||||
const isSinglePage = use(urlState().state).params?.style === 'singlePage';
|
||||
if (isSinglePage || use(this.maximizedSectionId)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,6 +47,25 @@ export function addColTypeSuffix(type: string, column: ColumnRec, docModel: DocM
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Infers the suffix for a column type, based on the type of the column and the type to convert it to.
|
||||
* Currently only used for Ref and RefList types, where the suffix is the tableId of the reference.
|
||||
*/
|
||||
export function inferColTypeSuffix(newPure: string, column: ColumnRec) {
|
||||
// We can infer only for Ref and RefList types.
|
||||
if (newPure !== "Ref" && newPure !== "RefList") {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If the old type was also Ref/RefList, just return the tableId from the old type.
|
||||
const existingTable = column.type.peek().split(':')[1];
|
||||
const oldPure = gristTypes.extractTypeFromColType(column.type.peek());
|
||||
if (existingTable && (oldPure === "Ref" || oldPure === "RefList")) {
|
||||
return `${newPure}:${existingTable}`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks through the data of the given column to find the first value of the form
|
||||
* [R|r, <tableId>, <rowId>] (a Reference(List) value returned from a formula), and returns the tableId
|
||||
|
||||
Reference in New Issue
Block a user