Merge pull request #1 from lyweiwei/master
Check in the new backbone-toolbar
This commit is contained in:
Коммит
b89cfe0c07
|
@ -0,0 +1,11 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
|
@ -0,0 +1,8 @@
|
|||
# npm packages
|
||||
node_modules
|
||||
|
||||
# code coverage
|
||||
coverage
|
||||
|
||||
# output folder
|
||||
dist
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
"extends": "xo-space"
|
||||
"env":
|
||||
"mocha": true
|
||||
"amd": true
|
||||
"browser": true
|
||||
"rules":
|
||||
"comma-dangle":
|
||||
- 2
|
||||
- "always-multiline"
|
||||
"object-curly-spacing":
|
||||
- 2
|
||||
- "always"
|
||||
"linebreak-style": 0
|
|
@ -0,0 +1,16 @@
|
|||
# npm packages
|
||||
node_modules
|
||||
|
||||
# code coverage
|
||||
coverage
|
||||
|
||||
# output folder
|
||||
dist
|
||||
|
||||
# npm logs
|
||||
node-debug.log
|
||||
npm-debug.log
|
||||
|
||||
# generated code for example pages
|
||||
examples/requirejs/require.config.js
|
||||
examples/webpack/dist
|
|
@ -0,0 +1,6 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- v5
|
||||
- v4
|
||||
- '0.12'
|
||||
- '0.10'
|
34
README.md
34
README.md
|
@ -1,2 +1,36 @@
|
|||
# backbone-toolbar
|
||||
|
||||
[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][daviddm-image]][daviddm-url] [![Coverage percentage][coveralls-image]][coveralls-url]
|
||||
|
||||
A Backbone based toolbar implementation.
|
||||
|
||||
## Usage
|
||||
```bash
|
||||
npm install --save backbone-toolbar
|
||||
```
|
||||
|
||||
In your JavaScript code
|
||||
### As AMD
|
||||
```javascript
|
||||
require(['backbone-toolbar'], function (backboneToolbar) {
|
||||
// use the backboneToolbar
|
||||
});
|
||||
```
|
||||
|
||||
### As CMD
|
||||
```javascript
|
||||
var backboneToolbar = require('backbone-toolbar');
|
||||
|
||||
// use the backboneToolbar
|
||||
```
|
||||
|
||||
|
||||
[npm-image]: https://badge.fury.io/js/backbone-toolbar.svg
|
||||
[npm-url]: https://npmjs.org/package/backbone-toolbar
|
||||
[travis-image]: https://travis-ci.org/Microsoft/backbone-toolbar.svg?branch=master
|
||||
[travis-url]: https://travis-ci.org/Microsoft/backbone-toolbar
|
||||
[daviddm-image]: https://david-dm.org/Microsoft/backbone-toolbar.svg?theme=shields.io
|
||||
[daviddm-url]: https://david-dm.org/Microsoft/backbone-toolbar
|
||||
[coveralls-image]: https://coveralls.io/repos/Microsoft/backbone-toolbar/badge.svg
|
||||
[coveralls-url]: https://coveralls.io/r/Microsoft/backbone-toolbar
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"template": "./index.jade"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
doctype html
|
||||
html(lang="en")
|
||||
head
|
||||
title= title
|
||||
body
|
||||
.toolbar-container
|
||||
script(type="application/javascript", src=bundle)
|
|
@ -0,0 +1,60 @@
|
|||
import { ToolbarView } from '../../js';
|
||||
import 'bootstrap-webpack';
|
||||
|
||||
window.toolbar = new ToolbarView({
|
||||
el: '.toolbar-container',
|
||||
id: 'demo-toolbar',
|
||||
items: [{
|
||||
type: 'button',
|
||||
classes: ['btn', 'btn-primary'],
|
||||
id: 'button-1st',
|
||||
text: 'A button',
|
||||
onClick: () => console.log('click button-1st'),
|
||||
}, {
|
||||
type: 'button',
|
||||
iconLeft: 'glyphicon-th-large',
|
||||
text: 'The 2nd Button',
|
||||
id: 'button-2nd',
|
||||
onClick: () => console.log('click button-2nd'),
|
||||
}, {
|
||||
type: 'button',
|
||||
text: 'The 3rd Button',
|
||||
iconRight: 'glyphicon-th-list',
|
||||
id: 'button-3rd',
|
||||
onClick: () => console.log('click button-3rd'),
|
||||
}, {
|
||||
type: 'dropdown',
|
||||
button: {
|
||||
text: 'Dropdown',
|
||||
},
|
||||
menu: {
|
||||
items: [{
|
||||
text: 'The 1st DropdownItem',
|
||||
id: 'dropdown-item-1st',
|
||||
onClick: () => console.log('click dropdown-item-1st'),
|
||||
}, {
|
||||
type: 'dropdown-divider',
|
||||
}, {
|
||||
type: 'dropdown-header',
|
||||
text: 'Hello world!',
|
||||
}, {
|
||||
text: 'The 2nd DropdownItem',
|
||||
id: 'dropdown-item-2nd',
|
||||
onClick: () => console.log('click dropdown-item-2nd'),
|
||||
}, {
|
||||
type: 'dropdown-divider',
|
||||
}, {
|
||||
type: 'dropdown-radio-group',
|
||||
id: 'dropdown-radio-group-1st',
|
||||
onSelect: value => console.log(`select ${value}`),
|
||||
onRemove: value => console.log(`remove ${value}`),
|
||||
title: 'A simple radio group',
|
||||
items: [
|
||||
{ text: 'foo', value: 'foo' },
|
||||
{ text: 'bar', value: 'bar' },
|
||||
],
|
||||
}],
|
||||
},
|
||||
}],
|
||||
}).render();
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
var expect = require('chai').expect;
|
||||
|
||||
/* global browser */
|
||||
/* eslint no-unused-expressions: 0 */
|
||||
describe('webdriver.io page', function () {
|
||||
it('should render the head line correctly', function () {
|
||||
var headLine = browser.element('h1');
|
||||
expect(headLine).to.be.exist;
|
||||
});
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
var webpack = require('webpack');
|
||||
|
||||
var webpackConfig = require('../webpack.config');
|
||||
|
||||
webpackConfig.plugins = webpackConfig.plugins || [];
|
||||
webpackConfig.plugins.push(new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 }));
|
||||
|
||||
webpackConfig.module.loaders = webpackConfig.module.loaders.concat([
|
||||
{ test: /bootstrap[\\\/]js[\\\/]/, loader: 'imports?jQuery=jquery' },
|
||||
{ test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/font-woff' },
|
||||
{ test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/octet-stream' },
|
||||
{ test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file' },
|
||||
{ test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=image/svg+xml' },
|
||||
]);
|
||||
|
||||
module.exports = webpackConfig;
|
|
@ -1,182 +0,0 @@
|
|||
.grid-toolbar {
|
||||
background-color: #fff;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.grid-menu-container {
|
||||
vertical-align: middle;
|
||||
padding: 7px 10px;
|
||||
display: inline-block;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.grid-menu {
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
min-width: 0;
|
||||
height: auto;
|
||||
line-height: normal;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.grid-menu-container:hover, .grid-groupmenu-container:hover {
|
||||
background-color: #e5e5e5;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.spritedimage {
|
||||
display: inline-block;
|
||||
margin: 0 5px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.icon-arrowdown-normal {
|
||||
background: transparent url('./sharedUI_14.png') no-repeat scroll -2px -860px;
|
||||
width: 9px;
|
||||
height: 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
display: none;
|
||||
float: left;
|
||||
min-width: 160px;
|
||||
padding: 0px;
|
||||
margin: 2px 0 0;
|
||||
list-style: none;
|
||||
font-size: 12px;
|
||||
background-color: #ffffff;
|
||||
border: 2px solid #cccccc;
|
||||
border-radius: 0px;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
.dropdown-menu .dropdown-header {
|
||||
padding: 7px 10px;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.dropdown-header {
|
||||
display: block;
|
||||
padding: 3px 20px;
|
||||
font-size: 11px;
|
||||
line-height: 1.45833333;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.dropdown-menu > li > .anchor:hover,
|
||||
.dropdown-menu > li > .anchor:focus {
|
||||
text-decoration: none;
|
||||
color: #262626;
|
||||
background-color: #e5e5e5;
|
||||
}
|
||||
|
||||
.dropdown-menu > li > .anchor {
|
||||
display: block;
|
||||
padding: 7px 10px;
|
||||
clear: both;
|
||||
font-family: Arial;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
line-height: 1.45833333;
|
||||
color: #333333;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dropdown-menu > li .primary {
|
||||
padding-right: 80px;
|
||||
}
|
||||
|
||||
.dropdown-menu > li .secondary {
|
||||
font-size: 10.2px;
|
||||
line-height: 17.5px;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.dropdown-menu .divider {
|
||||
height: 1px;
|
||||
margin: 7.5px 5px;
|
||||
overflow: hidden;
|
||||
background-color: #e5e5e5;
|
||||
margin: 3px 5px;
|
||||
cursor: auto;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.dropdown-menu > li .glyphicon-offset-left5 {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.dropdown-menu > li .glyphicon {
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
.glyphicon {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
display: inline-block;
|
||||
font-family: 'Glyphicons Halflings';
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
line-height: 1;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
button.filter_search_icon_small {
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
background-repeat: no-repeat;
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.filter_search_icon_small {
|
||||
background: transparent url('./sharedUI_14.png') no-repeat scroll -2px -443px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
button.filter_search_icon_small {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
input, button, select, textarea {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.wunderbartreeclose, .grid-expand-icon, .expand .alert-arrow-icon {
|
||||
background: transparent url('./sharedUI_18.png') no-repeat scroll -2px -1486px;
|
||||
width: 5px;
|
||||
height: 10px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.toolbar-icon.toolbar-icon-left {
|
||||
margin-left: 0px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.icon-export-normal {
|
||||
background: transparent url('./sharedUI_18.png') no-repeat scroll -2px -983px;
|
||||
width: 9px;
|
||||
height: 12px;
|
||||
overflow: hidden;
|
||||
}
|
|
@ -1,180 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<link href="../../../.out/lib/bootstrap/dist/css/bootstrap.css" rel="stylesheet" media="screen">
|
||||
<link href="http://localhost:8004/StaticResourcesWeb/Application/Styles/theme_next.css" rel="stylesheet" media="screen">
|
||||
<link href="http://localhost:8004/StaticResourcesWeb/Application/Styles/controls_next.css" rel="stylesheet" media="screen">
|
||||
<link href="../style/grid-toolbar.css" rel="stylesheet" media="screen">
|
||||
<script type="text/javascript" src="../../../.out/lib/requirejs-2.1.14/require.js"></script>
|
||||
<script type="text/javascript" src="../../require.config.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class='container'>
|
||||
<div class='row'>
|
||||
<div class="panel panel-info">
|
||||
<div class="panel-heading">Commands you can run in the console</div>
|
||||
<div class="panel-body">
|
||||
<pre>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-info">
|
||||
<div class="panel-heading">Notes</div>
|
||||
<div class="panel-body">
|
||||
<ul>
|
||||
<li>Column 'ContactName' is sortable</li>
|
||||
<li>Shift Columns right and left</li>
|
||||
<li>Use Pager to navigate OData Source</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='row'>
|
||||
<div id='pagination_host_a'></div>
|
||||
<div id='grid_host_a'></div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
window.BINGADS_DEBUG = true;
|
||||
require.config({
|
||||
'baseUrl': '../../../'
|
||||
});
|
||||
|
||||
require(['lib/squire', 'component/auto-config'], function(Squire){
|
||||
var injector = new Squire();
|
||||
|
||||
injector
|
||||
.mock({
|
||||
'component/config/index': { get : function(){ return 'en-us'; } },
|
||||
'data/i18n/component/grid-toolbar/en-us': {
|
||||
'GridToolbar_FilterBar_Title': 'Filter',
|
||||
'GridToolbar_FilterBar_Edit': 'Edit',
|
||||
'GridToolbar_FilterBar_Remove': 'Remove'
|
||||
}
|
||||
})
|
||||
.require([
|
||||
'lib/underscore',
|
||||
'lib/jquery',
|
||||
'lib/backbone',
|
||||
'component/i18n/index',
|
||||
'component/grid-toolbar/button',
|
||||
'component/grid-toolbar/dropdown',
|
||||
'component/grid-toolbar/buttonMenuItem',
|
||||
'component/grid-toolbar/dividerMenuItem',
|
||||
'component/grid-toolbar/headerMenuItem',
|
||||
'component/grid-toolbar/radioGroupMenuItem',
|
||||
'component/grid-toolbar/radioMenuItem',
|
||||
'component/grid-toolbar/subMenuItem',
|
||||
'component/grid-toolbar/filterInput',
|
||||
'component/grid-toolbar/gridToolbarItemContainer',
|
||||
'component/grid-toolbar/gridToolbar',
|
||||
'component/grid-toolbar/filterBar',
|
||||
],
|
||||
function(_, $, Backbone, i18nModel, Button, Dropdown, ButtonMenuItem, DividerMenuItem, HeaderMenuItem,
|
||||
RadioGroupMenuItem, RadioMenuItem, SubMenuItem, FilterInput, GridToolbarItemContainer,
|
||||
GridToolbar, FilterBar) {
|
||||
_.templateSettings = {
|
||||
interpolate: /\{\{(.+?)\}\}/g
|
||||
};
|
||||
|
||||
$(function(){
|
||||
var columnMenu = new Dropdown({
|
||||
title: "Columns",
|
||||
menuItems: [
|
||||
new ButtonMenuItem({text: 'Modify Columns'}),
|
||||
new DividerMenuItem(),
|
||||
new HeaderMenuItem({text: 'Apply saved columns'}),
|
||||
new RadioGroupMenuItem({items: [
|
||||
new RadioMenuItem({text: 'Custom'})
|
||||
]})
|
||||
]
|
||||
});
|
||||
|
||||
var helloMenu = new Dropdown({
|
||||
title: "Hello World",
|
||||
menuItems: [
|
||||
new HeaderMenuItem({text: "Header"}),
|
||||
new ButtonMenuItem({text: "hello", linkText: 'Remove'}),
|
||||
new DividerMenuItem(),
|
||||
new ButtonMenuItem({text: "world"}),
|
||||
new RadioGroupMenuItem({items: [
|
||||
new RadioMenuItem({text: "radio1", linkText: "Remove"}),
|
||||
new RadioMenuItem({text: "radio2", linkText: "Remove"})
|
||||
]}),
|
||||
new DividerMenuItem(),
|
||||
new RadioGroupMenuItem({items: [
|
||||
new RadioMenuItem({text: "radio11", linkText: "Change"}),
|
||||
new RadioMenuItem({text: "radio22", linkText: "Change"})
|
||||
]})
|
||||
]
|
||||
});
|
||||
|
||||
function getGroupItem(dropdown, index) {
|
||||
var groupItems = dropdown.getMenuItems().filter(function(item) {
|
||||
return item.isGroup;
|
||||
});
|
||||
return groupItems[index];
|
||||
}
|
||||
getGroupItem(helloMenu, 0).on('click:link', function(button, item) {
|
||||
button.removeItem(item);
|
||||
});
|
||||
|
||||
getGroupItem(helloMenu, 1).on('click:link', function(button, item) {
|
||||
item.setText('Changed!!!');
|
||||
});
|
||||
|
||||
helloMenu.on('click:link', function(button, item) {
|
||||
if (item.getGroup == null) {
|
||||
button.removeMenuItem(item);
|
||||
}
|
||||
});
|
||||
|
||||
var filterMenu = new Dropdown({
|
||||
title: 'Filter',
|
||||
menuItems: [
|
||||
new ButtonMenuItem({text: 'Create filter'}),
|
||||
new DividerMenuItem(),
|
||||
new HeaderMenuItem({text: 'Apply saved filters'}),
|
||||
new SubMenuItem({text: 'The Sub MenuItem', children: [
|
||||
new ButtonMenuItem({text: 'Good Study'}),
|
||||
new ButtonMenuItem({text: 'Hello World'}),
|
||||
new ButtonMenuItem({text: 'Nimei HAHAH'}),
|
||||
]}),
|
||||
new SubMenuItem({text: 'New SubItem', children: [
|
||||
new ButtonMenuItem({text: 'Change current bids'}),
|
||||
new ButtonMenuItem({text: 'Pass ad group'}),
|
||||
new ButtonMenuItem({text: 'Enable ad groups'})
|
||||
]})
|
||||
]
|
||||
});
|
||||
|
||||
var filterInput = new FilterInput({placeholder: "Hello World"});
|
||||
filterInput.on('change', function(button, filterVal) {
|
||||
button.setValue('Apply!');
|
||||
});
|
||||
|
||||
var filterContainer = new GridToolbarItemContainer({items: [filterMenu, filterInput]});
|
||||
|
||||
var buttonMenu = new Button({text: 'Click Me', leftIconClass: 'icon-export-normal'});
|
||||
buttonMenu.on('click', function() {
|
||||
alert('The button is clicked!');
|
||||
});
|
||||
|
||||
var toolbar = new GridToolbar({items: [buttonMenu, helloMenu, columnMenu, filterContainer]});
|
||||
var filterBar = new FilterBar({columnName: 'Account Name', filterOperator: 'Contains', filterValue: 'ss'});
|
||||
filterBar.on('edit', function() {
|
||||
alert('Click the edit button');
|
||||
}),
|
||||
filterBar.on('remove', function() {
|
||||
alert('Click the remove button');
|
||||
});
|
||||
|
||||
$('body').append(toolbar.render().$el);
|
||||
$('body').append(filterBar.render().$el);
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Двоичные данные
examples/sharedUI_14.png
Двоичные данные
examples/sharedUI_14.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 56 KiB |
Двоичные данные
examples/sharedUI_18.png
Двоичные данные
examples/sharedUI_18.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 83 KiB |
|
@ -0,0 +1,169 @@
|
|||
var fs = require('fs');
|
||||
var os = require('os');
|
||||
var http = require('http');
|
||||
var path = require('path');
|
||||
var childProcess = require('child_process');
|
||||
var resolve = require('resolve');
|
||||
var gulp = require('gulp');
|
||||
var gutil = require('gulp-util');
|
||||
var eslint = require('gulp-eslint');
|
||||
var democase = require('gulp-democase');
|
||||
var excludeGitignore = require('gulp-exclude-gitignore');
|
||||
var webpack = require('webpack');
|
||||
var del = require('del');
|
||||
// coveralls
|
||||
var coveralls = require('gulp-coveralls');
|
||||
// coveralls-end
|
||||
var jsdoc = require('gulp-jsdoc3');
|
||||
|
||||
function webpackBuild(configFilePath) {
|
||||
return function (cb) {
|
||||
webpack(require(configFilePath), function (err, stats) {
|
||||
gutil.log(stats.toString({ colors: true }));
|
||||
cb(err || stats.hasErrors() && new Error('webpack compile error'));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function getSeleniumFilePath() {
|
||||
var SELENIUM_NAME = 'selenium-server-standalone-2.53.0.jar';
|
||||
return path.resolve(os.tmpdir(), SELENIUM_NAME);
|
||||
}
|
||||
|
||||
gulp.task('download-selenium', function (cb) {
|
||||
var filePath = getSeleniumFilePath();
|
||||
fs.stat(filePath, function (err) {
|
||||
if (!err) {
|
||||
return cb(null);
|
||||
}
|
||||
var file = fs.createWriteStream(filePath);
|
||||
var URL = 'http://selenium-release.storage.googleapis.com/2.53/selenium-server-standalone-2.53.0.jar';
|
||||
http.get(URL, function (response) {
|
||||
response.pipe(file);
|
||||
});
|
||||
file.on('error', function (err) {
|
||||
fs.unlinkSync(filePath);
|
||||
cb(err);
|
||||
});
|
||||
file.on('finish', cb);
|
||||
});
|
||||
});
|
||||
|
||||
function startSeleniumServer() {
|
||||
var filePath = getSeleniumFilePath();
|
||||
return childProcess.spawn('java', ['-jar', filePath], {
|
||||
stdio: 'inherit',
|
||||
env: { path: path.join(__dirname, 'node_modules', '.bin') },
|
||||
});
|
||||
}
|
||||
|
||||
function testWithKarmaCmd(handler) {
|
||||
var karmaCmd = path.resolve('./node_modules/.bin/karma');
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
karmaCmd += '.cmd';
|
||||
}
|
||||
|
||||
childProcess.spawn(karmaCmd, [
|
||||
'start',
|
||||
'--single-run',
|
||||
], { stdio: 'inherit' }).on('close', handler);
|
||||
}
|
||||
|
||||
/*
|
||||
function testWithKarmaAPI(handler) {
|
||||
var Server = require('karma').Server;
|
||||
new Server({
|
||||
configFile: path.join(__dirname, 'karma.conf.js'),
|
||||
singleRun: true,
|
||||
}, handler).start();
|
||||
}
|
||||
*/
|
||||
|
||||
gulp.task('test:unit', function (cb) {
|
||||
var handler = function (code) {
|
||||
if (code) {
|
||||
cb(new Error('test failure'));
|
||||
} else {
|
||||
cb();
|
||||
}
|
||||
};
|
||||
testWithKarmaCmd(handler);
|
||||
});
|
||||
|
||||
// coveralls
|
||||
gulp.task('coveralls', ['test'], function () {
|
||||
if (!process.env.CI) {
|
||||
return;
|
||||
}
|
||||
|
||||
return gulp.src(path.join(__dirname, 'coverage/report-lcov/lcov.info'))
|
||||
.pipe(coveralls());
|
||||
});
|
||||
// coveralls-end
|
||||
|
||||
gulp.task('static', function () {
|
||||
return gulp.src(['js/**/*.js', 'demos/**/*.js', 'spec/**/*.js'])
|
||||
.pipe(excludeGitignore())
|
||||
.pipe(eslint())
|
||||
.pipe(eslint.format())
|
||||
.pipe(eslint.failAfterError());
|
||||
});
|
||||
|
||||
gulp.task('docs', function (cb) {
|
||||
gulp.src(['README.md', './src/**/*.js'], { read: false })
|
||||
.pipe(jsdoc(require('./jsdoc.json'), cb));
|
||||
});
|
||||
|
||||
gulp.task('webpack', webpackBuild('./webpack.config'));
|
||||
|
||||
gulp.task('demos', function () {
|
||||
return gulp.src('./demos').pipe(democase());
|
||||
});
|
||||
|
||||
gulp.task('test:demos', ['download-selenium'], function (done) {
|
||||
var pathCli = path.resolve(path.dirname(resolve.sync('webdriverio', {
|
||||
basedir: '.',
|
||||
})), 'lib/cli');
|
||||
var cpSelenium = null;
|
||||
var cpWdio = null;
|
||||
|
||||
cpSelenium = startSeleniumServer().on('error', function () {
|
||||
if (cpWdio) {
|
||||
cpWdio.kill();
|
||||
}
|
||||
done(new Error('Failed to launch the selenium standalone server. Make sure you have JRE available'));
|
||||
});
|
||||
|
||||
cpWdio = childProcess.fork(pathCli, [path.join(__dirname, 'wdio.conf.js')], {
|
||||
env: { DEMOCASE_HTTP_PORT: 8081 },
|
||||
}).on('close', function (code) {
|
||||
cpSelenium.kill();
|
||||
if (code) {
|
||||
done(new Error('selenium test failue'));
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('test', ['test:unit']);
|
||||
|
||||
gulp.task('prepublish', ['webpack']);
|
||||
|
||||
gulp.task('clean:test', function () {
|
||||
return del(['coverage']);
|
||||
});
|
||||
|
||||
gulp.task('clean:build', function () {
|
||||
return del(['dist']);
|
||||
});
|
||||
|
||||
gulp.task('clean', ['clean:build', 'clean:test']);
|
||||
|
||||
gulp.task('default', [
|
||||
'static',
|
||||
'webpack',
|
||||
// coveralls
|
||||
'coveralls',
|
||||
// coveralls-end
|
||||
]);
|
|
@ -1,19 +0,0 @@
|
|||
define([
|
||||
'component/grid-toolbar/menuItem'
|
||||
],
|
||||
function(MenuItem) {
|
||||
'use strict';
|
||||
|
||||
// the divider menu item of dropdown button
|
||||
|
||||
var DividerMenuItem = MenuItem.extend({
|
||||
|
||||
className: 'divider',
|
||||
|
||||
attributes: {
|
||||
role: 'presentation'
|
||||
}
|
||||
});
|
||||
|
||||
return DividerMenuItem;
|
||||
});
|
|
@ -1,30 +0,0 @@
|
|||
define([
|
||||
'component/grid-toolbar/menuItem'
|
||||
],
|
||||
function(MenuItem) {
|
||||
'use strict';
|
||||
|
||||
// the menu item of dropdown button
|
||||
/*
|
||||
The options can contain the following properties:
|
||||
* text: the text of menu bar
|
||||
*/
|
||||
var HeaderMenuItem = MenuItem.extend({
|
||||
|
||||
tagName: 'li',
|
||||
|
||||
className: 'dropdown-header',
|
||||
|
||||
attributes: {
|
||||
role: 'presentation'
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var text = this.options ? this.options.text : '';
|
||||
this.$el.text(text);
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
return HeaderMenuItem;
|
||||
});
|
|
@ -0,0 +1,8 @@
|
|||
mixin buttonMixin(options)
|
||||
button&attributes(options.attributes)(class=options.classes, id=options.id, type='button', role='button', tabindex=options.tabindex)
|
||||
if options.iconLeft
|
||||
span.icon-left.glyphicon(class=options.iconLeft)
|
||||
span.text=options.text
|
||||
if options.iconRight
|
||||
span.icon-right.glyphicon(class=options.iconRight)
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
include ./button-mixin.jade
|
||||
|
||||
-
|
||||
var options = {
|
||||
classes: classes,
|
||||
attributes: {},
|
||||
id: id,
|
||||
tabindex: tabindex,
|
||||
iconLeft: iconLeft,
|
||||
text: text,
|
||||
iconRight: iconRight,
|
||||
};
|
||||
|
||||
+buttonMixin(options)
|
63
js/button.js
63
js/button.js
|
@ -1,44 +1,25 @@
|
|||
define([
|
||||
'lib/underscore',
|
||||
'lib/backbone',
|
||||
'component/grid-toolbar/template/button'
|
||||
],
|
||||
function(_, Backbone, buttonTmpl) {
|
||||
'use strict';
|
||||
import _ from 'underscore';
|
||||
import buttonTemplate from './button.jade';
|
||||
|
||||
// the button toolbar menu.
|
||||
/*
|
||||
The options can contain the following properties:
|
||||
* text: the text of menu bar
|
||||
* leftIconClass: the left icon class of the menu
|
||||
|
||||
## events
|
||||
* `click`: `function(MenuItem)` trigger when user click the toolbar menu.
|
||||
*/
|
||||
var Button = Backbone.View.extend({
|
||||
|
||||
className: 'grid-menu-container',
|
||||
|
||||
events: {
|
||||
'click': '_onClick'
|
||||
},
|
||||
|
||||
initialize: function(options) {
|
||||
this.options = _.defaults(options || {}, {
|
||||
text: '',
|
||||
leftIconClass: null
|
||||
});
|
||||
},
|
||||
|
||||
_onClick: function(event) {
|
||||
this.trigger('click', this);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
this.$el.html(buttonTmpl(this.options));
|
||||
return this;
|
||||
}
|
||||
export function renderButton(button) {
|
||||
const options = _.defaults({}, button, {
|
||||
classes: ['btn', 'btn-default'],
|
||||
id: _.uniqueId('button-'),
|
||||
text: '',
|
||||
iconLeft: null,
|
||||
iconRight: null,
|
||||
tabindex: -1,
|
||||
onClick: null,
|
||||
});
|
||||
const html = buttonTemplate(options);
|
||||
const events = {};
|
||||
|
||||
const { id, onClick } = options;
|
||||
|
||||
if (_.isFunction(onClick)) {
|
||||
events[`click button#${id}`] = onClick;
|
||||
}
|
||||
|
||||
return { html, events };
|
||||
}
|
||||
|
||||
return Button;
|
||||
});
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
define([
|
||||
'lib/underscore',
|
||||
'component/grid-toolbar/menuItem',
|
||||
'component/grid-toolbar/template/buttonMenuItem'
|
||||
],
|
||||
function(_, MenuItem, menuItem) {
|
||||
'use strict';
|
||||
|
||||
// the menu item of dropdown button
|
||||
/*
|
||||
The options can contain the following properties:
|
||||
* text: the text of menu bar
|
||||
* linkText: the text that show on the right of text
|
||||
|
||||
## events
|
||||
* `click`: `function(MenuItem)` trigger when user click the menu item.
|
||||
* `click:link`: `function(MenuItem)` trigger when user click teh link item.
|
||||
*/
|
||||
var ButtonMenuItem = MenuItem.extend({
|
||||
|
||||
attributes: {
|
||||
role: 'presentation'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click' : '_onClick',
|
||||
'click .secondary' : '_onClickLink'
|
||||
},
|
||||
|
||||
initialize: function(options) {
|
||||
this.options = _.defaults(options || {}, {
|
||||
text: '',
|
||||
linkText: ''
|
||||
});
|
||||
},
|
||||
|
||||
setText: function(text) {
|
||||
if (this.options.linkText != null) {
|
||||
this.$el.find('.primary').text(text);
|
||||
} else {
|
||||
this.$el.find('.anchor').text(text);
|
||||
}
|
||||
},
|
||||
|
||||
setLinkText: function(text) {
|
||||
this.$el.find('.secondary').text(text);
|
||||
},
|
||||
|
||||
_onClick: function(event) {
|
||||
this.trigger('click', this);
|
||||
},
|
||||
|
||||
_onClickLink: function(event) {
|
||||
this.trigger('click:link', this);
|
||||
event.stopPropagation();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
this.$el.html(menuItem(this.options));
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
return ButtonMenuItem;
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
li.divider(role='separator')
|
|
@ -0,0 +1,6 @@
|
|||
import dropdownDividerTemplate from './dropdown-divider.jade';
|
||||
|
||||
export function renderDropdownDivider() {
|
||||
return { html: dropdownDividerTemplate() };
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
li.dropdown-header(role='presentation')=text
|
|
@ -0,0 +1,9 @@
|
|||
import _ from 'underscore';
|
||||
import dropdownHeaderTemplate from './dropdown-header.jade';
|
||||
|
||||
export function renderDropdownHeader(dropdownHeader) {
|
||||
return {
|
||||
html: dropdownHeaderTemplate(_.defaults(dropdownHeader, { text: '' })),
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
li.dropdown-item(role='presentation', class=classes, id=id)
|
||||
a(role='menuitem')
|
||||
if iconLeft
|
||||
span.icon-left.glyphicon(class=iconLeft)
|
||||
span.text=text
|
||||
if iconRight
|
||||
span.icon-right.glyphicon(class=iconRight)
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import _ from 'underscore';
|
||||
import dropdownItemTemplate from './dropdown-item.jade';
|
||||
|
||||
export function renderDropdownItem(dropdownItem) {
|
||||
const options = _.extend({
|
||||
classes: [],
|
||||
id: _.uniqueId('dropdown-item-'),
|
||||
text: '',
|
||||
iconLeft: null,
|
||||
iconRight: null,
|
||||
tabindex: -1,
|
||||
onClick: null,
|
||||
}, dropdownItem);
|
||||
const html = dropdownItemTemplate(options);
|
||||
const events = {};
|
||||
|
||||
const { id, onClick } = options;
|
||||
|
||||
if (_.isFunction(onClick)) {
|
||||
events[`click .dropdown-item#${id}`] = onClick;
|
||||
}
|
||||
|
||||
return { html, events };
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
li.dropdown-radio-group(class=classes, id=id)
|
||||
ul.dropdown-menu.dropdown-menu-group
|
||||
li.dropdown-header(role='presentation')=title
|
||||
each item in items
|
||||
li.dropdown-radio-item(role='presentation', data-value=item.value)
|
||||
a(role='menuitem')
|
||||
if item.value === value
|
||||
span.glyphicon.glyphicon-ok.selection.selection-selected
|
||||
else
|
||||
span.glyphicon.glyphicon-ok.selection
|
||||
span.text=item.text
|
||||
if !item.locked
|
||||
span.remove
|
||||
if removeText
|
||||
span.pull-right.remove-text=removeText
|
||||
if removeIcon
|
||||
span.pull-right.glyphicon.remove-icon(class=removeIcon)
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
import $ from 'jquery';
|
||||
import _ from 'underscore';
|
||||
import dropdownRadioGroupTemplate from './dropdown-radio-group.jade';
|
||||
import './dropdown-radio-group.less';
|
||||
|
||||
function normalizeItem(item) {
|
||||
const dropdownRadioGroup = _.extend({
|
||||
classes: [],
|
||||
title: '',
|
||||
items: [],
|
||||
removeText: null,
|
||||
removeIcon: null,
|
||||
}, item);
|
||||
|
||||
if (!dropdownRadioGroup.removeIcon && !dropdownRadioGroup.removeText) {
|
||||
dropdownRadioGroup.removeIcon = 'glyphicon-remove';
|
||||
}
|
||||
|
||||
return dropdownRadioGroup;
|
||||
}
|
||||
|
||||
export function renderDropdownRadioGroup(item, renderItem) {
|
||||
const dropdownRadioGroup = normalizeItem(item);
|
||||
const html = dropdownRadioGroupTemplate(dropdownRadioGroup);
|
||||
const {
|
||||
id,
|
||||
onSelect = _.noop,
|
||||
onRemove = _.noop,
|
||||
} = dropdownRadioGroup;
|
||||
|
||||
function onClick(e) {
|
||||
const $el = $(e.target);
|
||||
const $li = $el.closest('li');
|
||||
const $remove = $el.closest('.remove', $li.get(0));
|
||||
const value = $li.attr('data-value');
|
||||
if ($remove.length > 0) {
|
||||
onRemove(value);
|
||||
} else {
|
||||
this.update({ id, value });
|
||||
onSelect(value);
|
||||
}
|
||||
}
|
||||
|
||||
const events = {
|
||||
[`click #${id} .dropdown-radio-item`]: onClick,
|
||||
};
|
||||
|
||||
return { html, events };
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.toolbar {
|
||||
.dropdown-radio-group {
|
||||
> .dropdown-menu.dropdown-menu-group {
|
||||
display: block;
|
||||
position: static;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
|
||||
span.selection {
|
||||
visibility: hidden;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
span.selection.selection-selected {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
include ./button-mixin.jade
|
||||
|
||||
.dropdown(class=classes, id=id)
|
||||
+buttonMixin(button)
|
||||
ul.dropdown-menu(role='menu', aria-labeledby=button.id, class=classes, id=id)
|
||||
each item in menu.items
|
||||
!= item.html
|
151
js/dropdown.js
151
js/dropdown.js
|
@ -1,113 +1,46 @@
|
|||
define([
|
||||
'lib/underscore',
|
||||
'lib/backbone',
|
||||
'component/grid-toolbar/template/dropdown',
|
||||
'lib/bootstrap'
|
||||
],
|
||||
function(_, Backbone, buttonTmpl) {
|
||||
'use strict';
|
||||
import _ from 'underscore';
|
||||
import dropdownTemplate from './dropdown.jade';
|
||||
|
||||
// The toolbar view implement the grid toolbar such as filter or select action
|
||||
export function renderDropdown(dropdown, renderItem) {
|
||||
const { button = {}, menu = {} } = dropdown;
|
||||
|
||||
/*
|
||||
## props
|
||||
|
||||
* text: `string` the button text
|
||||
* isShowRightIcon: `boolean` if the right icon needs to show.
|
||||
* leftIconClass/rightIconClass: `string` the class name of button's left/right icon.
|
||||
* menuItems: `Array` the menu items
|
||||
|
||||
## events
|
||||
|
||||
* `click:item`: ``function(Dropdown, MenuItem)``
|
||||
* `click:link`: `function(Dropdown, MenuItem)`
|
||||
*/
|
||||
var Dropdown = Backbone.View.extend({
|
||||
|
||||
className: 'grid-groupmenu-container dropdown',
|
||||
|
||||
initialize: function(options) {
|
||||
this.options = _.defaults(options || {}, {
|
||||
isShowRightIcon: true,
|
||||
menuItems: []
|
||||
});
|
||||
this.menuItems = this.options.menuItems.slice(0);
|
||||
_.each(this.menuItems, this._initMenuItem.bind(this));
|
||||
},
|
||||
|
||||
getMenuItem: function(index) {
|
||||
return this.menuItems[index];
|
||||
},
|
||||
|
||||
getMenuItems: function() {
|
||||
return this.menuItems;
|
||||
},
|
||||
|
||||
// Add one menu item(Backbone.View instance) into the menu items.
|
||||
pushMenuItem: function(item) {
|
||||
this.menuItems.push(item);
|
||||
this._initMenuItem(item);
|
||||
this._renderMenuItem(item);
|
||||
},
|
||||
|
||||
pushMenuItems: function(items) {
|
||||
if (!_.isArray(items)) {
|
||||
items = [items];
|
||||
}
|
||||
_.each(items, this.pushMenuItem, this);
|
||||
},
|
||||
|
||||
removeMenuItem: function(item) {
|
||||
if (item.getGroup) {
|
||||
return this.removeRadioItem(item);
|
||||
}
|
||||
var index = this.menuItems.indexOf(item);
|
||||
if (index !== -1) {
|
||||
this.menuItems.splice(index, 1);
|
||||
}
|
||||
item.isGroup ? item.remove() : item.$el.remove();
|
||||
},
|
||||
|
||||
removeMenuItems: function(items) {
|
||||
if (!_.isArray(items)) items = [items];
|
||||
_.each(items, this.removeMenuItem, this);
|
||||
},
|
||||
|
||||
removeRadioItem: function(item) {
|
||||
var groupItem = item.getGroup && item.getGroup();
|
||||
groupItem && groupItem.removeItem(item);
|
||||
},
|
||||
|
||||
_applyItemEvents: function(item) {
|
||||
item.on('click', function(item) {
|
||||
this.trigger('click:item', this, item);
|
||||
}, this);
|
||||
|
||||
item.on('click:link', function(item) {
|
||||
this.trigger('click:link', this, item);
|
||||
}, this);
|
||||
},
|
||||
|
||||
_initMenuItem: function(item) {
|
||||
if (item.isGroup) {
|
||||
_.each(item.items, this._applyItemEvents, this);
|
||||
} else {
|
||||
this._applyItemEvents(item);
|
||||
}
|
||||
},
|
||||
|
||||
_renderMenuItem: function(item) {
|
||||
this.$itemList.append(item.render().$el);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
this.$el.html(buttonTmpl(this.options));
|
||||
this.$itemList = this.$el.children('.dropdown-menu');
|
||||
// Add all the menu items into the button
|
||||
_.each(this.menuItems, this._renderMenuItem, this);
|
||||
return this;
|
||||
}
|
||||
const events = {};
|
||||
const options = _.defaults({
|
||||
button: _.defaults({
|
||||
tabindex: dropdown.tabindex,
|
||||
attributes: _.defaults({
|
||||
'data-toggle': 'dropdown',
|
||||
'aria-haspopup': 'true',
|
||||
'aria-expanded': 'false',
|
||||
}, button.attributes),
|
||||
classes: _.union(button.classes || [
|
||||
'btn',
|
||||
'btn-default',
|
||||
], [
|
||||
'dropdown-toggle',
|
||||
]),
|
||||
}, button, {
|
||||
id: _.uniqueId('dropdown-button-'),
|
||||
text: '',
|
||||
iconLeft: null,
|
||||
iconRight: 'glyphicon-triangle-bottom',
|
||||
}),
|
||||
menu: _.defaults({
|
||||
items: _.map(menu.items, (item, index) => ({
|
||||
html: renderItem(_.defaults({
|
||||
tabindex: index === 0 ? 0 : -1,
|
||||
}, item, { type: 'dropdown-item' }), events),
|
||||
})),
|
||||
}, menu, {
|
||||
classes: [],
|
||||
id: _.uniqueId('menu-'),
|
||||
}),
|
||||
}, dropdown, {
|
||||
classes: [],
|
||||
id: _.uniqueId('dropdown-'),
|
||||
});
|
||||
const html = dropdownTemplate(options);
|
||||
|
||||
return { html, events };
|
||||
}
|
||||
|
||||
return Dropdown;
|
||||
});
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
define([
|
||||
'lib/underscore',
|
||||
'lib/backbone',
|
||||
'component/grid-toolbar/template/filterBar',
|
||||
'$/i18n!component/grid-toolbar'
|
||||
],
|
||||
function(_, Backbone, filterBarTmpl, i18n) {
|
||||
'use strict';
|
||||
|
||||
// the base class of menu item
|
||||
/**
|
||||
## Options:
|
||||
*columnName: the column name that want to apply to quick filter
|
||||
*filterOperator: the quick filter operator, such as 'Contains'
|
||||
*filterValue: the quick filter value
|
||||
*i18n: i18n module that to localize the string
|
||||
|
||||
## Events
|
||||
*`edit`: triggered when the Edit button is clicked
|
||||
*`remove`: triggered when the Remove button is clicked
|
||||
*/
|
||||
var FilterBar = Backbone.View.extend({
|
||||
className: 'filter-bar',
|
||||
|
||||
events: {
|
||||
'click a.editFilterLink' : '_onClickEdit',
|
||||
'click a.removeFilterLink' : '_onClickRemove'
|
||||
},
|
||||
|
||||
initialize: function(options) {
|
||||
this.options = _.extend({
|
||||
title: i18n.get('GridToolbar_FilterBar_Title'),
|
||||
editText: i18n.get('GridToolbar_FilterBar_Edit'),
|
||||
removeText: i18n.get('GridToolbar_FilterBar_Remove')
|
||||
}, options || {});
|
||||
},
|
||||
|
||||
_onClickEdit: function(event) {
|
||||
this.trigger('edit', this);
|
||||
},
|
||||
|
||||
_onClickRemove: function(event) {
|
||||
this.trigger('remove', this);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
this.$el.html(filterBarTmpl(this.options));
|
||||
return this;
|
||||
},
|
||||
});
|
||||
|
||||
return FilterBar;
|
||||
});
|
|
@ -1,55 +0,0 @@
|
|||
define([
|
||||
'lib/underscore',
|
||||
'lib/backbone',
|
||||
'component/grid-toolbar/template/filterInput'
|
||||
],
|
||||
function(_, Backbone, buttonTmpl) {
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
## props
|
||||
* placeholder: `string` the placeholder for the text input.
|
||||
|
||||
## events
|
||||
* `change`: `function(FilterInput, String)` when the button is clicked.
|
||||
*/
|
||||
var FilterInput = Backbone.View.extend({
|
||||
|
||||
className: 'grid-filter-container',
|
||||
|
||||
events: {
|
||||
'click button': '_onClickButton',
|
||||
'keypress': '_onKeypress',
|
||||
},
|
||||
|
||||
setValue: function(val) {
|
||||
this.$input.val(val);
|
||||
},
|
||||
|
||||
getValue: function() {
|
||||
return this.$input.val();
|
||||
},
|
||||
|
||||
_onClickButton: function(event) {
|
||||
this.trigger('change', this, this.$input.val());
|
||||
},
|
||||
|
||||
_onKeypress: function(event) {
|
||||
var RETURN_CODE = 13;
|
||||
if (event.which === RETURN_CODE) {
|
||||
this.trigger('change', this, this.$input.val());
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var props = _.defaults(this.options || {}, {
|
||||
placeholder: ''
|
||||
});
|
||||
this.$el.html(buttonTmpl(props));
|
||||
this.$input = this.$('input');
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
return FilterInput;
|
||||
});
|
|
@ -1,30 +0,0 @@
|
|||
define([
|
||||
'lib/underscore',
|
||||
'lib/backbone'
|
||||
],
|
||||
function(_, Backbone) {
|
||||
'use strict';
|
||||
|
||||
// the container for the menu buttons
|
||||
|
||||
/*
|
||||
## props
|
||||
* items: `Array` the list of toolbar menus.
|
||||
*/
|
||||
var GridToolbar = Backbone.View.extend({
|
||||
className: 'grid-toolbar',
|
||||
|
||||
initialize: function(options) {
|
||||
this.items = (options || {}).items || [];
|
||||
},
|
||||
|
||||
render: function() {
|
||||
_.each(this.items, function(item) {
|
||||
this.$el.append(item.render().$el);
|
||||
}, this);
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
return GridToolbar;
|
||||
});
|
|
@ -1,31 +0,0 @@
|
|||
define([
|
||||
'lib/underscore',
|
||||
'lib/backbone'
|
||||
],
|
||||
function(_, Backbone) {
|
||||
'use strict';
|
||||
|
||||
// the container for the menu buttons
|
||||
|
||||
/*
|
||||
## props
|
||||
* items: `Array` the list of toolbar menus.
|
||||
*/
|
||||
|
||||
var GridToolbarItemContainer = Backbone.View.extend({
|
||||
className: 'grid-toolbar-item-container',
|
||||
|
||||
initialize: function(options) {
|
||||
this.items = (options || {}).items || [];
|
||||
},
|
||||
|
||||
render: function() {
|
||||
_.each(this.items, function(item) {
|
||||
this.$el.append(item.render().$el);
|
||||
}, this);
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
return GridToolbarItemContainer;
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
import { register } from './item-register.js';
|
||||
import { renderToolbar } from './toolbar.js';
|
||||
import { renderButton } from './button.js';
|
||||
import { renderDropdown } from './dropdown.js';
|
||||
import { renderDropdownItem } from './dropdown-item.js';
|
||||
import { renderDropdownHeader } from './dropdown-header.js';
|
||||
import { renderDropdownDivider } from './dropdown-divider.js';
|
||||
import { renderDropdownRadioGroup } from './dropdown-radio-group.js';
|
||||
|
||||
register('toolbar', renderToolbar);
|
||||
register('button', renderButton);
|
||||
register('dropdown', renderDropdown);
|
||||
register('dropdown-item', renderDropdownItem);
|
||||
register('dropdown-header', renderDropdownHeader);
|
||||
register('dropdown-divider', renderDropdownDivider);
|
||||
register('dropdown-radio-group', renderDropdownRadioGroup);
|
||||
|
||||
export { register } from './item-register.js';
|
||||
export { ToolbarView } from './toolbar-view.js';
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import _ from 'underscore';
|
||||
|
||||
const renderers = {};
|
||||
|
||||
export function register(name, builder) {
|
||||
if (_.has(renderers, name)) {
|
||||
throw new Error('Duplicated registration');
|
||||
}
|
||||
|
||||
if (!_.isFunction(builder)) {
|
||||
throw new Error('Item builder has to be a function');
|
||||
}
|
||||
|
||||
renderers[name] = builder;
|
||||
}
|
||||
|
||||
export function getRenderer(type) {
|
||||
if (!_.isFunction(renderers[type])) {
|
||||
throw new Error('Unknown item type');
|
||||
}
|
||||
|
||||
return renderers[type];
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
define([
|
||||
'lib/backbone'
|
||||
],
|
||||
function(Backbone) {
|
||||
'use strict';
|
||||
|
||||
// the base class of menu item
|
||||
var MenuItem = Backbone.View.extend({
|
||||
tagName: 'li',
|
||||
});
|
||||
|
||||
return MenuItem;
|
||||
});
|
|
@ -1,118 +0,0 @@
|
|||
define([
|
||||
'lib/underscore',
|
||||
'component/grid-toolbar/menuItem'
|
||||
],
|
||||
function(_, MenuItem) {
|
||||
'use strict';
|
||||
|
||||
// the container for the menu buttons
|
||||
|
||||
/*
|
||||
## props
|
||||
* `Array` the list of radio menus.
|
||||
|
||||
## events
|
||||
* `change:selection` the event will be triggered when the selection has changed.
|
||||
* `click:item` the event will be triggered when this item is clicked
|
||||
* `click:link` the event will be triggered when item's link text is clicked.
|
||||
*/
|
||||
var RadioGroupMenuItem = MenuItem.extend({
|
||||
|
||||
initialize: function(options) {
|
||||
this.items = (options || {}).items || [];
|
||||
this.isGroup = true;
|
||||
this.checkedItem = null;
|
||||
this._findCheckedItem();
|
||||
_.each(this.items, this._initItem, this);
|
||||
},
|
||||
|
||||
addItem: function(item) {
|
||||
this.items.push(item);
|
||||
this.$el.append(item.render().$el);
|
||||
this._initItem(item);
|
||||
},
|
||||
|
||||
removeItem: function(item) {
|
||||
item.$el.remove();
|
||||
var index = this.items.indexOf(item);
|
||||
if (index !== -1) {
|
||||
this.items.splice(index, 1);
|
||||
}
|
||||
// if delete the checked menu item, switch the checked state to other radio menu item.
|
||||
if (item === this.checkedItem) {
|
||||
this.checkedItem = this.items[0] || null;
|
||||
this.checkedItem && this.checkedItem.check();
|
||||
this.trigger('change:selection', this, this.checkedItem);
|
||||
}
|
||||
},
|
||||
|
||||
getItems: function() {
|
||||
return this.items;
|
||||
},
|
||||
|
||||
getItemCount: function() {
|
||||
return this.items.length;
|
||||
},
|
||||
|
||||
checkItem: function(item) {
|
||||
if (item !== this.checkedItem) {
|
||||
this.checkedItem && this.checkedItem.uncheck(); // Uncheck the original menu item
|
||||
this.checkedItem = item;
|
||||
item.check();
|
||||
this.trigger('change:selection', this, item);
|
||||
}
|
||||
},
|
||||
|
||||
remove: function() {
|
||||
_.each(this.items, function(item) {
|
||||
item.$el.remove();
|
||||
});
|
||||
this.items = [];
|
||||
},
|
||||
|
||||
_initItem: function(item) {
|
||||
item.setGroup(this);
|
||||
this._applyEvents(item);
|
||||
},
|
||||
|
||||
_applyEvents: function(item) {
|
||||
item.on('click', this._onClickItem, this);
|
||||
item.on('click:link', function(item) {
|
||||
this.trigger('click:link', this, item);
|
||||
}, this);
|
||||
},
|
||||
|
||||
_onClickItem: function(item) {
|
||||
this.checkItem(item);
|
||||
this.trigger('click:item', this, item);
|
||||
},
|
||||
|
||||
_findCheckedItem: function() {
|
||||
var items = this.items;
|
||||
// Get the checked menu item index
|
||||
var checkedItem = this.checkedItem = _.find(items, function(item) {
|
||||
return item.checked;
|
||||
}) || items[0];
|
||||
|
||||
if (!checkedItem) return ;
|
||||
checkedItem.checked = true;
|
||||
this.trigger('change:selection', this, checkedItem);
|
||||
|
||||
// Make other menu items's checked property false
|
||||
_.each(items, function(item) {
|
||||
item.checked = (item === checkedItem);
|
||||
}, this);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var $ul = $('<ul/>');
|
||||
_.each(this.items, function(item) {
|
||||
$ul.append(item.render().$el);
|
||||
});
|
||||
this.$el.append($ul);
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
return RadioGroupMenuItem;
|
||||
});
|
|
@ -1,60 +0,0 @@
|
|||
define([
|
||||
'lib/underscore',
|
||||
'lib/backbone',
|
||||
'component/grid-toolbar/buttonMenuItem',
|
||||
'component/grid-toolbar/template/radioMenuItem'
|
||||
],
|
||||
function(_, Backbone, ButtonMenuItem, menuItem) {
|
||||
'use strict';
|
||||
|
||||
// the menu item of dropdown button
|
||||
/*
|
||||
The options can contain the following properties:
|
||||
* text: the text of menu bar
|
||||
* linkText: the text that show on the right of text
|
||||
* checked: it's valid only if isRadio is true.
|
||||
*/
|
||||
var checkedClassName = 'glyphicon-ok';
|
||||
|
||||
var RadioMenuItem = ButtonMenuItem.extend({
|
||||
initialize: function(options) {
|
||||
this.options = _.defaults(options || {}, {
|
||||
text : '',
|
||||
checked : false,
|
||||
linkText: null
|
||||
});
|
||||
this.checked = this.options.checked;
|
||||
},
|
||||
|
||||
getText: function() {
|
||||
return this.options.text;
|
||||
},
|
||||
|
||||
setGroup: function(groupItem) {
|
||||
this.groupItem = groupItem;
|
||||
},
|
||||
|
||||
getGroup: function() {
|
||||
return this.groupItem;
|
||||
},
|
||||
|
||||
check: function(checked) {
|
||||
this.checked = true;
|
||||
this.$icon.addClass(checkedClassName);
|
||||
},
|
||||
|
||||
uncheck: function() {
|
||||
this.checked = false;
|
||||
this.$icon.removeClass(checkedClassName);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
this.options.checked = this.checked;
|
||||
this.$el.html(menuItem(this.options));
|
||||
this.$icon = this.$el.find('.glyphicon');
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
return RadioMenuItem;
|
||||
});
|
|
@ -1,40 +0,0 @@
|
|||
define([
|
||||
'lib/underscore',
|
||||
'component/grid-toolbar/menuItem',
|
||||
'component/grid-toolbar/template/subMenuItem'
|
||||
], function(_, MenuItem, menuItem) {
|
||||
|
||||
/*
|
||||
options can be:
|
||||
* text: the text of menu bar
|
||||
|
||||
*/
|
||||
var SubMenuItem = MenuItem.extend({
|
||||
|
||||
events: {
|
||||
'mouseenter': '_onMouseOver',
|
||||
'mouseleave': '_onMouseLeave'
|
||||
},
|
||||
|
||||
_onMouseOver: function(event) {
|
||||
this.$('.dropdown-submenu').removeClass('hidden');
|
||||
},
|
||||
|
||||
_onMouseLeave: function(event) {
|
||||
this.$('.dropdown-submenu').addClass('hidden');
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var options = this.options || {};
|
||||
this.$el.html(menuItem(options));
|
||||
var submenu = this.$('.dropdown-submenu');
|
||||
var children = options.children || [];
|
||||
_.each(children, function(child) {
|
||||
submenu.append(child.render().$el);
|
||||
}, this);
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
return SubMenuItem;
|
||||
});
|
|
@ -0,0 +1,129 @@
|
|||
import _ from 'underscore';
|
||||
import Backbone from 'backbone';
|
||||
import { sequence } from './util.js';
|
||||
import { getRenderer } from './item-register.js';
|
||||
import './toolbar.less';
|
||||
|
||||
function defaultState() {
|
||||
return {
|
||||
items: [],
|
||||
classes: [],
|
||||
events: {},
|
||||
};
|
||||
}
|
||||
|
||||
function getItemContext(item) {
|
||||
if (!_.isString(item.type)) {
|
||||
throw new Error('Invalie item');
|
||||
}
|
||||
|
||||
const id = item.id || _.uniqueId(`${item.type}-`);
|
||||
const renderer = getRenderer(item.type);
|
||||
|
||||
return { id, item, renderer, children: [] };
|
||||
}
|
||||
|
||||
function renderItemTree(root) {
|
||||
const contexts = {};
|
||||
const stack = [];
|
||||
|
||||
const renderItem = item => {
|
||||
const context = getItemContext(item);
|
||||
const { renderer, id } = context;
|
||||
|
||||
contexts[id] = context;
|
||||
if (stack.length > 0) {
|
||||
_.last(stack).children.push(id);
|
||||
}
|
||||
stack.push(context);
|
||||
_.extend(context, renderer(_.defaults({ id }, item), renderItem));
|
||||
stack.pop();
|
||||
|
||||
return context.html;
|
||||
};
|
||||
|
||||
renderItem(root);
|
||||
|
||||
return contexts;
|
||||
}
|
||||
|
||||
export class ToolbarView extends Backbone.View {
|
||||
initialize({
|
||||
id = _.uniqueId('toolbar-'),
|
||||
classes = [],
|
||||
items = [],
|
||||
events = {},
|
||||
}) {
|
||||
this._root = { type: 'toolbar', id, classes, items };
|
||||
this._events = events;
|
||||
this._contexts = renderItemTree(this._root);
|
||||
}
|
||||
|
||||
get id() {
|
||||
return this._root.id;
|
||||
}
|
||||
|
||||
events() {
|
||||
const handlerHash = {};
|
||||
const mergeEvents = events => {
|
||||
_.each(events, (handler, key) => {
|
||||
if (!_.has(handlerHash, key)) {
|
||||
handlerHash[key] = [];
|
||||
}
|
||||
handlerHash[key].push(handler);
|
||||
});
|
||||
};
|
||||
mergeEvents(this._events || {});
|
||||
_.each(this._contexts || {}, context => mergeEvents(context.events));
|
||||
|
||||
return _.mapObject(handlerHash, sequence);
|
||||
}
|
||||
|
||||
_removeContext(id) {
|
||||
_.each(this._contexts[id].children, this._removeContext, this);
|
||||
delete this._contexts[id];
|
||||
}
|
||||
|
||||
get(id) {
|
||||
return _.chain(this._contexts).result(id).result('item').value();
|
||||
}
|
||||
|
||||
update(item) {
|
||||
const id = item.id || this.id;
|
||||
const itemNew = _.defaults({ id }, item, this.get(id));
|
||||
|
||||
if (id === this.id) {
|
||||
if (!itemNew.type !== 'toolbar') {
|
||||
throw new Error('The root item must be a toolbar');
|
||||
}
|
||||
this._root = itemNew;
|
||||
}
|
||||
|
||||
if (_.has(this._contexts, id)) {
|
||||
const contexts = renderItemTree(itemNew);
|
||||
this._removeContext(id);
|
||||
_.each(contexts, (context, id) => {
|
||||
if (_.has(this._contexts, id)) {
|
||||
throw new Error('duplicated item id');
|
||||
}
|
||||
this._contexts[id] = context;
|
||||
});
|
||||
if (this._isRendered) {
|
||||
this.undelegateEvents();
|
||||
this.$(`#${id}`).replaceWith(contexts[id].html);
|
||||
this.delegateEvents();
|
||||
}
|
||||
} else {
|
||||
console.warn(`Trying to update invalid item with id '${id}'`);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
this._isRendered = true;
|
||||
this.undelegateEvents();
|
||||
this.$el.html(this._contexts[this.id].html);
|
||||
this.delegateEvents();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
.toolbar(class=classes, id=id)
|
||||
each item in items
|
||||
.toolbar-item
|
||||
!= item.html
|
|
@ -0,0 +1,29 @@
|
|||
import _ from 'underscore';
|
||||
import { parseSelector } from './util.js';
|
||||
import toolbarTemplate from './toolbar.jade';
|
||||
|
||||
function normalizeItem(item) {
|
||||
if (_.isString(item)) {
|
||||
return _.extend({ type: 'stub' }, parseSelector(item));
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
export function renderToolbar(toolbar, renderItem) {
|
||||
const events = {};
|
||||
|
||||
const options = _.defaults({
|
||||
items: _.map(toolbar.items, (item, index) => ({
|
||||
html: renderItem(_.defaults({
|
||||
tabindex: index === 0 ? 0 : -1,
|
||||
}, normalizeItem(item)), events),
|
||||
})),
|
||||
}, toolbar, {
|
||||
classes: [],
|
||||
id: _.uniqueId('toolbar-'),
|
||||
});
|
||||
const html = toolbarTemplate(options);
|
||||
|
||||
return { html, events };
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
.toolbar {
|
||||
.toolbar-item {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.toolbar-item + .toolbar-item {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
span.icon-left + span.text {
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
span.text + span.icon-right {
|
||||
margin-left: 3px;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
import _ from 'underscore';
|
||||
|
||||
export function parseSelector(selector) {
|
||||
const classes = [];
|
||||
const ids = [];
|
||||
const regex = /([#\.])([^#\.]+)/g;
|
||||
let match = null;
|
||||
|
||||
while ((match = regex.exec(selector)) !== null) {
|
||||
if (match[1] === '#') {
|
||||
ids.push(match[2]);
|
||||
} else if (match[1] === '.') {
|
||||
classes.push(match[2]);
|
||||
}
|
||||
}
|
||||
|
||||
const result = { classes };
|
||||
if (!_.isEmpty(ids)) {
|
||||
result.id = _.first(ids);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function sequence(funcs) {
|
||||
return function (...args) {
|
||||
_.each(funcs, func => func.apply(this, args));
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
var _ = require('underscore');
|
||||
var path = require('path');
|
||||
|
||||
function getWebpackConfig() {
|
||||
var webpackConfig = _.omit(require('./webpack.config'), 'entry', 'externals');
|
||||
_.defaults(webpackConfig, { module: {} });
|
||||
|
||||
webpackConfig.module.preLoaders = [{
|
||||
test: /\.js$/,
|
||||
include: path.resolve('./js/'),
|
||||
loader: 'babel',
|
||||
}, {
|
||||
test: /\.js$/,
|
||||
include: path.resolve('./js/'),
|
||||
loader: 'isparta',
|
||||
}].concat(webpackConfig.module.preLoaders || []);
|
||||
|
||||
return webpackConfig;
|
||||
}
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
files: [
|
||||
'speclist.js',
|
||||
],
|
||||
|
||||
frameworks: [
|
||||
'mocha',
|
||||
],
|
||||
|
||||
client: {
|
||||
mocha: {
|
||||
reporter: 'html', // change Karma's debug.html to the mocha web reporter
|
||||
},
|
||||
},
|
||||
|
||||
reporters: ['mocha', 'coverage'],
|
||||
|
||||
preprocessors: {
|
||||
'speclist.js': 'webpack',
|
||||
},
|
||||
|
||||
webpack: getWebpackConfig(),
|
||||
|
||||
coverageReporter: {
|
||||
dir: 'coverage/',
|
||||
reporters: [
|
||||
{ type: 'html', subdir: 'report-html' },
|
||||
{ type: 'lcov', subdir: 'report-lcov' },
|
||||
],
|
||||
},
|
||||
|
||||
browsers: [
|
||||
// 'PhantomJS',
|
||||
'Firefox',
|
||||
],
|
||||
});
|
||||
};
|
88
package.json
88
package.json
|
@ -1,7 +1,85 @@
|
|||
{
|
||||
"testSuites": {
|
||||
"unit": {
|
||||
"cwd": "."
|
||||
}
|
||||
"main": "dist/backbone-toolbar.js",
|
||||
"version": "0.0.1",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "gulp test coveralls",
|
||||
"prepublish": "gulp prepublish"
|
||||
},
|
||||
"license": "MIT",
|
||||
"name": "backbone-toolbar",
|
||||
"description": "A Backbone based toolbar implementation.",
|
||||
"keywords": [],
|
||||
"author": {
|
||||
"name": "Wei Wei",
|
||||
"email": "lyweiwei@outlook.com"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Microsoft/backbone-toolbar.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.13.2",
|
||||
"babel-loader": "^6.2.5",
|
||||
"babel-preset-es2015": "^6.13.2",
|
||||
"babel-preset-stage-3": "^6.11.0",
|
||||
"backbone": "^1.3.3",
|
||||
"bootstrap": "^3.3.7",
|
||||
"bootstrap-webpack": "0.0.5",
|
||||
"chai": "^3.5.0",
|
||||
"css-loader": "^0.23.1",
|
||||
"del": "^2.2.2",
|
||||
"democase": "^0.1.9",
|
||||
"eslint": "^3.3.1",
|
||||
"eslint-config-xo": "^0.15.3",
|
||||
"eslint-config-xo-space": "^0.14.0",
|
||||
"exports-loader": "^0.6.3",
|
||||
"extract-text-webpack-plugin": "^1.0.1",
|
||||
"file-loader": "^0.9.0",
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-coveralls": "^0.1.4",
|
||||
"gulp-democase": "0.0.1",
|
||||
"gulp-eslint": "^3.0.1",
|
||||
"gulp-exclude-gitignore": "^1.0.0",
|
||||
"gulp-file": "^0.3.0",
|
||||
"gulp-jsdoc3": "^0.3.0",
|
||||
"gulp-util": "^3.0.7",
|
||||
"imports-loader": "^0.6.5",
|
||||
"isparta-loader": "^2.0.0",
|
||||
"istanbul-instrumenter-loader": "^0.2.0",
|
||||
"jade": "^1.11.0",
|
||||
"jade-loader": "^0.8.0",
|
||||
"jquery": "^2.2.4",
|
||||
"karma": "^1.2.0",
|
||||
"karma-chrome-launcher": "^2.0.0",
|
||||
"karma-coverage": "^1.1.1",
|
||||
"karma-firefox-launcher": "^1.0.0",
|
||||
"karma-mocha": "^1.1.1",
|
||||
"karma-mocha-reporter": "^2.1.0",
|
||||
"karma-phantomjs-launcher": "^1.0.1",
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-webpack": "^1.8.0",
|
||||
"less": "^2.7.1",
|
||||
"less-loader": "^2.2.3",
|
||||
"lodash": "^4.15.0",
|
||||
"mocha": "^3.0.2",
|
||||
"phantomjs-prebuilt": "^2.1.12",
|
||||
"requirejs": "^2.2.0",
|
||||
"source-map-loader": "^0.1.5",
|
||||
"style-loader": "^0.13.1",
|
||||
"underscore": "^1.8.3",
|
||||
"url-loader": "^0.5.7",
|
||||
"wdio-junit-reporter": "^0.1.0",
|
||||
"wdio-mocha-framework": "^0.4.0",
|
||||
"webdriverio": "^4.2.7",
|
||||
"webpack": "^1.13.2",
|
||||
"webpack-stream": "^3.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"backbone": "^1.3.3",
|
||||
"jquery": "^2.2.4",
|
||||
"underscore": "^1.8.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
"rules":
|
||||
"no-unused-expressions": 0
|
|
@ -1,50 +0,0 @@
|
|||
define([
|
||||
'sinon',
|
||||
'should',
|
||||
'component/grid-toolbar/dropdown',
|
||||
'component/grid-toolbar/buttonMenuItem'
|
||||
],
|
||||
function(sinon, should, Dropdown, ButtonMenuItem) {
|
||||
'use strict';
|
||||
describe('GridToolbar-dropdownbutton', function() {
|
||||
var item1, item2, button;
|
||||
|
||||
// trigger the el's click event.
|
||||
function click(btn) {
|
||||
btn.$el.trigger('click');
|
||||
}
|
||||
|
||||
// trigger the secondary element's click event.
|
||||
function clickSecondary(btn) {
|
||||
btn.$el.find('.secondary').trigger('click');
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
item1 = new ButtonMenuItem({title: "hello", linkText: "remove"});
|
||||
item2 = new ButtonMenuItem({title: "world"});
|
||||
button = new Dropdown({
|
||||
title: "test",
|
||||
menuItems: [item1, item2]
|
||||
});
|
||||
button.render();
|
||||
});
|
||||
|
||||
it('onDidClickMenuItem is normal', function() {
|
||||
var onClick = sinon.spy();
|
||||
button.on('click:item', onClick);
|
||||
click(item1);
|
||||
onClick.should.have.been.called;
|
||||
|
||||
onClick.reset();
|
||||
click(item2);
|
||||
onClick.should.have.been.called;
|
||||
});
|
||||
|
||||
it('onDidClickMenuItemSecondary', function() {
|
||||
var onClick = sinon.spy();
|
||||
button.on('click:link', onClick);
|
||||
clickSecondary(item1);
|
||||
onClick.should.have.been.called;
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,27 +0,0 @@
|
|||
define([
|
||||
'sinon',
|
||||
'should',
|
||||
'component/grid-toolbar/filterInput',
|
||||
],
|
||||
function(sinon, should, FilterInput) {
|
||||
'use strict';
|
||||
describe('GridToolbar-FilterInput', function() {
|
||||
var filterInput;
|
||||
|
||||
function click(filterInput) {
|
||||
filterInput.$el.find('button').trigger('click');
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
filterInput = new FilterInput({placeholder: "Hello World"});
|
||||
filterInput.render();
|
||||
});
|
||||
|
||||
it('onDidApply is OK', function() {
|
||||
var onApply = sinon.spy();
|
||||
filterInput.on('change', onApply);
|
||||
click(filterInput);
|
||||
onApply.should.have.been.called;
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,4 @@
|
|||
var expect = require('chai').expect;
|
||||
|
||||
describe('backbone-toolbar', function () {
|
||||
});
|
|
@ -0,0 +1,29 @@
|
|||
import chai from 'chai';
|
||||
import { parseSelector } from '../js/util.js';
|
||||
|
||||
const expect = chai.expect;
|
||||
|
||||
describe('parseSelector', function () {
|
||||
it('should parse classes correctly', function () {
|
||||
const selector = '.foo.foo-bar';
|
||||
const classes = ['foo', 'foo-bar'];
|
||||
|
||||
expect(parseSelector(selector)).to.deep.equal({ classes });
|
||||
});
|
||||
|
||||
it('should parse id correctly', function () {
|
||||
const selector = '#foo#foo-bar';
|
||||
const id = 'foo';
|
||||
|
||||
expect(parseSelector(selector)).to.deep.equal({ classes: [], id });
|
||||
});
|
||||
|
||||
it('shoul parse mixed selector correctly', function () {
|
||||
const selector = '.foo#bar.foo-bar';
|
||||
const classes = ['foo', 'foo-bar'];
|
||||
const id = 'bar';
|
||||
|
||||
expect(parseSelector(selector)).to.deep.equal({ classes, id });
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
var testsContext = require.context('./spec', true, /\.js$/);
|
||||
testsContext.keys().forEach(testsContext);
|
|
@ -1,75 +0,0 @@
|
|||
.grid-toolbar .grid-toolbar-item-container {
|
||||
margin-left: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.grid-toolbar div:first-child {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
.grid-toolbar {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.grid-toolbar .grid-groupmenu-container {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.grid-toolbar .grid-filter-container {
|
||||
min-width: 151px;
|
||||
border: solid 1px #ccc;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.grid-toolbar .grid-filter-container:hover {
|
||||
border-color: #606060;
|
||||
}
|
||||
|
||||
.grid-toolbar .grid-filter-container .grid-filter-input {
|
||||
width: 130px;
|
||||
color: #333;
|
||||
height: 22px;
|
||||
padding-left: 4px;
|
||||
border-width: 0px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.grid-toolbar .dropdown-menu ul {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.grid-toolbar .dropdown-menu .anchor {
|
||||
display: block;
|
||||
padding: 7px 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.grid-toolbar .dropdown-menu li .anchor:hover,
|
||||
.grid-toolbar .dropdown-menu li .anchor:focus {
|
||||
text-decoration: none;
|
||||
color: #262626;
|
||||
background-color: #e5e5e5;
|
||||
}
|
||||
|
||||
.grid-toolbar .dropdown-menu .dropdown-submenu-item {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.grid-toolbar .dropdown-menu .dropdown-submenu {
|
||||
position: absolute;
|
||||
left: 100%;
|
||||
top: 0px;
|
||||
border: 2px solid #cccccc;
|
||||
list-style: none;
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.grid-toolbar .dropdown-menu .dropdown-submenu-item .spritedimage {
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
.filter-bar .panel-component .panel-body > *{
|
||||
display: inline-block;
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
if leftIconClass
|
||||
- classes = ['spritedimage', 'toolbar-icon', 'toolbar-icon-left', leftIconClass]
|
||||
span(class=classes)
|
||||
input.grid-menu(type="button" value=text || '')
|
|
@ -1,12 +0,0 @@
|
|||
define(['component/jade/util'], function(jade) { if(jade && jade['runtime'] !== undefined) { jade = jade.runtime; } return function template(locals) {
|
||||
var buf = [];
|
||||
var jade_mixins = {};
|
||||
var jade_interp;
|
||||
var locals_ = (locals || {}),leftIconClass = locals_.leftIconClass,classes = locals_.classes,text = locals_.text;
|
||||
if ( leftIconClass)
|
||||
{
|
||||
classes = ['spritedimage', 'toolbar-icon', 'toolbar-icon-left', leftIconClass]
|
||||
buf.push("<span" + (jade.cls([classes], [true])) + "></span>");
|
||||
}
|
||||
buf.push("<input type=\"button\"" + (jade.attr("value", text || '', true, false)) + " class=\"grid-menu\"/>");;return buf.join("");
|
||||
}});
|
|
@ -1,4 +0,0 @@
|
|||
include ./menuItemTitle.jade
|
||||
|
||||
.anchor
|
||||
+generateTitle(text, linkText)
|
|
@ -1,20 +0,0 @@
|
|||
define(['component/jade/util'], function(jade) { if(jade && jade['runtime'] !== undefined) { jade = jade.runtime; } return function template(locals) {
|
||||
var buf = [];
|
||||
var jade_mixins = {};
|
||||
var jade_interp;
|
||||
var locals_ = (locals || {}),text = locals_.text,linkText = locals_.linkText;
|
||||
jade_mixins["generateTitle"] = function(text, linkText){
|
||||
var block = (this && this.block), attributes = (this && this.attributes) || {};
|
||||
if ( linkText)
|
||||
{
|
||||
buf.push("<span class=\"primary\">" + (jade.escape(null == (jade_interp = text) ? "" : jade_interp)) + "</span><a class=\"secondary text-right\">" + (jade.escape(null == (jade_interp = linkText) ? "" : jade_interp)) + "</a>");
|
||||
}
|
||||
else
|
||||
{
|
||||
buf.push(jade.escape(null == (jade_interp = text) ? "" : jade_interp));
|
||||
}
|
||||
};
|
||||
buf.push("<div class=\"anchor\">");
|
||||
jade_mixins["generateTitle"](text, linkText);
|
||||
buf.push("</div>");;return buf.join("");
|
||||
}});
|
|
@ -1,10 +0,0 @@
|
|||
|
||||
div(type="button" data-toggle="dropdown" class="grid-menu-container grid-colchooser dropdown-toggle")
|
||||
if leftIconClass != null
|
||||
span(class="spritedimage toolbar-icon toolbar-icon-left " + leftIconClass)
|
||||
span(class="grid-menu")
|
||||
=title
|
||||
if isShowRightIcon
|
||||
span(class="spritedimage toolbar-icon " + (rightIconClass || "icon-arrowdown-normal"))
|
||||
|
||||
ul.dropdown-menu
|
|
@ -1,17 +0,0 @@
|
|||
define(['component/jade/util'], function(jade) { if(jade && jade['runtime'] !== undefined) { jade = jade.runtime; } return function template(locals) {
|
||||
var buf = [];
|
||||
var jade_mixins = {};
|
||||
var jade_interp;
|
||||
var locals_ = (locals || {}),leftIconClass = locals_.leftIconClass,title = locals_.title,isShowRightIcon = locals_.isShowRightIcon,rightIconClass = locals_.rightIconClass;
|
||||
buf.push("<div type=\"button\" data-toggle=\"dropdown\" class=\"grid-menu-container grid-colchooser dropdown-toggle\">");
|
||||
if ( leftIconClass != null)
|
||||
{
|
||||
buf.push("<span" + (jade.cls(["spritedimage toolbar-icon toolbar-icon-left " + leftIconClass], [true])) + "></span>");
|
||||
}
|
||||
buf.push("<span class=\"grid-menu\">" + (jade.escape(null == (jade_interp = title) ? "" : jade_interp)) + "</span>");
|
||||
if ( isShowRightIcon)
|
||||
{
|
||||
buf.push("<span" + (jade.cls(["spritedimage toolbar-icon " + (rightIconClass || "icon-arrowdown-normal")], [true])) + "></span>");
|
||||
}
|
||||
buf.push("</div><ul class=\"dropdown-menu\"></ul>");;return buf.join("");
|
||||
}});
|
|
@ -1,14 +0,0 @@
|
|||
.panel-component
|
||||
.panel-body.filter-bar
|
||||
.filter-bar-item.filter_title_collapsed=title
|
||||
.filter-bar-item.filter_row_readonly
|
||||
span.column=columnName
|
||||
span.filter_operator=filterOperator
|
||||
span.filterValue=filterValue
|
||||
.filter-bar-item.filter_links
|
||||
a.editFilterLink
|
||||
.spritedimage.filtericonsalign.edit_icon(title=editText)
|
||||
a.editFilterLink=editText
|
||||
a.removeFilterLink
|
||||
.spritedimage.filtericonsalign.remove_icon_gray_16(title=removeText)
|
||||
a.removeFilterLink=removeText
|
|
@ -1,7 +0,0 @@
|
|||
define(['component/jade/util'], function(jade) { if(jade && jade['runtime'] !== undefined) { jade = jade.runtime; } return function template(locals) {
|
||||
var buf = [];
|
||||
var jade_mixins = {};
|
||||
var jade_interp;
|
||||
var locals_ = (locals || {}),title = locals_.title,columnName = locals_.columnName,filterOperator = locals_.filterOperator,filterValue = locals_.filterValue,editText = locals_.editText,removeText = locals_.removeText;
|
||||
buf.push("<div class=\"panel-component\"><div class=\"panel-body filter-bar\"><div class=\"filter-bar-item filter_title_collapsed\">" + (jade.escape(null == (jade_interp = title) ? "" : jade_interp)) + "</div><div class=\"filter-bar-item filter_row_readonly\"><span class=\"column\">" + (jade.escape(null == (jade_interp = columnName) ? "" : jade_interp)) + "</span><span class=\"filter_operator\">" + (jade.escape(null == (jade_interp = filterOperator) ? "" : jade_interp)) + "</span><span class=\"filterValue\">" + (jade.escape(null == (jade_interp = filterValue) ? "" : jade_interp)) + "</span></div><div class=\"filter-bar-item filter_links\"><a class=\"editFilterLink\"><div" + (jade.attr("title", editText, true, false)) + " class=\"spritedimage filtericonsalign edit_icon\"></div></a><a class=\"editFilterLink\">" + (jade.escape(null == (jade_interp = editText ) ? "" : jade_interp)) + "</a><a class=\"removeFilterLink\"><div" + (jade.attr("title", removeText, true, false)) + " class=\"spritedimage filtericonsalign remove_icon_gray_16\"></div></a><a class=\"removeFilterLink\">" + (jade.escape(null == (jade_interp = removeText) ? "" : jade_interp)) + "</a></div></div></div>");;return buf.join("");
|
||||
}});
|
|
@ -1,2 +0,0 @@
|
|||
input.grid-filter-input(placeholder=placeholder)
|
||||
button.filter_search_icon_small
|
|
@ -1,7 +0,0 @@
|
|||
define(['component/jade/util'], function(jade) { if(jade && jade['runtime'] !== undefined) { jade = jade.runtime; } return function template(locals) {
|
||||
var buf = [];
|
||||
var jade_mixins = {};
|
||||
var jade_interp;
|
||||
var locals_ = (locals || {}),placeholder = locals_.placeholder;
|
||||
buf.push("<input" + (jade.attr("placeholder", placeholder, true, false)) + " class=\"grid-filter-input\"/><button class=\"filter_search_icon_small\"></button>");;return buf.join("");
|
||||
}});
|
|
@ -1,8 +0,0 @@
|
|||
mixin generateTitle(text, linkText)
|
||||
if linkText
|
||||
span.primary
|
||||
=text
|
||||
a.secondary.text-right
|
||||
=linkText
|
||||
else
|
||||
=text
|
|
@ -1,17 +0,0 @@
|
|||
define(['component/jade/util'], function(jade) { if(jade && jade['runtime'] !== undefined) { jade = jade.runtime; } return function template(locals) {
|
||||
var buf = [];
|
||||
var jade_mixins = {};
|
||||
var jade_interp;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
;return buf.join("");
|
||||
}});
|
|
@ -1,8 +0,0 @@
|
|||
include ./menuItemTitle.jade
|
||||
|
||||
.anchor
|
||||
- var classNames = ['glyphicon', 'glyphicon-offset-left5']
|
||||
- checked ? classNames.push("glyphicon-ok") : null
|
||||
|
||||
span(class=classNames)
|
||||
+generateTitle(text, linkText)
|
|
@ -1,23 +0,0 @@
|
|||
define(['component/jade/util'], function(jade) { if(jade && jade['runtime'] !== undefined) { jade = jade.runtime; } return function template(locals) {
|
||||
var buf = [];
|
||||
var jade_mixins = {};
|
||||
var jade_interp;
|
||||
var locals_ = (locals || {}),checked = locals_.checked,text = locals_.text,linkText = locals_.linkText;
|
||||
jade_mixins["generateTitle"] = function(text, linkText){
|
||||
var block = (this && this.block), attributes = (this && this.attributes) || {};
|
||||
if ( linkText)
|
||||
{
|
||||
buf.push("<span class=\"primary\">" + (jade.escape(null == (jade_interp = text) ? "" : jade_interp)) + "</span><a class=\"secondary text-right\">" + (jade.escape(null == (jade_interp = linkText) ? "" : jade_interp)) + "</a>");
|
||||
}
|
||||
else
|
||||
{
|
||||
buf.push(jade.escape(null == (jade_interp = text) ? "" : jade_interp));
|
||||
}
|
||||
};
|
||||
buf.push("<div class=\"anchor\">");
|
||||
var classNames = ['glyphicon', 'glyphicon-offset-left5']
|
||||
checked ? classNames.push("glyphicon-ok") : null
|
||||
buf.push("<span" + (jade.cls([classNames], [true])) + "></span>");
|
||||
jade_mixins["generateTitle"](text, linkText);
|
||||
buf.push("</div>");;return buf.join("");
|
||||
}});
|
|
@ -1,4 +0,0 @@
|
|||
.anchor.dropdown-submenu-item
|
||||
span=text || ''
|
||||
span.spritedimage.grid-expand-icon.pull-right
|
||||
ul.dropdown-submenu.hidden
|
|
@ -1,7 +0,0 @@
|
|||
define(['component/jade/util'], function(jade) { if(jade && jade['runtime'] !== undefined) { jade = jade.runtime; } return function template(locals) {
|
||||
var buf = [];
|
||||
var jade_mixins = {};
|
||||
var jade_interp;
|
||||
var locals_ = (locals || {}),text = locals_.text;
|
||||
buf.push("<div class=\"anchor dropdown-submenu-item\"><span>" + (jade.escape(null == (jade_interp = text || '') ? "" : jade_interp)) + "</span><span class=\"spritedimage grid-expand-icon pull-right\"></span><ul class=\"dropdown-submenu hidden\"></ul></div>");;return buf.join("");
|
||||
}});
|
|
@ -0,0 +1,13 @@
|
|||
var path = require('path');
|
||||
var democase = require('democase');
|
||||
|
||||
var demoSet = democase.loadSync(path.resolve(__dirname, 'demos'));
|
||||
var config = demoSet.wdioConfig({
|
||||
capabilities: [{
|
||||
browserName: 'firefox',
|
||||
}],
|
||||
reporters: ['dot', 'junit'],
|
||||
reporterOptions: { outputDir: './test-results/' },
|
||||
});
|
||||
|
||||
module.exports = { config: config };
|
|
@ -0,0 +1,2 @@
|
|||
// Config your webpack resolve.alias in this file
|
||||
module.exports = {};
|
|
@ -0,0 +1,54 @@
|
|||
var _ = require('underscore');
|
||||
var path = require('path');
|
||||
var pkg = require('./package');
|
||||
|
||||
var webpackAlias = pkg.webpackAlias || {};
|
||||
|
||||
try {
|
||||
webpackAlias = require('./webpack.alias');
|
||||
} catch (e) { }
|
||||
|
||||
function getExternals() {
|
||||
var deps = _.keys(pkg.peerDependencies);
|
||||
var externals = _.object(deps, deps);
|
||||
|
||||
return _.reduce(_.pairs(webpackAlias), function (exts, pair) {
|
||||
if (_.has(externals, pair[1])) {
|
||||
exts[pair[0]] = pair[1];
|
||||
}
|
||||
return exts;
|
||||
}, externals);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
entry: path.resolve('./js/index.js'),
|
||||
output: {
|
||||
path: path.join(__dirname, 'dist'),
|
||||
filename: 'backbone-toolbar.js',
|
||||
library: 'backbone-toolbar',
|
||||
libraryTarget: 'umd',
|
||||
umdNamedDefine: false,
|
||||
devtoolModuleFilenameTemplate: function (info) {
|
||||
if (path.isAbsolute(info.absoluteResourcePath)) {
|
||||
return 'webpack-src:///backbone-toolbar-example/' + path.relative('.', info.absoluteResourcePath);
|
||||
}
|
||||
return info.absoluteResourcePath;
|
||||
},
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
// jade
|
||||
{ test: /\.jade$/, loader: 'jade-loader' },
|
||||
// jade-end
|
||||
// es2015
|
||||
{ test: /\.js$/, exclude: /\bnode_modules\b/, loader: 'babel-loader' },
|
||||
// es2015-end
|
||||
// react
|
||||
{ test: /\.less$/, loader: 'style!css!less' },
|
||||
],
|
||||
},
|
||||
babel: { presets: ['es2015', 'stage-3'] },
|
||||
externals: [getExternals()],
|
||||
resolve: { alias: webpackAlias },
|
||||
devtool: 'source-map',
|
||||
};
|
Загрузка…
Ссылка в новой задаче