mirror of https://github.com/lukechilds/docs.git
Thomas Osmonson
4 years ago
50 changed files with 1431 additions and 1670 deletions
@ -0,0 +1,51 @@ |
|||||
|
const path = require('path'); |
||||
|
const remark = require('remark'); |
||||
|
const flatMap = require('unist-util-flatmap'); |
||||
|
const { readSync } = require('to-vfile'); |
||||
|
|
||||
|
module.exports = function includeMarkdownPlugin({ resolveFrom } = {}) { |
||||
|
return function transformer(tree, file) { |
||||
|
return flatMap(tree, node => { |
||||
|
if (node.type !== 'paragraph') return [node]; |
||||
|
|
||||
|
// detect an `@include` statement
|
||||
|
const includeMatch = |
||||
|
node.children[0].value && node.children[0].value.match(/^@include\s['"](.*)['"]$/); |
||||
|
if (!includeMatch) return [node]; |
||||
|
|
||||
|
// read the file contents
|
||||
|
const includePath = path.join(resolveFrom || file.dirname, includeMatch[1]); |
||||
|
let includeContents; |
||||
|
try { |
||||
|
includeContents = readSync(includePath, 'utf8'); |
||||
|
} catch (err) { |
||||
|
console.log(err); |
||||
|
throw new Error( |
||||
|
`The @include file path at ${includePath} was not found.\n\nInclude Location: ${file.path}:${node.position.start.line}:${node.position.start.column}` |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
// if we are including a ".md" or ".mdx" file, we add the contents as processed markdown
|
||||
|
// if any other file type, they are embedded into a code block
|
||||
|
if (includePath.match(/\.md(?:x)?$/)) { |
||||
|
// return the file contents in place of the @include
|
||||
|
// this takes a couple steps because we allow recursive includes
|
||||
|
const processor = remark().use(includeMarkdownPlugin, { resolveFrom }); |
||||
|
const ast = processor.parse(includeContents); |
||||
|
return processor.runSync(ast, includeContents).children; |
||||
|
} else { |
||||
|
// trim trailing newline
|
||||
|
includeContents.contents = includeContents.contents.trim(); |
||||
|
|
||||
|
// return contents wrapped inside a "code" node
|
||||
|
return [ |
||||
|
{ |
||||
|
type: 'code', |
||||
|
lang: includePath.match(/\.(\w+)$/)[1], |
||||
|
value: includeContents, |
||||
|
}, |
||||
|
]; |
||||
|
} |
||||
|
}); |
||||
|
}; |
||||
|
}; |
@ -0,0 +1,16 @@ |
|||||
|
const memoize = require('micro-memoize'); |
||||
|
const path = require('path'); |
||||
|
|
||||
|
const remarkPlugins = [ |
||||
|
[require('./remark-include'), { resolveFrom: path.join(__dirname, '../src/includes') }], |
||||
|
require('remark-vscode'), |
||||
|
memoize(require('./remark-paragraph-alerts')), |
||||
|
memoize(require('remark-external-links')), |
||||
|
memoize(require('remark-emoji')), |
||||
|
memoize(require('remark-images')), |
||||
|
memoize(require('remark-unwrap-images')), |
||||
|
memoize(require('remark-normalize-headings')), |
||||
|
memoize(require('remark-slug')), |
||||
|
]; |
||||
|
|
||||
|
module.exports = { remarkPlugins }; |
@ -1,37 +0,0 @@ |
|||||
const shiki = require('shiki'); |
|
||||
const visit = require('unist-util-visit'); |
|
||||
|
|
||||
module.exports = function shikiPlugin(options) { |
|
||||
return async function transformer(tree) { |
|
||||
const theme = (options && options.theme) || 'zeit'; |
|
||||
let shikiTheme; |
|
||||
|
|
||||
try { |
|
||||
shikiTheme = shiki.getTheme(theme); |
|
||||
} catch (_) { |
|
||||
try { |
|
||||
shikiTheme = shiki.loadTheme(theme); |
|
||||
} catch (_) { |
|
||||
throw new Error(`Unable to load theme: ${theme}`); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
const highlighter = await shiki.getHighlighter({ |
|
||||
theme: shikiTheme, |
|
||||
}); |
|
||||
|
|
||||
visit(tree, 'code', (node, _, parent) => { |
|
||||
node.type = 'html'; |
|
||||
node.children = undefined; |
|
||||
if (!node.lang && !options.defaultLang) { |
|
||||
node.value = `<pre class="shiki-unknown"><code>${node.value}</code></pre>`; |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
node.value = highlighter.codeToHtml( |
|
||||
node.value, |
|
||||
(node.lang && node.lang.toLowerCase()) || options.defaultLang |
|
||||
); |
|
||||
}); |
|
||||
}; |
|
||||
}; |
|
@ -1,17 +1,11 @@ |
|||||
import { MDXComponents } from '@components/mdx/mdx-components'; |
import { MDXComponents } from '@components/mdx/mdx-components'; |
||||
import renderToString from 'next-mdx-remote/render-to-string'; |
import renderToString from 'next-mdx-remote/render-to-string'; |
||||
|
const { remarkPlugins } = require('../../../lib/remark-plugins'); |
||||
const remarkPlugins = [ |
|
||||
require('remark-squeeze-paragraphs'), |
|
||||
require('remark-external-links'), |
|
||||
require('remark-emoji'), |
|
||||
require('remark-images'), |
|
||||
require('remark-unwrap-images'), |
|
||||
require('remark-normalize-headings'), |
|
||||
require('remark-slug'), |
|
||||
]; |
|
||||
|
|
||||
export const wrapValueInTicks = value => '`' + value.replace('`', '').replace('`', '') + '`'; |
export const wrapValueInTicks = value => '`' + value.replace('`', '').replace('`', '') + '`'; |
||||
|
|
||||
export const convertRemoteDataToMDX = async (arr: any[], key: string) => |
export const convertRemoteDataToMDX = async (arr: any[], key: string) => |
||||
Promise.all(arr.map(entry => renderToString(entry[key], MDXComponents, { remarkPlugins }))); |
Promise.all(arr.map(entry => renderToString(entry[key], MDXComponents, { remarkPlugins }))); |
||||
|
|
||||
|
export const renderMdx = async (content: string) => |
||||
|
renderToString(content, MDXComponents, { remarkPlugins }); |
||||
|
@ -0,0 +1,422 @@ |
|||||
|
/** |
||||
|
* The state machine used here is based on the one provided |
||||
|
* in react-native-web: |
||||
|
* |
||||
|
* https://github.com/necolas/react-native-web/blob/master/packages/react-native-web/src/exports/Touchable/index.js
|
||||
|
*/ |
||||
|
|
||||
|
import * as React from 'react'; |
||||
|
import { isHoverEnabled } from '@common/utils/hover-enabled'; |
||||
|
import { useGestureResponder } from 'react-gesture-responder'; |
||||
|
|
||||
|
/** |
||||
|
* useTouchable |
||||
|
* |
||||
|
* useTouchable is a hook that attempt to emulate native touch behaviour for things |
||||
|
* like list items, buttons, etc. |
||||
|
* |
||||
|
* const { bind, active } = useTouchable({ |
||||
|
* onPress: () => console.log('hello'), |
||||
|
* disabled: false, |
||||
|
* delay: 120 |
||||
|
* }) |
||||
|
* |
||||
|
*/ |
||||
|
|
||||
|
const HIGHLIGHT_DELAY_MS = 100; |
||||
|
const PRESS_EXPAND_PX = 20; |
||||
|
const LONG_PRESS_DELAY = 500 - HIGHLIGHT_DELAY_MS; |
||||
|
|
||||
|
type States = |
||||
|
| 'ERROR' |
||||
|
| 'NOT_RESPONDER' |
||||
|
| 'RESPONDER_ACTIVE_IN' |
||||
|
| 'RESPONDER_ACTIVE_OUT' |
||||
|
| 'RESPONDER_PRESSED_IN' |
||||
|
| 'RESPONDER_PRESSED_OUT' |
||||
|
| 'RESPONDER_LONG_PRESSED_IN'; |
||||
|
|
||||
|
type Events = |
||||
|
| 'DELAY' |
||||
|
| 'RESPONDER_GRANT' |
||||
|
| 'RESPONDER_RELEASE' |
||||
|
| 'RESPONDER_TERMINATED' |
||||
|
| 'ENTER_PRESS_RECT' |
||||
|
| 'LEAVE_PRESS_RECT' |
||||
|
| 'LONG_PRESS_DETECTED'; |
||||
|
|
||||
|
type TransitionsType = { [key in States]: TransitionType }; |
||||
|
|
||||
|
type TransitionType = { [key in Events]: States }; |
||||
|
|
||||
|
const transitions = { |
||||
|
NOT_RESPONDER: { |
||||
|
DELAY: 'NOT_RESPONDER', |
||||
|
RESPONDER_GRANT: 'RESPONDER_ACTIVE_IN', |
||||
|
RESPONDER_RELEASE: 'NOT_RESPONDER', |
||||
|
RESPONDER_TERMINATED: 'NOT_RESPONDER', |
||||
|
ENTER_PRESS_RECT: 'NOT_RESPONDER', |
||||
|
LEAVE_PRESS_RECT: 'NOT_RESPONDER', |
||||
|
LONG_PRESS_DETECTED: 'NOT_RESPONDER', |
||||
|
}, |
||||
|
RESPONDER_ACTIVE_IN: { |
||||
|
DELAY: 'RESPONDER_PRESSED_IN', |
||||
|
RESPONDER_GRANT: 'ERROR', |
||||
|
RESPONDER_RELEASE: 'NOT_RESPONDER', |
||||
|
RESPONDER_TERMINATED: 'NOT_RESPONDER', |
||||
|
ENTER_PRESS_RECT: 'RESPONDER_ACTIVE_IN', |
||||
|
LEAVE_PRESS_RECT: 'RESPONDER_ACTIVE_OUT', |
||||
|
LONG_PRESS_DETECTED: 'ERROR', |
||||
|
}, |
||||
|
RESPONDER_ACTIVE_OUT: { |
||||
|
DELAY: 'RESPONDER_PRESSED_OUT', |
||||
|
RESPONDER_GRANT: 'ERROR', |
||||
|
RESPONDER_RELEASE: 'NOT_RESPONDER', |
||||
|
RESPONDER_TERMINATED: 'NOT_RESPONDER', |
||||
|
ENTER_PRESS_RECT: 'RESPONDER_ACTIVE_IN', |
||||
|
LEAVE_PRESS_RECT: 'RESPONDER_ACTIVE_OUT', |
||||
|
LONG_PRESS_DETECTED: 'ERROR', |
||||
|
}, |
||||
|
RESPONDER_PRESSED_IN: { |
||||
|
DELAY: 'ERROR', |
||||
|
RESPONDER_GRANT: 'ERROR', |
||||
|
RESPONDER_RELEASE: 'NOT_RESPONDER', |
||||
|
RESPONDER_TERMINATED: 'NOT_RESPONDER', |
||||
|
ENTER_PRESS_RECT: 'RESPONDER_PRESSED_IN', |
||||
|
LEAVE_PRESS_RECT: 'RESPONDER_PRESSED_OUT', |
||||
|
LONG_PRESS_DETECTED: 'RESPONDER_LONG_PRESSED_IN', |
||||
|
}, |
||||
|
RESPONDER_PRESSED_OUT: { |
||||
|
DELAY: 'ERROR', |
||||
|
RESPONDER_GRANT: 'ERROR', |
||||
|
RESPONDER_RELEASE: 'NOT_RESPONDER', |
||||
|
RESPONDER_TERMINATED: 'NOT_RESPONDER', |
||||
|
ENTER_PRESS_RECT: 'RESPONDER_PRESSED_IN', |
||||
|
LEAVE_PRESS_RECT: 'RESPONDER_PRESSED_OUT', |
||||
|
LONG_PRESS_DETECTED: 'ERROR', |
||||
|
}, |
||||
|
RESPONDER_LONG_PRESSED_IN: { |
||||
|
DELAY: 'ERROR', |
||||
|
RESPONDER_GRANT: 'ERROR', |
||||
|
RESPONDER_RELEASE: 'NOT_RESPONDER', |
||||
|
RESPONDER_TERMINATED: 'NOT_RESPONDER', |
||||
|
ENTER_PRESS_RECT: 'RESPONDER_PRESSED_IN', |
||||
|
LEAVE_PRESS_RECT: 'RESPONDER_PRESSED_OUT', |
||||
|
LONG_PRESS_DETECTED: 'RESPONDER_LONG_PRESSED_IN', |
||||
|
}, |
||||
|
ERROR: { |
||||
|
DELAY: 'NOT_RESPONDER', |
||||
|
RESPONDER_GRANT: 'RESPONDER_ACTIVE_IN', |
||||
|
RESPONDER_RELEASE: 'NOT_RESPONDER', |
||||
|
RESPONDER_TERMINATED: 'NOT_RESPONDER', |
||||
|
ENTER_PRESS_RECT: 'NOT_RESPONDER', |
||||
|
LEAVE_PRESS_RECT: 'NOT_RESPONDER', |
||||
|
LONG_PRESS_DETECTED: 'NOT_RESPONDER', |
||||
|
}, |
||||
|
} as TransitionsType; |
||||
|
|
||||
|
export type OnPressFunction = ( |
||||
|
e?: React.TouchEvent | React.MouseEvent | React.KeyboardEvent | Event |
||||
|
) => void; |
||||
|
|
||||
|
export interface TouchableOptions { |
||||
|
delay: number; |
||||
|
longPressDelay: number; |
||||
|
pressExpandPx: number; |
||||
|
behavior: 'button' | 'link'; |
||||
|
disabled: boolean; |
||||
|
terminateOnScroll: boolean; |
||||
|
onPress?: OnPressFunction; |
||||
|
onLongPress?: OnPressFunction; |
||||
|
} |
||||
|
|
||||
|
const defaultOptions: TouchableOptions = { |
||||
|
delay: HIGHLIGHT_DELAY_MS, |
||||
|
pressExpandPx: PRESS_EXPAND_PX, |
||||
|
longPressDelay: LONG_PRESS_DELAY, |
||||
|
behavior: 'button', |
||||
|
disabled: false, |
||||
|
terminateOnScroll: true, |
||||
|
onPress: undefined, |
||||
|
onLongPress: undefined, |
||||
|
}; |
||||
|
|
||||
|
export function useTouchable(options: Partial<TouchableOptions> = {}) { |
||||
|
const { |
||||
|
onPress, |
||||
|
onLongPress, |
||||
|
longPressDelay, |
||||
|
terminateOnScroll, |
||||
|
delay, |
||||
|
behavior, |
||||
|
disabled: localDisabled, |
||||
|
} = { |
||||
|
...defaultOptions, |
||||
|
...options, |
||||
|
}; |
||||
|
const disabled = localDisabled; |
||||
|
const ref = React.useRef<HTMLAnchorElement | HTMLDivElement | any>(null); |
||||
|
const delayTimer = React.useRef<number>(); |
||||
|
const longDelayTimer = React.useRef<number>(); |
||||
|
const bounds = React.useRef<ClientRect>(); |
||||
|
const [hover, setHover] = React.useState(false); |
||||
|
const [showHover, setShowHover] = React.useState(true); |
||||
|
const [active, setActive] = React.useState(false); |
||||
|
const state = React.useRef<States>('NOT_RESPONDER'); |
||||
|
|
||||
|
/** |
||||
|
* Transition from one state to another |
||||
|
* @param event |
||||
|
*/ |
||||
|
|
||||
|
function dispatch(event: Events) { |
||||
|
const nextState = transitions[state.current][event]; |
||||
|
state.current = nextState; |
||||
|
|
||||
|
if (nextState === 'RESPONDER_PRESSED_IN' || nextState === 'RESPONDER_LONG_PRESSED_IN') { |
||||
|
setActive(true); |
||||
|
} else { |
||||
|
setActive(false); |
||||
|
} |
||||
|
|
||||
|
if (nextState === 'NOT_RESPONDER') { |
||||
|
clearTimeout(delayTimer.current); |
||||
|
clearTimeout(longDelayTimer.current); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// create a pan responder to handle mouse / touch gestures
|
||||
|
const { bind, terminateCurrentResponder } = useGestureResponder({ |
||||
|
onStartShouldSet: () => true, |
||||
|
onGrant: () => { |
||||
|
onStart(isHoverEnabled() ? 0 : undefined); |
||||
|
}, |
||||
|
onRelease: (_state, e) => onEnd(e), |
||||
|
onMove: (_state, e) => onTouchMove(e), |
||||
|
onTerminate: _state => onTerminate(), |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* Emit a press event if not disabled |
||||
|
* @param e |
||||
|
*/ |
||||
|
|
||||
|
function emitPress(e: React.TouchEvent | React.MouseEvent | React.KeyboardEvent | Event) { |
||||
|
if (!disabled && onPress) { |
||||
|
onPress(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function emitLongPress() { |
||||
|
if (!disabled && onLongPress) { |
||||
|
onLongPress(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function bindScroll() { |
||||
|
if (terminateOnScroll) { |
||||
|
document.addEventListener('scroll', onScroll, { |
||||
|
capture: true, |
||||
|
passive: true, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function unbindScroll() { |
||||
|
document.removeEventListener('scroll', onScroll, true); |
||||
|
} |
||||
|
|
||||
|
function afterDelay() { |
||||
|
dispatch('DELAY'); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get our initial bounding box clientRect and set any delay |
||||
|
* timers if necessary. |
||||
|
* @param delayPressMs |
||||
|
*/ |
||||
|
|
||||
|
function onStart(delayPressMs = delay) { |
||||
|
dispatch('RESPONDER_GRANT'); |
||||
|
bounds.current = ref.current?.getBoundingClientRect(); |
||||
|
delayTimer.current = delayPressMs > 0 ? window.setTimeout(afterDelay, delayPressMs) : undefined; |
||||
|
|
||||
|
if (delayPressMs === 0) { |
||||
|
dispatch('DELAY'); |
||||
|
} |
||||
|
|
||||
|
longDelayTimer.current = window.setTimeout(afterLongDelay, longPressDelay); |
||||
|
|
||||
|
bindScroll(); |
||||
|
setShowHover(false); |
||||
|
} |
||||
|
|
||||
|
function afterLongDelay() { |
||||
|
dispatch('LONG_PRESS_DETECTED'); |
||||
|
emitLongPress(); |
||||
|
} |
||||
|
|
||||
|
// onTerminate should be disambiguated from onRelease
|
||||
|
// because it should never trigger onPress events.
|
||||
|
function onTerminate() { |
||||
|
if (state.current === 'NOT_RESPONDER') { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
dispatch('RESPONDER_RELEASE'); |
||||
|
setShowHover(true); |
||||
|
unbindScroll(); |
||||
|
} |
||||
|
|
||||
|
function onEnd(e?: React.TouchEvent | React.MouseEvent | React.KeyboardEvent | Event) { |
||||
|
// consider unbinding the end event instead
|
||||
|
if (state.current === 'NOT_RESPONDER') { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if ( |
||||
|
e && |
||||
|
(state.current === 'RESPONDER_ACTIVE_IN' || state.current === 'RESPONDER_PRESSED_IN') |
||||
|
) { |
||||
|
emitPress(e); |
||||
|
} |
||||
|
|
||||
|
dispatch('RESPONDER_RELEASE'); |
||||
|
setShowHover(true); |
||||
|
unbindScroll(); |
||||
|
} |
||||
|
|
||||
|
function isWithinActiveBounds( |
||||
|
clientX: number, |
||||
|
clientY: number, |
||||
|
rect: ClientRect, |
||||
|
expandPx: number = PRESS_EXPAND_PX |
||||
|
) { |
||||
|
return ( |
||||
|
clientX > rect.left - expandPx && |
||||
|
clientY > rect.top - expandPx && |
||||
|
clientX < rect.right + expandPx && |
||||
|
clientY < rect.bottom + expandPx |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Determine if the touch remains in the active bounds |
||||
|
* @param e |
||||
|
*/ |
||||
|
|
||||
|
function onTouchMove(e: any) { |
||||
|
if (state.current === 'NOT_RESPONDER' || state.current === 'ERROR') { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
clearTimeout(longDelayTimer.current); |
||||
|
|
||||
|
const { clientX, clientY } = e.touches && e.touches[0] ? e.touches[0] : e; |
||||
|
const withinBounds = isWithinActiveBounds(clientX, clientY, bounds.current); |
||||
|
|
||||
|
if (withinBounds) { |
||||
|
dispatch('ENTER_PRESS_RECT'); |
||||
|
} else { |
||||
|
dispatch('LEAVE_PRESS_RECT'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Scrolling cancels all responder events. This enables |
||||
|
* the user to scroll without selecting something |
||||
|
*/ |
||||
|
|
||||
|
function onScroll() { |
||||
|
unbindScroll(); |
||||
|
dispatch('RESPONDER_TERMINATED'); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* If our mouse leaves we terminate our responder, |
||||
|
* even if our press remains down. This emulates |
||||
|
* native mouse behaviour. |
||||
|
* @param e |
||||
|
*/ |
||||
|
|
||||
|
function onMouseLeave() { |
||||
|
if (hover) { |
||||
|
setHover(false); |
||||
|
} |
||||
|
if (!showHover) { |
||||
|
setShowHover(true); |
||||
|
} |
||||
|
if (state.current !== 'NOT_RESPONDER') { |
||||
|
terminateCurrentResponder(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function onMouseEnter() { |
||||
|
if (!hover) { |
||||
|
setHover(true); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Handle timer and disabled side-effects |
||||
|
*/ |
||||
|
|
||||
|
React.useEffect(() => { |
||||
|
return () => { |
||||
|
clearTimeout(delayTimer.current); |
||||
|
clearTimeout(longDelayTimer.current); |
||||
|
unbindScroll(); |
||||
|
}; |
||||
|
}, []); |
||||
|
|
||||
|
React.useEffect(() => { |
||||
|
if (disabled && state.current !== 'NOT_RESPONDER') { |
||||
|
dispatch('RESPONDER_TERMINATED'); |
||||
|
setShowHover(true); |
||||
|
} |
||||
|
}, [disabled]); |
||||
|
|
||||
|
/** |
||||
|
* Keyboard support |
||||
|
* button: |
||||
|
* onEnterDown -> onPress |
||||
|
* onSpaceUp -> onPress |
||||
|
* Prevent default. |
||||
|
* |
||||
|
* link: Don't prevent default |
||||
|
*/ |
||||
|
|
||||
|
function onKey(e: React.KeyboardEvent) { |
||||
|
const ENTER = 13; |
||||
|
const SPACE = 32; |
||||
|
|
||||
|
if (e.type === 'keydown' && e.which === SPACE) { |
||||
|
onStart(0); |
||||
|
} else if (e.type === 'keydown' && e.which === ENTER) { |
||||
|
emitPress(e); |
||||
|
} else if (e.type === 'keyup' && e.which === SPACE) { |
||||
|
onEnd(e); |
||||
|
} else { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
e.stopPropagation(); |
||||
|
|
||||
|
if (!(e.which === ENTER && behavior === 'link')) { |
||||
|
e.preventDefault(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
bind: { |
||||
|
...bind, |
||||
|
onKeyUp: onKey, |
||||
|
onKeyDown: onKey, |
||||
|
onMouseEnter, |
||||
|
onMouseLeave, |
||||
|
ref, |
||||
|
}, |
||||
|
active: !disabled && active, |
||||
|
hover: isHoverEnabled() && !disabled && hover && showHover, |
||||
|
}; |
||||
|
} |
@ -0,0 +1,43 @@ |
|||||
|
const canUseDOM = !!( |
||||
|
typeof window !== 'undefined' && |
||||
|
window.document && |
||||
|
window.document.createElement |
||||
|
); |
||||
|
|
||||
|
let isEnabled = false; |
||||
|
|
||||
|
const HOVER_THRESHOLD_MS = 1000; |
||||
|
let lastTouchTimestamp = 0; |
||||
|
|
||||
|
function enableHover() { |
||||
|
if (isEnabled || Date.now() - lastTouchTimestamp < HOVER_THRESHOLD_MS) { |
||||
|
return; |
||||
|
} |
||||
|
isEnabled = true; |
||||
|
} |
||||
|
|
||||
|
function disableHover() { |
||||
|
lastTouchTimestamp = Date.now(); |
||||
|
if (isEnabled) { |
||||
|
isEnabled = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (canUseDOM) { |
||||
|
document.addEventListener('touchstart', disableHover, { |
||||
|
capture: true, |
||||
|
passive: true, |
||||
|
}); |
||||
|
document.addEventListener('touchmove', disableHover, { |
||||
|
capture: true, |
||||
|
passive: true, |
||||
|
}); |
||||
|
document.addEventListener('mousemove', enableHover, { |
||||
|
capture: true, |
||||
|
passive: true, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
export function isHoverEnabled() { |
||||
|
return isEnabled; |
||||
|
} |
@ -1,91 +1,19 @@ |
|||||
import React from 'react'; |
import React from 'react'; |
||||
import { MDXComponents } from '@components/mdx/mdx-components'; |
import { MDXComponents } from '@components/mdx/mdx-components'; |
||||
import { Box } from '@blockstack/ui'; |
|
||||
import { TableOfContents } from '@components/toc'; |
import { TableOfContents } from '@components/toc'; |
||||
import { hydrate } from '@common/hydrate-mdx'; |
import { hydrate } from '@common/hydrate-mdx'; |
||||
|
|
||||
const renderFunctionsSection = entry => ( |
export const ClarityKeywordReference = ({ content, headings }) => { |
||||
<> |
|
||||
<MDXComponents.h3>{entry.name}</MDXComponents.h3> |
|
||||
|
|
||||
<MDXComponents.p> |
|
||||
<strong>Signature:</strong>{' '} |
|
||||
<MDXComponents.inlineCode>{entry.signature}</MDXComponents.inlineCode> |
|
||||
</MDXComponents.p> |
|
||||
|
|
||||
<MDXComponents.p> |
|
||||
<strong>Input:</strong>{' '} |
|
||||
<MDXComponents.inlineCode>{entry.input_type}</MDXComponents.inlineCode> |
|
||||
</MDXComponents.p> |
|
||||
|
|
||||
<MDXComponents.p> |
|
||||
<strong>Output:</strong>{' '} |
|
||||
<MDXComponents.inlineCode>{entry.output_type}</MDXComponents.inlineCode> |
|
||||
</MDXComponents.p> |
|
||||
|
|
||||
{hydrate(entry.description, { |
|
||||
...MDXComponents, |
|
||||
p: (props: any) => ( |
|
||||
<MDXComponents.p |
|
||||
{...props} |
|
||||
style={{ display: 'block', wordBreak: 'break-word', hyphens: 'auto' }} |
|
||||
/> |
|
||||
), |
|
||||
})} |
|
||||
|
|
||||
<MDXComponents.h4>Example</MDXComponents.h4> |
|
||||
|
|
||||
{/* @ts-ignore */} |
|
||||
<MDXComponents.code>{entry.example}</MDXComponents.code> |
|
||||
</> |
|
||||
); |
|
||||
|
|
||||
const renderKeywordsSection = entry => ( |
|
||||
<> |
|
||||
<MDXComponents.h3>{entry.name}</MDXComponents.h3> |
|
||||
|
|
||||
<MDXComponents.p> |
|
||||
<strong>Output:</strong>{' '} |
|
||||
<MDXComponents.inlineCode>{entry.output_type}</MDXComponents.inlineCode> |
|
||||
</MDXComponents.p> |
|
||||
|
|
||||
{hydrate(entry.description, MDXComponents)} |
|
||||
|
|
||||
<MDXComponents.h4>Example</MDXComponents.h4> |
|
||||
|
|
||||
{/* @ts-ignore */} |
|
||||
<MDXComponents.code>{entry.example}</MDXComponents.code> |
|
||||
</> |
|
||||
); |
|
||||
|
|
||||
export const ClarityKeywordReference = ({ entries }) => { |
|
||||
return ( |
return ( |
||||
<> |
<> |
||||
<Box> |
<TableOfContents label="Contents" headings={headings} /> |
||||
<TableOfContents |
{hydrate(content, MDXComponents)} |
||||
label="Contents" |
|
||||
headings={entries.map(entry => ({ |
|
||||
content: entry.name, |
|
||||
level: 1, |
|
||||
}))} |
|
||||
/> |
|
||||
</Box> |
|
||||
{entries.map(renderKeywordsSection)} |
|
||||
</> |
</> |
||||
); |
); |
||||
}; |
}; |
||||
export const ClarityFunctionReference = ({ entries }) => ( |
export const ClarityFunctionReference = ({ content, headings }) => ( |
||||
<> |
<> |
||||
<Box> |
<TableOfContents columns={[2, 2, 3]} label="Contents" headings={headings} /> |
||||
<TableOfContents |
{hydrate(content, MDXComponents)} |
||||
columns={[2, 2, 3]} |
|
||||
label="Contents" |
|
||||
headings={entries.map(entry => ({ |
|
||||
content: entry.name, |
|
||||
level: 1, |
|
||||
}))} |
|
||||
/> |
|
||||
</Box> |
|
||||
{entries.map(renderFunctionsSection)} |
|
||||
</> |
</> |
||||
); |
); |
||||
|
@ -1,98 +0,0 @@ |
|||||
import React from 'react'; |
|
||||
import { Highlighter, HighlighterProps } from '../highlighter'; |
|
||||
import { Box, BoxProps, color } from '@blockstack/ui'; |
|
||||
import { css } from '@styled-system/css'; |
|
||||
|
|
||||
// Languages used in docs
|
|
||||
// when adding a new language in the docs, import the theme here
|
|
||||
import 'prismjs/components/prism-bash'; |
|
||||
import 'prismjs/components/prism-css'; |
|
||||
import 'prismjs/components/prism-jsx'; |
|
||||
import 'prismjs/components/prism-tsx'; |
|
||||
import 'prismjs/components/prism-json'; |
|
||||
import 'prismjs/components/prism-toml'; |
|
||||
import 'prismjs/components/prism-python'; |
|
||||
import 'prismjs/components/prism-kotlin'; |
|
||||
|
|
||||
interface CodeBlock { |
|
||||
live?: boolean; |
|
||||
showLineNumbers?: boolean; |
|
||||
highlight?: string; |
|
||||
} |
|
||||
|
|
||||
export type CodeBlockProps = CodeBlock & HighlighterProps & BoxProps; |
|
||||
|
|
||||
const getHighlightLineNumbers = str => |
|
||||
str && |
|
||||
str |
|
||||
.split(' ') |
|
||||
.join('') |
|
||||
.split(',') |
|
||||
.flatMap(s => { |
|
||||
if (!s.includes('-')) return +s; |
|
||||
|
|
||||
const [min, max] = s.split('-'); |
|
||||
|
|
||||
return Array.from({ length: max - min + 1 }, (_, n) => n + +min); |
|
||||
}); |
|
||||
|
|
||||
const CodeBlock = React.memo( |
|
||||
React.forwardRef( |
|
||||
( |
|
||||
{ |
|
||||
code, |
|
||||
showLineNumbers, |
|
||||
hideLineHover, |
|
||||
style = {}, |
|
||||
highlightedLines, |
|
||||
className, |
|
||||
live = true, |
|
||||
highlight, |
|
||||
children, |
|
||||
...props |
|
||||
}: CodeBlockProps, |
|
||||
ref: React.Ref<HTMLDivElement> |
|
||||
) => { |
|
||||
const language = className && className.replace(/language-/, ''); |
|
||||
|
|
||||
const displayNumbers = showLineNumbers || (language && language !== 'bash'); |
|
||||
|
|
||||
return ( |
|
||||
<Box |
|
||||
className={displayNumbers ? 'line-numbers' : ''} |
|
||||
bg="ink" |
|
||||
border="1px solid" |
|
||||
borderColor={color('border')} |
|
||||
borderRightWidth={['0px', '0px', '1px']} |
|
||||
borderLeftWidth={['0px', '0px', '1px']} |
|
||||
borderRadius={[0, 0, '12px']} |
|
||||
overflow="hidden" |
|
||||
> |
|
||||
<Box |
|
||||
ref={ref} |
|
||||
css={css({ |
|
||||
...style, |
|
||||
// @ts-ignore
|
|
||||
overflowX: 'auto', |
|
||||
// @ts-ignore
|
|
||||
color: 'white', |
|
||||
// @ts-ignore
|
|
||||
whiteSpace: 'pre', |
|
||||
...props, |
|
||||
})} |
|
||||
> |
|
||||
<Highlighter |
|
||||
language={language as any} |
|
||||
code={children.toString().trim()} |
|
||||
showLineNumbers={displayNumbers} |
|
||||
highlightedLines={getHighlightLineNumbers(highlight)} |
|
||||
hideLineHover |
|
||||
/> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
); |
|
||||
} |
|
||||
) |
|
||||
); |
|
||||
|
|
||||
export default CodeBlock; |
|
@ -1,100 +0,0 @@ |
|||||
import React from 'react'; |
|
||||
|
|
||||
import Editor from 'react-simple-code-editor'; |
|
||||
import { createGlobalStyle } from 'styled-components'; |
|
||||
import { Box, BoxProps, Highlighter } from '@blockstack/ui'; |
|
||||
// TODO: change when new version is published
|
|
||||
import { Language } from '@blockstack/ui/dist/ui/src/highlighter/types'; |
|
||||
|
|
||||
const TextAreaOverrides = createGlobalStyle` |
|
||||
.code-editor{ |
|
||||
input, |
|
||||
textarea, |
|
||||
[contenteditable] { |
|
||||
caret-color: white; |
|
||||
} |
|
||||
& * { |
|
||||
font-size: 14px !important; |
|
||||
} |
|
||||
textarea{ |
|
||||
width: 100% !important; |
|
||||
padding-left: 16px !important; |
|
||||
font-size: 14px !important; |
|
||||
padding-top: 1px !important; |
|
||||
font-family: 'Fira Code',monospace !important; |
|
||||
line-height: 24px !important; |
|
||||
outline: transparent; |
|
||||
} |
|
||||
& > div{ |
|
||||
overflow: initial !important; |
|
||||
} |
|
||||
textarea, pre { |
|
||||
white-space: pre !important; |
|
||||
overflow-wrap: unset !important; |
|
||||
} |
|
||||
} |
|
||||
`;
|
|
||||
|
|
||||
interface CodeEditorProps extends Partial<Omit<BoxProps, 'onChange'>> { |
|
||||
value: string; |
|
||||
disabled?: boolean; |
|
||||
language?: Language; |
|
||||
onChange?: (code: string) => void; |
|
||||
name?: string; |
|
||||
id?: string; |
|
||||
} |
|
||||
|
|
||||
export const CodeEditor = React.memo((props: CodeEditorProps) => { |
|
||||
const { style, value, onChange, language, id, disabled, maxHeight, ...rest } = props; |
|
||||
const [code, setState] = React.useState(value); |
|
||||
|
|
||||
const updateContent = (c: string) => { |
|
||||
if (c === code) { |
|
||||
return; |
|
||||
} |
|
||||
setState(s => { |
|
||||
if (props.onChange) { |
|
||||
props.onChange(c); |
|
||||
} |
|
||||
return c; |
|
||||
}); |
|
||||
}; |
|
||||
|
|
||||
React.useEffect(() => { |
|
||||
if (value !== code) { |
|
||||
updateContent(value); |
|
||||
} |
|
||||
}, [value]); |
|
||||
|
|
||||
return ( |
|
||||
<> |
|
||||
<TextAreaOverrides /> |
|
||||
<Box |
|
||||
className="code-editor" |
|
||||
bg="ink" |
|
||||
borderRadius={['unset', 'unset', '12px', '12px']} |
|
||||
py="base-tight" |
|
||||
border="1px solid var(--colors-border)" |
|
||||
overflowX="auto" |
|
||||
minWidth="100%" |
|
||||
maxHeight={maxHeight} |
|
||||
> |
|
||||
<Editor |
|
||||
textareaId={id} |
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
|
||||
// @ts-ignore
|
|
||||
language={language} |
|
||||
onValueChange={updateContent} |
|
||||
highlight={c => <Highlighter code={c} language={language} />} |
|
||||
style={{ |
|
||||
...style, |
|
||||
overflowWrap: 'unset', |
|
||||
whiteSpace: 'pre !important' as any, |
|
||||
}} |
|
||||
value={code} |
|
||||
{...rest} |
|
||||
/> |
|
||||
</Box> |
|
||||
</> |
|
||||
); |
|
||||
}); |
|
@ -1,232 +0,0 @@ |
|||||
import React from 'react'; |
|
||||
import Highlight from 'prism-react-renderer'; |
|
||||
import { Box, Flex, space, useTheme } from '@blockstack/ui'; |
|
||||
import { GrammaticalToken, GetGrammaticalTokenProps, RenderProps, Language } from './types'; |
|
||||
import Prism from 'prism-react-renderer/prism'; |
|
||||
import { theme } from '@components/highlighter/prism-theme'; |
|
||||
import './language-definition'; |
|
||||
import { css } from '@styled-system/css'; |
|
||||
|
|
||||
const startPad = (n: number, z = 2, s = '0') => |
|
||||
(n + '').length <= z ? ['', '-'][+(n < 0)] + (s.repeat(z) + Math.abs(n)).slice(-1 * z) : n + ''; |
|
||||
|
|
||||
const LINE_NUMBER_WIDTH = 50; |
|
||||
const getLineNumber = (n: number, length: number) => startPad(n + 1, length.toString().length); |
|
||||
|
|
||||
const Tokens = ({ |
|
||||
tokens, |
|
||||
getTokenProps, |
|
||||
showLineNumbers, |
|
||||
...rest |
|
||||
}: { |
|
||||
tokens: GrammaticalToken[]; |
|
||||
getTokenProps: GetGrammaticalTokenProps; |
|
||||
showLineNumbers?: boolean; |
|
||||
}) => { |
|
||||
const bsTheme = useTheme(); |
|
||||
const pl = showLineNumbers |
|
||||
? [`calc(${LINE_NUMBER_WIDTH}px + ${(bsTheme as any).sizes['base']})`] |
|
||||
: ['unset', 'unset', 'base', 'base']; |
|
||||
|
|
||||
return ( |
|
||||
<Box pl={pl} pr="base" position="relative" zIndex={2} {...rest}> |
|
||||
{tokens.map( |
|
||||
(token, key) => |
|
||||
token.content !== '// highlight' && ( |
|
||||
<Box py="2px" display="inline-block" {...getTokenProps({ token, key })} /> |
|
||||
) |
|
||||
)} |
|
||||
</Box> |
|
||||
); |
|
||||
}; |
|
||||
const LineNumber = ({ number, length, ...rest }: { number: number; length: number }) => ( |
|
||||
<Flex |
|
||||
textAlign="right" |
|
||||
pr={space('tight')} |
|
||||
pl={space('tight')} |
|
||||
width={LINE_NUMBER_WIDTH} |
|
||||
borderRight="1px solid" |
|
||||
borderRightColor="inherit" |
|
||||
color="ink.400" |
|
||||
flexShrink={0} |
|
||||
style={{ userSelect: 'none' }} |
|
||||
position="absolute" |
|
||||
left={0} |
|
||||
height="100%" |
|
||||
align="baseline" |
|
||||
justify="center" |
|
||||
zIndex={1} |
|
||||
css={css({ |
|
||||
color: 'rgba(255,255,255,0.6)', |
|
||||
whiteSpace: 'pre', |
|
||||
fontFamily: 'Fira Code, Consolata, monospace', |
|
||||
fontSize: '14.556040756914118px', |
|
||||
lineHeight: '24px', |
|
||||
padding: '2px 0', |
|
||||
'::before': { |
|
||||
content: "''", |
|
||||
marginTop: '-0.47483499999999995em', |
|
||||
display: 'block', |
|
||||
height: 0, |
|
||||
}, |
|
||||
'::after': { |
|
||||
content: "''", |
|
||||
marginBottom: '-0.493835em', |
|
||||
display: 'block', |
|
||||
height: 0, |
|
||||
}, |
|
||||
})} |
|
||||
{...rest} |
|
||||
> |
|
||||
{getLineNumber(number, length)} |
|
||||
</Flex> |
|
||||
); |
|
||||
|
|
||||
const Line = ({ |
|
||||
tokens, |
|
||||
getTokenProps, |
|
||||
index, |
|
||||
length, |
|
||||
showLineNumbers, |
|
||||
hideLineHover, |
|
||||
highlighted, |
|
||||
...rest |
|
||||
}: { |
|
||||
tokens: GrammaticalToken[]; |
|
||||
index: number; |
|
||||
length: number; |
|
||||
getTokenProps: GetGrammaticalTokenProps; |
|
||||
showLineNumbers?: boolean; |
|
||||
hideLineHover?: boolean; |
|
||||
highlighted?: boolean; |
|
||||
}) => { |
|
||||
const highlightedStyle = { |
|
||||
bg: ['unset', 'unset', 'ink.900'], |
|
||||
borderColor: ['ink.900', 'ink.900', 'ink.600'], |
|
||||
}; |
|
||||
const hasHighlightComment = !!tokens.find(token => token.content === '// highlight'); |
|
||||
const isHighlighted = highlighted || hasHighlightComment; |
|
||||
const highlighedProps = isHighlighted ? highlightedStyle : {}; |
|
||||
|
|
||||
return ( |
|
||||
<Flex |
|
||||
height="loose" |
|
||||
align="baseline" |
|
||||
borderColor="ink.900" |
|
||||
_hover={hideLineHover ? undefined : highlightedStyle} |
|
||||
position="relative" |
|
||||
{...highlighedProps} |
|
||||
{...rest} |
|
||||
> |
|
||||
{showLineNumbers ? <LineNumber number={index} length={length} /> : null} |
|
||||
<Tokens showLineNumbers={showLineNumbers} getTokenProps={getTokenProps} tokens={tokens} /> |
|
||||
</Flex> |
|
||||
); |
|
||||
}; |
|
||||
|
|
||||
const Spacer = ({ showLineNumbers }: { showLineNumbers?: boolean }) => ( |
|
||||
<Flex height="base-loose" bg="ink" width="100%"> |
|
||||
{showLineNumbers && ( |
|
||||
<Box |
|
||||
height="base-loose" |
|
||||
borderRight="1px solid" |
|
||||
borderRightColor="ink.900" |
|
||||
width={`${LINE_NUMBER_WIDTH}px`} |
|
||||
/> |
|
||||
)} |
|
||||
</Flex> |
|
||||
); |
|
||||
const Lines = ({ |
|
||||
tokens: lines, |
|
||||
getLineProps, |
|
||||
getTokenProps, |
|
||||
className, |
|
||||
showLineNumbers, |
|
||||
hideLineHover, |
|
||||
highlightedLines, |
|
||||
}: { |
|
||||
showLineNumbers?: boolean; |
|
||||
hideLineHover?: boolean; |
|
||||
highlightedLines?: number[]; |
|
||||
} & RenderProps) => { |
|
||||
const displayNumbers = lines?.length > 2 && showLineNumbers; |
|
||||
return ( |
|
||||
<Box display="block" className={className}> |
|
||||
<Box display="block"> |
|
||||
<Spacer showLineNumbers={displayNumbers} /> |
|
||||
{lines.map((tokens, i) => ( |
|
||||
<Box |
|
||||
css={css({ |
|
||||
'& > *': { |
|
||||
fontFamily: 'Fira Code, Consolata, monospace', |
|
||||
fontSize: '14.556040756914118px', |
|
||||
lineHeight: '24px', |
|
||||
padding: '0.05px 0', |
|
||||
'::before': { |
|
||||
content: "''", |
|
||||
marginTop: '-0.47483499999999995em', |
|
||||
display: 'block', |
|
||||
height: 0, |
|
||||
}, |
|
||||
'::after': { |
|
||||
content: "''", |
|
||||
marginBottom: '-0.493835em', |
|
||||
display: 'block', |
|
||||
height: 0, |
|
||||
}, |
|
||||
}, |
|
||||
})} |
|
||||
> |
|
||||
<Line |
|
||||
index={i} |
|
||||
tokens={tokens} |
|
||||
getTokenProps={getTokenProps} |
|
||||
length={lines.length + 1} |
|
||||
showLineNumbers={displayNumbers} |
|
||||
highlighted={ |
|
||||
highlightedLines?.length && |
|
||||
!!highlightedLines.find(lineNumber => lineNumber === i + 1) |
|
||||
} |
|
||||
hideLineHover={hideLineHover || lines.length < 3} |
|
||||
{...getLineProps({ line: tokens, key: i })} |
|
||||
/> |
|
||||
</Box> |
|
||||
))} |
|
||||
<Spacer showLineNumbers={displayNumbers} /> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
); |
|
||||
}; |
|
||||
|
|
||||
export interface HighlighterProps { |
|
||||
code: string; |
|
||||
language?: Language; |
|
||||
showLineNumbers?: boolean; |
|
||||
hideLineHover?: boolean; |
|
||||
highlightedLines?: number[]; |
|
||||
} |
|
||||
|
|
||||
export const Highlighter = React.memo( |
|
||||
({ |
|
||||
code, |
|
||||
language = 'clarity', |
|
||||
showLineNumbers, |
|
||||
hideLineHover, |
|
||||
highlightedLines, |
|
||||
}: HighlighterProps) => { |
|
||||
return ( |
|
||||
<Highlight Prism={Prism} theme={theme} code={code} language={language as any}> |
|
||||
{props => ( |
|
||||
<Lines |
|
||||
showLineNumbers={showLineNumbers} |
|
||||
highlightedLines={highlightedLines} |
|
||||
hideLineHover={hideLineHover} |
|
||||
{...props} |
|
||||
/> |
|
||||
)} |
|
||||
</Highlight> |
|
||||
); |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
Highlighter.displayName = 'Highlighter'; |
|
@ -1,117 +0,0 @@ |
|||||
// @ts-nocheck
|
|
||||
import Prism from 'prism-react-renderer/prism'; |
|
||||
|
|
||||
(function (Prism) { |
|
||||
// Functions to construct regular expressions
|
|
||||
// simple form
|
|
||||
// e.g. (interactive ... or (interactive)
|
|
||||
function simple_form(name) { |
|
||||
return RegExp('(\\()' + name + '(?=[\\s\\)])'); |
|
||||
} |
|
||||
// booleans and numbers
|
|
||||
function primitive(pattern) { |
|
||||
return RegExp('([\\s([])' + pattern + '(?=[\\s)])'); |
|
||||
} |
|
||||
|
|
||||
// Patterns in regular expressions
|
|
||||
|
|
||||
// Open parenthesis for look-behind
|
|
||||
const par = '(\\()'; |
|
||||
const endpar = '(?=\\))'; |
|
||||
// End the pattern with look-ahead space
|
|
||||
const space = '(?=\\s)'; |
|
||||
|
|
||||
const language = { |
|
||||
// Three or four semicolons are considered a heading.
|
|
||||
heading: { |
|
||||
pattern: /;;;.*/, |
|
||||
alias: ['comment', 'title'], |
|
||||
}, |
|
||||
comment: /;;.*/, |
|
||||
string: [ |
|
||||
{ |
|
||||
pattern: /"(?:[^"\\]|\\.)*"/, |
|
||||
greedy: true, |
|
||||
}, |
|
||||
{ |
|
||||
pattern: /0x[0-9a-fA-F]*/, |
|
||||
greedy: true, |
|
||||
}, |
|
||||
], |
|
||||
symbol: { |
|
||||
pattern: /'[^()#'\s]+/, |
|
||||
greedy: true, |
|
||||
}, |
|
||||
keyword: [ |
|
||||
{ |
|
||||
pattern: RegExp( |
|
||||
par + |
|
||||
'(?:or|and|xor|not|begin|let|if|ok|err|unwrap\\!|unwrap-err\\!|unwrap-panic|unwrap-err-panic|match|try\\!|asserts\\!|\ |
|
||||
map-get\\?|var-get|contract-map-get\\?|get|tuple|\ |
|
||||
define-public|define-private|define-constant|define-map|define-data-var|\ |
|
||||
define-fungible-token|define-non-fungible-token|\ |
|
||||
define-read-only)' + |
|
||||
space |
|
||||
), |
|
||||
lookbehind: true, |
|
||||
}, |
|
||||
{ |
|
||||
pattern: RegExp(par + '(?:is-eq|is-some|is-none|is-ok|is-er)' + space), |
|
||||
lookbehind: true, |
|
||||
}, |
|
||||
{ |
|
||||
pattern: RegExp( |
|
||||
par + |
|
||||
'(?:var-set|map-set|map-delete|map-insert|\ |
|
||||
ft-transfer\\?|nft-transfer\\?|nft-mint\\?|ft-mint\\?|nft-get-owner\\?|ft-get-balance\\?|\ |
|
||||
contract-call\\?)' + |
|
||||
space |
|
||||
), |
|
||||
lookbehind: true, |
|
||||
}, |
|
||||
{ |
|
||||
pattern: RegExp( |
|
||||
par + |
|
||||
'(?:list|map|filter|fold|len|concat|append|as-max-len\\?|to-int|to-uint|\ |
|
||||
buff|hash160|sha256|sha512|sha512/256|keccak256|true|false|none)' + |
|
||||
space |
|
||||
), |
|
||||
lookbehind: true, |
|
||||
}, |
|
||||
{ |
|
||||
pattern: RegExp( |
|
||||
par + |
|
||||
'(?:as-contract|contract-caller|tx-sender|block-height|at-block|get-block-info\\?)' + |
|
||||
space |
|
||||
), |
|
||||
lookbehind: true, |
|
||||
}, |
|
||||
{ |
|
||||
pattern: RegExp(par + '(?:is-eq|is-some|is-none|is-ok|is-err)' + space), |
|
||||
lookbehind: true, |
|
||||
}, |
|
||||
], |
|
||||
boolean: /(?:false|true|none)/, |
|
||||
number: { |
|
||||
pattern: primitive('[-]?u?\\d+'), |
|
||||
lookbehind: true, |
|
||||
}, |
|
||||
address: { |
|
||||
pattern: /([\s()])(?:\'[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{28,41})(?=[()\s]|$)/, |
|
||||
lookbehind: true, |
|
||||
}, |
|
||||
operator: { |
|
||||
pattern: /(\()(?:[-+*\/]|[<>]=?|=>?)(?=[()\s]|$)/, |
|
||||
lookbehind: true, |
|
||||
}, |
|
||||
function: { |
|
||||
pattern: /(\()[^()'\s]+(?=[()\s]|$)/, |
|
||||
lookbehind: true, |
|
||||
}, |
|
||||
punctuation: /[()']/, |
|
||||
}; |
|
||||
|
|
||||
if (Prism && Prism.languages) { |
|
||||
Prism.languages.clarity = language; |
|
||||
} |
|
||||
})(Prism); |
|
@ -1,205 +0,0 @@ |
|||||
code[class*="language-"], |
|
||||
pre[class*="language-"] { |
|
||||
text-align: left; |
|
||||
white-space: pre; |
|
||||
word-spacing: normal; |
|
||||
word-break: normal; |
|
||||
word-wrap: normal; |
|
||||
color: #eee; |
|
||||
background: #2f2f2f; |
|
||||
font-family: Roboto Mono, monospace; |
|
||||
font-size: 1em; |
|
||||
line-height: 1.5em; |
|
||||
|
|
||||
-moz-tab-size: 4; |
|
||||
-o-tab-size: 4; |
|
||||
tab-size: 4; |
|
||||
|
|
||||
-webkit-hyphens: none; |
|
||||
-moz-hyphens: none; |
|
||||
-ms-hyphens: none; |
|
||||
hyphens: none; |
|
||||
} |
|
||||
|
|
||||
code[class*="language-"]::-moz-selection, |
|
||||
pre[class*="language-"]::-moz-selection, |
|
||||
code[class*="language-"] ::-moz-selection, |
|
||||
pre[class*="language-"] ::-moz-selection { |
|
||||
background: #363636; |
|
||||
} |
|
||||
|
|
||||
code[class*="language-"]::selection, |
|
||||
pre[class*="language-"]::selection, |
|
||||
code[class*="language-"] ::selection, |
|
||||
pre[class*="language-"] ::selection { |
|
||||
background: #363636; |
|
||||
} |
|
||||
|
|
||||
:not(pre) > code[class*="language-"] { |
|
||||
white-space: normal; |
|
||||
border-radius: 0.2em; |
|
||||
padding: 0.1em; |
|
||||
} |
|
||||
|
|
||||
pre[class*="language-"] { |
|
||||
overflow: auto; |
|
||||
position: relative; |
|
||||
margin: 0.5em 0; |
|
||||
padding: 1.25em 1em; |
|
||||
} |
|
||||
|
|
||||
.language-css > code, |
|
||||
.language-sass > code, |
|
||||
.language-scss > code { |
|
||||
color: #fd9170; |
|
||||
} |
|
||||
|
|
||||
[class*="language-"] .namespace { |
|
||||
opacity: 0.7; |
|
||||
} |
|
||||
|
|
||||
.token.atrule { |
|
||||
color: #c792ea; |
|
||||
} |
|
||||
|
|
||||
.token.attr-name { |
|
||||
color: #ffcb6b; |
|
||||
} |
|
||||
|
|
||||
.token.attr-value { |
|
||||
color: #a5e844; |
|
||||
} |
|
||||
|
|
||||
.token.attribute { |
|
||||
color: #a5e844; |
|
||||
} |
|
||||
|
|
||||
.token.boolean { |
|
||||
color: #c792ea; |
|
||||
} |
|
||||
|
|
||||
.token.builtin { |
|
||||
color: #ffcb6b; |
|
||||
} |
|
||||
|
|
||||
.token.cdata { |
|
||||
color: #80cbc4; |
|
||||
} |
|
||||
|
|
||||
.token.char { |
|
||||
color: #80cbc4; |
|
||||
} |
|
||||
|
|
||||
.token.class { |
|
||||
color: #ffcb6b; |
|
||||
} |
|
||||
|
|
||||
.token.class-name { |
|
||||
color: #f2ff00; |
|
||||
} |
|
||||
|
|
||||
.token.comment { |
|
||||
color: #616161; |
|
||||
} |
|
||||
|
|
||||
.token.constant { |
|
||||
color: #c792ea; |
|
||||
} |
|
||||
|
|
||||
.token.deleted { |
|
||||
color: #ff6666; |
|
||||
} |
|
||||
|
|
||||
.token.doctype { |
|
||||
color: #616161; |
|
||||
} |
|
||||
|
|
||||
.token.entity { |
|
||||
color: #ff6666; |
|
||||
} |
|
||||
|
|
||||
.token.function { |
|
||||
color: #c792ea; |
|
||||
} |
|
||||
|
|
||||
.token.hexcode { |
|
||||
color: #f2ff00; |
|
||||
} |
|
||||
|
|
||||
.token.id { |
|
||||
color: #c792ea; |
|
||||
font-weight: bold; |
|
||||
} |
|
||||
|
|
||||
.token.important { |
|
||||
color: #c792ea; |
|
||||
font-weight: bold; |
|
||||
} |
|
||||
|
|
||||
.token.inserted { |
|
||||
color: #80cbc4; |
|
||||
} |
|
||||
|
|
||||
.token.keyword { |
|
||||
color: #c792ea; |
|
||||
} |
|
||||
|
|
||||
.token.number { |
|
||||
color: #fd9170; |
|
||||
} |
|
||||
|
|
||||
.token.operator { |
|
||||
color: #89ddff; |
|
||||
} |
|
||||
|
|
||||
.token.prolog { |
|
||||
color: #616161; |
|
||||
} |
|
||||
|
|
||||
.token.property { |
|
||||
color: #80cbc4; |
|
||||
} |
|
||||
|
|
||||
.token.pseudo-class { |
|
||||
color: #a5e844; |
|
||||
} |
|
||||
|
|
||||
.token.pseudo-element { |
|
||||
color: #a5e844; |
|
||||
} |
|
||||
|
|
||||
.token.punctuation { |
|
||||
color: #89ddff; |
|
||||
} |
|
||||
|
|
||||
.token.regex { |
|
||||
color: #f2ff00; |
|
||||
} |
|
||||
|
|
||||
.token.selector { |
|
||||
color: #ff6666; |
|
||||
} |
|
||||
|
|
||||
.token.string { |
|
||||
color: #a5e844; |
|
||||
} |
|
||||
|
|
||||
.token.symbol { |
|
||||
color: #c792ea; |
|
||||
} |
|
||||
|
|
||||
.token.tag { |
|
||||
color: #ff6666; |
|
||||
} |
|
||||
|
|
||||
.token.unit { |
|
||||
color: #fd9170; |
|
||||
} |
|
||||
|
|
||||
.token.url { |
|
||||
color: #ff6666; |
|
||||
} |
|
||||
|
|
||||
.token.variable { |
|
||||
color: #ff6666; |
|
||||
} |
|
@ -1,140 +0,0 @@ |
|||||
import { PrismTheme } from 'prism-react-renderer'; |
|
||||
|
|
||||
export const theme: PrismTheme = { |
|
||||
plain: { |
|
||||
color: 'rgba(255,255,255,1)', |
|
||||
backgroundColor: '#0f111a', |
|
||||
}, |
|
||||
styles: [ |
|
||||
{ |
|
||||
types: ['string'], |
|
||||
style: { |
|
||||
color: 'rgb(195, 232, 141)', |
|
||||
}, |
|
||||
}, |
|
||||
{ |
|
||||
types: ['boolean'], |
|
||||
style: { |
|
||||
color: 'rgb(255, 156, 172)', |
|
||||
}, |
|
||||
}, |
|
||||
{ |
|
||||
types: ['number', 'keyword', 'operator'], |
|
||||
style: { |
|
||||
color: 'rgb(247, 140, 108)', |
|
||||
}, |
|
||||
}, |
|
||||
{ |
|
||||
types: ['comment'], |
|
||||
style: { |
|
||||
color: 'rgba(255,255,255,0.6)', |
|
||||
fontStyle: 'italic', |
|
||||
}, |
|
||||
}, |
|
||||
{ |
|
||||
types: ['punctuation', 'builtin'], |
|
||||
style: { |
|
||||
color: 'rgb(137, 221, 255)', |
|
||||
}, |
|
||||
}, |
|
||||
{ |
|
||||
types: ['tag'], |
|
||||
style: { |
|
||||
color: 'rgb(240, 113, 120)', |
|
||||
}, |
|
||||
}, |
|
||||
{ |
|
||||
types: ['attr-name'], |
|
||||
style: { |
|
||||
color: 'rgb(255, 203, 107)', |
|
||||
}, |
|
||||
}, |
|
||||
{ |
|
||||
types: ['function'], |
|
||||
style: { |
|
||||
color: 'rgb(130, 170, 255)', |
|
||||
}, |
|
||||
}, |
|
||||
{ |
|
||||
types: ['constant'], |
|
||||
style: { |
|
||||
color: 'rgb(137, 221, 255)', |
|
||||
fontStyle: 'italic', |
|
||||
}, |
|
||||
}, |
|
||||
], |
|
||||
}; |
|
||||
// export const theme: PrismTheme = {
|
|
||||
// plain: {
|
|
||||
// color: '#fff',
|
|
||||
// backgroundColor: 'transparent',
|
|
||||
// },
|
|
||||
// styles: [
|
|
||||
// {
|
|
||||
// types: ['prolog'],
|
|
||||
// style: {
|
|
||||
// color: 'rgb(0, 0, 128)',
|
|
||||
// },
|
|
||||
// },
|
|
||||
// {
|
|
||||
// types: ['comment', 'punctuation'],
|
|
||||
// style: {
|
|
||||
// color: 'rgb(106, 153, 85)',
|
|
||||
// },
|
|
||||
// },
|
|
||||
// {
|
|
||||
// types: ['builtin', 'tag', 'changed', 'function', 'keyword'],
|
|
||||
// style: {
|
|
||||
// color: 'rgb(86, 156, 214)',
|
|
||||
// },
|
|
||||
// },
|
|
||||
// {
|
|
||||
// types: ['number', 'variable', 'inserted'],
|
|
||||
// style: {
|
|
||||
// color: '#A58FFF',
|
|
||||
// },
|
|
||||
// },
|
|
||||
// {
|
|
||||
// types: ['operator'],
|
|
||||
// style: {
|
|
||||
// color: 'rgb(212, 212, 212)',
|
|
||||
// },
|
|
||||
// },
|
|
||||
// {
|
|
||||
// types: ['constant'],
|
|
||||
// style: {
|
|
||||
// color: 'rgb(100, 102, 149)',
|
|
||||
// },
|
|
||||
// },
|
|
||||
// {
|
|
||||
// types: ['attr-name'],
|
|
||||
// style: {
|
|
||||
// color: 'rgb(156, 220, 254)',
|
|
||||
// },
|
|
||||
// },
|
|
||||
// {
|
|
||||
// types: ['car'],
|
|
||||
// style: {
|
|
||||
// color: 'rgb(156, 220, 254)',
|
|
||||
// },
|
|
||||
// },
|
|
||||
// {
|
|
||||
// types: ['deleted', 'string'],
|
|
||||
// style: {
|
|
||||
// color: '#FF7B48',
|
|
||||
// },
|
|
||||
// },
|
|
||||
// {
|
|
||||
// types: ['class-name'],
|
|
||||
// style: {
|
|
||||
// color: 'rgb(78, 201, 176)',
|
|
||||
// },
|
|
||||
// },
|
|
||||
// {
|
|
||||
// types: ['char'],
|
|
||||
// style: {
|
|
||||
// color: '#FF7B48',
|
|
||||
// },
|
|
||||
// },
|
|
||||
// ],
|
|
||||
// };
|
|
@ -1,90 +0,0 @@ |
|||||
import * as React from 'react'; |
|
||||
|
|
||||
export interface GrammaticalToken { |
|
||||
types: string[]; |
|
||||
content: string; |
|
||||
empty?: boolean; |
|
||||
} |
|
||||
|
|
||||
export interface StyleObj { |
|
||||
[key: string]: string | number | null; |
|
||||
} |
|
||||
|
|
||||
export interface GrammaticalTokenOutputProps { |
|
||||
key?: React.Key; |
|
||||
style?: StyleObj; |
|
||||
className: string; |
|
||||
children: string; |
|
||||
[otherProp: string]: any; |
|
||||
} |
|
||||
|
|
||||
export interface GrammaticalTokenInputProps { |
|
||||
key?: React.Key; |
|
||||
style?: StyleObj; |
|
||||
className?: string; |
|
||||
token: GrammaticalToken; |
|
||||
[otherProp: string]: any; |
|
||||
} |
|
||||
|
|
||||
export interface LineInputProps { |
|
||||
key?: React.Key; |
|
||||
style?: StyleObj; |
|
||||
className?: string; |
|
||||
line: GrammaticalToken[]; |
|
||||
[otherProp: string]: any; |
|
||||
} |
|
||||
|
|
||||
export interface LineOutputProps { |
|
||||
key?: React.Key; |
|
||||
style?: StyleObj; |
|
||||
className: string; |
|
||||
[otherProps: string]: any; |
|
||||
} |
|
||||
|
|
||||
export interface RenderProps { |
|
||||
tokens: GrammaticalToken[][]; |
|
||||
className: string; |
|
||||
style: StyleObj; |
|
||||
getLineProps: (input: LineInputProps) => LineOutputProps; |
|
||||
getTokenProps: (input: GrammaticalTokenInputProps) => GrammaticalTokenOutputProps; |
|
||||
} |
|
||||
|
|
||||
export type GetGrammaticalTokenProps = ( |
|
||||
input: GrammaticalTokenInputProps |
|
||||
) => GrammaticalTokenOutputProps; |
|
||||
|
|
||||
export type Language = |
|
||||
| 'markup' |
|
||||
| 'bash' |
|
||||
| 'clarity' |
|
||||
| 'clike' |
|
||||
| 'c' |
|
||||
| 'cpp' |
|
||||
| 'css' |
|
||||
| 'javascript' |
|
||||
| 'jsx' |
|
||||
| 'coffeescript' |
|
||||
| 'actionscript' |
|
||||
| 'css-extr' |
|
||||
| 'diff' |
|
||||
| 'git' |
|
||||
| 'go' |
|
||||
| 'graphql' |
|
||||
| 'handlebars' |
|
||||
| 'json' |
|
||||
| 'less' |
|
||||
| 'lisp' |
|
||||
| 'makefile' |
|
||||
| 'markdown' |
|
||||
| 'objectivec' |
|
||||
| 'ocaml' |
|
||||
| 'python' |
|
||||
| 'reason' |
|
||||
| 'sass' |
|
||||
| 'scss' |
|
||||
| 'sql' |
|
||||
| 'stylus' |
|
||||
| 'tsx' |
|
||||
| 'typescript' |
|
||||
| 'wasm' |
|
||||
| 'yaml'; |
|
@ -1,383 +0,0 @@ |
|||||
import { |
|
||||
Box, |
|
||||
Flex, |
|
||||
FlexProps, |
|
||||
BoxProps, |
|
||||
color, |
|
||||
themeColor, |
|
||||
useClipboard, |
|
||||
space, |
|
||||
} from '@blockstack/ui'; |
|
||||
import NextLink from 'next/link'; |
|
||||
import React, { forwardRef, Ref } from 'react'; |
|
||||
import LinkIcon from 'mdi-react/LinkVariantIcon'; |
|
||||
import HashtagIcon from 'mdi-react/HashtagIcon'; |
|
||||
import { useHover } from 'use-events'; |
|
||||
import { Tooltip } from '@components/tooltip'; |
|
||||
import { useActiveHeading } from '@common/hooks/use-active-heading'; |
|
||||
import { Text, Title } from '@components/typography'; |
|
||||
import { border } from '@common/utils'; |
|
||||
import { css } from '@styled-system/css'; |
|
||||
import { getHeadingStyles, baseTypeStyles } from '@components/mdx/typography'; |
|
||||
import { useRouter } from 'next/router'; |
|
||||
import { HEADER_HEIGHT } from '@components/header'; |
|
||||
import { CheckCircleIcon } from '@components/icons/check-circle'; |
|
||||
import { AlertTriangleIcon } from '@components/icons/alert-triangle'; |
|
||||
import { AlertCircleIcon } from '@components/icons/alert-circle'; |
|
||||
import { InfoCircleIcon } from '@components/icons/info-circle'; |
|
||||
|
|
||||
const preProps = { |
|
||||
display: 'inline-block', |
|
||||
border: border(), |
|
||||
borderRadius: '4px', |
|
||||
padding: '2px 6px', |
|
||||
boxShadow: '0 1px 2px rgba(0, 0, 0, 0.04)', |
|
||||
bg: color('bg'), |
|
||||
}; |
|
||||
export const InlineCode: React.FC<BoxProps> = ({ children, ...rest }) => ( |
|
||||
<Text |
|
||||
as="code" |
|
||||
css={css({ |
|
||||
// @ts-ignore
|
|
||||
fontSize: '14px', |
|
||||
// @ts-ignore
|
|
||||
lineHeight: '20px', |
|
||||
...preProps, |
|
||||
...rest, |
|
||||
})} |
|
||||
> |
|
||||
{children} |
|
||||
</Text> |
|
||||
); |
|
||||
|
|
||||
export const Pre = (props: any) => <Text as="pre" {...props} />; |
|
||||
|
|
||||
export const SmartLink = ({ href, ...rest }: { href: string }) => { |
|
||||
const isExternal = href.includes('http') || href.includes('mailto'); |
|
||||
const link = <Link href={href} {...rest} />; |
|
||||
|
|
||||
return isExternal ? ( |
|
||||
link |
|
||||
) : ( |
|
||||
<NextLink href={href} passHref> |
|
||||
{link} |
|
||||
</NextLink> |
|
||||
); |
|
||||
}; |
|
||||
|
|
||||
export const Table = ({ children, ...rest }: any) => ( |
|
||||
<Box my={space('extra-loose')} maxWidth="100%" {...rest}> |
|
||||
<Box borderRadius={[0, 0, '12px']} border={border()} overflow="hidden"> |
|
||||
<Box overflowX="auto"> |
|
||||
<Box color={color('text-body')} textAlign="left" width="100%" as="table" maxWidth="100%"> |
|
||||
{children} |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
); |
|
||||
|
|
||||
export const THead = (props: any) => { |
|
||||
return ( |
|
||||
<Box |
|
||||
as="th" |
|
||||
color="var(--colors-text-caption)" |
|
||||
borderRight={border()} |
|
||||
bg={color('bg-alt')} |
|
||||
fontSize="12px" |
|
||||
px={space('base-tight')} |
|
||||
pt={space('tight')} |
|
||||
pb={space('extra-tight')} |
|
||||
{...props} |
|
||||
/> |
|
||||
); |
|
||||
}; |
|
||||
|
|
||||
export const TData = (props: any) => ( |
|
||||
<Box |
|
||||
as="td" |
|
||||
fontSize="14px" |
|
||||
p={space('tight')} |
|
||||
px={space('base-tight')} |
|
||||
pt={space('base-tight')} |
|
||||
borderRight={border()} |
|
||||
borderTop={border()} |
|
||||
color={color('text-body')} |
|
||||
whiteSpace="normal" |
|
||||
{...props} |
|
||||
/> |
|
||||
); |
|
||||
|
|
||||
export const Link = forwardRef( |
|
||||
(props: { href: string; target?: string; rel?: string } & BoxProps, ref: Ref<HTMLDivElement>) => ( |
|
||||
<Box |
|
||||
as="a" |
|
||||
ref={ref} |
|
||||
color="var(--colors-accent)" |
|
||||
cursor="pointer" |
|
||||
textDecoration="underline" |
|
||||
_hover={{ textDecoration: 'none' }} |
|
||||
_focus={{ boxShadow: 'outline' }} |
|
||||
{...props} |
|
||||
/> |
|
||||
) |
|
||||
); |
|
||||
|
|
||||
export const TextItem = (props: any) => ( |
|
||||
<Text |
|
||||
mb="1em" |
|
||||
mt="2em" |
|
||||
css={{ |
|
||||
'&[id]': { |
|
||||
pointerEvents: 'none', |
|
||||
}, |
|
||||
'&[id]:before': { |
|
||||
display: 'block', |
|
||||
height: ' 6rem', |
|
||||
marginTop: '-6rem', |
|
||||
visibility: 'hidden', |
|
||||
content: `""`, |
|
||||
}, |
|
||||
'&[id]:hover a': { opacity: 1 }, |
|
||||
}} |
|
||||
{...props} |
|
||||
> |
|
||||
<Box |
|
||||
// @ts-ignore
|
|
||||
pointerEvents="auto" |
|
||||
> |
|
||||
{props.children} |
|
||||
{props.id && ( |
|
||||
<Box |
|
||||
aria-label="anchor" |
|
||||
as="a" |
|
||||
color="teal.500" |
|
||||
fontWeight="normal" |
|
||||
_focus={{ opacity: 1, boxShadow: 'outline' }} |
|
||||
opacity={0} |
|
||||
ml="0.375rem" |
|
||||
// @ts-ignore
|
|
||||
href={`#${props.id}`} |
|
||||
> |
|
||||
# |
|
||||
</Box> |
|
||||
)} |
|
||||
</Box> |
|
||||
</Text> |
|
||||
); |
|
||||
|
|
||||
const LinkButton = React.memo(({ link, onClick, ...rest }: BoxProps & { link: string }) => { |
|
||||
const url = |
|
||||
typeof document !== 'undefined' && document.location.origin + document.location.pathname + link; |
|
||||
|
|
||||
const { onCopy } = useClipboard(url); |
|
||||
const label = 'Copy url'; |
|
||||
return ( |
|
||||
<Box |
|
||||
as="span" |
|
||||
display={['none', 'none', 'block', 'block']} |
|
||||
onClick={e => { |
|
||||
onClick && onClick(e); |
|
||||
onCopy?.(); |
|
||||
}} |
|
||||
{...rest} |
|
||||
> |
|
||||
<Tooltip label={label} aria-label={label}> |
|
||||
<Link |
|
||||
opacity={0.5} |
|
||||
_hover={{ |
|
||||
opacity: 1, |
|
||||
}} |
|
||||
color={color('text-title')} |
|
||||
as="a" |
|
||||
href={link} |
|
||||
display="block" |
|
||||
ml={space('tight')} |
|
||||
> |
|
||||
<LinkIcon size="1rem" /> |
|
||||
</Link> |
|
||||
</Tooltip> |
|
||||
</Box> |
|
||||
); |
|
||||
}); |
|
||||
|
|
||||
// this is to adjust the offset of where the page scrolls to when an anchor is present
|
|
||||
const AnchorOffset = ({ id }: BoxProps) => |
|
||||
id ? ( |
|
||||
<Box |
|
||||
as="span" |
|
||||
display="block" |
|
||||
position="absolute" |
|
||||
style={{ userSelect: 'none', pointerEvents: 'none' }} |
|
||||
top={`-${HEADER_HEIGHT + 42}px`} |
|
||||
id={id} |
|
||||
/> |
|
||||
) : null; |
|
||||
|
|
||||
const Hashtag = () => ( |
|
||||
<Box position="absolute" as="span" left="10px" color={color('text-caption')}> |
|
||||
<HashtagIcon size="1rem" /> |
|
||||
</Box> |
|
||||
); |
|
||||
|
|
||||
export const Heading = ({ as, children, id, ...rest }: FlexProps) => { |
|
||||
const { isActive, doChangeActiveSlug } = useActiveHeading(id); |
|
||||
const [isHovered, bind] = useHover(); |
|
||||
const router = useRouter(); |
|
||||
|
|
||||
const link = `#${id}`; |
|
||||
|
|
||||
const handleLinkClick = () => { |
|
||||
void router.push(router.pathname, router.pathname + link, { shallow: true }); |
|
||||
doChangeActiveSlug(id); |
|
||||
}; |
|
||||
const styles = getHeadingStyles(as as any); |
|
||||
|
|
||||
return ( |
|
||||
<Title |
|
||||
as={as} |
|
||||
{...bind} |
|
||||
css={css({ |
|
||||
...baseTypeStyles, |
|
||||
...styles, |
|
||||
color: isActive ? color('accent') : (color('text-title') as any), |
|
||||
// @ts-ignore
|
|
||||
alignItems: 'center', |
|
||||
// @ts-ignore
|
|
||||
position: 'relative', |
|
||||
// @ts-ignore
|
|
||||
display: 'flex', |
|
||||
// @ts-ignore
|
|
||||
justifyContent: 'flex-start', |
|
||||
...rest, |
|
||||
})} |
|
||||
> |
|
||||
<Box as="span" display="inline-block"> |
|
||||
{children} |
|
||||
</Box> |
|
||||
<AnchorOffset id={id} /> |
|
||||
{id && isActive && <Hashtag />} |
|
||||
{id && <LinkButton opacity={isHovered ? 1 : 0} onClick={handleLinkClick} link={link} />} |
|
||||
</Title> |
|
||||
); |
|
||||
}; |
|
||||
|
|
||||
const BaseHeading: React.FC<BoxProps> = React.memo(props => ( |
|
||||
<Heading width="100%" mt={space('base-loose')} {...props} /> |
|
||||
)); |
|
||||
|
|
||||
export const H1: React.FC<BoxProps> = props => <BaseHeading as="h1" {...props} />; |
|
||||
export const H2: React.FC<BoxProps> = props => <BaseHeading as="h2" {...props} />; |
|
||||
export const H3: React.FC<BoxProps> = props => <BaseHeading as="h3" {...props} />; |
|
||||
export const H4: React.FC<BoxProps> = props => <BaseHeading as="h4" {...props} />; |
|
||||
export const H5: React.FC<BoxProps> = props => <BaseHeading as="h5" {...props} />; |
|
||||
export const H6: React.FC<BoxProps> = props => <BaseHeading as="h6" {...props} />; |
|
||||
|
|
||||
export const Br: React.FC<BoxProps> = props => <Box height="24px" {...props} />; |
|
||||
export const Hr: React.FC<BoxProps> = props => ( |
|
||||
<Box |
|
||||
as="hr" |
|
||||
borderTopWidth="1px" |
|
||||
borderColor={color('border')} |
|
||||
my={space('extra-loose')} |
|
||||
mx={space('extra-loose')} |
|
||||
{...props} |
|
||||
/> |
|
||||
); |
|
||||
|
|
||||
export const P: React.FC<BoxProps> = props => <Text as="p" {...props} />; |
|
||||
export const Ol: React.FC<BoxProps> = props => ( |
|
||||
<Box pl={space('base')} mt={space('base')} mb={space('base-tight')} as="ol" {...props} /> |
|
||||
); |
|
||||
export const Ul: React.FC<BoxProps> = props => ( |
|
||||
<Box pl={space('base-loose')} mt={space('base')} mb={space('base-tight')} as="ul" {...props} /> |
|
||||
); |
|
||||
export const Li: React.FC<BoxProps> = props => ( |
|
||||
<Box as="li" color={color('text-body')} pb={space('tight')} {...props} /> |
|
||||
); |
|
||||
|
|
||||
const getAlertStyles = (className: string) => { |
|
||||
if (className?.includes('alert-success')) { |
|
||||
return { |
|
||||
borderTopColor: themeColor('green'), |
|
||||
borderTopWidth: '2px', |
|
||||
borderTopRightRadius: '0px', |
|
||||
borderTopLeftRadius: '0px', |
|
||||
accent: themeColor('green'), |
|
||||
icon: CheckCircleIcon, |
|
||||
}; |
|
||||
} |
|
||||
if (className?.includes('alert-info')) { |
|
||||
return { |
|
||||
border: border(), |
|
||||
borderRadius: 'md', |
|
||||
boxShadow: 'mid', |
|
||||
accent: color('accent'), |
|
||||
icon: InfoCircleIcon, |
|
||||
}; |
|
||||
} |
|
||||
if (className?.includes('alert-warning')) { |
|
||||
return { |
|
||||
borderTopColor: '#F7AA00', |
|
||||
borderTopWidth: '2px', |
|
||||
borderTopRightRadius: '0px', |
|
||||
borderTopLeftRadius: '0px', |
|
||||
accent: '#F7AA00', |
|
||||
icon: AlertTriangleIcon, |
|
||||
}; |
|
||||
} |
|
||||
if (className?.includes('alert-danger')) { |
|
||||
return { |
|
||||
borderTopColor: themeColor('red'), |
|
||||
borderTopWidth: '2px', |
|
||||
borderTopRightRadius: '0px', |
|
||||
borderTopLeftRadius: '0px', |
|
||||
accent: themeColor('red'), |
|
||||
icon: AlertCircleIcon, |
|
||||
}; |
|
||||
} |
|
||||
return {}; |
|
||||
}; |
|
||||
|
|
||||
export const BlockQuote: React.FC<BoxProps> = ({ children, className, ...rest }) => { |
|
||||
const isAlert = className?.includes('alert'); |
|
||||
const { accent, icon: Icon, ...styles } = getAlertStyles(className); |
|
||||
return ( |
|
||||
<Box as="blockquote" display="block" my={space('extra-loose')} className={className} {...rest}> |
|
||||
<Box |
|
||||
border="1px solid" |
|
||||
css={css({ |
|
||||
position: 'relative', |
|
||||
display: 'grid', |
|
||||
placeItems: 'center', |
|
||||
gridTemplateColumns: Icon ? '22px 1fr' : '1fr', |
|
||||
alignItems: 'flex-start', |
|
||||
border: isAlert ? border() : border(), |
|
||||
bg: isAlert ? color('bg') : color('bg-alt'), |
|
||||
borderRadius: 'md', |
|
||||
boxShadow: isAlert ? 'mid' : 'unset', |
|
||||
py: space('base'), |
|
||||
px: space('base'), |
|
||||
'& p': { |
|
||||
flexGrow: 1, |
|
||||
pt: '4px', |
|
||||
}, |
|
||||
...styles, |
|
||||
})} |
|
||||
> |
|
||||
{Icon && ( |
|
||||
<Flex align="center" height="28x" flexShrink={0} color={accent} width="22px"> |
|
||||
<Box position="absolute" top="16px" size="22px"> |
|
||||
<Icon /> |
|
||||
</Box> |
|
||||
</Flex> |
|
||||
)} |
|
||||
<Box width="100%" pl={Icon && space('tight')} flexGrow={1}> |
|
||||
{children} |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
); |
|
||||
}; |
|
||||
|
|
||||
export const Sup: React.FC<any> = props => <Text as="sup" mr={space('extra-tight')} {...props} />; |
|
@ -0,0 +1,102 @@ |
|||||
|
import { Box, Flex, BoxProps, color, themeColor, space } from '@blockstack/ui'; |
||||
|
import React from 'react'; |
||||
|
|
||||
|
import { border } from '@common/utils'; |
||||
|
import { css } from '@styled-system/css'; |
||||
|
import { CheckCircleIcon } from '@components/icons/check-circle'; |
||||
|
import { AlertTriangleIcon } from '@components/icons/alert-triangle'; |
||||
|
import { AlertCircleIcon } from '@components/icons/alert-circle'; |
||||
|
import { InfoCircleIcon } from '@components/icons/info-circle'; |
||||
|
|
||||
|
const getAlertStyles = (className: string) => { |
||||
|
if (className?.includes('alert-success')) { |
||||
|
return { |
||||
|
borderTopColor: themeColor('green'), |
||||
|
borderTopWidth: '2px', |
||||
|
borderTopRightRadius: '0px', |
||||
|
borderTopLeftRadius: '0px', |
||||
|
accent: themeColor('green'), |
||||
|
icon: CheckCircleIcon, |
||||
|
}; |
||||
|
} |
||||
|
if (className?.includes('alert-info')) { |
||||
|
return { |
||||
|
border: border(), |
||||
|
borderRadius: 'md', |
||||
|
boxShadow: 'mid', |
||||
|
accent: color('accent'), |
||||
|
icon: InfoCircleIcon, |
||||
|
}; |
||||
|
} |
||||
|
if (className?.includes('alert-warning')) { |
||||
|
return { |
||||
|
borderTopColor: '#F7AA00', |
||||
|
borderTopWidth: '2px', |
||||
|
borderTopRightRadius: '0px', |
||||
|
borderTopLeftRadius: '0px', |
||||
|
accent: '#F7AA00', |
||||
|
icon: AlertTriangleIcon, |
||||
|
}; |
||||
|
} |
||||
|
if (className?.includes('alert-danger')) { |
||||
|
return { |
||||
|
borderTopColor: themeColor('red'), |
||||
|
borderTopWidth: '2px', |
||||
|
borderTopRightRadius: '0px', |
||||
|
borderTopLeftRadius: '0px', |
||||
|
accent: themeColor('red'), |
||||
|
icon: AlertCircleIcon, |
||||
|
}; |
||||
|
} |
||||
|
return {}; |
||||
|
}; |
||||
|
|
||||
|
export const Blockquote: React.FC<BoxProps> = React.memo( |
||||
|
React.forwardRef(({ children, className, ...rest }, ref) => { |
||||
|
const isAlert = className?.includes('alert'); |
||||
|
const { accent, icon: Icon, ...styles } = getAlertStyles(className); |
||||
|
return ( |
||||
|
<Box |
||||
|
as="blockquote" |
||||
|
display="block" |
||||
|
my={space('extra-loose')} |
||||
|
className={className} |
||||
|
ref={ref} |
||||
|
{...rest} |
||||
|
> |
||||
|
<Box |
||||
|
border="1px solid" |
||||
|
css={css({ |
||||
|
position: 'relative', |
||||
|
display: 'grid', |
||||
|
placeItems: 'center', |
||||
|
gridTemplateColumns: Icon ? '22px 1fr' : '1fr', |
||||
|
alignItems: 'flex-start', |
||||
|
border: isAlert ? border() : border(), |
||||
|
bg: isAlert ? color('bg') : color('bg-alt'), |
||||
|
borderRadius: 'md', |
||||
|
boxShadow: isAlert ? 'mid' : 'unset', |
||||
|
py: space('base'), |
||||
|
px: space('base'), |
||||
|
'& p': { |
||||
|
flexGrow: 1, |
||||
|
pt: '4px', |
||||
|
}, |
||||
|
...styles, |
||||
|
})} |
||||
|
> |
||||
|
{Icon && ( |
||||
|
<Flex align="center" height="28x" flexShrink={0} color={accent} width="22px"> |
||||
|
<Box position="absolute" top="16px" size="22px"> |
||||
|
<Icon /> |
||||
|
</Box> |
||||
|
</Flex> |
||||
|
)} |
||||
|
<Box width="100%" pl={Icon && space('tight')} flexGrow={1}> |
||||
|
{children} |
||||
|
</Box> |
||||
|
</Box> |
||||
|
</Box> |
||||
|
); |
||||
|
}) |
||||
|
); |
@ -0,0 +1,63 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import { Box, BoxProps, color, themeColor } from '@blockstack/ui'; |
||||
|
import { border } from '@common/utils'; |
||||
|
import { css } from '@styled-system/css'; |
||||
|
import { Text } from '@components/typography'; |
||||
|
|
||||
|
export const Code: React.FC<any> = React.memo( |
||||
|
React.forwardRef(({ children, ...rest }, ref) => { |
||||
|
return ( |
||||
|
<Box ref={ref as any} overflowX="auto"> |
||||
|
<Box |
||||
|
as="code" |
||||
|
css={css({ |
||||
|
width: '100%', |
||||
|
display: 'flex', |
||||
|
flexDirection: 'column', |
||||
|
minWidth: 'fit-content', |
||||
|
'.token-line': { |
||||
|
display: 'inline-block', |
||||
|
'&.token-line--highlighted': { |
||||
|
bg: 'rgba(255,255,255,0.05)', |
||||
|
'&::before': { |
||||
|
borderRightColor: themeColor('ink.600'), |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
})} |
||||
|
{...rest} |
||||
|
> |
||||
|
<Box height="16px" width="100%" /> |
||||
|
{children} |
||||
|
<Box height="16px" width="100%" /> |
||||
|
</Box> |
||||
|
</Box> |
||||
|
); |
||||
|
}) |
||||
|
); |
||||
|
|
||||
|
const preProps = { |
||||
|
display: 'inline-block', |
||||
|
border: border(), |
||||
|
borderRadius: '4px', |
||||
|
padding: '2px 6px', |
||||
|
boxShadow: '0 1px 2px rgba(0, 0, 0, 0.04)', |
||||
|
bg: color('bg'), |
||||
|
}; |
||||
|
|
||||
|
export const InlineCode: React.FC<BoxProps> = ({ children, ...rest }) => ( |
||||
|
<Text |
||||
|
as="code" |
||||
|
css={css({ |
||||
|
// @ts-ignore
|
||||
|
fontSize: '14px', |
||||
|
// @ts-ignore
|
||||
|
lineHeight: '20px', |
||||
|
...preProps, |
||||
|
...rest, |
||||
|
})} |
||||
|
> |
||||
|
{children} |
||||
|
</Text> |
||||
|
); |
@ -0,0 +1,117 @@ |
|||||
|
import { Box, FlexProps, BoxProps, color, useClipboard, space } from '@blockstack/ui'; |
||||
|
|
||||
|
import React from 'react'; |
||||
|
import LinkIcon from 'mdi-react/LinkVariantIcon'; |
||||
|
import HashtagIcon from 'mdi-react/HashtagIcon'; |
||||
|
import { useTouchable } from '@common/hooks/use-touchable'; |
||||
|
import { Tooltip } from '@components/tooltip'; |
||||
|
import { useActiveHeading } from '@common/hooks/use-active-heading'; |
||||
|
import { Title } from '@components/typography'; |
||||
|
import { css } from '@styled-system/css'; |
||||
|
import { getHeadingStyles, baseTypeStyles } from '@components/mdx/typography'; |
||||
|
import { useRouter } from 'next/router'; |
||||
|
import { HEADER_HEIGHT } from '@components/header'; |
||||
|
import { Link } from '@components/mdx/components/link'; |
||||
|
|
||||
|
const LinkButton = React.memo(({ link, onClick, ...rest }: BoxProps & { link: string }) => { |
||||
|
const url = |
||||
|
typeof document !== 'undefined' && document.location.origin + document.location.pathname + link; |
||||
|
|
||||
|
const { onCopy } = useClipboard(url); |
||||
|
const label = 'Copy url'; |
||||
|
return ( |
||||
|
<Box |
||||
|
as="span" |
||||
|
display={['none', 'none', 'block', 'block']} |
||||
|
onClick={e => { |
||||
|
onClick && onClick(e); |
||||
|
onCopy?.(); |
||||
|
}} |
||||
|
{...rest} |
||||
|
> |
||||
|
<Tooltip label={label} aria-label={label}> |
||||
|
<Link |
||||
|
opacity={0.5} |
||||
|
_hover={{ |
||||
|
opacity: 1, |
||||
|
}} |
||||
|
color={color('text-title')} |
||||
|
as="a" |
||||
|
href={link} |
||||
|
display="block" |
||||
|
ml={space('tight')} |
||||
|
> |
||||
|
<LinkIcon size="1rem" /> |
||||
|
</Link> |
||||
|
</Tooltip> |
||||
|
</Box> |
||||
|
); |
||||
|
}); |
||||
|
|
||||
|
// this is to adjust the offset of where the page scrolls to when an anchor is present
|
||||
|
const AnchorOffset = ({ id }: BoxProps) => |
||||
|
id ? ( |
||||
|
<Box |
||||
|
as="span" |
||||
|
display="block" |
||||
|
position="absolute" |
||||
|
style={{ userSelect: 'none', pointerEvents: 'none' }} |
||||
|
top={`-${HEADER_HEIGHT + 42}px`} |
||||
|
id={id} |
||||
|
/> |
||||
|
) : null; |
||||
|
|
||||
|
const Hashtag = () => ( |
||||
|
<Box position="absolute" as="span" left="10px" color={color('text-caption')}> |
||||
|
<HashtagIcon size="1rem" /> |
||||
|
</Box> |
||||
|
); |
||||
|
|
||||
|
export const Heading = ({ as, children, id, ...rest }: FlexProps) => { |
||||
|
const { isActive, doChangeActiveSlug } = useActiveHeading(id); |
||||
|
|
||||
|
const { bind, hover, active } = useTouchable({ |
||||
|
behavior: 'link', |
||||
|
}); |
||||
|
const router = useRouter(); |
||||
|
|
||||
|
const link = `#${id}`; |
||||
|
|
||||
|
const handleLinkClick = () => { |
||||
|
void router.push(router.pathname, router.pathname + link, { shallow: true }); |
||||
|
doChangeActiveSlug(id); |
||||
|
}; |
||||
|
const styles = getHeadingStyles(as as any); |
||||
|
|
||||
|
return ( |
||||
|
<Title |
||||
|
as={as} |
||||
|
{...bind} |
||||
|
css={css({ |
||||
|
...baseTypeStyles, |
||||
|
...styles, |
||||
|
color: isActive ? color('accent') : (color('text-title') as any), |
||||
|
// @ts-ignore
|
||||
|
alignItems: 'center', |
||||
|
// @ts-ignore
|
||||
|
position: 'relative', |
||||
|
// @ts-ignore
|
||||
|
display: 'flex', |
||||
|
// @ts-ignore
|
||||
|
justifyContent: 'flex-start', |
||||
|
...rest, |
||||
|
})} |
||||
|
> |
||||
|
<Box as="span" display="inline-block"> |
||||
|
{children} |
||||
|
</Box> |
||||
|
<AnchorOffset id={id} /> |
||||
|
{id && isActive && <Hashtag />} |
||||
|
{id && <LinkButton opacity={hover || active ? 1 : 0} onClick={handleLinkClick} link={link} />} |
||||
|
</Title> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export const BaseHeading: React.FC<BoxProps> = React.memo(props => ( |
||||
|
<Heading width="100%" mt={space('base-loose')} {...props} /> |
||||
|
)); |
@ -0,0 +1,7 @@ |
|||||
|
export * from './blockquote'; |
||||
|
export * from './code'; |
||||
|
export * from './heading'; |
||||
|
export * from './link'; |
||||
|
export * from './list'; |
||||
|
export * from './table'; |
||||
|
export * from './typography'; |
@ -0,0 +1,31 @@ |
|||||
|
import { Box, BoxProps } from '@blockstack/ui'; |
||||
|
import NextLink from 'next/link'; |
||||
|
import React, { forwardRef, Ref } from 'react'; |
||||
|
|
||||
|
export const SmartLink = ({ href, ...rest }: { href: string }) => { |
||||
|
const isExternal = href.includes('http') || href.includes('mailto'); |
||||
|
const link = <Link href={href} {...rest} />; |
||||
|
|
||||
|
return isExternal ? ( |
||||
|
link |
||||
|
) : ( |
||||
|
<NextLink href={href} passHref> |
||||
|
{link} |
||||
|
</NextLink> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export const Link = forwardRef( |
||||
|
(props: { href: string; target?: string; rel?: string } & BoxProps, ref: Ref<HTMLDivElement>) => ( |
||||
|
<Box |
||||
|
as="a" |
||||
|
ref={ref} |
||||
|
color="var(--colors-accent)" |
||||
|
cursor="pointer" |
||||
|
textDecoration="underline" |
||||
|
_hover={{ textDecoration: 'none' }} |
||||
|
_focus={{ boxShadow: 'outline' }} |
||||
|
{...props} |
||||
|
/> |
||||
|
) |
||||
|
); |
@ -0,0 +1,14 @@ |
|||||
|
import { Box, BoxProps, color, space } from '@blockstack/ui'; |
||||
|
import React, { forwardRef, Ref } from 'react'; |
||||
|
|
||||
|
export const Ol: React.FC<BoxProps> = props => ( |
||||
|
<Box pl={space('base')} mt={space('base')} mb={space('base-tight')} as="ol" {...props} /> |
||||
|
); |
||||
|
|
||||
|
export const Ul: React.FC<BoxProps> = props => ( |
||||
|
<Box pl={space('base-loose')} mt={space('base')} mb={space('base-tight')} as="ul" {...props} /> |
||||
|
); |
||||
|
|
||||
|
export const Li: React.FC<BoxProps> = props => ( |
||||
|
<Box as="li" color={color('text-body')} pb={space('tight')} {...props} /> |
||||
|
); |
@ -0,0 +1,48 @@ |
|||||
|
import { Box, color, space } from '@blockstack/ui'; |
||||
|
import React from 'react'; |
||||
|
import { P } from '@components/mdx/components'; |
||||
|
import { border } from '@common/utils'; |
||||
|
|
||||
|
export const Table = ({ children, ...rest }: any) => ( |
||||
|
<Box my={space('extra-loose')} maxWidth="100%" {...rest}> |
||||
|
<Box borderRadius={[0, 0, '12px']} border={border()} overflow="hidden"> |
||||
|
<Box overflowX="auto"> |
||||
|
<Box color={color('text-body')} textAlign="left" width="100%" as="table" maxWidth="100%"> |
||||
|
{children} |
||||
|
</Box> |
||||
|
</Box> |
||||
|
</Box> |
||||
|
</Box> |
||||
|
); |
||||
|
|
||||
|
export const THead = (props: any) => { |
||||
|
return ( |
||||
|
<Box |
||||
|
as="th" |
||||
|
color="var(--colors-text-caption)" |
||||
|
borderRight={border()} |
||||
|
bg={color('bg-alt')} |
||||
|
fontSize="12px" |
||||
|
px={space('base-tight')} |
||||
|
pt={space('tight')} |
||||
|
pb={space('extra-tight')} |
||||
|
{...props} |
||||
|
/> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export const TData = (props: any) => ( |
||||
|
<Box |
||||
|
as="td" |
||||
|
fontSize="14px" |
||||
|
p={space('tight')} |
||||
|
px={space('base-tight')} |
||||
|
pt={space('base-tight')} |
||||
|
borderRight={border()} |
||||
|
borderTop={border()} |
||||
|
color={color('text-body')} |
||||
|
whiteSpace="normal" |
||||
|
> |
||||
|
<P {...props} /> |
||||
|
</Box> |
||||
|
); |
@ -0,0 +1,28 @@ |
|||||
|
import { Box, BoxProps, color, space } from '@blockstack/ui'; |
||||
|
import React from 'react'; |
||||
|
import { Text } from '@components/typography'; |
||||
|
import { BaseHeading } from '@components/mdx/components/heading'; |
||||
|
|
||||
|
export const H1: React.FC<BoxProps> = props => <BaseHeading as="h1" {...props} />; |
||||
|
export const H2: React.FC<BoxProps> = props => <BaseHeading as="h2" {...props} />; |
||||
|
export const H3: React.FC<BoxProps> = props => <BaseHeading as="h3" {...props} />; |
||||
|
export const H4: React.FC<BoxProps> = props => <BaseHeading as="h4" {...props} />; |
||||
|
export const H5: React.FC<BoxProps> = props => <BaseHeading as="h5" {...props} />; |
||||
|
export const H6: React.FC<BoxProps> = props => <BaseHeading as="h6" {...props} />; |
||||
|
|
||||
|
export const Br: React.FC<BoxProps> = props => <Box height="24px" {...props} />; |
||||
|
export const Hr: React.FC<BoxProps> = props => ( |
||||
|
<Box |
||||
|
as="hr" |
||||
|
borderTopWidth="1px" |
||||
|
borderColor={color('border')} |
||||
|
my={space('extra-loose')} |
||||
|
mx={space('extra-loose')} |
||||
|
{...props} |
||||
|
/> |
||||
|
); |
||||
|
|
||||
|
export const P: React.FC<BoxProps> = props => <Text as="p" {...props} />; |
||||
|
|
||||
|
export const Pre = (props: any) => <Text as="pre" {...props} />; |
||||
|
export const Sup: React.FC<any> = props => <Text as="sup" mr={space('extra-tight')} {...props} />; |
Loading…
Reference in new issue