mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
Merge pull request #406 from incubateur-territoires/column-description
feat: Add a description to a grist table column
This commit is contained in:
@@ -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};
|
||||
}
|
||||
`);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user