gristlabs_grist-core/app/client/widgets/HyperLinkTextBox.ts
Alex Hall 15f1ef96fa (core) Allow creating custom document tours with a special table
Summary:
Like the welcome tour, a special URL hash triggers startDocTour which uses data from a table GristDocTour to construct the appropriate popups.

This is the basic version described in https://grist.quip.com/sN2RAHI2dchm/Document-tours

Test Plan:
Added a new nbrowser test which tests the data produced by makeDocTour. The general behaviour of the UI and popups has hardly changed so existing tests cover that well enough.

The new test uses a new fixture document which you can open to easily experience the tour.

Error cases where there's no valid document tour are not tested because that behaviour is likely to change significantly and this feature is still quite 'private'.

Reviewers: georgegevoian

Reviewed By: georgegevoian

Subscribers: jarek, dsagal

Differential Revision: https://phab.getgrist.com/D2938
2021-07-23 20:23:30 +02:00

76 lines
2.7 KiB
TypeScript

import {CellValue} from 'app/common/DocActions';
import {DataRowModel} from 'app/client/models/DataRowModel';
import {ViewFieldRec} from 'app/client/models/entities/ViewFieldRec';
import {colors, testId} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons';
import {NTextBox} from 'app/client/widgets/NTextBox';
import {dom, styled} from 'grainjs';
import {constructUrl, sameDocumentUrlState, urlState} from "app/client/models/gristUrlState";
/**
* Creates a widget for displaying links. Links can entered directly or following a title.
* The last entry following a space is used as the url.
* ie 'google https://www.google.com' would apears as 'google' to the user but link to the url.
*/
export class HyperLinkTextBox extends NTextBox {
constructor(field: ViewFieldRec) {
super(field, {defaultTextColor: colors.lightGreen.value});
}
public buildDom(row: DataRowModel) {
const value = row.cells[this.field.colId()];
return cssFieldClip(
dom.style('text-align', this.alignment),
dom.cls('text_wrapping', this.wrapping),
dom.maybe((use) => Boolean(use(value)), () =>
dom('a',
dom.attr('href', (use) => constructUrl(use(value))),
dom.attr('target', '_blank'),
dom.on('click', (ev) =>
_onClickHyperlink(ev, value.peek())),
// As per Google and Mozilla recommendations to prevent opened links
// from running on the same process as Grist:
// https://developers.google.com/web/tools/lighthouse/audits/noopener
dom.attr('rel', 'noopener noreferrer'),
cssLinkIcon('FieldLink', testId('tb-link-icon')),
testId('tb-link')
)
),
dom.text((use) => _formatValue(use(value))),
);
}
}
/**
* Formats value like `foo bar baz` by discarding `baz` and returning `foo bar`.
*/
function _formatValue(value: CellValue): string {
if (typeof value !== 'string') { return ''; }
const index = value.lastIndexOf(' ');
return index >= 0 ? value.slice(0, index) : value;
}
/**
* If possible (i.e. if `url` points to somewhere in the current document)
* use pushUrl to navigate without reloading or opening a new tab
*/
async function _onClickHyperlink(ev: MouseEvent, url: CellValue) {
// Only override plain-vanilla clicks.
if (ev.shiftKey || ev.metaKey || ev.ctrlKey || ev.altKey) { return; }
const newUrlState = sameDocumentUrlState(url);
if (!newUrlState) { return; }
ev.preventDefault();
await urlState().pushUrl(newUrlState);
}
const cssFieldClip = styled('div.field_clip', `
color: var(--grist-actual-cell-color, ${colors.lightGreen});
`);
const cssLinkIcon = styled(icon, `
background-color: var(--grist-actual-cell-color, ${colors.lightGreen});
margin: -1px 2px 2px 0;
`);