Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
KELiON committed Feb 10, 2017
0 parents commit 8478650
Show file tree
Hide file tree
Showing 130 changed files with 849 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"plugins": [
"transform-react-jsx",
"transform-object-rest-spread",
"transform-es2015-destructuring",
"syntax-jsx"
]
}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
dist
6 changes: 6 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
src
.babelrc
node_modules
scripts
screenshot.gif
webpack.config.js
24 changes: 24 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# cerebro-devdocs

> [Cerebro](http://www.cerebroapp.com) plugin for devdocs.io documentations
![](screenshot.gif)

## Usage

First, you need to select documentations that you want to use. Type `devdocs` and select "DevDocs Settings". Choose all documentations that you want to use. I.e. you've chosen ruby documentation. Now you can type something like `ruby =~` to find what you need.

## Features
* More than 200 supported documentations;
* Offline access;
* Fast fuzzy-search;
* Navigation inside docs;

## Related

- [Cerebro](http://github.com/KELiON/cerebro) – main repo for Cerebro app;
- [devdocs.io](http://devdocs.io)

## License

MIT © [Alexandr Subbotin](http://asubbotin.ru)
43 changes: 43 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "cerebro-devdocs",
"version": "0.0.1",
"description": "Cerebro plugin for devdocs.io documentations",
"license": "MIT",
"repository": "KELiON/cerebro-devdocs",
"author": {
"name": "Alexandr Subbotin",
"email": "[email protected]",
"url": "asubbotin.ru"
},
"engines": {
"node": ">=4"
},
"scripts": {
"build": "webpack && babili dist -d dist --compact --no-comments",
"debug": "./scripts/debug",
"prepublish": "rm -rf ./dist && npm run build"
},
"main": "dist/index.js",
"keywords": [
"cerebro",
"cerebro-plugin"
],
"devDependencies": {
"babel-cli": "^6.18.0",
"babel-loader": "^6.2.8",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-es2015-destructuring": "^6.22.0",
"babel-plugin-transform-object-rest-spread": "^6.22.0",
"babel-plugin-transform-react-jsx": "^6.8.0",
"babili": "0.0.9",
"cerebro-tools": "^0.1.0",
"css-loader": "^0.26.0",
"fuse.js": "^2.6.1",
"lodash": "^4.17.4",
"raw-loader": "^0.5.1",
"react": "^15.4.1",
"style-loader": "^0.13.1",
"url-loader": "^0.5.7",
"webpack": "2.1.0-beta.27"
}
}
Binary file added screenshot.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions scripts/debug
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/sh
if [[ $1 == "dev" ]]; then
appname="Electron"
else
appname="Cerebro"
fi

case "$(uname -s)" in

Darwin)
symlink="${HOME}/Library/Application Support/${appname}/plugins/node_modules/${PWD##*/}"
;;

Linux)
symlink="${HOME}/.config/${appname}/plugins/node_modules/${PWD##*/}"
;;

CYGWIN*|MINGW32*|MINGW64*|MSYS*)
symlink="${APPDATA}\\${appname}\plugins\node_modules\\${PWD##*/}"
;;

*)
echo "Unknown system. Please, open an issue in https://github.com/KELiON/cerebro-plugin/issues"
exit
;;
esac

echo "Creating symlink: $symlink -> ${PWD}"
ln -s "${PWD}" "$symlink"
trap "echo 'Deleting symlink' && rm -rf \"$symlink\"" SIGHUP SIGINT SIGTERM
./node_modules/.bin/webpack --watch
10 changes: 10 additions & 0 deletions src/Preview/Doc/devdocs_styles

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions src/Preview/Doc/formatContent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const iframeStyles = require('raw-loader!./devdocs_styles')

module.exports = (content) => (
`<html>
<head>
<style>${iframeStyles}</style>
</head>
<body>
<main class="_content" role="main" tabindex="1">
${content}
</main>
</body>
</html>`
)
33 changes: 33 additions & 0 deletions src/Preview/Doc/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const React = require('react')
const styles = require('./styles.css')
const format = require('./formatContent')
const each = require('lodash/each')

class Doc extends React.Component {
componentDidMount() {
if (!this._iframe) return
const { anchor, onNavigate } = this.props
const { contentWindow } = this._iframe
contentWindow.addEventListener('load', () => {
if (anchor) {
contentWindow.location.hash = anchor
}

each(this._iframe.contentDocument.links, (link) => {
link.addEventListener('click', (e) => {
const href = e.currentTarget.getAttribute('href')
if (href) {
onNavigate(href)
e.preventDefault()
}
})
})
})
}
render() {
const { content, anchor } = this.props
return <iframe ref={(el) => this._iframe = el } srcDoc={format(content)} className={styles.preview} />
}
}

module.exports = Doc
6 changes: 6 additions & 0 deletions src/Preview/Doc/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.preview {
border: 0;
width: 100%;
height: 100%;
align-self: flex-start;
}
39 changes: 39 additions & 0 deletions src/Preview/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const React = require('react')
const Doc = require('./Doc')
const db = require('../db')
const url = require('url')
const { shell } = require('electron')

