mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Migrate UI Framework source into Kibana.
- Add dependencies to package.json. - Add task for building UI Framework docs and serving locally. - Import UI Framework scss from autoload/styles.js instead of importing the CSS in base.less.
This commit is contained in:
parent
dd46f75b6e
commit
de3f34f8d5
59 changed files with 2599 additions and 5 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -33,3 +33,4 @@ selenium
|
|||
*.swp
|
||||
*.swo
|
||||
*.out
|
||||
src/ui_framework/public/*.js*
|
||||
|
|
23
package.json
23
package.json
|
@ -62,7 +62,8 @@
|
|||
"makelogs": "makelogs",
|
||||
"mocha": "mocha",
|
||||
"mocha:debug": "mocha --debug-brk",
|
||||
"sterilize": "grunt sterilize"
|
||||
"sterilize": "grunt sterilize",
|
||||
"uiFramework:start": "./node_modules/.bin/webpack-dev-server --config src/ui_framework/webpack.config.js --hot --inline --content-base src/ui_framework/public/"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -171,6 +172,8 @@
|
|||
"cheerio": "0.22.0",
|
||||
"chokidar": "1.6.0",
|
||||
"chromedriver": "2.24.1",
|
||||
"classnames": "2.2.5",
|
||||
"del": "1.2.1",
|
||||
"elasticdump": "2.1.1",
|
||||
"eslint": "1.10.3",
|
||||
"eslint-plugin-mocha": "1.1.0",
|
||||
|
@ -190,6 +193,9 @@
|
|||
"gruntify-eslint": "1.0.1",
|
||||
"gulp-sourcemaps": "1.7.3",
|
||||
"handlebars": "4.0.5",
|
||||
"highlight.js": "9.0.0",
|
||||
"history": "2.1.1",
|
||||
"html-loader": "0.4.3",
|
||||
"husky": "0.8.1",
|
||||
"image-diff": "1.6.0",
|
||||
"intern": "3.2.3",
|
||||
|
@ -201,6 +207,7 @@
|
|||
"karma-ie-launcher": "0.2.0",
|
||||
"karma-mocha": "0.2.0",
|
||||
"karma-safari-launcher": "0.1.1",
|
||||
"keymirror": "0.1.1",
|
||||
"license-checker": "5.1.2",
|
||||
"load-grunt-config": "0.19.2",
|
||||
"makelogs": "3.0.2",
|
||||
|
@ -209,15 +216,27 @@
|
|||
"murmurhash3js": "3.0.1",
|
||||
"ncp": "2.0.0",
|
||||
"nock": "8.0.0",
|
||||
"node-sass": "3.8.0",
|
||||
"npm": "3.10.8",
|
||||
"numeral": "1.5.3",
|
||||
"portscanner": "1.0.0",
|
||||
"proxyquire": "1.7.10",
|
||||
"react": "15.2.0",
|
||||
"react-addons-test-utils": "15.2.0",
|
||||
"react-dom": "15.2.0",
|
||||
"react-redux": "4.4.5",
|
||||
"react-router": "2.0.0",
|
||||
"react-router-redux": "4.0.4",
|
||||
"redux": "3.0.0",
|
||||
"redux-thunk": "0.1.0",
|
||||
"sass-loader": "4.0.0",
|
||||
"simple-git": "1.37.0",
|
||||
"sinon": "1.17.2",
|
||||
"source-map": "0.5.6",
|
||||
"source-map-support": "0.2.10",
|
||||
"supertest": "1.2.0",
|
||||
"supertest-as-promised": "2.0.2"
|
||||
"supertest-as-promised": "2.0.2",
|
||||
"webpack-dev-server": "1.14.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "6.9.0",
|
||||
|
|
|
@ -108,6 +108,10 @@ class BaseOptimizer {
|
|||
`css${mapQ}!autoprefixer${mapQPre}{ "browsers": ["last 2 versions","> 5%"] }!less${mapQPre}dumpLineNumbers=comments`
|
||||
)
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
loaders: ['style', 'css', 'sass'],
|
||||
},
|
||||
{ test: /\.css$/, loader: ExtractTextPlugin.extract('style', `css${mapQ}`) },
|
||||
{ test: /\.jade$/, loader: 'jade' },
|
||||
{ test: /\.json$/, loader: 'json' },
|
||||
|
|
|
@ -1,2 +1,6 @@
|
|||
// Kibana UI Framework
|
||||
require('../../../ui_framework/src/framework/framework.scss');
|
||||
|
||||
// All Kibana styles inside of the /styles dir
|
||||
const context = require.context('../styles', false, /[\/\\](?!mixins|variables|_|\.)[^\/\\]+\.less/);
|
||||
context.keys().forEach(key => context(key));
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
@import (reference) "./variables";
|
||||
@import (reference) "~ui/styles/bootstrap/bootstrap";
|
||||
|
||||
// Kibana UI Framework
|
||||
@import (less) "~@elastic/kibana-ui-framework/dist/framework.css";
|
||||
|
||||
html,
|
||||
body {
|
||||
.flex-parent();
|
||||
|
|
57
src/ui_framework/README.md
Normal file
57
src/ui_framework/README.md
Normal file
|
@ -0,0 +1,57 @@
|
|||
# Kibana UI Framework
|
||||
|
||||
## Development
|
||||
|
||||
* Start development server `npm run uiFramework:start`.
|
||||
* View docs on `http://localhost:8080/`.
|
||||
|
||||
## What is this?
|
||||
|
||||
The UI Framework provides you with UI components you can quickly use to build UIs, as well as interactive examples which document how they're supposed to be used. These UI components are currently only implemented in CSS and markup, but eventually they'll grow to involve JS as well.
|
||||
|
||||
When you build a UI using this framework (e.g. a plugin's UI), you can rest assured it will fit into the overall Kibana UI.
|
||||
|
||||
## Benefits
|
||||
|
||||
### Dynamic, interactive documentation
|
||||
|
||||
By having a "living style guide", we relieve our designers of the burden of creating and maintaining static style guides. This also makes it easier for our engineers to translate mockups, prototypes, and wireframes into products.
|
||||
|
||||
### Copy-pasteable UI
|
||||
|
||||
Engineers can copy and paste sample code into their projects to quickly get reliable, consistent results.
|
||||
|
||||
### Remove CSS from the day-to-day
|
||||
|
||||
The CSS portion of this framework means engineers don't need to spend mental cycles. These cycles can be spent on the things critical to the identity of the specific project they're working on, like architecture and business logic.
|
||||
|
||||
Once this framework also provides JS components, engineers won't even need to _see_ CSS -- it will be encapsulated behind the JS components' interfaces.
|
||||
|
||||
### More UI tests === fewer UI bugs
|
||||
|
||||
By covering our UI components with great unit tests and having those tests live within the framework itself, we can rest assured that our UI layer is tested and remove some of that burden from out integration/end-to-end tests.
|
||||
|
||||
## Why not just use Bootstrap?
|
||||
|
||||
In short: we've outgrown it! Third-party CSS frameworks like Bootstrap and Foundation are designed
|
||||
for a general audience, so they offer things we don't need and _don't_ offer things we _do_ need.
|
||||
As a result, we've forced to override their styles until the original framework is no longer
|
||||
recognizable. When the CSS reaches that point, it's time to take ownership over it and build
|
||||
your own framework.
|
||||
|
||||
We also gain the ability to fix some of the common issues with third-party CSS frameworks:
|
||||
|
||||
* They have non-semantic markup.
|
||||
* They deeply nest their selectors.
|
||||
|
||||
For a more in-depth analysis of the problems with Bootstrap (and similar frameworks), check out this article and the links it has at the bottom: ["Bootstrap Bankruptcy"](http://www.matthewcopeland.me/blog/2013/11/04/bootstrap-bankruptcy/).
|
||||
|
||||
## Examples of other in-house UI frameworks
|
||||
|
||||
* [Ubiquiti CSS Framework](http://ubnt-css.herokuapp.com/#/app/popover)
|
||||
* [Smaato React UI Framework](http://smaato.github.io/ui-framework/#/modal)
|
||||
* [Lonely Planet Style Guide](http://rizzo.lonelyplanet.com/styleguide/design-elements/colours)
|
||||
* [MailChimp Patterns Library](http://ux.mailchimp.com/patterns)
|
||||
* [Salesforce Lightning Design System](https://www.lightningdesignsystem.com/)
|
||||
* [Refills](http://refills.bourbon.io/)
|
||||
* [Formstone](https://formstone.it/)
|
13
src/ui_framework/public/index.html
Normal file
13
src/ui_framework/public/index.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF8">
|
||||
<link href='https://fonts.googleapis.com/css?family=Lato:300,400,700|Ubuntu+Mono:400' rel='stylesheet' type='text/css'>
|
||||
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet" type="text/css">
|
||||
<script src="https://use.fontawesome.com/a9649923ac.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="guide" style="height: 100%"></div>
|
||||
<script src="bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,48 @@
|
|||
// Normal colors
|
||||
$localNavTextColor: $textColor;
|
||||
$localNavBackgroundColor: #e4e4e4;
|
||||
$localNavButtonTextColor: #5a5a5a;
|
||||
$localNavButtonTextColor-isHover: #000000;
|
||||
$localNavButtonBackgroundColor: transparent;
|
||||
$localNavButtonBackgroundColor-isHover: rgba(#000000, 0.1);
|
||||
$localNavButtonBackgroundColor-isSelected: #f6f6f6;
|
||||
$localNavBreadcrumbDelimiterColor: #5a5a5a;
|
||||
$localSearchBackgroundColor: #ffffff;
|
||||
$localSearchBorderColor-isInvalid: #e74C3c;
|
||||
$localDropdownBackgroundColor: $localNavButtonBackgroundColor-isSelected;
|
||||
$localDropdownFormNoteTextColor: #737373;
|
||||
$localTabTextColor: $localNavButtonTextColor;
|
||||
$localTabTextColor-isHover: $localNavButtonTextColor-isHover;
|
||||
$localTabTextColor-isSelected: $localNavButtonTextColor-isHover;
|
||||
|
||||
// Dark theme colors
|
||||
$localNavTextColor--darkTheme: $textColor--darkTheme;
|
||||
$localNavBackgroundColor--darkTheme: #333333;
|
||||
$localNavButtonTextColor--darkTheme: #dedede;
|
||||
$localNavButtonTextColor-isHover--darkTheme: #ffffff;
|
||||
$localNavButtonBackgroundColor-isHover--darkTheme: #000000;
|
||||
$localNavButtonBackgroundColor-isSelected--darkTheme: #525252;
|
||||
$localNavBreadcrumbDelimiterColor--darkTheme: #a5a5a5;
|
||||
$localSearchBackgroundColor--darkTheme: #4e4e4e;
|
||||
$localSearchBorderColor-isInvalid--darkTheme: #ff6758;
|
||||
$localDropdownBackgroundColor--darkTheme: $localNavButtonBackgroundColor-isSelected--darkTheme;
|
||||
$localDropdownFormNoteTextColor--darkTheme: #a2a2a2;
|
||||
$localDropdownWarningTextColor--darkTheme: $textColor--darkTheme;
|
||||
$localDropdownWarningBackgroundColor--darkTheme: #636363;
|
||||
$localTabTextColor--darkTheme: $localNavButtonTextColor--darkTheme;
|
||||
$localTabTextColor-isHover--darkTheme: $localNavButtonTextColor-isHover--darkTheme;
|
||||
$localTabTextColor-isSelected--darkTheme: $localNavButtonTextColor-isHover--darkTheme;
|
||||
|
||||
// Spacing
|
||||
$localNavSideSpacing: 10px;
|
||||
|
||||
// Font size
|
||||
$localNavFontSizeNormal: 14px;
|
||||
|
||||
@import "local_breadcrumbs";
|
||||
@import "local_dropdown";
|
||||
@import "local_menu";
|
||||
@import "local_nav";
|
||||
@import "local_search";
|
||||
@import "local_tabs";
|
||||
@import "local_title";
|
|
@ -0,0 +1,58 @@
|
|||
|
||||
/**
|
||||
* 1. Breadcrumbs are placed in the top-left corner and need to be bumped over
|
||||
* a bit.
|
||||
*/
|
||||
.localBreadcrumbs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
padding-left: $localNavSideSpacing; /* 1 */
|
||||
}
|
||||
|
||||
.localBreadcrumb {
|
||||
& + & {
|
||||
margin-left: 6px;
|
||||
|
||||
&:before {
|
||||
content: '/';
|
||||
user-select: none;
|
||||
margin-right: 4px;
|
||||
color: $localNavBreadcrumbDelimiterColor;
|
||||
|
||||
@include darkTheme {
|
||||
color: $localNavBreadcrumbDelimiterColor--darkTheme;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
.localBreadcrumb__link {
|
||||
pointer-events: none;
|
||||
color: $localNavTextColor;
|
||||
|
||||
@include darkTheme {
|
||||
color: $localNavTextColor--darkTheme;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.localBreadcrumb__link {
|
||||
font-size: $localNavFontSizeNormal;
|
||||
color: #5a5a5a;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@include darkTheme {
|
||||
color: $localNavButtonTextColor--darkTheme;
|
||||
}
|
||||
}
|
||||
|
||||
.localBreadcrumb__emphasis {
|
||||
font-weight: 700;
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
|
||||
.localDropdown {
|
||||
padding: 10px $localNavSideSpacing 14px;
|
||||
background-color: $localDropdownBackgroundColor;
|
||||
line-height: 20px;
|
||||
|
||||
@include darkTheme {
|
||||
background-color: $localDropdownBackgroundColor--darkTheme;
|
||||
}
|
||||
}
|
||||
|
||||
.localDropdownPanels {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.localDropdownPanel {
|
||||
flex: 1 1 0%;
|
||||
}
|
||||
|
||||
.localDropdownPanel--left {
|
||||
margin-right: 30px;
|
||||
}
|
||||
|
||||
.localDropdownPanel--right {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
.localDropdownTitle {
|
||||
margin-bottom: 12px;
|
||||
font-size: 18px;
|
||||
color: $localNavTextColor;
|
||||
|
||||
@include darkTheme {
|
||||
color: $localNavTextColor--darkTheme;
|
||||
}
|
||||
}
|
||||
|
||||
.localDropdownSection {
|
||||
margin-bottom: 16px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.localDropdownHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.localDropdownHeader__label {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: $localNavTextColor;
|
||||
|
||||
@include darkTheme {
|
||||
color: $localNavTextColor--darkTheme;
|
||||
}
|
||||
}
|
||||
|
||||
.localDropdownHeader__actions {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.localDropdownHeader__action {
|
||||
color: $linkColor;
|
||||
font-size: 12px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
|
||||
& + & {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
color: $linkColor-isHover;
|
||||
}
|
||||
|
||||
@include darkTheme {
|
||||
color: $linkColor--darkTheme;
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
color: $linkColor-isHover--darkTheme;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.localDropdownInput {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-bottom: 12px;
|
||||
padding: 5px 15px;
|
||||
font-size: 14px;
|
||||
color: $inputTextColor;
|
||||
background-color: $inputBackgroundColor;
|
||||
border: 2px solid $inputBorderColor;
|
||||
border-radius: 4px;
|
||||
|
||||
@include darkTheme {
|
||||
color: $inputTextColor--darkTheme;
|
||||
background-color: $inputBackgroundColor--darkTheme;
|
||||
border-color: $inputBorderColor--darkTheme;
|
||||
}
|
||||
}
|
||||
|
||||
.localDropdownFormNote {
|
||||
font-size: 14px;
|
||||
color: $localDropdownFormNoteTextColor;
|
||||
|
||||
@include darkTheme {
|
||||
color: $localDropdownFormNoteTextColor--darkTheme;
|
||||
}
|
||||
}
|
||||
|
||||
.localDropdownWarning {
|
||||
margin-bottom: 16px;
|
||||
padding: 6px 10px;
|
||||
font-size: 14px;
|
||||
color: $textColor;
|
||||
background-color: $localNavBackgroundColor;
|
||||
|
||||
@include darkTheme {
|
||||
color: $localDropdownWarningTextColor--darkTheme;
|
||||
background-color: $localDropdownWarningBackgroundColor--darkTheme;
|
||||
}
|
||||
}
|
||||
|
||||
.localDropdownHelpText {
|
||||
margin-bottom: 16px;
|
||||
font-size: 14px;
|
||||
color: #2D2D2D;
|
||||
|
||||
@include darkTheme {
|
||||
color: #9e9e9e;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
|
||||
.localMenu {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.localMenuItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
padding: 0 $localNavSideSpacing;
|
||||
font-size: $localNavFontSizeNormal;
|
||||
background-color: $localNavButtonBackgroundColor;
|
||||
color: $localNavButtonTextColor;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: $localNavButtonBackgroundColor-isHover;
|
||||
color: $localNavButtonTextColor-isHover;
|
||||
}
|
||||
|
||||
&.localMenuItem-isSelected {
|
||||
background-color: $localNavButtonBackgroundColor-isSelected;
|
||||
}
|
||||
|
||||
&.localMenuItem-isDisabled {
|
||||
opacity: 0.5;
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@include darkTheme {
|
||||
color: $localNavButtonTextColor--darkTheme;
|
||||
|
||||
&:hover {
|
||||
background-color: $localNavButtonBackgroundColor-isHover--darkTheme;
|
||||
color: $localNavButtonTextColor-isHover--darkTheme;
|
||||
}
|
||||
|
||||
&.localMenuItem-isSelected {
|
||||
background-color: $localNavButtonBackgroundColor-isSelected--darkTheme;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.localMenuItem__icon {
|
||||
margin-right: 5px;
|
||||
margin-bottom: -1px;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
|
||||
/**
|
||||
* 1. Match height of logo in side bar, but allow it to expand to accommodate
|
||||
* dropdown.
|
||||
*/
|
||||
.localNav {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
min-height: 70px; /* 1 */
|
||||
color: $localNavTextColor;
|
||||
background-color: $localNavBackgroundColor;
|
||||
|
||||
@include darkTheme {
|
||||
color: $localNavTextColor--darkTheme;
|
||||
background-color: $localNavBackgroundColor--darkTheme;
|
||||
}
|
||||
}
|
||||
|
||||
.localNavRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.localNavRow__section {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.localNavRow--secondary {
|
||||
height: 38px;
|
||||
padding: 0 $localNavSideSpacing;
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
|
||||
$localSearchHeight: 30px;
|
||||
|
||||
.localSearch {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: $localSearchHeight;
|
||||
}
|
||||
|
||||
.localSearchInput {
|
||||
flex: 1 1 100%;
|
||||
padding: 5px 15px;
|
||||
font-size: $localNavFontSizeNormal;
|
||||
color: $localNavTextColor;
|
||||
background-color: $localSearchBackgroundColor;
|
||||
border: 2px solid $localSearchBackgroundColor;
|
||||
border-bottom-left-radius: 4px;
|
||||
border-top-left-radius: 4px;
|
||||
border-bottom-right-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
|
||||
&.localSearchInput-isInvalid {
|
||||
border-color: $localSearchBorderColor-isInvalid;
|
||||
}
|
||||
|
||||
@include darkTheme {
|
||||
color: $localNavTextColor--darkTheme;
|
||||
background-color: $localSearchBackgroundColor--darkTheme;
|
||||
border-color: $localSearchBackgroundColor--darkTheme;
|
||||
|
||||
&.localSearchInput-isInvalid {
|
||||
border-color: $localSearchBorderColor-isInvalid--darkTheme;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.localSearchButton {
|
||||
width: 43px;
|
||||
height: $localSearchHeight;
|
||||
font-size: $localNavFontSizeNormal;
|
||||
color: $buttonTextColor;
|
||||
background-color: $buttonBackgroundColor;
|
||||
border: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-right-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
|
||||
@include darkTheme {
|
||||
color: $buttonTextColor--darkTheme;
|
||||
background-color: $buttonBackgroundColor--darkTheme;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* 1. We want the bottom border on selected tabs to be flush with the bottom of the container.
|
||||
*/
|
||||
.localTabs {
|
||||
display: flex;
|
||||
align-items: flex-end; // 1
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Make sure the bottom border is flush with the bottom of the LocalNav.
|
||||
*/
|
||||
.localTab {
|
||||
padding: 5px 0 6px 0;
|
||||
font-size: 18px;
|
||||
line-height: 22px; /* 1 */
|
||||
color: $localTabTextColor;
|
||||
border-bottom: 2px solid transparent;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
color: $localTabTextColor-isHover;
|
||||
|
||||
@include darkTheme {
|
||||
color: $localTabTextColor-isHover--darkTheme;
|
||||
}
|
||||
}
|
||||
|
||||
&.localTab-isSelected {
|
||||
color: $localTabTextColor-isSelected;
|
||||
border-bottom-color: $localTabTextColor-isSelected;
|
||||
cursor: default;
|
||||
|
||||
@include darkTheme {
|
||||
color: $localTabTextColor-isSelected--darkTheme;
|
||||
border-bottom-color: $localTabTextColor-isSelected--darkTheme;
|
||||
}
|
||||
}
|
||||
|
||||
& + & {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
@include darkTheme {
|
||||
color: $localTabTextColor--darkTheme;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
.localTitle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
padding-left: $localNavSideSpacing;
|
||||
font-size: $localNavFontSizeNormal;
|
||||
font-weight: bold;
|
||||
}
|
27
src/ui_framework/src/framework/framework.scss
Normal file
27
src/ui_framework/src/framework/framework.scss
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Normal colors
|
||||
$textColor: #2d2d2d;
|
||||
$buttonTextColor: #ffffff;
|
||||
$buttonBackgroundColor: #9c9c9c;
|
||||
$linkColor: #328CAA;
|
||||
$linkColor-isHover: #105A73;
|
||||
$inputTextColor: $textColor;
|
||||
$inputBackgroundColor: #ffffff;
|
||||
$inputBorderColor: $inputBackgroundColor;
|
||||
|
||||
// Dark theme colors
|
||||
$textColor--darkTheme: #cecece;
|
||||
$buttonTextColor--darkTheme: #ffffff;
|
||||
$buttonBackgroundColor--darkTheme: #777777;
|
||||
$linkColor--darkTheme: #b7e2ea;
|
||||
$linkColor-isHover--darkTheme: #def2f6;
|
||||
$inputTextColor--darkTheme: $textColor--darkTheme;
|
||||
$inputBackgroundColor--darkTheme: #444444;
|
||||
$inputBorderColor--darkTheme: $inputBackgroundColor--darkTheme;
|
||||
|
||||
@mixin darkTheme() {
|
||||
.theme-dark & {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@import "components/local_nav/index";
|
13
src/ui_framework/src/guide/actions/action_types.js
Normal file
13
src/ui_framework/src/guide/actions/action_types.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
|
||||
import keyMirror from 'keymirror';
|
||||
|
||||
export default keyMirror({
|
||||
|
||||
// Source code viewer actions
|
||||
OPEN_CODE_VIEWER: null,
|
||||
UPDATE_CODE_VIEWER: null,
|
||||
CLOSE_CODE_VIEWER: null,
|
||||
REGISTER_CODE: null,
|
||||
UNREGISTER_CODE: null,
|
||||
|
||||
});
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
import ActionTypes from '../action_types';
|
||||
|
||||
export default {
|
||||
|
||||
openCodeViewer: slug => ({
|
||||
type: ActionTypes.OPEN_CODE_VIEWER,
|
||||
slug,
|
||||
}),
|
||||
|
||||
updateCodeViewer: slug => ({
|
||||
type: ActionTypes.UPDATE_CODE_VIEWER,
|
||||
slug,
|
||||
}),
|
||||
|
||||
closeCodeViewer: () => ({
|
||||
type: ActionTypes.CLOSE_CODE_VIEWER,
|
||||
}),
|
||||
|
||||
registerCode: code => ({
|
||||
type: ActionTypes.REGISTER_CODE,
|
||||
code,
|
||||
}),
|
||||
|
||||
unregisterCode: code => ({
|
||||
type: ActionTypes.UNREGISTER_CODE,
|
||||
code
|
||||
}),
|
||||
|
||||
};
|
4
src/ui_framework/src/guide/actions/index.js
Normal file
4
src/ui_framework/src/guide/actions/index.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
export {
|
||||
default as CodeViewerActions,
|
||||
} from './code_viewer/code_viewer_actions';
|
|
@ -0,0 +1,114 @@
|
|||
|
||||
.guideCodeViewer {
|
||||
position: fixed;
|
||||
top: $guideNavHeight;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: $guideCodeViewerWidth;
|
||||
padding: 40px 20px 40px 0;
|
||||
background-color: white;
|
||||
transform: translateX($guideCodeViewerWidth);
|
||||
transition: transform $guideCodeViewerTransition;
|
||||
overflow: auto;
|
||||
|
||||
@include scrollbar;
|
||||
|
||||
@include whenNarrowerThan($normalBreakpoint) {
|
||||
width: $guideCodeViewerSmallWidth;
|
||||
}
|
||||
|
||||
&.is-code-viewer-open {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.guideCodeViewer__header {
|
||||
padding-bottom: 10px;
|
||||
line-height: $guideLineHeight;
|
||||
border-bottom: 1px solid #d6d6d6;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.guideCodeViewer__closeButton {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
cursor: pointer;
|
||||
padding: 10px;
|
||||
border-radius: 3px;
|
||||
color: #6b7490;
|
||||
|
||||
&:hover {
|
||||
color: #2b52cc;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.guideCodeViewer__title {
|
||||
padding-bottom: 6px;
|
||||
border-bottom: 1px solid #d6d6d6;
|
||||
line-height: $guideLineHeight;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.guideCodeViewer__content {
|
||||
margin: 0 0 16px;
|
||||
}
|
||||
|
||||
// HLJS
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
padding: 15px 20px;
|
||||
color: #637c84;
|
||||
font-size: 14px;
|
||||
line-height: 1.3;
|
||||
font-family: 'Ubuntu Mono', monospace;
|
||||
}
|
||||
|
||||
.hljs-keyword {
|
||||
color: #b58900;
|
||||
}
|
||||
|
||||
.hljs-function {
|
||||
.hljs-keyword {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
.hljs-title {
|
||||
color: #7441c6;
|
||||
}
|
||||
}
|
||||
|
||||
.hljs-built_in {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
.hljs-string {
|
||||
color: #36958e;
|
||||
}
|
||||
|
||||
.hljs-comment {
|
||||
color: #9d9d9d;
|
||||
}
|
||||
|
||||
.hljs-number,
|
||||
.hljs-literal {
|
||||
color: #d84a7e;
|
||||
}
|
||||
|
||||
.hljs-tag {
|
||||
.hljs-name {
|
||||
color: #63a35c;
|
||||
}
|
||||
|
||||
.hljs-attr {
|
||||
color: #795da3;
|
||||
}
|
||||
|
||||
.hljs-string {
|
||||
color: #df5000;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
|
||||
import React, {
|
||||
Component,
|
||||
PropTypes,
|
||||
} from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import hljs from 'highlight.js';
|
||||
|
||||
export default class GuideCodeViewer extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.refs.html) {
|
||||
hljs.highlightBlock(this.refs.html);
|
||||
}
|
||||
|
||||
if (this.refs.javascript) {
|
||||
hljs.highlightBlock(this.refs.javascript);
|
||||
}
|
||||
}
|
||||
|
||||
renderSection(title, content, codeClass) {
|
||||
if (content) {
|
||||
return (
|
||||
<div className="guideCodeViewer__section">
|
||||
<div className="guideCodeViewer__title">
|
||||
{title}
|
||||
</div>
|
||||
<pre className="guideCodeViewer__content">
|
||||
<code
|
||||
ref={codeClass}
|
||||
className={codeClass}
|
||||
>
|
||||
{content}
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const classes = classNames('guideCodeViewer', {
|
||||
'is-code-viewer-open': this.props.isOpen,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={classes}>
|
||||
<div className="guideCodeViewer__header">
|
||||
{this.props.title}
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="guideCodeViewer__closeButton fa fa-times"
|
||||
onClick={this.props.onClose}
|
||||
/>
|
||||
|
||||
{this.renderSection('HTML', this.props.html, 'html')}
|
||||
{this.renderSection('JavaScript', this.props.js, 'javascript')}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GuideCodeViewer.propTypes = {
|
||||
isOpen: PropTypes.bool,
|
||||
onClose: PropTypes.func,
|
||||
title: PropTypes.string,
|
||||
html: PropTypes.string,
|
||||
js: PropTypes.string,
|
||||
};
|
|
@ -0,0 +1,3 @@
|
|||
.guideExample {
|
||||
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
|
||||
import React, {
|
||||
Component,
|
||||
PropTypes,
|
||||
} from 'react';
|
||||
|
||||
import {
|
||||
Slugify,
|
||||
} from '../../services';
|
||||
|
||||
import {
|
||||
GuidePage,
|
||||
GuidePageSection,
|
||||
} from '../';
|
||||
|
||||
export default class GuideExample extends Component {
|
||||
|
||||
constructor(props, sections) {
|
||||
super(props);
|
||||
|
||||
this.sections = sections.map(section => Object.assign({}, section, {
|
||||
slug: Slugify.one(section.title),
|
||||
}));
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.sections.forEach(section => {
|
||||
this.context.registerCode(section);
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.sections.forEach(section => {
|
||||
this.context.unregisterCode(section);
|
||||
});
|
||||
}
|
||||
|
||||
renderSections() {
|
||||
return this.sections.map((section, index) => (
|
||||
<GuidePageSection
|
||||
key={index}
|
||||
title={section.title}
|
||||
slug={section.slug}
|
||||
html={section.html}
|
||||
js={section.js}
|
||||
>
|
||||
{section.description}
|
||||
</GuidePageSection>
|
||||
));
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<GuidePage
|
||||
title={this.props.route.name}
|
||||
>
|
||||
{this.renderSections()}
|
||||
</GuidePage>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GuideExample.contextTypes = {
|
||||
registerCode: PropTypes.func,
|
||||
unregisterCode: PropTypes.func,
|
||||
};
|
||||
|
||||
GuideExample.propTypes = {
|
||||
route: PropTypes.object.isRequired,
|
||||
sections: PropTypes.arrayOf(React.PropTypes.shape({
|
||||
title: React.PropTypes.string.isRequired,
|
||||
description: React.PropTypes.any,
|
||||
html: React.PropTypes.string.isRequired,
|
||||
js: React.PropTypes.string,
|
||||
})),
|
||||
};
|
|
@ -0,0 +1,67 @@
|
|||
.guideNav {
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: $guideNavHeight;
|
||||
padding: 0 20px;
|
||||
background-color: #e8488b;
|
||||
color: #ffffff;
|
||||
box-shadow:
|
||||
inset 0 -20px 18px rgba(#5a1029, 0.2),
|
||||
inset 0 -5px 4px rgba(#5a1029, 0.3);
|
||||
transition: height 0.3s ease;
|
||||
overflow: hidden;
|
||||
|
||||
&.is-guide-nav-open {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.guideNav__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.guideNav__menu {
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
padding: 10px;
|
||||
border-radius: 3px;
|
||||
|
||||
&.is-menu-button-pinned,
|
||||
&:hover {
|
||||
background-color: rgba(black, 0.15);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: rgba(black, 0.2);
|
||||
box-shadow: inset 0 2px 8px rgba(black, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.guideNav__title {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.guideNav__version {
|
||||
margin-left: 10px;
|
||||
font-weight: 300;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.guideNavItem {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
font-size: 20px;
|
||||
padding: 10px;
|
||||
border-radius: 3px;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(black, 0.15);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
|
||||
import React, {
|
||||
PropTypes,
|
||||
} from 'react';
|
||||
|
||||
import {
|
||||
Link,
|
||||
} from 'react-router';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
const GuideNav = props => {
|
||||
const classes = classNames('guideNav', {
|
||||
'is-guide-nav-open': props.isNavOpen,
|
||||
});
|
||||
|
||||
const buttonClasses = classNames('guideNav__menu fa fa-bars', {
|
||||
'is-menu-button-pinned': props.isNavOpen,
|
||||
});
|
||||
|
||||
const navItems = props.items.map((item, index) => {
|
||||
return (
|
||||
<Link
|
||||
key={index}
|
||||
className="guideNavItem"
|
||||
to={item.path}
|
||||
onClick={props.onClickNavItem}
|
||||
>
|
||||
{item.name}
|
||||
</Link>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={classes}>
|
||||
<div className="guideNav__header">
|
||||
<div
|
||||
className={buttonClasses}
|
||||
onClick={props.onToggleNav}
|
||||
/>
|
||||
<Link
|
||||
className="guideNav__title"
|
||||
to="/"
|
||||
onClick={props.onClickNavItem}
|
||||
>
|
||||
Kibana UI Framework <span className="guideNav__version">{props.version}</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="guideNavItems">
|
||||
{navItems}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
GuideNav.propTypes = {
|
||||
isNavOpen: PropTypes.bool,
|
||||
onToggleNav: PropTypes.func,
|
||||
onClickNavItem: PropTypes.func,
|
||||
version: PropTypes.string,
|
||||
items: PropTypes.array,
|
||||
};
|
||||
|
||||
export default GuideNav;
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
@import "../../variables";
|
||||
|
||||
.guidePage {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.guidePageBody {
|
||||
flex: 1 1 auto;
|
||||
padding: 0 80px 0 80px + $guideSideNavWidth;
|
||||
|
||||
@include whenNarrowerThan($normalBreakpoint) {
|
||||
padding: 0 20px 0 $guideSideNavSmallWidth;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
|
||||
import React, {
|
||||
Component,
|
||||
PropTypes,
|
||||
} from 'react';
|
||||
|
||||
import {
|
||||
Slugify,
|
||||
} from '../../services';
|
||||
|
||||
import {
|
||||
GuidePageSideNav,
|
||||
GuidePageSideNavItem,
|
||||
} from '../';
|
||||
|
||||
export default class GuidePage extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.onClickLink = this.onClickLink.bind(this);
|
||||
}
|
||||
|
||||
onClickLink(slug) {
|
||||
// Scroll to element.
|
||||
$('html, body').animate({
|
||||
scrollTop: $(`#${slug}`).offset().top - 100
|
||||
}, 250);
|
||||
|
||||
// Load in code viewer.
|
||||
this.context.updateCodeViewer(slug);
|
||||
}
|
||||
|
||||
renderSideNavMenu() {
|
||||
// Traverse children and build side nav from it.
|
||||
return this.props.children.map((section, index) => {
|
||||
return (
|
||||
<GuidePageSideNavItem
|
||||
key={index}
|
||||
slug={Slugify.one(section.props.title)}
|
||||
onClick={this.onClickLink}
|
||||
>
|
||||
{section.props.title}
|
||||
</GuidePageSideNavItem>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="guidePage">
|
||||
<GuidePageSideNav title={this.props.title}>
|
||||
{this.renderSideNavMenu()}
|
||||
</GuidePageSideNav>
|
||||
|
||||
<div className="guidePageBody">
|
||||
{this.props.children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GuidePage.contextTypes = {
|
||||
updateCodeViewer: PropTypes.func,
|
||||
};
|
||||
|
||||
GuidePage.propTypes = {
|
||||
children: PropTypes.any,
|
||||
title: PropTypes.string,
|
||||
};
|
|
@ -0,0 +1,56 @@
|
|||
|
||||
@import "../../variables";
|
||||
|
||||
.guidePageSection {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.guidePageSection__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-bottom: 10px;
|
||||
line-height: $guideLineHeight;
|
||||
border-bottom: 1px solid #d6d6d6;
|
||||
}
|
||||
|
||||
.guidePageSection__title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.guidePageSection__sourceButton {
|
||||
line-height: 10px;
|
||||
padding: 4px 10px;
|
||||
background-color: #19a8e0;
|
||||
color: white;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(#95e1ff, 1),
|
||||
0 2px 4px rgba(black, 0.2);
|
||||
}
|
||||
|
||||
&:active {
|
||||
box-shadow:
|
||||
inset 0 20px 20px rgba(black, 0.1),
|
||||
inset 0 2px 8px rgba(black, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.guidePageSection__description {
|
||||
font-size: 14px;
|
||||
line-height: 21px;
|
||||
}
|
||||
|
||||
.guidePageSection__example {
|
||||
& + & {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.guidePageSection__example--standalone {
|
||||
margin-top: 10px;
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
|
||||
import React, {
|
||||
Component,
|
||||
PropTypes,
|
||||
} from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import {
|
||||
JsInjector,
|
||||
} from '../../services';
|
||||
|
||||
export default class GuidePageSection extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.onClickSource = this.onClickSource.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// NOTE: This will cause a race condition if a GuidePage adds and removes
|
||||
// GuidePageSection instances during its lifetime (e.g. if a user is allowed
|
||||
// to click "add" and "delete" buttons to add and remove GuidePageSections).
|
||||
//
|
||||
// In such a race condition, we could end up with GuidePageSections with
|
||||
// identical id values.
|
||||
//
|
||||
// As long as all GuidePageSection instances are added when a GuidePage
|
||||
// is instantiated, and then they're all removed when a GuidePage is
|
||||
// removed, we won't encounter this race condition.
|
||||
if (this.props.js) {
|
||||
this.scriptId = `${GuidePageSection.SCRIPT_ID}${GuidePageSection.count}`;
|
||||
GuidePageSection.count++;
|
||||
// JS injection must occur _after_ the component has been mounted, so
|
||||
// the component DOM is available for the JS to manipulate.
|
||||
JsInjector.inject(this.props.js, this.scriptId);
|
||||
}
|
||||
|
||||
function trimChildren(node) {
|
||||
if (node.children.length > 0) {
|
||||
[...node.children].forEach(trimChildren);
|
||||
return;
|
||||
}
|
||||
node.textContent = node.textContent.trim();
|
||||
}
|
||||
|
||||
trimChildren(this.refs.html);
|
||||
trimChildren(this.refs.htmlDarkTheme);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
JsInjector.remove(this.scriptId);
|
||||
GuidePageSection.count--;
|
||||
}
|
||||
|
||||
onClickSource() {
|
||||
this.context.openCodeViewer(this.props.slug);
|
||||
}
|
||||
|
||||
render() {
|
||||
let description;
|
||||
|
||||
if (this.props.children) {
|
||||
description = (
|
||||
<div className="guidePageSection__description">
|
||||
{this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const exampleClasses = classNames('guidePageSection__example', {
|
||||
'guidePageSection__example--standalone': !this.props.children,
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
id={this.props.slug}
|
||||
className="guidePageSection"
|
||||
>
|
||||
<div className="guidePageSection__header">
|
||||
<div className="guidePageSection__title">
|
||||
{this.props.title}
|
||||
</div>
|
||||
<div
|
||||
className="guidePageSection__sourceButton fa fa-code"
|
||||
onClick={this.onClickSource}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{description}
|
||||
|
||||
<div
|
||||
ref="html"
|
||||
className={exampleClasses}
|
||||
dangerouslySetInnerHTML={{ __html: this.props.html }}
|
||||
/>
|
||||
|
||||
<div
|
||||
ref="htmlDarkTheme"
|
||||
className={`${exampleClasses} theme-dark`}
|
||||
dangerouslySetInnerHTML={{ __html: this.props.html }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GuidePageSection.count = 0;
|
||||
GuidePageSection.SCRIPT_ID = 'EXAMPLE_SCRIPT';
|
||||
|
||||
GuidePageSection.contextTypes = {
|
||||
openCodeViewer: PropTypes.func,
|
||||
};
|
||||
|
||||
GuidePageSection.propTypes = {
|
||||
title: PropTypes.string,
|
||||
slug: PropTypes.string,
|
||||
html: PropTypes.string,
|
||||
js: PropTypes.string,
|
||||
children: PropTypes.any,
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
|
||||
.guidePageSideNav {
|
||||
position: fixed;
|
||||
top: 100px;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: $guideSideNavWidth;
|
||||
padding: 0 20px 30px 80px;
|
||||
overflow: auto;
|
||||
|
||||
@include scrollbar;
|
||||
|
||||
@include whenNarrowerThan($normalBreakpoint) {
|
||||
padding: 0 20px 30px 20px;
|
||||
width: $guideSideNavSmallWidth;
|
||||
}
|
||||
}
|
||||
|
||||
.guidePageSideNav__title {
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 22px;
|
||||
line-height: $guideLineHeight;
|
||||
border-bottom: 1px solid #d6d6d6;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.guidePageSideNavMenu {
|
||||
line-height: $guideLineHeight;
|
||||
}
|
||||
|
||||
.guidePageSideNavMenu__item {
|
||||
& + & {
|
||||
margin-top: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.guidePageSideNavMenu__itemLink {
|
||||
cursor: pointer;
|
||||
color: #6b7490;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
color: #2b52cc;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
import React, {
|
||||
Component,
|
||||
PropTypes,
|
||||
} from 'react';
|
||||
|
||||
export default class GuidePageSideNav extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="guidePageSideNav">
|
||||
<div className="guidePageSideNav__title">
|
||||
{this.props.title}
|
||||
</div>
|
||||
|
||||
<div className="guidePageSideNavMenu">
|
||||
{this.props.children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GuidePageSideNav.propTypes = {
|
||||
title: PropTypes.string,
|
||||
children: PropTypes.any,
|
||||
};
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
import React, {
|
||||
Component,
|
||||
PropTypes,
|
||||
} from 'react';
|
||||
|
||||
export default class GuidePageSideNavItem extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.onClick = this.onClick.bind(this);
|
||||
}
|
||||
|
||||
onClick() {
|
||||
this.props.onClick(this.props.slug);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="guidePageSideNavMenu__item">
|
||||
<div
|
||||
className="guidePageSideNavMenu__itemLink"
|
||||
onClick={this.onClick}
|
||||
>
|
||||
{this.props.children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GuidePageSideNavItem.propTypes = {
|
||||
slug: PropTypes.string,
|
||||
children: PropTypes.any,
|
||||
onClick: PropTypes.func,
|
||||
};
|
21
src/ui_framework/src/guide/components/index.js
Normal file
21
src/ui_framework/src/guide/components/index.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
|
||||
export * from './guide_code_viewer/guide_code_viewer.jsx';
|
||||
export { default as GuideCodeViewer } from './guide_code_viewer/guide_code_viewer.jsx';
|
||||
|
||||
export * from './guide_example/guide_example.jsx';
|
||||
export { default as GuideExample } from './guide_example/guide_example.jsx';
|
||||
|
||||
export * from './guide_nav/guide_nav.jsx';
|
||||
export { default as GuideNav } from './guide_nav/guide_nav.jsx';
|
||||
|
||||
export * from './guide_page/guide_page.jsx';
|
||||
export { default as GuidePage } from './guide_page/guide_page.jsx';
|
||||
|
||||
export * from './guide_page_section/guide_page_section.jsx';
|
||||
export { default as GuidePageSection } from './guide_page_section/guide_page_section.jsx';
|
||||
|
||||
export * from './guide_page_side_nav/guide_page_side_nav.jsx';
|
||||
export { default as GuidePageSideNav } from './guide_page_side_nav/guide_page_side_nav.jsx';
|
||||
|
||||
export * from './guide_page_side_nav/guide_page_side_nav_item.jsx';
|
||||
export { default as GuidePageSideNavItem } from './guide_page_side_nav/guide_page_side_nav_item.jsx';
|
80
src/ui_framework/src/guide/index.js
Normal file
80
src/ui_framework/src/guide/index.js
Normal file
|
@ -0,0 +1,80 @@
|
|||
require('./main.scss');
|
||||
|
||||
import 'babel-polyfill';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import {
|
||||
Router,
|
||||
useRouterHistory,
|
||||
} from 'react-router';
|
||||
import { syncHistoryWithStore } from 'react-router-redux';
|
||||
import createHashHistory from 'history/lib/createHashHistory';
|
||||
|
||||
// Store.
|
||||
import configureStore from './store/configure_store';
|
||||
|
||||
// Guide views.
|
||||
import AppContainer from './views/app_container';
|
||||
import HomeView from './views/home/home_view.jsx';
|
||||
import NotFoundView from './views/not_found/not_found_view.jsx';
|
||||
|
||||
import {
|
||||
Routes,
|
||||
} from './services';
|
||||
|
||||
const store = configureStore();
|
||||
const browserHistory = useRouterHistory(createHashHistory)({
|
||||
queryKey: false,
|
||||
});
|
||||
const history = syncHistoryWithStore(browserHistory, store);
|
||||
|
||||
const childRoutes = Routes.getAppRoutes();
|
||||
childRoutes.push({
|
||||
path: '*',
|
||||
component: NotFoundView,
|
||||
name: 'Page Not Found',
|
||||
});
|
||||
|
||||
const routes = [{
|
||||
path: '/',
|
||||
component: AppContainer,
|
||||
indexRoute: {
|
||||
component: HomeView,
|
||||
source: 'views/home/HomeView.jsx',
|
||||
},
|
||||
childRoutes,
|
||||
}];
|
||||
|
||||
// Update document title with route name.
|
||||
const onRouteEnter = route => {
|
||||
const leafRoute = route.routes[route.routes.length - 1];
|
||||
document.title = leafRoute.name ?
|
||||
`Kibana UI Framework - ${leafRoute.name}` :
|
||||
'Kibana UI Framework';
|
||||
};
|
||||
|
||||
const syncTitleWithRoutes = routesList => {
|
||||
if (!routesList) return;
|
||||
routesList.forEach(route => {
|
||||
route.onEnter = onRouteEnter; // eslint-disable-line no-param-reassign
|
||||
if (route.indexRoute) {
|
||||
// Index routes have a weird relationship with their "parent" routes,
|
||||
// so it seems we need to give their own onEnter hooks.
|
||||
route.indexRoute.onEnter = onRouteEnter; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
syncTitleWithRoutes(route.childRoutes);
|
||||
});
|
||||
};
|
||||
|
||||
syncTitleWithRoutes(routes);
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<Router
|
||||
history={history}
|
||||
routes={routes}
|
||||
/>
|
||||
</Provider>,
|
||||
document.getElementById('guide')
|
||||
);
|
28
src/ui_framework/src/guide/main.scss
Normal file
28
src/ui_framework/src/guide/main.scss
Normal file
|
@ -0,0 +1,28 @@
|
|||
|
||||
@import "../framework/framework";
|
||||
@import "./views/app";
|
||||
@import "./components/guide_code_viewer/guide_code_viewer";
|
||||
@import "./components/guide_nav/guide_nav";
|
||||
@import "./components/guide_page/guide_page";
|
||||
@import "./components/guide_page_section/guide_page_section";
|
||||
@import "./components/guide_page_side_nav/guide_page_side_nav";
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Insane line-height makes it easier to notice when components are relying
|
||||
* on styles inherited from body.
|
||||
*/
|
||||
body {
|
||||
font-family: 'Lato', 'Helvetica Neue', sans-serif;
|
||||
background: #ffffff;
|
||||
line-height: 40px; /* 1 */
|
||||
margin: 0;
|
||||
}
|
16
src/ui_framework/src/guide/services/example/createExample.js
Normal file
16
src/ui_framework/src/guide/services/example/createExample.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
|
||||
import {
|
||||
GuideExample,
|
||||
} from '../../components';
|
||||
|
||||
export default function creatExample(examples) {
|
||||
class Example extends GuideExample {
|
||||
constructor(props) {
|
||||
super(props, examples);
|
||||
}
|
||||
}
|
||||
|
||||
Example.propTypes = Object.assign({}, GuideExample.propTypes);
|
||||
|
||||
return Example;
|
||||
}
|
12
src/ui_framework/src/guide/services/index.js
Normal file
12
src/ui_framework/src/guide/services/index.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
export * from './example/createExample';
|
||||
export { default as createExample } from './example/createExample';
|
||||
|
||||
export * from './js_injector/js_injector';
|
||||
export { default as JsInjector } from './js_injector/js_injector';
|
||||
|
||||
export * from './routes/routes';
|
||||
export { default as Routes } from './routes/routes';
|
||||
|
||||
export * from './string/slugify';
|
||||
export { default as Slugify } from './string/slugify';
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
import $ from 'jquery';
|
||||
|
||||
const ID_ATTRIBUTE = 'injected-js-tag-id';
|
||||
|
||||
export default {
|
||||
|
||||
inject(js, id) {
|
||||
if (id) {
|
||||
$(`[${ID_ATTRIBUTE}=${id}]`).remove();
|
||||
}
|
||||
|
||||
const script = $(`<script ${ID_ATTRIBUTE}=${id}>${js}</script>`);
|
||||
$('body').append(script);
|
||||
},
|
||||
|
||||
remove(id) {
|
||||
$(`[${ID_ATTRIBUTE}=${id}]`).remove();
|
||||
},
|
||||
|
||||
};
|
19
src/ui_framework/src/guide/services/routes/Routes.js
Normal file
19
src/ui_framework/src/guide/services/routes/Routes.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
|
||||
import Slugify from '../string/slugify';
|
||||
|
||||
import LocalNavExample
|
||||
from '../../views/local_nav/local_nav_example.jsx';
|
||||
|
||||
// Component route names should match the component name exactly.
|
||||
const components = [{
|
||||
name: 'LocalNav',
|
||||
component: LocalNavExample,
|
||||
}];
|
||||
|
||||
export default {
|
||||
components: Slugify.each(components, 'name', 'path'),
|
||||
getAppRoutes: function getAppRoutes() {
|
||||
const list = this.components;
|
||||
return list.slice(0);
|
||||
},
|
||||
};
|
27
src/ui_framework/src/guide/services/string/slugify.js
Normal file
27
src/ui_framework/src/guide/services/string/slugify.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
|
||||
|
||||
/**
|
||||
* Lowercases input and replaces spaces with hyphens:
|
||||
* e.g. 'GridView Example' -> 'gridview-example'
|
||||
*/
|
||||
function one(str) {
|
||||
const parts = str
|
||||
.toLowerCase()
|
||||
.replace(/[-]+/g, ' ')
|
||||
.replace(/[^\w^\s]+/g, '')
|
||||
.replace(/ +/g, ' ').split(' ');
|
||||
return parts.join('-');
|
||||
}
|
||||
|
||||
function each(items, src, dest) {
|
||||
return items.map(item => {
|
||||
const _item = item;
|
||||
_item[dest] = one(_item[src]);
|
||||
return _item;
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
one,
|
||||
each,
|
||||
};
|
35
src/ui_framework/src/guide/store/configure_store.js
Normal file
35
src/ui_framework/src/guide/store/configure_store.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
import {
|
||||
applyMiddleware,
|
||||
createStore,
|
||||
compose,
|
||||
} from 'redux';
|
||||
import thunk from 'redux-thunk';
|
||||
import { browserHistory } from 'react-router';
|
||||
import {
|
||||
routerMiddleware,
|
||||
routerReducer,
|
||||
} from 'react-router-redux';
|
||||
|
||||
import codeViewerReducer from './reducers/code_viewer_reducer';
|
||||
|
||||
/**
|
||||
* @param {Object} initialState An object defining the application's initial
|
||||
* state.
|
||||
*/
|
||||
export default function configureStore(initialState) {
|
||||
function rootReducer(state = {}, action) {
|
||||
return {
|
||||
routing: routerReducer(state.routing, action),
|
||||
codeViewer: codeViewerReducer(state.codeViewer, action),
|
||||
};
|
||||
}
|
||||
|
||||
const finalStore = compose(
|
||||
applyMiddleware(
|
||||
thunk,
|
||||
routerMiddleware(browserHistory)
|
||||
)
|
||||
)(createStore)(rootReducer, initialState);
|
||||
|
||||
return finalStore;
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
|
||||
import ActionTypes from '../../actions/action_types';
|
||||
|
||||
const defaultState = {
|
||||
isOpen: false,
|
||||
codesBySlug: {},
|
||||
code: undefined,
|
||||
};
|
||||
|
||||
export default function codeViewerReducer(state = defaultState, action) {
|
||||
switch (action.type) {
|
||||
case ActionTypes.OPEN_CODE_VIEWER: {
|
||||
const newCode = state.codesBySlug[action.slug];
|
||||
|
||||
if (state.code === newCode) {
|
||||
// If we are opening the existing code, then close the viewer.
|
||||
return Object.assign({}, state, {
|
||||
isOpen: false,
|
||||
code: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
return Object.assign({}, state, {
|
||||
isOpen: true,
|
||||
code: newCode,
|
||||
});
|
||||
}
|
||||
|
||||
case ActionTypes.UPDATE_CODE_VIEWER: {
|
||||
if (state.isOpen) {
|
||||
return Object.assign({}, state, {
|
||||
code: state.codesBySlug[action.slug],
|
||||
});
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
case ActionTypes.CLOSE_CODE_VIEWER: {
|
||||
return Object.assign({}, state, {
|
||||
isOpen: false,
|
||||
code: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
case ActionTypes.REGISTER_CODE: {
|
||||
const codesBySlug = Object.assign({}, state.codesBySlug, {
|
||||
[action.code.slug]: action.code,
|
||||
});
|
||||
|
||||
return Object.assign({}, state, {
|
||||
codesBySlug
|
||||
});
|
||||
}
|
||||
|
||||
case ActionTypes.UNREGISTER_CODE: {
|
||||
const codesBySlug = Object.assign({}, state.codesBySlug);
|
||||
delete codesBySlug[action.code.slug];
|
||||
|
||||
return Object.assign({}, state, {
|
||||
codesBySlug
|
||||
});
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
39
src/ui_framework/src/guide/variables.scss
Normal file
39
src/ui_framework/src/guide/variables.scss
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
$guideLineHeight: 24px;
|
||||
$guideNavHeight: 60px;
|
||||
$guideSideNavWidth: 400px;
|
||||
$guideSideNavSmallWidth: 220px;
|
||||
$guideCodeViewerWidth: 700px;
|
||||
$guideCodeViewerSmallWidth: 580px;
|
||||
$guideCodeViewerTransition: 0.2s ease;
|
||||
|
||||
$normalBreakpoint: 1900px;
|
||||
|
||||
@mixin whenNarrowerThan($browserWidth) {
|
||||
@media only screen and (max-width: #{$browserWidth}) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin whenWiderThan($browserWidth) {
|
||||
@media only screen and (min-width: #{$browserWidth}) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin scrollbar($color: rgba(#454D58, 0.4)) {
|
||||
&::-webkit-scrollbar {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: $color;
|
||||
border: 6px solid transparent;
|
||||
background-clip: content-box;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
22
src/ui_framework/src/guide/views/_app.scss
Normal file
22
src/ui_framework/src/guide/views/_app.scss
Normal file
|
@ -0,0 +1,22 @@
|
|||
|
||||
@import "../variables";
|
||||
@import "./home/home_view";
|
||||
|
||||
.guide {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.guideContent {
|
||||
flex: 1 1 auto;
|
||||
padding-top: 100px;
|
||||
transition: padding-right $guideCodeViewerTransition;
|
||||
|
||||
&.is-code-viewer-open {
|
||||
padding-right: $guideCodeViewerWidth;
|
||||
|
||||
@include whenNarrowerThan($normalBreakpoint) {
|
||||
padding-right: $guideCodeViewerSmallWidth;
|
||||
}
|
||||
}
|
||||
}
|
30
src/ui_framework/src/guide/views/app_container.js
Normal file
30
src/ui_framework/src/guide/views/app_container.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import AppView from './app_view.jsx';
|
||||
|
||||
import {
|
||||
CodeViewerActions,
|
||||
} from '../actions';
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
return {
|
||||
routes: ownProps.routes,
|
||||
isCodeViewerOpen: state.codeViewer.isOpen,
|
||||
code: state.codeViewer.code,
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
const actions = {
|
||||
openCodeViewer: CodeViewerActions.openCodeViewer,
|
||||
updateCodeViewer: CodeViewerActions.updateCodeViewer,
|
||||
closeCodeViewer: CodeViewerActions.closeCodeViewer,
|
||||
registerCode: CodeViewerActions.registerCode,
|
||||
unregisterCode: CodeViewerActions.unregisterCode,
|
||||
};
|
||||
|
||||
return bindActionCreators(actions, dispatch);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(AppView);
|
113
src/ui_framework/src/guide/views/app_view.jsx
Normal file
113
src/ui_framework/src/guide/views/app_view.jsx
Normal file
|
@ -0,0 +1,113 @@
|
|||
|
||||
import React, {
|
||||
Component,
|
||||
PropTypes,
|
||||
} from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import {
|
||||
Routes,
|
||||
} from '../services';
|
||||
|
||||
import {
|
||||
GuideCodeViewer,
|
||||
GuideNav,
|
||||
} from '../components';
|
||||
|
||||
// Inject version into header.
|
||||
const pkg = require('json!../../../../../package.json');
|
||||
|
||||
export default class AppView extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isNavOpen: false,
|
||||
};
|
||||
|
||||
this.onClickNavItem = this.onClickNavItem.bind(this);
|
||||
this.onToggleNav = this.onToggleNav.bind(this);
|
||||
this.onCloseCodeViewer = this.onCloseCodeViewer.bind(this);
|
||||
}
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
openCodeViewer: this.props.openCodeViewer,
|
||||
updateCodeViewer: this.props.updateCodeViewer,
|
||||
registerCode: this.props.registerCode,
|
||||
unregisterCode: this.props.unregisterCode,
|
||||
};
|
||||
}
|
||||
|
||||
onClickNavItem() {
|
||||
this.setState({
|
||||
isNavOpen: false,
|
||||
});
|
||||
}
|
||||
|
||||
onCloseCodeViewer() {
|
||||
this.props.closeCodeViewer();
|
||||
}
|
||||
|
||||
onToggleNav() {
|
||||
this.setState({
|
||||
isNavOpen: !this.state.isNavOpen,
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const contentClasses = classNames('guideContent', {
|
||||
'is-code-viewer-open': this.props.isCodeViewerOpen,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="guide">
|
||||
<GuideNav
|
||||
isNavOpen={this.state.isNavOpen}
|
||||
onToggleNav={this.onToggleNav}
|
||||
onClickNavItem={this.onClickNavItem}
|
||||
version={pkg.version}
|
||||
items={Routes.components}
|
||||
/>
|
||||
|
||||
<div className={contentClasses}>
|
||||
{this.props.children}
|
||||
</div>
|
||||
|
||||
<GuideCodeViewer
|
||||
isOpen={this.props.isCodeViewerOpen}
|
||||
onClose={this.onCloseCodeViewer}
|
||||
title={this.props.code.title}
|
||||
html={this.props.code.html}
|
||||
js={this.props.code.js}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
AppView.childContextTypes = {
|
||||
openCodeViewer: PropTypes.func,
|
||||
updateCodeViewer: PropTypes.func,
|
||||
registerCode: PropTypes.func,
|
||||
unregisterCode: PropTypes.func,
|
||||
};
|
||||
|
||||
AppView.propTypes = {
|
||||
children: PropTypes.any,
|
||||
routes: PropTypes.array.isRequired,
|
||||
openCodeViewer: PropTypes.func,
|
||||
updateCodeViewer: PropTypes.func,
|
||||
closeCodeViewer: PropTypes.func,
|
||||
registerCode: PropTypes.func,
|
||||
unregisterCode: PropTypes.func,
|
||||
isCodeViewerOpen: PropTypes.bool,
|
||||
code: PropTypes.object,
|
||||
};
|
||||
|
||||
AppView.defaultProps = {
|
||||
code: {},
|
||||
};
|
28
src/ui_framework/src/guide/views/home/_home_view.scss
Normal file
28
src/ui_framework/src/guide/views/home/_home_view.scss
Normal file
|
@ -0,0 +1,28 @@
|
|||
|
||||
@import "../../variables";
|
||||
|
||||
.guideHome {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.guideHome__panel {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
max-height: 500px;
|
||||
padding: 60px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 3px;
|
||||
background-color: #e8e8e8;
|
||||
line-height: $guideLineHeight;
|
||||
}
|
||||
|
||||
.guideHome__panelTitle {
|
||||
font-weight: 700;
|
||||
font-size: 22px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.guideHome__panelText {
|
||||
font-size: 18px;
|
||||
}
|
28
src/ui_framework/src/guide/views/home/home_view.jsx
Normal file
28
src/ui_framework/src/guide/views/home/home_view.jsx
Normal file
|
@ -0,0 +1,28 @@
|
|||
|
||||
import React, {
|
||||
Component,
|
||||
} from 'react';
|
||||
|
||||
export default class HomeView extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="guideHome">
|
||||
<div className="guideHome__panel">
|
||||
<div className="guideHome__panelTitle">
|
||||
Welcome to the Kibana UI Framework
|
||||
</div>
|
||||
|
||||
<div className="guideHome__panelText">
|
||||
Get started by clicking the menu button in the top left corner of the screen.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
|
||||
<div class="localNav">
|
||||
<div class="localNavRow">
|
||||
<div class="localNavRow__section">
|
||||
<div class="localBreadcrumbs">
|
||||
<div class="localBreadcrumb">
|
||||
<a class="localBreadcrumb__link" href="#">
|
||||
Discover
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="localBreadcrumb">
|
||||
<a class="localBreadcrumb__link" href="#">
|
||||
<span class="localBreadcrumb__emphasis">0</span> hits
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="localNavRow__section">
|
||||
<div class="localMenu">
|
||||
<div class="localMenuItem">
|
||||
New
|
||||
</div>
|
||||
|
||||
<div class="localMenuItem">
|
||||
Save
|
||||
</div>
|
||||
|
||||
<div class="localMenuItem">
|
||||
Open
|
||||
</div>
|
||||
|
||||
<button class="localMenuItem">
|
||||
<div class="localMenuItem__icon fa fa-clock-o"></div>
|
||||
Last 5 minutes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,122 @@
|
|||
|
||||
<div class="localNav">
|
||||
<div class="localNavRow">
|
||||
<div class="localNavRow__section">
|
||||
<div class="localBreadcrumbs">
|
||||
<div class="localBreadcrumb">
|
||||
<a class="localBreadcrumb__link" href="#">
|
||||
Discover
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="localBreadcrumb">
|
||||
<a class="localBreadcrumb__link" href="#">
|
||||
<span class="localBreadcrumb__emphasis">0</span> hits
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="localNavRow__section">
|
||||
<div class="localMenu">
|
||||
<div class="localMenuItem localMenuItem-isSelected">
|
||||
New
|
||||
</div>
|
||||
|
||||
<div class="localMenuItem">
|
||||
Save
|
||||
</div>
|
||||
|
||||
<div class="localMenuItem">
|
||||
Open
|
||||
</div>
|
||||
|
||||
<button class="localMenuItem">
|
||||
<div class="localMenuItem__icon fa fa-clock-o"></div>
|
||||
Last 5 minutes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="localDropdown">
|
||||
<!-- Title -->
|
||||
<div class="localDropdownTitle">Dropdown title</div>
|
||||
|
||||
<!-- Help text -->
|
||||
<div class="localDropdownHelpText">
|
||||
Here's some help text to explain the purpose of the dropdown.
|
||||
</div>
|
||||
|
||||
<!-- Warning -->
|
||||
<div class="localDropdownWarning">
|
||||
Here's some warning text in case the user has something misconfigured.
|
||||
</div>
|
||||
|
||||
<div class="localDropdownSection">
|
||||
<!-- Header -->
|
||||
<div class="localDropdownHeader">
|
||||
<div class="localDropdownHeader__label">
|
||||
Header for a section of content
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Input -->
|
||||
<input
|
||||
class="localDropdownInput"
|
||||
type="text"
|
||||
placeholder="Input something here"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="localDropdownSection">
|
||||
<!-- Header -->
|
||||
<div class="localDropdownHeader">
|
||||
<div class="localDropdownHeader__label">
|
||||
Header for another section of content
|
||||
</div>
|
||||
<div class="localDropdownHeader__actions">
|
||||
<a
|
||||
class="localDropdownHeader__action"
|
||||
href=""
|
||||
>
|
||||
Action A
|
||||
</a>
|
||||
<a
|
||||
class="localDropdownHeader__action"
|
||||
href=""
|
||||
>
|
||||
Action B
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Input -->
|
||||
<input
|
||||
class="localDropdownInput"
|
||||
type="text"
|
||||
readonly
|
||||
value="This is some text inside of a read-only input"
|
||||
/>
|
||||
|
||||
<!-- Notes -->
|
||||
<div class="localDropdownFormNote">
|
||||
Here are some notes to explain the purpose of this section of the dropdown.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="localNavRow localNavRow--secondary">
|
||||
<div class="localSearch">
|
||||
<input
|
||||
class="localSearchInput"
|
||||
type="text"
|
||||
placeholder="Filter..."
|
||||
autocomplete="off"
|
||||
>
|
||||
<button class="localSearchButton">
|
||||
<span class="fa fa-search"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,81 @@
|
|||
|
||||
<div class="localNav">
|
||||
<div class="localNavRow">
|
||||
<div class="localNavRow__section">
|
||||
<div class="localBreadcrumbs">
|
||||
<div class="localBreadcrumb">
|
||||
<a class="localBreadcrumb__link" href="#">
|
||||
Discover
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="localBreadcrumb">
|
||||
<a class="localBreadcrumb__link" href="#">
|
||||
<span class="localBreadcrumb__emphasis">0</span> hits
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="localNavRow__section">
|
||||
<div class="localMenu">
|
||||
<div class="localMenuItem localMenuItem-isSelected">
|
||||
New
|
||||
</div>
|
||||
|
||||
<div class="localMenuItem">
|
||||
Save
|
||||
</div>
|
||||
|
||||
<div class="localMenuItem">
|
||||
Open
|
||||
</div>
|
||||
|
||||
<button class="localMenuItem">
|
||||
<div class="localMenuItem__icon fa fa-clock-o"></div>
|
||||
Last 5 minutes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="localDropdown">
|
||||
<div class="localDropdownPanels">
|
||||
<!-- Left panel -->
|
||||
<div class="localDropdownPanel localDropdownPanel--left">
|
||||
<!-- Title -->
|
||||
<div class="localDropdownTitle">Left panel</div>
|
||||
|
||||
<!-- Help text -->
|
||||
<div class="localDropdownHelpText">
|
||||
Here's some help text to explain the purpose of the dropdown.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right panel -->
|
||||
<div class="localDropdownPanel localDropdownPanel--left">
|
||||
<!-- Title -->
|
||||
<div class="localDropdownTitle">Right panel</div>
|
||||
|
||||
<!-- Help text -->
|
||||
<div class="localDropdownHelpText">
|
||||
Here's some help text to explain the purpose of the dropdown.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="localNavRow localNavRow--secondary">
|
||||
<div class="localSearch">
|
||||
<input
|
||||
class="localSearchInput"
|
||||
type="text"
|
||||
placeholder="Filter..."
|
||||
autocomplete="off"
|
||||
>
|
||||
<button class="localSearchButton">
|
||||
<span class="fa fa-search"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,56 @@
|
|||
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
createExample,
|
||||
} from '../../services';
|
||||
|
||||
export default createExample([{
|
||||
title: 'Simple',
|
||||
description: (
|
||||
<p>Here's a simple LocalNav with a Title in the top left corner and Menu in the top right.</p>
|
||||
),
|
||||
html: require('./local_nav_simple/local_nav_simple.html'),
|
||||
}, {
|
||||
title: 'Breadcrumbs',
|
||||
description: (
|
||||
<p>You can replace the Title with Breadcrumbs.</p>
|
||||
),
|
||||
html: require('./local_nav_breadcrumbs/local_nav_breadcrumbs.html'),
|
||||
}, {
|
||||
title: 'Search',
|
||||
description: (
|
||||
<p>You can add a Search component for filtering results.</p>
|
||||
),
|
||||
html: require('./local_nav_search/local_nav_search.html'),
|
||||
}, {
|
||||
title: 'Invalid Search',
|
||||
html: require('./local_nav_search_error/local_nav_search_error.html'),
|
||||
}, {
|
||||
title: 'Selected and disabled Menu Item states',
|
||||
description: (
|
||||
<div>
|
||||
<p>When the user selects a Menu Item, additional content can be displayed inside of a Dropdown.</p>
|
||||
<p>Menu Items can also be disabled, in which case they become non-interactive.</p>
|
||||
</div>
|
||||
),
|
||||
html: require('./local_nav_menu_item_states/local_nav_menu_item_states.html'),
|
||||
}, {
|
||||
title: 'Dropdown',
|
||||
description: (
|
||||
<p>Selecting a Menu Item will commonly result in an open Dropdown.</p>
|
||||
),
|
||||
html: require('./local_nav_dropdown/local_nav_dropdown.html'),
|
||||
}, {
|
||||
title: 'Dropdown panels',
|
||||
description: (
|
||||
<p>You can split the Dropdown into side-by-side Panels.</p>
|
||||
),
|
||||
html: require('./local_nav_dropdown_panels/local_nav_dropdown_panels.html'),
|
||||
}, {
|
||||
title: 'Tabs',
|
||||
description: (
|
||||
<p>You can display Tabs for navigating local content.</p>
|
||||
),
|
||||
html: require('./local_nav_tabs/local_nav_tabs.html'),
|
||||
}]);
|
|
@ -0,0 +1,55 @@
|
|||
|
||||
<div class="localNav">
|
||||
<div class="localNavRow">
|
||||
<div class="localNavRow__section">
|
||||
<div class="localBreadcrumbs">
|
||||
<div class="localBreadcrumb">
|
||||
<a class="localBreadcrumb__link" href="#">
|
||||
Discover
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="localBreadcrumb">
|
||||
<a class="localBreadcrumb__link" href="#">
|
||||
<span class="localBreadcrumb__emphasis">0</span> hits
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="localNavRow__section">
|
||||
<div class="localMenu">
|
||||
<div class="localMenuItem localMenuItem-isSelected">
|
||||
New
|
||||
</div>
|
||||
|
||||
<div class="localMenuItem">
|
||||
Save
|
||||
</div>
|
||||
|
||||
<div class="localMenuItem localMenuItem-isDisabled">
|
||||
Open
|
||||
</div>
|
||||
|
||||
<button class="localMenuItem">
|
||||
<div class="localMenuItem__icon fa fa-clock-o"></div>
|
||||
Last 5 minutes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="localNavRow localNavRow--secondary">
|
||||
<div class="localSearch">
|
||||
<input
|
||||
class="localSearchInput"
|
||||
type="text"
|
||||
placeholder="Filter..."
|
||||
autocomplete="off"
|
||||
>
|
||||
<button class="localSearchButton">
|
||||
<span class="fa fa-search"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,55 @@
|
|||
|
||||
<div class="localNav">
|
||||
<div class="localNavRow">
|
||||
<div class="localNavRow__section">
|
||||
<div class="localBreadcrumbs">
|
||||
<div class="localBreadcrumb">
|
||||
<a class="localBreadcrumb__link" href="#">
|
||||
Discover
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="localBreadcrumb">
|
||||
<a class="localBreadcrumb__link" href="#">
|
||||
<span class="localBreadcrumb__emphasis">0</span> hits
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="localNavRow__section">
|
||||
<div class="localMenu">
|
||||
<button class="localMenuItem">
|
||||
New
|
||||
</button>
|
||||
|
||||
<button class="localMenuItem">
|
||||
Save
|
||||
</button>
|
||||
|
||||
<button class="localMenuItem">
|
||||
Open
|
||||
</button>
|
||||
|
||||
<button class="localMenuItem">
|
||||
<div class="localMenuItem__icon fa fa-clock-o"></div>
|
||||
Last 5 minutes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="localNavRow localNavRow--secondary">
|
||||
<div class="localSearch">
|
||||
<input
|
||||
class="localSearchInput"
|
||||
type="text"
|
||||
placeholder="Filter..."
|
||||
autocomplete="off"
|
||||
>
|
||||
<button class="localSearchButton">
|
||||
<span class="fa fa-search"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,55 @@
|
|||
|
||||
<div class="localNav">
|
||||
<div class="localNavRow">
|
||||
<div class="localNavRow__section">
|
||||
<div class="localBreadcrumbs">
|
||||
<div class="localBreadcrumb">
|
||||
<a class="localBreadcrumb__link" href="#">
|
||||
Discover
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="localBreadcrumb">
|
||||
<a class="localBreadcrumb__link" href="#">
|
||||
<span class="localBreadcrumb__emphasis">0</span> hits
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="localNavRow__section">
|
||||
<div class="localMenu">
|
||||
<button class="localMenuItem">
|
||||
New
|
||||
</button>
|
||||
|
||||
<button class="localMenuItem">
|
||||
Save
|
||||
</button>
|
||||
|
||||
<button class="localMenuItem">
|
||||
Open
|
||||
</button>
|
||||
|
||||
<button class="localMenuItem">
|
||||
<div class="localMenuItem__icon fa fa-clock-o"></div>
|
||||
Last 5 minutes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="localNavRow localNavRow--secondary">
|
||||
<div class="localSearch">
|
||||
<input
|
||||
class="localSearchInput localSearchInput-isInvalid"
|
||||
type="text"
|
||||
placeholder="Filter..."
|
||||
autocomplete="off"
|
||||
>
|
||||
<button class="localSearchButton">
|
||||
<span class="fa fa-search"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
<div class="localNav">
|
||||
<div class="localNavRow">
|
||||
<div class="localNavRow__section">
|
||||
<div class="localTitle">
|
||||
Untitled Document
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="localNavRow__section">
|
||||
<div class="localMenu">
|
||||
<div class="localMenuItem">
|
||||
New
|
||||
</div>
|
||||
|
||||
<div class="localMenuItem">
|
||||
Save
|
||||
</div>
|
||||
|
||||
<div class="localMenuItem">
|
||||
Open
|
||||
</div>
|
||||
|
||||
<button class="localMenuItem">
|
||||
<div class="localMenuItem__icon fa fa-clock-o"></div>
|
||||
Last 5 minutes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,57 @@
|
|||
|
||||
<div class="localNav">
|
||||
<div class="localNavRow">
|
||||
<div class="localNavRow__section">
|
||||
<div class="localBreadcrumbs">
|
||||
<div class="localBreadcrumb">
|
||||
<a class="localBreadcrumb__link" href="#">
|
||||
Discover
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="localBreadcrumb">
|
||||
<a class="localBreadcrumb__link" href="#">
|
||||
<span class="localBreadcrumb__emphasis">0</span> <span>hits</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="localNavRow__section">
|
||||
<div class="localMenu">
|
||||
<button class="localMenuItem">
|
||||
New
|
||||
</button>
|
||||
|
||||
<button class="localMenuItem">
|
||||
Save
|
||||
</button>
|
||||
|
||||
<button class="localMenuItem">
|
||||
Open
|
||||
</button>
|
||||
|
||||
<button class="localMenuItem">
|
||||
<div class="localMenuItem__icon fa fa-clock-o"></div>
|
||||
Last 5 minutes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="localNavRow localNavRow--secondary">
|
||||
<div class="localTabs">
|
||||
<a class="localTab localTab-isSelected" href="#">
|
||||
Overview
|
||||
</a>
|
||||
|
||||
<a class="localTab" href="#">
|
||||
Your Documents
|
||||
</a>
|
||||
|
||||
<a class="localTab" href="#">
|
||||
Another Tab
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
import React, {
|
||||
Component,
|
||||
} from 'react';
|
||||
|
||||
export default class NotFoundView extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Page not found.</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
39
src/ui_framework/webpack.config.js
Normal file
39
src/ui_framework/webpack.config.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
var path = require('path');
|
||||
|
||||
module.exports = {
|
||||
devtool: 'source-map',
|
||||
|
||||
entry: {
|
||||
guide: './src/ui_framework/src/guide/index.js'
|
||||
},
|
||||
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'src/ui_framework/public'),
|
||||
filename: 'bundle.js'
|
||||
},
|
||||
|
||||
resolve: {
|
||||
root: [
|
||||
path.resolve(__dirname, 'src/ui_framework/src')
|
||||
]
|
||||
},
|
||||
|
||||
module: {
|
||||
loaders: [{
|
||||
test: /\.jsx?$/,
|
||||
loader: 'babel',
|
||||
exclude: /node_modules/
|
||||
}, {
|
||||
test: /\.scss$/,
|
||||
loaders: ['style', 'css', 'sass'],
|
||||
exclude: /node_modules/
|
||||
}, {
|
||||
test: /\.html$/,
|
||||
loader: 'html',
|
||||
exclude: /node_modules/
|
||||
}, {
|
||||
test: require.resolve('jquery'),
|
||||
loader: 'expose?jQuery!expose?$'
|
||||
}]
|
||||
}
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue