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;