(core) updates from grist-core

This commit is contained in:
Paul Fitzpatrick
2023-02-27 09:30:46 -05:00
23 changed files with 1453 additions and 40 deletions

View File

@@ -144,12 +144,9 @@ AceEditor.prototype.attachSaveCommand = function() {
if (!this.observable) {
throw new Error("Cannot attach save command to editor with no bound observable");
}
var key = 'Shift+Enter';
this.editor.commands.addCommand({
name: 'saveFormula',
bindKey: {
win: key,
mac: key,
sender: 'editor|cli'
},
// AceEditor wants a command to return true if it got handled

View File

@@ -30,6 +30,17 @@
padding: .5rem;
}
.g_record_detail_label_container {
display: flex;
justify-content: flex-start;
gap: 3px;
}
.g_record_detail_label_container .info_toggle_icon {
width: 13px;
height: 13px;
margin-bottom: 3px;
}
.g_record_detail_label {
min-height: 1rem;
color: #666;
@@ -150,7 +161,7 @@
-webkit-flex-direction: column-reverse;
}
.detail_theme_field_under > .g_record_detail_label {
.detail_theme_field_under .g_record_detail_label {
border-top: 1px solid #333;
}
@@ -208,7 +219,7 @@
line-height: 1.2;
}
.detail_theme_field_compact > .g_record_detail_label {
.detail_theme_field_compact .g_record_detail_label {
font-weight: normal;
font-size: var(--grist-small-font-size);
color: var(--grist-theme-card-compact-label, var(--grist-color-slate));
@@ -230,7 +241,7 @@
padding: 1px 1px 1px 5px;
}
.detail_theme_field_form > .g_record_detail_label {
.detail_theme_field_form .g_record_detail_label {
font-size: var(--grist-small-font-size);
color: var(--grist-theme-card-form-label, var(--grist-color-slate));
font-weight: bold;
@@ -241,6 +252,10 @@
margin-right: -8px;
}
.detail_theme_field_form .g_record_detail_label_container {
gap: 8px;
}
/* TODO want to style better the values themselves (e.g. more padding, rounded corners, move label
* inside value box for compact view for better cursor looks, etc), but first the cell editor
* needs to learn to match the value box's style. Right now, the cell editor style is hard-coded.
@@ -288,7 +303,7 @@
border-radius: 2px;
}
.detail_theme_field_blocks > .g_record_detail_label {
.detail_theme_field_blocks .g_record_detail_label {
font-size: var(--grist-small-font-size);
color: var(--grist-theme-card-blocks-label, var(--grist-color-slate));
font-weight: normal;

View File

@@ -15,6 +15,7 @@ const RecordLayout = require('./RecordLayout');
const commands = require('./commands');
const {RowContextMenu} = require('../ui/RowContextMenu');
const {parsePasteForView} = require("./BaseView2");
const {columnInfoTooltip} = require("../ui/tooltips");
/**
* DetailView component implements a list of record layouts.
@@ -227,8 +228,10 @@ DetailView.prototype.buildFieldDom = function(field, row) {
if (field.isNewField) {
return dom('div.g_record_detail_el.flexitem',
kd.cssClass(function() { return 'detail_theme_field_' + self.viewSection.themeDef(); }),
dom('div.g_record_detail_label', field.label),
dom('div.g_record_detail_value', field.value)
dom('div.g_record_detail_label_container',
dom('div.g_record_detail_label', kd.text(field.displayLabel)),
kd.scope(field.description, desc => desc ? columnInfoTooltip(kd.text(field.description)) : null)
),
);
}
@@ -257,7 +260,10 @@ DetailView.prototype.buildFieldDom = function(field, row) {
dom.autoDispose(isCellSelected),
dom.autoDispose(isCellActive),
kd.cssClass(function() { return 'detail_theme_field_' + self.viewSection.themeDef(); }),
dom('div.g_record_detail_label', kd.text(field.displayLabel)),
dom('div.g_record_detail_label_container',
dom('div.g_record_detail_label', kd.text(field.displayLabel)),
kd.scope(field.description, desc => desc ? columnInfoTooltip(kd.text(field.description)) : null)
),
dom('div.g_record_detail_value',
kd.toggleClass('scissors', isCopyActive),
kd.toggleClass('record-add', row._isAddRow),

View File

@@ -20,6 +20,7 @@ export interface ViewFieldRec extends IRowModel<"_grist_Views_section_field">, R
origCol: ko.Computed<ColumnRec>;
colId: ko.Computed<string>;
label: ko.Computed<string>;
description: ko.Computed<string>;
// displayLabel displays label by default but switches to the more helpful colId whenever a
// formula field in the view is being edited.
@@ -108,6 +109,7 @@ export function createViewFieldRec(this: ViewFieldRec, docModel: DocModel): void
this.origCol = ko.pureComputed(() => this.column().origCol());
this.colId = ko.pureComputed(() => this.column().colId());
this.label = ko.pureComputed(() => this.column().label());
this.description = ko.pureComputed(() => this.column().description());
// displayLabel displays label by default but switches to the more helpful colId whenever a
// formula field in the view is being edited.

View File

@@ -5,10 +5,10 @@ import {BEHAVIOR, ColumnRec} from 'app/client/models/entities/ColumnRec';
import {buildHighlightedCode, cssCodeBlock} from 'app/client/ui/CodeHighlight';
import {GristTooltips} from 'app/client/ui/GristTooltips';
import {cssBlockedCursor, cssLabel, cssRow} from 'app/client/ui/RightPanelStyles';
import {withInfoTooltip} from 'app/client/ui/tooltips';
import { withInfoTooltip } from 'app/client/ui/tooltips';
import {buildFormulaTriggers} from 'app/client/ui/TriggerFormulas';
import {textButton} from 'app/client/ui2018/buttons';
import {testId, theme} from 'app/client/ui2018/cssVars';
import { testId, theme } from 'app/client/ui2018/cssVars';
import {textInput} from 'app/client/ui2018/editableLabel';
import {cssIconButton, icon} from 'app/client/ui2018/icons';
import {IconName} from 'app/client/ui2018/IconList';
@@ -18,6 +18,7 @@ import {sanitizeIdent} from 'app/common/gutil';
import {bundleChanges, Computed, dom, DomContents, DomElementArg, fromKo, MultiHolder,
Observable, styled} from 'grainjs';
import * as ko from 'knockout';
import { textarea } from './inputs';
const t = makeT('FieldConfig');
@@ -88,6 +89,40 @@ export function buildNameConfig(
];
}
export function buildDescriptionConfig(
owner: MultiHolder,
origColumn: ColumnRec,
cursor: ko.Computed<CursorPos>,
) {
// We will listen to cursor position and force a blur event on
// the text input, which will trigger save before the column observable
// will change its value.
// Otherwise, blur will be invoked after column change and save handler will
// update a different column.
let editor: HTMLTextAreaElement | undefined;
owner.autoDispose(
cursor.subscribe(() => {
editor?.blur();
})
);
return [
cssLabel(t("DESCRIPTION")),
cssRow(
editor = cssTextArea(fromKo(origColumn.description),
{ onInput: false },
{ rows: '3' },
dom.on('blur', async (e, elem) => {
await origColumn.description.saveOnly(elem.value);
}),
testId('column-description'),
)
),
];
}
type SaveHandler = (column: ColumnRec, formula: string) => Promise<void>;
type BuildEditor = (
cellElem: Element,
@@ -494,3 +529,22 @@ const cssInput = styled(textInput, `
color: ${theme.inputDisabledFg};
}
`);
const cssTextArea = styled(textarea, `
color: ${theme.inputFg};
background-color: ${theme.mainPanelBg};
border: 1px solid ${theme.inputBorder};
width: 100%;
outline: none;
border-radius: 3px;
padding: 3px 7px;
&::placeholder {
color: ${theme.inputPlaceholderFg};
}
&[readonly] {
background-color: ${theme.inputDisabledBg};
color: ${theme.inputDisabledFg};
}
`);

View File

@@ -43,6 +43,7 @@ import {bundleChanges, Computed, Disposable, dom, domComputed, DomContents,
DomElementArg, DomElementMethod, IDomComponent} from 'grainjs';
import {MultiHolder, Observable, styled, subscribe} from 'grainjs';
import * as ko from 'knockout';
import { buildDescriptionConfig } from './FieldConfig';
const t = makeT('RightPanel');
@@ -235,6 +236,9 @@ export class RightPanel extends Disposable {
cssSection(
dom.create(buildNameConfig, origColumn, cursor, isMultiSelect),
),
cssSection(
dom.create(buildDescriptionConfig, origColumn, cursor),
),
cssSeparator(),
cssSection(
dom.create(buildFormulaConfig,

View File

@@ -242,7 +242,7 @@ export function tooltipCloseButton(ctl: ITooltipControl): HTMLElement {
/**
* Renders an info icon that shows a tooltip with the specified `content` on click.
*/
function infoTooltip(content: DomContents, menuOptions?: IMenuOptions, ...domArgs: DomElementArg[]) {
export function infoTooltip(content: DomContents, menuOptions?: IMenuOptions, ...domArgs: DomElementArg[]) {
return cssInfoTooltipButton('?',
(elem) => {
setPopupToCreateDom(
@@ -314,6 +314,62 @@ export function withInfoTooltip(
);
}
/**
* Renders an column info icon that shows a tooltip with the specified `content` on click.
*/
export function columnInfoTooltip(content: DomContents, menuOptions?: IMenuOptions, ...domArgs: DomElementArg[]) {
return cssColumnInfoTooltipButton(
icon('Info', dom.cls("info_toggle_icon")),
(elem) => {
setPopupToCreateDom(
elem,
(ctl) => {
return cssInfoTooltipPopup(
cssInfoTooltipPopupCloseButton(
icon('CrossSmall'),
dom.on('click', () => ctl.close()),
testId('column-info-tooltip-close'),
),
cssInfoTooltipPopupBody(
content,
{ style: 'white-space: pre-wrap;' },
testId('column-info-tooltip-popup-body'),
),
dom.cls(menuCssClass),
dom.cls(cssMenu.className),
dom.onKeyDown({
Enter: () => ctl.close(),
Escape: () => ctl.close(),
}),
(popup) => { setTimeout(() => popup.focus(), 0); },
testId('column-info-tooltip-popup'),
);
},
{ ...defaultMenuOptions, ...{ placement: 'bottom-end' }, ...menuOptions },
);
},
testId('column-info-tooltip'),
...domArgs,
);
}
const cssColumnInfoTooltipButton = styled('div', `
cursor: pointer;
--icon-color: ${theme.infoButtonFg};
border-radius: 50%;
display: inline-block;
margin-left: 5px;
line-height: 0px;
&:hover {
--icon-color: ${theme.infoButtonHoverFg};
}
&:active {
--icon-color: ${theme.infoButtonActiveFg};
}
`);
const cssTooltip = styled('div', `
position: absolute;
z-index: 5000; /* should be higher than a modal */

View File

@@ -605,6 +605,11 @@ export const theme = {
menuToggleBg: new CustomProp('theme-menu-toggle-bg', undefined, 'white'),
menuToggleBorder: new CustomProp('theme-menu-toggle-border', undefined, colors.slate),
/* Info Button */
infoButtonFg: new CustomProp('theme-info-button-fg', undefined, "#8F8F8F"),
infoButtonHoverFg: new CustomProp('theme-info-button-hover-fg', undefined, "#707070"),
infoButtonActiveFg: new CustomProp('theme-info-button-active-fg', undefined, "#5C5C5C"),
/* Button Groups */
buttonGroupFg: new CustomProp('theme-button-group-fg', undefined, colors.dark),
buttonGroupLightFg: new CustomProp('theme-button-group-light-fg', undefined, colors.slate),

View File

@@ -4,7 +4,7 @@ import { GristObjCode } from "app/plugin/GristData";
// tslint:disable:object-literal-key-quotes
export const SCHEMA_VERSION = 35;
export const SCHEMA_VERSION = 36;
export const schema = {
@@ -34,6 +34,7 @@ export const schema = {
isFormula : "Bool",
formula : "Text",
label : "Text",
description : "Text",
untieColIdFromLabel : "Bool",
summarySourceCol : "Ref:_grist_Tables_column",
displayCol : "Ref:_grist_Tables_column",
@@ -239,6 +240,7 @@ export interface SchemaTypes {
isFormula: boolean;
formula: string;
label: string;
description: string;
untieColIdFromLabel: boolean;
summarySourceCol: number;
displayCol: number;

View File

@@ -6,9 +6,9 @@ export const GRIST_DOC_SQL = `
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE IF NOT EXISTS "_grist_DocInfo" (id INTEGER PRIMARY KEY, "docId" TEXT DEFAULT '', "peers" TEXT DEFAULT '', "basketId" TEXT DEFAULT '', "schemaVersion" INTEGER DEFAULT 0, "timezone" TEXT DEFAULT '', "documentSettings" TEXT DEFAULT '');
INSERT INTO _grist_DocInfo VALUES(1,'','','',35,'','');
INSERT INTO _grist_DocInfo VALUES(1,'','','',36,'','');
CREATE TABLE IF NOT EXISTS "_grist_Tables" (id INTEGER PRIMARY KEY, "tableId" TEXT DEFAULT '', "primaryViewId" INTEGER DEFAULT 0, "summarySourceTable" INTEGER DEFAULT 0, "onDemand" BOOLEAN DEFAULT 0, "rawViewSectionRef" INTEGER DEFAULT 0);
CREATE TABLE IF NOT EXISTS "_grist_Tables_column" (id INTEGER PRIMARY KEY, "parentId" INTEGER DEFAULT 0, "parentPos" NUMERIC DEFAULT 1e999, "colId" TEXT DEFAULT '', "type" TEXT DEFAULT '', "widgetOptions" TEXT DEFAULT '', "isFormula" BOOLEAN DEFAULT 0, "formula" TEXT DEFAULT '', "label" TEXT DEFAULT '', "untieColIdFromLabel" BOOLEAN DEFAULT 0, "summarySourceCol" INTEGER DEFAULT 0, "displayCol" INTEGER DEFAULT 0, "visibleCol" INTEGER DEFAULT 0, "rules" TEXT DEFAULT NULL, "recalcWhen" INTEGER DEFAULT 0, "recalcDeps" TEXT DEFAULT NULL);
CREATE TABLE IF NOT EXISTS "_grist_Tables_column" (id INTEGER PRIMARY KEY, "parentId" INTEGER DEFAULT 0, "parentPos" NUMERIC DEFAULT 1e999, "colId" TEXT DEFAULT '', "type" TEXT DEFAULT '', "widgetOptions" TEXT DEFAULT '', "isFormula" BOOLEAN DEFAULT 0, "formula" TEXT DEFAULT '', "label" TEXT DEFAULT '', "description" TEXT DEFAULT '', "untieColIdFromLabel" BOOLEAN DEFAULT 0, "summarySourceCol" INTEGER DEFAULT 0, "displayCol" INTEGER DEFAULT 0, "visibleCol" INTEGER DEFAULT 0, "rules" TEXT DEFAULT NULL, "recalcWhen" INTEGER DEFAULT 0, "recalcDeps" TEXT DEFAULT NULL);
CREATE TABLE IF NOT EXISTS "_grist_Imports" (id INTEGER PRIMARY KEY, "tableRef" INTEGER DEFAULT 0, "origFileName" TEXT DEFAULT '', "parseFormula" TEXT DEFAULT '', "delimiter" TEXT DEFAULT '', "doublequote" BOOLEAN DEFAULT 0, "escapechar" TEXT DEFAULT '', "quotechar" TEXT DEFAULT '', "skipinitialspace" BOOLEAN DEFAULT 0, "encoding" TEXT DEFAULT '', "hasHeaders" BOOLEAN DEFAULT 0);
CREATE TABLE IF NOT EXISTS "_grist_External_database" (id INTEGER PRIMARY KEY, "host" TEXT DEFAULT '', "port" INTEGER DEFAULT 0, "username" TEXT DEFAULT '', "dialect" TEXT DEFAULT '', "database" TEXT DEFAULT '', "storage" TEXT DEFAULT '');
CREATE TABLE IF NOT EXISTS "_grist_External_table" (id INTEGER PRIMARY KEY, "tableRef" INTEGER DEFAULT 0, "databaseRef" INTEGER DEFAULT 0, "tableName" TEXT DEFAULT '');
@@ -43,14 +43,14 @@ export const GRIST_DOC_WITH_TABLE1_SQL = `
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE IF NOT EXISTS "_grist_DocInfo" (id INTEGER PRIMARY KEY, "docId" TEXT DEFAULT '', "peers" TEXT DEFAULT '', "basketId" TEXT DEFAULT '', "schemaVersion" INTEGER DEFAULT 0, "timezone" TEXT DEFAULT '', "documentSettings" TEXT DEFAULT '');
INSERT INTO _grist_DocInfo VALUES(1,'','','',35,'','');
INSERT INTO _grist_DocInfo VALUES(1,'','','',36,'','');
CREATE TABLE IF NOT EXISTS "_grist_Tables" (id INTEGER PRIMARY KEY, "tableId" TEXT DEFAULT '', "primaryViewId" INTEGER DEFAULT 0, "summarySourceTable" INTEGER DEFAULT 0, "onDemand" BOOLEAN DEFAULT 0, "rawViewSectionRef" INTEGER DEFAULT 0);
INSERT INTO _grist_Tables VALUES(1,'Table1',1,0,0,2);
CREATE TABLE IF NOT EXISTS "_grist_Tables_column" (id INTEGER PRIMARY KEY, "parentId" INTEGER DEFAULT 0, "parentPos" NUMERIC DEFAULT 1e999, "colId" TEXT DEFAULT '', "type" TEXT DEFAULT '', "widgetOptions" TEXT DEFAULT '', "isFormula" BOOLEAN DEFAULT 0, "formula" TEXT DEFAULT '', "label" TEXT DEFAULT '', "untieColIdFromLabel" BOOLEAN DEFAULT 0, "summarySourceCol" INTEGER DEFAULT 0, "displayCol" INTEGER DEFAULT 0, "visibleCol" INTEGER DEFAULT 0, "rules" TEXT DEFAULT NULL, "recalcWhen" INTEGER DEFAULT 0, "recalcDeps" TEXT DEFAULT NULL);
INSERT INTO _grist_Tables_column VALUES(1,1,1,'manualSort','ManualSortPos','',0,'','manualSort',0,0,0,0,NULL,0,NULL);
INSERT INTO _grist_Tables_column VALUES(2,1,2,'A','Any','',1,'','A',0,0,0,0,NULL,0,NULL);
INSERT INTO _grist_Tables_column VALUES(3,1,3,'B','Any','',1,'','B',0,0,0,0,NULL,0,NULL);
INSERT INTO _grist_Tables_column VALUES(4,1,4,'C','Any','',1,'','C',0,0,0,0,NULL,0,NULL);
CREATE TABLE IF NOT EXISTS "_grist_Tables_column" (id INTEGER PRIMARY KEY, "parentId" INTEGER DEFAULT 0, "parentPos" NUMERIC DEFAULT 1e999, "colId" TEXT DEFAULT '', "type" TEXT DEFAULT '', "widgetOptions" TEXT DEFAULT '', "isFormula" BOOLEAN DEFAULT 0, "formula" TEXT DEFAULT '', "label" TEXT DEFAULT '', "description" TEXT DEFAULT '', "untieColIdFromLabel" BOOLEAN DEFAULT 0, "summarySourceCol" INTEGER DEFAULT 0, "displayCol" INTEGER DEFAULT 0, "visibleCol" INTEGER DEFAULT 0, "rules" TEXT DEFAULT NULL, "recalcWhen" INTEGER DEFAULT 0, "recalcDeps" TEXT DEFAULT NULL);
INSERT INTO _grist_Tables_column VALUES(1,1,1,'manualSort','ManualSortPos','',0,'','manualSort','',0,0,0,0,NULL,0,NULL);
INSERT INTO _grist_Tables_column VALUES(2,1,2,'A','Any','',1,'','A','',0,0,0,0,NULL,0,NULL);
INSERT INTO _grist_Tables_column VALUES(3,1,3,'B','Any','',1,'','B','',0,0,0,0,NULL,0,NULL);
INSERT INTO _grist_Tables_column VALUES(4,1,4,'C','Any','',1,'','C','',0,0,0,0,NULL,0,NULL);
CREATE TABLE IF NOT EXISTS "_grist_Imports" (id INTEGER PRIMARY KEY, "tableRef" INTEGER DEFAULT 0, "origFileName" TEXT DEFAULT '', "parseFormula" TEXT DEFAULT '', "delimiter" TEXT DEFAULT '', "doublequote" BOOLEAN DEFAULT 0, "escapechar" TEXT DEFAULT '', "quotechar" TEXT DEFAULT '', "skipinitialspace" BOOLEAN DEFAULT 0, "encoding" TEXT DEFAULT '', "hasHeaders" BOOLEAN DEFAULT 0);
CREATE TABLE IF NOT EXISTS "_grist_External_database" (id INTEGER PRIMARY KEY, "host" TEXT DEFAULT '', "port" INTEGER DEFAULT 0, "username" TEXT DEFAULT '', "dialect" TEXT DEFAULT '', "database" TEXT DEFAULT '', "storage" TEXT DEFAULT '');
CREATE TABLE IF NOT EXISTS "_grist_External_table" (id INTEGER PRIMARY KEY, "tableRef" INTEGER DEFAULT 0, "databaseRef" INTEGER DEFAULT 0, "tableName" TEXT DEFAULT '');