import { Head, Notes } from 'mdx-deck'; import { Appear, Image } from '@mdx-deck/components'; import { Split } from '@mdx-deck/layouts'; import { CodeSurferLayout } from 'code-surfer';
import Iframe from './components/Iframe'; import Hero from './components/Hero'; import TeaserText from './components/TeaserText'; import TeaserImage from './components/TeaserImage';
import Switcher from './components/Switcher'; import SwitchSteps from './components/SwitchSteps'; import { SwitchRow, SettingsList } from './components/SwitchHelpers';
import Phone from './components/Phone'; import PhoneWithContent from './components/PhoneWithContent'; import TabletWithContent from './components/TabletWithContent'; import LaptopWithContent from './components/LaptopWithContent';
import IconGrid from './components/IconGrid'; import DecoupleLine from './components/DecoupleLine'; import Database from './components/Database'; import BusinessLogic from './components/BusinessLogic'; import API from './components/API'; import ABTest from './components/ABTest'; import Render from './components/Render'; import Components from './components/Components'; import DataFetching from './components/DataFetching'; import FeatureFlag from './components/FeatureFlag';
import Center from './components/layout/Center'; import Jumbo from './components/layout/Jumbo'; import SideBySide from './components/layout/SideBySide'; import TopCenter from './components/TopCenter';
import { SlideIn, BlurIn, ZoomSteps } from './components/lib'; import config from './config';
export { themes } from './theme';
import './global.css';
<title>Components as Data, by Luke Herrington</title><img style={{ borderRadius: '50%', height: '50%' }} src="https://lukeherrington.com/static/cdf656371789fe7d89f21fc88d7dffb2/fdbb0/luke-square-sm.png" />
<TabletWithContent size={0} style={{ position: 'absolute', top: '60%', left: '35%', transform: 'translate(-35%, -60%)' }} /> <LaptopWithContent size={0} style={{ position: 'absolute', top: '60%', left: '80%', transform: 'translate(-80%, -60%)' }} /> <PhoneWithContent size={0} style={{ position: 'absolute', top: '70%', left: '50%', transform: 'translate(-50%, -70%)' }} />
component = render(props + state);<Phone screenTitle="Settings" style={{ position: 'absolute', top: '-20%', left: '50%', transform: 'translate(-50%, 20%)', }} size="2"
<SwitchRow style={{ gridColumn: '1/4', gridRow: '1/2', width: '100%' }}> <SwitchRow style={{ gridColumn: '1/4', gridRow: '2/3', width: '100%' }}> <SwitchRow style={{ gridColumn: '1/4', gridRow: '3/4', width: '100%' }}> <SwitchRow style={{ gridColumn: '1/4', gridRow: '4/5', width: '100%' }}> <SwitchRow style={{ gridColumn: '1/4', gridRow: '5/6', width: '100%' }}>
<DecoupleLine steps={['MID_LEFT', 'LEFT', 'RIGHT', 'MID_RIGHT']} />
{
"links": {
"self": "https://api.test.com/list?page[number]=1&page[size]=100",
"next": "https://api.test.com/list?page[number]=2&page[size]=100",
"prev": "https://api.test.com/list?page[number]=0&page[size]=100",
"last": "https://api.test.com/list?page[number]=33&page[size]=100",
"first": "https://api.test.com/list?page[number]=0&page[size]=100"
}
}/get-off-soapbox
<API style={{ height: '40%'}} />
{
"component": "Hero",
"data": {
"title": "Welcome!",
"backgroundImage": "https://via.placeholder.com/700x400",
"subtitle": "Make yourself at home"
}
}{
"component": "Hero",
"data": {
"title": "Welcome!",
"backgroundImage": "https://via.placeholder.com/700x400",
"subtitle": "Make yourself at home"
}
}{
"component": "Hero",
"data": {
"title": "Welcome!",
"backgroundImage": "https://via.placeholder.com/700x400",
"subtitle": "Make yourself at home"
}
}<Iframe fallback={ } src="https://nbc.com" title="NBC.com" ></Iframe>
interface Component {
component: String!
treatment: String
}
type VideoTile implements Component {
component: String!
treatment: String
data: VideoData
}
type VideoData {
image: String!
title: String!
secondaryTitle: String!
percentViewed: Float
}
type SeriesTile implements Component {
component: String!
treatment: String
data: SeriesData
}
type SeriesData {
image: String!
title: String!
secondaryTitle: String!
favoriteId: String
}
union Tile = VideoTile | SeriesTile
type ShelfData {
title: String
items: [Tile]
}
interface Section {
component: String!
treatment: String
}
type Shelf implements Section & Component {
component: String!
treatment: String
data: ShelfData
}
interface Featured {
component: String!
treatment: String
}
type SlideshowData {
items: [Slide]
}
type Slideshow implements Featured & Component {
component: String!
treatment: String
data: SlideshowData
}
type SlideData {
image: String!
title: String!
secondaryTitle: String
}
type Slide {
component: String!
treatment: String
data: SlideData
}
type Page implements Component {
id: ID!
treatment: String
component: String!
name: String!
featured: Featured
sections: [Section]!
}
enum SupportedPlatforms {
iOS
tvOS
roku
rokuStick
samsungTv
android
androidTv
xboxOne
web
mobileWeb
fireTablet
fireTv
fireTvStick
vizio
}
type Query {
page(
id: ID
name: String!
userId: String!
platform: SupportedPlatforms!
): Page
}interface Component {
component: String!
treatment: String
}
type VideoTile implements Component {
component: String!
treatment: String
data: VideoData
}
type VideoData {
image: String!
title: String!
secondaryTitle: String!
percentViewed: Float
}
type SeriesTile implements Component {
component: String!
treatment: String
data: SeriesData
}
type SeriesData {
image: String!
title: String!
secondaryTitle: String!
favoriteId: String
}
union Tile = VideoTile | SeriesTile
type ShelfData {
title: String
items: [Tile]
}
interface Section {
component: String!
treatment: String
}
type Shelf implements Section & Component {
component: String!
treatment: String
data: ShelfData
}
interface Featured {
component: String!
treatment: String
}
type SlideshowData {
items: [Slide]
}
type Slideshow implements Featured & Component {
component: String!
treatment: String
data: SlideshowData
}
type SlideData {
image: String!
title: String!
secondaryTitle: String
}
type Slide {
component: String!
treatment: String
data: SlideData
}
type Page implements Component {
id: ID!
treatment: String
component: String!
name: String!
featured: Featured
sections: [Section]!
}
enum SupportedPlatforms {
iOS
tvOS
roku
rokuStick
samsungTv
android
androidTv
xboxOne
web
mobileWeb
fireTablet
fireTv
fireTvStick
vizio
}
type Query {
page(
id: ID
name: String!
userId: String!
platform: SupportedPlatforms!
): Page
}interface Component {
component: String!
treatment: String
}
type VideoTile implements Component {
component: String!
treatment: String
data: VideoData
}
type VideoData {
image: String!
title: String!
secondaryTitle: String!
percentViewed: Float
}
type SeriesTile implements Component {
component: String!
treatment: String
data: SeriesData
}
type SeriesData {
image: String!
title: String!
secondaryTitle: String!
favoriteId: String
}
union Tile = VideoTile | SeriesTile
type ShelfData {
title: String
items: [Tile]
}
interface Section {
component: String!
treatment: String
}
type Shelf implements Section & Component {
component: String!
treatment: String
data: ShelfData
}
interface Featured {
component: String!
treatment: String
}
type SlideshowData {
items: [Slide]
}
type Slideshow implements Featured & Component {
component: String!
treatment: String
data: SlideshowData
}
type SlideData {
image: String!
title: String!
secondaryTitle: String
}
type Slide {
component: String!
treatment: String
data: SlideData
}
type Page implements Component {
id: ID!
treatment: String
component: String!
name: String!
featured: Featured
sections: [Section]!
}
enum SupportedPlatforms {
iOS
tvOS
roku
rokuStick
samsungTv
android
androidTv
xboxOne
web
mobileWeb
fireTablet
fireTv
fireTvStick
vizio
}
type Query {
page(
id: ID
name: String!
userId: String!
platform: SupportedPlatforms!
): Page
}interface Component {
component: String!
treatment: String
}
type VideoTile implements Component {
component: String!
treatment: String
data: VideoData
}
type VideoData {
image: String!
title: String!
secondaryTitle: String!
percentViewed: Float
}
type SeriesTile implements Component {
component: String!
treatment: String
data: SeriesData
}
type SeriesData {
image: String!
title: String!
secondaryTitle: String!
favoriteId: String
}
union Tile = VideoTile | SeriesTile
type ShelfData {
title: String
items: [Tile]
}
interface Section {
component: String!
treatment: String
}
type Shelf implements Section & Component {
component: String!
treatment: String
data: ShelfData
}
interface Featured {
component: String!
treatment: String
}
type SlideshowData {
items: [Slide]
}
type Slideshow implements Featured & Component {
component: String!
treatment: String
data: SlideshowData
}
type SlideData {
image: String!
title: String!
secondaryTitle: String
}
type Slide {
component: String!
treatment: String
data: SlideData
}
type Page implements Component {
id: ID!
treatment: String
component: String!
name: String!
featured: Featured
sections: [Section]!
}
enum SupportedPlatforms {
iOS
tvOS
roku
rokuStick
samsungTv
android
androidTv
xboxOne
web
mobileWeb
fireTablet
fireTv
fireTvStick
vizio
}
type Query {
page(
id: ID
name: String!
userId: String!
platform: SupportedPlatforms!
): Page
}interface Component {
component: String!
treatment: String
}
type VideoTile implements Component {
component: String!
treatment: String
data: VideoData
}
type VideoData {
image: String!
title: String!
secondaryTitle: String!
percentViewed: Float
}
type SeriesTile implements Component {
component: String!
treatment: String
data: SeriesData
}
type SeriesData {
image: String!
title: String!
secondaryTitle: String!
favoriteId: String
}
union Tile = VideoTile | SeriesTile
type ShelfData {
title: String
items: [Tile]
}
interface Section {
component: String!
treatment: String
}
type Shelf implements Section & Component {
component: String!
treatment: String
data: ShelfData
}
interface Featured {
component: String!
treatment: String
}
type SlideshowData {
items: [Slide]
}
type Slideshow implements Featured & Component {
component: String!
treatment: String
data: SlideshowData
}
type SlideData {
image: String!
title: String!
secondaryTitle: String
}
type Slide {
component: String!
treatment: String
data: SlideData
}
type Page implements Component {
id: ID!
treatment: String
component: String!
name: String!
featured: Featured
sections: [Section]!
}
type Query {
page(
id: ID
name: String!
userId: String!
platform: SupportedPlatforms!
): Page
}<Iframe fallback={ } src={`${config.baseURL}/.netlify/functions/graphql`} title="GraphQL Playground" ></Iframe>
<Iframe fallback={ } src={`${config.baseURL}/.netlify/functions/voyager`} title="GraphQL Voyager" ></Iframe>
import React from 'react';
function Slideshow(props) {
// Slideshow manages its own slide number state
const slideNumber = useTimedCounter(props.items.length);
return (
<div className="slideshow">
{props.items.map(slide =>
// Render children with the current slideNumber and the API data
renderComponent(COMPONENTS_DICTIONARY, { ...slide, slideNumber })
)}
</div>
);
}
function Slide(props) {
return (
<div className="slide">
<img alt={props.title} src={props.image} />
<h1>{props.title}</h1>
</div>
);
}
function Shelf(props) {
return (
<div className="shelf">
{props.items.map(tile =>
// Render children with the current slideNumber and the API data
renderComponent(COMPONENTS_DICTIONARY, { ...tile, slideNumber })
)}
</div>
);
}
function SeriesTile(props) {
return (
<div className="seriesTile">
<Link to={`/shows/${props.urlAlias}`}>
<img alt={props.title} src={props.image} />
</Link>
<p>{props.title}</p>
</div>
);
}
/**
* These dictionaries match our GraphQL interfaces
*/
const FEATURED_DICTIONARY = {
Slideshow,
};
const COMPONENTS_DICTIONARY = {
Slide,
SeriesTile,
};
const SECTIONS_DICTIONARY = {
Shelf,
};
export function renderComponent(DICTIONARY, { component, data }) {
const Component = DICTIONARY[component];
if (!Component) {
return null;
}
return <Component {...data} />;
}
export default function App() {
const [loading, data] = useComponentAPIFetch();
if (loading) {
return 'loading...';
}
return (
<div>
<section className="featured">
{renderComponent(FEATURED_DICTIONARY, data.featured)}
</section>
<section className="sections">
{data.sections.map(section =>
renderComponent(SECTIONS_DICTIONARY, section)
)}
</section>
</div>
);
}import React from 'react';
function Slideshow(props) {
// Slideshow manages its own slide number state
const slideNumber = useTimedCounter(props.items.length);
return (
<div className="slideshow">
{props.items.map(slide =>
// Render children with the current slideNumber and the API data
renderComponent(COMPONENTS_DICTIONARY, { ...slide, slideNumber })
)}
</div>
);
}
function Slide(props) {
return (
<div className="slide">
<img alt={props.title} src={props.image} />
<h1>{props.title}</h1>
</div>
);
}
function Shelf(props) {
return (
<div className="shelf">
{props.items.map(tile =>
// Render children with the current slideNumber and the API data
renderComponent(COMPONENTS_DICTIONARY, { ...tile, slideNumber })
)}
</div>
);
}
function SeriesTile(props) {
return (
<div className="seriesTile">
<Link to={`/shows/${props.urlAlias}`}>
<img alt={props.title} src={props.image} />
</Link>
<p>{props.title}</p>
</div>
);
}
/**
* These dictionaries match our GraphQL interfaces
*/
const FEATURED_DICTIONARY = {
Slideshow,
};
const COMPONENTS_DICTIONARY = {
Slide,
SeriesTile,
};
const SECTIONS_DICTIONARY = {
Shelf,
};
export function renderComponent(DICTIONARY, { component, data }) {
const Component = DICTIONARY[component];
if (!Component) {
return null;
}
return <Component {...data} />;
}
export default function App() {
const [loading, data] = useComponentAPIFetch();
if (loading) {
return 'loading...';
}
return (
<div>
<section className="featured">
{renderComponent(FEATURED_DICTIONARY, data.featured)}
</section>
<section className="sections">
{data.sections.map(section =>
renderComponent(SECTIONS_DICTIONARY, section)
)}
</section>
</div>
);
}import React from 'react';
function Slideshow(props) {
// Slideshow manages its own slide number state
const slideNumber = useTimedCounter(props.items.length);
return (
<div className="slideshow">
{props.items.map(slide =>
// Render children with the current slideNumber and the API data
renderComponent(COMPONENTS_DICTIONARY, { ...slide, slideNumber })
)}
</div>
);
}
function Slide(props) {
return (
<div className="slide">
<img alt={props.title} src={props.image} />
<h1>{props.title}</h1>
</div>
);
}
function Shelf(props) {
return (
<div className="shelf">
{props.items.map(tile =>
// Render children with the current slideNumber and the API data
renderComponent(COMPONENTS_DICTIONARY, { ...tile, slideNumber })
)}
</div>
);
}
function SeriesTile(props) {
const a = `/shows/${props.urlAlias}`;
return (
<div className="seriesTile">
<Link to={a}>
<img alt={props.title} src={props.image} />
</Link>
<p>{props.title}</p>
</div>
);
}
/**
* These dictionaries match our GraphQL interfaces
*/
const FEATURED_DICTIONARY = {
Slideshow,
};
const COMPONENTS_DICTIONARY = {
Slide,
SeriesTile,
};
const SECTIONS_DICTIONARY = {
Shelf,
};
export function renderComponent(DICTIONARY, { component, data }) {
const Component = DICTIONARY[component];
if (!Component) {
return null;
}
return <Component {...data} />;
}
export default function App() {
const [loading, data] = useComponentAPIFetch();
if (loading) {
return 'loading...';
}
return (
<div>
<section className="featured">
{renderComponent(FEATURED_DICTIONARY, data.featured)}
</section>
<section className="sections">
{data.sections.map(section =>
renderComponent(SECTIONS_DICTIONARY, section)
)}
</section>
</div>
);
}import React from 'react';
function Slideshow(props) {
// Slideshow manages its own slide number state
const slideNumber = useTimedCounter(props.items.length);
return (
<div className="slideshow">
{props.items.map(slide =>
// Render children with the current slideNumber and the API data
renderComponent(COMPONENTS_DICTIONARY, { ...slide, slideNumber })
)}
</div>
);
}
function Slide(props) {
return (
<div className="slide">
<img alt={props.title} src={props.image} />
<h1>{props.title}</h1>
</div>
);
}
function Shelf(props) {
return (
<div className="shelf">
{props.items.map(tile =>
// Render children with the current slideNumber and the API data
renderComponent(COMPONENTS_DICTIONARY, tile)
)}
</div>
);
}
function SeriesTile(props) {
const a = `/shows/${props.urlAlias}`;
return (
<div className="seriesTile">
<Link to={a}>
<img alt={props.title} src={props.image} />
</Link>
<p>{props.title}</p>
</div>
);
}
/**
* These dictionaries match our GraphQL interfaces
*/
const FEATURED_DICTIONARY = {
Slideshow,
};
const COMPONENTS_DICTIONARY = {
Slide,
SeriesTile,
};
const SECTIONS_DICTIONARY = {
Shelf,
};
export function renderComponent(DICTIONARY, { component, data }) {
const Component = DICTIONARY[component];
if (!Component) {
return null;
}
return <Component {...data} />;
}
export default function App() {
const [loading, data] = useComponentAPIFetch();
if (loading) {
return 'loading...';
}
return (
<div>
<section className="featured">
{renderComponent(FEATURED_DICTIONARY, data.featured)}
</section>
<section className="sections">
{data.sections.map(section =>
renderComponent(SECTIONS_DICTIONARY, section)
)}
</section>
</div>
);
}import React from 'react';
function Slideshow(props) {
// Slideshow manages its own slide number state
const slideNumber = useTimedCounter(props.items.length);
return (
<div className="slideshow">
{props.items.map(slide =>
// Render children with the current slideNumber and the API data
renderComponent(COMPONENTS_DICTIONARY, { ...slide, slideNumber })
)}
</div>
);
}
function Slide(props) {
return (
<div className="slide">
<img alt={props.title} src={props.image} />
<h1>{props.title}</h1>
</div>
);
}
function Shelf(props) {
return (
<div className="shelf">
{props.items.map(tile =>
// Render children with the current slideNumber and the API data
renderComponent(COMPONENTS_DICTIONARY, tile)
)}
</div>
);
}
function SeriesTile(props) {
const a = `/shows/${props.urlAlias}`;
return (
<div className="seriesTile">
<Link to={a}>
<img alt={props.title} src={props.image} />
</Link>
<p>{props.title}</p>
</div>
);
}
/**
* These dictionaries match our GraphQL interfaces
*/
const FEATURED_DICTIONARY = {
Slideshow,
};
const COMPONENTS_DICTIONARY = {
Slide,
SeriesTile,
};
const SECTIONS_DICTIONARY = {
Shelf,
};
export function renderComponent(DICTIONARY, { component, data }) {
const Component = DICTIONARY[component];
if (!Component) {
return null;
}
return <Component {...data} />;
}
export default function App() {
const [loading, data] = useComponentAPIFetch();
if (loading) {
return 'loading...';
}
return (
<div>
<section className="featured">
{renderComponent(FEATURED_DICTIONARY, data.featured)}
</section>
<section className="sections">
{data.sections.map(section =>
renderComponent(SECTIONS_DICTIONARY, section)
)}
</section>
</div>
);
}import React from 'react';
function Slideshow(props) {
// Slideshow manages its own slide number state
const slideNumber = useTimedCounter(props.items.length);
return (
<div className="slideshow">
{props.items.map(slide =>
// Render children with the current slideNumber and the API data
renderComponent(COMPONENTS_DICTIONARY, { ...slide, slideNumber })
)}
</div>
);
}
function Slide(props) {
return (
<div className="slide">
<img alt={props.title} src={props.image} />
<h1>{props.title}</h1>
</div>
);
}
function Shelf(props) {
return (
<div className="shelf">
{props.items.map(tile =>
// Render children with the current slideNumber and the API data
renderComponent(COMPONENTS_DICTIONARY, tile)
)}
</div>
);
}
function SeriesTile(props) {
const a = `/shows/${props.urlAlias}`;
return (
<div className="seriesTile">
<Link to={a}>
<img alt={props.title} src={props.image} />
</Link>
<p>{props.title}</p>
</div>
);
}
/**
* These dictionaries match our GraphQL interfaces
*/
const FEATURED_DICTIONARY = {
Slideshow,
};
const COMPONENTS_DICTIONARY = {
Slide,
SeriesTile,
};
const SECTIONS_DICTIONARY = {
Shelf,
};
export function renderComponent(DICTIONARY, { component, data }) {
const Component = DICTIONARY[component];
if (!Component) {
return null;
}
return <Component {...data} />;
}
export default function App() {
const [loading, data] = useComponentAPIFetch();
if (loading) {
return 'loading...';
}
return (
<div>
<section className="featured">
{renderComponent(FEATURED_DICTIONARY, data.featured)}
</section>
<section className="sections">
{data.sections.map(section =>
renderComponent(SECTIONS_DICTIONARY, section)
)}
</section>
</div>
);
}

