(core) Add Markdown cell format

Summary:
Text columns can now display their values as Markdown-formatted text
by changing their cell format to "Markdown". A minimal subset of the
Markdown specification is currently supported.

Test Plan: Browser tests.

Reviewers: Spoffy, dsagal

Reviewed By: Spoffy, dsagal

Subscribers: dsagal, Spoffy

Differential Revision: https://phab.getgrist.com/D4326
This commit is contained in:
George Gevoian
2024-08-22 23:51:09 -04:00
parent 5c486e686e
commit 292c894b93
26 changed files with 353 additions and 84 deletions

View File

@@ -23,6 +23,7 @@ export type IconName = "ChartArea" |
"FieldFunctionEqual" |
"FieldInteger" |
"FieldLink" |
"FieldMarkdown" |
"FieldNumeric" |
"FieldReference" |
"FieldSpinner" |
@@ -185,6 +186,7 @@ export const IconList: IconName[] = ["ChartArea",
"FieldFunctionEqual",
"FieldInteger",
"FieldLink",
"FieldMarkdown",
"FieldNumeric",
"FieldReference",
"FieldSpinner",

View File

@@ -895,6 +895,13 @@ export const theme = {
undefined, colors.slate),
widgetGallerySecondaryHeaderBgHover: new CustomProp(
'theme-widget-gallery-secondary-header-bg-hover', undefined, '#7E7E85'),
/* Markdown Cell */
markdownCellLightBg: new CustomProp('theme-markdown-cell-light-bg', undefined, colors.lightGrey),
markdownCellLightBorder: new CustomProp('theme-markdown-cell-light-border', undefined,
colors.mediumGreyOpaque),
markdownCellMediumBorder: new CustomProp('theme-markdown-cell-medium-border', undefined,
colors.darkGrey),
};
const cssColors = values(colors).map(v => v.decl()).join('\n');

View File

@@ -56,7 +56,7 @@ import { IconName } from './IconList';
/**
* Defaults for all icons.
*/
const iconDiv = styled('div', `
const iconStyles = `
position: relative;
display: inline-block;
vertical-align: middle;
@@ -66,24 +66,35 @@ const iconDiv = styled('div', `
width: 16px;
height: 16px;
background-color: var(--icon-color, var(--grist-theme-text, black));
`);
`;
export const cssIconBackground = styled(iconDiv, `
background-color: var(--icon-background, inherit);
-webkit-mask: none;
& .${iconDiv.className} {
transition: inherit;
display: block;
}
`);
const cssIconDiv = styled('div', iconStyles);
const cssIconSpan = styled('span', iconStyles);
export function icon(name: IconName, ...domArgs: DomElementArg[]): HTMLElement {
return iconDiv(
return cssIconDiv(
dom.style('-webkit-mask-image', `var(--icon-${name})`),
...domArgs
);
}
export function iconSpan(name: IconName, ...domArgs: DomElementArg[]): HTMLElement {
return cssIconSpan(
dom.style('-webkit-mask-image', `var(--icon-${name})`),
...domArgs
);
}
export const cssIconSpanBackground = styled(cssIconSpan, `
background-color: var(--icon-background, inherit);
-webkit-mask: none;
& .${cssIconSpan.className} {
transition: inherit;
display: block;
}
`);
/**
* Container box for an icon to serve as a button..
*/

View File

@@ -1,9 +1,9 @@
import {findLinks} from 'app/client/lib/textUtils';
import { sameDocumentUrlState, urlState } from 'app/client/models/gristUrlState';
import { hideInPrintView, testId, theme } from 'app/client/ui2018/cssVars';
import {cssIconBackground, icon} from 'app/client/ui2018/icons';
import { CellValue } from 'app/plugin/GristData';
import { dom, DomArg, IDomArgs, Observable, styled } from 'grainjs';
import {sameDocumentUrlState, urlState} from 'app/client/models/gristUrlState';
import {hideInPrintView, testId, theme} from 'app/client/ui2018/cssVars';
import {cssIconSpanBackground, iconSpan} from 'app/client/ui2018/icons';
import {CellValue} from 'app/plugin/GristData';
import {dom, DomArg, IDomArgs, Observable, styled} from 'grainjs';
/**
* Styling for a simple <A HREF> link.
@@ -37,6 +37,19 @@ export function gristLink(href: string|Observable<string>, ...args: IDomArgs<HTM
);
}
export function gristIconLink(href: string, label = href) {
return cssMaybeWrap(
gristLink(href,
cssIconSpanBackground(
iconSpan("FieldLink", testId('tb-link-icon')),
dom.cls(cssHoverInText.className),
),
),
linkColor(label),
testId("text-link"),
);
}
/**
* If possible (i.e. if `url` points to somewhere in the current document)
* use pushUrl to navigate without reloading or opening a new tab
@@ -60,17 +73,7 @@ export function makeLinks(text: string) {
const domElements: DomArg[] = [];
for (const {value, isLink} of findLinks(text)) {
if (isLink) {
// Wrap link with a span to provide hover on and to override wrapping.
domElements.push(cssMaybeWrap(
gristLink(value,
cssIconBackground(
icon("FieldLink", testId('tb-link-icon')),
dom.cls(cssHoverInText.className),
),
),
linkColor(value),
testId("text-link")
));
domElements.push(gristIconLink(value));
} else {
domElements.push(value);
}