From 63523caa04628bcc0b8b34fe4f947c92f515a92f Mon Sep 17 00:00:00 2001 From: Zach Sherman Date: Fri, 25 May 2018 12:38:21 -0400 Subject: [PATCH 01/11] allow choice of syntax highlighter --- src/core/syntax.js | 8 ++++---- src/utils/config.js | 7 ++++--- themes/default/index.js | 14 ++++++++++++++ themes/default/markdown/index.js | 7 +------ themes/default/markdown/overrides/Code.js | 10 ++++++++-- themes/default/markdown/overrides/Header.js | 13 ++++++++----- themes/default/markdown/styles.js | 1 + themes/default/page/index.js | 2 +- themes/default/search/index.js | 2 +- 9 files changed, 42 insertions(+), 22 deletions(-) diff --git a/src/core/syntax.js b/src/core/syntax.js index fd5e318..86afe9a 100644 --- a/src/core/syntax.js +++ b/src/core/syntax.js @@ -7,17 +7,17 @@ module.exports = async (config) => { const { temp, languages, - theme_custom, + syntax, } = config const rsh = syspath.dirname(require.resolve('react-syntax-highlighter')) const langs = languages - .filter(lang => fs.pathExistsSync(`${rsh}/languages/prism/${lang}`)) - .map(lang => `{ name: '${lang}', func: require('${rsh}/languages/prism/${lang}').default },`) + .filter(lang => fs.pathExistsSync(`${rsh}/languages/${syntax.renderer}/${lang}.js`)) + .map(lang => `{ name: '${lang}', func: require('${rsh}/languages/${syntax.renderer}/${lang}').default },`) .join('\n') const content = `module.exports = { - theme: require('${rsh}/styles/prism/${theme_custom.syntaxTheme}').default, + theme: require('${rsh}/styles/${syntax.renderer}/${syntax.theme}').default, languages: [${langs}] }` diff --git a/src/utils/config.js b/src/utils/config.js index e801522..dcb1948 100644 --- a/src/utils/config.js +++ b/src/utils/config.js @@ -26,9 +26,10 @@ const DEFAULT_CONFIG = { languages: ['bash', 'json'], header_links: [], theme: 'default', - theme_custom: { - syntaxTheme: 'prism', - syntaxLineNumbers: false, + syntax: { + theme: 'atom-one-light', + renderer: 'hljs', + lineNumbers: true, }, } diff --git a/themes/default/index.js b/themes/default/index.js index ebf28ca..1937ad0 100644 --- a/themes/default/index.js +++ b/themes/default/index.js @@ -2,6 +2,9 @@ import React from 'react' import ReactDOM from 'react-dom' import { Router } from 'react-router-dom' import { hydrate } from 'emotion' +import { registerLanguage as registerHighlight } from 'react-syntax-highlighter/light' +import { registerLanguage as registerPrism } from 'react-syntax-highlighter/prism-light' +import { languages } from '@codegen/loadSyntax' // eslint-disable-line import history from './history' import App from './application' @@ -15,6 +18,17 @@ const render = ReactDOM.render isDev && module.hot && module.hot.accept() _EMOTION_IDS_ && hydrate(_EMOTION_IDS_) +// Ensure required languages are registered +const renderers = { + hljs: registerHighlight, + prism: registerPrism, +} + +if (window) { + const register = renderers[process.env.PROPS.config.syntax.renderer] + languages.forEach(lang => register(lang.name, lang.func)) +} + render( diff --git a/themes/default/markdown/index.js b/themes/default/markdown/index.js index 9234d38..56c7af2 100644 --- a/themes/default/markdown/index.js +++ b/themes/default/markdown/index.js @@ -1,19 +1,14 @@ import React from 'react' import Markdown from 'react-markdown' -import { registerLanguage } from 'react-syntax-highlighter/prism-light' -import { languages } from '@codegen/loadSyntax' // eslint-disable-line import { Wrapper } from './styles' import Code from './overrides/Code' import Header from './overrides/Header' import Link from './overrides/Link' export default function (props) { - languages.forEach(lang => - registerLanguage(lang.name, lang.func)) - const options = { renderers: { - code: Code, + code: p => , link: Link, heading: Header, }, diff --git a/themes/default/markdown/overrides/Code.js b/themes/default/markdown/overrides/Code.js index cf3c960..ed3404f 100644 --- a/themes/default/markdown/overrides/Code.js +++ b/themes/default/markdown/overrides/Code.js @@ -1,10 +1,12 @@ import React from 'react' -import Syntax from 'react-syntax-highlighter/prism-light' +import Highlight from 'react-syntax-highlighter/light' +import Prism from 'react-syntax-highlighter/prism-light' import { theme, languages } from '@codegen/loadSyntax' // eslint-disable-line export default function (props) { let { language } = props - const { value } = props + const { value, renderer } = props + console.log(props) if (language) { language = language // language name aliases @@ -18,6 +20,10 @@ export default function (props) { } } + const Syntax = renderer === 'prism' + ? Prism + : Highlight + return ( diff --git a/themes/default/markdown/styles.js b/themes/default/markdown/styles.js index 6b76c42..d209a03 100644 --- a/themes/default/markdown/styles.js +++ b/themes/default/markdown/styles.js @@ -2,6 +2,7 @@ import styled from 'react-emotion' export const Wrapper = styled('div')` word-wrap: break-word; + a { text-decoration: none } h1, h2, h3, h4 { font-weight: bold; text-decoration: none; diff --git a/themes/default/page/index.js b/themes/default/page/index.js index 6c014aa..cf5cb03 100644 --- a/themes/default/page/index.js +++ b/themes/default/page/index.js @@ -73,7 +73,7 @@ export default class Page extends Component { : ( )} diff --git a/themes/default/search/index.js b/themes/default/search/index.js index 13224c9..f06897a 100644 --- a/themes/default/search/index.js +++ b/themes/default/search/index.js @@ -135,7 +135,7 @@ class Search extends Component {

3 ? query.split(' ') : []} + searchWords={query.length > 2 ? query.split(' ') : []} autoEscape textToHighlight={strip(ellipsify(r.content, 400))} /> From d011b0fc551bba1ce78b276802b27bb00f2724bd Mon Sep 17 00:00:00 2001 From: Zach Sherman Date: Fri, 25 May 2018 14:52:01 -0400 Subject: [PATCH 02/11] add simple divider component, update markdown styling --- themes/default/markdown/styles.js | 13 ++++++++++--- themes/default/sidebar/index.js | 14 +++++++++++--- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/themes/default/markdown/styles.js b/themes/default/markdown/styles.js index d209a03..bccdc62 100644 --- a/themes/default/markdown/styles.js +++ b/themes/default/markdown/styles.js @@ -2,11 +2,17 @@ import styled from 'react-emotion' export const Wrapper = styled('div')` word-wrap: break-word; - a { text-decoration: none } + color: #2f3138; + + a { + text-decoration: none; + color: #5944CC; + } h1, h2, h3, h4 { font-weight: bold; text-decoration: none; margin: 0; + color: #0D0A2B; } p { @@ -39,11 +45,12 @@ export const Wrapper = styled('div')` code { border-radius: 4px; - padding: .25rem .5rem; + padding: .15rem .25rem; display: inline; line-height: 1.5; word-break: break-all; - background: #F4F5F6; + background: #EEEAFE; + color: #5742CA; } pre code { diff --git a/themes/default/sidebar/index.js b/themes/default/sidebar/index.js index 1bae472..67512d3 100644 --- a/themes/default/sidebar/index.js +++ b/themes/default/sidebar/index.js @@ -1,6 +1,7 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import { withRouter, NavLink } from 'react-router-dom' +import styled from 'react-emotion' import { Reveal } from '@timberio/ui' import Logo from '../logo' import IconHamburger from '../icons/hamburger' @@ -18,6 +19,15 @@ import { Callout, } from './styles' +const Divider = styled('div')` + border-bottom: 1px solid #dfe2e6; + margin: .5rem 1rem .5rem 0; +` + +const componentMap = { + Divider +} + class Sidebar extends Component { static propTypes = { manifest: PropTypes.object, @@ -50,9 +60,7 @@ class Sidebar extends Component { renderTrigger = ({ title, url, component }) => { // @TODO: custom components if (component) { - // return ( - //

{`<${component} />`}
- // ) + return React.createElement(componentMap[component]) } if (!url) { From 22dfa303b1d77688a8537f5e8ef20c7782dddacb Mon Sep 17 00:00:00 2001 From: Zach Sherman Date: Sat, 26 May 2018 12:00:30 -0400 Subject: [PATCH 03/11] update search styles, add icons, responsive widths --- package.json | 3 + src/core/output.js | 6 +- src/core/socket.js | 6 +- src/utils/config.js | 2 + themes/default/application/styles.js | 12 +- themes/default/components/container.js | 6 + themes/default/loading/index.js | 137 +++++++++++++++++++++- themes/default/loading/styles.js | 40 +------ themes/default/logo/styles.js | 4 +- themes/default/markdown/overrides/Code.js | 1 - themes/default/markdown/styles.js | 5 + themes/default/page/index.js | 86 +++++++++++--- themes/default/page/styles.js | 53 ++++++++- themes/default/routes.js | 2 +- themes/default/search/index.js | 22 ++-- themes/default/search/styles.js | 7 +- themes/default/sidebar/styles.js | 9 +- themes/default/utils/index.js | 9 ++ yarn.lock | 120 ++++++++++++++++++- 19 files changed, 439 insertions(+), 91 deletions(-) create mode 100644 themes/default/components/container.js create mode 100644 themes/default/utils/index.js diff --git a/package.json b/package.json index b8ecdaa..ec870c8 100644 --- a/package.json +++ b/package.json @@ -76,13 +76,16 @@ "history": "^4.7.2", "html-minifier": "^3.5.16", "js-search": "^1.4.2", + "markdown-toc": "^1.2.0", "minimist": "^1.2.0", "ncp": "^2.0.0", "progress": "^2.0.0", "prop-types": "^15.6.1", "react": "^16.4.0", + "react-content-loader": "^3.1.2", "react-dom": "^16.4.0", "react-emotion": "^9.1.3", + "react-feather": "^1.1.0", "react-helmet": "^5.2.0", "react-highlight-words": "^0.11.0", "react-markdown": "^3.3.2", diff --git a/src/core/output.js b/src/core/output.js index ac5b28a..81897bd 100644 --- a/src/core/output.js +++ b/src/core/output.js @@ -1,5 +1,6 @@ const fs = require('fs-extra') const syspath = require('path') +const toc = require('markdown-toc') const { generateDatabase } = require('./database') const { templateForProduction } = require('./template') const { getContent } = require('./filesystem') @@ -19,7 +20,10 @@ module.exports = async (entrypoints, props) => { const outputJson = syspath.join(item.outputDir, 'index.json') await fs.outputFile(outputHtml, template) - await fs.outputJson(outputJson, { content: item.content }) + await fs.outputJson(outputJson, { + content: item.content, + toc: toc(item.content).json + }) } if (items) { diff --git a/src/core/socket.js b/src/core/socket.js index 234b6b5..dd1dd6c 100644 --- a/src/core/socket.js +++ b/src/core/socket.js @@ -1,5 +1,6 @@ const fs = require('fs') const WebSocket = require('ws') +const toc = require('markdown-toc') const { getContent } = require('./filesystem') module.exports = (server) => { @@ -13,7 +14,10 @@ module.exports = (server) => { client.on('message', file => { const send = async () => { const content = await getContent(file) - client.send(content) + client.send(JSON.stringify({ + content, + toc: toc(content).json, + })) } send() diff --git a/src/utils/config.js b/src/utils/config.js index dcb1948..4cf4e45 100644 --- a/src/utils/config.js +++ b/src/utils/config.js @@ -26,6 +26,8 @@ const DEFAULT_CONFIG = { languages: ['bash', 'json'], header_links: [], theme: 'default', + prefixTitles: true, + tableOfContents: true, syntax: { theme: 'atom-one-light', renderer: 'hljs', diff --git a/themes/default/application/styles.js b/themes/default/application/styles.js index 5efe4da..6943087 100644 --- a/themes/default/application/styles.js +++ b/themes/default/application/styles.js @@ -37,9 +37,19 @@ export const WrapperNav = styled('nav')` flex: 1; background: linear-gradient(90deg, #F0F2F4 0%, #F5F7F9 100%); border-right: 1px solid #E6E9EB; - min-width: 250px; text-align: right; overflow: auto; + + @media (min-width: 850px) { + min-width: 270px; + max-width: 270px; + } + + @media (min-width: 1440px) { + min-width: initial; + max-width: initial; + } + @media (max-width: 850px) { flex: 0 auto; overflow: hidden; diff --git a/themes/default/components/container.js b/themes/default/components/container.js new file mode 100644 index 0000000..cdbb4e1 --- /dev/null +++ b/themes/default/components/container.js @@ -0,0 +1,6 @@ +import styled from 'react-emotion' + +export default styled('div')` + width: 1024px; + margin: 0 auto; +` diff --git a/themes/default/loading/index.js b/themes/default/loading/index.js index 08f36c3..ce87c7d 100644 --- a/themes/default/loading/index.js +++ b/themes/default/loading/index.js @@ -1,14 +1,141 @@ import React from 'react' +import ContentLoader from 'react-content-loader' import { Wrapper } from './styles' +// @TODO: Make this a little more responsive by passing in 400 for the height +// on smaller screens export default function (props) { return ( -
-
-
-
-
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) } + +// +// +// +// +// +// \ No newline at end of file diff --git a/themes/default/loading/styles.js b/themes/default/loading/styles.js index d7bb81c..bcedcdf 100644 --- a/themes/default/loading/styles.js +++ b/themes/default/loading/styles.js @@ -1,40 +1,4 @@ -import styled, { keyframes } from 'react-emotion' - -const stretch = keyframes` - 0%, - 40%, - 100% { - transform: scaleY(0.4); - } - 20% { - transform: scaleY(1.0); - } -` +import styled from 'react-emotion' export const Wrapper = styled('div')` - width: 50px; - height: 40px; - text-align: center; - font-size: 10px; - div { - background: #E6E9EB; - height: 100%; - width: 5px; - display: inline-block; - animation: ${stretch} 1.2s infinite ease-in-out; - margin: 0 1px; - &:nth-child(2) { - animation-delay: -1.1s; - } - &:nth-child(3) { - animation-delay: -1.0s; - } - &:nth-child(4) { - animation-delay: -0.9s; - } - &:nth-child(5) { - animation-delay: -0.8s; - } - } - }), -` +` \ No newline at end of file diff --git a/themes/default/logo/styles.js b/themes/default/logo/styles.js index 1fd2047..1bdfbb4 100644 --- a/themes/default/logo/styles.js +++ b/themes/default/logo/styles.js @@ -7,11 +7,9 @@ export const Wrapper = styled('div')` ` export const CustomLogo = styled('div')` - padding: 30px; img { display: block; - width: 100%; - height: 100%; + height: 35px; } ` diff --git a/themes/default/markdown/overrides/Code.js b/themes/default/markdown/overrides/Code.js index ed3404f..f12b993 100644 --- a/themes/default/markdown/overrides/Code.js +++ b/themes/default/markdown/overrides/Code.js @@ -6,7 +6,6 @@ import { theme, languages } from '@codegen/loadSyntax' // eslint-disable-line export default function (props) { let { language } = props const { value, renderer } = props - console.log(props) if (language) { language = language // language name aliases diff --git a/themes/default/markdown/styles.js b/themes/default/markdown/styles.js index bccdc62..4fe946f 100644 --- a/themes/default/markdown/styles.js +++ b/themes/default/markdown/styles.js @@ -8,6 +8,7 @@ export const Wrapper = styled('div')` text-decoration: none; color: #5944CC; } + h1, h2, h3, h4 { font-weight: bold; text-decoration: none; @@ -15,6 +16,10 @@ export const Wrapper = styled('div')` color: #0D0A2B; } + a:first-of-type { + margin-top: 0; + } + p { line-height: 1.7rem; } diff --git a/themes/default/page/index.js b/themes/default/page/index.js index cf5cb03..7970670 100644 --- a/themes/default/page/index.js +++ b/themes/default/page/index.js @@ -4,7 +4,54 @@ import axios from 'axios' import Markdown from '../markdown' import Loading from '../loading' import { ConfigContext } from '../context' -import { Wrapper } from './styles' +import { ellipsify } from '../utils' +import { Wrapper, TOC } from './styles' + +const TableOfContents = ({ toc }) => { + // Don't show this if there aren't enough headers + if (toc.length < 2) return null + console.log(toc) + + // Create TOC hierarchy and link to headers + const items = toc.map(t => ( +
  • + + {ellipsify(t.content, 20)} + +
  • + )) + + return ( + +
      +
      Contents
      + {items} +
    +
    + ) +} + +const Content = ({ content, config, route }) => { + const defaultContent = '##### _You don\'t have any content here yet!_' + + // Prepend route title to content if `prependTitles` is set to true in config + let md = content + if (config.prefixTitles) { + md = `# ${route.title} \n ${content}` + } + + return ( +
    + +
    + ) +} export default class Page extends Component { static displayName = 'Page' @@ -14,6 +61,7 @@ export default class Page extends Component { this.state = { loading: !props.route.content, content: props.route.content, + toc: props.route.toc, } } @@ -27,8 +75,10 @@ export default class Page extends Component { }) this._socket.addEventListener('message', evt => { + const { content, toc } = JSON.parse(evt.data) this.setState({ - content: evt.data, + content, + toc, loading: false, }) }) @@ -36,6 +86,7 @@ export default class Page extends Component { const { data } = await axios.get('index.json') this.setState({ // eslint-disable-line content: data.content, + toc: data.toc, loading: false, }) } @@ -55,10 +106,9 @@ export default class Page extends Component { const { loading, content, + toc, } = this.state - const defaultContent = '##### _You don\'t have any content here yet!_' - return ( {config => @@ -66,17 +116,23 @@ export default class Page extends Component { {route.title} - -
    - {loading - ? - : ( - - )} -
    + { + loading && + + } + { + !loading && + + } + { + !loading && + config.tableOfContents && + + } }
    diff --git a/themes/default/page/styles.js b/themes/default/page/styles.js index a487fbb..f6a5d6f 100644 --- a/themes/default/page/styles.js +++ b/themes/default/page/styles.js @@ -1,8 +1,55 @@ import styled from 'react-emotion' export const Wrapper = styled('div')` - padding: 20px 40px; - width: 100%; - max-width: 800px; + padding: 30px 50px; + max-width: 1024px; box-sizing: border-box; + display: flex; + @media(max-width: 1024px) { + flex-direction: column-reverse; + nav { margin-left: 0 } + } + + @media(min-width: 1440px) { + max-width: 1200px; + } +` + +export const TOC = styled('nav')` + margin-left: 2rem; + min-width: 150px; + flex-grow: 0; + + ul { + list-style: none; + border-left: 1px solid #E6E9EB; + padding-left: 2rem; + } + + h5 { + color: #848B8E; + margin: 0; + } + + li { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: 13px; + line-height: 30px; + display: block; + } + + a { + text-decoration: none; + color: #626469; + display: block; + &:hover { + color: #5742C7; + } + } +` + +export const Body = styled('div')` + ` diff --git a/themes/default/routes.js b/themes/default/routes.js index cb7506b..7f85f96 100644 --- a/themes/default/routes.js +++ b/themes/default/routes.js @@ -19,7 +19,7 @@ class Routes extends Component { path={data.url} render={({ staticContext }) => ( )} diff --git a/themes/default/search/index.js b/themes/default/search/index.js index f06897a..b9ddd51 100644 --- a/themes/default/search/index.js +++ b/themes/default/search/index.js @@ -2,9 +2,11 @@ import React, { Component } from 'react' import { Link } from 'react-router-dom' import axios from 'axios' import Highlight from 'react-highlight-words' +import { Search as SearchIcon } from 'react-feather' import { createDB } from './db' import strip from './strip' import history from '../history' +import { ellipsify } from '../utils' import { Wrapper, Input, Results, Result } from './styles' const UP = 'ArrowUp' @@ -12,16 +14,6 @@ const DOWN = 'ArrowDown' const ENTER = 'Enter' const ESCAPE = 'Escape' -function ellipsify (text, limit) { - if (!text) return '' - - if (text.length <= limit) { - return text - } - - return `${text.substring(0, limit)}...` -} - class Search extends Component { constructor (props) { super(props) @@ -119,6 +111,13 @@ class Search extends Component { this.setState({ loading: false, query: '', results: [] }) } + renderBreadCrumb (result) { + return result + .breadcrumb + .slice(1) + .map((b, i) => {b} {">"}) + } + renderResults () { const { query, loading, selectedIndex, results } = this.state if (!query.length) return null @@ -131,7 +130,7 @@ class Search extends Component { onClick={this.clearSearch} > -

    {r.title}

    +

    {this.renderBreadCrumb(r)}

    + Date: Sat, 26 May 2018 13:14:06 -0400 Subject: [PATCH 04/11] add search scrolling, toc, and better empty state --- package.json | 1 + themes/default/page/index.js | 1 - themes/default/search/index.js | 65 +++++++++++++++++++++++++++++---- themes/default/search/styles.js | 30 +++++++++++---- yarn.lock | 8 +++- 5 files changed, 88 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index ec870c8..039ab26 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "progress": "^2.0.0", "prop-types": "^15.6.1", "react": "^16.4.0", + "react-click-outside": "^3.0.1", "react-content-loader": "^3.1.2", "react-dom": "^16.4.0", "react-emotion": "^9.1.3", diff --git a/themes/default/page/index.js b/themes/default/page/index.js index 7970670..0b05104 100644 --- a/themes/default/page/index.js +++ b/themes/default/page/index.js @@ -10,7 +10,6 @@ import { Wrapper, TOC } from './styles' const TableOfContents = ({ toc }) => { // Don't show this if there aren't enough headers if (toc.length < 2) return null - console.log(toc) // Create TOC hierarchy and link to headers const items = toc.map(t => ( diff --git a/themes/default/search/index.js b/themes/default/search/index.js index b9ddd51..0483650 100644 --- a/themes/default/search/index.js +++ b/themes/default/search/index.js @@ -1,13 +1,14 @@ import React, { Component } from 'react' +import enhanceClickOutside from 'react-click-outside' import { Link } from 'react-router-dom' import axios from 'axios' import Highlight from 'react-highlight-words' -import { Search as SearchIcon } from 'react-feather' +import { Search as SearchIcon, ChevronRight } from 'react-feather' import { createDB } from './db' import strip from './strip' import history from '../history' import { ellipsify } from '../utils' -import { Wrapper, Input, Results, Result } from './styles' +import { Wrapper, Input, Results, Result, Center } from './styles' const UP = 'ArrowUp' const DOWN = 'ArrowDown' @@ -30,6 +31,32 @@ class Search extends Component { this.loadResults() } + componentDidUpdate (prevProps, prevState) { + if (this.state.selectedIndex !== prevState.selectedIndex) { + this.ensureActiveItemVisible() + } + } + + ensureActiveItemVisible () { + if (!this.activeItem) return false + const distanceFromTop = this.activeItem.offsetTop + const height = this.activeItem.offsetHeight + const scrollTop = this.results.scrollTop + const clientHeight = this.results.clientHeight + + if (distanceFromTop === 0) { + return this.results.scrollTop = 0 + } + + if (distanceFromTop < scrollTop) { + return this.results.scrollTop = distanceFromTop + } + + if ((distanceFromTop + height) > (scrollTop + clientHeight)) { + return this.results.scrollTop = distanceFromTop - height + } + } + async loadResults () { // Initialize search instance and set indices const resp = await axios.get('/db.json') @@ -98,6 +125,10 @@ class Search extends Component { this.setState({ selectedIndex: nextIndex }) } + handleClickOutside () { + this.clearSearch() + } + fetchResults (query) { return new Promise((resolve, reject) => { const results = this.db @@ -114,8 +145,25 @@ class Search extends Component { renderBreadCrumb (result) { return result .breadcrumb - .slice(1) - .map((b, i) => {b} {">"}) + .slice(1, result.breadcrumb.length) + .concat(result.title) + .map((b, i) => ( + + { + i !== 0 && + + } + {b} + + )) } renderResults () { @@ -127,6 +175,7 @@ class Search extends Component { i === selectedIndex ? this.activeItem = ref : null} onClick={this.clearSearch} > @@ -136,7 +185,7 @@ class Search extends Component { highlightClassName="highlight" searchWords={query.length > 2 ? query.split(' ') : []} autoEscape - textToHighlight={strip(ellipsify(r.content, 400))} + textToHighlight={strip(ellipsify(r.content, 200))} />

    {r.url} @@ -145,9 +194,9 @@ class Search extends Component { ) return ( - + this.results = ref}> {items.length !== 0 && !loading && items} - {items.length === 0 && !loading && No Results...} + {items.length === 0 && !loading &&
    No Results Found matching "{query}"...
    } {loading && Loading...}
    ) @@ -169,4 +218,4 @@ class Search extends Component { } } -export default Search +export default enhanceClickOutside(Search) diff --git a/themes/default/search/styles.js b/themes/default/search/styles.js index 0323a20..2b40351 100644 --- a/themes/default/search/styles.js +++ b/themes/default/search/styles.js @@ -11,10 +11,10 @@ export const Input = styled('input')` outline: none; font-size: .9rem; padding: 8px 15px; - border-radius: 30px; display: block; width: 200px; border: none; + flex-grow: 1; ::placeholder { color: #ACB0B2; } @@ -23,33 +23,49 @@ export const Input = styled('input')` export const Results = styled('div')` position: absolute; width: 100%; - top: 75px; - height: 100vh; + top: 70px; + height: 50vh; + max-height: 700px; + overflow: scroll; + box-shadow: 0 1px 5px 0 rgba(0,0,0,.07), 0 7px 17px 0 rgba(0,0,0,.1); background: #FFF; z-index: 99; ` export const Result = styled('div')` - padding: .5rem; + padding: 1rem; background: ${props => props.selected ? '#f6f5fb' : '#FFF'}; a { text-decoration: none } &:hover { background: #f6f5fb; - h4 { color: #6457DC; } + h4 { color: #6457DC; text-decoration: underline; } } h4 { font-weight: bold; - text-decoration: underline; + text-decoration: none; margin: 0; color: ${props => props.selected ? '#6457DC' : '#0d2b3e'}; + text-decoration: ${props => props.selected ? 'underline' : 'none'}; } p { - color: rgba(0,0,0,0.85); + color: rgba(0,0,0,0.75); text-decoration: none; margin: 0; + font-size: 1rem; } p .highlight { background: #d1ccec } .url { font-size: 12px; } ` + +export const Center = styled('div')` + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; +` diff --git a/yarn.lock b/yarn.lock index 32166d4..d6cf9c2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3613,7 +3613,7 @@ hoek@5.x.x: version "5.0.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-5.0.3.tgz#b71d40d943d0a95da01956b547f83c4a5b4a34ac" -hoist-non-react-statics@^2.3.0, hoist-non-react-statics@^2.3.1: +hoist-non-react-statics@^2.1.1, hoist-non-react-statics@^2.3.0, hoist-non-react-statics@^2.3.1: version "2.5.0" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.0.tgz#d2ca2dfc19c5a91c5a6615ce8e564ef0347e2a40" @@ -5649,6 +5649,12 @@ rc@^1.1.7: minimist "^1.2.0" strip-json-comments "~2.0.1" +react-click-outside@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/react-click-outside/-/react-click-outside-3.0.1.tgz#6e77e84d2f17afaaac26dbad743cbbf909f5e24c" + dependencies: + hoist-non-react-statics "^2.1.1" + react-content-loader@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/react-content-loader/-/react-content-loader-3.1.2.tgz#98230b4604b4b744eaa2d3fc88917dd988df6766" From 21a9ccb4f9264b56b075bfc828e571da224054c2 Mon Sep 17 00:00:00 2001 From: Zach Sherman Date: Sat, 26 May 2018 13:18:41 -0400 Subject: [PATCH 05/11] linting fixes --- themes/default/loading/index.js | 44 ++++++++++++++++---------------- themes/default/loading/styles.js | 2 +- themes/default/search/index.js | 2 +- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/themes/default/loading/index.js b/themes/default/loading/index.js index ce87c7d..dd87936 100644 --- a/themes/default/loading/index.js +++ b/themes/default/loading/index.js @@ -57,14 +57,14 @@ export default function (props) { secondaryColor="#ececf1" {...props} > - - - - - - - - + + + + + + + + @@ -97,14 +97,14 @@ export default function (props) { secondaryColor="#ececf1" {...props} > - - - - - - - - + + + + + + + + @@ -133,9 +133,9 @@ export default function (props) { ) } -// -// -// -// -// -// \ No newline at end of file +// +// +// +// +// +// diff --git a/themes/default/loading/styles.js b/themes/default/loading/styles.js index bcedcdf..19d0a8e 100644 --- a/themes/default/loading/styles.js +++ b/themes/default/loading/styles.js @@ -1,4 +1,4 @@ import styled from 'react-emotion' export const Wrapper = styled('div')` -` \ No newline at end of file +` diff --git a/themes/default/search/index.js b/themes/default/search/index.js index 0483650..8d28492 100644 --- a/themes/default/search/index.js +++ b/themes/default/search/index.js @@ -196,7 +196,7 @@ class Search extends Component { return ( this.results = ref}> {items.length !== 0 && !loading && items} - {items.length === 0 && !loading &&
    No Results Found matching "{query}"...
    } + {items.length === 0 && !loading &&
    No Results Found matching {`"${query}"`}...
    } {loading && Loading...}
    ) From c912368935e7b8bb702e0d64dfbfe559ae247c10 Mon Sep 17 00:00:00 2001 From: Zach Sherman Date: Sat, 26 May 2018 13:19:15 -0400 Subject: [PATCH 06/11] remove comment --- themes/default/loading/index.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/themes/default/loading/index.js b/themes/default/loading/index.js index dd87936..bcfa42a 100644 --- a/themes/default/loading/index.js +++ b/themes/default/loading/index.js @@ -132,10 +132,3 @@ export default function (props) { ) } - -// -// -// -// -// -// From 5b21c7ce1ef27db8a1dff9783bce4a08645bd260 Mon Sep 17 00:00:00 2001 From: Zach Sherman Date: Sat, 26 May 2018 14:07:18 -0400 Subject: [PATCH 07/11] adjust monospace styles --- themes/default/application/styles.js | 5 +++-- themes/default/markdown/styles.js | 9 ++++----- themes/default/search/index.js | 8 +++++++- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/themes/default/application/styles.js b/themes/default/application/styles.js index 6943087..dabd82c 100644 --- a/themes/default/application/styles.js +++ b/themes/default/application/styles.js @@ -14,9 +14,10 @@ const fontMain = css` ` const fontMono = css` - font-family: 'Menlo', monospace; + font-family: "Menlo", "Source Code Pro", "Inconsolata","monospace", serif; line-height: 1; - font-size: 1.1rem; + font-size: 13.5px; + line-height: 22px; ` export const Wrapper = styled('div')` diff --git a/themes/default/markdown/styles.js b/themes/default/markdown/styles.js index 4fe946f..f124999 100644 --- a/themes/default/markdown/styles.js +++ b/themes/default/markdown/styles.js @@ -41,11 +41,11 @@ export const Wrapper = styled('div')` pre { overflow-x: scroll; + background: #FAFAFD !important; } pre, code { - font-family: monospace; - -webkit-font-smoothing: initial !important; + // -webkit-font-smoothing: initial; } code { @@ -59,18 +59,17 @@ export const Wrapper = styled('div')` } pre code { - font-size: 1.1rem; border: none; word-break: break-all; white-space: pre-wrap; display: inline-block !important; background: inherit; - color: rgb(52, 50, 64); + color: #485672; } pre { border-radius: 4px; - font-family: Roboto Mono, monospace; + // font-family: Roboto Mono, monospace; border-radius: 3px; line-height: 19px; } diff --git a/themes/default/search/index.js b/themes/default/search/index.js index 8d28492..bf879f1 100644 --- a/themes/default/search/index.js +++ b/themes/default/search/index.js @@ -39,6 +39,7 @@ class Search extends Component { ensureActiveItemVisible () { if (!this.activeItem) return false + const distanceFromTop = this.activeItem.offsetTop const height = this.activeItem.offsetHeight const scrollTop = this.results.scrollTop @@ -139,7 +140,12 @@ class Search extends Component { } clearSearch = () => { - this.setState({ loading: false, query: '', results: [] }) + this.setState({ + loading: false, + query: '', + results: [], + selectedIndex: 0, + }) } renderBreadCrumb (result) { From 239622893da3d1d9adf4cf98aa3e9c36bd5ce5ad Mon Sep 17 00:00:00 2001 From: Zach Sherman Date: Sat, 26 May 2018 14:13:02 -0400 Subject: [PATCH 08/11] ellipsify in css --- themes/default/application/styles.js | 4 ++-- themes/default/markdown/overrides/Code.js | 2 +- themes/default/markdown/styles.js | 5 ----- themes/default/page/index.js | 5 ++--- themes/default/page/styles.js | 7 ++++--- 5 files changed, 9 insertions(+), 14 deletions(-) diff --git a/themes/default/application/styles.js b/themes/default/application/styles.js index dabd82c..e388dcc 100644 --- a/themes/default/application/styles.js +++ b/themes/default/application/styles.js @@ -16,8 +16,8 @@ const fontMain = css` const fontMono = css` font-family: "Menlo", "Source Code Pro", "Inconsolata","monospace", serif; line-height: 1; - font-size: 13.5px; - line-height: 22px; + font-size: 13px; + line-height: 21px; ` export const Wrapper = styled('div')` diff --git a/themes/default/markdown/overrides/Code.js b/themes/default/markdown/overrides/Code.js index f12b993..a462310 100644 --- a/themes/default/markdown/overrides/Code.js +++ b/themes/default/markdown/overrides/Code.js @@ -28,7 +28,7 @@ export default function (props) { style={theme} language={language} showLineNumbers={props.lineNumbers} - lineNumberStyle={{ opacity: 0.5 }} + lineNumberStyle={{ opacity: 0.3 }} useInlineStyles > {value} diff --git a/themes/default/markdown/styles.js b/themes/default/markdown/styles.js index f124999..6f132a4 100644 --- a/themes/default/markdown/styles.js +++ b/themes/default/markdown/styles.js @@ -52,7 +52,6 @@ export const Wrapper = styled('div')` border-radius: 4px; padding: .15rem .25rem; display: inline; - line-height: 1.5; word-break: break-all; background: #EEEAFE; color: #5742CA; @@ -137,10 +136,6 @@ export const Wrapper = styled('div')` padding-left: 5px !important; } - .react-syntax-highlighter-line-number { - font-size: .85rem; - } - .syntax-shell .react-syntax-highlighter-line-number { opacity: 0.5; visibility: hidden; diff --git a/themes/default/page/index.js b/themes/default/page/index.js index 0b05104..8a3645b 100644 --- a/themes/default/page/index.js +++ b/themes/default/page/index.js @@ -4,7 +4,6 @@ import axios from 'axios' import Markdown from '../markdown' import Loading from '../loading' import { ConfigContext } from '../context' -import { ellipsify } from '../utils' import { Wrapper, TOC } from './styles' const TableOfContents = ({ toc }) => { @@ -15,10 +14,10 @@ const TableOfContents = ({ toc }) => { const items = toc.map(t => (
  • - {ellipsify(t.content, 20)} + {t.content}
  • )) diff --git a/themes/default/page/styles.js b/themes/default/page/styles.js index f6a5d6f..f8bf639 100644 --- a/themes/default/page/styles.js +++ b/themes/default/page/styles.js @@ -32,9 +32,6 @@ export const TOC = styled('nav')` } li { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; font-size: 13px; line-height: 30px; display: block; @@ -44,6 +41,10 @@ export const TOC = styled('nav')` text-decoration: none; color: #626469; display: block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + &:hover { color: #5742C7; } From 3cb10e43721ab1553b73106776a90ff795acba79 Mon Sep 17 00:00:00 2001 From: Zach Sherman Date: Sat, 26 May 2018 14:48:51 -0400 Subject: [PATCH 09/11] pull out toc logic, fix config casing, don't parse JSON when we don't need to --- src/core/filesystem.js | 6 ++++++ src/core/output.js | 5 ++--- src/core/socket.js | 5 ++--- src/utils/config.js | 4 ++-- themes/default/application/styles.js | 2 +- themes/default/page/index.js | 6 +++--- themes/default/routes.js | 2 +- themes/default/search/index.js | 3 ++- themes/default/sidebar/index.js | 15 +++++++-------- themes/default/sidebar/styles.js | 6 ++++-- 10 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/core/filesystem.js b/src/core/filesystem.js index 2bcef7d..a6f0a03 100644 --- a/src/core/filesystem.js +++ b/src/core/filesystem.js @@ -1,6 +1,7 @@ const fs = require('fs-extra') const syspath = require('path') const { ncp } = require('ncp') +const toc = require('markdown-toc') const { parseFrontmatter } = require('../utils/frontmatter') const INDEX_FILES = ['index', 'readme'] @@ -154,6 +155,10 @@ async function getContent (path) { return content } +function getTableOfContents (content) { + return toc(content).json +} + /** * because of https://github.com/zeit/pkg/issues/420 */ @@ -171,6 +176,7 @@ module.exports = { checkForConflicts, dirTree, getContent, + getTableOfContents, copyDir, } diff --git a/src/core/output.js b/src/core/output.js index 81897bd..6c37250 100644 --- a/src/core/output.js +++ b/src/core/output.js @@ -1,9 +1,8 @@ const fs = require('fs-extra') const syspath = require('path') -const toc = require('markdown-toc') const { generateDatabase } = require('./database') const { templateForProduction } = require('./template') -const { getContent } = require('./filesystem') +const { getContent, getTableOfContents } = require('./filesystem') module.exports = async (entrypoints, props) => { const db = await generateDatabase(props.manifest) @@ -22,7 +21,7 @@ module.exports = async (entrypoints, props) => { await fs.outputFile(outputHtml, template) await fs.outputJson(outputJson, { content: item.content, - toc: toc(item.content).json + toc: getTableOfContents(item.content), }) } diff --git a/src/core/socket.js b/src/core/socket.js index dd1dd6c..1260481 100644 --- a/src/core/socket.js +++ b/src/core/socket.js @@ -1,7 +1,6 @@ const fs = require('fs') const WebSocket = require('ws') -const toc = require('markdown-toc') -const { getContent } = require('./filesystem') +const { getContent, getTableOfContents } = require('./filesystem') module.exports = (server) => { const socket = new WebSocket.Server({ @@ -16,7 +15,7 @@ module.exports = (server) => { const content = await getContent(file) client.send(JSON.stringify({ content, - toc: toc(content).json, + toc: getTableOfContents(content), })) } diff --git a/src/utils/config.js b/src/utils/config.js index 4cf4e45..5fdce4d 100644 --- a/src/utils/config.js +++ b/src/utils/config.js @@ -26,8 +26,8 @@ const DEFAULT_CONFIG = { languages: ['bash', 'json'], header_links: [], theme: 'default', - prefixTitles: true, - tableOfContents: true, + prefix_titles: true, + table_of_contents: true, syntax: { theme: 'atom-one-light', renderer: 'hljs', diff --git a/themes/default/application/styles.js b/themes/default/application/styles.js index e388dcc..57868a4 100644 --- a/themes/default/application/styles.js +++ b/themes/default/application/styles.js @@ -15,7 +15,6 @@ const fontMain = css` const fontMono = css` font-family: "Menlo", "Source Code Pro", "Inconsolata","monospace", serif; - line-height: 1; font-size: 13px; line-height: 21px; ` @@ -38,6 +37,7 @@ export const WrapperNav = styled('nav')` flex: 1; background: linear-gradient(90deg, #F0F2F4 0%, #F5F7F9 100%); border-right: 1px solid #E6E9EB; + box-shadow: inset -4px 0px 2px -2px rgba(202, 209, 226, 0.2); text-align: right; overflow: auto; diff --git a/themes/default/page/index.js b/themes/default/page/index.js index 8a3645b..53f1c48 100644 --- a/themes/default/page/index.js +++ b/themes/default/page/index.js @@ -14,7 +14,7 @@ const TableOfContents = ({ toc }) => { const items = toc.map(t => (
  • {t.content} @@ -37,7 +37,7 @@ const Content = ({ content, config, route }) => { // Prepend route title to content if `prependTitles` is set to true in config let md = content - if (config.prefixTitles) { + if (config.prefix_titles) { md = `# ${route.title} \n ${content}` } @@ -128,7 +128,7 @@ export default class Page extends Component { } { !loading && - config.tableOfContents && + config.table_of_contents && } diff --git a/themes/default/routes.js b/themes/default/routes.js index 7f85f96..cb7506b 100644 --- a/themes/default/routes.js +++ b/themes/default/routes.js @@ -19,7 +19,7 @@ class Routes extends Component { path={data.url} render={({ staticContext }) => ( )} diff --git a/themes/default/search/index.js b/themes/default/search/index.js index bf879f1..5a0f272 100644 --- a/themes/default/search/index.js +++ b/themes/default/search/index.js @@ -72,7 +72,8 @@ class Search extends Component { const { value } = e.target this.setState({ query: value, - loading: value.length !== 0 + loading: value.length !== 0, + selectedIndex: 0, }, async () => { const results = await this.fetchResults(value) this.setState({ results, loading: false }) diff --git a/themes/default/sidebar/index.js b/themes/default/sidebar/index.js index 67512d3..d917116 100644 --- a/themes/default/sidebar/index.js +++ b/themes/default/sidebar/index.js @@ -148,15 +148,14 @@ class Sidebar extends Component { - - - Powered by GitDocs - + + Powered by GitDocs + } diff --git a/themes/default/sidebar/styles.js b/themes/default/sidebar/styles.js index e92e301..0b61091 100644 --- a/themes/default/sidebar/styles.js +++ b/themes/default/sidebar/styles.js @@ -42,9 +42,11 @@ export const TopWrapper = styled('div')` ` export const MenuWrapper = styled('div')` - flex: 1; + flex: 1 1 auto; display: flex; flex-direction: column; + overflow: scroll; + @media (max-width: 850px) { background: #F5F7F9; box-shadow: -2px 0 3px rgba(0, 0, 0, .2); @@ -83,7 +85,6 @@ export const Hamburger = styled('div')` export const Nav = styled('nav')` flex: 1 0 auto; - border-bottom: 1px solid #E6E9EB; padding: 20px 0 20px 15px; ` @@ -135,6 +136,7 @@ export const Callout = styled('a')` padding: 20px 0; text-decoration: none; text-align: center; + border-top: 1px solid #E6E9EB; :hover { color: rgba(0, 0, 0, .3); } From f9fad76eee8bfc8b00e52c618cb91a56a88f6f8b Mon Sep 17 00:00:00 2001 From: Zach Sherman Date: Sat, 26 May 2018 15:31:40 -0400 Subject: [PATCH 10/11] adjust content wrapper --- themes/default/application/styles.js | 2 +- themes/default/page/index.js | 6 +++--- themes/default/page/styles.js | 9 +++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/themes/default/application/styles.js b/themes/default/application/styles.js index 57868a4..0ab11b6 100644 --- a/themes/default/application/styles.js +++ b/themes/default/application/styles.js @@ -46,7 +46,7 @@ export const WrapperNav = styled('nav')` max-width: 270px; } - @media (min-width: 1440px) { + @media (min-width: 1450px) { min-width: initial; max-width: initial; } diff --git a/themes/default/page/index.js b/themes/default/page/index.js index 53f1c48..ead7265 100644 --- a/themes/default/page/index.js +++ b/themes/default/page/index.js @@ -4,7 +4,7 @@ import axios from 'axios' import Markdown from '../markdown' import Loading from '../loading' import { ConfigContext } from '../context' -import { Wrapper, TOC } from './styles' +import { Wrapper, ContentWrapper, TOC } from './styles' const TableOfContents = ({ toc }) => { // Don't show this if there aren't enough headers @@ -42,12 +42,12 @@ const Content = ({ content, config, route }) => { } return ( -
    + -
    + ) } diff --git a/themes/default/page/styles.js b/themes/default/page/styles.js index f8bf639..b28887f 100644 --- a/themes/default/page/styles.js +++ b/themes/default/page/styles.js @@ -5,19 +5,20 @@ export const Wrapper = styled('div')` max-width: 1024px; box-sizing: border-box; display: flex; + @media(max-width: 1024px) { flex-direction: column-reverse; nav { margin-left: 0 } } - @media(min-width: 1440px) { + @media(min-width: 1450px) { max-width: 1200px; } ` export const TOC = styled('nav')` margin-left: 2rem; - min-width: 150px; + width: 150px; flex-grow: 0; ul { @@ -51,6 +52,6 @@ export const TOC = styled('nav')` } ` -export const Body = styled('div')` - +export const ContentWrapper = styled('div')` + padding: 0 50px; ` From dad524719a61339a3dd61bf4da2b661eefe65929 Mon Sep 17 00:00:00 2001 From: Zach Sherman Date: Sat, 26 May 2018 16:05:57 -0400 Subject: [PATCH 11/11] update background --- themes/default/application/styles.js | 1 + 1 file changed, 1 insertion(+) diff --git a/themes/default/application/styles.js b/themes/default/application/styles.js index 0ab11b6..405bc66 100644 --- a/themes/default/application/styles.js +++ b/themes/default/application/styles.js @@ -36,6 +36,7 @@ export const Wrapper = styled('div')` export const WrapperNav = styled('nav')` flex: 1; background: linear-gradient(90deg, #F0F2F4 0%, #F5F7F9 100%); + background: #FAFAFD; border-right: 1px solid #E6E9EB; box-shadow: inset -4px 0px 2px -2px rgba(202, 209, 226, 0.2); text-align: right;