gristlabs_grist-core/app/client/ui/OnboardingCards.ts

233 lines
6.0 KiB
TypeScript
Raw Normal View History

import {makeT} from 'app/client/lib/localization';
import {urlState} from 'app/client/models/gristUrlState';
import {HomeModel} from 'app/client/models/HomeModel';
import {openVideoTour} from 'app/client/ui/OpenVideoTour';
import {bigPrimaryButtonLink} from 'app/client/ui2018/buttons';
import {colors, theme} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons';
import {isFeatureEnabled} from 'app/common/gristUrls';
import {getGristConfig} from 'app/common/urlUtils';
import {Computed, dom, IDisposableOwner, makeTestId, styled, subscribeElem} from 'grainjs';
interface BuildOnboardingCardsOptions {
homeModel: HomeModel;
}
const t = makeT('OnboardingCards');
const testId = makeTestId('test-onboarding-');
export function buildOnboardingCards(
owner: IDisposableOwner,
{homeModel}: BuildOnboardingCardsOptions
) {
const {templateOrg, onboardingTutorialDocId} = getGristConfig();
if (!isFeatureEnabled('tutorials') || !templateOrg || !onboardingTutorialDocId) { return null; }
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;
});
const shouldShowCards = Computed.create(owner, (use) =>
!use(homeModel.app.dismissedPopups).includes('onboardingCards'));
let videoPlayButtonElement: HTMLElement;
return dom.maybe(shouldShowCards, () =>
cssOnboardingCards(
cssTutorialCard(
cssDismissCardsButton(
icon('CrossBig'),
dom.on('click', () => homeModel.app.dismissPopup('onboardingCards', true)),
testId('dismiss-cards'),
),
cssTutorialCardHeader(
t('Complete our basics tutorial'),
),
cssTutorialCardSubHeader(
t('Learn the basic of reference columns, linked widgets, column types, & cards.')
),
cssTutorialCardBody(
cssTutorialProgress(
cssTutorialProgressText(
cssProgressPercentage(
dom.domComputed(percentComplete, (percent) => percent !== undefined ? `${percent}%` : null),
testId('tutorial-percent-complete'),
),
cssStarIcon('Star'),
),
cssTutorialProgressBar(
(elem) => subscribeElem(elem, percentComplete, (val) => {
elem.style.setProperty('--percent-complete', String(val ?? 0));
})
),
),
bigPrimaryButtonLink(
t('Complete the tutorial'),
urlState().setLinkUrl({org: templateOrg, doc: onboardingTutorialDocId}),
),
),
testId('tutorial-card'),
),
cssVideoCard(
cssVideoThumbnail(
cssVideoThumbnailSpacer(),
videoPlayButtonElement = cssVideoPlayButton(
cssPlayIcon('VideoPlay2'),
),
cssVideoThumbnailText(t('3 minute video tour')),
),
dom.on('click', () => openVideoTour(videoPlayButtonElement)),
),
)
);
}
const cssOnboardingCards = styled('div', `
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, max-content));
gap: 24px;
margin: 24px 0;
`);
const cssTutorialCard = styled('div', `
position: relative;
border-radius: 4px;
color: ${theme.announcementPopupFg};
background-color: ${theme.announcementPopupBg};
padding: 16px 24px;
`);
const cssTutorialCardHeader = styled('div', `
display: flex;
align-items: flex-start;
justify-content: space-between;
font-size: 18px;
font-style: normal;
font-weight: 700;
`);
const cssDismissCardsButton = styled('div', `
position: absolute;
top: 8px;
right: 8px;
padding: 4px;
border-radius: 4px;
cursor: pointer;
--icon-color: ${theme.popupCloseButtonFg};
&:hover {
background-color: ${theme.hover};
}
`);
const cssTutorialCardSubHeader = styled('div', `
font-size: 14px;
font-style: normal;
font-weight: 500;
margin: 8px 0;
`);
const cssTutorialCardBody = styled('div', `
display: flex;
flex-wrap: wrap;
gap: 24px;
margin: 16px 0;
align-items: end;
`);
const cssTutorialProgress = styled('div', `
flex: auto;
min-width: 120px;
`);
const cssTutorialProgressText = styled('div', `
display: flex;
justify-content: space-between;
`);
const cssProgressPercentage = styled('div', `
font-size: 20px;
font-style: normal;
font-weight: 700;
`);
const cssStarIcon = styled(icon, `
--icon-color: ${theme.accentIcon};
width: 24px;
height: 24px;
`);
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 cssVideoCard = styled('div', `
width: 220px;
height: 158px;
overflow: hidden;
cursor: pointer;
border-radius: 4px;
`);
const cssVideoThumbnail = 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 cssVideoThumbnailSpacer = styled('div', ``);
const cssVideoPlayButton = styled('div', `
display: flex;
justify-content: center;
align-items: center;
align-self: center;
width: 32px;
height: 32px;
background-color: ${theme.controlPrimaryBg};
border-radius: 50%;
.${cssVideoThumbnail.className}:hover & {
background-color: ${theme.controlPrimaryHoverBg};
}
`);
const cssPlayIcon = styled(icon, `
--icon-color: ${theme.controlPrimaryFg};
width: 24px;
height: 24px;
`);
const cssVideoThumbnailText = styled('div', `
color: ${colors.light};
font-weight: 700;
text-align: center;
`);