From 1fee2a846a8490cff9d3e6f194ad90341d254e31 Mon Sep 17 00:00:00 2001 From: Rokt33r Date: Fri, 11 Sep 2015 17:20:16 +0900 Subject: [PATCH 01/43] - cleanup root directory - improve window behaviour --- browser/finder/index.electron.html | 6 +- browser/main/index.electron.html | 10 +- browser/styles/main/index.styl | 3 + finder-window.js | 26 ++++ main-window.js | 22 +++ main.js | 135 ++++-------------- package.json | 5 +- readme.md | 20 --- .../Lato-Regular.ttf | Bin .../Lato-Regular.woff | Bin .../Lato-Regular.woff2 | Bin tray-icon.png => resources/tray-icon.png | Bin .../tray-icon@2x.png | Bin update-log.md | 18 --- updater.js | 37 +++++ 15 files changed, 129 insertions(+), 153 deletions(-) create mode 100644 finder-window.js create mode 100644 main-window.js delete mode 100644 readme.md rename Lato-Regular.ttf => resources/Lato-Regular.ttf (100%) rename Lato-Regular.woff => resources/Lato-Regular.woff (100%) rename Lato-Regular.woff2 => resources/Lato-Regular.woff2 (100%) rename tray-icon.png => resources/tray-icon.png (100%) rename tray-icon@2x.png => resources/tray-icon@2x.png (100%) delete mode 100644 update-log.md create mode 100644 updater.js diff --git a/browser/finder/index.electron.html b/browser/finder/index.electron.html index 9aa524c9..af0ac669 100644 --- a/browser/finder/index.electron.html +++ b/browser/finder/index.electron.html @@ -11,9 +11,9 @@ - - - -
- - - - diff --git a/browser/finder/index.html b/browser/finder/index.html index e69de29b..c8ac50d1 100644 --- a/browser/finder/index.html +++ b/browser/finder/index.html @@ -0,0 +1,39 @@ + + + + + CodeXen Popup + + + + + + + + + +
+ + + + diff --git a/browser/finder/index.jsx b/browser/finder/index.jsx index 7de3859a..d3668aa8 100644 --- a/browser/finder/index.jsx +++ b/browser/finder/index.jsx @@ -3,7 +3,7 @@ var remote = require('remote') var hideFinder = remote.getGlobal('hideFinder') var clipboard = require('clipboard') -var React = require('react/addons') +var React = require('react') var ArticleFilter = require('../main/Mixins/ArticleFilter') diff --git a/browser/index.html b/browser/index.html deleted file mode 100644 index cbb53cad..00000000 --- a/browser/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - Go Main - Go Popup - - diff --git a/browser/main/Components/AboutModal.jsx b/browser/main/Components/AboutModal.jsx index d1f1a3eb..030700c0 100644 --- a/browser/main/Components/AboutModal.jsx +++ b/browser/main/Components/AboutModal.jsx @@ -1,4 +1,4 @@ -var React = require('react/addons') +var React = require('react') var ExternalLink = require('../Mixins/ExternalLink') var KeyCaster = require('../Mixins/KeyCaster') diff --git a/browser/main/Components/AddMemberModal.jsx b/browser/main/Components/AddMemberModal.jsx index f79ce647..9a173f1a 100644 --- a/browser/main/Components/AddMemberModal.jsx +++ b/browser/main/Components/AddMemberModal.jsx @@ -1,4 +1,4 @@ -var React = require('react/addons') +var React = require('react') var Select = require('react-select') var LinkedState = require('../Mixins/LinkedState') diff --git a/browser/main/Components/ArticleDetail.jsx b/browser/main/Components/ArticleDetail.jsx new file mode 100644 index 00000000..4cb67b75 --- /dev/null +++ b/browser/main/Components/ArticleDetail.jsx @@ -0,0 +1,209 @@ +var React = require('react') +var moment = require('moment') +var _ = require('lodash') + +var CodeEditor = require('./CodeEditor') +var MarkdownPreview = require('./MarkdownPreview') +var ModeIcon = require('./ModeIcon') +var Select = require('react-select') + +var Modal = require('../Mixins/Modal') +var ForceUpdate = require('../Mixins/ForceUpdate') +var LinkedState = require('../Mixins/LinkedState') + +var aceModes = require('../../../modules/ace-modes') + +var modeOptions = aceModes.map(function (mode) { + return { + label: mode, + value: mode + } +}) + +module.exports = React.createClass({ + mixins: [ForceUpdate(60000), Modal, LinkedState], + propTypes: { + currentArticle: React.PropTypes.object, + showOnlyWithTag: React.PropTypes.func, + planet: React.PropTypes.object, + switchDetailMode: React.PropTypes.func, + user: React.PropTypes.shape({ + id: React.PropTypes.number, + name: React.PropTypes.string, + Folders: React.PropTypes.array + }), + article: React.PropTypes.object, + saveCurrentArticle: React.PropTypes.func, + detailMode: React.PropTypes.string + }, + getInitialState: function () { + var article = this.props.currentArticle != null ? { + id: this.props.currentArticle.id, + title: this.props.currentArticle.title, + content: this.props.currentArticle.CurrentRevision.title, + tags: this.props.currentArticle.Tags.map(function (tag) { + return tag.name + }), + mode: this.props.currentArticle.mode, + status: this.props.currentArticle.status + } : null + // console.log('init staet') + // console.log(article) + return { + isEditModalOpen: false, + article: article + } + }, + componentWillReceiveProps: function (nextProps) { + if (nextProps.detailMode === 'edit') { + var article = { + id: nextProps.currentArticle.id, + title: nextProps.currentArticle.title, + content: nextProps.currentArticle.CurrentRevision.content, + tags: nextProps.currentArticle.Tags.map(function (tag) { + return tag.name + }), + mode: nextProps.currentArticle.mode, + FolderId: nextProps.currentArticle.FolderId, + status: nextProps.currentArticle.status + } + this.setState({article: article}) + } + }, + openDeleteModal: function () { + if (this.props.article == null) return + }, + handleFolderIdChange: function (FolderId) { + this.state.article.FolderId = FolderId + this.setState({article: this.state.article}) + }, + handleTagsChange: function (tag, tags) { + tags = _.uniq(tags, function (tag) { + return tag.value + }) + + this.state.article.tags = tags.map(function (tag) { + return tag.value + }) + this.setState({article: this.state.article}) + }, + handleModeChange: function (mode) { + this.state.article.mode = mode + this.setState({article: this.state.article}) + }, + handleContentChange: function (e, value) { + var article = this.state.article + article.content = value + this.setState({article: article}) + }, + saveArticle: function () { + if (this.state.article.mode === '') { + return this.refs.mode.focus() + } + if (this.state.article.FolderId === '') { + return this.refs.folder.focus() + } + this.props.saveCurrentArticle(this.state.article) + }, + render: function () { + if (this.props.currentArticle == null) { + return ( +
+ Nothing selected +
+ ) + } + + if (this.props.detailMode === 'show') { + return this.renderViewer() + } + if (this.state.article == null) { + return ( +
+ Nothing selected +
+ ) + } + return this.renderEditor() + }, + renderEditor: function () { + var article = this.state.article + + var folderOptions = this.props.user.Folders.map(function (folder) { + return { + label: folder.name, + value: folder.id + } + }) + + return ( +
+
+
+ +
+
+ + +
+
+
+
+
+
+ +
+ +
+
+ +
+ + +
Boost official page
+
+
+
+ ) + } +}) diff --git a/browser/main/Components/UserNavigator.jsx b/browser/main/Components/UserNavigator.jsx new file mode 100644 index 00000000..ef6c8993 --- /dev/null +++ b/browser/main/Components/UserNavigator.jsx @@ -0,0 +1,53 @@ +var React = require('react') +var _ = require('lodash') + +module.exports = React.createClass({ + propTypes: { + createNewArticle: React.PropTypes.func, + search: React.PropTypes.string, + user: React.PropTypes.object + }, + render: function () { + var user = this.props.user + + var folders = _.isArray(user.Folders) ? user.Folders.map(function (folder) { + var isActive = this.props.search.match(new RegExp('in:' + folder.name)) + return ( + + ) + }.bind(this)) : null + + var members = _.isArray(user.Members) ? user.Members.map(function (member) { + return + }) : null + + return ( +
+
+
{user.profileName}
+
{user.name}
+
+
+ +
+ +
+ +
+
+
+ Folders + +
+ + {folders} +
+ + {user.userType === 'team' ? ( +
{members}
+ ) : null} +
+
+ ) + } +}) diff --git a/browser/main/Containers/HomeContainer.jsx b/browser/main/Containers/HomeContainer.jsx deleted file mode 100644 index 32e5fad0..00000000 --- a/browser/main/Containers/HomeContainer.jsx +++ /dev/null @@ -1,41 +0,0 @@ -/* global localStorage */ - -var React = require('react/addons') -var ReactRouter = require('react-router') -var RouteHandler = ReactRouter.RouteHandler -var State = ReactRouter.State -var Navigation = ReactRouter.Navigation - -var AuthFilter = require('../Mixins/AuthFilter') -var KeyCaster = require('../Mixins/KeyCaster') - -var HomeNavigator = require('../Components/HomeNavigator') - -module.exports = React.createClass({ - mixins: [AuthFilter.OnlyUser, State, Navigation, KeyCaster('homeContainer')], - componentDidMount: function () { - if (this.isActive('homeEmpty')) { - var user = JSON.parse(localStorage.getItem('currentUser')) - if (user.Planets != null && user.Planets.length > 0) { - this.transitionTo('planet', {userName: user.name, planetName: user.Planets[0].name}) - return - } - this.transitionTo('userHome', {userName: user.name}) - } - }, - onKeyCast: function (e) { - switch (e.status) { - case 'switchPlanet': - this.refs.navigator.switchPlanetByIndex(e.data) - break - } - }, - render: function () { - return ( -
- - -
- ) - } -}) diff --git a/browser/main/Containers/LoginContainer.js b/browser/main/Containers/LoginContainer.js new file mode 100644 index 00000000..dbde65aa --- /dev/null +++ b/browser/main/Containers/LoginContainer.js @@ -0,0 +1,92 @@ +var Hq = require('../Services/Hq') +var socket = require('../Services/socket') + +import React, { PropTypes } from 'react' +import { Link } from 'react-router' +import linkState from '../helpers/linkState' + +export default class LoginPage extends React.Component { + constructor (props) { + super(props) + + this.state = { + user: {}, + isSending: false, + error: null + } + this.linkState = linkState + } + + handleSubmit (e) { + e.preventDefault() + this.setState({ + isSending: true, + error: null + }, function () { + console.log(this.state.user) + Hq.login(this.state.user) + .then(function (res) { + localStorage.setItem('token', res.body.token) + localStorage.setItem('currentUser', JSON.stringify(res.body.user)) + + try { + this.props.history.pushState('home') + } catch(e) { + console.error(e) + } + }.bind(this)) + .catch(function (err) { + console.error(err) + if (err.response == null) { + return this.setState({ + error: {name: 'CunnectionRefused', message: 'API server doesn\'t respond. Check your internet connection.'}, + isSending: false + }) + } + + var res = err.response + + // Connection Failed or Whatever + this.setState({ + error: err.response.body, + isSending: false + }) + }.bind(this)) + }) + } + + render () { + return ( +
+ + + + +
this.handleSubmit(e)}> +
+ +
+
+ +
+ + {this.state.isSending ? ( +

Logging in...

+ ) : null} + + {this.state.error != null ?

{this.state.error.message}

: null} + +
+ +
+
+
+ ) + } +} + +LoginPage.propTypes = { + history: PropTypes.shape({ + pushState: PropTypes.func + }) +} diff --git a/browser/main/Containers/LoginContainer.jsx b/browser/main/Containers/LoginContainer.jsx deleted file mode 100644 index a1c01a1e..00000000 --- a/browser/main/Containers/LoginContainer.jsx +++ /dev/null @@ -1,108 +0,0 @@ -/* global localStorage */ -var React = require('react/addons') -var ReactRouter = require('react-router') -var Link = ReactRouter.Link - -var AuthFilter = require('../Mixins/AuthFilter') -var LinkedState = require('../Mixins/LinkedState') -var Hq = require('../Services/Hq') -var socket = require('../Services/socket') - -module.exports = React.createClass({ - mixins: [LinkedState, ReactRouter.Navigation, AuthFilter.OnlyGuest], - getInitialState: function () { - return { - user: {}, - authenticationFailed: false, - connectionFailed: false, - isSending: false - } - }, - onListen: function (res) { - if (res.status === 'failedToLogIn') { - if (res.data.status === 401) { - // Wrong E-mail or Password - this.setState({ - authenticationFailed: true, - connectionFailed: false, - isSending: false - }) - return - } - // Connection Failed or Whatever - this.setState({ - authenticationFailed: false, - connectionFailed: true, - isSending: false - }) - return - } - }, - handleSubmit: function (e) { - this.setState({ - authenticationFailed: false, - connectionFailed: false, - isSending: true - }, function () { - Hq.login(this.state.user) - .then(function (res) { - localStorage.setItem('token', res.body.token) - localStorage.setItem('currentUser', JSON.stringify(res.body.user)) - socket.reconnect() - - this.transitionTo('userHome', {userName: res.body.user.name}) - }.bind(this)) - .catch(function (err) { - if (err.status === 401) { - this.setState({ - authenticationFailed: true, - connectionFailed: false, - isSending: false - }) - return - } - this.setState({ - authenticationFailed: false, - connectionFailed: true, - isSending: false - }) - }.bind(this)) - }) - - e.preventDefault() - }, - render: function () { - return ( -
- - - - -
-
- -
-
- -
- - {this.state.isSending ? ( -

Logging in...

- ) : null} - - {this.state.connectionFailed ? ( -

Please try again.

- ) : null} - - {this.state.authenticationFailed ? ( -

Wrong E-mail or Password.

- ) : null} - -
- -
-
-
- ) - } -}) diff --git a/browser/main/Containers/MainContainer.js b/browser/main/Containers/MainContainer.js new file mode 100644 index 00000000..4cba559d --- /dev/null +++ b/browser/main/Containers/MainContainer.js @@ -0,0 +1,45 @@ +var ipc = require('ipc') +import React, { PropTypes } from 'react' + +var ContactModal = require('../Components/ContactModal') + +export default class MainContainer extends React.Component { + // mixins: [Modal], + constructor (props) { + super(props) + this.state = {updateAvailable: false} + } + + componentDidMount () { + ipc.on('update-available', function (message) { + this.setState({updateAvailable: true}) + }.bind(this)) + } + + updateApp () { + ipc.send('update-app', 'Deal with it.') + } + + openContactModal () { + this.openModal(ContactModal) + } + + render () { + return ( +
+ {this.state.updateAvailable ? ( + + ) : null} + + {this.props.children} +
+ ) + } +} + +MainContainer.propTypes = { + children: PropTypes.element +} diff --git a/browser/main/Containers/MainContainer.jsx b/browser/main/Containers/MainContainer.jsx deleted file mode 100644 index 544c8173..00000000 --- a/browser/main/Containers/MainContainer.jsx +++ /dev/null @@ -1,107 +0,0 @@ -/* global localStorage */ - -var ipc = require('ipc') - -var React = require('react/addons') -var ReactRouter = require('react-router') -var RouteHandler = ReactRouter.RouteHandler -var Navigation = ReactRouter.Navigation -var State = ReactRouter.State - -var Hq = require('../Services/Hq') -var socket = require('../Services/socket') - -var Modal = require('../Mixins/Modal') - -var UserStore = require('../Stores/UserStore') - -var ContactModal = require('../Components/ContactModal') - -function fetchPlanet (userName, planetName) { - return Hq.fetchPlanet(userName, planetName) - .then(function (res) { - var planet = res.body - - planet.Codes.forEach(function (code) { - code.type = 'code' - }) - - planet.Notes.forEach(function (note) { - note.type = 'note' - }) - - localStorage.setItem('planet-' + planet.id, JSON.stringify(planet)) - - return planet - }) - .catch(function (err) { - console.error(err) - }) -} - -module.exports = React.createClass({ - mixins: [State, Navigation, Modal], - getInitialState: function () { - return { - updateAvailable: false - } - }, - componentDidMount: function () { - ipc.on('update-available', function (message) { - this.setState({updateAvailable: true}) - }.bind(this)) - - if (this.isActive('root')) { - if (localStorage.getItem('currentUser') == null) { - this.transitionTo('login') - return - } else { - this.transitionTo('home') - } - } - - Hq.getUser() - .then(function (res) { - var user = res.body - UserStore.Actions.update(user) - - user.Planets.forEach(function (planet) { - fetchPlanet(user.name, planet.name) - }) - user.Teams.forEach(function (team) { - team.Planets.forEach(function (planet) { - fetchPlanet(team.name, planet.name) - }) - }) - }) - .catch(function (err) { - if (err.status === 401) { - console.log('Not logged in yet') - localStorage.removeItem('currentUser') - this.transitionTo('login') - return - } - console.error(err) - }.bind(this)) - }, - updateApp: function () { - ipc.send('update-app', 'Deal with it.') - }, - openContactModal: function () { - this.openModal(ContactModal) - }, - render: function () { - return ( -
- {this.state.updateAvailable ? ( - - ) : null} - - -
- ) - } -}) diff --git a/browser/main/Containers/PlanetContainer.jsx b/browser/main/Containers/PlanetContainer.js similarity index 99% rename from browser/main/Containers/PlanetContainer.jsx rename to browser/main/Containers/PlanetContainer.js index 8abc9a73..ecd1270e 100644 --- a/browser/main/Containers/PlanetContainer.jsx +++ b/browser/main/Containers/PlanetContainer.js @@ -1,6 +1,5 @@ /* global localStorage*/ -'strict' -var React = require('react/addons') +var React = require('react') var ReactRouter = require('react-router') var Reflux = require('reflux') diff --git a/browser/main/Containers/SignupContainer.js b/browser/main/Containers/SignupContainer.js new file mode 100644 index 00000000..2e35d58d --- /dev/null +++ b/browser/main/Containers/SignupContainer.js @@ -0,0 +1,98 @@ +import React, { PropTypes } from 'react' +import { Link } from 'react-router' +import linkState from '../helpers/linkState' +import openExternal from '../helpers/openExternal' + +var Hq = require('../Services/Hq') + +export default class SignupContainer extends React.Component { + constructor (props) { + super(props) + this.state = { + user: {}, + connectionFailed: false, + emailConflicted: false, + nameConflicted: false, + validationFailed: false, + isSending: false, + error: null + } + this.linkState = linkState + this.openExternal = openExternal + } + + handleSubmit (e) { + this.setState({ + isSending: true, + error: null + }, function () { + Hq.signup(this.state.user) + .then(res => { + localStorage.setItem('token', res.body.token) + localStorage.setItem('currentUser', JSON.stringify(res.body.user)) + + this.props.history.pushState('userHome', {userId: res.body.user.id}) + }) + .catch(function (err) { + console.error(err) + if (err.response == null) { + return this.setState({ + error: {name: 'CunnectionRefused', message: 'API server doesn\'t respond. Check your internet connection.'}, + isSending: false + }) + } + + // Connection Failed or Whatever + this.setState({ + error: err.response.body, + isSending: false + }) + }.bind(this)) + }) + + e.preventDefault() + } + + render () { + return ( +
+ + + + +
this.handleSubmit(e)}> +
+ +
+
+ +
+
+ +
+
+ +
+ + {this.state.isSending ? ( +

Signing up...

+ ) : null} + + {this.state.error != null ?

{this.state.error.message}

: null} + +
+ +
+
+ +

会員登録することで、当サイトの利用規約及びCookieの使用を含むデータに関するポリシーに同意するものとします。

+
+ ) + } +} + +SignupContainer.propTypes = { + history: PropTypes.shape({ + pushState: PropTypes.func + }) +} diff --git a/browser/main/Containers/SignupContainer.jsx b/browser/main/Containers/SignupContainer.jsx deleted file mode 100644 index 114dbc27..00000000 --- a/browser/main/Containers/SignupContainer.jsx +++ /dev/null @@ -1,139 +0,0 @@ -/* global localStorage */ - -var React = require('react/addons') -var ReactRouter = require('react-router') -var Link = ReactRouter.Link - -var AuthFilter = require('../Mixins/AuthFilter') -var LinkedState = require('../Mixins/LinkedState') -var ExternalLink = require('../Mixins/ExternalLink') -var Hq = require('../Services/Hq') -var socket = require('../Services/socket') - -module.exports = React.createClass({ - mixins: [LinkedState, ReactRouter.Navigation, AuthFilter.OnlyGuest, ExternalLink], - getInitialState: function () { - return { - user: {}, - connectionFailed: false, - emailConflicted: false, - nameConflicted: false, - validationFailed: false, - isSending: false - } - }, - handleSubmit: function (e) { - this.setState({ - connectionFailed: false, - emailConflicted: false, - nameConflicted: false, - validationFailed: false, - isSending: true - }, function () { - Hq.signup(this.state.user) - .then(function (res) { - localStorage.setItem('token', res.body.token) - localStorage.setItem('currentUser', JSON.stringify(res.body.user)) - socket.reconnect() - - this.transitionTo('userHome', {userName: res.body.user.name}) - }.bind(this)) - .catch(function (err) { - console.error(err) - var res = err.response - if (err.status === 409) { - // Confliction - var emailConflicted = res.body.errors[0].path === 'email' - var nameConflicted = res.body.errors[0].path === 'name' - - this.setState({ - connectionFailed: false, - emailConflicted: emailConflicted, - nameConflicted: nameConflicted, - validationFailed: false, - isSending: false - }) - return - } - - if (err.status === 422) { - // Validation Failed - this.setState({ - connectionFailed: false, - emailConflicted: false, - nameConflicted: false, - validationFailed: { - errors: res.body.errors.map(function (error) { - return error.path - }) - }, - isSending: false - }) - return - } - - // Connection Failed or Whatever - this.setState({ - connectionFailed: true, - emailConflicted: false, - nameConflicted: false, - validationFailed: false, - isSending: false - }) - return - }.bind(this)) - }) - - e.preventDefault() - }, - render: function () { - return ( -
- - - - -
-
- -
-
- -
-
- -
-
- -
- - {this.state.isSending ? ( -

Signing up...

- ) : null} - - {this.state.connectionFailed ? ( -

Please try again.

- ) : null} - - {this.state.emailConflicted ? ( -

E-mail already exists.

- ) : null} - - {this.state.nameConflicted ? ( -

Username already exists.

- ) : null} - - {this.state.validationFailed ? ( -

Please fill every field correctly: {this.state.validationFailed.errors.join(', ')}

- ) : null} - -
- -
-
- -

会員登録することで、当サイトの利用規約及びCookieの使用を含むデータに関するポリシーに同意するものとします。

-
- ) - } -}) diff --git a/browser/main/Containers/UserContainer.jsx b/browser/main/Containers/UserContainer.jsx deleted file mode 100644 index c0ef398b..00000000 --- a/browser/main/Containers/UserContainer.jsx +++ /dev/null @@ -1,367 +0,0 @@ -/* global localStorage */ - -var React = require('react/addons') -var ReactRouter = require('react-router') -var Navigation = ReactRouter.Navigation -var State = ReactRouter.State -var RouteHandler = ReactRouter.RouteHandler -var Link = ReactRouter.Link -var Reflux = require('reflux') - -var LinkedState = require('../Mixins/LinkedState') -var Modal = require('../Mixins/Modal') -var Helper = require('../Mixins/Helper') - -var Hq = require('../Services/Hq') - -var ProfileImage = require('../Components/ProfileImage') -var EditProfileModal = require('../Components/EditProfileModal') -var TeamSettingsModal = require('../Components/TeamSettingsModal') -var PlanetCreateModal = require('../Components/PlanetCreateModal') -var AddMemberModal = require('../Components/AddMemberModal') -var TeamCreateModal = require('../Components/TeamCreateModal') - -var UserStore = require('../Stores/UserStore') -var PlanetStore = require('../Stores/PlanetStore') - -module.exports = React.createClass({ - mixins: [LinkedState, State, Navigation, Modal, Reflux.listenTo(UserStore, 'onUserChange'), Reflux.listenTo(PlanetStore, 'onPlanetChange'), Helper], - propTypes: { - params: React.PropTypes.shape({ - userName: React.PropTypes.string, - planetName: React.PropTypes.string - }) - }, - getInitialState: function () { - return { - user: null - } - }, - componentDidMount: function () { - this.fetchUser() - }, - componentWillReceiveProps: function (nextProps) { - if (this.state.user == null) { - this.fetchUser(nextProps.params.userName) - return - } - - if (nextProps.params.userName !== this.state.user.name) { - this.setState({ - user: null - }, function () { - this.fetchUser(nextProps.params.userName) - }) - } - }, - onUserChange: function (res) { - if (this.state.user == null) return - - var member - switch (res.status) { - case 'userUpdated': - if (this.state.user.id === res.data.id) { - this.setState({user: res.data}) - } - break - case 'memberAdded': - member = res.data - if (this.state.user.userType === 'team' && member.TeamMember.TeamId === this.state.user.id) { - this.state.user.Members = this.updateItemToTargetArray(member, this.state.user.Members) - - this.setState({user: this.state.user}) - } - break - case 'memberRemoved': - member = res.data - if (this.state.user.userType === 'team' && member.TeamMember.TeamId === this.state.user.id) { - this.state.user.Members = this.deleteItemFromTargetArray(member, this.state.user.Members) - - this.setState({user: this.state.user}) - } - break - } - }, - onPlanetChange: function (res) { - if (this.state.user == null) return - - var currentUser, planet, isOwner, team - switch (res.status) { - case 'updated': - // if state.user is currentUser, planet will be fetched by UserStore - currentUser = JSON.parse(localStorage.getItem('currentUser')) - if (currentUser.id === this.state.user.id) return - - planet = res.data - isOwner = planet.Owner.id === this.state.user.id - if (isOwner) { - this.state.user.Planets = this.updateItemToTargetArray(planet, this.state.user.Planets) - this.setState({user: this.state.user}) - return - } - // check if team of user has this planet - team = null - this.state.user.userType !== 'team' && this.state.user.Teams.some(function (_team) { - if (planet.Owner.id === _team.id) { - team = _team - return true - } - return false - }) - if (team != null) { - team.Planets = this.updateItemToTargetArray(planet, team.Planets) - this.setState({user: this.state.user}) - return - } - - break - case 'destroyed': - // if state.user is currentUser, planet will be fetched by UserStore - currentUser = JSON.parse(localStorage.getItem('currentUser')) - if (currentUser.id === this.state.user.id) return - - planet = res.data - isOwner = planet.Owner.id === this.state.user.id - if (isOwner) { - this.state.user.Planets = this.deleteItemFromTargetArray(planet, this.state.user.Planets) - this.setState({user: this.state.user}) - return - } - // check if team of user has this planet - team = null - this.state.user.userType !== 'team' && this.state.user.Teams.some(function (_team) { - if (planet.Owner.id === _team.id) { - team = _team - return true - } - return false - }) - if (team != null) { - team.Planets = this.deleteItemFromTargetArray(planet, team.Planets) - this.setState({user: this.state.user}) - return - } - break - } - }, - fetchUser: function (userName) { - if (userName == null) userName = this.props.params.userName - - Hq.fetchUser(userName) - .then(function (res) { - this.setState({user: res.body}) - }.bind(this)) - .catch(function (err) { - console.error(err) - }) - }, - openEditProfileModal: function () { - this.openModal(EditProfileModal, {user: this.state.user}) - }, - openTeamSettingsModal: function () { - this.openModal(TeamSettingsModal, {team: this.state.user}) - }, - openAddUserModal: function () { - this.openModal(AddMemberModal, {team: this.state.user}) - }, - openTeamCreateModal: function () { - this.openModal(TeamCreateModal, {user: this.state.user}) - }, - openPlanetCreateModalWithOwnerName: function (name) { - return function () { - this.openModal(PlanetCreateModal, {ownerName: name}) - }.bind(this) - }, - render: function () { - var user = this.state.user - - var currentUser = JSON.parse(localStorage.getItem('currentUser')) - - if (this.isActive('userHome')) { - if (user == null) { - return ( -
- User Loading... -
- ) - } else if (user.userType === 'team') { - return this.renderTeamHome(currentUser) - } else { - return this.renderUserHome(currentUser) - } - } else if (this.isActive('planet') && user != null && user.userType === 'team') { - var members = user.Members.map(function (member) { - return ( -
  • - -
    -
    {member.profileName}
    -
    @{member.name}
    -
    -
  • - ) - }) - return ( -
    - -
    -
    Members
    -
      - {members} -
    -
    -
    - ) - } else { - return ( -
    - -
    - ) - } - }, - renderTeamHome: function (currentUser) { - var user = this.state.user - - var isOwner = user.Members == null ? false : user.Members.some(function (member) { - return member.id === currentUser.id && member.TeamMember.role === 'owner' - }) - - var userPlanets = user.Planets.map(function (planet) { - return ( -
  • - {user.name}/{planet.name} -  {!planet.public ? () : null} -
  • - ) - }) - - var members = user.Members == null ? [] : user.Members.map(function (member) { - return ( -
  • - - -
    -
    {member.profileName} ({member.TeamMember.role})
    -
    @{member.name}
    -
    - -
    -
  • - ) - }) - return ( -
    -
    - -
    -
    {user.profileName}
    -
    {user.name}
    -
    - - {isOwner ? () : null} -
    -
    -
    {members.length} {members.length > 1 ? 'Members' : 'Member'}
    -
      - {members} - {isOwner ? (
    • ) : null} -
    -
    -
    -
    {userPlanets.length} {userPlanets.length > 0 ? 'Planets' : 'Planet'}
    -
    -
      - {userPlanets} - {isOwner ? (
    • ) : null} -
    -
    -
    -
    - ) - }, - renderUserHome: function (currentUser) { - var user = this.state.user - - var isOwner = currentUser.id === user.id - - var userPlanets = user.Planets.map(function (planet) { - return ( -
  • - {user.name}/{planet.name} -  {!planet.public ? () : null} -
  • - ) - }) - - var teams = user.Teams == null ? [] : user.Teams.map(function (team) { - return ( -
  • - -
    -
    {team.profileName}
    -
    @{team.name}
    -
    - -
  • - ) - }) - - var teamPlanets = user.Teams == null ? [] : user.Teams.map(function (team) { - var planets = (team.Planets == null ? [] : team.Planets).map(function (planet) { - return ( -
  • - {team.name}/{planet.name} -  {!planet.public ? () : null} -
  • - ) - }) - return ( -
    -
    {team.profileName} @{team.name}
    -
      - {planets} - {isOwner ? (
    • ) : null} -
    -
    - ) - }.bind(this)) - - var planetCount = userPlanets.length + user.Teams.reduce(function (sum, team) { - return sum + (team.Planets != null ? team.Planets.length : 0) - }, 0) - - return ( -
    -
    - -
    -
    {user.profileName}
    -
    {user.name}
    -
    - - {isOwner ? ( - ) : null} -
    -
    -
    {teams.length} {teams.length > 1 ? 'Teams' : 'Team'}
    -
      - {teams} - {isOwner ? (
    • ) : null} -
    -
    -
    -
    {planetCount} {planetCount > 1 ? 'Planets' : 'Planet'}
    -
    -
    {user.profileName} @{user.name}
    -
      - {userPlanets} - {isOwner ? (
    • ) : null} -
    -
    - {teamPlanets} -
    -
    - ) - } -}) diff --git a/browser/main/HomeContainer/Components/ArticleDetail.js b/browser/main/HomeContainer/Components/ArticleDetail.js new file mode 100644 index 00000000..f53a69c8 --- /dev/null +++ b/browser/main/HomeContainer/Components/ArticleDetail.js @@ -0,0 +1,9 @@ +import React, { PropTypes } from 'react' + +export default class ArticleDetail extends React.Component { + render () { + return ( +
    + ) + } +} diff --git a/browser/main/HomeContainer/Components/ArticleList.js b/browser/main/HomeContainer/Components/ArticleList.js new file mode 100644 index 00000000..ef0f0042 --- /dev/null +++ b/browser/main/HomeContainer/Components/ArticleList.js @@ -0,0 +1,11 @@ +import React, { PropTypes } from 'react' + +class ArticleList extends React.Component { + render() { + return ( +
    + ) + } +} + +export default ArticleList diff --git a/browser/main/HomeContainer/Components/ArticleNavigator.js b/browser/main/HomeContainer/Components/ArticleNavigator.js new file mode 100644 index 00000000..3c03d534 --- /dev/null +++ b/browser/main/HomeContainer/Components/ArticleNavigator.js @@ -0,0 +1,12 @@ +import React, { PropTypes } from 'react' + +class ArticleNavigator extends React.Component { + render () { + return ( +
    +
    + ) + } +} + +export default ArticleNavigator diff --git a/browser/main/HomeContainer/Components/UserNavigator.js b/browser/main/HomeContainer/Components/UserNavigator.js new file mode 100644 index 00000000..6e212893 --- /dev/null +++ b/browser/main/HomeContainer/Components/UserNavigator.js @@ -0,0 +1,33 @@ +import React, { Component, PropTypes } from 'react' +import { Link } from 'react-router' + +export default class UserNavigator extends Component { + + renderUserList () { + var users = this.props.users.map(user => ( +
  • + +
    {user.name}
    + +
  • + )) + + return ( +
    + {users} +
    + ) + } + + render () { + return ( +
    + {this.renderUserList()} +
    + ) + } +} + +UserNavigator.propTypes = { + users: PropTypes.array +} diff --git a/browser/main/HomeContainer/actions.js b/browser/main/HomeContainer/actions.js new file mode 100644 index 00000000..d208a0ef --- /dev/null +++ b/browser/main/HomeContainer/actions.js @@ -0,0 +1,10 @@ +function updateUser (user) { + return { + type: 'USER_UPDATE', + data: user + } +} + +module.exports = { + updateUser: updateUser +} diff --git a/browser/main/HomeContainer/index.js b/browser/main/HomeContainer/index.js new file mode 100644 index 00000000..0e0be732 --- /dev/null +++ b/browser/main/HomeContainer/index.js @@ -0,0 +1,50 @@ +import React from 'react' +// import { connect } from 'react-redux' +// import actionss.... +import UserNavigator from './Components/UserNavigator' +import ArticleNavigator from './Components/ArticleNavigator' +import ArticleList from './Components/ArticleList' +import ArticleDetail from './Components/ArticleDetail' + +// var AuthFilter = require('../Mixins/AuthFilter') +// var KeyCaster = require('../Mixins/KeyCaster') + +class HomeContainer extends React.Component { + componentDidMount () { + // if (!this.isActive('user')) { + // console.log('redirect to user home') + // var user = JSON.parse(localStorage.getItem('currentUser')) + // this.transitionTo('userHome', {userId: user.id}) + // } + } + render () { + let users = [ + { + id: 1, + name: 'me', + email: 'fll@eme.com' + }, + { + id: 2, + name: 'me', + email: 'fll@eme.com' + } + ] + return ( +
    + + + + +
    + ) + } +} + +// function remap (state) { +// console.log('mapped') +// console.log(state) +// return {} +// } + +export default HomeContainer diff --git a/browser/main/HomeContainer/reducer.js b/browser/main/HomeContainer/reducer.js new file mode 100644 index 00000000..78224524 --- /dev/null +++ b/browser/main/HomeContainer/reducer.js @@ -0,0 +1,15 @@ +import {combineReducers} from 'redux' + +const initialCurrentUser = JSON.parse(localStorage.getItem('currentUser')) + +function currentUser (state, action) { + switch (action.type) { + + default: + return initialCurrentUser + } +} + +export default combineReducers({ + currentUser +}) diff --git a/browser/main/Mixins/AuthFilter.js b/browser/main/Mixins/AuthFilter.js index 22629fc9..3903302b 100644 --- a/browser/main/Mixins/AuthFilter.js +++ b/browser/main/Mixins/AuthFilter.js @@ -4,12 +4,12 @@ var mixin = {} mixin.OnlyGuest = { componentDidMount: function () { - var currentUser = localStorage.getItem('currentUser') + var currentUser = JSON.parse(localStorage.getItem('currentUser')) if (currentUser == null) { return } - this.transitionTo('userHome', {userName: currentUser.name}) + this.transitionTo('homeDefault') } } diff --git a/browser/main/Mixins/Markdown.js b/browser/main/Mixins/Markdown.js index 6d26080e..4a59c3d9 100644 --- a/browser/main/Mixins/Markdown.js +++ b/browser/main/Mixins/Markdown.js @@ -6,6 +6,7 @@ var md = markdownit({ var Markdown = { markdown: function (content) { + if (content == null) content = '' return md.render(content) } } diff --git a/browser/main/Mixins/Modal.jsx b/browser/main/Mixins/Modal.jsx index 1a14f5c1..3367eda7 100644 --- a/browser/main/Mixins/Modal.jsx +++ b/browser/main/Mixins/Modal.jsx @@ -1,4 +1,4 @@ -var React = require('react/addons') +import React from 'react' var ModalBase = React.createClass({ getInitialState: function () { return { diff --git a/browser/main/Mixins/LinkedState.js b/browser/main/Mixins/linkState.js similarity index 75% rename from browser/main/Mixins/LinkedState.js rename to browser/main/Mixins/linkState.js index 57fc6ea6..6b2be4a0 100644 --- a/browser/main/Mixins/LinkedState.js +++ b/browser/main/Mixins/linkState.js @@ -21,11 +21,9 @@ function setPartialState (component, path, value) { updateIn(component.state, path, value)) } -module.exports = { - linkState: function (path) { - return { - value: getIn(this.state, path), - requestChange: setPartialState.bind(null, this, path) - } +export default function linkState (path) { + return { + value: getIn(this.state, path), + requestChange: setPartialState.bind(null, this, path) } } diff --git a/browser/main/Services/Hq.js b/browser/main/Services/Hq.js index 500e5922..5953ef8f 100644 --- a/browser/main/Services/Hq.js +++ b/browser/main/Services/Hq.js @@ -7,12 +7,12 @@ module.exports = { // Auth login: function (input) { return request - .post(apiUrl + 'auth') + .post(apiUrl + 'auth/login') .send(input) }, signup: function (input) { return request - .post(apiUrl + 'auth/signup') + .post(apiUrl + 'auth/register') .send(input) }, getUser: function () { @@ -30,124 +30,36 @@ module.exports = { }) .send(input) }, - - // Resources - fetchUser: function (userName) { + fetchArticles: function (userId) { return request - .get(apiUrl + 'resources/' + userName) + .get(apiUrl + 'teams/' + userId +'/articles') .set({ Authorization: 'Bearer ' + localStorage.getItem('token') }) }, - updateUser: function (userName, input) { + fetchArticlesByFolderId: function (folderId) { return request - .put(apiUrl + 'resources/' + userName) + .get(apiUrl + 'folders/' + folderId +'/articles') + .set({ + Authorization: 'Bearer ' + localStorage.getItem('token') + }) + }, + createArticle: function (input) { + return request + .post(apiUrl + 'folders/' + input.FolderId + '/articles') .set({ Authorization: 'Bearer ' + localStorage.getItem('token') }) .send(input) }, - createTeam: function (userName, input) { + updateArticle: function (articleId, input) { return request - .post(apiUrl + 'resources/' + userName + '/teams') + .put(apiUrl + 'articles/' + articleId) .set({ Authorization: 'Bearer ' + localStorage.getItem('token') }) .send(input) }, - addMember: function (userName, input) { - return request - .post(apiUrl + 'resources/' + userName + '/members') - .set({ - Authorization: 'Bearer ' + localStorage.getItem('token') - }) - .send(input) - }, - removeMember: function (userName, input) { - return request - .del(apiUrl + 'resources/' + userName + '/members') - .set({ - Authorization: 'Bearer ' + localStorage.getItem('token') - }) - .send(input) - }, - createPlanet: function (userName, input) { - return request - .post(apiUrl + 'resources/' + userName + '/planets') - .set({ - Authorization: 'Bearer ' + localStorage.getItem('token') - }) - .send(input) - }, - fetchPlanet: function (userName, planetName) { - return request - .get(apiUrl + 'resources/' + userName + '/planets/' + planetName) - .set({ - Authorization: 'Bearer ' + localStorage.getItem('token') - }) - }, - updatePlanet: function (userName, planetName, input) { - return request - .put(apiUrl + 'resources/' + userName + '/planets/' + planetName) - .set({ - Authorization: 'Bearer ' + localStorage.getItem('token') - }) - .send(input) - }, - destroyPlanet: function (userName, planetName) { - return request - .del(apiUrl + 'resources/' + userName + '/planets/' + planetName) - .set({ - Authorization: 'Bearer ' + localStorage.getItem('token') - }) - }, - createCode: function (userName, planetName, input) { - return request - .post(apiUrl + 'resources/' + userName + '/planets/' + planetName + '/codes') - .set({ - Authorization: 'Bearer ' + localStorage.getItem('token') - }) - .send(input) - }, - updateCode: function (userName, planetName, localId, input) { - return request - .put(apiUrl + 'resources/' + userName + '/planets/' + planetName + '/codes/' + localId) - .set({ - Authorization: 'Bearer ' + localStorage.getItem('token') - }) - .send(input) - }, - destroyCode: function (userName, planetName, localId) { - return request - .del(apiUrl + 'resources/' + userName + '/planets/' + planetName + '/codes/' + localId) - .set({ - Authorization: 'Bearer ' + localStorage.getItem('token') - }) - }, - createNote: function (userName, planetName, input) { - return request - .post(apiUrl + 'resources/' + userName + '/planets/' + planetName + '/notes') - .set({ - Authorization: 'Bearer ' + localStorage.getItem('token') - }) - .send(input) - }, - updateNote: function (userName, planetName, localId, input) { - return request - .put(apiUrl + 'resources/' + userName + '/planets/' + planetName + '/notes/' + localId) - .set({ - Authorization: 'Bearer ' + localStorage.getItem('token') - }) - .send(input) - }, - destroyNote: function (userName, planetName, localId) { - return request - .del(apiUrl + 'resources/' + userName + '/planets/' + planetName + '/notes/' + localId) - .set({ - Authorization: 'Bearer ' + localStorage.getItem('token') - }) - }, - // Search searchTag: function (tagName) { return request diff --git a/browser/main/Services/socket.js b/browser/main/Services/socket.js index 39d65f0d..2e285324 100644 --- a/browser/main/Services/socket.js +++ b/browser/main/Services/socket.js @@ -1,64 +1,17 @@ /* global localStorage */ var config = require('../../../config') -var UserStore = require('../Stores/UserStore') -var PlanetStore = require('../Stores/PlanetStore') var io = require('socket.io-client')(config.apiUrl) io.on('connected', function (data) { console.log('connected by WS') - reconnect() }) io.on('userUpdated', function (data) { console.log('userUpdated') - UserStore.Actions.update(data) }) -// Planet -io.on('planetUpdated', function (data) { - console.log('planetUpdated') - PlanetStore.Actions.update(data) -}) - -io.on('planetDestroyed', function (data) { - console.log('planetDestroyed') - PlanetStore.Actions.destroy(data) -}) - -// Article -io.on('codeUpdated', function (data) { - console.log('codeUpdated') - PlanetStore.Actions.updateCode(data) -}) -io.on('codeDestroyed', function (data) { - console.log('codeDestroyed') - PlanetStore.Actions.destroyCode(data) -}) -io.on('noteUpdated', function (data) { - console.log('noteUpdated') - PlanetStore.Actions.updateNote(data) -}) -io.on('noteDestroyed', function (data) { - console.log('noteDestroyed') - PlanetStore.Actions.destroyNote(data) -}) - -var reconnect = function (currentUser) { - if (currentUser == null) currentUser = JSON.parse(localStorage.getItem('currentUser')) - if (currentUser != null) { - var rooms = ['user:' + currentUser.id].concat(currentUser.Teams.map(function (team) { - return 'user:' + team.id - })) - - io.emit('room:sync', {rooms: rooms}) - } else { - io.emit('room:sync', {rooms: []}) - } -} - module.exports = { - io: io, - reconnect: reconnect + io: io } diff --git a/browser/main/Stores/AuthStore.js b/browser/main/Stores/AuthStore.js deleted file mode 100644 index 5f64f310..00000000 --- a/browser/main/Stores/AuthStore.js +++ /dev/null @@ -1,131 +0,0 @@ -/* global localStorage */ -var Reflux = require('reflux') -var request = require('superagent') - -var apiUrl = require('../../../config').apiUrl - -var AuthStore = Reflux.createStore({ - init: function () { - }, - // Reflux Store - login: function (input) { - request - .post(apiUrl + 'auth/login') - .send(input) - .set('Accept', 'application/json') - .end(function (err, res) { - if (err) { - console.error(err) - this.trigger({ - status: 'failedToLogIn', - data: res - }) - return - } - - var user = res.body.user - localStorage.setItem('token', res.body.token) - localStorage.setItem('user', JSON.stringify(res.body.user)) - - this.trigger({ - status: 'loggedIn', - data: user - }) - }.bind(this)) - }, - register: function (input) { - request - .post(apiUrl + 'auth/signup') - .send(input) - .set('Accept', 'application/json') - .end(function (err, res) { - if (err) { - console.error(res) - this.trigger({ - status: 'failedToRegister', - data: res - }) - return - } - - var user = res.body.user - localStorage.setItem('token', res.body.token) - localStorage.setItem('user', JSON.stringify(res.body.user)) - - this.trigger({ - status: 'registered', - data: user - }) - }.bind(this)) - }, - refreshUser: function () { - request - .get(apiUrl + 'auth/user') - .set({ - Authorization: 'Bearer ' + localStorage.getItem('token') - }) - .end(function (err, res) { - if (err) { - console.error(err) - if (res.status === 401 || res.status === 403) { - AuthActions.logout() - } - return - } - - var user = res.body - localStorage.setItem('user', JSON.stringify(user)) - - this.trigger({ - status: 'userRefreshed', - data: user - }) - }.bind(this)) - }, - logout: function () { - localStorage.removeItem('token') - localStorage.removeItem('currentUser') - - this.trigger({ - status: 'loggedOut' - }) - }, - updateProfile: function (input) { - request - .put(apiUrl + 'auth/user') - .set({ - Authorization: 'Bearer ' + localStorage.getItem('token') - }) - .send(input) - .end(function (err, res) { - if (err) { - console.error(err) - this.trigger({ - status: 'userProfileUpdatingFailed', - data: err - }) - return - } - - var user = res.body - localStorage.setItem('user', JSON.stringify(user)) - - this.trigger({ - status: 'userProfileUpdated', - data: user - }) - }.bind(this)) - }, - // Methods - check: function () { - if (localStorage.getItem('token')) return true - return false - }, - getUser: function () { - var userJSON = localStorage.getItem('currentUser') - if (userJSON == null) return null - return JSON.parse(userJSON) - } -}) - -module.exports = AuthStore diff --git a/browser/main/Stores/PlanetStore.js b/browser/main/Stores/PlanetStore.js deleted file mode 100644 index 6b33cdcf..00000000 --- a/browser/main/Stores/PlanetStore.js +++ /dev/null @@ -1,179 +0,0 @@ -/* global localStorage */ - -var Reflux = require('reflux') - -var UserStore = require('./UserStore') - -var Helper = require('../Mixins/Helper') - -var actions = Reflux.createActions([ - 'update', - 'destroy', - 'updateCode', - 'destroyCode', - 'updateNote', - 'destroyNote' -]) - -module.exports = Reflux.createStore({ - mixins: [Helper], - listenables: [actions], - Actions: actions, - /* - Planet must be updated like below - Planet - Codes - Tags - User - Notes - Tags - User - Owner - */ - onUpdate: function (planet) { - // Copy the planet object - var aPlanet = Object.assign({}, planet) - delete aPlanet.Codes - delete aPlanet.Notes - delete aPlanet.Owner - - // Check if the planet should be updated to currentUser - var currentUser = JSON.parse(localStorage.getItem('currentUser')) - - var currentUserMustBeUpdated = false - - var ownedByCurrentUser = currentUser.id === aPlanet.OwnerId - if (ownedByCurrentUser) { - currentUser.Planets = this.updateItemToTargetArray(aPlanet, currentUser.Planets) - currentUserMustBeUpdated = true - } else { - var team = null - if (currentUser.Teams.some(function (_team) { - if (_team.id === aPlanet.OwnerId) { - team = _team - return true - } - return false - })) { - team.Planets = this.updateItemToTargetArray(aPlanet, team.Planets) - currentUserMustBeUpdated = true - } - } - - // Update currentUser - if (currentUserMustBeUpdated) { - UserStore.Actions.update(currentUser) - } - - planet.Codes.forEach(function (code) { - code.type = 'code' - }) - - planet.Notes.forEach(function (note) { - note.type = 'note' - }) - - // Update the planet - localStorage.setItem('planet-' + planet.id, JSON.stringify(planet)) - - this.trigger({ - status: 'updated', - data: planet - }) - }, - onDestroy: function (planet) { - // Check if the planet should be updated to currentUser - var currentUser = JSON.parse(localStorage.getItem('currentUser')) - - var ownedByCurrentUser = currentUser.id === planet.OwnerId - - if (ownedByCurrentUser) { - currentUser.Planets = this.deleteItemFromTargetArray(planet, currentUser.Planets) - } - - if (!ownedByCurrentUser) { - var team = null - currentUser.Teams.some(function (_team) { - if (_team.id === planet.OwnerId) { - team = _team - return true - } - return - }) - - if (team) { - team.Planets = this.deleteItemFromTargetArray(planet, team.Planets) - } - } - - // Update currentUser - localStorage.setItem('currentUser', JSON.stringify(currentUser)) - UserStore.Actions.update(currentUser) - - // Update the planet - localStorage.setItem('planet-' + planet.id, JSON.stringify(planet)) - - this.trigger({ - status: 'destroyed', - data: planet - }) - }, - onUpdateCode: function (code) { - code.type = 'code' - - var planet = JSON.parse(localStorage.getItem('planet-' + code.PlanetId)) - if (planet != null) { - planet.Codes = this.updateItemToTargetArray(code, planet.Codes) - - localStorage.setItem('planet-' + code.PlanetId, JSON.stringify(planet)) - } - - this.trigger({ - status: 'codeUpdated', - data: code - }) - }, - onDestroyCode: function (code) { - var planet = JSON.parse(localStorage.getItem('planet-' + code.PlanetId)) - if (planet != null) { - planet.Codes = this.deleteItemFromTargetArray(code, planet.Codes) - - localStorage.setItem('planet-' + code.PlanetId, JSON.stringify(planet)) - } - code.type = 'code' - - this.trigger({ - status: 'codeDestroyed', - data: code - }) - }, - onUpdateNote: function (note) { - note.type = 'note' - - var planet = JSON.parse(localStorage.getItem('planet-' + note.PlanetId)) - if (planet != null) { - planet.Notes = this.updateItemToTargetArray(note, planet.Notes) - - localStorage.setItem('planet-' + note.PlanetId, JSON.stringify(planet)) - } - - this.trigger({ - status: 'noteUpdated', - data: note - }) - }, - onDestroyNote: function (note) { - var planet = JSON.parse(localStorage.getItem('planet-' + note.PlanetId)) - if (planet != null) { - planet.Notes = this.deleteItemFromTargetArray(note, planet.Notes) - - localStorage.setItem('planet-' + note.PlanetId, JSON.stringify(planet)) - } - note.type = 'note' - - this.trigger({ - status: 'noteDestroyed', - data: note - }) - } -}) diff --git a/browser/main/Stores/UserStore.js b/browser/main/Stores/UserStore.js deleted file mode 100644 index 0fd92fdd..00000000 --- a/browser/main/Stores/UserStore.js +++ /dev/null @@ -1,58 +0,0 @@ -/* global localStorage */ - -var Reflux = require('reflux') - -var actions = Reflux.createActions([ - 'update', - 'destroy' -]) - -module.exports = Reflux.createStore({ - listenables: [actions], - onUpdate: function (user) { - if (this.socket == null) this.socket = require('../Services/socket') - - var currentUser = JSON.parse(localStorage.getItem('currentUser')) - if (currentUser.id === user.id) { - localStorage.setItem('currentUser', JSON.stringify(user)) - - this.socket.reconnect(user) - } - - if (user.userType === 'team') { - var isMyTeam = user.Members.some(function (member) { - if (currentUser.id === member.id) { - return true - } - return false - }) - - if (isMyTeam) { - var isNew = !currentUser.Teams.some(function (team, index) { - if (user.id === team.id) { - currentUser.Teams.splice(index, 1, user) - return true - } - return false - }) - - if (isNew) { - currentUser.Teams.push(user) - } - localStorage.setItem('currentUser', JSON.stringify(currentUser)) - } - } - - this.trigger({ - status: 'userUpdated', - data: user - }) - }, - onDestroy: function (user) { - this.trigger({ - status: 'userDestroyed', - data: user - }) - }, - Actions: actions -}) diff --git a/browser/main/helpers/linkState.js b/browser/main/helpers/linkState.js new file mode 100644 index 00000000..6b2be4a0 --- /dev/null +++ b/browser/main/helpers/linkState.js @@ -0,0 +1,29 @@ +function getIn (object, path) { + var stack = path.split('.') + while (stack.length > 1) { + object = object[stack.shift()] + } + return object[stack.shift()] +} + +function updateIn (object, path, value) { + var current = object + var stack = path.split('.') + while (stack.length > 1) { + current = current[stack.shift()] + } + current[stack.shift()] = value + return object +} + +function setPartialState (component, path, value) { + component.setState( + updateIn(component.state, path, value)) +} + +export default function linkState (path) { + return { + value: getIn(this.state, path), + requestChange: setPartialState.bind(null, this, path) + } +} diff --git a/browser/main/helpers/openExternal.js b/browser/main/helpers/openExternal.js new file mode 100644 index 00000000..f8223159 --- /dev/null +++ b/browser/main/helpers/openExternal.js @@ -0,0 +1,6 @@ +var shell = require('shell') + +export default function (e) { + shell.openExternal(e.currentTarget.href) + e.preventDefault() +} diff --git a/browser/main/index.electron.html b/browser/main/index.electron.html deleted file mode 100644 index 04fe21fa..00000000 --- a/browser/main/index.electron.html +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - - - - - - - -
    - -
    Loading...
    -
    - -
    - - - - - - diff --git a/browser/main/index.html b/browser/main/index.html index 243ec598..da8f0ce9 100644 --- a/browser/main/index.html +++ b/browser/main/index.html @@ -1,56 +1,64 @@ - CodeXen + + - + + + - - - - - - - - + #loadingCover{ + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + box-sizing: border-box; + padding: 65px 0; + font-family: sans-serif; + } + #loadingCover img{ + display: block; + margin: 75px auto 5px; + width: 160px; + height: 160px; + } + #loadingCover .message{ + font-size: 30px; + text-align: center; + line-height: 1.6; + font-weight: 100; + color: #888; + } + +
    + +
    Loading...
    +
    +
    - - - + + + diff --git a/browser/main/index.js b/browser/main/index.js new file mode 100644 index 00000000..78e7dfa4 --- /dev/null +++ b/browser/main/index.js @@ -0,0 +1,29 @@ +import React from 'react' +import { Router, Route, IndexRoute } from 'react-router' +import MainContainer from './Containers/MainContainer' +import LoginContainer from './Containers/LoginContainer' +import SignupContainer from './Containers/SignupContainer' +import HomeContainer from './HomeContainer' + +function onlyUser (state, replaceState) { + var currentUser = JSON.parse(localStorage.getItem('currentUser')) + if (currentUser == null) replaceState('login', '/login') +} + +let routes = ( + + + + + + + + + +) + +let el = document.getElementById('content') +React.render({routes}, el, function () { + let loadingCover = document.getElementById('loadingCover') + loadingCover.parentNode.removeChild(loadingCover) +}) diff --git a/browser/main/index.jsx b/browser/main/index.jsx deleted file mode 100644 index 126f1648..00000000 --- a/browser/main/index.jsx +++ /dev/null @@ -1,46 +0,0 @@ -var React = require('react/addons') - -var ReactRouter = require('react-router') -var Route = ReactRouter.Route -var DefaultRoute = ReactRouter.DefaultRoute - -var MainContainer = require('./Containers/MainContainer') - -var LoginContainer = require('./Containers/LoginContainer') -var SignupContainer = require('./Containers/SignupContainer') - -var HomeContainer = require('./Containers/HomeContainer') -var UserContainer = require('./Containers/UserContainer') - -var PlanetContainer = require('./Containers/PlanetContainer') - -var routes = ( - - - - - - - - - - - - - - - - - - -) -var loadingCover = document.getElementById('loadingCover') - -ReactRouter.run(routes, ReactRouter.HashLocation, function (Root) { - React.render(, document.getElementById('content')) - - if (loadingCover != null) { - loadingCover.parentNode.removeChild(loadingCover) - loadingCover = null - } -}) diff --git a/browser/main/style.js b/browser/main/style.js deleted file mode 100644 index dd1009a9..00000000 --- a/browser/main/style.js +++ /dev/null @@ -1,2 +0,0 @@ -require('../styles/main/index.styl') -require('react-select/dist/default.css') diff --git a/browser/styles/main/components/ArticleDetail.styl b/browser/styles/main/components/ArticleDetail.styl new file mode 100644 index 00000000..1e069f7c --- /dev/null +++ b/browser/styles/main/components/ArticleDetail.styl @@ -0,0 +1,169 @@ +noTagsColor = #999 + +.ArticleDetail + absolute right bottom + top 60px + left 250px + padding 10px + * + -webkit-user-select all + .detailInfo + height 70px + width 100% + transition 0.1s + font-size 12px + position relative + .left + absolute top left bottom + right 120px + .right + absolute top right + .detailBody + absolute left right bottom + top 70px + overflow-x hidden + overflow-y auto + .detailPanel + absolute top + left 10px + right 10px + bottom 10px + background-color white + border-radius 5px + border solid 1px borderColor + &>.header + absolute top left right + height 60px + .MarkdownPreview + absolute left right bottom + top 60px + marked() + box-sizing border-box + padding 5px 15px + border-top solid 1px borderColor + overflow-y auto + .CodeEditor + absolute left right bottom + top 60px + border-top solid 1px borderColor + min-height 300px + border-bottom-left-radius 5px + border-bottom-right-radius 5px + &.edit + .detailInfo + .left + .Select + .Select-control + border none + background-color transparent + .folder.Select + width 150px + .Select-control + &:hover + background-color darken(white, 5%) + &.is-focused + .Select-control + background-color white + .tags.Select + .Select-control + white-space nowrap + overflow-x auto + position relative + .Select-arrow-zone, .Select-arrow + display none + .right + button + cursor pointer + height 33px + width 55px + margin-left 5px + font-size 14px + color inactiveTextColor + background-color darken(white, 5%) + border solid 1px borderColor + border-radius 5px + &:hover + background-color white + &.primary + border none + background-color brandColor + color white + &:hover + color white + background-color lighten(brandColor, 10%) + .detailBody + .detailPanel + &>.header + .mode + absolute top bottom right + display block + height 33px + margin-top 12px + width 120px + margin-right 15px + .title + absolute left top bottom + right 120px + padding 0 15px + input + width 100% + border none + background-color transparent + line-height 60px + font-size 32px + font-weight bold + outline none + &.show + .detailInfo + .left + right 99px + .info + padding 5px + overflow ellipsis + .tags + padding 10px 10px 5px + color articleItemColor + a + background-color brandColor + color white + border-radius 2px + padding 1.5px 5px + margin 2px + font-size 10px + opacity 0.8 + &:hover + opacity 1 + span.noTags + color noTagsColor + .right + button + cursor pointer + height 33px + width 33px + border none + font-size 18px + color inactiveTextColor + background-color transparent + padding 0 + &:hover + color inherit + .detailBody + .detailPanel + &>.header + .mode + display block + line-height 60px + width 45px + height 60px + font-size 18px + text-align center + .title + absolute top bottom + left 45px + right 15px + font-size 32px + line-height 60px + font-weight bold + white-space nowrap + overflow-x auto + overflow-y hidden diff --git a/browser/styles/main/components/ArticleList.styl b/browser/styles/main/components/ArticleList.styl new file mode 100644 index 00000000..9a209e12 --- /dev/null +++ b/browser/styles/main/components/ArticleList.styl @@ -0,0 +1,69 @@ +articleItemHoverBgColor = darken(white, 5%) +articleItemColor = #777 + +.ArticleList + absolute left bottom + top 60px + width 250px + border-right solid 1px highlightenBorderColor + &>ul + absolute top bottom left right + overflow-y auto + noSelect() + li + .articleItem + border solid 2px transparent + position relative + height 88px + width 100% + cursor pointer + transition 0.1s + background-color white + padding 0 10px + font-size 12px + .top + clearfix() + line-height 20px + padding 5px 0 + color articleItemColor + .profileImage + vertical-align middle + .updatedAt + float right + line-height 20px + .middle + clearfix() + padding 3px 0 7px + font-size 16px + .mode + float left + font-size 12px + line-height 16px + .title + float left + overflow ellipsis + padding 0 5px + .bottom + padding 5px 0 + overflow-x auto + white-space nowrap + .tags + color articleItemColor + a + background-color brandColor + color white + border-radius 2px + padding 1.5px 5px + margin 2px + font-size 10px + opacity 0.8 + &:hover + opacity 1 + &:hover, &.hover + background-color articleItemHoverBgColor + &:active, &.active + background-color white + &:active, &.active + border-color brandBorderColor + .divider + border-bottom solid 1px borderColor diff --git a/browser/styles/main/components/Select.styl b/browser/styles/main/components/Select.styl index 08265706..57cecb4f 100644 --- a/browser/styles/main/components/Select.styl +++ b/browser/styles/main/components/Select.styl @@ -23,7 +23,7 @@ transition: all 200ms ease; } .Select-control:hover { - box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06); + // box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06); } .is-searchable.is-open > .Select-control { cursor: text; @@ -42,8 +42,8 @@ cursor: text; } .is-focused:not(.is-open) > .Select-control { - border-color: #0088cc #0099e6 #0099e6; - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 0 5px -1px rgba(0, 136, 204, 0.5); + // border-color: #0088cc #0099e6 #0099e6; + // box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 0 5px -1px rgba(0, 136, 204, 0.5); } .Select-placeholder { color: #aaaaaa; @@ -196,10 +196,10 @@ padding: 3px 0; } .Select-item { - background-color: #f2f9fc; + background-color: brandColor; border-radius: 2px; - border: 1px solid #c9e6f2; - color: #0088cc; + // border: 1px solid #c9e6f2; + color: white; display: inline-block; font-size: 1em; margin: 2px; @@ -216,20 +216,19 @@ padding: 3px 5px; } .Select-item-label .Select-item-label__a { - color: #0088cc; - cursor: pointer; + color: white; + cursor: white; } .Select-item-icon { cursor: pointer; border-bottom-left-radius: 2px; border-top-left-radius: 2px; - border-right: 1px solid #c9e6f2; + border-right: 1px solid darken(brandColor, 10%) padding: 2px 5px 4px; } .Select-item-icon:hover, .Select-item-icon:focus { - background-color: #ddeff7; - color: #0077b3; + background-color: lighten(brandColor, 10%) } .Select-item-icon:active { background-color: #c9e6f2; diff --git a/browser/styles/main/components/TopBar.styl b/browser/styles/main/components/TopBar.styl new file mode 100644 index 00000000..2f8f9f87 --- /dev/null +++ b/browser/styles/main/components/TopBar.styl @@ -0,0 +1,43 @@ +.TopBar + absolute top left right + height 60px + border-bottom solid 1px borderColor + noSelect() + .left + float left + .search + position absolute + top 13.5px + left 15px + height 33px + i.fa + position absolute + line-height 33px + z-index 1 + width 33px + text-align center + input.searchInput + absolute top left + background-color white + borderInput() + width 350px + padding-left 30px + border-radius 16.5px + font-size 14px + height 33px + line-height 33px + outline none + &:focus + border-color brandColor + .right + float right + .logo + &>img + margin-top 7px + margin-right 15px + .tooltip + tooltip() + right 5px + &:hover + .tooltip + opacity 1.0 diff --git a/browser/styles/main/containers/HomeContainer.styl b/browser/styles/main/containers/HomeContainer.styl new file mode 100644 index 00000000..8e3bfa32 --- /dev/null +++ b/browser/styles/main/containers/HomeContainer.styl @@ -0,0 +1,153 @@ +homeNavigatorBgColor = #1B1C1C +homeNavigatorColor = #DDD +userAnchorColor = #979797 +userAnchorBgColor = #BEBEBE +userAnchorActiveColor = textColor +userAnchorActiveBgColor = white + +.HomeContainer + .HomeNavigator + noSelect() + background-color homeNavigatorBgColor + absolute left top bottom + width 60px + text-align center + box-sizing border-box + // must be moved + // .profilePopup + // position fixed + // left 35px + // top 35px + // z-index popupZIndex + // width 200px + // background-color backgroundColor + // box-shadow popupShadow + // border-radius 10px + // padding 10px 0 0px + // &.close + // display none + // .profileGroup + // margin-bottom 10px + // .profileGroupLabel + // text-align left + // height 1em + // padding 0 15px + // span + // position absolute + // z-index 2 + // background-color backgroundColor + // padding-right 5px + // color inactiveTextColor + // font-size 0.8em + // &::before + // content '' + // position absolute + // display block + // z-index 1 + // height 0.5em + // width 175px + // border-bottom solid 1px borderColor + // .profileGroupList + // li + // clearfix() + // &:hover + // background-color hoverBackgroundColor + // .userName + // width 155px + // padding 10px 15px + // text-align left + // display block + // text-decoration none + // cursor pointer + // .createNewTeam + // btnStripDefault() + // width 100% + // padding 10px 20px + // font-size 1em + // cursor pointer + // text-align left + // .controlGroup + // list-style none + // border-top solid 1px borderColor + // padding 10px 0 + // li + // &:hover + // background-color hoverBackgroundColor + // button + // btnStripDefault() + // width 100% + // padding 10px 20px + // font-size 1em + // cursor pointer + // text-align left + ul.userList + margin-top 25px + &>li + .shortCut + margin-top 5px + font-size 0.8em + color homeNavigatorColor + a + display block + width 44px + height 44px + margin 0 auto + text-align center + background-color userAnchorBgColor + text-decoration none + color userAnchorColor + line-height 44px + font-size 1.1em + cursor pointer + circle() + img + width 44px + height 44px + transition 0.1s + &:hover, &.active + background-color userAnchorActiveBgColor + color userAnchorActiveColor + .userTooltip + position absolute + z-index popupZIndex + background-color transparentify(invBackgroundColor, 80%) + color invTextColor + padding 10px + line-height 1em + border-radius 5px + margin-top -52px + margin-left 52px + white-space nowrap + opacity 0 + transition 0.1s + pointer-events none + &:hover .userTooltip + opacity 1 + button.newTeamButton + display block + margin 0 auto + width 30px + height 30px + circle() + border solid 1px lightButtonColor + color lightButtonColor + text-align center + background-image none + background-color transparent + box-sizing border-box + absolute left bottom right + bottom 15px + &:hover, &.hover, &:focus, &.focus + border-color darken(lightButtonColor, 50%) + color darken(lightButtonColor, 50%) + &:active, &.active + border-color darken(brandBorderColor, 10%) + background-color brandColor + color white + .tooltip + tooltip() + margin-top -22px + margin-left 33px + font-size 14px + &:hover .tooltip + opacity 1 diff --git a/browser/styles/main/containers/LoginContainer.styl b/browser/styles/main/containers/LoginContainer.styl index c190cf44..aa35c41a 100644 --- a/browser/styles/main/containers/LoginContainer.styl +++ b/browser/styles/main/containers/LoginContainer.styl @@ -1,6 +1,6 @@ .LoginContainer, .SignupContainer margin 0 auto - padding 25px 15px + padding 105px 15px box-sizing border-box color inactiveTextColor .logo @@ -58,17 +58,24 @@ .alertInfo, .alertError margin-top 15px margin-bottom 15px - height 44px - padding 5px - border-radius 10px - line-height 44px + padding 10px + border-radius 5px + line-height 1.6 text-align center .alertInfo alertInfo() .alertError alertError() - div.form-group:last-child - margin-top 15px + div.formField + input + stripInput() + height 33px + width 100% + margin-bottom 10px + text-align center + font-size 1.1em + &:last-child + margin-top 15px button.logInButton btnPrimary() height 44px diff --git a/browser/styles/main/containers/UserContainer.styl b/browser/styles/main/containers/UserContainer.styl index 0bfc021c..e8b17cea 100644 --- a/browser/styles/main/containers/UserContainer.styl +++ b/browser/styles/main/containers/UserContainer.styl @@ -1,310 +1,123 @@ -.HomeContainer - .HomeNavigator - noSelect() - background-color planetNavBgColor - absolute left top bottom - width 55px - text-align center - box-sizing border-box - border-right solid 1px borderColor - .profileButton - display block - width 55px - height 55px - border-bottom solid 1px borderColor - overflow hidden - background-color black - margin 0 - padding 0 - cursor pointer - box-sizing border-box - border none - img - transition 0.1s - opacity 0.9 - &.vivid.active, &.focus, &:focus, &.hover, &:hover - img - opacity 1 - .profilePopup - position fixed - left 35px - top 35px - z-index popupZIndex - width 200px - background-color backgroundColor - box-shadow popupShadow - border-radius 10px - padding 10px 0 0px - &.close - display none - .profileGroup - margin-bottom 10px - .profileGroupLabel - text-align left - height 1em - padding 0 15px - span - position absolute - z-index 2 - background-color backgroundColor - padding-right 5px - color inactiveTextColor - font-size 0.8em - &::before - content '' - position absolute - display block - z-index 1 - height 0.5em - width 175px - border-bottom solid 1px borderColor - .profileGroupList - li - clearfix() - &:hover - background-color hoverBackgroundColor - .userName - width 155px - padding 10px 15px - text-align left - display block - text-decoration none - cursor pointer - .createNewTeam - btnStripDefault() - width 100% - padding 10px 20px - font-size 1em - cursor pointer - text-align left - .controlGroup - list-style none - border-top solid 1px borderColor - padding 10px 0 - li - &:hover - background-color hoverBackgroundColor - button - btnStripDefault() - width 100% - padding 10px 20px - font-size 1em - cursor pointer - text-align left +userNavigatorWidth = 200px +userNavigatorBgColor = #333 +userNavigatorColor = #DDD +userNavigatorProfileNameColor = brandColor +userNavigatorBorderColor = #666 - ul.planetList>li - margin 15px 0 - .shortCut - margin-top 5px - color lighten(textColor, 5%) - font-size 0.8em - &.active - a - background-color planetAnchorActiveBgColor - color planetAnchorActiveColor - a - display block - width 44px - height 44px - margin 0 auto - text-align center - background-color planetAnchorBgColor - text-decoration none - color planetAnchorColor - line-height 44px - font-size 1.1em - cursor pointer - circle() - transition 0.1s - &:hover, &:active - background-color white - .planetTooltip - position absolute - z-index popupZIndex - background-color transparentify(invBackgroundColor, 80%) - color invTextColor - padding 10px - line-height 1em - border-radius 5px - margin-top -41px - margin-left 52px - white-space nowrap - opacity 0 - transition 0.1s - pointer-events none - &:hover .planetTooltip - opacity 1 - img - circle() - width 55px - height 55px - button.newPlanet - display block - margin 0 auto - width 30px - height 30px - circle() - border solid 1px lightButtonColor - color lightButtonColor - text-align center - font-size 1 - background-image none - background-color transparent +userContentBgColor = #E6E6E6 + +.UserContainer + absolute top bottom right + left 60px + .content + absolute top bottom right + left userNavigatorWidth + background-color userContentBgColor + .UserNavigator + absolute left top bottom + width userNavigatorWidth + background-color userNavigatorBgColor + color userNavigatorColor + noSelect() + &>.profile + height 60px + padding 10px 15px 0 box-sizing border-box - absolute left bottom right - bottom 15px - &:hover, &.hover, &:focus, &.focus - border-color darken(lightButtonColor, 50%) - color darken(lightButtonColor, 50%) - &:active, &.active - border-color darken(brandBorderColor, 10%) + position relative + border-bottom solid 1px userNavigatorBorderColor + cursor pointer + &>.profileName + color userNavigatorProfileNameColor + font-size 22px + cursor pointer + transition 0.1s + &>.name + padding 5px 10px + font-size 14px + color userNavigatorColor + cursor pointer + transition 0.1s + &>.dropdownIcon + position absolute + top 20px + right 25px + float right + width 20px + height 20px + line-height 20px + font-size 8px + border solid 1px userNavigatorColor + border-radius 12.5px + text-align center + transition 0.1s + &:hover + &>.profileName + color lighten(brandColor, 10%) + &>.name + color white + &>.dropdownIcon + border-color white + &:active + &>.dropdownIcon + background-color brandColor + border-color brandColor + &>.control + padding 15px 15px + &>.newPostButton background-color brandColor color white - .tooltip - tooltip() - margin-top -22px - margin-left 33px - &:hover .tooltip - opacity 1 - .UserContainer - absolute top bottom right - left 55px - .memberPopup - absolute left - top 235px - z-index 1 - padding 0 15px 10px - width 200px - .label - padding 10px 0 - font-size 0.9em - border-bottom solid 1px borderColor - margin-bottom 15px - .members - li - padding 0 10px - margin-bottom 15px - clearfix() - .memberImage - float left - margin-right 7px - circle() - .memberInfo - float left - .memberProfileName - margin-bottom 5px - font-size 1.05em - .memberName - margin-left 5px - font-size 0.9em - color inactiveTextColor - a:hover .memberProfileName, a:hover .memberName - text-decoration underline - .userProfile - absolute top left right - padding 15px - border-bottom solid 1px borderColor - height 125px - clearfix() - .userPhoto - circle() - float left - margin 5px 15px 15px - .userInfo - float left - margin-top 15px - .userProfileName - font-size 1.5em - color brandColor - margin-bottom 10px - .userName - font-size 1.1em - .editProfileButton - float right - btnDefault() - margin-top 25px - padding 10px 15px + height 44px + width 100% + border none border-radius 5px - .teamList, .memberList - absolute left bottom - top 125px - width 200px - padding 15px - border-right solid 1px borderColor - overflow-y auto - .teamLabel, .memberLabel - font-size 1.2em - margin-bottom 15px - .teams - li - padding 0 10px - margin-bottom 15px - clearfix() - .teamInfo - float left - .teamProfileName - margin-bottom 5px - font-size 1.05em - .teamName - margin-left 5px - font-size 0.9em - color inactiveTextColor - a:hover .teamProfileName, a:hover .teamName - text-decoration underline + font-size 16px + font-weight 600 + transition 0.1s + &:hover + background-color lighten(brandColor, 10%) + &>.menu + absolute left right bottom + top 134px + padding 15px 0 + overflow auto + &>.menuGruop + &>.label + border-bottom 1px solid userNavigatorBorderColor + padding 10px 15px + font-size 18px margin-bottom 10px - font-size 1.1em - .createTeamButton, .addMemberButton - btnStripDefault() - .members - li - padding 0 10px - margin-bottom 15px - clearfix() - .memberImage - float left - margin-right 7px - circle() - .memberInfo - float left - .memberProfileName - margin-bottom 5px - font-size 1.05em - .memberRole - font-size 0.9em - color inactiveTextColor - .memberName - margin-left 5px - font-size 0.9em - color inactiveTextColor - .createTeamButton, .addMemberButton - btnStripDefault() - a:hover .memberProfileName, a:hover .memberName - text-decoration underline - .planetList - absolute right bottom - top 125px - left 200px - padding 15px - overflow-y auto - .planetLabel - font-size 1.2em - margin-bottom 15px - .planetGroup - margin-left 15px - .planetGroupLabel - font-size 1.1em - margin-bottom 15px - small - font-size 0.8em - color inactiveTextColor - .planets - margin-left 15px - li - a - font-size 1.1em - text-decoration none - &:hover - text-decoration underline - margin-bottom 10px - .createPlanetButton - btnStripDefault() + &>.plusButton + float right + width 20px + height 20px + margin-top -2.5px + margin-right -5px + line-height 15px + font-size 8px + border solid 1px userNavigatorColor + border-radius 10px + background-color transparent + text-align center + color userNavigatorColor + &:hover + border-color white + color white + &:active + background-color brandColor + border-color brandColor + &>.folders + .folderButton + padding 10px 25px + width 100% + background-color transparent + border none + font-size 14px + color userNavigatorColor + transition 0.1s + text-align left + &:hover + background-color transparentify(white, 20%) + color white + &.active + background-color brandColor + color white diff --git a/browser/styles/main/index.css b/browser/styles/main/index.css new file mode 100644 index 00000000..e9146a6e --- /dev/null +++ b/browser/styles/main/index.css @@ -0,0 +1,3733 @@ +html, +body, +div, +span, +applet, +object, +iframe, +h1, +h2, +h3, +h4, +h5, +h6, +p, +blockquote, +pre, +a, +abbr, +acronym, +address, +big, +cite, +code, +del, +dfn, +em, +img, +ins, +kbd, +q, +s, +samp, +small, +strike, +strong, +sub, +sup, +tt, +var, +dl, +dt, +dd, +ol, +ul, +li, +fieldset, +form, +label, +legend, +table, +caption, +tbody, +tfoot, +thead, +tr, +th, +td { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-weight: inherit; + font-style: inherit; + font-family: inherit; + font-size: 100%; + vertical-align: baseline; +} +body { + line-height: 1; + color: #000; + background: #fff; +} +ol, +ul { + list-style: none; +} +table { + border-collapse: separate; + border-spacing: 0; + vertical-align: middle; +} +caption, +th, +td { + text-align: left; + font-weight: normal; + vertical-align: middle; +} +a img { + border: none; +} +.btn-primary, +.btn-default { + border-style: solid; + border-width: 1px; + background-image: none; + height: 44px; + padding: 0 15px; + -webkit-border-radius: 5px; + border-radius: 5px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + font-size: 1em; + font-family: 'Lato'; + font-weight: 400; + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; + cursor: pointer; + margin: 0 5px; +} +.btn-block { + display: block; + width: 100%; + margin: 0 auto; +} +.btn-square { + display: inline-block; + width: 44px; + padding: 0; + border-width: 1px; +} +.btn-sm { + height: 32px; + -webkit-border-radius: 16px; + border-radius: 16px; +} +.btn-sm.btn-square { + width: 32px; +} +.btn-primary { + border-color: #3fb399; + background-color: transparent; + color: #2bac8f; +} +.btn-primary:hover, +.btn-primary.hover, +.btn-primary:focus, +.btn-primary.focus { + border-color: #2c7d6b; + color: #1e7864; +} +.btn-primary:active, +.btn-primary.active { + background-color: #2bac8f; + color: #fff; +} +.btn-default { + border-color: #898989; + background-color: transparent; + color: #898989; +} +.btn-default:hover, +.btn-default.hover, +.btn-default:focus, +.btn-default.focus { + border-color: #454545; + color: #454545; +} +.btn-default:active, +.btn-default.active { + border-color: #39a18a; + background-color: #2bac8f; + color: #fff; +} +.ModalBase { + position: fixed; + top: 0; + left: 0; + bottom: 0; + right: 0; + z-index: 1000; +} +.ModalBase.hide { + display: none; +} +.ModalBase .modalBack { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + background-color: rgba(255,255,255,0.65); + z-index: 1001; +} +.ModalBase .modal { + position: relative; + width: 650px; + margin: 50px auto 0; + z-index: 1002; + -webkit-box-shadow: 0 0 5px 0 #888; + box-shadow: 0 0 5px 0 #888; + background-color: #fff; + -webkit-border-radius: 10px; + border-radius: 10px; + padding: 15px; +} +.ModalBase .modal .modal-header { + border-bottom: solid 1px #d0d0d0; + margin-bottom: 10px; +} +.ModalBase .modal .modal-header h1 { + padding: 10px 0 15px; + font-size: 1.5em; +} +.ModalBase .modal .modal-body p { + margin-bottom: 10px; +} +.ModalBase .modal .modal-footer { + zoom: 1; + border-top: solid 1px #d0d0d0; + padding-top: 10px; +} +.ModalBase .modal .modal-footer:before, +.ModalBase .modal .modal-footer:after { + content: ""; + display: table; +} +.ModalBase .modal .modal-footer:after { + clear: both; +} +.ModalBase .modal .modal-footer .modal-control { + float: right; +} +.ModalBase .sideNavModal { + height: 500px; +} +.ModalBase .sideNavModal .leftPane { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 175px; + padding: 20px; + border-right: solid 1px #d0d0d0; +} +.ModalBase .sideNavModal .leftPane .modalLabel { + font-size: 1.5em; + margin-top: 25px; + margin-bottom: 35px; + color: #2bac8f; +} +.ModalBase .sideNavModal .leftPane .tabList button { + border: none; + background-color: transparent; + color: #898989; + display: block; + width: 100%; + font-size: 1.1em; + padding: 10px 5px; + margin-bottom: 15px; + text-align: left; +} +.ModalBase .sideNavModal .leftPane .tabList button:hover, +.ModalBase .sideNavModal .leftPane .tabList button.hover, +.ModalBase .sideNavModal .leftPane .tabList button:focus, +.ModalBase .sideNavModal .leftPane .tabList button.focus { + color: #454545; +} +.ModalBase .sideNavModal .leftPane .tabList button:active, +.ModalBase .sideNavModal .leftPane .tabList button.active { + color: #2bac8f; +} +.ModalBase .sideNavModal .rightPane { + position: absolute; + top: 0; + bottom: 0; + right: 0; + left: 175px; + padding: 15px; + overflow-y: auto; +} +.ModalBase .sideNavModal .tab { + padding-top: 45px; +} +.ModalBase .sideNavModal .tab .formField { + position: relative; + zoom: 1; + margin-bottom: 15px; +} +.ModalBase .sideNavModal .tab .formField:before, +.ModalBase .sideNavModal .tab .formField:after { + content: ""; + display: table; +} +.ModalBase .sideNavModal .tab .formField:after { + clear: both; +} +.ModalBase .sideNavModal .tab .formField label { + width: 30%; + display: block; + line-height: 33px; + float: left; +} +.ModalBase .sideNavModal .tab .formField input { + width: 70%; + display: block; + border: solid 1px #d0d0d0; + padding: 5px 15px; + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; + height: 33px; + font-size: 1em; + -webkit-border-radius: 5px; + border-radius: 5px; + float: left; +} +.ModalBase .sideNavModal .tab .formField input:focus, +.ModalBase .sideNavModal .tab .formField input.focus { + border-color: #3fb399; + outline: none; +} +.ModalBase .sideNavModal .tab .formRadioField { + margin-bottom: 15px; +} +.ModalBase .sideNavModal .tab .formRadioField input { + margin-left: 25px; +} +.ModalBase .sideNavModal .tab .formConfirm { + position: relative; + zoom: 1; + margin-bottom: 15px; +} +.ModalBase .sideNavModal .tab .formConfirm:before, +.ModalBase .sideNavModal .tab .formConfirm:after { + content: ""; + display: table; +} +.ModalBase .sideNavModal .tab .formConfirm:after { + clear: both; +} +.ModalBase .sideNavModal .tab .formConfirm button { + float: right; + border-style: solid; + border-width: 1px; + border-color: #898989; + background-color: transparent; + color: #898989; + padding: 10px 15px; + -webkit-border-radius: 5px; + border-radius: 5px; + font-size: 1em; + margin-left: 5px; +} +.ModalBase .sideNavModal .tab .formConfirm button:hover, +.ModalBase .sideNavModal .tab .formConfirm button.hover, +.ModalBase .sideNavModal .tab .formConfirm button:focus, +.ModalBase .sideNavModal .tab .formConfirm button.focus { + border-color: #454545; + color: #454545; +} +.ModalBase .sideNavModal .tab .formConfirm button:active, +.ModalBase .sideNavModal .tab .formConfirm button.active { + border-color: #39a18a; + background-color: #2bac8f; + color: #fff; +} +.ModalBase .sideNavModal .tab .formConfirm button:disabled, +.ModalBase .sideNavModal .tab .formConfirm button.disabled { + opacity: 0.6; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; + filter: alpha(opacity=60); +} +.ModalBase .sideNavModal .tab .formConfirm .alertInfo, +.ModalBase .sideNavModal .tab .formConfirm .alertSuccess, +.ModalBase .sideNavModal .tab .formConfirm .alertError { + float: right; + padding: 12px 10px; + -webkit-border-radius: 5px; + border-radius: 5px; + width: 320px; + font-size: 1em; + overflow-x: hidden; + white-space: nowrap; + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; +} +.ModalBase .sideNavModal .tab .formConfirm .alertInfo.hide, +.ModalBase .sideNavModal .tab .formConfirm .alertSuccess.hide, +.ModalBase .sideNavModal .tab .formConfirm .alertError.hide { + width: 0; + padding: 12px 0; +} +.ModalBase .sideNavModal .tab .formConfirm .alertInfo { + background-color: #d9edf7; + color: #34708e; +} +.ModalBase .sideNavModal .tab .formConfirm .alertSuccess { + background-color: #e0f0d9; + color: #3e753f; +} +.ModalBase .sideNavModal .tab .formConfirm .alertError { + background-color: #f2dede; + color: #a64444; +} +.ModalBase .PreferencesModal .settingsTab .categoryLabel { + font-size: 1.5em; + margin-bottom: 25px; +} +.ModalBase .PreferencesModal .settingsTab .example hr { + border-top: none; + border-bottom: solid 1px #d0d0d0; + margin: 15px 0; +} +.ModalBase .PreferencesModal .settingsTab .example h1 { + font-size: 2em; + border-bottom: solid 2px #d0d0d0; + margin: 0.33em auto 0.67em; +} +.ModalBase .PreferencesModal .settingsTab .example h2 { + font-size: 1.5em; + margin: 0.42em auto 0.83em; +} +.ModalBase .PreferencesModal .settingsTab .example h3 { + font-size: 1.17em; + margin: 0.5em auto 1em; +} +.ModalBase .PreferencesModal .settingsTab .example h4 { + font-size: 1em; + margin: 0.67em auto 1.33em; +} +.ModalBase .PreferencesModal .settingsTab .example h5 { + font-size: 0.83em; + margin: 0.84em auto 1.67em; +} +.ModalBase .PreferencesModal .settingsTab .example h6 { + font-size: 0.67em; + margin: 1.16em auto 2.33em; +} +.ModalBase .PreferencesModal .settingsTab .example h1, +.ModalBase .PreferencesModal .settingsTab .example h2, +.ModalBase .PreferencesModal .settingsTab .example h3, +.ModalBase .PreferencesModal .settingsTab .example h4, +.ModalBase .PreferencesModal .settingsTab .example h5, +.ModalBase .PreferencesModal .settingsTab .example h6 { + font-weight: 700; + line-height: 1.8em; +} +.ModalBase .PreferencesModal .settingsTab .example p { + line-height: 1.8em; + margin: 15px 0 25px; +} +.ModalBase .PreferencesModal .settingsTab .example img { + max-width: 100%; +} +.ModalBase .PreferencesModal .settingsTab .example strong { + font-weight: bold; +} +.ModalBase .PreferencesModal .settingsTab .example em { + font-style: italic; +} +.ModalBase .PreferencesModal .settingsTab .example s { + text-decoration: line-through; +} +.ModalBase .PreferencesModal .settingsTab .example blockquote { + border-left: solid 4px #3fb399; + margin: 15px 0 25px; + padding: 0 25px; +} +.ModalBase .PreferencesModal .settingsTab .example ul { + list-style-type: disc; + padding-left: 35px; + margin-bottom: 35px; +} +.ModalBase .PreferencesModal .settingsTab .example ul li { + display: list-item; + margin: 15px 0; +} +.ModalBase .PreferencesModal .settingsTab .example ul>li>ul { + list-style-type: circle; +} +.ModalBase .PreferencesModal .settingsTab .example ul>li>ul>li>ul { + list-style-type: square; +} +.ModalBase .PreferencesModal .settingsTab .example ol { + list-style-type: decimal; + padding-left: 35px; + margin-bottom: 35px; +} +.ModalBase .PreferencesModal .settingsTab .example ol li { + display: list-item; + margin: 15px 0; +} +.ModalBase .PreferencesModal .settingsTab .example code { + font-family: monospace; + padding: 2px 4px; + border: solid 1px #d0d0d0; + -webkit-border-radius: 4px; + border-radius: 4px; + font-size: 0.9em; + color: #000; + text-decoration: none; + background-color: #f6f6f6; +} +.ModalBase .PreferencesModal .settingsTab .example pre { + padding: 5px; + border: solid 1px #d0d0d0; + -webkit-border-radius: 5px; + border-radius: 5px; + overflow-x: auto; + margin: 15px 0 25px; + background-color: #f6f6f6; +} +.ModalBase .PreferencesModal .settingsTab .example pre>code { + padding: 0; + border: none; + -webkit-border-radius: 0; + border-radius: 0; + color: #000; +} +.ModalBase .PreferencesModal .settingsTab .example table { + width: 100%; + margin: 15px 0 25px; +} +.ModalBase .PreferencesModal .settingsTab .example table thead tr { + background-color: #fff; +} +.ModalBase .PreferencesModal .settingsTab .example table thead th { + border-style: solid; + padding: 15px 5px; + border-width: 1px 0 2px 1px; + border-color: #d0d0d0; +} +.ModalBase .PreferencesModal .settingsTab .example table thead th:last-child { + border-right: solid 1px #d0d0d0; +} +.ModalBase .PreferencesModal .settingsTab .example table tbody tr:nth-child(2n + 1) { + background-color: #f9f9f9; +} +.ModalBase .PreferencesModal .settingsTab .example table tbody tr:nth-child(2n) { + background-color: #fff; +} +.ModalBase .PreferencesModal .settingsTab .example table tbody td { + border-style: solid; + padding: 15px 5px; + border-width: 0 0 1px 1px; + border-color: #d0d0d0; +} +.ModalBase .PreferencesModal .settingsTab .example table tbody td:last-child { + border-right: solid 1px #d0d0d0; +} +.ModalBase .PreferencesModal .aboutTab { + padding-top: 30px; +} +.ModalBase .PreferencesModal .aboutTab .about1 { + margin-bottom: 25px; +} +.ModalBase .PreferencesModal .aboutTab .about1 .logo { + display: block; + margin: 0 auto; +} +.ModalBase .PreferencesModal .aboutTab .about1 .appInfo { + font-size: 1.5em; + text-align: center; +} +.ModalBase .PreferencesModal .aboutTab .about2 { + width: 200px; + margin: 0 auto; +} +.ModalBase .PreferencesModal .aboutTab .about2 .externalLabel { + font-size: 1.2em; + margin-bottom: 15px; +} +.ModalBase .PreferencesModal .aboutTab .about2 .externalList li { + margin-bottom: 15px; +} +.ModalBase .PlanetSettingModal .planetDeleteTab { + padding-top: 65px; +} +.ModalBase .PlanetSettingModal .planetDeleteTab p { + margin-bottom: 25px; +} +.ModalBase .PlanetSettingModal .planetDeleteTab p strong { + color: #2bac8f; + font-size: 1.1em; +} +.ModalBase .PlanetSettingModal .planetDeleteTab input { + border: solid 1px #d0d0d0; + padding: 5px 15px; + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; + margin-right: 5px; + height: 33px; + font-size: 1em; + -webkit-border-radius: 10px; + border-radius: 10px; +} +.ModalBase .PlanetSettingModal .planetDeleteTab input:focus, +.ModalBase .PlanetSettingModal .planetDeleteTab input.focus { + border-color: #3fb399; + outline: none; +} +.ModalBase .PlanetSettingModal .planetDeleteTab .formConfirm { + position: relative; + zoom: 1; + margin-bottom: 15px; +} +.ModalBase .PlanetSettingModal .planetDeleteTab .formConfirm:before, +.ModalBase .PlanetSettingModal .planetDeleteTab .formConfirm:after { + content: ""; + display: table; +} +.ModalBase .PlanetSettingModal .planetDeleteTab .formConfirm:after { + clear: both; +} +.ModalBase .PlanetSettingModal .planetDeleteTab .formConfirm button { + float: right; + border-style: solid; + border-width: 1px; + border-color: #898989; + background-color: transparent; + color: #898989; + padding: 10px 15px; + -webkit-border-radius: 5px; + border-radius: 5px; + font-size: 1em; + margin-left: 5px; +} +.ModalBase .PlanetSettingModal .planetDeleteTab .formConfirm button:hover, +.ModalBase .PlanetSettingModal .planetDeleteTab .formConfirm button.hover, +.ModalBase .PlanetSettingModal .planetDeleteTab .formConfirm button:focus, +.ModalBase .PlanetSettingModal .planetDeleteTab .formConfirm button.focus { + border-color: #454545; + color: #454545; +} +.ModalBase .PlanetSettingModal .planetDeleteTab .formConfirm button:active, +.ModalBase .PlanetSettingModal .planetDeleteTab .formConfirm button.active { + border-color: #39a18a; + background-color: #2bac8f; + color: #fff; +} +.ModalBase .PlanetSettingModal .planetDeleteTab .formConfirm button:disabled, +.ModalBase .PlanetSettingModal .planetDeleteTab .formConfirm button.disabled { + opacity: 0.6; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; + filter: alpha(opacity=60); +} +.ModalBase .PlanetSettingModal .planetDeleteTab .formConfirm .alertInfo, +.ModalBase .PlanetSettingModal .planetDeleteTab .formConfirm .alertSuccess, +.ModalBase .PlanetSettingModal .planetDeleteTab .formConfirm .alertError { + float: right; + padding: 12px 10px; + -webkit-border-radius: 5px; + border-radius: 5px; + width: 320px; + font-size: 1em; + overflow-x: hidden; + white-space: nowrap; + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; +} +.ModalBase .PlanetSettingModal .planetDeleteTab .formConfirm .alertInfo.hide, +.ModalBase .PlanetSettingModal .planetDeleteTab .formConfirm .alertSuccess.hide, +.ModalBase .PlanetSettingModal .planetDeleteTab .formConfirm .alertError.hide { + width: 0; + padding: 12px 0; +} +.ModalBase .PlanetSettingModal .planetDeleteTab .formConfirm .alertInfo { + background-color: #d9edf7; + color: #34708e; +} +.ModalBase .PlanetSettingModal .planetDeleteTab .formConfirm .alertSuccess { + background-color: #e0f0d9; + color: #3e753f; +} +.ModalBase .PlanetSettingModal .planetDeleteTab .formConfirm .alertError { + background-color: #f2dede; + color: #a64444; +} +.ModalBase .TeamSettingsModal .membersTab .memberTable { + width: 100%; + margin-bottom: 25px; +} +.ModalBase .TeamSettingsModal .membersTab .memberTable th { + border-bottom: solid 2px #d0d0d0; +} +.ModalBase .TeamSettingsModal .membersTab .memberTable td { + border-bottom: solid 1px #d0d0d0; + height: 38px; +} +.ModalBase .TeamSettingsModal .membersTab .memberTable td button { + border-style: solid; + border-width: 1px; + border-color: #898989; + background-color: transparent; + color: #898989; + padding: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; +} +.ModalBase .TeamSettingsModal .membersTab .memberTable td button:hover, +.ModalBase .TeamSettingsModal .membersTab .memberTable td button.hover, +.ModalBase .TeamSettingsModal .membersTab .memberTable td button:focus, +.ModalBase .TeamSettingsModal .membersTab .memberTable td button.focus { + border-color: #454545; + color: #454545; +} +.ModalBase .TeamSettingsModal .membersTab .memberTable td button:active, +.ModalBase .TeamSettingsModal .membersTab .memberTable td button.active { + border-color: #39a18a; + background-color: #2bac8f; + color: #fff; +} +.ModalBase .TeamSettingsModal .membersTab .memberTable td button:disabled, +.ModalBase .TeamSettingsModal .membersTab .memberTable td button.disabled { + opacity: 0.6; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; + filter: alpha(opacity=60); +} +.ModalBase .TeamSettingsModal .membersTab .memberTable td .roleSelect { + height: 33px; + border: solid 1px #d0d0d0; + background-color: #fff; +} +.ModalBase .TeamSettingsModal .membersTab .memberTable th, +.ModalBase .TeamSettingsModal .membersTab .memberTable td { + padding: 5px 0; +} +.ModalBase .TeamSettingsModal .membersTab .addMemberForm .formLabel { + margin-bottom: 5px; +} +.ModalBase .TeamSettingsModal .membersTab .addMemberForm .formGroup { + zoom: 1; +} +.ModalBase .TeamSettingsModal .membersTab .addMemberForm .formGroup:before, +.ModalBase .TeamSettingsModal .membersTab .addMemberForm .formGroup:after { + content: ""; + display: table; +} +.ModalBase .TeamSettingsModal .membersTab .addMemberForm .formGroup:after { + clear: both; +} +.ModalBase .TeamSettingsModal .membersTab .addMemberForm .formGroup .userNameSelect { + display: block; + width: 200px; + margin-right: 5px; + float: left; +} +.ModalBase .TeamSettingsModal .membersTab .addMemberForm .formGroup .roleSelect { + display: block; + height: 33px; + border: solid 1px #d0d0d0; + background-color: #fff; + float: left; + margin-right: 5px; +} +.ModalBase .TeamSettingsModal .membersTab .addMemberForm .formGroup .confirmButton { + display: block; + height: 33px; + border-style: solid; + border-width: 1px; + border-color: #898989; + background-color: transparent; + color: #898989; + -webkit-border-radius: 5px; + border-radius: 5px; + float: left; +} +.ModalBase .TeamSettingsModal .membersTab .addMemberForm .formGroup .confirmButton:hover, +.ModalBase .TeamSettingsModal .membersTab .addMemberForm .formGroup .confirmButton.hover, +.ModalBase .TeamSettingsModal .membersTab .addMemberForm .formGroup .confirmButton:focus, +.ModalBase .TeamSettingsModal .membersTab .addMemberForm .formGroup .confirmButton.focus { + border-color: #454545; + color: #454545; +} +.ModalBase .TeamSettingsModal .membersTab .addMemberForm .formGroup .confirmButton:active, +.ModalBase .TeamSettingsModal .membersTab .addMemberForm .formGroup .confirmButton.active { + border-color: #39a18a; + background-color: #2bac8f; + color: #fff; +} +.ModalBase .TeamSettingsModal .membersTab .addMemberForm .formGroup .confirmButton:disabled, +.ModalBase .TeamSettingsModal .membersTab .addMemberForm .formGroup .confirmButton.disabled { + opacity: 0.6; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; + filter: alpha(opacity=60); +} +.ModalBase .LaunchModal .modal-tab { + text-align: center; + margin-bottom: 10px; +} +.ModalBase .LaunchModal .modal-tab .btn-primary, +.ModalBase .LaunchModal .modal-tab .btn-default { + margin: 0; + -webkit-border-radius: 0; + border-radius: 0; + border-width: 1px; + width: 150px; + -webkit-border-radius: 0; + border-radius: 0; +} +.ModalBase .LaunchModal .modal-tab .btn-primary:nth-child(1), +.ModalBase .LaunchModal .modal-tab .btn-default:nth-child(1) { + border-right: solid 1px #d0d0d0; + border-top-left-radius: 5px; + border-bottom-left-radius: 5px; +} +.ModalBase .LaunchModal .modal-tab .btn-primary:nth-child(2), +.ModalBase .LaunchModal .modal-tab .btn-default:nth-child(2) { + border-left: none; + border-top-right-radius: 5px; + border-bottom-right-radius: 5px; +} +.ModalBase .Select .Select-control { + border-color: #d0d0d0; +} +.ModalBase .Select.is-focused .Select-control { + border-color: #3fb399; +} +.ModalBase .Select .Select-menu-outer { + border-color: #d0d0d0; +} +.ModalBase .ace_editor { + -webkit-border-radius: 5px; + border-radius: 5px; + border: solid 1px #d0d0d0; +} +.ModalBase .CodeForm .form-group, +.ModalBase .NoteForm .form-group { + margin-bottom: 10px; +} +.ModalBase .CodeForm textarea.codeDescription { + height: 75px; + font-size: 0.9em; + margin-bottom: 10px; +} +.ModalBase .CodeForm .modeSelect.Select { + display: inline-block; + width: 200px; + height: 37px; +} +.ModalBase .CodeForm .modeSelect.Select .Select-control { + height: 37px; +} +.ModalBase .CodeForm .ace_editor { + height: 258px; +} +.ModalBase .NoteForm .ace_editor { + height: 358px; +} +.ModalBase .NoteForm .previewMode { + position: absolute; + top: 0; + right: 0; + font-size: 0.8em; + line-height: 24px; + padding: 5 15px; + background-color: rgba(0,0,0,0.2); + color: #fff; + border-top-right-radius: 5px; +} +.ModalBase .marked { + height: 360px; + overflow-x: hidden; + overflow-y: auto; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 5px; + border: solid 1px #d0d0d0; + -webkit-border-radius: 5px; + border-radius: 5px; +} +.ModalBase .marked hr { + border-top: none; + border-bottom: solid 1px #d0d0d0; + margin: 15px 0; +} +.ModalBase .marked h1 { + font-size: 2em; + border-bottom: solid 2px #d0d0d0; + margin: 0.33em auto 0.67em; +} +.ModalBase .marked h2 { + font-size: 1.5em; + margin: 0.42em auto 0.83em; +} +.ModalBase .marked h3 { + font-size: 1.17em; + margin: 0.5em auto 1em; +} +.ModalBase .marked h4 { + font-size: 1em; + margin: 0.67em auto 1.33em; +} +.ModalBase .marked h5 { + font-size: 0.83em; + margin: 0.84em auto 1.67em; +} +.ModalBase .marked h6 { + font-size: 0.67em; + margin: 1.16em auto 2.33em; +} +.ModalBase .marked h1, +.ModalBase .marked h2, +.ModalBase .marked h3, +.ModalBase .marked h4, +.ModalBase .marked h5, +.ModalBase .marked h6 { + font-weight: 700; + line-height: 1.8em; +} +.ModalBase .marked p { + line-height: 1.8em; + margin: 15px 0 25px; +} +.ModalBase .marked img { + max-width: 100%; +} +.ModalBase .marked strong { + font-weight: bold; +} +.ModalBase .marked em { + font-style: italic; +} +.ModalBase .marked s { + text-decoration: line-through; +} +.ModalBase .marked blockquote { + border-left: solid 4px #3fb399; + margin: 15px 0 25px; + padding: 0 25px; +} +.ModalBase .marked ul { + list-style-type: disc; + padding-left: 35px; + margin-bottom: 35px; +} +.ModalBase .marked ul li { + display: list-item; + margin: 15px 0; +} +.ModalBase .marked ul>li>ul { + list-style-type: circle; +} +.ModalBase .marked ul>li>ul>li>ul { + list-style-type: square; +} +.ModalBase .marked ol { + list-style-type: decimal; + padding-left: 35px; + margin-bottom: 35px; +} +.ModalBase .marked ol li { + display: list-item; + margin: 15px 0; +} +.ModalBase .marked code { + font-family: monospace; + padding: 2px 4px; + border: solid 1px #d0d0d0; + -webkit-border-radius: 4px; + border-radius: 4px; + font-size: 0.9em; + color: #000; + text-decoration: none; + background-color: #f6f6f6; +} +.ModalBase .marked pre { + padding: 5px; + border: solid 1px #d0d0d0; + -webkit-border-radius: 5px; + border-radius: 5px; + overflow-x: auto; + margin: 15px 0 25px; + background-color: #f6f6f6; +} +.ModalBase .marked pre>code { + padding: 0; + border: none; + -webkit-border-radius: 0; + border-radius: 0; + color: #000; +} +.ModalBase .marked table { + width: 100%; + margin: 15px 0 25px; +} +.ModalBase .marked table thead tr { + background-color: #fff; +} +.ModalBase .marked table thead th { + border-style: solid; + padding: 15px 5px; + border-width: 1px 0 2px 1px; + border-color: #d0d0d0; +} +.ModalBase .marked table thead th:last-child { + border-right: solid 1px #d0d0d0; +} +.ModalBase .marked table tbody tr:nth-child(2n + 1) { + background-color: #f9f9f9; +} +.ModalBase .marked table tbody tr:nth-child(2n) { + background-color: #fff; +} +.ModalBase .marked table tbody td { + border-style: solid; + padding: 15px 5px; + border-width: 0 0 1px 1px; + border-color: #d0d0d0; +} +.ModalBase .marked table tbody td:last-child { + border-right: solid 1px #d0d0d0; +} +.ModalBase .PlanetCreateModal.modal, +.ModalBase .TeamCreateModal.modal, +.ModalBase .AddMemberModal.modal { + padding: 60px 0; +} +.ModalBase .PlanetCreateModal.modal .nameInput, +.ModalBase .TeamCreateModal.modal .nameInput, +.ModalBase .AddMemberModal.modal .nameInput { + width: 80%; + font-size: 1.3em; + margin: 25px auto 15px; + text-align: center; +} +.ModalBase .PlanetCreateModal.modal .userNameSelect, +.ModalBase .TeamCreateModal.modal .userNameSelect, +.ModalBase .AddMemberModal.modal .userNameSelect { + width: 80%; + font-size: 1.3em; + margin: 35px auto; + text-align: center; +} +.ModalBase .PlanetCreateModal.modal .formField, +.ModalBase .TeamCreateModal.modal .formField, +.ModalBase .AddMemberModal.modal .formField { + text-align: center; + margin: 0 auto 25px; +} +.ModalBase .PlanetCreateModal.modal .formField select, +.ModalBase .TeamCreateModal.modal .formField select, +.ModalBase .AddMemberModal.modal .formField select { + display: inline-block; + width: 150px; + height: 33px; + border: solid 1px #d0d0d0; + background-color: #fff; + padding: 0 10px; + margin: 0 15px; +} +.ModalBase .PlanetCreateModal.modal .submitButton, +.ModalBase .TeamCreateModal.modal .submitButton, +.ModalBase .AddMemberModal.modal .submitButton { + display: block; + margin: 0 auto; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 55px; + height: 55px; + -webkit-border-radius: 50%; + border-radius: 50%; + overflow: hidden; + border-style: solid; + border-width: 1px; + border-color: #3fb399; + background-color: transparent; + color: #2bac8f; +} +.ModalBase .PlanetCreateModal.modal .submitButton:hover, +.ModalBase .TeamCreateModal.modal .submitButton:hover, +.ModalBase .AddMemberModal.modal .submitButton:hover, +.ModalBase .PlanetCreateModal.modal .submitButton.hover, +.ModalBase .TeamCreateModal.modal .submitButton.hover, +.ModalBase .AddMemberModal.modal .submitButton.hover, +.ModalBase .PlanetCreateModal.modal .submitButton:focus, +.ModalBase .TeamCreateModal.modal .submitButton:focus, +.ModalBase .AddMemberModal.modal .submitButton:focus, +.ModalBase .PlanetCreateModal.modal .submitButton.focus, +.ModalBase .TeamCreateModal.modal .submitButton.focus, +.ModalBase .AddMemberModal.modal .submitButton.focus { + border-color: #2c7d6b; + color: #1e7864; +} +.ModalBase .PlanetCreateModal.modal .submitButton:active, +.ModalBase .TeamCreateModal.modal .submitButton:active, +.ModalBase .AddMemberModal.modal .submitButton:active, +.ModalBase .PlanetCreateModal.modal .submitButton.active, +.ModalBase .TeamCreateModal.modal .submitButton.active, +.ModalBase .AddMemberModal.modal .submitButton.active { + background-color: #2bac8f; + color: #fff; +} +.ModalBase .PlanetCreateModal.modal .submitButton:disabled, +.ModalBase .TeamCreateModal.modal .submitButton:disabled, +.ModalBase .AddMemberModal.modal .submitButton:disabled, +.ModalBase .PlanetCreateModal.modal .submitButton.disabled, +.ModalBase .TeamCreateModal.modal .submitButton.disabled, +.ModalBase .AddMemberModal.modal .submitButton.disabled { + opacity: 0.6; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; + filter: alpha(opacity=60); +} +.ModalBase .PlanetCreateModal.modal .errorAlert, +.ModalBase .TeamCreateModal.modal .errorAlert, +.ModalBase .AddMemberModal.modal .errorAlert { + background-color: #f2dede; + color: #a64444; + padding: 12px 10px; + -webkit-border-radius: 5px; + border-radius: 5px; + text-align: center; + display: block; + width: 360px; + margin: 0 auto 15px; +} +.ModalBase .ContactModal { + padding: 15px; +} +.ModalBase .ContactModal .contactForm .formField { + width: 100%; + margin-bottom: 10px; +} +.ModalBase .ContactModal .contactForm .formField input, +.ModalBase .ContactModal .contactForm .formField textarea { + display: block; + width: 100%; + border: solid 1px #d0d0d0; + padding: 5px 15px; + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; + -webkit-border-radius: 5px; + border-radius: 5px; +} +.ModalBase .ContactModal .contactForm .formField input:focus, +.ModalBase .ContactModal .contactForm .formField textarea:focus, +.ModalBase .ContactModal .contactForm .formField input.focus, +.ModalBase .ContactModal .contactForm .formField textarea.focus { + border-color: #3fb399; + outline: none; +} +.ModalBase .ContactModal .contactForm .formField input { + height: 33px; + font-size: 1em; +} +.ModalBase .ContactModal .contactForm .formField textarea { + height: 175px; + font-size: 1em; +} +.ModalBase .ContactModal .contactForm .formControl { + zoom: 1; +} +.ModalBase .ContactModal .contactForm .formControl:before, +.ModalBase .ContactModal .contactForm .formControl:after { + content: ""; + display: table; +} +.ModalBase .ContactModal .contactForm .formControl:after { + clear: both; +} +.ModalBase .ContactModal .contactForm .formControl button { + float: right; + border-style: solid; + border-width: 1px; + border-color: #898989; + background-color: transparent; + color: #898989; + height: 44px; + padding: 0 15px; + -webkit-border-radius: 5px; + border-radius: 5px; + margin-left: 5px; + font-size: 1em; +} +.ModalBase .ContactModal .contactForm .formControl button:hover, +.ModalBase .ContactModal .contactForm .formControl button.hover, +.ModalBase .ContactModal .contactForm .formControl button:focus, +.ModalBase .ContactModal .contactForm .formControl button.focus { + border-color: #454545; + color: #454545; +} +.ModalBase .ContactModal .contactForm .formControl button:active, +.ModalBase .ContactModal .contactForm .formControl button.active { + border-color: #39a18a; + background-color: #2bac8f; + color: #fff; +} +.ModalBase .ContactModal .contactForm .formControl button:disabled, +.ModalBase .ContactModal .contactForm .formControl button.disabled { + opacity: 0.6; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; + filter: alpha(opacity=60); +} +.ModalBase .ContactModal .contactForm .formControl button.sendButton { + border-style: solid; + border-width: 1px; + border-color: #3fb399; + background-color: transparent; + color: #2bac8f; +} +.ModalBase .ContactModal .contactForm .formControl button.sendButton:hover, +.ModalBase .ContactModal .contactForm .formControl button.sendButton.hover, +.ModalBase .ContactModal .contactForm .formControl button.sendButton:focus, +.ModalBase .ContactModal .contactForm .formControl button.sendButton.focus { + border-color: #2c7d6b; + color: #1e7864; +} +.ModalBase .ContactModal .contactForm .formControl button.sendButton:active, +.ModalBase .ContactModal .contactForm .formControl button.sendButton.active { + background-color: #2bac8f; + color: #fff; +} +.ModalBase .ContactModal .contactForm .formControl button.sendButton:disabled, +.ModalBase .ContactModal .contactForm .formControl button.sendButton.disabled { + opacity: 0.6; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; + filter: alpha(opacity=60); +} +.ModalBase .ContactModal .confirmation .confirmationMessage { + padding: 35px 0; + text-align: center; + font-size: 1.1em; +} +.ModalBase .ContactModal .confirmation .doneButton { + border-style: solid; + border-width: 1px; + border-color: #898989; + background-color: transparent; + color: #898989; + height: 44px; + padding: 0 35px; + -webkit-border-radius: 5px; + border-radius: 5px; + display: block; + margin: 0 auto 25px; +} +.ModalBase .ContactModal .confirmation .doneButton:hover, +.ModalBase .ContactModal .confirmation .doneButton.hover, +.ModalBase .ContactModal .confirmation .doneButton:focus, +.ModalBase .ContactModal .confirmation .doneButton.focus { + border-color: #454545; + color: #454545; +} +.ModalBase .ContactModal .confirmation .doneButton:active, +.ModalBase .ContactModal .confirmation .doneButton.active { + border-color: #39a18a; + background-color: #2bac8f; + color: #fff; +} +.ModalBase .ContactModal .confirmation .doneButton:disabled, +.ModalBase .ContactModal .confirmation .doneButton.disabled { + opacity: 0.6; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; + filter: alpha(opacity=60); +} +.ModalBase .LogoutModal { + padding: 65px 0 45px; + width: 350px; +} +.ModalBase .LogoutModal .messageLabel { + text-align: center; + font-size: 1.1em; + margin-bottom: 35px; +} +.ModalBase .LogoutModal .formControl { + text-align: center; +} +.ModalBase .LogoutModal .formControl button { + border-style: solid; + border-width: 1px; + border-color: #898989; + background-color: transparent; + color: #898989; + -webkit-border-radius: 5px; + border-radius: 5px; + height: 44px; + margin: 15px 5px; + padding: 0 15px; +} +.ModalBase .LogoutModal .formControl button:hover, +.ModalBase .LogoutModal .formControl button.hover, +.ModalBase .LogoutModal .formControl button:focus, +.ModalBase .LogoutModal .formControl button.focus { + border-color: #454545; + color: #454545; +} +.ModalBase .LogoutModal .formControl button:active, +.ModalBase .LogoutModal .formControl button.active { + border-color: #39a18a; + background-color: #2bac8f; + color: #fff; +} +.ModalBase .LogoutModal .formControl button:disabled, +.ModalBase .LogoutModal .formControl button.disabled { + opacity: 0.6; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; + filter: alpha(opacity=60); +} +.ModalBase .LogoutModal .formControl button.logoutButton { + border-style: solid; + border-width: 1px; + border-color: #3fb399; + background-color: transparent; + color: #2bac8f; +} +.ModalBase .LogoutModal .formControl button.logoutButton:hover, +.ModalBase .LogoutModal .formControl button.logoutButton.hover, +.ModalBase .LogoutModal .formControl button.logoutButton:focus, +.ModalBase .LogoutModal .formControl button.logoutButton.focus { + border-color: #2c7d6b; + color: #1e7864; +} +.ModalBase .LogoutModal .formControl button.logoutButton:active, +.ModalBase .LogoutModal .formControl button.logoutButton.active { + background-color: #2bac8f; + color: #fff; +} +.ModalBase .LogoutModal .formControl button.logoutButton:disabled, +.ModalBase .LogoutModal .formControl button.logoutButton.disabled { + opacity: 0.6; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; + filter: alpha(opacity=60); +} +.ArticleDetail { + position: absolute; + right: 0; + bottom: 0; + top: 60px; + left: 250px; + padding: 10px; +} +.ArticleDetail * { + -webkit-user-select: all; +} +.ArticleDetail .detailInfo { + height: 70px; + width: 100%; + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; + font-size: 12px; + position: relative; +} +.ArticleDetail .detailInfo .left { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 120px; +} +.ArticleDetail .detailInfo .right { + position: absolute; + top: 0; + right: 0; +} +.ArticleDetail .detailBody { + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 70px; + overflow-x: hidden; + overflow-y: auto; +} +.ArticleDetail .detailBody .detailPanel { + position: absolute; + top: 0; + left: 10px; + right: 10px; + bottom: 10px; + background-color: #fff; + -webkit-border-radius: 5px; + border-radius: 5px; + border: solid 1px #d0d0d0; +} +.ArticleDetail .detailBody .detailPanel>.header { + position: absolute; + top: 0; + left: 0; + right: 0; + height: 60px; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview { + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 60px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 5px 15px; + border-top: solid 1px #d0d0d0; + overflow-y: auto; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview hr { + border-top: none; + border-bottom: solid 1px #d0d0d0; + margin: 15px 0; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview h1 { + font-size: 2em; + border-bottom: solid 2px #d0d0d0; + margin: 0.33em auto 0.67em; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview h2 { + font-size: 1.5em; + margin: 0.42em auto 0.83em; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview h3 { + font-size: 1.17em; + margin: 0.5em auto 1em; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview h4 { + font-size: 1em; + margin: 0.67em auto 1.33em; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview h5 { + font-size: 0.83em; + margin: 0.84em auto 1.67em; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview h6 { + font-size: 0.67em; + margin: 1.16em auto 2.33em; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview h1, +.ArticleDetail .detailBody .detailPanel .MarkdownPreview h2, +.ArticleDetail .detailBody .detailPanel .MarkdownPreview h3, +.ArticleDetail .detailBody .detailPanel .MarkdownPreview h4, +.ArticleDetail .detailBody .detailPanel .MarkdownPreview h5, +.ArticleDetail .detailBody .detailPanel .MarkdownPreview h6 { + font-weight: 700; + line-height: 1.8em; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview p { + line-height: 1.8em; + margin: 15px 0 25px; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview img { + max-width: 100%; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview strong { + font-weight: bold; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview em { + font-style: italic; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview s { + text-decoration: line-through; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview blockquote { + border-left: solid 4px #3fb399; + margin: 15px 0 25px; + padding: 0 25px; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview ul { + list-style-type: disc; + padding-left: 35px; + margin-bottom: 35px; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview ul li { + display: list-item; + margin: 15px 0; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview ul>li>ul { + list-style-type: circle; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview ul>li>ul>li>ul { + list-style-type: square; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview ol { + list-style-type: decimal; + padding-left: 35px; + margin-bottom: 35px; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview ol li { + display: list-item; + margin: 15px 0; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview code { + font-family: monospace; + padding: 2px 4px; + border: solid 1px #d0d0d0; + -webkit-border-radius: 4px; + border-radius: 4px; + font-size: 0.9em; + color: #000; + text-decoration: none; + background-color: #f6f6f6; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview pre { + padding: 5px; + border: solid 1px #d0d0d0; + -webkit-border-radius: 5px; + border-radius: 5px; + overflow-x: auto; + margin: 15px 0 25px; + background-color: #f6f6f6; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview pre>code { + padding: 0; + border: none; + -webkit-border-radius: 0; + border-radius: 0; + color: #000; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview table { + width: 100%; + margin: 15px 0 25px; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview table thead tr { + background-color: #fff; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview table thead th { + border-style: solid; + padding: 15px 5px; + border-width: 1px 0 2px 1px; + border-color: #d0d0d0; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview table thead th:last-child { + border-right: solid 1px #d0d0d0; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview table tbody tr:nth-child(2n + 1) { + background-color: #f9f9f9; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview table tbody tr:nth-child(2n) { + background-color: #fff; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview table tbody td { + border-style: solid; + padding: 15px 5px; + border-width: 0 0 1px 1px; + border-color: #d0d0d0; +} +.ArticleDetail .detailBody .detailPanel .MarkdownPreview table tbody td:last-child { + border-right: solid 1px #d0d0d0; +} +.ArticleDetail .detailBody .detailPanel .CodeEditor { + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 60px; + border-top: solid 1px #d0d0d0; + min-height: 300px; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; +} +.ArticleDetail.edit .detailInfo .left .Select .Select-control { + border: none; + background-color: transparent; +} +.ArticleDetail.edit .detailInfo .left .folder.Select { + width: 150px; +} +.ArticleDetail.edit .detailInfo .left .folder.Select .Select-control:hover { + background-color: #f2f2f2; +} +.ArticleDetail.edit .detailInfo .left .folder.Select.is-focused .Select-control { + background-color: #fff; +} +.ArticleDetail.edit .detailInfo .left .tags.Select .Select-control { + white-space: nowrap; + overflow-x: auto; + position: relative; +} +.ArticleDetail.edit .detailInfo .left .tags.Select .Select-control .Select-arrow-zone, +.ArticleDetail.edit .detailInfo .left .tags.Select .Select-control .Select-arrow { + display: none; +} +.ArticleDetail.edit .detailInfo .right button { + cursor: pointer; + height: 33px; + width: 55px; + margin-left: 5px; + font-size: 14px; + color: #888; + background-color: #f2f2f2; + border: solid 1px #d0d0d0; + -webkit-border-radius: 5px; + border-radius: 5px; +} +.ArticleDetail.edit .detailInfo .right button:hover { + background-color: #fff; +} +.ArticleDetail.edit .detailInfo .right button.primary { + border: none; + background-color: #2bac8f; + color: #fff; +} +.ArticleDetail.edit .detailInfo .right button.primary:hover { + color: #fff; + background-color: #31c4a3; +} +.ArticleDetail.edit .detailBody .detailPanel>.header .mode { + position: absolute; + top: 0; + bottom: 0; + right: 0; + display: block; + height: 33px; + margin-top: 12px; + width: 120px; + margin-right: 15px; +} +.ArticleDetail.edit .detailBody .detailPanel>.header .title { + position: absolute; + left: 0; + top: 0; + bottom: 0; + right: 120px; + padding: 0 15px; +} +.ArticleDetail.edit .detailBody .detailPanel>.header .title input { + width: 100%; + border: none; + background-color: transparent; + line-height: 60px; + font-size: 32px; + font-weight: bold; + outline: none; +} +.ArticleDetail.show .detailInfo .left { + right: 99px; +} +.ArticleDetail.show .detailInfo .left .info { + padding: 5px; + white-space: nowrap; + overflow: hidden; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; +} +.ArticleDetail.show .detailInfo .left .tags { + padding: 10px 10px 5px; + color: articleItemColor; +} +.ArticleDetail.show .detailInfo .left .tags a { + background-color: #2bac8f; + color: #fff; + -webkit-border-radius: 2px; + border-radius: 2px; + padding: 1.5px 5px; + margin: 2px; + font-size: 10px; + opacity: 0.8; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"; + filter: alpha(opacity=80); +} +.ArticleDetail.show .detailInfo .left .tags a:hover { + opacity: 1; + -ms-filter: none; + filter: none; +} +.ArticleDetail.show .detailInfo .left .tags span.noTags { + color: #999; +} +.ArticleDetail.show .detailInfo .right button { + cursor: pointer; + height: 33px; + width: 33px; + border: none; + font-size: 18px; + color: #888; + background-color: transparent; + padding: 0; +} +.ArticleDetail.show .detailInfo .right button:hover { + color: inherit; +} +.ArticleDetail.show .detailBody .detailPanel>.header .mode { + display: block; + line-height: 60px; + width: 45px; + height: 60px; + font-size: 18px; + text-align: center; +} +.ArticleDetail.show .detailBody .detailPanel>.header .title { + position: absolute; + top: 0; + bottom: 0; + left: 45px; + right: 15px; + font-size: 32px; + line-height: 60px; + font-weight: bold; + white-space: nowrap; + overflow-x: auto; + overflow-y: hidden; +} +.ArticleList { + position: absolute; + left: 0; + bottom: 0; + top: 60px; + width: 250px; + border-right: solid 1px #a6a6a6; +} +.ArticleList>ul { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + overflow-y: auto; + -webkit-user-select: none; + cursor: default; +} +.ArticleList>ul li .articleItem { + border: solid 2px transparent; + position: relative; + height: 88px; + width: 100%; + cursor: pointer; + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; + background-color: #fff; + padding: 0 10px; + font-size: 12px; +} +.ArticleList>ul li .articleItem .top { + zoom: 1; + line-height: 20px; + padding: 5px 0; + color: #777; +} +.ArticleList>ul li .articleItem .top:before, +.ArticleList>ul li .articleItem .top:after { + content: ""; + display: table; +} +.ArticleList>ul li .articleItem .top:after { + clear: both; +} +.ArticleList>ul li .articleItem .top .profileImage { + vertical-align: middle; +} +.ArticleList>ul li .articleItem .top .updatedAt { + float: right; + line-height: 20px; +} +.ArticleList>ul li .articleItem .middle { + zoom: 1; + padding: 3px 0 7px; + font-size: 16px; +} +.ArticleList>ul li .articleItem .middle:before, +.ArticleList>ul li .articleItem .middle:after { + content: ""; + display: table; +} +.ArticleList>ul li .articleItem .middle:after { + clear: both; +} +.ArticleList>ul li .articleItem .middle .mode { + float: left; + font-size: 12px; + line-height: 16px; +} +.ArticleList>ul li .articleItem .middle .title { + float: left; + white-space: nowrap; + overflow: hidden; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; + padding: 0 5px; +} +.ArticleList>ul li .articleItem .bottom { + padding: 5px 0; + overflow-x: auto; + white-space: nowrap; +} +.ArticleList>ul li .articleItem .bottom .tags { + color: #777; +} +.ArticleList>ul li .articleItem .bottom .tags a { + background-color: #2bac8f; + color: #fff; + -webkit-border-radius: 2px; + border-radius: 2px; + padding: 1.5px 5px; + margin: 2px; + font-size: 10px; + opacity: 0.8; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"; + filter: alpha(opacity=80); +} +.ArticleList>ul li .articleItem .bottom .tags a:hover { + opacity: 1; + -ms-filter: none; + filter: none; +} +.ArticleList>ul li .articleItem:hover, +.ArticleList>ul li .articleItem.hover { + background-color: #f2f2f2; +} +.ArticleList>ul li .articleItem:hover:active, +.ArticleList>ul li .articleItem.hover:active, +.ArticleList>ul li .articleItem:hover.active, +.ArticleList>ul li .articleItem.hover.active { + background-color: #fff; +} +.ArticleList>ul li .articleItem:active, +.ArticleList>ul li .articleItem.active { + border-color: #3fb399; +} +.ArticleList>ul li .divider { + border-bottom: solid 1px #d0d0d0; +} +.Select { + position: relative; +} +.Select-control { + position: relative; + overflow: hidden; + background-color: #fff; + border: 1px solid #ccc; + border-color: #d9d9d9 #ccc #b3b3b3; + -webkit-border-radius: 4px; + border-radius: 4px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + color: #333; + cursor: default; + outline: none; + padding: 8px 52px 8px 10px; + -webkit-transition: all 200ms ease; + -moz-transition: all 200ms ease; + -o-transition: all 200ms ease; + -ms-transition: all 200ms ease; + transition: all 200ms ease; +} +.is-searchable.is-open > .Select-control { + cursor: text; +} +.is-open > .Select-control { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + background: #fff; + border-color: #b3b3b3 #ccc #d9d9d9; +} +.is-open > .Select-control > .Select-arrow { + border-color: transparent transparent #999; + border-width: 0 5px 5px; +} +.is-searchable.is-focused:not(.is-open) > .Select-control { + cursor: text; +} +.Select-placeholder { + color: #aaa; + padding: 8px 52px 8px 10px; + position: absolute; + top: 0; + left: 0; + right: -15px; + max-width: 100%; + overflow: hidden; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; + white-space: nowrap; +} +.has-value > .Select-control > .Select-placeholder { + color: #333; +} +.Select-input > input { + cursor: default; + background: none transparent; + -webkit-box-shadow: none; + box-shadow: none; + height: auto; + border: 0 none; + font-family: inherit; + font-size: inherit; + margin: 0; + padding: 0; + outline: none; + display: inline-block; + -webkit-appearance: none; +} +.is-focused .Select-input > input { + cursor: text; +} +.Select-control:not(.is-searchable) > .Select-input { + outline: none; +} +.Select-loading { + -webkit-animation: Select-animation-spin 400ms infinite linear; + -o-animation: Select-animation-spin 400ms infinite linear; + -webkit-animation: Select-animation-spin 400ms infinite linear; + -moz-animation: Select-animation-spin 400ms infinite linear; + -o-animation: Select-animation-spin 400ms infinite linear; + -ms-animation: Select-animation-spin 400ms infinite linear; + animation: Select-animation-spin 400ms infinite linear; + width: 16px; + height: 16px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-border-radius: 50%; + border-radius: 50%; + border: 2px solid #ccc; + border-right-color: #333; + display: inline-block; + position: relative; + margin-top: -8px; + position: absolute; + right: 30px; + top: 50%; +} +.has-value > .Select-control > .Select-loading { + right: 46px; +} +.Select-clear { + color: #999; + cursor: pointer; + display: inline-block; + font-size: 16px; + padding: 6px 10px; + position: absolute; + right: 17px; + top: 0; +} +.Select-clear:hover { + color: #c0392b; +} +.Select-clear > span { + font-size: 1.1em; +} +.Select-arrow-zone { + content: " "; + display: block; + position: absolute; + right: 0; + top: 0; + bottom: 0; + width: 30px; + cursor: pointer; +} +.Select-arrow { + border-color: #999 transparent transparent; + border-style: solid; + border-width: 5px 5px 0; + content: " "; + display: block; + height: 0; + margin-top: -ceil(2.5px); + position: absolute; + right: 10px; + top: 14px; + width: 0; + cursor: pointer; +} +.Select-menu-outer { + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + background-color: #fff; + border: 1px solid #ccc; + border-top-color: #e6e6e6; + -webkit-box-shadow: 0 1px 0 rgba(0,0,0,0.06); + box-shadow: 0 1px 0 rgba(0,0,0,0.06); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + margin-top: -1px; + max-height: 200px; + position: absolute; + top: 100%; + width: 100%; + z-index: 1000; + -webkit-overflow-scrolling: touch; +} +.Select-menu { + max-height: 198px; + overflow-y: auto; +} +.Select-option { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + color: #666; + cursor: pointer; + display: block; + padding: 8px 10px; +} +.Select-option:last-child { + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.Select-option.is-focused { + background-color: #f2f9fc; + color: #333; +} +.Select-option.is-disabled { + color: #ccc; + cursor: not-allowed; +} +.Select-noresults { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + color: #999; + cursor: default; + display: block; + padding: 8px 10px; +} +.Select.is-multi .Select-control { + padding: 2px 52px 2px 3px; +} +.Select.is-multi .Select-input { + vertical-align: middle; + border: 1px solid transparent; + margin: 2px; + padding: 3px 0; +} +.Select-item { + background-color: #2bac8f; + -webkit-border-radius: 2px; + border-radius: 2px; + color: #fff; + display: inline-block; + font-size: 1em; + margin: 2px; +} +.Select-item-icon, +.Select-item-label { + display: inline-block; + vertical-align: middle; +} +.Select-item-label { + cursor: default; + border-bottom-right-radius: 2px; + border-top-right-radius: 2px; + padding: 3px 5px; +} +.Select-item-label .Select-item-label__a { + color: #fff; + cursor: #fff; +} +.Select-item-icon { + cursor: pointer; + border-bottom-left-radius: 2px; + border-top-left-radius: 2px; + border-right: 1px solid #279b81; + padding: 2px 5px 4px; +} +.Select-item-icon:hover, +.Select-item-icon:focus { + background-color: #31c4a3; +} +.Select-item-icon:active { + background-color: #c9e6f2; +} +.Select.is-multi.is-disabled .Select-item { + background-color: #f2f2f2; + border: 1px solid #d9d9d9; + color: #888; +} +.Select.is-multi.is-disabled .Select-item-icon { + cursor: not-allowed; + border-right: 1px solid #d9d9d9; +} +.Select.is-multi.is-disabled .Select-item-icon:hover, +.Select.is-multi.is-disabled .Select-item-icon:focus, +.Select.is-multi.is-disabled .Select-item-icon:active { + background-color: #f2f2f2; +} +@-webkit-keyframes Select-animation-spin { + to { + -webkit-transform: rotate(1turn); + } +} +@-moz-keyframes Select-animation-spin { + to { + -webkit-transform: rotate(1turn); + -moz-transform: rotate(1turn); + -o-transform: rotate(1turn); + -ms-transform: rotate(1turn); + transform: rotate(1turn); + } +} +@-webkit-keyframes Select-animation-spin { + to { + -webkit-transform: rotate(1turn); + -moz-transform: rotate(1turn); + -o-transform: rotate(1turn); + -ms-transform: rotate(1turn); + transform: rotate(1turn); + } +} +@-o-keyframes Select-animation-spin { + to { + -webkit-transform: rotate(1turn); + -moz-transform: rotate(1turn); + -o-transform: rotate(1turn); + -ms-transform: rotate(1turn); + transform: rotate(1turn); + } +} +@keyframes Select-animation-spin { + to { + -webkit-transform: rotate(1turn); + -moz-transform: rotate(1turn); + -o-transform: rotate(1turn); + -ms-transform: rotate(1turn); + transform: rotate(1turn); + } +} +.TopBar { + position: absolute; + top: 0; + left: 0; + right: 0; + height: 60px; + border-bottom: solid 1px #d0d0d0; + -webkit-user-select: none; + cursor: default; +} +.TopBar .left { + float: left; +} +.TopBar .left .search { + position: absolute; + top: 13.5px; + left: 15px; + height: 33px; +} +.TopBar .left .search i.fa { + position: absolute; + line-height: 33px; + z-index: 1; + width: 33px; + text-align: center; +} +.TopBar .left .search input.searchInput { + position: absolute; + top: 0; + left: 0; + background-color: #fff; + border: solid 1px #d0d0d0; + padding: 5px 15px; + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; + width: 350px; + padding-left: 30px; + -webkit-border-radius: 16.5px; + border-radius: 16.5px; + font-size: 14px; + height: 33px; + line-height: 33px; + outline: none; +} +.TopBar .left .search input.searchInput:focus, +.TopBar .left .search input.searchInput.focus { + border-color: #3fb399; + outline: none; +} +.TopBar .left .search input.searchInput:focus { + border-color: #2bac8f; +} +.TopBar .right { + float: right; +} +.TopBar .right .logo>img { + margin-top: 7px; + margin-right: 15px; +} +.TopBar .right .logo .tooltip { + position: fixed; + z-index: 500; + background-color: rgba(31,31,31,0.8); + color: #fff; + padding: 10px; + font-size: 12px; + line-height: 12px; + -webkit-border-radius: 5px; + border-radius: 5px; + white-space: nowrap; + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; + pointer-events: none; + right: 5px; +} +.TopBar .right .logo:hover .tooltip { + opacity: 1; + -ms-filter: none; + filter: none; +} +.HomeContainer .HomeNavigator { + -webkit-user-select: none; + cursor: default; + background-color: #1b1c1c; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 60px; + text-align: center; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.HomeContainer .HomeNavigator ul.userList { + margin-top: 25px; +} +.HomeContainer .HomeNavigator ul.userList>li .shortCut { + margin-top: 5px; + font-size: 0.8em; + color: #ddd; +} +.HomeContainer .HomeNavigator ul.userList>li a { + display: block; + width: 44px; + height: 44px; + margin: 0 auto; + text-align: center; + background-color: #bebebe; + text-decoration: none; + color: #979797; + line-height: 44px; + font-size: 1.1em; + cursor: pointer; + -webkit-border-radius: 50%; + border-radius: 50%; + overflow: hidden; + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; +} +.HomeContainer .HomeNavigator ul.userList>li a img { + width: 44px; + height: 44px; +} +.HomeContainer .HomeNavigator ul.userList>li a:hover, +.HomeContainer .HomeNavigator ul.userList>li a.active { + background-color: #fff; + color: #4d4d4d; +} +.HomeContainer .HomeNavigator ul.userList>li a .userTooltip { + position: absolute; + z-index: 500; + background-color: rgba(31,31,31,0.8); + color: #fff; + padding: 10px; + line-height: 1em; + -webkit-border-radius: 5px; + border-radius: 5px; + margin-top: -52px; + margin-left: 52px; + white-space: nowrap; + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; + pointer-events: none; +} +.HomeContainer .HomeNavigator ul.userList>li a:hover .userTooltip { + opacity: 1; + -ms-filter: none; + filter: none; +} +.HomeContainer .HomeNavigator button.newTeamButton { + display: block; + margin: 0 auto; + width: 30px; + height: 30px; + -webkit-border-radius: 50%; + border-radius: 50%; + overflow: hidden; + border: solid 1px #898989; + color: #898989; + text-align: center; + background-image: none; + background-color: transparent; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + position: absolute; + left: 0; + bottom: 0; + right: 0; + bottom: 15px; +} +.HomeContainer .HomeNavigator button.newTeamButton:hover, +.HomeContainer .HomeNavigator button.newTeamButton.hover, +.HomeContainer .HomeNavigator button.newTeamButton:focus, +.HomeContainer .HomeNavigator button.newTeamButton.focus { + border-color: #454545; + color: #454545; +} +.HomeContainer .HomeNavigator button.newTeamButton:active, +.HomeContainer .HomeNavigator button.newTeamButton.active { + border-color: #39a18a; + background-color: #2bac8f; + color: #fff; +} +.HomeContainer .HomeNavigator button.newTeamButton .tooltip { + position: fixed; + z-index: 500; + background-color: rgba(31,31,31,0.8); + color: #fff; + padding: 10px; + font-size: 12px; + line-height: 12px; + -webkit-border-radius: 5px; + border-radius: 5px; + white-space: nowrap; + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; + pointer-events: none; + margin-top: -22px; + margin-left: 33px; + font-size: 14px; +} +.HomeContainer .HomeNavigator button.newTeamButton:hover .tooltip { + opacity: 1; + -ms-filter: none; + filter: none; +} +.LoginContainer, +.SignupContainer { + margin: 0 auto; + padding: 105px 15px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + color: #888; +} +.LoginContainer .logo, +.SignupContainer .logo { + width: 150px; + height: 150px; + display: block; + margin: 0 auto; +} +.LoginContainer .authNavigator, +.SignupContainer .authNavigator { + margin: 15px 0 25px; +} +.LoginContainer .authNavigator a, +.SignupContainer .authNavigator a { + font-size: 1.5em; + text-decoration: none; + color: #888; +} +.LoginContainer .authNavigator a:hover, +.SignupContainer .authNavigator a:hover, +.LoginContainer .authNavigator a.hover, +.SignupContainer .authNavigator a.hover, +.LoginContainer .authNavigator a:active, +.SignupContainer .authNavigator a:active, +.LoginContainer .authNavigator a.active, +.SignupContainer .authNavigator a.active { + color: #2bac8f; +} +.LoginContainer .socialControl, +.SignupContainer .socialControl { + text-align: center; + margin: 25px 0; +} +.LoginContainer .socialControl p, +.SignupContainer .socialControl p { + margin-bottom: 25px; +} +.LoginContainer .socialControl .facebookBtn, +.SignupContainer .socialControl .facebookBtn, +.LoginContainer .socialControl .githubBtn, +.SignupContainer .socialControl .githubBtn { + margin: 0 45px; + width: 50px; + height: 50px; + line-height: 50px; + font-size: 25px; + text-align: center; + background-image: none; + color: #fff; + border: none; + -webkit-border-radius: 25px; + border-radius: 25px; + cursor: pointer; +} +.LoginContainer .socialControl .facebookBtn, +.SignupContainer .socialControl .facebookBtn { + background-color: #3b5998; +} +.LoginContainer .socialControl .facebookBtn:hover, +.SignupContainer .socialControl .facebookBtn:hover, +.LoginContainer .socialControl .facebookBtn.hover, +.SignupContainer .socialControl .facebookBtn.hover { + background-color: #5d7dc0; +} +.LoginContainer .socialControl .githubBtn, +.SignupContainer .socialControl .githubBtn { + background-color: #201f1f; + font-size: 30px; + line-height: 30px; +} +.LoginContainer .socialControl .githubBtn:hover, +.SignupContainer .socialControl .githubBtn:hover, +.LoginContainer .socialControl .githubBtn.hover, +.SignupContainer .socialControl .githubBtn.hover { + background-color: #595656; +} +.LoginContainer .divider .dividerLabel, +.SignupContainer .divider .dividerLabel { + text-align: center; + position: relative; + top: -27px; + font-size: 1.3em; + background-color: #fff; + margin: 0 auto; + width: 50px; +} +.LoginContainer form, +.SignupContainer form { + width: 400px; + margin: 0 auto 45px; +} +.LoginContainer form .alertInfo, +.SignupContainer form .alertInfo, +.LoginContainer form .alertError, +.SignupContainer form .alertError { + margin-top: 15px; + margin-bottom: 15px; + padding: 10px; + -webkit-border-radius: 5px; + border-radius: 5px; + line-height: 1.6; + text-align: center; +} +.LoginContainer form .alertInfo, +.SignupContainer form .alertInfo { + background-color: #d9edf7; + color: #34708e; +} +.LoginContainer form .alertError, +.SignupContainer form .alertError { + background-color: #f2dede; + color: #a64444; +} +.LoginContainer form div.formField input, +.SignupContainer form div.formField input { + border: none; + border-bottom: 1px solid #d0d0d0; + padding: 5px 15px; + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; + height: 33px; + width: 100%; + margin-bottom: 10px; + text-align: center; + font-size: 1.1em; +} +.LoginContainer form div.formField input:focus, +.SignupContainer form div.formField input:focus, +.LoginContainer form div.formField input.focus, +.SignupContainer form div.formField input.focus { + border-bottom: 1px solid #3fb399; + outline: none; +} +.LoginContainer form div.formField:last-child, +.SignupContainer form div.formField:last-child { + margin-top: 15px; +} +.LoginContainer form div.formField button.logInButton, +.SignupContainer form div.formField button.logInButton { + border-style: solid; + border-width: 1px; + border-color: #3fb399; + background-color: transparent; + color: #2bac8f; + height: 44px; + -webkit-border-radius: 22px; + border-radius: 22px; + display: block; + width: 200px; + font-size: 1em; + margin: 0 auto; +} +.LoginContainer form div.formField button.logInButton:hover, +.SignupContainer form div.formField button.logInButton:hover, +.LoginContainer form div.formField button.logInButton.hover, +.SignupContainer form div.formField button.logInButton.hover, +.LoginContainer form div.formField button.logInButton:focus, +.SignupContainer form div.formField button.logInButton:focus, +.LoginContainer form div.formField button.logInButton.focus, +.SignupContainer form div.formField button.logInButton.focus { + border-color: #2c7d6b; + color: #1e7864; +} +.LoginContainer form div.formField button.logInButton:active, +.SignupContainer form div.formField button.logInButton:active, +.LoginContainer form div.formField button.logInButton.active, +.SignupContainer form div.formField button.logInButton.active { + background-color: #2bac8f; + color: #fff; +} +.LoginContainer form div.formField button.logInButton:disabled, +.SignupContainer form div.formField button.logInButton:disabled, +.LoginContainer form div.formField button.logInButton.disabled, +.SignupContainer form div.formField button.logInButton.disabled { + opacity: 0.6; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; + filter: alpha(opacity=60); +} +.LoginContainer p.alert, +.SignupContainer p.alert { + text-align: center; + font-size: 0.8em; +} +.PlanetContainer { + position: absolute; + top: 0; + bottom: 0; + right: 0; + left: 0; +} +.PlanetContainer .tags { + white-space: nowrap; + overflow-x: auto; +} +.PlanetContainer .tags a { + margin: 0 2px; + text-decoration: underline; + cursor: pointer; + font-size: 0.95em; +} +.PlanetContainer .tags a.noTag { + color: #888; + font-size: 0.8em; +} +.PlanetHeader { + position: absolute; + left: 0; + right: 0; + top: 0; + overflow-y: hidden; + height: 55px; + background-color: #fff; + border-bottom: solid 1px #d0d0d0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 5px 15px; + zoom: 1; +} +.PlanetHeader:before, +.PlanetHeader:after { + content: ""; + display: table; +} +.PlanetHeader:after { + clear: both; +} +.PlanetHeader .headerLabel { + -webkit-user-select: none; + cursor: default; + position: absolute; + top: 0; + left: 0; + bottom: 0; + overflow: hidden; + display: inline-block; + width: 200px; +} +.PlanetHeader .headerLabel .userName { + position: absolute; + left: 15px; + top: 30px; + width: 140px; + font-size: 1em; + color: #4d4d4d; + text-decoration: none; +} +.PlanetHeader .headerLabel .userName:hover { + color: #454545; + text-decoration: underline; +} +.PlanetHeader .headerLabel .planetName { + position: absolute; + top: 5px; + left: 10px; + width: 145px; + font-size: 1.6em; + color: #2bac8f; + overflow: hidden; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; + white-space: nowrap; +} +.PlanetHeader .headerLabel .planetName:hover { + color: #2c7d6b; +} +.PlanetHeader .headerLabel .private { + position: absolute; + top: 12px; + right: 38px; + width: 33px; + height: 33px; + line-height: 33px; + text-align: center; + color: inactiveColor; +} +.PlanetHeader .headerLabel .private:hover { + color: #4d4d4d; +} +.PlanetHeader .headerLabel .private .tooltip { + position: fixed; + z-index: 500; + background-color: rgba(31,31,31,0.8); + color: #fff; + padding: 10px; + font-size: 12px; + line-height: 12px; + -webkit-border-radius: 5px; + border-radius: 5px; + white-space: nowrap; + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; + pointer-events: none; + margin-left: -30px; +} +.PlanetHeader .headerLabel .private:hover .tooltip { + opacity: 1; + -ms-filter: none; + filter: none; +} +.PlanetHeader .headerLabel .planetSettingButton { + position: absolute; + top: 15px; + right: 5px; + font-size: 0.8em; + border-style: solid; + border-width: 1px; + border-color: #898989; + background-color: transparent; + color: #898989; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-border-radius: 50%; + border-radius: 50%; + overflow: hidden; + width: 26px; + height: 26px; + text-align: center; + cursor: pointer; + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; +} +.PlanetHeader .headerLabel .planetSettingButton:hover, +.PlanetHeader .headerLabel .planetSettingButton.hover, +.PlanetHeader .headerLabel .planetSettingButton:focus, +.PlanetHeader .headerLabel .planetSettingButton.focus { + border-color: #454545; + color: #454545; +} +.PlanetHeader .headerLabel .planetSettingButton:active, +.PlanetHeader .headerLabel .planetSettingButton.active { + border-color: #39a18a; + background-color: #2bac8f; + color: #fff; +} +.PlanetHeader .headerLabel .planetSettingButton:disabled, +.PlanetHeader .headerLabel .planetSettingButton.disabled { + opacity: 0.6; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; + filter: alpha(opacity=60); +} +.PlanetHeader .headerLabel .planetSettingButton:focus, +.PlanetHeader .headerLabel .planetSettingButton.focus { + outline: none; +} +.PlanetHeader .headerLabel .planetSettingButton .tooltip { + position: fixed; + z-index: 500; + background-color: rgba(31,31,31,0.8); + color: #fff; + padding: 10px; + font-size: 12px; + line-height: 12px; + -webkit-border-radius: 5px; + border-radius: 5px; + white-space: nowrap; + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; + pointer-events: none; + margin-top: 11px; + margin-left: -36px; +} +.PlanetHeader .headerLabel .planetSettingButton:hover .tooltip { + opacity: 1; + -ms-filter: none; + filter: none; +} +.PlanetHeader .headerControl { + -webkit-user-select: none; + cursor: default; + position: absolute; + top: 0; + bottom: 0; + right: 0; + left: 200px; +} +.PlanetHeader .headerControl .searchInput { + display: block; + position: absolute; + top: 12px; + left: 0; +} +.PlanetHeader .headerControl .searchInput input { + padding-left: 32px; + width: 300px; +} +.PlanetHeader .headerControl .searchInput .fa { + position: absolute; + top: 8px; + left: 12px; + color: #888; +} +.PlanetHeader .headerControl .refreshButton { + display: block; + position: absolute; + top: 15px; + right: 55px; + width: 26px; + height: 26px; + font-size: 0.8em; + border-style: solid; + border-width: 1px; + border-color: #898989; + background-color: transparent; + color: #898989; + -webkit-border-radius: 50%; + border-radius: 50%; + overflow: hidden; + text-align: center; + cursor: pointer; + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; +} +.PlanetHeader .headerControl .refreshButton:hover, +.PlanetHeader .headerControl .refreshButton.hover, +.PlanetHeader .headerControl .refreshButton:focus, +.PlanetHeader .headerControl .refreshButton.focus { + border-color: #454545; + color: #454545; +} +.PlanetHeader .headerControl .refreshButton:active, +.PlanetHeader .headerControl .refreshButton.active { + border-color: #39a18a; + background-color: #2bac8f; + color: #fff; +} +.PlanetHeader .headerControl .refreshButton:disabled, +.PlanetHeader .headerControl .refreshButton.disabled { + opacity: 0.6; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; + filter: alpha(opacity=60); +} +.PlanetHeader .headerControl .refreshButton:focus, +.PlanetHeader .headerControl .refreshButton.focus { + outline: none; +} +.PlanetHeader .headerControl .refreshButton .tooltip { + position: fixed; + z-index: 500; + background-color: rgba(31,31,31,0.8); + color: #fff; + padding: 10px; + font-size: 12px; + line-height: 12px; + -webkit-border-radius: 5px; + border-radius: 5px; + white-space: nowrap; + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; + pointer-events: none; + margin-top: 11px; + margin-left: -39px; +} +.PlanetHeader .headerControl .refreshButton:hover .tooltip { + opacity: 1; + -ms-filter: none; + filter: none; +} +.PlanetHeader .headerControl .logo { + display: block; + position: absolute; + top: 4px; + right: 10px; + cursor: pointer; +} +.PlanetHeader .headerControl .logo img { + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; + opacity: 0.9; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=90)"; + filter: alpha(opacity=90); +} +.PlanetHeader .headerControl .logo:hover img, +.PlanetHeader .headerControl .logo:hover .tooltip { + opacity: 1; + -ms-filter: none; + filter: none; +} +.PlanetHeader .headerControl .logo .tooltip { + position: fixed; + z-index: 500; + background-color: rgba(31,31,31,0.8); + color: #fff; + padding: 10px; + font-size: 12px; + line-height: 12px; + -webkit-border-radius: 5px; + border-radius: 5px; + white-space: nowrap; + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; + pointer-events: none; + margin-top: -5px; + margin-left: -67px; +} +.PlanetNavigator { + position: absolute; + bottom: 0; + left: 0; + -webkit-user-select: none; + cursor: default; + top: 55px; + width: 200px; + border-right: solid 1px #a6a6a6; + padding: 10px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.PlanetNavigator .launchButton { + -webkit-border-radius: 22px; + border-radius: 22px; + font-size: 1.1em; +} +.PlanetNavigator nav a { + display: block; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 15px 15px; + margin: 10px 0; + -webkit-border-radius: 10px; + border-radius: 10px; + text-decoration: none; + background-color: transparent; + color: #4d4d4d; + cursor: pointer; + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; + border-style: solid; + border-width: 1px; + border-color: #898989; + background-color: transparent; + color: #898989; + border: none; +} +.PlanetNavigator nav a:hover, +.PlanetNavigator nav a.hover, +.PlanetNavigator nav a:focus, +.PlanetNavigator nav a.focus { + border-color: #454545; + color: #454545; +} +.PlanetNavigator nav a:active, +.PlanetNavigator nav a.active { + border-color: #39a18a; + background-color: #2bac8f; + color: #fff; +} +.PlanetNavigator nav a:disabled, +.PlanetNavigator nav a.disabled { + opacity: 0.6; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; + filter: alpha(opacity=60); +} +.PlanetArticleList { + position: absolute; + bottom: 0; + right: 0; + left: 200px; + top: 55px; + width: 275px; + border-right: solid 1px #a6a6a6; +} +.PlanetArticleList>ul { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + overflow-y: auto; +} +.PlanetArticleList>ul li .articleItem { + -webkit-user-select: none; + cursor: default; + border: solid 2px transparent; + position: relative; + height: 94px; + width: 100%; + cursor: pointer; + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; +} +.PlanetArticleList>ul li .articleItem .itemLeft { + position: absolute; + top: 4px; + bottom: 4px; + width: 38px; + padding: 3px 0 3px 3px; + text-align: center; +} +.PlanetArticleList>ul li .articleItem .itemLeft .profileImage { + margin-bottom: 5px; + -webkit-border-radius: 50%; + border-radius: 50%; + overflow: hidden; +} +.PlanetArticleList>ul li .articleItem .itemLeft .fa { + line-height: 25px; +} +.PlanetArticleList>ul li .articleItem .itemRight { + position: absolute; + top: 4px; + bottom: 4px; + right: 2px; + left: 40px; + overflow-x: hidden; + padding: 3px 10px 3px 3px; +} +.PlanetArticleList>ul li .articleItem .itemRight .itemInfo { + margin: 5px 0 13px; + color: #7a7a7a; + font-size: 0.7em; +} +.PlanetArticleList>ul li .articleItem .itemRight .itemInfo .userProfileName { + color: #2bac8f; + font-size: 1.2em; +} +.PlanetArticleList>ul li .articleItem .itemRight .description { + line-height: 120%; + margin-bottom: 10px; + font-size: 1em; + overflow-x: hidden; + white-space: nowrap; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; +} +.PlanetArticleList>ul li .articleItem .itemRight .tags { + position: absolute; + bottom: 5px; + font-size: 0.9em; +} +.PlanetArticleList>ul li .articleItem:hover, +.PlanetArticleList>ul li .articleItem.hover { + background-color: rgba(0,0,0,0.04); +} +.PlanetArticleList>ul li .articleItem:hover:active, +.PlanetArticleList>ul li .articleItem.hover:active, +.PlanetArticleList>ul li .articleItem:hover.active, +.PlanetArticleList>ul li .articleItem.hover.active { + background-color: #fff; +} +.PlanetArticleList>ul li .articleItem:active, +.PlanetArticleList>ul li .articleItem.active { + border-color: #3fb399; +} +.PlanetArticleList>ul li .divider { + border-bottom: solid 1px #d0d0d0; +} +.PlanetArticleDetail { + position: absolute; + right: 0; + bottom: 0; + top: 55px; + left: 475px; +} +.PlanetArticleDetail .detailHeader { + border: solid 2px transparent; + position: relative; + height: 105px; + width: 100%; + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; +} +.PlanetArticleDetail .detailHeader .itemLeft { + position: absolute; + top: 7px; + bottom: 4px; + width: 38px; + padding: 3px 0 3px 3px; + text-align: center; +} +.PlanetArticleDetail .detailHeader .itemLeft .profileImage { + margin-bottom: 5px; + -webkit-border-radius: 50%; + border-radius: 50%; + overflow: hidden; +} +.PlanetArticleDetail .detailHeader .itemLeft .fa { + line-height: 25px; +} +.PlanetArticleDetail .detailHeader .itemRight { + position: absolute; + top: 7px; + bottom: 4px; + right: 2px; + left: 40px; + overflow-x: hidden; + padding: 3px 10px 3px 3px; +} +.PlanetArticleDetail .detailHeader .itemRight .itemInfo { + margin: 5px 0 13px; + color: #7a7a7a; + font-size: 0.7em; +} +.PlanetArticleDetail .detailHeader .itemRight .itemInfo .userProfileName { + color: #2bac8f; + font-size: 1.2em; +} +.PlanetArticleDetail .detailHeader .itemRight .description { + line-height: 120%; + margin-bottom: 10px; + font-size: 1em; + overflow-x: auto; + white-space: nowrap; +} +.PlanetArticleDetail .detailHeader .itemRight .tags { + position: absolute; + bottom: 5px; + font-size: 0.9em; +} +.PlanetArticleDetail .detailHeader .itemControl { + position: absolute; + z-index: 1; + top: 2px; + right: 2px; +} +.PlanetArticleDetail .detailHeader .itemControl .deleteButton, +.PlanetArticleDetail .detailHeader .itemControl .editButton { + border-style: solid; + border-width: 1px; + border-color: #898989; + background-color: transparent; + color: #898989; + text-align: center; + width: 33px; + height: 33px; + -webkit-border-radius: 16.5px; + border-radius: 16.5px; + font-size: 15px; + margin: 0 3px; +} +.PlanetArticleDetail .detailHeader .itemControl .deleteButton:hover, +.PlanetArticleDetail .detailHeader .itemControl .editButton:hover, +.PlanetArticleDetail .detailHeader .itemControl .deleteButton.hover, +.PlanetArticleDetail .detailHeader .itemControl .editButton.hover, +.PlanetArticleDetail .detailHeader .itemControl .deleteButton:focus, +.PlanetArticleDetail .detailHeader .itemControl .editButton:focus, +.PlanetArticleDetail .detailHeader .itemControl .deleteButton.focus, +.PlanetArticleDetail .detailHeader .itemControl .editButton.focus { + border-color: #454545; + color: #454545; +} +.PlanetArticleDetail .detailHeader .itemControl .deleteButton:active, +.PlanetArticleDetail .detailHeader .itemControl .editButton:active, +.PlanetArticleDetail .detailHeader .itemControl .deleteButton.active, +.PlanetArticleDetail .detailHeader .itemControl .editButton.active { + border-color: #39a18a; + background-color: #2bac8f; + color: #fff; +} +.PlanetArticleDetail .detailHeader .itemControl .deleteButton:disabled, +.PlanetArticleDetail .detailHeader .itemControl .editButton:disabled, +.PlanetArticleDetail .detailHeader .itemControl .deleteButton.disabled, +.PlanetArticleDetail .detailHeader .itemControl .editButton.disabled { + opacity: 0.6; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; + filter: alpha(opacity=60); +} +.PlanetArticleDetail .detailHeader .itemControl .deleteButton .tooltip, +.PlanetArticleDetail .detailHeader .itemControl .editButton .tooltip { + position: fixed; + z-index: 500; + background-color: rgba(31,31,31,0.8); + color: #fff; + padding: 10px; + font-size: 12px; + line-height: 12px; + -webkit-border-radius: 5px; + border-radius: 5px; + white-space: nowrap; + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; + pointer-events: none; + margin-top: 10px; +} +.PlanetArticleDetail .detailHeader .itemControl .deleteButton:hover .tooltip, +.PlanetArticleDetail .detailHeader .itemControl .editButton:hover .tooltip { + opacity: 1; + -ms-filter: none; + filter: none; +} +.PlanetArticleDetail .detailHeader .itemControl .editButton .tooltip { + margin-left: -12px; +} +.PlanetArticleDetail .detailHeader .itemControl .deleteButton .tooltip { + margin-left: -26px; +} +.PlanetArticleDetail .detailBody { + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 105px; +} +.PlanetArticleDetail .detailBody .content { + position: absolute; + top: 5px; + bottom: 5px; + left: 2px; + right: 2px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 5px; + border-top: solid 1px #d0d0d0; +} +.PlanetArticleDetail.noteDetail .detailBody .content { + overflow-x: hidden; + overflow-y: auto; +} +.PlanetArticleDetail.noteDetail .detailBody .content hr { + border-top: none; + border-bottom: solid 1px #d0d0d0; + margin: 15px 0; +} +.PlanetArticleDetail.noteDetail .detailBody .content h1 { + font-size: 2em; + border-bottom: solid 2px #d0d0d0; + margin: 0.33em auto 0.67em; +} +.PlanetArticleDetail.noteDetail .detailBody .content h2 { + font-size: 1.5em; + margin: 0.42em auto 0.83em; +} +.PlanetArticleDetail.noteDetail .detailBody .content h3 { + font-size: 1.17em; + margin: 0.5em auto 1em; +} +.PlanetArticleDetail.noteDetail .detailBody .content h4 { + font-size: 1em; + margin: 0.67em auto 1.33em; +} +.PlanetArticleDetail.noteDetail .detailBody .content h5 { + font-size: 0.83em; + margin: 0.84em auto 1.67em; +} +.PlanetArticleDetail.noteDetail .detailBody .content h6 { + font-size: 0.67em; + margin: 1.16em auto 2.33em; +} +.PlanetArticleDetail.noteDetail .detailBody .content h1, +.PlanetArticleDetail.noteDetail .detailBody .content h2, +.PlanetArticleDetail.noteDetail .detailBody .content h3, +.PlanetArticleDetail.noteDetail .detailBody .content h4, +.PlanetArticleDetail.noteDetail .detailBody .content h5, +.PlanetArticleDetail.noteDetail .detailBody .content h6 { + font-weight: 700; + line-height: 1.8em; +} +.PlanetArticleDetail.noteDetail .detailBody .content p { + line-height: 1.8em; + margin: 15px 0 25px; +} +.PlanetArticleDetail.noteDetail .detailBody .content img { + max-width: 100%; +} +.PlanetArticleDetail.noteDetail .detailBody .content strong { + font-weight: bold; +} +.PlanetArticleDetail.noteDetail .detailBody .content em { + font-style: italic; +} +.PlanetArticleDetail.noteDetail .detailBody .content s { + text-decoration: line-through; +} +.PlanetArticleDetail.noteDetail .detailBody .content blockquote { + border-left: solid 4px #3fb399; + margin: 15px 0 25px; + padding: 0 25px; +} +.PlanetArticleDetail.noteDetail .detailBody .content ul { + list-style-type: disc; + padding-left: 35px; + margin-bottom: 35px; +} +.PlanetArticleDetail.noteDetail .detailBody .content ul li { + display: list-item; + margin: 15px 0; +} +.PlanetArticleDetail.noteDetail .detailBody .content ul>li>ul { + list-style-type: circle; +} +.PlanetArticleDetail.noteDetail .detailBody .content ul>li>ul>li>ul { + list-style-type: square; +} +.PlanetArticleDetail.noteDetail .detailBody .content ol { + list-style-type: decimal; + padding-left: 35px; + margin-bottom: 35px; +} +.PlanetArticleDetail.noteDetail .detailBody .content ol li { + display: list-item; + margin: 15px 0; +} +.PlanetArticleDetail.noteDetail .detailBody .content code { + font-family: monospace; + padding: 2px 4px; + border: solid 1px #d0d0d0; + -webkit-border-radius: 4px; + border-radius: 4px; + font-size: 0.9em; + color: #000; + text-decoration: none; + background-color: #f6f6f6; +} +.PlanetArticleDetail.noteDetail .detailBody .content pre { + padding: 5px; + border: solid 1px #d0d0d0; + -webkit-border-radius: 5px; + border-radius: 5px; + overflow-x: auto; + margin: 15px 0 25px; + background-color: #f6f6f6; +} +.PlanetArticleDetail.noteDetail .detailBody .content pre>code { + padding: 0; + border: none; + -webkit-border-radius: 0; + border-radius: 0; + color: #000; +} +.PlanetArticleDetail.noteDetail .detailBody .content table { + width: 100%; + margin: 15px 0 25px; +} +.PlanetArticleDetail.noteDetail .detailBody .content table thead tr { + background-color: #fff; +} +.PlanetArticleDetail.noteDetail .detailBody .content table thead th { + border-style: solid; + padding: 15px 5px; + border-width: 1px 0 2px 1px; + border-color: #d0d0d0; +} +.PlanetArticleDetail.noteDetail .detailBody .content table thead th:last-child { + border-right: solid 1px #d0d0d0; +} +.PlanetArticleDetail.noteDetail .detailBody .content table tbody tr:nth-child(2n + 1) { + background-color: #f9f9f9; +} +.PlanetArticleDetail.noteDetail .detailBody .content table tbody tr:nth-child(2n) { + background-color: #fff; +} +.PlanetArticleDetail.noteDetail .detailBody .content table tbody td { + border-style: solid; + padding: 15px 5px; + border-width: 0 0 1px 1px; + border-color: #d0d0d0; +} +.PlanetArticleDetail.noteDetail .detailBody .content table tbody td:last-child { + border-right: solid 1px #d0d0d0; +} +.PlanetArticleDetail.codeDetail .detailBody .content .ace_editor { + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; +} +.UserContainer { + position: absolute; + top: 0; + bottom: 0; + right: 0; + left: 60px; +} +.UserContainer .content { + position: absolute; + top: 0; + bottom: 0; + right: 0; + left: 200px; + background-color: #e6e6e6; +} +.UserContainer .UserNavigator { + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 200px; + background-color: #333; + color: #ddd; + -webkit-user-select: none; + cursor: default; +} +.UserContainer .UserNavigator>.profile { + height: 60px; + padding: 10px 15px 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + position: relative; + border-bottom: solid 1px #666; + cursor: pointer; +} +.UserContainer .UserNavigator>.profile>.profileName { + color: #2bac8f; + font-size: 22px; + cursor: pointer; + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; +} +.UserContainer .UserNavigator>.profile>.name { + padding: 5px 10px; + font-size: 14px; + color: #ddd; + cursor: pointer; + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; +} +.UserContainer .UserNavigator>.profile>.dropdownIcon { + position: absolute; + top: 20px; + right: 25px; + float: right; + width: 20px; + height: 20px; + line-height: 20px; + font-size: 8px; + border: solid 1px #ddd; + -webkit-border-radius: 12.5px; + border-radius: 12.5px; + text-align: center; + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; +} +.UserContainer .UserNavigator>.profile:hover>.profileName { + color: #31c4a3; +} +.UserContainer .UserNavigator>.profile:hover>.name { + color: #fff; +} +.UserContainer .UserNavigator>.profile:hover>.dropdownIcon { + border-color: #fff; +} +.UserContainer .UserNavigator>.profile:hover:active>.dropdownIcon { + background-color: #2bac8f; + border-color: #2bac8f; +} +.UserContainer .UserNavigator>.control { + padding: 15px 15px; +} +.UserContainer .UserNavigator>.control>.newPostButton { + background-color: #2bac8f; + color: #fff; + height: 44px; + width: 100%; + border: none; + -webkit-border-radius: 5px; + border-radius: 5px; + font-size: 16px; + font-weight: 600; + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; +} +.UserContainer .UserNavigator>.control>.newPostButton:hover { + background-color: #31c4a3; +} +.UserContainer .UserNavigator>.menu { + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 134px; + padding: 15px 0; + overflow: auto; +} +.UserContainer .UserNavigator>.menu>.menuGruop>.label { + border-bottom: 1px solid #666; + padding: 10px 15px; + font-size: 18px; + margin-bottom: 10px; +} +.UserContainer .UserNavigator>.menu>.menuGruop>.label>.plusButton { + float: right; + width: 20px; + height: 20px; + margin-top: -2.5px; + margin-right: -5px; + line-height: 15px; + font-size: 8px; + border: solid 1px #ddd; + -webkit-border-radius: 10px; + border-radius: 10px; + background-color: transparent; + text-align: center; + color: #ddd; +} +.UserContainer .UserNavigator>.menu>.menuGruop>.label>.plusButton:hover { + border-color: #fff; + color: #fff; +} +.UserContainer .UserNavigator>.menu>.menuGruop>.label>.plusButton:hover:active { + background-color: #2bac8f; + border-color: #2bac8f; +} +.UserContainer .UserNavigator>.menu>.folders .folderButton { + padding: 10px 25px; + width: 100%; + background-color: transparent; + border: none; + font-size: 14px; + color: #ddd; + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; + text-align: left; +} +.UserContainer .UserNavigator>.menu>.folders .folderButton:hover { + background-color: rgba(255,255,255,0.2); + color: #fff; +} +.UserContainer .UserNavigator>.menu>.folders .folderButton.active { + background-color: #2bac8f; + color: #fff; +} +* { + -webkit-app-region: no-drag; + -webkit-user-select: none; +} +html, +body { + width: 100%; + height: 100%; + overflow: hidden; +} +body { + font-family: "Lato"; + color: #4d4d4d; + font-size: 14px; + font-weight: 400; +} +button, +input, +select { + font-family: "Lato"; +} +div, +span, +a, +button, +input, +textarea { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +a { + color: #2bac8f; +} +a:hover { + color: #25927a; +} +a:visited { + color: #2bac8f; +} +hr { + border-top: none; + border-bottom: solid 1px #d0d0d0; + margin: 15px 0; +} +button { + font-weight: 400; + cursor: pointer; +} +button:focus, +button.focus { + outline: none; +} +.noSelect { + -webkit-user-select: none; + cursor: default; +} +.text-center { + text-align: center; +} +.form-group { + margin-bottom: 15px; +} +.form-group>label { + display: block; + margin-bottom: 5px; +} +.block-input, +.inline-input { + border: solid 1px #d0d0d0; + padding: 0 10px; + font-size: 1em; + height: 33px; + -webkit-border-radius: 5px; + border-radius: 5px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.block-input:focus, +.inline-input:focus, +.block-input.focus, +.inline-input.focus { + border: solid 1px #3fb399; + outline: none; +} +.block-input.circleInput, +.inline-input.circleInput { + -webkit-border-radius: 16.5px; + border-radius: 16.5px; +} +.block-input { + display: block; + width: 100%; +} +.inline-input { + display: inline-block; + margin-right: 5px; +} +.relative { + position: relative; +} +textarea.block-input { + resize: vertical; + height: 125px; + -webkit-border-radius: 5px; + border-radius: 5px; + padding: 5px 10px; +} +#content { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; +} +.Main .appUpdateButton { + position: fixed; + z-index: 2000; + bottom: 5px; + right: 53px; + padding: 10px 15px; + border: none; + -webkit-border-radius: 5px; + border-radius: 5px; + background-color: #2bac8f; + color: #fff; + opacity: 0.7; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=70)"; + filter: alpha(opacity=70); +} +.Main .appUpdateButton:hover { + opacity: 1; + -ms-filter: none; + filter: none; + background-color: #31c4a3; +} +.Main .contactButton { + position: fixed; + z-index: 2000; + bottom: 5px; + right: 5px; + padding: 10px 15px; + border: none; + -webkit-border-radius: 5px; + border-radius: 5px; + background-color: #2bac8f; + color: #fff; + opacity: 0.7; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=70)"; + filter: alpha(opacity=70); +} +.Main .contactButton:hover { + opacity: 1; + -ms-filter: none; + filter: none; + background-color: #31c4a3; +} +.Main .contactButton .tooltip { + position: fixed; + z-index: 500; + background-color: rgba(31,31,31,0.8); + color: #fff; + padding: 10px; + font-size: 12px; + line-height: 12px; + -webkit-border-radius: 5px; + border-radius: 5px; + white-space: nowrap; + opacity: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -webkit-transition: 0.1s; + -moz-transition: 0.1s; + -o-transition: 0.1s; + -ms-transition: 0.1s; + transition: 0.1s; + pointer-events: none; + margin-top: -22px; + margin-left: -97px; +} +.Main .contactButton:hover .tooltip { + opacity: 1; + -ms-filter: none; + filter: none; +} diff --git a/browser/styles/main/index.styl b/browser/styles/main/index.styl index e0175c3b..81c0dbd2 100644 --- a/browser/styles/main/index.styl +++ b/browser/styles/main/index.styl @@ -8,6 +8,7 @@ global-reset() * -webkit-app-region no-drag + -webkit-user-select none html, body width 100% @@ -19,24 +20,12 @@ body color textColor font-size fontSize font-weight 400 -button +button, input, select font-family "Lato" div, span, a, button, input, textarea box-sizing border-box -h1 - font-size 2em -h2 - font-size 1.5em -h3 - font-size 1.17em -h4 - font-size 1em -h5 - font-size 0.83em -h6 - font-size 0.67em a color brandColor &:hover @@ -55,6 +44,9 @@ button &:focus, &.focus outline none +.noSelect + noSelect() + .text-center text-align center @@ -64,13 +56,6 @@ button display block margin-bottom 5px -.stripInput - stripInput() - display block - width 100% - font-size 1em - height 33px - .block-input, .inline-input border solid 1px borderColor padding 0 10px @@ -110,19 +95,29 @@ textarea.block-input z-index 2000 bottom 5px right 53px - btnPrimary() padding 10px 15px + border none border-radius 5px - background-color backgroundColor + background-color brandColor + color white + opacity 0.7 + &:hover + opacity 1 + background-color lighten(brandColor, 10%) .contactButton position fixed z-index 2000 bottom 5px right 5px - btnPrimary() padding 10px 15px + border none border-radius 5px - background-color backgroundColor + background-color brandColor + color white + opacity 0.7 + &:hover + opacity 1 + background-color lighten(brandColor, 10%) .tooltip tooltip() margin-top -22px diff --git a/browser/styles/mixins/marked.styl b/browser/styles/mixins/marked.styl index acc4e7f0..1dcf1947 100644 --- a/browser/styles/mixins/marked.styl +++ b/browser/styles/mixins/marked.styl @@ -6,28 +6,28 @@ marked() h1 font-size 2em border-bottom solid 2px borderColor - margin 0.67em auto + margin 0.33em auto 0.67em h2 font-size 1.5em - margin 0.83em auto + margin 0.42em auto 0.83em h3 font-size 1.17em - margin 1em auto + margin 0.5em auto 1em h4 font-size 1em - margin 1.33em auto + margin 0.67em auto 1.33em h5 font-size 0.83em - margin 1.67em auto + margin 0.84em auto 1.67em h6 font-size 0.67em - margin 2.33em auto + margin 1.16em auto 2.33em h1, h2, h3, h4, h5, h6 font-weight 700 line-height 1.8em p line-height 1.8em - margin-bottom 25px + margin 15px 0 25px img max-width 100% strong @@ -38,12 +38,12 @@ marked() text-decoration line-through blockquote border-left solid 4px brandBorderColor - margin 15px 0 15px + margin 15px 0 25px padding 0 25px ul list-style-type disc padding-left 35px - margin-bottom 25px + margin-bottom 35px li display list-item margin 15px 0 @@ -54,7 +54,7 @@ marked() ol list-style-type decimal padding-left 35px - margin-bottom 25px + margin-bottom 35px li display list-item margin 15px 0 @@ -72,7 +72,7 @@ marked() border solid 1px borderColor border-radius 5px overflow-x auto - margin-bottom 25px + margin 15px 0 25px background-color #F6F6F6 &>code padding 0 diff --git a/browser/styles/mixins/util.styl b/browser/styles/mixins/util.styl index c6913cf1..1e0b61b6 100644 --- a/browser/styles/mixins/util.styl +++ b/browser/styles/mixins/util.styl @@ -3,5 +3,4 @@ borderBox() noSelect() -webkit-user-select none - -webkit-app-region drag - + cursor default diff --git a/browser/styles/vars.styl b/browser/styles/vars.styl index ebe54635..c8747771 100644 --- a/browser/styles/vars.styl +++ b/browser/styles/vars.styl @@ -1,4 +1,4 @@ -borderColor = #E8E8E8 +borderColor = #D0D0D0 highlightenBorderColor = darken(borderColor, 20%) invBorderColor = #404849 brandBorderColor = #3FB399 @@ -24,12 +24,6 @@ btnHighlightenColor = #000 brandColor = #2BAC8F -planetNavBgColor = #ECECEC -planetAnchorColor = #979797 -planetAnchorBgColor = #BEBEBE -planetAnchorActiveColor = textColor -planetAnchorActiveBgColor = white - popupShadow = 0 0 5px 0 #888 modalBackColor = transparentify(white, 65%) diff --git a/config.js b/config.js index 0ce393d9..7d3ed42b 100644 --- a/config.js +++ b/config.js @@ -1,5 +1,5 @@ module.exports = { // apiUrl: 'https://api.b00st.io/' - apiUrl: 'https://api2.b00st.io/' - // apiUrl: 'http://localhost:8000/' + // apiUrl: 'https://api2.b00st.io/' + apiUrl: 'http://localhost:8000/' } diff --git a/finder-window.js b/finder-window.js index d71dda6f..1038456a 100644 --- a/finder-window.js +++ b/finder-window.js @@ -6,16 +6,16 @@ var finderWindow = new BrowserWindow({ show: false, frame: false, resizable: false, + 'zoom-factor': 1.0, 'always-on-top': true, 'web-preferences': { - 'zoom-factor': 1.0, 'overlay-scrollbars': true, 'skip-taskbar': true }, 'standard-window': false }) -finderWindow.loadUrl('file://' + __dirname + '/browser/finder/index.electron.html') +finderWindow.loadUrl('file://' + __dirname + '/browser/finder/index.html') finderWindow.on('blur', function () { finderWindow.hide() diff --git a/lib/key-gen.js b/lib/key-gen.js new file mode 100644 index 00000000..4cc04385 --- /dev/null +++ b/lib/key-gen.js @@ -0,0 +1,7 @@ +var crypto = require('crypto') + +module.exports = function () { + var shasum = crypto.createHash('sha1') + shasum.update(((new Date()).getTime()).toString()) + return shasum.digest('hex') +} diff --git a/main-window.js b/main-window.js index e4038f22..b2a5b56d 100644 --- a/main-window.js +++ b/main-window.js @@ -3,15 +3,14 @@ var BrowserWindow = require('browser-window') var mainWindow = new BrowserWindow({ width: 1080, height: 720, - // frame: false, + 'zoom-factor': 1.0, 'web-preferences': { - 'zoom-factor': 1.0, 'overlay-scrollbars': true }, 'standard-window': false }) -mainWindow.loadUrl('file://' + __dirname + '/browser/main/index.electron.html') +mainWindow.loadUrl('file://' + __dirname + '/browser/main/index.html') mainWindow.setVisibleOnAllWorkspaces(true) diff --git a/package.json b/package.json index ea620ac1..a60ff850 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,16 @@ { "name": "boost", - "version": "0.3.1", + "version": "0.4.0", "description": "Boost App", "main": "main.js", "scripts": { "start": "electron ./main.js", - "web": "npm run serve | npm run dev", - "serve": "./node_modules/.bin/http-server ./browser -p 8080", - "dev": "webpack-dev-server --progress --colors --port 8090" + "build": "electron-packager ./ Boost $npm_package_config_platform $npm_package_config_version $npm_package_config_ignore --overwrite" + }, + "config": { + "version": "--version=0.33.0 --app-version=$npm_package_version --app-bundle-id=com.maisin.boost", + "platform": "--platform=darwin --arch=x64 --prune --icon=app.icns", + "ignore": "--ignore=Boost-darwin-x64 --ignore=node_modules/devicon/icons --ignore=submodules\/ace\/(?!src-min)|submodules\/ace\/(?=src-min-noconflict)" }, "repository": { "type": "git", @@ -31,38 +34,38 @@ }, "homepage": "https://github.com/Rokt33r/codexen-app#readme", "dependencies": { + "babel-core": "^5.8.25", + "devicon": "^2.0.0", "font-awesome": "^4.3.0", "fs-jetpack": "^0.7.0", + "lodash": "^3.10.1", "markdown-it": "^4.3.1", "md5": "^2.0.0", "moment": "^2.10.3", - "nib": "^1.1.0", - "node-jsx": "^0.13.3", "node-notifier": "^4.2.3", "react": "^0.13.3", - "react-router": "^0.13.3", - "react-select": "^0.5.4", + "react-redux": "^3.1.0", + "react-router": "^1.0.0-rc1", + "react-select": "^0.6.10", + "redux": "^3.0.2", "reflux": "^0.2.8", - "stylus": "^0.52.0", + "socket.io-client": "^1.3.6", "superagent": "^1.2.0", "superagent-promise": "^1.0.3", "titlebar": "^1.3.0" }, "devDependencies": { - "css-loader": "^0.15.1", - "http-server": "^0.8.0", - "jsx-loader": "^0.13.2", - "node-libs-browser": "^0.5.2", - "style-loader": "^0.12.3", - "stylus-loader": "^1.2.1", - "webpack": "^1.10.0", - "webpack-dev-server": "^1.10.1" + "electron-packager": "^5.1.0", + "electron-prebuilt": "^0.33.6", + "nib": "^1.1.0", + "standard": "^5.3.1", + "stylus": "^0.52.4" }, "standard": { "ignore": [ "/browser/ace/" ], - "global": [ + "globals": [ "localStorage" ] } diff --git a/submodules/ace b/submodules/ace new file mode 160000 index 00000000..b082bcb4 --- /dev/null +++ b/submodules/ace @@ -0,0 +1 @@ +Subproject commit b082bcb4bf2da27a8def3c438a33ebf0dffe5afe From 2e4fc557ea50685c06d062c4f25c58833e5930b5 Mon Sep 17 00:00:00 2001 From: Rokt33r Date: Fri, 9 Oct 2015 20:12:01 +0900 Subject: [PATCH 05/43] use webpack & add some styles --- .babelrc | 20 ++ browser/main/Components/ExternalLink.js | 19 ++ browser/main/Components/ProfileImage.js | 24 ++ browser/main/Components/ProfileImage.jsx | 15 -- .../HomeContainer/Components/ArticleDetail.js | 2 +- .../HomeContainer/Components/UserNavigator.js | 9 +- browser/main/HomeContainer/actions.js | 8 +- .../HomeContainer/components/ArticleTopBar.js | 27 +++ browser/main/HomeContainer/index.js | 47 ++-- browser/main/HomeContainer/reducer.js | 8 +- browser/main/index.html | 9 +- browser/main/index.js | 49 +++- .../ArticleDetail.styl | 2 +- .../ArticleList.styl | 3 +- .../main/HomeContainer/ArticleTopBar.styl | 92 +++++++ .../main/HomeContainer/UserNavigator.styl | 85 +++++++ browser/styles/main/HomeContainer/index.styl | 4 + browser/styles/main/components/TopBar.styl | 43 ---- .../styles/main/containers/HomeContainer.styl | 153 ------------ browser/styles/main/index.css | 227 +++++++++++++++--- browser/styles/main/index.styl | 1 + browser/styles/mixins/input.styl | 2 + package.json | 16 +- .../favicon-230x230.png | Bin webpack.config.js | 53 ++-- 25 files changed, 601 insertions(+), 317 deletions(-) create mode 100644 .babelrc create mode 100644 browser/main/Components/ExternalLink.js create mode 100644 browser/main/Components/ProfileImage.js delete mode 100644 browser/main/Components/ProfileImage.jsx create mode 100644 browser/main/HomeContainer/components/ArticleTopBar.js rename browser/styles/main/{components => HomeContainer}/ArticleDetail.styl (99%) rename browser/styles/main/{components => HomeContainer}/ArticleList.styl (98%) create mode 100644 browser/styles/main/HomeContainer/ArticleTopBar.styl create mode 100644 browser/styles/main/HomeContainer/UserNavigator.styl create mode 100644 browser/styles/main/HomeContainer/index.styl delete mode 100644 browser/styles/main/components/TopBar.styl delete mode 100644 browser/styles/main/containers/HomeContainer.styl rename {browser/main/resources => resources}/favicon-230x230.png (100%) diff --git a/.babelrc b/.babelrc new file mode 100644 index 00000000..a3a2c1d4 --- /dev/null +++ b/.babelrc @@ -0,0 +1,20 @@ +{ + "stage": 0, + "env": { + "development": { + "plugins": ["react-transform"], + "extra": { + "react-transform": { + "transforms": [{ + "transform": "react-transform-hmr", + "imports": ["react"], + "locals": ["module"] + }, { + "transform": "react-transform-catch-errors", + "imports": ["react", "redbox-react"] + }] + } + } + } + } +} diff --git a/browser/main/Components/ExternalLink.js b/browser/main/Components/ExternalLink.js new file mode 100644 index 00000000..0cac86f6 --- /dev/null +++ b/browser/main/Components/ExternalLink.js @@ -0,0 +1,19 @@ +import React, { PropTypes } from 'react' +import shell from 'shell' + +export default class ExternalLink extends React.Component { + handleClick (e) { + shell.openExternal(this.props.href) + e.preventDefault() + } + + render () { + return ( + this.handleClick(e)} {...this.props}/> + ) + } +} + +ExternalLink.propTypes = { + href: PropTypes.string +} diff --git a/browser/main/Components/ProfileImage.js b/browser/main/Components/ProfileImage.js new file mode 100644 index 00000000..f99b64b5 --- /dev/null +++ b/browser/main/Components/ProfileImage.js @@ -0,0 +1,24 @@ +import React, { PropTypes} from 'react' +import md5 from 'md5' + +export default class ProfileImage extends React.Component { + render () { + let className = this.props.className == null ? 'ProfileImage' : 'ProfileImage ' + this.props.className + let email = this.props.email != null ? this.props.email : '' + let src = 'http://www.gravatar.com/avatar/' + md5(email.trim().toLowerCase()) + '?s=' + this.props.size + + return ( + + ) + } +} + +ProfileImage.propTypes = { + email: PropTypes.string, + size: PropTypes.string, + className: PropTypes.string +} diff --git a/browser/main/Components/ProfileImage.jsx b/browser/main/Components/ProfileImage.jsx deleted file mode 100644 index 77bef87e..00000000 --- a/browser/main/Components/ProfileImage.jsx +++ /dev/null @@ -1,15 +0,0 @@ -var React = require('react') -var md5 = require('md5') - -module.exports = React.createClass({ - propTypes: { - email: React.PropTypes.string, - size: React.PropTypes.string, - className: React.PropTypes.string - }, - render: function () { - return ( -
    +
    ) } } diff --git a/browser/main/HomeContainer/Components/UserNavigator.js b/browser/main/HomeContainer/Components/UserNavigator.js index 6e212893..481559d5 100644 --- a/browser/main/HomeContainer/Components/UserNavigator.js +++ b/browser/main/HomeContainer/Components/UserNavigator.js @@ -1,21 +1,24 @@ import React, { Component, PropTypes } from 'react' import { Link } from 'react-router' +import ProfileImage from '../../components/ProfileImage' export default class UserNavigator extends Component { renderUserList () { - var users = this.props.users.map(user => ( + var users = this.props.users.map((user, index) => (
  • +
    {user.name}
    +
    {'⌘' + (index + 1)}
  • )) return ( -
    +
      {users} -
    + ) } diff --git a/browser/main/HomeContainer/actions.js b/browser/main/HomeContainer/actions.js index d208a0ef..41e2c390 100644 --- a/browser/main/HomeContainer/actions.js +++ b/browser/main/HomeContainer/actions.js @@ -1,10 +1,8 @@ -function updateUser (user) { +export const USER_UPDATE = 'USER_UPDATE' + +export function updateUser (user) { return { type: 'USER_UPDATE', data: user } } - -module.exports = { - updateUser: updateUser -} diff --git a/browser/main/HomeContainer/components/ArticleTopBar.js b/browser/main/HomeContainer/components/ArticleTopBar.js new file mode 100644 index 00000000..c9b5a5c2 --- /dev/null +++ b/browser/main/HomeContainer/components/ArticleTopBar.js @@ -0,0 +1,27 @@ +import React, { PropTypes } from 'react' +import ExternalLink from '../../components/ExternalLink' + +const ArticleTopBar = React.createClass({ + render () { + return ( +
    +
    +
    + + +
    + +
    +
    + + + + + +
    +
    + ) + } +}) + +export default ArticleTopBar diff --git a/browser/main/HomeContainer/index.js b/browser/main/HomeContainer/index.js index 0e0be732..bbc6a3c1 100644 --- a/browser/main/HomeContainer/index.js +++ b/browser/main/HomeContainer/index.js @@ -1,8 +1,9 @@ -import React from 'react' -// import { connect } from 'react-redux' +import React, { PropTypes} from 'react' +import { connect } from 'react-redux' // import actionss.... import UserNavigator from './Components/UserNavigator' import ArticleNavigator from './Components/ArticleNavigator' +import ArticleTopBar from './Components/ArticleTopBar' import ArticleList from './Components/ArticleList' import ArticleDetail from './Components/ArticleDetail' @@ -10,30 +11,13 @@ import ArticleDetail from './Components/ArticleDetail' // var KeyCaster = require('../Mixins/KeyCaster') class HomeContainer extends React.Component { - componentDidMount () { - // if (!this.isActive('user')) { - // console.log('redirect to user home') - // var user = JSON.parse(localStorage.getItem('currentUser')) - // this.transitionTo('userHome', {userId: user.id}) - // } - } render () { - let users = [ - { - id: 1, - name: 'me', - email: 'fll@eme.com' - }, - { - id: 2, - name: 'me', - email: 'fll@eme.com' - } - ] + const { users } = this.props return (
    +
    @@ -41,10 +25,19 @@ class HomeContainer extends React.Component { } } -// function remap (state) { -// console.log('mapped') -// console.log(state) -// return {} -// } +function remap (state) { + let currentUser = state.currentUser + let teams = Array.isArray(currentUser.Teams) ? currentUser.Teams : [] -export default HomeContainer + let users = [currentUser, ...teams] + + return { + users + } +} + +HomeContainer.propTypes = { + users: PropTypes.array +} + +export default connect(remap, {})(HomeContainer) diff --git a/browser/main/HomeContainer/reducer.js b/browser/main/HomeContainer/reducer.js index 78224524..693130f1 100644 --- a/browser/main/HomeContainer/reducer.js +++ b/browser/main/HomeContainer/reducer.js @@ -1,10 +1,14 @@ -import {combineReducers} from 'redux' +import { combineReducers } from 'redux' +import { USER_UPDATE } from './actions' const initialCurrentUser = JSON.parse(localStorage.getItem('currentUser')) function currentUser (state, action) { switch (action.type) { - + case USER_UPDATE: + let user = action.data + localStorage.setItem('currentUser', JSON.stringify(user)) + return user default: return initialCurrentUser } diff --git a/browser/main/index.html b/browser/main/index.html index da8f0ce9..619e5ed4 100644 --- a/browser/main/index.html +++ b/browser/main/index.html @@ -6,7 +6,7 @@ - + -
    - + diff --git a/browser/finder/index.js b/browser/finder/index.js new file mode 100644 index 00000000..fb2a40a7 --- /dev/null +++ b/browser/finder/index.js @@ -0,0 +1,183 @@ +import React, { PropTypes } from 'react' +import ReactDOM from 'react-dom' +import { connect, Provider } from 'react-redux' +import reducer from './reducer' +import { createStore } from 'redux' +import FinderInput from './FinderInput' +import FinderList from './FinderList' +import FinderDetail from './FinderDetail' +import { selectArticle, searchArticle, refreshData } from './actions' +import _ from 'lodash' + +import remote from 'remote' +var hideFinder = remote.getGlobal('hideFinder') +import clipboard from 'clipboard' + +require('../styles/finder/index.styl') + +const FOLDER_FILTER = 'FOLDER_FILTER' +const TEXT_FILTER = 'TEXT_FILTER' +const TAG_FILTER = 'TAG_FILTER' + +class FinderMain extends React.Component { + constructor (props) { + super(props) + } + + componentDidMount () { + ReactDOM.findDOMNode(this.refs.finderInput.refs.input).focus() + } + + handleClick (e) { + ReactDOM.findDOMNode(this.refs.finderInput.refs.input).focus() + } + + handleKeyDown (e) { + if (e.keyCode === 38) { + this.selectPrevious() + e.preventDefault() + } + + if (e.keyCode === 40) { + this.selectNext() + e.preventDefault() + } + + if (e.keyCode === 13) { + let { activeArticle } = this.props + clipboard.writeText(activeArticle.content) + hideFinder() + e.preventDefault() + } + if (e.keyCode === 27) { + hideFinder() + e.preventDefault() + } + } + + handleSearchChange (e) { + let { dispatch } = this.props + + dispatch(searchArticle(e.target.value)) + } + + selectArticle (article) { + this.setState({currentArticle: article}) + } + + selectPrevious () { + let { activeArticle, dispatch } = this.props + let index = this.refs.finderList.props.articles.indexOf(activeArticle) + let previousArticle = this.refs.finderList.props.articles[index - 1] + if (previousArticle != null) dispatch(selectArticle(previousArticle.key)) + } + + selectNext () { + let { activeArticle, dispatch } = this.props + let index = this.refs.finderList.props.articles.indexOf(activeArticle) + let previousArticle = this.refs.finderList.props.articles[index + 1] + if (previousArticle != null) dispatch(selectArticle(previousArticle.key)) + } + + render () { + let { articles, activeArticle, status, dispatch } = this.props + return ( +
    this.handleClick(e)} onKeyDown={e => this.handleKeyDown(e)} className='Finder'> + this.handleSearchChange(e)} + ref='finderInput' + onChange={this.handleChange} + value={status.search} + /> + this.selectArticle(article)} + /> + +
    + ) + } +} + +FinderMain.propTypes = { + articles: PropTypes.array, + activeArticle: PropTypes.shape({ + key: PropTypes.string, + tags: PropTypes.array, + title: PropTypes.string, + content: PropTypes.string + }), + status: PropTypes.shape(), + dispatch: PropTypes.func +} + +function remap (state) { + let { articles, folders, status } = state + + let filters = status.search.split(' ').map(key => key.trim()).filter(key => key.length > 0 && !key.match(/^#$/)).map(key => { + if (key.match(/^in:.+$/)) { + return {type: FOLDER_FILTER, value: key.match(/^in:(.+)$/)[1]} + } + if (key.match(/^#(.+)/)) { + return {type: TAG_FILTER, value: key.match(/^#(.+)$/)[1]} + } + return {type: TEXT_FILTER, value: key} + }) + let folderFilters = filters.filter(filter => filter.type === FOLDER_FILTER) + let textFilters = filters.filter(filter => filter.type === TEXT_FILTER) + let tagFilters = filters.filter(filter => filter.type === TAG_FILTER) + + if (folders != null) { + let targetFolders = folders.filter(folder => { + return _.findWhere(folderFilters, {value: folder.name}) + }) + status.targetFolders = targetFolders + + if (targetFolders.length > 0) { + articles = articles.filter(article => { + return _.findWhere(targetFolders, {key: article.FolderKey}) + }) + } + + if (textFilters.length > 0) { + articles = textFilters.reduce((articles, textFilter) => { + return articles.filter(article => { + return article.title.match(new RegExp(textFilter.value, 'i')) || article.content.match(new RegExp(textFilter.value, 'i')) + }) + }, articles) + } + + if (tagFilters.length > 0) { + articles = tagFilters.reduce((articles, tagFilter) => { + return articles.filter(article => { + return _.find(article.tags, tag => tag.match(new RegExp(tagFilter.value, 'i'))) + }) + }, articles) + } + } + + let activeArticle = _.findWhere(articles, {key: status.articleKey}) + if (activeArticle == null) activeArticle = articles[0] + + return { + articles, + activeArticle, + status + } +} + +var Finder = connect(remap)(FinderMain) +var store = createStore(reducer) + +window.onfocus = e => { + store.dispatch(refreshData()) +} + +ReactDOM.render(( + + + +), document.getElementById('content')) diff --git a/browser/finder/index.jsx b/browser/finder/index.jsx deleted file mode 100644 index d3668aa8..00000000 --- a/browser/finder/index.jsx +++ /dev/null @@ -1,135 +0,0 @@ -/* global localStorage */ -var remote = require('remote') -var hideFinder = remote.getGlobal('hideFinder') -var clipboard = require('clipboard') - -var React = require('react') - -var ArticleFilter = require('../main/Mixins/ArticleFilter') - -var FinderInput = require('./Components/FinderInput') -var FinderList = require('./Components/FinderList') -var FinderDetail = require('./Components/FinderDetail') - -// Filter end - -function fetchArticles () { - var user = JSON.parse(localStorage.getItem('currentUser')) - if (user == null) { - console.log('need to login') - return [] - } - - var articles = [] - user.Planets.forEach(function (planet) { - var _planet = JSON.parse(localStorage.getItem('planet-' + planet.id)) - articles = articles.concat(_planet.Codes, _planet.Notes) - }) - user.Teams.forEach(function (team) { - team.Planets.forEach(function (planet) { - var _planet = JSON.parse(localStorage.getItem('planet-' + planet.id)) - articles = articles.concat(_planet.Codes, _planet.Notes) - }) - }) - - return articles -} - -var Finder = React.createClass({ - mixins: [ArticleFilter], - getInitialState: function () { - var articles = fetchArticles() - return { - articles: articles, - currentArticle: articles[0], - search: '' - } - }, - componentDidMount: function () { - document.addEventListener('keydown', this.handleKeyDown) - document.addEventListener('click', this.handleClick) - window.addEventListener('focus', this.handleFinderFocus) - this.handleFinderFocus() - }, - componentWillUnmount: function () { - document.removeEventListener('keydown', this.handleKeyDown) - document.removeEventListener('click', this.handleClick) - window.removeEventListener('focus', this.handleFinderFocus) - }, - handleFinderFocus: function () { - console.log('focusseeddddd') - this.focusInput() - var articles = fetchArticles() - this.setState({ - articles: articles, - search: '' - }, function () { - var firstArticle = this.refs.finderList.props.articles[0] - if (firstArticle) { - this.setState({ - currentArticle: firstArticle - }) - } - }) - }, - handleKeyDown: function (e) { - if (e.keyCode === 38) { - this.selectPrevious() - e.preventDefault() - } - - if (e.keyCode === 40) { - this.selectNext() - e.preventDefault() - } - - if (e.keyCode === 13) { - var article = this.state.currentArticle - clipboard.writeText(article.content) - hideFinder() - e.preventDefault() - } - if (e.keyCode === 27) { - hideFinder() - e.preventDefault() - } - }, - focusInput: function () { - React.findDOMNode(this.refs.finderInput).querySelector('input').focus() - }, - handleClick: function () { - this.focusInput() - }, - selectPrevious: function () { - var index = this.refs.finderList.props.articles.indexOf(this.state.currentArticle) - if (index > 0) { - this.setState({currentArticle: this.refs.finderList.props.articles[index - 1]}) - } - }, - selectNext: function () { - var index = this.refs.finderList.props.articles.indexOf(this.state.currentArticle) - if (index > -1 && index < this.refs.finderList.props.articles.length - 1) { - this.setState({currentArticle: this.refs.finderList.props.articles[index + 1]}) - } - }, - selectArticle: function (article) { - this.setState({currentArticle: article}) - }, - handleChange: function (e) { - this.setState({search: e.target.value}, function () { - this.setState({currentArticle: this.refs.finderList.props.articles[0]}) - }) - }, - render: function () { - var articles = this.searchArticle(this.state.search, this.state.articles) - return ( -
    - - - -
    - ) - } -}) - -React.render(, document.getElementById('content')) diff --git a/browser/finder/reducer.js b/browser/finder/reducer.js new file mode 100644 index 00000000..72b087c3 --- /dev/null +++ b/browser/finder/reducer.js @@ -0,0 +1,49 @@ +import { combineReducers } from 'redux' +import { SELECT_ARTICLE, SEARCH_ARTICLE, REFRESH_DATA } from './actions' + +let data = JSON.parse(localStorage.getItem('local')) + +let initialArticles = data != null ? data.articles : [] +let initialFolders = data != null ? data.folders : [] +let initialStatus = { + articleKey: null, + search: '' +} + +function status (state = initialStatus, action) { + switch (action.type) { + case SELECT_ARTICLE: + state.articleKey = action.data.key + return state + case SEARCH_ARTICLE: + state.search = action.data.input + return state + default: + return state + } +} + +function articles (state = initialArticles, action) { + switch (action.type) { + case REFRESH_DATA: + return action.data.articles + default: + return state + } +} + +function folders (state = initialFolders, action) { + switch (action.type) { + case REFRESH_DATA: + console.log(action) + return action.data.folders + default: + return state + } +} + +export default combineReducers({ + status, + folders, + articles +}) diff --git a/browser/main/HomePage.js b/browser/main/HomePage.js index edc7f887..eb8f3f84 100644 --- a/browser/main/HomePage.js +++ b/browser/main/HomePage.js @@ -1,65 +1,43 @@ import React, { PropTypes} from 'react' import { connect } from 'react-redux' -import { CREATE_MODE, IDLE_MODE, switchUser, NEW, refreshArticles } from 'boost/actions' -import UserNavigator from './HomePage/UserNavigator' +import { CREATE_MODE, IDLE_MODE, NEW } from 'boost/actions' +// import UserNavigator from './HomePage/UserNavigator' import ArticleNavigator from './HomePage/ArticleNavigator' import ArticleTopBar from './HomePage/ArticleTopBar' import ArticleList from './HomePage/ArticleList' import ArticleDetail from './HomePage/ArticleDetail' -import _, { findWhere, findIndex, pick } from 'lodash' +import _ from 'lodash' import keygen from 'boost/keygen' -import api from 'boost/api' -import auth from 'boost/auth' -import io from 'boost/socket' const TEXT_FILTER = 'TEXT_FILTER' const FOLDER_FILTER = 'FOLDER_FILTER' const TAG_FILTER = 'TAG_FILTER' class HomePage extends React.Component { - componentDidMount () { - const { dispatch } = this.props - - dispatch(switchUser(this.props.params.userId)) - - let currentUser = auth.user() - - let users = currentUser.Teams != null ? [currentUser].concat(currentUser.Teams) : [currentUser] - users.forEach(user => { - api.fetchArticles(user.id) - .then(res => { - dispatch(refreshArticles(user.id, res.body)) - }) - .catch(err => { - if (err.status == null) throw err - console.error(err) - }) - }) - - let token = auth.token() - if (token != null) { - io.emit('JOIN', {token}) - } - } - - componentWillReceiveProps (nextProps) { - const { dispatch, status } = this.props - - if (nextProps.params.userId !== status.userId) { - dispatch(switchUser(nextProps.params.userId)) - } - } - render () { - const { dispatch, status, users, activeUser, articles, activeArticle } = this.props + let { dispatch, status, articles, activeArticle, folders } = this.props return (
    - - + - - + +
    ) } @@ -67,17 +45,9 @@ class HomePage extends React.Component { function remap (state) { let status = state.status - - let currentUser = state.currentUser - if (currentUser == null) return state - let teams = Array.isArray(currentUser.Teams) ? currentUser.Teams : [] - - let users = [currentUser, ...teams] - let activeUser = findWhere(users, {id: parseInt(status.userId, 10)}) - if (activeUser == null) activeUser = users[0] - // Fetch articles - let articles = state.articles['team-' + activeUser.id] + let data = JSON.parse(localStorage.getItem('local')) + let { folders, articles } = data if (articles == null) articles = [] articles.sort((a, b) => { return new Date(b.updatedAt) - new Date(a.updatedAt) @@ -97,17 +67,18 @@ function remap (state) { let textFilters = filters.filter(filter => filter.type === TEXT_FILTER) let tagFilters = filters.filter(filter => filter.type === TAG_FILTER) - if (activeUser.Folders != null) { - let targetFolders = activeUser.Folders.filter(folder => { - return findWhere(folderFilters, {value: folder.name}) + if (folders != null) { + let targetFolders = folders.filter(folder => { + return _.findWhere(folderFilters, {value: folder.name}) }) status.targetFolders = targetFolders if (targetFolders.length > 0) { articles = articles.filter(article => { - return findWhere(targetFolders, {id: article.FolderId}) + return _.findWhere(targetFolders, {key: article.FolderKey}) }) } + if (textFilters.length > 0) { articles = textFilters.reduce((articles, textFilter) => { return articles.filter(article => { @@ -119,19 +90,19 @@ function remap (state) { if (tagFilters.length > 0) { articles = tagFilters.reduce((articles, tagFilter) => { return articles.filter(article => { - return _.find(article.Tags, tag => tag.name.match(new RegExp(tagFilter.value, 'i'))) + return _.find(article.tags, tag => tag.match(new RegExp(tagFilter.value, 'i'))) }) }, articles) } } // Grab active article - let activeArticle = findWhere(articles, {key: status.articleKey}) + let activeArticle = _.findWhere(articles, {key: status.articleKey}) if (activeArticle == null) activeArticle = articles[0] // remove Unsaved new article if user is not CREATE_MODE if (status.mode !== CREATE_MODE) { - let targetIndex = findIndex(articles, article => article.status === NEW) + let targetIndex = _.findIndex(articles, article => article.status === NEW) if (targetIndex >= 0) articles.splice(targetIndex, 1) } @@ -140,8 +111,8 @@ function remap (state) { // restrict // 1. team have one folder at least // or Change IDLE MODE - if (status.mode === CREATE_MODE && activeUser.Folders.length > 0) { - var newArticle = findWhere(articles, {status: 'NEW'}) + if (status.mode === CREATE_MODE) { + var newArticle = _.findWhere(articles, {status: 'NEW'}) if (newArticle == null) { newArticle = { id: null, @@ -149,9 +120,8 @@ function remap (state) { title: '', content: '', mode: 'markdown', - Tags: [], - User: pick(currentUser, ['email', 'name', 'profileName']), - FolderId: activeUser.Folders[0].id, + tags: [], + FolderKey: folders[0].key, status: NEW } articles.unshift(newArticle) @@ -162,8 +132,7 @@ function remap (state) { } let props = { - users, - activeUser, + folders, status, articles, activeArticle @@ -173,8 +142,6 @@ function remap (state) { } HomePage.propTypes = { - users: PropTypes.array, - activeUser: PropTypes.object, params: PropTypes.shape({ userId: PropTypes.string }), @@ -183,7 +150,8 @@ HomePage.propTypes = { }), articles: PropTypes.array, activeArticle: PropTypes.shape(), - dispatch: PropTypes.func + dispatch: PropTypes.func, + folders: PropTypes.array } export default connect(remap)(HomePage) diff --git a/browser/main/HomePage/ArticleDetail.js b/browser/main/HomePage/ArticleDetail.js index 1b6d187e..0f2f2682 100644 --- a/browser/main/HomePage/ArticleDetail.js +++ b/browser/main/HomePage/ArticleDetail.js @@ -1,16 +1,16 @@ import React, { PropTypes } from 'react' import moment from 'moment' -import { findWhere, uniq } from 'lodash' +import _ from 'lodash' import ModeIcon from 'boost/components/ModeIcon' import MarkdownPreview from 'boost/components/MarkdownPreview' import CodeEditor from 'boost/components/CodeEditor' -import { UNSYNCED, IDLE_MODE, CREATE_MODE, EDIT_MODE, switchMode, switchArticle, updateArticle, destroyArticle } from 'boost/actions' +import { IDLE_MODE, CREATE_MODE, EDIT_MODE, switchMode, updateArticle, destroyArticle } from 'boost/actions' import aceModes from 'boost/ace-modes' import Select from 'react-select' import linkState from 'boost/linkState' -import api from 'boost/api' import FolderMark from 'boost/components/FolderMark' import TagLink from 'boost/components/TagLink' +import TagSelect from 'boost/components/TagSelect' var modeOptions = aceModes.map(function (mode) { return { @@ -20,9 +20,7 @@ var modeOptions = aceModes.map(function (mode) { }) function makeInstantArticle (article) { - let instantArticle = Object.assign({}, article) - instantArticle.Tags = Array.isArray(instantArticle.Tags) ? instantArticle.Tags.map(tag => tag.name) : [] - return instantArticle + return Object.assign({}, article) } export default class ArticleDetail extends React.Component { @@ -35,7 +33,7 @@ export default class ArticleDetail extends React.Component { } componentWillReceiveProps (nextProps) { - if (nextProps.activeArticle != null && nextProps.activeArticle.id !== this.state.article.id) { + if (nextProps.activeArticle != null && (nextProps.activeArticle.key !== this.state.article.key) || (nextProps.status.mode !== this.props.status.mode)) { this.setState({article: makeInstantArticle(nextProps.activeArticle)}, function () { console.log('receive props') }) @@ -44,8 +42,8 @@ export default class ArticleDetail extends React.Component { renderEmpty () { return ( -
    - Empty article +
    + Command(⌘) + Enter to create a new post
    ) } @@ -60,23 +58,9 @@ export default class ArticleDetail extends React.Component { } handleDeleteConfirmButtonClick (e) { - let { dispatch, activeUser, activeArticle } = this.props + let { dispatch, activeArticle } = this.props - api.destroyArticle(activeArticle.id) - .then(res => { - console.log(res.body) - }) - .catch(err => { - // connect failed need to queue data - if (err.code === 'ECONNREFUSED') { - return - } - - if (err.status != null) throw err - else console.log(err) - }) - - dispatch(destroyArticle(activeUser.id, activeArticle.id)) + dispatch(destroyArticle(activeArticle.key)) this.setState({openDeleteConfirmMenu: false}) } @@ -85,17 +69,16 @@ export default class ArticleDetail extends React.Component { } renderIdle () { - let { activeArticle, activeUser } = this.props + let { activeArticle, folders } = this.props - let tags = activeArticle.Tags.length > 0 - ? activeArticle.Tags.map(tag => { - return () + let tags = activeArticle.tags != null ? activeArticle.tags.length > 0 + ? activeArticle.tags.map(tag => { + return () }) : ( Not tagged yet - ) - let folder = findWhere(activeUser.Folders, {id: activeArticle.FolderId}) - let folderName = folder != null ? folder.name : '(unknown)' + ) : null + let folder = _.findWhere(folders, {key: activeArticle.FolderKey}) return (
    @@ -117,8 +100,7 @@ export default class ArticleDetail extends React.Component {
    - {folderName}  - by {activeArticle.User.profileName}  + {folder.name}  Created {moment(activeArticle.createdAt).format('YYYY/MM/DD')}  Updated {moment(activeArticle.updatedAt).format('YYYY/MM/DD')}
    @@ -127,7 +109,6 @@ export default class ArticleDetail extends React.Component {
    -
    ) @@ -141,7 +122,7 @@ export default class ArticleDetail extends React.Component {
    {activeArticle.mode === 'markdown' ? - : + : this.handleContentChange(e, value)} mode={activeArticle.mode} code={activeArticle.content}/> }
    @@ -154,86 +135,30 @@ export default class ArticleDetail extends React.Component { } handleSaveButtonClick (e) { - let { activeArticle } = this.props - - if (activeArticle.id == null) this.saveAsNew() - else this.save() - } - - saveAsNew () { - let { dispatch, activeUser } = this.props - let article = this.state.article - let newArticle = Object.assign({}, article) - article.tags = article.Tags - - api.createArticle(article) - .then(res => { - console.log('saved as new') - console.log(res.body) - }) - .catch(err => { - // connect failed need to queue data - if (err.code === 'ECONNREFUSED') { - return - } - - if (err.status != null) throw err - else console.log(err) - }) - - newArticle.status = UNSYNCED - newArticle.Tags = newArticle.Tags.map(tag => { return {name: tag} }) - - dispatch(updateArticle(activeUser.id, newArticle)) - dispatch(switchMode(IDLE_MODE)) - dispatch(switchArticle(article.id)) - } - - save () { - let { dispatch, activeUser } = this.props + let { dispatch, folders } = this.props let article = this.state.article let newArticle = Object.assign({}, article) - article.tags = article.Tags + let folder = _.findWhere(folders, {key: article.FolderKey}) + if (folder == null) return false - api.saveArticle(article) - .then(res => { - console.log('saved') - console.log(res.body) - }) - .catch(err => { - // connect failed need to queue data - if (err.code === 'ECONNREFUSED') { - return - } + delete newArticle.status + newArticle.updatedAt = new Date() - if (err.status != null) throw err - else console.log(err) - }) - - newArticle.status = UNSYNCED - newArticle.Tags = newArticle.Tags.map(tag => { return {name: tag} }) - - dispatch(updateArticle(activeUser.id, newArticle)) + dispatch(updateArticle(newArticle)) dispatch(switchMode(IDLE_MODE)) - dispatch(switchArticle(article.id)) } - handleFolderIdChange (value) { + handleFolderKeyChange (e) { let article = this.state.article - article.FolderId = value + article.FolderKey = e.target.value + this.setState({article: article}) } - handleTagsChange (tag, tags) { - tags = uniq(tags, function (tag) { - return tag.value - }) - - var article = this.state.article - article.Tags = tags.map(function (tag) { - return tag.value - }) + handleTagsChange (newTag, tags) { + let article = this.state.article + article.tags = tags this.setState({article: article}) } @@ -251,21 +176,31 @@ export default class ArticleDetail extends React.Component { } renderEdit () { - let { activeUser } = this.props + let { folders } = this.props - let folderOptions = activeUser.Folders.map(folder => { - return { - label: folder.name, - value: folder.id - } + let folderOptions = folders.map(folder => { + return ( + + ) }) + console.log('edit rendered') return (
    - this.handleTagsChange(tag, tags)} clearable={false} multi placeholder='add some tags...' allowCreate value={this.state.article.Tags} className='tags'/> + + + this.handleTagsChange(tags, tag)} + />
    @@ -278,9 +213,22 @@ export default class ArticleDetail extends React.Component {
    - this.handleModeChange(value)} + clearable={false} + options={modeOptions} + placeholder='select mode...' + value={this.state.article.mode} + className='mode' + />
    - this.handleContentChange(e, value)} mode={this.state.article.mode} code={this.state.article.content}/> + this.handleContentChange(e, value)} + readOnly={false} + mode={this.state.article.mode} + code={this.state.article.content} + />
    diff --git a/browser/main/HomePage/ArticleList.js b/browser/main/HomePage/ArticleList.js index 750a9fee..67442513 100644 --- a/browser/main/HomePage/ArticleList.js +++ b/browser/main/HomePage/ArticleList.js @@ -1,42 +1,54 @@ import React, { PropTypes } from 'react' -import ProfileImage from 'boost/components/ProfileImage' import ModeIcon from 'boost/components/ModeIcon' import moment from 'moment' import { switchArticle, NEW } from 'boost/actions' import FolderMark from 'boost/components/FolderMark' import TagLink from 'boost/components/TagLink' +import _ from 'lodash' export default class ArticleList extends React.Component { - handleArticleClick (key) { + componentDidMount () { + this.refreshTimer = setInterval(() => this.forceUpdate(), 60 * 1000) + } + + componentWillUnmount () { + clearInterval(this.refreshTimer) + } + + handleArticleClick (article) { let { dispatch } = this.props return function (e) { - dispatch(switchArticle(key)) + if (article.status === NEW) return null + dispatch(switchArticle(article.key)) } } render () { - let { articles, activeArticle } = this.props + let { articles, activeArticle, folders } = this.props - let articlesEl = articles.map(article => { - let tags = Array.isArray(article.Tags) && article.Tags.length > 0 - ? article.Tags.map(tag => { - return () + let articleElements = articles.map(article => { + let tagElements = Array.isArray(article.tags) && article.tags.length > 0 + ? article.tags.map(tag => { + return () }) : (Not tagged yet) + let folder = _.findWhere(folders, {key: article.FolderKey}) return (
    -
    this.handleArticleClick(article.key)(e)} className={'articleItem' + (activeArticle.key === article.key ? ' active' : '')}> +
    this.handleArticleClick(article)(e)} className={'articleItem' + (activeArticle.key === article.key ? ' active' : '')}>
    - - by {article.User.profileName} + {folder != null + ? {folder.name} + : Unknown + } {article.status != null ? article.status : moment(article.updatedAt).fromNow()}
    {article.status !== NEW ? article.title : '(New article)'}
    -
    {tags}
    +
    {tagElements}
    @@ -46,13 +58,14 @@ export default class ArticleList extends React.Component { return (
    - {articlesEl} + {articleElements}
    ) } } ArticleList.propTypes = { + folders: PropTypes.array, articles: PropTypes.array, activeArticle: PropTypes.shape(), dispatch: PropTypes.func diff --git a/browser/main/HomePage/ArticleNavigator.js b/browser/main/HomePage/ArticleNavigator.js index b277916c..a3a022ba 100644 --- a/browser/main/HomePage/ArticleNavigator.js +++ b/browser/main/HomePage/ArticleNavigator.js @@ -36,38 +36,25 @@ export default class ArticleNavigator extends React.Component { } render () { - let { activeUser, status } = this.props - if (activeUser == null) return (
    ) + let { status, folders } = this.props let { targetFolders } = status if (targetFolders == null) targetFolders = [] - let folders = activeUser.Folders != null - ? activeUser.Folders.map((folder, index) => { - let isActive = findWhere(targetFolders, {id: folder.id}) + let folderElememts = folders.map((folder, index) => { + let isActive = findWhere(targetFolders, {key: folder.key}) - return ( - - ) - }) - : [] - - let members = Array.isArray(activeUser.Members) ? activeUser.Members.sort((a, b) => { - return new Date(a._pivot_createdAt) - new Date(b._pivot_createdAt) - }).map(member => { return ( -
    - -
    {member.profileName}
    -
    + ) - }) : null + }) return (
    -
    {activeUser.profileName}
    -
    {activeUser.name}
    +
    {process.env.USER}
    +
    local
    @@ -82,22 +69,9 @@ export default class ArticleNavigator extends React.Component {
    - {folders} + {folderElememts}
    - - {activeUser.userType === 'team' ? ( -
    -
    -
    Members
    - -
    -
    - {members} -
    -
    - ) : null} -
    ) } @@ -105,6 +79,7 @@ export default class ArticleNavigator extends React.Component { ArticleNavigator.propTypes = { activeUser: PropTypes.object, + folders: PropTypes.array, status: PropTypes.shape({ folderId: PropTypes.number }), diff --git a/browser/main/HomePage/ArticleTopBar.js b/browser/main/HomePage/ArticleTopBar.js index 2b7e1cf7..cf612a24 100644 --- a/browser/main/HomePage/ArticleTopBar.js +++ b/browser/main/HomePage/ArticleTopBar.js @@ -17,11 +17,9 @@ export default class ArticleTopBar extends React.Component { this.handleSearchChange(e)} placeholder='Search' type='text'/>
    -
    - diff --git a/browser/main/HomePage/untitled b/browser/main/HomePage/untitled new file mode 100644 index 00000000..e69de29b diff --git a/browser/main/index.html b/browser/main/index.html index eb510dde..b3b2cbe8 100644 --- a/browser/main/index.html +++ b/browser/main/index.html @@ -6,7 +6,6 @@ -