@startupjs-ui/mdx 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +35 -0
- package/client/Code/index.js +144 -0
- package/client/Code/index.styl +96 -0
- package/client/mdxComponents/index.js +320 -0
- package/client/mdxComponents/index.styl +115 -0
- package/index.js +15 -0
- package/package.json +34 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
+
|
|
6
|
+
## [0.1.3](https://github.com/startupjs/startupjs-ui/compare/v0.1.2...v0.1.3) (2025-12-29)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @startupjs-ui/mdx
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## [0.1.2](https://github.com/startupjs/startupjs-ui/compare/v0.1.1...v0.1.2) (2025-12-29)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* add mdx and docs packages. Refactor docs to get rid of any @startupjs/ui usage and use startupjs-ui instead ([703c926](https://github.com/startupjs/startupjs-ui/commit/703c92636efb0421ffd11783f692fc892b74018f))
|
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# MDX components
|
|
2
|
+
|
|
3
|
+
> Components to use during .mdx compilation
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
yarn @startupjs-ui/mdx @react-native-clipboard/clipboard
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
This will be used automatically by `@startupjs/bundler` to compile `.mdx` and `.md` files.
|
|
14
|
+
|
|
15
|
+
## Custom components
|
|
16
|
+
|
|
17
|
+
You can override the default MDX components by setting the custom ones
|
|
18
|
+
using the `overrideMDXComponents` function. Do it once as early as possible
|
|
19
|
+
(for example in the topmost `_layout` file of your Expo project):
|
|
20
|
+
|
|
21
|
+
```jsx
|
|
22
|
+
// _layout.js
|
|
23
|
+
import { overrideMDXComponents } from '@startupjs/mdx'
|
|
24
|
+
|
|
25
|
+
overrideMDXComponents({
|
|
26
|
+
h1: ({ children }) => <Text style={{ fontSize: 40 }}>{children}</Text>,
|
|
27
|
+
p: ({ children }) => <Text style={{ fontSize: 14 }}>{children}</Text>
|
|
28
|
+
})
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
For the full list of components check [here](https://mdxjs.com/table-of-components)
|
|
32
|
+
|
|
33
|
+
## License
|
|
34
|
+
|
|
35
|
+
MIT
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import React, { useMemo, memo } from 'react'
|
|
2
|
+
import { pug } from 'startupjs'
|
|
3
|
+
import Div from '@startupjs-ui/div'
|
|
4
|
+
import Span from '@startupjs-ui/span'
|
|
5
|
+
import ScrollView from '@startupjs-ui/scroll-view'
|
|
6
|
+
import refractor from 'refractor/core.js'
|
|
7
|
+
// Supported languages
|
|
8
|
+
import languageJsx from 'refractor/lang/jsx.js'
|
|
9
|
+
import languageStyl from 'refractor/lang/stylus.js'
|
|
10
|
+
import languagePug from 'refractor/lang/pug.js'
|
|
11
|
+
import languageMarkdown from 'refractor/lang/markdown.js'
|
|
12
|
+
import languageJson from 'refractor/lang/json.js'
|
|
13
|
+
import languageBash from 'refractor/lang/bash.js'
|
|
14
|
+
import './index.styl'
|
|
15
|
+
|
|
16
|
+
const SUB_LANGUAGE_REGEX = /(^|\W)(pug|styl|css)(`\s*\n)([^`]*\s*\n)(`)/
|
|
17
|
+
|
|
18
|
+
// Register all supported languages
|
|
19
|
+
refractor.register(languageJsx)
|
|
20
|
+
refractor.register(languageStyl)
|
|
21
|
+
refractor.register(languagePug)
|
|
22
|
+
refractor.register(languageMarkdown)
|
|
23
|
+
refractor.register(languageJson)
|
|
24
|
+
refractor.register(languageBash)
|
|
25
|
+
|
|
26
|
+
// Register aliases
|
|
27
|
+
refractor.alias({ stylus: ['styl'] })
|
|
28
|
+
refractor.alias({ bash: ['sh'] })
|
|
29
|
+
|
|
30
|
+
// This method mutates highlighted array to remove the last template
|
|
31
|
+
// backtick symbol and also returns it
|
|
32
|
+
function modifyAndGetLastBacktick (highlighted) {
|
|
33
|
+
if (!(highlighted && highlighted.length)) return []
|
|
34
|
+
const lastLine = highlighted[highlighted.length - 1]
|
|
35
|
+
const lastSymbol = lastLine.children[lastLine.children.length - 1]
|
|
36
|
+
|
|
37
|
+
// check close backtick
|
|
38
|
+
if (lastSymbol?.value !== '`') {
|
|
39
|
+
throw new Error(`
|
|
40
|
+
[@startupjs/mdx] Last symbol is not a backtick.
|
|
41
|
+
This should never happen.
|
|
42
|
+
Maybe refractor got updated or <Code> component is broken.
|
|
43
|
+
`)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// remove last line with backtick
|
|
47
|
+
highlighted.pop()
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
type: 'element',
|
|
51
|
+
tagName: 'span',
|
|
52
|
+
properties: { className: ['line'] },
|
|
53
|
+
children: [{ type: 'text', value: '`' }]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getLines (code, language) {
|
|
58
|
+
const lines = code.split('\n')
|
|
59
|
+
if (lines[lines.length - 1] === '') lines.pop()
|
|
60
|
+
|
|
61
|
+
return lines.reduce((acc, line) => {
|
|
62
|
+
const children = refractor.highlight(line, language)
|
|
63
|
+
const className = ['line']
|
|
64
|
+
const node = {
|
|
65
|
+
type: 'element',
|
|
66
|
+
tagName: 'span',
|
|
67
|
+
properties: { className },
|
|
68
|
+
children
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
acc.push(node)
|
|
72
|
+
return acc
|
|
73
|
+
}, [])
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function highlight (code, language) {
|
|
77
|
+
if (language === 'jsx') {
|
|
78
|
+
const match = code.match(SUB_LANGUAGE_REGEX)
|
|
79
|
+
if (match) {
|
|
80
|
+
const splitIndex = match.index + match[0].length
|
|
81
|
+
const start = code.slice(0, splitIndex)
|
|
82
|
+
const next = code.slice(splitIndex + 1)
|
|
83
|
+
|
|
84
|
+
const jsx = start.replace(SUB_LANGUAGE_REGEX, '$1$2$3$5')
|
|
85
|
+
const highlightedJsx = getLines(jsx, 'jsx')
|
|
86
|
+
|
|
87
|
+
const subLanguageName = match[2]
|
|
88
|
+
const bodySubLanguage = match[4]
|
|
89
|
+
const closingBacktick = modifyAndGetLastBacktick(highlightedJsx)
|
|
90
|
+
|
|
91
|
+
const merge = [
|
|
92
|
+
...highlightedJsx, // without trailing ` sign
|
|
93
|
+
...highlight(bodySubLanguage, subLanguageName),
|
|
94
|
+
closingBacktick // the trailing ` sign
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
if (next) merge.push(...highlight(next, 'jsx'))
|
|
98
|
+
return merge
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return getLines(code, language)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export default memo(function Code ({
|
|
105
|
+
children = '',
|
|
106
|
+
language = 'txt',
|
|
107
|
+
style,
|
|
108
|
+
textStyle,
|
|
109
|
+
...props
|
|
110
|
+
}) {
|
|
111
|
+
if (typeof children !== 'string') throw Error('[Code] Code must be a string')
|
|
112
|
+
const code = useMemo(() => {
|
|
113
|
+
if (!language) {
|
|
114
|
+
return pug`
|
|
115
|
+
Span= children
|
|
116
|
+
`
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return renderer(highlight(children, language), textStyle)
|
|
120
|
+
}, [children, language]) // eslint-disable-line react-hooks/exhaustive-deps
|
|
121
|
+
|
|
122
|
+
return pug`
|
|
123
|
+
ScrollView.root(
|
|
124
|
+
...props
|
|
125
|
+
horizontal
|
|
126
|
+
style=style
|
|
127
|
+
)
|
|
128
|
+
Div= code
|
|
129
|
+
`
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
function renderer (tree, style) {
|
|
133
|
+
if (!(tree && Array.isArray(tree))) return null
|
|
134
|
+
return tree.map((child, index) => {
|
|
135
|
+
if (child.type === 'text') {
|
|
136
|
+
return child.value
|
|
137
|
+
} else {
|
|
138
|
+
const className = (child.properties && child.properties.className) || []
|
|
139
|
+
return pug`
|
|
140
|
+
Span(key=index style=style styleName=className)= renderer(child.children)
|
|
141
|
+
`
|
|
142
|
+
}
|
|
143
|
+
})
|
|
144
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
$bg = var(--color-bg-main)
|
|
2
|
+
|
|
3
|
+
monospace()
|
|
4
|
+
font-family monospace
|
|
5
|
+
+ios()
|
|
6
|
+
font-family Menlo-Regular
|
|
7
|
+
|
|
8
|
+
.root
|
|
9
|
+
padding 2u
|
|
10
|
+
background-color $bg
|
|
11
|
+
border-radius 1u
|
|
12
|
+
|
|
13
|
+
.line
|
|
14
|
+
min-height 2.5u
|
|
15
|
+
monospace()
|
|
16
|
+
+web()
|
|
17
|
+
white-space pre
|
|
18
|
+
|
|
19
|
+
.token
|
|
20
|
+
monospace()
|
|
21
|
+
|
|
22
|
+
// default Prism theme
|
|
23
|
+
// ref: https://github.com/PrismJS/prism/blob/master/themes/prism.css
|
|
24
|
+
|
|
25
|
+
.token.comment,
|
|
26
|
+
.token.prolog,
|
|
27
|
+
.token.doctype,
|
|
28
|
+
.token.cdata {
|
|
29
|
+
color: slategray;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.token.punctuation {
|
|
33
|
+
color: #999;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.token.namespace {
|
|
37
|
+
opacity: .7;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.token.property,
|
|
41
|
+
.token.tag,
|
|
42
|
+
.token.boolean,
|
|
43
|
+
.token.number,
|
|
44
|
+
.token.constant,
|
|
45
|
+
.token.symbol,
|
|
46
|
+
.token.deleted {
|
|
47
|
+
color: #905;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.token.selector,
|
|
51
|
+
.token.attr-name,
|
|
52
|
+
.token.string,
|
|
53
|
+
.token.char,
|
|
54
|
+
.token.builtin,
|
|
55
|
+
.token.inserted {
|
|
56
|
+
color: #690;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.token.operator,
|
|
60
|
+
.token.entity,
|
|
61
|
+
.token.url,
|
|
62
|
+
.language-css .token.string,
|
|
63
|
+
.style .token.string {
|
|
64
|
+
color: #9a6e3a;
|
|
65
|
+
/* This background color was intended by the author of this theme. */
|
|
66
|
+
background: hsla(0, 0%, 100%, .5);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.token.atrule,
|
|
70
|
+
.token.attr-value,
|
|
71
|
+
.token.keyword {
|
|
72
|
+
color: #07a;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.token.function,
|
|
76
|
+
.token.class-name {
|
|
77
|
+
color: #DD4A68;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.token.regex,
|
|
81
|
+
.token.important,
|
|
82
|
+
.token.variable {
|
|
83
|
+
color: #e90;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.token.important,
|
|
87
|
+
.token.bold {
|
|
88
|
+
font-weight: bold;
|
|
89
|
+
}
|
|
90
|
+
.token.italic {
|
|
91
|
+
font-style: italic;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.token.entity {
|
|
95
|
+
cursor: help;
|
|
96
|
+
}
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import React, { useState, useContext } from 'react'
|
|
2
|
+
import { Image, ScrollView, Platform } from 'react-native'
|
|
3
|
+
import Clipboard from '@react-native-clipboard/clipboard'
|
|
4
|
+
import { pug, observer, $, BASE_URL } from 'startupjs'
|
|
5
|
+
import Alert from '@startupjs-ui/alert'
|
|
6
|
+
import Div from '@startupjs-ui/div'
|
|
7
|
+
import Span from '@startupjs-ui/span'
|
|
8
|
+
import Divider from '@startupjs-ui/divider'
|
|
9
|
+
import Br from '@startupjs-ui/br'
|
|
10
|
+
import Link from '@startupjs-ui/link'
|
|
11
|
+
import Icon from '@startupjs-ui/icon'
|
|
12
|
+
import { Table, Tbody, Td, Th, Thead, Tr } from '@startupjs-ui/table'
|
|
13
|
+
import Collapse from '@startupjs-ui/collapse'
|
|
14
|
+
// import { Anchor, scrollTo } from '@startupjs/scrollable-anchors'
|
|
15
|
+
// import { faLink } from '@fortawesome/free-solid-svg-icons/faLink'
|
|
16
|
+
import { faCode } from '@fortawesome/free-solid-svg-icons/faCode'
|
|
17
|
+
import { faCopy } from '@fortawesome/free-solid-svg-icons/faCopy'
|
|
18
|
+
// import _kebabCase from 'lodash/kebabCase'
|
|
19
|
+
// import _get from 'lodash/get'
|
|
20
|
+
import './index.styl'
|
|
21
|
+
import Code from '../Code'
|
|
22
|
+
|
|
23
|
+
// const RowComponent = props => pug`Div(...props row)`
|
|
24
|
+
const ALPHABET = 'abcdefghigklmnopqrstuvwxyz'
|
|
25
|
+
const ListLevelContext = React.createContext()
|
|
26
|
+
const BlockquoteContext = React.createContext()
|
|
27
|
+
const PreContext = React.createContext()
|
|
28
|
+
|
|
29
|
+
function getOrderedListMark (index, level) {
|
|
30
|
+
switch (level) {
|
|
31
|
+
case 1:
|
|
32
|
+
return ALPHABET.charAt(index % ALPHABET.length) + ')'
|
|
33
|
+
default:
|
|
34
|
+
return '' + (index + 1) + '.'
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function P (props) {
|
|
39
|
+
return pug`
|
|
40
|
+
Span.p(style=props.style ...props)
|
|
41
|
+
`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// function getTextChildren (children) {
|
|
45
|
+
// const nestedChildren = _get(children, 'props.children')
|
|
46
|
+
// if (nestedChildren) {
|
|
47
|
+
// return getTextChildren(nestedChildren)
|
|
48
|
+
// }
|
|
49
|
+
|
|
50
|
+
// return children
|
|
51
|
+
// }
|
|
52
|
+
|
|
53
|
+
// function MDXAnchor ({
|
|
54
|
+
// children,
|
|
55
|
+
// style,
|
|
56
|
+
// anchor,
|
|
57
|
+
// size
|
|
58
|
+
// }) {
|
|
59
|
+
// const [hover, setHover] = useState()
|
|
60
|
+
// const anchorKebab = _kebabCase(anchor)
|
|
61
|
+
|
|
62
|
+
// return pug`
|
|
63
|
+
// Anchor.anchor(
|
|
64
|
+
// style=style
|
|
65
|
+
// id=anchorKebab
|
|
66
|
+
// Component=RowComponent
|
|
67
|
+
// vAlign='center'
|
|
68
|
+
// onMouseEnter=() => setHover(true)
|
|
69
|
+
// onMouseLeave=() => setHover()
|
|
70
|
+
// )
|
|
71
|
+
// = children
|
|
72
|
+
// Link.anchor-link(
|
|
73
|
+
// styleName={ hover }
|
|
74
|
+
// to='#' + anchorKebab
|
|
75
|
+
// )
|
|
76
|
+
// Icon(icon=faLink size=size)
|
|
77
|
+
// `
|
|
78
|
+
// }
|
|
79
|
+
|
|
80
|
+
export default {
|
|
81
|
+
wrapper: ({ children }) => pug`
|
|
82
|
+
Div= children
|
|
83
|
+
`,
|
|
84
|
+
// TODO: MDXAnchor(anchor=getTextChildren(children) size='xl')
|
|
85
|
+
h1: ({ children }) => pug`
|
|
86
|
+
Span(h2 bold)
|
|
87
|
+
= children
|
|
88
|
+
`,
|
|
89
|
+
// TODO: MDXAnchor.h2(anchor=getTextChildren(children))
|
|
90
|
+
h2: ({ children }) => pug`
|
|
91
|
+
Span.h2.h2-text(h5)= children
|
|
92
|
+
Div.divider
|
|
93
|
+
`,
|
|
94
|
+
// TODO: MDXAnchor.h3(anchor=getTextChildren(children) size='s')
|
|
95
|
+
h3: ({ children }) => pug`
|
|
96
|
+
Span.h3.h3-text(h6 bold)= children
|
|
97
|
+
`,
|
|
98
|
+
h4: P,
|
|
99
|
+
h5: P,
|
|
100
|
+
h6: P,
|
|
101
|
+
p: ({ children }) => {
|
|
102
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
103
|
+
const inBlockquote = useContext(BlockquoteContext)
|
|
104
|
+
|
|
105
|
+
if (inBlockquote) {
|
|
106
|
+
return pug`
|
|
107
|
+
Span.blockquoteP= children
|
|
108
|
+
`
|
|
109
|
+
}
|
|
110
|
+
return pug`
|
|
111
|
+
P= children
|
|
112
|
+
`
|
|
113
|
+
},
|
|
114
|
+
strong: ({ children }) => pug`
|
|
115
|
+
P(bold)= children
|
|
116
|
+
`,
|
|
117
|
+
em: ({ children }) => pug`
|
|
118
|
+
P(italic)= children
|
|
119
|
+
`,
|
|
120
|
+
hr: ({ children }) => pug`
|
|
121
|
+
Divider(size='l')
|
|
122
|
+
`,
|
|
123
|
+
center: ({ children }) => {
|
|
124
|
+
return pug`
|
|
125
|
+
P.center= children
|
|
126
|
+
`
|
|
127
|
+
},
|
|
128
|
+
br: Br,
|
|
129
|
+
thematicBreak: P,
|
|
130
|
+
table: ({ children }) => {
|
|
131
|
+
return pug`
|
|
132
|
+
Table(style={ marginTop: 16 })= children
|
|
133
|
+
`
|
|
134
|
+
},
|
|
135
|
+
thead: Thead,
|
|
136
|
+
tbody: Tbody,
|
|
137
|
+
tr: Tr,
|
|
138
|
+
td: Td,
|
|
139
|
+
th: Th,
|
|
140
|
+
delete: P,
|
|
141
|
+
a: ({ children, href }) => {
|
|
142
|
+
// function onPress (event) {
|
|
143
|
+
// const { url, hash } = $root.get('$render')
|
|
144
|
+
// const [_url, _hash] = href.split('#')
|
|
145
|
+
// if (url === _url && hash === `#${_hash}`) {
|
|
146
|
+
// event.preventDefault()
|
|
147
|
+
// // scrollTo({ anchorId: _hash })
|
|
148
|
+
// }
|
|
149
|
+
// }
|
|
150
|
+
|
|
151
|
+
// TODO: handle Anchor click with onPress
|
|
152
|
+
return pug`
|
|
153
|
+
Link.link(
|
|
154
|
+
to=href
|
|
155
|
+
size='l'
|
|
156
|
+
color='primary'
|
|
157
|
+
)= children
|
|
158
|
+
`
|
|
159
|
+
},
|
|
160
|
+
ul: ({ children }) => children,
|
|
161
|
+
ol: ({ children }) => {
|
|
162
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
163
|
+
const currentLevel = useContext(ListLevelContext)
|
|
164
|
+
const nextLevel = currentLevel == null ? 0 : currentLevel + 1
|
|
165
|
+
const filteredChildren = React.Children
|
|
166
|
+
.toArray(children)
|
|
167
|
+
.filter(child => child !== '\n')
|
|
168
|
+
.map((child, index) => React.cloneElement(child, { index }))
|
|
169
|
+
return pug`
|
|
170
|
+
ListLevelContext.Provider(value=nextLevel)
|
|
171
|
+
Div
|
|
172
|
+
= filteredChildren
|
|
173
|
+
`
|
|
174
|
+
},
|
|
175
|
+
li: ({ children, index }) => {
|
|
176
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
177
|
+
const level = useContext(ListLevelContext)
|
|
178
|
+
const listIndex = index == null ? '•' : getOrderedListMark(index, level)
|
|
179
|
+
let hasTextChild = false
|
|
180
|
+
children = React.Children
|
|
181
|
+
.toArray(children)
|
|
182
|
+
.filter(child => child !== '\n')
|
|
183
|
+
.map(child => {
|
|
184
|
+
if (typeof child === 'string') {
|
|
185
|
+
hasTextChild = true
|
|
186
|
+
}
|
|
187
|
+
return child
|
|
188
|
+
})
|
|
189
|
+
return pug`
|
|
190
|
+
Div(row)
|
|
191
|
+
Span.listIndex= listIndex
|
|
192
|
+
Div.listContent
|
|
193
|
+
if hasTextChild
|
|
194
|
+
P(size='l')= children
|
|
195
|
+
else
|
|
196
|
+
= children
|
|
197
|
+
`
|
|
198
|
+
},
|
|
199
|
+
blockquote: ({ children }) => {
|
|
200
|
+
const filteredChildren = React.Children
|
|
201
|
+
.toArray(children)
|
|
202
|
+
.filter(child => child !== '\n')
|
|
203
|
+
|
|
204
|
+
return pug`
|
|
205
|
+
BlockquoteContext.Provider(value=true)
|
|
206
|
+
Alert.alert= filteredChildren
|
|
207
|
+
`
|
|
208
|
+
},
|
|
209
|
+
img: ({ src }) => {
|
|
210
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
211
|
+
const [style, setStyle] = useState({})
|
|
212
|
+
const isUrl = /^(http|https):\/\//.test(src)
|
|
213
|
+
const isLocalUrl = /^\//.test(src)
|
|
214
|
+
|
|
215
|
+
if (isLocalUrl) {
|
|
216
|
+
src = BASE_URL + src
|
|
217
|
+
} else if (!isUrl) {
|
|
218
|
+
console.warn('[@startupjs/mdx] Need to provide the url for the image')
|
|
219
|
+
return null
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function onLayout (e) {
|
|
223
|
+
const maxWidth = e.nativeEvent.layout.width
|
|
224
|
+
Image.getSize(src, (width, height) => {
|
|
225
|
+
const coefficient = maxWidth / width
|
|
226
|
+
setStyle({
|
|
227
|
+
width: Math.min(width, maxWidth),
|
|
228
|
+
height: coefficient < 1 ? Math.ceil(height * coefficient) : height
|
|
229
|
+
})
|
|
230
|
+
},
|
|
231
|
+
error => console.warn(`[@startupjs/mdx], ${error}`)
|
|
232
|
+
)
|
|
233
|
+
}
|
|
234
|
+
return pug`
|
|
235
|
+
Div.img(onLayout=onLayout)
|
|
236
|
+
Image(style=style source={ uri: src })
|
|
237
|
+
`
|
|
238
|
+
},
|
|
239
|
+
section: ({ children, noscroll }) => {
|
|
240
|
+
const Wrapper = noscroll
|
|
241
|
+
? ({ children }) => pug`
|
|
242
|
+
Div.example.padding= children
|
|
243
|
+
`
|
|
244
|
+
: ({ children }) => pug`
|
|
245
|
+
ScrollView.example(
|
|
246
|
+
contentContainerStyleName=['exampleContent', 'padding']
|
|
247
|
+
horizontal
|
|
248
|
+
)= children
|
|
249
|
+
`
|
|
250
|
+
|
|
251
|
+
return pug`
|
|
252
|
+
Wrapper= children
|
|
253
|
+
`
|
|
254
|
+
},
|
|
255
|
+
pre: ({ children }) => {
|
|
256
|
+
return pug`
|
|
257
|
+
PreContext.Provider(value=true)
|
|
258
|
+
= children
|
|
259
|
+
`
|
|
260
|
+
},
|
|
261
|
+
code: observer(({ children, className, ...props }) => {
|
|
262
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
263
|
+
const isBlockCode = useContext(PreContext)
|
|
264
|
+
|
|
265
|
+
if (!isBlockCode) {
|
|
266
|
+
return pug`
|
|
267
|
+
Span.inlineCodeWrapper
|
|
268
|
+
Span.inlineCodeSpacer  
|
|
269
|
+
Span.inlineCode(style={
|
|
270
|
+
fontFamily: Platform.OS === 'ios' ? 'Menlo-Regular' : 'monospace'
|
|
271
|
+
})= children
|
|
272
|
+
Span.inlineCodeSpacer  
|
|
273
|
+
`
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const language = (className || 'language-txt').replace(/language-/, '')
|
|
277
|
+
const [open, setOpen] = useState(false)
|
|
278
|
+
const $copyText = $('Copy code')
|
|
279
|
+
|
|
280
|
+
function copyHandler () {
|
|
281
|
+
Clipboard.setString(children)
|
|
282
|
+
$copyText.set('Copied')
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function onMouseEnter () {
|
|
286
|
+
// we need to reutrn default text if it was copied
|
|
287
|
+
$copyText.set('Copy code')
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
let example
|
|
291
|
+
|
|
292
|
+
if (typeof children === 'string' && children.includes('[HACK EXAMPLE CODE]')) {
|
|
293
|
+
children = children.replace('[HACK EXAMPLE CODE]', '')
|
|
294
|
+
example = true
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return pug`
|
|
298
|
+
Div.code(styleName={ 'code-example': example })
|
|
299
|
+
if example
|
|
300
|
+
Collapse.code-collapse(open=open variant='pure')
|
|
301
|
+
Collapse.Header.code-collapse-header(icon=false onPress=null)
|
|
302
|
+
Div.code-actions(align='right' row)
|
|
303
|
+
Div.code-action(
|
|
304
|
+
tooltip=open ? 'Hide code' : 'Show code'
|
|
305
|
+
onPress=()=> setOpen(!open)
|
|
306
|
+
)
|
|
307
|
+
Icon.code-action-collapse(icon=faCode color='error')
|
|
308
|
+
Div.code-action(
|
|
309
|
+
tooltip=$copyText.get()
|
|
310
|
+
onPress=copyHandler
|
|
311
|
+
onMouseEnter=onMouseEnter
|
|
312
|
+
)
|
|
313
|
+
Icon.code-action-copy(icon=faCopy)
|
|
314
|
+
Collapse.Content.code-collapse-content
|
|
315
|
+
Code(language=language)= children
|
|
316
|
+
else
|
|
317
|
+
Code(language=language)= children
|
|
318
|
+
`
|
|
319
|
+
})
|
|
320
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
$codeBg = var(--color-bg-main)
|
|
2
|
+
$exampleBg = var(--color-bg-main)
|
|
3
|
+
|
|
4
|
+
.p
|
|
5
|
+
.blockquoteP
|
|
6
|
+
.link
|
|
7
|
+
.listIndex
|
|
8
|
+
.inlineCode
|
|
9
|
+
.inlineCodeSpacer
|
|
10
|
+
font(body1)
|
|
11
|
+
|
|
12
|
+
.h2
|
|
13
|
+
margin-top 8u
|
|
14
|
+
|
|
15
|
+
&-text
|
|
16
|
+
fontFamily('heading', 600)
|
|
17
|
+
|
|
18
|
+
.h3
|
|
19
|
+
margin-top 6u
|
|
20
|
+
|
|
21
|
+
&-text
|
|
22
|
+
fontFamily('heading', 600)
|
|
23
|
+
|
|
24
|
+
.anchor
|
|
25
|
+
align-self flex-start
|
|
26
|
+
word-break break-word
|
|
27
|
+
|
|
28
|
+
&-link
|
|
29
|
+
margin-left 1u
|
|
30
|
+
opacity 0
|
|
31
|
+
|
|
32
|
+
&.hover
|
|
33
|
+
opacity 1
|
|
34
|
+
|
|
35
|
+
.divider
|
|
36
|
+
height 0.5u
|
|
37
|
+
background-color var(--color-bg-secondary)
|
|
38
|
+
margin-bottom 1.5u
|
|
39
|
+
|
|
40
|
+
.p
|
|
41
|
+
margin-top 2u
|
|
42
|
+
|
|
43
|
+
.center
|
|
44
|
+
text-align center
|
|
45
|
+
|
|
46
|
+
.listIndex
|
|
47
|
+
width 3u
|
|
48
|
+
margin-top 2u
|
|
49
|
+
text-align center
|
|
50
|
+
padding-right 2px
|
|
51
|
+
|
|
52
|
+
.listContent
|
|
53
|
+
flex 1
|
|
54
|
+
|
|
55
|
+
.alert
|
|
56
|
+
margin-top 2u
|
|
57
|
+
|
|
58
|
+
.img
|
|
59
|
+
margin-top 2u
|
|
60
|
+
width 100%
|
|
61
|
+
|
|
62
|
+
.inlineCodeWrapper
|
|
63
|
+
background-color $codeBg
|
|
64
|
+
border-width 1px
|
|
65
|
+
border-color var(--color-border-main)
|
|
66
|
+
border-radius .5u
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
.example
|
|
73
|
+
margin-top 2u
|
|
74
|
+
background-color $exampleBg
|
|
75
|
+
border-top-left-radius 1u
|
|
76
|
+
border-top-right-radius 1u
|
|
77
|
+
|
|
78
|
+
&Content
|
|
79
|
+
flex-direction column
|
|
80
|
+
flex-grow 1
|
|
81
|
+
|
|
82
|
+
.padding
|
|
83
|
+
padding 2u
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
.code
|
|
88
|
+
margin-top 2u
|
|
89
|
+
|
|
90
|
+
&-example
|
|
91
|
+
margin-top 0
|
|
92
|
+
|
|
93
|
+
&-collapse
|
|
94
|
+
background-color $exampleBg
|
|
95
|
+
border-bottom-left-radius 1u
|
|
96
|
+
border-bottom-right-radius 1u
|
|
97
|
+
|
|
98
|
+
&-header
|
|
99
|
+
padding-top 1u
|
|
100
|
+
padding-bottom 2u
|
|
101
|
+
padding-left 2u
|
|
102
|
+
padding-right 2u
|
|
103
|
+
|
|
104
|
+
+tablet()
|
|
105
|
+
padding-top 2u
|
|
106
|
+
|
|
107
|
+
&-content
|
|
108
|
+
padding 2u
|
|
109
|
+
|
|
110
|
+
&-action
|
|
111
|
+
margin-left 2u
|
|
112
|
+
|
|
113
|
+
&-collapse
|
|
114
|
+
&-copy
|
|
115
|
+
color var(--color-text-description)
|
package/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import defaultMdxComponents from './client/mdxComponents/index.js'
|
|
2
|
+
|
|
3
|
+
const mdxComponents = { ...defaultMdxComponents }
|
|
4
|
+
|
|
5
|
+
// this can be used to override default MDX components globally
|
|
6
|
+
export function overrideMDXComponents (newComponents) {
|
|
7
|
+
Object.assign(mdxComponents, newComponents)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// magic named export used by @mdx-js/mdx (in mdxLoader of @startupjs/bundler)
|
|
11
|
+
export function useMDXComponents () {
|
|
12
|
+
return mdxComponents
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default mdxComponents
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@startupjs-ui/mdx",
|
|
3
|
+
"description": "MDX provider with a set of custom components for react-native support and syntax highlighting",
|
|
4
|
+
"version": "0.1.3",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"type": "module",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./index.js"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@fortawesome/free-solid-svg-icons": "^5.12.0",
|
|
14
|
+
"@mdx-js/react": "^3.0.0",
|
|
15
|
+
"@startupjs-ui/alert": "^0.1.3",
|
|
16
|
+
"@startupjs-ui/br": "^0.1.3",
|
|
17
|
+
"@startupjs-ui/collapse": "^0.1.3",
|
|
18
|
+
"@startupjs-ui/div": "^0.1.3",
|
|
19
|
+
"@startupjs-ui/divider": "^0.1.3",
|
|
20
|
+
"@startupjs-ui/icon": "^0.1.3",
|
|
21
|
+
"@startupjs-ui/link": "^0.1.3",
|
|
22
|
+
"@startupjs-ui/scroll-view": "^0.1.3",
|
|
23
|
+
"@startupjs-ui/span": "^0.1.3",
|
|
24
|
+
"@startupjs-ui/table": "^0.1.3",
|
|
25
|
+
"refractor": "^3.0.0"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"@react-native-clipboard/clipboard": "*",
|
|
29
|
+
"react": "*",
|
|
30
|
+
"react-native": "*",
|
|
31
|
+
"startupjs": "*"
|
|
32
|
+
},
|
|
33
|
+
"gitHead": "fd964ebc3892d3dd0a6c85438c0af619cc50c3f0"
|
|
34
|
+
}
|