codeStyle(column-desc): create separate iconTooltip for column description

This commit is contained in:
Camille 2023-02-01 13:55:41 +01:00
parent 95f1a41618
commit bd9c9aa8f9
9 changed files with 115 additions and 112 deletions

View File

@ -244,6 +244,7 @@
.detail_theme_field_form .info_toggle_icon { .detail_theme_field_form .info_toggle_icon {
width: 13px; width: 13px;
height: 13px; height: 13px;
margin-bottom: 3px;
} }
/* TODO want to style better the values themselves (e.g. more padding, rounded corners, move label /* TODO want to style better the values themselves (e.g. more padding, rounded corners, move label

View File

@ -15,7 +15,7 @@ const RecordLayout = require('./RecordLayout');
const commands = require('./commands'); const commands = require('./commands');
const {RowContextMenu} = require('../ui/RowContextMenu'); const {RowContextMenu} = require('../ui/RowContextMenu');
const {parsePasteForView} = require("./BaseView2"); const {parsePasteForView} = require("./BaseView2");
const {withInfoTooltip} = require('../ui/tooltips'); const {columnInfoTooltip} = require("../ui/tooltips");
/** /**
* DetailView component implements a list of record layouts. * DetailView component implements a list of record layouts.
@ -228,12 +228,10 @@ DetailView.prototype.buildFieldDom = function(field, row) {
if (field.isNewField) { if (field.isNewField) {
return dom('div.g_record_detail_el.flexitem', return dom('div.g_record_detail_el.flexitem',
kd.cssClass(function() { return 'detail_theme_field_' + self.viewSection.themeDef(); }), kd.cssClass(function() { return 'detail_theme_field_' + self.viewSection.themeDef(); }),
field.description.peek() ? dom('div.g_record_detail_label',
withInfoTooltip( kd.text(field.displayLabel),
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_description', kd.text(field.description)), ),
) : dom('div.g_record_detail_label', kd.text(field.displayLabel)),
dom('div.g_record_detail_value', field.value)
); );
} }
@ -262,11 +260,10 @@ DetailView.prototype.buildFieldDom = function(field, row) {
dom.autoDispose(isCellSelected), dom.autoDispose(isCellSelected),
dom.autoDispose(isCellActive), dom.autoDispose(isCellActive),
kd.cssClass(function() { return 'detail_theme_field_' + self.viewSection.themeDef(); }), kd.cssClass(function() { return 'detail_theme_field_' + self.viewSection.themeDef(); }),
field.description.peek() ? dom('div.g_record_detail_label',
withInfoTooltip( kd.text(field.displayLabel),
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_description', kd.text(field.description)), ),
) : dom('div.g_record_detail_label', kd.text(field.displayLabel)),
dom('div.g_record_detail_value', dom('div.g_record_detail_value',
kd.toggleClass('scissors', isCopyActive), kd.toggleClass('scissors', isCopyActive),
kd.toggleClass('record-add', row._isAddRow), kd.toggleClass('record-add', row._isAddRow),

View File

@ -2,7 +2,7 @@ import {DocPageModel} from 'app/client/models/DocPageModel';
import {urlState} from 'app/client/models/gristUrlState'; import {urlState} from 'app/client/models/gristUrlState';
import {docListHeader} from 'app/client/ui/DocMenuCss'; import {docListHeader} from 'app/client/ui/DocMenuCss';
import {GristTooltips, TooltipContentFunc} from 'app/client/ui/GristTooltips'; import {GristTooltips, TooltipContentFunc} from 'app/client/ui/GristTooltips';
import { withQuestionMarkTooltip } from 'app/client/ui/tooltips'; import {withInfoTooltip} from 'app/client/ui/tooltips';
import {mediaXSmall, theme} from 'app/client/ui2018/cssVars'; import {mediaXSmall, theme} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {loadingDots, loadingSpinner} from 'app/client/ui2018/loaders'; import {loadingDots, loadingSpinner} from 'app/client/ui2018/loaders';
@ -284,7 +284,7 @@ function buildUsageMetric(options: MetricOptions, ...domArgs: DomElementArg[]) {
return cssUsageMetric( return cssUsageMetric(
cssMetricName( cssMetricName(
tooltipContentFunc tooltipContentFunc
? withQuestionMarkTooltip( ? withInfoTooltip(
cssOverflowableText(name, testId('name')), cssOverflowableText(name, testId('name')),
tooltipContentFunc() tooltipContentFunc()
) )

View File

@ -5,7 +5,7 @@ import {BEHAVIOR, ColumnRec} from 'app/client/models/entities/ColumnRec';
import {buildHighlightedCode, cssCodeBlock} from 'app/client/ui/CodeHighlight'; import {buildHighlightedCode, cssCodeBlock} from 'app/client/ui/CodeHighlight';
import {GristTooltips} from 'app/client/ui/GristTooltips'; import {GristTooltips} from 'app/client/ui/GristTooltips';
import {cssBlockedCursor, cssLabel, cssRow} from 'app/client/ui/RightPanelStyles'; import {cssBlockedCursor, cssLabel, cssRow} from 'app/client/ui/RightPanelStyles';
import { withQuestionMarkTooltip } from 'app/client/ui/tooltips'; import { withInfoTooltip } from 'app/client/ui/tooltips';
import {buildFormulaTriggers} from 'app/client/ui/TriggerFormulas'; import {buildFormulaTriggers} from 'app/client/ui/TriggerFormulas';
import {textButton} from 'app/client/ui2018/buttons'; import {textButton} from 'app/client/ui2018/buttons';
import { testId, theme, vars } from 'app/client/ui2018/cssVars'; import { testId, theme, vars } from 'app/client/ui2018/cssVars';
@ -371,7 +371,7 @@ export function buildFormulaConfig(
dom.prop("disabled", disableOtherActions), dom.prop("disabled", disableOtherActions),
testId("field-set-formula") testId("field-set-formula")
)), )),
cssRow(withQuestionMarkTooltip( cssRow(withInfoTooltip(
textButton( textButton(
t("Set trigger formula"), t("Set trigger formula"),
dom.on("click", setTrigger), dom.on("click", setTrigger),
@ -424,7 +424,7 @@ export function buildFormulaConfig(
// Else offer a way to convert to trigger formula. // Else offer a way to convert to trigger formula.
dom.maybe((use) => !(use(maybeTrigger) || use(origColumn.hasTriggerFormula)), () => [ dom.maybe((use) => !(use(maybeTrigger) || use(origColumn.hasTriggerFormula)), () => [
cssEmptySeparator(), cssEmptySeparator(),
cssRow(withQuestionMarkTooltip( cssRow(withInfoTooltip(
textButton( textButton(
t("Set trigger formula"), t("Set trigger formula"),
dom.on("click", convertDataColumnToTriggerColumn), dom.on("click", convertDataColumnToTriggerColumn),

View File

@ -5,7 +5,7 @@ import { reportError } from 'app/client/models/AppModel';
import { ColumnRec, TableRec, ViewSectionRec } from 'app/client/models/DocModel'; import { ColumnRec, TableRec, ViewSectionRec } from 'app/client/models/DocModel';
import { GristTooltips } from 'app/client/ui/GristTooltips'; import { GristTooltips } from 'app/client/ui/GristTooltips';
import { linkId, NoLink } from 'app/client/ui/selectBy'; import { linkId, NoLink } from 'app/client/ui/selectBy';
import { withQuestionMarkTooltip } from 'app/client/ui/tooltips'; import { withInfoTooltip } from 'app/client/ui/tooltips';
import { getWidgetTypes, IWidgetType } from 'app/client/ui/widgetTypes'; import { getWidgetTypes, IWidgetType } from 'app/client/ui/widgetTypes';
import { bigPrimaryButton } from "app/client/ui2018/buttons"; import { bigPrimaryButton } from "app/client/ui2018/buttons";
import { theme, vars } from "app/client/ui2018/cssVars"; import { theme, vars } from "app/client/ui2018/cssVars";
@ -358,7 +358,7 @@ export class PageWidgetSelect extends Disposable {
cssFooterContent( cssFooterContent(
// If _selectByOptions exists and has more than then "NoLinkOption", show the selector. // If _selectByOptions exists and has more than then "NoLinkOption", show the selector.
dom.maybe((use) => this._selectByOptions && use(this._selectByOptions).length > 1, () => dom.maybe((use) => this._selectByOptions && use(this._selectByOptions).length > 1, () =>
withQuestionMarkTooltip( withInfoTooltip(
cssSelectBy( cssSelectBy(
cssSmallLabel('SELECT BY'), cssSmallLabel('SELECT BY'),
dom.update(cssSelect(this._value.link, this._selectByOptions!), dom.update(cssSelect(this._value.link, this._selectByOptions!),

View File

@ -5,7 +5,7 @@ import {docUrl, urlState} from 'app/client/models/gristUrlState';
import {GristTooltips} from 'app/client/ui/GristTooltips'; import {GristTooltips} from 'app/client/ui/GristTooltips';
import {makeCopy, replaceTrunkWithFork} from 'app/client/ui/MakeCopyMenu'; import {makeCopy, replaceTrunkWithFork} from 'app/client/ui/MakeCopyMenu';
import {sendToDrive} from 'app/client/ui/sendToDrive'; import {sendToDrive} from 'app/client/ui/sendToDrive';
import { hoverTooltip, withQuestionMarkTooltip } from 'app/client/ui/tooltips'; import {hoverTooltip, withInfoTooltip} from 'app/client/ui/tooltips';
import {cssHoverCircle, cssTopBarBtn} from 'app/client/ui/TopBarCss'; import {cssHoverCircle, cssTopBarBtn} from 'app/client/ui/TopBarCss';
import {primaryButton} from 'app/client/ui2018/buttons'; import {primaryButton} from 'app/client/ui2018/buttons';
import {mediaXSmall, testId, theme} from 'app/client/ui2018/cssVars'; import {mediaXSmall, testId, theme} from 'app/client/ui2018/cssVars';
@ -207,7 +207,7 @@ function menuWorkOnCopy(pageModel: DocPageModel) {
return [ return [
menuItem(makeUnsavedCopy, t("Work on a Copy"), testId('work-on-copy')), menuItem(makeUnsavedCopy, t("Work on a Copy"), testId('work-on-copy')),
menuText( menuText(
withQuestionMarkTooltip( withInfoTooltip(
t("Edit without affecting the original"), t("Edit without affecting the original"),
GristTooltips.workOnACopy(), GristTooltips.workOnACopy(),
{tooltipMenuOptions: {attach: null}} {tooltipMenuOptions: {attach: null}}

View File

@ -28,7 +28,7 @@ import {UserManagerModel, UserManagerModelImpl} from 'app/client/models/UserMana
import {getResourceParent, ResourceType} from 'app/client/models/UserManagerModel'; import {getResourceParent, ResourceType} from 'app/client/models/UserManagerModel';
import {GristTooltips} from 'app/client/ui/GristTooltips'; import {GristTooltips} from 'app/client/ui/GristTooltips';
import {shadowScroll} from 'app/client/ui/shadowScroll'; import {shadowScroll} from 'app/client/ui/shadowScroll';
import { hoverTooltip, ITooltipControl, showTransientTooltip, withQuestionMarkTooltip } from 'app/client/ui/tooltips'; import {hoverTooltip, ITooltipControl, showTransientTooltip, withInfoTooltip} from 'app/client/ui/tooltips';
import {createUserImage} from 'app/client/ui/UserImage'; import {createUserImage} from 'app/client/ui/UserImage';
import {cssMemberBtn, cssMemberImage, cssMemberListItem, import {cssMemberBtn, cssMemberImage, cssMemberListItem,
cssMemberPrimary, cssMemberSecondary, cssMemberText, cssMemberType, cssMemberTypeProblem, cssMemberPrimary, cssMemberSecondary, cssMemberText, cssMemberType, cssMemberTypeProblem,
@ -169,7 +169,7 @@ function buildUserManagerModal(
testId('um-cancel') testId('um-cancel')
), ),
(model.resourceType === 'document' && model.gristDoc && !model.isPersonal (model.resourceType === 'document' && model.gristDoc && !model.isPersonal
? withQuestionMarkTooltip( ? withInfoTooltip(
cssLink({href: urlState().makeUrl({docPage: 'acl'})}, cssLink({href: urlState().makeUrl({docPage: 'acl'})},
dom.text(use => use(model.isAnythingChanged) ? 'Save & ' : ''), dom.text(use => use(model.isAnythingChanged) ? 'Save & ' : ''),
'Open Access Rules', 'Open Access Rules',

View File

@ -9,7 +9,7 @@ import {prepareForTransition} from 'app/client/ui/transitions';
import {testId, theme, vars} from 'app/client/ui2018/cssVars'; import {testId, theme, vars} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons'; import {icon} from 'app/client/ui2018/icons';
import {menuCssClass} from 'app/client/ui2018/menus'; import {menuCssClass} from 'app/client/ui2018/menus';
import { dom, DomContents, DomElementArg, DomElementMethod, IDomArgs, styled } from 'grainjs'; import {dom, DomContents, DomElementArg, DomElementMethod, styled} from 'grainjs';
import Popper from 'popper.js'; import Popper from 'popper.js';
import {cssMenu, defaultMenuOptions, IMenuOptions, setPopupToCreateDom} from 'popweasel'; import {cssMenu, defaultMenuOptions, IMenuOptions, setPopupToCreateDom} from 'popweasel';
@ -240,59 +240,43 @@ export function tooltipCloseButton(ctl: ITooltipControl): HTMLElement {
} }
/** /**
* Renders an icon that shows a tooltip with the specified `content` on click. * Renders an info icon that shows a tooltip with the specified `content` on click.
*/ */
function iconTooltip( export function infoTooltip(content: DomContents, menuOptions?: IMenuOptions, ...domArgs: DomElementArg[]) {
cssStyledFunc: (...args: IDomArgs<HTMLElement>) => HTMLElement, return cssInfoTooltipButton('?',
tooltipButtonContent: HTMLElement, (elem) => {
content: DomContents,
menuOptions?: IMenuOptions,
...domArgs: DomElementArg[]
) {
return cssStyledFunc(tooltipButtonContent, (elem) => {
setPopupToCreateDom( setPopupToCreateDom(
elem, elem,
(ctl) => { (ctl) => {
return cssInfoTooltipPopup( return cssInfoTooltipPopup(
cssInfoTooltipPopupCloseButton( cssInfoTooltipPopupCloseButton(
icon("CrossSmall"), icon('CrossSmall'),
dom.on("click", () => ctl.close()), dom.on('click', () => ctl.close()),
testId("info-tooltip-close") testId('info-tooltip-close'),
),
cssInfoTooltipPopupBody(
content,
testId('info-tooltip-popup-body'),
), ),
cssInfoTooltipPopupBody(content, testId("info-tooltip-popup-body")),
dom.cls(menuCssClass), dom.cls(menuCssClass),
dom.cls(cssMenu.className), dom.cls(cssMenu.className),
dom.onKeyDown({ dom.onKeyDown({
Enter: () => ctl.close(), Enter: () => ctl.close(),
Escape: () => ctl.close(), Escape: () => ctl.close(),
}), }),
(popup) => { (popup) => { setTimeout(() => popup.focus(), 0); },
setTimeout(() => popup.focus(), 0); testId('info-tooltip-popup'),
},
testId("info-tooltip-popup")
); );
}, },
{ ...defaultMenuOptions, ...{ placement: "bottom-end" }, ...menuOptions } {...defaultMenuOptions, ...{placement: 'bottom-end'}, ...menuOptions},
);
},
testId('info-tooltip'),
...domArgs,
); );
});
} }
function questionMarkTooltip(content: DomContents, menuOptions?: IMenuOptions, ...domArgs: DomElementArg[]) { export interface WithInfoTooltipOptions {
return iconTooltip(
cssQuestionMarkTooltipButton,
cssQuestionMark('?', testId('info-tooltip'), ...domArgs),
content,
menuOptions);
}
function infoTooltip(content: DomContents, menuOptions?: IMenuOptions, ...domArgs: DomElementArg[]) {
return iconTooltip(
cssInfoTooltipButton,
icon('Info', dom.cls('info_toggle_icon'), testId('info-tooltip'), ...domArgs),
content, menuOptions);
}
export interface WithIconTooltipOptions {
domArgs?: DomElementArg[]; domArgs?: DomElementArg[];
tooltipButtonDomArgs?: DomElementArg[]; tooltipButtonDomArgs?: DomElementArg[];
tooltipMenuOptions?: IMenuOptions; tooltipMenuOptions?: IMenuOptions;
@ -312,30 +296,17 @@ export interface WithIconTooltipOptions {
* *
* Usage: * Usage:
* *
* withQuestionMarkTooltip( * withInfoTooltip(
* dom('div', 'Hello World!'), * dom('div', 'Hello World!'),
* dom('p', 'This is some text to show inside the tooltip.'), * dom('p', 'This is some text to show inside the tooltip.'),
* ) * )
*/ */
export function withQuestionMarkTooltip(
domContents: DomContents,
tooltipContent: DomContents,
options: WithIconTooltipOptions = {},
) {
const { domArgs, tooltipButtonDomArgs, tooltipMenuOptions } = options;
return cssDomWithTooltip(
domContents,
questionMarkTooltip(tooltipContent, tooltipMenuOptions, tooltipButtonDomArgs),
...(domArgs ?? [])
);
}
export function withInfoTooltip( export function withInfoTooltip(
domContents: DomContents, domContents: DomContents,
tooltipContent: DomContents, tooltipContent: DomContents,
options: WithIconTooltipOptions = {}, options: WithInfoTooltipOptions = {},
) { ) {
const { domArgs, tooltipButtonDomArgs, tooltipMenuOptions } = options; const {domArgs, tooltipButtonDomArgs, tooltipMenuOptions} = options;
return cssDomWithTooltip( return cssDomWithTooltip(
domContents, domContents,
infoTooltip(tooltipContent, tooltipMenuOptions, tooltipButtonDomArgs), infoTooltip(tooltipContent, tooltipMenuOptions, tooltipButtonDomArgs),
@ -343,6 +314,59 @@ 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,
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;
&:hover {
--icon-color: ${theme.infoButtonHoverFg};
}
&:active {
--icon-color: ${theme.infoButtonActiveFg};
}
`);
const cssTooltip = styled('div', ` const cssTooltip = styled('div', `
position: absolute; position: absolute;
z-index: 5000; /* should be higher than a modal */ z-index: 5000; /* should be higher than a modal */
@ -376,7 +400,7 @@ const cssTooltipCloseButton = styled('div', `
} }
`); `);
const cssQuestionMark = styled('div', ` const cssInfoTooltipButton = styled('div', `
flex-shrink: 0; flex-shrink: 0;
display: flex; display: flex;
align-items: center; align-items: center;
@ -395,25 +419,6 @@ const cssQuestionMark = styled('div', `
} }
`); `);
const cssQuestionMarkTooltipButton = styled('div', `
cursor: pointer;
`);
const cssInfoTooltipButton = styled('div', `
cursor: pointer;
--icon-color: ${theme.infoButtonFg};
border-radius: 50%;
&:hover {
--icon-color: ${theme.infoButtonHoverFg};
}
&:active {
--icon-color: ${theme.infoButtonActiveFg};
}
& > .info_toggle_icon {
display: block; /* don't create a line */
}
`);
const cssInfoTooltipPopup = styled('div', ` const cssInfoTooltipPopup = styled('div', `
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -6,7 +6,7 @@ import {RuleOwner} from 'app/client/models/RuleOwner';
import {Style} from 'app/client/models/Styles'; import {Style} from 'app/client/models/Styles';
import {cssFieldFormula} from 'app/client/ui/FieldConfig'; import {cssFieldFormula} from 'app/client/ui/FieldConfig';
import {GristTooltips} from 'app/client/ui/GristTooltips'; import {GristTooltips} from 'app/client/ui/GristTooltips';
import { withQuestionMarkTooltip } from 'app/client/ui/tooltips'; import {withInfoTooltip} from 'app/client/ui/tooltips';
import {textButton} from 'app/client/ui2018/buttons'; import {textButton} from 'app/client/ui2018/buttons';
import {ColorOption, colorSelect} from 'app/client/ui2018/ColorSelect'; import {ColorOption, colorSelect} from 'app/client/ui2018/ColorSelect';
import {theme, vars} from 'app/client/ui2018/cssVars'; import {theme, vars} from 'app/client/ui2018/cssVars';
@ -71,7 +71,7 @@ export class ConditionalStyle extends Disposable {
return [ return [
cssRow( cssRow(
{ style: 'margin-top: 16px' }, { style: 'margin-top: 16px' },
withQuestionMarkTooltip( withInfoTooltip(
textButton( textButton(
t('Add conditional style'), t('Add conditional style'),
testId('add-conditional-style'), testId('add-conditional-style'),