├── README.md ├── src ├── img │ └── Chatter-box-wendy.jpg ├── scss │ └── index.scss ├── js │ ├── Chat.js │ ├── Routes.js │ ├── ConversationAdd.js │ ├── index.js │ ├── Login.js │ ├── Conversations.js │ ├── Main.js │ └── ConversationForm.js └── index.html ├── .babelrc ├── .gitignore ├── .editorconfig ├── gulpfile.babel.js ├── grommet-toolbox.config.js ├── server └── server.js ├── package.json ├── .eslintrc ├── .scss-lint.yml └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | # grommet-chat 2 | 3 | This is an WIP project. Please do not use it yet. 4 | -------------------------------------------------------------------------------- /src/img/Chatter-box-wendy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grommet/grommet-chat/master/src/img/Chatter-box-wendy.jpg -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ "es2015", "react" ], 3 | "plugins": [ 4 | "transform-object-rest-spread" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /src/scss/index.scss: -------------------------------------------------------------------------------- 1 | // (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP 2 | 3 | @import '~grommet/scss/vanilla/index'; 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **bower_components** 2 | node_modules 3 | **/dist*/** 4 | **/dist-bower*/** 5 | .DS_Store 6 | .sass-cache 7 | *.log 8 | *tmp* 9 | **sublime-project** 10 | **sublime-workspace** 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | max_line_length = 80 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | max_line_length = 0 14 | trim_trailing_whitespace = false 15 | 16 | [COMMIT_EDITMSG] 17 | max_line_length = 0 18 | -------------------------------------------------------------------------------- /gulpfile.babel.js: -------------------------------------------------------------------------------- 1 | import gulp from 'gulp'; 2 | import { argv } from 'yargs'; 3 | import grommetToolbox, { getOptions } from 'grommet-toolbox'; 4 | 5 | const options = getOptions(); 6 | 7 | gulp.task('set-webpack-alias', function () { 8 | if (options.alias && argv.useAlias) { 9 | options.webpack.resolve.alias = options.alias; 10 | } 11 | }); 12 | 13 | grommetToolbox(gulp); 14 | -------------------------------------------------------------------------------- /src/js/Chat.js: -------------------------------------------------------------------------------- 1 | // (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP 2 | 3 | import React, { Component } from 'react'; 4 | import App from 'grommet/components/App'; 5 | 6 | export default class Chat extends Component { 7 | 8 | render () { 9 | return ( 10 | 11 | {this.props.children} 12 | 13 | ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/js/Routes.js: -------------------------------------------------------------------------------- 1 | // (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP 2 | 3 | import Chat from './Chat'; 4 | import Login from './Login'; 5 | import Main from './Main'; 6 | import Conversations from './Conversations'; 7 | import ConversationAdd from './ConversationAdd'; 8 | // import TBD from 'grommet/components/TBD'; 9 | 10 | export let routes = { 11 | path: '/', component: Chat, 12 | childRoutes: [ 13 | { path: 'login', component: Login }, 14 | { component: Main, 15 | childRoutes: [ 16 | { path: 'conversations', component: Conversations, 17 | childRoutes: [ 18 | { path: 'add', component: ConversationAdd } 19 | ] 20 | } 21 | ] 22 | } 23 | ] 24 | }; 25 | -------------------------------------------------------------------------------- /src/js/ConversationAdd.js: -------------------------------------------------------------------------------- 1 | // (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP 2 | 3 | import React, { Component, PropTypes } from 'react'; 4 | import Layer from 'grommet/components/Layer'; 5 | import ConversationForm from './ConversationForm'; 6 | 7 | export default class ConversationAdd extends Component { 8 | 9 | constructor () { 10 | super(); 11 | this._onSubmit = this._onSubmit.bind(this); 12 | } 13 | 14 | _onSubmit (size) { 15 | const { router } = this.context; 16 | // this.props.dispatch(addItem(size)); 17 | router.push({ pathname: '/conversations' }); 18 | } 19 | 20 | render () { 21 | return ( 22 | 23 | 25 | 26 | ); 27 | } 28 | } 29 | 30 | ConversationAdd.contextTypes = { 31 | router: PropTypes.any 32 | }; 33 | -------------------------------------------------------------------------------- /grommet-toolbox.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import webpack from 'webpack'; 3 | 4 | export default { 5 | copyAssets: [ 6 | 'src/index.html', 7 | { 8 | asset: 'src/img/**', 9 | dist: 'dist/img/' 10 | } 11 | ], 12 | scssAssets: ['src/scss/**/*.scss'], 13 | jsAssets: ['src/js/**/*.js'], 14 | mainJs: 'src/js/index.js', 15 | mainScss: 'src/scss/index.scss', 16 | webpack: { 17 | resolve: { 18 | root: [ 19 | path.resolve(__dirname, './node_modules') 20 | ] 21 | }, 22 | plugins: [ 23 | new webpack.ProvidePlugin({ 24 | 'fetch': 'exports?self.fetch!whatwg-fetch' 25 | }) 26 | ] 27 | }, 28 | devServerPort: 8019, 29 | devServerProxy: { 30 | "/rest/*": 'http://localhost:8119' 31 | }, 32 | websocketHost: 'localhost:8119', 33 | scsslint: true, 34 | sync: { 35 | hostname: 'grommet.us.rdlabs.hpecorp.net', 36 | username: 'ligo', 37 | remoteDestination: '/var/www/html/examples/chat/dist' 38 | }, 39 | alias: { 40 | 'grommet-templates': path.resolve(__dirname, '../grommet-templates/src/js'), 41 | 'grommet-addons': path.resolve(__dirname, '../grommet-addons/src/js'), 42 | 'grommet/scss': path.resolve(__dirname, '../grommet/src/scss'), 43 | 'grommet': path.resolve(__dirname, '../grommet/src/js') 44 | }, 45 | devPreprocess: ['set-webpack-alias'], 46 | distPreprocess: ['set-webpack-alias'] 47 | }; 48 | -------------------------------------------------------------------------------- /src/js/index.js: -------------------------------------------------------------------------------- 1 | // (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP 2 | 3 | import 'whatwg-fetch'; 4 | import { polyfill as promisePolyfill } from 'es6-promise'; 5 | promisePolyfill(); 6 | 7 | import '../scss/index.scss'; 8 | 9 | import React from 'react'; 10 | import ReactDOM from 'react-dom'; 11 | import { Router, browserHistory } from 'react-router'; 12 | import { createHistory, useBasename } from 'history'; 13 | import { routes } from './Routes'; 14 | 15 | // import store from './store'; 16 | // import { initialize as initializeSession } from './Session'; 17 | 18 | let history; 19 | if (window && window.location && 20 | 'grommet.us.rdlabs.hpecorp.net' === window.location.host) { 21 | history = useBasename(createHistory)({ basename: '/chat' }); 22 | } else { 23 | history = browserHistory; 24 | } 25 | 26 | // let wsProtocol = 'ws'; 27 | // if (window.location.protocol === 'https:') { 28 | // wsProtocol = 'wss'; 29 | // } 30 | // 31 | // // The port number needs to align with devServerProxy and websocketHost in grommet-toolbox.config.js 32 | // let hostName = NODE_ENV === 'development' ? 'localhost:8119' : window.location.host; 33 | // apiConfigure({ 34 | // urlPrefix: ROUTE_PREFIX, 35 | // webSocketUrl: `${wsProtocol}://${hostName}${ROUTE_PREFIX}/ws` 36 | // }); 37 | 38 | let element = document.getElementById('content'); 39 | 40 | ReactDOM.render(( 41 | 42 | ), element); 43 | 44 | document.body.classList.remove('loading'); 45 | 46 | // initializeSession(); 47 | -------------------------------------------------------------------------------- /src/js/Login.js: -------------------------------------------------------------------------------- 1 | // (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP 2 | 3 | import React, { Component, PropTypes } from 'react'; 4 | import Split from 'grommet/components/Split'; 5 | import Sidebar from 'grommet/components/Sidebar'; 6 | import LoginForm from 'grommet/components/LoginForm'; 7 | import ChatIcon from 'grommet/components/icons/base/Chat'; 8 | import Box from 'grommet/components/Box'; 9 | 10 | export default class Login extends Component { 11 | 12 | constructor (props) { 13 | super(props); 14 | this._onSubmit = this._onSubmit.bind(this); 15 | this.state = { busy: false }; 16 | } 17 | 18 | componentWillReceiveProps (nextProps) { 19 | if (this.state.busy) { 20 | this.setState({ busy: false }); 21 | } 22 | } 23 | 24 | _onSubmit (fields) { 25 | this.setState({ busy: true }); 26 | // this.props.dispatch(login('', fields.username, fields.password)); 27 | this.context.router.push('/conversations'); /// for now 28 | } 29 | 30 | render () { 31 | return ( 32 | 33 | 34 | 35 | } 37 | title="Chat" 38 | onSubmit={this.state.busy ? null : this._onSubmit} 39 | errors={[]} 40 | usernameType="email" /> 41 | 42 | 43 | ); 44 | } 45 | } 46 | 47 | /// for now 48 | Login.contextTypes = { 49 | router: PropTypes.any 50 | }; 51 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | // (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP 2 | 3 | var compression = require('compression'); 4 | var Ddos = require('ddos'); 5 | // increase ddos burst to avoid false positives with this app 6 | var ddos = new Ddos({ burst: 80 }); 7 | var express = require('express'); 8 | var http = require("http"); 9 | var morgan = require('morgan'); 10 | var bodyParser = require('body-parser'); 11 | var busboyBodyParser = require('busboy-body-parser'); 12 | var cookieParser = require('cookie-parser'); 13 | var path = require('path'); 14 | var rest = require('./rest'); 15 | var throng = require('throng'); 16 | 17 | var PORT = process.env.PORT || 8119; 18 | var PREFIX = '/'; 19 | if (process.env.PREFIX) { 20 | PREFIX = '/' + process.env.PREFIX + '/'; 21 | } 22 | 23 | var WORKERS = process.env.WEB_CONCURRENCY || 1; 24 | 25 | throng(WORKERS, start); 26 | 27 | function start () { 28 | var app = express(); 29 | 30 | app.use(ddos.express); 31 | 32 | app.use(compression()); 33 | 34 | app.use(cookieParser()); 35 | 36 | app.use(morgan('tiny')); 37 | 38 | app.use(bodyParser.json()); 39 | 40 | app.use(busboyBodyParser()); 41 | 42 | app.use('/rest', rest.router); 43 | 44 | // UI serving 45 | app.use('/', express.static(path.join(__dirname, '/../dist'))); 46 | app.get('/*', function (req, res) { 47 | res.sendFile(path.resolve(path.join(__dirname, '/../dist/index.html'))); 48 | }); 49 | 50 | var server = http.createServer(app); 51 | 52 | rest.setup(server, PREFIX); 53 | 54 | server.listen(PORT); 55 | 56 | console.log('Server started, listening at: http://localhost:' + PORT); 57 | } 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grommet-chat", 3 | "version": "0.1.0", 4 | "license": "Apache-2.0", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/grommet/grommet-chat.git" 8 | }, 9 | "dependencies": { 10 | "babel-plugin-transform-object-rest-spread": "^6.6.5", 11 | "babel-preset-es2015": "^6.3.13", 12 | "babel-preset-react": "^6.3.13", 13 | "babel-register": "^6.5.2", 14 | "body-parser": "^1.10.0", 15 | "busboy-body-parser": "0.0.9", 16 | "classnames": "^2.2.5", 17 | "compression": "^1.4.4", 18 | "cookie-parser": "^1.3.4", 19 | "ddos": "^0.1.7", 20 | "es6-promise": "^3.2.1", 21 | "exports-loader": "^0.6.3", 22 | "express": "^4.10.6", 23 | "grommet": "https://github.com/grommet/grommet/tarball/stable", 24 | "grommet-templates": "https://github.com/grommet/grommet-templates/tarball/stable", 25 | "json-server": "^0.5.5", 26 | "json-stringify-pretty-compact": "^1.0.1", 27 | "morgan": "^1.5.0", 28 | "react": "^15.3.0", 29 | "react-redux": "^3.1.0", 30 | "react-router": "^2.4.0", 31 | "redux": "^3.0.2", 32 | "redux-thunk": "^1.0.0", 33 | "request": "^2.53.0", 34 | "serve-static": "^1.7.1", 35 | "throng": "^4.0.0", 36 | "whatwg-fetch": "^1.0.0", 37 | "ws": "^0.7.2" 38 | }, 39 | "devDependencies": { 40 | "del": "^2.0.2", 41 | "grommet-toolbox": "^0.4.0", 42 | "gulp": "^3.9.0", 43 | "gulp-git": "^1.2.3", 44 | "gulp-nodemon": "^2.0.3", 45 | "gulp-rsync": "0.0.5", 46 | "mkdirp": "^0.5.1", 47 | "redux-devtools": "^2.1.5", 48 | "webpack": "^1.9.11", 49 | "webpack-stream": "^3.2.0", 50 | "yargs": "^4.7.0" 51 | }, 52 | "scripts": { 53 | "build": "gulp dist", 54 | "start": "node server/server.js" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | --- 2 | parser: babel-eslint 3 | 4 | plugins: 5 | - react 6 | 7 | ecmaFeatures: 8 | jsx: true 9 | 10 | env: 11 | browser: true 12 | node: true 13 | 14 | globals: 15 | __DEV__: true 16 | __THEME__: true 17 | __DEV_MODE__: true 18 | __SOCKET_HOST__: true 19 | IntlPolyfill: true 20 | Modernizr: true 21 | describe: true 22 | it: true 23 | beforeEach: true 24 | afterEach: true 25 | before: true 26 | after: true 27 | 28 | rules: 29 | # ERRORS 30 | space-before-blocks: 2 31 | indent: [2, 2, { SwitchCase: 1 }] 32 | brace-style: 2 33 | # keyword-spacing: 2 34 | comma-dangle: 2 35 | no-unused-expressions: 2 36 | block-scoped-var: 2 37 | eol-last: 2 38 | dot-notation: 2 39 | consistent-return: 2 40 | no-unused-vars: [2, args: none] 41 | semi: [2, "always"] 42 | 43 | # DISABLED 44 | max-len: 0 45 | #change soon back to max-len: [1, 80] 46 | no-underscore-dangle: 0 47 | new-cap: 0 48 | no-use-before-define: 0 49 | key-spacing: 0 50 | eqeqeq: 0 51 | strict: 0 52 | space-unary-ops: 0 53 | yoda: 0 54 | no-loop-func: 0 55 | no-trailing-spaces: 0 56 | no-multi-spaces: 0 57 | no-shadow: 0 58 | no-alert: 0 59 | no-process-exit: 0 60 | no-extend-native: 0 61 | # block-scoped-var: 0 62 | quotes: 0 63 | jsx-quotes: 0 64 | 65 | # REACT DISABLED 66 | react/display-name: 0 67 | react/jsx-sort-prop-types: 0 68 | react/prop-types: 0 69 | react/no-did-mount-set-state: 0 70 | react/no-did-update-set-state: 0 71 | react/jsx-max-props-per-line: 0 72 | react/jsx-sort-props: 0 73 | react/no-multi-comp: 0 74 | react/jsx-boolean-value: 0 75 | react/no-danger: 0 76 | 77 | react/jsx-curly-spacing: 1 78 | react/jsx-no-duplicate-props: 1 79 | react/jsx-no-undef: 1 80 | react/jsx-uses-react: 1 81 | react/jsx-uses-vars: 1 82 | react/no-unknown-property: 1 83 | react/react-in-jsx-scope: 1 84 | react/require-extension: 1 85 | react/self-closing-comp: 1 86 | react/sort-comp: 1 87 | react/wrap-multilines: 1 88 | -------------------------------------------------------------------------------- /src/js/Conversations.js: -------------------------------------------------------------------------------- 1 | // (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP 2 | 3 | import React, { Component, PropTypes } from 'react'; 4 | import Tiles from 'grommet/components/Tiles'; 5 | import Tile from 'grommet/components/Tile'; 6 | import Heading from 'grommet/components/Heading'; 7 | import Box from 'grommet/components/Box'; 8 | 9 | // hard code for now 10 | const STATE = { 11 | conversations: [ 12 | { 13 | _id: '1', 14 | name: 'grommet-core', 15 | messages: [ 16 | { 17 | _id: '1', 18 | text: 'Where do you guys eat lunch?', 19 | personId: '1', 20 | date: (new Date()).toISOString() 21 | }, 22 | { 23 | _id: '2', 24 | text: 'Sushirito', 25 | personId: '2', 26 | date: (new Date()).toISOString() 27 | } 28 | ] 29 | } 30 | ], 31 | people: { 32 | 1: { name: 'Alan' }, 33 | 2: { name: 'Chris' } 34 | } 35 | }; 36 | 37 | export default class Conversations extends Component { 38 | 39 | constructor () { 40 | super(); 41 | this._onSubmit = this._onSubmit.bind(this); 42 | this.state = STATE; 43 | } 44 | 45 | _onSubmit (size) { 46 | const { router } = this.context; 47 | // this.props.dispatch(addItem(size)); 48 | router.push({ pathname: '/conversations' }); 49 | } 50 | 51 | render () { 52 | const { conversations } = this.state; 53 | 54 | const tiles = conversations.map(conversation => { 55 | const messages = conversation.messages.map(message => { 56 | return ( 57 | 58 | {message.text} 59 | 60 | ); 61 | }); 62 | return ( 63 | 64 | {conversation.name} 65 | {messages} 66 | 67 | 68 | ); 69 | }); 70 | 71 | return ( 72 | 73 | 74 | {tiles} 75 | 76 | {this.props.children} 77 | 78 | ); 79 | } 80 | } 81 | 82 | Conversations.contextTypes = { 83 | router: PropTypes.any 84 | }; 85 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Grommet Chat 7 | 8 | 9 | 10 | 11 | 12 | 52 | 53 | 54 |
55 | 60 | 83 |
84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /src/js/Main.js: -------------------------------------------------------------------------------- 1 | // (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP 2 | 3 | import React, { Component } from 'react'; 4 | import SideSplit from 'grommet-addons/components/SideSplit'; 5 | import Sidebar from 'grommet/components/Sidebar'; 6 | import Header from 'grommet/components/Header'; 7 | import Footer from 'grommet/components/Footer'; 8 | import Title from 'grommet/components/Title'; 9 | import Heading from 'grommet/components/Heading'; 10 | import UserIcon from 'grommet/components/icons/base/User'; 11 | import Box from 'grommet/components/Box'; 12 | import Search from 'grommet/components/Search'; 13 | import Menu from 'grommet/components/Menu'; 14 | import Anchor from 'grommet/components/Anchor'; 15 | import Button from 'grommet/components/Button'; 16 | import ChatIcon from 'grommet/components/icons/base/Chat'; 17 | import AddIcon from 'grommet/components/icons/base/Add'; 18 | 19 | export default class Main extends Component { 20 | 21 | constructor () { 22 | super(); 23 | this.state = { navActive: true }; 24 | } 25 | 26 | render () { 27 | const { navActive } = this.state; 28 | 29 | let sidebar; 30 | if (navActive) { 31 | sidebar = ( 32 | 33 |
34 | 35 | <ChatIcon /> 36 | <span>Chat</span> 37 | 38 | this.setState({ navActive: false })} /> 40 |
41 | 42 | 43 | 44 | 45 | 46 |
47 | } dropAlign={{ bottom: 'top' }} 48 | a11yTitle="Session"> 49 | 50 | User name 51 | 52 | 53 | 54 |
55 |
56 | ); 57 | } 58 | 59 | return ( 60 | 61 | {sidebar} 62 | 63 |
64 | this.setState({ navActive: true })}> 66 | 67 | 68 | 70 | 71 | 73 |
74 | {this.props.children} 75 |
76 |
77 | ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /.scss-lint.yml: -------------------------------------------------------------------------------- 1 | # Default application configuration that all configurations inherit from. 2 | 3 | scss_files: "**/*.scss" 4 | 5 | linters: 6 | BangFormat: 7 | enabled: true 8 | space_before_bang: true 9 | space_after_bang: false 10 | 11 | BorderZero: 12 | enabled: true 13 | convention: none # or `zero` 14 | 15 | ColorKeyword: 16 | enabled: true 17 | 18 | ColorVariable: 19 | enabled: false 20 | 21 | Comment: 22 | enabled: true 23 | 24 | DebugStatement: 25 | enabled: true 26 | 27 | DeclarationOrder: 28 | enabled: false 29 | 30 | DuplicateProperty: 31 | enabled: true 32 | 33 | ElsePlacement: 34 | enabled: true 35 | style: same_line # or 'new_line' 36 | 37 | EmptyLineBetweenBlocks: 38 | enabled: true 39 | ignore_single_line_blocks: true 40 | 41 | EmptyRule: 42 | enabled: true 43 | 44 | FinalNewline: 45 | enabled: true 46 | present: true 47 | 48 | HexLength: 49 | enabled: false 50 | style: short # or 'long' 51 | 52 | HexNotation: 53 | enabled: false 54 | style: lowercase # or 'uppercase' 55 | 56 | HexValidation: 57 | enabled: true 58 | 59 | IdSelector: 60 | enabled: false 61 | 62 | ImportantRule: 63 | enabled: true 64 | 65 | ImportPath: 66 | enabled: true 67 | leading_underscore: false 68 | filename_extension: false 69 | 70 | Indentation: 71 | enabled: false 72 | allow_non_nested_indentation: false 73 | character: tab # or 'tab' 74 | width: 2 75 | 76 | LeadingZero: 77 | enabled: true 78 | style: include_zero # or 'exclude_zero' 79 | 80 | MergeableSelector: 81 | enabled: true 82 | force_nesting: true 83 | 84 | NameFormat: 85 | enabled: true 86 | allow_leading_underscore: true 87 | convention: hyphenated_lowercase # or 'BEM', or a regex pattern 88 | 89 | NestingDepth: 90 | enabled: false 91 | max_depth: 6 92 | 93 | PlaceholderInExtend: 94 | enabled: true 95 | 96 | PropertyCount: 97 | enabled: false 98 | include_nested: false 99 | max_properties: 10 100 | 101 | PropertySortOrder: 102 | enabled: false 103 | 104 | PropertySpelling: 105 | enabled: true 106 | extra_properties: ['animate'] 107 | 108 | QualifyingElement: 109 | enabled: true 110 | allow_element_with_attribute: true 111 | allow_element_with_class: true 112 | allow_element_with_id: false 113 | 114 | SelectorDepth: 115 | enabled: false 116 | max_depth: 4 117 | 118 | SelectorFormat: 119 | enabled: true 120 | convention: hyphenated_BEM # or 'BEM', or 'hyphenated_BEM', or 'snake_case', or 'camel_case', or a regex pattern 121 | 122 | Shorthand: 123 | enabled: true 124 | 125 | SingleLinePerProperty: 126 | enabled: true 127 | allow_single_line_rule_sets: true 128 | 129 | SingleLinePerSelector: 130 | enabled: true 131 | 132 | SpaceAfterComma: 133 | enabled: true 134 | 135 | SpaceAfterPropertyColon: 136 | enabled: true 137 | style: at_least_one_space # or 'no_space', or 'on_space', or 'at_least_one_space', or 'aligned' 138 | 139 | SpaceAfterPropertyName: 140 | enabled: true 141 | 142 | SpaceBeforeBrace: 143 | enabled: true 144 | style: space # or 'new_line' 145 | allow_single_line_padding: false 146 | 147 | SpaceBetweenParens: 148 | enabled: true 149 | spaces: 0 150 | 151 | StringQuotes: 152 | enabled: false 153 | style: single_quotes # or double_quotes 154 | 155 | TrailingSemicolon: 156 | enabled: true 157 | 158 | TrailingZero: 159 | enabled: false 160 | 161 | UnnecessaryMantissa: 162 | enabled: true 163 | 164 | UnnecessaryParentReference: 165 | enabled: true 166 | 167 | UrlFormat: 168 | enabled: false 169 | 170 | UrlQuotes: 171 | enabled: false 172 | 173 | VariableForProperty: 174 | enabled: false 175 | properties: [] 176 | 177 | VendorPrefix: 178 | enabled: false 179 | 180 | ZeroUnit: 181 | enabled: false 182 | 183 | Compass::*: 184 | enabled: false 185 | -------------------------------------------------------------------------------- /src/js/ConversationForm.js: -------------------------------------------------------------------------------- 1 | // (C) Copyright 2014-2015 Hewlett Packard Enterprise Development LP 2 | 3 | import React, { Component, PropTypes } from 'react'; 4 | import Article from 'grommet/components/Article'; 5 | import Header from 'grommet/components/Header'; 6 | import Form from 'grommet/components/Form'; 7 | import Footer from 'grommet/components/Footer'; 8 | import FormFields from 'grommet/components/FormFields'; 9 | import FormField from 'grommet/components/FormField'; 10 | import Button from 'grommet/components/Button'; 11 | import Anchor from 'grommet-addons/components/Anchor'; 12 | import CloseIcon from 'grommet/components/icons/base/Close'; 13 | import TrashIcon from 'grommet/components/icons/base/Trash'; 14 | // import SizeRemove from './SizeRemove'; 15 | 16 | export default class ConversationForm extends Component { 17 | 18 | constructor (props) { 19 | super(props); 20 | this._onSubmit = this._onSubmit.bind(this); 21 | this._onRemoveOpen = this._onRemoveOpen.bind(this); 22 | this._onRemoveClose = this._onRemoveClose.bind(this); 23 | 24 | this.state = { 25 | errors: {}, 26 | removing: false, 27 | conversation: props.conversation 28 | }; 29 | } 30 | 31 | componentWillReceiveProps (nextProps) { 32 | this.setState({ conversation: nextProps.conversation }); 33 | } 34 | 35 | _onSubmit (event) { 36 | event.preventDefault(); 37 | let conversation = this.state.conversation; 38 | let errors = {}; 39 | let noErrors = true; 40 | if (! conversation.name) { 41 | errors.name = 'required'; 42 | noErrors = false; 43 | } 44 | if (noErrors) { 45 | this.props.onSubmit(conversation); 46 | } else { 47 | this.setState({ errors: errors }); 48 | } 49 | } 50 | 51 | _change (propertyName) { 52 | return (event) => { 53 | let conversation = { ...this.state.conversation }; 54 | let value = event.target.value; 55 | let errors = this.state.errors; 56 | conversation[propertyName] = value; 57 | this.setState({ conversation: conversation, errors: errors }); 58 | }; 59 | } 60 | 61 | _onRemoveOpen () { 62 | this.setState({ removing: true }); 63 | } 64 | 65 | _onRemoveClose () { 66 | this.setState({ removing: false }); 67 | } 68 | 69 | render () { 70 | let { conversation, errors } = this.state; 71 | 72 | let removeControl; 73 | if (this.props.removable) { 74 | removeControl = ( 75 |