├── .babelrc ├── .eslintrc ├── .eslintrc.simple ├── .gitignore ├── .gitlab-ci.yml ├── README.md ├── develop ├── actions │ ├── api.actions.js │ ├── common.actions.js │ ├── modal.actions.js │ ├── pokeball.actions.js │ └── types │ │ ├── api.types.js │ │ ├── common.types.js │ │ ├── modal.types.js │ │ └── pokeball.types.js ├── api │ ├── ApiClient.js │ ├── Base.js │ ├── LocalStorage.js │ ├── PokeballAPI.js │ └── index.js ├── app.js ├── assets │ └── boot_scss │ │ ├── .csscomb.json │ │ ├── .scss-lint.yml │ │ ├── _alert.scss │ │ ├── _animation.scss │ │ ├── _breadcrumb.scss │ │ ├── _button-group.scss │ │ ├── _buttons.scss │ │ ├── _card.scss │ │ ├── _carousel.scss │ │ ├── _close.scss │ │ ├── _code.scss │ │ ├── _custom-forms.scss │ │ ├── _dropdown.scss │ │ ├── _forms.scss │ │ ├── _grid.scss │ │ ├── _images.scss │ │ ├── _input-group.scss │ │ ├── _jumbotron.scss │ │ ├── _labels.scss │ │ ├── _list-group.scss │ │ ├── _media.scss │ │ ├── _mixins.scss │ │ ├── _modal.scss │ │ ├── _nav.scss │ │ ├── _navbar.scss │ │ ├── _normalize.scss │ │ ├── _pager.scss │ │ ├── _pagination.scss │ │ ├── _popover.scss │ │ ├── _print.scss │ │ ├── _progress.scss │ │ ├── _reboot.scss │ │ ├── _responsive-embed.scss │ │ ├── _tables.scss │ │ ├── _tooltip.scss │ │ ├── _type.scss │ │ ├── _utilities-background.scss │ │ ├── _utilities-responsive.scss │ │ ├── _utilities-spacing.scss │ │ ├── _utilities.scss │ │ ├── _variables.scss │ │ ├── bootstrap-flex.scss │ │ ├── bootstrap-grid.scss │ │ ├── bootstrap-reboot.scss │ │ ├── bootstrap.scss │ │ └── mixins │ │ ├── _alert.scss │ │ ├── _background-variant.scss │ │ ├── _border-radius.scss │ │ ├── _breakpoints.scss │ │ ├── _buttons.scss │ │ ├── _cards.scss │ │ ├── _center-block.scss │ │ ├── _clearfix.scss │ │ ├── _forms.scss │ │ ├── _gradients.scss │ │ ├── _grid-framework.scss │ │ ├── _grid.scss │ │ ├── _hover.scss │ │ ├── _image.scss │ │ ├── _label.scss │ │ ├── _list-group.scss │ │ ├── _lists.scss │ │ ├── _nav-divider.scss │ │ ├── _navbar-align.scss │ │ ├── _pagination.scss │ │ ├── _progress.scss │ │ ├── _pulls.scss │ │ ├── _reset-filter.scss │ │ ├── _reset-text.scss │ │ ├── _resize.scss │ │ ├── _screen-reader.scss │ │ ├── _size.scss │ │ ├── _tab-focus.scss │ │ ├── _table-row.scss │ │ ├── _text-emphasis.scss │ │ ├── _text-hide.scss │ │ └── _text-truncate.scss ├── components │ ├── Todos │ │ ├── AddTodo.jsx │ │ └── TodosPage.less │ ├── pages │ │ ├── layouts │ │ │ └── Layout.page.jsx │ │ └── pokemons │ │ │ ├── Pokemon.page.jsx │ │ │ └── Pokemons.page.jsx │ └── pokemons │ │ ├── Pokemon.jsx │ │ └── PokemonExt.jsx ├── config │ ├── apiConfig.js │ ├── appHistory.js │ └── appRoutes.jsx ├── constants │ ├── endPoints.constant.js │ ├── httpErrors.constant.js │ ├── routes.constatnt.js │ └── schemas.constant.js ├── containers │ ├── App.jsx │ ├── layouts │ │ └── Layout.container.jsx │ ├── modal │ │ └── Modal.container.jsx │ ├── pokemons │ │ ├── Pokemon.container.jsx │ │ └── Pokemons.container.jsx │ └── todos │ │ └── Todos.container.jsx ├── index.html ├── reducers │ ├── api.reducer.js │ ├── common.reducer.js │ ├── modal.reducer.js │ ├── pokeball.reducer.js │ ├── root.reducer.js │ └── selectors │ │ └── pokeball.selectors.js ├── store │ └── root.store.js └── utils │ ├── helper.js │ ├── history.helper.js │ ├── mappers │ └── pokemons.mapper.js │ ├── schemas │ └── pokeball.schema.js │ └── valadation.helper.js ├── package.json ├── test ├── setup.js └── todos │ ├── actions.spec.js │ └── reducers.spec.js ├── webpack.config.js ├── webpack.develop.config.js └── webpack.production.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "react"], 3 | "plugins": [ 4 | "transform-object-rest-spread", 5 | "transform-class-properties", 6 | ["transform-react-remove-prop-types", {"mode": "wrap"}] 7 | ] 8 | } -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "rules": { 4 | "indent": [2, 4, {"SwitchCase": 1}], 5 | "no-use-before-define": ["error", { "functions": false, "classes": false }], 6 | "no-param-reassign": [2, {props: false}], 7 | 8 | // Legacy 9 | "max-len": [2, 120, 4, { "ignoreComments": true, "ignoreUrls": true }], 10 | "max-params": [2, 4], 11 | "no-bitwise": 2, 12 | 13 | // React 14 | "react/jsx-indent": [2, 4] 15 | }, 16 | "env": { 17 | "browser": true, 18 | "node": true, 19 | "mocha": true, 20 | "es6": true 21 | }, 22 | 23 | "parserOptions": { 24 | "ecmaFeatures": { 25 | "jsx": true, 26 | "modules": true, 27 | "experimentalObjectRestSpread": true 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.eslintrc.simple: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | 4 | "plugins": [ 5 | "react" 6 | ], 7 | 8 | "env": { 9 | "browser": true, 10 | "node": true, 11 | "mocha": true, 12 | "es6": true 13 | }, 14 | 15 | "ecmaFeatures": { 16 | "jsx": true, 17 | "modules": true, 18 | "experimentalObjectRestSpread": true 19 | }, 20 | 21 | "rules":{ 22 | // Possible Errors 23 | 24 | "comma-dangle": 2, 25 | "no-cond-assign": 2, 26 | "no-control-regex": 2, 27 | "no-dupe-args": 2, 28 | "no-dupe-keys": 2, 29 | "no-empty-character-class": 2, 30 | "no-empty": 2, 31 | "no-ex-assign": 2, 32 | "no-extra-boolean-cast": 2, 33 | "no-extra-semi": 2, 34 | "no-func-assign": 2, 35 | "no-inner-declarations": 2, 36 | "no-invalid-regexp": 2, 37 | "no-irregular-whitespace": 2, 38 | "no-negated-in-lhs": 2, 39 | "no-obj-calls": 2, 40 | "no-sparse-arrays": 2, 41 | "no-unreachable": 2, 42 | "use-isnan":2, 43 | "valid-typeof": 2, 44 | "no-unexpected-multiline": 2, 45 | 46 | // Best Practices 47 | "block-scoped-var": 2, 48 | "complexity": [2, 40], 49 | "default-case": 2, 50 | "dot-notation": 2, 51 | "eqeqeq": 2, 52 | "no-alert": 2, 53 | "no-caller": 2, 54 | "no-else-return": 1, 55 | "no-labels": 2, 56 | "no-eq-null": 2, 57 | "no-eval": 2, 58 | "no-extend-native": 2, 59 | "no-extra-bind": 2, 60 | "no-fallthrough": 2, 61 | "no-floating-decimal": 2, 62 | "no-implied-eval": 2, 63 | "no-iterator": 2, 64 | "no-lone-blocks": 2, 65 | "no-loop-func": 2, 66 | "no-multi-str": 2, 67 | "no-native-reassign": 2, 68 | "no-new-func": 2, 69 | "no-new-wrappers": 2, 70 | "no-new": 2, 71 | "no-octal-escape": 2, 72 | "no-octal": 2, 73 | "no-param-reassign": [2, {props: false}], 74 | "no-proto": 2, 75 | "no-redeclare": 2, 76 | "no-return-assign": 2, 77 | "no-script-url": 2, 78 | "no-self-compare": 2, 79 | "no-sequences": 2, 80 | "no-useless-call": 2, 81 | "no-with": 2, 82 | "radix": 2, 83 | "wrap-iife": 2, 84 | "yoda": 2, 85 | 86 | // Variables 87 | "no-catch-shadow": 2, 88 | "no-delete-var": 2, 89 | "no-label-var": 2, 90 | "no-shadow-restricted-names": 2, 91 | "no-shadow": 2, 92 | "no-undef-init": 2, 93 | "no-undef": 2, 94 | //"no-unused-vars": 2, 95 | 96 | // Node.js 97 | "callback-return": 2, 98 | "no-mixed-requires": 2, 99 | "no-path-concat": 2, 100 | "no-sync": 2, 101 | 102 | // Stylistic Issues 103 | "brace-style": 2, 104 | "camelcase": [2, {"properties": "always"}], 105 | "comma-spacing": [2, {"before": false, "after": true}], 106 | "consistent-this": [2, "self"], 107 | "eol-last": 2, 108 | "func-style": [2, "declaration"], 109 | "indent": [2, 4, {"SwitchCase": 1}], 110 | "linebreak-style": [2, "unix"], 111 | "max-nested-callbacks": [2, 4], 112 | "new-cap": 0, 113 | "new-parens": 2, 114 | "no-array-constructor": 2, 115 | "no-lonely-if": 2, 116 | "no-mixed-spaces-and-tabs": 2, 117 | "no-nested-ternary": 2, 118 | "no-new-object": 2, 119 | "no-spaced-func": 2, 120 | "no-trailing-spaces": 2, 121 | "no-unneeded-ternary": 2, 122 | "quotes": [2, "single", "avoid-escape"], 123 | "semi-spacing": [2, {"before": false, "after": true}], 124 | "semi": [2, "always"], 125 | "keyword-spacing": 2, 126 | "space-before-blocks": [2, "always"], 127 | "space-before-function-paren": [2, "never"], 128 | "space-infix-ops": 2, 129 | "space-unary-ops": [2, { "words": true, "nonwords": false }], 130 | "spaced-comment": [2, "always"], 131 | 132 | // ECMAScript 6 133 | "arrow-spacing": 2, 134 | "constructor-super": 2, 135 | "no-class-assign": 2, 136 | "no-const-assign": 2, 137 | "no-this-before-super": 2, 138 | "no-var": 2, 139 | "object-shorthand": 2, 140 | "prefer-const": 2, 141 | "prefer-spread": 2, 142 | "require-yield": 2, 143 | 144 | // Legacy 145 | "max-len": [2, 120, 4, { "ignoreComments": true, "ignoreUrls": true }], 146 | "max-params": [2, 4], 147 | "no-bitwise": 2 148 | }, 149 | 150 | "globals":{ 151 | "$": true, 152 | "ga": true 153 | } 154 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | node_modules/ 3 | public/ 4 | npm-debug.log 5 | .eslintcache 6 | 7 | .DS_Store -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: node:argon 2 | 3 | before_script: 4 | - apt-get -qq update 5 | - apt-get -qq install -y python2.7 python2.7-dev build-essential make gcc g++ libicu-dev 6 | - npm -g install npm --silent 7 | - "echo -e \"export default {CLIENT_ID: '$CLIENT_ID'}\" > app/scripts/settings.js" 8 | - npm set progress=false 9 | - npm install --silent 10 | 11 | stages: 12 | - test 13 | - build 14 | - deploy 15 | - clean 16 | 17 | test_job: 18 | stage: test 19 | script: 20 | - npm run test 21 | 22 | build_job: 23 | stage: build 24 | script: 25 | - npm run build 26 | - mkdir dist/build 27 | - tar czfC dist/build/latest.tar.gz dist/$CI_BUILD_REF_NAME/ . 28 | - tar czfC dist/build/$CI_BUILD_REF.tar.gz dist/$CI_BUILD_REF_NAME/ . 29 | - apt-get install -yqq ruby ruby-dev 30 | - gem install dpl 31 | - dpl --skip_cleanup --provider=s3 --region=eu-west-1 --access-key-id=$AWS_ACCESS_KEY --secret-access-key=$AWS_SECRET_KEY --bucket=$AWS_BUCKET --local-dir=dist/build/ --upload-dir=$CI_BUILD_REF_NAME 32 | artifacts: 33 | paths: 34 | - dist/$CI_BUILD_REF_NAME/ 35 | only: 36 | - master 37 | - develop 38 | 39 | deploy_job: 40 | stage: deploy 41 | script: 42 | - apt-get install -yqq ruby ruby-dev 43 | - gem install dpl 44 | - dpl --skip_cleanup --provider=s3 --region=eu-west-1 --access-key-id=$AWS_ACCESS_KEY --secret-access-key=$AWS_SECRET_KEY --bucket=$AWS_BUCKET --local-dir=dist/build/ --upload-dir=$CI_BUILD_REF_NAME 45 | artifacts: 46 | paths: 47 | - dist/$CI_BUILD_REF_NAME/ 48 | only: 49 | - master 50 | - develop 51 | 52 | clean_job: 53 | stage: clean 54 | script: 55 | - rm -rf node_modules 56 | - rm -rf ~/.node-gyp 57 | when: on_failure -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Redux React Skeleton 2 | This skeleton is designed to get up and running with a bunch of awesome new front-end technologies, all on top of a configurable, feature-rich webpack build system that's already setup to provide hot reloading, CSS modules with Sass support, unit testing, code coverage reports, bundle splitting, and a whole lot more. 3 | 4 | ## Requirements 5 | - ``` $ node -v ``` is a **^4.2.0** 6 | 7 | - ``` $ npm -v ``` is a **^3.0.0** 8 | 9 | 10 | ## Instalation 11 | ``` 12 | $ git clone https://github.com/Noxwille/redux-react-skeleton.git 13 | $ cd redux-react-skeleton 14 | $ npm install 15 | ``` 16 | 17 | 18 | ## Development 19 | 20 | - ``` $ develop:build ``` - Provide **compiled**, and **minified** bundles to 'public' directory. 21 | 22 | - ``` $ npm run develop:devserver ``` - Runs the project in development mode with hot-reloading of 'public' folder. Open your browser at http://localhost:3030. 23 | 24 | - ``` npm run test ``` - Runs tests **once** with Mocha(server side interpretation). Entry point: **'test/setup.js'**. 25 | 26 | - ``` npm run test:watch ``` - **Continuously** watch changes and run tests immediately after changes. 27 | 28 | - ``` npm run lint ``` - Runs eslint checker **once**. Entry point: **'.eslintrc'**. 29 | 30 | - ``` npm run lint:watch ``` - **Continuously** watch changes and run Run eslint checker immediately after some changes. 31 | 32 | - ``` npm run clear ``` - Remove 'public' and 'node_modules' folders. 33 | 34 | 35 | ## Libs 36 | - [`normalizr`](https://github.com/paularmstrong/normalizr) - Normalizes deeply nested JSON API responses according to a schema for Flux and Redux apps. 37 | Kudos to Jing Chen for suggesting this approach. 38 | - [`reselect`](https://github.com/reactjs/reselect) - Simple “selector” library for Redux. 39 | Selectors can compute derived data, allowing Redux to store the minimal possible state. 40 | Selectors are efficient. A selector is not recomputed unless one of its arguments change. 41 | Selectors are composable. They can be used as input to other selectors. 42 | - [`react-waypoint`](https://github.com/brigade/react-waypoint) - A React component to execute a function whenever you scroll to an element. Works in all containers that can scroll, including the window. Can be used to build features like lazy loading content, infinite scroll, scrollspies, or docking elements to the viewport on scroll. More info [`here`](http://brigade.github.io/react-waypoint/), and [`here`](https://medium.com/brigade-engineering/to-infinity-and-beyond-with-react-waypoint-cb5ba46a9150#.ox4qn8r6n) 43 | 44 | 45 | 46 | ## Code standart 47 | ### Common (es2015, React, Sass) 48 | - [`AirBnb code style`](https://github.com/airbnb/javascript) 49 | 50 | ### Types 51 | A single type must be a string. 52 | Type string should consist information about `action` (GET, SET, ...) and entity to which this action influences. 53 | ``` GET_USERS: 'GET_USERS' ``` 54 | 55 | ### Actions 56 | 57 | An action MUST 58 | 59 | - be a plain JavaScript object. 60 | - have a `type` property. 61 | 62 | An action MAY 63 | 64 | - have a `error` property. 65 | - have a `payload` property. 66 | - have a `meta` property. 67 | 68 | An action MUST NOT include properties other than `type`, `payload`, and `error`, and `meta`. 69 | 70 | ### `type` 71 | 72 | The `type` of an action identifies to the consumer the nature of the action that has occurred. Two actions with the same `type` MUST be strictly equivalent (using `===`). By convention, `type` is usually string constant or a Symbol. 73 | 74 | ### `payload` 75 | 76 | The optional `payload` property MAY be any type of value. It represents the payload of the action. Any information about the action that is not the `type` or status of the action should be part of the `payload` field. 77 | 78 | ### `error` 79 | 80 | The optional `error` property MAY be any type of value. Object if error exist or `null` if error not exist. 81 | 82 | ### `meta` 83 | 84 | The optional `meta` property MAY be any type of value. It is intended for any extra information that is not part of the payload. 85 | 86 | 87 | ## Contribution 88 | Before push commit make sure that all modules are added in package.json 89 | 90 | ### Git branching stategy 91 | ![Git branching stategy](https://s32.postimg.org/3t15v0lhx/Git_branching_model.jpg) 92 | 93 | ##### keywords: [`#Redux`](https://github.com/rackt/redux) [`#React`](https://facebook.github.io/react/) [`#ES2015`](http://www.ecma-international.org/ecma-262/6.0/) [`#Webpack`](https://webpack.github.io) [`#Babel`](https://babeljs.io) [`#Mocha`](https://mochajs.org) [`#Chai`](http://chaijs.com) 94 | -------------------------------------------------------------------------------- /develop/actions/api.actions.js: -------------------------------------------------------------------------------- 1 | import apiTypes from './types/api.types'; 2 | import { httpStatusHandler } from '../constants/httpErrors.constant'; 3 | 4 | // * Actions structure * 5 | // 6 | // - have a { type } property. 7 | // - have a { error } property. 8 | // - have a { payload } property. 9 | // - have a { meta } property. 10 | 11 | export default { 12 | // save in the api Reducer 13 | request(key, value = true) { 14 | return { 15 | type: apiTypes.SET_LOADING, 16 | payload: { key, value }, 17 | }; 18 | }, 19 | 20 | success(type, payload) { 21 | return (dispatch) => { 22 | dispatch(this.request(type, false)); 23 | 24 | const action = payload 25 | ? { type, payload } 26 | : { type }; 27 | 28 | dispatch(action); 29 | }; 30 | }, 31 | 32 | // save in the api Reducer 33 | failure(key, error) { 34 | return (dispatch) => { 35 | dispatch(this.request(key, false)); 36 | 37 | const action = { 38 | type: apiTypes.SET_ERROR, 39 | error: { 40 | key, 41 | value: { 42 | ...error, 43 | ...httpStatusHandler(error.status), 44 | }, 45 | }, 46 | }; 47 | 48 | dispatch(action); 49 | }; 50 | }, 51 | 52 | // save in the api Reducer 53 | paginator(key, paginator) { 54 | const action = { 55 | type: apiTypes.SET_PAGINATOR, 56 | payload: { key, value: paginator }, 57 | }; 58 | 59 | return action; 60 | }, 61 | 62 | clearState(key) { 63 | const action = { 64 | type: apiTypes.CLEAR_API_STATE, 65 | payload: { key }, 66 | }; 67 | 68 | return action; 69 | }, 70 | }; 71 | -------------------------------------------------------------------------------- /develop/actions/common.actions.js: -------------------------------------------------------------------------------- 1 | import cTypes from './types/common.types'; 2 | 3 | // * Actions structure * 4 | // 5 | // - have a { type } property. 6 | // - have a { error } property. 7 | // - have a { payload } property. 8 | // - have a { meta } property. 9 | 10 | export default { 11 | // todo: think about merging CLEAR_DATA_STATE + CLEAR_API_STATE 12 | }; 13 | 14 | export const openModal = (payload) => ({ 15 | type: cTypes.OPEN_MODAL, 16 | payload, 17 | }); 18 | 19 | export const closeModal = () => ({ 20 | type: cTypes.CLOSE_MODAL, 21 | }); 22 | 23 | export const openContainerModal = (payload) => ({ 24 | type: cTypes.OPEN_CONTAINER_MODAL, 25 | payload, 26 | }); 27 | 28 | export const closeContainerModal = () => ({ 29 | type: cTypes.CLOSE_CONTAINER_MODAL, 30 | }); 31 | -------------------------------------------------------------------------------- /develop/actions/modal.actions.js: -------------------------------------------------------------------------------- 1 | import mTypes from './types/modal.types'; 2 | 3 | export const openModal = (key) => ({ 4 | type: mTypes.OPEN_MODAL, 5 | key, 6 | }); 7 | 8 | export const closeModal = () => ({ 9 | type: mTypes.CLOSE_MODAL, 10 | }); 11 | -------------------------------------------------------------------------------- /develop/actions/pokeball.actions.js: -------------------------------------------------------------------------------- 1 | import api from '../api'; 2 | import apiActions from './api.actions'; 3 | import pTypes from './types/pokeball.types'; 4 | import normalizr from '../utils/mappers/pokemons.mapper'; 5 | import sc from '../constants/schemas.constant'; 6 | 7 | import { normalize } from 'normalizr'; 8 | 9 | export const getPokemons = (params) => (dispatch) => { 10 | dispatch(apiActions.request(pTypes.GET_POKEMONS)); 11 | 12 | return api.pokeball.getPokemons(params).then( 13 | (data) => { 14 | dispatch(apiActions.paginator(pTypes.GET_POKEMONS, data.meta)); 15 | 16 | // todo: Implement common mechanism for handle API response 17 | // Map API response 18 | const mResponse = normalizr.res.getAll(data.objects); 19 | 20 | // Normalize API response 21 | const nResponse = normalize(mResponse, sc.pokeball.getPokemons()); 22 | 23 | dispatch(apiActions.success(pTypes.GET_POKEMONS, nResponse)); 24 | }, 25 | (error) => { 26 | dispatch(apiActions.failure(pTypes.GET_POKEMONS, error)); 27 | } 28 | ); 29 | }; 30 | 31 | export const getPokemon = (params) => (dispatch) => { 32 | dispatch(apiActions.request(pTypes.GET_POKEMON)); 33 | 34 | return api.pokeball.getPokemon(params).then( 35 | (data) => { 36 | // todo: Implement common mechanism for handle API response 37 | // Map API response 38 | const mResponse = normalizr.res.getOne(data); 39 | 40 | // Normalize API response 41 | const nResponse = normalize(mResponse, sc.pokeball.getPokemon()); 42 | 43 | dispatch(apiActions.success(pTypes.GET_POKEMON, nResponse)); 44 | }, 45 | (error) => { 46 | dispatch(apiActions.failure(pTypes.GET_POKEMON, error)); 47 | } 48 | ); 49 | }; 50 | 51 | export const clearDataState = () => ({ 52 | type: pTypes.CLEAR_DATA_STATE, 53 | }); 54 | -------------------------------------------------------------------------------- /develop/actions/types/api.types.js: -------------------------------------------------------------------------------- 1 | // Naming: ACTION_ENTITY_RESULT 2 | export default { 3 | SET_LOADING: 'SET_LOADING', 4 | SET_PAGINATOR: 'SET_PAGINATOR', 5 | SET_ERROR: 'SET_ERROR', 6 | CLEAR_API_STATE: 'CLEAR_API_STATE', 7 | }; 8 | -------------------------------------------------------------------------------- /develop/actions/types/common.types.js: -------------------------------------------------------------------------------- 1 | export default { 2 | OPEN_MODAL: 'OPEN_MODAL', 3 | CLOSE_MODAL: 'CLOSE_MODAL', 4 | 5 | OPEN_CONTAINER_MODAL: 'OPEN_CONTAINER_MODAL', 6 | CLOSE_CONTAINER_MODAL: 'CLOSE_CONTAINER_MODAL', 7 | }; 8 | -------------------------------------------------------------------------------- /develop/actions/types/modal.types.js: -------------------------------------------------------------------------------- 1 | export default { 2 | OPEN_MODAL: 'OPEN_MODAL', 3 | CLOSE_MODAL: 'CLOSE_MODAL', 4 | }; 5 | -------------------------------------------------------------------------------- /develop/actions/types/pokeball.types.js: -------------------------------------------------------------------------------- 1 | // Naming: ACTION_ENTITY_RESULT 2 | export default { 3 | GET_POKEMON: 'GET_POKEMON', 4 | GET_POKEMONS: 'GET_POKEMONS', 5 | 6 | CLEAR_DATA_STATE: 'CLEAR_DATA_STATE', 7 | }; 8 | -------------------------------------------------------------------------------- /develop/api/ApiClient.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import queryString from 'query-string'; 3 | 4 | class ApiClient { 5 | constructor({ baseURL }) { 6 | this.baseURL = baseURL; 7 | } 8 | 9 | get(requestUrl, payload = {}, params = {}) { 10 | return this.request({ 11 | url: requestUrl, 12 | method: 'get', 13 | data: payload, 14 | params, 15 | }); 16 | } 17 | 18 | put(requestUrl, payload = {}) { 19 | return this.request({ 20 | url: requestUrl, 21 | method: 'put', 22 | data: payload, 23 | }); 24 | } 25 | 26 | post(requestUrl, payload = {}) { 27 | return this.request({ 28 | url: requestUrl, 29 | method: 'post', 30 | data: payload, 31 | }); 32 | } 33 | 34 | postFormData(requestUrl, formData) { 35 | return this.request({ 36 | url: requestUrl, 37 | method: 'post', 38 | headers: { 39 | 'Content-Type': 'application/x-www-form-urlencoded', 40 | }, 41 | data: formData, 42 | }); 43 | } 44 | 45 | patch(requestUrl, payload = {}) { 46 | return this.request({ 47 | url: requestUrl, 48 | method: 'patch', 49 | data: payload, 50 | }); 51 | } 52 | 53 | delete(requestUrl) { 54 | return this.request({ 55 | url: requestUrl, 56 | method: 'delete', 57 | }); 58 | } 59 | 60 | request({ url, method, params = {}, headers, data }) { 61 | const config = { 62 | url, 63 | method, 64 | baseURL: this.baseURL, 65 | params, 66 | paramsSerializer(p) { 67 | return queryString.stringify(p, { encode: true }); 68 | }, 69 | headers: headers || { 'Content-Type': 'application/json' }, 70 | data, 71 | }; 72 | 73 | // Add a request interceptor 74 | axios.interceptors.request.use( 75 | (request) => request, 76 | (error) => Promise.reject(error) 77 | ); 78 | 79 | // Add a response interceptor 80 | axios.interceptors.response.use( 81 | (response) => response, 82 | (error) => Promise.reject(error) 83 | ); 84 | 85 | return axios(config).then( 86 | (response) => Promise.resolve(response.data), 87 | (response) => Promise.reject(response.data) 88 | ); 89 | } 90 | } 91 | 92 | export default ApiClient; 93 | 94 | -------------------------------------------------------------------------------- /develop/api/Base.js: -------------------------------------------------------------------------------- 1 | import { mandatory } from '../utils/valadation.helper'; 2 | 3 | class Base { 4 | constructor({ apiClient = mandatory('apiClient') }) { 5 | this.apiClient = apiClient; 6 | } 7 | } 8 | 9 | export default Base; 10 | 11 | -------------------------------------------------------------------------------- /develop/api/LocalStorage.js: -------------------------------------------------------------------------------- 1 | export default { 2 | set(property, value) { 3 | window.localStorage.setItem(property, JSON.stringify(value)); 4 | }, 5 | 6 | get(property) { 7 | return JSON.parse(localStorage.getItem(property)) || null; 8 | }, 9 | 10 | remove(property) { 11 | window.localStorage.removeItem(property); 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /develop/api/PokeballAPI.js: -------------------------------------------------------------------------------- 1 | import Base from './Base'; 2 | 3 | import ep from '../constants/endPoints.constant'; 4 | 5 | class PokeballAPI extends Base { 6 | getPokemon({ pokemonId }) { 7 | const url = ep.pokeball.getPokemon(pokemonId); 8 | 9 | return this.apiClient.get(url); 10 | } 11 | 12 | getPokemons({ limit, offset }) { 13 | const url = ep.pokeball.getPokemons(); 14 | const queryParams = (offset !== undefined) 15 | ? { limit, offset: (offset + limit) } 16 | : { limit }; 17 | 18 | return this.apiClient.get(url, {}, queryParams); 19 | } 20 | } 21 | 22 | export default PokeballAPI; 23 | -------------------------------------------------------------------------------- /develop/api/index.js: -------------------------------------------------------------------------------- 1 | import { baseUrl } from '../config/apiConfig'; 2 | import ApiClient from './ApiClient'; 3 | import { mandatory } from '../utils/valadation.helper'; 4 | 5 | import PokeballAPI from './PokeballAPI'; 6 | 7 | function apiFactory({ baseURL = mandatory('baseURL') } = {}) { 8 | const api = new ApiClient({ baseURL }); 9 | 10 | return { 11 | pokeball: new PokeballAPI({ apiClient: api }), 12 | }; 13 | } 14 | 15 | export default apiFactory({ baseURL: baseUrl }); 16 | -------------------------------------------------------------------------------- /develop/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { Provider } from 'react-redux'; 4 | 5 | import { Router } from 'react-router'; 6 | 7 | import routes from './config/appRoutes.jsx'; 8 | import configureStore from './store/root.store'; 9 | import appHistory from './config/appHistory'; 10 | 11 | const initialState = {}; 12 | const store = configureStore(initialState); 13 | 14 | ReactDOM.render( 15 | 16 | 17 | , 18 | 19 | document.getElementById('react-view') 20 | ); 21 | 22 | -------------------------------------------------------------------------------- /develop/assets/boot_scss/.csscomb.json: -------------------------------------------------------------------------------- 1 | { 2 | "always-semicolon": true, 3 | "block-indent": 2, 4 | "color-case": "lower", 5 | "color-shorthand": true, 6 | "element-case": "lower", 7 | "eof-newline": true, 8 | "leading-zero": false, 9 | "remove-empty-rulesets": true, 10 | "space-after-colon": 1, 11 | "space-after-combinator": 1, 12 | "space-before-selector-delimiter": 0, 13 | "space-between-declarations": "\n", 14 | "space-after-opening-brace": "\n", 15 | "space-before-closing-brace": "\n", 16 | "space-before-colon": 0, 17 | "space-before-combinator": 1, 18 | "space-before-opening-brace": 1, 19 | "strip-spaces": true, 20 | "unitless-zero": true, 21 | "vendor-prefix-align": true, 22 | "sort-order": [ 23 | [ 24 | "position", 25 | "top", 26 | "right", 27 | "bottom", 28 | "left", 29 | "z-index", 30 | "display", 31 | "float", 32 | "width", 33 | "min-width", 34 | "max-width", 35 | "height", 36 | "min-height", 37 | "max-height", 38 | "-webkit-box-sizing", 39 | "-moz-box-sizing", 40 | "box-sizing", 41 | "-webkit-appearance", 42 | "padding", 43 | "padding-top", 44 | "padding-right", 45 | "padding-bottom", 46 | "padding-left", 47 | "margin", 48 | "margin-top", 49 | "margin-right", 50 | "margin-bottom", 51 | "margin-left", 52 | "overflow", 53 | "overflow-x", 54 | "overflow-y", 55 | "-webkit-overflow-scrolling", 56 | "-ms-overflow-x", 57 | "-ms-overflow-y", 58 | "-ms-overflow-style", 59 | "clip", 60 | "clear", 61 | "font", 62 | "font-family", 63 | "font-size", 64 | "font-style", 65 | "font-weight", 66 | "font-variant", 67 | "font-size-adjust", 68 | "font-stretch", 69 | "font-effect", 70 | "font-emphasize", 71 | "font-emphasize-position", 72 | "font-emphasize-style", 73 | "font-smooth", 74 | "-webkit-hyphens", 75 | "-moz-hyphens", 76 | "hyphens", 77 | "line-height", 78 | "color", 79 | "text-align", 80 | "-webkit-text-align-last", 81 | "-moz-text-align-last", 82 | "-ms-text-align-last", 83 | "text-align-last", 84 | "text-emphasis", 85 | "text-emphasis-color", 86 | "text-emphasis-style", 87 | "text-emphasis-position", 88 | "text-decoration", 89 | "text-indent", 90 | "text-justify", 91 | "text-outline", 92 | "-ms-text-overflow", 93 | "text-overflow", 94 | "text-overflow-ellipsis", 95 | "text-overflow-mode", 96 | "text-shadow", 97 | "text-transform", 98 | "text-wrap", 99 | "-webkit-text-size-adjust", 100 | "-ms-text-size-adjust", 101 | "letter-spacing", 102 | "-ms-word-break", 103 | "word-break", 104 | "word-spacing", 105 | "-ms-word-wrap", 106 | "word-wrap", 107 | "-moz-tab-size", 108 | "-o-tab-size", 109 | "tab-size", 110 | "white-space", 111 | "vertical-align", 112 | "list-style", 113 | "list-style-position", 114 | "list-style-type", 115 | "list-style-image", 116 | "pointer-events", 117 | "-ms-touch-action", 118 | "touch-action", 119 | "cursor", 120 | "visibility", 121 | "zoom", 122 | "flex-direction", 123 | "flex-order", 124 | "flex-pack", 125 | "flex-align", 126 | "table-layout", 127 | "empty-cells", 128 | "caption-side", 129 | "border-spacing", 130 | "border-collapse", 131 | "content", 132 | "quotes", 133 | "counter-reset", 134 | "counter-increment", 135 | "resize", 136 | "-webkit-user-select", 137 | "-moz-user-select", 138 | "-ms-user-select", 139 | "-o-user-select", 140 | "user-select", 141 | "nav-index", 142 | "nav-up", 143 | "nav-right", 144 | "nav-down", 145 | "nav-left", 146 | "background", 147 | "background-color", 148 | "background-image", 149 | "-ms-filter:\\'progid:DXImageTransform.Microsoft.gradient", 150 | "filter:progid:DXImageTransform.Microsoft.gradient", 151 | "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader", 152 | "filter", 153 | "background-repeat", 154 | "background-attachment", 155 | "background-position", 156 | "background-position-x", 157 | "background-position-y", 158 | "-webkit-background-clip", 159 | "-moz-background-clip", 160 | "background-clip", 161 | "background-origin", 162 | "-webkit-background-size", 163 | "-moz-background-size", 164 | "-o-background-size", 165 | "background-size", 166 | "border", 167 | "border-color", 168 | "border-style", 169 | "border-width", 170 | "border-top", 171 | "border-top-color", 172 | "border-top-style", 173 | "border-top-width", 174 | "border-right", 175 | "border-right-color", 176 | "border-right-style", 177 | "border-right-width", 178 | "border-bottom", 179 | "border-bottom-color", 180 | "border-bottom-style", 181 | "border-bottom-width", 182 | "border-left", 183 | "border-left-color", 184 | "border-left-style", 185 | "border-left-width", 186 | "border-radius", 187 | "border-top-left-radius", 188 | "border-top-right-radius", 189 | "border-bottom-right-radius", 190 | "border-bottom-left-radius", 191 | "-webkit-border-image", 192 | "-moz-border-image", 193 | "-o-border-image", 194 | "border-image", 195 | "-webkit-border-image-source", 196 | "-moz-border-image-source", 197 | "-o-border-image-source", 198 | "border-image-source", 199 | "-webkit-border-image-slice", 200 | "-moz-border-image-slice", 201 | "-o-border-image-slice", 202 | "border-image-slice", 203 | "-webkit-border-image-width", 204 | "-moz-border-image-width", 205 | "-o-border-image-width", 206 | "border-image-width", 207 | "-webkit-border-image-outset", 208 | "-moz-border-image-outset", 209 | "-o-border-image-outset", 210 | "border-image-outset", 211 | "-webkit-border-image-repeat", 212 | "-moz-border-image-repeat", 213 | "-o-border-image-repeat", 214 | "border-image-repeat", 215 | "outline", 216 | "outline-width", 217 | "outline-style", 218 | "outline-color", 219 | "outline-offset", 220 | "-webkit-box-shadow", 221 | "-moz-box-shadow", 222 | "box-shadow", 223 | "filter:progid:DXImageTransform.Microsoft.Alpha(Opacity", 224 | "-ms-filter:\\'progid:DXImageTransform.Microsoft.Alpha", 225 | "opacity", 226 | "-ms-interpolation-mode", 227 | "-webkit-transition", 228 | "-moz-transition", 229 | "-ms-transition", 230 | "-o-transition", 231 | "transition", 232 | "-webkit-transition-delay", 233 | "-moz-transition-delay", 234 | "-ms-transition-delay", 235 | "-o-transition-delay", 236 | "transition-delay", 237 | "-webkit-transition-timing-function", 238 | "-moz-transition-timing-function", 239 | "-ms-transition-timing-function", 240 | "-o-transition-timing-function", 241 | "transition-timing-function", 242 | "-webkit-transition-duration", 243 | "-moz-transition-duration", 244 | "-ms-transition-duration", 245 | "-o-transition-duration", 246 | "transition-duration", 247 | "-webkit-transition-property", 248 | "-moz-transition-property", 249 | "-ms-transition-property", 250 | "-o-transition-property", 251 | "transition-property", 252 | "-webkit-transform", 253 | "-moz-transform", 254 | "-ms-transform", 255 | "-o-transform", 256 | "transform", 257 | "-webkit-transform-origin", 258 | "-moz-transform-origin", 259 | "-ms-transform-origin", 260 | "-o-transform-origin", 261 | "transform-origin", 262 | "-webkit-animation", 263 | "-moz-animation", 264 | "-ms-animation", 265 | "-o-animation", 266 | "animation", 267 | "-webkit-animation-name", 268 | "-moz-animation-name", 269 | "-ms-animation-name", 270 | "-o-animation-name", 271 | "animation-name", 272 | "-webkit-animation-duration", 273 | "-moz-animation-duration", 274 | "-ms-animation-duration", 275 | "-o-animation-duration", 276 | "animation-duration", 277 | "-webkit-animation-play-state", 278 | "-moz-animation-play-state", 279 | "-ms-animation-play-state", 280 | "-o-animation-play-state", 281 | "animation-play-state", 282 | "-webkit-animation-timing-function", 283 | "-moz-animation-timing-function", 284 | "-ms-animation-timing-function", 285 | "-o-animation-timing-function", 286 | "animation-timing-function", 287 | "-webkit-animation-delay", 288 | "-moz-animation-delay", 289 | "-ms-animation-delay", 290 | "-o-animation-delay", 291 | "animation-delay", 292 | "-webkit-animation-iteration-count", 293 | "-moz-animation-iteration-count", 294 | "-ms-animation-iteration-count", 295 | "-o-animation-iteration-count", 296 | "animation-iteration-count", 297 | "-webkit-animation-direction", 298 | "-moz-animation-direction", 299 | "-ms-animation-direction", 300 | "-o-animation-direction", 301 | "animation-direction" 302 | ] 303 | ] 304 | } 305 | -------------------------------------------------------------------------------- /develop/assets/boot_scss/_alert.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Base styles 3 | // 4 | 5 | .alert { 6 | padding: $alert-padding; 7 | margin-bottom: $spacer-y; 8 | border: $alert-border-width solid transparent; 9 | @include border-radius($alert-border-radius); 10 | 11 | // Improve alignment and spacing of inner content 12 | > p, 13 | > ul { 14 | margin-bottom: 0; 15 | } 16 | > p + p { 17 | margin-top: 5px; 18 | } 19 | } 20 | 21 | // Headings for larger alerts 22 | .alert-heading { 23 | // Specified to prevent conflicts of changing $headings-color 24 | color: inherit; 25 | } 26 | 27 | // Provide class for links that match alerts 28 | .alert-link { 29 | font-weight: $alert-link-font-weight; 30 | } 31 | 32 | 33 | // Dismissible alerts 34 | // 35 | // Expand the right padding and account for the close button's positioning. 36 | 37 | .alert-dismissible { 38 | padding-right: ($alert-padding + 20); 39 | 40 | // Adjust close link position 41 | .close { 42 | position: relative; 43 | top: -2px; 44 | right: -21px; 45 | color: inherit; 46 | } 47 | } 48 | 49 | 50 | // Alternate styles 51 | // 52 | // Generate contextual modifier classes for colorizing the alert. 53 | 54 | .alert-success { 55 | @include alert-variant($alert-success-bg, $alert-success-border, $alert-success-text); 56 | } 57 | .alert-info { 58 | @include alert-variant($alert-info-bg, $alert-info-border, $alert-info-text); 59 | } 60 | .alert-warning { 61 | @include alert-variant($alert-warning-bg, $alert-warning-border, $alert-warning-text); 62 | } 63 | .alert-danger { 64 | @include alert-variant($alert-danger-bg, $alert-danger-border, $alert-danger-text); 65 | } 66 | -------------------------------------------------------------------------------- /develop/assets/boot_scss/_animation.scss: -------------------------------------------------------------------------------- 1 | .fade { 2 | opacity: 0; 3 | transition: opacity .15s linear; 4 | 5 | &.in { 6 | opacity: 1; 7 | } 8 | } 9 | 10 | .collapse { 11 | display: none; 12 | 13 | &.in { 14 | display: block; 15 | } 16 | // tr&.in { display: table-row; } 17 | // tbody&.in { display: table-row-group; } 18 | } 19 | 20 | .collapsing { 21 | position: relative; 22 | height: 0; 23 | overflow: hidden; 24 | transition-timing-function: ease; 25 | transition-duration: .35s; 26 | transition-property: height; 27 | } 28 | -------------------------------------------------------------------------------- /develop/assets/boot_scss/_breadcrumb.scss: -------------------------------------------------------------------------------- 1 | .breadcrumb { 2 | padding: $breadcrumb-padding-vertical $breadcrumb-padding-horizontal; 3 | margin-bottom: $spacer-y; 4 | list-style: none; 5 | background-color: $breadcrumb-bg; 6 | @include border-radius($border-radius); 7 | @include clearfix; 8 | 9 | > li { 10 | float: left; 11 | 12 | + li::before { 13 | padding-right: .5rem; 14 | padding-left: .5rem; 15 | color: $breadcrumb-divider-color; 16 | content: "#{$breadcrumb-divider}"; 17 | } 18 | } 19 | 20 | > .active { 21 | color: $breadcrumb-active-color; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /develop/assets/boot_scss/_button-group.scss: -------------------------------------------------------------------------------- 1 | // Make the div behave like a button 2 | .btn-group, 3 | .btn-group-vertical { 4 | position: relative; 5 | display: inline-block; 6 | vertical-align: middle; // match .btn alignment given font-size hack above 7 | 8 | > .btn { 9 | position: relative; 10 | float: left; 11 | 12 | // Bring the "active" button to the front 13 | &:focus, 14 | &:active, 15 | &.active { 16 | z-index: 2; 17 | } 18 | @include hover { 19 | z-index: 2; 20 | } 21 | } 22 | } 23 | 24 | // Prevent double borders when buttons are next to each other 25 | .btn-group { 26 | .btn + .btn, 27 | .btn + .btn-group, 28 | .btn-group + .btn, 29 | .btn-group + .btn-group { 30 | margin-left: -$input-btn-border-width; 31 | } 32 | } 33 | 34 | // Optional: Group multiple button groups together for a toolbar 35 | .btn-toolbar { 36 | margin-left: -5px; // Offset the first child's margin 37 | @include clearfix(); 38 | 39 | .btn-group, 40 | .input-group { 41 | float: left; 42 | } 43 | 44 | > .btn, 45 | > .btn-group, 46 | > .input-group { 47 | margin-left: 5px; 48 | } 49 | } 50 | 51 | .btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { 52 | border-radius: 0; 53 | } 54 | 55 | // Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match 56 | .btn-group > .btn:first-child { 57 | margin-left: 0; 58 | 59 | &:not(:last-child):not(.dropdown-toggle) { 60 | @include border-right-radius(0); 61 | } 62 | } 63 | // Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it 64 | .btn-group > .btn:last-child:not(:first-child), 65 | .btn-group > .dropdown-toggle:not(:first-child) { 66 | @include border-left-radius(0); 67 | } 68 | 69 | // Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group) 70 | .btn-group > .btn-group { 71 | float: left; 72 | } 73 | .btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { 74 | border-radius: 0; 75 | } 76 | .btn-group > .btn-group:first-child:not(:last-child) { 77 | > .btn:last-child, 78 | > .dropdown-toggle { 79 | @include border-right-radius(0); 80 | } 81 | } 82 | .btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { 83 | @include border-left-radius(0); 84 | } 85 | 86 | // On active and open, don't show outline 87 | .btn-group .dropdown-toggle:active, 88 | .btn-group.open .dropdown-toggle { 89 | outline: 0; 90 | } 91 | 92 | 93 | // Sizing 94 | // 95 | // Remix the default button sizing classes into new ones for easier manipulation. 96 | 97 | .btn-group-sm > .btn { @extend .btn-sm; } 98 | .btn-group-lg > .btn { @extend .btn-lg; } 99 | 100 | 101 | // 102 | // Split button dropdowns 103 | // 104 | 105 | // Give the line between buttons some depth 106 | .btn-group > .btn + .dropdown-toggle { 107 | padding-right: 8px; 108 | padding-left: 8px; 109 | } 110 | .btn-group > .btn-lg + .dropdown-toggle { 111 | padding-right: 12px; 112 | padding-left: 12px; 113 | } 114 | 115 | // The clickable button for toggling the menu 116 | // Remove the gradient and set the same inset shadow as the :active state 117 | .btn-group.open .dropdown-toggle { 118 | @include box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); 119 | 120 | // Show no shadow for `.btn-link` since it has no other button styles. 121 | &.btn-link { 122 | @include box-shadow(none); 123 | } 124 | } 125 | 126 | 127 | // Reposition the caret 128 | .btn .caret { 129 | margin-left: 0; 130 | } 131 | // Carets in other button sizes 132 | .btn-lg .caret { 133 | border-width: $caret-width-lg $caret-width-lg 0; 134 | border-bottom-width: 0; 135 | } 136 | // Upside down carets for .dropup 137 | .dropup .btn-lg .caret { 138 | border-width: 0 $caret-width-lg $caret-width-lg; 139 | } 140 | 141 | 142 | 143 | // 144 | // Vertical button groups 145 | // 146 | 147 | .btn-group-vertical { 148 | > .btn, 149 | > .btn-group, 150 | > .btn-group > .btn { 151 | display: block; 152 | float: none; 153 | width: 100%; 154 | max-width: 100%; 155 | } 156 | 157 | // Clear floats so dropdown menus can be properly placed 158 | > .btn-group { 159 | @include clearfix(); 160 | 161 | > .btn { 162 | float: none; 163 | } 164 | } 165 | 166 | > .btn + .btn, 167 | > .btn + .btn-group, 168 | > .btn-group + .btn, 169 | > .btn-group + .btn-group { 170 | margin-top: -$input-btn-border-width; 171 | margin-left: 0; 172 | } 173 | } 174 | 175 | .btn-group-vertical > .btn { 176 | &:not(:first-child):not(:last-child) { 177 | border-radius: 0; 178 | } 179 | &:first-child:not(:last-child) { 180 | border-top-right-radius: $btn-border-radius; 181 | @include border-bottom-radius(0); 182 | } 183 | &:last-child:not(:first-child) { 184 | border-bottom-left-radius: $btn-border-radius; 185 | @include border-top-radius(0); 186 | } 187 | } 188 | .btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { 189 | border-radius: 0; 190 | } 191 | .btn-group-vertical > .btn-group:first-child:not(:last-child) { 192 | > .btn:last-child, 193 | > .dropdown-toggle { 194 | @include border-bottom-radius(0); 195 | } 196 | } 197 | .btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { 198 | @include border-top-radius(0); 199 | } 200 | 201 | 202 | // Checkbox and radio options 203 | // 204 | // In order to support the browser's form validation feedback, powered by the 205 | // `required` attribute, we have to "hide" the inputs via `clip`. We cannot use 206 | // `display: none;` or `visibility: hidden;` as that also hides the popover. 207 | // Simply visually hiding the inputs via `opacity` would leave them clickable in 208 | // certain cases which is prevented by using `clip` and `pointer-events`. 209 | // This way, we ensure a DOM element is visible to position the popover from. 210 | // 211 | // See https://github.com/twbs/bootstrap/pull/12794 and 212 | // https://github.com/twbs/bootstrap/pull/14559 for more information. 213 | 214 | [data-toggle="buttons"] { 215 | > .btn, 216 | > .btn-group > .btn { 217 | input[type="radio"], 218 | input[type="checkbox"] { 219 | position: absolute; 220 | clip: rect(0,0,0,0); 221 | pointer-events: none; 222 | } 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /develop/assets/boot_scss/_buttons.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Base styles 3 | // 4 | 5 | .btn { 6 | display: inline-block; 7 | font-weight: $btn-font-weight; 8 | text-align: center; 9 | white-space: nowrap; 10 | vertical-align: middle; 11 | cursor: pointer; 12 | user-select: none; 13 | border: $input-btn-border-width solid transparent; 14 | @include button-size($btn-padding-y, $btn-padding-x, $font-size-base, $line-height, $btn-border-radius); 15 | @include transition(all .2s ease-in-out); 16 | 17 | &, 18 | &:active, 19 | &.active { 20 | &:focus, 21 | &.focus { 22 | @include tab-focus(); 23 | } 24 | } 25 | 26 | @include hover-focus { 27 | text-decoration: none; 28 | } 29 | &.focus { 30 | text-decoration: none; 31 | } 32 | 33 | &:active, 34 | &.active { 35 | background-image: none; 36 | outline: 0; 37 | @include box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); 38 | } 39 | 40 | &.disabled, 41 | &:disabled { 42 | cursor: $cursor-disabled; 43 | opacity: .65; 44 | @include box-shadow(none); 45 | } 46 | } 47 | 48 | // Future-proof disabling of clicks on `` elements 49 | a.btn.disabled, 50 | fieldset[disabled] a.btn { 51 | pointer-events: none; 52 | } 53 | 54 | 55 | // 56 | // Alternate buttons 57 | // 58 | 59 | .btn-primary { 60 | @include button-variant($btn-primary-color, $btn-primary-bg, $btn-primary-border); 61 | } 62 | .btn-secondary { 63 | @include button-variant($btn-secondary-color, $btn-secondary-bg, $btn-secondary-border); 64 | } 65 | .btn-info { 66 | @include button-variant($btn-info-color, $btn-info-bg, $btn-info-border); 67 | } 68 | .btn-success { 69 | @include button-variant($btn-success-color, $btn-success-bg, $btn-success-border); 70 | } 71 | .btn-warning { 72 | @include button-variant($btn-warning-color, $btn-warning-bg, $btn-warning-border); 73 | } 74 | .btn-danger { 75 | @include button-variant($btn-danger-color, $btn-danger-bg, $btn-danger-border); 76 | } 77 | 78 | // Remove all backgrounds 79 | .btn-primary-outline { 80 | @include button-outline-variant($btn-primary-bg); 81 | } 82 | .btn-secondary-outline { 83 | @include button-outline-variant($btn-secondary-border); 84 | } 85 | .btn-info-outline { 86 | @include button-outline-variant($btn-info-bg); 87 | } 88 | .btn-success-outline { 89 | @include button-outline-variant($btn-success-bg); 90 | } 91 | .btn-warning-outline { 92 | @include button-outline-variant($btn-warning-bg); 93 | } 94 | .btn-danger-outline { 95 | @include button-outline-variant($btn-danger-bg); 96 | } 97 | 98 | 99 | // 100 | // Link buttons 101 | // 102 | 103 | // Make a button look and behave like a link 104 | .btn-link { 105 | font-weight: normal; 106 | color: $link-color; 107 | border-radius: 0; 108 | 109 | &, 110 | &:active, 111 | &.active, 112 | &:disabled { 113 | background-color: transparent; 114 | @include box-shadow(none); 115 | } 116 | &, 117 | &:focus, 118 | &:active { 119 | border-color: transparent; 120 | } 121 | @include hover { 122 | border-color: transparent; 123 | } 124 | @include hover-focus { 125 | color: $link-hover-color; 126 | text-decoration: $link-hover-decoration; 127 | background-color: transparent; 128 | } 129 | &:disabled { 130 | @include hover-focus { 131 | color: $btn-link-disabled-color; 132 | text-decoration: none; 133 | } 134 | } 135 | } 136 | 137 | 138 | // 139 | // Button Sizes 140 | // 141 | 142 | .btn-lg { 143 | // line-height: ensure even-numbered height of button next to large input 144 | @include button-size($btn-padding-y-lg, $btn-padding-x-lg, $font-size-lg, $line-height-lg, $btn-border-radius-lg); 145 | } 146 | .btn-sm { 147 | // line-height: ensure proper height of button next to small input 148 | @include button-size($btn-padding-y-sm, $btn-padding-x-sm, $font-size-sm, $line-height-sm, $btn-border-radius-sm); 149 | } 150 | 151 | 152 | // 153 | // Block button 154 | // 155 | 156 | .btn-block { 157 | display: block; 158 | width: 100%; 159 | } 160 | 161 | // Vertically space out multiple block buttons 162 | .btn-block + .btn-block { 163 | margin-top: 5px; 164 | } 165 | 166 | // Specificity overrides 167 | input[type="submit"], 168 | input[type="reset"], 169 | input[type="button"] { 170 | &.btn-block { 171 | width: 100%; 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /develop/assets/boot_scss/_card.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Base styles 3 | // 4 | 5 | .card { 6 | position: relative; 7 | display: block; 8 | margin-bottom: $card-spacer-y; 9 | background-color: $card-bg; 10 | border: $card-border-width solid $card-border-color; 11 | @include border-radius($card-border-radius); 12 | } 13 | 14 | .card-block { 15 | padding: $card-spacer-x; 16 | } 17 | 18 | .card-title { 19 | margin-bottom: $card-spacer-y; 20 | } 21 | 22 | .card-subtitle { 23 | margin-top: -($card-spacer-y / 2); 24 | margin-bottom: 0; 25 | } 26 | 27 | .card-text:last-child { 28 | margin-bottom: 0; 29 | } 30 | 31 | // .card-actions { 32 | // padding: $card-spacer-y $card-spacer-x; 33 | 34 | // .card-link + .card-link { 35 | // margin-left: $card-spacer-x; 36 | // } 37 | // } 38 | 39 | .card-link { 40 | @include hover { 41 | text-decoration: none; 42 | } 43 | 44 | + .card-link { 45 | margin-left: $card-spacer-x; 46 | } 47 | } 48 | 49 | @if $enable-rounded { 50 | .card { 51 | > .list-group:first-child { 52 | .list-group-item:first-child { 53 | border-radius: $card-border-radius $card-border-radius 0 0; 54 | } 55 | } 56 | 57 | > .list-group:last-child { 58 | .list-group-item:last-child { 59 | border-radius: 0 0 $card-border-radius $card-border-radius; 60 | } 61 | } 62 | } 63 | } 64 | 65 | 66 | // 67 | // Optional textual caps 68 | // 69 | 70 | .card-header { 71 | padding: $card-spacer-y $card-spacer-x; 72 | background-color: $card-cap-bg; 73 | border-bottom: $card-border-width solid $card-border-color; 74 | 75 | &:first-child { 76 | @include border-radius($card-border-radius-inner $card-border-radius-inner 0 0); 77 | } 78 | } 79 | 80 | .card-footer { 81 | padding: $card-spacer-y $card-spacer-x; 82 | background-color: $card-cap-bg; 83 | border-top: $card-border-width solid $card-border-color; 84 | 85 | &:last-child { 86 | @include border-radius(0 0 $card-border-radius-inner $card-border-radius-inner); 87 | } 88 | } 89 | 90 | 91 | // 92 | // Background variations 93 | // 94 | 95 | .card-primary { 96 | @include card-variant($brand-primary, $brand-primary); 97 | } 98 | .card-success { 99 | @include card-variant($brand-success, $brand-success); 100 | } 101 | .card-info { 102 | @include card-variant($brand-info, $brand-info); 103 | } 104 | .card-warning { 105 | @include card-variant($brand-warning, $brand-warning); 106 | } 107 | .card-danger { 108 | @include card-variant($brand-danger, $brand-danger); 109 | } 110 | 111 | // Remove all backgrounds 112 | .card-primary-outline { 113 | @include card-outline-variant($btn-primary-bg); 114 | } 115 | .card-secondary-outline { 116 | @include card-outline-variant($btn-secondary-border); 117 | } 118 | .card-info-outline { 119 | @include card-outline-variant($btn-info-bg); 120 | } 121 | .card-success-outline { 122 | @include card-outline-variant($btn-success-bg); 123 | } 124 | .card-warning-outline { 125 | @include card-outline-variant($btn-warning-bg); 126 | } 127 | .card-danger-outline { 128 | @include card-outline-variant($btn-danger-bg); 129 | } 130 | 131 | // 132 | // Inverse text within a card for use with dark backgrounds 133 | // 134 | 135 | .card-inverse { 136 | @include card-inverse; 137 | } 138 | 139 | // 140 | // Blockquote 141 | // 142 | 143 | .card-blockquote { 144 | padding: 0; 145 | margin-bottom: 0; 146 | border-left: 0; 147 | } 148 | 149 | // Card image 150 | .card-img { 151 | // margin: -1.325rem; 152 | @include border-radius(.25rem); 153 | } 154 | .card-img-overlay { 155 | position: absolute; 156 | top: 0; 157 | right: 0; 158 | bottom: 0; 159 | left: 0; 160 | padding: 1.25rem; 161 | } 162 | 163 | 164 | 165 | // Card image caps 166 | .card-img-top { 167 | @include border-radius($card-border-radius-inner $card-border-radius-inner 0 0); 168 | } 169 | .card-img-bottom { 170 | @include border-radius(0 0 $card-border-radius-inner $card-border-radius-inner); 171 | } 172 | 173 | 174 | // 175 | // Card set 176 | // 177 | 178 | @if $enable-flex { 179 | @include media-breakpoint-up(sm) { 180 | .card-deck { 181 | display: flex; 182 | flex-flow: row wrap; 183 | margin-right: -.625rem; 184 | margin-left: -.625rem; 185 | 186 | .card { 187 | flex: 1 0 0; 188 | margin-right: .625rem; 189 | margin-left: .625rem; 190 | } 191 | } 192 | } 193 | } @else { 194 | @include media-breakpoint-up(sm) { 195 | .card-deck { 196 | display: table; 197 | table-layout: fixed; 198 | border-spacing: 1.25rem 0; 199 | 200 | .card { 201 | display: table-cell; 202 | width: 1%; 203 | vertical-align: top; 204 | } 205 | } 206 | .card-deck-wrapper { 207 | margin-right: -1.25rem; 208 | margin-left: -1.25rem; 209 | } 210 | } 211 | } 212 | 213 | // 214 | // Card groups 215 | // 216 | 217 | @include media-breakpoint-up(sm) { 218 | .card-group { 219 | @if $enable-flex { 220 | display: flex; 221 | flex-flow: row wrap; 222 | } @else { 223 | display: table; 224 | width: 100%; 225 | table-layout: fixed; 226 | } 227 | 228 | .card { 229 | @if $enable-flex { 230 | flex: 1 0 0; 231 | } @else { 232 | display: table-cell; 233 | vertical-align: top; 234 | } 235 | 236 | + .card { 237 | margin-left: 0; 238 | border-left: 0; 239 | } 240 | 241 | // Handle rounded corners 242 | @if $enable-rounded { 243 | &:first-child { 244 | @include border-right-radius(0); 245 | 246 | .card-img-top { 247 | border-top-right-radius: 0; 248 | } 249 | .card-img-bottom { 250 | border-bottom-right-radius: 0; 251 | } 252 | } 253 | &:last-child { 254 | @include border-left-radius(0); 255 | 256 | .card-img-top { 257 | border-top-left-radius: 0; 258 | } 259 | .card-img-bottom { 260 | border-bottom-left-radius: 0; 261 | } 262 | } 263 | 264 | &:not(:first-child):not(:last-child) { 265 | border-radius: 0; 266 | 267 | .card-img-top, 268 | .card-img-bottom { 269 | border-radius: 0; 270 | } 271 | } 272 | } 273 | } 274 | } 275 | } 276 | 277 | 278 | // 279 | // Card 280 | // 281 | 282 | @include media-breakpoint-up(sm) { 283 | .card-columns { 284 | column-count: 3; 285 | column-gap: 1.25rem; 286 | 287 | .card { 288 | display: inline-block; 289 | width: 100%; // Don't let them exceed the column width 290 | } 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /develop/assets/boot_scss/_carousel.scss: -------------------------------------------------------------------------------- 1 | // Wrapper for the slide container and indicators 2 | .carousel { 3 | position: relative; 4 | } 5 | 6 | .carousel-inner { 7 | position: relative; 8 | width: 100%; 9 | overflow: hidden; 10 | 11 | > .carousel-item { 12 | position: relative; 13 | display: none; 14 | transition: .6s ease-in-out left; 15 | 16 | // Account for jankitude on images 17 | > img, 18 | > a > img { 19 | @extend .img-fluid; 20 | line-height: 1; 21 | } 22 | 23 | // WebKit CSS3 transforms for supported devices 24 | @media all and (transform-3d), (-webkit-transform-3d) { 25 | transition: transform .6s ease-in-out; 26 | backface-visibility: hidden; 27 | perspective: 1000px; 28 | 29 | &.next, 30 | &.active.right { 31 | left: 0; 32 | transform: translate3d(100%, 0, 0); 33 | } 34 | &.prev, 35 | &.active.left { 36 | left: 0; 37 | transform: translate3d(-100%, 0, 0); 38 | } 39 | &.next.left, 40 | &.prev.right, 41 | &.active { 42 | left: 0; 43 | transform: translate3d(0, 0, 0); 44 | } 45 | } 46 | } 47 | 48 | > .active, 49 | > .next, 50 | > .prev { 51 | display: block; 52 | } 53 | 54 | > .active { 55 | left: 0; 56 | } 57 | 58 | > .next, 59 | > .prev { 60 | position: absolute; 61 | top: 0; 62 | width: 100%; 63 | } 64 | 65 | > .next { 66 | left: 100%; 67 | } 68 | > .prev { 69 | left: -100%; 70 | } 71 | > .next.left, 72 | > .prev.right { 73 | left: 0; 74 | } 75 | 76 | > .active.left { 77 | left: -100%; 78 | } 79 | > .active.right { 80 | left: 100%; 81 | } 82 | } 83 | 84 | 85 | // 86 | // Left/right controls for nav 87 | // 88 | 89 | .carousel-control { 90 | position: absolute; 91 | top: 0; 92 | bottom: 0; 93 | left: 0; 94 | width: $carousel-control-width; 95 | font-size: $carousel-control-font-size; 96 | color: $carousel-control-color; 97 | text-align: center; 98 | text-shadow: $carousel-text-shadow; 99 | opacity: $carousel-control-opacity; 100 | // We can't have this transition here because WebKit cancels the carousel 101 | // animation if you trip this while in the middle of another animation. 102 | 103 | // Set gradients for backgrounds 104 | &.left { 105 | @include gradient-horizontal($start-color: rgba(0,0,0,.5), $end-color: rgba(0,0,0,.0001)); 106 | } 107 | &.right { 108 | right: 0; 109 | left: auto; 110 | @include gradient-horizontal($start-color: rgba(0,0,0,.0001), $end-color: rgba(0,0,0,.5)); 111 | } 112 | 113 | // Hover/focus state 114 | @include hover-focus { 115 | color: $carousel-control-color; 116 | text-decoration: none; 117 | outline: 0; 118 | opacity: .9; 119 | } 120 | 121 | // Toggles 122 | .icon-prev, 123 | .icon-next { 124 | position: absolute; 125 | top: 50%; 126 | z-index: 5; 127 | display: inline-block; 128 | width: 20px; 129 | height: 20px; 130 | margin-top: -10px; 131 | font-family: serif; 132 | line-height: 1; 133 | } 134 | .icon-prev { 135 | left: 50%; 136 | margin-left: -10px; 137 | } 138 | .icon-next { 139 | right: 50%; 140 | margin-right: -10px; 141 | } 142 | 143 | .icon-prev { 144 | &::before { 145 | content: "\2039";// SINGLE LEFT-POINTING ANGLE QUOTATION MARK (U+2039) 146 | } 147 | } 148 | .icon-next { 149 | &::before { 150 | content: "\203a";// SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (U+203A) 151 | } 152 | } 153 | } 154 | 155 | 156 | // Optional indicator pips 157 | // 158 | // Add an unordered list with the following class and add a list item for each 159 | // slide your carousel holds. 160 | 161 | .carousel-indicators { 162 | position: absolute; 163 | bottom: 10px; 164 | left: 50%; 165 | z-index: 15; 166 | width: 60%; 167 | padding-left: 0; 168 | margin-left: -30%; 169 | text-align: center; 170 | list-style: none; 171 | 172 | li { 173 | display: inline-block; 174 | width: 10px; 175 | height: 10px; 176 | margin: 1px; 177 | text-indent: -999px; 178 | cursor: pointer; 179 | // IE9 hack for event handling 180 | // 181 | // Internet Explorer 9 does not properly handle clicks on elements with a `background-color` of `transparent`, 182 | // so we use `rgba(0,0,0,0)` instead since it's a non-buggy equivalent. 183 | // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Internet_Explorer 184 | background-color: rgba(0,0,0,0); // IE9 185 | border: 1px solid $carousel-indicator-border-color; 186 | border-radius: 10px; 187 | } 188 | .active { 189 | width: 12px; 190 | height: 12px; 191 | margin: 0; 192 | background-color: $carousel-indicator-active-bg; 193 | } 194 | } 195 | 196 | 197 | // Optional captions 198 | // 199 | // Hidden by default for smaller viewports. 200 | 201 | .carousel-caption { 202 | position: absolute; 203 | right: 15%; 204 | bottom: 20px; 205 | left: 15%; 206 | z-index: 10; 207 | padding-top: 20px; 208 | padding-bottom: 20px; 209 | color: $carousel-caption-color; 210 | text-align: center; 211 | text-shadow: $carousel-text-shadow; 212 | 213 | .btn { 214 | text-shadow: none; // No shadow for button elements in carousel-caption 215 | } 216 | } 217 | 218 | 219 | // 220 | // Responsive variations 221 | // 222 | 223 | @include media-breakpoint-up(sm) { 224 | // Scale up the controls a smidge 225 | .carousel-control { 226 | .icon-prev, 227 | .icon-next { 228 | width: 30px; 229 | height: 30px; 230 | margin-top: -15px; 231 | font-size: 30px; 232 | } 233 | .icon-prev { 234 | margin-left: -15px; 235 | } 236 | .icon-next { 237 | margin-right: -15px; 238 | } 239 | } 240 | 241 | // Show and left align the captions 242 | .carousel-caption { 243 | right: 20%; 244 | left: 20%; 245 | padding-bottom: 30px; 246 | } 247 | 248 | // Move up the indicators 249 | .carousel-indicators { 250 | bottom: 20px; 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /develop/assets/boot_scss/_close.scss: -------------------------------------------------------------------------------- 1 | .close { 2 | float: right; 3 | font-size: ($font-size-base * 1.5); 4 | font-weight: $close-font-weight; 5 | line-height: 1; 6 | color: $close-color; 7 | text-shadow: $close-text-shadow; 8 | opacity: .2; 9 | 10 | @include hover-focus { 11 | color: $close-color; 12 | text-decoration: none; 13 | cursor: pointer; 14 | opacity: .5; 15 | } 16 | } 17 | 18 | // Additional properties for button version 19 | // iOS requires the button element instead of an anchor tag. 20 | // If you want the anchor version, it requires `href="#"`. 21 | // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile 22 | button.close { 23 | padding: 0; 24 | cursor: pointer; 25 | background: transparent; 26 | border: 0; 27 | -webkit-appearance: none; 28 | } 29 | -------------------------------------------------------------------------------- /develop/assets/boot_scss/_code.scss: -------------------------------------------------------------------------------- 1 | // Inline and block code styles 2 | code, 3 | kbd, 4 | pre, 5 | samp { 6 | font-family: $font-family-monospace; 7 | } 8 | 9 | // Inline code 10 | code { 11 | padding: .2rem .4rem; 12 | font-size: 90%; 13 | color: $code-color; 14 | background-color: $code-bg; 15 | @include border-radius($border-radius); 16 | } 17 | 18 | // User input typically entered via keyboard 19 | kbd { 20 | padding: .2rem .4rem; 21 | font-size: 90%; 22 | color: $kbd-color; 23 | background-color: $kbd-bg; 24 | @include border-radius($border-radius-sm); 25 | @include box-shadow(inset 0 -.1rem 0 rgba(0,0,0,.25)); 26 | 27 | kbd { 28 | padding: 0; 29 | font-size: 100%; 30 | font-weight: $nested-kbd-font-weight; 31 | @include box-shadow(none); 32 | } 33 | } 34 | 35 | // Blocks of code 36 | pre { 37 | display: block; 38 | margin-top: 0; 39 | margin-bottom: 1rem; 40 | font-size: 90%; 41 | line-height: $line-height; 42 | color: $pre-color; 43 | 44 | // Account for some code outputs that place code tags in pre tags 45 | code { 46 | padding: 0; 47 | font-size: inherit; 48 | color: inherit; 49 | background-color: transparent; 50 | border-radius: 0; 51 | } 52 | } 53 | 54 | // Enable scrollable blocks of code 55 | .pre-scrollable { 56 | max-height: $pre-scrollable-max-height; 57 | overflow-y: scroll; 58 | } 59 | -------------------------------------------------------------------------------- /develop/assets/boot_scss/_custom-forms.scss: -------------------------------------------------------------------------------- 1 | // Embedded icons from Open Iconic. 2 | // Released under MIT and copyright 2014 Waybury. 3 | // http://useiconic.com/open 4 | 5 | 6 | // Checkboxes and radios 7 | // 8 | // Base class takes care of all the key behavioral aspects. 9 | 10 | .c-input { 11 | position: relative; 12 | display: inline; 13 | padding-left: 1.5rem; 14 | color: #555; 15 | cursor: pointer; 16 | 17 | > input { 18 | position: absolute; 19 | z-index: -1; // Put the input behind the label so it doesn't overlay text 20 | opacity: 0; 21 | 22 | &:checked ~ .c-indicator { 23 | color: #fff; 24 | background-color: #0074d9; 25 | @include box-shadow(none); 26 | } 27 | 28 | &:focus ~ .c-indicator { 29 | // the mixin is not used here to make sure there is feedback 30 | box-shadow: 0 0 0 .075rem #fff, 0 0 0 .2rem #0074d9; 31 | } 32 | 33 | &:active ~ .c-indicator { 34 | color: #fff; 35 | background-color: #84c6ff; 36 | @include box-shadow(none); 37 | } 38 | } 39 | 40 | + .c-input { 41 | margin-left: 1rem; 42 | } 43 | } 44 | 45 | // Custom indicator 46 | // 47 | // Generates a shadow element to create our makeshift checkbox/radio background. 48 | 49 | .c-indicator { 50 | position: absolute; 51 | top: 0; 52 | left: 0; 53 | display: block; 54 | width: 1rem; 55 | height: 1rem; 56 | font-size: 65%; 57 | line-height: 1rem; 58 | color: #eee; 59 | text-align: center; 60 | user-select: none; 61 | background-color: #eee; 62 | background-repeat: no-repeat; 63 | background-position: center center; 64 | background-size: 50% 50%; 65 | @include box-shadow(inset 0 .125rem .125rem rgba(0,0,0,.1)); 66 | } 67 | 68 | // Checkboxes 69 | // 70 | // Tweak just a few things for checkboxes. 71 | 72 | .c-checkbox { 73 | .c-indicator { 74 | border-radius: .25rem; 75 | } 76 | 77 | input:checked ~ .c-indicator { 78 | background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNy4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSIwIDAgOCA4IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA4IDgiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPHBhdGggZmlsbD0iI0ZGRkZGRiIgZD0iTTYuNCwxTDUuNywxLjdMMi45LDQuNUwyLjEsMy43TDEuNCwzTDAsNC40bDAuNywwLjdsMS41LDEuNWwwLjcsMC43bDAuNy0wLjdsMy41LTMuNWwwLjctMC43TDYuNCwxTDYuNCwxeiINCgkvPg0KPC9zdmc+DQo=); 79 | } 80 | 81 | input:indeterminate ~ .c-indicator { 82 | background-color: #0074d9; 83 | background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNy4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iOHB4IiBoZWlnaHQ9IjhweCIgdmlld0JveD0iMCAwIDggOCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgOCA4IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik0wLDN2Mmg4VjNIMHoiLz4NCjwvc3ZnPg0K); 84 | @include box-shadow(none); 85 | } 86 | } 87 | 88 | // Radios 89 | // 90 | // Tweak just a few things for radios. 91 | 92 | .c-radio { 93 | .c-indicator { 94 | border-radius: 50%; 95 | } 96 | 97 | input:checked ~ .c-indicator { 98 | background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNy4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSIwIDAgOCA4IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA4IDgiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPHBhdGggZmlsbD0iI0ZGRkZGRiIgZD0iTTQsMUMyLjMsMSwxLDIuMywxLDRzMS4zLDMsMywzczMtMS4zLDMtM1M1LjcsMSw0LDF6Ii8+DQo8L3N2Zz4NCg==); 99 | } 100 | } 101 | 102 | 103 | // Layout options 104 | // 105 | // By default radios and checkboxes are `inline-block` with no additional spacing 106 | // set. Use these optional classes to tweak the layout. 107 | 108 | .c-inputs-stacked { 109 | .c-input { 110 | display: inline; 111 | 112 | &::after { 113 | display: block; 114 | margin-bottom: .25rem; 115 | content: ""; 116 | } 117 | 118 | + .c-input { 119 | margin-left: 0; 120 | } 121 | } 122 | } 123 | 124 | 125 | // Select 126 | // 127 | // Replaces the browser default select with a custom one, mostly pulled from 128 | // http://primercss.io. 129 | // 130 | // Includes IE9-specific hacks (noted by ` \9`). 131 | 132 | .c-select { 133 | display: inline-block; 134 | max-width: 100%; 135 | padding: .375rem 1.75rem .375rem .75rem; 136 | padding-right: .75rem \9; 137 | color: $input-color; 138 | vertical-align: middle; 139 | background: #fff url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAUCAMAAACzvE1FAAAADFBMVEUzMzMzMzMzMzMzMzMKAG/3AAAAA3RSTlMAf4C/aSLHAAAAPElEQVR42q3NMQ4AIAgEQTn//2cLdRKppSGzBYwzVXvznNWs8C58CiussPJj8h6NwgorrKRdTvuV9v16Afn0AYFOB7aYAAAAAElFTkSuQmCC) no-repeat right .75rem center; 140 | background-image: none \9; 141 | background-size: 8px 10px; 142 | border: $input-btn-border-width solid $input-border-color; 143 | // Use vendor prefixes as `appearance` isn't part of the CSS spec. 144 | -moz-appearance: none; 145 | -webkit-appearance: none; 146 | 147 | &:focus { 148 | border-color: #51a7e8; 149 | outline: none; 150 | @include box-shadow(inset 0 1px 2px rgba(0, 0, 0, 0.075), 0 0 5px rgba(81, 167, 232, 0.5)); 151 | } 152 | 153 | // Hides the default caret in IE11 154 | &::-ms-expand { 155 | opacity: 0; 156 | } 157 | } 158 | 159 | .c-select-sm { 160 | padding-top: 3px; 161 | padding-bottom: 3px; 162 | font-size: 12px; 163 | 164 | &:not([multiple]) { 165 | height: 26px; 166 | min-height: 26px; 167 | } 168 | } 169 | 170 | 171 | // File 172 | // 173 | // Custom file input. 174 | 175 | .file { 176 | position: relative; 177 | display: inline-block; 178 | height: 2.5rem; 179 | cursor: pointer; 180 | } 181 | .file input { 182 | min-width: 14rem; 183 | margin: 0; 184 | filter: alpha(opacity = 0); 185 | opacity: 0; 186 | } 187 | .file-custom { 188 | position: absolute; 189 | top: 0; 190 | right: 0; 191 | left: 0; 192 | z-index: 5; 193 | height: 2.5rem; 194 | padding: .5rem 1rem; 195 | line-height: 1.5; 196 | color: #555; 197 | user-select: none; 198 | background-color: #fff; 199 | border: $input-btn-border-width solid #ddd; 200 | border-radius: .25rem; 201 | @include box-shadow(inset 0 .2rem .4rem rgba(0,0,0,.05)); 202 | } 203 | .file-custom::after { 204 | content: "Choose file..."; 205 | } 206 | .file-custom::before { 207 | position: absolute; 208 | top: -.075rem; 209 | right: -.075rem; 210 | bottom: -.075rem; 211 | z-index: 6; 212 | display: block; 213 | height: 2.5rem; 214 | padding: .5rem 1rem; 215 | line-height: 1.5; 216 | color: #555; 217 | content: "Browse"; 218 | background-color: #eee; 219 | border: $input-btn-border-width solid #ddd; 220 | border-radius: 0 .25rem .25rem 0; 221 | } 222 | 223 | // Focus state 224 | .file input:focus ~ .file-custom { 225 | @include box-shadow(0 0 0 .075rem #fff, 0 0 0 .2rem #0074d9); 226 | } 227 | -------------------------------------------------------------------------------- /develop/assets/boot_scss/_dropdown.scss: -------------------------------------------------------------------------------- 1 | // The dropdown wrapper (`
`) 2 | .dropup, 3 | .dropdown { 4 | position: relative; 5 | } 6 | 7 | .dropdown-toggle { 8 | // Generate the caret automatically 9 | &::after { 10 | display: inline-block; 11 | width: 0; 12 | height: 0; 13 | margin-right: .25rem; 14 | margin-left: .25rem; 15 | vertical-align: middle; 16 | content: ""; 17 | border-top: $caret-width solid; 18 | border-right: $caret-width solid transparent; 19 | border-left: $caret-width solid transparent; 20 | } 21 | 22 | // Prevent the focus on the dropdown toggle when closing dropdowns 23 | &:focus { 24 | outline: 0; 25 | } 26 | } 27 | 28 | .dropup { 29 | .dropdown-toggle { 30 | &::after { 31 | border-top: 0; 32 | border-bottom: $caret-width solid; 33 | } 34 | } 35 | } 36 | 37 | // The dropdown menu 38 | .dropdown-menu { 39 | position: absolute; 40 | top: 100%; 41 | left: 0; 42 | z-index: $zindex-dropdown; 43 | display: none; // none by default, but block on "open" of the menu 44 | float: left; 45 | min-width: 160px; 46 | padding: 5px 0; 47 | margin: 2px 0 0; // override default ul 48 | font-size: $font-size-base; 49 | color: $body-color; 50 | text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer) 51 | list-style: none; 52 | background-color: $dropdown-bg; 53 | background-clip: padding-box; 54 | border: $dropdown-border-width solid $dropdown-border-color; 55 | @include border-radius($border-radius); 56 | @include box-shadow(0 6px 12px rgba(0,0,0,.175)); 57 | } 58 | 59 | // Dividers (basically an `
`) within the dropdown 60 | .dropdown-divider { 61 | @include nav-divider($dropdown-divider-bg); 62 | } 63 | 64 | // Links, buttons, and more within the dropdown menu 65 | // 66 | // `