Yoga Docs: Rename website-next
to website
(#1613)
Summary: Pull Request resolved: https://github.com/facebook/yoga/pull/1613 So that GitHub links to edit a page point to the right place. This will fail in OSS build until I switch the directory used by Vercel instance, but I am waiting to do that until ready to land, since that would cause other in progress changes to fail when exported. Reviewed By: joevilches Differential Revision: D54837857 fbshipit-source-id: 9bec90232dbe3ec8638568685671185d597fcf2d
This commit is contained in:
committed by
Facebook GitHub Bot
parent
108c2f30a2
commit
206b95aba5
77
website/src/components/EditorToolbar.module.css
Normal file
77
website/src/components/EditorToolbar.module.css
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
.toolbar {
|
||||
display: flex;
|
||||
column-gap: 8px;
|
||||
}
|
||||
|
||||
.toolbar button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: var(--prism-background-color);
|
||||
color: var(--prism-color);
|
||||
border: 1px solid var(--ifm-color-emphasis-300);
|
||||
border-radius: var(--ifm-global-radius);
|
||||
padding: 0.4rem;
|
||||
line-height: 0;
|
||||
transition: opacity var(--ifm-transition-fast) ease-in-out;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.toolbar button:focus-visible,
|
||||
.toolbar button:hover {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.iconSwitcher {
|
||||
position: relative;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.actionIcon,
|
||||
.successIcon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
fill: currentColor;
|
||||
opacity: inherit;
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
transition: all var(--ifm-transition-fast) ease;
|
||||
}
|
||||
|
||||
.successIcon {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%) scale(0.33);
|
||||
opacity: 0;
|
||||
color: #00d600;
|
||||
}
|
||||
|
||||
.clicked .actionIcon {
|
||||
transform: scale(0.33);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.clicked .successIcon {
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
opacity: 1;
|
||||
transition-delay: 0.075s;
|
||||
}
|
||||
|
||||
@media (max-width: 996px) {
|
||||
.toolbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
80
website/src/components/EditorToolbar.tsx
Normal file
80
website/src/components/EditorToolbar.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {useCallback, useEffect, useRef, useState} from 'react';
|
||||
import clsx from 'clsx';
|
||||
import lzString from 'lz-string';
|
||||
|
||||
import LinkIcon from '../../static/img/link.svg';
|
||||
import SuccessIcon from '@theme/Icon/Success';
|
||||
|
||||
import styles from './EditorToolbar.module.css';
|
||||
|
||||
export type Props = Readonly<{
|
||||
code: string;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
}>;
|
||||
|
||||
export default function EditorToolbar({
|
||||
code,
|
||||
className,
|
||||
style,
|
||||
}: Props): JSX.Element {
|
||||
const handleShare = useCallback(() => {
|
||||
navigator.clipboard.writeText(
|
||||
window.location.origin +
|
||||
`/playground?code=${lzString.compressToEncodedURIComponent(code)}`,
|
||||
);
|
||||
}, [code]);
|
||||
|
||||
return (
|
||||
<div className={clsx(styles.toolbar, className)} style={style}>
|
||||
<ToolbarButton Icon={LinkIcon} label="Share" onClick={handleShare} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type ToolbarButtonProps = Readonly<{
|
||||
onClick: () => void;
|
||||
Icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
|
||||
label?: string;
|
||||
}>;
|
||||
|
||||
function ToolbarButton({
|
||||
onClick,
|
||||
Icon,
|
||||
label,
|
||||
}: ToolbarButtonProps): JSX.Element {
|
||||
const [isSuccess, setIsSuccess] = useState(false);
|
||||
const copyTimeout = useRef<number | undefined>(undefined);
|
||||
|
||||
useEffect(() => () => window.clearTimeout(copyTimeout.current), []);
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
onClick();
|
||||
setIsSuccess(true);
|
||||
copyTimeout.current = window.setTimeout(() => {
|
||||
setIsSuccess(false);
|
||||
}, 1000);
|
||||
}, [onClick]);
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={clsx('clean-btn', isSuccess && styles.clicked)}
|
||||
onClick={handleClick}
|
||||
aria-label={label}>
|
||||
<span className={styles.iconSwitcher} aria-hidden="true">
|
||||
<Icon className={styles.actionIcon} />
|
||||
<SuccessIcon className={styles.successIcon} />
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
}
|
426
website/src/components/FlexStyle.ts
Normal file
426
website/src/components/FlexStyle.ts
Normal file
@@ -0,0 +1,426 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {
|
||||
Align,
|
||||
Direction,
|
||||
Display,
|
||||
Edge,
|
||||
FlexDirection,
|
||||
Gutter,
|
||||
Justify,
|
||||
Overflow,
|
||||
PositionType,
|
||||
Wrap,
|
||||
Node as YogaNode,
|
||||
} from 'yoga-layout';
|
||||
|
||||
export type AlignContent =
|
||||
| 'flex-start'
|
||||
| 'flex-end'
|
||||
| 'center'
|
||||
| 'stretch'
|
||||
| 'space-between'
|
||||
| 'space-around'
|
||||
| 'space-evenly';
|
||||
|
||||
export type AlignItems =
|
||||
| 'flex-start'
|
||||
| 'flex-end'
|
||||
| 'center'
|
||||
| 'stretch'
|
||||
| 'baseline';
|
||||
|
||||
export type JustifyContent =
|
||||
| 'flex-start'
|
||||
| 'flex-end'
|
||||
| 'center'
|
||||
| 'space-between'
|
||||
| 'space-around'
|
||||
| 'space-evenly';
|
||||
|
||||
export type FlexStyle = {
|
||||
alignContent?: AlignContent;
|
||||
alignItems?: AlignItems;
|
||||
alignSelf?: AlignItems;
|
||||
aspectRatio?: number;
|
||||
borderBottomWidth?: number;
|
||||
borderEndWidth?: number;
|
||||
borderLeftWidth?: number;
|
||||
borderRightWidth?: number;
|
||||
borderStartWidth?: number;
|
||||
borderTopWidth?: number;
|
||||
borderWidth?: number;
|
||||
borderInlineWidth?: number;
|
||||
borderBlockWidth?: number;
|
||||
bottom?: number | `${number}%`;
|
||||
direction?: 'ltr' | 'rtl';
|
||||
display?: 'none' | 'flex';
|
||||
end?: number | `${number}%`;
|
||||
flex?: number;
|
||||
flexBasis?: number | 'auto' | `${number}%`;
|
||||
flexDirection?: 'row' | 'column' | 'row-reverse' | 'column-reverse';
|
||||
rowGap?: number;
|
||||
gap?: number;
|
||||
columnGap?: number;
|
||||
flexGrow?: number;
|
||||
flexShrink?: number;
|
||||
flexWrap?: 'wrap' | 'nowrap' | 'wrap-reverse';
|
||||
height?: number | 'auto' | `${number}%`;
|
||||
justifyContent?: JustifyContent;
|
||||
left?: number | `${number}%`;
|
||||
margin?: number | 'auto' | `${number}%`;
|
||||
marginBottom?: number | 'auto' | `${number}%`;
|
||||
marginEnd?: number | 'auto' | `${number}%`;
|
||||
marginLeft?: number | 'auto' | `${number}%`;
|
||||
marginRight?: number | 'auto' | `${number}%`;
|
||||
marginStart?: number | 'auto' | `${number}%`;
|
||||
marginTop?: number | 'auto' | `${number}%`;
|
||||
marginInline?: number | 'auto' | `${number}%`;
|
||||
marginBlock?: number | 'auto' | `${number}%`;
|
||||
maxHeight?: number | `${number}%`;
|
||||
maxWidth?: number | `${number}%`;
|
||||
minHeight?: number | `${number}%`;
|
||||
minWidth?: number | `${number}%`;
|
||||
overflow?: 'visible' | 'hidden' | 'scroll';
|
||||
padding?: number | `${number}%`;
|
||||
paddingBottom?: number | `${number}%`;
|
||||
paddingEnd?: number | `${number}%`;
|
||||
paddingLeft?: number | `${number}%`;
|
||||
paddingRight?: number | `${number}%`;
|
||||
paddingStart?: number | `${number}%`;
|
||||
paddingTop?: number | `${number}%`;
|
||||
paddingInline?: number | `${number}%`;
|
||||
paddingBlock?: number | `${number}%`;
|
||||
position?: 'absolute' | 'relative' | 'static';
|
||||
right?: number | `${number}%`;
|
||||
start?: number | `${number}%`;
|
||||
top?: number | `${number}%`;
|
||||
insetInline?: number | `${number}%`;
|
||||
insetBlock?: number | `${number}%`;
|
||||
inset?: number | `${number}%`;
|
||||
width?: number | 'auto' | `${number}%`;
|
||||
};
|
||||
|
||||
export function applyStyle(node: YogaNode, style: FlexStyle = {}): void {
|
||||
for (const key of Object.keys(style)) {
|
||||
try {
|
||||
switch (key) {
|
||||
case 'alignContent':
|
||||
node.setAlignContent(alignContent(style.alignContent));
|
||||
break;
|
||||
case 'alignItems':
|
||||
node.setAlignItems(alignItems(style.alignItems));
|
||||
break;
|
||||
case 'alignSelf':
|
||||
node.setAlignSelf(alignItems(style.alignSelf));
|
||||
break;
|
||||
case 'aspectRatio':
|
||||
node.setAspectRatio(style.aspectRatio);
|
||||
break;
|
||||
case 'borderBottomWidth':
|
||||
node.setBorder(Edge.Bottom, style.borderBottomWidth);
|
||||
break;
|
||||
case 'borderEndWidth':
|
||||
node.setBorder(Edge.End, style.borderEndWidth);
|
||||
break;
|
||||
case 'borderLeftWidth':
|
||||
node.setBorder(Edge.Left, style.borderLeftWidth);
|
||||
break;
|
||||
case 'borderRightWidth':
|
||||
node.setBorder(Edge.Right, style.borderRightWidth);
|
||||
break;
|
||||
case 'borderStartWidth':
|
||||
node.setBorder(Edge.Start, style.borderStartWidth);
|
||||
break;
|
||||
case 'borderTopWidth':
|
||||
node.setBorder(Edge.Top, style.borderTopWidth);
|
||||
break;
|
||||
case 'borderWidth':
|
||||
node.setBorder(Edge.All, style.borderWidth);
|
||||
break;
|
||||
case 'borderInlineWidth':
|
||||
node.setBorder(Edge.Horizontal, style.borderInlineWidth);
|
||||
break;
|
||||
case 'borderBlockWidth':
|
||||
node.setBorder(Edge.Vertical, style.borderBlockWidth);
|
||||
break;
|
||||
case 'bottom':
|
||||
node.setPosition(Edge.Bottom, style.bottom);
|
||||
break;
|
||||
case 'direction':
|
||||
node.setDirection(direction(style.direction));
|
||||
break;
|
||||
case 'display':
|
||||
node.setDisplay(display(style.display));
|
||||
break;
|
||||
case 'end':
|
||||
node.setPosition(Edge.End, style.end);
|
||||
break;
|
||||
case 'flex':
|
||||
node.setFlex(style.flex);
|
||||
break;
|
||||
case 'flexBasis':
|
||||
node.setFlexBasis(style.flexBasis);
|
||||
break;
|
||||
case 'flexDirection':
|
||||
node.setFlexDirection(flexDirection(style.flexDirection));
|
||||
break;
|
||||
case 'rowGap':
|
||||
node.setGap(Gutter.Row, style.rowGap);
|
||||
break;
|
||||
case 'gap':
|
||||
node.setGap(Gutter.All, style.gap);
|
||||
break;
|
||||
case 'columnGap':
|
||||
node.setGap(Gutter.Column, style.columnGap);
|
||||
break;
|
||||
case 'flexGrow':
|
||||
node.setFlexGrow(style.flexGrow);
|
||||
break;
|
||||
case 'flexShrink':
|
||||
node.setFlexShrink(style.flexShrink);
|
||||
break;
|
||||
case 'flexWrap':
|
||||
node.setFlexWrap(flexWrap(style.flexWrap));
|
||||
break;
|
||||
case 'height':
|
||||
node.setHeight(style.height);
|
||||
break;
|
||||
case 'justifyContent':
|
||||
node.setJustifyContent(justifyContent(style.justifyContent));
|
||||
break;
|
||||
case 'left':
|
||||
node.setPosition(Edge.Left, style.left);
|
||||
break;
|
||||
case 'margin':
|
||||
node.setMargin(Edge.All, style.margin);
|
||||
break;
|
||||
case 'marginBottom':
|
||||
node.setMargin(Edge.Bottom, style.marginBottom);
|
||||
break;
|
||||
case 'marginEnd':
|
||||
node.setMargin(Edge.End, style.marginEnd);
|
||||
break;
|
||||
case 'marginLeft':
|
||||
node.setMargin(Edge.Left, style.marginLeft);
|
||||
break;
|
||||
case 'marginRight':
|
||||
node.setMargin(Edge.Right, style.marginRight);
|
||||
break;
|
||||
case 'marginStart':
|
||||
node.setMargin(Edge.Start, style.marginStart);
|
||||
break;
|
||||
case 'marginTop':
|
||||
node.setMargin(Edge.Top, style.marginTop);
|
||||
break;
|
||||
case 'marginInline':
|
||||
node.setMargin(Edge.Horizontal, style.marginInline);
|
||||
break;
|
||||
case 'marginBlock':
|
||||
node.setMargin(Edge.Vertical, style.marginBlock);
|
||||
break;
|
||||
case 'maxHeight':
|
||||
node.setMaxHeight(style.maxHeight);
|
||||
break;
|
||||
case 'maxWidth':
|
||||
node.setMaxWidth(style.maxWidth);
|
||||
break;
|
||||
case 'minHeight':
|
||||
node.setMinHeight(style.minHeight);
|
||||
break;
|
||||
case 'minWidth':
|
||||
node.setMinWidth(style.minWidth);
|
||||
break;
|
||||
case 'overflow':
|
||||
node.setOverflow(overflow(style.overflow));
|
||||
break;
|
||||
case 'padding':
|
||||
node.setPadding(Edge.All, style.padding);
|
||||
break;
|
||||
case 'paddingBottom':
|
||||
node.setPadding(Edge.Bottom, style.paddingBottom);
|
||||
break;
|
||||
case 'paddingEnd':
|
||||
node.setPadding(Edge.End, style.paddingEnd);
|
||||
break;
|
||||
case 'paddingLeft':
|
||||
node.setPadding(Edge.Left, style.paddingLeft);
|
||||
break;
|
||||
case 'paddingRight':
|
||||
node.setPadding(Edge.Right, style.paddingRight);
|
||||
break;
|
||||
case 'paddingStart':
|
||||
node.setPadding(Edge.Start, style.paddingStart);
|
||||
break;
|
||||
case 'paddingTop':
|
||||
node.setPadding(Edge.Top, style.paddingTop);
|
||||
break;
|
||||
case 'paddingInline':
|
||||
node.setPadding(Edge.Horizontal, style.paddingInline);
|
||||
break;
|
||||
case 'paddingBlock':
|
||||
node.setPadding(Edge.Vertical, style.paddingBlock);
|
||||
break;
|
||||
case 'position':
|
||||
node.setPositionType(position(style.position));
|
||||
break;
|
||||
case 'right':
|
||||
node.setPosition(Edge.Right, style.right);
|
||||
break;
|
||||
case 'start':
|
||||
node.setPosition(Edge.Start, style.start);
|
||||
break;
|
||||
case 'top':
|
||||
node.setPosition(Edge.Top, style.top);
|
||||
break;
|
||||
case 'insetInline':
|
||||
node.setPosition(Edge.Horizontal, style.insetInline);
|
||||
break;
|
||||
case 'insetBlock':
|
||||
node.setPosition(Edge.Vertical, style.insetBlock);
|
||||
break;
|
||||
case 'inset':
|
||||
node.setPosition(Edge.All, style.inset);
|
||||
break;
|
||||
case 'width':
|
||||
node.setWidth(style.width);
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
// Fail gracefully
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function alignContent(str?: AlignContent): Align {
|
||||
switch (str) {
|
||||
case 'flex-start':
|
||||
return Align.FlexStart;
|
||||
case 'flex-end':
|
||||
return Align.FlexEnd;
|
||||
case 'center':
|
||||
return Align.Center;
|
||||
case 'stretch':
|
||||
return Align.Stretch;
|
||||
case 'space-between':
|
||||
return Align.SpaceBetween;
|
||||
case 'space-around':
|
||||
return Align.SpaceAround;
|
||||
case 'space-evenly':
|
||||
return Align.SpaceEvenly;
|
||||
}
|
||||
throw new Error(`"${str}" is not a valid value for alignContent`);
|
||||
}
|
||||
|
||||
function alignItems(str?: AlignItems): Align {
|
||||
switch (str) {
|
||||
case 'flex-start':
|
||||
return Align.FlexStart;
|
||||
case 'flex-end':
|
||||
return Align.FlexEnd;
|
||||
case 'center':
|
||||
return Align.Center;
|
||||
case 'stretch':
|
||||
return Align.Stretch;
|
||||
case 'baseline':
|
||||
return Align.Baseline;
|
||||
}
|
||||
throw new Error(`"${str}" is not a valid value for alignItems`);
|
||||
}
|
||||
|
||||
function direction(str?: 'ltr' | 'rtl'): Direction {
|
||||
switch (str) {
|
||||
case 'ltr':
|
||||
return Direction.LTR;
|
||||
case 'rtl':
|
||||
return Direction.RTL;
|
||||
}
|
||||
throw new Error(`"${str}" is not a valid value for direction`);
|
||||
}
|
||||
|
||||
function display(str?: 'none' | 'flex'): Display {
|
||||
switch (str) {
|
||||
case 'none':
|
||||
return Display.None;
|
||||
case 'flex':
|
||||
return Display.Flex;
|
||||
}
|
||||
throw new Error(`"${str}" is not a valid value for display`);
|
||||
}
|
||||
|
||||
function flexDirection(
|
||||
str?: 'row' | 'column' | 'row-reverse' | 'column-reverse',
|
||||
): FlexDirection {
|
||||
switch (str) {
|
||||
case 'row':
|
||||
return FlexDirection.Row;
|
||||
case 'column':
|
||||
return FlexDirection.Column;
|
||||
case 'row-reverse':
|
||||
return FlexDirection.RowReverse;
|
||||
case 'column-reverse':
|
||||
return FlexDirection.ColumnReverse;
|
||||
}
|
||||
throw new Error(`"${str}" is not a valid value for flexDirection`);
|
||||
}
|
||||
|
||||
function flexWrap(str?: 'wrap' | 'nowrap' | 'wrap-reverse'): Wrap {
|
||||
switch (str) {
|
||||
case 'wrap':
|
||||
return Wrap.Wrap;
|
||||
case 'nowrap':
|
||||
return Wrap.NoWrap;
|
||||
case 'wrap-reverse':
|
||||
return Wrap.WrapReverse;
|
||||
}
|
||||
throw new Error(`"${str}" is not a valid value for flexWrap`);
|
||||
}
|
||||
|
||||
function justifyContent(str?: JustifyContent): Justify {
|
||||
switch (str) {
|
||||
case 'flex-start':
|
||||
return Justify.FlexStart;
|
||||
case 'flex-end':
|
||||
return Justify.FlexEnd;
|
||||
case 'center':
|
||||
return Justify.Center;
|
||||
case 'space-between':
|
||||
return Justify.SpaceBetween;
|
||||
case 'space-around':
|
||||
return Justify.SpaceAround;
|
||||
case 'space-evenly':
|
||||
return Justify.SpaceEvenly;
|
||||
}
|
||||
throw new Error(`"${str}" is not a valid value for justifyContent`);
|
||||
}
|
||||
|
||||
function overflow(str?: 'visible' | 'hidden' | 'scroll'): Overflow {
|
||||
switch (str) {
|
||||
case 'visible':
|
||||
return Overflow.Visible;
|
||||
case 'hidden':
|
||||
return Overflow.Hidden;
|
||||
case 'scroll':
|
||||
return Overflow.Scroll;
|
||||
}
|
||||
throw new Error(`"${str}" is not a valid value for overflow`);
|
||||
}
|
||||
|
||||
function position(str?: 'absolute' | 'relative' | 'static'): PositionType {
|
||||
switch (str) {
|
||||
case 'absolute':
|
||||
return PositionType.Absolute;
|
||||
case 'relative':
|
||||
return PositionType.Relative;
|
||||
case 'static':
|
||||
return PositionType.Static;
|
||||
}
|
||||
throw new Error(`"${str}" is not a valid value for position`);
|
||||
}
|
68
website/src/components/LayoutBox.module.css
Normal file
68
website/src/components/LayoutBox.module.css
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
html[data-theme='light'] {
|
||||
--yg-color-node-depth-0: var(--ifm-color-gray-0);
|
||||
--yg-color-node-depth-1: var(--ifm-color-gray-200);
|
||||
--yg-color-node-depth-2: var(--ifm-color-gray-400);
|
||||
--yg-color-node-depth-3: var(--ifm-color-gray-600);
|
||||
--yg-color-node-depth-4: var(--ifm-color-gray-800);
|
||||
|
||||
--yg-border-node-depth-0: 1px solid var(--ifm-color-gray-200);
|
||||
--yg-border-node-depth-1: 1px solid var(--ifm-color-gray-600);
|
||||
--yg-border-node-depth-2: 1px solid var(--ifm-color-gray-700);
|
||||
--yg-border-node-depth-3: 1px solid var(--ifm-color-gray-800);
|
||||
--yg-border-node-depth-4: 1px solid var(--ifm-color-gray-900);
|
||||
}
|
||||
|
||||
html[data-theme='dark'] {
|
||||
--yg-color-node-depth-0: var(--ifm-color-gray-900);
|
||||
--yg-color-node-depth-1: var(--ifm-color-gray-800);
|
||||
--yg-color-node-depth-2: var(--ifm-color-gray-700);
|
||||
--yg-color-node-depth-3: var(--ifm-color-gray-600);
|
||||
--yg-color-node-depth-4: var(--ifm-color-gray-500);
|
||||
|
||||
--yg-border-node-depth-0: 1px solid var(--ifm-color-gray-800);
|
||||
--yg-border-node-depth-1: 1px solid var(--ifm-color-gray-700);
|
||||
--yg-border-node-depth-2: 1px solid var(--ifm-color-gray-600);
|
||||
--yg-border-node-depth-3: 1px solid var(--ifm-color-gray-500);
|
||||
--yg-border-node-depth-4: 1px solid var(--ifm-color-gray-400);
|
||||
}
|
||||
|
||||
.layoutBox {
|
||||
box-sizing: border-box;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.zeroDim {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
.depthZero {
|
||||
background: var(--yg-color-node-depth-0);
|
||||
border: var(--yg-border-node-depth-0);
|
||||
}
|
||||
|
||||
.depthOne {
|
||||
background-color: var(--yg-color-node-depth-1);
|
||||
border: var(--yg-border-node-depth-1);
|
||||
}
|
||||
|
||||
.depthTwo {
|
||||
background-color: var(--yg-color-node-depth-2);
|
||||
border: var(--yg-border-node-depth-2);
|
||||
}
|
||||
|
||||
.depthThree {
|
||||
background-color: var(--yg-color-node-depth-3);
|
||||
border: var(--yg-border-node-depth-3);
|
||||
}
|
||||
|
||||
.depthFour {
|
||||
background-color: var(--yg-color-node-depth-4);
|
||||
border: var(--yg-border-node-depth-4);
|
||||
}
|
56
website/src/components/LayoutBox.tsx
Normal file
56
website/src/components/LayoutBox.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
import styles from './LayoutBox.module.css';
|
||||
import clsx from 'clsx';
|
||||
|
||||
export type LayoutMetrics = {
|
||||
top: number;
|
||||
left: number;
|
||||
width: number;
|
||||
height: number;
|
||||
overflow?: 'visible' | 'hidden' | 'scroll';
|
||||
children?: LayoutMetrics[];
|
||||
};
|
||||
|
||||
export type Props = Readonly<{
|
||||
metrics: LayoutMetrics;
|
||||
className?: string;
|
||||
depth: number;
|
||||
}>;
|
||||
|
||||
export default function LayoutBox({metrics, depth, className}: Props) {
|
||||
const {children, ...style} = metrics;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
styles.layoutBox,
|
||||
(metrics.height === 0 || metrics.width === 0) && styles.zeroDim,
|
||||
depth % 5 == 0 && styles.depthZero,
|
||||
depth % 5 == 1 && styles.depthOne,
|
||||
depth % 5 == 2 && styles.depthTwo,
|
||||
depth % 5 == 3 && styles.depthThree,
|
||||
depth % 5 == 4 && styles.depthFour,
|
||||
className,
|
||||
)}
|
||||
style={{
|
||||
top: style.top,
|
||||
left: style.left,
|
||||
width: style.width,
|
||||
height: style.height,
|
||||
overflow: style.overflow,
|
||||
position: depth === 0 ? 'relative' : 'absolute',
|
||||
}}>
|
||||
{children?.map((child, i) => (
|
||||
<LayoutBox key={i} metrics={child} depth={depth + 1} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
130
website/src/components/Playground.module.css
Normal file
130
website/src/components/Playground.module.css
Normal file
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
html[data-theme='light'] {
|
||||
--yg-color-preview-background: var(--ifm-color-primary-lighter);
|
||||
--yg-color-editor-border: var(--ifm-color-gray-400);
|
||||
--yg-color-editor-fallback-bg: rgb(246, 248, 250);
|
||||
--yg-color-editor-fallback-text: rgb(0, 0, 159);
|
||||
}
|
||||
|
||||
html[data-theme='dark'] {
|
||||
--yg-color-preview-background: var(--ifm-color-primary-dark);
|
||||
--yg-color-editor-border: var(--ifm-color-gray-800);
|
||||
--yg-color-editor-fallback-bg: rgb(40, 44, 52);
|
||||
--yg-color-editor-fallback-text: rgb(209, 154, 102);
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
min-width: 600px;
|
||||
padding-block: 16px;
|
||||
}
|
||||
|
||||
.playgroundRow {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
column-gap: 16px;
|
||||
}
|
||||
|
||||
.editorColumn {
|
||||
flex: 8;
|
||||
min-width: 0;
|
||||
overflow-y: auto;
|
||||
border: 1px solid var(--yg-color-editor-border);
|
||||
border-radius: var(--ifm-pre-border-radius);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.editorScroll {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.editorToolbar {
|
||||
position: absolute;
|
||||
top: var(--ifm-pre-padding);
|
||||
right: var(--ifm-pre-padding);
|
||||
}
|
||||
|
||||
.playgroundEditor {
|
||||
font: var(--ifm-code-font-size) / var(--ifm-pre-line-height)
|
||||
var(--ifm-font-family-monospace) !important;
|
||||
direction: ltr;
|
||||
height: calc(var(--yg-playground-height) - 32px);
|
||||
}
|
||||
|
||||
.playgroundEditor :global(.prism-code) {
|
||||
box-shadow: var(--ifm-global-shadow-lw);
|
||||
height: calc(var(--yg-playground-height) - 32px);
|
||||
min-height: 300px;
|
||||
border-radius: 0;
|
||||
padding: var(--ifm-pre-padding) !important;
|
||||
}
|
||||
|
||||
.liveEditorFallback {
|
||||
background-color: var(--yg-color-editor-fallback-bg);
|
||||
color: var(--yg-color-editor-fallback-text);
|
||||
}
|
||||
|
||||
.previewColumn {
|
||||
display: flex;
|
||||
flex: 5;
|
||||
height: calc(var(--yg-playground-height) - 32px);
|
||||
min-height: 300px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--yg-color-preview-background);
|
||||
overflow: hidden;
|
||||
border-radius: var(--ifm-pre-border-radius);
|
||||
align-self: stretch;
|
||||
box-shadow: var(--ifm-global-shadow-lw);
|
||||
}
|
||||
|
||||
.livePreviewWrapper {
|
||||
box-shadow: var(--ifm-global-shadow-md);
|
||||
}
|
||||
|
||||
.liveError {
|
||||
align-self: flex-start;
|
||||
font-size: 12px;
|
||||
box-shadow: var(--ifm-global-shadow-lw);
|
||||
background-color:var(--ifm-color-danger-darker);
|
||||
color: white;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
@media (max-width: 996px) {
|
||||
.wrapper {
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.playgroundEditor {
|
||||
height: unset;
|
||||
}
|
||||
|
||||
.playgroundEditor :global(.prism-code) {
|
||||
height: unset;
|
||||
min-height: 10em;
|
||||
}
|
||||
|
||||
.playgroundRow {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.editorColumn {
|
||||
padding: 0;
|
||||
margin-bottom: 10px;
|
||||
flex: unset;
|
||||
}
|
||||
|
||||
.previewColumn {
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
flex: unset;
|
||||
}
|
||||
}
|
187
website/src/components/Playground.tsx
Normal file
187
website/src/components/Playground.tsx
Normal file
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
import React, {
|
||||
CSSProperties,
|
||||
Suspense,
|
||||
lazy,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
|
||||
import {usePrismTheme} from '@docusaurus/theme-common';
|
||||
import clsx from 'clsx';
|
||||
import {LiveProvider, LiveEditor, LivePreview, LiveError} from 'react-live';
|
||||
import EditorToolbar from './EditorToolbar';
|
||||
|
||||
import type {FlexStyle} from './FlexStyle';
|
||||
import type {StyleNode} from './YogaViewer';
|
||||
|
||||
import styles from './Playground.module.css';
|
||||
import useIsBrowser from '@docusaurus/useIsBrowser';
|
||||
|
||||
export type Props = Readonly<{
|
||||
code: string;
|
||||
height?: CSSProperties['height'];
|
||||
autoFocus?: boolean;
|
||||
}>;
|
||||
|
||||
export default function Playground({code, height, autoFocus}: Props) {
|
||||
const prismTheme = usePrismTheme();
|
||||
const editorScrollRef = useRef<HTMLDivElement>(null);
|
||||
const isBrowser = useIsBrowser();
|
||||
|
||||
const [liveCode, setLiveCode] = useState(code);
|
||||
const [hasCodeChanged, setHasCodeChanged] = useState(false);
|
||||
const [scrollbarWidth, setScrollbarWidth] = useState(0);
|
||||
|
||||
// Once react-live has hydrated the content-editable area, set focus to it
|
||||
// if requested
|
||||
useEffect(() => {
|
||||
if (autoFocus && hasCodeChanged) {
|
||||
const codeElem = editorScrollRef?.current?.querySelector('.prism-code');
|
||||
const sel = window.getSelection();
|
||||
if (codeElem?.clientHeight && sel != null) {
|
||||
sel.selectAllChildren(codeElem);
|
||||
sel.collapseToStart();
|
||||
}
|
||||
}
|
||||
}, [autoFocus, hasCodeChanged]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
// The toolbar is positioned relative to the outside of the scrolling
|
||||
// container so it stays in the same place when scrolling, but this means
|
||||
// it isn't automatically adjusted for scrollbar width. If code change
|
||||
// causes overflow/scrollbar, adjust its position based on its width progrmatically.
|
||||
if (editorScrollRef.current) {
|
||||
setScrollbarWidth(
|
||||
editorScrollRef.current.offsetWidth -
|
||||
editorScrollRef.current.clientWidth,
|
||||
);
|
||||
}
|
||||
}, [editorScrollRef, code]);
|
||||
|
||||
const heightStyle = height
|
||||
? ({'--yg-playground-height': height} as React.CSSProperties)
|
||||
: undefined;
|
||||
|
||||
const handleCodeChange = useCallback((code: string) => {
|
||||
setHasCodeChanged(true);
|
||||
setLiveCode(code);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<LiveProvider
|
||||
code={liveCode}
|
||||
theme={prismTheme}
|
||||
scope={{Node: LiveNode, Layout: RootLiveNode}}>
|
||||
<div className={styles.wrapper} style={heightStyle}>
|
||||
<div className={clsx(styles.playgroundRow)}>
|
||||
<div className={clsx(styles.editorColumn, 'playground-editor')}>
|
||||
<div className={styles.editorScroll} ref={editorScrollRef}>
|
||||
<EditorToolbar
|
||||
code={liveCode}
|
||||
className={styles.editorToolbar}
|
||||
style={{paddingRight: scrollbarWidth + 'px'}}
|
||||
/>
|
||||
|
||||
{isBrowser ? (
|
||||
<LiveEditor
|
||||
className={clsx(styles.playgroundEditor)}
|
||||
onChange={handleCodeChange}
|
||||
/>
|
||||
) : (
|
||||
<LiveEditorFallback code={liveCode} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={clsx(styles.previewColumn)}>
|
||||
<LivePreview className={clsx(styles.livePreview)} />
|
||||
<LiveError className={clsx(styles.liveError)} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LiveProvider>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a non-editable approximation of the LiveEditor result, without
|
||||
* relying on prism rendering, for use during SSR.
|
||||
* See https://github.com/facebook/docusaurus/issues/9629
|
||||
*/
|
||||
function LiveEditorFallback({code}: Readonly<{code: string}>) {
|
||||
return (
|
||||
<div className={clsx(styles.playgroundEditor)}>
|
||||
<pre className={clsx('prism-code', styles.liveEditorFallback)}>
|
||||
{code}
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type RootLiveNodeProps = Readonly<{
|
||||
children: React.ReactNode;
|
||||
config?: {useWebDefaults?: boolean};
|
||||
}>;
|
||||
|
||||
function RootLiveNode({children, config}: RootLiveNodeProps) {
|
||||
if (React.Children.count(children) !== 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const child = React.Children.only(children);
|
||||
if (!React.isValidElement(child) || child.type !== LiveNode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const styleNode = styleNodeFromLiveNode(child as unknown as LiveNode);
|
||||
|
||||
return (
|
||||
<Suspense fallback={null}>
|
||||
<LazyYogaViewer
|
||||
rootNode={styleNode}
|
||||
useWebDefaults={config?.useWebDefaults}
|
||||
/>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
type LiveNodeProps = Readonly<{
|
||||
children: React.ReactNode;
|
||||
style: FlexStyle;
|
||||
}>;
|
||||
|
||||
class LiveNode extends React.PureComponent<LiveNodeProps> {}
|
||||
|
||||
function styleNodeFromLiveNode(
|
||||
liveNode: React.ElementRef<typeof LiveNode>,
|
||||
): StyleNode {
|
||||
const children: StyleNode[] = [];
|
||||
|
||||
React.Children.forEach(liveNode.props.children, child => {
|
||||
if (React.isValidElement(child) && child.type === LiveNode) {
|
||||
children.push(styleNodeFromLiveNode(child as unknown as LiveNode));
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
style: liveNode.props.style,
|
||||
children,
|
||||
};
|
||||
}
|
||||
|
||||
// Docusaurus SSR does not correctly support top-level await in the import
|
||||
// chain
|
||||
// 1. https://github.com/facebook/docusaurus/issues/7238
|
||||
// 2. https://github.com/facebook/docusaurus/issues/9468
|
||||
const LazyYogaViewer = lazy(() => import('./YogaViewer'));
|
114
website/src/components/YogaViewer.tsx
Normal file
114
website/src/components/YogaViewer.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {useMemo} from 'react';
|
||||
import Yoga, {Direction, Overflow, Node as YogaNode} from 'yoga-layout';
|
||||
import {FlexStyle, applyStyle} from './FlexStyle';
|
||||
import LayoutBox from './LayoutBox';
|
||||
|
||||
import type {LayoutMetrics} from './LayoutBox';
|
||||
|
||||
export type Props = Readonly<{
|
||||
rootNode: StyleNode;
|
||||
width?: number;
|
||||
height?: number;
|
||||
className?: string;
|
||||
useWebDefaults?: boolean;
|
||||
}>;
|
||||
|
||||
export type StyleNode = {
|
||||
style?: FlexStyle;
|
||||
children?: StyleNode[];
|
||||
};
|
||||
|
||||
export default function YogaViewer({
|
||||
rootNode,
|
||||
width,
|
||||
height,
|
||||
className,
|
||||
useWebDefaults,
|
||||
}: Props) {
|
||||
const layout = useMemo(
|
||||
() => layoutStyleTree(rootNode, width, height, {useWebDefaults}),
|
||||
[rootNode, width, height, useWebDefaults],
|
||||
);
|
||||
return <LayoutBox metrics={layout} depth={0} className={className} />;
|
||||
}
|
||||
|
||||
type LayoutConfig = Readonly<{
|
||||
useWebDefaults?: boolean;
|
||||
}>;
|
||||
|
||||
// This is not efficient and not a good real-world-example for the best way to use Yoga, but sufficient for a playground
|
||||
function layoutStyleTree(
|
||||
node: StyleNode,
|
||||
rootWidth: number | undefined,
|
||||
rootHeight: number | undefined,
|
||||
layoutConfig: LayoutConfig,
|
||||
): LayoutMetrics {
|
||||
const root = yogaNodeFromStyleNode(node, layoutConfig);
|
||||
root.calculateLayout(rootWidth, rootHeight, Direction.LTR);
|
||||
|
||||
const layoutMetrics = metricsFromYogaNode(root);
|
||||
layoutMetrics.overflow = node.style?.overflow;
|
||||
|
||||
root.freeRecursive();
|
||||
return layoutMetrics;
|
||||
}
|
||||
|
||||
function yogaNodeFromStyleNode(
|
||||
styleNode: StyleNode,
|
||||
layoutConfig: LayoutConfig,
|
||||
): YogaNode {
|
||||
const node = Yoga.Node.create(
|
||||
layoutConfig.useWebDefaults ? webDefaultsConfig : undefined,
|
||||
);
|
||||
applyStyle(node, styleNode.style);
|
||||
|
||||
for (const child of styleNode.children ?? []) {
|
||||
node.insertChild(
|
||||
yogaNodeFromStyleNode(child, layoutConfig),
|
||||
node.getChildCount(),
|
||||
);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
const webDefaultsConfig = Yoga.Config.create();
|
||||
webDefaultsConfig.setUseWebDefaults(true);
|
||||
|
||||
function metricsFromYogaNode(node: YogaNode): LayoutMetrics {
|
||||
const children: LayoutMetrics[] = [];
|
||||
for (let i = 0; i < node.getChildCount(); i++) {
|
||||
children.push(metricsFromYogaNode(node.getChild(i)));
|
||||
}
|
||||
|
||||
// Offset is relative to parent padding box, so we need to subtract the extra
|
||||
// border we show as part of the box.
|
||||
const parentBorderThickness = 1;
|
||||
|
||||
return {
|
||||
top: node.getComputedTop() - parentBorderThickness,
|
||||
left: node.getComputedLeft() - parentBorderThickness,
|
||||
width: node.getComputedWidth(),
|
||||
height: node.getComputedHeight(),
|
||||
overflow: (() => {
|
||||
switch (node.getOverflow()) {
|
||||
case Overflow.Hidden:
|
||||
return 'hidden';
|
||||
case Overflow.Scroll:
|
||||
return 'scroll';
|
||||
case Overflow.Visible:
|
||||
return 'visible';
|
||||
}
|
||||
})(),
|
||||
children,
|
||||
};
|
||||
}
|
37
website/src/css/custom.css
Normal file
37
website/src/css/custom.css
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Any CSS included here will be global. The classic template
|
||||
* bundles Infima by default. Infima is a CSS framework designed to
|
||||
* work well for content-centric websites.
|
||||
*/
|
||||
|
||||
/* You can override the default Infima variables here. */
|
||||
:root {
|
||||
--ifm-color-primary-lightest: rgb(99, 183, 168);
|
||||
--ifm-color-primary-lighter: rgb(70, 159, 143);
|
||||
--ifm-color-primary-light: rgb(48, 135, 119);
|
||||
--ifm-color-primary: rgb(33, 111, 97);
|
||||
--ifm-color-primary-dark: rgb(22, 87, 75);
|
||||
--ifm-color-primary-darker: rgb(14, 63, 54);
|
||||
--ifm-color-primary-darkest: rgb(8, 39, 33);
|
||||
--ifm-code-font-size: 95%;
|
||||
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* For readability concerns, you should choose a lighter palette in dark mode. */
|
||||
[data-theme='dark'] {
|
||||
--ifm-color-primary-lightest: rgb(192, 231, 224);
|
||||
--ifm-color-primary-lighter: rgb(146, 207, 196);
|
||||
--ifm-color-primary-light: rgb(106, 183, 169);
|
||||
--ifm-color-primary: rgb(74, 159, 144);
|
||||
--ifm-color-primary-dark: rgb(51, 135, 120);
|
||||
--ifm-color-primary-darker: rgb(34, 111, 97);
|
||||
--ifm-color-primary-darkest: rgb(22, 87, 75);
|
||||
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
|
||||
}
|
34
website/src/pages/index.module.css
Normal file
34
website/src/pages/index.module.css
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
.heroBanner {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.heroRow {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.heroLogo {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
@media (max-width: 996px) {
|
||||
.heroLogo {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.playgroundSection :global(.playground-editor) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.bg {
|
||||
background-color: var(--yg-color-playground-background);
|
||||
}
|
84
website/src/pages/index.tsx
Normal file
84
website/src/pages/index.tsx
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import clsx from 'clsx';
|
||||
import Link from '@docusaurus/Link';
|
||||
import Layout from '@theme/Layout';
|
||||
|
||||
import styles from './index.module.css';
|
||||
|
||||
import YogaLogo from '../../static/img/logo.svg';
|
||||
import Playground from '../components/Playground';
|
||||
|
||||
function HeroSection() {
|
||||
return (
|
||||
<header className={clsx('hero', styles.heroBanner)}>
|
||||
<div className={clsx('row', 'container', styles.heroRow)}>
|
||||
<div className="col col--6">
|
||||
<h1 className="hero__title">Yoga</h1>
|
||||
<p className="hero__subtitle">
|
||||
A portable layout engine targeting web standards
|
||||
</p>
|
||||
|
||||
<Link
|
||||
className="button button--primary button--lg"
|
||||
to="/docs/about-yoga">
|
||||
Learn more
|
||||
</Link>
|
||||
</div>
|
||||
<div className="col col--2">
|
||||
<YogaLogo className={styles.heroLogo} />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
const playgroundCode = `
|
||||
<Layout config={{useWebDefaults: false}}>
|
||||
<Node style={{width: 250, height: 475, padding: 10}}>
|
||||
<Node style={{flex: 1, rowGap: 10}}>
|
||||
<Node style={{height: 60}} />
|
||||
<Node style={{flex: 1, marginInline: 10}} />
|
||||
<Node style={{flex: 2, marginInline: 10}} />
|
||||
<Node
|
||||
style={{
|
||||
position: "absolute",
|
||||
width: "100%",
|
||||
bottom: 0,
|
||||
height: 64,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-around",
|
||||
}}
|
||||
>
|
||||
<Node style={{height: 40, width: 40}} />
|
||||
<Node style={{height: 40, width: 40}} />
|
||||
<Node style={{height: 40, width: 40}} />
|
||||
<Node style={{height: 40, width: 40}} />
|
||||
</Node>
|
||||
</Node>
|
||||
</Node>
|
||||
</Layout>
|
||||
`.trim();
|
||||
|
||||
function PlaygroundSection() {
|
||||
return (
|
||||
<main className={clsx('container', styles.playgroundSection)}>
|
||||
<Playground height="600px" code={playgroundCode} autoFocus={true} />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Home(): JSX.Element {
|
||||
return (
|
||||
<Layout>
|
||||
<HeroSection />
|
||||
<PlaygroundSection />
|
||||
</Layout>
|
||||
);
|
||||
}
|
7
website/src/pages/markdown-page.md
Normal file
7
website/src/pages/markdown-page.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
title: Markdown page example
|
||||
---
|
||||
|
||||
# Markdown page example
|
||||
|
||||
You don't need React to write simple standalone pages.
|
11
website/src/pages/playground.module.css
Normal file
11
website/src/pages/playground.module.css
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
.playgroundContainer {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
56
website/src/pages/playground.tsx
Normal file
56
website/src/pages/playground.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import Layout from '@theme/Layout';
|
||||
import {useLocation} from '@docusaurus/router';
|
||||
import lzString from 'lz-string';
|
||||
|
||||
import Playground from '../components/Playground';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import styles from './playground.module.css';
|
||||
import useIsBrowser from '@docusaurus/useIsBrowser';
|
||||
|
||||
const defaultCode = `
|
||||
<Layout config={{useWebDefaults: false}}>
|
||||
<Node style={{width: 350, height: 350, padding: 20}}>
|
||||
<Node style={{flex: 1}} />
|
||||
</Node>
|
||||
</Layout>
|
||||
`.trim();
|
||||
|
||||
export default function PlaygroundPage(): JSX.Element {
|
||||
const code = useCodeFromQueryParam();
|
||||
|
||||
return (
|
||||
// @ts-ignore missing prop for `wrapperClassName`
|
||||
<Layout wrapperClassName={clsx('container', styles.bg)} title="Playground">
|
||||
<Playground
|
||||
height="max(80vh, 600px)"
|
||||
code={code}
|
||||
autoFocus={true}
|
||||
key={String(useIsBrowser())}
|
||||
/>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
function useCodeFromQueryParam(): string {
|
||||
const location = useLocation();
|
||||
|
||||
// We don't know the query param ahead of time when doing SSR, so just render
|
||||
// blank to avoid the appearance of code changing.
|
||||
if (!useIsBrowser()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const params = new URLSearchParams(location.search);
|
||||
const codeParam = params.get('code');
|
||||
return codeParam
|
||||
? lzString.decompressFromEncodedURIComponent(codeParam)
|
||||
: defaultCode;
|
||||
}
|
Reference in New Issue
Block a user