(core) Removing the new menu flag

Summary: Enabling the `GRIST_NEW_COLUMN_MENU` flag by default and removing it.

Test Plan: Existing

Reviewers: georgegevoian

Reviewed By: georgegevoian

Differential Revision: https://phab.getgrist.com/D4098
This commit is contained in:
Jarosław Sadziński
2023-11-06 16:42:04 +01:00
parent 9262e1f1ef
commit 3c219e05f6
15 changed files with 1415 additions and 475 deletions

View File

@@ -44,7 +44,6 @@ const {
buildAddColumnMenu,
buildColumnContextMenu,
buildMultiColumnMenu,
buildOldAddColumnMenu,
calcFieldsCondition,
freezeAction,
} = require('app/client/ui/GridViewMenus');
@@ -56,7 +55,6 @@ 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 {GRIST_NEW_COLUMN_MENU} = require("../models/features");
const t = makeT('GridView');
@@ -311,14 +309,14 @@ GridView.gridCommands = {
editField: function() { closeRegisteredMenu(); this.scrollToCursor(true); this.activateEditorAtCursor(); },
insertFieldBefore: function(maybeKeyboardEvent) {
if (GRIST_NEW_COLUMN_MENU() && !maybeKeyboardEvent) {
if (!maybeKeyboardEvent) {
this._openInsertColumnMenu(this.cursor.fieldIndex());
} else {
this.insertColumn(null, {index: this.cursor.fieldIndex()});
}
},
insertFieldAfter: function(maybeKeyboardEvent) {
if (GRIST_NEW_COLUMN_MENU() && !maybeKeyboardEvent) {
if (!maybeKeyboardEvent) {
this._openInsertColumnMenu(this.cursor.fieldIndex() + 1);
} else {
this.insertColumn(null, {index: this.cursor.fieldIndex() + 1});
@@ -1302,8 +1300,7 @@ GridView.prototype.buildDom = function() {
testId('column-menu-trigger'),
),
dom('div.selection'),
// FIXME: remove once New Column menu is enabled by default.
GRIST_NEW_COLUMN_MENU() ? this._buildInsertColumnMenu({field}) : null,
this._buildInsertColumnMenu({field}),
);
}),
this.isPreview ? null : kd.maybe(() => !this.gristDoc.isReadonlyKo(), () => (
@@ -2004,66 +2001,50 @@ GridView.prototype._scrollColumnIntoView = function(colIndex) {
* the GridView.
*/
GridView.prototype._buildInsertColumnMenu = function(options = {}) {
if (GRIST_NEW_COLUMN_MENU()) {
const {field} = options;
const triggers = [];
if (!field) { triggers.push('click'); }
const {field} = options;
const triggers = [];
if (!field) { triggers.push('click'); }
return [
field ? kd.toggleClass('field-insert-before', () =>
this._insertColumnIndex() === field._index()) : null,
menu(
ctl => {
ctl.onDispose(() => this._insertColumnIndex(null));
return [
field ? kd.toggleClass('field-insert-before', () =>
this._insertColumnIndex() === field._index()) : null,
menu(
ctl => {
ctl.onDispose(() => this._insertColumnIndex(null));
let index = this._insertColumnIndex.peek();
if (index === null || index === -1) {
index = undefined;
}
let index = this._insertColumnIndex.peek();
if (index === null || index === -1) {
index = undefined;
}
return [
buildAddColumnMenu(this, index),
elem => { FocusLayer.create(ctl, {defaultFocusElem: elem, pauseMousetrap: true}); },
testId('new-columns-menu'),
];
},
{
modifiers: {
offset: {
offset: '8,8',
},
return [
buildAddColumnMenu(this, index),
elem => { FocusLayer.create(ctl, {defaultFocusElem: elem, pauseMousetrap: true}); },
testId('new-columns-menu'),
];
},
{
modifiers: {
offset: {
offset: '8,8',
},
selectOnOpen: true,
trigger: [
...triggers,
(_, ctl) => {
ctl.autoDispose(this._insertColumnIndex.subscribe((index) => {
if (field?._index() === index || (!field && index === -1)) {
ctl.open();
} else if (!ctl.isDisposed()) {
ctl.close();
}
}));
},
],
}
),
];
} else {
// FIXME: remove once New Column menu is enabled by default.
return [
dom.on('click', async ev => {
// If there are no hidden columns, clicking the plus just adds a new column.
// If there are hidden columns, display a dropdown menu.
if (this.viewSection.hiddenColumns().length === 0) {
// Don't open the menu defined below.
ev.stopImmediatePropagation();
await this.insertColumn();
}
}),
menu((() => buildOldAddColumnMenu(this, this.viewSection))),
]
}
},
selectOnOpen: true,
trigger: [
...triggers,
(_, ctl) => {
ctl.autoDispose(this._insertColumnIndex.subscribe((index) => {
if (field?._index() === index || (!field && index === -1)) {
ctl.open();
} else if (!ctl.isDisposed()) {
ctl.close();
}
}));
},
],
}
),
];
}
GridView.prototype._openInsertColumnMenu = function(columnIndex) {

View File

@@ -25,10 +25,6 @@ export function WHICH_FORMULA_ASSISTANT() {
return getGristConfig().assistantService;
}
export function GRIST_NEW_COLUMN_MENU(){
return Boolean(getGristConfig().gristNewColumnMenu);
}
export function PERMITTED_CUSTOM_WIDGETS(): Observable<string[]> {
const G = getBrowserGlobals('document', 'window');
if (!G.window.PERMITTED_CUSTOM_WIDGETS) {

View File

@@ -1,7 +1,6 @@
import {allCommands} from 'app/client/components/commands';
import GridView from 'app/client/components/GridView';
import {makeT} from 'app/client/lib/localization';
import {ViewSectionRec} from 'app/client/models/DocModel';
import {ViewFieldRec} from 'app/client/models/entities/ViewFieldRec';
import {GristTooltips} from 'app/client/ui/GristTooltips';
import {withInfoTooltip} from 'app/client/ui/tooltips';
@@ -29,18 +28,6 @@ import isEqual = require('lodash/isEqual');
const t = makeT('GridViewMenus');
// FIXME: remove once New Column menu is enabled by default.
export function buildOldAddColumnMenu(gridView: GridView, viewSection: ViewSectionRec) {
return [
menuItem(async () => { await gridView.insertColumn(); }, t("Add Column")),
menuDivider(),
...viewSection.hiddenColumns().map((col: any) => menuItem(
async () => {
await gridView.showColumn(col.id());
}, t("Show column {{- label}}", {label: col.label()})))
];
}
export function buildAddColumnMenu(gridView: GridView, index?: number) {
const isSummaryTable = Boolean(gridView.viewSection.table().summarySourceTable());
return [
@@ -66,13 +53,14 @@ function buildHiddenColumnsMenuItems(gridView: GridView, index?: number) {
if (hiddenColumns.length <= 5) {
return [
menuDivider(),
menuSubHeader(t('Hidden Columns'), testId('new-columns-menu-hidden-columns')),
menuSubHeader(t('Hidden Columns'), testId('new-columns-menu-hidden-columns-header')),
hiddenColumns.map((col: ColumnRec) =>
menuItem(
async () => {
await gridView.showColumn(col.id(), index);
},
col.label(),
testId('new-columns-menu-hidden-column-inlined'),
)
),
];
@@ -84,13 +72,18 @@ function buildHiddenColumnsMenuItems(gridView: GridView, index?: number) {
return searchableMenu(
hiddenColumns.map((col) => ({
cleanText: col.label().trim().toLowerCase(),
builder: () => menuItemTrimmed(() => gridView.showColumn(col.id(), index), col.label())
builder: () => menuItemTrimmed(
() => gridView.showColumn(col.id(), index),
col.label(),
testId('new-columns-menu-hidden-column-collapsed'),
)
})),
{searchInputPlaceholder: t('Search columns')}
);
},
{allowNothingSelected: true},
t('Hidden Columns'),
testId('new-columns-menu-hidden-columns-menu')
),
];
}
@@ -145,7 +138,10 @@ function buildTimestampMenuItems(gridView: GridView, index?: number) {
t("Apply on record changes"),
testId('new-columns-menu-shortcuts-timestamp-change'),
),
], {}, t("Timestamp"), testId('new-columns-menu-shortcuts-timestamp'));
], {},
t("Timestamp"),
testId('new-columns-menu-shortcuts-timestamp')
);
}
function buildAuthorshipMenuItems(gridView: GridView, index?: number) {
@@ -291,7 +287,7 @@ function buildUUIDMenuItem(gridView: GridView, index?: number) {
);
}
function menuLabelWithToast(label: string, toast: string) {
function menuLabelWithBadge(label: string, toast: string) {
return cssListLabel(
cssListCol(label),
cssListFun(toast));
@@ -332,7 +328,7 @@ function buildLookupSection(gridView: GridView, index?: number){
case 'count':
case 'sum': return `SUM(${referenceToSource}.${col.colId()})`;
case 'percent':
return `AVERAGE(map(int, ${referenceToSource}.${col.colId()})) if ${referenceToSource} else 0`;
return `AVERAGE(map(int, ${referenceToSource}.${col.colId()})) if ${referenceToSource} else None`;
default: return `${referenceToSource}`;
}
}
@@ -365,8 +361,6 @@ function buildLookupSection(gridView: GridView, index?: number){
};
}
function buildLookupsMenuItems() {
// Function that builds a menu for one of our Ref columns, we will show all columns
// from the referenced table and offer to create a formula column with aggregation in case
@@ -383,7 +377,7 @@ function buildLookupSection(gridView: GridView, index?: number){
} else {
// For RefList column we will show the column name and the aggregation function which is the first
// on of suggested action (and a default action).
label = menuLabelWithToast(col.label(), suggestAggregation(col)[0]);
label = menuLabelWithBadge(col.label(), suggestAggregation(col)[0]);
}
return {
@@ -394,14 +388,36 @@ function buildLookupSection(gridView: GridView, index?: number){
function buildItem() {
if (ref.pureType() === 'Ref') {
// Just insert a plain menu item that will insert a formula column with lookup.
return menuItemTrimmed(() => insertPlainLookup(), col.label());
} else {
// Built nested menu.
return menuItemSubmenu(
() => suggestAggregation(col).map((fun) => menuItem(() => insertAggLookup(fun), fun)),
{},
label
return menuItemTrimmed(
() => insertPlainLookup(), col.label(),
testId(`new-columns-menu-lookup-column`),
testId(`new-columns-menu-lookup-column-${col.colId()}`),
);
} else {
// Depending on the number of aggregation functions we will either create a plain menu item
// or submenu with all the functions.
const functions = suggestAggregation(col);
if (functions.length === 1) {
const action = () => insertAggLookup(functions[0]);
return menuItem(action, label,
testId(`new-columns-menu-lookup-column`),
testId(`new-columns-menu-lookup-column-${col.colId()}`)
);
} else {
return menuItemSubmenu(
() => functions.map((fun) => menuItem(
() => insertAggLookup(fun), fun,
testId(`new-columns-menu-lookup-submenu-function`),
testId(`new-columns-menu-lookup-submenu-function-${fun}`),
)),
{
action: () => insertAggLookup(suggestAggregation(col)[0]),
},
label,
testId(`new-columns-menu-lookup-submenu`),
testId(`new-columns-menu-lookup-submenu-${col.colId()}`),
);
}
}
}
@@ -451,7 +467,8 @@ function buildLookupSection(gridView: GridView, index?: number){
),
{allowNothingSelected: true},
`${ref.refTable()?.tableNameDef()} [${ref.label()}]`,
testId(`new-columns-menu-lookups-${ref.colId()}`),
testId(`new-columns-menu-lookup-${ref.colId()}`),
testId(`new-columns-menu-lookup`),
));
}
@@ -463,6 +480,7 @@ function buildLookupSection(gridView: GridView, index?: number){
}
function buildReverseLookupsMenuItems() {
const getReferencesToThisTable = (): RefTable[] => {
const {viewSection} = gridView;
const otherTables = gridView.gristDoc.docModel.allTables.all().filter((tab) =>
@@ -473,16 +491,16 @@ function buildLookupSection(gridView: GridView, index?: number){
tableName: tab.tableNameDef(),
columns: tab.visibleColumns(),
referenceFields:
tab.columns().peek().filter((c) => (c.pureType() === 'Ref' || c.pureType() == 'RefList') &&
tab.visibleColumns.peek().filter((c) => (c.pureType() === 'Ref' || c.pureType() == 'RefList') &&
c.refTable()?.tableId() === viewSection.tableId())
};
})
.filter((tab) => tab.referenceFields.length > 0);
};
const buildColumn = async (tab: RefTable, col: ColumnRec, refCol: ColumnRec, aggregate: string) => {
const formula = `${tab.tableId}.lookupRecords(${refCol.colId()}=
${refCol.pureType() == 'RefList' ? 'CONTAINS($id)' : '$id'})`;
const insertColumn = async (tab: RefTable, col: ColumnRec, refCol: ColumnRec, aggregate: string) => {
const formula =
`${tab.tableId}.lookupRecords(${refCol.colId()}=${refCol.pureType() == 'RefList' ? 'CONTAINS($id)' : '$id'})`;
await gridView.insertColumn(`${tab.tableId}_${col.label()}`, {
colInfo: {
label: `${tab.tableId}_${col.label()}`,
@@ -495,41 +513,51 @@ function buildLookupSection(gridView: GridView, index?: number){
});
};
const buildSubmenuForRevLookup = (tab: RefTable, refCol: any) => {
const tablesWithAnyRefColumn = getReferencesToThisTable();
return tablesWithAnyRefColumn.map((tab: RefTable) => tab.referenceFields.map((refCol) => {
const buildSubmenuForRevLookupMenuItem = (col: ColumnRec): SearchableMenuItem => {
const suggestedColumns = suggestAggregation(col);
const primarySuggestedColumn = suggestedColumns[0];
const aggregationList = suggestAggregation(col);
const firstAggregation = aggregationList[0];
if (!firstAggregation) {
throw new Error(`No aggregation suggested for column ${col.label()}`);
}
return {
cleanText: col.label().trim().toLowerCase(),
builder: () => {
if (suggestedColumns.length === 1) {
return menuItem(() => buildColumn(tab, col, refCol, primarySuggestedColumn),
menuLabelWithToast(col.label(), primarySuggestedColumn));
const content = menuLabelWithBadge(col.label(), firstAggregation);
// In case we have only one suggested column we will just insert it, and there is no,
// need for submenu.
if (aggregationList.length === 1) {
const action = () => insertColumn(tab, col, refCol, firstAggregation);
return menuItem(action, content, testId('new-columns-menu-revlookup-column'));
} else {
return menuItemSubmenu((ctl) =>
suggestedColumns.map(fun =>
menuItem(async () =>
buildColumn(tab, col, refCol, fun), t(fun)))
, {}, menuLabelWithToast(col.label(), primarySuggestedColumn));
// We have some other suggested columns, we will build submenu for them.
const submenu = () => {
const items = aggregationList.map((fun) => {
const action = () => insertColumn(tab, col, refCol, fun);
return menuItem(action, fun, testId('new-columns-menu-revlookup-column-function'));
});
return items;
};
const options = {};
return menuItemSubmenu(
submenu,
options,
content,
testId('new-columns-menu-revlookup-submenu'),
);
}
}
};
};
return menuItemSubmenu(
() =>
searchableMenu(
tab.columns.map(col => buildSubmenuForRevLookupMenuItem(col)),
{searchInputPlaceholder: t('Search columns')}
),
{allowNothingSelected: true}, `${tab.tableName} [← ${refCol.label()}]`);
};
const tablesWithAnyRefColumn = getReferencesToThisTable();
return tablesWithAnyRefColumn.map((tab: RefTable) => tab.referenceFields.map((refCol) =>
buildSubmenuForRevLookup(tab, refCol)
));
const label = `${tab.tableName} [← ${refCol.label()}]`;
const options = {allowNothingSelected: true};
const submenu = () => {
const subItems = tab.columns.map(buildSubmenuForRevLookupMenuItem);
return searchableMenu(subItems, {searchInputPlaceholder: t('Search columns')});
};
return menuItemSubmenu(submenu, options, label, testId('new-columns-menu-revlookup'));
}));
}
const lookupMenu = buildLookupsMenuItems();

View File

@@ -701,9 +701,6 @@ export interface GristLoadConfig {
permittedCustomWidgets?: IAttachedCustomWidget[];
// Feature flag for the new column menu.
gristNewColumnMenu?: boolean;
// Used to determine which disclosure links should be provided to user of
// formula assistance.
assistantService?: 'OpenAI' | undefined;

View File

@@ -80,7 +80,6 @@ export function makeGristConfig(options: MakeGristConfigOptions): GristLoadConfi
featureFormulaAssistant: Boolean(process.env.OPENAI_API_KEY || process.env.ASSISTANT_CHAT_COMPLETION_ENDPOINT),
assistantService: process.env.OPENAI_API_KEY ? 'OpenAI' : undefined,
permittedCustomWidgets: getPermittedCustomWidgets(server),
gristNewColumnMenu: isAffirmative(process.env.GRIST_NEW_COLUMN_MENU),
supportEmail: SUPPORT_EMAIL,
userLocale: (req as RequestWithLogin | undefined)?.user?.options?.locale,
telemetry: server?.getTelemetry().getTelemetryConfig(req as RequestWithLogin | undefined),