Compare commits
148 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d35cab0a67 | |||
| b4a094bc89 | |||
| e43706e2a6 | |||
| 2befc91d90 | |||
| 7a35f5177c | |||
| 6efc3ea790 | |||
| ef0f3ffbfb | |||
| 1e14cc695e | |||
| c074ccf460 | |||
| 5436469128 | |||
| 32714efeb9 | |||
| cf777571dc | |||
| 44b4340a91 | |||
| 24e3171311 | |||
| 575c1a11c2 | |||
| 47b8421d35 | |||
| 38d853a272 | |||
| 0320d6149f | |||
| ba9b3530fa | |||
| e78832e2c8 | |||
| 804060ad34 | |||
| 840519a437 | |||
| 7c54ebcbd0 | |||
| d07c0d67bf | |||
| a0ee2f7bb8 | |||
| d070288e17 | |||
| 681f80f6ab | |||
| eb5c87308a | |||
| 3e7fe1b5be | |||
| 552ca3026d | |||
| ba50e5a878 | |||
| acd1ce28f4 | |||
| 1b2b1bd447 | |||
| 7083d2369d | |||
| 8fed06b494 | |||
| 75650c19ab | |||
| ade47dadde | |||
| eaec617756 | |||
| 2199394abc | |||
| 93a18a8a20 | |||
| 2190b7af68 | |||
| 7da4956f1e | |||
| 31e27865bb | |||
| 967f213cba | |||
| 0e556aa794 | |||
| e2ac6099e3 | |||
| a4088e7acf | |||
| 550bd48fdb | |||
| f0f82a96fb | |||
| 8d303ea5af | |||
| c0b5c36115 | |||
| ba3c707da3 | |||
| b09f19f399 | |||
| 539f32911d | |||
| e009caaf25 | |||
| 1761ffbd3b | |||
| 33d6499dd4 | |||
| 3839eb2be4 | |||
| a04ab09ed6 | |||
| 89d6b920b1 | |||
| d425ed6b9e | |||
| e4215557c8 | |||
| fcc4045a4e | |||
| 5bcce71b36 | |||
| c9abec8a46 | |||
| 62e195d186 | |||
| e5f69a5e9a | |||
| e31ff492d5 | |||
| 99bbcda709 | |||
| 8aa941e704 | |||
| fdda30da67 | |||
| 399ee27af9 | |||
| d2c43b9ef1 | |||
| 7e6c6a037d | |||
| e1f19a1832 | |||
| 7b5cea3613 | |||
| 5d875d23e2 | |||
| e24fc6c81d | |||
| 72c6266474 | |||
| 4a711788f6 | |||
| c373c901a2 | |||
| 32846194b6 | |||
| 1a5d33b1f0 | |||
| 0e330a6529 | |||
| e9f0638ca2 | |||
| ce8fef79dc | |||
| b9fa0e2d60 | |||
| 41a04d6b53 | |||
| e527099b00 | |||
| 7e04cef964 | |||
| cb1208acaa | |||
| f985ce6402 | |||
| f54a36f240 | |||
| 4182f0993c | |||
| 28f7466595 | |||
| 6cfd4240de | |||
| 6e24015f76 | |||
| 5390c5cea8 | |||
| adcc81ba52 | |||
| 31a1f21dcd | |||
| b7316e15a2 | |||
| ad92ffa447 | |||
| 45044c0f61 | |||
| cc7c47cbab | |||
| 27f4006f05 | |||
| 576313a7b0 | |||
| 13c3feaa69 | |||
| a0dd8b925b | |||
| 21f187cac2 | |||
| d8951caddb | |||
| 206c657b98 | |||
| 6e262537a4 | |||
| 0587559939 | |||
| 939baec1a8 | |||
| 429f670c12 | |||
| b21d2ad864 | |||
| 8f841522a3 | |||
| fa7db66f4e | |||
| 499b22af15 | |||
| f992fb10bd | |||
| 603224e906 | |||
| bbea07b2a7 | |||
| b9d78efaff | |||
| 1a58099413 | |||
| e2d02d357c | |||
| 0812ff3467 | |||
| 6dafc26deb | |||
| a418ddbe52 | |||
| 6a5eb5a313 | |||
| 6354b03717 | |||
| 5d6b4a5255 | |||
| 3f0b1ccbcd | |||
| f00ecfd9ab | |||
| ee0d3370b1 | |||
| 73d7ed52dd | |||
| 6a31b0d579 | |||
| 869c4c0ae9 | |||
| 4be696ea62 | |||
| 83a0d56314 | |||
| 0d1418e595 | |||
| 7db521a72a | |||
| e7ff606891 | |||
| 60de3d8a31 | |||
| f12740dfa8 | |||
| 020a1c5682 | |||
| 576e2741a9 | |||
| bc19363bf9 | |||
| 72cf4b495e |
+1
-1
@@ -5,5 +5,5 @@
|
|||||||
|
|
||||||
Setting `disableAnalytics` to true will prevent any data from being sent.
|
Setting `disableAnalytics` to true will prevent any data from being sent.
|
||||||
*/
|
*/
|
||||||
"disableAnalytics": false
|
"disableAnalytics": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 2017,
|
||||||
|
sourceType: 'module'
|
||||||
|
},
|
||||||
|
globals: {
|
||||||
|
console: true
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
'ember'
|
||||||
|
],
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:ember/recommended'
|
||||||
|
],
|
||||||
|
env: {
|
||||||
|
browser: true
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'ember/avoid-leaking-state-in-ember-objects': 'warn',
|
||||||
|
'no-console': 'off'
|
||||||
|
},
|
||||||
|
overrides: [
|
||||||
|
// node files
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
'ember-cli-build.js',
|
||||||
|
'testem.js',
|
||||||
|
'config/**/*.js',
|
||||||
|
'lib/*/index.js'
|
||||||
|
],
|
||||||
|
parserOptions: {
|
||||||
|
sourceType: 'script',
|
||||||
|
ecmaVersion: 2015
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
browser: false,
|
||||||
|
node: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
@@ -14,4 +14,11 @@
|
|||||||
/coverage/*
|
/coverage/*
|
||||||
/libpeerconnection.log
|
/libpeerconnection.log
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
|
yarn-error.log
|
||||||
testem.log
|
testem.log
|
||||||
|
.tm_properties
|
||||||
|
|
||||||
|
# ember-try
|
||||||
|
.node_modules.ember-try/
|
||||||
|
bower.json.ember-try
|
||||||
|
package.json.ember-try
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
{
|
|
||||||
"predef": [
|
|
||||||
"document",
|
|
||||||
"window",
|
|
||||||
"-Promise"
|
|
||||||
],
|
|
||||||
"browser": true,
|
|
||||||
"boss": true,
|
|
||||||
"curly": true,
|
|
||||||
"debug": false,
|
|
||||||
"devel": true,
|
|
||||||
"eqeqeq": true,
|
|
||||||
"evil": true,
|
|
||||||
"forin": false,
|
|
||||||
"immed": false,
|
|
||||||
"laxbreak": false,
|
|
||||||
"newcap": true,
|
|
||||||
"noarg": true,
|
|
||||||
"noempty": false,
|
|
||||||
"nonew": false,
|
|
||||||
"nomen": false,
|
|
||||||
"onevar": false,
|
|
||||||
"plusplus": false,
|
|
||||||
"regexp": false,
|
|
||||||
"undef": true,
|
|
||||||
"sub": true,
|
|
||||||
"strict": false,
|
|
||||||
"white": false,
|
|
||||||
"eqnull": true,
|
|
||||||
"esversion": 6,
|
|
||||||
"unused": true
|
|
||||||
}
|
|
||||||
+10
-9
@@ -4,22 +4,23 @@ node_js:
|
|||||||
- "6"
|
- "6"
|
||||||
|
|
||||||
sudo: false
|
sudo: false
|
||||||
|
dist: trusty
|
||||||
|
|
||||||
|
addons:
|
||||||
|
chrome: stable
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
- $HOME/.npm
|
- $HOME/.npm
|
||||||
- $HOME/.cache # includes bowers cache
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
# See https://git.io/vdao3 for details.
|
||||||
|
- JOBS=1
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- npm config set spin false
|
- npm config set spin false
|
||||||
- npm install -g bower
|
|
||||||
- bower --version
|
|
||||||
- npm install phantomjs-prebuilt
|
|
||||||
- node_modules/phantomjs-prebuilt/bin/phantomjs --version
|
|
||||||
|
|
||||||
install:
|
|
||||||
- npm install
|
|
||||||
- bower install
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
|
- npm run lint:js
|
||||||
- npm test
|
- npm test
|
||||||
|
|||||||
@@ -10,22 +10,24 @@ A short introduction of this app could easily go here.
|
|||||||
You will need the following things properly installed on your computer.
|
You will need the following things properly installed on your computer.
|
||||||
|
|
||||||
* [Git](https://git-scm.com/)
|
* [Git](https://git-scm.com/)
|
||||||
* [Node.js](https://nodejs.org/) (with NPM)
|
* [Node.js](https://nodejs.org/) (with npm)
|
||||||
* [Bower](https://bower.io/)
|
|
||||||
* [Ember CLI](https://ember-cli.com/)
|
* [Ember CLI](https://ember-cli.com/)
|
||||||
* [PhantomJS](http://phantomjs.org/)
|
* [Google Chrome](https://google.com/chrome/)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
* `git clone <repository-url>` this repository
|
* `git clone git@github.com:67P/kredits-web.git` this repository
|
||||||
* `cd kredits-web`
|
* `cd kredits-web`
|
||||||
* `npm install`
|
* `npm install`
|
||||||
* `bower install`
|
|
||||||
|
|
||||||
## Running / Development
|
## Running / Development
|
||||||
|
|
||||||
* `ember serve`
|
|
||||||
|
* `ember serve` - by default kredits-web connects to the Ethreum Kovan network
|
||||||
* Visit your app at [http://localhost:4200](http://localhost:4200).
|
* Visit your app at [http://localhost:4200](http://localhost:4200).
|
||||||
|
* Visit your tests at [http://localhost:4200/tests](http://localhost:4200/tests).
|
||||||
|
|
||||||
|
See [working with locally deployed contracts](https://github.com/67P/kredits-web#working-with-locally-deployed-contracts) for details on how to develop with locally deployed contracts.
|
||||||
|
|
||||||
### Code Generators
|
### Code Generators
|
||||||
|
|
||||||
@@ -36,6 +38,11 @@ Make use of the many generators for code, try `ember help generate` for more det
|
|||||||
* `ember test`
|
* `ember test`
|
||||||
* `ember test --server`
|
* `ember test --server`
|
||||||
|
|
||||||
|
### Linting
|
||||||
|
|
||||||
|
* `npm run lint:js`
|
||||||
|
* `npm run lint:js -- --fix`
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
* `ember build` (development)
|
* `ember build` (development)
|
||||||
@@ -43,35 +50,61 @@ Make use of the many generators for code, try `ember help generate` for more det
|
|||||||
|
|
||||||
### Deploying
|
### Deploying
|
||||||
|
|
||||||
Specify what it takes to deploy your app.
|
_(You need collaborator permissions on the 5apps Deploy project.)_
|
||||||
|
|
||||||
|
`npm run deploy`
|
||||||
|
|
||||||
## Working with locally deployed contracts
|
## Working with locally deployed contracts
|
||||||
|
|
||||||
For development you might want to run your own development chain with your own development contracts.
|
The smart contracts and their JavaScript wrapper library are developed in the
|
||||||
The kredits-contracts helper scripts are available in kredits-web. See [kredits-contracts](https://github.com/67P/kredits-contracts) for more information.
|
[kredits-contracts](https://github.com/67P/kredits-contracts) repo/package.
|
||||||
|
|
||||||
Start your parity node with a KreditsChain:
|
You can run `kredits-web` on your machine, against a local, simulated Ethereum
|
||||||
|
network, provided e.g. by [ganache](http://truffleframework.com/ganache/) or
|
||||||
|
[ganache-cli](https://github.com/trufflesuite/ganache-cli).
|
||||||
|
|
||||||
npm run kredits-parity
|
[kredits-contracts](https://github.com/67P/kredits-contracts) holds all the tools
|
||||||
|
to start and set up such a simulated network, as well as to deploy smart
|
||||||
|
contracts to it.
|
||||||
|
|
||||||
Deploy the contracts to your local chain:
|
These are the basic steps to get up and running:
|
||||||
|
|
||||||
npm run kredits-deploy
|
#### 1. IPFS
|
||||||
|
|
||||||
Run kredits-web with your localy deployed contracts:
|
Run a local IPFS deamon in offline mode.
|
||||||
|
|
||||||
KREDITS_CONTRACT_ADDR=`kredits-inspect -c Kredits -r -w address` \
|
* Make sure CORS headers are configured. See [IPFS](#ipfs) for more info.
|
||||||
TOKEN_CONTRACT_ADDR=`kredits-inspect -c Token -r -w address` \
|
* `ipfs daemon --offline`
|
||||||
npm start
|
|
||||||
|
|
||||||
You need to have `./node_modules/.bin` in your path for this to work. Otherwise simply copy the contract addresses and set the environment variables manually.
|
#### 2. kredits-contracts
|
||||||
|
|
||||||
|
Get your local Ethereum development node running.
|
||||||
|
|
||||||
|
* Clone [kredits-contracts](https://github.com/67P/kredits-contracts)
|
||||||
|
* `npm install`
|
||||||
|
* `npm run ganache` - which is basically: `ganache-cli -p 7545 -i 100` (we use the non-default port for local networks and a fixed network id)
|
||||||
|
* `npm run bootstrap` - bootstrap runs fresh migrations, adds some seed data and writes the address/abi information to JSON that will be used by kredits-web
|
||||||
|
* `npm link` - make the `kredits-contracts` module linkable as `kredits-contracts` on your machine
|
||||||
|
|
||||||
|
#### 3. kredits-web
|
||||||
|
|
||||||
|
With IPFS and Ethereum/ganache running, you can now start this Ember app.
|
||||||
|
|
||||||
|
* `npm link kredits-contracts` - link the local `kredits-contracts` package
|
||||||
|
* `npm run start:local` - NETWORK_ID=100 and WEB3_PROVIDER_URL=http://localhost:7545 must be set for local settings
|
||||||
|
|
||||||
|
#### IPFS
|
||||||
|
|
||||||
|
Install IPFS with your favorite package manager and run
|
||||||
|
|
||||||
|
ipfs init (on initial installation)
|
||||||
|
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["localhost:4200"]'
|
||||||
|
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods '["PUT", "GET", "POST"]'
|
||||||
|
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Credentials '["true"]'
|
||||||
|
|
||||||
## Further Reading / Useful Links
|
## Further Reading / Useful Links
|
||||||
|
|
||||||
* [ember.js](http://emberjs.com/)
|
* [ember.js](https://emberjs.com/)
|
||||||
* [ember-cli](https://ember-cli.com/)
|
* [ember-cli](https://ember-cli.com/)
|
||||||
* Development Browser Extensions
|
* Development Browser Extensions
|
||||||
* [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi)
|
* [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi)
|
||||||
|
|||||||
+2
-6
@@ -1,13 +1,9 @@
|
|||||||
import Ember from 'ember';
|
import Application from '@ember/application';
|
||||||
import Resolver from './resolver';
|
import Resolver from './resolver';
|
||||||
import loadInitializers from 'ember-load-initializers';
|
import loadInitializers from 'ember-load-initializers';
|
||||||
import config from './config/environment';
|
import config from './config/environment';
|
||||||
|
|
||||||
let App;
|
const App = Application.extend({
|
||||||
|
|
||||||
Ember.MODEL_FACTORY_INJECTIONS = true;
|
|
||||||
|
|
||||||
App = Ember.Application.extend({
|
|
||||||
modulePrefix: config.modulePrefix,
|
modulePrefix: config.modulePrefix,
|
||||||
podModulePrefix: config.podModulePrefix,
|
podModulePrefix: config.podModulePrefix,
|
||||||
Resolver
|
Resolver
|
||||||
|
|||||||
@@ -1,81 +1,64 @@
|
|||||||
import Ember from 'ember';
|
import Component from '@ember/component';
|
||||||
import Contributor from 'kredits-web/models/contributor';
|
import { and, notEmpty } from '@ember/object/computed';
|
||||||
|
import { inject as injectService } from '@ember/service';
|
||||||
|
|
||||||
const {
|
|
||||||
Component,
|
|
||||||
isPresent,
|
|
||||||
inject: {
|
|
||||||
service
|
|
||||||
},
|
|
||||||
computed
|
|
||||||
} = Ember;
|
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
|
kredits: injectService(),
|
||||||
|
|
||||||
kredits: service(),
|
// Default attributes used by reset
|
||||||
|
attributes: {
|
||||||
|
account: null,
|
||||||
|
name: null,
|
||||||
|
kind: 'person',
|
||||||
|
url: null,
|
||||||
|
github_username: null,
|
||||||
|
github_uid: null,
|
||||||
|
wiki_username: null,
|
||||||
|
isCore: false,
|
||||||
|
},
|
||||||
|
|
||||||
newContributor: null,
|
didInsertElement() {
|
||||||
inProgress: false,
|
this._super(...arguments);
|
||||||
|
this.reset();
|
||||||
|
},
|
||||||
|
|
||||||
isValidAddress: function() {
|
// TODO: add proper address validation
|
||||||
return this.get('kredits.web3')
|
isValidAccount: notEmpty('account'),
|
||||||
.isAddress(this.get('newContributor.address'));
|
isValidName: notEmpty('name'),
|
||||||
}.property('kredits.web3', 'newContributor.address'),
|
isValidURL: notEmpty('url'),
|
||||||
|
isValidGithubUID: notEmpty('github_uid'),
|
||||||
isValidName: function() {
|
isValidGithubUsername: notEmpty('github_username'),
|
||||||
return isPresent(this.get('newContributor.name'));
|
isValidWikiUsername: notEmpty('wiki_username'),
|
||||||
}.property('newContributor.name'),
|
isValid: and(
|
||||||
|
'isValidAccount',
|
||||||
isValidURL: function() {
|
|
||||||
return isPresent(this.get('newContributor.url'));
|
|
||||||
}.property('newContributor.url'),
|
|
||||||
|
|
||||||
isValidGithubUID: function() {
|
|
||||||
return isPresent(this.get('newContributor.github_uid'));
|
|
||||||
}.property('newContributor.github_uid'),
|
|
||||||
|
|
||||||
isValidGithubUsername: function() {
|
|
||||||
return isPresent(this.get('newContributor.github_username'));
|
|
||||||
}.property('newContributor.github_username'),
|
|
||||||
|
|
||||||
isValidWikiUsername: function() {
|
|
||||||
return isPresent(this.get('newContributor.wiki_username'));
|
|
||||||
}.property('newContributor.wiki_username'),
|
|
||||||
|
|
||||||
isValid: computed.and(
|
|
||||||
'isValidAddress',
|
|
||||||
'isValidName',
|
'isValidName',
|
||||||
'isValidGithubUID'
|
'isValidGithubUID'
|
||||||
),
|
),
|
||||||
|
|
||||||
reset: function() {
|
reset: function() {
|
||||||
this.setProperties({
|
this.setProperties(this.attributes);
|
||||||
newContributor: Contributor.create({ kind: 'person' }),
|
|
||||||
inProgress: false
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
submit() {
|
||||||
save() {
|
if (!this.isValid) {
|
||||||
if (!this.get('contractInteractionEnabled')) {
|
alert('Invalid data. Please review and try again.');
|
||||||
alert('Only core team members can add new contributors. Please ask someone to set you up.');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.get('isValid')) {
|
let attributes = Object.keys(this.attributes);
|
||||||
this.set('inProgress', true);
|
let contributor = this.getProperties(attributes);
|
||||||
|
let saved = this.save(contributor);
|
||||||
|
|
||||||
this.get('kredits').addContributor(this.get('newContributor')).then(contributor => {
|
// The promise handles inProgress
|
||||||
|
this.set('inProgress', saved);
|
||||||
|
|
||||||
|
saved.then(() => {
|
||||||
this.reset();
|
this.reset();
|
||||||
this.get('contributors').pushObject(contributor);
|
|
||||||
window.scroll(0,0);
|
window.scroll(0,0);
|
||||||
|
window.alert('Contributor added.');
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
alert('Invalid data. Please review and try again.');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,59 +1,64 @@
|
|||||||
<form {{action "save" on="submit"}}>
|
<form {{action "submit" on="submit"}}>
|
||||||
<p>
|
<p>
|
||||||
{{input type="checkbox" name="is-core" id="is-core" checked=newContributor.isCore}}
|
{{input name="is-core"
|
||||||
|
type="checkbox"
|
||||||
|
id="is-core"
|
||||||
|
checked=isCore}}
|
||||||
<label for="is-core" class="checkbox">
|
<label for="is-core" class="checkbox">
|
||||||
Core team member (can add contributors)
|
Core team member (can add contributors)
|
||||||
</label>
|
</label>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{input name="address"
|
{{input name="account"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="0xF18E631Ea191aE4ebE70046Fcb01a436554421BA4"
|
placeholder="0xF18E631Ea191aE4ebE70046Fcb01a436554421BA4"
|
||||||
value=newContributor.address
|
value=account
|
||||||
class=(if isValidAddress 'valid' '')}}
|
class=(if isValidAccount 'valid' '')}}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<select required onchange={{action (mut newContributor.kind) value="target.value"}}>
|
<select required onchange={{action (mut kind) value="target.value"}}>
|
||||||
<option value="person" selected={{eq newContributor.kind "person"}}>Person</option>
|
<option value="person" selected={{eq kind "person"}}>Person</option>
|
||||||
<option value="organization" selected={{eq newContributor.kind "organization"}}>Organization</option>
|
<option value="organization" selected={{eq kind "organization"}}>Organization</option>
|
||||||
</select>
|
</select>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{input name="name"
|
{{input name="name"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Name"
|
placeholder="Name"
|
||||||
value=newContributor.name
|
value=name
|
||||||
class=(if isValidName 'valid' '')}}
|
class=(if isValidName 'valid' '')}}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{input name="url"
|
{{input name="url"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="URL"
|
placeholder="URL"
|
||||||
value=newContributor.url
|
value=url
|
||||||
class=(if isValidURL 'valid' '')}}
|
class=(if isValidURL 'valid' '')}}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{input name="github_uid"
|
{{input name="github_uid"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="GitHub UID (123)"
|
placeholder="GitHub UID (123)"
|
||||||
value=newContributor.github_uid
|
value=github_uid
|
||||||
class=(if isValidGithubUID 'valid' '')}}
|
class=(if isValidGithubUID 'valid' '')}}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{input name="github_username"
|
{{input name="github_username"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="GitHub username"
|
placeholder="GitHub username"
|
||||||
value=newContributor.github_username
|
value=github_username
|
||||||
class=(if isValidGithubUsername 'valid' '')}}
|
class=(if isValidGithubUsername 'valid' '')}}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{input name="wiki_username"
|
{{input name="wiki_username"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Wiki Username"
|
placeholder="Wiki Username"
|
||||||
value=newContributor.wiki_username
|
value=wiki_username
|
||||||
class=(if isValidWikiUsername 'valid' '')}}
|
class=(if isValidWikiUsername 'valid' '')}}
|
||||||
</p>
|
</p>
|
||||||
<p class="actions">
|
<p class="actions">
|
||||||
{{input type="submit" value=(if inProgress 'Processing' 'Save') disabled=inProgress}}
|
{{input type="submit"
|
||||||
|
disabled=(is-pending inProgress)
|
||||||
|
value=(if (is-pending inProgress) 'Processing' 'Save')}}
|
||||||
</p>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -1,66 +1,57 @@
|
|||||||
import Ember from 'ember';
|
import Component from '@ember/component';
|
||||||
|
import { computed } from '@ember/object';
|
||||||
const {
|
import { and, notEmpty } from '@ember/object/computed';
|
||||||
Component,
|
|
||||||
isPresent,
|
|
||||||
inject: {
|
|
||||||
service
|
|
||||||
},
|
|
||||||
computed
|
|
||||||
} = Ember;
|
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
|
// Default attributes used by reset
|
||||||
|
attributes: {
|
||||||
|
contributorId: null,
|
||||||
|
kind: 'community',
|
||||||
|
amount: null,
|
||||||
|
description: null,
|
||||||
|
url: null,
|
||||||
|
},
|
||||||
|
|
||||||
kredits: service(),
|
didInsertElement() {
|
||||||
|
this._super(...arguments);
|
||||||
|
this.reset();
|
||||||
|
},
|
||||||
|
|
||||||
proposal: null,
|
contributors: [],
|
||||||
contributors: null,
|
|
||||||
inProgress: false,
|
|
||||||
|
|
||||||
isValidRecipient: computed('proposal.recipientAddress', function() {
|
isValidContributor: notEmpty('contributorId'),
|
||||||
return this.get('kredits.web3').isAddress(this.get('proposal.recipientAddress'));
|
isValidAmount: computed('amount', function() {
|
||||||
|
return parseInt(this.amount, 10) > 0;
|
||||||
}),
|
}),
|
||||||
|
isValidDescription: notEmpty('description'),
|
||||||
isValidAmount: computed('proposal.amount', function() {
|
isValidUrl: notEmpty('url'),
|
||||||
return parseInt(this.get('proposal.amount'), 10) > 0;
|
isValid: and('isValidContributor',
|
||||||
}),
|
|
||||||
|
|
||||||
isValidUrl: computed('proposal.url', function() {
|
|
||||||
return isPresent(this.get('proposal.url'));
|
|
||||||
}),
|
|
||||||
|
|
||||||
isValidDescription: computed('proposal.description', function() {
|
|
||||||
return isPresent(this.get('proposal.description'));
|
|
||||||
}),
|
|
||||||
|
|
||||||
isValid: computed.and('isValidRecipient',
|
|
||||||
'isValidAmount',
|
'isValidAmount',
|
||||||
'isValidDescription'),
|
'isValidDescription'),
|
||||||
|
|
||||||
|
reset: function() {
|
||||||
|
this.setProperties(this.attributes);
|
||||||
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
save() {
|
submit() {
|
||||||
if (! this.get('isValid')) {
|
if (!this.isValid) {
|
||||||
alert('Invalid data. Please review and try again.');
|
alert('Invalid data. Please review and try again.');
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
this.set('inProgress', true);
|
|
||||||
let proposal = this.get('proposal');
|
|
||||||
|
|
||||||
// Set the recipient's IPFS profile hash so it can be used in the
|
let attributes = Object.keys(this.attributes);
|
||||||
// contribution object (which is to be stored in IPFS as well)
|
let proposal = this.getProperties(attributes);
|
||||||
let contributor = this.get('contributors').findBy('address', proposal.get('recipientAddress'));
|
let saved = this.save(proposal);
|
||||||
proposal.set('recipientProfile', contributor.get('ipfsHash'));
|
|
||||||
|
|
||||||
this.get('kredits').addProposal(proposal)
|
// The promise handles inProgress
|
||||||
.then(() => {
|
this.set('inProgress', saved);
|
||||||
this.attrs.onSave();
|
|
||||||
}).catch((error) => {
|
saved.then(() => {
|
||||||
Ember.Logger.error('[add-proposal] error creating the proposal', error);
|
this.reset();
|
||||||
alert('Something went wrong.');
|
window.scroll(0,0);
|
||||||
}).finally(() => {
|
window.alert('Proposal added.');
|
||||||
this.set('inProgress', false);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,41 +1,43 @@
|
|||||||
<form {{action "save" on="submit"}}>
|
<form {{action "submit" on="submit"}}>
|
||||||
<p>
|
<p>
|
||||||
<select required onchange={{action (mut proposal.recipientAddress) value="target.value"}}>
|
<select required onchange={{action (mut contributorId) value="target.value"}}>
|
||||||
<option value="" selected disabled hidden>Contributor</option>
|
<option value="" selected disabled hidden>Contributor</option>
|
||||||
{{#each contributors as |contributor|}}
|
{{#each contributors as |contributor|}}
|
||||||
<option value={{contributor.address}} selected={{eq proposal.recipientAddress contributor.address}}>{{contributor.github_username}}</option>
|
<option value={{contributor.id}} selected={{eq contributorId contributor.id}}>{{contributor.github_username}}</option>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</select>
|
</select>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<select required onchange={{action (mut proposal.kind) value="target.value"}}>
|
<select required onchange={{action (mut kind) value="target.value"}}>
|
||||||
<option value="community" selected={{eq proposal.kind "community"}}>Community</option>
|
<option value="community" selected={{eq kind "community"}}>Community</option>
|
||||||
<option value="design" selected={{eq proposal.kind "design"}}>Design</option>
|
<option value="design" selected={{eq kind "design"}}>Design</option>
|
||||||
<option value="dev" selected={{eq proposal.kind "dev"}}>Development</option>
|
<option value="dev" selected={{eq kind "dev"}}>Development</option>
|
||||||
<option value="docs" selected={{eq proposal.kind "docs"}}>Documentation</option>
|
<option value="docs" selected={{eq kind "docs"}}>Documentation</option>
|
||||||
<option value="ops" selected={{eq proposal.kind "ops"}}>IT Operations</option>
|
<option value="ops" selected={{eq kind "ops"}}>IT Operations</option>
|
||||||
</select>
|
</select>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{input type="text"
|
{{input type="text"
|
||||||
placeholder="100"
|
placeholder="100"
|
||||||
value=proposal.amount
|
value=amount
|
||||||
class=(if isValidAmount 'valid' '')}}
|
class=(if isValidAmount 'valid' '')}}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{input type="text"
|
{{input type="text"
|
||||||
placeholder="Description"
|
placeholder="Description"
|
||||||
value=proposal.description
|
value=description
|
||||||
class=(if isValidDescription 'valid' '')}}
|
class=(if isValidDescription 'valid' '')}}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{input type="text"
|
{{input type="text"
|
||||||
placeholder="URL (optional)"
|
placeholder="URL (optional)"
|
||||||
value=proposal.url
|
value=url
|
||||||
class=(if isValidUrl 'valid' '')}}
|
class=(if isValidUrl 'valid' '')}}
|
||||||
</p>
|
</p>
|
||||||
<p class="actions">
|
<p class="actions">
|
||||||
{{input type="submit" value=(if inProgress 'Processing' 'Save') disabled=inProgress}}
|
{{input type="submit"
|
||||||
|
disabled=(is-pending inProgress)
|
||||||
|
value=(if (is-pending inProgress) 'Processing' 'Save')}}
|
||||||
{{#link-to 'index'}}Back{{/link-to}}
|
{{#link-to 'index'}}Back{{/link-to}}
|
||||||
</p>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import Component from '@ember/component';
|
||||||
|
import { computed } from '@ember/object';
|
||||||
|
|
||||||
|
let categoryColors = {
|
||||||
|
community: "#fb6868",
|
||||||
|
design: "#fbe468",
|
||||||
|
dev: "#e068fb",
|
||||||
|
docs: "#97fb68",
|
||||||
|
ops: "#8f68fb",
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Component.extend({
|
||||||
|
|
||||||
|
contributions: null,
|
||||||
|
|
||||||
|
chartData: computed('contributions', function() {
|
||||||
|
let kredits = this.get('contributions')
|
||||||
|
.map(c => {
|
||||||
|
return { kind: c.kind, amount: c.amount }
|
||||||
|
}).reduce(function (kinds, c) {
|
||||||
|
kinds[c.kind] = (kinds[c.kind] || 0) + c.amount;
|
||||||
|
return kinds;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return {
|
||||||
|
datasets: [{
|
||||||
|
data: [
|
||||||
|
kredits['community'],
|
||||||
|
kredits['design'],
|
||||||
|
kredits['dev'],
|
||||||
|
kredits['ops'],
|
||||||
|
kredits['docs'],
|
||||||
|
],
|
||||||
|
borderColor: [
|
||||||
|
categoryColors.community,
|
||||||
|
categoryColors.design,
|
||||||
|
categoryColors.dev,
|
||||||
|
categoryColors.ops,
|
||||||
|
categoryColors.docs,
|
||||||
|
],
|
||||||
|
borderWidth: 1
|
||||||
|
}],
|
||||||
|
labels: [
|
||||||
|
'Community',
|
||||||
|
'Design',
|
||||||
|
'Development',
|
||||||
|
'Operations & Infrastructure',
|
||||||
|
'Documentation'
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
chartOptions: {
|
||||||
|
legend: {
|
||||||
|
display: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<div class="chart">
|
||||||
|
{{ember-chart type='doughnut'
|
||||||
|
data=chartData
|
||||||
|
options=chartOptions
|
||||||
|
width=200
|
||||||
|
height=200}}
|
||||||
|
</div>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import Ember from 'ember';
|
import Component from '@ember/component';
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Component.extend({
|
||||||
|
|
||||||
tagName: 'table',
|
tagName: 'table',
|
||||||
classNames: 'contributor-list',
|
classNames: 'contributor-list',
|
||||||
@@ -12,7 +12,7 @@ export default Ember.Component.extend({
|
|||||||
if (contributor.get('showMetadata')) {
|
if (contributor.get('showMetadata')) {
|
||||||
contributor.set('showMetadata', false);
|
contributor.set('showMetadata', false);
|
||||||
} else {
|
} else {
|
||||||
this.get('contributors').setEach('showMetadata', false);
|
this.contributors.setEach('showMetadata', false);
|
||||||
contributor.set('showMetadata', true);
|
contributor.set('showMetadata', true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,18 +6,21 @@
|
|||||||
{{contributor.name}}
|
{{contributor.name}}
|
||||||
</td>
|
</td>
|
||||||
<td class="kredits">
|
<td class="kredits">
|
||||||
<span class="amount">{{contributor.kredits}}</span>
|
<span class="amount">{{contributor.balance}}</span>
|
||||||
<span class="symbol">₭S</span>
|
<span class="symbol">₭S</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="metadata {{if contributor.isCurrentUser 'current-user'}} {{if contributor.showMetadata 'visible'}}">
|
<tr class="metadata {{if contributor.isCurrentUser 'current-user'}} {{if contributor.showMetadata 'visible'}}">
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="https://testnet.etherscan.io/address/{{contributor.address}}">Inspect Ethereum transactions</a></li>
|
<li><a href="https://testnet.etherscan.io/address/{{contributor.account}}">Inspect Ethereum transactions</a></li>
|
||||||
{{#if contributor.ipfsHash}}
|
{{#if contributor.ipfsHash}}
|
||||||
<li><a href="https://ipfs.io/ipfs/{{contributor.ipfsHash}}">Inspect IPFS profile</a></li>
|
<li><a href="https://ipfs.io/ipfs/{{contributor.ipfsHash}}">Inspect IPFS profile</a></li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</ul>
|
</ul>
|
||||||
|
{{#if contributor.showMetadata}}
|
||||||
|
<pre>{{contributor.ipfsData}}</pre>
|
||||||
|
{{/if}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import Ember from 'ember';
|
import Component from '@ember/component';
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Component.extend({
|
||||||
|
|
||||||
classNames: ['loading-spinner']
|
classNames: ['loading-spinner']
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import Ember from 'ember';
|
import Component from '@ember/component';
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Component.extend({
|
||||||
|
|
||||||
tagName: 'ul',
|
tagName: 'ul',
|
||||||
classNames: ['proposal-list'],
|
classNames: ['proposal-list'],
|
||||||
@@ -8,8 +8,8 @@ export default Ember.Component.extend({
|
|||||||
actions: {
|
actions: {
|
||||||
|
|
||||||
confirm(proposalId) {
|
confirm(proposalId) {
|
||||||
if (this.get('contractInteractionEnabled')) {
|
if (this.contractInteractionEnabled) {
|
||||||
this.sendAction('confirmAction', proposalId);
|
this.confirmProposal(proposalId);
|
||||||
} else {
|
} else {
|
||||||
window.alert('Only members can vote on proposals. Please ask someone to set you up.');
|
window.alert('Only members can vote on proposals. Please ask someone to set you up.');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,20 @@
|
|||||||
{{#each proposals as |proposal|}}
|
{{#each proposals as |proposal|}}
|
||||||
<li data-proposal-id={{proposal.id}} title="({{proposal.kind}}) {{proposal.description}}">
|
<li data-proposal-id={{proposal.id}} class={{if proposal.isExecuted "confirmed" "unconfirmed"}}>
|
||||||
<span class="category {{proposal.kind}}">♥</span>
|
<p class="meta">
|
||||||
|
<span class="category {{proposal.kind}}">♥ ({{proposal.kind}})</span>
|
||||||
|
<span class="recipient">{{proposal.contributor.name}}:</span>
|
||||||
|
</p>
|
||||||
|
<p class="kredits-amount">
|
||||||
<span class="amount">{{proposal.amount}}</span><span class="symbol">₭S</span>
|
<span class="amount">{{proposal.amount}}</span><span class="symbol">₭S</span>
|
||||||
for <span class="recipient">{{proposal.recipientName}}</span>
|
</p>
|
||||||
|
<p class="description">
|
||||||
|
<span class="description">{{proposal.description}}</span>
|
||||||
|
</p>
|
||||||
|
<p class="voting">
|
||||||
|
{{#unless proposal.isExecuted}}
|
||||||
<span class="votes">({{proposal.votesCount}}/{{proposal.votesNeeded}} votes)</span>
|
<span class="votes">({{proposal.votesCount}}/{{proposal.votesNeeded}} votes)</span>
|
||||||
|
<button {{action "confirm" proposal.id}}>+1</button>
|
||||||
{{#unless proposal.executed}}<button {{action "confirm" proposal.id}}>+1</button>{{/unless}}
|
{{/unless}}
|
||||||
|
</p>
|
||||||
</li>
|
</li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import Ember from 'ember';
|
import Controller from '@ember/controller';
|
||||||
|
import { inject as injectService } from '@ember/service';
|
||||||
|
|
||||||
export default Ember.Controller.extend({
|
export default Controller.extend({
|
||||||
|
kredits: injectService(),
|
||||||
});
|
});
|
||||||
|
|||||||
+26
-137
@@ -1,148 +1,37 @@
|
|||||||
import Ember from 'ember';
|
import Controller from '@ember/controller';
|
||||||
import Proposal from 'kredits-web/models/proposal';
|
import { alias, filter, filterBy, sort } from '@ember/object/computed';
|
||||||
|
import { inject as injectService } from '@ember/service';
|
||||||
|
|
||||||
const {
|
export default Controller.extend({
|
||||||
computed,
|
kredits: injectService(),
|
||||||
inject: {
|
|
||||||
service
|
|
||||||
}
|
|
||||||
} = Ember;
|
|
||||||
|
|
||||||
export default Ember.Controller.extend({
|
contributors: alias('kredits.contributors'),
|
||||||
|
contributorsWithKredits: filter('contributors', function(contributor) {
|
||||||
kredits: service(),
|
return contributor.get('balance') !== 0;
|
||||||
|
}),
|
||||||
contractInteractionEnabled: computed.alias('kredits.web3Provided'),
|
contributorsSorting: ['balance:desc'],
|
||||||
|
contributorsSorted: sort('contributorsWithKredits', 'contributorsSorting'),
|
||||||
findContributorByAddress(address) {
|
|
||||||
return this.get('model.contributors')
|
|
||||||
.findBy('address', address);
|
|
||||||
},
|
|
||||||
|
|
||||||
proposalsOpen: function() {
|
|
||||||
let proposals = this.get('model.proposals')
|
|
||||||
.filterBy('executed', false)
|
|
||||||
.map(p => {
|
|
||||||
p.set('recipientName', this.findContributorByAddress(p.get('recipientAddress')).name);
|
|
||||||
return p;
|
|
||||||
});
|
|
||||||
return proposals;
|
|
||||||
}.property('model.proposals.[]', 'model.proposals.@each.executed', 'model.contributors.[]'),
|
|
||||||
|
|
||||||
proposalsClosed: function() {
|
|
||||||
let proposals = this.get('model.proposals')
|
|
||||||
.filterBy('executed', true)
|
|
||||||
.map(p => {
|
|
||||||
p.set('recipientName', this.findContributorByAddress(p.get('recipientAddress')).name);
|
|
||||||
return p;
|
|
||||||
});
|
|
||||||
return proposals;
|
|
||||||
}.property('model.proposals.[]', 'model.proposals.@each.executed', 'model.contributors.[]'),
|
|
||||||
|
|
||||||
|
proposals: alias('kredits.proposals'),
|
||||||
|
proposalsOpen: filterBy('proposals', 'isExecuted', false),
|
||||||
|
proposalsClosed: filterBy('proposals', 'isExecuted', true),
|
||||||
proposalsSorting: ['id:desc'],
|
proposalsSorting: ['id:desc'],
|
||||||
proposalsClosedSorted: Ember.computed.sort('proposalsClosed', 'proposalsSorting'),
|
proposalsClosedSorted: sort('proposalsClosed', 'proposalsSorting'),
|
||||||
proposalsOpenSorted: Ember.computed.sort('proposalsOpen', 'proposalsSorting'),
|
proposalsOpenSorted: sort('proposalsOpen', 'proposalsSorting'),
|
||||||
|
|
||||||
contributorsWithKredits: function() {
|
|
||||||
return this.get('model.contributors').filter(c => {
|
|
||||||
return c.get('kredits') !== 0;
|
|
||||||
});
|
|
||||||
}.property('model.contributors.@each.kredits'),
|
|
||||||
|
|
||||||
contributorsSorting: ['kredits:desc'],
|
|
||||||
contributorsSorted: Ember.computed.sort('contributorsWithKredits', 'contributorsSorting'),
|
|
||||||
|
|
||||||
watchContractEvents: function() {
|
|
||||||
let events = this.get('kredits.kreditsContract')
|
|
||||||
.allEvents(/* [additionalFilterObject], */);
|
|
||||||
|
|
||||||
events.watch((error, data) => {
|
|
||||||
Ember.Logger.debug('[index] Received contract event', data);
|
|
||||||
|
|
||||||
switch (data.event) {
|
|
||||||
case 'ProposalCreated':
|
|
||||||
this._handleProposalCreated(data);
|
|
||||||
break;
|
|
||||||
case 'ProposalExecuted':
|
|
||||||
this._handleProposalExecuted(data);
|
|
||||||
break;
|
|
||||||
case 'ProposalVoted':
|
|
||||||
this._handleProposalVoted(data);
|
|
||||||
break;
|
|
||||||
case 'Transfer':
|
|
||||||
this._handleTransfer(data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}.on('init'),
|
|
||||||
|
|
||||||
_handleProposalCreated(data) {
|
|
||||||
if (Ember.isPresent(this.get('model.proposals')
|
|
||||||
.findBy('id', data.args.id.toNumber()))) {
|
|
||||||
Ember.Logger.debug('[index] proposal exists, not adding from event');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let proposal = Proposal.create({
|
|
||||||
id: data.args.id.toNumber(),
|
|
||||||
creatorAddress: data.args.creator,
|
|
||||||
recipientAddress: data.args.recipient,
|
|
||||||
recipientName: null,
|
|
||||||
votesCount: 0,
|
|
||||||
votesNeeded: 2,
|
|
||||||
amount: data.args.amount.toNumber(),
|
|
||||||
executed: false,
|
|
||||||
url: data.args.url,
|
|
||||||
ipfsHash: data.args.ipfsHash
|
|
||||||
});
|
|
||||||
|
|
||||||
this.get('model.proposals').pushObject(proposal);
|
|
||||||
},
|
|
||||||
|
|
||||||
_handleProposalExecuted(data) {
|
|
||||||
if (this.get('model.proposals')
|
|
||||||
.findBy('id', data.args.id.toNumber())
|
|
||||||
.get('executed')) {
|
|
||||||
Ember.Logger.debug('[index] proposal already executed, not adding from event');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.get('model.proposals')
|
|
||||||
.findBy('id', data.args.id.toNumber())
|
|
||||||
.setProperties({
|
|
||||||
'executed': true,
|
|
||||||
'votesCount': 2 // TODO use real count
|
|
||||||
});
|
|
||||||
|
|
||||||
this.get('model.contributors')
|
|
||||||
.findBy('address', data.args.recipient)
|
|
||||||
.incrementProperty('kredits', data.args.amount.toNumber());
|
|
||||||
},
|
|
||||||
|
|
||||||
_handleProposalVoted(data) {
|
|
||||||
this.get('model.proposals')
|
|
||||||
.findBy('id', data.args.id.toNumber())
|
|
||||||
.incrementProperty('votesCount', 1);
|
|
||||||
},
|
|
||||||
|
|
||||||
_handleTransfer(data) {
|
|
||||||
this.get('model.contributors')
|
|
||||||
.findBy('address', data.args.from)
|
|
||||||
.incrementProperty('kredits', - data.args.value.toNumber());
|
|
||||||
this.get('model.contributors')
|
|
||||||
.findBy('address', data.args.to)
|
|
||||||
.incrementProperty('kredits', data.args.value.toNumber());
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
|
||||||
confirmProposal(proposalId) {
|
confirmProposal(proposalId) {
|
||||||
this.get('kredits').vote(proposalId).then(transactionId => {
|
this.kredits.vote(proposalId).then(transaction => {
|
||||||
window.confirm('Vote submitted to Ethereum blockhain: '+transactionId);
|
window.confirm('Vote submitted to Ethereum blockhain: '+transaction.hash);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
save(contributor) {
|
||||||
|
return this.kredits.addContributor(contributor)
|
||||||
|
.then((contributor) => {
|
||||||
|
this.contributors.pushObject(contributor);
|
||||||
|
return contributor;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,29 +1,24 @@
|
|||||||
import Ember from 'ember';
|
import Controller from '@ember/controller';
|
||||||
import QueryParams from 'ember-parachute';
|
import { alias, filterBy } from '@ember/object/computed';
|
||||||
|
import { inject as injectService } from '@ember/service';
|
||||||
|
|
||||||
export const queryParams = new QueryParams({
|
export default Controller.extend({
|
||||||
recipient: {
|
kredits: injectService(),
|
||||||
defaultValue: ''
|
|
||||||
},
|
|
||||||
amount: {
|
|
||||||
defaultValue: ''
|
|
||||||
},
|
|
||||||
url: {
|
|
||||||
defaultValue: ''
|
|
||||||
},
|
|
||||||
ipfsHash: {
|
|
||||||
defaultValue: ''
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default Ember.Controller.extend(queryParams.Mixin, {
|
contributors: alias('kredits.contributors'),
|
||||||
|
minedContributors: filterBy('contributors', 'id'),
|
||||||
contributors: null,
|
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
onSave() {
|
save(proposal) {
|
||||||
this.transitionToRoute('index');
|
// contributorIpfsHash is needed for the proposal ipfs data. I'm not happy to do this here but I think to load all the contributors in addProposal again is a bit too much. I hope we can refactor it later.
|
||||||
}
|
let contributor = this.contributors.findBy('id', proposal.contributorId);
|
||||||
}
|
proposal.contributorIpfsHash = contributor.get('ipfsHash');
|
||||||
|
|
||||||
|
return this.kredits.addProposal(proposal)
|
||||||
|
.then((proposal) => {
|
||||||
|
this.transitionToRoute('index');
|
||||||
|
return proposal;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
+2
-2
@@ -9,8 +9,8 @@
|
|||||||
|
|
||||||
{{content-for "head"}}
|
{{content-for "head"}}
|
||||||
|
|
||||||
<link rel="stylesheet" href="{{rootURL}}assets/vendor.css">
|
<link integrity="" rel="stylesheet" href="{{rootURL}}assets/vendor.css">
|
||||||
<link rel="stylesheet" href="{{rootURL}}assets/kredits-web.css">
|
<link integrity="" rel="stylesheet" href="{{rootURL}}assets/kredits-web.css">
|
||||||
|
|
||||||
{{content-for "head-footer"}}
|
{{content-for "head-footer"}}
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
/**
|
|
||||||
* This pauses the app's boot process until the window load event is fired.
|
|
||||||
* It allows the user provided Web3 instance (e.g. Metamask, Mist Browser)
|
|
||||||
* to be inserted before we try to use it.
|
|
||||||
*/
|
|
||||||
export default {
|
|
||||||
name: 'defer-loading',
|
|
||||||
initialize: function(application) {
|
|
||||||
// Load event already fired, so web3 should be loaded if available
|
|
||||||
if (window.windowLoadComplete) { return true; }
|
|
||||||
|
|
||||||
// Pause app loading
|
|
||||||
application.deferReadiness();
|
|
||||||
|
|
||||||
window.addEventListener('load', function() {
|
|
||||||
// Continue app loading
|
|
||||||
application.advanceReadiness();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
+20
-108
@@ -1,119 +1,31 @@
|
|||||||
import Ember from 'ember';
|
import { computed } from '@ember/object';
|
||||||
|
import EmberObject from '@ember/object';
|
||||||
|
import bignumber from 'kredits-web/utils/cps/bignumber';
|
||||||
|
|
||||||
const {
|
export default EmberObject.extend({
|
||||||
isPresent,
|
// Contract
|
||||||
} = Ember;
|
id: bignumber('idRaw', 'toString'),
|
||||||
|
account: null,
|
||||||
|
balance: bignumber('balanceRaw', 'toNumber'),
|
||||||
|
isCore: false,
|
||||||
|
ipfsHash: null,
|
||||||
|
|
||||||
export default Ember.Object.extend({
|
// IPFS
|
||||||
|
|
||||||
address: null,
|
|
||||||
name: null,
|
|
||||||
kind: null,
|
kind: null,
|
||||||
|
name: null,
|
||||||
url: null,
|
url: null,
|
||||||
github_username: null,
|
github_username: null,
|
||||||
github_uid: null,
|
github_uid: null,
|
||||||
wiki_username: null,
|
wiki_username: null,
|
||||||
ipfsHash: null,
|
ipfsData: '',
|
||||||
kredits: null,
|
|
||||||
isCore: false,
|
// Deprecated
|
||||||
isCurrentUser: false,
|
isCurrentUser: false,
|
||||||
|
|
||||||
avatarURL: function() {
|
avatarURL: computed('github_uid', function() {
|
||||||
return `https\:\/\/avatars2.githubusercontent.com/u/${this.get('github_uid')}?v=3&s=128`;
|
let github_uid = this.github_uid;
|
||||||
}.property('github_uid'),
|
if (github_uid) {
|
||||||
|
return `https://avatars2.githubusercontent.com/u/${github_uid}?v=3&s=128`;
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads the contributor's profile data from IPFS and sets local instance
|
|
||||||
* properties from it
|
|
||||||
*
|
|
||||||
* @method
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
loadProfile(ipfs) {
|
|
||||||
let promise = new Ember.RSVP.Promise((resolve, reject) => {
|
|
||||||
ipfs.getFile(this.get('ipfsHash')).then(content => {
|
|
||||||
let profileJSON = JSON.parse(content);
|
|
||||||
let profile = Ember.Object.create(profileJSON);
|
|
||||||
|
|
||||||
this.setProperties({
|
|
||||||
name: profile.get('name'),
|
|
||||||
kind: profile.get('kind')
|
|
||||||
});
|
|
||||||
|
|
||||||
let accounts = profile.get('accounts');
|
|
||||||
let github = accounts.findBy('site', 'github.com');
|
|
||||||
let wiki = accounts.findBy('site', 'wiki.kosmos.org');
|
|
||||||
|
|
||||||
if (isPresent(github)) {
|
|
||||||
this.setProperties({
|
|
||||||
github_username: github.username,
|
|
||||||
github_uid: github.uid,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
if (isPresent(wiki)) {
|
}),
|
||||||
this.setProperties({
|
|
||||||
wiki_username: wiki.username
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ember.Logger.debug('[contributor] loaded contributor profile', profile);
|
|
||||||
resolve();
|
|
||||||
}).catch((err) => {
|
|
||||||
Ember.Logger.error('[contributor] error trying to load contributor profile', this.get('ipfsHash'), err);
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return promise;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a JSON-LD object of the contributor, according to
|
|
||||||
* https://github.com/67P/kosmos-schemas/blob/master/schemas/contributor.json
|
|
||||||
*
|
|
||||||
* @method
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
toJSON() {
|
|
||||||
let contributor = {
|
|
||||||
"@context": "https://schema.kosmos.org",
|
|
||||||
"@type": "Contributor",
|
|
||||||
"kind": this.get('kind'),
|
|
||||||
"name": this.get('name'),
|
|
||||||
"accounts": []
|
|
||||||
};
|
|
||||||
|
|
||||||
if (Ember.isPresent(this.get('url'))) {
|
|
||||||
contributor["url"] = this.get('url');
|
|
||||||
}
|
|
||||||
if (Ember.isPresent(this.get('github_uid'))) {
|
|
||||||
contributor.accounts.push({
|
|
||||||
"site": "github.com",
|
|
||||||
"uid": this.get('github_uid'),
|
|
||||||
"username": this.get('github_username'),
|
|
||||||
"url": `https://github.com/${this.get('github_username')}`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (Ember.isPresent(this.get('wiki_username'))) {
|
|
||||||
contributor.accounts.push({
|
|
||||||
"site": "wiki.kosmos.org",
|
|
||||||
"username": this.get('wiki_username'),
|
|
||||||
"url": `https://wiki.kosmos.org/User:${this.get('wiki_username')}`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return contributor;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the JSON-LD representation of the model as a string
|
|
||||||
*
|
|
||||||
* @method
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
serialize() {
|
|
||||||
return JSON.stringify(this.toJSON());
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
+19
-96
@@ -1,102 +1,25 @@
|
|||||||
import Ember from 'ember';
|
import EmberObject from '@ember/object';
|
||||||
|
import { alias } from '@ember/object/computed';
|
||||||
|
import bignumber from 'kredits-web/utils/cps/bignumber';
|
||||||
|
|
||||||
const {
|
export default EmberObject.extend({
|
||||||
isPresent,
|
// Contract
|
||||||
isEmpty,
|
id: bignumber('idRaw', 'toString'),
|
||||||
} = Ember;
|
creatorAccount: null,
|
||||||
|
contributorId: bignumber('contributorIdRaw', 'toString'),
|
||||||
export default Ember.Object.extend({
|
amount: bignumber('amountRaw', 'toNumber'),
|
||||||
|
votesCount: bignumber('votesCountRaw', 'toNumber'),
|
||||||
id: null,
|
votesNeeded: bignumber('votesNeededRaw', 'toNumber'),
|
||||||
creatorAddress: null,
|
|
||||||
recipientAddress: null,
|
|
||||||
recipientName: null,
|
|
||||||
recipientProfile: null,
|
|
||||||
votesCount: null,
|
|
||||||
votesNeeded: null,
|
|
||||||
amount: null,
|
|
||||||
executed: null,
|
executed: null,
|
||||||
contribution: null,
|
|
||||||
kind: null,
|
|
||||||
description: null,
|
|
||||||
url: null,
|
|
||||||
details: null,
|
|
||||||
ipfsHash: null,
|
ipfsHash: null,
|
||||||
|
|
||||||
/**
|
// Shortcuts
|
||||||
* Loads the contribution details from IPFS and sets local instance
|
isExecuted: alias('executed'),
|
||||||
* properties from it
|
|
||||||
*
|
|
||||||
* @method
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
loadContribution(ipfs) {
|
|
||||||
let promise = new Ember.RSVP.Promise((resolve, reject) => {
|
|
||||||
ipfs.getFile(this.get('ipfsHash')).then(content => {
|
|
||||||
let contributionJSON = JSON.parse(content);
|
|
||||||
let contribution = Ember.Object.create(contributionJSON);
|
|
||||||
|
|
||||||
this.setProperties({
|
|
||||||
kind: contribution.get('kind'),
|
|
||||||
description: contribution.get('description'),
|
|
||||||
url: contribution.get('url')
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO load details
|
|
||||||
// let details = profile.get('accounts');
|
|
||||||
|
|
||||||
Ember.Logger.debug('[proposal] loaded contribution details', contributionJSON);
|
|
||||||
resolve();
|
|
||||||
}).catch((err) => {
|
|
||||||
Ember.Logger.error('[proposal] error trying to load contribution details', this.get('ipfsHash'), err);
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return promise;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a JSON-LD object of the contribution, according to
|
|
||||||
* https://github.com/67P/kosmos-schemas/blob/master/schemas/contribution.json
|
|
||||||
*
|
|
||||||
* @method
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
contributionToJSON() {
|
|
||||||
if (isEmpty(this.get('recipientProfile'))) {
|
|
||||||
throw new Error('IPFS hash for recipient profile missing from proposal object');
|
|
||||||
}
|
|
||||||
if (isEmpty(this.get('kind')) || isEmpty(this.get('description'))) {
|
|
||||||
throw new Error('Missing one or more required properties: kind, description');
|
|
||||||
}
|
|
||||||
|
|
||||||
let contribution = {
|
|
||||||
"@context": "https://schema.kosmos.org",
|
|
||||||
"@type": "Contribution",
|
|
||||||
"contributor": {
|
|
||||||
"ipfs": this.get('recipientProfile')
|
|
||||||
},
|
|
||||||
"kind": this.get('kind'),
|
|
||||||
"description": this.get('description'),
|
|
||||||
"details": {}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isPresent(this.get('url'))) {
|
|
||||||
contribution["url"] = this.get('url');
|
|
||||||
}
|
|
||||||
|
|
||||||
return contribution;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the JSON-LD representation of the contribution as a string
|
|
||||||
*
|
|
||||||
* @method
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
serializeContribution() {
|
|
||||||
return JSON.stringify(this.contributionToJSON());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// IPFS
|
||||||
|
kind: null,
|
||||||
|
description: null,
|
||||||
|
details: {},
|
||||||
|
url: null,
|
||||||
|
ipfsData: '',
|
||||||
});
|
});
|
||||||
|
|||||||
+2
-3
@@ -1,13 +1,12 @@
|
|||||||
import Ember from 'ember';
|
import EmberRouter from '@ember/routing/router';
|
||||||
import config from './config/environment';
|
import config from './config/environment';
|
||||||
|
|
||||||
const Router = Ember.Router.extend({
|
const Router = EmberRouter.extend({
|
||||||
location: config.locationType,
|
location: config.locationType,
|
||||||
rootURL: config.rootURL
|
rootURL: config.rootURL
|
||||||
});
|
});
|
||||||
|
|
||||||
Router.map(function() {
|
Router.map(function() {
|
||||||
this.route('spinner');
|
|
||||||
this.route('proposals', function() {
|
this.route('proposals', function() {
|
||||||
this.route('new');
|
this.route('new');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,27 @@
|
|||||||
import Ember from 'ember';
|
import { inject as injectService } from '@ember/service';
|
||||||
|
import Route from '@ember/routing/route';
|
||||||
|
|
||||||
export default Ember.Route.extend({
|
export default Route.extend({
|
||||||
|
kredits: injectService(),
|
||||||
|
|
||||||
|
beforeModel(transition) {
|
||||||
|
const kredits = this.kredits;
|
||||||
|
|
||||||
|
return kredits.setup().then(() => {
|
||||||
|
if (kredits.get('accountNeedsUnlock')) {
|
||||||
|
if (confirm('It looks like you have an Ethereum wallet available. Please unlock your account.')) {
|
||||||
|
transition.retry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log('Error initializing Kredits', error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
afterModel() {
|
||||||
|
return this.kredits.loadContributorsAndProposals()
|
||||||
|
.then(() => {
|
||||||
|
this.kredits.addContractEventHandlers();
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
import Ember from 'ember';
|
|
||||||
import Contributor from 'kredits-web/models/contributor';
|
|
||||||
|
|
||||||
export default Ember.Route.extend({
|
|
||||||
|
|
||||||
kredits: Ember.inject.service(),
|
|
||||||
|
|
||||||
beforeModel(transition) {
|
|
||||||
const kredits = this.get('kredits');
|
|
||||||
|
|
||||||
if (kredits.get('web3') && kredits.get('web3Provided')) {
|
|
||||||
kredits.get('web3').eth.getAccounts((error, accounts) => {
|
|
||||||
if (error || accounts.length === 0) {
|
|
||||||
if (confirm('It looks like you have an Ethereum wallet available. Please unlock your account.')) {
|
|
||||||
transition.retry();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
model() {
|
|
||||||
let kredits = this.get('kredits');
|
|
||||||
|
|
||||||
return Ember.RSVP.hash({
|
|
||||||
contributors: kredits.getContributors(),
|
|
||||||
totalSupply: kredits.getValueFromContract('tokenContract', 'totalSupply'),
|
|
||||||
proposals: kredits.getProposals(),
|
|
||||||
newContributor: Contributor.create({ kind: 'person' })
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import Ember from 'ember';
|
|
||||||
import Proposal from 'kredits-web/models/proposal';
|
|
||||||
|
|
||||||
const {
|
|
||||||
Route,
|
|
||||||
RSVP,
|
|
||||||
inject: {
|
|
||||||
service
|
|
||||||
}
|
|
||||||
} = Ember;
|
|
||||||
|
|
||||||
export default Route.extend({
|
|
||||||
|
|
||||||
kredits: service(),
|
|
||||||
|
|
||||||
model(params) {
|
|
||||||
const proposal = Proposal.create({
|
|
||||||
recipientAddress: params.recipient,
|
|
||||||
amount: params.amount,
|
|
||||||
url: params.url,
|
|
||||||
kind: params.kind || 'dev',
|
|
||||||
ipfsHash: params.ipfsHash
|
|
||||||
});
|
|
||||||
|
|
||||||
return RSVP.hash({
|
|
||||||
proposal,
|
|
||||||
contributors: this.get('kredits').getContributors()
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
setupController(controller, model) {
|
|
||||||
this._super(controller, model.proposal);
|
|
||||||
|
|
||||||
controller.set('contributors', model.contributors);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Route.extend({
|
|
||||||
});
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import Ember from 'ember';
|
|
||||||
import ipfsAPI from 'npm:ipfs-api';
|
|
||||||
import config from 'kredits-web/config/environment';
|
|
||||||
|
|
||||||
export default Ember.Service.extend({
|
|
||||||
|
|
||||||
ipfsInstance: null,
|
|
||||||
|
|
||||||
ipfs: function() {
|
|
||||||
if (this.get('ipfsInstance')) {
|
|
||||||
return this.get('ipfsInstance');
|
|
||||||
}
|
|
||||||
let ipfs = ipfsAPI(config.ipfs);
|
|
||||||
this.set('ipfsInstance', ipfs);
|
|
||||||
return ipfs;
|
|
||||||
}.property('ipfsInstance'),
|
|
||||||
|
|
||||||
storeFile(content) {
|
|
||||||
let ipfs = this.get('ipfs');
|
|
||||||
return ipfs.add(new ipfs.Buffer(content)).then(res => {
|
|
||||||
Ember.Logger.debug('[ipfs] stored content in IPFS', content, res[0].hash);
|
|
||||||
return res[0].hash;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
getFile(hash) {
|
|
||||||
return this.get('ipfs').cat(hash, { buffer: true }).then(res => {
|
|
||||||
return res.toString();
|
|
||||||
}, err => {
|
|
||||||
Ember.Logger.error('[ipfs] error trying to fetch file', hash, err);
|
|
||||||
throw err;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
+192
-194
@@ -1,233 +1,231 @@
|
|||||||
import Ember from 'ember';
|
import ethers from 'npm:ethers';
|
||||||
import Web3 from 'npm:web3';
|
import Kredits from 'npm:kredits-contracts';
|
||||||
import config from 'kredits-web/config/environment';
|
import RSVP from 'rsvp';
|
||||||
import Contributor from 'kredits-web/models/contributor';
|
|
||||||
import Proposal from 'kredits-web/models/proposal';
|
import Service from '@ember/service';
|
||||||
import kreditsContracts from 'npm:kredits-contracts';
|
import { computed } from '@ember/object';
|
||||||
import uuid from 'npm:uuid';
|
import { alias, notEmpty } from '@ember/object/computed';
|
||||||
|
import { isEmpty } from '@ember/utils';
|
||||||
|
|
||||||
|
import config from 'kredits-web/config/environment';
|
||||||
|
import Contributor from 'kredits-web/models/contributor'
|
||||||
|
import Proposal from 'kredits-web/models/proposal'
|
||||||
|
|
||||||
const {
|
|
||||||
Service,
|
|
||||||
isPresent,
|
|
||||||
inject: {
|
|
||||||
service
|
|
||||||
}
|
|
||||||
} = Ember;
|
|
||||||
|
|
||||||
export default Service.extend({
|
export default Service.extend({
|
||||||
|
|
||||||
ipfs: service(),
|
currentUserAccounts: null, // default to not having an account. this is the wen web3 is loaded.
|
||||||
|
currentUser: null,
|
||||||
web3Instance: null,
|
currentUserIsContributor: notEmpty('currentUser'),
|
||||||
web3Provided: false, // Web3 provided (using Mist Browser, Metamask et al.)
|
currentUserIsCore: alias('currentUser.isCore'),
|
||||||
|
hasAccounts: notEmpty('currentUserAccounts'),
|
||||||
web3: function() {
|
accountNeedsUnlock: computed('currentUserAccounts', function() {
|
||||||
if (this.get('web3Instance')) {
|
return this.currentUserAccounts && isEmpty(this.currentUserAccounts);
|
||||||
return this.get('web3Instance');
|
}),
|
||||||
}
|
|
||||||
|
|
||||||
let web3Instance;
|
|
||||||
|
|
||||||
|
// this is called in the routes beforeModel(). So it is initialized before everything else
|
||||||
|
// and we can rely on the ethProvider and the potential currentUserAccounts to be available
|
||||||
|
getEthProvider: function() {
|
||||||
|
return new RSVP.Promise((resolve) => {
|
||||||
|
let ethProvider;
|
||||||
|
let networkId;
|
||||||
if (typeof window.web3 !== 'undefined') {
|
if (typeof window.web3 !== 'undefined') {
|
||||||
Ember.Logger.debug('[kredits] Using user-provided instance, e.g. from Mist browser or Metamask');
|
console.debug('[kredits] Using user-provided instance, e.g. from Mist browser or Metamask');
|
||||||
web3Instance = window.web3;
|
networkId = parseInt(window.web3.version.network);
|
||||||
this.set('web3Provided', true);
|
ethProvider = new ethers.providers.Web3Provider(
|
||||||
} else {
|
window.web3.currentProvider,
|
||||||
Ember.Logger.debug('[kredits] Creating new instance from npm module class');
|
{ chainId: networkId }
|
||||||
let providerUrl = localStorage.getItem('config:web3ProviderUrl') || config.web3ProviderUrl;
|
|
||||||
let provider = new Web3.providers.HttpProvider(providerUrl);
|
|
||||||
web3Instance = new Web3(provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.set('web3Instance', web3Instance);
|
|
||||||
window.web3 = web3Instance;
|
|
||||||
|
|
||||||
return web3Instance;
|
|
||||||
}.property('web3Instance'),
|
|
||||||
|
|
||||||
currentUserAccounts: function() {
|
|
||||||
return (this.get('web3Provided') && this.get('web3').eth.accounts) || [];
|
|
||||||
}.property('web3Provided', 'web3'),
|
|
||||||
|
|
||||||
initializeKreditsContract() {
|
|
||||||
let contract = null;
|
|
||||||
|
|
||||||
if (isPresent(config.contractMetadata)) {
|
|
||||||
if (localStorage.getItem('config:networkId')) {
|
|
||||||
config.contractMetadata['networkId'] = localStorage.getItem('config:networkId');
|
|
||||||
}
|
|
||||||
contract = kreditsContracts(this.get('web3'), config.contractMetadata)['Kredits'];
|
|
||||||
} else {
|
|
||||||
contract = kreditsContracts(this.get('web3'))['Kredits'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return contract;
|
|
||||||
},
|
|
||||||
|
|
||||||
kreditsContract: function() {
|
|
||||||
if (this.get('kreditsContractInstance')) {
|
|
||||||
return this.get('kreditsContractInstance');
|
|
||||||
}
|
|
||||||
|
|
||||||
let contract = this.initializeKreditsContract();
|
|
||||||
|
|
||||||
this.set('kreditsContractInstance', contract);
|
|
||||||
// window.Kredits = contract;
|
|
||||||
return contract;
|
|
||||||
}.property('kreditsContractInstance', 'web3'),
|
|
||||||
|
|
||||||
tokenContract: function() {
|
|
||||||
if (this.get('tokenContractInstance')) {
|
|
||||||
return this.get('tokenContractInstance');
|
|
||||||
}
|
|
||||||
|
|
||||||
let contract = kreditsContracts(this.get('web3'), config.contractMetadata)['Token'];
|
|
||||||
this.set('tokenContractInstance', contract);
|
|
||||||
window.Token = contract;
|
|
||||||
return contract;
|
|
||||||
}.property('tokenContractInstance', 'web3'),
|
|
||||||
|
|
||||||
getValueFromContract(contract, contractMethod, ...args) {
|
|
||||||
Ember.Logger.debug('[kredits] read from contract', contract);
|
|
||||||
return new Ember.RSVP.Promise((resolve, reject) => {
|
|
||||||
this.get(contract)[contractMethod](...args, (err, data) => {
|
|
||||||
if (err) { reject(err); return; }
|
|
||||||
resolve(data);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
getContributorData(i) {
|
|
||||||
let promise = new Ember.RSVP.Promise((resolve, reject) => {
|
|
||||||
this.getValueFromContract('kreditsContract', 'contributorAddresses', i).then(address => {
|
|
||||||
this.getValueFromContract('kreditsContract', 'contributors', address).then(person => {
|
|
||||||
this.getValueFromContract('tokenContract', 'balanceOf', address).then(balance => {
|
|
||||||
Ember.Logger.debug('[kredits] person', address, person);
|
|
||||||
|
|
||||||
let contributor = Contributor.create({
|
|
||||||
address: address,
|
|
||||||
ipfsHash: person[2],
|
|
||||||
kredits: balance.toNumber(),
|
|
||||||
isCurrentUser: this.get('currentUserAccounts').includes(address)
|
|
||||||
});
|
|
||||||
|
|
||||||
contributor.loadProfile(this.get('ipfs')).then(
|
|
||||||
() => resolve(contributor),
|
|
||||||
err => reject(err)
|
|
||||||
);
|
);
|
||||||
|
ethProvider.listAccounts().then((accounts) => {
|
||||||
|
this.set('currentUserAccounts', accounts);
|
||||||
|
const ethSigner = accounts.length === 0 ? null : ethProvider.getSigner();
|
||||||
|
resolve({
|
||||||
|
ethProvider,
|
||||||
|
ethSigner
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}).catch(err => reject(err));
|
} else {
|
||||||
|
console.debug('[kredits] Creating new instance from npm module class');
|
||||||
|
networkId = parseInt(config.contractMetadata.networkId);
|
||||||
|
console.debug(`[kredits] networkId=${networkId} providerURL: ${config.web3ProviderUrl}`);
|
||||||
|
ethProvider = new ethers.providers.JsonRpcProvider(
|
||||||
|
config.web3ProviderUrl,
|
||||||
|
{ chainId: networkId }
|
||||||
|
);
|
||||||
|
resolve({
|
||||||
|
ethProvider: ethProvider,
|
||||||
|
ethSigner: null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
return this.getEthProvider().then((providerAndSigner) => {
|
||||||
|
|
||||||
|
let kredits = new Kredits(providerAndSigner.ethProvider, providerAndSigner.ethSigner, {
|
||||||
|
ipfsConfig: config.ipfs
|
||||||
|
});
|
||||||
|
return kredits
|
||||||
|
.init()
|
||||||
|
.then((kredits) => {
|
||||||
|
this.set('kredits', kredits);
|
||||||
|
if (this.currentUserAccounts && this.currentUserAccounts.length > 0) {
|
||||||
|
this.getCurrentUser.then((contributorData) => {
|
||||||
|
this.set('currentUser', contributorData);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return kredits;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
totalSupply: computed(function() {
|
||||||
|
return this.kredits.Token.functions.totalSupply();
|
||||||
|
}),
|
||||||
|
|
||||||
|
contributors: [],
|
||||||
|
proposals: [],
|
||||||
|
|
||||||
|
loadContributorsAndProposals() {
|
||||||
|
return this.getContributors()
|
||||||
|
.then(contributors => this.contributors.pushObjects(contributors))
|
||||||
|
.then(() => this.getProposals())
|
||||||
|
.then(proposals => this.proposals.pushObjects(proposals))
|
||||||
|
},
|
||||||
|
|
||||||
|
addContributor(attributes) {
|
||||||
|
console.debug('[kredits] add contributor', attributes);
|
||||||
|
|
||||||
|
return this.kredits.Contributor.add(attributes)
|
||||||
|
.then((data) => {
|
||||||
|
console.debug('[kredits] add contributor response', data);
|
||||||
|
return Contributor.create(attributes);
|
||||||
});
|
});
|
||||||
return promise;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getContributors() {
|
getContributors() {
|
||||||
return this.getValueFromContract('kreditsContract', 'contributorsCount').then(contributorsCount => {
|
return this.kredits.Contributor.all()
|
||||||
let contributors = [];
|
.then((contributors) => {
|
||||||
|
return contributors.map((contributor) => {
|
||||||
for(var i = 0; i < contributorsCount.toNumber(); i++) {
|
return Contributor.create(contributor);
|
||||||
contributors.push(this.getContributorData(i));
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return Ember.RSVP.all(contributors);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
getProposalData(i) {
|
addProposal(attributes) {
|
||||||
let promise = new Ember.RSVP.Promise((resolve, reject) => {
|
console.debug('[kredits] add proposal', attributes);
|
||||||
this.getValueFromContract('kreditsContract', 'proposals', i).then(p => {
|
|
||||||
let proposal = Proposal.create({
|
|
||||||
id : i,
|
|
||||||
creatorAddress : p[0],
|
|
||||||
recipientAddress : p[1],
|
|
||||||
votesCount : p[2].toNumber(),
|
|
||||||
votesNeeded : p[3].toNumber(),
|
|
||||||
amount : p[4].toNumber(),
|
|
||||||
executed : p[5],
|
|
||||||
url : p[6],
|
|
||||||
ipfsHash : p[7]
|
|
||||||
});
|
|
||||||
|
|
||||||
if (proposal.get('ipfsHash')) {
|
return this.kredits.Operator.addProposal(attributes)
|
||||||
proposal.loadContribution(this.get('ipfs')).then(
|
.then((data) => {
|
||||||
() => resolve(proposal),
|
console.debug('[kredits] add proposal response', data);
|
||||||
err => reject(err)
|
attributes.contributor = this.contributors.findBy('id', attributes.contributorId);
|
||||||
);
|
return Proposal.create(attributes);
|
||||||
} else {
|
|
||||||
Ember.Logger.warn('[kredits] proposal from blockchain is missing IPFS hash', proposal);
|
|
||||||
resolve(proposal);
|
|
||||||
}
|
|
||||||
}).catch(err => reject(err));
|
|
||||||
});
|
});
|
||||||
return promise;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getProposals() {
|
getProposals() {
|
||||||
return this.getValueFromContract('kreditsContract', 'proposalsCount').then(proposalsCount => {
|
return this.kredits.Operator.all()
|
||||||
let proposals = [];
|
.then((proposals) => {
|
||||||
|
return proposals.map((proposal) => {
|
||||||
for(var i = 0; i < proposalsCount.toNumber(); i++) {
|
proposal.contributor = this.contributors.findBy('id', proposal.contributorId.toString());
|
||||||
proposals.push(this.getProposalData(i));
|
return Proposal.create(proposal);
|
||||||
}
|
});
|
||||||
|
|
||||||
return Ember.RSVP.all(proposals);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
vote(proposalId) {
|
vote(proposalId) {
|
||||||
Ember.Logger.debug('[kredits] vote for', proposalId);
|
console.debug('[kredits] vote for', proposalId);
|
||||||
return new Ember.RSVP.Promise((resolve, reject) => {
|
|
||||||
this.get('kreditsContract').vote(proposalId, (err, data) => {
|
return this.kredits.Operator.functions.vote(proposalId)
|
||||||
if (err) { reject(err); return; }
|
.then((data) => {
|
||||||
Ember.Logger.debug('[kredits] vote response', data);
|
console.debug('[kredits] vote response', data);
|
||||||
resolve(data);
|
return data;
|
||||||
});
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
addContributor(contributor) {
|
getCurrentUser: computed('kredits.provider', function() {
|
||||||
Ember.Logger.debug('[kredits] add contributor', contributor);
|
if (isEmpty(this.currentUserAccounts)) {
|
||||||
|
return RSVP.resolve();
|
||||||
contributor.setProperties({
|
}
|
||||||
kredits: 0,
|
return this.kredits.Contributor
|
||||||
isCurrentUser: this.get('currentUserAccounts').includes(contributor.address)
|
.functions.getContributorIdByAddress(this.get('currentUserAccounts.firstObject'))
|
||||||
|
.then((id) => {
|
||||||
|
id = id.toNumber();
|
||||||
|
// check if the user is a contributor or not
|
||||||
|
if (id === 0) {
|
||||||
|
return RSVP.resolve();
|
||||||
|
} else {
|
||||||
|
return this.kredits.Contributor.getById(id);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
let id = uuid.v4();
|
findProposalById(proposalId) {
|
||||||
|
return this.proposals.findBy('id', proposalId.toString());
|
||||||
|
},
|
||||||
|
|
||||||
return new Ember.RSVP.Promise((resolve, reject) => {
|
// Contract events
|
||||||
this.get('ipfs').storeFile(contributor.serialize()).then(ipfsHash => {
|
addContractEventHandlers() {
|
||||||
contributor.set('ipfsHash', ipfsHash);
|
// Operator events
|
||||||
this.get('kreditsContract').addContributor(contributor.address, contributor.name, contributor.ipfsHash, contributor.isCore, id, (err, data) => {
|
this.kredits.Operator
|
||||||
if (err) { reject(err); return; }
|
.on('ProposalCreated', this.handleProposalCreated.bind(this))
|
||||||
Ember.Logger.debug('[kredits] add contributor response', data);
|
.on('ProposalVoted', this.handleProposalVoted.bind(this))
|
||||||
resolve(contributor);
|
.on('ProposalExecuted', this.handleProposalExecuted.bind(this));
|
||||||
});
|
|
||||||
});
|
// Token events
|
||||||
|
this.kredits.Token
|
||||||
|
.on('Transfer', this.handleTransfer.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
handleProposalCreated(proposalId) {
|
||||||
|
let proposal = this.findProposalById(proposalId);
|
||||||
|
|
||||||
|
if (proposal) {
|
||||||
|
console.debug('[events] proposal exists, not adding from event');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.kredits.Operator.getById(proposalId)
|
||||||
|
.then((proposal) => {
|
||||||
|
proposal.contributor = this.contributors.findBy('id', proposal.contributorId.toString());
|
||||||
|
this.proposals.pushObject(Proposal.create(proposal));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
addProposal(proposal) {
|
// TODO: We may want to reload that proposal to show the voter as voted
|
||||||
return new Ember.RSVP.Promise((resolve, reject) => {
|
handleProposalVoted(proposalId, voterId, totalVotes) {
|
||||||
const {
|
let proposal = this.findProposalById(proposalId);
|
||||||
recipientAddress,
|
|
||||||
amount,
|
|
||||||
url
|
|
||||||
} = proposal.getProperties('recipientAddress', 'amount', 'url');
|
|
||||||
|
|
||||||
this.get('ipfs').storeFile(proposal.serializeContribution()).then(ipfsHash => {
|
if (proposal) {
|
||||||
this.get('kreditsContract').addProposal(recipientAddress, amount, url, ipfsHash, (err, data) => {
|
proposal.set('votesCount', totalVotes);
|
||||||
if (err) { reject(err); return; }
|
}
|
||||||
Ember.Logger.debug('[kredits] add proposal response', data);
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// logKreditsContract: function() {
|
handleProposalExecuted(proposalId, contributorId, amount) {
|
||||||
// Ember.Logger.debug('[kredits] kreditsContract', this.get('kreditsContract'));
|
let proposal = this.findProposalById(proposalId);
|
||||||
// }.on('init')
|
|
||||||
|
|
||||||
|
if (proposal.get('isExecuted')) {
|
||||||
|
console.debug('[events] proposal already executed, not adding from event');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
proposal.set('executed', true);
|
||||||
|
|
||||||
|
this.contributors
|
||||||
|
.findBy('id', contributorId.toString())
|
||||||
|
.incrementProperty('balance', amount.toNumber());
|
||||||
|
},
|
||||||
|
|
||||||
|
handleTransfer(from, to, value) {
|
||||||
|
value = value.toNumber();
|
||||||
|
|
||||||
|
this.contributors
|
||||||
|
.findBy('address', from)
|
||||||
|
.decrementProperty('balance', value);
|
||||||
|
|
||||||
|
this.contributors
|
||||||
|
.findBy('address', to)
|
||||||
|
.incrementProperty('balance', value);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
+14
-1
@@ -5,4 +5,17 @@ $green: #97fb68;
|
|||||||
$yellow: #fbe468;
|
$yellow: #fbe468;
|
||||||
$red: #fb6868;
|
$red: #fb6868;
|
||||||
|
|
||||||
$primaryColor: $blue;
|
$primary-color: $blue;
|
||||||
|
|
||||||
|
$body-text-color: #fff;
|
||||||
|
|
||||||
|
@mixin body-background {
|
||||||
|
background-image: linear-gradient(to bottom, rgba(22, 21, 40, .4), rgba(0, 0, 0, .75)), url('/img/bg.jpg');
|
||||||
|
background-repeat: none;
|
||||||
|
background-attachment: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
#topbar {
|
||||||
|
background-color: rgba(255, 0, 255, .2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
+36
-14
@@ -1,31 +1,53 @@
|
|||||||
section {
|
#topbar {
|
||||||
@include outer-container;
|
height: 3rem;
|
||||||
margin-bottom: 8rem;
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
padding: 1rem;
|
||||||
|
|
||||||
|
&#index {
|
||||||
|
width: 100%;
|
||||||
|
display: grid;
|
||||||
|
grid-row-gap: 2rem;
|
||||||
|
grid-template-areas:
|
||||||
|
"stats"
|
||||||
|
"contributions";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 550px) {
|
||||||
|
main {
|
||||||
|
&#index {
|
||||||
|
grid-column-gap: 4rem;
|
||||||
|
grid-row-gap: 2rem;
|
||||||
|
grid-template-columns: 2fr 4fr;
|
||||||
|
grid-template-areas:
|
||||||
|
"stats contributions";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main section {
|
||||||
|
margin-bottom: 5rem;
|
||||||
|
|
||||||
&:first-of-type {
|
&:first-of-type {
|
||||||
margin-top: 6rem;
|
margin-top: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include media($mobile) {
|
@include media($mobile) {
|
||||||
margin-bottom: 5rem;
|
margin-bottom: 5rem;
|
||||||
&:first-of-type {
|
|
||||||
margin-top: 2rem;
|
&#proposals-open, &#proposals-closed {
|
||||||
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
@include span-columns(12);
|
|
||||||
padding-bottom: 3rem;
|
padding-bottom: 3rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
// background-color: purple;
|
|
||||||
|
|
||||||
@include media($mobile) {
|
@include media($mobile) {
|
||||||
@include span-columns(10);
|
|
||||||
@include shift(1);
|
|
||||||
padding-bottom: 2rem;
|
padding-bottom: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.content {
|
|
||||||
@include span-columns(12);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-16
@@ -2,6 +2,7 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
*:hover, *:active, *:focus {
|
*:hover, *:active, *:focus {
|
||||||
@@ -10,9 +11,9 @@
|
|||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@import "bourbon";
|
@import "bourbon/bourbon";
|
||||||
@import "settings/neat";
|
@import "settings/neat";
|
||||||
@import "neat";
|
@import "neat/neat";
|
||||||
@import "settings/breakpoints";
|
@import "settings/breakpoints";
|
||||||
@import "layout";
|
@import "layout";
|
||||||
@import "colors";
|
@import "colors";
|
||||||
@@ -20,13 +21,10 @@
|
|||||||
$font-family-sans: 'Open Sans', sans-serif;
|
$font-family-sans: 'Open Sans', sans-serif;
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-image: url('/img/bg.jpg');
|
@include body-background;
|
||||||
background-repeat: none;
|
|
||||||
background-attachment: fixed;
|
|
||||||
font-family: $font-family-sans;
|
font-family: $font-family-sans;
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
color: #fff;
|
color: $body-text-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 900px) {
|
@media screen and (min-width: 900px) {
|
||||||
@@ -43,14 +41,11 @@ h1, h2, h3, h4, h5, input, button {
|
|||||||
|
|
||||||
section {
|
section {
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 2.8rem;
|
font-size: 1.4rem;
|
||||||
color: $primaryColor;
|
color: $primary-color;
|
||||||
@include media($mobile) {
|
|
||||||
font-size: 2rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&#contributors {
|
&#people {
|
||||||
.content {
|
.content {
|
||||||
p.stats {
|
p.stats {
|
||||||
padding-top: 3rem;
|
padding-top: 3rem;
|
||||||
@@ -67,18 +62,30 @@ section {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&#contributions-by-type {
|
||||||
|
.chart {
|
||||||
|
width: 50%;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include media($mobile) {
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&#proposals-open, &#proposals-closed {
|
&#proposals-open, &#proposals-closed {
|
||||||
.actions {
|
.actions {
|
||||||
padding-top: 3rem;
|
padding-top: 3rem;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
color: $primaryColor;
|
color: $primary-color;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@include media($mobile) {
|
@include media($mobile) {
|
||||||
padding-top: 2rem;
|
padding-top: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: $primaryColor;
|
color: $primary-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,7 +95,7 @@ button, input[type=submit] {
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
border: 1px solid rgba(22, 21, 40, 1);
|
border: 1px solid rgba(22, 21, 40, 1);
|
||||||
background-color: rgba(22, 21, 40, 0.6);
|
background-color: rgba(22, 21, 40, 0.6);
|
||||||
color: $primaryColor;
|
color: $primary-color;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
@@ -100,6 +107,15 @@ button, input[type=submit] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (min-width: 550px) {
|
||||||
|
section {
|
||||||
|
h2 {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@import "components/topbar";
|
||||||
@import "components/loading-spinner";
|
@import "components/loading-spinner";
|
||||||
@import "components/contributor-list";
|
@import "components/contributor-list";
|
||||||
@import "components/add-contributor";
|
@import "components/add-contributor";
|
||||||
|
|||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Throws Sass warnings to announce library deprecations. You can disable them
|
||||||
|
/// by setting the `$output-bourbon-deprecation-warnings` variable to `false`.
|
||||||
|
///
|
||||||
|
/// @access private
|
||||||
|
|
||||||
|
@mixin _bourbon-deprecate($feature, $message: null) {
|
||||||
|
@if $output-bourbon-deprecation-warnings == true {
|
||||||
|
@warn "[Bourbon] [Deprecation] `#{$feature}` is deprecated and will be " +
|
||||||
|
"removed in 5.0.0. #{$message}";
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin _bourbon-deprecate-for-prefixing($feature) {
|
||||||
|
@include _bourbon-deprecate($feature, "We suggest using an automated " +
|
||||||
|
"prefixing tool, like Autoprefixer.");
|
||||||
|
}
|
||||||
@@ -0,0 +1,425 @@
|
|||||||
|
// The following features have been deprecated and will be removed in the next MAJOR version release
|
||||||
|
|
||||||
|
@mixin inline-block {
|
||||||
|
@include _bourbon-deprecate("inline-block");
|
||||||
|
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin button ($style: simple, $base-color: #4294f0, $text-size: inherit, $padding: 7px 18px) {
|
||||||
|
@include _bourbon-deprecate("button");
|
||||||
|
|
||||||
|
@if type-of($style) == string and type-of($base-color) == color {
|
||||||
|
@include buttonstyle($style, $base-color, $text-size, $padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
@if type-of($style) == string and type-of($base-color) == number {
|
||||||
|
$padding: $text-size;
|
||||||
|
$text-size: $base-color;
|
||||||
|
$base-color: #4294f0;
|
||||||
|
|
||||||
|
@if $padding == inherit {
|
||||||
|
$padding: 7px 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include buttonstyle($style, $base-color, $text-size, $padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
@if type-of($style) == color and type-of($base-color) == color {
|
||||||
|
$base-color: $style;
|
||||||
|
$style: simple;
|
||||||
|
@include buttonstyle($style, $base-color, $text-size, $padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
@if type-of($style) == color and type-of($base-color) == number {
|
||||||
|
$padding: $text-size;
|
||||||
|
$text-size: $base-color;
|
||||||
|
$base-color: $style;
|
||||||
|
$style: simple;
|
||||||
|
|
||||||
|
@if $padding == inherit {
|
||||||
|
$padding: 7px 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include buttonstyle($style, $base-color, $text-size, $padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
@if type-of($style) == number {
|
||||||
|
$padding: $base-color;
|
||||||
|
$text-size: $style;
|
||||||
|
$base-color: #4294f0;
|
||||||
|
$style: simple;
|
||||||
|
|
||||||
|
@if $padding == #4294f0 {
|
||||||
|
$padding: 7px 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include buttonstyle($style, $base-color, $text-size, $padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Selector Style Button
|
||||||
|
@mixin buttonstyle($type, $b-color, $t-size, $pad) {
|
||||||
|
// Grayscale button
|
||||||
|
@if $type == simple and $b-color == grayscale($b-color) {
|
||||||
|
@include simple($b-color, true, $t-size, $pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $type == shiny and $b-color == grayscale($b-color) {
|
||||||
|
@include shiny($b-color, true, $t-size, $pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $type == pill and $b-color == grayscale($b-color) {
|
||||||
|
@include pill($b-color, true, $t-size, $pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $type == flat and $b-color == grayscale($b-color) {
|
||||||
|
@include flat($b-color, true, $t-size, $pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Colored button
|
||||||
|
@if $type == simple {
|
||||||
|
@include simple($b-color, false, $t-size, $pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
@else if $type == shiny {
|
||||||
|
@include shiny($b-color, false, $t-size, $pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
@else if $type == pill {
|
||||||
|
@include pill($b-color, false, $t-size, $pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
@else if $type == flat {
|
||||||
|
@include flat($b-color, false, $t-size, $pad);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple Button
|
||||||
|
@mixin simple($base-color, $grayscale: false, $textsize: inherit, $padding: 7px 18px) {
|
||||||
|
$color: hsl(0, 0, 100%);
|
||||||
|
$border: adjust-color($base-color, $saturation: 9%, $lightness: -14%);
|
||||||
|
$inset-shadow: adjust-color($base-color, $saturation: -8%, $lightness: 15%);
|
||||||
|
$stop-gradient: adjust-color($base-color, $saturation: 9%, $lightness: -11%);
|
||||||
|
$text-shadow: adjust-color($base-color, $saturation: 15%, $lightness: -18%);
|
||||||
|
|
||||||
|
@if is-light($base-color) {
|
||||||
|
$color: hsl(0, 0, 20%);
|
||||||
|
$text-shadow: adjust-color($base-color, $saturation: 10%, $lightness: 4%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $grayscale == true {
|
||||||
|
$border: grayscale($border);
|
||||||
|
$inset-shadow: grayscale($inset-shadow);
|
||||||
|
$stop-gradient: grayscale($stop-gradient);
|
||||||
|
$text-shadow: grayscale($text-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 3px;
|
||||||
|
box-shadow: inset 0 1px 0 0 $inset-shadow;
|
||||||
|
color: $color;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: $textsize;
|
||||||
|
font-weight: bold;
|
||||||
|
@include linear-gradient ($base-color, $stop-gradient);
|
||||||
|
padding: $padding;
|
||||||
|
text-decoration: none;
|
||||||
|
text-shadow: 0 1px 0 $text-shadow;
|
||||||
|
background-clip: padding-box;
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
$base-color-hover: adjust-color($base-color, $saturation: -4%, $lightness: -5%);
|
||||||
|
$inset-shadow-hover: adjust-color($base-color, $saturation: -7%, $lightness: 5%);
|
||||||
|
$stop-gradient-hover: adjust-color($base-color, $saturation: 8%, $lightness: -14%);
|
||||||
|
|
||||||
|
@if $grayscale == true {
|
||||||
|
$base-color-hover: grayscale($base-color-hover);
|
||||||
|
$inset-shadow-hover: grayscale($inset-shadow-hover);
|
||||||
|
$stop-gradient-hover: grayscale($stop-gradient-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
@include linear-gradient ($base-color-hover, $stop-gradient-hover);
|
||||||
|
|
||||||
|
box-shadow: inset 0 1px 0 0 $inset-shadow-hover;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active:not(:disabled),
|
||||||
|
&:focus:not(:disabled) {
|
||||||
|
$border-active: adjust-color($base-color, $saturation: 9%, $lightness: -14%);
|
||||||
|
$inset-shadow-active: adjust-color($base-color, $saturation: 7%, $lightness: -17%);
|
||||||
|
|
||||||
|
@if $grayscale == true {
|
||||||
|
$border-active: grayscale($border-active);
|
||||||
|
$inset-shadow-active: grayscale($inset-shadow-active);
|
||||||
|
}
|
||||||
|
|
||||||
|
border: 1px solid $border-active;
|
||||||
|
box-shadow: inset 0 0 8px 4px $inset-shadow-active, inset 0 0 8px 4px $inset-shadow-active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shiny Button
|
||||||
|
@mixin shiny($base-color, $grayscale: false, $textsize: inherit, $padding: 7px 18px) {
|
||||||
|
$color: hsl(0, 0, 100%);
|
||||||
|
$border: adjust-color($base-color, $red: -117, $green: -111, $blue: -81);
|
||||||
|
$border-bottom: adjust-color($base-color, $red: -126, $green: -127, $blue: -122);
|
||||||
|
$fourth-stop: adjust-color($base-color, $red: -79, $green: -70, $blue: -46);
|
||||||
|
$inset-shadow: adjust-color($base-color, $red: 37, $green: 29, $blue: 12);
|
||||||
|
$second-stop: adjust-color($base-color, $red: -56, $green: -50, $blue: -33);
|
||||||
|
$text-shadow: adjust-color($base-color, $red: -140, $green: -141, $blue: -114);
|
||||||
|
$third-stop: adjust-color($base-color, $red: -86, $green: -75, $blue: -48);
|
||||||
|
|
||||||
|
@if is-light($base-color) {
|
||||||
|
$color: hsl(0, 0, 20%);
|
||||||
|
$text-shadow: adjust-color($base-color, $saturation: 10%, $lightness: 4%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $grayscale == true {
|
||||||
|
$border: grayscale($border);
|
||||||
|
$border-bottom: grayscale($border-bottom);
|
||||||
|
$fourth-stop: grayscale($fourth-stop);
|
||||||
|
$inset-shadow: grayscale($inset-shadow);
|
||||||
|
$second-stop: grayscale($second-stop);
|
||||||
|
$text-shadow: grayscale($text-shadow);
|
||||||
|
$third-stop: grayscale($third-stop);
|
||||||
|
}
|
||||||
|
|
||||||
|
@include linear-gradient(top, $base-color 0%, $second-stop 50%, $third-stop 50%, $fourth-stop 100%);
|
||||||
|
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-bottom: 1px solid $border-bottom;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: inset 0 1px 0 0 $inset-shadow;
|
||||||
|
color: $color;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: $textsize;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: $padding;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
text-shadow: 0 -1px 1px $text-shadow;
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
$first-stop-hover: adjust-color($base-color, $red: -13, $green: -15, $blue: -18);
|
||||||
|
$second-stop-hover: adjust-color($base-color, $red: -66, $green: -62, $blue: -51);
|
||||||
|
$third-stop-hover: adjust-color($base-color, $red: -93, $green: -85, $blue: -66);
|
||||||
|
$fourth-stop-hover: adjust-color($base-color, $red: -86, $green: -80, $blue: -63);
|
||||||
|
|
||||||
|
@if $grayscale == true {
|
||||||
|
$first-stop-hover: grayscale($first-stop-hover);
|
||||||
|
$second-stop-hover: grayscale($second-stop-hover);
|
||||||
|
$third-stop-hover: grayscale($third-stop-hover);
|
||||||
|
$fourth-stop-hover: grayscale($fourth-stop-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
@include linear-gradient(top, $first-stop-hover 0%,
|
||||||
|
$second-stop-hover 50%,
|
||||||
|
$third-stop-hover 50%,
|
||||||
|
$fourth-stop-hover 100%);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active:not(:disabled),
|
||||||
|
&:focus:not(:disabled) {
|
||||||
|
$inset-shadow-active: adjust-color($base-color, $red: -111, $green: -116, $blue: -122);
|
||||||
|
|
||||||
|
@if $grayscale == true {
|
||||||
|
$inset-shadow-active: grayscale($inset-shadow-active);
|
||||||
|
}
|
||||||
|
|
||||||
|
box-shadow: inset 0 0 20px 0 $inset-shadow-active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pill Button
|
||||||
|
@mixin pill($base-color, $grayscale: false, $textsize: inherit, $padding: 7px 18px) {
|
||||||
|
$color: hsl(0, 0, 100%);
|
||||||
|
$border-bottom: adjust-color($base-color, $hue: 8, $saturation: -11%, $lightness: -26%);
|
||||||
|
$border-sides: adjust-color($base-color, $hue: 4, $saturation: -21%, $lightness: -21%);
|
||||||
|
$border-top: adjust-color($base-color, $hue: -1, $saturation: -30%, $lightness: -15%);
|
||||||
|
$inset-shadow: adjust-color($base-color, $hue: -1, $saturation: -1%, $lightness: 7%);
|
||||||
|
$stop-gradient: adjust-color($base-color, $hue: 8, $saturation: 14%, $lightness: -10%);
|
||||||
|
$text-shadow: adjust-color($base-color, $hue: 5, $saturation: -19%, $lightness: -15%);
|
||||||
|
|
||||||
|
@if is-light($base-color) {
|
||||||
|
$color: hsl(0, 0, 20%);
|
||||||
|
$text-shadow: adjust-color($base-color, $saturation: 10%, $lightness: 4%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $grayscale == true {
|
||||||
|
$border-bottom: grayscale($border-bottom);
|
||||||
|
$border-sides: grayscale($border-sides);
|
||||||
|
$border-top: grayscale($border-top);
|
||||||
|
$inset-shadow: grayscale($inset-shadow);
|
||||||
|
$stop-gradient: grayscale($stop-gradient);
|
||||||
|
$text-shadow: grayscale($text-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
border: 1px solid $border-top;
|
||||||
|
border-color: $border-top $border-sides $border-bottom;
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: inset 0 1px 0 0 $inset-shadow;
|
||||||
|
color: $color;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: $textsize;
|
||||||
|
font-weight: normal;
|
||||||
|
line-height: 1;
|
||||||
|
@include linear-gradient ($base-color, $stop-gradient);
|
||||||
|
padding: $padding;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
text-shadow: 0 -1px 1px $text-shadow;
|
||||||
|
background-clip: padding-box;
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
$base-color-hover: adjust-color($base-color, $lightness: -4.5%);
|
||||||
|
$border-bottom: adjust-color($base-color, $hue: 8, $saturation: 13.5%, $lightness: -32%);
|
||||||
|
$border-sides: adjust-color($base-color, $hue: 4, $saturation: -2%, $lightness: -27%);
|
||||||
|
$border-top: adjust-color($base-color, $hue: -1, $saturation: -17%, $lightness: -21%);
|
||||||
|
$inset-shadow-hover: adjust-color($base-color, $saturation: -1%, $lightness: 3%);
|
||||||
|
$stop-gradient-hover: adjust-color($base-color, $hue: 8, $saturation: -4%, $lightness: -15.5%);
|
||||||
|
$text-shadow-hover: adjust-color($base-color, $hue: 5, $saturation: -5%, $lightness: -22%);
|
||||||
|
|
||||||
|
@if $grayscale == true {
|
||||||
|
$base-color-hover: grayscale($base-color-hover);
|
||||||
|
$border-bottom: grayscale($border-bottom);
|
||||||
|
$border-sides: grayscale($border-sides);
|
||||||
|
$border-top: grayscale($border-top);
|
||||||
|
$inset-shadow-hover: grayscale($inset-shadow-hover);
|
||||||
|
$stop-gradient-hover: grayscale($stop-gradient-hover);
|
||||||
|
$text-shadow-hover: grayscale($text-shadow-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
@include linear-gradient ($base-color-hover, $stop-gradient-hover);
|
||||||
|
|
||||||
|
background-clip: padding-box;
|
||||||
|
border: 1px solid $border-top;
|
||||||
|
border-color: $border-top $border-sides $border-bottom;
|
||||||
|
box-shadow: inset 0 1px 0 0 $inset-shadow-hover;
|
||||||
|
cursor: pointer;
|
||||||
|
text-shadow: 0 -1px 1px $text-shadow-hover;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active:not(:disabled),
|
||||||
|
&:focus:not(:disabled) {
|
||||||
|
$active-color: adjust-color($base-color, $hue: 4, $saturation: -12%, $lightness: -10%);
|
||||||
|
$border-active: adjust-color($base-color, $hue: 6, $saturation: -2.5%, $lightness: -30%);
|
||||||
|
$border-bottom-active: adjust-color($base-color, $hue: 11, $saturation: 6%, $lightness: -31%);
|
||||||
|
$inset-shadow-active: adjust-color($base-color, $hue: 9, $saturation: 2%, $lightness: -21.5%);
|
||||||
|
$text-shadow-active: adjust-color($base-color, $hue: 5, $saturation: -12%, $lightness: -21.5%);
|
||||||
|
|
||||||
|
@if $grayscale == true {
|
||||||
|
$active-color: grayscale($active-color);
|
||||||
|
$border-active: grayscale($border-active);
|
||||||
|
$border-bottom-active: grayscale($border-bottom-active);
|
||||||
|
$inset-shadow-active: grayscale($inset-shadow-active);
|
||||||
|
$text-shadow-active: grayscale($text-shadow-active);
|
||||||
|
}
|
||||||
|
|
||||||
|
background: $active-color;
|
||||||
|
border: 1px solid $border-active;
|
||||||
|
border-bottom: 1px solid $border-bottom-active;
|
||||||
|
box-shadow: inset 0 0 6px 3px $inset-shadow-active;
|
||||||
|
text-shadow: 0 -1px 1px $text-shadow-active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flat Button
|
||||||
|
@mixin flat($base-color, $grayscale: false, $textsize: inherit, $padding: 7px 18px) {
|
||||||
|
$color: hsl(0, 0, 100%);
|
||||||
|
|
||||||
|
@if is-light($base-color) {
|
||||||
|
$color: hsl(0, 0, 20%);
|
||||||
|
}
|
||||||
|
|
||||||
|
background-color: $base-color;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 0;
|
||||||
|
color: $color;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: $textsize;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: $padding;
|
||||||
|
text-decoration: none;
|
||||||
|
background-clip: padding-box;
|
||||||
|
|
||||||
|
&:hover:not(:disabled){
|
||||||
|
$base-color-hover: adjust-color($base-color, $saturation: 4%, $lightness: 5%);
|
||||||
|
|
||||||
|
@if $grayscale == true {
|
||||||
|
$base-color-hover: grayscale($base-color-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
background-color: $base-color-hover;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active:not(:disabled),
|
||||||
|
&:focus:not(:disabled) {
|
||||||
|
$base-color-active: adjust-color($base-color, $saturation: -4%, $lightness: -5%);
|
||||||
|
|
||||||
|
@if $grayscale == true {
|
||||||
|
$base-color-active: grayscale($base-color-active);
|
||||||
|
}
|
||||||
|
|
||||||
|
background-color: $base-color-active;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flexible grid
|
||||||
|
@function flex-grid($columns, $container-columns: $fg-max-columns) {
|
||||||
|
@if $output-bourbon-deprecation-warnings == true {
|
||||||
|
@warn "[Bourbon] [Deprecation] `flex-grid` is deprecated and will be " +
|
||||||
|
"removed in 5.0.0. For grid functions, check out Bourbon's sister library" +
|
||||||
|
"Neat.";
|
||||||
|
}
|
||||||
|
|
||||||
|
$width: $columns * $fg-column + ($columns - 1) * $fg-gutter;
|
||||||
|
$container-width: $container-columns * $fg-column + ($container-columns - 1) * $fg-gutter;
|
||||||
|
@return percentage($width / $container-width);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flexible gutter
|
||||||
|
@function flex-gutter($container-columns: $fg-max-columns, $gutter: $fg-gutter) {
|
||||||
|
@if $output-bourbon-deprecation-warnings == true {
|
||||||
|
@warn "[Bourbon] [Deprecation] `flex-gutter` is deprecated and will be " +
|
||||||
|
"removed in 5.0.0. For grid functions, check out Bourbon's sister library" +
|
||||||
|
"Neat.";
|
||||||
|
}
|
||||||
|
|
||||||
|
$container-width: $container-columns * $fg-column + ($container-columns - 1) * $fg-gutter;
|
||||||
|
@return percentage($gutter / $container-width);
|
||||||
|
}
|
||||||
|
|
||||||
|
@function grid-width($n) {
|
||||||
|
@if $output-bourbon-deprecation-warnings == true {
|
||||||
|
@warn "[Bourbon] [Deprecation] `grid-width` is deprecated and will be " +
|
||||||
|
"removed in 5.0.0. For grid functions, check out Bourbon's sister library" +
|
||||||
|
"Neat.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@return $n * $gw-column + ($n - 1) * $gw-gutter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@function golden-ratio($value, $increment) {
|
||||||
|
@if $output-bourbon-deprecation-warnings == true {
|
||||||
|
@warn "[Bourbon] [Deprecation] `golden-ratio` is deprecated and will be " +
|
||||||
|
"removed in 5.0.0. You can use the `modular-scale` function instead.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@return modular-scale($increment, $value, $ratio: $golden);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin box-sizing($box) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("box-sizing");
|
||||||
|
|
||||||
|
@include prefixer(box-sizing, $box, webkit moz spec);
|
||||||
|
}
|
||||||
Vendored
+90
@@ -0,0 +1,90 @@
|
|||||||
|
// Bourbon 4.3.0
|
||||||
|
// http://bourbon.io
|
||||||
|
// Copyright 2011-2017 thoughtbot, inc.
|
||||||
|
// MIT License
|
||||||
|
|
||||||
|
@import "settings/deprecation-warnings";
|
||||||
|
@import "settings/prefixer";
|
||||||
|
@import "settings/px-to-em";
|
||||||
|
@import "settings/asset-pipeline";
|
||||||
|
|
||||||
|
@import "bourbon-deprecate";
|
||||||
|
|
||||||
|
@import "functions/assign-inputs";
|
||||||
|
@import "functions/contains";
|
||||||
|
@import "functions/contains-falsy";
|
||||||
|
@import "functions/is-length";
|
||||||
|
@import "functions/is-light";
|
||||||
|
@import "functions/is-number";
|
||||||
|
@import "functions/is-size";
|
||||||
|
@import "functions/px-to-em";
|
||||||
|
@import "functions/px-to-rem";
|
||||||
|
@import "functions/shade";
|
||||||
|
@import "functions/strip-units";
|
||||||
|
@import "functions/tint";
|
||||||
|
@import "functions/transition-property-name";
|
||||||
|
@import "functions/unpack";
|
||||||
|
@import "functions/modular-scale";
|
||||||
|
|
||||||
|
@import "helpers/convert-units";
|
||||||
|
@import "helpers/directional-values";
|
||||||
|
@import "helpers/font-source-declaration";
|
||||||
|
@import "helpers/gradient-positions-parser";
|
||||||
|
@import "helpers/linear-angle-parser";
|
||||||
|
@import "helpers/linear-gradient-parser";
|
||||||
|
@import "helpers/linear-positions-parser";
|
||||||
|
@import "helpers/linear-side-corner-parser";
|
||||||
|
@import "helpers/radial-arg-parser";
|
||||||
|
@import "helpers/radial-positions-parser";
|
||||||
|
@import "helpers/radial-gradient-parser";
|
||||||
|
@import "helpers/render-gradients";
|
||||||
|
@import "helpers/shape-size-stripper";
|
||||||
|
@import "helpers/str-to-num";
|
||||||
|
|
||||||
|
@import "css3/animation";
|
||||||
|
@import "css3/appearance";
|
||||||
|
@import "css3/backface-visibility";
|
||||||
|
@import "css3/background";
|
||||||
|
@import "css3/background-image";
|
||||||
|
@import "css3/border-image";
|
||||||
|
@import "css3/calc";
|
||||||
|
@import "css3/columns";
|
||||||
|
@import "css3/filter";
|
||||||
|
@import "css3/flex-box";
|
||||||
|
@import "css3/font-face";
|
||||||
|
@import "css3/font-feature-settings";
|
||||||
|
@import "css3/hidpi-media-query";
|
||||||
|
@import "css3/hyphens";
|
||||||
|
@import "css3/image-rendering";
|
||||||
|
@import "css3/keyframes";
|
||||||
|
@import "css3/linear-gradient";
|
||||||
|
@import "css3/perspective";
|
||||||
|
@import "css3/placeholder";
|
||||||
|
@import "css3/radial-gradient";
|
||||||
|
@import "css3/selection";
|
||||||
|
@import "css3/text-decoration";
|
||||||
|
@import "css3/transform";
|
||||||
|
@import "css3/transition";
|
||||||
|
@import "css3/user-select";
|
||||||
|
|
||||||
|
@import "addons/border-color";
|
||||||
|
@import "addons/border-radius";
|
||||||
|
@import "addons/border-style";
|
||||||
|
@import "addons/border-width";
|
||||||
|
@import "addons/buttons";
|
||||||
|
@import "addons/clearfix";
|
||||||
|
@import "addons/ellipsis";
|
||||||
|
@import "addons/font-stacks";
|
||||||
|
@import "addons/hide-text";
|
||||||
|
@import "addons/margin";
|
||||||
|
@import "addons/padding";
|
||||||
|
@import "addons/position";
|
||||||
|
@import "addons/prefixer";
|
||||||
|
@import "addons/retina-image";
|
||||||
|
@import "addons/size";
|
||||||
|
@import "addons/text-inputs";
|
||||||
|
@import "addons/timing-functions";
|
||||||
|
@import "addons/triangle";
|
||||||
|
@import "addons/word-wrap";
|
||||||
|
|
||||||
|
@import "bourbon-deprecated-upcoming";
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Provides a quick method for targeting `border-color` on specific sides of a box. Use a `null` value to “skip” a side.
|
||||||
|
///
|
||||||
|
/// @param {Arglist} $vals
|
||||||
|
/// List of arguments
|
||||||
|
///
|
||||||
|
/// @example scss - Usage
|
||||||
|
/// .element {
|
||||||
|
/// @include border-color(#a60b55 #76cd9c null #e8ae1a);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @example css - CSS Output
|
||||||
|
/// .element {
|
||||||
|
/// border-left-color: #e8ae1a;
|
||||||
|
/// border-right-color: #76cd9c;
|
||||||
|
/// border-top-color: #a60b55;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @require {mixin} directional-property
|
||||||
|
///
|
||||||
|
/// @output `border-color`
|
||||||
|
|
||||||
|
@mixin border-color($vals...) {
|
||||||
|
@include directional-property(border, color, $vals...);
|
||||||
|
}
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Provides a quick method for targeting `border-radius` on both corners on the side of a box.
|
||||||
|
///
|
||||||
|
/// @param {Number} $radii
|
||||||
|
/// List of arguments
|
||||||
|
///
|
||||||
|
/// @example scss - Usage
|
||||||
|
/// .element-one {
|
||||||
|
/// @include border-top-radius(5px);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// .element-two {
|
||||||
|
/// @include border-left-radius(3px);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @example css - CSS Output
|
||||||
|
/// .element-one {
|
||||||
|
/// border-top-left-radius: 5px;
|
||||||
|
/// border-top-right-radius: 5px;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// .element-two {
|
||||||
|
/// border-bottom-left-radius: 3px;
|
||||||
|
/// border-top-left-radius: 3px;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @output `border-radius`
|
||||||
|
|
||||||
|
@mixin border-top-radius($radii) {
|
||||||
|
border-top-left-radius: $radii;
|
||||||
|
border-top-right-radius: $radii;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin border-right-radius($radii) {
|
||||||
|
border-bottom-right-radius: $radii;
|
||||||
|
border-top-right-radius: $radii;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin border-bottom-radius($radii) {
|
||||||
|
border-bottom-left-radius: $radii;
|
||||||
|
border-bottom-right-radius: $radii;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin border-left-radius($radii) {
|
||||||
|
border-bottom-left-radius: $radii;
|
||||||
|
border-top-left-radius: $radii;
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Provides a quick method for targeting `border-style` on specific sides of a box. Use a `null` value to “skip” a side.
|
||||||
|
///
|
||||||
|
/// @param {Arglist} $vals
|
||||||
|
/// List of arguments
|
||||||
|
///
|
||||||
|
/// @example scss - Usage
|
||||||
|
/// .element {
|
||||||
|
/// @include border-style(dashed null solid);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @example css - CSS Output
|
||||||
|
/// .element {
|
||||||
|
/// border-bottom-style: solid;
|
||||||
|
/// border-top-style: dashed;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @require {mixin} directional-property
|
||||||
|
///
|
||||||
|
/// @output `border-style`
|
||||||
|
|
||||||
|
@mixin border-style($vals...) {
|
||||||
|
@include directional-property(border, style, $vals...);
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Provides a quick method for targeting `border-width` on specific sides of a box. Use a `null` value to “skip” a side.
|
||||||
|
///
|
||||||
|
/// @param {Arglist} $vals
|
||||||
|
/// List of arguments
|
||||||
|
///
|
||||||
|
/// @example scss - Usage
|
||||||
|
/// .element {
|
||||||
|
/// @include border-width(1em null 20px);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @example css - CSS Output
|
||||||
|
/// .element {
|
||||||
|
/// border-bottom-width: 20px;
|
||||||
|
/// border-top-width: 1em;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @require {mixin} directional-property
|
||||||
|
///
|
||||||
|
/// @output `border-width`
|
||||||
|
|
||||||
|
@mixin border-width($vals...) {
|
||||||
|
@include directional-property(border, width, $vals...);
|
||||||
|
}
|
||||||
+64
@@ -0,0 +1,64 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Generates variables for all buttons. Please note that you must use interpolation on the variable: `#{$all-buttons}`.
|
||||||
|
///
|
||||||
|
/// @example scss - Usage
|
||||||
|
/// #{$all-buttons} {
|
||||||
|
/// background-color: #f00;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// #{$all-buttons-focus},
|
||||||
|
/// #{$all-buttons-hover} {
|
||||||
|
/// background-color: #0f0;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// #{$all-buttons-active} {
|
||||||
|
/// background-color: #00f;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @example css - CSS Output
|
||||||
|
/// button,
|
||||||
|
/// input[type="button"],
|
||||||
|
/// input[type="reset"],
|
||||||
|
/// input[type="submit"] {
|
||||||
|
/// background-color: #f00;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// button:focus,
|
||||||
|
/// input[type="button"]:focus,
|
||||||
|
/// input[type="reset"]:focus,
|
||||||
|
/// input[type="submit"]:focus,
|
||||||
|
/// button:hover,
|
||||||
|
/// input[type="button"]:hover,
|
||||||
|
/// input[type="reset"]:hover,
|
||||||
|
/// input[type="submit"]:hover {
|
||||||
|
/// background-color: #0f0;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// button:active,
|
||||||
|
/// input[type="button"]:active,
|
||||||
|
/// input[type="reset"]:active,
|
||||||
|
/// input[type="submit"]:active {
|
||||||
|
/// background-color: #00f;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @require assign-inputs
|
||||||
|
///
|
||||||
|
/// @type List
|
||||||
|
///
|
||||||
|
/// @todo Remove double assigned variables (Lines 59–62) in v5.0.0
|
||||||
|
|
||||||
|
$buttons-list: 'button',
|
||||||
|
'input[type="button"]',
|
||||||
|
'input[type="reset"]',
|
||||||
|
'input[type="submit"]';
|
||||||
|
|
||||||
|
$all-buttons: assign-inputs($buttons-list);
|
||||||
|
$all-buttons-active: assign-inputs($buttons-list, active);
|
||||||
|
$all-buttons-focus: assign-inputs($buttons-list, focus);
|
||||||
|
$all-buttons-hover: assign-inputs($buttons-list, hover);
|
||||||
|
|
||||||
|
$all-button-inputs: $all-buttons;
|
||||||
|
$all-button-inputs-active: $all-buttons-active;
|
||||||
|
$all-button-inputs-focus: $all-buttons-focus;
|
||||||
|
$all-button-inputs-hover: $all-buttons-hover;
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Provides an easy way to include a clearfix for containing floats.
|
||||||
|
///
|
||||||
|
/// @link http://cssmojo.com/latest_new_clearfix_so_far/
|
||||||
|
///
|
||||||
|
/// @example scss - Usage
|
||||||
|
/// .element {
|
||||||
|
/// @include clearfix;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @example css - CSS Output
|
||||||
|
/// .element::after {
|
||||||
|
/// clear: both;
|
||||||
|
/// content: "";
|
||||||
|
/// display: table;
|
||||||
|
/// }
|
||||||
|
|
||||||
|
@mixin clearfix {
|
||||||
|
&::after {
|
||||||
|
clear: both;
|
||||||
|
content: "";
|
||||||
|
display: table;
|
||||||
|
}
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Truncates text and adds an ellipsis to represent overflow.
|
||||||
|
///
|
||||||
|
/// @param {Number} $width [100%]
|
||||||
|
/// Max-width for the string to respect before being truncated
|
||||||
|
///
|
||||||
|
/// @example scss - Usage
|
||||||
|
/// .element {
|
||||||
|
/// @include ellipsis;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @example css - CSS Output
|
||||||
|
/// .element {
|
||||||
|
/// display: inline-block;
|
||||||
|
/// max-width: 100%;
|
||||||
|
/// overflow: hidden;
|
||||||
|
/// text-overflow: ellipsis;
|
||||||
|
/// white-space: nowrap;
|
||||||
|
/// word-wrap: normal;
|
||||||
|
/// }
|
||||||
|
|
||||||
|
@mixin ellipsis($width: 100%) {
|
||||||
|
display: inline-block;
|
||||||
|
max-width: $width;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
word-wrap: normal;
|
||||||
|
}
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Georgia font stack.
|
||||||
|
///
|
||||||
|
/// @type List
|
||||||
|
|
||||||
|
$georgia: "Georgia", "Cambria", "Times New Roman", "Times", serif;
|
||||||
|
|
||||||
|
/// Helvetica font stack.
|
||||||
|
///
|
||||||
|
/// @type List
|
||||||
|
|
||||||
|
$helvetica: "Helvetica Neue", "Helvetica", "Roboto", "Arial", sans-serif;
|
||||||
|
|
||||||
|
/// Lucida Grande font stack.
|
||||||
|
///
|
||||||
|
/// @type List
|
||||||
|
|
||||||
|
$lucida-grande: "Lucida Grande", "Tahoma", "Verdana", "Arial", sans-serif;
|
||||||
|
|
||||||
|
/// Monospace font stack.
|
||||||
|
///
|
||||||
|
/// @type List
|
||||||
|
|
||||||
|
$monospace: "Bitstream Vera Sans Mono", "Consolas", "Courier", monospace;
|
||||||
|
|
||||||
|
/// Verdana font stack.
|
||||||
|
///
|
||||||
|
/// @type List
|
||||||
|
|
||||||
|
$verdana: "Verdana", "Geneva", sans-serif;
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
/// Hides the text in an element, commonly used to show an image. Some elements will need block-level styles applied.
|
||||||
|
///
|
||||||
|
/// @link http://zeldman.com/2012/03/01/replacing-the-9999px-hack-new-image-replacement
|
||||||
|
///
|
||||||
|
/// @example scss - Usage
|
||||||
|
/// .element {
|
||||||
|
/// @include hide-text;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @example css - CSS Output
|
||||||
|
/// .element {
|
||||||
|
/// overflow: hidden;
|
||||||
|
/// text-indent: 101%;
|
||||||
|
/// white-space: nowrap;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @todo Remove height argument in v5.0.0
|
||||||
|
|
||||||
|
@mixin hide-text($height: null) {
|
||||||
|
overflow: hidden;
|
||||||
|
text-indent: 101%;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
@if $height {
|
||||||
|
@warn "The `hide-text` mixin has changed and no longer requires a height. The height argument will no longer be accepted in v5.0.0";
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Provides a quick method for targeting `margin` on specific sides of a box. Use a `null` value to “skip” a side.
|
||||||
|
///
|
||||||
|
/// @param {Arglist} $vals
|
||||||
|
/// List of arguments
|
||||||
|
///
|
||||||
|
/// @example scss - Usage
|
||||||
|
/// .element {
|
||||||
|
/// @include margin(null 10px 3em 20vh);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @example css - CSS Output
|
||||||
|
/// .element {
|
||||||
|
/// margin-bottom: 3em;
|
||||||
|
/// margin-left: 20vh;
|
||||||
|
/// margin-right: 10px;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @require {mixin} directional-property
|
||||||
|
///
|
||||||
|
/// @output `margin`
|
||||||
|
|
||||||
|
@mixin margin($vals...) {
|
||||||
|
@include directional-property(margin, false, $vals...);
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Provides a quick method for targeting `padding` on specific sides of a box. Use a `null` value to “skip” a side.
|
||||||
|
///
|
||||||
|
/// @param {Arglist} $vals
|
||||||
|
/// List of arguments
|
||||||
|
///
|
||||||
|
/// @example scss - Usage
|
||||||
|
/// .element {
|
||||||
|
/// @include padding(12vh null 10px 5%);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @example css - CSS Output
|
||||||
|
/// .element {
|
||||||
|
/// padding-bottom: 10px;
|
||||||
|
/// padding-left: 5%;
|
||||||
|
/// padding-top: 12vh;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @require {mixin} directional-property
|
||||||
|
///
|
||||||
|
/// @output `padding`
|
||||||
|
|
||||||
|
@mixin padding($vals...) {
|
||||||
|
@include directional-property(padding, false, $vals...);
|
||||||
|
}
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Provides a quick method for setting an element’s position. Use a `null` value to “skip” a side.
|
||||||
|
///
|
||||||
|
/// @param {Position} $position [relative]
|
||||||
|
/// A CSS position value
|
||||||
|
///
|
||||||
|
/// @param {Arglist} $coordinates [null null null null]
|
||||||
|
/// List of values that correspond to the 4-value syntax for the edges of a box
|
||||||
|
///
|
||||||
|
/// @example scss - Usage
|
||||||
|
/// .element {
|
||||||
|
/// @include position(absolute, 0 null null 10em);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @example css - CSS Output
|
||||||
|
/// .element {
|
||||||
|
/// left: 10em;
|
||||||
|
/// position: absolute;
|
||||||
|
/// top: 0;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @require {function} is-length
|
||||||
|
/// @require {function} unpack
|
||||||
|
|
||||||
|
@mixin position($position: relative, $coordinates: null null null null) {
|
||||||
|
@if type-of($position) == list {
|
||||||
|
$coordinates: $position;
|
||||||
|
$position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
$coordinates: unpack($coordinates);
|
||||||
|
|
||||||
|
$offsets: (
|
||||||
|
top: nth($coordinates, 1),
|
||||||
|
right: nth($coordinates, 2),
|
||||||
|
bottom: nth($coordinates, 3),
|
||||||
|
left: nth($coordinates, 4)
|
||||||
|
);
|
||||||
|
|
||||||
|
position: $position;
|
||||||
|
|
||||||
|
@each $offset, $value in $offsets {
|
||||||
|
@if is-length($value) {
|
||||||
|
#{$offset}: $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+66
@@ -0,0 +1,66 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// A mixin for generating vendor prefixes on non-standardized properties.
|
||||||
|
///
|
||||||
|
/// @param {String} $property
|
||||||
|
/// Property to prefix
|
||||||
|
///
|
||||||
|
/// @param {*} $value
|
||||||
|
/// Value to use
|
||||||
|
///
|
||||||
|
/// @param {List} $prefixes
|
||||||
|
/// Prefixes to define
|
||||||
|
///
|
||||||
|
/// @example scss - Usage
|
||||||
|
/// .element {
|
||||||
|
/// @include prefixer(border-radius, 10px, webkit ms spec);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @example css - CSS Output
|
||||||
|
/// .element {
|
||||||
|
/// -webkit-border-radius: 10px;
|
||||||
|
/// -moz-border-radius: 10px;
|
||||||
|
/// border-radius: 10px;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @require {variable} $prefix-for-webkit
|
||||||
|
/// @require {variable} $prefix-for-mozilla
|
||||||
|
/// @require {variable} $prefix-for-microsoft
|
||||||
|
/// @require {variable} $prefix-for-opera
|
||||||
|
/// @require {variable} $prefix-for-spec
|
||||||
|
|
||||||
|
@mixin prefixer($property, $value, $prefixes) {
|
||||||
|
@each $prefix in $prefixes {
|
||||||
|
@if $prefix == webkit {
|
||||||
|
@if $prefix-for-webkit {
|
||||||
|
-webkit-#{$property}: $value;
|
||||||
|
}
|
||||||
|
} @else if $prefix == moz {
|
||||||
|
@if $prefix-for-mozilla {
|
||||||
|
-moz-#{$property}: $value;
|
||||||
|
}
|
||||||
|
} @else if $prefix == ms {
|
||||||
|
@if $prefix-for-microsoft {
|
||||||
|
-ms-#{$property}: $value;
|
||||||
|
}
|
||||||
|
} @else if $prefix == o {
|
||||||
|
@if $prefix-for-opera {
|
||||||
|
-o-#{$property}: $value;
|
||||||
|
}
|
||||||
|
} @else if $prefix == spec {
|
||||||
|
@if $prefix-for-spec {
|
||||||
|
#{$property}: $value;
|
||||||
|
}
|
||||||
|
} @else {
|
||||||
|
@warn "Unrecognized prefix: #{$prefix}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin disable-prefix-for-all() {
|
||||||
|
$prefix-for-webkit: false !global;
|
||||||
|
$prefix-for-mozilla: false !global;
|
||||||
|
$prefix-for-microsoft: false !global;
|
||||||
|
$prefix-for-opera: false !global;
|
||||||
|
$prefix-for-spec: false !global;
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
@mixin retina-image($filename, $background-size, $extension: png, $retina-filename: null, $retina-suffix: _2x, $asset-pipeline: $asset-pipeline) {
|
||||||
|
@include _bourbon-deprecate("retina-image");
|
||||||
|
|
||||||
|
@if $asset-pipeline {
|
||||||
|
background-image: image-url("#{$filename}.#{$extension}");
|
||||||
|
} @else {
|
||||||
|
background-image: url("#{$filename}.#{$extension}");
|
||||||
|
}
|
||||||
|
|
||||||
|
@include hidpi {
|
||||||
|
@if $asset-pipeline {
|
||||||
|
@if $retina-filename {
|
||||||
|
background-image: image-url("#{$retina-filename}.#{$extension}");
|
||||||
|
} @else {
|
||||||
|
background-image: image-url("#{$filename}#{$retina-suffix}.#{$extension}");
|
||||||
|
}
|
||||||
|
} @else {
|
||||||
|
@if $retina-filename {
|
||||||
|
background-image: url("#{$retina-filename}.#{$extension}");
|
||||||
|
} @else {
|
||||||
|
background-image: url("#{$filename}#{$retina-suffix}.#{$extension}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
background-size: $background-size;
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+51
@@ -0,0 +1,51 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Sets the `width` and `height` of the element.
|
||||||
|
///
|
||||||
|
/// @param {List} $size
|
||||||
|
/// A list of at most 2 size values.
|
||||||
|
///
|
||||||
|
/// If there is only a single value in `$size` it is used for both width and height. All units are supported.
|
||||||
|
///
|
||||||
|
/// @example scss - Usage
|
||||||
|
/// .first-element {
|
||||||
|
/// @include size(2em);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// .second-element {
|
||||||
|
/// @include size(auto 10em);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @example css - CSS Output
|
||||||
|
/// .first-element {
|
||||||
|
/// width: 2em;
|
||||||
|
/// height: 2em;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// .second-element {
|
||||||
|
/// width: auto;
|
||||||
|
/// height: 10em;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @todo Refactor in 5.0.0 to use a comma-separated argument
|
||||||
|
|
||||||
|
@mixin size($value) {
|
||||||
|
$width: nth($value, 1);
|
||||||
|
$height: $width;
|
||||||
|
|
||||||
|
@if length($value) > 1 {
|
||||||
|
$height: nth($value, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@if is-size($height) {
|
||||||
|
height: $height;
|
||||||
|
} @else {
|
||||||
|
@warn "`#{$height}` is not a valid length for the `$height` parameter in the `size` mixin.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@if is-size($width) {
|
||||||
|
width: $width;
|
||||||
|
} @else {
|
||||||
|
@warn "`#{$width}` is not a valid length for the `$width` parameter in the `size` mixin.";
|
||||||
|
}
|
||||||
|
}
|
||||||
+113
@@ -0,0 +1,113 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Generates variables for all text-based inputs. Please note that you must use interpolation on the variable: `#{$all-text-inputs}`.
|
||||||
|
///
|
||||||
|
/// @example scss - Usage
|
||||||
|
/// #{$all-text-inputs} {
|
||||||
|
/// border: 1px solid #f00;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// #{$all-text-inputs-focus},
|
||||||
|
/// #{$all-text-inputs-hover} {
|
||||||
|
/// border: 1px solid #0f0;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// #{$all-text-inputs-active} {
|
||||||
|
/// border: 1px solid #00f;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @example css - CSS Output
|
||||||
|
/// input[type="color"],
|
||||||
|
/// input[type="date"],
|
||||||
|
/// input[type="datetime"],
|
||||||
|
/// input[type="datetime-local"],
|
||||||
|
/// input[type="email"],
|
||||||
|
/// input[type="month"],
|
||||||
|
/// input[type="number"],
|
||||||
|
/// input[type="password"],
|
||||||
|
/// input[type="search"],
|
||||||
|
/// input[type="tel"],
|
||||||
|
/// input[type="text"],
|
||||||
|
/// input[type="time"],
|
||||||
|
/// input[type="url"],
|
||||||
|
/// input[type="week"],
|
||||||
|
/// textarea {
|
||||||
|
/// border: 1px solid #f00;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// input[type="color"]:focus,
|
||||||
|
/// input[type="date"]:focus,
|
||||||
|
/// input[type="datetime"]:focus,
|
||||||
|
/// input[type="datetime-local"]:focus,
|
||||||
|
/// input[type="email"]:focus,
|
||||||
|
/// input[type="month"]:focus,
|
||||||
|
/// input[type="number"]:focus,
|
||||||
|
/// input[type="password"]:focus,
|
||||||
|
/// input[type="search"]:focus,
|
||||||
|
/// input[type="tel"]:focus,
|
||||||
|
/// input[type="text"]:focus,
|
||||||
|
/// input[type="time"]:focus,
|
||||||
|
/// input[type="url"]:focus,
|
||||||
|
/// input[type="week"]:focus,
|
||||||
|
/// textarea:focus,
|
||||||
|
/// input[type="color"]:hover,
|
||||||
|
/// input[type="date"]:hover,
|
||||||
|
/// input[type="datetime"]:hover,
|
||||||
|
/// input[type="datetime-local"]:hover,
|
||||||
|
/// input[type="email"]:hover,
|
||||||
|
/// input[type="month"]:hover,
|
||||||
|
/// input[type="number"]:hover,
|
||||||
|
/// input[type="password"]:hover,
|
||||||
|
/// input[type="search"]:hover,
|
||||||
|
/// input[type="tel"]:hover,
|
||||||
|
/// input[type="text"]:hover,
|
||||||
|
/// input[type="time"]:hover,
|
||||||
|
/// input[type="url"]:hover,
|
||||||
|
/// input[type="week"]:hover,
|
||||||
|
/// textarea:hover {
|
||||||
|
/// border: 1px solid #0f0;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// input[type="color"]:active,
|
||||||
|
/// input[type="date"]:active,
|
||||||
|
/// input[type="datetime"]:active,
|
||||||
|
/// input[type="datetime-local"]:active,
|
||||||
|
/// input[type="email"]:active,
|
||||||
|
/// input[type="month"]:active,
|
||||||
|
/// input[type="number"]:active,
|
||||||
|
/// input[type="password"]:active,
|
||||||
|
/// input[type="search"]:active,
|
||||||
|
/// input[type="tel"]:active,
|
||||||
|
/// input[type="text"]:active,
|
||||||
|
/// input[type="time"]:active,
|
||||||
|
/// input[type="url"]:active,
|
||||||
|
/// input[type="week"]:active,
|
||||||
|
/// textarea:active {
|
||||||
|
/// border: 1px solid #00f;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @require assign-inputs
|
||||||
|
///
|
||||||
|
/// @type List
|
||||||
|
|
||||||
|
$text-inputs-list: 'input[type="color"]',
|
||||||
|
'input[type="date"]',
|
||||||
|
'input[type="datetime"]',
|
||||||
|
'input[type="datetime-local"]',
|
||||||
|
'input[type="email"]',
|
||||||
|
'input[type="month"]',
|
||||||
|
'input[type="number"]',
|
||||||
|
'input[type="password"]',
|
||||||
|
'input[type="search"]',
|
||||||
|
'input[type="tel"]',
|
||||||
|
'input[type="text"]',
|
||||||
|
'input[type="time"]',
|
||||||
|
'input[type="url"]',
|
||||||
|
'input[type="week"]',
|
||||||
|
'input:not([type])',
|
||||||
|
'textarea';
|
||||||
|
|
||||||
|
$all-text-inputs: assign-inputs($text-inputs-list);
|
||||||
|
$all-text-inputs-active: assign-inputs($text-inputs-list, active);
|
||||||
|
$all-text-inputs-focus: assign-inputs($text-inputs-list, focus);
|
||||||
|
$all-text-inputs-hover: assign-inputs($text-inputs-list, hover);
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// CSS cubic-bezier timing functions. Timing functions courtesy of jquery.easie (github.com/jaukia/easie)
|
||||||
|
///
|
||||||
|
/// Timing functions are the same as demoed here: http://jqueryui.com/resources/demos/effect/easing.html
|
||||||
|
///
|
||||||
|
/// @type cubic-bezier
|
||||||
|
|
||||||
|
$ease-in-quad: cubic-bezier(0.550, 0.085, 0.680, 0.530);
|
||||||
|
$ease-in-cubic: cubic-bezier(0.550, 0.055, 0.675, 0.190);
|
||||||
|
$ease-in-quart: cubic-bezier(0.895, 0.030, 0.685, 0.220);
|
||||||
|
$ease-in-quint: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
||||||
|
$ease-in-sine: cubic-bezier(0.470, 0.000, 0.745, 0.715);
|
||||||
|
$ease-in-expo: cubic-bezier(0.950, 0.050, 0.795, 0.035);
|
||||||
|
$ease-in-circ: cubic-bezier(0.600, 0.040, 0.980, 0.335);
|
||||||
|
$ease-in-back: cubic-bezier(0.600, -0.280, 0.735, 0.045);
|
||||||
|
|
||||||
|
$ease-out-quad: cubic-bezier(0.250, 0.460, 0.450, 0.940);
|
||||||
|
$ease-out-cubic: cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
||||||
|
$ease-out-quart: cubic-bezier(0.165, 0.840, 0.440, 1.000);
|
||||||
|
$ease-out-quint: cubic-bezier(0.230, 1.000, 0.320, 1.000);
|
||||||
|
$ease-out-sine: cubic-bezier(0.390, 0.575, 0.565, 1.000);
|
||||||
|
$ease-out-expo: cubic-bezier(0.190, 1.000, 0.220, 1.000);
|
||||||
|
$ease-out-circ: cubic-bezier(0.075, 0.820, 0.165, 1.000);
|
||||||
|
$ease-out-back: cubic-bezier(0.175, 0.885, 0.320, 1.275);
|
||||||
|
|
||||||
|
$ease-in-out-quad: cubic-bezier(0.455, 0.030, 0.515, 0.955);
|
||||||
|
$ease-in-out-cubic: cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||||
|
$ease-in-out-quart: cubic-bezier(0.770, 0.000, 0.175, 1.000);
|
||||||
|
$ease-in-out-quint: cubic-bezier(0.860, 0.000, 0.070, 1.000);
|
||||||
|
$ease-in-out-sine: cubic-bezier(0.445, 0.050, 0.550, 0.950);
|
||||||
|
$ease-in-out-expo: cubic-bezier(1.000, 0.000, 0.000, 1.000);
|
||||||
|
$ease-in-out-circ: cubic-bezier(0.785, 0.135, 0.150, 0.860);
|
||||||
|
$ease-in-out-back: cubic-bezier(0.680, -0.550, 0.265, 1.550);
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
@mixin triangle($size, $color, $direction) {
|
||||||
|
$width: nth($size, 1);
|
||||||
|
$height: nth($size, length($size));
|
||||||
|
$foreground-color: nth($color, 1);
|
||||||
|
$background-color: if(length($color) == 2, nth($color, 2), transparent);
|
||||||
|
height: 0;
|
||||||
|
width: 0;
|
||||||
|
|
||||||
|
@if ($direction == up) or ($direction == down) or ($direction == right) or ($direction == left) {
|
||||||
|
$width: $width / 2;
|
||||||
|
$height: if(length($size) > 1, $height, $height/2);
|
||||||
|
|
||||||
|
@if $direction == up {
|
||||||
|
border-bottom: $height solid $foreground-color;
|
||||||
|
border-left: $width solid $background-color;
|
||||||
|
border-right: $width solid $background-color;
|
||||||
|
} @else if $direction == right {
|
||||||
|
border-bottom: $width solid $background-color;
|
||||||
|
border-left: $height solid $foreground-color;
|
||||||
|
border-top: $width solid $background-color;
|
||||||
|
} @else if $direction == down {
|
||||||
|
border-left: $width solid $background-color;
|
||||||
|
border-right: $width solid $background-color;
|
||||||
|
border-top: $height solid $foreground-color;
|
||||||
|
} @else if $direction == left {
|
||||||
|
border-bottom: $width solid $background-color;
|
||||||
|
border-right: $height solid $foreground-color;
|
||||||
|
border-top: $width solid $background-color;
|
||||||
|
}
|
||||||
|
} @else if ($direction == up-right) or ($direction == up-left) {
|
||||||
|
border-top: $height solid $foreground-color;
|
||||||
|
|
||||||
|
@if $direction == up-right {
|
||||||
|
border-left: $width solid $background-color;
|
||||||
|
} @else if $direction == up-left {
|
||||||
|
border-right: $width solid $background-color;
|
||||||
|
}
|
||||||
|
} @else if ($direction == down-right) or ($direction == down-left) {
|
||||||
|
border-bottom: $height solid $foreground-color;
|
||||||
|
|
||||||
|
@if $direction == down-right {
|
||||||
|
border-left: $width solid $background-color;
|
||||||
|
} @else if $direction == down-left {
|
||||||
|
border-right: $width solid $background-color;
|
||||||
|
}
|
||||||
|
} @else if ($direction == inset-up) {
|
||||||
|
border-color: $background-color $background-color $foreground-color;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: $height $width;
|
||||||
|
} @else if ($direction == inset-down) {
|
||||||
|
border-color: $foreground-color $background-color $background-color;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: $height $width;
|
||||||
|
} @else if ($direction == inset-right) {
|
||||||
|
border-color: $background-color $background-color $background-color $foreground-color;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: $width $height;
|
||||||
|
} @else if ($direction == inset-left) {
|
||||||
|
border-color: $background-color $foreground-color $background-color $background-color;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: $width $height;
|
||||||
|
}
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Provides an easy way to change the `word-wrap` property.
|
||||||
|
///
|
||||||
|
/// @param {String} $wrap [break-word]
|
||||||
|
/// Value for the `word-break` property.
|
||||||
|
///
|
||||||
|
/// @example scss - Usage
|
||||||
|
/// .wrapper {
|
||||||
|
/// @include word-wrap(break-word);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @example css - CSS Output
|
||||||
|
/// .wrapper {
|
||||||
|
/// overflow-wrap: break-word;
|
||||||
|
/// word-break: break-all;
|
||||||
|
/// word-wrap: break-word;
|
||||||
|
/// }
|
||||||
|
|
||||||
|
@mixin word-wrap($wrap: break-word) {
|
||||||
|
overflow-wrap: $wrap;
|
||||||
|
word-wrap: $wrap;
|
||||||
|
|
||||||
|
@if $wrap == break-word {
|
||||||
|
word-break: break-all;
|
||||||
|
} @else {
|
||||||
|
word-break: $wrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
// http://www.w3.org/TR/css3-animations/#the-animation-name-property-
|
||||||
|
// Each of these mixins support comma separated lists of values, which allows different transitions for individual properties to be described in a single style rule. Each value in the list corresponds to the value at that same position in the other properties.
|
||||||
|
|
||||||
|
@mixin animation($animations...) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("animation");
|
||||||
|
|
||||||
|
@include prefixer(animation, $animations, webkit moz spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin animation-name($names...) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("animation-name");
|
||||||
|
|
||||||
|
@include prefixer(animation-name, $names, webkit moz spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin animation-duration($times...) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("animation-duration");
|
||||||
|
|
||||||
|
@include prefixer(animation-duration, $times, webkit moz spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin animation-timing-function($motions...) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("animation-timing-function");
|
||||||
|
|
||||||
|
// ease | linear | ease-in | ease-out | ease-in-out
|
||||||
|
@include prefixer(animation-timing-function, $motions, webkit moz spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin animation-iteration-count($values...) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("animation-iteration-count");
|
||||||
|
|
||||||
|
// infinite | <number>
|
||||||
|
@include prefixer(animation-iteration-count, $values, webkit moz spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin animation-direction($directions...) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("animation-direction");
|
||||||
|
|
||||||
|
// normal | alternate
|
||||||
|
@include prefixer(animation-direction, $directions, webkit moz spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin animation-play-state($states...) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("animation-play-state");
|
||||||
|
|
||||||
|
// running | paused
|
||||||
|
@include prefixer(animation-play-state, $states, webkit moz spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin animation-delay($times...) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("animation-delay");
|
||||||
|
|
||||||
|
@include prefixer(animation-delay, $times, webkit moz spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin animation-fill-mode($modes...) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("animation-fill-mode");
|
||||||
|
|
||||||
|
// none | forwards | backwards | both
|
||||||
|
@include prefixer(animation-fill-mode, $modes, webkit moz spec);
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
@mixin appearance($value) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("appearance");
|
||||||
|
|
||||||
|
@include prefixer(appearance, $value, webkit moz ms o spec);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
@mixin backface-visibility($visibility) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("backface-visibility");
|
||||||
|
|
||||||
|
@include prefixer(backface-visibility, $visibility, webkit spec);
|
||||||
|
}
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
//************************************************************************//
|
||||||
|
// Background-image property for adding multiple background images with
|
||||||
|
// gradients, or for stringing multiple gradients together.
|
||||||
|
//************************************************************************//
|
||||||
|
|
||||||
|
@mixin background-image($images...) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("background-image");
|
||||||
|
|
||||||
|
$webkit-images: ();
|
||||||
|
$spec-images: ();
|
||||||
|
|
||||||
|
@each $image in $images {
|
||||||
|
$webkit-image: ();
|
||||||
|
$spec-image: ();
|
||||||
|
|
||||||
|
@if (type-of($image) == string) {
|
||||||
|
$url-str: str-slice($image, 1, 3);
|
||||||
|
$gradient-type: str-slice($image, 1, 6);
|
||||||
|
|
||||||
|
@if $url-str == "url" {
|
||||||
|
$webkit-image: $image;
|
||||||
|
$spec-image: $image;
|
||||||
|
}
|
||||||
|
|
||||||
|
@else if $gradient-type == "linear" {
|
||||||
|
$gradients: _linear-gradient-parser($image);
|
||||||
|
$webkit-image: map-get($gradients, webkit-image);
|
||||||
|
$spec-image: map-get($gradients, spec-image);
|
||||||
|
}
|
||||||
|
|
||||||
|
@else if $gradient-type == "radial" {
|
||||||
|
$gradients: _radial-gradient-parser($image);
|
||||||
|
$webkit-image: map-get($gradients, webkit-image);
|
||||||
|
$spec-image: map-get($gradients, spec-image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$webkit-images: append($webkit-images, $webkit-image, comma);
|
||||||
|
$spec-images: append($spec-images, $spec-image, comma);
|
||||||
|
}
|
||||||
|
|
||||||
|
background-image: $webkit-images;
|
||||||
|
background-image: $spec-images;
|
||||||
|
}
|
||||||
+57
@@ -0,0 +1,57 @@
|
|||||||
|
//************************************************************************//
|
||||||
|
// Background property for adding multiple backgrounds using shorthand
|
||||||
|
// notation.
|
||||||
|
//************************************************************************//
|
||||||
|
|
||||||
|
@mixin background($backgrounds...) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("background");
|
||||||
|
|
||||||
|
$webkit-backgrounds: ();
|
||||||
|
$spec-backgrounds: ();
|
||||||
|
|
||||||
|
@each $background in $backgrounds {
|
||||||
|
$webkit-background: ();
|
||||||
|
$spec-background: ();
|
||||||
|
$background-type: type-of($background);
|
||||||
|
|
||||||
|
@if $background-type == string or $background-type == list {
|
||||||
|
$background-str: if($background-type == list, nth($background, 1), $background);
|
||||||
|
|
||||||
|
$url-str: str-slice($background-str, 1, 3);
|
||||||
|
$gradient-type: str-slice($background-str, 1, 6);
|
||||||
|
|
||||||
|
@if $url-str == "url" {
|
||||||
|
$webkit-background: $background;
|
||||||
|
$spec-background: $background;
|
||||||
|
}
|
||||||
|
|
||||||
|
@else if $gradient-type == "linear" {
|
||||||
|
$gradients: _linear-gradient-parser("#{$background}");
|
||||||
|
$webkit-background: map-get($gradients, webkit-image);
|
||||||
|
$spec-background: map-get($gradients, spec-image);
|
||||||
|
}
|
||||||
|
|
||||||
|
@else if $gradient-type == "radial" {
|
||||||
|
$gradients: _radial-gradient-parser("#{$background}");
|
||||||
|
$webkit-background: map-get($gradients, webkit-image);
|
||||||
|
$spec-background: map-get($gradients, spec-image);
|
||||||
|
}
|
||||||
|
|
||||||
|
@else {
|
||||||
|
$webkit-background: $background;
|
||||||
|
$spec-background: $background;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@else {
|
||||||
|
$webkit-background: $background;
|
||||||
|
$spec-background: $background;
|
||||||
|
}
|
||||||
|
|
||||||
|
$webkit-backgrounds: append($webkit-backgrounds, $webkit-background, comma);
|
||||||
|
$spec-backgrounds: append($spec-backgrounds, $spec-background, comma);
|
||||||
|
}
|
||||||
|
|
||||||
|
background: $webkit-backgrounds;
|
||||||
|
background: $spec-backgrounds;
|
||||||
|
}
|
||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
@mixin border-image($borders...) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("border-image");
|
||||||
|
|
||||||
|
$webkit-borders: ();
|
||||||
|
$spec-borders: ();
|
||||||
|
|
||||||
|
@each $border in $borders {
|
||||||
|
$webkit-border: ();
|
||||||
|
$spec-border: ();
|
||||||
|
$border-type: type-of($border);
|
||||||
|
|
||||||
|
@if $border-type == string or list {
|
||||||
|
$border-str: if($border-type == list, nth($border, 1), $border);
|
||||||
|
|
||||||
|
$url-str: str-slice($border-str, 1, 3);
|
||||||
|
$gradient-type: str-slice($border-str, 1, 6);
|
||||||
|
|
||||||
|
@if $url-str == "url" {
|
||||||
|
$webkit-border: $border;
|
||||||
|
$spec-border: $border;
|
||||||
|
}
|
||||||
|
|
||||||
|
@else if $gradient-type == "linear" {
|
||||||
|
$gradients: _linear-gradient-parser("#{$border}");
|
||||||
|
$webkit-border: map-get($gradients, webkit-image);
|
||||||
|
$spec-border: map-get($gradients, spec-image);
|
||||||
|
}
|
||||||
|
|
||||||
|
@else if $gradient-type == "radial" {
|
||||||
|
$gradients: _radial-gradient-parser("#{$border}");
|
||||||
|
$webkit-border: map-get($gradients, webkit-image);
|
||||||
|
$spec-border: map-get($gradients, spec-image);
|
||||||
|
}
|
||||||
|
|
||||||
|
@else {
|
||||||
|
$webkit-border: $border;
|
||||||
|
$spec-border: $border;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@else {
|
||||||
|
$webkit-border: $border;
|
||||||
|
$spec-border: $border;
|
||||||
|
}
|
||||||
|
|
||||||
|
$webkit-borders: append($webkit-borders, $webkit-border, comma);
|
||||||
|
$spec-borders: append($spec-borders, $spec-border, comma);
|
||||||
|
}
|
||||||
|
|
||||||
|
-webkit-border-image: $webkit-borders;
|
||||||
|
border-image: $spec-borders;
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Examples:
|
||||||
|
// @include border-image(url("image.png"));
|
||||||
|
// @include border-image(url("image.png") 20 stretch);
|
||||||
|
// @include border-image(linear-gradient(45deg, orange, yellow));
|
||||||
|
// @include border-image(linear-gradient(45deg, orange, yellow) stretch);
|
||||||
|
// @include border-image(linear-gradient(45deg, orange, yellow) 20 30 40 50 stretch round);
|
||||||
|
// @include border-image(radial-gradient(top, cover, orange, yellow, orange));
|
||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
@mixin calc($property, $value) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("calc");
|
||||||
|
|
||||||
|
#{$property}: -webkit-calc(#{$value});
|
||||||
|
#{$property}: calc(#{$value});
|
||||||
|
}
|
||||||
+67
@@ -0,0 +1,67 @@
|
|||||||
|
@mixin columns($arg: auto) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("columns");
|
||||||
|
|
||||||
|
// <column-count> || <column-width>
|
||||||
|
@include prefixer(columns, $arg, webkit moz spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin column-count($int: auto) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("column-count");
|
||||||
|
|
||||||
|
// auto || integer
|
||||||
|
@include prefixer(column-count, $int, webkit moz spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin column-gap($length: normal) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("column-gap");
|
||||||
|
|
||||||
|
// normal || length
|
||||||
|
@include prefixer(column-gap, $length, webkit moz spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin column-fill($arg: auto) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("column-fill");
|
||||||
|
|
||||||
|
// auto || length
|
||||||
|
@include prefixer(column-fill, $arg, webkit moz spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin column-rule($arg) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("column-rule");
|
||||||
|
|
||||||
|
// <border-width> || <border-style> || <color>
|
||||||
|
@include prefixer(column-rule, $arg, webkit moz spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin column-rule-color($color) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("column-rule-color");
|
||||||
|
|
||||||
|
@include prefixer(column-rule-color, $color, webkit moz spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin column-rule-style($style: none) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("column-rule-style");
|
||||||
|
|
||||||
|
// none | hidden | dashed | dotted | double | groove | inset | inset | outset | ridge | solid
|
||||||
|
@include prefixer(column-rule-style, $style, webkit moz spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin column-rule-width($width: none) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("column-rule-width");
|
||||||
|
|
||||||
|
@include prefixer(column-rule-width, $width, webkit moz spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin column-span($arg: none) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("column-span");
|
||||||
|
|
||||||
|
// none || all
|
||||||
|
@include prefixer(column-span, $arg, webkit moz spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin column-width($length: auto) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("column-width");
|
||||||
|
|
||||||
|
// auto || length
|
||||||
|
@include prefixer(column-width, $length, webkit moz spec);
|
||||||
|
}
|
||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
@mixin filter($function: none) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("filter");
|
||||||
|
|
||||||
|
// <filter-function> [<filter-function]* | none
|
||||||
|
@include prefixer(filter, $function, webkit spec);
|
||||||
|
}
|
||||||
+327
@@ -0,0 +1,327 @@
|
|||||||
|
// CSS3 Flexible Box Model and property defaults
|
||||||
|
|
||||||
|
// Custom shorthand notation for flexbox
|
||||||
|
@mixin box($orient: inline-axis, $pack: start, $align: stretch) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("box");
|
||||||
|
|
||||||
|
@include display-box;
|
||||||
|
@include box-orient($orient);
|
||||||
|
@include box-pack($pack);
|
||||||
|
@include box-align($align);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin display-box {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("display-box");
|
||||||
|
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -moz-box;
|
||||||
|
display: -ms-flexbox; // IE 10
|
||||||
|
display: box;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin box-orient($orient: inline-axis) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("box-orient");
|
||||||
|
|
||||||
|
// horizontal|vertical|inline-axis|block-axis|inherit
|
||||||
|
@include prefixer(box-orient, $orient, webkit moz spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin box-pack($pack: start) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("box-pack");
|
||||||
|
|
||||||
|
// start|end|center|justify
|
||||||
|
@include prefixer(box-pack, $pack, webkit moz spec);
|
||||||
|
-ms-flex-pack: $pack; // IE 10
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin box-align($align: stretch) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("box-align");
|
||||||
|
|
||||||
|
// start|end|center|baseline|stretch
|
||||||
|
@include prefixer(box-align, $align, webkit moz spec);
|
||||||
|
-ms-flex-align: $align; // IE 10
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin box-direction($direction: normal) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("box-direction");
|
||||||
|
|
||||||
|
// normal|reverse|inherit
|
||||||
|
@include prefixer(box-direction, $direction, webkit moz spec);
|
||||||
|
-ms-flex-direction: $direction; // IE 10
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin box-lines($lines: single) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("box-lines");
|
||||||
|
|
||||||
|
// single|multiple
|
||||||
|
@include prefixer(box-lines, $lines, webkit moz spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin box-ordinal-group($int: 1) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("box-ordinal-group");
|
||||||
|
|
||||||
|
@include prefixer(box-ordinal-group, $int, webkit moz spec);
|
||||||
|
-ms-flex-order: $int; // IE 10
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin box-flex($value: 0) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("box-flex");
|
||||||
|
|
||||||
|
@include prefixer(box-flex, $value, webkit moz spec);
|
||||||
|
-ms-flex: $value; // IE 10
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin box-flex-group($int: 1) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("box-flex-group");
|
||||||
|
|
||||||
|
@include prefixer(box-flex-group, $int, webkit moz spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CSS3 Flexible Box Model and property defaults
|
||||||
|
// Unified attributes for 2009, 2011, and 2012 flavours.
|
||||||
|
|
||||||
|
// 2009 - display (box | inline-box)
|
||||||
|
// 2011 - display (flexbox | inline-flexbox)
|
||||||
|
// 2012 - display (flex | inline-flex)
|
||||||
|
@mixin display($value) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("display");
|
||||||
|
|
||||||
|
// flex | inline-flex
|
||||||
|
@if $value == "flex" {
|
||||||
|
// 2009
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -moz-box;
|
||||||
|
display: box;
|
||||||
|
|
||||||
|
// 2012
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: -moz-flex;
|
||||||
|
display: -ms-flexbox; // 2011 (IE 10)
|
||||||
|
display: flex;
|
||||||
|
} @else if $value == "inline-flex" {
|
||||||
|
display: -webkit-inline-box;
|
||||||
|
display: -moz-inline-box;
|
||||||
|
display: inline-box;
|
||||||
|
|
||||||
|
display: -webkit-inline-flex;
|
||||||
|
display: -moz-inline-flex;
|
||||||
|
display: -ms-inline-flexbox;
|
||||||
|
display: inline-flex;
|
||||||
|
} @else {
|
||||||
|
display: $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2009 - box-flex (integer)
|
||||||
|
// 2011 - flex (decimal | width decimal)
|
||||||
|
// 2012 - flex (integer integer width)
|
||||||
|
@mixin flex($value) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("flex");
|
||||||
|
|
||||||
|
// Grab flex-grow for older browsers.
|
||||||
|
$flex-grow: nth($value, 1);
|
||||||
|
|
||||||
|
// 2009
|
||||||
|
@include prefixer(box-flex, $flex-grow, webkit moz spec);
|
||||||
|
|
||||||
|
// 2011 (IE 10), 2012
|
||||||
|
@include prefixer(flex, $value, webkit moz ms spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2009 - box-orient ( horizontal | vertical | inline-axis | block-axis)
|
||||||
|
// - box-direction (normal | reverse)
|
||||||
|
// 2011 - flex-direction (row | row-reverse | column | column-reverse)
|
||||||
|
// 2012 - flex-direction (row | row-reverse | column | column-reverse)
|
||||||
|
@mixin flex-direction($value: row) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("flex-direction");
|
||||||
|
|
||||||
|
// Alt values.
|
||||||
|
$value-2009: $value;
|
||||||
|
$value-2011: $value;
|
||||||
|
$direction: normal;
|
||||||
|
|
||||||
|
@if $value == row {
|
||||||
|
$value-2009: horizontal;
|
||||||
|
} @else if $value == "row-reverse" {
|
||||||
|
$value-2009: horizontal;
|
||||||
|
$direction: reverse;
|
||||||
|
} @else if $value == column {
|
||||||
|
$value-2009: vertical;
|
||||||
|
} @else if $value == "column-reverse" {
|
||||||
|
$value-2009: vertical;
|
||||||
|
$direction: reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2009
|
||||||
|
@include prefixer(box-orient, $value-2009, webkit moz spec);
|
||||||
|
@include prefixer(box-direction, $direction, webkit moz spec);
|
||||||
|
|
||||||
|
// 2012
|
||||||
|
@include prefixer(flex-direction, $value, webkit moz spec);
|
||||||
|
|
||||||
|
// 2011 (IE 10)
|
||||||
|
-ms-flex-direction: $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2009 - box-lines (single | multiple)
|
||||||
|
// 2011 - flex-wrap (nowrap | wrap | wrap-reverse)
|
||||||
|
// 2012 - flex-wrap (nowrap | wrap | wrap-reverse)
|
||||||
|
@mixin flex-wrap($value: nowrap) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("flex-wrap");
|
||||||
|
|
||||||
|
// Alt values
|
||||||
|
$alt-value: $value;
|
||||||
|
@if $value == nowrap {
|
||||||
|
$alt-value: single;
|
||||||
|
} @else if $value == wrap {
|
||||||
|
$alt-value: multiple;
|
||||||
|
} @else if $value == "wrap-reverse" {
|
||||||
|
$alt-value: multiple;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include prefixer(box-lines, $alt-value, webkit moz spec);
|
||||||
|
@include prefixer(flex-wrap, $value, webkit moz ms spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2009 - TODO: parse values into flex-direction/flex-wrap
|
||||||
|
// 2011 - TODO: parse values into flex-direction/flex-wrap
|
||||||
|
// 2012 - flex-flow (flex-direction || flex-wrap)
|
||||||
|
@mixin flex-flow($value) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("flex-flow");
|
||||||
|
|
||||||
|
@include prefixer(flex-flow, $value, webkit moz spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2009 - box-ordinal-group (integer)
|
||||||
|
// 2011 - flex-order (integer)
|
||||||
|
// 2012 - order (integer)
|
||||||
|
@mixin order($int: 0) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("order");
|
||||||
|
|
||||||
|
// 2009
|
||||||
|
@include prefixer(box-ordinal-group, $int, webkit moz spec);
|
||||||
|
|
||||||
|
// 2012
|
||||||
|
@include prefixer(order, $int, webkit moz spec);
|
||||||
|
|
||||||
|
// 2011 (IE 10)
|
||||||
|
-ms-flex-order: $int;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2012 - flex-grow (number)
|
||||||
|
@mixin flex-grow($number: 0) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("flex-grow");
|
||||||
|
|
||||||
|
@include prefixer(flex-grow, $number, webkit moz spec);
|
||||||
|
-ms-flex-positive: $number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2012 - flex-shrink (number)
|
||||||
|
@mixin flex-shrink($number: 1) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("flex-shrink");
|
||||||
|
|
||||||
|
@include prefixer(flex-shrink, $number, webkit moz spec);
|
||||||
|
-ms-flex-negative: $number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2012 - flex-basis (number)
|
||||||
|
@mixin flex-basis($width: auto) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("flex-basis");
|
||||||
|
|
||||||
|
@include prefixer(flex-basis, $width, webkit moz spec);
|
||||||
|
-ms-flex-preferred-size: $width;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2009 - box-pack (start | end | center | justify)
|
||||||
|
// 2011 - flex-pack (start | end | center | justify)
|
||||||
|
// 2012 - justify-content (flex-start | flex-end | center | space-between | space-around)
|
||||||
|
@mixin justify-content($value: flex-start) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("justify-content");
|
||||||
|
|
||||||
|
// Alt values.
|
||||||
|
$alt-value: $value;
|
||||||
|
@if $value == "flex-start" {
|
||||||
|
$alt-value: start;
|
||||||
|
} @else if $value == "flex-end" {
|
||||||
|
$alt-value: end;
|
||||||
|
} @else if $value == "space-between" {
|
||||||
|
$alt-value: justify;
|
||||||
|
} @else if $value == "space-around" {
|
||||||
|
$alt-value: distribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2009
|
||||||
|
@include prefixer(box-pack, $alt-value, webkit moz spec);
|
||||||
|
|
||||||
|
// 2012
|
||||||
|
@include prefixer(justify-content, $value, webkit moz ms o spec);
|
||||||
|
|
||||||
|
// 2011 (IE 10)
|
||||||
|
-ms-flex-pack: $alt-value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2009 - box-align (start | end | center | baseline | stretch)
|
||||||
|
// 2011 - flex-align (start | end | center | baseline | stretch)
|
||||||
|
// 2012 - align-items (flex-start | flex-end | center | baseline | stretch)
|
||||||
|
@mixin align-items($value: stretch) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("align-items");
|
||||||
|
|
||||||
|
$alt-value: $value;
|
||||||
|
|
||||||
|
@if $value == "flex-start" {
|
||||||
|
$alt-value: start;
|
||||||
|
} @else if $value == "flex-end" {
|
||||||
|
$alt-value: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2009
|
||||||
|
@include prefixer(box-align, $alt-value, webkit moz spec);
|
||||||
|
|
||||||
|
// 2012
|
||||||
|
@include prefixer(align-items, $value, webkit moz ms o spec);
|
||||||
|
|
||||||
|
// 2011 (IE 10)
|
||||||
|
-ms-flex-align: $alt-value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2011 - flex-item-align (auto | start | end | center | baseline | stretch)
|
||||||
|
// 2012 - align-self (auto | flex-start | flex-end | center | baseline | stretch)
|
||||||
|
@mixin align-self($value: auto) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("align-self");
|
||||||
|
|
||||||
|
$value-2011: $value;
|
||||||
|
@if $value == "flex-start" {
|
||||||
|
$value-2011: start;
|
||||||
|
} @else if $value == "flex-end" {
|
||||||
|
$value-2011: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2012
|
||||||
|
@include prefixer(align-self, $value, webkit moz spec);
|
||||||
|
|
||||||
|
// 2011 (IE 10)
|
||||||
|
-ms-flex-item-align: $value-2011;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2011 - flex-line-pack (start | end | center | justify | distribute | stretch)
|
||||||
|
// 2012 - align-content (flex-start | flex-end | center | space-between | space-around | stretch)
|
||||||
|
@mixin align-content($value: stretch) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("align-content");
|
||||||
|
|
||||||
|
$value-2011: $value;
|
||||||
|
@if $value == "flex-start" {
|
||||||
|
$value-2011: start;
|
||||||
|
} @else if $value == "flex-end" {
|
||||||
|
$value-2011: end;
|
||||||
|
} @else if $value == "space-between" {
|
||||||
|
$value-2011: justify;
|
||||||
|
} @else if $value == "space-around" {
|
||||||
|
$value-2011: distribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2012
|
||||||
|
@include prefixer(align-content, $value, webkit moz spec);
|
||||||
|
|
||||||
|
// 2011 (IE 10)
|
||||||
|
-ms-flex-line-pack: $value-2011;
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
@mixin font-face(
|
||||||
|
$font-family,
|
||||||
|
$file-path,
|
||||||
|
$weight: normal,
|
||||||
|
$style: normal,
|
||||||
|
$asset-pipeline: $asset-pipeline,
|
||||||
|
$file-formats: eot woff2 woff ttf svg) {
|
||||||
|
|
||||||
|
$font-url-prefix: font-url-prefixer($asset-pipeline);
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: $font-family;
|
||||||
|
font-style: $style;
|
||||||
|
font-weight: $weight;
|
||||||
|
|
||||||
|
src: font-source-declaration(
|
||||||
|
$font-family,
|
||||||
|
$file-path,
|
||||||
|
$asset-pipeline,
|
||||||
|
$file-formats,
|
||||||
|
$font-url-prefix
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
@mixin font-feature-settings($settings...) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("font-feature-settings");
|
||||||
|
|
||||||
|
@if length($settings) == 0 { $settings: none; }
|
||||||
|
@include prefixer(font-feature-settings, $settings, webkit moz ms spec);
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// HiDPI mixin. Default value set to 1.3 to target Google Nexus 7 (http://bjango.com/articles/min-device-pixel-ratio/)
|
||||||
|
@mixin hidpi($ratio: 1.3) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("hidpi");
|
||||||
|
|
||||||
|
@media only screen and (-webkit-min-device-pixel-ratio: $ratio),
|
||||||
|
only screen and (min--moz-device-pixel-ratio: $ratio),
|
||||||
|
only screen and (-o-min-device-pixel-ratio: #{$ratio}/1),
|
||||||
|
only screen and (min-resolution: round($ratio * 96dpi)),
|
||||||
|
only screen and (min-resolution: $ratio * 1dppx) {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
@mixin hyphens($hyphenation: none) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("hyphens");
|
||||||
|
|
||||||
|
// none | manual | auto
|
||||||
|
@include prefixer(hyphens, $hyphenation, webkit moz ms spec);
|
||||||
|
}
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
@mixin image-rendering ($mode:auto) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("image-rendering");
|
||||||
|
|
||||||
|
@if ($mode == crisp-edges) {
|
||||||
|
-ms-interpolation-mode: nearest-neighbor; // IE8+
|
||||||
|
image-rendering: -moz-crisp-edges;
|
||||||
|
image-rendering: -o-crisp-edges;
|
||||||
|
image-rendering: -webkit-optimize-contrast;
|
||||||
|
image-rendering: crisp-edges;
|
||||||
|
}
|
||||||
|
|
||||||
|
@else {
|
||||||
|
image-rendering: $mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
// Adds keyframes blocks for supported prefixes, removing redundant prefixes in the block's content
|
||||||
|
@mixin keyframes($name) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("keyframes");
|
||||||
|
|
||||||
|
$original-prefix-for-webkit: $prefix-for-webkit;
|
||||||
|
$original-prefix-for-mozilla: $prefix-for-mozilla;
|
||||||
|
$original-prefix-for-microsoft: $prefix-for-microsoft;
|
||||||
|
$original-prefix-for-opera: $prefix-for-opera;
|
||||||
|
$original-prefix-for-spec: $prefix-for-spec;
|
||||||
|
|
||||||
|
@if $original-prefix-for-webkit {
|
||||||
|
@include disable-prefix-for-all();
|
||||||
|
$prefix-for-webkit: true !global;
|
||||||
|
@-webkit-keyframes #{$name} {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $original-prefix-for-mozilla {
|
||||||
|
@include disable-prefix-for-all();
|
||||||
|
$prefix-for-mozilla: true !global;
|
||||||
|
@-moz-keyframes #{$name} {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$prefix-for-webkit: $original-prefix-for-webkit !global;
|
||||||
|
$prefix-for-mozilla: $original-prefix-for-mozilla !global;
|
||||||
|
$prefix-for-microsoft: $original-prefix-for-microsoft !global;
|
||||||
|
$prefix-for-opera: $original-prefix-for-opera !global;
|
||||||
|
$prefix-for-spec: $original-prefix-for-spec !global;
|
||||||
|
|
||||||
|
@if $original-prefix-for-spec {
|
||||||
|
@keyframes #{$name} {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
@mixin linear-gradient($pos, $g1, $g2: null,
|
||||||
|
$g3: null, $g4: null,
|
||||||
|
$g5: null, $g6: null,
|
||||||
|
$g7: null, $g8: null,
|
||||||
|
$g9: null, $g10: null,
|
||||||
|
$fallback: null) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("linear-gradient");
|
||||||
|
|
||||||
|
// Detect what type of value exists in $pos
|
||||||
|
$pos-type: type-of(nth($pos, 1));
|
||||||
|
$pos-spec: null;
|
||||||
|
$pos-degree: null;
|
||||||
|
|
||||||
|
// If $pos is missing from mixin, reassign vars and add default position
|
||||||
|
@if ($pos-type == color) or (nth($pos, 1) == "transparent") {
|
||||||
|
$g10: $g9; $g9: $g8; $g8: $g7; $g7: $g6; $g6: $g5;
|
||||||
|
$g5: $g4; $g4: $g3; $g3: $g2; $g2: $g1; $g1: $pos;
|
||||||
|
$pos: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $pos {
|
||||||
|
$positions: _linear-positions-parser($pos);
|
||||||
|
$pos-degree: nth($positions, 1);
|
||||||
|
$pos-spec: nth($positions, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
$full: $g1, $g2, $g3, $g4, $g5, $g6, $g7, $g8, $g9, $g10;
|
||||||
|
|
||||||
|
// Set $g1 as the default fallback color
|
||||||
|
$fallback-color: nth($g1, 1);
|
||||||
|
|
||||||
|
// If $fallback is a color use that color as the fallback color
|
||||||
|
@if (type-of($fallback) == color) or ($fallback == "transparent") {
|
||||||
|
$fallback-color: $fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
background-color: $fallback-color;
|
||||||
|
background-image: -webkit-linear-gradient($pos-degree $full); // Safari 5.1+, Chrome
|
||||||
|
background-image: unquote("linear-gradient(#{$pos-spec}#{$full})");
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
@mixin perspective($depth: none) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("perspective");
|
||||||
|
|
||||||
|
// none | <length>
|
||||||
|
@include prefixer(perspective, $depth, webkit moz spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin perspective-origin($value: 50% 50%) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("perspective-origin");
|
||||||
|
|
||||||
|
@include prefixer(perspective-origin, $value, webkit moz spec);
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
@mixin placeholder {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("placeholder");
|
||||||
|
|
||||||
|
$placeholders: ":-webkit-input" ":-moz" "-moz" "-ms-input";
|
||||||
|
@each $placeholder in $placeholders {
|
||||||
|
&:#{$placeholder}-placeholder {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
// Requires Sass 3.1+
|
||||||
|
@mixin radial-gradient($g1, $g2,
|
||||||
|
$g3: null, $g4: null,
|
||||||
|
$g5: null, $g6: null,
|
||||||
|
$g7: null, $g8: null,
|
||||||
|
$g9: null, $g10: null,
|
||||||
|
$pos: null,
|
||||||
|
$shape-size: null,
|
||||||
|
$fallback: null) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("radial-gradient");
|
||||||
|
|
||||||
|
$data: _radial-arg-parser($g1, $g2, $pos, $shape-size);
|
||||||
|
$g1: nth($data, 1);
|
||||||
|
$g2: nth($data, 2);
|
||||||
|
$pos: nth($data, 3);
|
||||||
|
$shape-size: nth($data, 4);
|
||||||
|
|
||||||
|
$full: $g1, $g2, $g3, $g4, $g5, $g6, $g7, $g8, $g9, $g10;
|
||||||
|
|
||||||
|
// Strip deprecated cover/contain for spec
|
||||||
|
$shape-size-spec: _shape-size-stripper($shape-size);
|
||||||
|
|
||||||
|
// Set $g1 as the default fallback color
|
||||||
|
$first-color: nth($full, 1);
|
||||||
|
$fallback-color: nth($first-color, 1);
|
||||||
|
|
||||||
|
@if (type-of($fallback) == color) or ($fallback == "transparent") {
|
||||||
|
$fallback-color: $fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Commas and spaces
|
||||||
|
$shape-size: if($shape-size, "#{$shape-size}, ", null);
|
||||||
|
$pos: if($pos, "#{$pos}, ", null);
|
||||||
|
$pos-spec: if($pos, "at #{$pos}", null);
|
||||||
|
$shape-size-spec: if(($shape-size-spec != " ") and ($pos == null), "#{$shape-size-spec}, ", "#{$shape-size-spec} ");
|
||||||
|
|
||||||
|
background-color: $fallback-color;
|
||||||
|
background-image: -webkit-radial-gradient(#{$pos}#{$shape-size}#{$full});
|
||||||
|
background-image: radial-gradient(#{$shape-size-spec}#{$pos-spec}#{$full});
|
||||||
|
}
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Outputs the spec and prefixed versions of the `::selection` pseudo-element.
|
||||||
|
///
|
||||||
|
/// @param {Bool} $current-selector [false]
|
||||||
|
/// If set to `true`, it takes the current element into consideration.
|
||||||
|
///
|
||||||
|
/// @example scss - Usage
|
||||||
|
/// .element {
|
||||||
|
/// @include selection(true) {
|
||||||
|
/// background-color: #ffbb52;
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @example css - CSS Output
|
||||||
|
/// .element::-moz-selection {
|
||||||
|
/// background-color: #ffbb52;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// .element::selection {
|
||||||
|
/// background-color: #ffbb52;
|
||||||
|
/// }
|
||||||
|
|
||||||
|
@mixin selection($current-selector: false) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("selection");
|
||||||
|
|
||||||
|
@if $current-selector {
|
||||||
|
&::-moz-selection {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::selection {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
} @else {
|
||||||
|
::-moz-selection {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
@mixin text-decoration($value) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("text-decoration");
|
||||||
|
|
||||||
|
// <text-decoration-line> || <text-decoration-style> || <text-decoration-color>
|
||||||
|
@include prefixer(text-decoration, $value, moz);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin text-decoration-line($line: none) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("text-decoration-line");
|
||||||
|
|
||||||
|
// none || underline || overline || line-through
|
||||||
|
@include prefixer(text-decoration-line, $line, moz);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin text-decoration-style($style: solid) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("text-decoration-style");
|
||||||
|
|
||||||
|
// solid || double || dotted || dashed || wavy
|
||||||
|
@include prefixer(text-decoration-style, $style, moz webkit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin text-decoration-color($color: currentColor) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("text-decoration-color");
|
||||||
|
|
||||||
|
// currentColor || <color>
|
||||||
|
@include prefixer(text-decoration-color, $color, moz);
|
||||||
|
}
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
@mixin transform($property: none) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("transform");
|
||||||
|
|
||||||
|
// none | <transform-function>
|
||||||
|
@include prefixer(transform, $property, webkit moz ms o spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin transform-origin($axes: 50%) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("transform-origin");
|
||||||
|
|
||||||
|
// x-axis - left | center | right | length | %
|
||||||
|
// y-axis - top | center | bottom | length | %
|
||||||
|
// z-axis - length
|
||||||
|
@include prefixer(transform-origin, $axes, webkit moz ms o spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin transform-style($style: flat) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("transform-style");
|
||||||
|
|
||||||
|
@include prefixer(transform-style, $style, webkit moz ms o spec);
|
||||||
|
}
|
||||||
+81
@@ -0,0 +1,81 @@
|
|||||||
|
// Shorthand mixin. Supports multiple parentheses-deliminated values for each variable.
|
||||||
|
// Example: @include transition (all 2s ease-in-out);
|
||||||
|
// @include transition (opacity 1s ease-in 2s, width 2s ease-out);
|
||||||
|
// @include transition-property (transform, opacity);
|
||||||
|
|
||||||
|
@mixin transition($properties...) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("transition");
|
||||||
|
|
||||||
|
// Fix for vendor-prefix transform property
|
||||||
|
$needs-prefixes: false;
|
||||||
|
$webkit: ();
|
||||||
|
$moz: ();
|
||||||
|
$spec: ();
|
||||||
|
|
||||||
|
// Create lists for vendor-prefixed transform
|
||||||
|
@each $list in $properties {
|
||||||
|
@if nth($list, 1) == "transform" {
|
||||||
|
$needs-prefixes: true;
|
||||||
|
$list1: -webkit-transform;
|
||||||
|
$list2: -moz-transform;
|
||||||
|
$list3: ();
|
||||||
|
|
||||||
|
@each $var in $list {
|
||||||
|
$list3: join($list3, $var);
|
||||||
|
|
||||||
|
@if $var != "transform" {
|
||||||
|
$list1: join($list1, $var);
|
||||||
|
$list2: join($list2, $var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$webkit: append($webkit, $list1);
|
||||||
|
$moz: append($moz, $list2);
|
||||||
|
$spec: append($spec, $list3);
|
||||||
|
} @else {
|
||||||
|
$webkit: append($webkit, $list, comma);
|
||||||
|
$moz: append($moz, $list, comma);
|
||||||
|
$spec: append($spec, $list, comma);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $needs-prefixes {
|
||||||
|
-webkit-transition: $webkit;
|
||||||
|
-moz-transition: $moz;
|
||||||
|
transition: $spec;
|
||||||
|
} @else {
|
||||||
|
@if length($properties) >= 1 {
|
||||||
|
@include prefixer(transition, $properties, webkit moz spec);
|
||||||
|
} @else {
|
||||||
|
$properties: all 0.15s ease-out 0s;
|
||||||
|
@include prefixer(transition, $properties, webkit moz spec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin transition-property($properties...) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("transition-property");
|
||||||
|
|
||||||
|
-webkit-transition-property: transition-property-names($properties, "webkit");
|
||||||
|
-moz-transition-property: transition-property-names($properties, "moz");
|
||||||
|
transition-property: transition-property-names($properties, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin transition-duration($times...) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("transition-duration");
|
||||||
|
|
||||||
|
@include prefixer(transition-duration, $times, webkit moz spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin transition-timing-function($motions...) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("transition-timing-function");
|
||||||
|
|
||||||
|
// ease | linear | ease-in | ease-out | ease-in-out | cubic-bezier()
|
||||||
|
@include prefixer(transition-timing-function, $motions, webkit moz spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin transition-delay($times...) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("transition-delay");
|
||||||
|
|
||||||
|
@include prefixer(transition-delay, $times, webkit moz spec);
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
@mixin user-select($value: none) {
|
||||||
|
@include _bourbon-deprecate-for-prefixing("user-select");
|
||||||
|
|
||||||
|
@include prefixer(user-select, $value, webkit moz ms spec);
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
@function assign-inputs($inputs, $pseudo: null) {
|
||||||
|
@if $output-bourbon-deprecation-warnings == true {
|
||||||
|
@warn "[Bourbon] [Deprecation] `assign-inputs` is deprecated and will be " +
|
||||||
|
"removed in 5.0.0.";
|
||||||
|
}
|
||||||
|
|
||||||
|
$list: ();
|
||||||
|
|
||||||
|
@each $input in $inputs {
|
||||||
|
$input: unquote($input);
|
||||||
|
$input: if($pseudo, $input + ":" + $pseudo, $input);
|
||||||
|
$list: append($list, $input, comma);
|
||||||
|
}
|
||||||
|
|
||||||
|
@return $list;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Checks if a list does not contains a value.
|
||||||
|
///
|
||||||
|
/// @access private
|
||||||
|
///
|
||||||
|
/// @param {List} $list
|
||||||
|
/// The list to check against.
|
||||||
|
///
|
||||||
|
/// @return {Bool}
|
||||||
|
|
||||||
|
@function contains-falsy($list) {
|
||||||
|
@if $output-bourbon-deprecation-warnings == true {
|
||||||
|
@warn "[Bourbon] [Deprecation] `contains-falsy` is deprecated and will be " +
|
||||||
|
"removed in 5.0.0.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $item in $list {
|
||||||
|
@if not $item {
|
||||||
|
@return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@return false;
|
||||||
|
}
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Checks if a list contains a value(s).
|
||||||
|
///
|
||||||
|
/// @access private
|
||||||
|
///
|
||||||
|
/// @param {List} $list
|
||||||
|
/// The list to check against.
|
||||||
|
///
|
||||||
|
/// @param {List} $values
|
||||||
|
/// A single value or list of values to check for.
|
||||||
|
///
|
||||||
|
/// @example scss - Usage
|
||||||
|
/// contains($list, $value)
|
||||||
|
///
|
||||||
|
/// @return {Bool}
|
||||||
|
|
||||||
|
@function contains($list, $values...) {
|
||||||
|
@if $output-bourbon-deprecation-warnings == true {
|
||||||
|
@warn "[Bourbon] [Deprecation] `contains` is deprecated and will be " +
|
||||||
|
"removed in 5.0.0.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $value in $values {
|
||||||
|
@if type-of(index($list, $value)) != "number" {
|
||||||
|
@return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@return true;
|
||||||
|
}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Checks for a valid CSS length.
|
||||||
|
///
|
||||||
|
/// @param {String} $value
|
||||||
|
|
||||||
|
@function is-length($value) {
|
||||||
|
@if $output-bourbon-deprecation-warnings == true {
|
||||||
|
@warn "[Bourbon] [Deprecation] `is-length` is deprecated and will be " +
|
||||||
|
"removed in 5.0.0.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@return type-of($value) != "null" and (str-slice($value + "", 1, 4) == "calc"
|
||||||
|
or index(auto inherit initial 0, $value)
|
||||||
|
or (type-of($value) == "number" and not(unitless($value))));
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Programatically determines whether a color is light or dark.
|
||||||
|
///
|
||||||
|
/// @link http://robots.thoughtbot.com/closer-look-color-lightness
|
||||||
|
///
|
||||||
|
/// @param {Color (Hex)} $color
|
||||||
|
///
|
||||||
|
/// @example scss - Usage
|
||||||
|
/// is-light($color)
|
||||||
|
///
|
||||||
|
/// @return {Bool}
|
||||||
|
|
||||||
|
@function is-light($hex-color) {
|
||||||
|
@if $output-bourbon-deprecation-warnings == true {
|
||||||
|
@warn "[Bourbon] [Deprecation] `is-light` is deprecated and will be " +
|
||||||
|
"removed in 5.0.0.";
|
||||||
|
}
|
||||||
|
|
||||||
|
$-local-red: red(rgba($hex-color, 1));
|
||||||
|
$-local-green: green(rgba($hex-color, 1));
|
||||||
|
$-local-blue: blue(rgba($hex-color, 1));
|
||||||
|
$-local-lightness: ($-local-red * 0.2126 + $-local-green * 0.7152 + $-local-blue * 0.0722) / 255;
|
||||||
|
|
||||||
|
@return $-local-lightness > 0.6;
|
||||||
|
}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Checks for a valid number.
|
||||||
|
///
|
||||||
|
/// @param {Number} $value
|
||||||
|
///
|
||||||
|
/// @require {function} contains
|
||||||
|
|
||||||
|
@function is-number($value) {
|
||||||
|
@if $output-bourbon-deprecation-warnings == true {
|
||||||
|
@warn "[Bourbon] [Deprecation] `is-number` is deprecated and will be " +
|
||||||
|
"removed in 5.0.0.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@return contains("0" "1" "2" "3" "4" "5" "6" "7" "8" "9" 0 1 2 3 4 5 6 7 8 9, $value);
|
||||||
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Checks for a valid CSS size.
|
||||||
|
///
|
||||||
|
/// @param {String} $value
|
||||||
|
///
|
||||||
|
/// @require {function} contains
|
||||||
|
/// @require {function} is-length
|
||||||
|
|
||||||
|
@function is-size($value) {
|
||||||
|
@if $output-bourbon-deprecation-warnings == true {
|
||||||
|
@warn "[Bourbon] [Deprecation] `is-size` is deprecated and will be " +
|
||||||
|
"removed in 5.0.0.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@return is-length($value)
|
||||||
|
or contains("fill" "fit-content" "min-content" "max-content", $value);
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
// Scaling Variables
|
||||||
|
$golden: 1.618;
|
||||||
|
$minor-second: 1.067;
|
||||||
|
$major-second: 1.125;
|
||||||
|
$minor-third: 1.2;
|
||||||
|
$major-third: 1.25;
|
||||||
|
$perfect-fourth: 1.333;
|
||||||
|
$augmented-fourth: 1.414;
|
||||||
|
$perfect-fifth: 1.5;
|
||||||
|
$minor-sixth: 1.6;
|
||||||
|
$major-sixth: 1.667;
|
||||||
|
$minor-seventh: 1.778;
|
||||||
|
$major-seventh: 1.875;
|
||||||
|
$octave: 2;
|
||||||
|
$major-tenth: 2.5;
|
||||||
|
$major-eleventh: 2.667;
|
||||||
|
$major-twelfth: 3;
|
||||||
|
$double-octave: 4;
|
||||||
|
|
||||||
|
$modular-scale-ratio: $perfect-fourth !default;
|
||||||
|
$modular-scale-base: em($em-base) !default;
|
||||||
|
|
||||||
|
@function modular-scale($increment, $value: $modular-scale-base, $ratio: $modular-scale-ratio) {
|
||||||
|
$v1: nth($value, 1);
|
||||||
|
$v2: nth($value, length($value));
|
||||||
|
$value: $v1;
|
||||||
|
|
||||||
|
// scale $v2 to just above $v1
|
||||||
|
@while $v2 > $v1 {
|
||||||
|
$v2: ($v2 / $ratio); // will be off-by-1
|
||||||
|
}
|
||||||
|
@while $v2 < $v1 {
|
||||||
|
$v2: ($v2 * $ratio); // will fix off-by-1
|
||||||
|
}
|
||||||
|
|
||||||
|
// check AFTER scaling $v2 to prevent double-counting corner-case
|
||||||
|
$double-stranded: $v2 > $v1;
|
||||||
|
|
||||||
|
@if $increment > 0 {
|
||||||
|
@for $i from 1 through $increment {
|
||||||
|
@if $double-stranded and ($v1 * $ratio) > $v2 {
|
||||||
|
$value: $v2;
|
||||||
|
$v2: ($v2 * $ratio);
|
||||||
|
} @else {
|
||||||
|
$v1: ($v1 * $ratio);
|
||||||
|
$value: $v1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $increment < 0 {
|
||||||
|
// adjust $v2 to just below $v1
|
||||||
|
@if $double-stranded {
|
||||||
|
$v2: ($v2 / $ratio);
|
||||||
|
}
|
||||||
|
|
||||||
|
@for $i from $increment through -1 {
|
||||||
|
@if $double-stranded and ($v1 / $ratio) < $v2 {
|
||||||
|
$value: $v2;
|
||||||
|
$v2: ($v2 / $ratio);
|
||||||
|
} @else {
|
||||||
|
$v1: ($v1 / $ratio);
|
||||||
|
$value: $v1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@return $value;
|
||||||
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
// Convert pixels to ems
|
||||||
|
// eg. for a relational value of 12px write em(12) when the parent is 16px
|
||||||
|
// if the parent is another value say 24px write em(12, 24)
|
||||||
|
|
||||||
|
@function em($pxval, $base: $em-base) {
|
||||||
|
@if $output-bourbon-deprecation-warnings == true {
|
||||||
|
@warn "[Bourbon] [Deprecation] `em` is deprecated and will be " +
|
||||||
|
"removed in 5.0.0.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@if not unitless($pxval) {
|
||||||
|
$pxval: strip-units($pxval);
|
||||||
|
}
|
||||||
|
@if not unitless($base) {
|
||||||
|
$base: strip-units($base);
|
||||||
|
}
|
||||||
|
@return ($pxval / $base) * 1em;
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
// Convert pixels to rems
|
||||||
|
// eg. for a relational value of 12px write rem(12)
|
||||||
|
// Assumes $em-base is the font-size of <html>
|
||||||
|
|
||||||
|
@function rem($pxval) {
|
||||||
|
@if $output-bourbon-deprecation-warnings == true {
|
||||||
|
@warn "[Bourbon] [Deprecation] `rem` is deprecated and will be " +
|
||||||
|
"removed in 5.0.0.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@if not unitless($pxval) {
|
||||||
|
$pxval: strip-units($pxval);
|
||||||
|
}
|
||||||
|
|
||||||
|
$base: $em-base;
|
||||||
|
@if not unitless($base) {
|
||||||
|
$base: strip-units($base);
|
||||||
|
}
|
||||||
|
@return ($pxval / $base) * 1rem;
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Mixes a color with black.
|
||||||
|
///
|
||||||
|
/// @param {Color} $color
|
||||||
|
///
|
||||||
|
/// @param {Number (Percentage)} $percent
|
||||||
|
/// The amount of black to be mixed in.
|
||||||
|
///
|
||||||
|
/// @example scss - Usage
|
||||||
|
/// .element {
|
||||||
|
/// background-color: shade(#ffbb52, 60%);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @example css - CSS Output
|
||||||
|
/// .element {
|
||||||
|
/// background-color: #664a20;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @return {Color}
|
||||||
|
|
||||||
|
@function shade($color, $percent) {
|
||||||
|
@return mix(#000, $color, $percent);
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Strips the unit from a number.
|
||||||
|
///
|
||||||
|
/// @param {Number (With Unit)} $value
|
||||||
|
///
|
||||||
|
/// @example scss - Usage
|
||||||
|
/// $dimension: strip-units(10em);
|
||||||
|
///
|
||||||
|
/// @example css - CSS Output
|
||||||
|
/// $dimension: 10;
|
||||||
|
///
|
||||||
|
/// @return {Number (Unitless)}
|
||||||
|
|
||||||
|
@function strip-units($value) {
|
||||||
|
@if $output-bourbon-deprecation-warnings == true {
|
||||||
|
@warn "[Bourbon] [Deprecation] `strip-units` is deprecated and will be " +
|
||||||
|
"removed in 5.0.0. Use the renamed `strip-unit` function instead.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@return ($value / ($value * 0 + 1));
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Mixes a color with white.
|
||||||
|
///
|
||||||
|
/// @param {Color} $color
|
||||||
|
///
|
||||||
|
/// @param {Number (Percentage)} $percent
|
||||||
|
/// The amount of white to be mixed in.
|
||||||
|
///
|
||||||
|
/// @example scss - Usage
|
||||||
|
/// .element {
|
||||||
|
/// background-color: tint(#6ecaa6, 40%);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @example css - CSS Output
|
||||||
|
/// .element {
|
||||||
|
/// background-color: #a8dfc9;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @return {Color}
|
||||||
|
|
||||||
|
@function tint($color, $percent) {
|
||||||
|
@return mix(#fff, $color, $percent);
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
// Return vendor-prefixed property names if appropriate
|
||||||
|
// Example: transition-property-names((transform, color, background), moz) -> -moz-transform, color, background
|
||||||
|
//************************************************************************//
|
||||||
|
@function transition-property-names($props, $vendor: false) {
|
||||||
|
@if $output-bourbon-deprecation-warnings == true {
|
||||||
|
@warn "[Bourbon] [Deprecation] `transition-property-names` is deprecated " +
|
||||||
|
"and will be removed in 5.0.0.";
|
||||||
|
}
|
||||||
|
|
||||||
|
$new-props: ();
|
||||||
|
|
||||||
|
@each $prop in $props {
|
||||||
|
$new-props: append($new-props, transition-property-name($prop, $vendor), comma);
|
||||||
|
}
|
||||||
|
|
||||||
|
@return $new-props;
|
||||||
|
}
|
||||||
|
|
||||||
|
@function transition-property-name($prop, $vendor: false) {
|
||||||
|
@if $output-bourbon-deprecation-warnings == true {
|
||||||
|
@warn "[Bourbon] [Deprecation] `transition-property-name` is deprecated " +
|
||||||
|
"and will be removed in 5.0.0.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// put other properties that need to be prefixed here aswell
|
||||||
|
@if $vendor and $prop == transform {
|
||||||
|
@return unquote('-'+$vendor+'-'+$prop);
|
||||||
|
}
|
||||||
|
@else {
|
||||||
|
@return $prop;
|
||||||
|
}
|
||||||
|
}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Converts shorthand to the 4-value syntax.
|
||||||
|
///
|
||||||
|
/// @param {List} $shorthand
|
||||||
|
///
|
||||||
|
/// @example scss - Usage
|
||||||
|
/// .element {
|
||||||
|
/// margin: unpack(1em 2em);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @example css - CSS Output
|
||||||
|
/// .element {
|
||||||
|
/// margin: 1em 2em 1em 2em;
|
||||||
|
/// }
|
||||||
|
|
||||||
|
@function unpack($shorthand) {
|
||||||
|
@if $output-bourbon-deprecation-warnings == true {
|
||||||
|
@warn "[Bourbon] [Deprecation] `unpack` is deprecated and will be " +
|
||||||
|
"removed in 5.0.0.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@if length($shorthand) == 1 {
|
||||||
|
@return nth($shorthand, 1) nth($shorthand, 1) nth($shorthand, 1) nth($shorthand, 1);
|
||||||
|
} @else if length($shorthand) == 2 {
|
||||||
|
@return nth($shorthand, 1) nth($shorthand, 2) nth($shorthand, 1) nth($shorthand, 2);
|
||||||
|
} @else if length($shorthand) == 3 {
|
||||||
|
@return nth($shorthand, 1) nth($shorthand, 2) nth($shorthand, 3) nth($shorthand, 2);
|
||||||
|
} @else {
|
||||||
|
@return $shorthand;
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
//************************************************************************//
|
||||||
|
// Helper function for str-to-num fn.
|
||||||
|
// Source: http://sassmeister.com/gist/9647408
|
||||||
|
//************************************************************************//
|
||||||
|
@function _convert-units($number, $unit) {
|
||||||
|
@if $output-bourbon-deprecation-warnings == true {
|
||||||
|
@warn "[Bourbon] [Deprecation] `_convert-units` is deprecated and will " +
|
||||||
|
"be removed in 5.0.0.";
|
||||||
|
}
|
||||||
|
|
||||||
|
$strings: "px", "cm", "mm", "%", "ch", "pica", "in", "em", "rem", "pt", "pc", "ex", "vw", "vh", "vmin", "vmax", "deg", "rad", "grad", "turn";
|
||||||
|
$units: 1px, 1cm, 1mm, 1%, 1ch, 1pica, 1in, 1em, 1rem, 1pt, 1pc, 1ex, 1vw, 1vh, 1vmin, 1vmax, 1deg, 1rad, 1grad, 1turn;
|
||||||
|
$index: index($strings, $unit);
|
||||||
|
|
||||||
|
@if not $index {
|
||||||
|
@warn "Unknown unit `#{$unit}`.";
|
||||||
|
@return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@if type-of($number) != "number" {
|
||||||
|
@warn "`#{$number} is not a number`";
|
||||||
|
@return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@return $number * nth($units, $index);
|
||||||
|
}
|
||||||
+103
@@ -0,0 +1,103 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
/// Directional-property mixins are shorthands for writing properties like the following
|
||||||
|
///
|
||||||
|
/// @ignore You can also use `false` instead of `null`.
|
||||||
|
///
|
||||||
|
/// @param {List} $vals
|
||||||
|
/// List of directional values
|
||||||
|
///
|
||||||
|
/// @example scss - Usage
|
||||||
|
/// .element {
|
||||||
|
/// @include border-style(dotted null);
|
||||||
|
/// @include margin(null 0 10px);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @example css - CSS Output
|
||||||
|
/// .element {
|
||||||
|
/// border-bottom-style: dotted;
|
||||||
|
/// border-top-style: dotted;
|
||||||
|
/// margin-bottom: 10px;
|
||||||
|
/// margin-left: 0;
|
||||||
|
/// margin-right: 0;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @require {function} contains-falsy
|
||||||
|
///
|
||||||
|
/// @return {List}
|
||||||
|
|
||||||
|
@function collapse-directionals($vals) {
|
||||||
|
@if $output-bourbon-deprecation-warnings == true {
|
||||||
|
@warn "[Bourbon] [Deprecation] `collapse-directionals` is deprecated and " +
|
||||||
|
"will be removed in 5.0.0.";
|
||||||
|
}
|
||||||
|
|
||||||
|
$output: null;
|
||||||
|
|
||||||
|
$a: nth($vals, 1);
|
||||||
|
$b: if(length($vals) < 2, $a, nth($vals, 2));
|
||||||
|
$c: if(length($vals) < 3, $a, nth($vals, 3));
|
||||||
|
$d: if(length($vals) < 2, $a, nth($vals, if(length($vals) < 4, 2, 4)));
|
||||||
|
|
||||||
|
@if $a == 0 { $a: 0; }
|
||||||
|
@if $b == 0 { $b: 0; }
|
||||||
|
@if $c == 0 { $c: 0; }
|
||||||
|
@if $d == 0 { $d: 0; }
|
||||||
|
|
||||||
|
@if $a == $b and $a == $c and $a == $d { $output: $a; }
|
||||||
|
@else if $a == $c and $b == $d { $output: $a $b; }
|
||||||
|
@else if $b == $d { $output: $a $b $c; }
|
||||||
|
@else { $output: $a $b $c $d; }
|
||||||
|
|
||||||
|
@return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Output directional properties, for instance `margin`.
|
||||||
|
///
|
||||||
|
/// @access private
|
||||||
|
///
|
||||||
|
/// @param {String} $pre
|
||||||
|
/// Prefix to use
|
||||||
|
/// @param {String} $suf
|
||||||
|
/// Suffix to use
|
||||||
|
/// @param {List} $vals
|
||||||
|
/// List of values
|
||||||
|
///
|
||||||
|
/// @require {function} collapse-directionals
|
||||||
|
/// @require {function} contains-falsy
|
||||||
|
|
||||||
|
@mixin directional-property($pre, $suf, $vals) {
|
||||||
|
@include _bourbon-deprecate("directional-property");
|
||||||
|
|
||||||
|
// Property Names
|
||||||
|
$top: $pre + "-top" + if($suf, "-#{$suf}", "");
|
||||||
|
$bottom: $pre + "-bottom" + if($suf, "-#{$suf}", "");
|
||||||
|
$left: $pre + "-left" + if($suf, "-#{$suf}", "");
|
||||||
|
$right: $pre + "-right" + if($suf, "-#{$suf}", "");
|
||||||
|
$all: $pre + if($suf, "-#{$suf}", "");
|
||||||
|
|
||||||
|
$vals: collapse-directionals($vals);
|
||||||
|
|
||||||
|
@if contains-falsy($vals) {
|
||||||
|
@if nth($vals, 1) { #{$top}: nth($vals, 1); }
|
||||||
|
|
||||||
|
@if length($vals) == 1 {
|
||||||
|
@if nth($vals, 1) { #{$right}: nth($vals, 1); }
|
||||||
|
} @else {
|
||||||
|
@if nth($vals, 2) { #{$right}: nth($vals, 2); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@if length($vals) == 2 {
|
||||||
|
@if nth($vals, 1) { #{$bottom}: nth($vals, 1); }
|
||||||
|
@if nth($vals, 2) { #{$left}: nth($vals, 2); }
|
||||||
|
} @else if length($vals) == 3 {
|
||||||
|
@if nth($vals, 3) { #{$bottom}: nth($vals, 3); }
|
||||||
|
@if nth($vals, 2) { #{$left}: nth($vals, 2); }
|
||||||
|
} @else if length($vals) == 4 {
|
||||||
|
@if nth($vals, 3) { #{$bottom}: nth($vals, 3); }
|
||||||
|
@if nth($vals, 4) { #{$left}: nth($vals, 4); }
|
||||||
|
}
|
||||||
|
} @else {
|
||||||
|
#{$all}: $vals;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user