gristlabs_grist-core/app/client/ui/HomeIntroCards.ts
George Gevoian da6c39aa50 (core) Add new home page cards
Summary:
New cards on the home page link to useful resources like the welcome
video, tutorial, webinars, and the Help Center. They are shown by
default to new and exisiting users, and may be hidden via a toggle.

Test Plan: Browser tests.

Reviewers: jarek

Reviewed By: jarek

Differential Revision: https://phab.getgrist.com/D4340
2024-09-17 17:32:10 -04:00

387 lines
9.7 KiB
TypeScript

import {makeT} from 'app/client/lib/localization';
import {urlState} from 'app/client/models/gristUrlState';
import {HomeModel} from 'app/client/models/HomeModel';
import {newDocMethods} from 'app/client/ui/NewDocMethods';
import {openVideoTour} from 'app/client/ui/OpenVideoTour';
import {basicButtonLink, bigPrimaryButton, primaryButtonLink} from 'app/client/ui2018/buttons';
import {colors, theme, vars} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons';
import {commonUrls, isFeatureEnabled} from 'app/common/gristUrls';
import {getGristConfig} from 'app/common/urlUtils';
import {Computed, dom, IDisposableOwner, makeTestId, styled, subscribeElem} from 'grainjs';
interface BuildHomeIntroCardsOptions {
homeModel: HomeModel;
}
const t = makeT('HomeIntroCards');
const testId = makeTestId('test-intro-');
export function buildHomeIntroCards(
owner: IDisposableOwner,
{homeModel}: BuildHomeIntroCardsOptions
) {
const {onboardingTutorialDocId, templateOrg} = getGristConfig();
const percentComplete = Computed.create(owner, (use) => {
if (!homeModel.app.currentValidUser) { return 0; }
const tutorial = use(homeModel.onboardingTutorial);
if (!tutorial) { return undefined; }
return tutorial.forks?.[0]?.options?.tutorial?.percentComplete ?? 0;
});
let videoPlayButtonElement: HTMLElement;
return dom.maybe(use => !use(homeModel.onlyShowDocuments), () => cssHomeIntroCards(
cssVideoTour(
cssVideoTourThumbnail(
cssVideoTourThumbnailSpacer(),
videoPlayButtonElement = cssVideoTourPlayButton(
cssVideoTourPlayIcon('VideoPlay2'),
),
cssVideoTourThumbnailText(t('3 minute video tour')),
),
dom.on('click', () => openVideoTour(videoPlayButtonElement)),
testId('video-tour'),
),
cssTutorial(
dom.hide(() => !isFeatureEnabled('tutorials') || !templateOrg || !onboardingTutorialDocId),
cssTutorialHeader(t('Finish our basics tutorial')),
cssTutorialBody(
cssTutorialProgress(
cssTutorialProgressText(
cssTutorialProgressPercentage(
dom.domComputed(percentComplete, (percent) => percent !== undefined ? `${percent}%` : null),
testId('tutorial-percent-complete'),
),
),
cssTutorialProgressBar(
(elem) => subscribeElem(elem, percentComplete, (val) => {
elem.style.setProperty('--percent-complete', String(val ?? 0));
})
),
),
dom('div',
primaryButtonLink(
t('Tutorial'),
urlState().setLinkUrl({org: templateOrg!, doc: onboardingTutorialDocId}),
),
)
),
testId('tutorial'),
),
cssNewDocument(
cssNewDocumentHeader(t('Start a new document')),
cssNewDocumentBody(
cssNewDocumentButton(
cssNewDocumentButtonIcon('Page'),
t('Blank document'),
dom.on('click', () => newDocMethods.createDocAndOpen(homeModel)),
dom.boolAttr('disabled', use => !use(homeModel.newDocWorkspace)),
testId('create-doc'),
),
cssNewDocumentButton(
cssNewDocumentButtonIcon('Import'),
t('Import file'),
dom.on('click', () => newDocMethods.importDocAndOpen(homeModel)),
dom.boolAttr('disabled', use => !use(homeModel.newDocWorkspace)),
testId('import-doc'),
),
cssNewDocumentButton(
dom.show(isFeatureEnabled("templates") && Boolean(templateOrg)),
cssNewDocumentButtonIcon('FieldTable'),
t('Templates'),
urlState().setLinkUrl({homePage: 'templates'}),
testId('templates'),
),
),
),
cssWebinars(
dom.show(isFeatureEnabled('helpCenter')),
cssWebinarsImage({src: 'img/webinars.svg'}),
t('Learn more {{webinarsLinks}}', {
webinarsLinks: cssWebinarsButton(
t('Webinars'),
{href: commonUrls.webinars, target: '_blank'},
testId('webinars'),
),
}),
),
cssHelpCenter(
dom.show(isFeatureEnabled('helpCenter')),
cssHelpCenterImage({src: 'img/help-center.svg'}),
t('Find solutions and explore more resources {{helpCenterLink}}', {
helpCenterLink: cssHelpCenterButton(
t('Help center'),
{href: commonUrls.help, target: '_blank'},
testId('help-center'),
),
}),
),
testId('cards'),
));
}
// Cards are hidden at specific breakpoints; we use non-standard ones
// here, as they work better than the ones defined in `cssVars.ts`.
const mediaXLarge = `(max-width: ${1440 - 0.02}px)`;
const mediaLarge = `(max-width: ${1280 - 0.02}px)`;
const mediaMedium = `(max-width: ${1048 - 0.02}px)`;
const mediaSmall = `(max-width: ${828 - 0.02}px)`;
const cssHomeIntroCards = styled('div', `
display: grid;
gap: 24px;
margin-bottom: 24px;
display: grid;
grid-template-columns: 239px minmax(0, 437px) minmax(196px, 1fr) minmax(196px, 1fr);
grid-template-rows: repeat(2, 1fr);
@media ${mediaLarge} {
& {
grid-template-columns: 239px minmax(0, 437px) minmax(196px, 1fr);
}
}
@media ${mediaMedium} {
& {
grid-template-columns: 239px minmax(0, 437px);
}
}
@media ${mediaSmall} {
& {
display: flex;
flex-direction: column;
}
}
`);
const cssVideoTour = styled('div', `
grid-area: 1 / 1 / 2 / 2;
flex-shrink: 0;
width: 239px;
overflow: hidden;
cursor: pointer;
border-radius: 4px;
@media ${mediaSmall} {
& {
width: unset;
}
}
`);
const cssVideoTourThumbnail = styled('div', `
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 36px 32px;
background-image: url("img/youtube-screenshot.png");
background-color: rgba(0, 0, 0, 0.4);
background-blend-mode: multiply;
background-size: cover;
transform: scale(1.2);
width: 100%;
height: 100%;
`);
const cssVideoTourThumbnailSpacer = styled('div', ``);
const cssVideoTourPlayButton = styled('div', `
display: flex;
justify-content: center;
align-items: center;
align-self: center;
width: 32px;
height: 32px;
background-color: ${theme.controlPrimaryBg};
border-radius: 50%;
.${cssVideoTourThumbnail.className}:hover & {
background-color: ${theme.controlPrimaryHoverBg};
}
`);
const cssVideoTourPlayIcon = styled(icon, `
--icon-color: ${theme.controlPrimaryFg};
width: 24px;
height: 24px;
`);
const cssVideoTourThumbnailText = styled('div', `
color: ${colors.light};
font-weight: 700;
text-align: center;
`);
const cssTutorial = styled('div', `
grid-area: 1 / 2 / 2 / 3;
position: relative;
border-radius: 4px;
color: ${theme.announcementPopupFg};
background-color: ${theme.announcementPopupBg};
padding: 16px;
`);
const cssTutorialHeader = styled('div', `
display: flex;
align-items: flex-start;
justify-content: space-between;
font-size: 16px;
font-style: normal;
font-weight: 500;
margin-bottom: 8px;
`);
const cssTutorialBody = styled('div', `
display: flex;
flex-direction: column;
gap: 16px;
`);
const cssTutorialProgress = styled('div', `
display
flex: auto;
min-width: 120px;
`);
const cssTutorialProgressText = styled('div', `
display: flex;
justify-content: space-between;
`);
const cssTutorialProgressPercentage = styled('div', `
font-size: 18px;
font-style: normal;
font-weight: 700;
min-height: 21.5px;
`);
const cssTutorialProgressBar = styled('div', `
margin-top: 4px;
height: 10px;
border-radius: 8px;
background: ${theme.mainPanelBg};
--percent-complete: 0;
&::after {
content: '';
border-radius: 8px;
background: ${theme.progressBarFg};
display: block;
height: 100%;
width: calc((var(--percent-complete) / 100) * 100%);
}
`);
const cssNewDocument = styled('div', `
grid-area: 2 / 1 / 3 / 3;
grid-column: span 2;
display: flex;
flex-direction: column;
justify-content: space-between;
position: relative;
border-radius: 4px;
color: ${theme.announcementPopupFg};
background-color: ${theme.announcementPopupBg};
padding: 24px;
`);
const cssNewDocumentHeader = styled('div', `
font-weight: 500;
font-size: ${vars.xxlargeFontSize};
`);
const cssNewDocumentBody = styled('div', `
display: flex;
gap: 16px;
margin-top: 16px;
@media ${mediaSmall} {
& {
flex-direction: column;
}
}
`);
const cssNewDocumentButton = styled(bigPrimaryButton, `
display: flex;
align-items: center;
justify-content: center;
flex-grow: 1;
padding: 6px;
`);
const cssNewDocumentButtonIcon = styled(icon, `
flex-shrink: 0;
margin-right: 8px;
@media ${mediaXLarge} {
& {
display: none;
}
}
`);
const cssSecondaryCard = styled('div', `
font-weight: 500;
font-size: 14px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
justify-content: center;
min-width: 196px;
color: ${theme.text};
background-color: ${theme.popupSecondaryBg};
position: relative;
border-radius: 4px;
padding: 16px;
`);
const cssSecondaryCardImage = styled('img', `
display: block;
height: auto;
`);
const cssSecondaryCardButton = styled(basicButtonLink, `
font-weight: 400;
font-size: ${vars.mediumFontSize};
margin-top: 8px;
`);
const cssWebinars = styled(cssSecondaryCard, `
grid-area: 2 / 3 / 3 / 4;
@media ${mediaMedium} {
& {
display: none;
}
}
`);
const cssWebinarsImage = styled(cssSecondaryCardImage, `
width: 105.78px;
margin-bottom: 8px;
`);
const cssWebinarsButton = cssSecondaryCardButton;
const cssHelpCenter = styled(cssSecondaryCard, `
grid-area: 2 / 4 / 3 / 5;
@media ${mediaLarge} {
& {
display: none;
}
}
`);
const cssHelpCenterImage = styled(cssSecondaryCardImage, `
width: 67.77px;
`);
const cssHelpCenterButton = cssSecondaryCardButton;