| 1 |
import React, { useEffect, useRef } from 'react' |
| 2 |
import { __ } from '@wordpress/i18n' |
| 3 |
import { WithRestAPIContext } from '../../../hooks/useRestAPI' |
| 4 |
import { ScreenMetaSlot } from '../../common/ScreenMetaSlot' |
| 5 |
import { DemoPageHeader } from '../../common/demo/DemoPageHeader' |
| 6 |
import { DemoSpotlight } from '../../common/demo/DemoSpotlight' |
| 7 |
import { DemoCallout } from '../../common/demo/DemoCallout' |
| 8 |
import { DemoUpsell } from '../../common/demo/DemoUpsell' |
| 9 |
import { useMarkDemoSeen } from '../../common/demo/useDemoSeen' |
| 10 |
import { BlueprintFormPanel } from './BlueprintFormPanel' |
| 11 |
import { BlueprintSidebar } from './BlueprintSidebar' |
| 12 |
import { GeneratedNotice } from './GeneratedNotice' |
| 13 |
import { getCallout } from './callouts' |
| 14 |
import { BLUEPRINT_DESCRIPTION, BLUEPRINT_TITLE, getSection } from './demoBlueprint' |
| 15 |
import { useBlueprintsDemo } from './useBlueprintsDemo' |
| 16 |
import { hasReached } from './types' |
| 17 |
import type { DemoStage } from './types' |
| 18 |
|
| 19 |
/** |
| 20 |
* Only the steps that point at one particular control are spotlit. Filling in |
| 21 |
* the three sections needs no dimming: the active tab and the panel crossfade |
| 22 |
* already say where to look. |
| 23 |
*/ |
| 24 |
const STAGE_SPOTLIGHTS: Partial<Record<DemoStage, { target: string, padding?: number }>> = { |
| 25 |
generating: { target: '.blueprint-form-sidebar__generate', padding: 8 }, |
| 26 |
generated: { target: '.blueprints-demo-generated', padding: 6 } |
| 27 |
} |
| 28 |
|
| 29 |
const STAGE_ANNOUNCEMENTS: Partial<Record<DemoStage, string>> = { |
| 30 |
general: __('Filling in the general settings.', 'code-snippets'), |
| 31 |
attributes: __('Adding the shortcode attributes.', 'code-snippets'), |
| 32 |
output: __('Setting the shortcode output.', 'code-snippets'), |
| 33 |
generating: __('Generating the code.', 'code-snippets'), |
| 34 |
generated: __('The snippet has been generated.', 'code-snippets'), |
| 35 |
finished: __('Demo complete.', 'code-snippets') |
| 36 |
} |
| 37 |
|
| 38 |
const BlueprintHeader: React.FC = () => |
| 39 |
<header className="blueprint-detail__header"> |
| 40 |
<div className="blueprint-detail__header-content"> |
| 41 |
<div> |
| 42 |
<h2>{BLUEPRINT_TITLE}</h2> |
| 43 |
<p>{BLUEPRINT_DESCRIPTION}</p> |
| 44 |
</div> |
| 45 |
</div> |
| 46 |
</header> |
| 47 |
|
| 48 |
// eslint-disable-next-line max-lines-per-function -- the page reads as one sequence. |
| 49 |
const BlueprintsDemoPage: React.FC = () => { |
| 50 |
const { |
| 51 |
stage, |
| 52 |
activeSection, |
| 53 |
isFading, |
| 54 |
sectionsBrowsable, |
| 55 |
hasStarted, |
| 56 |
isFinished, |
| 57 |
reducedMotion, |
| 58 |
selectSection, |
| 59 |
play, |
| 60 |
skip, |
| 61 |
replay |
| 62 |
} = useBlueprintsDemo() |
| 63 |
|
| 64 |
const generatedRef = useRef<HTMLDivElement>(null) |
| 65 |
const showGenerated = hasReached(stage, 'generated') |
| 66 |
const markSeen = useMarkDemoSeen('blueprints') |
| 67 |
|
| 68 |
useEffect(() => { |
| 69 |
if (isFinished) { |
| 70 |
markSeen() |
| 71 |
} |
| 72 |
}, [isFinished, markSeen]) |
| 73 |
|
| 74 |
useEffect(() => { |
| 75 |
const behavior = reducedMotion ? 'auto' : 'smooth' |
| 76 |
|
| 77 |
if ('generated' === stage) { |
| 78 |
generatedRef.current?.scrollIntoView({ behavior, block: 'center' }) |
| 79 |
} |
| 80 |
|
| 81 |
// Once the closing panel is in place, settle on the foot of the page so |
| 82 |
// both it and the generated notice are in view together. |
| 83 |
if ('finished' === stage) { |
| 84 |
window.scrollTo({ top: document.body.scrollHeight, behavior }) |
| 85 |
} |
| 86 |
}, [reducedMotion, stage]) |
| 87 |
|
| 88 |
return ( |
| 89 |
<> |
| 90 |
<ScreenMetaSlot /> |
| 91 |
|
| 92 |
<DemoPageHeader |
| 93 |
title={__('Blueprints', 'code-snippets')} |
| 94 |
description={__('A guided walkthrough of Pro Blueprints. Press play and watch a shortcode blueprint fill itself in and generate a snippet.', 'code-snippets')} |
| 95 |
hasStarted={hasStarted} |
| 96 |
isFinished={isFinished} |
| 97 |
onPlay={play} |
| 98 |
onSkip={skip} |
| 99 |
onReplay={replay} |
| 100 |
/> |
| 101 |
|
| 102 |
<div className="blueprints-demo"> |
| 103 |
<div className="screen-reader-text" aria-live="polite">{STAGE_ANNOUNCEMENTS[stage]}</div> |
| 104 |
|
| 105 |
<DemoCallout callout={getCallout(stage)} /> |
| 106 |
|
| 107 |
<DemoSpotlight {...STAGE_SPOTLIGHTS[stage]} /> |
| 108 |
|
| 109 |
<div className="blueprint-detail"> |
| 110 |
<BlueprintHeader /> |
| 111 |
|
| 112 |
<div className="blueprint-form-layout"> |
| 113 |
<BlueprintSidebar |
| 114 |
activeSection={activeSection} |
| 115 |
browsable={sectionsBrowsable} |
| 116 |
generating={'generating' === stage} |
| 117 |
onSelect={selectSection} |
| 118 |
/> |
| 119 |
|
| 120 |
<BlueprintFormPanel section={getSection(activeSection)} isFading={isFading} /> |
| 121 |
</div> |
| 122 |
</div> |
| 123 |
|
| 124 |
{showGenerated && <GeneratedNotice ref={generatedRef} />} |
| 125 |
|
| 126 |
{isFinished && <DemoUpsell |
| 127 |
title={__('That was a demo — Blueprints build the code for you', 'code-snippets')} |
| 128 |
onReplay={replay} |
| 129 |
> |
| 130 |
<p>{__('The whole walkthrough was scripted and ran inside this plugin. No code was generated and nothing was saved.', 'code-snippets')}</p> |
| 131 |
<p>{__('Code Snippets Pro ships blueprints for shortcodes, post types, taxonomies, settings pages, and more — fill in the form and it writes the snippet for you.', 'code-snippets')}</p> |
| 132 |
</DemoUpsell>} |
| 133 |
</div> |
| 134 |
</> |
| 135 |
) |
| 136 |
} |
| 137 |
|
| 138 |
export const BlueprintsDemo: React.FC = () => |
| 139 |
<WithRestAPIContext> |
| 140 |
<BlueprintsDemoPage /> |
| 141 |
</WithRestAPIContext> |
| 142 |
|