(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),