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 {
width: 13px;
height: 13px;
margin-bottom: 3px;
}
/* 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 {RowContextMenu} = require('../ui/RowContextMenu');
const {parsePasteForView} = require("./BaseView2");
const {withInfoTooltip} = require('../ui/tooltips');
const {columnInfoTooltip} = require("../ui/tooltips");
/**
* DetailView component implements a list of record layouts.
@ -228,12 +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(); }),
field.description.peek() ?
withInfoTooltip(
dom('div.g_record_detail_label', kd.text(field.displayLabel)),
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)
dom('div.g_record_detail_label',
kd.text(field.displayLabel),
kd.scope(field.description, desc => desc ? columnInfoTooltip(kd.text(field.description)) : null)
),
);
}
@ -262,11 +260,10 @@ DetailView.prototype.buildFieldDom = function(field, row) {
dom.autoDispose(isCellSelected),
dom.autoDispose(isCellActive),
kd.cssClass(function() { return 'detail_theme_field_' + self.viewSection.themeDef(); }),
field.description.peek() ?
withInfoTooltip(
dom('div.g_record_detail_label', kd.text(field.displayLabel)),
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_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

@ -2,7 +2,7 @@ import {DocPageModel} from 'app/client/models/DocPageModel';
import {urlState} from 'app/client/models/gristUrlState';
import {docListHeader} from 'app/client/ui/DocMenuCss';
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 {icon} from 'app/client/ui2018/icons';
import {loadingDots, loadingSpinner} from 'app/client/ui2018/loaders';
@ -284,7 +284,7 @@ function buildUsageMetric(options: MetricOptions, ...domArgs: DomElementArg[]) {
return cssUsageMetric(
cssMetricName(
tooltipContentFunc
? withQuestionMarkTooltip(
? withInfoTooltip(
cssOverflowableText(name, testId('name')),
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 {GristTooltips} from 'app/client/ui/GristTooltips';
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 {textButton} from 'app/client/ui2018/buttons';
import { testId, theme, vars } from 'app/client/ui2018/cssVars';
@ -371,7 +371,7 @@ export function buildFormulaConfig(
dom.prop("disabled", disableOtherActions),
testId("field-set-formula")
)),
cssRow(withQuestionMarkTooltip(
cssRow(withInfoTooltip(
textButton(
t("Set trigger formula"),
dom.on("click", setTrigger),
@ -424,7 +424,7 @@ export function buildFormulaConfig(
// Else offer a way to convert to trigger formula.
dom.maybe((use) => !(use(maybeTrigger) || use(origColumn.hasTriggerFormula)), () => [
cssEmptySeparator(),
cssRow(withQuestionMarkTooltip(
cssRow(withInfoTooltip(
textButton(
t("Set trigger formula"),
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 { GristTooltips } from 'app/client/ui/GristTooltips';
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 { bigPrimaryButton } from "app/client/ui2018/buttons";
import { theme, vars } from "app/client/ui2018/cssVars";
@ -358,7 +358,7 @@ export class PageWidgetSelect extends Disposable {
cssFooterContent(
// If _selectByOptions exists and has more than then "NoLinkOption", show the selector.
dom.maybe((use) => this._selectByOptions && use(this._selectByOptions).length > 1, () =>
withQuestionMarkTooltip(
withInfoTooltip(
cssSelectBy(
cssSmallLabel('SELECT BY'),
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 {makeCopy, replaceTrunkWithFork} from 'app/client/ui/MakeCopyMenu';
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 {primaryButton} from 'app/client/ui2018/buttons';
import {mediaXSmall, testId, theme} from 'app/client/ui2018/cssVars';
@ -207,7 +207,7 @@ function menuWorkOnCopy(pageModel: DocPageModel) {
return [
menuItem(makeUnsavedCopy, t("Work on a Copy"), testId('work-on-copy')),
menuText(
withQuestionMarkTooltip(
withInfoTooltip(
t("Edit without affecting the original"),
GristTooltips.workOnACopy(),
{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 {GristTooltips} from 'app/client/ui/GristTooltips';
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 {cssMemberBtn, cssMemberImage, cssMemberListItem,
cssMemberPrimary, cssMemberSecondary, cssMemberText, cssMemberType, cssMemberTypeProblem,
@ -169,7 +169,7 @@ function buildUserManagerModal(
testId('um-cancel')
),
(model.resourceType === 'document' && model.gristDoc && !model.isPersonal
? withQuestionMarkTooltip(
? withInfoTooltip(
cssLink({href: urlState().makeUrl({docPage: 'acl'})},
dom.text(use => use(model.isAnythingChanged) ? 'Save & ' : ''),
'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 {icon} from 'app/client/ui2018/icons';
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 {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(
cssStyledFunc: (...args: IDomArgs<HTMLElement>) => HTMLElement,
tooltipButtonContent: HTMLElement,
content: DomContents,
menuOptions?: IMenuOptions,
...domArgs: DomElementArg[]
) {
return cssStyledFunc(tooltipButtonContent, (elem) => {
setPopupToCreateDom(
elem,
(ctl) => {
return cssInfoTooltipPopup(
cssInfoTooltipPopupCloseButton(
icon("CrossSmall"),
dom.on("click", () => ctl.close()),
testId("info-tooltip-close")
),
cssInfoTooltipPopupBody(content, testId("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("info-tooltip-popup")
);
},
{ ...defaultMenuOptions, ...{ placement: "bottom-end" }, ...menuOptions }
);
});
export function infoTooltip(content: DomContents, menuOptions?: IMenuOptions, ...domArgs: DomElementArg[]) {
return cssInfoTooltipButton('?',
(elem) => {
setPopupToCreateDom(
elem,
(ctl) => {
return cssInfoTooltipPopup(
cssInfoTooltipPopupCloseButton(
icon('CrossSmall'),
dom.on('click', () => ctl.close()),
testId('info-tooltip-close'),
),
cssInfoTooltipPopupBody(
content,
testId('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('info-tooltip-popup'),
);
},
{...defaultMenuOptions, ...{placement: 'bottom-end'}, ...menuOptions},
);
},
testId('info-tooltip'),
...domArgs,
);
}
function questionMarkTooltip(content: DomContents, menuOptions?: IMenuOptions, ...domArgs: DomElementArg[]) {
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 {
export interface WithInfoTooltipOptions {
domArgs?: DomElementArg[];
tooltipButtonDomArgs?: DomElementArg[];
tooltipMenuOptions?: IMenuOptions;
@ -312,30 +296,17 @@ export interface WithIconTooltipOptions {
*
* Usage:
*
* withQuestionMarkTooltip(
* withInfoTooltip(
* dom('div', 'Hello World!'),
* 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(
domContents: DomContents,
tooltipContent: DomContents,
options: WithIconTooltipOptions = {},
options: WithInfoTooltipOptions = {},
) {
const { domArgs, tooltipButtonDomArgs, tooltipMenuOptions } = options;
const {domArgs, tooltipButtonDomArgs, tooltipMenuOptions} = options;
return cssDomWithTooltip(
domContents,
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', `
position: absolute;
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;
display: flex;
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', `
display: flex;
flex-direction: column;

View File

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