diff --git a/app/client/components/DetailView.css b/app/client/components/DetailView.css index 84e7e171..cf7d026e 100644 --- a/app/client/components/DetailView.css +++ b/app/client/components/DetailView.css @@ -37,13 +37,6 @@ font-weight: bold; } -.g_record_detail_description { - min-height: 1rem; - color: #666; - font-size: 0.9rem; - font-weight: normal; - margin-bottom: 2px; -} .g_record_detail_value { position: relative; min-height: 16px; @@ -237,7 +230,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; @@ -248,16 +241,11 @@ margin-right: -8px; } -.detail_theme_field_form>.g_record_detail_description { - font-size: var(--grist-small-font-size); - color: var(--grist-theme-card-form-label, var(--grist-color-slate)); - font-weight: normal; - min-height: 0px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - margin-right: -8px; +.detail_theme_field_form .info_toggle_icon { + width: 13px; + height: 13px; } + /* 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. diff --git a/app/client/components/DetailView.js b/app/client/components/DetailView.js index ba0eaa58..b4d4d753 100644 --- a/app/client/components/DetailView.js +++ b/app/client/components/DetailView.js @@ -15,6 +15,7 @@ const RecordLayout = require('./RecordLayout'); const commands = require('./commands'); const {RowContextMenu} = require('../ui/RowContextMenu'); const {parsePasteForView} = require("./BaseView2"); +const {withInfoTooltip} = require('../ui/tooltips'); /** * DetailView component implements a list of record layouts. @@ -227,8 +228,11 @@ 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_description', field.description), + 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) ); } @@ -258,8 +262,11 @@ 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_description', kd.text(field.description)), + 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', kd.toggleClass('scissors', isCopyActive), kd.toggleClass('record-add', row._isAddRow), diff --git a/app/client/components/DocumentUsage.ts b/app/client/components/DocumentUsage.ts index 243c6f64..4dc82616 100644 --- a/app/client/components/DocumentUsage.ts +++ b/app/client/components/DocumentUsage.ts @@ -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 {withInfoTooltip} from 'app/client/ui/tooltips'; +import { withQuestionMarkTooltip } 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 - ? withInfoTooltip( + ? withQuestionMarkTooltip( cssOverflowableText(name, testId('name')), tooltipContentFunc() ) diff --git a/app/client/ui/FieldConfig.ts b/app/client/ui/FieldConfig.ts index a35ee7cb..ac66ffd2 100644 --- a/app/client/ui/FieldConfig.ts +++ b/app/client/ui/FieldConfig.ts @@ -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 {withInfoTooltip} from 'app/client/ui/tooltips'; +import { withQuestionMarkTooltip } 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'; @@ -371,7 +371,7 @@ export function buildFormulaConfig( dom.prop("disabled", disableOtherActions), testId("field-set-formula") )), - cssRow(withInfoTooltip( + cssRow(withQuestionMarkTooltip( 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(withInfoTooltip( + cssRow(withQuestionMarkTooltip( textButton( t("Set trigger formula"), dom.on("click", convertDataColumnToTriggerColumn), diff --git a/app/client/ui/PageWidgetPicker.ts b/app/client/ui/PageWidgetPicker.ts index 8f4b278f..6d351fa6 100644 --- a/app/client/ui/PageWidgetPicker.ts +++ b/app/client/ui/PageWidgetPicker.ts @@ -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 { withInfoTooltip } from 'app/client/ui/tooltips'; +import { withQuestionMarkTooltip } 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, () => - withInfoTooltip( + withQuestionMarkTooltip( cssSelectBy( cssSmallLabel('SELECT BY'), dom.update(cssSelect(this._value.link, this._selectByOptions!), diff --git a/app/client/ui/ShareMenu.ts b/app/client/ui/ShareMenu.ts index 1c65bd9f..572e45e7 100644 --- a/app/client/ui/ShareMenu.ts +++ b/app/client/ui/ShareMenu.ts @@ -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, withInfoTooltip} from 'app/client/ui/tooltips'; +import { hoverTooltip, withQuestionMarkTooltip } 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( - withInfoTooltip( + withQuestionMarkTooltip( t("Edit without affecting the original"), GristTooltips.workOnACopy(), {tooltipMenuOptions: {attach: null}} diff --git a/app/client/ui/UserManager.ts b/app/client/ui/UserManager.ts index 82c75ca2..98c5d862 100644 --- a/app/client/ui/UserManager.ts +++ b/app/client/ui/UserManager.ts @@ -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, withInfoTooltip} from 'app/client/ui/tooltips'; +import { hoverTooltip, ITooltipControl, showTransientTooltip, withQuestionMarkTooltip } 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 - ? withInfoTooltip( + ? withQuestionMarkTooltip( cssLink({href: urlState().makeUrl({docPage: 'acl'})}, dom.text(use => use(model.isAnythingChanged) ? 'Save & ' : ''), 'Open Access Rules', diff --git a/app/client/ui/tooltips.ts b/app/client/ui/tooltips.ts index 68d26655..73866d42 100644 --- a/app/client/ui/tooltips.ts +++ b/app/client/ui/tooltips.ts @@ -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, styled} from 'grainjs'; +import { dom, DomContents, DomElementArg, DomElementMethod, IDomArgs, styled } from 'grainjs'; import Popper from 'popper.js'; import {cssMenu, defaultMenuOptions, IMenuOptions, setPopupToCreateDom} from 'popweasel'; @@ -240,43 +240,59 @@ export function tooltipCloseButton(ctl: ITooltipControl): HTMLElement { } /** - * Renders an info icon that shows a tooltip with the specified `content` on click. + * Renders an icon that shows a tooltip with the specified `content` on click. */ -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 iconTooltip( + cssStyledFunc: (...args: IDomArgs) => 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 interface WithInfoTooltipOptions { +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 { domArgs?: DomElementArg[]; tooltipButtonDomArgs?: DomElementArg[]; tooltipMenuOptions?: IMenuOptions; @@ -296,17 +312,30 @@ export interface WithInfoTooltipOptions { * * Usage: * - * withInfoTooltip( + * withQuestionMarkTooltip( * 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: WithInfoTooltipOptions = {}, + options: WithIconTooltipOptions = {}, ) { - const {domArgs, tooltipButtonDomArgs, tooltipMenuOptions} = options; + const { domArgs, tooltipButtonDomArgs, tooltipMenuOptions } = options; return cssDomWithTooltip( domContents, infoTooltip(tooltipContent, tooltipMenuOptions, tooltipButtonDomArgs), @@ -347,7 +376,7 @@ const cssTooltipCloseButton = styled('div', ` } `); -const cssInfoTooltipButton = styled('div', ` +const cssQuestionMark = styled('div', ` flex-shrink: 0; display: flex; align-items: center; @@ -366,6 +395,25 @@ const cssInfoTooltipButton = 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; diff --git a/app/client/ui2018/cssVars.ts b/app/client/ui2018/cssVars.ts index 6b84698c..e995f6c1 100644 --- a/app/client/ui2018/cssVars.ts +++ b/app/client/ui2018/cssVars.ts @@ -605,10 +605,10 @@ export const theme = { menuToggleBg: new CustomProp('theme-menu-toggle-bg', undefined, 'white'), menuToggleBorder: new CustomProp('theme-menu-toggle-border', undefined, colors.slate), - /* Info Toggles */ - infoToggleFg: new CustomProp('theme-info-toggle-fg', undefined, "#8F8F8F"), - infoToggleHoverFg: new CustomProp('theme-info-toggle-hover-fg', undefined, "#707070"), - infoToggleActiveFg: new CustomProp('theme-info-toggle-active-fg', undefined, "#5C5C5C"), + /* 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), diff --git a/app/client/widgets/ConditionalStyle.ts b/app/client/widgets/ConditionalStyle.ts index 7ae0f87c..945b07d2 100644 --- a/app/client/widgets/ConditionalStyle.ts +++ b/app/client/widgets/ConditionalStyle.ts @@ -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 {withInfoTooltip} from 'app/client/ui/tooltips'; +import { withQuestionMarkTooltip } 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' }, - withInfoTooltip( + withQuestionMarkTooltip( textButton( t('Add conditional style'), testId('add-conditional-style'),