From beffd02c41f03e4d887939c125eefadf40ac1d61 Mon Sep 17 00:00:00 2001 From: George Gevoian Date: Thu, 20 Jul 2023 14:18:32 -0400 Subject: [PATCH] (core) Improve highlighting of previewed formula Summary: Also improves highlighting of columns when the "Click to insert" tooltip is shown, and improves highlighting of transforming columns. Test Plan: Manual. Reviewers: paulfitz Reviewed By: paulfitz Differential Revision: https://phab.getgrist.com/D3962 --- app/client/components/GridView.css | 30 ++++++++-------- app/client/components/GridView.js | 5 +-- app/client/components/viewCommon.css | 13 ++++++- app/client/models/RuleOwner.ts | 3 ++ app/client/models/entities/ViewFieldRec.ts | 1 + app/client/ui2018/cssVars.ts | 2 ++ app/client/widgets/FormulaAssistant.ts | 41 ++++++++++++++++------ app/client/widgets/TextBox.css | 4 --- app/common/ThemePrefs-ti.ts | 2 ++ app/common/ThemePrefs.ts | 2 ++ app/common/themes/GristDark.ts | 2 ++ app/common/themes/GristLight.ts | 2 ++ 12 files changed, 74 insertions(+), 33 deletions(-) diff --git a/app/client/components/GridView.css b/app/client/components/GridView.css index dc596c4d..9580c545 100644 --- a/app/client/components/GridView.css +++ b/app/client/components/GridView.css @@ -346,23 +346,23 @@ } } -/* Column hover effect */ +/* Highlight the entire column when the "Click to insert" tooltip is shown. */ +.column_name.hover-column > .selection, +.column_name.hover-column.selected > .selection, +.gridview_row .field.hover-column > .selection { + background-color: var(--grist-theme-selection, var(--grist-color-selection)); + inset: 0; + position: absolute; +} -.gridview_row .field.hover-column, /* normal field in a row */ -.gridview_row .field.hover-column .field_clip, -.column_name.hover-column, /* column name */ -.column_name.hover-column.selected, /* selected column name */ -.gridview_row .field.frozen.hover-column /* frozen field in a row */ { - /* for frozen fields can't use alpha channel */ - background-color: var(--grist-theme-selection-opaque-bg, var(--grist-color-selection-opaque)); - color: var(--grist-theme-selection-opaque-fg, unset); +/* Use a darker highlight if the column is being transformed. */ +.gridview_row .field.transform_field.hover-column > .selection { + background-color: var(--grist-theme-selection-darkest, rgba(22,179,120,0.35)); + inset: 0; + position: absolute; } -/* For zebra stripes, make the selection little darker */ -.record-zebra.record-even .field.hover-column { - background-color: var(--grist-theme-selection-opaque-dark-bg, var(--grist-color-selection-darker-opaque)); - color: var(--grist-theme-selection-opaque-fg, unset); -} -/* When column has a hover, remove menu button. */ + +/* And hide the column menu button. */ .column_name.hover-column .menu_toggle { visibility: hidden; } diff --git a/app/client/components/GridView.js b/app/client/components/GridView.js index 45dd5d56..702e2fbd 100644 --- a/app/client/components/GridView.js +++ b/app/client/components/GridView.js @@ -1139,7 +1139,8 @@ GridView.prototype.buildDom = function() { }, menu(ctl => this.columnContextMenu(ctl, this.getSelection(), field, filterTriggerCtl)), testId('column-menu-trigger'), - ) + ), + dom('div.selection'), ); }), this.isPreview ? null : kd.maybe(() => !this.gristDoc.isReadonlyKo(), () => ( @@ -1346,7 +1347,7 @@ GridView.prototype.buildDom = function() { kd.toggleClass('selected', isSelected), fieldBuilder.buildDomWithCursor(row, isCellActive, isCellSelected), - dom('div.field_selection') + dom('div.selection'), ); }) ) diff --git a/app/client/components/viewCommon.css b/app/client/components/viewCommon.css index 3f1077c9..edd431ba 100644 --- a/app/client/components/viewCommon.css +++ b/app/client/components/viewCommon.css @@ -110,13 +110,20 @@ color: var(--grist-actual-cell-color, unset); } -.field.selected > .field_selection { +.gridview_row .field.selected > .selection { background-color: var(--grist-theme-selection, var(--grist-color-selection)); position: absolute; inset: 0; pointer-events: none; } +.field.transform_field > .selection { + background-color: var(--grist-theme-selection-darker, rgba(22,179,120,0.25)); + position: absolute; + inset: 0; + pointer-events: none; +} + .field_clip.invalid, .field_clip.field-error-from-style { background-color: #ffb6c1; color: black; @@ -126,6 +133,10 @@ background-color: unset; } +.field.transform_field > .field_clip.invalid + .selection { + background-color: unset; +} + .field_clip.field-error-P { color: #B0B0B0; background-color: unset; diff --git a/app/client/models/RuleOwner.ts b/app/client/models/RuleOwner.ts index ecc84b82..068c3e20 100644 --- a/app/client/models/RuleOwner.ts +++ b/app/client/models/RuleOwner.ts @@ -1,6 +1,7 @@ import {ColumnRec, DocModel} from 'app/client/models/DocModel'; import {Style} from 'app/client/models/Styles'; import * as modelUtil from 'app/client/models/modelUtil'; +import {GristObjCode} from 'app/plugin/GristData'; export interface RuleOwner { // Field or Section can have a list of conditional styling rules. Each style is a combination of a formula and options @@ -9,6 +10,8 @@ export interface RuleOwner { tableId: ko.Computed; // If this field (or column) has a list of conditional styling rules. hasRules: ko.Computed; + // List of rules. + rulesList: ko.Computed<[GristObjCode.List, ...number[]] | null>; // List of columns that are used as rules for conditional styles. rulesCols: ko.Computed; // List of columns ids that are used as rules for conditional styles. diff --git a/app/client/models/entities/ViewFieldRec.ts b/app/client/models/entities/ViewFieldRec.ts index bb22183b..ca38abf2 100644 --- a/app/client/models/entities/ViewFieldRec.ts +++ b/app/client/models/entities/ViewFieldRec.ts @@ -253,6 +253,7 @@ export function createViewFieldRec(this: ViewFieldRec, docModel: DocModel): void }); this.tableId = ko.pureComputed(() => this.column().table().tableId()); + this.rulesList = ko.pureComputed(() => this._fieldOrColumn().rules()); this.rulesCols = refListRecords(docModel.columns, ko.pureComputed(() => this._fieldOrColumn().rules())); this.rulesColsIds = ko.pureComputed(() => this.rulesCols().map(c => c.colId())); this.rulesStyles = modelUtil.fieldWithDefault( diff --git a/app/client/ui2018/cssVars.ts b/app/client/ui2018/cssVars.ts index a23c6770..ea94e30c 100644 --- a/app/client/ui2018/cssVars.ts +++ b/app/client/ui2018/cssVars.ts @@ -337,6 +337,8 @@ export const theme = { /* Selection */ selection: new CustomProp('theme-selection', undefined, colors.selection), + selectionDarker: new CustomProp('theme-selection-darker', undefined, 'rgba(22,179,120,0.25)'), + selectionDarkest: new CustomProp('theme-selection-darkest', undefined, 'rgba(22,179,120,0.35)'), selectionOpaqueFg: new CustomProp('theme-selection-opaque-fg', undefined, 'unset'), selectionOpaqueBg: new CustomProp('theme-selection-opaque-bg', undefined, colors.selectionOpaque), diff --git a/app/client/widgets/FormulaAssistant.ts b/app/client/widgets/FormulaAssistant.ts index 7aa8ee89..dcc39bc2 100644 --- a/app/client/widgets/FormulaAssistant.ts +++ b/app/client/widgets/FormulaAssistant.ts @@ -56,6 +56,8 @@ export class FormulaAssistant extends Disposable { private _waiting = Observable.create(this, false); /** Is this feature enabled at all */ private _assistantEnabled: Computed; + /** Preview column ref */ + private _transformColRef: string; /** Preview column id */ private _transformColId: string; /** Method to invoke when we are closed, it saves or reverts */ @@ -136,14 +138,20 @@ export class FormulaAssistant extends Disposable { description: 'Formula Editor', prepare: () => this._preparePreview(), finalize: () => this._cleanupPreview(), - shouldIncludeInBundle: (a) => { - const tableId = this._options.column.table.peek().tableId.peek(); - const allowed = a.length === 1 - && a[0][0] === 'ModifyColumn' - && a[0][1] === tableId - && typeof a[0][2] === 'string' - && [this._transformColId, this._options.column.id.peek()].includes(a[0][2]); - return allowed; + shouldIncludeInBundle: (actions) => { + if (actions.length !== 1) { return false; } + + const actionName = actions[0][0]; + if (actionName === 'ModifyColumn') { + const tableId = this._options.column.table.peek().tableId.peek(); + return actions[0][1] === tableId + && typeof actions[0][2] === 'string' + && [this._transformColId, this._options.column.id.peek()].includes(actions[0][2]); + } else if (actionName === 'UpdateRecord') { + return actions[0][1] === '_grist_Tables_column' && actions[0][2] === this._transformColRef; + } else { + return false; + } } }); @@ -342,14 +350,25 @@ export class FormulaAssistant extends Disposable { const tableId = this._options.column.table.peek().tableId.peek(); // Add a new column to the table, and set it as the transform column. - const colInfo = await docData.sendAction(['AddColumn', tableId, 'gristHelper_Transform', { + const {colRef, colId} = await docData.sendAction(['AddColumn', tableId, 'gristHelper_Transform', { type: this._options.column.type.peek(), label: this._options.column.colId.peek(), isFormula: true, formula: this._options.column.formula.peek(), + widgetOptions: JSON.stringify(this._options.field?.widgetOptionsJson()), }]); - this._options.field?.colRef(colInfo.colRef); // Don't save, it is only in browser. - this._transformColId = colInfo.colId; + + this._transformColRef = colRef; + this._transformColId = colId; + + const rules = this._options.field?.rulesList(); + if (rules) { + await docData.sendAction(['UpdateRecord', '_grist_Tables_column', colRef, { + rules: this._options.field?.rulesList(), + }]); + } + + this._options.field?.colRef(colRef); // Don't save, it is only in browser. // Update the transform column so that it points to the original column. const transformColumn = this._options.field?.column.peek(); diff --git a/app/client/widgets/TextBox.css b/app/client/widgets/TextBox.css index 3c43ec82..51aae901 100644 --- a/app/client/widgets/TextBox.css +++ b/app/client/widgets/TextBox.css @@ -1,10 +1,6 @@ .record-add .field_clip { background-color: var(--grist-theme-table-add-new-bg, inherit); } -.transform_field { - color: black; - background-color: #FEFFE8; -} @media not print { .formula_field, .formula_field_edit { diff --git a/app/common/ThemePrefs-ti.ts b/app/common/ThemePrefs-ti.ts index 3d86dd87..833a7151 100644 --- a/app/common/ThemePrefs-ti.ts +++ b/app/common/ThemePrefs-ti.ts @@ -132,6 +132,8 @@ export const ThemeColors = t.iface([], { "card-list-form-border": "string", "card-list-blocks-border": "string", "selection": "string", + "selection-darker": "string", + "selection-darkest": "string", "selection-opaque-fg": "string", "selection-opaque-bg": "string", "selection-opaque-dark-bg": "string", diff --git a/app/common/ThemePrefs.ts b/app/common/ThemePrefs.ts index 0d084a08..cb4ac57b 100644 --- a/app/common/ThemePrefs.ts +++ b/app/common/ThemePrefs.ts @@ -168,6 +168,8 @@ export interface ThemeColors { /* Selection */ 'selection': string; + 'selection-darker': string; + 'selection-darkest': string; 'selection-opaque-fg': string; 'selection-opaque-bg': string; 'selection-opaque-dark-bg': string; diff --git a/app/common/themes/GristDark.ts b/app/common/themes/GristDark.ts index d9013038..759459f8 100644 --- a/app/common/themes/GristDark.ts +++ b/app/common/themes/GristDark.ts @@ -147,6 +147,8 @@ export const GristDark: ThemeColors = { /* Selection */ 'selection': 'rgba(22,179,120,0.15)', + 'selection-darker': 'rgba(22,179,120,0.25)', + 'selection-darkest': 'rgba(22,179,120,0.35)', 'selection-opaque-fg': 'white', 'selection-opaque-bg': '#2F4748', 'selection-opaque-dark-bg': '#253E3E', diff --git a/app/common/themes/GristLight.ts b/app/common/themes/GristLight.ts index 28819ab6..36d12d5d 100644 --- a/app/common/themes/GristLight.ts +++ b/app/common/themes/GristLight.ts @@ -147,6 +147,8 @@ export const GristLight: ThemeColors = { /* Selection */ 'selection': 'rgba(22,179,120,0.15)', + 'selection-darker': 'rgba(22,179,120,0.25)', + 'selection-darkest': 'rgba(22,179,120,0.35)', 'selection-opaque-fg': 'black', 'selection-opaque-bg': '#DCF4EB', 'selection-opaque-dark-bg': '#D6EEE5',