class Preview extends React.Component {
constructor(props) {
super(props)
this.onNavigate = this.onNavigate.bind(this)
this.state = {
path: props.path
}
}
onNavigate(newPath) {
if (url.parse(newPath).host) {
// Open external url in default browser
return shell.openExternal(newPath)
}
this.setState(state => ({
path: url.resolve(state.path, newPath)
}))
}
render() {
const path = this.state.path
const anchor = url.parse(path).hash
const content = db.getContent(path)
return (
<Doc
key={content}
anchor={anchor}
content={content}
onNavigate={this.onNavigate}
/>
)
}
}

module.exports = Preview
18 changes: 18 additions & 0 deletions src/Settings/KeyboardNav/focusableSelector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* DOM selector for all focusable elements
* @type {Array}
*/
module.exports = [
// Links & areas with href attribute
'a[href]',
'area[href]',

// Not disabled form elements
'button:not([disabled])',
'input:not([disabled])',
'select:not([disabled])',
'textarea:not([disabled])',

// All elements with tabindex >= 0
'[tabindex]:not([tabindex^="-"])'
].join(', ')
81 changes: 81 additions & 0 deletions src/Settings/KeyboardNav/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
const React = require('react')
const focusableSelector = require('./focusableSelector')

/**
* Focus element with index from elements array.
* If `index` >= `elements.length` then first element is selected;
* If `index` <= 0 then last element is selected.
*
* @param {Array<DOMElement>} elements
* @param {Integer} index
*/
const moveSelectionTo = (elements, index) => {
let nextIndex = index
if (index < 0) {
nextIndex = elements.length - 1
} else if (index >= elements.length) {
nextIndex = 0
}
elements[nextIndex].focus()
}

/**
* Handler keydown in keyboard navigation component
*
* @param {DOMElement} wrapper
* @param {KeyboardEvent} event
*/
const onKeyDown = (wrapper, event) => {
const { target, keyCode } = event
if (keyCode === 37) {
// Move control back to main list when ← is clicked
const mainInput = document.querySelector('#main-input')
const position = mainInput.value.length
mainInput.focus()
mainInput.setSelectionRange(position, position)
event.preventDefault()
event.stopPropagation()
return false
}
if (keyCode !== 40 && keyCode !== 38) {
return false
}

// Get all focusable element in element
const focusable = wrapper.querySelectorAll(focusableSelector)

// Get index of currently focused element
const index = Array.prototype.findIndex.call(focusable, (el) => el === target)

if (keyCode === 40) {
// Select next focusable element when arrow down clicked
moveSelectionTo(focusable, index + 1)
event.stopPropagation()
} else if (keyCode === 38) {
// Select previous focusable element when arrow down clicked
moveSelectionTo(focusable, index - 1)
event.stopPropagation()
}
}

class KeyboardNav extends React.Component {
onKeyDown(event) {
onKeyDown(this.wrapper, event)
}
render() {
return (
<div onKeyDown={this.onKeyDown.bind(this)} ref={(el) => { this.wrapper = el }}>
{this.props.children}
</div>
)
}
}

KeyboardNav.propTypes = {
children: React.PropTypes.oneOfType([
React.PropTypes.element,
React.PropTypes.arrayOf(React.PropTypes.element),
])
}

module.exports = KeyboardNav
36 changes: 36 additions & 0 deletions src/Settings/KeyboardNavItem/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const React = require('react')
const styles = require('./styles.css')

const KeyboardNavItem = (props) => {
let className = styles.item
className += props.className ? ` ${props.className}` : ''
const onSelect = props.onSelect || (() => {})
const onClick = onSelect
const onKeyDown = (event) => {
if (props.onKeyDown) {
props.onKeyDown(event)
}
if (!event.defaultPrevented && event.keyCode === 13) {
onSelect()
}
}
const itemProps = {
className,
onClick,
onKeyDown,
tabIndex: 0,
}
const TagName = props.tagName ? props.tagName : 'div'
return (
<TagName {...props} {...itemProps} />
)
}

KeyboardNavItem.propTypes = {
className: React.PropTypes.string,
tagName: React.PropTypes.string,
onSelect: React.PropTypes.func,
onKeyDown: React.PropTypes.func,
}

module.exports = KeyboardNavItem
20 changes: 20 additions & 0 deletions src/Settings/KeyboardNavItem/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.item {
cursor: pointer;
border-bottom: var(--main-border);
background: var(--result-background);
color: var(--result-title-color);
padding: 0 5px;
height: 35px;
display: flex;
align-items: center;
}

.item:last-child {
border-bottom: 0;
}

.item:focus {
outline: none;
background: var(--selected-result-background);
color: var(--selected-result-title-color);
}
Loading

0 comments on commit 8478650

Please sign in to comment.