diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..2a73b4f
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1,3 @@
+node_modules/
+_assets/
+/index.js
diff --git a/.eslintrc b/.eslintrc
index c306715..b6f4662 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,20 +1,3 @@
{
- "rules": {
- "no-extra-boolean-cast": [ 0 ],
- "indent": [ 2, 4 ],
- "quotes": [ 2, "single" ],
- "linebreak-style": [ 2, "unix" ],
- "semi": [ 2, "always" ],
- "no-unused-vars": [ 2, {
- "vars": "all",
- "args": "none"
- } ],
- "spaced-comment": [ 2, "always" ]
- },
- "env": {
- "node": true,
- "mocha": true,
- "browser": true
- },
- "extends": "eslint:recommended"
-}
\ No newline at end of file
+ "extends": "gitbook"
+}
diff --git a/.gitignore b/.gitignore
index 87a4029..390b049 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,30 +1,5 @@
-# Logs
-logs
-*.log
-
-# Runtime data
-pids
-*.pid
-*.seed
-
-# Directory for instrumented libs generated by jscoverage/JSCover
-lib-cov
-
-# Coverage directory used by tools like istanbul
-coverage
-
-# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
-.grunt
-
-# Compiled binary addons (http://nodejs.org/api/addons.html)
-build/Release
-
# Dependency directory
-# Deployed apps should consider commenting this line out:
-# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
node_modules
-# vim swapfile
-*.swp
-
-assets
+# Plugin assets
+_assets/
diff --git a/.npmignore b/.npmignore
index ec955b5..6fcb8dd 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1,2 +1,2 @@
-# Allow _assets folder
-!assets
+# Publish assets on NPM
+!_assets/
diff --git a/_layouts/website/page.html b/_layouts/website/page.html
deleted file mode 100644
index d62b780..0000000
--- a/_layouts/website/page.html
+++ /dev/null
@@ -1,4 +0,0 @@
-{% extends template.self %}
-
-{% block book_navigation %}
-{% endblock %}
\ No newline at end of file
diff --git a/index.js b/index.js
index a198a6d..0710109 100644
--- a/index.js
+++ b/index.js
@@ -1,108 +1,56 @@
-var _ = require('lodash');
-var Q = require('q-plus');
-var cheerio = require('cheerio');
+var _ = require('lodash');
+var Promise = require('q-plus');
var DEFAULT_LANGUAGES = require('./languages');
var configLanguages = [];
-function generateMethod(book, body, examples) {
- // Main container
- var $ = cheerio.load('
'),
- $apiMethod = $('div.api-method'),
- // Method definition
- $apiDefinition = $(''),
- // Method code
- $apiCode = $('');
-
- // Append elements
- $apiMethod.append($apiDefinition);
- $apiMethod.append($apiCode);
-
- // Render method body
- return Q()
- .then(function() {
- return book.renderBlock('markdown', body);
- })
- .then(function(apiDefinition) {
- $apiDefinition.html(apiDefinition);
-
- // Set method examples
- return Q(examples).eachSeries(function(example) {
- var $example;
-
- // Common text
- if (example.type == 'common') {
- $example = $('');
-
- }
-
- // Example code snippet
- if (example.type == 'sample') {
- $example = $('');
- }
-
- return book.renderBlock('markdown', example.body)
- .then(function(body) {
- $example.html(body);
- $apiCode.append($example);
- });
- });
- })
- .then(function() {
- // Return whole HTML
- return $.html('div.api-method');
- });
-}
-
module.exports = {
- book: {
- assets: './assets',
- js: [
- 'theme-api.js'
- ],
- css: [
- 'theme-api.css'
- ]
- },
-
blocks: {
method: {
- blocks: ['sample', 'common'],
- process: function(blk) {
+ blocks: [ 'sample', 'common' ],
+ process: function(block) {
+ var book = this;
var examples = [];
- _.each(blk.blocks, function(_blk) {
+ return Promise(block.blocks).eachSeries(function(_block) {
var languageName;
// Search if is user-defined language
- if (_blk.name == 'sample') {
+ if (_block.name == 'sample') {
// Sample blocks should have a lang argument
- if (!_blk.kwargs.lang) {
+ if (!_block.kwargs.lang) {
throw Error('sample blocks must provide a "lang" argument');
}
- var language = _.find(configLanguages, { lang: _blk.kwargs.lang });
+ var language = _.find(configLanguages, { lang: _block.kwargs.lang });
if (!!language) {
languageName = language.name;
} else {
// Default to upper-cased lang
- languageName = _blk.kwargs.lang.toUpperCase();
+ languageName = _block.kwargs.lang.toUpperCase();
}
}
- examples.push({
- type: _blk.name,
- body: _blk.body.trim(),
- lang: _blk.kwargs.lang,
- name: languageName
+ return book.renderBlock('markdown', _block.children.trim())
+ .then(function(content) {
+ examples.push({
+ type: _block.name,
+ content: content,
+ language: _block.kwargs.lang,
+ name: languageName
+ });
+ });
+ })
+ .then(function() {
+ return book.renderBlock('markdown', block.children.trim())
+ .then(function(definition) {
+ return {
+ definition: definition,
+ examples: examples
+ };
});
});
-
- return {
- parse: true,
- body: generateMethod(this, blk.body.trim(), examples)
- };
}
}
},
diff --git a/languages.js b/languages.js
index ba5f097..1d64b4d 100644
--- a/languages.js
+++ b/languages.js
@@ -1027,4 +1027,4 @@ module.exports = [
'lang': 'zep',
'name': 'Zephir'
}
-];
\ No newline at end of file
+];
diff --git a/package.json b/package.json
index b81fa05..a210247 100644
--- a/package.json
+++ b/package.json
@@ -3,25 +3,34 @@
"description": "Theme for using GitBook as an API documentation",
"version": "1.1.2",
"engines": {
- "gitbook": ">=3.0.4"
+ "gitbook": ">=4.0.0-alpha"
},
"dependencies": {
- "cheerio": "0.20.0",
- "gitbook-plugin-search": ">=2.0.0",
+ "gitbook-core": "^4.0.0",
"lodash": "4.12.0",
"q": "1.4.1",
- "q-plus": "0.0.8"
+ "q-plus": "0.0.8",
+ "react": "^15.3.1",
+ "react-dom": "^15.3.1"
},
"devDependencies": {
- "eslint": "2.9.0",
- "less": "2.6.0",
+ "classnames": "^2.2.5",
+ "eslint": "^3.7.1",
+ "eslint-config-gitbook": "^1.3.1",
+ "gitbook-plugin": "^4.0.0",
+ "less": "^2.7.1",
"less-plugin-clean-css": "^1.5.1",
"preboot": "git+https://github.com/mdo/preboot.git#4aab4edd85f076d50609cbe28e4fe66cc0771701",
- "uglify-js": "2.6.1"
+ "react-sticky": "5.0.5"
},
"scripts": {
- "prepublish": "./src/build.sh"
+ "build-js": "gitbook-plugin build ./src/index.js ./_assets/plugin.js",
+ "build-css": "./src/build.sh",
+ "prepublish": "npm run build-js && npm run build-css"
},
+ "browser": "./_assets/plugin.js",
+ "ebook": "./_assets/plugin.js",
+ "title": "API Theme",
"repository": {
"type": "git",
"url": "https://github.com/GitbookIO/theme-api.git"
diff --git a/src/actions/index.js b/src/actions/index.js
new file mode 100644
index 0000000..106168e
--- /dev/null
+++ b/src/actions/index.js
@@ -0,0 +1,26 @@
+const ACTIONS_TYPES = require('./types');
+
+/**
+ * Change language from toolbar
+ * @return {Action}
+ */
+function selectLanguage(language) {
+ return {
+ type: ACTIONS_TYPES.SELECT_LANGUAGE,
+ language
+ };
+}
+
+/**
+ * Toggle split display mode
+ */
+function toggleDisplayMode() {
+ return {
+ type: ACTIONS_TYPES.TOGGLE_DISPLAY_MODE
+ };
+}
+
+module.exports = {
+ selectLanguage,
+ toggleDisplayMode
+};
diff --git a/src/actions/types.js b/src/actions/types.js
new file mode 100644
index 0000000..a301e3f
--- /dev/null
+++ b/src/actions/types.js
@@ -0,0 +1,4 @@
+module.exports = {
+ SELECT_LANGUAGE: 'theme-api/language/select',
+ TOGGLE_DISPLAY_MODE: 'theme-api/display/toggle'
+};
diff --git a/src/build.sh b/src/build.sh
index 99b3db5..6bd6995 100755
--- a/src/build.sh
+++ b/src/build.sh
@@ -1,13 +1,14 @@
#! /bin/bash
+echo "Cleaning up folder..."
# Cleanup folder
-rm -rf assets
+rm -rf _assets/website
# Recreate folder
-mkdir -p assets
-
-# Compile JS
-uglifyjs -mc -- src/js/theme-api.js > assets/theme-api.js
+mkdir -p _assets/website
+echo "Compiling LESS sources..."
# Compile Website CSS
-lessc -clean-css src/less/website.less assets/theme-api.css
\ No newline at end of file
+lessc -clean-css src/less/website.less _assets/website/theme-api.css
+
+echo "Done :)"
diff --git a/src/components/BodyWrapper.js b/src/components/BodyWrapper.js
new file mode 100644
index 0000000..23acc4a
--- /dev/null
+++ b/src/components/BodyWrapper.js
@@ -0,0 +1,19 @@
+const GitBook = require('gitbook-core');
+const { React } = GitBook;
+const { StickyContainer } = require('react-sticky');
+
+const BodyWrapper = React.createClass({
+ propTypes: {
+ children: React.PropTypes.node.isRequired
+ },
+
+ render() {
+ return (
+
+ {this.props.children}
+
+ );
+ }
+});
+
+module.exports = BodyWrapper;
diff --git a/src/components/DisplayButton.js b/src/components/DisplayButton.js
new file mode 100644
index 0000000..0aa2151
--- /dev/null
+++ b/src/components/DisplayButton.js
@@ -0,0 +1,25 @@
+const GitBook = require('gitbook-core');
+const { React } = GitBook;
+
+const toggleDisplayMode = require('../actions').toggleDisplayMode;
+
+const DisplayButton = React.createClass({
+ propTypes: {
+ dispatch: React.PropTypes.func.isRequired
+ },
+
+ onClick() {
+ const { dispatch } = this.props;
+ dispatch(toggleDisplayMode());
+ },
+
+ render() {
+ return (
+
+
+
+ );
+ }
+});
+
+module.exports = GitBook.connect(DisplayButton);
diff --git a/src/components/LanguagesButtons.js b/src/components/LanguagesButtons.js
new file mode 100644
index 0000000..2bbcc15
--- /dev/null
+++ b/src/components/LanguagesButtons.js
@@ -0,0 +1,64 @@
+const GitBook = require('gitbook-core');
+const { React } = GitBook;
+const { List } = GitBook.Immutable;
+const classNames = require('classnames');
+const selectLanguage = require('../actions').selectLanguage;
+
+const languageShape = React.PropTypes.shape({
+ lang: React.PropTypes.string.isRequired,
+ name: React.PropTypes.string.isRequired,
+ default: React.PropTypes.bool
+});
+
+const LanguageButton = React.createClass({
+ propTypes: {
+ language: languageShape.isRequired,
+ active: React.PropTypes.bool,
+ onClick: React.PropTypes.func.isRequired
+ },
+
+ render() {
+ const { language, active, onClick } = this.props;
+
+ const className = classNames('ThemeApi-LanguageButton', {
+ 'ThemeApi-ActiveButton': active
+ });
+
+ return (
+ onClick(language.lang)}>
+ { language.name }
+
+ );
+ }
+});
+
+const LanguagesButtons = React.createClass({
+ propTypes: {
+ dispatch: React.PropTypes.func.isRequired,
+ languages: React.PropTypes.arrayOf(languageShape).isRequired,
+ selectedLanguage: React.PropTypes.string.isRequired
+ },
+
+ onButtonClick(language) {
+ const { dispatch } = this.props;
+ dispatch(selectLanguage(language));
+ },
+
+ render() {
+ const { languages, selectedLanguage } = this.props;
+ return (
+
+ { languages.map((language, i) => ) }
+
+ );
+ }
+});
+
+function mapStateToProps({ config, themeApi }) {
+ return {
+ languages: config.getIn(['pluginsConfig', 'theme-api', 'languages'], List()).toJS(),
+ selectedLanguage: themeApi.get('selectedLanguage')
+ };
+}
+
+module.exports = GitBook.connect(LanguagesButtons, mapStateToProps);
diff --git a/src/components/MethodBlock.js b/src/components/MethodBlock.js
new file mode 100644
index 0000000..f81ac5a
--- /dev/null
+++ b/src/components/MethodBlock.js
@@ -0,0 +1,94 @@
+const GitBook = require('gitbook-core');
+const { React } = GitBook;
+
+const exampleShape = React.PropTypes.shape({
+ type: React.PropTypes.string.isRequired,
+ content: React.PropTypes.string,
+ language: React.PropTypes.string,
+ name: React.PropTypes.string
+});
+
+const ApiCommon = React.createClass({
+ propTypes: {
+ content: React.PropTypes.string
+ },
+
+ render() {
+ const { content } = this.props;
+ return (
+
+
+
+ );
+ }
+});
+
+const ApiSample = React.createClass({
+ propTypes: {
+ example: exampleShape,
+ selectedLanguage: React.PropTypes.string
+ },
+
+ render() {
+ const { example, selectedLanguage } = this.props;
+ // Don't display if is not selected language
+ if (selectedLanguage != example.language) {
+ return null;
+ }
+
+ return (
+
+
+
+ );
+ }
+});
+
+const ApiExample = React.createClass({
+ propTypes: {
+ example: exampleShape,
+ selectedLanguage: React.PropTypes.string
+ },
+
+ render() {
+ const { example } = this.props;
+
+ if (example.type == 'common') {
+ return ;
+ }
+ else {
+ return ;
+ }
+ }
+});
+
+const MethodBlock = React.createClass({
+ propTypes: {
+ definition: React.PropTypes.string,
+ examples: React.PropTypes.arrayOf(exampleShape),
+ selectedLanguage: React.PropTypes.string
+ },
+
+ render() {
+ const { definition, examples, selectedLanguage } = this.props;
+
+ return (
+
+
+
+
+
+ { examples.map((example, i) =>
) }
+
+
+ );
+ }
+});
+
+function mapStateToProps({ themeApi }) {
+ return {
+ selectedLanguage: themeApi.get('selectedLanguage')
+ };
+}
+
+module.exports = GitBook.connect(MethodBlock, mapStateToProps);
diff --git a/src/components/PageContainer.js b/src/components/PageContainer.js
new file mode 100644
index 0000000..a56efcb
--- /dev/null
+++ b/src/components/PageContainer.js
@@ -0,0 +1,34 @@
+const GitBook = require('gitbook-core');
+const { React } = GitBook;
+const classNames = require('classnames');
+
+const PageContainer = React.createClass({
+ propTypes: {
+ page: GitBook.PropTypes.Page,
+ split: React.PropTypes.bool
+ },
+
+ render() {
+ const { page, split } = this.props;
+
+ const className = classNames({
+ 'ThemeApi-TwoColumns': split
+ });
+
+ return (
+
+
+
+
+ );
+ }
+});
+
+function mapStateToProps({ themeApi, page }) {
+ return {
+ split: themeApi.get('split'),
+ page
+ };
+}
+
+module.exports = GitBook.connect(PageContainer, mapStateToProps);
diff --git a/src/components/ToolbarWrapper.js b/src/components/ToolbarWrapper.js
new file mode 100644
index 0000000..ea48bbd
--- /dev/null
+++ b/src/components/ToolbarWrapper.js
@@ -0,0 +1,19 @@
+const GitBook = require('gitbook-core');
+const { React } = GitBook;
+const { Sticky } = require('react-sticky');
+
+const ToolbarWrapper = React.createClass({
+ propTypes: {
+ children: React.PropTypes.node.isRequired
+ },
+
+ render() {
+ return (
+
+ {this.props.children}
+
+ );
+ }
+});
+
+module.exports = ToolbarWrapper;
diff --git a/src/index.js b/src/index.js
new file mode 100644
index 0000000..0eb0482
--- /dev/null
+++ b/src/index.js
@@ -0,0 +1,39 @@
+const GitBook = require('gitbook-core');
+const BodyWrapper = require('./components/BodyWrapper');
+const ToolbarWrapper = require('./components/ToolbarWrapper');
+const MethodBlock = require('./components/MethodBlock');
+const PageContainer = require('./components/PageContainer');
+const DisplayButton = require('./components/DisplayButton');
+const LanguagesButtons = require('./components/LanguagesButtons');
+
+const actions = require('./actions');
+const reduce = require('./reducers');
+
+module.exports = GitBook.createPlugin({
+ activate: (dispatch, getState, { Components }) => {
+ dispatch(Components.registerComponent(BodyWrapper, { role: 'body:wrapper' }));
+ dispatch(Components.registerComponent(ToolbarWrapper, { role: 'toolbar:wrapper' }));
+ dispatch(Components.registerComponent(MethodBlock, { role: 'block:method' }));
+ dispatch(Components.registerComponent(PageContainer, { role: 'page:container' }));
+ dispatch(Components.registerComponent(DisplayButton, { role: 'toolbar:buttons:left' }));
+ dispatch(Components.registerComponent(LanguagesButtons, { role: 'toolbar:buttons:right' }));
+
+ // Get default language in config
+ const configLanguages = getState().config.getIn(['pluginsConfig', 'theme-api', 'languages']);
+ let defaultLanguage = configLanguages.find((language) => {
+ return Boolean(language.get('default'));
+ });
+
+ // Or use first language in list
+ if (!defaultLanguage) {
+ defaultLanguage = configLanguages.get(0);
+ }
+
+ // Set as selected language
+ dispatch(actions.selectLanguage(defaultLanguage.get('lang')));
+ },
+ reduce,
+ actions: {
+ ThemeApi: actions
+ }
+});
diff --git a/src/js/theme-api.js b/src/js/theme-api.js
deleted file mode 100644
index b670345..0000000
--- a/src/js/theme-api.js
+++ /dev/null
@@ -1,166 +0,0 @@
-require(['gitbook', 'jquery'], function(gitbook, $) {
- var buttonsId = [],
- $codes,
- themeApi;
-
- // Default themes
- var THEMES = [
- {
- config: 'light',
- text: 'Light',
- id: 0
- },
- {
- config: 'dark',
- text: 'Dark',
- id: 3
- }
- ];
-
- // Instantiate localStorage
- function init(config) {
- themeApi = gitbook.storage.get('themeApi', {
- split: config.split,
- currentLang: null
- });
- }
-
- // Update localStorage settings
- function saveSettings() {
- gitbook.storage.set('themeApi', themeApi);
- updateDisplay();
- }
-
- // Update display
- function updateDisplay() {
- // Update layout
- $('.book').toggleClass('two-columns', themeApi.split);
-
- // Update code samples elements
- $codes = $('.api-method-sample');
- // Display corresponding code snippets
- $codes.each(function() {
- // Show corresponding
- var hidden = !($(this).data('lang') == themeApi.currentLang);
- $(this).toggleClass('hidden', hidden);
- });
- }
-
- // Update code tabs
- function updateCodeTabs() {
- // Remove languages buttons
- gitbook.toolbar.removeButtons(buttonsId);
- buttonsId = [];
-
- // Update code snippets elements
- $codes = $('.api-method-sample');
-
- // Recreate languages buttons
- var languages = [],
- hasCurrentLang = false;
-
- $codes.each(function() {
- var isDefault = false,
- codeLang = $(this).data('lang'),
- codeName = $(this).data('name'),
- exists,
- found;
-
- // Check if is current language
- if (codeLang == themeApi.currentLang) {
- hasCurrentLang = true;
- isDefault = true;
- }
-
- // Check if already added
- exists = $.grep(languages, function(language) {
- return language.name == codeName;
- });
-
- found = !!exists.length;
-
- if (!found) {
- // Add language
- languages.push({
- name: codeName,
- lang: codeLang,
- default: isDefault
- });
- }
- });
-
- // Set languages in good order
- languages.reverse();
- $.each(languages, function(i, language) {
- // Set first (last in array) language as active if no default
- var isDefault = language.default || (!hasCurrentLang && i == (languages.length - 1)),
- buttonId;
-
- // Create button
- buttonId = gitbook.toolbar.createButton({
- text: language.name,
- position: 'right',
- className: 'lang-switcher' + (isDefault? ' active': ''),
- onClick: function(e) {
- // Update language
- themeApi.currentLang = language.lang;
- saveSettings();
-
- // Update active button
- $('.btn.lang-switcher.active').removeClass('active');
- $(e.currentTarget).addClass('active');
- }
- });
-
- // Add to list of buttons
- buttonsId.push(buttonId);
-
- // Set as current language if is default
- if (isDefault) {
- themeApi.currentLang = language.lang;
- }
- });
- }
-
- // Initialization
- gitbook.events.bind('start', function(e, config) {
- var opts = config['theme-api'];
-
- // Create layout button in toolbar
- gitbook.toolbar.createButton({
- icon: 'fa fa-columns',
- label: 'Change Layout',
- onClick: function() {
- // Update layout
- themeApi.split = !themeApi.split;
- saveSettings();
- }
- });
-
- // Initialize themes
- gitbook.fontsettings.setThemes(THEMES);
-
- // Set to configured theme
- gitbook.fontsettings.setTheme(opts.theme);
-
- // Init current settings
- init(opts);
- });
-
- // Update state
- gitbook.events.on('page.change', function() {
- updateCodeTabs();
- // updateComments();
- updateDisplay();
- });
-
- // Comments toggled event
- gitbook.events.on('comment.toggled', function(e, $from, open) {
- // If triggering element is in a definition
- if (!!$from.parents('.api-method-definition').length) {
- // Add class to wrapper only if comments are open and in two-columns mode
- var $wrapper = gitbook.state.$book.find('.page-wrapper');
- $wrapper.toggleClass('comments-open-from-definition', open && themeApi.split);
- }
- });
-});
diff --git a/src/less/website.less b/src/less/website.less
index e24b604..347aa55 100755
--- a/src/less/website.less
+++ b/src/less/website.less
@@ -2,21 +2,20 @@
@import "website/mixins.less";
@import "website/variables.less";
-@import "website/api-method.less";
+@import "website/ApiMethod.less";
+@import "website/Toolbar.less";
@import "website/header.less";
-@import "website/langswitcher.less";
+@import "website/LanguageButton.less";
@import "website/search.less";
@import "website/comments.less";
@import "themes/themes.less";
-.page-inner {
+.PageContainer {
max-width: 100%;
padding: 0;
- margin-top: @header-height;
}
-.markdown-section {
- padding: @markdown-section-padding @api-method-padding 0;
+.Page {
+ padding-left: @api-method-padding + 10;
+ padding-right: @api-method-padding + 10;
}
-
-
diff --git a/src/less/website/ApiMethod.less b/src/less/website/ApiMethod.less
new file mode 100644
index 0000000..bf36733
--- /dev/null
+++ b/src/less/website/ApiMethod.less
@@ -0,0 +1,63 @@
+.ThemeApi-ApiMethod {
+ margin: @api-method-padding -@api-method-padding;
+
+ &:last-of-type {
+ margin-bottom: 0;
+ }
+
+ .ThemeApi-ApiMethodDefinition {
+ padding: 0 @api-method-padding;
+ }
+
+ .ThemeApi-ApiMethodCode {
+ padding: @api-method-padding @api-method-padding @api-method-padding/2;
+ background-color: @api-code-background;
+ border-top: @api-method-border;
+ border-bottom: @api-method-border;
+
+ pre>code {
+ .white-space(pre-wrap);
+ word-wrap: break-word;
+ }
+ }
+
+ &:after {
+ clear: both;
+ }
+}
+
+.ThemeApi-TwoColumns {
+ .ThemeApi-ApiMethod {
+ position: relative;
+ width: calc(~"100% +" 2*@api-method-padding);
+
+ &:after {
+ content: " ";
+ display: block;
+ visibility: hidden;
+ clear: both;
+ }
+
+ .ThemeApi-ApiMethodDefinition {
+ float: left;
+ width: 50%;
+ }
+
+ .ThemeApi-ApiMethodCode {
+ position: relative;
+ float: left;
+ width: 50%;
+ padding: @api-method-padding @api-method-padding @api-method-padding/2;
+ border-left: @api-method-border;
+ border-top: none;
+ border-bottom: none;
+ transition: opacity 300ms ease;
+ }
+ }
+
+ .page-wrapper.comments-open-from-definition {
+ .ThemeApi-ApiMethodCode {
+ opacity: 0.1;
+ }
+ }
+}
diff --git a/src/less/website/LanguageButton.less b/src/less/website/LanguageButton.less
new file mode 100644
index 0000000..c77cd96
--- /dev/null
+++ b/src/less/website/LanguageButton.less
@@ -0,0 +1,10 @@
+.ThemeApi-LanguageButton {
+ text-transform: none;
+ font-weight: 500;
+ border-radius: 0;
+
+ &.ThemeApi-ActiveButton {
+ background-color: @language-button-active-bg-color;
+ color: @language-button-active-color;
+ }
+}
diff --git a/src/less/website/Toolbar.less b/src/less/website/Toolbar.less
new file mode 100644
index 0000000..fc8332c
--- /dev/null
+++ b/src/less/website/Toolbar.less
@@ -0,0 +1,4 @@
+.ThemeApi-Toolbar {
+ background-color: white;
+ z-index: 300;
+}
diff --git a/src/less/website/api-method.less b/src/less/website/api-method.less
deleted file mode 100644
index 334d4b4..0000000
--- a/src/less/website/api-method.less
+++ /dev/null
@@ -1,69 +0,0 @@
-.api-method {
- margin: @api-method-padding -@api-method-padding;
-
- &:last-of-type {
- margin-bottom: 0;
- }
-
- .api-method-definition {
- padding: 0 @api-method-padding;
- }
-
- .api-method-code {
- padding: @api-method-padding @api-method-padding @api-method-padding/2;
- background-color: @api-code-background;
- border-top: @api-method-border;
- border-bottom: @api-method-border;
-
- pre>code {
- .white-space(pre-wrap);
- word-wrap: break-word;
- }
- }
-
- &:after {
- clear: both;
- }
-}
-
-.book {
- &.two-columns {
- .api-method {
- position: relative;
- width: calc(~"100% +" 2*@api-method-padding);
-
- &:after {
- content: " ";
- display: block;
- visibility: hidden;
- clear: both;
- }
-
- .api-method-title {
- margin-top: 0;
- }
-
- .api-method-definition {
- float: left;
- width: 50%;
- }
-
- .api-method-code {
- position: relative;
- float: left;
- width: 50%;
- padding: @api-method-padding @api-method-padding @api-method-padding/2;
- border-left: @api-method-border;
- border-top: none;
- border-bottom: none;
- transition: opacity 300ms ease;
- }
- }
-
- .page-wrapper.comments-open-from-definition {
- .api-method-code {
- opacity: 0.1;
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/less/website/langswitcher.less b/src/less/website/langswitcher.less
deleted file mode 100644
index b249042..0000000
--- a/src/less/website/langswitcher.less
+++ /dev/null
@@ -1,11 +0,0 @@
-.book-header .btn.lang-switcher {
- /*color: @button-hover-color;*/
- text-transform: none;
- font-weight: 500;
- border-radius: 0;
-
- &.active {
- background-color: @lang-switcher-active-bg-color;
- color: @lang-switcher-active-color;
- }
-}
\ No newline at end of file
diff --git a/src/less/website/variables.less b/src/less/website/variables.less
index 0ab25d2..a5a5b04 100644
--- a/src/less/website/variables.less
+++ b/src/less/website/variables.less
@@ -7,9 +7,9 @@
@api-code-background: @color-gray-lightest;
@api-method-border: 1px solid @api-soft-grey;
-// Language switchers
-@lang-switcher-active-bg-color: #03677D;
-@lang-switcher-active-color: #FFF;
+// Language buttons
+@language-button-active-bg-color: #03677D;
+@language-button-active-color: #FFF;
// Page
@markdown-section-padding: 20px;
@@ -22,4 +22,4 @@
// Sidebar
@sidebar-transition-duration: 250ms;
@sidebar-width: 300px;
-@sidebar-breakpoint: 600px;
\ No newline at end of file
+@sidebar-breakpoint: 600px;
diff --git a/src/reducers/index.js b/src/reducers/index.js
new file mode 100644
index 0000000..7e9aede
--- /dev/null
+++ b/src/reducers/index.js
@@ -0,0 +1,3 @@
+const GitBook = require('gitbook-core');
+
+module.exports = GitBook.createReducer('themeApi', require('./themeApi'));
diff --git a/src/reducers/themeApi.js b/src/reducers/themeApi.js
new file mode 100644
index 0000000..923942c
--- /dev/null
+++ b/src/reducers/themeApi.js
@@ -0,0 +1,25 @@
+const GitBook = require('gitbook-core');
+const { Record } = GitBook.Immutable;
+
+const ACTIONS_TYPES = require('../actions/types');
+
+const ThemeApiState = Record({
+ // current displayed language
+ selectedLanguage: String(''),
+ // split display
+ split: Boolean(true)
+});
+
+module.exports = (state = ThemeApiState(), action) => {
+ switch (action.type) {
+
+ case ACTIONS_TYPES.SELECT_LANGUAGE:
+ return state.set('selectedLanguage', action.language);
+
+ case ACTIONS_TYPES.TOGGLE_DISPLAY_MODE:
+ return state.set('split', !state.get('split'));
+
+ default:
+ return state;
+ }
+};